Android--SoundPool


前言

  在Android中播放音頻文件經常會用到MediaPlayer,但是MediaPlayer存在一些不足的地方,如:資源占用量較高、加載延遲時間較長、不支持多個音頻同時播放等。這些缺點決定了MediaPlayer在某些需要密集使用不同音頻的情況不會理想,例如游戲開發。在游戲開發中,我們經常需要播放一些游戲的音效,這些音效的都需要是短促、密集、延遲小的,在這種場景下,需要使用到SoundPool來替代MediaPlayer播放這些音效,本篇博客就主要講解SoundPool的使用以及需要注意的地方,最后將以一個示例演示SoundPool的使用。

  本篇博客的主要內容:

  1. SoundPool
  2. SoundPool的簡單示例
  3. SoundPool的注意事項 

  

SoundPool

  SoundPool(聲音池),所處於"android.media.SoundPool"包下,主要用於播放一些較短的聲音片段,支持從程序的資源或文件系統加載。與MediaPlayer相比,SoundPool的優勢在於CPU的資源占用量低、反應延遲小,並且可以加載多個音頻到SoundPool中,通過資源ID來管理。另外SoundPool還支持執行設置聲音的品質、音量、播放比率等參數。

  SoundPool提供一個構造函數,以下是它的完整簽名:

    SoundPool(int maxStreams,int streamType,int srcQuality)

  通過上面的構造函數即可完成SoundPool的初始化,第一個參數為音頻池最多支持裝載多少個音頻,就是音頻池的大小;第二個參數指定聲音的類型,在AudioManager類中以常量的形式定義,一般指定為AudioManager.STREAM_MUSIC即可;第三個參數為音頻的質量,默認為0,這個參數為預留參數,現在沒有實際意義,為擴展預留字段,一般傳0即可。

 

  對於一個音頻池,涉及到音頻的加載、播放、暫停、繼續、釋放資源等操作,SoundPool也為我們提供了相應的方法,其底層也是用C++編寫的native方法。以下介紹一些常用的SoundPool方法:

  • int load(Context context,int resId,int priority):從一個文件夾raw下裝載一段音頻資源,返回值為音頻資源在SoundPool的ID。
  • int load(String path,int priority):從一個資源文件的路徑裝載一段音頻資源,返回值為音頻資源在SoundPool的ID。
  • final int play(int soundID,float leftVolume,float rightVolume,int priority,int loop,float rate):根據資源ID,播放一段音頻資源。
  • final void pause(int streamID):根據裝載資源ID,暫停音頻資源的播放。
  • final void resume(int streamID):根據裝載資源ID,繼續播放暫停的音頻資源。
  • final void stop(int streamID):根據裝載資源ID,停止音頻資源的播放。
  • final boolean unload(int soundID) :從音頻池中卸載音頻資源ID為soundID的資源。
  • final void release():釋放音頻池資源。

  上面方法無疑Load()和play()是最重要的,Load()具有多種重載方法,從參數名就可以看出是什么意思。這里講解一下play()方法,soundID參數為資源ID;leftVolume和rightVolume個參數為左右聲道的音量,從大到小取0.0f~1.0f之間的值;priority為音頻質量,暫時沒有實際意義,傳0即可;loop為循環次數,0為播放一次,-1為無線循環,其他正數+1為播放次數,如傳遞3,循環播放4次;rate為播放速率,從大到小取0.0f~2.0f,1.0f為正常速率播放。

  在使用load()裝載音頻的時候需要注意,load()方法是一個異步的方法,也就是說,在播放音頻的時候,很可能此段音頻還沒有裝載到音頻池中,這里可以借助SoundPool的一個裝載完成的監聽事件SoundPool.setOnLoadCompleteListener來保證裝載完成在播放聲音。SoundPool.setOnLoadCompleteListener()需要實現一個SoundPool.OnLoadCompleteListener接口,其中需要實現onLoadComplete()方法,一下是onLoadComplete()方法的完整簽名:

    onLoadComplete(SoundPool soundPool, int sampleId, int status)

  • soundPool:當前觸發事件的聲音池。
  • sampleId:當前裝載完成的音頻資源在音頻池中的ID。
  • status:狀態碼,展示沒有意義,為預留參數,會傳遞0。

 

使用SoundPool示例

  上面已經介紹了SoundPool的使用所涉及到的內容,下面通過一個簡單的示例來演示一下SoundPool的使用,播放的音頻資源都是我從其他app中拷貝出來的,沒有實際意義。示例中的注釋寫的比較全,這里不再累述了。

 1 package cn.bgxt.soundpooldemo;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 
 6 import android.media.AudioManager;
 7 import android.media.SoundPool;
 8 import android.media.SoundPool.OnLoadCompleteListener;
 9 import android.os.Bundle;
