標籤:android audiotrack audiorecord
說到音頻應用,首先想到的就是音樂播放器。有些播放器可以播放流媒體,有些可以播放本地音樂檔案。隨著Android平台的演變,需要更多進階的音頻API。好在Google新增了這方面的API,支援低延遲的音頻流媒體和錄製。
Android音頻API提供了一些進階的功能,開發人員可以把它們整合到自己的應用中。有了這些API,現在可以更容易地實現VoIP應用程式,構建定製的流媒體音樂用戶端,實現低延遲的遊戲音效。此外,還有提供文字轉換語音以及語音辨識的API,使用者可以直接使用音頻和應用互動,而不需要使用使用者介面或者觸控技術。
低延遲音頻
Android有四個用來播放音訊API(算上MIDI的話一共五個)和三個用來錄音的API。接下來會簡要的介紹這些API,以及一些進階用法樣本。
①音頻播放API
音樂播放預設使用MediaPlayer。該類適合播放音樂或者視頻,既能播放流式資源,還可以播放本地檔案。每個MediaPlayer都有一個關聯的狀態機器,需要在應用程式中跟蹤這些狀態。開發人員可以使用MediaPlayer類的API在自己應用中嵌入音樂或者視頻播放功能,而無需額外處理或者考慮延遲要求。
第二個選擇是SoundPool類,它提供了低延遲支援,適合播放音效和其他比較短的音頻,比如可以使用SoundPool播放遊戲聲音。但是,它不支援音頻流,所以不適合那些需要即時音頻流處理的應用,如VoIP。
第三個選擇是AudioTrack類,它允許把音頻流緩衝到硬體中,支援低延遲播放,甚至適合流媒體情境。AudioTrack類通常提供足夠低的延遲,可在VoIP或類似應用中使用。
下面的代碼展示了如何在VoIP應用中使用AudioTrack:
public class AudioTrackDemo { private final int mMinBufferSize; private final AudioTrack mAudioTrack; public AudioTrackDemo() { this.mMinBufferSize = AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); this.mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, this.mMinBufferSize*2, AudioTrack.MODE_STREAM); } public void playPcmPacket(byte[] pcmData) { if (this.mAudioTrack != null && this.mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED) { if (this.mAudioTrack.getPlaybackRate() != AudioTrack.PLAYSTATE_PLAYING) { this.mAudioTrack.play(); } this.mAudioTrack.write(pcmData, 0, pcmData.length); } } public void stopPlayback() { if (this.mAudioTrack != null) { this.mAudioTrack.stop(); this.mAudioTrack.release(); } }}
首先,確定音頻流的最小緩衝區大小。要做到這一點,需要知道採樣率,資料是單聲道還是立體聲,以及是否使用8位或者16位PCM編碼。然後以採樣率和採樣大小作為參數調用AudioTrack.getMinBufferSize(),該方法會以位元組形式返回AudioTrack執行個體的最小緩衝區大小。
接下來,根據需要使用正確參數建立AudioTrack執行個體。第一個參數為音訊類型,不同的應用使用不同的值。對VoIP應用來說,使用STREAM_VOICE_CALL,而對於流媒體音樂應用則使用STREAM_MUSIC。
具體的選擇有:
STREAM_ALARM:手機鬧鈴的聲音
STREAM_MUSIC:手機音樂的聲音
STREAM_DTMF:DTMF音調的聲音
STREAM_RING:電話鈴聲的聲音
STREAM_NOTFICATION:系統提示的聲音
STREAM_SYSTEM:系統的聲音
STREAM_VOICE_CALL:語音電話聲音
第二,第三,第四個參數根據使用情境會有所不同。這些參數分別表示採樣率,立體聲或者單聲道,以及採樣大小。一般而言,一個VoIP應用會使用16KHZ的16位單聲道,而常規的音樂CD可能採用44.1KHZ的16位立體聲。16位立體聲高採樣率需要更大的緩衝區以及更多的資料轉送,但是音質會更好。所有的Android裝置都支援PCM以8KHZ,16KHZ,44.1KHZ的採樣率播放8或者16位立體聲。
緩衝區大小參數應該是最小緩衝區的倍數,實際取決於具體的需求,有時網路延遲等因素也會影響緩衝區大小。
任何時候都應該避免使用空的緩衝區,因為可能導致播放出現故障。
最後一個參數決定只發送一次音頻資料(MODE_STATIC)還是連續發送資料流(MODE_STREAM)。第一種情況需要一次發送整個音頻剪輯。對於持續發送音頻流的情況,可以發送任大小塊的PCM資料,處理流媒體音樂或者VoIP通話可以會使用這種方式。
②錄製API
談到錄製音頻,首先要考慮的API是MediaRecorder。和MediaPlayer類似,需要在應用代碼中跟蹤MediaRecorder類的內部狀態。由於MediaRecorder只能把錄音儲存到檔案中,所以它不適合錄製流媒體。
如果需要錄製流媒體,可以使用AudioRecord,和剛才展示的代碼非常類似。
下面的樣本顯示了如何建立AudioRecord執行個體錄製16位單聲道16KHZ的音頻採樣:
public class AudioRecordDemo { private final AudioRecord mAudioRecord; private final int mMinBufferSize; private boolean mDoRecord = false; public AudioRecordDemo() { this.mMinBufferSize = AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); this.mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, this.mMinBufferSize * 2); } public void writeAudioToStream(OutputStream stream){ this.mDoRecord=true; this.mAudioRecord.startRecording(); byte[] buffer=new byte[this.mMinBufferSize*2]; while(this.mDoRecord){ int byteWritten=this.mAudioRecord.read(buffer,0,buffer.length); try{ stream.write(buffer,0,byteWritten); }catch(IOException e){ this.mDoRecord=false; } } this.mAudioRecord.stop(); this.mAudioRecord.release(); } public void stopRecording(){ this.mDoRecord=false; }}
因為和AudioTrack的建立過程非常類似,在使用VoIP或者類似應用時可以很方便地把它們結合起來。
相信學過多媒體的人對採樣率等這些東西並不陌生,如果缺乏這方面的知識,可以適當的補充後在來看這段代碼。
如果看的認真的人會發現本文只介紹了三個播放API和兩個錄製API,說好的四個的三個呢?其實最後一個不是一兩句就能說清楚,還需要單獨列出一篇文章講解——OpenSL ES請關注本部落格後續會講解到。
Android進階音頻應用