Android音頻開發之——如何播放一幀音頻

來源:互聯網
上載者:User

標籤:

本文重點關注如何在Android平台上播放一幀音頻資料。閱讀本文之前,建議先讀一下《Android音頻開發(1):基礎知識》,因為音頻開發過程中,經常要涉及到這些基礎知識,掌握了這些重要的概念後,開發過程中的很多參數和流程就會更加容易理解。

 

Android SDK 提供了3套音頻播放的API,分別是:MediaPlayer,SoundPool,AudioTrack,關於它們的區別可以看這篇文章:《Intro to the three Android Audio APIs》,簡單來說,MediaPlayer 更加適合在後台長時間播放本地音樂檔案或者線上的流式資源; SoundPool 則適合播放比較短的音頻片段,比如遊戲聲音、按鍵聲、鈴聲片段等等,它可以同時播放多個音頻; 而 AudioTrack 則更接近底層,提供了非常強大的控制能力,支援低延遲播放,適合流媒體和VoIP語音電話等情境。

 

音訊開發,更廣泛地應用不僅僅局限於播放本地檔案或者音頻片段,因此,本文重點關注如何利AudioTrack API 來播放音頻資料(注意,使用AudioTrack播放的音頻必須是解碼後的PCM資料)。

 

1. AudioTrack 的工作流程

 

首先,我們瞭解一下 AudioTrack 的工作流程:

 

(1) 配置參數,初始化內部的音頻播放緩衝區

(2) 開始播放

(3) 需要一個線程,不斷地向 AudioTrack 的緩衝區“寫入”音頻資料,注意,這個過程一定要及時,否則就會出現“underrun”的錯誤,該錯誤在音頻開發中比較常見,意味著應用程式層沒有及時地“送入”音頻資料,導致內部的音頻播放緩衝區為空白。

(4) 停止播放,釋放資源

 

2. AudioTrack 的參數配置

 

 

上面是 AudioTrack 的建構函式原型,主要靠建構函式來配置相關的參數,下面一一解釋(再次建議先閱讀一下《Android音頻開發(1):基礎知識》):

 

(1) streamType

 

這個參數代表著當前應用使用的哪一種音頻管理原則,當系統有多個進程需要播放音頻時,這個管理原則會決定最終的展現效果,該參數的可選的值以常量的形式定義在 AudioManager 類中,主要包括:

 

STREAM_VOCIE_CALL:電話聲音

STREAM_SYSTEM:系統聲音

STREAM_RING:鈴聲

STREAM_MUSCI:音樂聲

STREAM_ALARM:警告聲

STREAM_NOTIFICATION:通知聲

 

(2) sampleRateInHz

 

採樣率,從AudioTrack源碼的“audioParamCheck”函數可以看到,這個採樣率的取值範圍必須在 4000Hz~192000Hz 之間。

 

(3) channelConfig

 

通道數的配置,可選的值以常量的形式定義在 AudioFormat 類中,常用的是 CHANNEL_IN_MONO(單通道),CHANNEL_IN_STEREO(雙通道)

 

(4) audioFormat

 

這個參數是用來配置“資料位元寬”的,可選的值也是以常量的形式定義在 AudioFormat 類中,常用的是 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit),注意,前者是可以保證相容所有Android手機的。

 

(5) bufferSizeInBytes

 

這個是最難理解又最重要的一個參數,它配置的是 AudioTrack 內部的音頻緩衝區的大小,該緩衝區的值不能低於一幀“音訊框架”(Frame)的大小,而前一篇文章介紹過,一幀音訊框架的大小計算如下:

 

int size = 採樣率 x 位寬 x 採樣時間 x 通道數

 

採樣時間一般取 2.5ms~120ms 之間,由廠商或者具體的應用決定,我們其實可以推斷,每一幀的採樣時間取得越短,產生的延時就應該會越小,當然,片段化的資料也就會越多。

 

在Android開發中,AudioTrack 類提供了一個協助你確定這個 bufferSizeInBytes 的函數,原型如下:

 

int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);

 