10 import android.app.Activity;
11 import android.util.Log;
12 import android.view.View;
13 import android.view.View.OnClickListener;
14 import android.widget.Button;
15 import android.widget.Toast;
16 
17 public class MainActivity extends Activity {
18     private Button btn_newqqmsg, btn_newweibontf, btn_newweibotoast;
19     private SoundPool pool;
20     private Map<String, Integer> poolMap;
21 
22     @Override
23     protected void onCreate(Bundle savedInstanceState) {
24         super.onCreate(savedInstanceState);
25         setContentView(R.layout.activity_main);
26         btn_newqqmsg = (Button) findViewById(R.id.btn_newqqmsg);
27         btn_newweibontf = (Button) findViewById(R.id.btn_newweibontf);
28         btn_newweibotoast = (Button) findViewById(R.id.btn_newweibotoast);
29 
30         poolMap = new HashMap<String, Integer>();
31         // 實例化SoundPool,大小為3
32         pool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
33         // 裝載音頻進音頻池,並且把ID記錄在Map中
34         poolMap.put("newqqmsg", pool.load(this, R.raw.qqmsg, 1));
35         poolMap.put("newweibontf", pool.load(this, R.raw.notificationsound, 1));
36         poolMap.put("newweibotoast", pool.load(this, R.raw.newblogtoast, 1));
37 
38         pool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
39 
40             @Override
41             public void onLoadComplete(SoundPool soundPool, int sampleId,
42                     int status) {
43                 // 每次裝載完成均會回調
44                 Log.i("main", "音頻池資源id為:" + sampleId + "的資源裝載完成");
45                 // 當前裝載完成ID為map的最大值,即為最后一次裝載完成
46                 if (sampleId == poolMap.size()) {
47                     Toast.makeText(MainActivity.this, "加載聲音池完成!",
48                             Toast.LENGTH_SHORT).show();
49                     btn_newqqmsg.setOnClickListener(click);
50                     btn_newweibontf.setOnClickListener(click);
51                     btn_newweibotoast.setOnClickListener(click);
52                     // 進入應用播放四次聲音
53                     pool.play(poolMap.get("newweibotoast"), 1.0f, 1.0f, 0, 3,
54                             1.0f);
55                 }
56             }
57         });
58     }
59 
60     private View.OnClickListener click = new OnClickListener() {
61 
62         @Override
63         public void onClick(View v) {
64 
65             switch (v.getId()) {
66             case R.id.btn_newqqmsg:
67                 if (pool != null) {
68                     pool.play(poolMap.get("newqqmsg"), 1.0f, 1.0f, 0, 0, 1.0f);
69                 }
70                 break;
71             case R.id.btn_newweibontf:
72                 if (pool != null) {
73                     pool.play(poolMap.get("newweibontf"), 1.0f, 1.0f, 0, 0,
74                             1.0f);
75                 }
76                 break;
77             case R.id.btn_newweibotoast:
78                 if (pool != null) {
79                     pool.play(poolMap.get("newweibotoast"), 1.0f, 1.0f, 0, 0,
80                             1.0f);
81                 }
82                 break;
83             default:
84                 break;
85             }
86         }
87     };
88 
89     @Override
90     protected void onDestroy() {
91         // 銷毀的時候釋放SoundPool資源
92         if (pool != null) {
93             pool.release();
94             pool = null;
95         }
96         super.onDestroy();
97     }
98 }

  效果展示:

 

SoundPool使用的注意事項

  因為SoundPool的一些設計上的BUG,從固件版本1.0開始就有些沒有修復的,以后應該會慢慢修復。這里簡單提一下:

  1. 雖然SoundPool可以裝載多個音頻資源,但是最大只能申請1MB的內存空間,這就意味着只能用使用它播放一些很短的聲音片段,而不是用它來播放歌曲或者做游戲背景音樂。
  2. SoundPool提供的pause()、resume()、stop()最好不要輕易使用,因為它有時候會使程序莫名其妙的終止,如果使用,最好做大量的測試。而且有時候也不會立即終止播放聲音,而是會等緩沖區的音頻數據播放完才會停止。
  3. 雖然SoundPool比MediaPlayer的效率好,但也不是絕對不存在延遲的問題,尤其在那些性能不太好的手機中,SoundPool的延遲問題會更嚴重,但是現在一般的手機配置,那一點的延遲還是可以接受的。

 

  源碼下載

 

總結

  本篇博客介紹了SoundPool的使用,雖然SoundPool還有一些不足的地方,當時對於應用中一些簡短的特效音,如按鍵音、短消息音,或者一些游戲中密集的聲音,如射擊的槍聲,爆破的聲音等。都是可以使用SoundPool的,效率會比使用MediaPlayer要高的多。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM