SoundPool 音頻播放 詳解 示例


官方文檔


The SoundPool class manages and plays audio resources for applications.
SoundPool類管理和播放應用程序的音頻資源

A SoundPool is a collection of samples that can be loaded into memory from a resource inside the APK or from a file in the file system. The SoundPool library uses the MediaPlayer service to decode the audio into a raw 16-bit PCM mono or stereo stream. This allows applications to ship with compressed streams without having to suffer the CPU load and latency of decompressing during playback.
SoundPool是可以從APK內的資源或文件系統中的文件加載到內存中的樣本集合。 SoundPool庫使用MediaPlayer服務將音頻解碼為原始的16位PCM單聲道或立體聲流。 這允許應用程序使用壓縮流進行傳輸,而不必在播放期間遭受CPU負載和解壓縮的延遲。

In addition to low-latency playback, SoundPool can also manage the number of audio streams being rendered at once. When the SoundPool object is constructed, the maxStreams parameter sets the maximum number of streams that can be played at a time from this single SoundPool. SoundPool tracks the number of active streams. If the maximum number of streams is exceeded, SoundPool will automatically stop a previously playing stream based first on priority and then by age within that priority. Limiting the maximum number of streams helps to cap CPU loading and reducing the likelihood that audio mixing will impact visuals or UI performance.
除了低延遲播放之外,SoundPool還可以管理立即呈現的音頻流的數量。當SoundPool對象被構造時,maxStreams參數設置從一個SoundPool可以一次播放的最大流數。 SoundPool跟蹤活動流的數量。如果超過流的最大數量,SoundPool將首先優先按照該優先級的年齡自動停止先前播放的流。限制流的最大數量有助於加快CPU加載速度,並減少音頻混合影響視覺效果或UI性能的可能性。

Sounds can be looped by setting a non-zero loop value. A value of -1 causes the sound to loop forever. In this case, the application must explicitly call the stop() function to stop the sound. Any other non-zero value will cause the sound to repeat the specified number of times, e.g. a value of 3 causes the sound to play a total of 4 times.
可以通過設置非零的值來循環播放聲音。值為-1會使聲音永久循環。在這種情況下,應用程序必須顯式地調用stop()函數來停止聲音。任何其他非零值將導致聲音重復指定次數,例如一個值3會使聲音共播放4次。

The playback rate can also be changed. A playback rate of 1.0 causes the sound to play at its original frequency (resampled, if necessary, to the hardware output frequency). A playback rate of 2.0 causes the sound to play at twice its original frequency, and a playback rate of 0.5 causes it to play at half its original frequency. The playback rate range is 0.5 to 2.0.
播放速度也可以改變。播放速率為1.0會導致聲音以其原始頻率播放(如有必要,重新采樣到硬件輸出頻率)。 2.0的播放速度使聲音以原始頻率的兩倍播放,並且播放速率為0.5使其以原始頻率的一半播放。播放速度范圍為0.5〜2.0。

Priority runs low to high, i.e. higher numbers are higher priority. Priority is used when a call to play() would cause the number of active streams to exceed the value established by the maxStreams parameter when the SoundPool was created. In this case, the stream allocator will stop the lowest priority stream. If there are multiple streams with the same low priority, it will choose the oldest stream to stop. In the case where the priority of the new stream is lower than all the active streams, the new sound will not play and the play() function will return a streamID of zero.
優先級從低到高,即較高的數字是較高的優先級。當play()的調用將導致,活動流的數量超過 創建SoundPool時 maxStreams參數建立的值時,將使用優先級。在這種情況下,流分配器將停止最低優先級流。如果存在多個具有相同低優先級的流,則它將選擇最舊的流來停止。在新流的優先級低於所有活動流的情況下,新的聲音將不會播放,並且play()函數將返回0的streamID。

Let's examine a typical use case: A game consists of several levels of play. For each level, there is a set of unique sounds that are used only by that level. In this case, the game logic should create a new SoundPool object when the first level is loaded. The level data itself might contain the list of sounds to be used by this level. The loading logic iterates through the list of sounds calling the appropriate SoundPool.load() function. This should typically be done early in the process to allow time for decompressing the audio to raw PCM format before they are needed for playback.
我們來研究一個典型的用例:游戲由幾個 級別 組成。對於每個級別,都有一組僅由該級使用的唯一聲音。在這種情況下,游戲邏輯應在第一級加載完成時創建一個新的SoundPool對象。級別數據本身可能包含此級別要使用的聲音數據列表。加載邏輯迭代器通過聲音列表調用相應的SoundPool.load()函數。這通常應該在進程的早期完成,以便在播放之前需要時間將音頻解壓縮為原始的PCM格式。

