首页 关于 微信公众号
欢迎关注我的微信公众号

AVAudioSession Programming Guide

介绍

在ios中,处理跨应用,设备级别以及应用本身中的音频是通过 AVAudioSession 来处理的。

使用 AVAudioSession 你可以处理一下问题:

AVAudioSession 提供给你处理app中音频的表现行为:

定义一个 Audio Session

audio session(音频会话) 是你的app和其它配置了audio行为的app之间的桥梁。在你的app启动的时候,会自动得到一个单例audio session. 可以通过配置这个 audio session 来表达你app中的音频表现。例如:

Audio Session 默认的行为

Audio Session(音频会话)带有一些默认的行为。特别是:

所有上述行为由默认音频会话类别AVAudioSessionCategorySoloAmbient`提供。 使用类别提供了有关如何将类别合并到应用程序中的信息。

当你开始录制或播放一个音频的时候,你的音频会话自动开启;然而,依赖于默认激活对于您的应用程序是一种风险状态。例如,如果iPhone响起并且用户忽略了呼叫,则您的应用程序正在运行 - 您的音频可能无法播放,具体取决于您使用的播放技术。下一节描述了处理这些问题的一些策略,“响应中断”深入讨论了这一主题。

在开发中,你可以利用这些默认行为。但是,只有这些时间,您才能安全地忽略应用程序的音频会话:

在所有其他情况下,请不要使用默认音频会话在应用程序中。

为什么默认的 Audio Session 通常不是你想要的

如果您不初始化,配置和明确使用您的音频会话,您的应用程序不能响应中断或音频硬件路由更改。此外,您的应用程序无法控制操作系统决定关于应用程序之间的音频混合。

以下是一些演示音频会话默认行为以及如何更改它的方案:

系统如何解决竞争的 Audio Session 需求

随着 ios 应用启动,系统内建的应用(如 Message,Music,Safari)可能会在后台运行。这些应用中的每一个都有可能会产生一个音频:一条信息到达,你10分钟之前播放的博客继续播放等。

如果你认为一个 ios 设备是一个飞机场,那么app就相当于每一架飞机,系统的一系列服务就相当于一些控制塔台。你的应用可以发出音频请求并制定所需的优先级,但对于在停机坪上发生的事情的最终权威来自系统。您使用音频会话与“控制塔台”通信。下图说明了一个典型的情况 - 您的应用想要在音乐应用已经在播放时使用音频。 在这种情况下,您的应用程序会中断音乐应用程序。

系统具有激活或停用设备上存在的任何音频会话的最终权限。 系统遵循不可侵犯的规则,“电话总是赢得”。没有应用程序,无论它需要优先,多么强烈,可以胜过电话。 当呼叫到达时,用户会收到通知,您的应用程序会中断 - 无论您正在进行什么音频操作,无论您设置了什么类别。

结合 AVCaptureSession

AVFoundation 捕获 API(AVCaptureDevice,AVCaptureSession)使您能够从摄像头和麦克风输入捕获同步的音频和视频。从 ios7开始,代表麦克风输入的AVCaptureDevice对象可以共享您的应用程序的AVAudioSession。默认情况下,AVCaptureSession将在您的AVCaptureSession使用麦克风时最佳地配置您的AVAudioSession以进行录制。将automaticallyConfiguresApplicationAudioSession属性设置为NO以覆盖默认行为,AVCaptureDevice将使用您当前的AVAudioSession设置,而不更改它们。

初始化你的 Audio Session

AVAudioSession *session = [AVAudioSession shareInstance];

添加音量和线路控制

使用MPVolumeView类来呈现应用程序的音量和路由控制。音量视图提供了一个用于控制应用程序内部音量的滑块以及用于选择输出音频路径的按钮。当将音频路由到内置扬声器时,Apple建议使用AVAudioSessionPortOverride上的MPVolumeView线路选择。

响应远程控制时间

远程控制事件让用户控制应用的多媒体。如果您的应用播放音频或视频内容,您可能希望其响应来自传输控制或外部配件的遥控事件。iOS转换UIEvent对象中的命令,并将事件传递给应用程序。 应用程序将它们发送到第一个响应者,如果第一个响应者不处理它们,它们会在响应者链上向上传播。您的应用程式必须是「正在播放」的应用程序,目前正在播放音频,才能回应活动。

设置你的 Audio Session 为活动和不活动状态

系统在应用启动时激活您的音频会话。 即使如此,Apple建议您在viewDidLoad方法中明确激活会话,并在激活音频会话之前设置首选硬件值。看Setting Preferred Hardware Values 代码示例。这使您有机会测试激活是否成功。但是,如果您的应用程序有播放/暂停UI元素,请编写您的代码,以便用户必须在激活会话之前按播放。同样,当更改音频会话的活动/非活动状态时,请检查以确保呼叫成功。编写代码以正常处理系统拒绝激活会话。

系统拒绝激活你的音频会话可能是因为一个闹钟,或者是一个呼入的电话。当用户关闭警报或选择忽略电话时,系统将允许您的会话再次处于活动状态。是否在中断结束时重新激活会话取决于应用类型,如所述 Audio Guidelines By App Type

激活一个Audio Session 使用 AVFoundation中的:

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

在使用AVAudioPlayer或AVAudioRecorder对象播放或记录音频的特定情况下,系统在中断结束时负责音频会话重新激活。 尽管如此,Apple建议您注册通知消息并明确重新激活您的音频会话。 因此,您可以确保重新启动成功,并且您可以更新应用程序的状态和用户界面。

在我们的应用启动时检测其它应用是否正在播放音频

用户启动您的应用时,声音可能已在设备上播放。 例如,音乐应用程序可能正在播放音乐,或者Safari可能是您的应用程序启动时的流式音频。 如果你的应用程序是一个游戏,这种情况尤其突出。许多游戏都有音乐声轨以及声音效果。在这种情况下,[ios Human Interface Guidelines]建议您假设用户期望其他音频继续播放游戏,同时仍然播放游戏的声音效果。

检查otherAudioPlaying属性,以确定在您启动应用程序时是否已经在播放音频。 如果应用程式启动时播放其他音讯,请将您的游戏音轨设为静音,然后指派AVAudioSessionCategorySoloAmbient类别。 有关类别的详细信息,请参阅working with category

使用类别

音频会话类别是标识应用程序的一组音频行为的键。 通过设置类别,您可以向系统指示您的音频意图,例如,在翻转铃声/静音开关时是否应继续播放音频。 iOS中的七个音频会话类别,以及一组覆盖和修改开关,可让您自定义应用程序的音频行为。

每个音频会话类别为以下行为中的每一个指定特定模式“是”和“否”,具体:

大多数应用只需在启动时设置一次类别。 也就是说,您可以根据需要频繁更改类别,无论您的会话是活动还是非活动,都可以这样做。 如果您的会话处于非活动状态,则在激活会话时会发送类别请求。 如果您的会话已处于活动状态,则会立即发送类别请求。

选择最合适的类别

与每个类别相关联的精确行为不在您的应用程序的控制之下,而是由操作系统设置。苹果可能会在未来版本的iOS中改进类别行为。您最好的策略是选择最准确地描述您想要的音频行为的意图的类别。附录Audio Session Categories and Modes总结每个类别的行为详细信息。

以下列表描述了与其关联的类别和音频行为。 AVAudioSessionCategoryAmbient类别允许其他音频继续播放; 也就是说,它是一个可混合的应用程序。 其余类别表示您希望在会话变为活动状态时停止其他音频。 但是,您可以自定义非混音“播放”和“播放和录制”类别以允许混音,如Fine-Tuning a Category所述。

使用 Multiroute Category 扩展选项

多路径类别的工作方式与其他类别稍有不同。 所有类别都遵循“最后胜利”规则,决定于其中插入输入或输出线路的最后一个设备。 但是,多线路类别使应用程序能够使用所有连接的输出端口,而不仅仅是最后一个端口。 例如,如果您通过HDMI输出路径收听音频并插入一套耳机,则音频会通过耳机播放。 您的应用程序可以通过HDMI输出路径继续播放音频,同时通过耳机播放音频。

您的应用程序可以将不同的音频流发送到不同的输出线路。 例如,您的应用程序可以向左耳机发送一个音频流,向右耳机发送另一个音频流,以及向HDMI路由发送第三个音频流。 图2-1显示了将多个文件发送到不同音频路由的示例。

根据设备和任何连接的附件,以下是有效的输出路由组合:

多路由类别支持使用单个输入端口。

设置 Audio Session 的类别

于大多数iOS应用程序,在启动时设置音频会话类别 - 从不更改它 - 很好。 这提供了最佳的用户体验,因为设备的音频行为在您的应用运行时保持一致。

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

使用模式专门化类别

虽然类别设置了应用程序的基本行为,但模式用于专门设置音频会话类别。 设置类别的模式以进一步定义应用程序的音频行为。 有七种模式可供选择:

为 AirPlay选择类别和模式

只有特定类别和模式支持AirPlay。 以下类别支持Airplay的镜像版本和非镜像版本:

AVAudioSessionCategoryPlayAndRecord类别仅支持镜像的Airplay。

模式仅在与播放和记录类别结合使用时才支持AirPlay。 以下模式仅支持AirPlay镜像:

微调类别

您可以通过多种方式微调音频会话类别。 根据类别,您可以:

您可以覆盖AVAudioSessionCategoryPlayback,AVAudioSessionCategoryPlayAndRecord和AVAudioSessionCategoryMultiRoute类别的中断特性,以便允许其他音频与您的音频混合。 要执行覆盖,请将AVAudioSessionCategoryOptionMixWithOthers属性应用于音频会话。 当您将应用设置为可混合时,您的应用在其音频会话变为活动状态时不会中断来自其他不可混合应用的音频。 此外,您应用的音频不会被其他应用的不可混合音频会话(例如音乐应用)中断。

您可以以编程方式影响音频输出线路。 当使用AVAudioSessionCategoryPlayAndRecord类别时,音频通常发送到接收器(在打电话时,您握住耳朵的小扬声器)。 您可以使用overrideOutputAudioPort:error:方法将音频重定向到手机底部的扬声器。

最后,您可以增强类别,以在播放音频时自动降低其他音频的音量。 这可以用于,例如,在锻炼应用程序。 假设用户正在练习音乐应用程序,当您的应用程序想要覆盖语言消息时,例如,“你已经划船10分钟”。要确保来自您的应用程序的消息是可理解的,请应用AVAudioSessionCategoryOptionDuckOthers属性 您的音频会话。 当低音发生时,设备上的所有其他音频(除了手机音频)降低音量。 使用回避的应用程序必须管理其会话的激活状态。 在播放音频之前激活音频会话,在播放音频后取消激活会话。

响应中断

添加音频会话代码以处理中断,可确保在电话来电,闹钟或日历闹钟响起,或其他应用程序激活其音频会话时,应用的音频会继续正常运行。

音频中断是指应用程序的音频会话停用 - 根据您使用的技术,会立即停止或暂停您的音频。 当来自应用程序的竞争音频会话激活并且该会话没有被系统分类以与您的混合时,中断发生。 在会话变为非活动状态后,系统将发送“您被中断”消息,您可以通过保存状态,更新用户界面等来响应。

你的app可能会暂时挂起当一个中断到来时。 当用户决定接受电话呼叫时,会发生这种情况。 如果用户改为选择忽略呼叫或关闭报警,系统将发出中断结束的消息,并且您的应用程序继续运行。 要恢复音频,您的音频会话必须重新激活。

音频响应中断处理技术

通过注册通知的形式来处理音频中断。您在中断代码中做什么取决于您使用的音频技术以及您正在使用它进行播放,录制,音频格式转换,读取流式音频包等。一般来说,从用户的角度看,你要尽可能小的扰乱应用,并且尽快的恢复应用状态。

表3-1总结了中断期间的适当音频会话行为。 如果使用AVAudioPlayer或AVAudioRecorder对象,则系统会自动处理这些步骤中的一些步骤。

表3-2总结了几种音频技术的中断

音频技术 如何处理中断
AV Foundation framework AVAudioPlayerAVAudioRecorder都提供了相应的代理方法来处理音频中断。系统会在中断时自动暂停播放或录制,并在恢复播放或录制时重新激活音频会话。
如果要在应用启动的时候保存和恢复播放位置,要在中断时以及退出应用时保存播放位置。
Audio Queue Services, I/O audio unit 这些技术使您的应用程序控制处理中断。 您负责保存播放或录制位置,并在中断结束后重新启动音频会话。
OpenAL 当使用OpenAL播放时,注册适当的NSNotification通知 - 当使用音频队列服务时。 但是,代理必须另外管理OpenAL上下文。 请参见OpenAL和音频中断。
System Sound Services 使用系统声音服务播放的声音在中断开始时保持静音。 如果中断结束,它们可以自动再次使用。 应用程序不能影响使用此播放技术的声音的中断行为。

处理来自 Siri 的音频中断

当您的应用程序在Siri播放期间中断时,您必须跟踪Siri发出的任何遥控命令,同时音频会话中断。 在中断状态期间,跟踪由Siri发出的任何命令,并且在中断结束时相应地进行响应。 例如,在中断期间,用户要求Siri暂停应用程序的音频播放。 当应用程序收到中断已结束的通知时,它不应自动恢复播放。 相反,您的应用的用户界面应该指示它处于暂停状态。

优化你应用程序的硬件设备

利用 audio session 的属性,你可以优化你应用程序音频的行为在硬件设备的层面上。

音频会话的属性能让你做以下操作:

设置首选硬件值

在激活音频会话之前设置首选硬件值。 当应用程序设置首选值时,它不会生效,直到音频会话被激活。 在音频会话重新激活后,请验证所选的值。 当您的应用程序正在运行时,Apple建议您在更改任何设置值之前停用音频会话。 清单4-1显示了如何设置首选硬件值以及如何检查所使用的实际值。

NSError *audioSessionError = nil;
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:&audioSessionError];
if (audioSessionError) {
     NSLog(@"Error %ld, %@, (long)audioSessionError.code, audioSessionError.localizedDescription);
}
 
NSTimeInterval bufferDuration =.005;
[session setPreferredIOBufferDuration:bufferDuration error:&audioSessionError];
if (audioSessionError) {
     NSLog(@"Error %ld, %@, (long)audioSessionError.code, audioSessionError.localizedDescription);
}
 
double sampleRate = 44100.0
[session setPreferredSampleRate:samplerate error:&audioSessionError];
if (audioSessionError) {
     NSLog(@"Error %ld, %@, (long)audioSessionError.code, audioSessionError.localizedDescription);
}
 
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleRouteChange:)
                                             name:AVAudioSessionRouteChangeNotification
                                           object:session];
 
[session setActive:YES error:&audioSessionError];
if (audioSessionError) {
     NSLog(@"Error %ld, %@, (long)audioSessionError.code, audioSessionError.localizedDescription);
}
 
sampeRate = session.sampleRate;
bufferDuration = session.IOBuffferDuration;
NSLog(@"Sampe Rate:%0.0fHZ I/O Buffer Duration:%f", sampleRate, bufferDuration);

查询硬件特征

ios设备的硬件特征可以在你应用运行的时候去改变,并且不同的设备也有不同的特征值。例如,当您对原始iPhone使用内置麦克风时,录制采样率限制为8 kHz; 连接耳机和使用耳机麦克风提供较高的采样率。 较新的iOS设备支持内置麦克风的更高硬件采样率。您应用的音频会话可以告诉您设备的许多硬件特性。 这些特性可以在运行时改变。 例如,当用户插入耳机时,输入采样率可能改变。 有关属性的完整列表,请参阅“AVAudioSession类参考”。

在指定首选硬件特性之前,请确保音频会话处于非活动状态。 建立您的首选项后,激活会话,然后查询它以确定实际特性。 这最后一步很重要,因为在某些情况下,系统不能提供您要求的特性值(有可能设备不支持)。

要获取有意义的硬件特性值,请确保在发出查询之前,音频会话已初始化并处于活动状态。

两个最有用的音频会话硬件属性是sampleRate和outputLatency。 sampleRate属性包含设备的硬件采样率。 outputLatency属性包含设备的播放延迟。

指定首选硬件I / O缓冲区持续时间

使用AVAudioSession类来指定首选硬件采样率和首选硬件I / O缓冲区持续时间,如清单4-2所示。 要设置首选采样率,您需要使用类似的代码:

NSError *setPreferenceError = nil;
NSTimeInterval preferredBufferDuration = 0.005;
[[AVAudiosession sharedInstance]
            setPreferredIOBufferDuration: preferredBufferDuration
                                   error: &setPreferenceError];

响应音频线路变化

随着应用程序运行,用户可能会插入或拔下耳机,或使用带音频连接的扩展坞。 编写音频会话代码以处理音频硬件线路更改。 某些类型的应用程序(如游戏)并不总是对线路更改做出响应。 但是,其他类型的应用程序(如媒体播放器)必须响应所有线路更改。

音频硬件线路变化的种类

音频硬件线路是用于音频信号的有线电子路径。当iOS设备的用户插入或拔出耳机时,系统自动更改音频硬件线路。 您的应用程序可以通过AVAudioSessionRouteChangeNotification方式监听此类更改。

响应音频硬件线路更改

相应应用的音频线路更改需要做两部分:

  1. 实现音频线路更改时调用的方法
  2. 监听音频线路更改的通知 AVAudioSessionRouteChangeNotification

例如,当用户在播放期间拔下耳机时,您的应用会收到通知。 按照苹果指南,您的应用程序暂停。 然后,您的应用可以提供一个显示,提示用户继续播放。

当系统发送音频线路更改通知时,它提供您需要了解要采取的操作的信息。 注册AVAudioSessionRouteChangeNotification通知,如图所示:

NSNotificationCenter *nc [NSNotificationCenter defaultCenter];
[nc addObserver:self
       selector:@selector(routeChanged:)
           name:AVAudioSessionRouteChangeNotification
         object:nil];

收到通知后,您的应用会调用您指定的方法,并根据通知中包含的信息更改其行为。 AVAudioSessionRouteChangeNotification 中的 userInfo 自爱的那包含了一下信息:

通知中的字典信息的 key 是 AVAudioSessionRouteChangeReasonKeyAVAudioSessionRouteChangePreviousRouteKey 。 硬件音频线路更改的各种原因,通过 AVAudioSessionRouteChangeReasonKey 来获取,并在 AVAudioSessionRouteChangeReason中有描述。由AVAudioSessionRouteChangePreviousRouteKey键访问的先前音频线路信息是描述从线路改变之前的音频线路设置的对象。

在iOS中音频硬件线路更改的一个原因是AVAudioSessionRouteChangeReasonCategoryChange。 换句话说,系统在该上下文中认为音频会话类别的改变是线路改变,并且将调用线路改变通知。 因此,如果您的通知响应方法旨在仅响应耳机插入和拔出,则应明确忽略此类线路更改。

Blog

Opinion

Project