成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Android SoundPool 音效播放庫

移動開發 Android
這里只是介紹了我們如何正確使用SoundPool?以及相關api。如果你看完了整個內容,我相信你在使用SoundPool進行播放音頻時,就不會出現無法播放,播放失敗等情況了。

1. 介紹

我們如果想在應用中進行播放一些音效,例如提示音,提示短語等簡短的音頻文件。可以使用 SoundPool 這個工具進行快捷播放。

它利用 MediaCodec? 服務為音頻解碼為一個原始16位 PCM? 流。這個特性使得應用程序可以進行流壓縮,而無須忍受在播放音頻時解壓所帶來的CPU負載和時延。SoundPool 會將音頻解碼后進行預編碼到內存中。然后再根據需求進行播放。

匯總特性如下:

  1. 單個文件不能大于1M。如果解碼的音頻超過1兆字節的存儲空間,則該音頻將被截斷。
  2. 可以一次性播放多個音頻。通過設置maxStreams?設置單個SoundPool?中可以播放的最大音頻數量。如果播放數量超過最大數量,SoundPool?會根據優先級自動關閉先前播放的音頻。(PS:默認限制數量maxStreams=1,限制最大數量有助于限制CPU負載,降低音頻混合影響視覺效果或UI性能的可能性。)
  3. 可設置循環播放,也可以指定播放次數。
  4. 可以設置播放速度,最大為2倍數,最小為0.5倍數。進行音頻的快速播放或者慢速播放。
  5. 可以設置優先級(priority?)。優先級從低到高,即數字越高,優先級越高。當調用play()?會導致活動流的數量超過創建SoundPool時maxStreams?參數所確定的值時,將使用優先級。在這種情況下,流分配器將停止優先級最低的流。如果有多個流具有相同的低優先級,它將選擇最舊的流停止。在新流的優先級低于所有活動流的情況下,新聲音將不會播放,play()?函數將返回streamID為零。(ps:該功能暫時還沒有效果,后續版本會支持優先級配置)
  6. 不用關心各種音頻流的生命周期,調用各種streamID的相關方法不會因為找不到播放流而出現各種錯誤和異常。

以上信息來源于 Android-32 android\media\SoundPool.java 源碼中的注釋

總而言之就是:

使用SoundPool? 可以播放多種音頻,甚至可以混音播放。但是不能播放比較大的音頻文件。長時間的音頻建議使用 MediaPlayer。

2. 使用

老版本SoundPool?是可以直接new SoundPool()?進行創建的,但是自從Android-API 21 之后就被廢棄了。改為SoundPool.Builder?進行創建SoundPool對象。

PS:SoundPool?對象不是一個單例對象,所以,我們其實是可以創建多個SoundPool對象的,但是不建議大量創建,影響性能。

主要步驟為:

  1. 創建SoundPool對象。
  2. 調用soundPool.load() 加載音頻文件。加載成功后返回soundId,如果是0就代表加載失敗了。
  3. 監聽setOnLoadCompleteListener方法,得到音頻文件是否加載成功。
  4. 調用soundPool.play()進行音頻播放。使用soundId進行播放。播放成功后會返回streamId,我們之后可以通過該streamId進行暫停,恢復,停止,修改循環次數,修改優先級,修改聲音等。
  5. 界面關閉時,調用soundPool.release()釋放資源。會釋放所有加載的音頻文件。

2.1 創建 SoundPool

SoundPool.Builder spb = new SoundPool.Builder();
SoundPool soundPool = spb.build(); //創建SoundPool對象

上述方法就創建了一個soundPool?播放對象了。默認最大 MaxStreams=1?,默認音效為:AudioAttributes.USAGE_MEDIA。

我們如果想設置最大streams數量,需要通過Builder對象進行設置:

SoundPool.Builder spb = new SoundPool.Builder();
spb.setMaxStreams(15); //但是不建議將這個值設置的較大,較大會占用比較大的內存空間的。

