Android音頻開發(fā)之音頻采集

Android音頻開發(fā)之音頻采集

在 Android 系統(tǒng)中,一般使用 AudioRecord 或者 MediaRecord 來采集音頻。

AudioRecord 是一個(gè)比較偏底層的API,它可以獲取到一幀幀 PCM 數(shù)據(jù),之后可以對(duì)這些數(shù)據(jù)進(jìn)行處理。

而 MediaRecorder 是基于 AudioRecorder 的 API(最終還是會(huì)創(chuàng)建AudioRecord用來與AudioFlinger進(jìn)行交互) ,它可以直接將采集到的音頻數(shù)據(jù)轉(zhuǎn)化為執(zhí)行的編碼格式,并保存。

直播技術(shù)采用的就是 AudioRecorder 采集音頻數(shù)據(jù)。

本文主要介紹例如 AudioRecord 進(jìn)行音頻的采集。

基本API

  • 獲取最小的緩沖區(qū)大小,用于存放 AudioRecord 采集到的音頻數(shù)據(jù)。
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)
  • AudioRecord構(gòu)造方法

根據(jù)具體的參數(shù)配置,請(qǐng)求硬件資源創(chuàng)建一個(gè)可以用于采集音頻的 AudioRecord 對(duì)象。

參數(shù)描述:參考Android音頻開發(fā)之音頻基本概念

  • audioResource

    音頻采集的來源

  • audioSampleRate

    音頻采樣率

  • channelConfig

    聲道

  • audioFormat

    音頻采樣精度,指定采樣的數(shù)據(jù)的格式和每次采樣的大小。

  • bufferSizeInBytes

    AudioRecord 采集到的音頻數(shù)據(jù)所存放的緩沖區(qū)大小。

//設(shè)置采集來源為麥克風(fēng)
private static final int AUDIO_RESOURCE = MediaRecorder.AudioSource.MIC;
//設(shè)置采樣率為44100,目前為常用的采樣率,官方文檔表示這個(gè)值可以兼容所有的設(shè)置
private final static int AUDIO_SAMPLE_RATE = 44100;
//設(shè)置聲道聲道數(shù)量為雙聲道
private final static int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
//設(shè)置采樣精度,將采樣的數(shù)據(jù)以PCM進(jìn)行編碼,每次采集的數(shù)據(jù)位寬為16bit。
private final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
  • 開始采集

開始采集之后,狀態(tài)變?yōu)镽ECORDSTATE_RECORDING 。

public void startRecording ()
  • 讀取錄制內(nèi)容,將采集到的數(shù)據(jù)讀取到緩沖區(qū)

方法調(diào)用的返回值的狀態(tài)碼:
情況異常:
1.ERROR_INVALID_OPERATION if the object wasn't properly initialized
2.ERROR_BAD_VALUE if the parameters don't resolve to valid data and indexes.
情況正常:the number of bytes that were read

public int read (ByteBuffer audioBuffer, int sizeInBytes)
public int read (byte[] audioData, int offsetInBytes, int sizeInBytes)
public int read (short[] audioData, int offsetInShorts, int sizeInShorts)
  • 停止采集

停止采集之后,狀態(tài)變?yōu)?RECORDSTATE_STOPPED 。

public void stop ()
  • 獲取AudioRecord的狀態(tài)

用于檢測(cè)AudioRecord是否確保了獲得適當(dāng)?shù)挠布Y源。在AudioRecord對(duì)象實(shí)例化之后調(diào)用。

STATE_INITIALIZED 初始完畢
STATE_UNINITIALIZED 未初始化

public int getState ()
  • 返回當(dāng)前AudioRecord的采集狀態(tài)

public static final int RECORDSTATE_STOPPED = 1; 停止?fàn)顟B(tài)
調(diào)用 void stop() 之后的狀態(tài)
public static final int RECORDSTATE_RECORDING = 3;正在采集
調(diào)用 startRecording () 之后的狀態(tài)

public int getRecordingState() 

AudioRecord 采集音頻的基本流程

  • 權(quán)限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 構(gòu)造一個(gè) AudioRecord 對(duì)象。

  • 開始采集。

  • 讀取采集的數(shù)據(jù)。

  • 停止采集。

構(gòu)造一個(gè) AudioRecord 對(duì)象

AudioRecord audioRecord = new AudioRecord(audioResource, audioSampleRate, channelConfig, audioFormat, bufferSizeInBytes);

獲取 bufferSizeInBytes 值

bufferSizeInBytes 是 AudioRecord 采集到的音頻數(shù)據(jù)所存放的緩沖區(qū)大小。

