音頻采集:Android基于AudioRecord的實(shí)現(xiàn)

前言

這篇文章簡單介紹下移動(dòng)端Android系統(tǒng)下利用AudioRecord進(jìn)行音頻采集方法。

按照慣例開始前先提供一份源碼 AudioRecordLib 。
AudioRecord采集的核心實(shí)現(xiàn)在于 AudioRecordCore.java 這個(gè)文件。

權(quán)限申請

想要使用AudioRecord這個(gè)API,需要在AndroidManifest.xml的配置文件里面增加錄音權(quán)限:

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

初始化

AudioRecord的初始化需要先創(chuàng)建一個(gè)AudioRecord實(shí)例。
構(gòu)造函數(shù)原型如下:

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig,
 int audioFormat,int bufferSizeInBytes)

具體參數(shù)說明:

  • audioSource 這個(gè)參數(shù)指的是音頻采集的輸入源,接受的值定義在MediaRecorder.AudioSource里面,一般來說使用DEFAULT或者M(jìn)IC即可。
  • sampleRateInHz 指定采集音頻的采樣頻率,比較通用的是44100(44.1kHz),這個(gè)值是科學(xué)家們通過奈葵斯特采樣定理得出的一個(gè)人能接受最佳的采樣頻率值。
  • channelConfig 指定AudioRecord采集幾個(gè)聲道的聲音,預(yù)設(shè)值定義在AudioFormat中,常用值有 CHANNEL_CONFIGURATION_MONO(單聲道) 和 CHANNEL_CONFIGURATION_STEREO(雙聲道)。
  • audioFormat 指定采樣PCM數(shù)據(jù)的采樣格式,預(yù)設(shè)值定義在也AudioFormat中,常用值有 ENCODING_PCM_8BIT、ENCODING_PCM_16BIT和ENCODING_PCM_FLOAT,值得強(qiáng)調(diào)的是ENCODING_PCM_16BIT可以保證兼容大部分Andorid手機(jī)。
  • bufferSizeInBytes 配置AudioRecord內(nèi)部的音頻數(shù)據(jù)緩沖區(qū),一般來說緩存區(qū)越小,產(chǎn)生的音頻延遲也越??;值得注意的是,我們可以利用AudioRecord.getMinBufferSize()這個(gè)方法幫我們算出最小的緩存區(qū)大小,這個(gè)數(shù)值最好不要自己計(jì)算,畢竟不同廠商可能有不同的緩存區(qū)采集實(shí)現(xiàn)。

檢測AudioRecord當(dāng)前狀態(tài)

由于可能存在權(quán)限問題導(dǎo)致配置AudioRecord失敗,所以我們需要在開始采集前檢查一下AudioRecord的狀態(tài):

if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
    //todo start
}

如果getState()不等于AudioRecord.STATE_INITIALIZED說明創(chuàng)建AudioRecord失敗,這時(shí)候應(yīng)該給用戶反饋信息。

完整代碼如下:

//獲取最低AudioRecord內(nèi)部音視頻緩沖區(qū)大小,此大小依賴于各產(chǎn)商實(shí)現(xiàn),最好不要自己計(jì)算
mRecordBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
//初始化AudioRecord實(shí)例
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, mRecordBufSize);
//檢測AudioRecord初始化是否成功
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
    mAudioRecord = null;
    mRecordBufSize = 0;
    return false;
}
else {
    //創(chuàng)建一個(gè)位置用于存放后續(xù)的PCM數(shù)據(jù)
    mPcmData = new byte[mRecordBufSize];
    mState = INIT;
    return true;
}

開始采集

創(chuàng)建好了AudioRecord實(shí)例,調(diào)用如下的方法即可開始麥克風(fēng)采集:

mAudioRecord.startRecording();

提取數(shù)據(jù)

調(diào)用了開始采集后,我們需要另起一條線程進(jìn)行PCM數(shù)據(jù)提取。
我們需要循環(huán)不斷從AudioRecord的緩沖區(qū)里面將數(shù)據(jù)讀取出來,值得注意的是這個(gè)過程一定要及時(shí),不然會出現(xiàn)“overrun”的錯(cuò)誤,也就是沒有及時(shí)取走音頻數(shù)據(jù)導(dǎo)致音頻緩存區(qū)溢出了。

private Thread mReadDataThread = new Thread() {
    @Override
    public void run() {
        int read;
        while (mState == RECORDING) {
            //讀取mRecordBufSize長度的音頻數(shù)據(jù)存入mPcmData中
            read = mAudioRecord.read(mPcmData, 0, mRecordBufSize);
            //如果讀取音頻數(shù)據(jù)沒有出現(xiàn)錯(cuò)誤 ===> read 大于0
            if (read >= AudioRecord.SUCCESS) {
                synchronized (AudioRecordRecord.class){
                    if (mCallback != null)
                        mCallback.onPCMDataAvailable(mPcmData, read);
                }
            }
        }
    }
};

停止采集,釋放資源

停止錄音我們可以調(diào)用AudioRecord的stop方法來實(shí)現(xiàn)。

mAudioRecord.stop();

但是我們存在采集(音頻提取)線程,所以我們需要更改一個(gè)狀態(tài)變量讓線程結(jié)束

mState = INIT;

使得 while (mState == RECORDING) 退出循環(huán)邏輯。
接著我們需要釋放錄制器的資源,以便設(shè)備的其他應(yīng)用可以正常使用錄音器,我們可以調(diào)用AudioRecord的release方法。

mAudioRecord.release();

這樣就完整的結(jié)束了AudioRecord的采集業(yè)務(wù)。

播放PCM文件

Audacity這個(gè)工具可以導(dǎo)入pcm原始文件,并且提供了波形圖查看和播放功能。
操作流程是:
文件 => 導(dǎo)入 => 原始數(shù)據(jù) => 設(shè)置PCM數(shù)據(jù)格式 => 導(dǎo)入
具體效果圖如下:

p1.png

結(jié)語

下一篇博客會介紹一下Android利用OpenSL ES進(jìn)行錄音導(dǎo)出PCM數(shù)據(jù)。
本文同步發(fā)布于簡書CSDN。

End!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容