其次就是配置AudioAttributes(音頻屬性了)。

SoundPool.Builder spb = new SoundPool.Builder();
AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME).build();
spb.setAudioAttributes(attrBuilder.build());

下面詳細介紹音頻屬性的相關配置項。

2.1.1 音頻屬性-AudioAttributes

音頻屬性類中,有很多配置項。這里只是簡單介紹部分,更詳細的建議大家可以通過源碼進行查詢了解。

聲音用途-usage

那么默認情況下配置的setUsage(AudioAttributes.USAGE_MEDIA)是什么呢?

是用來描述音頻的用途為媒體文件使用的,其他可選配置如下:

AudioAttributes.USAGE_UNKNOWN://用法未知時要使用的用法值。也就是這個音頻預期用途不屬于以下定義的
AudioAttributes.USAGE_MEDIA: //當用途為媒體(如音樂或電影配樂)時要使用的用途值。
AudioAttributes.USAGE_VOICE_COMMUNICATION: //當使用是語音通信(如電話或VoIP)時要使用的使用值。
AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING://在呼叫信號中使用時使用的用法值,例如“忙碌”的嘟嘟聲或DTMF音調。
AudioAttributes.USAGE_ALARM: //當使用是警報(例如喚醒警報)時要使用的使用值。
AudioAttributes.USAGE_NOTIFICATION://使用情況為通知時要使用的使用情況值。
AudioAttributes.USAGE_NOTIFICATION_RINGTONE://當使用是電話鈴聲時要使用的使用值。
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: //當使用是請求進入/結束通信(如VoIP通信或視頻會議)時要使用的使用值。
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT://當使用是“即時”通信(如聊天或短信)的通知時使用的使用值。
AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED://當用途是通知非即時類型的通信(如電子郵件)時要使用的用途值。
AudioAttributes.USAGE_NOTIFICATION_EVENT: //當使用是為了吸引用戶的注意力時要使用的使用值,例如提醒或電池電量不足警告。
AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: //用于輔助功能時要使用的用法值,例如用于屏幕閱讀器。
AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: //當用途是駕駛或導航方向時要使用的用途值。
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: //當使用是聲音處理時要使用的使用值,例如用戶界面聲音。
AudioAttributes.USAGE_GAME: //用于游戲音頻時要使用的用法值。
AudioAttributes.USAGE_VIRTUAL_SOURCE: //用于虛擬資源生產時的用途值。
AudioAttributes.USAGE_ASSISTANT://用于對用戶查詢、音頻指令或幫助話語的音頻響應的用法值。

示例代碼如:

AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
attrBuilder.setUsage(AudioAttributes.USAGE_GAME);

當我們不配置setUsage()?的時候,音頻屬性默認的用途描述為:AudioAttributes.USAGE_INVALID 該值為無效值,僅用于未初始化的用法值。

所以,建議大家還是根據自己的音頻文件的使用用途,進行配置相關的用途值。

PS1:這個Usage用途值是用來告訴系統,我們這個音頻文件是屬于什么類型的。 如果關注過手機音量設置,就會知道我們可以針對通知,鬧鐘,音樂,視頻游戲,通話等不同場景設置相關音量。

這個用途決定了我們的音頻文件會被系統哪個音量設置進行控制。

PS2:這也就是為啥有些app中的音效在手機媒體音效都禁音了,還在播放。因為它可能將聲音的用途標注為了通知鈴聲等。

首次啟動SoundPool?進行播放音頻時,沒有配置Usage參數值,這個時候程序觸發了系統提示音的播放。

那么我們的SoundPool?調用load()就會得到返回值為0。音頻加載失敗。

