Android音頻開(kāi)發(fā)之AudioTrack

在前兩節(jié)中分享了Android音頻開(kāi)發(fā)之音頻基本概念Android音頻開(kāi)發(fā)之音頻采集,本文分享的是如何使用 AudioTrack 來(lái)播放 使用AudioRecord 采集后的 PCM 數(shù)據(jù)。

1. 構(gòu)造 AudioTrack 實(shí)例

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)

在采樣 pcm 音頻數(shù)據(jù)需要設(shè)置對(duì)應(yīng)的采樣率,采樣精度,采樣的通道數(shù)和采樣的緩沖區(qū)大小,如果播放的音頻是使用 AudioRecord 錄制的,那么這些參數(shù)配置信息需要和 AudioRerord一致,不然播放就會(huì)出現(xiàn)奇怪的問(wèn)題。

1.1 AudioTrack 播放音頻時(shí)會(huì)有兩種方式:

音頻播放的方式,有兩種方式 MODE_STATIC 或者 MODE_STREAM 。

  • MODE_STATIC 預(yù)先將需要播放的音頻數(shù)據(jù)讀取到內(nèi)存中,然后才開(kāi)始播放。
  • MODE_STREAM 邊讀邊播,不會(huì)將數(shù)據(jù)直接加載到內(nèi)存

1.2 MODE_STREAM 的方式構(gòu)建 AudioTrack 實(shí)例

/**
* 構(gòu)建 AudioTrack 實(shí)例對(duì)象
*/
private void createStreamModeAudioTrack() {
    if (audioTrack == null) {
        bufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
    }
}

1.3 MODE_STATIC 的方式構(gòu)建 AudioTrack 實(shí)例

/**
* 構(gòu)建 AudioTrack 實(shí)例對(duì)象
*/
//file 就是需要播放的音頻文件,這里的buffersize就是文件的大小
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
        44100, AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT, (int) file.length(), AudioTrack.MODE_STATIC);

2. 寫(xiě)入數(shù)據(jù)

不間斷通過(guò) write 方法的寫(xiě)數(shù)據(jù)給 AudioTrack .

注意: 對(duì)于 MODE_STREAM 寫(xiě)入數(shù)據(jù),會(huì)阻塞,直到寫(xiě)入的數(shù)據(jù)都傳輸給 AudioTrack。
對(duì)于 MODE_STATIC 會(huì)將數(shù)據(jù)拷貝到緩沖區(qū)中,并在該方法返回后執(zhí)行 play() 方法播放音頻數(shù)據(jù)。

  • int write (byte[] audioData, int offsetInBytes,int sizeInBytes)
  • int write (short[] audioData, int offsetInShorts, int sizeInShorts)
  • int write (float[] audioData, int offsetInFloats, int sizeInFloats,int writeMode)

該方法的返回值:

  • 正確:>=0 該值表示寫(xiě)入的數(shù)據(jù)量。

  • 錯(cuò)誤:<0

    • ERROR_INVALID_OPERATION
    • ERROR_BAD_VALUE
    • ERROR_DEAD_OBJECT
    • ERROR

2.1 兩種方式寫(xiě)入數(shù)據(jù)的區(qū)別

  • MODE_STATIC

在 AudioTrack 創(chuàng)建之處,會(huì)初始化一個(gè)與其相關(guān)聯(lián)的 buffer 緩沖區(qū),這個(gè)緩沖區(qū)的大小是在構(gòu)造方法指定的。這個(gè)大小表示 AudioTrack 可以播放多久。對(duì)于 MODE_STATIC 這種模式下,這個(gè) buffer 的大小就是需要播放的文件或者流的大小。

//寫(xiě)入數(shù)據(jù)大小 array 就是預(yù)先將音頻數(shù)據(jù)加載到array數(shù)組中
int writeResult = audioTrack.write(array, 0, array.length);
//檢查寫(xiě)入的結(jié)果,如果是異常情況,則直接需要釋放資源
if (writeResult == AudioTrack.ERROR_INVALID_OPERATION || writeResult == AudioTrack.ERROR_BAD_VALUE
        || writeResult == AudioTrack.ERROR_DEAD_OBJECT || writeResult == AudioTrack.ERROR) {
    //出異常情況
    isPlaying = false;
    release();
    return;
}

  • MODE_STREAM

使用這種方式是通過(guò)將數(shù)據(jù)寫(xiě)入到緩沖區(qū)中,而需要注意寫(xiě)入到這個(gè)緩沖區(qū)的數(shù)據(jù)大小,需要確保小于或者等于這個(gè)構(gòu)造 AudioTrack 的緩沖區(qū)大小。

AudioTrack 不是 final 類型,也就是說(shuō)可以使用繼承實(shí)現(xiàn)自己的功能,但是官方文檔表示不建議這樣做。

 //邊讀邊播
 byte[] buffer = new byte[bufferSize];
 while (fis.available() > 0) {
     int readCount = fis.read(buffer);
     if (readCount == -1) {
         Log.e(TAG, "沒(méi)有更多數(shù)據(jù)可以讀取了");
         break;
     }
     int writeResult = audioTrack.write(buffer, 0, readCount);
     if (writeResult >= 0) {
         //success
     } else {
         //fail
         //丟掉這一塊數(shù)據(jù)
         continue;
     }
 }

