【IOS】Audio Session Programming Guide 翻譯 by kk(二)

定義一個音頻會話

音頻會話是 App 和 IOS 之間的媒介,用來為 App 配置相關(guān)的音頻屬性和行為。在加載過程中,App 會自動創(chuàng)建一個音頻會話的單例。開發(fā)者可以通過配置音頻會話來描述 App 對音頻的需求。比如:

  • 在 App 播放聲音的時候,開發(fā)者是想讓其他 App 的聲音停止還是和自己的聲音混合在一起?
  • 當(dāng)碰到系統(tǒng)鬧鐘或其他聲音響起的時候,App 中的聲音會作出什么反應(yīng)?
  • 當(dāng)用戶插拔耳機時, App 中的聲音功能會作出什么反應(yīng)?
    音頻會話的配置會影響 App 運行期間幾乎所有的音頻活動(除了通過系統(tǒng)聲音服務(wù) API 播放的 UI 音效)。開發(fā)者可以通過查詢音頻會話來獲取 App 運行設(shè)備上的硬件特性(頻道數(shù)、采樣率等)。這些硬件特性因設(shè)備而異, App 可以根據(jù)用戶行為改變這些特性。
    開發(fā)者可以顯式得開啟或關(guān)閉自己的音頻會話。開發(fā)者需要在 App 開始播放聲音或使用錄音功能之前開啟音頻會話。另外,系統(tǒng)可以在接到電話或鬧鐘響起時關(guān)閉App音頻會話,這種行為被稱為中斷(interruption)。音頻會話的 API 中提供了對中斷進行響應(yīng)和恢復(fù)的方法。

音頻會話的默認(rèn)行為

音頻會話具有如下的默認(rèn)行為:

  • 支持后臺播放,不支持錄音
  • 當(dāng)用戶將手機切換到靜音模式后,App 會被靜音。
  • 設(shè)備鎖屏后,App 會被靜音。
  • 當(dāng) App 的音頻開始后,設(shè)備正在播放的其他聲音會被靜音。

上述行為由默認(rèn)音頻會話類別 <code>AVAudioSessionCategorySoloAmbient</code> 提供。音頻會話類別(coming soon)介紹了如何在 App 內(nèi)使用類別。
盡管音頻會話會在 App 開始播放或錄制音頻時自動開啟,但這種默認(rèn)的開啟方式會帶來風(fēng)險。舉例來說,如果用戶使用 App 過程中有電話打入,而用戶選擇了拒接電話讓 App 繼續(xù)運行。如果沒有使用合理的后臺播放技術(shù),那 App 的聲音將不會再播放。下一章(coming soon)描述了一些處理這類問題的策略,處理中斷(coming soon)中有進一步的討論。

開發(fā)者可以在開發(fā)過程中使用這種默認(rèn)行為來提高開發(fā)效率。如果要發(fā)布 App ,那么只有在以下場景中才能安全的忽略音頻會話:

  • App 除了系統(tǒng)聲音服務(wù)(<code>System Sound Services</code>)或 <code>UIKit</code> 中的 <code>playInputClick</code> 方法外,不使用其他音頻 API 處理音頻。
    系統(tǒng)聲音服務(wù)是一種用來播放UI音效及觸發(fā)震動的IOS技術(shù),不適用于其他任何場景。詳情見System Sound Services Reference。
    UIKit 中的 <code>playInputClick</code> 方法允許開發(fā)者在特定的輸入或鍵盤輔助視圖(accessory view)中播放標(biāo)準(zhǔn)的鍵盤按鍵音。它的音頻會話行為由系統(tǒng)自動處理。詳情見Playing Input Clicks
  • App 不使用音頻

如果不滿足上述條件,一定不要在需要發(fā)布的 App 中使用默認(rèn)的音頻會話。

為什么通常情況下默認(rèn)的音頻會話不能滿足開發(fā)者的要求