AudioAttributes 類除了上面的聲音用途(Usage)以外。還有一些其他方法:

  • setContentType(int contentType)?:設置描述音頻信號的內容類型的屬性,例如語音或音樂。可選參數如下:
  • AudioAttributes.CONTENT_TYPE_UNKNOWN: 默認值,當內容類型未知或不是定義的內容類型時要使用的內容類型值。
  • AudioAttributes.CONTENT_TYPE_MOVIE:當內容類型為配樂(通常伴隨電影或電視節目)時要使用的內容類型值
  • AudioAttributes.CONTENT_TYPE_MUSIC:內容類型為音樂時要使用的內容類型值。
  • AudioAttributes.CONTENT_TYPE_SONIFICATION:當內容類型是用于伴隨用戶動作的聲音時使用的內容類型值,例如表示按鍵的嘟嘟聲或聲音效果,或事件,例如游戲中收到的獎金的聲音類型。這些聲音大多是合成的或簡短的 Foley 音。
  • AudioAttributes.CONTENT_TYPE_SPEECH:當內容類型為語音時要使用的內容類型值。
  • setFlage(int flags):設置標志的組合。設置的參數將會與已有值進行位運算。參數有兩個選項:
  • AudioAttributes.FLAG_AUDIBILITY_ENFORCED:定義一種行為的標志,其中聲音的可聽性將由系統確保。
  • AudioAttributes.FLAG_HW_AV_SYNC:請求使用支持硬件A/V同步的輸出流的標志。
  • setAllowedCapturePolicy(int capturePolicy):指定其他應用程序或系統是否可以捕獲音頻。這個配置的結果會組合在Flags參數中的。
  • AudioAttributes.ALLOW_CAPTURE_BY_ALL?:默認值,指示音頻可以被任何應用程序捕獲。這個捕獲會受到Usage參數的影響,因為涉及敏感操作。從Android API 29 開始只能捕獲USAGE_UNKNOWN,USAGE_MEDIA和USAGE_GAME。
  • AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM:指示音頻只能由系統應用程序捕獲。系統應用程序可以捕獲多種用途,如輔助功能、實時字幕、用戶指南等等但要遵守以下限制:1.音頻不能離開設備,2.音頻不能傳遞給第三方應用程序,3.音頻不能以高于16kHz 16位單聲道的質量。
  • AudioAttributes.ALLOW_CAPTURE_BY_NONE?:指示任何應用程序都不會錄制音頻,即使是系統應用程序也是如此。鼓勵使用ALLOW_CAPTURE_BY_SYSTEM而不是此值,因為系統應用程序為用戶提供了重要而有用的功能(如實時字幕和可訪問性)。
  • setHapticChannelsMuted(boolean muted): 指定在播放音頻觸覺耦合數據時是否應靜音觸覺。默認情況下,觸覺通道處于禁用狀態。簡單理解就是,當在播放音頻時。按鍵聲音,觸摸反饋等會設置為禁止狀態。
  • true:默認值,設置觸覺反饋靜音。
  • false:設置允許觸摸反饋聲音。
  • setIsContentSpatialized(boolean isSpatialized):指定是否已經對內容進行了空間化處理。如果有,則將其設置為true將防止諸如雙重處理之類的問題。
  • true:已經對音頻內容進行了空間化處理,系統不需要再進行雙重處理了。
  • false:默認值,沒有對音頻進行空間化處理。
  • setSpatializationBehavior(int sb) :設置使用空間化的行為。主要有兩個可選參數:(PS:沒有太能理解這個方法的意義,應該是需要更多的音頻相關知識才能弄明白吧。)
  • AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER:指示與這些屬性相關聯的音頻內容的常量永遠不應該被虛擬化。
  • AudioAttributes.SPATIALIZATION_BEHAVIOR_AUTO:默認值,指示與這些屬性相關聯的音頻內容將遵循默認的平臺行為,關于哪些內容將被空間化或不被空間化。

除了上面的方法。我們經常也看到一些分享的代碼中,并沒有使用上面的方法,而是只使用setLegacyStreamType()方法:

AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
attrBuilder.setLegacyStreamType(AudioManager.STREAM_MUSIC);