Once the sounds are loaded and play has started, the application can trigger sounds by calling SoundPool.play(). Playing streams can be paused or resumed, and the application can also alter the pitch by adjusting the playback rate in real-time for doppler or synthesis effects.
一旦聲音被加載並且播放已經開始,應用程序可以通過調用SoundPool.play()來觸發聲音。播放流可以暫停或恢復,並且應用程序還可以通過實時調整多普勒或綜合效果的播放速率來改變音調。

Note that since streams can be stopped due to resource constraints, the streamID is a reference to a particular instance of a stream. If the stream is stopped to allow a higher priority stream to play, the stream is no longer be valid. However, the application is allowed to call methods on the streamID without error. This may help simplify program logic since the application need not concern itself with the stream lifecycle.
注意,由於資源約束可以停止流,所以streamID是對流的特定實例的引用。如果流停止以允許更高優先級的流播放,流不再有效。但是,允許應用程序調用streamID上的方法,而不會出錯。這可能有助於簡化程序邏輯,因為應用程序不需要關注流生命周期。

In our example, when the player has completed the level, the game logic should call SoundPool.release() to release all the native resources in use and then set the SoundPool reference to null. If the player starts another level, a new SoundPool is created, sounds are loaded, and play resumes.
在我們的示例中,當玩家已經完成了級別時,游戲邏輯應該調用SoundPool.release() 來釋放所有正在使用的本地資源,然后將SoundPool引用設置為null。如果玩家開始另一個級別,則會創建一個新的SoundPool,聲音被加載,播放恢復。

API 介紹

構造方法(廢棄了)
  • SoundPool(int maxStreams, int streamType, int srcQuality)    This constructor was deprecated in API level 21. use SoundPool.Builder instead to create and configure a SoundPool instance
    • maxStream:同時播放的流的最大數量
    • streamType流的類型,一般為STREAM_MUSIC(具體在AudioManager類中列出)
    • srcQuality采樣率轉化質量,當前無效果,使用0作為默認值

公共方法
  • static void    deprecateStreamTypeForPlayback(int streamType, String className, String opName)    
    • Use to generate warning or exception in legacy code paths that allowed passing stream types to qualify audio playback. 用於在舊代碼路徑中生成警告或異常,允許傳遞流類型限定音頻播放。

加載資源,並返回一個 【soundID】
  • int  soundID  load(Context context, int resId, int priority)    Load the sound from the specified APK resource.
    • Note that the extension is dropped. For example, if you want to load a sound from the raw resource file "explosion.mp3", you would specify "R.raw.explosion" as the resource ID. Note that this means you cannot have both an "explosion.wav" and an "explosion.mp3" in the res/raw directory. 請注意,擴展名被刪除。 例如,如果要從raw資源文件“explosion.mp3”加載聲音,則應該指定“R.raw.explosion”作為資源ID。 請注意,這意味着您在res / raw目錄中不能同時擁有“explosion.wav”和“explosion.mp3”文件。
    • int priority: the priority of the sound. Currently has no effect. Use a value of 1 for future compatibility.
    • Returnsa sound ID. This value can be used to play or unload the sound.
  • int  soundID  load(String path, int priority)    Load the sound from the specified path.
  • int  soundID  load(AssetFileDescriptor afd, int priority)    Load the sound from an asset file descriptor.
  • int  soundID  load(FileDescriptor fd, long offset, long length, int priority)    Load the sound from a FileDescriptor.
    • This version is useful if you store multiple sounds in a single binary. The offset specifies the offset from the start of the file and the length specifies the length of the sound within the file. 如果您將多個聲音存儲在單個二進制文件中,則此版本很有用。 offset指定從文件開頭的偏移量,length指定文件中聲音的長度。

播放指定的【soundID】,並返回一個【streamID】
  • final int  streamID  play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)    Play a sound from a sound ID.

對指定【 streamID】 播放進行控制
  • final void    pause(int streamID)    Pause a playback stream.  暫停播放
    • Pause the stream specified by the streamID. This is the value returned by the play() function. If the stream is playing, it will be paused. If the stream is not playing (e.g. is stopped or was previously paused), calling this function will have no effect. 暫停streamID指定的流。 這是play()函數返回的值。 如果流正在播放,它將被暫停。 如果流未播放(例如,停止或先前已暫停),則調用此功能將不起作用。
  • final void    resume(int streamID)    Resume a playback stream. 繼續播放
    • Resume the stream specified by the streamID. This is the value returned by the play() function. If the stream is paused, this will resume playback. If the stream was not previously paused, calling this function will have no effect.
  • final void    stop(int streamID)    Stop a playback stream.  終止播放
    • Stop the stream specified by the streamID. This is the value returned by the play() function. If the stream is playing, it will be stopped. It also releases any native resources associated with this stream. If the stream is not playing, it will have no effect.

對指定【streamID】的參數進行設置
  • final void    setLoop(int streamID, int loop)    Set loop mode.  設置指定播放流循環次數
    • A loop value of -1 means loop forever, a value of 0 means don't loop, other values indicate the number of repeats, e.g. a value of 1 plays the audio twice. If the stream does not exist, it will have no effect.
  • final void    setPriority(int streamID, int priority)    Change stream priority.  設置指定播放流的優先級
    • Change the priority of the stream specified by the streamID. This is the value returned by the play() function. Affects the order in which streams are re-used to play new sounds. If the stream does not exist, it will have no effect.
  • final void    setRate(int streamID, float rate)    Change playback rate.  設置指定播放流的播放速率
    • The playback rate allows the application to vary the playback rate (pitch) of the sound. A value of 1.0 means playback at the original frequency. A value of 2.0 means playback twice as fast, and a value of 0.5 means playback at half speed. If the stream does not exist, it will have no effect.
  • final void    setVolume(int streamID, float leftVolume, float rightVolume)    Set stream volume.  設置指定播放流的音量大小
    • Sets the volume on the stream specified by the streamID. This is the value returned by the play() function. The value must be in the range of 0.0 to 1.0. If the stream does not exist, it will have no effect.

全部流的暫停播放與恢復 播放
  • final void    autoPause()    Pause all active streams.
    • Pause all streams that are currently playing. This function iterates through all the active streams and pauses any that are playing. It also sets a flag so that any streams that are playing can be resumed by calling autoResume().  暫停正在播放的所有流。 此函數遍歷所有活動流並暫停播放任何正在播放的流。 它還設置一個標志,以便可以通過調用autoResume()來恢復正在播放的任何流。
  • final void    autoResume()    Resume all previously active streams.
    • Automatically resumes all streams that were paused in previous calls to autoPause().

資源釋放
  • final boolean    unload(int soundID)    Unload a sound from a sound ID.  卸載一個指定的音頻資源
    • Unloads the sound specified by the soundID. This is the value returned by the load() function. Returns true if the sound is successfully unloaded, false if the sound was already unloaded.
  • final void    release()    Release the SoundPool resources.  釋放所有資源
    • Release all memory and native resources used by the SoundPool object. The SoundPool can no longer be used and the reference should be set to null. 

設置監聽
  • void    setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)    Sets the callback hook for the OnLoadCompleteListener.
內部 接口:SoundPool.OnLoadCompleteListener
  • void onLoadComplete(SoundPool soundPool, int sampleId, int status)  Called when a sound has completed loading.

內部類: SoundPool.Builder
  • SoundPool.Builder()   Constructs a new Builder with the defaults format values.
    • If not provided, the maximum number of streams is 1 , and the audio attributes have a usage value of USAGE_MEDIA (see setAudioAttributes(AudioAttributes) to change them).
  • SoundPool.Builder   setAudioAttributes(AudioAttributes attributes)  Sets the AudioAttributes.
    • Sets the AudioAttributes. For examples, game applications will use attributes built with usage information set to USAGE_GAME.
    • AudioAttributes:A class to encapsulate a collection of attributes describing information about an audio stream. 封裝描述有關音頻流的信息的屬性集合的類。
  • SoundPool.Builder   setMaxStreams(int maxStreams)  Sets the maximum of number of simultaneous streams that can be played simultaneously.
  • SoundPool   build()

加載與播放方法

1、加載音頻資源
可以通過以下四種途徑來加載一個音頻資源(API中指出,其中的priority參數目前沒有效果,建議設置為1)
  • 通過一個資源ID,int load(Context context, int resId, int priority)
  • 通過指定的路徑,int load(String path, int priority)
  • 通過一個AssetFileDescriptor對象,int load(AssetFileDescriptor afd, int priority)
  • 通過FileDescriptor,int load(FileDescriptor fd, long offset, long length, int priority)