這個(gè)緩沖區(qū)大小可以通過(guò) AudioTrack.getMinBufferSize 來(lái)獲取

bufferSize = AudioTrack.getMinBufferSize(44000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

3. 狀態(tài)判斷

3.1 AudioTrack 狀態(tài)判斷

檢測(cè)一個(gè)已經(jīng)創(chuàng)建好的 AudioTrack 的狀態(tài),確保操作在正確初始化之后進(jìn)行。當(dāng)需要進(jìn)行播放前,校驗(yàn) AudioTrack 是否處于正確的狀態(tài)。

int getState ()

返回值介紹:

  • STATE_INITIALIZED 表示 AudioTrack 已經(jīng)是可以使用了。
  • STATE_UNINITIALIZED 表示 AudioTrack 創(chuàng)建時(shí)沒(méi)有成功地初始化。
  • STATE_NO_STATIC_DATA 表示當(dāng)前是使用 MODE_STATIC ,但是還沒(méi)往緩沖區(qū)中寫(xiě)入數(shù)據(jù)。當(dāng)接收數(shù)據(jù)之后會(huì)變?yōu)?STATE_INITIALIZED 狀態(tài)。
//播放時(shí),狀態(tài)校驗(yàn)
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
    Log.e(TAG, "不能播放,當(dāng)前播放器未處于初始化狀態(tài)..");
    return;
}

3.2 AudioTrack 播放狀態(tài)

int getPlayState()
  • PLAYSTATE_STOPPED 停止
  • PLAYSTATE_PAUSED 暫停
  • PLAYSTATE_PLAYING 正在播放

4. 播放 play

  • 對(duì)于 MODE_STATIC 模式,必須要調(diào)用 write(...) 相關(guān)方法將數(shù)據(jù)寫(xiě)入到對(duì)應(yīng)的緩沖區(qū)中,然后才可以調(diào)用 paly(...) 方法進(jìn)行播放操作。
//先將所有的數(shù)據(jù)寫(xiě)入到緩沖區(qū)
write(...)
//然后在播放
play(..)
  • 對(duì)于 MODE_STREAM 模式
paly(...)

new Thread() {
    public void run() {
        //一系列的 write 操作
        `write(...)`
    }
    
}.start();

5. AudioTrack 狀態(tài)

5.1 停止播放

對(duì)于 MODE_STREAM 模式,如果單是調(diào)用 stop 方法, AudioTrack 會(huì)等待緩沖的最后一幀數(shù)據(jù)播放完畢之后,才會(huì)停止,如果需要立即停止,那么就需要調(diào)用 pause 然后調(diào)用 flush 這兩個(gè)方法,那么 AudioTrack 就是丟緩沖區(qū)中剩余的數(shù)據(jù)。

void stop ()

5.2 暫停

暫停播放,但是緩沖區(qū)中沒(méi)有被播放的數(shù)據(jù)不會(huì)被舍棄,調(diào)用 play 方法即可接著播放,

void pause ()

5.3 刷新

刷新正在排隊(duì)播放的音頻數(shù)據(jù),調(diào)用該方法會(huì)將寫(xiě)入到緩沖區(qū)但沒(méi)有被播放的音頻數(shù)據(jù)都會(huì)被丟棄。如果是非 STREAM 或者沒(méi)有執(zhí)行 pasuse 或者 stop 將不會(huì)有任何效果。

void flush()

5.4 釋放

釋放本地 AudioTrack 對(duì)象。

void release ()

示例代碼

public void stop() { 
    if ((audioTrack != null) && (audioTrack.getState() == AudioTrack.STATE_INITIALIZED)) {
        if (audioTrack.getPlayState() != AudioTrack.PLAYSTATE_STOPPED) {
            audioTrack.flush();
            audioTrack.stop();
        } 
} 

6. AudioTrack 和 MediaPlayer 的區(qū)別?

  • AudioTrack 只能播放 pcm 原始數(shù)據(jù),不能播放視頻。

  • MediaPlayer 可以播放視頻和音頻。

  • AudioTrack 只支持 pcm 原始音頻數(shù)據(jù)。

  • MediaPlayer 支持 mp3,wav,aac...

  • MediaPlayer 在底層會(huì)創(chuàng)建指定的格式的解碼器,將音頻數(shù)據(jù)轉(zhuǎn)化為 pcm 然后再交給 pcm 去播放。MediaPlayer底層會(huì)創(chuàng)建 AudioTrack,將解碼后的數(shù)據(jù)交給 AudioTrack 播放。

  • 每一個(gè)音頻流對(duì)應(yīng)著一個(gè)AudioTrack類的一個(gè)實(shí)例,
    每個(gè)AudioTrack會(huì)在創(chuàng)建時(shí)注冊(cè)到 AudioFlinger中,
    由AudioFlinger把所有的AudioTrack進(jìn)行混合(Mixer),然后輸送到AudioHardware中進(jìn)行播放,目前Android同時(shí)最多可以創(chuàng)建32個(gè)音頻流,也就是說(shuō),Mixer最多會(huì)同時(shí)處理32個(gè)AudioTrack的數(shù)據(jù)流。

7. 參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容