這個方法主要用來設置從傳統流類型推斷的屬性。AudioAttributes將會通過從遺留流類型派生的信息初始化某些屬性。

簡單理解就是,我們配置的Usage,ContentType,Flage?等等信息數據。AudioAttributes會從系統歷史痕跡中找到某個音頻流的屬性,進行復用配置。

官方注釋中,建議我們少使用該方法,而應該通過setUsage,setContentType等方法明確設置音頻的用法和內容類型等信息。

由于會覆蓋我們配置的Usage,ContentType,Flage,HapticChannelsMuted等方法值。

所以如果使用setLegacyStreamType? 就不要使用上面的配置音頻相關信息的方法。因為setLegacyStreamType優先級高,會覆蓋掉我們配置的信息。該方法的建議傳參有6個值:

但是首先會先從歷史痕跡中獲取信息,獲取不到的才會按照下面的配置項進行默認初始化。

  • AudioManager.STREAM_VOICE_CALL:將會ContentType設置為 CONTENT_TYPE_SPEECH,Usage設置為USAGE_VOICE_COMMUNICATION。
  • AudioManager.STREAM_SYSTEM:將會ContentType設置為 CONTENT_TYPE_SONIFICATION,Usage設置為USAGE_ASSISTANCE_SONIFICATION。
  • AudioManager.STREAM_RING:將會ContentType設置為 CONTENT_TYPE_SONIFICATION,Usage設置為USAGE_NOTIFICATION_RINGTONE。
  • AudioManager.STREAM_MUSIC:將會ContentType設置為 CONTENT_TYPE_MUSIC,Usage設置為USAGE_NOTIFICATION_RINGTONE。
  • AudioManager.STREAM_ALARM:將會ContentType設置為 CONTENT_TYPE_SONIFICATION,Usage設置為USAGE_NOTIFICATION_RINGTONE。
  • AudioManager.STREAM_NOTIFICATION:將會ContentType設置為 CONTENT_TYPE_SONIFICATION,Usage設置為USAGE_NOTIFICATION_RINGTONE。

除了上面六個傳參外,還可以傳一下其他的。這里就不詳細說明了。

音效的相關配置到這里就差不多了。我們繼續接著處理SoundPool播放。

2.2 加載音頻文件

當我們初始化基本的音頻播放器信息之后。我們就可以進行加載音頻文件了。

SoundPool?通過load()?方法進行加載文件。可以從assets,raw,本地磁盤等進行加載音頻。

下面介紹這幾種加載方式。

例如,從res?資源目錄下raw文件中加載音頻:

soundPool.load(this, R.raw.drill,1);

例如,從assets?目錄下加載音頻文件:從assets目錄下的sound文件夾中加載名為zinyan.mp3的音頻文件。

AssetFileDescriptor descriptor = null;
try {
descriptor = am.openFd("sound/zinyan.mp3");
} catch (IOException e) {
e.printStackTrace();
}
if(descriptor!=null){
soundPool.load(descriptor, 1);
}

例如,從本地磁盤中加載音頻文件:

soundPool.load("本地文件路徑", 1);

還可以從FileDescriptor中加載音頻文件進行播放。傳offset=0,length=文件大小,protity=1就可以了。

傳值中的protity 目前沒有效果。為了將來的兼容性,請使用值1。這個值就是所謂的優先級。

PS:常見應用是將部分音頻存儲在assets目錄或者raw目錄下。而如果是有比較多音效,那需要進行在線下載后調用FileDescripor進行加載。

當我們使用load()?進行加載音頻時,如果音頻文件正確那么就會返回一個id。該值為sound Id。

如果是錯誤會返回0。代表我們的音頻文件并沒有被轉為PCM流。

在這里我們需要注意一下,SoundID只是以下兩個方法才會使用到。

soundPool.play(soundId,1,1,1,0,1f)
soundPool.stop(soundId);