如果開發(fā)者不對音頻會話進行初始化、配置和顯式調(diào)用,那么 App 就不能對中斷或音頻源的變化作出響應(yīng),也不能控制系統(tǒng)如何處理不同 App 間音頻的混合。
以下場景描述了音頻會話的默認(rèn)行為,以及開發(fā)者如何來改變它:

  1. 開發(fā)者開發(fā)了一款播放有聲書的 App。用戶開始聽《 The Merchant of Venice》,正當(dāng) Bassanio 大人要出場的時候,自動鎖屏的時間到了,屏幕變黑了,有聲書的音頻也被靜音了。
    為了避免鎖屏靜音這種情況,開發(fā)者需為音頻會話配置一個支持后臺播放的類別,同時要在 <code>UIBackgoundModes</code> 中添加 <code>audio</code> 標(biāo)志。
  2. 開發(fā)者開發(fā)了一款使用基于 OpenAL 音效的第一視角射擊類游戲。游戲中提供背景音樂,但也為用戶提供了關(guān)閉游戲背景音樂,并播發(fā)音樂庫中的音樂的功能。在選擇一曲激昂的音樂開始播放后,用戶朝著敵軍開了一槍,槍響了,用戶播放的音樂卻停了。
    為了保證用戶選擇的音樂能不被干擾的繼續(xù)播放,需要將音頻會話設(shè)置為允許混合的模式。開發(fā)者可以選擇 <code>AVAudioSessionCategoryAmbient</code>類別 ,也可以通過修改 <code>AVAudioSessionCategoryPlayback</code> 類別來支持混合。
  3. 開發(fā)者開發(fā)了一款使用音頻隊列服務(wù)(<code>Audio Queue Services</code>)進行后臺播放的流媒體電臺 App。正當(dāng)用戶在收聽的時候,電話來了,App 的聲音按照期望中的那樣停止了。用戶選擇了拒接這個電話,關(guān)閉了鬧鐘,然后點擊播放按鈕來繼續(xù)收聽,卻發(fā)現(xiàn)未能如愿。用戶必須重啟 App 才能恢復(fù)后臺播放。
    要優(yōu)雅的處理這種音頻隊列的中斷,開發(fā)者需要設(shè)置合適的類別,注冊 <code>AVAudioSessionInterruptionNotification</code> 通知,并讓 App 對不同的通知作出相應(yīng)的反應(yīng)。

系統(tǒng)怎樣解決音頻需求之間的競爭

在 App 啟動時,系統(tǒng)的內(nèi)置 App(短信、音樂、Safari、電話等)可能會在后臺運行。這些內(nèi)置的 App 可能會播放聲音,比如收到短信后。
如果將 IOS 設(shè)備看作一個飛機場,把 App 看作滑行的飛機,那么系統(tǒng)就是調(diào)度中心。飛機向調(diào)度中心發(fā)布一個使用聲音的請求,同時聲明它需要的優(yōu)先級,而最終何時獲得超過“正在跑道上”使用音頻的其他飛機的權(quán)限由調(diào)度中心決定。App 通過音頻會話來跟系統(tǒng)進行交互。下圖描述了一個典型的場景:你的 App 想要在音樂 App 正在播放音樂的時候播放聲音。這種情況下,你的 App 會中斷音樂 App。


圖片來自官方文檔

<small>
第一步,App 請求開啟它的音頻會話。這種請求可能會在 App 加載過程中產(chǎn)生,也可能會在響應(yīng)用戶點擊某個播放按鈕的事件中產(chǎn)生。第二步,系統(tǒng)開始處理這次請求。圖中的 SpeakHere App 使用了需要其他音頻靜音的類別。
在第三和第四步中,系統(tǒng)關(guān)閉了音樂 App 的音頻會話,停止了音樂在后臺的播放。最終,系統(tǒng)開啟了 SpeakHere 的音頻會話,SpeakHere 可以開始播放聲音了。
系統(tǒng)對于是否開啟或關(guān)閉設(shè)備上音頻會話有最終的決定權(quán)。決策過程中,電話總是擁有最高的優(yōu)先權(quán),沒有 App 的音頻權(quán)限能夠超過電話。接到電話后,無論當(dāng)前正在執(zhí)行什么音頻動作或是設(shè)置了哪種音頻類別,App 都會被中斷,用戶都會獲得“你接到了電話”的提醒。
</small>