不同的廠商的底層實現是不一樣的,但無外乎就是根據上面的計算公式得到一幀的大小,音頻緩衝區的大小則必須是一幀大小的2~N倍,有興趣的朋友可以繼續深入源碼探究探究。

 

實際開發中,強烈建議由該Function Compute出需要傳入的 bufferSizeInBytes,而不是自己手動計算。

 

(6) mode

 

AudioTrack 提供了兩種播放模式,一種是 static 方式,一種是 streaming 方式,前者需要一次性將所有的資料都寫入播放緩衝區,簡單高效,通常用於播放鈴聲、系統提醒的音頻片段; 後者則是按照一定的時間間隔不間斷地寫入音頻資料,理論上它可用於任何音頻播放的情境。

 

可選的值以常量的形式定義在 AudioTrack 類中,一個是 MODE_STATIC,另一個是 MODE_STREAM,根據具體的應用傳入對應的值即可。

 

4. 範例程式碼

 

我將 AudioTrack 類的介面簡單封裝了一下,提供了一個 AudioPlayer 類,可以到我的Github下載:https://github.com/Jhuster/Android/blob/master/Audio/AudioPlayer.java

 

這裡也貼出來一份:

 1 import android.util.Log; 2 import android.media.AudioFormat; 3 import android.media.AudioManager; 4 import android.media.AudioTrack; 5   6 public class AudioPlayer { 7       8     private static final String TAG = "AudioPlayer"; 9  10     private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;11     private static final int DEFAULT_SAMPLE_RATE = 44100;12     private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;13     private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;14     private static final int DEFAULT_PLAY_MODE = AudioTrack.MODE_STREAM;15              16     private boolean mIsPlayStarted = false;17     private int mMinBufferSize = 0;18     private AudioTrack mAudioTrack;  19      20     public boolean startPlayer() {21         return startPlayer(DEFAULT_STREAM_TYPE,DEFAULT_SAMPLE_RATE,DEFAULT_CHANNEL_CONFIG,DEFAULT_AUDIO_FORMAT);22     }23      24     public boolean startPlayer(int streamType, int sampleRateInHz, int channelConfig, int audioFormat) {25          26         if (mIsPlayStarted) {27             Log.e(TAG, "Player already started !");28             return false;29         }30          31         mMinBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz,channelConfig,audioFormat);32         if (mMinBufferSize == AudioTrack.ERROR_BAD_VALUE) {33             Log.e(TAG, "Invalid parameter !");34             return false;35         }36         Log.d(TAG , "getMinBufferSize = "+mMinBufferSize+" bytes !");37          38         mAudioTrack = new AudioTrack(streamType,sampleRateInHz,channelConfig,audioFormat,mMinBufferSize,DEFAULT_PLAY_MODE);39         if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {40             Log.e(TAG, "AudioTrack initialize fail !");41             return false;42         }            43          44         mIsPlayStarted = true;45          46         Log.d(TAG, "Start audio player success !");47          48         return true;49     }50      51     public int getMinBufferSize() {52         return mMinBufferSize;53     }54      55     public void stopPlayer() {56          57         if (!mIsPlayStarted) {58             return;59         }60          61         if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {62             mAudioTrack.stop();                        63         }64          65         mAudioTrack.release();66         mIsPlayStarted = false;67             68         Log.d(TAG, "Stop audio player success !");69     }70      71     public boolean play(byte[] audioData, int offsetInBytes, int sizeInBytes) {72          73         if (!mIsPlayStarted) {74             Log.e(TAG, "Player not started !");75             return false;76         }77          78         if (sizeInBytes < mMinBufferSize) {79             Log.e(TAG, "audio data is not enough !");80             return false;81         }82          83         if (mAudioTrack.write(audioData,offsetInBytes,sizeInBytes) != sizeInBytes) {                84             Log.e(TAG, "Could not write all the samples to the audio device !");85         }                                   86                                                     87         mAudioTrack.play();88          89         Log.d(TAG , "OK, Played "+sizeInBytes+" bytes !");90          91         return true;92     }93 }

 

原文:http://ticktick.blog.51cto.com/823160/1750593

Android音頻開發之——如何播放一幀音頻

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.