一個SoundPool能同時管理多個音頻,所以可以通過多次調用load函數來加載,如果加載成功將返回一個非0的soundID,用於播放時指定特定的音頻。 

2、播放
播放指定音頻的音效,並返回一個streamID
   
   
   
           
  1. final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) 
Play the sound specified by the soundID. This is the value returned by the load() function. Returns a non-zero streamID if successful, zero if it fails. The streamID can be used to further control playback. Note that calling play() may cause another sound to stop playing if the maximum number of active streams is exceeded
播放soundID指定的聲音。 這是load()函數返回的值。 如果成功,返回非零streamID;如果失敗,返回0。 streamID可用於進一步控制播放。 請注意,如果超過最大數量的活動流,呼叫play () 可能會導致另一個聲音停止播放。

A loop value of -1 means loop forever, a value of 0 means don't loop, other values indicate the number of repeats, e.g. a value of 1 plays the audio twice. The playback rate allows the application to vary the playback rate (pitch) of the sound. A value of 1.0 means play back at the original frequency. A value of 2.0 means play back twice as fast, and a value of 0.5 means playback at half speed.
循環值為-1表示循環永遠,值為0表示不循環,其他值表示重復次數,例如。 值為1播放音頻兩次。 播放速率允許應用程序改變聲音的播放速率(音高)。 值為1.0表示以原始頻率播放。 值為2.0意味着播放速度快兩倍,值為0.5表示以半速播放。

Parameters
  • soundID int: a soundID returned by the load() function
  • leftVolume float: left volume value (range = 0.0 to 1.0)  左聲道的音量
  • rightVolume float: right volume value (range = 0.0 to 1.0)
  • priority int: stream priority (0 = lowest priority)  流的優先級
  • loop int: loop mode (0 = no loop, -1 = loop forever)
  • rate float: playback rate (1.0 = normal playback, range 0.5 to 2.0)  播放的速率
Returns
  • int non-zero streamID if successful, zero if failed

個人對SoundPool的認識與總結

   
   
   
           
如果應用程序經常播放密集、急促而又短暫的音效 ( 如游戲音效 ) 那么使用MediaPlayer顯得有些不太適合了。因為MediaPlayer存在如下缺點:
  • 1) 延時時間較長,且資源占用率高。
  • 2) 不支持多個音頻同時播放。

Android中除了MediaPlayer播放音頻之外還提供了SoundPool來播放音效, SoundPool使用【音效池】的概念來管理多個短促的音效 ,例如它可以開始就加載20個音效,以后在程序中按音效的ID進行播放。

SoundPool主要用於播放一些較短的聲音片段,與MediaPlayer相比, SoundPool的優勢在於CPU資源占用量低和反應延遲小

SoundPool 和其他聲音播放類相比,其特點是 可以自行設置聲音的品質、音量、播放比率等參數。並且它可以同時管理多個音頻流 ,每個流都有獨自的ID,對某個音頻流的管理都是通過ID進行的。

實際使用SoundPool播放聲音時需要注意:
  • SoundPool最大只能申請1M的內存空間,這就意味着我們只能用一些很短的聲音片段,而不是用它來播放歌曲或者做游戲背景音樂。
  • SoundPool提供了pause和stop方法,但這些方法建議最好不要輕易使用,因為有些時候它們可能會使你的程序莫名其妙的終止。有些朋友反映它們不會立即中止播放聲音,而是把緩沖區里的數據播放完才會停下來,也許會多播放一秒鍾。
  • 流的加載過程是一個將音頻解壓為原始16位PCM數據的過程,由一個后台線程來進行異步處理,所以初始化后不能立即播放,需要等待一點時間。
  • SoundPool的效率在這些播放類中算是很好的了,但也不是絕對不存在延遲問題,尤其在那些性能不太好的手機中,SoundPool的延遲問題可能會很嚴重。

總結:
  • 1、一個SoundPool可以管理多個音頻資源,通過load()函數,成功則返回非0的soundID
  • 2、一個SoundPool可以同時播放多個音頻資源,通過play()函數,成功則返回非0的streamID
  • 3、pause()、resume()和stop()等操作是針對streamID(播放流)的
  • 4、當設置為無限循環時,需要手動調用stop()來終止播放
  • 5、播放流的優先級(play()中的priority參數),只在同時播放數超過設定的最大數時起作用
  • 6、程序中不用考慮播放流的生命周期,無效的soundID/streamID不會導致程序錯誤

