android.media.AudioRecord
android.media.AudioRecord
. It writes data to the internal buffer, from which we must periodically take it.audioSource | Where is the recording. In our case it is MediaRecorder.AudioSource.MIC |
---|---|
sampleRateInHz | The sampling rate in hertz. Documentation states that 44100Hz is supported by all devices. |
channelConfig | Channel configuration. May be CHANNEL_IN_MONO CHANNEL_IN_MONO or CHANNEL_IN_STEREO CHANNEL_IN_STEREO . Mono works everywhere.Important: these constants do not coincide with the number of channels that they designate. This parameter cannot be passed 1 or 2. |
audioFormat | The input format, better known as codec. May be ENCODING_PCM_16BIT ENCODING_PCM_16BIT or ENCODING_PCM_8BIT |
bufferSizeInBytes | The size of that inner buffer. From it you can read the audio stream. The size of the read portion should not exceed this value. This parameter has a minimum value that can be obtained through getMinBufferSize() getMinBufferSize() . |
getState()
getState()
. If she returns STATE_INITIALIZED
STATE_INITIALIZED
, then everything is fine if STATE_UNINITIALIZED
STATE_UNINITIALIZED
means an error has occurred.getMinBufferSize()
. The second, in fact, it is.AudioRecord
object can work. Parameters have the same meaning as for the constructor. It should be noted that using this value for recording is not the best idea. If the system is still busy with something, the program may still not have time to read all the data in a row, and there will be holes in the record. I met the advice to take a size ten times larger.getMinBufferSize()
method has a nice feature - to swear at the parameters that are invalid for this device , returning ERROR_BAD_VALUE
ERROR_BAD_VALUE
or ERROR
ERROR
. This means that by going through all the possible combinations, you can find out which formats the device supports. int[] rates = {8000, 11025, 22050,44100, 48000, 96000 }; int[] chans = {AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO}; int[] encs = {AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT}; for(int enc : encs) { for(int ch : chans) { for(int rate : rates) { int t = AudioRecord.getMinBufferSize(rate, ch, enc); if((t != AudioRecord.ERROR) && (t != AudioRecord.ERROR_BAD_VALUE)) { // } } } }
read()
read()
. It exists in three versions:read(byte[] audioData, int offsetInBytes, int sizeInBytes)
read(short[] audioData, int offsetInShorts, int sizeInShorts)
read(ByteBuffer audioBuffer, int sizeInBytes)
audioData | array in which data will be written |
---|---|
audioBuffer | the buffer to which the data will be written |
offsetInBytes / offsetInShorts | index from which to start recording |
sizeInShorts | size of the requested data block. In bytes for ByteBuffer and byte[] , in short integers for short[] |
ByteBuffer
or byte[]
, or read short integers for short[]
. If at the time of the call the object was not correctly initialized, it will return ERROR_INVALID_OPERATION , and if something is wrong with the parameters - ERROR_BAD_VALUEread()
will wait until they come from the microphone. Therefore, the method should be called from a separate thread , otherwise the application will hang. <uses-permission android:name="android.permission.RECORD_AUDIO" />
startRecording()
startRecording()
, and to finish - stop()
stop()
You can start and stop recording as many times as you like.release()
release()
. It will release all system resources captured by the object. After that, the object cannot be used, and the variable referring to it should be set to null
.IllegalStateException
IllegalStateException
, if called for an uninitialized (well, the word ... :) object or in the wrong order. Therefore, it is necessary to handle them “carefully”, i.e. through the try
block.Handler
Handler
messages received data. This data will be processed in another stream, therefore, in order not to overwrite data that has not yet been processed with new data, a cyclic buffer is used.AudioFormatInfo
class. It is a POJO with three fields describing the recording format: sampleRateInHz
, channelConfig
and audioFormat
. package com.MyCompany; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.os.Handler; import android.os.Process; //AudioFormatInfo - POJO sampleRateInHz, channelConfig audioFormat public class AudioReciever implements Runnable { private boolean mIsRunning; private List<Handler> handlers; private AudioFormatInfo format; private AudioRecord mRecord; private final int BUFF_COUNT = 32; public AudioReciever(AudioFormatInfo format) { this.format = format; handlers = new ArrayList<Handler>(); mIsRunning = true; mRecord = null; } public void addHandler(Handler handler) { handlers.add(handler); } public void stop() { mIsRunning = false; } @Override public void run() { // Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); mIsRunning = true; int buffSize = AudioRecord.getMinBufferSize(format.getSampleRateInHz(), format.getChannelConfig(), format.getAudioFormat()); if(buffSize == AudioRecord.ERROR) { System.err.println("getMinBufferSize returned ERROR"); return; } if(buffSize == AudioRecord.ERROR_BAD_VALUE) { System.err.println("getMinBufferSize returned ERROR_BAD_VALUE"); return; } // short, 16-bit if(format.getAudioFormat() != AudioFormat.ENCODING_PCM_16BIT) { System.err.println("unknown format"); return; } // . , // short[][] buffers = new short[BUFF_COUNT][buffSize >> 1]; mRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, format.getSampleRateInHz(), format.getChannelConfig(), format.getAudioFormat(), buffSize * 10); if(mRecord.getState() != AudioRecord.STATE_INITIALIZED) { System.err.println("getState() != STATE_INITIALIZED"); return; } try { mRecord.startRecording(); } catch(IllegalStateException e) { e.printStackTrace(); return; } int count = 0; while(mIsRunning) { int samplesRead = mRecord.read(buffers[count], 0, buffers[count].length); if(samplesRead == AudioRecord.ERROR_INVALID_OPERATION) { System.err.println("read() returned ERROR_INVALID_OPERATION"); return; } if(samplesRead == AudioRecord.ERROR_BAD_VALUE) { System.err.println("read() returned ERROR_BAD_VALUE"); return; } // sendMsg(buffers[count]); count = (count + 1) % BUFF_COUNT; } try { try { mRecord.stop(); } catch(IllegalStateException e) { e.printStackTrace(); return; } } finally { // mRecord.release(); mRecord = null; } } private void sendMsg(short[] data) { for(Handler handler : handlers) { handler.sendMessage(handler.obtainMessage(MSG_DATA, data)); } } }
Source: https://habr.com/ru/post/137708/
All Articles