定義一個(gè)音頻會(huì)話
音頻會(huì)話是 App 和 IOS 之間的媒介,用來為 App 配置相關(guān)的音頻屬性和行為。在加載過程中,App 會(huì)自動(dòng)創(chuàng)建一個(gè)音頻會(huì)話的單例。開發(fā)者可以通過配置音頻會(huì)話來描述 App 對(duì)音頻的需求。比如:
- 在 App 播放聲音的時(shí)候,開發(fā)者是想讓其他 App 的聲音停止還是和自己的聲音混合在一起?
- 當(dāng)碰到系統(tǒng)鬧鐘或其他聲音響起的時(shí)候,App 中的聲音會(huì)作出什么反應(yīng)?
- 當(dāng)用戶插拔耳機(jī)時(shí), App 中的聲音功能會(huì)作出什么反應(yīng)?
音頻會(huì)話的配置會(huì)影響 App 運(yùn)行期間幾乎所有的音頻活動(dòng)(除了通過系統(tǒng)聲音服務(wù) API 播放的 UI 音效)。開發(fā)者可以通過查詢音頻會(huì)話來獲取 App 運(yùn)行設(shè)備上的硬件特性(頻道數(shù)、采樣率等)。這些硬件特性因設(shè)備而異, App 可以根據(jù)用戶行為改變這些特性。
開發(fā)者可以顯式得開啟或關(guān)閉自己的音頻會(huì)話。開發(fā)者需要在 App 開始播放聲音或使用錄音功能之前開啟音頻會(huì)話。另外,系統(tǒng)可以在接到電話或鬧鐘響起時(shí)關(guān)閉App音頻會(huì)話,這種行為被稱為中斷(interruption)。音頻會(huì)話的 API 中提供了對(duì)中斷進(jìn)行響應(yīng)和恢復(fù)的方法。
音頻會(huì)話的默認(rèn)行為
音頻會(huì)話具有如下的默認(rèn)行為:
- 支持后臺(tái)播放,不支持錄音
- 當(dāng)用戶將手機(jī)切換到靜音模式后,App 會(huì)被靜音。
- 設(shè)備鎖屏后,App 會(huì)被靜音。
- 當(dāng) App 的音頻開始后,設(shè)備正在播放的其他聲音會(huì)被靜音。
上述行為由默認(rèn)音頻會(huì)話類別 <code>AVAudioSessionCategorySoloAmbient</code> 提供。音頻會(huì)話類別(coming soon)介紹了如何在 App 內(nèi)使用類別。
盡管音頻會(huì)話會(huì)在 App 開始播放或錄制音頻時(shí)自動(dòng)開啟,但這種默認(rèn)的開啟方式會(huì)帶來風(fēng)險(xiǎn)。舉例來說,如果用戶使用 App 過程中有電話打入,而用戶選擇了拒接電話讓 App 繼續(xù)運(yùn)行。如果沒有使用合理的后臺(tái)播放技術(shù),那 App 的聲音將不會(huì)再播放。下一章(coming soon)描述了一些處理這類問題的策略,處理中斷(coming soon)中有進(jìn)一步的討論。
開發(fā)者可以在開發(fā)過程中使用這種默認(rèn)行為來提高開發(fā)效率。如果要發(fā)布 App ,那么只有在以下場(chǎng)景中才能安全的忽略音頻會(huì)話:
- App 除了系統(tǒng)聲音服務(wù)(<code>System Sound Services</code>)或 <code>UIKit</code> 中的 <code>playInputClick</code> 方法外,不使用其他音頻 API 處理音頻。
系統(tǒng)聲音服務(wù)是一種用來播放UI音效及觸發(fā)震動(dòng)的IOS技術(shù),不適用于其他任何場(chǎng)景。詳情見System Sound Services Reference。
UIKit 中的 <code>playInputClick</code> 方法允許開發(fā)者在特定的輸入或鍵盤輔助視圖(accessory view)中播放標(biāo)準(zhǔn)的鍵盤按鍵音。它的音頻會(huì)話行為由系統(tǒng)自動(dòng)處理。詳情見Playing Input Clicks - App 不使用音頻
如果不滿足上述條件,一定不要在需要發(fā)布的 App 中使用默認(rèn)的音頻會(huì)話。
為什么通常情況下默認(rèn)的音頻會(huì)話不能滿足開發(fā)者的要求
如果開發(fā)者不對(duì)音頻會(huì)話進(jìn)行初始化、配置和顯式調(diào)用,那么 App 就不能對(duì)中斷或音頻源的變化作出響應(yīng),也不能控制系統(tǒng)如何處理不同 App 間音頻的混合。
以下場(chǎng)景描述了音頻會(huì)話的默認(rèn)行為,以及開發(fā)者如何來改變它:
- 開發(fā)者開發(fā)了一款播放有聲書的 App。用戶開始聽《 The Merchant of Venice》,正當(dāng) Bassanio 大人要出場(chǎng)的時(shí)候,自動(dòng)鎖屏的時(shí)間到了,屏幕變黑了,有聲書的音頻也被靜音了。
為了避免鎖屏靜音這種情況,開發(fā)者需為音頻會(huì)話配置一個(gè)支持后臺(tái)播放的類別,同時(shí)要在 <code>UIBackgoundModes</code> 中添加 <code>audio</code> 標(biāo)志。 - 開發(fā)者開發(fā)了一款使用基于 OpenAL 音效的第一視角射擊類游戲。游戲中提供背景音樂,但也為用戶提供了關(guān)閉游戲背景音樂,并播發(fā)音樂庫(kù)中的音樂的功能。在選擇一曲激昂的音樂開始播放后,用戶朝著敵軍開了一槍,槍響了,用戶播放的音樂卻停了。
為了保證用戶選擇的音樂能不被干擾的繼續(xù)播放,需要將音頻會(huì)話設(shè)置為允許混合的模式。開發(fā)者可以選擇 <code>AVAudioSessionCategoryAmbient</code>類別 ,也可以通過修改 <code>AVAudioSessionCategoryPlayback</code> 類別來支持混合。 - 開發(fā)者開發(fā)了一款使用音頻隊(duì)列服務(wù)(<code>Audio Queue Services</code>)進(jìn)行后臺(tái)播放的流媒體電臺(tái) App。正當(dāng)用戶在收聽的時(shí)候,電話來了,App 的聲音按照期望中的那樣停止了。用戶選擇了拒接這個(gè)電話,關(guān)閉了鬧鐘,然后點(diǎn)擊播放按鈕來繼續(xù)收聽,卻發(fā)現(xiàn)未能如愿。用戶必須重啟 App 才能恢復(fù)后臺(tái)播放。
要優(yōu)雅的處理這種音頻隊(duì)列的中斷,開發(fā)者需要設(shè)置合適的類別,注冊(cè) <code>AVAudioSessionInterruptionNotification</code> 通知,并讓 App 對(duì)不同的通知作出相應(yīng)的反應(yīng)。
系統(tǒng)怎樣解決音頻需求之間的競(jìng)爭(zhēng)
在 App 啟動(dòng)時(shí),系統(tǒng)的內(nèi)置 App(短信、音樂、Safari、電話等)可能會(huì)在后臺(tái)運(yùn)行。這些內(nèi)置的 App 可能會(huì)播放聲音,比如收到短信后。
如果將 IOS 設(shè)備看作一個(gè)飛機(jī)場(chǎng),把 App 看作滑行的飛機(jī),那么系統(tǒng)就是調(diào)度中心。飛機(jī)向調(diào)度中心發(fā)布一個(gè)使用聲音的請(qǐng)求,同時(shí)聲明它需要的優(yōu)先級(jí),而最終何時(shí)獲得超過“正在跑道上”使用音頻的其他飛機(jī)的權(quán)限由調(diào)度中心決定。App 通過音頻會(huì)話來跟系統(tǒng)進(jìn)行交互。下圖描述了一個(gè)典型的場(chǎng)景:你的 App 想要在音樂 App 正在播放音樂的時(shí)候播放聲音。這種情況下,你的 App 會(huì)中斷音樂 App。
<small>
第一步,App 請(qǐng)求開啟它的音頻會(huì)話。這種請(qǐng)求可能會(huì)在 App 加載過程中產(chǎn)生,也可能會(huì)在響應(yīng)用戶點(diǎn)擊某個(gè)播放按鈕的事件中產(chǎn)生。第二步,系統(tǒng)開始處理這次請(qǐng)求。圖中的 SpeakHere App 使用了需要其他音頻靜音的類別。
在第三和第四步中,系統(tǒng)關(guān)閉了音樂 App 的音頻會(huì)話,停止了音樂在后臺(tái)的播放。最終,系統(tǒng)開啟了 SpeakHere 的音頻會(huì)話,SpeakHere 可以開始播放聲音了。
系統(tǒng)對(duì)于是否開啟或關(guān)閉設(shè)備上音頻會(huì)話有最終的決定權(quán)。決策過程中,電話總是擁有最高的優(yōu)先權(quán),沒有 App 的音頻權(quán)限能夠超過電話。接到電話后,無(wú)論當(dāng)前正在執(zhí)行什么音頻動(dòng)作或是設(shè)置了哪種音頻類別,App 都會(huì)被中斷,用戶都會(huì)獲得“你接到了電話”的提醒。
</small>
集成 <code>AVCaptureSession</code>
<code>AV Foudation</code> 中的捕獲 API(<code>AVCaptureDevice</code>、<code>AVCaptureSession</code>)可以使開發(fā)者得到同步獲取來自相機(jī)或麥克風(fēng)的音頻或視頻輸入。在 IOS7 中,表示麥克風(fēng)輸入的 <code>AVCaptureDevice</code> 對(duì)象可以共享 App 的 <code>AVAudioSession</code>。默認(rèn)情況下,<code>AVCaptureSession</code> 會(huì)在使用麥克風(fēng)的時(shí)候會(huì)給 <code>AVAudioSession</code> 設(shè)置最適合錄音的配置。如果將 <code>automaticallyConfiguresApplicationAudioSession</code> 屬性設(shè)為 <code>NO</code>,這種默認(rèn)配置會(huì)被當(dāng)前開發(fā)者的AVAudioSession配置覆蓋,<code>AVCaptureDevice</code> 也會(huì)不加修改的采用開發(fā)者的配置。在 AVCaptureSession Class Reference 和 Media Capture 中可以獲得更多相關(guān)信息。
初始化音頻會(huì)話
系統(tǒng)在 App 的加載過程中提供了一個(gè)音頻會(huì)話的對(duì)象。在處理中斷之前,開發(fā)者必須初始化這個(gè)會(huì)話。
<code>AV Foundation</code> 框架會(huì)利用開發(fā)者獲取對(duì) <code>AVAudioSession</code> 對(duì)象的引用時(shí)觸發(fā)的隱式初始化,來管理中斷。
//隱式初始化音頻會(huì)話
AVAudioSession *session = [AVAudioSession sharedInstance];
<code>session</code> 變量代表了一個(gè)已經(jīng)被初始化且可以馬上使用的音頻會(huì)話。官方推薦在使用 <code>AVAudioSession</code> 類中的中斷通知,或 <code>AVAudioPlayer</code> 和 <code>AVAudioRecorder</code> 的代理協(xié)議來處理音頻中斷時(shí),隱式的初始化音頻會(huì)話。
添加音量和音頻源管理
<code>MPVolumeView</code> 類提供了在 App 中控制音量和音頻源的方法。音量視圖提供了一個(gè)控制音量的滑塊和一個(gè)選擇音頻輸出源的按鈕。官方建議在將音頻源切換到內(nèi)置揚(yáng)聲器時(shí),使用 <code>MPVolumeView</code> 的音頻源選擇器(route picker)而不是 <code>AVAudioSessionPortOverride</code>。詳情見 MPVolumeView Class Reference。
響應(yīng)遙控器事件
用戶可以通過遙控器事件來控制 App 中的多媒體。開發(fā)者可能希望 App 中播放的音頻或視頻內(nèi)容對(duì)來自 transport controls 或外接輔助設(shè)備的遙控事件做出響應(yīng)。IOS 將這些命令轉(zhuǎn)化為 <code>UIEvent</code> 對(duì)象分發(fā)給 App。App 接收到事件后將它們發(fā)送給對(duì)應(yīng)的 first responder。如果 first responder 不對(duì)事件進(jìn)行處理,那么事件將會(huì)在 responder 鏈中向上傳遞。
只有正在播放音頻、且具有“Now Playing”信息的 app,才能對(duì)遙控器事件做出響應(yīng)。詳情見 Remote Control Events和MPNowPlayingInfoCenter Class Reference。
開啟或關(guān)閉音頻會(huì)話
雖然系統(tǒng)在 App 加載的時(shí)候自動(dòng)開啟你的音頻會(huì)話,但蘋果官方推薦的做法是在 App 的 <code>viewDidLoad</code> 方法中顯式的開啟,并在開啟前設(shè)置合適的硬件參數(shù)。為 App 進(jìn)行硬件優(yōu)化 中有相關(guān)的示例代碼。通過這種方式,開發(fā)者可以測(cè)試音頻會(huì)話是否成功開啟。如果 App 中包含一個(gè)類似播放/暫停的 UI 元素,在用戶按下播放鍵時(shí)開啟會(huì)話則是更好的方式。在切換音頻會(huì)話的開啟/關(guān)閉狀態(tài)時(shí),對(duì)是否成功的切換了會(huì)話狀態(tài)做出檢查是有必要的。開發(fā)者應(yīng)在代碼中對(duì)系統(tǒng)駁回的請(qǐng)求進(jìn)行優(yōu)雅的處理。
系統(tǒng)會(huì)在鬧鐘提醒、日歷提醒或接到電話時(shí)將關(guān)閉你的音頻會(huì)話。當(dāng)用戶關(guān)閉提醒或拒接電話后,系統(tǒng)會(huì)允許你重新開啟會(huì)話。在中斷結(jié)束后是否重新開啟音頻會(huì)話由 App 的類型決定,[Audio Guidelines By App Type](Audio Guidelines By App Type.md) 中有相關(guān)的介紹。
下面的代碼展示了如何開啟音頻會(huì)話。
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)閉會(huì)話。
如果使用 <code>AVAudioPlayer</code> 或 <code>AVAudioRecorder</code> 來播放或錄制音頻時(shí),系統(tǒng)會(huì)負(fù)責(zé)在中斷結(jié)束后重新開啟音頻會(huì)話。然而,官方推薦通過注冊(cè)消息通知來顯式開啟會(huì)話,來保證會(huì)話成功開啟并對(duì) App 的狀態(tài)和 UI 進(jìn)行更新。
大多數(shù) App 不需要顯式的關(guān)閉音頻會(huì)話。一些需要顯式關(guān)閉的特例包括 VoIP(網(wǎng)絡(luò)電話)App、逐向(在轉(zhuǎn)彎時(shí)對(duì)用戶做出提醒)導(dǎo)航 App 和某些錄音 App。
對(duì)于一般在后臺(tái)運(yùn)行網(wǎng)絡(luò)電話 App,要保證它的音頻會(huì)話在處理通話時(shí)是開啟的,而在后臺(tái)準(zhǔn)備接收通話時(shí)則處于關(guān)閉狀態(tài)。
對(duì)于使用錄音類別的 App 的音頻會(huì)話僅在錄音時(shí)開啟。在錄音開始前和錄音結(jié)束后要關(guān)閉會(huì)話以保證類似短信提示音的其他聲音能夠順利播放。
App加載時(shí)檢查是否存在正在播放的其他音頻
在用戶打開 App 時(shí),設(shè)備可能正在播放其他的聲音:音樂 App 可能正在播放音樂,或者瀏覽器正在播放流媒體。這種情況產(chǎn)生的影響對(duì)于游戲 App 來說可能更為顯著。許多游戲會(huì)有自己的背景音樂和音效。IOS Human Interface Guidelines 建議開發(fā)者假定用戶在玩游戲時(shí)希望保持他們?cè)瓉聿シ诺囊纛l作為背景音樂,同時(shí)保留游戲的音效。
檢查 <code>otherAudioPlaying</code> 的屬性值來判斷 App 加載過程中是否正在播放其他音頻;如果是的話,將游戲的背景音樂靜音,并使用 <code>AVAudioSessionCategorySoloAmbient</code> 類別。詳情見音頻會(huì)話類別(coming soon)。
跨 App 音頻(Inter-App Audio)
跨 App 音頻的最基礎(chǔ)的使用形式是通過一個(gè)節(jié)點(diǎn)(node) app 將它的音頻輸出到另一個(gè)寄主(host) App。寄主 App 可能會(huì)將它的輸出發(fā)送給節(jié)點(diǎn) App,經(jīng)過節(jié)點(diǎn) App 的處理后,將處理結(jié)果反饋給寄主 App。寄主 App 需要一個(gè)始終處于開啟狀態(tài)的音頻會(huì)話,而節(jié)點(diǎn) App 只需要在從寄主 App 或系統(tǒng)接收音頻輸入時(shí)開啟音頻會(huì)話。根據(jù)以下方案來設(shè)置跨 App 的音頻:
- 為寄主 App 和節(jié)點(diǎn) App 設(shè)置“inter-app-audio”權(quán)限
- 為寄主 App 在 <code>UIBackgoundModes</code> 添加 <code>audio</code> 屬性。
- 為使用音頻輸入輸出源、同時(shí)連接到跨 App 音頻寄主的節(jié)點(diǎn) app 的 <code>UIBackgoundModes</code> 添加 <code>audio</code> 屬性。
- 將寄主和節(jié)點(diǎn) App 的類別設(shè)為 <code> AVAudioSessionCategoryOptionMixWithOthers</code>。
- 對(duì)于連接到寄主的節(jié)點(diǎn) App,保證它的音頻會(huì)話在收到系統(tǒng)的音頻輸入或產(chǎn)生音頻輸出時(shí)處于開啟狀態(tài)。
以上內(nèi)容翻譯自蘋果官方文檔,僅供學(xué)習(xí),請(qǐng)勿用于商業(yè)用途,侵刪。轉(zhuǎn)載注明出處。