Android音頻播放之SoundPool

SoundPoolhtml

1、基本概念java

  在Android應用程序的開發過程當中,常常須要播放多媒體文件,也許最早想到的會是MediaPlayer類了,該類提供了播放、暫停、中止及重複播放等功能性方法(該類位於android.media包下,詳見API文檔)。也可參考博文http://www.cnblogs.com/tgyf/p/4700177.htmlandroid

  但使用MediaPlayer類的問題是佔用資源較多,對於遊戲這樣複雜、簡短配音多的應用可能不是很適合,這時候就須要用到SoundPool類了,其定義在SDK的android.media.SoundPool文件中,顧名思義是聲音池的意思。主要用於播放一些較短的聲音片斷,能夠從程序的資源或文件系統加載,相對於MediaPlayer類能夠作到使用較少的CPU資源和較短的反應延遲。app

  SoundPool類和其餘聲音播放類相比,優勢是能夠自行設置聲音的品質、音量、播放速率等參數,並且能夠同時管理多個音頻流,每一個流都有獨自的ID,對每一個音頻流的管理都是經過該ID進行的。異步

 

2、使用流程ide

一、建立實例函數

  建立一個SoundPool類實例,(構造函數)方法爲public SoundPool(int maxStream, int streamType, int srcQuality)。三個參數的含義以下:oop

  maxStream——同時播放的流的最大個數;測試

  streamType——流的類型(通常爲STREAM_MUSIC,具體定義見在AudioManager類);this

  srcQuality——採樣率轉化質量,使用0做爲默認值(目前設置並沒有效果);

  舉例:

1 SoundPool soundPool = new SoundPool(5,AudioManager.STREAM_MUSIC, 0);

  建立了一個最多支持5個流同時播放的,類型標記爲音樂的SoundPool。

二、加載音頻

  能夠經過如下四種途徑來加載一個音頻文件資源:

  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 ,用於播放時指定具體的音頻流。以下面的代碼所示,音頻資源是以上述第二種方法進行加載:

1 int soundID1 = soundPool.load(this, R.raw.sound1, 1);
2 if(soundID1 == 0){
3   // 記載失敗
4 }else{
5   // 加載成功
6 }
7 int soundID2 = soundPool.load(this, R.raw.sound2, 1);
8 ...

  這裏加載了兩個流,並分別記錄了返回的soundID,理論上是每load一次,返回的ID值就會加1。

  在實際應用中,若音頻文件過多,那借助HashMap類會方便不少。如音頻資源加載先後soundID的賦值與提取過程以下:

1 //加載兩個音頻資源
2 HashMap soundMap=new HashMap<Integer, Integer>();
3 soundMap.put(1, soundPool.load(MainActivity.this, R.raw.sound1, 1));
4 soundMap.put(2, soundPool.load(MainActivity.this, R.raw.sound2, 1));
5 //提取soundID,播放時直接寫入play()方法中做爲參數便可
6 soundMap.get(1);

  須要注意的是,流的加載過程是一個將音頻解壓爲原始16位PCM數據,由一個後臺線程經過異步處理的過程。所以,初始化後並不能當即播放,須要等待一點時間。

三、播放控制

  SoundPool類用於控制播放的函數主要有如下幾個:

  final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate),播放指定音頻的音效,並返回一個streamID(同soundID,也是隨着音頻播放個數的增長而增長)。函數各個參數含義以下:

  leftVolume——最小音量;

  rightVolume——最大音量;

  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) ,繼續播放指定播放流的音效;

  final void stop(int streamID) 終止指定播放流的音效;

  這裏有四點須要注意一下:

  A、play()函數傳遞的是一個load()方法返回的soundID——指向一個被加載的音頻資源,若是播放成功則返回一個非0的streamID——指向一個成功播放的流;同一個soundID能夠經過屢次調用play()而得到多個不一樣的streamID (只要不超出同時播放的最大數量);

  B、pause()、resume()及stop()是針對播放流操做的,傳遞的是play()返回的streamID;

  C、play()中的priority參數,只在同時播放的流的數量超過了預先設定的最大數量時纔會起做用,管理器將自動終止優先級低的播放流。若是存在多個一樣優先級的流,再進一步根據其建立事件來處理,新建立的流的年齡是最小的,將最早被終止;

  D、不管如何,程序退出時,手動終止播放並釋放資源是必要的;

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;

 

五、釋放資源

  通常用到如下兩個函數:

  final boolean unload(int soundID),卸載一個指定的音頻資源;

  final void release(),釋放SoundPool中的全部音頻資源;

 

3、示例代碼

  先貼上整個Java代碼:

 1 package com.xxx.soundpool;
 2 
 3 import android.app.Activity;
 4 import android.media.AudioManager;
 5 import android.media.SoundPool;
 6 import android.os.Bundle;
 7 import android.view.View;
 8 import android.widget.Button;
 9 import android.widget.TextView;
10 
11 import java.util.HashMap;
12 import java.util.Map;
13 
14 public class MainActivity extends Activity {
15     private Button wav1_play = null;
16     private Button wav2_play = null;
17     private Button wav3_play = null;
18     private Button wav4_play = null;
19     private Button wav5_play = null;
20     private TextView name = null;
21 
22     private SoundPool soundPool = null;
23 
24     private Map<Integer, Integer> soundMap = null;
25 
26     @Override
27     protected void onCreate(Bundle savedInstanceState) {
28         super.onCreate(savedInstanceState);
29         setContentView(R.layout.activity_main);
30 
31         wav1_play = (Button)findViewById(R.id.wav1_play);
32         wav2_play = (Button)findViewById(R.id.wav2_play);
33         wav3_play = (Button)findViewById(R.id.wav3_play);
34         wav4_play = (Button)findViewById(R.id.wav4_play);
35         wav5_play = (Button)findViewById(R.id.wav5_play);
36         name = (TextView)findViewById(R.id.name);
37 
38         //建立一個SoundPool對象,該對象能夠容納5個音頻流
39         soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC,0);
40 
41         soundMap = new HashMap<Integer, Integer>();
42         soundMap.put(1, soundPool.load(MainActivity.this, R.drawable.wav1, 1));
43         soundMap.put(2, soundPool.load(MainActivity.this, R.drawable.wav2, 1));
44         soundMap.put(3, soundPool.load(MainActivity.this, R.drawable.wav3, 1));
45         soundMap.put(4, soundPool.load(MainActivity.this, R.drawable.wav4, 1));
46         soundMap.put(5, soundPool.load(MainActivity.this, R.drawable.wav5, 1));
47 
48         wav1_play.setOnClickListener(new View.OnClickListener() {
49             @Override
50             public void onClick(View arg0) {
51                 // TODO Auto-generated method stub
52                 soundPool.play(soundMap.get(1), 1, 1, 0, 0, 1);
53                 name.setText("wav1.wav");
54             }
55         });
56         wav2_play.setOnClickListener(new View.OnClickListener() {
57             @Override
58             public void onClick(View arg0) {
59                 // TODO Auto-generated method stub
60                 soundPool.play(soundMap.get(2), 1, 1, 0, 0, 1);
61                 name.setText("wav2.wav");
62             }
63         });
64         wav3_play.setOnClickListener(new View.OnClickListener() {
65             @Override
66             public void onClick(View arg0) {
67                 // TODO Auto-generated method stub
68                 soundPool.play(soundMap.get(3), 1, 1, 0, 0, 1);
69                 name.setText("wav3.wav");
70             }
71         });
72         wav4_play.setOnClickListener(new View.OnClickListener() {
73             @Override
74             public void onClick(View arg0) {
75                 // TODO Auto-generated method stub
76                 soundPool.play(soundMap.get(4), 1, 1, 0, 0, 1);
77                 name.setText("wav4.wav");
78             }
79         });
80         wav5_play.setOnClickListener(new View.OnClickListener() {
81             @Override
82             public void onClick(View arg0) {
83                 // TODO Auto-generated method stub
84                 soundPool.play(soundMap.get(5), 1, 1, 0, 0, 1);
85                 name.setText("wav5.wav");
86             }
87         });
88     }
89 }

  能夠看到,事先準備好的五個音頻資源(名稱分別爲wav1.wav,wav2.wav,wav3.wav,wav4.wav,wav5.wav),直接放到了drawable目錄下。點擊按鈕會播放相對應的音頻資源,在頂部的TextView組件會顯示當前播放的文件名稱,並且和預想的同樣,連續點擊幾個按鈕,可以達到幾個音頻同時播放的效果。

  不過不知道爲何,這樣的實現方式運行後播放的音效只有幾秒鐘,查了一下,網上不少人遇到過相似問題,5、六秒的樣子。至於播放中止(stop()),復播(resume())等操做就比較簡單了,這裏未加入測試代碼中,感興趣的小夥伴能夠本身實現下。

  簡單界面以下:

 

4、總結

  經過SoundPool類來加載、播放及釋放資源的過程,須要掌握如下六點:

  一、管理多個音頻資源,經過load()函數,成功則返回非0的soundID;

  二、同時播放多個音頻,經過play()函數,成功則返回非0的streamID;

  三、pause()、resume()及stop()等操做是針對streamID(播放流)的;

  四、當設置爲無限循環時,須要手動調用stop()來終止播放;

  五、播放流的優先級(play()中的priority參數),只在同時播放數超過設定的最大數時纔會起做用;

  六、程序中不用考慮(play()觸發的)播放流的生命週期,無效的soundID or streamID不會致使程序錯誤。

相關文章
相關標籤/搜索