PS:soundId? 和streamID并不是同一個值,雖然我們打印輸出的時候可能都顯示的一樣的數。但是并不能代表兩個是一致的。

如果你確保該音頻文件是一個比較高頻使用的音頻,那么可以在初始化的時候批量調用load()方法進行預加載。

之后在需要播放的地方,直接調用soundPool.play? 傳遞該soundId就可以了。

在實際使用中,提取音頻文件到內存。然后可以進行play播放,中間的耗時是非常短的。但是,我們任然不能直接就執行play播放,因為時間再短它也是有耗時的。如果沒有加載完成就播放,是沒有聲音的

2.3 監聽加載狀態

當我們使用load()方法進行加載之后,只是將音頻文件提取存儲在內存中了。這個提取和存儲過程是在異步線程中進行操作的。所以并不會影響到我們UI線程的顯示。

示例如下:

//加載完畢,執行音頻播放
soundPool.setOnLoadCompleteListener((soundPool, sampleId, status) -> {
Log.e("onLoadComplete", "音頻加載狀態(0表示加載成功):" + status);
int streamID = soundPool.play(soundId, 1, 1, 1, 0, 1f);

});

因為我的音頻文件需要動態切換,而且量比較少。所以直接在加載完畢的回調中。

執行了play播放。

如果是相對固定,并且加載比較多的情況下。建議通過HashMap?進行存儲streamId和soundId

其中 sampleId?就是聲音樣本ID。也就是load?方法中返回的soundId。

2.4 播放音頻

當我們調用soundPool.play()?方法的時候,該方法調用成功會返回streamId,如果調用失敗就會返回0。

而該方法的完整傳值為:

soundPool.play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate)

soundID?: load()?函數返回的soundID?值,告訴soudPool要播放哪個音頻。

leftVolume:左側音量值(范圍0.0~1.0)。左聲道聲音值。

rightVolume:右側音量值(范圍0.0~1.0)。右聲道聲音值。

priority:音頻流播放優先級(0=最低優先級,通常默認讓設置為1)。

loop:循環模式(0=無循環,-1=永遠循環,其他表示數字表示當前數字對應的循環次數+默認播放的一次。例如循環2次,那么實際播放3次)。

rate:播放速率(1.0=正常播放,范圍為0.5~2.0),也就是0.5倍慢放,1正常,2倍快放。

這些配置,在初始化播放的時候就需要配置上。

我們如果播放成功后想修改聲道,優先級(暫時意義沒有多大),循環模式,播放速率等。調用相關方法修改即可:

int streamId = soundPool.play(soundId, 1, 1, 1, 0, 1f);
soundPool.setLoop(streamId,1); //循環一次
soundPool.setVolume(streamId,1,1);
soundPool.setPriority(streamId,1);
soundPool.setRate(streamId,1f);

要注意了,這些修改方法的調用前提是已經執行play?方法得到streamID之后才有意義。

否則是沒有意義和作用的。因為這些修改方法中streamID傳錯了也不會觸發崩潰等錯誤的。

相較于MediaPlayer。SoundPool因為針對的都是一些快速簡單的音效。

所以是沒有音頻播放結束的回調方法的。我們如果自己想知道音頻播放完畢,可以自己寫一個時間線程,線程結束后就當音頻已經播放完畢了吧。

雖然沒有音頻結束的監聽。但是我們可以針對音頻做停止,暫停和恢復等操作。

2.5 暫停,恢復,停止

當我們配置loop?循環模式為-1 無限循環時。我們需要主動調用stop停止方法才能中斷音頻的播放。

soundPool.stop(streamId);//停止
soundPool.pause(streamId);//暫停
soundPool.resume(streamId);//恢復

當我們調用stop?停止之后是不能通過resume進行恢復的。

要想恢復,只能是重新調用play方法進行播放。

以上是單個音頻流的操作,SoundPool還提供了批量操作的方法:

soundPool.autoPause(); //批量暫停
soundPool.autoResume(); //批量恢復

2.6 釋放資源

在一開始就介紹了SoundPool會將音頻文件加載到內存中。

我們操作比較多的音頻后,要注意資源的釋放。

否則會造成比較大的內存占用。

請注意:當我們調用音頻的stop()?方法時,只是將音頻流給回收了,也就是streamId失效了。

但是soundId?還是生效狀態,也就是說load()方法加載到內存中的資源是并沒有被釋放的。

釋放資源有兩種方法,釋放某個音頻:

soundPool.unload(soundId);//移除指定的加載的的音頻文件

如果該soundId指向的音頻文件不存在,也不會造成錯誤的。

上述的方法是移除某一個音頻文件的加載,其他加載的音頻文件是不會受到影響的。

釋放全部音頻:

soundPool.release();
soundPool = null;

當我們,使用release?方法進行操作時,會將load?加載的全部資源進行釋放,也會釋放SoundPool?對象使用的所有內存和本機資源。簡單理解就是soundPool對象和null沒有什么區別了

后面該對象就不能再被使用了。要想使用就需要重新new一個新對象,并賦值音頻屬性,加載音頻文件等操作。

3. 小結

這里只是介紹了我們如何正確使用SoundPool?以及相關api。如果你看完了整個內容,我相信你在使用SoundPool進行播放音頻時,就不會出現無法播放,播放失敗等情況了。

如果覺得本篇內容對你有一點點幫助,希望能夠給我點個贊鼓勵一下,謝謝。

責任編輯:武曉燕 來源: Zinyan
相關推薦

2013-05-21 14:10:11

Android游戲開發SoundPool類同時多音效

2012-04-17 12:47:27

cocos2d-x

2013-05-21 13:33:02

Android游戲開發異步音樂播放

2015-10-28 13:29:21

音頻源碼audio

2011-06-02 09:23:58

Android 效果

2013-06-14 17:28:11

Windows PhoWP開發播放聲音

2014-10-20 09:55:02

2012-12-27 14:29:38

Android開發流媒體

2013-07-01 10:53:05

2023-07-06 10:40:41

微軟Windows

2011-09-09 11:28:35

Android Mus

2021-08-09 14:00:24

微軟Windows 11Windows

2015-01-22 15:44:55

Android源碼音樂播放器

2012-12-24 09:04:04

iOSUnity3D

2010-01-27 16:21:29

Android多媒體播

2011-06-07 15:38:29

2013-04-19 01:42:02

2010-10-26 09:00:48

Winamp應用

2010-01-05 13:36:01

Windows 7音量調整

2012-02-23 14:02:04

win7
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产美女一区二区 | 伊人网综合 | 亚洲欧美中文日韩在线v日本 | 欧美一级毛片在线播放 | 干干干操操操 | 色中文在线 | 国产乱码精品一区二区三区忘忧草 | 国产精品一区二区不卡 | 欧美激情久久久久久 | 久久精品中文 | 免费在线视频a | 91超碰caoporn97人人 | 国产成人综合亚洲欧美94在线 | 欧美一级二级视频 | www.99热这里只有精品 | 91一区 | 国产欧美在线视频 | 男女羞羞视频在线免费观看 | 天天操夜夜操 | 日韩欧美一区二区三区在线播放 | 亚洲国产免费 | 日本高清精品 | 欧美视频福利 | 自拍偷拍亚洲欧美 | 欧美影院 | 妹子干综合 | av手机免费在线观看 | 欧美bondage紧缚视频 | 日韩字幕一区 | 欧美成人h版在线观看 | 精品美女久久久久久免费 | 亚洲视频在线播放 | 国产乱码精品一区二区三区中文 | 亚洲品质自拍视频网站 | 久久人人爽人人爽人人片av免费 | 中文字幕av网站 | 国产精品久久久久免费 | 日本一区二区不卡视频 | 久久久久久久一区 | 国产精品一区一区 | 日韩色图视频 |