前言
這篇文章簡單介紹下移動端Android系統下利用AudioRecord進行音頻采集方法。
按照慣例開始前先提供一份源碼 AudioRecordLib 。
AudioRecord采集的核心實現在于 AudioRecordCore.java 這個文件。
權限申請
想要使用AudioRecord這個API,需要在AndroidManifest.xml的配置文件里面增加錄音權限:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
初始化
AudioRecord的初始化需要先創建一個AudioRecord實例。
構造函數原型如下:
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig,
int audioFormat,int bufferSizeInBytes)
具體參數說明:
- audioSource 這個參數指的是音頻采集的輸入源,接受的值定義在MediaRecorder.AudioSource里面,一般來說使用DEFAULT或者MIC即可。
- sampleRateInHz 指定采集音頻的采樣頻率,比較通用的是44100(44.1kHz),這個值是科學家們通過奈葵斯特采樣定理得出的一個人能接受最佳的采樣頻率值。
- channelConfig 指定AudioRecord采集幾個聲道的聲音,預設值定義在AudioFormat中,常用值有 CHANNEL_CONFIGURATION_MONO(單聲道) 和 CHANNEL_CONFIGURATION_STEREO(雙聲道)。
- audioFormat 指定采樣PCM數據的采樣格式,預設值定義在也AudioFormat中,常用值有 ENCODING_PCM_8BIT、ENCODING_PCM_16BIT和ENCODING_PCM_FLOAT,值得強調的是ENCODING_PCM_16BIT可以保證兼容大部分Andorid手機。
- bufferSizeInBytes 配置AudioRecord內部的音頻數據緩沖區,一般來說緩存區越小,產生的音頻延遲也越小;值得注意的是,我們可以利用AudioRecord.getMinBufferSize()這個方法幫我們算出最小的緩存區大小,這個數值最好不要自己計算,畢竟不同廠商可能有不同的緩存區采集實現。
檢測AudioRecord當前狀態
由于可能存在權限問題導致配置AudioRecord失敗,所以我們需要在開始采集前檢查一下AudioRecord的狀態:
if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
//todo start
}
如果getState()不等于AudioRecord.STATE_INITIALIZED說明創建AudioRecord失敗,這時候應該給用戶反饋信息。
完整代碼如下:
//獲取最低AudioRecord內部音視頻緩沖區大小,此大小依賴于各產商實現,最好不要自己計算
mRecordBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
//初始化AudioRecord實例
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, mRecordBufSize);
//檢測AudioRecord初始化是否成功
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
mAudioRecord = null;
mRecordBufSize = 0;
return false;
}
else {
//創建一個位置用于存放后續的PCM數據
mPcmData = new byte[mRecordBufSize];
mState = INIT;
return true;
}
開始采集
創建好了AudioRecord實例,調用如下的方法即可開始麥克風采集:
mAudioRecord.startRecording();
提取數據
調用了開始采集后,我們需要另起一條線程進行PCM數據提取。
我們需要循環不斷從AudioRecord的緩沖區里面將數據讀取出來,值得注意的是這個過程一定要及時,不然會出現“overrun”的錯誤,也就是沒有及時取走音頻數據導致音頻緩存區溢出了。
private Thread mReadDataThread = new Thread() {
@Override
public void run() {
int read;
while (mState == RECORDING) {
//讀取mRecordBufSize長度的音頻數據存入mPcmData中
read = mAudioRecord.read(mPcmData, 0, mRecordBufSize);
//如果讀取音頻數據沒有出現錯誤 ===> read 大于0
if (read >= AudioRecord.SUCCESS) {
synchronized (AudioRecordRecord.class){
if (mCallback != null)
mCallback.onPCMDataAvailable(mPcmData, read);
}
}
}
}
};
停止采集,釋放資源
停止錄音我們可以調用AudioRecord的stop方法來實現。
mAudioRecord.stop();
但是我們存在采集(音頻提取)線程,所以我們需要更改一個狀態變量讓線程結束
mState = INIT;
使得 while (mState == RECORDING) 退出循環邏輯。
接著我們需要釋放錄制器的資源,以便設備的其他應用可以正常使用錄音器,我們可以調用AudioRecord的release方法。
mAudioRecord.release();
這樣就完整的結束了AudioRecord的采集業務。
播放PCM文件
Audacity這個工具可以導入pcm原始文件,并且提供了波形圖查看和播放功能。
操作流程是:
文件 => 導入 => 原始數據 => 設置PCM數據格式 => 導入
具體效果圖如下:
結語
下一篇博客會介紹一下Android利用OpenSL ES進行錄音導出PCM數據。
本文同步發布于簡書、CSDN。
End!