android平臺上錄製音頻主要有兩種方式,MediaRecorder、AudioRecord。java
音頻開發應用場景不少,僅僅錄製保存知足不了大部分實際需求,因此,掌握 AudioRecord 的使用是必須的,本文針對AudioRecord的使用進行總結。android
AudioRecord 的使用主要有如下幾個步驟:算法
初始化AudioRecord數組
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) 複製代碼
int size = 採樣率 x 位寬 x 採樣時間 x 通道數
)與音頻採樣時間有關,通常取 2.5ms~120ms 之間,不一樣廠商取值不一樣,因此AudioRecord類提供了 int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)
函數來獲取改值,實際開發中,強烈建議由該函數計算出須要傳入的 bufferSizeInBytes,而不是本身手動計算。雖然不一樣的廠商的底層實現是不同的,但無外乎就是根據上面的計算公式獲得一幀的大小,音頻緩衝區的大小則必須是一幀大小的2~N倍。初始化音頻數據bufferide
經過前面獲取的 bufferSizeInBytes ,初始化讀取音頻數據的buffer。函數
final byte[] data = new byte[minBufferSize];
複製代碼
開啓採集編碼
調用 AudioRecord 實例的 startRecording()
方法,開始採集音頻spa
特別注意:開始採集後,要儘快經過音頻讀取數據取走音頻,一旦緩衝區中的數據大於前面設置的
bufferSizeInBytes
,會發生over-running
線程
啓動新線程,從音頻數據buffer中讀取音頻數據code
開啓新線程,調用 AudioRecord 實例的 public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)
方法,獲取音頻數據。
中止採集、釋放資源
調用 AudioRecord 實例的 stop()
、release()
方法,將改實例置爲 null。
示例代碼
別忘了配錄音權限
public class MainActivity extends AppCompatActivity {
//採樣率,如今可以保證在全部設備上使用的採樣率是44100Hz, 可是其餘的採樣率(22050, 16000, 11025)在一些設備上也可使用。
public static final int SAMPLE_RATE_INHZ = 44100;
//聲道數。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是能夠保證在全部設備可以使用的。
public static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
//返回的音頻數據的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT.
public static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
Button btnBegin;
Button btnEnd;
private AudioRecord audioRecord;
private boolean isRecording;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBegin = (Button) findViewById(R.id.btnBegin);
btnEnd = (Button) findViewById(R.id.btnEnd);
btnBegin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
beginRecord();
}
});
btnEnd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
endRecorde();
}
});
}
private void beginRecord() {
final int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_INHZ, CHANNEL_CONFIG, AUDIO_FORMAT);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_INHZ, CHANNEL_CONFIG, AUDIO_FORMAT, minBufferSize);
final byte[] data = new byte[minBufferSize];
audioRecord.startRecording();
isRecording = true;
new Thread(new Runnable() {
@Override
public void run() {
while (isRecording) {
int read = audioRecord.read(data, 0, minBufferSize);
if (read != AudioRecord.ERROR_INVALID_OPERATION) {
//處理音頻數據 data
}
}
}
}).start();
}
private void endRecorde() {
isRecording = false;
if (audioRecord != null) {
audioRecord.stop();
audioRecord.release();
audioRecord = null;
}
}
}
複製代碼
封裝
參考網上相關資源,這裏給出一個簡單的AudioRecord錄音的封裝類:AudioRecorder。
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
/** * Created by wangyt on 2018/10/26 */
public class AudioRecorder {
private static final String TAG = "AudioRecorder";
public interface OnAudioDataArrivedListener {
void onAudioDataArrived(byte[] audioData);
}
//聲源
private static final int DEFFAULT_AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
//採樣率,如今可以保證在全部設備上使用的採樣率是44100Hz, 可是其餘的採樣率(22050, 16000, 11025)在一些設備上也可使用。
private static final int DEFAULT_SAMPLE_RATE_INHZ = 44100;
//聲道數。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是能夠保證在全部設備可以使用的。
private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
//返回的音頻數據的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT.
private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
//內部緩衝區大小
private int minBufferSize = 0;
//是否已啓動錄音
private boolean isStarted = false;
//是否能夠從緩衝區中讀取數據
private boolean canReadDataFromBuffer = true;
//從緩衝區中讀取數據的回調方法
private OnAudioDataArrivedListener onAudioDataArrivedListener;
private AudioRecord audioRecord;
public boolean startRecord() {
return startRecord(DEFFAULT_AUDIO_SOURCE,
DEFAULT_SAMPLE_RATE_INHZ,
DEFAULT_CHANNEL_CONFIG,
DEFAULT_AUDIO_FORMAT);
}
public boolean startRecord(int audioSource, int sampleRate, int channel, int audioFormat) {
if (isStarted) {
Log.e(TAG, "startRecord: AudioRecorder has been already started");
return false;
}
//獲取內部緩衝區最小size
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channel, audioFormat);
if (minBufferSize == AudioRecord.ERROR_BAD_VALUE) {
Log.e(TAG, "startRecord: minBufferSize is error_bad_value");
return false;
}
Log.d(TAG, "startRecord: minBufferSize = " + minBufferSize + "bytes");
//初始化 audioRecord
audioRecord = new AudioRecord(audioSource, sampleRate, channel, audioFormat, minBufferSize);
if (audioRecord.getState() == AudioRecord.STATE_UNINITIALIZED) {
Log.e(TAG, "startRecord: audioRecord is uninitialized");
return false;
}
//啓動錄製
audioRecord.startRecording();
//能夠從內部緩衝區中讀取數據
canReadDataFromBuffer = true;
//啓動子線程
new Thread(new Runnable() {
@Override
public void run() {
while (canReadDataFromBuffer){
//初始化緩衝區數據接收數組
byte[] data = new byte[minBufferSize];
//讀取內部緩衝區中讀取數據
int result = audioRecord.read(data, 0, minBufferSize);
if (result == AudioRecord.ERROR_BAD_VALUE){
Log.e(TAG, "run: audioRecord.read result is ERROR_BAD_VALUE");
}else if (result == AudioRecord.ERROR_INVALID_OPERATION){
Log.e(TAG, "run: audioRecord.read result is ERROR_INVALID_OPERATION");
}else {
if (onAudioDataArrivedListener != null){
//調用讀取數據回調方法
onAudioDataArrivedListener.onAudioDataArrived(data);
}
Log.d(TAG, "run: audioRecord read " + result + "bytes");
}
}
}
}).start();
//設置錄音已啓動
isStarted = true;
Log.d(TAG, "startRecord: audioRecorder has been already started");
return true;
}
public void stopRecord(){
//若是錄音還沒有啓動,直接返回
if (!isStarted) return;
//設置內部緩衝區數據不可讀取
canReadDataFromBuffer = false;
//中止錄音
if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING){
audioRecord.stop();
}
//釋放資源
audioRecord.release();
//設置錄音未啓動
isStarted = false;
//回調置爲空
onAudioDataArrivedListener = null;
}
}
複製代碼