由于AudioTrack是Android SDK層提供的最底層的音頻播放API,因此只允許輸入裸數(shù)據(jù)。和MediaPlayer相比,對于一個壓縮的音頻文件(比如MP3、AAC等文件),它需要自行實現(xiàn)解碼操作和緩沖區(qū)控制。
首先來看一下AudioTrack的工作流程,具體如下。
1)根據(jù)音頻參數(shù)信息,配置出一個AudioTrack的實例。
2)調(diào)用play方法,將AudioTrack切換到播放狀態(tài)。
3)啟動播放線程,循環(huán)向AudioTrack的緩沖區(qū)中寫入音頻數(shù)據(jù)。
4)當數(shù)據(jù)寫完或者停止播放的時候,停止播放線程,并且釋放所有資源。
根據(jù)AudioTrack的上述工作流程,本節(jié)將以4個小部分分別介紹每個流程的詳細步驟。
1.配置AudioTrack
先來看一下AudioTrack的參數(shù)配置,要想構(gòu)造出一個AudioTrack類型的實例,必須先了解其構(gòu)造函數(shù)原型,代碼如下所示:
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig,int audioFormat, int bufferSizeInBytes, int mode);
其中構(gòu)造函數(shù)的參數(shù)說明如下。
- streamType,Android手機上提供了多重音頻管理策略(按一下手機側(cè)邊的按鍵,可以看到有多個音量管理,這其實就是不同音頻策略的音量控制展示),當系統(tǒng)有多個進程需要播放音頻的時候,管理策略會決定最終的呈現(xiàn)效果,該參數(shù)的可選值將以常量的形式定義在類AudioManager中,主要包括以下內(nèi)容:
STREAM_VOCIE_CALL:電話聲音
STREAM_SYSTEM:系統(tǒng)聲音
STREAM_RING:鈴聲
STREAM_MUSCI:音樂聲
STREAM_ALARM:警告聲
STREAM_NOTIFICATION:通知聲 - sampleRateInHz,采樣率,即播放的音頻每秒鐘會有多少次采樣,可選用的采樣頻率列表為:8000、16000、22050、24000、32000、44100、48000等,大家可以根據(jù)自己的應(yīng)用場景進行合理的選擇。
- channelConfig,聲道數(shù)(通道數(shù))的配置,可選值以常量的形式配置在類AudioFormat中,常用的是CHANNEL_IN_MONO(單聲道)、CHANNEL_IN_STEREO(雙聲道),因為現(xiàn)在大多數(shù)手機的麥克風都是偽立體聲的采集,為了性能考慮,筆者建議使用單聲道進行采集,而轉(zhuǎn)變?yōu)榱Ⅲw聲的過程可以在聲音的特效處理階段來完成。
- audioFormat,該參數(shù)是用來配置“數(shù)據(jù)位寬”的,即采樣格式,可選值以常量的形式定義在類AudioFormat中,分別為ENCODING_PCM_16BIT(16bit)ENCODING_PCM_8BIT(8bit),
注意,前者是可以兼容所有Android手機的。 - bufferSizeInBytes,其配置的是AudioTrack內(nèi)部的音頻緩沖區(qū)的大小,AudioTrack類提供了一個幫助開發(fā)者確定bufferSizeInBytes的函數(shù),其原型具體如下:
int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);
在實際開發(fā)中,強烈建議由該函數(shù)計算出需要傳入的bufferSizeInBytes,而不是自己手動計算。
- mode,AudioTrack提供了兩種播放模式,可選的值以常量的形式定義在類AudioTrack中,一個是MODE_STATIC,需要一次性將所有的數(shù)據(jù)都寫入播放緩沖區(qū)中,簡單高效,通常用于播放鈴聲、系統(tǒng)提醒的音頻片段;另一個是MODE_STREAM,需要按照一定的時間間隔不間
斷地寫入音頻數(shù)據(jù),理論上它可以應(yīng)用于任何音頻播放的場景。
2.將AudioTrack切換到播放狀態(tài)
首先判斷AudioTrack實例是否初始化成功,如果當前狀態(tài)處于初始成功的狀態(tài),那么就調(diào)用它的play方法,并切換到播放狀態(tài),代碼如下:
if (null != audioTrack && audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED)
{
audioTrack.play();
}
3.開啟播放線程
首先創(chuàng)建一個播放線程,代碼如下:
playerThread = new Thread(new PlayerThread(), "playerThread");
playerThread.start();
接下來看看該線程中執(zhí)行的任務(wù),代碼如下:
class PlayerThread implements Runnable {
private short[] samples;
public void run() {
samples = new short[minBufferSize];
while(!isStop) {
int actualSize = decoder.readSamples(samples);
audioTrack.write(samples, actualSize);
}
}
}
線程中的minBufferSize是在初始化AudioTrack的時候獲得的緩沖區(qū)大小,會對其進行換算,即以2個字節(jié)表示一個采樣的大小,也就是2倍的關(guān)系(因為初始化的時候是以字節(jié)為單位的);decoder是一個解碼器,假設(shè)已經(jīng)初始化成功,最后將調(diào)用write方法把從解碼器中獲得的PCM采樣數(shù)據(jù)寫入AudioTrack的緩沖區(qū)中,注意此方法是阻塞的方法,比如:一般要寫入200ms的音頻數(shù)據(jù)需要執(zhí)行接近200ms的時間。
4.銷毀資源
首先停止AudioTrack,代碼如下:
if (null != audioTrack && audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED)
{
audioTrack.stop();
}
然后停止線程:
isStop = true;
if (null != playerThread) {
playerThread.join();
playerThread = null;
}
最后釋放AudioTrack:
audioTrack.release();