Android音頻焦點管理實例詳解

音頻焦點管理的意義

兩個或兩個以上的 Android 應用可同時向同一輸出流播放音頻。系統會將所有音頻流混合在一起。雖然這是一項出色的技術,但卻會給用戶帶來很大的困擾。為瞭避免所有音樂應用同時播放,Android 引入瞭“音頻焦點”的概念。 一次隻能有一個應用獲得音頻焦點。

當您的應用需要輸出音頻時,它需要請求獲得音頻焦點,獲得焦點後,就可以播放聲音瞭。不過,在您獲得音頻焦點後,您可能無法將其一直持有到播放完成。其他應用可以請求焦點,從而占有您持有的音頻焦點。如果發生這種情況,您的應用應暫停播放或降低音量,以便於用戶聽到新的音頻源。

音頻焦點管理的行為準則

  • 在即將開始播放之前調用 requestAudioFocus(),並驗證調用是否返回 AUDIOFOCUS_REQUEST_GRANTED。
  • 在其他應用獲得音頻焦點時,應該停止或者暫停播放,或者降低音量。
  • 播放停止後應該放棄音頻焦點

版本兼容

從Android 8.0(O版本,API 26)開始,音頻焦點的請求方式以及系統管理有瞭細微的變化,下面分兩部分來說明。

在Android 8.0(API 26) 之前對音頻焦點具體處理實現

當想錄音或者播放歌曲的時候,最好(非必須)先請求音頻焦點,這個時候需要調用AudioManager.requestAudioFocus()方法,函數原型如下

AudioManager.requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)

第一個參數用於監聽焦點變化

第二個參數表明請求的音頻焦點影響的是那種類型流,例如,如果我們錄音,我們肯定是要影響Music這一類型的音頻流,因此可以選擇AudioManager.STREAM_MUSIC。當然還有許多類型。

/** 通話相關 */
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/** 系統聲音 */
public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
/** 鈴聲 */
public static final int STREAM_RING = AudioSystem.STREAM_RING;
/** 音樂相關 */
public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
/** 鬧鐘相關 */
public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;

第三個參數用於表明音頻焦點的持續時間,這個很關鍵,它也有許多種類型,下面一一列出。

  • AudioManager.AUDIOFOCUS_GAIN: API文檔說請求的這類音頻焦點持續時間是未知的,通常用來表示長時間獲取焦點,可以用來播放音樂,錄音等等。
  • AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 表明請求的音頻焦點持續時間比較短,通常用來播放導航路線的聲音,或者播放通知聲音。
  • AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 這個也是表明請求的音頻焦點持續時間比較短,但是在這段時間內,它希望其他應用以較低音量繼續播放。例如,我們在使用導航的時候可以聽音樂,當出現導航語音的時候,音樂音量會降低以便我們能聽清楚導航的語音,當導航語音播放完畢後,音樂恢復音量,繼續播放。
  • AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 這個也是表明音請求的音頻焦點持續時間比較短,但是在這段時間內,不希望任何應用(包括系統應用)來做任何與音頻相關的事情,就算是降低音量播放音樂也是不被希望的。例如當我們進行錄音或者語音識別的時候,我們不希望其他的聲音出現幹擾。

AudioManager.requestAudioFocus()的返回值表明請求的結果AudioManager.AUDIOFOCUS_REQUEST_FAILED表明請求焦點失敗,AudioManager.AUDIOFOCUS_REQUEST_GRANTED表明請求焦點成功。

當我們成功請求焦點後,就可以做一些與音頻有關的事情,例如播放音樂,錄音,或者語音識別。當完成這些工作後,我們必須調用AudioManager.abandonAudioFocus(onAudioFocusChangeListener l)釋放音頻焦點。

8.0 之後實現

從Android 8.0開始(API 26),請求音頻焦點的方式以及系統對音頻焦點變化的管理有些微妙的變化。 首先,對音頻焦點變化的管理的變化體現在兩個方面,延遲獲取焦點和自動降低音量。

延遲獲取焦點

在Android 8.0之前,當我們請求音頻焦點的時候,隻會返回兩種結果,要麼請求成功(AUDIOFOCUS_REQUEST_GRANTED),要麼請求失敗(AUDIOFOCUS_REQUEST_FAILED)。

而從Android 8.0開始,還有一種結果,延遲成功請求(AUDIOFOCUS_REQUEST_DELAYED),這個也是成功的請求,但是這個請求具有延遲性。例如當我們處於通話狀態的時候,我們很顯然不希望任何app來獲取到音頻焦點來做些事,例如播放音樂。

然而隻有設置過AudioFocusRequest.Builder.setAcceptsDelayedFocusGain(true)才能獲取到這種結果,這個我們後面會講到。那麼我們怎麼知道什麼時候獲取到瞭音頻焦點呢,當然還需要設置AudioManager.OnAudioFocusChangeListener這個音頻焦點變化的監聽器,通過回調確認何時獲取到瞭音頻焦點。

自動降低音量

在Android 8.0之前,如果請求焦點使用瞭AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK參數,它表明希望擁有瞭音頻焦點的其他應用降低音量來使用音頻,然而並不是所有的應用都會這樣做(有可能開發者忘記瞭優化),因為這並不是系統強制的。But, 從Android 8.0開始,這個降低音量的工作,就是系統默認行為瞭,可以說是一個良心的優化。

如果我不希望系統自動給我降低音量,而是想自己暫停音頻相關的工作,那咋辦?這個可以通過AudioFocusRequest.Builder.setWillPauseWhenDucked(true)方法取消系統的默認行為,然後通過監聽音頻焦點變化