演示示例

    
    
    
            
  1. public class SoundPoolActivity extends ListActivity {
  2. private SoundPool soundPool;
  3. private List<Integer> soundIdList = new ArrayList<>();
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. String[] array = {"0、raw:s10_bgm.ogg",
  7. "1、raw:cf_bgm1.mp3",
  8. "2、SD卡:s10_bgm.ogg",
  9. "3、URL:s10_bgm.ogg,網絡URL,不能播放",
  10. "4、AssetFileDescriptor:assets目錄",
  11. "5、AssetFileDescriptor:raw目錄",
  12. "6、FileDescriptor:不能播放",
  13. "7、FileDescriptor:不能播放",};
  14. setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(Arrays.asList(array))));
  15. soundPool = new SoundPool.Builder()
  16. .setMaxStreams(5)
  17. .setAudioAttributes(new AudioAttributes.Builder()
  18. .setUsage(AudioAttributes.USAGE_MEDIA)
  19. .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build())
  20. .build();
  21. soundPool.setOnLoadCompleteListener((soundPool1, sampleId, status) -> {
  22. Log.i("bqt", "【onLoadComplete】" + sampleId + " " + status);//status : 0 = success
  23. });
  24. Context context = this.getApplicationContext();//不管使用哪個Context,退出當前Activity后都會且會在延遲幾秒鍾后停止播放
  25. for (int i = 0; i < 8; i++) {
  26. soundIdList.add(-1);
  27. }
  28. //通過一個資源ID:Context context, int resId, int priority
  29. soundIdList.set(0, soundPool.load(context, R.raw.s1_ding, 0));
  30. soundIdList.set(1, soundPool.load(context, R.raw.cf_bgm1, 0));
  31. //通過指定的路徑:String path, int priority
  32. soundIdList.set(2, soundPool.load(Environment.getExternalStorageDirectory() + File.separator + "s10_bgm.ogg", 0));
  33. soundIdList.set(3, soundPool.load("http://www.baiqiantao.xyz/s10_bgm.ogg", 0));//網絡路徑,不能播放
  34. //通過FileDescriptor:FileDescriptor fd, long offset, long length, int priority
  35. try {
  36. AssetFileDescriptor afd = getAssets().openFd("s10_bgm.ogg");
  37. Log.i("bqt", "【afd】" + afd.getStartOffset() + "," + afd.getLength() + "," + afd.getDeclaredLength());//43868,42987,42987
  38. soundIdList.set(4, soundPool.load(afd, 0));
  39. AssetFileDescriptor afd2 = getResources().openRawResourceFd(R.raw.s1_ding);
  40. Log.i("bqt", "【afd2】" + afd2.getStartOffset() + "," + afd2.getLength() + "," + afd2.getDeclaredLength());//4300928,60647,60647
  41. soundIdList.set(5, soundPool.load(afd2, 0));
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. //通過FileDescriptor:FileDescriptor fd, long offset, long length, int priority
  46. try {
  47. FileDescriptor fd = getAssets().openFd("s1_fire.ogg").getFileDescriptor();
  48. Log.i("bqt", "【是否有效 1】" + fd.valid());//true
  49. soundIdList.set(6, soundPool.load(fd, 0, 1, 0));//Unable to load sample 【onLoadComplete】5 -2147483648
  50. } catch (IOException e) {
  51. e.printStackTrace();
  52. }
  53. FileDescriptor fd2 = getResources().openRawResourceFd(R.raw.s1_fire).getFileDescriptor();
  54. Log.i("bqt", "【是否有效 2】" + fd2.valid());//true
  55. soundIdList.set(7, soundPool.load(fd2, 0, 1, 0));//Unable to load sample 【onLoadComplete】5 -2147483648
  56. }
  57. @Override
  58. protected void onListItemClick(ListView l, View v, int position, long id) {
  59. int soundID = soundIdList.get(position);
  60. Toast.makeText(this, "" + soundID, Toast.LENGTH_SHORT).show();
  61. soundPool.play(soundID, 1, 1, 1, 0, 1);
  62. }
  63. }
2017-7-5






免責聲明!

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



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