集成 <code>AVCaptureSession</code>

<code>AV Foudation</code> 中的捕獲 API(<code>AVCaptureDevice</code>、<code>AVCaptureSession</code>)可以使開發(fā)者得到同步獲取來自相機或麥克風(fēng)的音頻或視頻輸入。在 IOS7 中,表示麥克風(fēng)輸入的 <code>AVCaptureDevice</code> 對象可以共享 App 的 <code>AVAudioSession</code>。默認(rèn)情況下,<code>AVCaptureSession</code> 會在使用麥克風(fēng)的時候會給 <code>AVAudioSession</code> 設(shè)置最適合錄音的配置。如果將 <code>automaticallyConfiguresApplicationAudioSession</code> 屬性設(shè)為 <code>NO</code>,這種默認(rèn)配置會被當(dāng)前開發(fā)者的AVAudioSession配置覆蓋,<code>AVCaptureDevice</code> 也會不加修改的采用開發(fā)者的配置。在 AVCaptureSession Class ReferenceMedia Capture 中可以獲得更多相關(guān)信息。

初始化音頻會話

系統(tǒng)在 App 的加載過程中提供了一個音頻會話的對象。在處理中斷之前,開發(fā)者必須初始化這個會話。
<code>AV Foundation</code> 框架會利用開發(fā)者獲取對 <code>AVAudioSession</code> 對象的引用時觸發(fā)的隱式初始化,來管理中斷。

//隱式初始化音頻會話
AVAudioSession *session = [AVAudioSession sharedInstance];

<code>session</code> 變量代表了一個已經(jīng)被初始化且可以馬上使用的音頻會話。官方推薦在使用 <code>AVAudioSession</code> 類中的中斷通知,或 <code>AVAudioPlayer</code> 和 <code>AVAudioRecorder</code> 的代理協(xié)議來處理音頻中斷時,隱式的初始化音頻會話。

添加音量和音頻源管理

<code>MPVolumeView</code> 類提供了在 App 中控制音量和音頻源的方法。音量視圖提供了一個控制音量的滑塊和一個選擇音頻輸出源的按鈕。官方建議在將音頻源切換到內(nèi)置揚聲器時,使用 <code>MPVolumeView</code> 的音頻源選擇器(route picker)而不是 <code>AVAudioSessionPortOverride</code>。詳情見 MPVolumeView Class Reference。

響應(yīng)遙控器事件

用戶可以通過遙控器事件來控制 App 中的多媒體。開發(fā)者可能希望 App 中播放的音頻或視頻內(nèi)容對來自 transport controls 或外接輔助設(shè)備的遙控事件做出響應(yīng)。IOS 將這些命令轉(zhuǎn)化為 <code>UIEvent</code> 對象分發(fā)給 App。App 接收到事件后將它們發(fā)送給對應(yīng)的 first responder。如果 first responder 不對事件進行處理,那么事件將會在 responder 鏈中向上傳遞。
只有正在播放音頻、且具有“Now Playing”信息的 app,才能對遙控器事件做出響應(yīng)。詳情見 Remote Control EventsMPNowPlayingInfoCenter Class Reference。

開啟或關(guān)閉音頻會話

雖然系統(tǒng)在 App 加載的時候自動開啟你的音頻會話,但蘋果官方推薦的做法是在 App 的 <code>viewDidLoad</code> 方法中顯式的開啟,并在開啟前設(shè)置合適的硬件參數(shù)。為 App 進行硬件優(yōu)化 中有相關(guān)的示例代碼。通過這種方式,開發(fā)者可以測試音頻會話是否成功開啟。如果 App 中包含一個類似播放/暫停的 UI 元素,在用戶按下播放鍵時開啟會話則是更好的方式。在切換音頻會話的開啟/關(guān)閉狀態(tài)時,對是否成功的切換了會話狀態(tài)做出檢查是有必要的。開發(fā)者應(yīng)在代碼中對系統(tǒng)駁回的請求進行優(yōu)雅的處理。
系統(tǒng)會在鬧鐘提醒、日歷提醒或接到電話時將關(guān)閉你的音頻會話。當(dāng)用戶關(guān)閉提醒或拒接電話后,系統(tǒng)會允許你重新開啟會話。在中斷結(jié)束后是否重新開啟音頻會話由 App 的類型決定,[Audio Guidelines By App Type](Audio Guidelines By App Type.md) 中有相關(guān)的介紹。
下面的代碼展示了如何開啟音頻會話。

NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) { /* handle the error in activationError */ }

將 <code>setActive</code> 的參數(shù)設(shè)置為 <code>NO</code> 可以關(guān)閉會話。
如果使用 <code>AVAudioPlayer</code> 或 <code>AVAudioRecorder</code> 來播放或錄制音頻時,系統(tǒng)會負(fù)責(zé)在中斷結(jié)束后重新開啟音頻會話。然而,官方推薦通過注冊消息通知來顯式開啟會話,來保證會話成功開啟并對 App 的狀態(tài)和 UI 進行更新。
大多數(shù) App 不需要顯式的關(guān)閉音頻會話。一些需要顯式關(guān)閉的特例包括 VoIP(網(wǎng)絡(luò)電話)App、逐向(在轉(zhuǎn)彎時對用戶做出提醒)導(dǎo)航 App 和某些錄音 App。
對于一般在后臺運行網(wǎng)絡(luò)電話 App,要保證它的音頻會話在處理通話時是開啟的,而在后臺準(zhǔn)備接收通話時則處于關(guān)閉狀態(tài)。
對于使用錄音類別的 App 的音頻會話僅在錄音時開啟。在錄音開始前和錄音結(jié)束后要關(guān)閉會話以保證類似短信提示音的其他聲音能夠順利播放。

App加載時檢查是否存在正在播放的其他音頻

在用戶打開 App 時,設(shè)備可能正在播放其他的聲音:音樂 App 可能正在播放音樂,或者瀏覽器正在播放流媒體。這種情況產(chǎn)生的影響對于游戲 App 來說可能更為顯著。許多游戲會有自己的背景音樂和音效。IOS Human Interface Guidelines 建議開發(fā)者假定用戶在玩游戲時希望保持他們原來播放的音頻作為背景音樂,同時保留游戲的音效。
檢查 <code>otherAudioPlaying</code> 的屬性值來判斷 App 加載過程中是否正在播放其他音頻;如果是的話,將游戲的背景音樂靜音,并使用 <code>AVAudioSessionCategorySoloAmbient</code> 類別。詳情見音頻會話類別(coming soon)。

跨 App 音頻(Inter-App Audio)

跨 App 音頻的最基礎(chǔ)的使用形式是通過一個節(jié)點(node) app 將它的音頻輸出到另一個寄主(host) App。寄主 App 可能會將它的輸出發(fā)送給節(jié)點 App,經(jīng)過節(jié)點 App 的處理后,將處理結(jié)果反饋給寄主 App。寄主 App 需要一個始終處于開啟狀態(tài)的音頻會話,而節(jié)點 App 只需要在從寄主 App 或系統(tǒng)接收音頻輸入時開啟音頻會話。根據(jù)以下方案來設(shè)置跨 App 的音頻:

  • 為寄主 App 和節(jié)點 App 設(shè)置“inter-app-audio”權(quán)限
  • 為寄主 App 在 <code>UIBackgoundModes</code> 添加 <code>audio</code> 屬性。
  • 為使用音頻輸入輸出源、同時連接到跨 App 音頻寄主的節(jié)點 app 的 <code>UIBackgoundModes</code> 添加 <code>audio</code> 屬性。
  • 將寄主和節(jié)點 App 的類別設(shè)為 <code> AVAudioSessionCategoryOptionMixWithOthers</code>。
  • 對于連接到寄主的節(jié)點 App,保證它的音頻會話在收到系統(tǒng)的音頻輸入或產(chǎn)生音頻輸出時處于開啟狀態(tài)。

以上內(nèi)容翻譯自蘋果官方文檔,僅供學(xué)習(xí),請勿用于商業(yè)用途,侵刪。轉(zhuǎn)載注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容