1. First understand the recording process
- 1. Define AudioRecord recording related parameters, such as audio acquisition source, audio sampling rate, channel, data format, and minimum recording cache
//Audio acquisition source private static final int mAudioSource = MediaRecorder.AudioSource.MIC; //Microphone //Audio sampling rate (the sampling rate of mediadecoder is generally 8000Hz AAC and 44100Hz. Setting the sampling rate to 44100 is currently the common sampling rate. The official document indicates that this value can be compatible with all settings) private static final int mSampleRateInHz = 44100; //Vocal tract private static final int mChannelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO; //Mono channel //Data format (specify the format of the sampled data and the size of each sample) //Specifies the number of audio quantization bits, and the following various possible constants are specified in the AudioFormaat class. Generally, we choose encoding PCM and encoding PCM to represent pulse code modulation, which is actually the original audio sample. //Therefore, the resolution of each sample can be set to 16 bits or 8 bits, 16 bits will take up more space and processing power, and the audio represented will be closer to the real. private static final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; //Specify the minimum recording buffer size private int mBufferSizeInBytes;
- 2. Calculate the minimum recording buffer size through audio sampling rate, channel and data format
//Calculate the minimum recording buffer size mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig,mAudioFormat);
- 3. Create AudioRecord with the above five parameters
mAudioRecord = new AudioRecord(mAudioSource,mSampleRateInHz,mChannelConfig,mAudioFormat,mBufferSizeInBytes);
- 4. Create a file to save the audio PCM.
//File to store AudioRecord PCM data private File mRecordingFile; //Recording pcm data file path + file name private static final String mFileName = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"audiorecordtest.pcm"; //Stream to write PCM data to file private DataOutputStream mDataOutputStream; //Creating a file for recording PCM data storage mRecordingFile = new File(mFileName); if (mRecordingFile.exists()){ mRecordingFile.delete(); } mRecordingFile.createNewFile(); //Initialization flow mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile)));
- 5. To start recording, first create a byte [] with the minimum recording buffer size, which is used to write sound data, then record and write files at the same time.
byte[] buffer = new byte[mBufferSizeInBytes]; //Start recording mAudioRecord.startRecording(); int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes); //If there is no error in the audio data, write to the file if (AudioRecord.ERROR_INVALID_OPERATION != bufferReadResult && mDataOutputStream != null){ for (int i=0 ; i < bufferReadResult ; i++){ mDataOutputStream.write(buffer[i]); } }
- 6. Close recording and release related resources after recording
mDataOutputStream.close(); mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null;
- 7. At this time, the audio PCM file has been generated, but it can't be played after clicking. So we need to add the file header WAV (or other) so that it can play.
- First define the storage path of the new file test.wav.
//Define the location of wav files String wavFilename = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"test.wav"; File wavFile = new File(wavFilename); if (wavFile.exists()){ wavFile.delete(); } wavFile.createNewFile();
- Then start converting PCM files to WAV files
public void pcmTowav(String pcmfilepath , String wavfilepath ) throws IOException { FileInputStream pcmIn; FileOutputStream wavOut; //The original pcm data size does not contain (file header). To add a file header, use long pcmLength; //The total size of the file (including the file header). To add a file header long dataLength; //Channel ID (1 (single channel) or 2 (dual channel), for adding file header) int channels = (mChannelConfig == AudioFormat.CHANNEL_OUT_MONO ? 1 : 2); //Sample rate, to add a file header int sampleRate = mSampleRateInHz; //Information transfer rate = ((sampling rate * number of channels * number of bits of each value) / 8), to add a file header int byteRate = sampleRate*channels*16/8; byte[] data = new byte[mBufferSizeInBytes]; pcmIn = new FileInputStream(pcmfilepath); wavOut = new FileOutputStream(wavfilepath); pcmLength = pcmIn.getChannel().size(); //wav file header 44 bytes dataLength = pcmLength+44; //Write wav file header first writeHeader(wavOut , pcmLength , dataLength , sampleRate , channels , byteRate); //Write data again while (pcmIn.read(data)!=-1){ wavOut.write(data); } Log.i("TAG","wav File write complete"); pcmIn.close(); wavOut.close(); }
- Write wav file header as follows:
private void writeHeader(FileOutputStream wavOut, long pcmLength, long dataLength, int sampleRate, int channels, int byteRate) throws IOException { //44 bytes of wave file header byte[] header = new byte[44]; /*0-11 Bytes (RIFF chunk: riff file description block)*/ //Files marked as RIFF files header[0]='R'; header[1]='I'; header[2]='F'; header[3]='F'; //Total file length header[4]= (byte) (dataLength * 0xff); //Take a byte (lower 8 bits) header[5]= (byte) ((dataLength >> 8) * 0xff); //Take a byte (middle 8 bits) header[6]= (byte) ((dataLength >> 16) * 0xff); //Take a byte (8-bit times) header[7]= (byte) ((dataLength >> 24) * 0xff); //Take a byte (high 8 bits) //File type is marked WAVE header[8]='W'; header[9]='A'; header[10]='V'; header[11]='E'; /*13-35 Byte (fmt chunk: data format information block)*/ //Mark format block, description data format information 4 bytes header[12]='f'; header[13]='m'; header[14]='t'; header[15]=' '; //There should be a space //Format data length 4 bytes header[16]=16; header[17]=0; header[18]=0; header[19]=0; //Format type (e.g. 1 is PCM) 2 bytes header[20]=1; header[21]=0; //Channel number 1 is mono, 2 is dual header[22]= (byte) channels; header[23]=0; //sampling rate header[24]= (byte) (sampleRate * 0xff); header[25]= (byte) ((sampleRate >> 8) * 0xff); header[26]= (byte) ((sampleRate >> 16) * 0xff); header[27]= (byte) ((sampleRate >> 24) * 0xff); //Bit rate = (sampling rate * bits of each value * channel) / 8. header[28] = (byte) (byteRate & 0xff); header[29] = (byte) ((byteRate >> 8) & 0xff); header[30] = (byte) ((byteRate >> 16) & 0xff); header[31] = (byte) ((byteRate >> 24) & 0xff); // (bits of each sample value * channel) / 8 the number of bits required for a sample value of all channels, of which the number of channels shall be at least 2, and those less than 2 shall be calculated as 2 header[32]= (16 * 2 / 8); //(for example, PCM16, one Frame of dual channel is equal to 16 * 2 / 8 = 4 bytes), header[33]= 0 ; //Bits of each value (usually 16 bits or 8 bits for sample value) header[34]=16; header[35]=0; /*36 After bytes (data chunk)*/ //The "data" block marks the beginning of the data section. header[36]='d'; header[37]='a'; header[38]='t'; header[39]='a'; //Describes the size of the data section header[40] = (byte) (pcmLength & 0xff); header[41] = (byte) ((pcmLength >> 8) & 0xff); header[42] = (byte) ((pcmLength >> 16) & 0xff); header[43] = (byte) ((pcmLength >> 24) & 0xff); //Write header wavOut.write(header,0,44); }
- 8. At this time, the audio wav file has been generated and can be clicked to play.
2. The source code is as follows:
Note: the recording should be in the thread
Activity.java
start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mThread = new Thread(){ @Override public void run() { try { startRecord(); } catch (IOException e) { e.printStackTrace(); stopRecord(); } } }; mThread.start(); } }); stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { stopRecord(); } }); addHead.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { addHead(); } catch (IOException e) { e.printStackTrace(); } } }); private void initAudioRecord() throws IOException { //Calculate the minimum recording buffer size mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig,mAudioFormat); //Create AudioRecord. The AudioRecord class does not actually save the captured audio, so you need to manually create the file and save the download. mAudioRecord = new AudioRecord(mAudioSource,mSampleRateInHz,mChannelConfig,mAudioFormat,mBufferSizeInBytes); //Create a file for recording data storage mRecordingFile = new File(mFileName); if (mRecordingFile.exists()){ mRecordingFile.delete(); } mRecordingFile.createNewFile(); //Initialization flow mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile))); } //Start recording private void startRecord() throws IOException { //Initializing AudioRecord and stored files and streams initAudioRecord(); //Check whether the parameters (sampling rate, channel, sampling precision) of AudioRecord.getMinBufferSize support the current hardware device if (mBufferSizeInBytes == AudioRecord.ERROR_BAD_VALUE || mBufferSizeInBytes == AudioRecord.ERROR){ Log.i("TAG","Unable To getMinBufferSize"); }else { //Test AudioRecord initialization succeeded if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED){ byte[] buffer = new byte[mBufferSizeInBytes]; //Start recording mAudioRecord.startRecording(); Log.i("TAG","Start recording"); isRecording = true; //If recording status while (mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING && isRecording){ Log.i("TAG","Recording"); int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes); //If there is no error in the audio data, write to the file if (AudioRecord.ERROR_INVALID_OPERATION != bufferReadResult && mDataOutputStream != null){ for (int i=0 ; i < bufferReadResult ; i++){ mDataOutputStream.write(buffer[i]); } } } mDataOutputStream.close(); } } } //Add wav header private void addHead() throws IOException { //Define the location of wav files String wavFilename = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"test.wav"; File wavFile = new File(wavFilename); if (wavFile.exists()){ wavFile.delete(); } wavFile.createNewFile(); mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig,mAudioFormat); PcmToWavUtil pcmtowavutil = new PcmToWavUtil(mSampleRateInHz,mChannelConfig,mBufferSizeInBytes,mAudioFormat); //Add wav file header and convert pcm data to WAV file pcmtowavutil.pcmTowav(mFileName,wavFilename); } //Stop recording, release resources private void stopRecord() { isRecording = false; if (mAudioRecord != null && mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED){ mAudioRecord.stop(); Log.i("TAG","Recording stop"); } if (mAudioRecord != null){ mAudioRecord.release(); mAudioRecord = null; mThread = null; } }
PcmToWavUtil.java
public class PcmToWavUtil { //sampling rate private int mSampleRateInHz; //Channels private int mChannelConfig; //Minimum buffer size private int mBufferSizeInBytes; //data format //Here, audioformat.encoding? PCM? 16bit is passed in, [so the number of bits of each value in the following code is 16, and the byte of each value is 2, which will also be used] private int mAudioFormat; public PcmToWavUtil(int mSampleRateInHz , int mChannelConfig , int mBufferSizeInBytes,int mAudioFormat){ this.mSampleRateInHz = mSampleRateInHz; this.mChannelConfig = mChannelConfig; this.mBufferSizeInBytes = mBufferSizeInBytes; this.mAudioFormat = mAudioFormat; } public void pcmTowav(String pcmfilepath , String wavfilepath ) throws IOException { FileInputStream pcmIn; FileOutputStream wavOut; //The original pcm data size does not contain (file header). To add a file header, use long pcmLength; //The total size of the file (including the file header). To add a file header long dataLength; //Channel ID (1 (single channel) or 2 (dual channel), for adding file header) int channels = (mChannelConfig == AudioFormat.CHANNEL_OUT_MONO ? 1 : 2); //Sample rate, to add a file header int sampleRate = mSampleRateInHz; //Information transfer rate = ((sampling rate * number of channels * number of bits of each value) / 8), to add a file header int byteRate = sampleRate*channels*16/8; byte[] data = new byte[mBufferSizeInBytes]; pcmIn = new FileInputStream(pcmfilepath); wavOut = new FileOutputStream(wavfilepath); pcmLength = pcmIn.getChannel().size(); //wav file header 44 bytes dataLength = pcmLength+44; //Write wav file header first writeHeader(wavOut , pcmLength , dataLength , sampleRate , channels , byteRate); //Write data again while (pcmIn.read(data)!=-1){ wavOut.write(data); } Log.i("TAG","wav File write complete"); pcmIn.close(); wavOut.close(); } private void writeHeader(FileOutputStream wavOut, long pcmLength, long dataLength, int sampleRate, int channels, int byteRate) throws IOException { //44 bytes of wave file header byte[] header = new byte[44]; /*0-11 Bytes (RIFF chunk: riff file description block)*/ header[0]='R'; header[1]='I'; header[2]='F'; header[3]='F'; header[4]= (byte) (dataLength * 0xff); //Take a byte (lower 8 bits) header[5]= (byte) ((dataLength >> 8) * 0xff); //Take a byte (middle 8 bits) header[6]= (byte) ((dataLength >> 16) * 0xff); //Take a byte (8-bit times) header[7]= (byte) ((dataLength >> 24) * 0xff); //Take a byte (high 8 bits) header[8]='W'; header[9]='A'; header[10]='V'; header[11]='E'; /*13-35 Byte (fmt chunk: data format information block)*/ header[12]='f'; header[13]='m'; header[14]='t'; header[15]=' '; //There should be a space header[16]=16; header[17]=0; header[18]=0; header[19]=0; header[20]=1; header[21]=0; header[22]= (byte) channels; header[23]=0; header[24]= (byte) (sampleRate * 0xff); header[25]= (byte) ((sampleRate >> 8) * 0xff); header[26]= (byte) ((sampleRate >> 16) * 0xff); header[27]= (byte) ((sampleRate >> 24) * 0xff); header[28] = (byte) (byteRate & 0xff); header[29] = (byte) ((byteRate >> 8) & 0xff); header[30] = (byte) ((byteRate >> 16) & 0xff); header[31] = (byte) ((byteRate >> 24) & 0xff); header[32]= (16 * 2 / 8); // header[33]= 0 ; header[34]=16; header[35]=0; /*36 After bytes (data chunk)*/ header[36]='d'; header[37]='a'; header[38]='t'; header[39]='a'; header[40] = (byte) (pcmLength & 0xff); header[41] = (byte) ((pcmLength >> 8) & 0xff); header[42] = (byte) ((pcmLength >> 16) & 0xff); header[43] = (byte) ((pcmLength >> 24) & 0xff); //Write header wavOut.write(header,0,44); } }