@[TOC](IOS音視頻(四十三)AVFoundation 之 Audio Session)
1.音頻會話概述
音頻是iOS、tvOS和watchOS中的托管服務(wù)。系統(tǒng)通過使用音頻會話來管理應(yīng)用程序桃移、應(yīng)用程序間和設(shè)備級別上的音頻行為胀蛮。
你使用一個音頻會話來與系統(tǒng)溝通,告訴系統(tǒng)你打算如何在你的應(yīng)用程序中使用音頻渐白。這個音頻會話充當(dāng)了你的應(yīng)用程序和操作系統(tǒng)之間的媒介尊浓,反過來,也就是底層音頻硬件之間的媒介纯衍。你可以使用它來與操作系統(tǒng)交流應(yīng)用程序音頻的性質(zhì)栋齿,而無需詳細說明具體的行為或與音頻硬件的必要交互。將這些細節(jié)的管理委托給音頻會話可以確保對用戶音頻體驗的最佳管理襟诸。
你與你的應(yīng)用程序的音頻會話使用AVAudioSession的一個實例:
- 配置音頻會話類別和模式瓦堵,以向系統(tǒng)傳達您打算如何在應(yīng)用程序中使用音頻
- 激活你的應(yīng)用程序的音頻會話,把你的類別和模式配置到行動
- 訂閱和響應(yīng)重要的音頻會話通知歌亲,如音頻中斷和路由更改
- 執(zhí)行高級音頻設(shè)備配置菇用,如設(shè)置采樣速率、I/O緩沖區(qū)持續(xù)時間和通道數(shù)量
音頻會話管理音頻行為
音頻會話是應(yīng)用程序和操作系統(tǒng)之間的中介陷揪,用于配置應(yīng)用程序的音頻行為惋鸥。在啟動時,你的應(yīng)用程序會自動提供一個單例音頻會話鹅龄。您可以配置它來提供所需的行為,并激活它來將該行為轉(zhuǎn)化為操作亭畜。類別表示音頻角色
表達音頻行為的主要機制是音頻會話類別扮休。通過設(shè)置類別,您可以指示應(yīng)用程序是否使用輸入或輸出路徑拴鸵,是否希望音樂與音頻一起繼續(xù)播放玷坠,等等。你指定的行為應(yīng)該滿足用戶的期望劲藐,正如在iOS人機界面指南音頻中描述的那樣八堡。
AVFoundation定義了許多音頻會話類別,以及一組覆蓋和修改開關(guān)聘芜,允許您根據(jù)應(yīng)用程序的個性或角色自定義音頻行為兄渺。各種類別支持回放、錄制和帶有錄制的回放汰现。當(dāng)系統(tǒng)知道您的應(yīng)用程序的音頻角色時挂谍,它將為您提供對硬件資源的適當(dāng)訪問。該系統(tǒng)還確保設(shè)備上的其他音頻以適合您的應(yīng)用程序的方式工作瞎饲,并符合用戶的期望口叙。
某些類別還可以通過指定用于專門化給定類別的行為的模式來進一步定制。例如嗅战,當(dāng)應(yīng)用程序使用視頻錄制模式時妄田,系統(tǒng)可能會選擇不同于使用默認模式時的內(nèi)置麥克風(fēng)。該系統(tǒng)還可能采用為視頻錄制用例進行調(diào)優(yōu)的麥克風(fēng)信號處理。
通知支持中斷處理
音頻中斷是應(yīng)用程序的音頻會話的停用疟呐,它會立即停止音頻脚曾。當(dāng)來自應(yīng)用程序的競爭音頻會話被激活,并且該會話未被系統(tǒng)分類以與您的會話混合時萨醒,就會出現(xiàn)中斷斟珊。您的應(yīng)用程序應(yīng)該通過保存狀態(tài)、更新用戶界面等方式來響應(yīng)中斷富纸。要在音頻中斷開始和結(jié)束時得到通知囤踩,請注冊以觀察AVAudioSessionInterruptionNotification類型的通知。通知支持音頻路由更改處理
當(dāng)用戶通過連接或斷開設(shè)備晓褪、插入或斷開耳機來啟動音頻路由更改時堵漱,他們有特定的期望。iOS人機界面指南描述了這些期望涣仿,并提供了如何滿足這些期望的指南勤庐。通過注冊來觀察AVAudioSessionRouteChangeNotification類型的通知來處理路由更改。音頻會話控制設(shè)備配置
應(yīng)用程序不能直接控制設(shè)備硬件好港,但音頻會話為您提供了請求首選硬件設(shè)備設(shè)置的接口愉镰。這個接口使您能夠執(zhí)行高級音頻設(shè)備配置,如設(shè)置采樣速率钧汹、I/O緩沖區(qū)持續(xù)時間和音頻通道的數(shù)量丈探。音頻會話保護用戶隱私
單獨或與視頻一起錄制音頻的應(yīng)用程序,在允許錄制之前拔莱,需要明確的用戶許可碗降。在用戶授予你的應(yīng)用錄制權(quán)限之前,應(yīng)用只能錄制靜音塘秦。AVAudioSession提供了請求此權(quán)限并確定用戶隱私設(shè)置的接口讼渊。
2. 配置音頻會話
音頻會話類別是一個鍵,用于識別應(yīng)用程序的一組音頻行為尊剔。通過設(shè)置一個類別爪幻,您可以向系統(tǒng)表明您的音頻意圖——例如,當(dāng)鈴聲/靜音開關(guān)翻轉(zhuǎn)時须误,音頻是否應(yīng)該繼續(xù)笔咽。幾個音頻會話類別,以及一組覆蓋和修改開關(guān)霹期,讓您自定義您的應(yīng)用程序的音頻行為叶组。
如表B-1所示,每個音頻會話類別都指定了一組特定的響應(yīng)历造,以響應(yīng)下列每個行為:
- 中斷非混合應(yīng)用程序音頻:如果是甩十,當(dāng)你的應(yīng)用程序激活其音頻會話時船庇,非混合應(yīng)用程序被中斷。
- 靜音開關(guān)靜音:如果是侣监,當(dāng)用戶激活靜音開關(guān)時鸭轮,您的音頻將被靜音。(在iPhone上橄霉,這個開關(guān)叫做鈴聲/靜音開關(guān)窃爷。)
- 支持音頻輸入:如果支持,則允許app音頻輸入(錄制)姓蜂。
- 支持音頻輸出:如果是按厘,應(yīng)用程序音頻輸出(播放)是允許的。
大多數(shù)應(yīng)用程序只需要在啟動時設(shè)置類別一次钱慢,但你可以根據(jù)需要隨時更改類別逮京。你可以改變它,而音頻會話是積極的;然而束莫,在更改類別或其他會話屬性之前懒棉,通常更可取的做法是禁用音頻會話。在會話被停用時進行這些更改可以防止音頻系統(tǒng)進行不必要的重新配置览绿。
2.1 音頻會話默認行為
所有的iOS策严、tvOS和watchOS應(yīng)用程序都有一個預(yù)設(shè)的默認音頻會話,如下所示:
- 支持音頻播放饿敲,但不允許錄制音頻妻导。
- 在iOS系統(tǒng)中,將鈴聲/靜音開關(guān)設(shè)置為靜音模式诀蓉,應(yīng)用程序播放的任何音頻都會被靜音栗竖。
- 在iOS系統(tǒng)中暑脆,當(dāng)設(shè)備被鎖定時渠啤,應(yīng)用程序的音頻會被靜音。
- 當(dāng)您的應(yīng)用程序播放音頻時添吗,任何其他背景音頻(如音樂應(yīng)用程序播放的音頻)都將被靜音沥曹。
默認的音頻會話具有有用的行為,但在大多數(shù)情況下碟联,您應(yīng)該自定義它以更好地滿足您的應(yīng)用程序的需要妓美。要更改行為,需要配置應(yīng)用程序的音頻會話鲤孵。
2.2 配置音頻會話
配置音頻會話的主要方法是設(shè)置其類別壶栋。音頻會話類別定義了一組音頻行為。與每個類別相關(guān)的精確行為并不在應(yīng)用程序的控制之下普监,而是由操作系統(tǒng)設(shè)置的贵试。蘋果可能會在未來的操作系統(tǒng)版本中完善類別行為琉兜,所以你最好的策略是選擇最準(zhǔn)確地描述你想要的音頻行為意圖的類別。音頻會話類別和模式總結(jié)了每個類別的行為細節(jié)毙玻。
雖然類別設(shè)置了應(yīng)用程序的基本音頻行為豌蟋,但您可以通過設(shè)置類別的模式進一步專門化這些行為。例如桑滩,IP語音(VoIP)應(yīng)用程序?qū)⑹褂?a target="_blank">AVAudioSessionCategoryPlayAndRecord梧疲。您可以通過將音頻會話模式設(shè)置為AVAudioSessionModeVoiceChat來專門化VoIP應(yīng)用程序的此類行為。這種模式確保通過系統(tǒng)提供的信號處理來優(yōu)化語音信號运准。
AVAudioSessionCategoryPlayAndRecord模式如下:
static let playAndRecord: AVAudioSession.Category
- 您的音頻繼續(xù)與靜音開關(guān)設(shè)置為靜音和與屏幕鎖定幌氮。(這個開關(guān)在iPhone上被稱為鈴聲/靜音開關(guān)。)為了在你的應(yīng)用程序轉(zhuǎn)換到背景時(例如戳吝,當(dāng)屏幕鎖定時)繼續(xù)播放音頻浩销,在你的信息屬性列表文件的UIBackgroundModes鍵中添加音頻值。
- 這個類別適用于同時錄制和回放听哭,也適用于錄制和回放但不是同時播放的應(yīng)用程序慢洋。
- 默認情況下,使用這個類別意味著你的應(yīng)用程序的音頻是不可混合的——激活你的會話將中斷任何其他不可混合的音頻會話陆盘。要允許混合此類別普筹,請使用mixWithOthers選項。
- 用戶必須為音頻錄制授予權(quán)限(參見錄制需要用戶權(quán)限)隘马。
這個類別支持Airplay的鏡像版本太防。然而,如果AVAudioSessionModeVoiceChat模式用于此類別酸员,則AirPlay鏡像將被禁用蜒车。- 某些類別支持通過在會話上設(shè)置一個或多個類別選項來覆蓋其默認行為(參見AVAudioSessionCategoryOptions)。例如幔嗦,當(dāng)會話被激活時酿愧,與AVAudioSessionCategoryPlayback類別相關(guān)聯(lián)的默認行為會中斷其他系統(tǒng)音頻。在大多數(shù)情況下邀泉,回放應(yīng)用程序需要這種行為嬉挡。但是,如果您希望您的音頻與其他系統(tǒng)音頻混合汇恤,您可以通過在會話中設(shè)置AVAudioSessionCategoryOptionMixWithOthers選項來覆蓋此行為庞钢。
-
音頻會話模式有:
音頻會話模式有
注意:當(dāng)鈴聲/靜音開關(guān)設(shè)置為靜音并鎖定屏幕時,為了讓你的應(yīng)用程序繼續(xù)播放音頻因谎,請確保UIBackgroundModes音頻鍵已添加到你的應(yīng)用程序的信息中基括。plist文件。這個要求是除了你使用正確的類別财岔。
-
模式及相關(guān)類別:
模式及相關(guān)類別
要設(shè)置音頻會話類別(以及可選的模式和選項)风皿,請調(diào)用setCategory:mode:options:error: method饭冬,使用AVFoundation框架設(shè)置音頻會話類別代碼如下:
// Access the shared, singleton audio session instance
let session = AVAudioSession.sharedInstance()
do {
// Configure the audio session for movie playback
try session.setCategory(AVAudioSessionCategoryPlayback,
mode: AVAudioSessionModeMoviePlayback,
options: [])
} catch let error as NSError {
print("Failed to set the audio session category and mode: \(error.localizedDescription)")
}
2.3 使用多路由類別擴展選項
多路由類別的工作方式與其他類別略有不同。所有其他類別的設(shè)備都遵循“最后入網(wǎng)”規(guī)則揪阶,即最后插入輸入或輸出路由的設(shè)備是占主導(dǎo)地位的設(shè)備昌抠。但是,多路由類允許應(yīng)用程序使用所有連接的輸出端口鲁僚,而不是僅使用最后一個端口炊苫。例如,如果您正在通過HDMI輸出路徑聽音頻冰沙,并插入一組耳機侨艾,您的應(yīng)用程序?qū)⒗^續(xù)通過HDMI輸出路徑播放音頻,同時也通過耳機播放音頻拓挥。
使用multiroute類別唠梨,您的應(yīng)用程序還可以將不同的音頻流發(fā)送到不同的輸出路徑。例如侥啤,您的應(yīng)用程序可以將一個音頻流發(fā)送到左側(cè)耳機当叭,另一個發(fā)送到右側(cè)耳機,第三個發(fā)送到HDMI路由盖灸。圖1-1顯示了將多個音頻流發(fā)送到不同音頻路由的示例蚁鳖。
根據(jù)設(shè)備和任何連接的附件,以下是有效的輸出路徑組合:
- USB和耳機
- HDMI和耳機
- 列隊爭球和耳機
多路由類別支持使用單個輸入端口赁炎。
重要提示:只有在沒有其他合適的輸出端口(USB醉箕、HDMI、LineOut)連接時才能使用內(nèi)置揚聲器徙垫。
2.4 為AirPlay選擇類別和模式
只有特定的類別和模式支持AirPlay讥裤。以下類別同時支持鏡像和非鏡像版本的AirPlay:
AVAudioSessionCategorySoloAmbient
AVAudioSessionCategoryAmbient
AVAudioSessionCategoryPlayback
AVAudioSessionCategoryPlayAndRecord類別和以下模式只支持鏡像版本的AirPlay:
AVAudioSessionModeDefault
AVAudioSessionModeVideoChat
AVAudioSessionModeGameChat
注意:從ios10開始,當(dāng)你使用AVAudioSessionCategoryPlayAndRecord目錄時姻报,你可以通過AVAudioSessionCategoryOptionAllowAirPlay選項激活你的會話來啟用非鏡像AirPlay輸出己英。
AVAudioSessionCategoryOptionAllowAirPlay = 0x40
- 只有在音頻會話類別為AVAudioSessionCategoryPlayAndRecord時,才能顯式設(shè)置此選項逗抑。對于大多數(shù)其他音頻會話類別剧辐,系統(tǒng)將隱式地設(shè)置此選項寒亥。使用AVAudioSessionCategoryMultiRoute或AVAudioSessionCategoryRecord類別的音頻會話隱式地清除此選項邮府。
- 如果您清除此選項,AirPlay設(shè)備將不會顯示為可用的音頻輸出路由溉奕。如果設(shè)置此選項褂傀,這些設(shè)備將顯示為可用的輸出路由。
2.5 開啟后臺音頻
iOS和tvOS應(yīng)用程序要求您為某些后臺操作啟用某些功能加勤∠杀伲回放應(yīng)用程序需要的一個常見功能是播放背景音頻同波。啟用這個功能后,當(dāng)用戶切換到其他應(yīng)用程序或鎖定iOS設(shè)備時叠国,應(yīng)用程序的音頻可以繼續(xù)未檩。在iOS中支持高級回放功能,如AirPlay流媒體播放和圖片中的圖片回放粟焊,也需要此功能冤狡。
配置這些功能的最簡單方法是使用Xcode。在Xcode中選擇應(yīng)用程序的目標(biāo)并選擇capability選項卡项棠。在capability選項卡下悲雳,將背景模式設(shè)置為ON,并從可用模式列表中選擇“Audio, AirPlay, and Picture in Picture”選項香追。
啟用此后臺模式合瓢,并將音頻會話配置為適當(dāng)?shù)念悇e,您的應(yīng)用程序就可以開始播放后臺音頻了
注意:要允許視頻演示的音頻部分在后臺播放透典,請參閱媒體播放編程指南中的播放背景音頻晴楔。
3. 激活音頻會話
您已經(jīng)通過設(shè)置音頻會話的類別、選項和模式來配置它峭咒。要使配置生效滥崩,現(xiàn)在需要激活音頻會話。
3.1 系統(tǒng)如何解決競爭的音頻需求
當(dāng)你的應(yīng)用程序啟動時讹语,內(nèi)置的應(yīng)用程序(消息钙皮、音樂、Safari顽决、手機)可能在后臺運行短条。每一個都可以產(chǎn)生音頻:一條短信來了,你10分鐘前開始的播客還在繼續(xù)播放才菠,等等茸时。
如果你把一個設(shè)備想象成一個機場,應(yīng)用程序就像滑行的飛機赋访,這個系統(tǒng)就像一個控制塔可都。你的應(yīng)用程序可以發(fā)出音頻請求并聲明它想要的優(yōu)先級,但是“在停機坪上”發(fā)生的事情的最終權(quán)威來自系統(tǒng)蚓耽。使用音頻會話與“控制塔”通信渠牲。圖2-1展示了一個典型的場景—您的應(yīng)用程序要求在音樂應(yīng)用程序已經(jīng)在播放時使用音頻。在這個場景中步悠,您的應(yīng)用程序中斷了音樂應(yīng)用程序签杈。
在圖的第1步中,應(yīng)用程序請求激活它的音頻會話鼎兽。例如答姥,在應(yīng)用程序啟動時铣除,或者在用戶輕擊音頻錄制和回放應(yīng)用程序中的Play按鈕時,您可能會發(fā)出這樣的請求鹦付。在步驟2中尚粘,系統(tǒng)將考慮激活請求。具體來說敲长,它會考慮您分配給音頻會話的類別背苦。在圖2-1中,您的應(yīng)用程序使用了一個類別潘明,該類別需要對其他音頻進行消音行剂。
在步驟3和步驟4中,系統(tǒng)關(guān)閉音樂應(yīng)用程序的音頻會話钳降,停止其音頻播放厚宰。最后,在第五步遂填,系統(tǒng)激活你的應(yīng)用程序的音頻會話和播放可以開始铲觉。
3.2 激活和關(guān)閉您的音頻會話
雖然AVFoundation回放和錄制類會自動激活音頻會話,但手動激活它會讓您有機會測試激活是否成功吓坚。但是撵幽,如果你的應(yīng)用程序有一個play/pause UI元素,那么編寫你的代碼礁击,這樣用戶必須在激活會話之前按play盐杂。同樣,在更改音頻會話的活動/非活動狀態(tài)時哆窿,請檢查以確保調(diào)用成功链烈。編寫代碼來優(yōu)雅地處理系統(tǒng)拒絕激活您的會話。
該系統(tǒng)將關(guān)閉您的音頻會話挚躯,用于時鐘或日歷鬧鐘或來電强衡。當(dāng)用戶解除警報或選擇忽略電話呼叫時,系統(tǒng)允許您的會話再次激活码荔。是否在中斷結(jié)束時重新激活會話取決于應(yīng)用程序類型漩勤,如Audio Guidelines By app type所述。
- 激活音頻會話的代碼如下:
let session = AVAudioSession.sharedInstance()
do {
// 1) Configure your audio session category, options, and mode
// 2) Activate your audio session to enable your custom configuration
try session.setActive(true)
} catch let error as NSError {
print("Unable to activate audio session: \(error.localizedDescription)")
}
若要停用音頻會話缩搅,請將false傳遞給setActive方法越败。
當(dāng)使用AVFoundation對象(AVPlayer、AVAudioRecorder等)播放或錄制音頻時誉己,系統(tǒng)負責(zé)在中斷結(jié)束時重新激活音頻會話眉尸。但是域蜗,如果您注冊了通知消息并顯式地重新激活音頻會話巨双,則可以驗證重新激活成功噪猾,并可以更新應(yīng)用程序的狀態(tài)和用戶界面。有關(guān)更多信息筑累,請參見圖3-1袱蜡。
許多應(yīng)用程序從來不需要顯式地停用它們的音頻會話梢卸。重要的例外包括VoIP應(yīng)用程序移迫、逐向?qū)Ш綉?yīng)用程序快毛,在某些情況下還包括回放和錄音應(yīng)用程序白粉。
- 確保VoIP應(yīng)用程序的音頻會話(通常在后臺運行)僅在應(yīng)用程序處理調(diào)用時是活動的译秦。在后臺迈窟,準(zhǔn)備接收電話時茂洒,VoIP應(yīng)用程序的音頻會話不應(yīng)處于激活狀態(tài)威根。
- 確保使用錄制類別的應(yīng)用程序的音頻會話僅在錄制時處于活動狀態(tài)缅茉。在開始錄制和停止錄制之前嘴脾,請確保您的會話處于非活動狀態(tài),以允許播放其他聲音蔬墩,例如傳入消息警報译打。
- 如果應(yīng)用程序支持后臺音頻播放或錄制,如果應(yīng)用程序不積極使用音頻(或準(zhǔn)備使用音頻)拇颅,則在進入后臺時禁用其音頻會話奏司。這樣做允許系統(tǒng)釋放音頻資源,以便其他進程可以使用它們樟插。它還可以防止應(yīng)用程序的音頻會話在應(yīng)用程序進程被操作系統(tǒng)掛起時失效(參見AVAudioSessionInterruptionWasSuspendedKey)韵洋。
這個userInfo鍵只出現(xiàn)在AVAudioSession.InterruptionType中。開始中斷事件黄锤,其中中斷是操作系統(tǒng)掛起應(yīng)用程序的直接結(jié)果麻献。它的關(guān)聯(lián)值是一個布爾NSNumber,其中一個真值表示中斷是由于系統(tǒng)掛起應(yīng)用程序造成的猜扮,而不是由另一個音頻會話中斷的勉吻。
3.2.1 Audio Guidelines By app type
3.3 檢查其他音頻是否正在播放
當(dāng)你的應(yīng)用程序激活時,聲音可能已經(jīng)在設(shè)備上播放了旅赢。例如齿桃,當(dāng)用戶啟動應(yīng)用程序時,音樂應(yīng)用程序可能正在播放歌曲煮盼,或者Safari可能正在播放音頻流短纵。如果你的應(yīng)用是一款游戲,了解是否有其他音頻在播放尤其重要僵控。許多游戲都有音樂音軌和音效香到。在iOS人機界面指南中,建議你假設(shè)用戶在玩游戲時希望其他音頻和游戲音效能夠繼續(xù)。
在你的應(yīng)用委派的applicationDidBecomeActive:方法中悠就,檢查音頻會話的secondaryAudioShouldBeSilencedHint屬性千绪,以確定音頻是否已經(jīng)在播放。當(dāng)另一個具有非混合音頻會話的應(yīng)用程序正在播放音頻時梗脾,該值為true荸型。應(yīng)用程序應(yīng)該使用這個屬性作為使次要于應(yīng)用程序功能的音頻靜音的提示。例如炸茧,一個使用AVAudioSessionCategoryAmbient的游戲可以使用這個屬性來決定是否應(yīng)該在保持聲音效果不靜音的情況下靜音音軌瑞妇。
您還可以訂閱類型為AVAudioSessionSilenceSecondaryAudioHintNotification的通知,以確保在可選的輔助音頻靜音應(yīng)該開始或結(jié)束時通知您的應(yīng)用程序梭冠。此通知僅發(fā)送給當(dāng)前處于前臺且具有活動音頻會話的已注冊偵聽器辕狰。
func setupNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleSecondaryAudio),
name: .AVAudioSessionSilenceSecondaryAudioHint,
object: AVAudioSession.sharedInstance())
}
func handleSecondaryAudio(notification: Notification) {
// Determine hint type
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey] as? UInt,
let type = AVAudioSessionSilenceSecondaryAudioHintType(rawValue: typeValue) else {
return
}
if type == .begin {
// Other app audio started playing - mute secondary audio
} else {
// Other app audio stopped playing - restart secondary audio
}
}
此通知的userInfo字典包含AVAudioSessionSilenceSecondaryAudioHintType值的AVAudioSessionSilenceSecondaryAudioHintTypeKey。使用音頻提示類型來確定輔助音頻靜音應(yīng)該開始還是結(jié)束
4. 響應(yīng)中斷
添加音頻會話代碼來處理中斷控漠,確保您的應(yīng)用程序的音頻在電話到來柳琢、時鐘或日歷鬧鈴響或其他應(yīng)用程序激活其音頻會話時繼續(xù)正常工作。
音頻中斷是應(yīng)用程序的音頻會話的停用润脸,它會立即停止音頻柬脸。當(dāng)來自應(yīng)用程序的競爭音頻會話被激活,并且該會話沒有被系統(tǒng)分類以與您的會話混合時毙驯,就會發(fā)生中斷倒堕。在您的會話變?yōu)榉腔顒訝顟B(tài)后,系統(tǒng)將發(fā)送一條“您被中斷了”消息爆价,您可以通過保存狀態(tài)垦巴、更新用戶界面等方式對其進行響應(yīng)。
您的應(yīng)用程序可能會在中斷后暫停铭段。當(dāng)用戶接電話時就會發(fā)生這種情況骤宣。如果用戶忽略了一個呼叫,或者取消了一個警報序愚,系統(tǒng)就會發(fā)出一個“中斷結(jié)束”的消息憔披,你的應(yīng)用程序就會繼續(xù)運行。要恢復(fù)您的音頻爸吮,您必須重新激活您的音頻會話芬膝。
4.1 中斷生命周期
圖3-1說明了播放應(yīng)用程序的音頻會話中斷之前、期間和之后的事件序列形娇。
中斷事件—在本例中锰霜,F(xiàn)aceTime請求的到來—按如下方式進行。編號的步驟與圖中的數(shù)字相對應(yīng)桐早。
- 你的應(yīng)用程序是活動的癣缅,播放音頻厨剪。
- FaceTime請求到達。系統(tǒng)激活FaceTime應(yīng)用程序的音頻會話友存。
- 系統(tǒng)將關(guān)閉您的音頻會話祷膳。此時,應(yīng)用程序中的回放已經(jīng)停止爬立。
- 系統(tǒng)會發(fā)出一個通知钾唬,指示您的會話已被停用万哪。
- 您的通知處理程序?qū)⒉扇∵m當(dāng)?shù)牟僮飨姥薄@纾梢愿掠脩艚缑娌⒈4嬖谕V够胤艜r恢復(fù)回放所需的信息奕巍。
- 如果用戶拒絕中斷(忽略傳入的FaceTime請求)吟策,系統(tǒng)就會發(fā)出通知,表明中斷已經(jīng)結(jié)束的止。
- 通知處理程序在中斷結(jié)束時采取適當(dāng)?shù)牟僮鏖菁帷@纾梢愿掠脩艚缑孀绺!⒅匦录せ钜纛l會話和恢復(fù)播放匾委。
- (圖中未顯示)如果用戶接了一個電話,而不是在第6步取消中斷氓润,你的應(yīng)用程序?qū)⒈粧炱稹?/li>
4.2 音頻中斷處理技術(shù)
通過注冊觀察AVAudioSession發(fā)布的中斷通知來處理中斷赂乐。您在中斷代碼中所做的事情取決于您正在使用的音頻技術(shù),以及您在回放咖气、錄制挨措、音頻格式轉(zhuǎn)換、讀取流音頻包等方面所使用的技術(shù)崩溪。一般來說浅役,從用戶的角度來看,您需要確保盡可能少的中斷和盡可能好的恢復(fù)伶唯。
表3-1總結(jié)了中斷期間適當(dāng)?shù)囊纛l會話行為觉既。如果您使用AVFoundation回放或錄制對象,系統(tǒng)會自動處理其中的一些步驟乳幸。
表3-1音頻會話中斷期間應(yīng)該發(fā)生什么:
時間 | 處理 |
---|---|
中斷開始后 | 1.保存狀態(tài)和上下文奋救,2.更新的用戶界面 |
中斷結(jié)束后 | 1.恢復(fù)狀態(tài)和上下文,2.更新的用戶界面反惕,3.重新激活音頻會話尝艘,如果適當(dāng)?shù)膽?yīng)用程序 |
表3-2總結(jié)了如何根據(jù)技術(shù)處理音頻中斷:
音頻技術(shù) | 中斷處理 |
---|---|
1. AVFoundation framework | 系統(tǒng)自動暫停播放或錄制中斷和重新激活您的音頻會話時,您恢復(fù)播放或錄制姿染。2.如果你想保存和恢復(fù)播放位置之間的應(yīng)用程序啟動背亥,保存播放位置在中斷以及在應(yīng)用程序退出秒际。 |
Audio Queue Services, I/O audio unit | 這些技術(shù)讓你的應(yīng)用程序控制處理中斷。你負責(zé)保存回放或錄制位置狡汉,并在中斷結(jié)束后重新激活音頻會話娄徊。 |
System Sound Services | 當(dāng)中斷開始時,使用系統(tǒng)聲音服務(wù)播放的聲音將變?yōu)殪o音盾戴。如果中斷結(jié)束寄锐,聲音可以再次播放。應(yīng)用程序不能影響使用這種回放技術(shù)的聲音的中斷行為尖啡。 |
4.3 處理Siri中斷
- 當(dāng)Siri打斷你的應(yīng)用程序回放時橄仆,你必須在音頻會話處于中斷狀態(tài)時跟蹤Siri發(fā)出的任何遠程控制命令。在中斷期間衅斩,跟蹤Siri發(fā)出的所有命令盆顾,并在中斷結(jié)束時做出相應(yīng)的響應(yīng)。例如畏梆,在中斷期間您宪,用戶要求Siri暫停應(yīng)用程序的音頻播放。當(dāng)你的應(yīng)用程序被通知中斷已經(jīng)結(jié)束奠涌,它不應(yīng)該自動恢復(fù)播放宪巨。相反,應(yīng)用程序的UI應(yīng)該指示應(yīng)用程序處于暫停狀態(tài)溜畅。
4.4 觀察音頻中斷
- 要處理音頻中斷捏卓,首先注冊AVAudioSessionInterruptionNotification類型的通知。
func registerForNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleInterruption),
name: .AVAudioSessionInterruption,
object: AVAudioSession.sharedInstance())
}
func handleInterruption(_ notification: Notification) {
// Handle interruption
}
- 發(fā)布的NSNotification實例包含一個已填充的userInfo字典达皿,提供中斷的詳細信息天吓。通過從userInfo字典中檢索AVAudioSessionInterruptionType值來確定中斷的類型。中斷類型指示中斷是已經(jīng)開始還是已經(jīng)結(jié)束峦椰。
func handleInterruption(_ notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
// Interruption began, take appropriate actions (save state, update user interface)
}
else if type == .ended {
guard let optionsValue =
userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
}
}
}
- 如果中斷類型是AVAudioSessionInterruptionTypeEnded龄寞,則userInfo字典可能包含AVAudioSessionInterruptionOptions值。AVAudioSessionInterruptionOptionShouldResume的一個選項值是一個提示汤功,指示如果應(yīng)用程序在被中斷時正在播放物邑,它是否應(yīng)該自動恢復(fù)播放。媒體播放應(yīng)用程序在中斷后開始播放之前應(yīng)始終尋找此標(biāo)志滔金。如果它不存在色解,播放不應(yīng)該再次開始,直到由用戶發(fā)起餐茵。沒有播放界面的應(yīng)用程序科阎,比如游戲,可以忽略這個標(biāo)志忿族,在中斷結(jié)束時重新激活并恢復(fù)播放锣笨。
注意:不能保證開始中斷會有相應(yīng)的結(jié)束中斷蝌矛。你的應(yīng)用程序需要知道一個切換到前臺運行狀態(tài)或用戶按下播放按鈕。在這兩種情況下错英,確定你的應(yīng)用程序是否應(yīng)該重新激活它的音頻會話入撒。
4.5 響應(yīng)媒體服務(wù)器重置
媒體服務(wù)器通過共享服務(wù)器進程提供音頻和其他多媒體功能。雖然很少見椭岩,但是媒體服務(wù)器可以在您的應(yīng)用程序處于活動狀態(tài)時進行重置茅逮。注冊AVAudioSessionMediaServicesWereResetNotification通知以監(jiān)視媒體服務(wù)器重置。收到通知后判哥,您的app需要做以下工作:
- 處理孤立的音頻對象(如播放器献雅、錄音機、轉(zhuǎn)換器或音頻隊列)并創(chuàng)建新對象
- 重置任何被跟蹤的內(nèi)部音頻狀態(tài)姨伟,包括AVAudioSession的所有屬性
- 在適當(dāng)?shù)臅r候惩琉,使用setActive:error:方法重新激活AVAudioSession實例
5. 響應(yīng)路由更改
當(dāng)您的應(yīng)用程序運行時豆励,用戶可能會插入或拔出耳機夺荒,或使用帶有音頻連接的塢站。iOS人機界面指南描述了應(yīng)用程序應(yīng)該如何應(yīng)對此類事件良蒸。要實現(xiàn)這些建議技扼,請編寫音頻會話代碼來處理音頻硬件路由更改。某些類型的應(yīng)用程序嫩痰,比如游戲剿吻,并不總是需要對路線變化做出響應(yīng)。然而串纺,其他類型的應(yīng)用程序丽旅,如媒體播放器,必須響應(yīng)所有的路由變化纺棺。
5.1 各種音頻硬件路由變化
音頻硬件路由是音頻信號的有線電子路徑榄笙。當(dāng)設(shè)備的用戶插入或拔出耳機時,系統(tǒng)會自動改變音頻硬件路線祷蝌。如果你注冊了AVAudioSessionRouteChangeNotification類型的通知茅撞,你的應(yīng)用程序可以被通知這些變化。
圖4-1描述了在錄制和回放期間各種路由更改的事件序列巨朦。圖的底部顯示了四個可能的結(jié)果米丘,它們來自您編寫的屬性偵聽器回調(diào)函數(shù)所采取的操作。
如圖所示糊啡,在您的app啟動后拄查,系統(tǒng)最初會確定音頻路由。它將在應(yīng)用程序運行時繼續(xù)監(jiān)視活動路由棚蓄。首先考慮用戶點擊應(yīng)用程序中的記錄按鈕的情況堕扶,該按鈕由圖左側(cè)的“開始記錄”框表示腺毫。
在錄制過程中,用戶可以將耳機插入或拔出挣柬,如圖左下方所示為鉆石形狀的決策元件潮酒。作為響應(yīng),系統(tǒng)發(fā)送一個路由更改通知邪蛔,其中包含更改的原因和前一個路由急黎。你的應(yīng)用程序應(yīng)該停止錄制。
回放的情況類似侧到,但結(jié)果不同勃教,如圖右側(cè)所示。如果用戶在播放過程中拔下耳機匠抗,應(yīng)用程序應(yīng)該暫停音頻故源。如果用戶在回放過程中插入耳機,應(yīng)用程序應(yīng)該允許回放繼續(xù)汞贸。
5.2 觀察音頻路由的變化
- 發(fā)生音頻路由更改的原因有很多绳军,包括用戶插入一對耳機、連接藍牙LE耳機或拔掉USB音頻接口矢腻。知道什么時候發(fā)生這些變化可能對你的應(yīng)用程序很重要门驾,這樣你就可以更新它的用戶界面或改變它的內(nèi)部狀態(tài)。通過注冊AVAudioSessionRouteChangeNotification類型的通知多柑,可以在音頻路由更改時通知您奶是。
func setupNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleRouteChange),
name: .AVAudioSessionRouteChange,
object: AVAudioSession.sharedInstance())
}
func handleRouteChange(_ notification: Notification) {
}
- 發(fā)布的通知包含一個已填充的userInfo字典,提供路由更改的詳細信息竣灌。您可以通過從userInfo字典中檢索AVAudioSessionRouteChangeReason值來確定這種變化的原因聂沙。當(dāng)連接一個新設(shè)備時,原因是AVAudioSessionRouteChangeReasonNewDeviceAvailable初嘹,當(dāng)一個設(shè)備被刪除時及汉,原因是avaudiosessionroutechangereasonolddeviceavailable。
func handleRouteChange(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSessionRouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
// Handle new device available.
case .oldDeviceUnavailable:
// Handle old device removed.
default: ()
}
}
- 當(dāng)新設(shè)備可用時削樊,您可以查詢音頻會話的currentRoute屬性豁生,以確定音頻輸出當(dāng)前路由到何處。這將返回一個AVAudioSessionRouteDescription對象漫贞,該對象列出了音頻會話的所有輸入和輸出甸箱。當(dāng)設(shè)備被移除時,您可以從userInfo字典中檢索上一條路由的AVAudioSessionRouteDescription對象迅脐。在這兩種情況下芍殖,您可以查詢路由描述的輸出屬性,該屬性返回一個AVAudioSessionPortDescription對象數(shù)組谴蔑,提供音頻輸出路由的詳細信息豌骏。
func handleRouteChange(notification: NSNotification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSessionRouteChangeReason(rawValue:reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
let session = AVAudioSession.sharedInstance()
for output in session.currentRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
headphonesConnected = true
}
case .oldDeviceUnavailable:
if let previousRoute =
userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
for output in previousRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
headphonesConnected = false
}
}
default: ()
}
}
重要提示:媒體播放應(yīng)用程序應(yīng)該暫停播放龟梦,如果路線改變的原因是AVAudioSessionRouteChangeReasonOldDeviceUnavailable,但不應(yīng)該窃躲,如果原因是AVAudioSessionRouteChangeReasonOverride计贰。
注意:音頻路由的更改也可能導(dǎo)致音頻會話的采樣率、I/O緩沖區(qū)持續(xù)時間蒂窒、通道計數(shù)或其他硬件相關(guān)值的更改躁倒。如果這些值對您的應(yīng)用程序很重要,請在路由更改后查詢它們洒琢,以查看它們的值是否已更改秧秉。
6. 配置設(shè)備硬件
使用音頻會話屬性,您可以在運行時為設(shè)備硬件優(yōu)化應(yīng)用程序的音頻行為衰抑。這樣做可以使您的代碼適應(yīng)它所運行的設(shè)備的特性象迎,以及用戶在應(yīng)用程序運行時所做的更改(如插入耳機或?qū)⒃O(shè)備停靠在其上)呛踊。
使用AVAudioSession:
- 為示例速率和I/O緩沖區(qū)持續(xù)時間指定首選硬件設(shè)置
- 查詢許多硬件特性砾淌,如輸入和輸出延遲、輸入和輸出通道計數(shù)恋技、硬件采樣率拇舀、硬件卷設(shè)置和音頻輸入的可用性
6.1 選擇首選音頻硬件值
使用音頻會話指定您的首選設(shè)備設(shè)置逻族,如采樣率和硬件I/O緩沖區(qū)持續(xù)時間蜻底。表5-1描述了這些偏好的好處和代價。
例如聘鳞,如果音頻質(zhì)量在您的應(yīng)用程序中非常重要薄辅,并且大文件或緩沖區(qū)大小不是一個重要問題,那么您可以指定一個高采樣率的首選項抠璃。
注意:默認的音頻I/O緩沖時間(44.1 kHz音頻大約0.02秒)為大多數(shù)應(yīng)用程序提供了足夠的響應(yīng)能力站楚。您可以為延遲關(guān)鍵型應(yīng)用程序(如live musical instrument monitoring)設(shè)置一個較低的I/O持續(xù)時間,但是對于大多數(shù)應(yīng)用程序搏嗡,您永遠不需要修改這個設(shè)置窿春。
6.2 設(shè)置首選音頻硬件值
在激活音頻會話之前設(shè)置首選硬件值。如果你已經(jīng)在運行一個音頻會話采盒,那么就停用它旧乞。對首選值的更改將在音頻會話激活后生效,您可以在此時驗證更改磅氨。清單5-1展示了如何設(shè)置首選硬件值以及如何驗證它們尺栖。
- 清單5-1設(shè)置和驗證音頻硬件值
let session = AVAudioSession.sharedInstance()
// Configure category and mode
do {
try session.setCategory(AVAudioSessionCategoryRecord, mode: AVAudioSessionModeDefault)
} catch let error as NSError {
print("Unable to set category: \(error.localizedDescription)")
}
// Set preferred sample rate
do {
try session.setPreferredSampleRate(44_100)
} catch let error as NSError {
print("Unable to set preferred sample rate: \(error.localizedDescription)")
}
// Set preferred I/O buffer duration
do {
try session.setPreferredIOBufferDuration(0.005)
} catch let error as NSError {
print("Unable to set preferred I/O buffer duration: \(error.localizedDescription)")
}
// Activate the audio session
do {
try session.setActive(true)
} catch let error as NSError {
print("Unable to activate session. \(error.localizedDescription)")
}
// Query the audio session's ioBufferDuration and sampleRate properties
// to determine if the preferred values were set
print("Audio Session ioBufferDuration: \(session.ioBufferDuration), sampleRate: \(session.sampleRate)")
注意:當(dāng)競爭音頻會話設(shè)置首選硬件值時,系統(tǒng)優(yōu)先考慮非混合會話烦租。使用AVAudioSessionCategoryAmbient類別或AVAudioSessionCategoryOptionMixWithOthers選項的音頻會話不太可能提供其首選的硬件設(shè)置延赌。
6.3 選擇和配置麥克風(fēng)
在擁有兩個或更多內(nèi)置麥克風(fēng)的設(shè)備上除盏,iOS會通過音頻會話模式自動選擇麥克風(fēng)。模式指定用于輸入的數(shù)字信號處理(DSP)和可能的路由挫以。為每種模式的用例優(yōu)化輸入和路由者蠕。設(shè)置模式還可能影響正在使用的路由的其他方面。
開發(fā)人員還可以手動選擇麥克風(fēng)掐松,如果硬件支持蠢棱,甚至可以選擇首選的麥克風(fēng)極性模式。
重要提示:在使用任何輸入選擇功能之前甩栈,為您的應(yīng)用程序設(shè)置音頻會話類別和模式泻仙,然后激活音頻會話。
- 設(shè)置首選輸入
要發(fā)現(xiàn)內(nèi)置的或連接的輸入端口量没,請使用音頻會話的availableInputs屬性玉转。此屬性返回AVAudioSessionPortDescription對象數(shù)組,這些對象描述設(shè)備的可用輸入端口殴蹄。端口可以通過它們的portType屬性來標(biāo)識究抓。要設(shè)置首選輸入端口(內(nèi)置麥克風(fēng)、有線麥克風(fēng)袭灯、USB輸入刺下,等等),請使用音頻會話的setPreferredInput:error:方法稽荧。
- 設(shè)置首選數(shù)據(jù)源
- 一些端口橘茉,如內(nèi)置麥克風(fēng)和一些USB配件,支持?jǐn)?shù)據(jù)源姨丈。應(yīng)用程序可以通過查詢端口描述的dataSources屬性來發(fā)現(xiàn)可用的數(shù)據(jù)源畅卓。對于內(nèi)置麥克風(fēng),返回的數(shù)據(jù)源描述對象表示每個麥克風(fēng)蟋恬。不同的設(shè)備為內(nèi)置麥克風(fēng)返回不同的值翁潘。例如,iPhone 4和iPhone 4S有兩個麥克風(fēng):底部和頂部歼争。iPhone 5有三個麥克風(fēng):底部拜马、前面和后面。
- 單個內(nèi)置麥克風(fēng)可以通過數(shù)據(jù)源描述的位置屬性(上沐绒、下)和方向?qū)傩?前俩莽、后等)的組合進行標(biāo)識。應(yīng)用程序可以使用AVAudioSessionPortDescription對象的setPreferredDataSource:error:方法設(shè)置首選數(shù)據(jù)源洒沦。
- 設(shè)置一個首選的極坐標(biāo)模式
一些iOS設(shè)備支持為一些內(nèi)置麥克風(fēng)配置麥克風(fēng)極性模式豹绪。麥克風(fēng)的極模式?jīng)Q定了它對聲音的敏感度與聲源的方向有關(guān)。當(dāng)前的iphone支持為前后內(nèi)置麥克風(fēng)設(shè)置首選的極坐標(biāo)模式÷鹘颍可用模式使用數(shù)據(jù)源描述對象的supportedPolarPatterns屬性返回蝉衣。此屬性為數(shù)據(jù)源返回一個受支持的極性模式數(shù)組(如cardioid或omnidirectional),如果沒有可用的可選模式巷蚪,則返回nil病毡。如果數(shù)據(jù)源有許多受支持的極性模式,您可以通過使用數(shù)據(jù)源描述的setPreferredPolarPattern:error:方法來設(shè)置首選的極性模式屁柏。
- 下面的代碼提供了一個簡單的示例啦膜,演示如何選擇特定的麥克風(fēng)并設(shè)置其極性模式。
// Preferred Mic = Front, Preferred Polar Pattern = Cardioid
let preferredMicOrientation = AVAudioSessionOrientationFront
let preferredPolarPattern = AVAudioSessionPolarPatternCardioid
// Retrieve your configured and activated audio session
let session = AVAudioSession.sharedInstance()
// Get available inputs
guard let inputs = session.availableInputs else { return }
// Find built-in mic
guard let builtInMic = inputs.first(where: {
$0.portType == AVAudioSessionPortBuiltInMic
}) else { return }
// Find the data source at the specified orientation
guard let dataSource = builtInMic.dataSources?.first (where: {
$0.orientation == preferredMicOrientation
}) else { return }
// Set data source's polar pattern
do {
try dataSource.setPreferredPolarPattern(preferredPolarPattern)
} catch let error as NSError {
print("Unable to preferred polar pattern: \(error.localizedDescription)")
}
// Set the data source as the input's preferred data source
do {
try builtInMic.setPreferredDataSource(dataSource)
} catch let error as NSError {
print("Unable to preferred dataSource: \(error.localizedDescription)")
}
// Set the built-in mic as the preferred input
// This call will be a no-op if already selected
do {
try session.setPreferredInput(builtInMic)
} catch let error as NSError {
print("Unable to preferred input: \(error.localizedDescription)")
}
// Print Active Configuration
session.currentRoute.inputs.forEach { portDesc in
print("Port: \(portDesc.portType)")
if let ds = portDesc.selectedDataSource {
print("Name: \(ds.dataSourceName)")
print("Polar Pattern: \(ds.selectedPolarPattern ?? "[none]")")
}
}
在iPhone 6s上運行這段代碼會產(chǎn)生以下控制臺輸出:
Port: MicrophoneBuiltIn
Name: Front
Polar Pattern: Cardioid
6.4 在模擬器中運行應(yīng)用程序
當(dāng)你添加音頻會話支持到你的應(yīng)用程序時淌喻,你可以在模擬器或設(shè)備上運行你的應(yīng)用程序僧家。但是,模擬器不能模擬不同進程或音頻路由更改中的音頻會話之間的大多數(shù)交互裸删。當(dāng)運行你的應(yīng)用程序在模擬器八拱,你不能:
- 調(diào)用一個中斷
- 模擬插入或拔出耳機
- 改變靜音開關(guān)的設(shè)置
- 模擬屏幕鎖
- 測試音頻混合行為——即將音頻與來自另一個應(yīng)用程序(如音樂應(yīng)用程序)的音頻一起播放。
由于模擬器的特性涯塔,您可能希望對代碼進行條件設(shè)置肌稻,以允許在模擬器中進行部分測試。下面代碼展示了如何做到這一點匕荸。
#if arch(i386) || arch(x86_64)
// Execute subset of code that works in the Simulator
#else
// Execute device-only code as well as the other code
#endif
7. 保護用戶隱私
為了保護用戶隱私爹谭,你的應(yīng)用程序在錄制音頻之前必須獲得用戶的許可。如果用戶不授予權(quán)限榛搔,則只記錄靜默诺凡。當(dāng)你使用一個支持記錄的類別,而應(yīng)用程序試圖使用輸入路徑時药薯,系統(tǒng)會自動提示用戶允許绑洛。
您可以使用requestRecordPermission:方法來手動請求權(quán)限,而不是等待系統(tǒng)提示用戶記錄權(quán)限童本。使用這種方法允許您的應(yīng)用程序在不中斷應(yīng)用程序的自然流的情況下獲得許可,從而獲得更好的用戶體驗脸候。
AVAudioSession.sharedInstance().requestRecordPermission { granted in
if granted {
// User granted access. Present recording interface.
} else {
// Present message to user indicating that recording
// can't be performed until they change their preference
// under Settings -> Privacy -> Microphone
}
}
重要提示:從iOS 10開始穷娱,所有訪問設(shè)備麥克風(fēng)的應(yīng)用程序必須靜態(tài)地聲明它們的意圖。要做到這一點运沦,應(yīng)用程序現(xiàn)在必須在它們的信息中包含NSMicrophoneUsageDescription鍵泵额。并為此鍵提供一個目的字符串。當(dāng)系統(tǒng)提示用戶允許訪問時携添,此字符串將作為警報的一部分顯示嫁盲。
如果一個應(yīng)用程序試圖訪問設(shè)備的麥克風(fēng)沒有這個鍵和值存在,應(yīng)用程序?qū)⒔K止烈掠。
8. AVAudioSession
AVAudioSession是一個對象羞秤,它向系統(tǒng)傳達你打算如何在你的應(yīng)用程序中使用音頻缸托。它的類聲明如下:
class AVAudioSession : NSObject
音頻會話充當(dāng)應(yīng)用程序和操作系統(tǒng)之間的中介,進而充當(dāng)?shù)讓右纛l硬件之間的中介瘾蛋。您使用一個音頻會話來與操作系統(tǒng)通信應(yīng)用程序音頻的一般性質(zhì)俐镐,而不詳細說明特定的行為或與音頻硬件所需的交互。您將這些細節(jié)的管理委托給音頻會話哺哼,以確保操作系統(tǒng)能夠最好地管理用戶的音頻體驗佩抹。
所有的iOS、tvOS和watchOS應(yīng)用程序都有一個默認的音頻會話取董,并預(yù)先配置了以下行為:
- 它支持音頻回放棍苹,但不允許音頻錄制(tvOS不支持音頻錄制)。
- 在iOS系統(tǒng)中茵汰,將鈴聲/靜音開關(guān)設(shè)置為靜音模式廊勃,應(yīng)用程序播放的任何音頻都會被靜音。
- 在iOS系統(tǒng)中经窖,鎖定設(shè)備會使應(yīng)用程序的音頻靜音坡垫。
- 當(dāng)應(yīng)用程序播放音頻時,它會靜音任何其他背景音頻画侣。
雖然默認的音頻會話提供了有用的行為冰悠,但它通常不提供媒體應(yīng)用程序需要的音頻行為。要更改默認行為配乱,需要配置應(yīng)用程序的音頻會話類別溉卓。
你可以使用七種可能的類別(參見音頻會話類別和模式),但是回放是回放應(yīng)用程序最常用的一種搬泥。這個類別表明音頻播放是你的應(yīng)用程序的核心功能桑寨。當(dāng)你指定這個類別時,你的應(yīng)用程序的音頻將繼續(xù)與鈴聲/靜音開關(guān)設(shè)置為靜音模式(只有iOS)忿檩。使用這個類別尉尾,你也可以播放背景音頻,如果你在圖片背景模式中使用音頻燥透,AirPlay和圖片沙咏。有關(guān)更多信息额划,請參見啟用背景音頻柬讨。
使用AVAudioSession對象來配置應(yīng)用程序的音頻會話。該類是一個單例對象巍虫,用于設(shè)置音頻會話的類別吱韭、模式和其他配置吆豹。您可以在應(yīng)用程序的整個生命周期中與音頻會話進行交互,但是在應(yīng)用程序啟動時執(zhí)行此配置通常很有用,如下面的示例所示痘煤。
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Get the singleton instance.
let audioSession = AVAudioSession.sharedInstance()
do {
// Set the audio session category, mode, and options.
try audioSession.setCategory(.playback, mode: .moviePlayback, options: [])
} catch {
print("Failed to set audio session category.")
}
// Other post-launch configuration.
return true
}
當(dāng)您使用setActive(:)或setActive(:options:)方法激活會話時凑阶,音頻會話將使用此配置。
請注意
你可以在設(shè)置類別后的任何時候激活音頻會話速勇,但通常最好將此調(diào)用推遲到應(yīng)用程序開始音頻播放之后晌砾。延遲調(diào)用可以確保您不會過早地中斷正在進行的任何其他背景音頻。
- AVAudioSession主要提供了以下這些接口
8.1 獲取音頻會話
class func sharedInstance() -> AVAudioSession
//Returns the shared audio session instance.
8.2 配置音頻會話
var category: AVAudioSession.Category
//The current audio session category.
func setCategory(AVAudioSession.Category)
//Sets the audio session’s category.
var availableCategories: [AVAudioSession.Category]
//The audio session categories available on the current device.
struct AVAudioSession.Category
//Audio session category identifiers.
var categoryOptions: AVAudioSession.CategoryOptions
//The set of options associated with the current audio session category.
func setCategory(AVAudioSession.Category, options: AVAudioSession.CategoryOptions)
//Sets the audio session’s category with the specified options.
struct AVAudioSession.CategoryOptions
//Constants that specify optional audio behaviors.
var mode: AVAudioSession.Mode
//The current audio session’s mode.
func setMode(AVAudioSession.Mode)
//Sets the audio session’s mode.
func setCategory(AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions)
//Sets the audio session’s category, mode, and options.
var availableModes: [AVAudioSession.Mode]
//The audio session modes available on the device.
struct AVAudioSession.Mode
//Audio session mode identifiers.
var routeSharingPolicy: AVAudioSession.RouteSharingPolicy
//The current route-sharing policy.
func setCategory(AVAudioSession.Category, mode: AVAudioSession.Mode, policy: AVAudioSession.RouteSharingPolicy, options: AVAudioSession.CategoryOptions)
//Sets the session category, mode, route-sharing policy, and options.
enum AVAudioSession.RouteSharingPolicy
//Cases that indicate the possible route-sharing policies for an audio session.
8.3 激活音頻會話
//使用指定的選項激活或關(guān)閉應(yīng)用程序的音頻會話烦磁。
func setActive(Bool, options: AVAudioSession.SetActiveOptions)
//在watchOS上異步激活音頻會話养匈。
func activate(options: AVAudioSessionActivationOptions, completionHandler: (Bool, Error?) -> Void)
8.4 請求錄音權(quán)限
//當(dāng)前的錄音許可狀態(tài)。
var recordPermission: AVAudioSession.RecordPermission
//請求用戶錄制音頻的權(quán)限都伪。
func requestRecordPermission(PermissionBlock)
8.5 與其他音頻混合
var isOtherAudioPlaying: Bool
//A Boolean value that indicates whether another app is playing audio.
var secondaryAudioShouldBeSilencedHint: Bool
//A Boolean value that indicates whether another app, with a nonmixable audio session, is playing audio.
var allowHapticsAndSystemSoundsDuringRecording: Bool
//A Boolean value that indicates whether system sounds and haptics play while recording from audio input.
func setAllowHapticsAndSystemSoundsDuringRecording(Bool)
//Sets a Boolean value that indicates whether system sounds and haptics play while recording from audio input.
var promptStyle: AVAudioSession.PromptStyle
//A hint to audio sessions that use voice prompt mode to alter the type of prompts they issue in response to other system audio, such as Siri and phone calls.
enum AVAudioSession.PromptStyle
//Constants that indicate the prompt style to use.
8.6 響應(yīng)音頻會話通知
class let interruptionNotification: NSNotification.Name
//A notification that’s posted when an audio interruption occurs.
class let routeChangeNotification: NSNotification.Name
//A notification that’s posted when the system’s audio route changes.
class let silenceSecondaryAudioHintNotification: NSNotification.Name
//A notification that’s posted when the primary audio from other applications starts and stops.
class let mediaServicesWereLostNotification: NSNotification.Name
//A notification that’s posted when the system terminates the media server.
class let mediaServicesWereResetNotification: NSNotification.Name
//A notification that’s posted when the media server restarts.
8.7 使用音頻路由
//當(dāng)前音頻路由的輸入和輸出端口的描述呕乎。
var currentRoute: AVAudioSessionRouteDescription
//一個對象,它描述與會話的音頻路由相關(guān)聯(lián)的輸入和輸出端口陨晶。
class AVAudioSessionRouteDescription
//一個布爾值猬仁,指示音頻輸入路徑是否可用。
var isInputAvailable: Bool
//一個可用于音頻路由的輸入端口數(shù)組先誉。
var availableInputs: [AVAudioSessionPortDescription]?
//音頻路由的首選輸入端口湿刽。
var preferredInput: AVAudioSessionPortDescription?
//設(shè)置音頻路由的首選輸入端口。
func setPreferredInput(AVAudioSessionPortDescription?)
//關(guān)于端口功能及其支持的硬件通道的信息褐耳。
class AVAudioSessionPortDescription
//當(dāng)前選擇的輸入數(shù)據(jù)源诈闺。
var inputDataSource: AVAudioSessionDataSourceDescription?
//音頻會話當(dāng)前輸入端口的可用數(shù)據(jù)源數(shù)組。
var inputDataSources: [AVAudioSessionDataSourceDescription]?
//選擇音頻會話當(dāng)前輸入端口的數(shù)據(jù)源铃芦。
func setInputDataSource(AVAudioSessionDataSourceDescription?)
//當(dāng)前音頻路由的可用輸出數(shù)據(jù)源的數(shù)組雅镊。
var outputDataSources: [AVAudioSessionDataSourceDescription]?
//當(dāng)前選擇的輸出數(shù)據(jù)源。
var outputDataSource: AVAudioSessionDataSourceDescription?
//設(shè)置音頻會話的輸出數(shù)據(jù)源刃滓。
func setOutputDataSource(AVAudioSessionDataSourceDescription?)
//為音頻輸入或輸出定義數(shù)據(jù)源的對象仁烹,提供數(shù)據(jù)源的名稱、位置和方向等信息咧虎。
class AVAudioSessionDataSourceDescription
//臨時改變當(dāng)前的音頻路由卓缰。
func overrideOutputAudioPort(AVAudioSession.PortOverride)
8.8 使用音頻通道
//當(dāng)前路由的音頻輸入通道的數(shù)量。
var inputNumberOfChannels: Int
//當(dāng)前音頻路由可用的最大輸入通道數(shù)老客。
var maximumInputNumberOfChannels: Int
//當(dāng)前路由的優(yōu)先輸入通道數(shù)僚饭。
var preferredInputNumberOfChannels: Int
//設(shè)置當(dāng)前路由的優(yōu)先輸入通道數(shù)。
func setPreferredInputNumberOfChannels(Int)
//音頻輸出通道的數(shù)目胧砰。
var outputNumberOfChannels: Int
//當(dāng)前音頻路由可用的最大輸出通道數(shù)。
var maximumOutputNumberOfChannels: Int
//當(dāng)前路由的輸出通道的優(yōu)先數(shù)量苇瓣。
var preferredOutputNumberOfChannels: Int
//設(shè)置當(dāng)前路由的輸出通道的首選數(shù)目尉间。
func setPreferredOutputNumberOfChannels(Int)
8.9 處理音頻設(shè)備設(shè)置
//應(yīng)用于與會話相關(guān)的輸入的增益。
var inputGain: Float
//一個布爾值,指示是否可以設(shè)置輸入增益哲嘲。
var isInputGainSettable: Bool
//將輸入增益更改為指定值贪薪。
func setInputGain(Float)
//由用戶設(shè)置的系統(tǒng)范圍的輸出卷。
var outputVolume: Float
//當(dāng)前音頻采樣率眠副,單位為赫茲画切。
var sampleRate: Double
//首選采樣率,單位為赫茲囱怕。
var preferredSampleRate: Double
//設(shè)置音頻輸入和輸出的首選采樣率霍弹。
func setPreferredSampleRate(Double)
//音頻輸入的延遲,以秒為單位娃弓。
var inputLatency: TimeInterval
//音頻輸出的延遲典格,以秒為單位。
var outputLatency: TimeInterval
//當(dāng)前I/O緩沖區(qū)持續(xù)時間(以秒為單位)台丛。
var ioBufferDuration: TimeInterval
//首選的I/O緩沖區(qū)持續(xù)時間(以秒為單位)耍缴。
var preferredIOBufferDuration: TimeInterval
//設(shè)置首選音頻I/O緩沖區(qū)持續(xù)時間。
func setPreferredIOBufferDuration(TimeInterval)
8.10 設(shè)置聚合的I/O首選項
//設(shè)置音頻會話的聚合I/O配置首選項挽霉。
func setAggregatedIOPreference(AVAudioSession.IOType)
參考蘋果官方文檔:音頻會話章節(jié)