音頻焦點請求方式

從 Android 8.0(API 級別 26)開始,當您調用 requestAudioFocus() 時,必須提供 AudioFocusRequest 參數。要釋放音頻焦點,請調用 abandonAudioFocusRequest() 方法,該方法也接受 AudioFocusRequest 作為參數。在請求和放棄焦點時,應使用相同的 AudioFocusRequest 實例

要創建 AudioFocusRequest,請使用 AudioFocusRequest.Builder。由於焦點請求始終必須指定請求的類型,因此此類型會包含在構建器的構造函數中。使用構建器的方法來設置請求的其他字段

  • setFocusGain(): 隻有這個方法是必須的,而傳入的參數與8.0之前使用AudioManager.requestAudioFocus()傳入的第三個參數一樣,都是用來表示持續時間。

  • setAudioAttributes(): 這個方法是用來描述app的使用情況。這方法需要傳入一個AudioAttributes對象,這個對象也是使用Builder模式來構造,例如使用AudioAttributes.Builder.setUsage()來描述使用這個音頻來幹什麼,我們可以傳入一個AudioAttributes.USAGE_MEDIA來表明用這個音頻來作為媒體文件來播放,也可以傳入一個AudioAttributes.USAGE_ALARM來表明用這個來作為鬧鈴。

  • setWillPauseWhenDucked(): 這個前面說過,是為瞭覆蓋系統默認降低音量的行為,但是必須要設置AudioManager.OnAudioFocusChangeListener才能自己處理這類情況。

  • setAcceptsDelayedFocusGain(): 這個前面也說過,這個是為瞭能夠延遲獲取到焦點的必須條件,但是同時也必須要設置AudioManager.OnAudioFocusChangeListener才能得知何時獲取到焦點。

  • setOnAudioFocusChangeListener(): 音頻焦點變化監聽器。值得一提的是這個方法有個重載的方法,有一個重載方法有兩個參數,第二個參數為Handler對象,看到Handler應該明白瞭,是為瞭使用它的消息隊列來順序處理這個回調

響應音頻焦點更改

當應用獲得音頻焦點後,它必須能夠在其他應用為自己請求音頻焦點時釋放該焦點。出現這種情況時,您的應用會收到對 AudioFocusChangeListener 中的 onAudioFocusChange() 方法的調用,該方法是您在應用調用 requestAudioFocus() 時指定的。

傳遞給 onAudioFocusChange() 的 focusChange 參數表示所發生的更改類型。它對應於獲取焦點的應用所使用的持續時間提示。您的應用應該做出適當的響應

暫時性失去焦點

如果焦點更改是暫時性的(AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 或 AUDIOFOCUS_LOSS_TRANSIENT),您的應用應該降低音量(如果您不依賴於自動降低音量)或暫停播放,否則保持相同的狀態。

在暫時性失去音頻焦點時,您應該繼續監控音頻焦點的變化,並準備好在重新獲得焦點後恢復正常播放。當搶占焦點的應用放棄焦點時,您會收到一個回調 (AUDIOFOCUS_GAIN)。此時,您可以將音量恢復到正常水平或重新開始播放。

永久性失去焦點

如果是永久性失去音頻焦點 (AUDIOFOCUS_LOSS),則其他應用會播放音頻。您的應用應立即暫停播放,因為它不會收到 AUDIOFOCUS_GAIN 回調。要重新開始播放,用戶必須執行明確的操作,例如在通知或應用界面中按播放傳輸控件。

附音頻基礎知識

采樣和采樣頻率:一秒鐘內采樣的次數稱為采樣頻率。采樣頻率越高,越接近原始信號,但是也加大瞭運算處理的復雜度。根據Nyquist采樣定理,要想重建原始信號,采樣頻率必須大於信號中最高頻率的兩倍。人能感受到的頻率范圍為20HZ–20kHZ, 一般音樂的采樣頻率為44.1kHZ, 更高的可以是48kHZ和96kHZ,不過一般人用耳聽感覺不出差別瞭。語音主要是以溝通為主,不需要像音樂那樣清晰,用16k采樣的語音就稱為高清語音瞭。現在主流的語音采樣頻率為16kHz。

采樣位數:數字信號是用0和1來表示的。采樣位數就是采樣值用多少位0和1來表示,也叫采樣精度,用的位數越多就越接近真實聲音。如用8位表示,采樣值取值范圍就是-128–127,如用16位表示,采樣值取值范圍就是-32768–32767。現在一般都用16位采樣位數。

聲道:聲道是指處理的聲音是單聲道還是立體聲。Android支持雙聲道立體聲和單聲道。CHANNEL_IN_MONO單聲道,CHANNEL_IN_STEREO立體聲。單聲道在聲音處理過程中隻有單數據流,而立體聲則需要左、右聲道的兩個數據流。顯然,立體聲的效果要好,但相應的數據量要比單聲道的數據量加倍。

碼率:就是比特率。比特率是指每秒傳送的比特(bit)數。碼率=采樣率X采樣位數X聲道數。

音頻采集和播放:一般用專門的芯片(通常叫codec芯片)采集音頻,做AD轉換,然後把數字信號通過I2S總線(主流用I2S總線,也可以用其他總線,比如PCM總線)送給CPU處理(也有的會把codec芯片與CPU芯片集成在一塊芯片中)。當要播放時CPU會把音頻數字信號通過I2S總線送給codec芯片,然後做DA轉換得到模擬信號再播放出來。

總結

到此這篇關於Android音頻焦點管理的文章就介紹到這瞭,更多相關Android音頻焦點管理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: