A. android音視頻【十二】使用OpenSLES和AudioTrack進行播放PCM
本節我們學習下如何播放pcm數據,在Android中有兩種方法:一種是使用java層的 AudioTrack 方法,一種是使用底層的 OpenSLES 直接在 jni 層調用系統的 OpenSLES的c方法 實現。
兩種使用場景不一樣:
AudioTrack 一般用於 比如本地播放一個pcm文件/流,又或者播放解碼後的音頻的pcm流,API較簡單。
OpenSLES 一般用於一些播放器中開發中,比如音頻/視頻播放器,聲音/音頻的播放採用的OpenSLES,一是播放器一般是c/c++實現,便於直接在c層調用OpenSLES的API,二也是如果用AudioTrack進行播放,務必會帶來java和jni層的反射調用的開銷,API較復雜。
可以根據業務自行決定來進行選擇。
AudioTrack的方式使用較簡單,直接在java層。
指定采樣率,采樣位數,聲道數進行創建。
其中44100是采樣率, AudioFormat.CHANNEL_OUT_STEREO 為雙聲道,還有 CHANNEL_OUT_MONO 單聲道。 AudioFormat.ENCODING_PCM_16BIT 為采樣位數16位,還有 ENCODING_PCM_8BIT 8位。 minBufferSize 是播放器緩沖的大小,也是根據采樣率和采樣位數,聲道數 進行獲取,只有滿足最小的buffer才去操作底層進程播放。
最後一個參數mode。可以指定的值有 AudioTrack.MODE_STREAM 和 AudioTrack.MODE_STATIC 。
MODE_STREAM 適用於大多數的場景,比如動態的處理audio buffer,或者播放很長的音頻文件,它是將audio buffers從java層傳遞到native層。音頻播放時音頻數據從Java流式傳輸到native層的創建模式。
MODE_STATIC 適用場景,比如播放很短的音頻,它是一次性將全部的音頻資源從java傳遞到native層。音頻數據在音頻開始播放前僅從Java傳輸到native層的創建模式。
是的,就這么一個方法。注意此方法是同步方法,是個耗時方法,一般是開啟一個線程循環調用 write 方法進行寫入。
注意在調用 write 方法前需要調用 audioTrack.play() 方法開始播放。
因為是pcm裸數據,無法像mediaplayer一樣提供了API。所以需要自己處理下。可以利用 getPlaybackHeadPosition 方法。
getPlaybackHeadPosition() 的意思是返回以幀為單位表示的播放頭位置
getPlaybackRate() 的意思是返回以Hz為單位返回當前播放采樣率。
所以當前播放時間可以通過如下方式獲取
OpenSLES:(Open Sound Library for Embedded Systems).
OpenSLES是跨平台是針對嵌入式系統精心優化的硬體音頻加速API。使用OpenSLES進行音頻播放的好處是可以不依賴第三方。比如一些音頻或者視頻播放器中都是用OpenSLES進行播放解碼後的pcm的,這樣免去了和java層的交互。
在Android中使用OpenSLES首先需要把Android 系統提供的so鏈接到外面自己的so。在CMakeLists.txt腳本中添加鏈接庫OpenSLES。庫的名字可以在 類似如下目錄中
需要去掉lib
然後導入頭文件即可使用了OpenSLES提供的底層方法了。
創建&使用的步驟大致分為:
一個 SLObjectItf 裡面可能包含了多個Interface,獲取Interface通過 GetInterface 方法,而 GetInterface 方法的地2個參數 SLInterfaceID 參數來指定到的需要獲取Object裡面的那個Interface。比如通過指定 SL_IID_ENGINE 的類型來獲取 SLEngineItf 。我們可以通過 SLEngineItf 去創建各種Object,例如播放器、錄音器、混音器的Object,然後在用這些Object去獲取各種Interface去實現各種功能。
如上所說,SLEngineItf可以創建混音器的Object。
在創建播放器前需要創建音頻的配置信息(比如采樣率,聲道數,每個采樣的位數等)
開始播放後會不斷的回調這個 pcmBufferCallBack 函數將音頻數據壓入隊列
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);
如果想要暫停播放參數直接設置為SL_PLAYSTATE_PAUSED,若暫停後繼續播放設置參數為SL_PLAYSTATE_PLAYING即可。若想要停止播放參數設置為SL_PLAYSTATE_STOPPED即可。
首先獲取播放器的用於控制音量的介面SLVolumeItf pcmVolumePlay
然後動態設置
首先也是獲取播放器的用於控制音量的介面SLMuteSoloItf pcmMutePlay
然後動態設置
看起來控制還是蠻簡單的哈。先熟悉這么多,OpenSLES還是蠻強大的。
https://github.com/ta893115871/PCMPlay
B. Android播放簡短音頻-SoulPool
前言
最近新接觸的項目中有一個業務功能是語音播報,所以有接觸到了SoulPool這個類,寫個文章記錄一下~
如果項目業務功能中需要播放簡短的音頻的話,可以使用SoundPool 。SoundPool 是 Android 提供的一個API類,用來播放簡短音頻的,使用起來簡單並且功能強大。
SoundPool 除了可以完成音頻的播放、暫停、恢復及停止的操作外,還可以調節左右聲道的音量值、調整播放的語速、設置播放的優先順序以及播放的次數等等。
創建SoulPool
SoundPool 的創建方式在Android 5.0 以前是直接使用SoundPool 的構建方法即可,在Android5.0之後,則是使用Builder模式來創建。為了更好的兼容性,我們可以判斷一下api版本進行對應的創建。
Android5.0之前SoundPool 的構造函數有三個參數分別是maxStreams、streamType和srcQuality。
maxStreams:同時播放流的最大數量,當播放的流大於此設置值時,則會選擇性停止優先順序較低的流;
streamType:流類型,例如STREAM_MUSIC、STREAM_ALARM、STREAM_NOTIFICATION等;
srcQuality:采樣率轉換器質量,很少用得上,默認設置0即可;
Android5.0 之後,使用Builder模式進行構造,可以設置多個參數。我這里放兩張源碼截圖,如果需要詳細了解的,可以自己點進去追蹤一下~
SoundPool.Builder中setMaxStreams和之前Android5.0SoundPool的構造函數maxStreams參數相同。SoundPool.Builder中setAudioAttributes用來設置audio 屬性,此值要麼不設,要麼設置不為null的值,否則會導致異常產生。
音頻資源的載入與播放
首先我們要進行音頻資源的載入,載入成功之後,才能進行播放。載入音頻,我們可以使用SoulPool實例中的load方法。
載入音頻資源時非同步執行的,此過程需要時間,所以我們可以使用SoulPool中的OnLoadCompleteListener介面進行監聽,其中的onLoadComplete方法則是在音頻資源載入完成後調用。
我們還可以通過SoulPool中的unload方法,卸載之前載入過的資源。
其實,只要我們點進去SoulPool類中,就能看見它提供的所有方法了,如上圖。
音頻播放,我們是使用到了SoulPool實例中的play方法,該方法要傳比較多的參數,分別是soundID、leftVolume、rightVolume、priority、loop和rate,它們分別代表的意思是:
soundID:是SoulPool載入資源load方法返回的值,指向某個已載入的音頻資源;
leftVolume和rightVolume:分別代表左聲道和右聲道的值,設置范圍在0f ~ 1f;
priority:則是流的優先順序;
loop:是循環播放的次數, 例如-1是表示無限循環;
rate:則是播放的速率 ,1是正常速率,如果設置2則表示2倍速率;
play方法返回的streamID,若返回0則播放失敗,其它值都代表播放成功。
圖中方法還有pause、resume、stop分別代表暫停音播放、恢復音頻播放和停止音頻播放。SoulPool提供的方法,源碼中都有,這里就不一一細說了。記得!一定要釋放資源(release)
注意事項
1.SoulPool並不是什麼格式的音頻資源都支持的,MP3這些常見的格式當然是支持的,如果有別的格式的音頻可以自己測試一下;
2.SoulPool是Android為了播放簡短音頻提供的api,所以盡量不要播放時間過長的音頻;
3.設置播放流的優先順序的問題,如果同時播放的活動流的數目超過設置的maxStreams時,會根據優先順序來停止優先順序較低的流;如果有多個具有相同低優先順序的流,它將選擇要停止的最舊流,並且該流不再有效;如果要播放的流的優先順序最低,則會播放失敗。
4.載入音頻資源的數量限制也注意一下,別載入過多。
2021年9月15日程序猿小鍾帶著【播放簡短音頻類-SoulPool】到此一游~
C. android 5.0怎麼設置音樂從聽筒播放
5.0 之前 可以用AudioManager.setMode來設置phone state 來達到切換聽筒的目的,但是5.0之後非系統應用沒有這個許可權了。發現微信依然可以切換,研究了下底層設備切換發現是如下操作的:
1.播放音頻流類型要設置為AudioManager.STREAM_VOICE_CALL
2.在播放前調用AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION,AudioSystem.FORCE_NONE);
這個調用需要 在AndroidManifest.xml中添加 android.permission.MODIFY_AUDIO_SETTINGS 許可權
並且AudioSystem這個類是隱藏類,在Android 公開API中沒有,所以需要用反射調用,或者自己編譯出一個framework.jar包,放到工程里,不過這樣有可能兼容性不是很好。