1. 程式人生 > >Android音訊播放之SoundPool 詳解

Android音訊播放之SoundPool 詳解

SoundPool —— 適合短促且對反應速度比較高的情況(遊戲音效或按鍵聲等)

下面介紹SoundPool的建立過程:

1. 建立一個SoundPool (建構函式)

public SoundPool(int maxStream, int streamType, int srcQuality) 
maxStream —— 同時播放的流的最大數量
streamType —— 流的型別,一般為STREAM_MUSIC(具體在AudioManager類中列出)
srcQuality —— 取樣率轉化質量,當前無效果,使用0作為預設值

初始化一個例項:
SoundPool soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0); 
建立了一個最多支援5個流同時播放的,型別標記為音樂的SoundPool。

2. 載入音訊資源 

可以通過四種途徑來記載一個音訊資源:
int load(AssetFileDescriptor afd, int priority) 
通過一個AssetFileDescriptor物件
int load(Context context, int resId, int priority) 
通過一個資源ID
int load(String path, int priority) 
通過指定的路徑載入
int load(FileDescriptor fd, long offset, long length, int priority) 
通過FileDescriptor載入

*API中指出,其中的priority引數目前沒有效果,建議設定為1。 

一個SoundPool能同時管理多個音訊,所以可以通過多次呼叫load函式來記載,如果記載成功將返回一個非0的soundID ,用於播放時指定特定的音訊。

int soundID1 = soundPool.load(this, R.raw.sound1, 1);
if(soundID1 ==0){
    // 記載失敗
}else{
   // 載入成功
}
int soundID2 = soundPool.load(this, R.raw.sound2, 1);
... 
這裡載入了兩個流,並分別記錄了返回的soundID 。

需要注意的是, 
流的載入過程是一個將音訊解壓為原始16位PCM資料的過程,由一個後臺執行緒來進行處理非同步,所以初始化後不能立即播放,需要等待一點時間。

3. 播放控制 

有以下幾個函式可用於控制播放:
final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) 
播放指定音訊的音效,並返回一個streamID 。
        priority —— 流的優先順序,值越大優先順序高,影響當同時播放數量超出了最大支援數時SoundPool對該流的處理;
        loop —— 迴圈播放的次數,0為值播放一次,-1為無限迴圈,其他值為播放loop+1次(例如,3為一共播放4次).
        rate —— 播放的速率,範圍0.5-2.0(0.5為一半速率,1.0為正常速率,2.0為兩倍速率)
final void pause(int streamID) 
暫停指定播放流的音效(streamID 應通過play()返回)。
final void resume(int streamID) 
繼續播放指定播放流的音效(streamID 應通過play()返回)。
final void stop(int streamID) 
終止指定播放流的音效(streamID 應通過play()返回)。

這裡需要注意的是, 
1.play()函式傳遞的是一個load()返回的soundID——指向一個被記載的音訊資源 ,如果播放成功則返回一個非0的streamID——指向一個成功播放的流 ;同一個soundID 可以通過多次呼叫play()而獲得多個不同的streamID (只要不超出同時播放的最大數量);
2.pause()、resume()和stop()是針對播放流操作的,傳遞的是play()返回的streamID ;
3.play()中的priority引數,只在同時播放的流的數量超過了預先設定的最大數量是起作用,管理器將自動終止優先順序低的播放流。如果存在多個同樣優先順序的流,再進一步根據其建立事件來處理,新建立的流的年齡是最小的,將被終止;
4.無論如何,程式退出時,手動終止播放並釋放資源是必要的。

4. 更多屬性設定 

其實就是paly()中的一些引數的獨立設定:
final void setLoop(int streamID, int loop) 
設定指定播放流的迴圈.
final void setVolume(int streamID, float leftVolume, float rightVolume) 
設定指定播放流的音量.
final void setPriority(int streamID, int priority) 
設定指定播放流的優先順序,上面已說明priority的作用.
final void setRate(int streamID, float rate) 
設定指定播放流的速率,0.5-2.0.

5. 釋放資源 

可操作的函式有:
final boolean unload(int soundID) 
解除安裝一個指定的音訊資源.
final void release() 
釋放SoundPool中的所有音訊資源.

下面對以上進行總結:

一個SoundPool可以:
1.管理多個音訊資源,通過load()函式,成功則返回非0的soundID;
2.同時播放多個音訊,通過play()函式,成功則返回非0的streamID;
3.pause()、resume()和stop()等操作是針對streamID(播放流)的;
4.當設定為無限迴圈時,需要手動呼叫stop()來終止播放;
5.播放流的優先順序(play()中的priority引數),只在同時播放數超過設定的最大數時起作用;
6.程式中不用考慮(play觸發的)播放流的生命週期,無效的soundID/streamID不會導致程式錯誤。

示例程式碼:

複製程式碼
SoundPool sp;
HashMap<Integer, Integer> sounddata;
Context mcontext;
Boolean isLoaded;

//初始化聲音
public void InitSound() { sp = new SoundPool(5, AudioManager.STREAM_MUSIC, 0); sounddata = new HashMap<Integer, Integer>(); sounddata.put(1, sp.load(this, R.raw.mp31, 1)); sounddata.put(2, sp.load(this, R.raw.mp32, 1)); sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener(){ @Override public void onLoadComplete(SoundPool sound,int sampleId,int status){ isLoaded=true; Toast.makeText(mcontext, "音效載入完成!", Toast.LENGTH_SHORT); } }); } …… public void playSound(int sound, int number) { AudioManager am = (AudioManager) this .getSystemService(Context.AUDIO_SERVICE); float audioMaxVolumn = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); float volumnCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC); float volumnRatio = volumnCurrent / audioMaxVolumn; sp.play(sounddata.get(sound), volumnRatio,// 左聲道音量 volumnRatio,// 右聲道音量 1, // 優先順序 number,// 迴圈播放次數 1);// 回放速度,該值在0.5-2.0之間 1為正常速度 } …… //2.播放聲音 if(isLoaded==true) playSound(1,1);
複製程式碼

經常有人在使用Soundpool時會報錯:

AudioFlinger could not create track, status: -12 

SoundPool: Error creating AudioTrack

可以從以下幾個方面解決此問題:

1.因為android系統一個裝置只允許同時有32個音效檔案播放,所以要你在程式中控制同時播放的音訊數。並且在Activity被覆蓋到下面或者鎖屏時對資源進行回收,即呼叫Soundpool物件的Release()方法。

For audio, there's a hard limit of 32 active AudioTrack objects per device (not per app: you need to share those 32 with rest of the system), and AudioTrack is used internally beneath SoundPool, ToneGenerator, MediaPlayer, native audio based on OpenSL ES, etc. But the actual AudioTrack limit is < 32; it depends more on soft factors such as memory, CPU load, etc. Also note that the limiter in the Android audio mixer does not currently have dynamic range compression, so it is possible to clip if you have a large number of active sounds and they're all loud.

2.每個音訊檔案大小要小於100k;可以修改音訊位元速率來壓縮音訊大小,把 128kbps改成32kbps ;

3.每次播放一次,soundpool.play方法有個引數是控制重複播放次數的,將之設定為0,即只播放一次;