定义一个音频会话
音频会话是 App 和 IOS 之间的媒介,用来为 App 配置相关的音频属性和行为。在加载过程中,App 会自动创建一个音频会话的单例。开发者可以通过配置音频会话来描述 App 对音频的需求。比如:
- 在 App 播放声音的时候,开发者是想让其他 App 的声音停止还是和自己的声音混合在一起?
- 当碰到系统闹钟或其他声音响起的时候,App 中的声音会作出什么反应?
- 当用户插拔耳机时, App 中的声音功能会作出什么反应?
音频会话的配置会影响 App 运行期间几乎所有的音频活动(除了通过系统声音服务 API 播放的 UI 音效)。开发者可以通过查询音频会话来获取 App 运行设备上的硬件特性(频道数、采样率等)。这些硬件特性因设备而异, App 可以根据用户行为改变这些特性。
开发者可以显式得开启或关闭自己的音频会话。开发者需要在 App 开始播放声音或使用录音功能之前开启音频会话。另外,系统可以在接到电话或闹钟响起时关闭App音频会话,这种行为被称为中断(interruption)。音频会话的 API 中提供了对中断进行响应和恢复的方法。
音频会话的默认行为
音频会话具有如下的默认行为:
- 支持后台播放,不支持录音
- 当用户将手机切换到静音模式后,App 会被静音。
- 设备锁屏后,App 会被静音。
- 当 App 的音频开始后,设备正在播放的其他声音会被静音。
上述行为由默认音频会话类别 <code>AVAudioSessionCategorySoloAmbient</code> 提供。音频会话类别(coming soon)介绍了如何在 App 内使用类别。
尽管音频会话会在 App 开始播放或录制音频时自动开启,但这种默认的开启方式会带来风险。举例来说,如果用户使用 App 过程中有电话打入,而用户选择了拒接电话让 App 继续运行。如果没有使用合理的后台播放技术,那 App 的声音将不会再播放。下一章(coming soon)描述了一些处理这类问题的策略,处理中断(coming soon)中有进一步的讨论。
开发者可以在开发过程中使用这种默认行为来提高开发效率。如果要发布 App ,那么只有在以下场景中才能安全的忽略音频会话:
- App 除了系统声音服务(<code>System Sound Services</code>)或 <code>UIKit</code> 中的 <code>playInputClick</code> 方法外,不使用其他音频 API 处理音频。
系统声音服务是一种用来播放UI音效及触发震动的IOS技术,不适用于其他任何场景。详情见System Sound Services Reference。
UIKit 中的 <code>playInputClick</code> 方法允许开发者在特定的输入或键盘辅助视图(accessory view)中播放标准的键盘按键音。它的音频会话行为由系统自动处理。详情见Playing Input Clicks - App 不使用音频
如果不满足上述条件,一定不要在需要发布的 App 中使用默认的音频会话。
为什么通常情况下默认的音频会话不能满足开发者的要求
如果开发者不对音频会话进行初始化、配置和显式调用,那么 App 就不能对中断或音频源的变化作出响应,也不能控制系统如何处理不同 App 间音频的混合。
以下场景描述了音频会话的默认行为,以及开发者如何来改变它:
- 开发者开发了一款播放有声书的 App。用户开始听《 The Merchant of Venice》,正当 Bassanio 大人要出场的时候,自动锁屏的时间到了,屏幕变黑了,有声书的音频也被静音了。
为了避免锁屏静音这种情况,开发者需为音频会话配置一个支持后台播放的类别,同时要在 <code>UIBackgoundModes</code> 中添加 <code>audio</code> 标志。 - 开发者开发了一款使用基于 OpenAL 音效的第一视角射击类游戏。游戏中提供背景音乐,但也为用户提供了关闭游戏背景音乐,并播发音乐库中的音乐的功能。在选择一曲激昂的音乐开始播放后,用户朝着敌军开了一枪,枪响了,用户播放的音乐却停了。
为了保证用户选择的音乐能不被干扰的继续播放,需要将音频会话设置为允许混合的模式。开发者可以选择 <code>AVAudioSessionCategoryAmbient</code>类别 ,也可以通过修改 <code>AVAudioSessionCategoryPlayback</code> 类别来支持混合。 - 开发者开发了一款使用音频队列服务(<code>Audio Queue Services</code>)进行后台播放的流媒体电台 App。正当用户在收听的时候,电话来了,App 的声音按照期望中的那样停止了。用户选择了拒接这个电话,关闭了闹钟,然后点击播放按钮来继续收听,却发现未能如愿。用户必须重启 App 才能恢复后台播放。
要优雅的处理这种音频队列的中断,开发者需要设置合适的类别,注册 <code>AVAudioSessionInterruptionNotification</code> 通知,并让 App 对不同的通知作出相应的反应。
系统怎样解决音频需求之间的竞争
在 App 启动时,系统的内置 App(短信、音乐、Safari、电话等)可能会在后台运行。这些内置的 App 可能会播放声音,比如收到短信后。
如果将 IOS 设备看作一个飞机场,把 App 看作滑行的飞机,那么系统就是调度中心。飞机向调度中心发布一个使用声音的请求,同时声明它需要的优先级,而最终何时获得超过“正在跑道上”使用音频的其他飞机的权限由调度中心决定。App 通过音频会话来跟系统进行交互。下图描述了一个典型的场景:你的 App 想要在音乐 App 正在播放音乐的时候播放声音。这种情况下,你的 App 会中断音乐 App。
<small>
第一步,App 请求开启它的音频会话。这种请求可能会在 App 加载过程中产生,也可能会在响应用户点击某个播放按钮的事件中产生。第二步,系统开始处理这次请求。图中的 SpeakHere App 使用了需要其他音频静音的类别。
在第三和第四步中,系统关闭了音乐 App 的音频会话,停止了音乐在后台的播放。最终,系统开启了 SpeakHere 的音频会话,SpeakHere 可以开始播放声音了。
系统对于是否开启或关闭设备上音频会话有最终的决定权。决策过程中,电话总是拥有最高的优先权,没有 App 的音频权限能够超过电话。接到电话后,无论当前正在执行什么音频动作或是设置了哪种音频类别,App 都会被中断,用户都会获得“你接到了电话”的提醒。
</small>
集成 <code>AVCaptureSession</code>
<code>AV Foudation</code> 中的捕获 API(<code>AVCaptureDevice</code>、<code>AVCaptureSession</code>)可以使开发者得到同步获取来自相机或麦克风的音频或视频输入。在 IOS7 中,表示麦克风输入的 <code>AVCaptureDevice</code> 对象可以共享 App 的 <code>AVAudioSession</code>。默认情况下,<code>AVCaptureSession</code> 会在使用麦克风的时候会给 <code>AVAudioSession</code> 设置最适合录音的配置。如果将 <code>automaticallyConfiguresApplicationAudioSession</code> 属性设为 <code>NO</code>,这种默认配置会被当前开发者的AVAudioSession配置覆盖,<code>AVCaptureDevice</code> 也会不加修改的采用开发者的配置。在 AVCaptureSession Class Reference 和 Media Capture 中可以获得更多相关信息。
初始化音频会话
系统在 App 的加载过程中提供了一个音频会话的对象。在处理中断之前,开发者必须初始化这个会话。
<code>AV Foundation</code> 框架会利用开发者获取对 <code>AVAudioSession</code> 对象的引用时触发的隐式初始化,来管理中断。
//隐式初始化音频会话
AVAudioSession *session = [AVAudioSession sharedInstance];
<code>session</code> 变量代表了一个已经被初始化且可以马上使用的音频会话。官方推荐在使用 <code>AVAudioSession</code> 类中的中断通知,或 <code>AVAudioPlayer</code> 和 <code>AVAudioRecorder</code> 的代理协议来处理音频中断时,隐式的初始化音频会话。
添加音量和音频源管理
<code>MPVolumeView</code> 类提供了在 App 中控制音量和音频源的方法。音量视图提供了一个控制音量的滑块和一个选择音频输出源的按钮。官方建议在将音频源切换到内置扬声器时,使用 <code>MPVolumeView</code> 的音频源选择器(route picker)而不是 <code>AVAudioSessionPortOverride</code>。详情见 MPVolumeView Class Reference。
响应遥控器事件
用户可以通过遥控器事件来控制 App 中的多媒体。开发者可能希望 App 中播放的音频或视频内容对来自 transport controls 或外接辅助设备的遥控事件做出响应。IOS 将这些命令转化为 <code>UIEvent</code> 对象分发给 App。App 接收到事件后将它们发送给对应的 first responder。如果 first responder 不对事件进行处理,那么事件将会在 responder 链中向上传递。
只有正在播放音频、且具有“Now Playing”信息的 app,才能对遥控器事件做出响应。详情见 Remote Control Events和MPNowPlayingInfoCenter Class Reference。
开启或关闭音频会话
虽然系统在 App 加载的时候自动开启你的音频会话,但苹果官方推荐的做法是在 App 的 <code>viewDidLoad</code> 方法中显式的开启,并在开启前设置合适的硬件参数。为 App 进行硬件优化 中有相关的示例代码。通过这种方式,开发者可以测试音频会话是否成功开启。如果 App 中包含一个类似播放/暂停的 UI 元素,在用户按下播放键时开启会话则是更好的方式。在切换音频会话的开启/关闭状态时,对是否成功的切换了会话状态做出检查是有必要的。开发者应在代码中对系统驳回的请求进行优雅的处理。
系统会在闹钟提醒、日历提醒或接到电话时将关闭你的音频会话。当用户关闭提醒或拒接电话后,系统会允许你重新开启会话。在中断结束后是否重新开启音频会话由 App 的类型决定,[Audio Guidelines By App Type](Audio Guidelines By App Type.md) 中有相关的介绍。
下面的代码展示了如何开启音频会话。
NSError *activationError = nil;
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) { /* handle the error in activationError */ }
将 <code>setActive</code> 的参数设置为 <code>NO</code> 可以关闭会话。
如果使用 <code>AVAudioPlayer</code> 或 <code>AVAudioRecorder</code> 来播放或录制音频时,系统会负责在中断结束后重新开启音频会话。然而,官方推荐通过注册消息通知来显式开启会话,来保证会话成功开启并对 App 的状态和 UI 进行更新。
大多数 App 不需要显式的关闭音频会话。一些需要显式关闭的特例包括 VoIP(网络电话)App、逐向(在转弯时对用户做出提醒)导航 App 和某些录音 App。
对于一般在后台运行网络电话 App,要保证它的音频会话在处理通话时是开启的,而在后台准备接收通话时则处于关闭状态。
对于使用录音类别的 App 的音频会话仅在录音时开启。在录音开始前和录音结束后要关闭会话以保证类似短信提示音的其他声音能够顺利播放。
App加载时检查是否存在正在播放的其他音频
在用户打开 App 时,设备可能正在播放其他的声音:音乐 App 可能正在播放音乐,或者浏览器正在播放流媒体。这种情况产生的影响对于游戏 App 来说可能更为显著。许多游戏会有自己的背景音乐和音效。IOS Human Interface Guidelines 建议开发者假定用户在玩游戏时希望保持他们原来播放的音频作为背景音乐,同时保留游戏的音效。
检查 <code>otherAudioPlaying</code> 的属性值来判断 App 加载过程中是否正在播放其他音频;如果是的话,将游戏的背景音乐静音,并使用 <code>AVAudioSessionCategorySoloAmbient</code> 类别。详情见音频会话类别(coming soon)。
跨 App 音频(Inter-App Audio)
跨 App 音频的最基础的使用形式是通过一个节点(node) app 将它的音频输出到另一个寄主(host) App。寄主 App 可能会将它的输出发送给节点 App,经过节点 App 的处理后,将处理结果反馈给寄主 App。寄主 App 需要一个始终处于开启状态的音频会话,而节点 App 只需要在从寄主 App 或系统接收音频输入时开启音频会话。根据以下方案来设置跨 App 的音频:
- 为寄主 App 和节点 App 设置“inter-app-audio”权限
- 为寄主 App 在 <code>UIBackgoundModes</code> 添加 <code>audio</code> 属性。
- 为使用音频输入输出源、同时连接到跨 App 音频寄主的节点 app 的 <code>UIBackgoundModes</code> 添加 <code>audio</code> 属性。
- 将寄主和节点 App 的类别设为 <code> AVAudioSessionCategoryOptionMixWithOthers</code>。
- 对于连接到寄主的节点 App,保证它的音频会话在收到系统的音频输入或产生音频输出时处于开启状态。
以上内容翻译自苹果官方文档,仅供学习,请勿用于商业用途,侵删。转载注明出处。