注意:這個(gè)大小不能隨便設(shè)置,AudioRecord 提供對(duì)應(yīng)的 API 來獲取這個(gè)值。

this.bufferSizeInBytes = AudioRecord.getMinBufferSize(audioSampleRate, channelConfig, audioFormat);

通過 bufferSizeInBytes 返回就可以知道傳入給AudioRecord.getMinBufferSize的參數(shù)是否支持當(dāng)前的硬件設(shè)備。

if (AudioRecord.ERROR_BAD_VALUE == bufferSizeInBytes || AudioRecord.ERROR == bufferSizeInBytes) {
    throw new RuntimeException("Unable to getMinBufferSize");
}

//bufferSizeInBytes is available...

開始采集

  • 在開始錄音之前,首先要判斷一下 AudioRecord 的狀態(tài)是否已經(jīng)初始化完畢了。
//判斷AudioRecord的狀態(tài)是否初始化完畢
//在AudioRecord對(duì)象構(gòu)造完畢之后,就處于AudioRecord.STATE_INITIALIZED狀態(tài)了。
int state = audioRecord.getState();
if (state == AudioRecord.STATE_UNINITIALIZED) {
    throw new RuntimeException("AudioRecord STATE_UNINITIALIZED");
}
  • 開始采集
audioRecord.startRecording();
//開啟線程讀取數(shù)據(jù)
new Thread(recordTask).start();

讀取采集的數(shù)據(jù)

上面提到, AudioRecord 在采集數(shù)據(jù)時(shí)會(huì)將數(shù)據(jù)存放到緩沖區(qū)中,因此我們只需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)流去從緩沖區(qū)中將采集的數(shù)據(jù)讀取出來即可。

創(chuàng)建一個(gè)數(shù)據(jù)流,一邊從 AudioRecord 中讀取音頻數(shù)據(jù)到緩沖區(qū),一邊將緩沖區(qū) 中數(shù)據(jù)寫入到數(shù)據(jù)流
因?yàn)樾枰褂肐O操作,因此讀取數(shù)據(jù)的過程應(yīng)該在子線程中執(zhí)行

//創(chuàng)建一個(gè)流,存放從AudioRecord讀取的數(shù)據(jù)
File saveFile = new File(Environment.getExternalStorageDirectory(), "audio-record.pcm");
DataOutputStream dataOutputStream = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream(saveFile)));

private Runnable recordTask = new Runnable() {
    @Override
    public void run() {
        //設(shè)置線程的優(yōu)先級(jí)
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIOR
        Log.i(TAG, "設(shè)置采集音頻線程優(yōu)先級(jí)");
        final byte[] data = new byte[bufferSizeInBytes];
        //標(biāo)記為開始采集狀態(tài)
        isRecording = true;
        Log.i(TAG, "設(shè)置當(dāng)前當(dāng)前狀態(tài)為采集狀態(tài)");
        //getRecordingState獲取當(dāng)前AudioReroding是否正在采集數(shù)據(jù)的狀態(tài)
        while (isRecording && audioRecord.getRecordingState() == AudioRecord
            //讀取采集數(shù)據(jù)到緩沖區(qū)中,read就是讀取到的數(shù)據(jù)量
            final int read = audioRecord.read(data, 0, bufferSizeInBytes);
            if (AudioRecord.ERROR_INVALID_OPERATION != read && AudioRecord.E
                //將數(shù)據(jù)寫入到文件中
                dataOutputStream.write(buffer,0,read);
            }
        }
    }
};

停止采集

/**
 * 停止錄音
 */
public void stopRecord() throws IOException {
    Log.i(TAG, "停止錄音,回收AudioRecord對(duì)象,釋放內(nèi)存");
    isRecording = false;
    if (audioRecord != null) {
        if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
            audioRecord.stop();
            Log.i(TAG, "audioRecord.stop()");
        }
        if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
            audioRecord.release();
            Log.i(TAG, "audioRecord.release()");
        }
    }
}

幾個(gè)小問題

  • 采集數(shù)據(jù)之后,保存的文件為audio-record.pcm,這個(gè)文件并不能使用普通的播放器播放。它是一個(gè)原始的文件,沒有任何播放格式,因此就無法被播放器識(shí)別并播放。

  • 上面的問題可以有兩種解決方法

    • 使用 AudioTrack 播放 pcm 格式的音頻數(shù)據(jù)。
    • 將 pcm 數(shù)據(jù)轉(zhuǎn)化為 wav 格式的數(shù)據(jù),這樣就可以被播放器識(shí)別。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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