Core Audio
Core Audio 编程接口分层(官方文档)
Low-Level Services
I/O Kit:与硬件驱动交互
Audio HAL:音频硬件抽象层,使API调用与实际硬件相分离
Core MIDI:为MIDI流和设备提供软件抽象工作层
Host Time Services:访问硬件时钟
Mid-Level Services
Audio Convert Services:负责音频数据格式的转换
Audio File Services:负责音频数据读写
Audio Unit Services 和 Audio Processing Graph Services 使应用程序可以使用数字信号处理(DSP)插件,例如均衡器和混频器
Core Audio Clock Services:用于音频和MIDI同步以及时间格式管理
Audio File Stream Services:创建可以解析流的应用程序,负责流解析,对音频进行解码
High-Level Services
AVAudioPlayer:高级接口,可以完成整个音频播放的过程
Audio Queue Services:录制、播放、暂停、循环、同步音频
Extended Audio File Services:Audio File Services 和 Audio Converter services的结合体
OpenAL:游戏音频
Audio Session
官方文档 Audio Session Programming Guide
概览
我们可以使用 AVAudioSession 实例与应用程序的音频会话进行交互
- 配置音频会话类别和模式,告诉系统使用音频的方式
- 激活音频会话使设置的类别和模式配置生效
- 订阅并响应音频会话通知,例如音频中断和路由更改
- 执行高级别的音频设备配置,例如采样率,I/O缓冲持续时间和通道数
1、配置 Audio Session
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSession.Category.playback,
mode: AVAudioSession.Mode.moviePlayback,
options: [])
} catch {
print("Failed to set the audio session category and mode: \(error.localizedDescription)")
}
Category 设置应用基本音频行为,可以通过mode进一步设置这些行为;
如IP语音(VoIP)使用AVAudioSessionCategoryPlayAndRecord
,mode设置为AVAudioSessionModeVoiceChat
,此模式可确保通过系统提供的信号处理来优化语音信号
某些Category通过会话上设置一个或多个Category选项来支持覆盖默认行为,如AVAudioSessionCategoryPlayback
类别的默认行为会在激活会话时中断其他系统音频,如果您希望音频与其他系统音频混合,则可以通过AVAudioSessionCategoryOptionMixWithOthers
在会话上设置选项来覆盖此行为
Category | 通过铃声/静音开关或锁屏是否需要静音 | 是否中断不可以混合的音频 | 支持录音和播放 |
---|---|---|---|
AVAudioSessionCategoryAmbient | Yes | NO | Output only |
AVAudioSessionCategorySoloAmbient (Default) | Yes | YES | Output only |
AVAudioSessionCategoryPlayback | NO | Yes by default; no by using override switch | Output only |
AVAudioSessionCategoryRecord | No (recording continues with screen locked) | YES | Input only |
AVAudioSessionCategoryPlayAndRecord | No | Yes by default; no by using override switch | Input and output |
AVAudioSessionCategoryMultiRoute | No | YES | Input and output |
大多数应用在启动时只需要设置一次Category,但可以根据需要更改Category,可以在音频会话处于激活状态进行更改,最好在更改Category或者其他会话属性前停用音频会话,停用会话的同时进行这些更改可以防止音频系统不必要的重新配置
枚举mode
Mode identifiers | Compatible categories |
---|---|
AVAudioSessionModeDefault | All |
AVAudioSessionModeMoviePlayback | AVAudioSessionCategoryPlayback |
AVAudioSessionModeVideoRecording | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord |
AVAudioSessionModeVoiceChat | AVAudioSessionCategoryPlayAndRecord |
AVAudioSessionModeGameChat | AVAudioSessionCategoryPlayAndRecord |
AVAudioSessionModeVideoChat | AVAudioSessionCategoryPlayAndRecord |
AVAudioSessionModeSpokenAudio | AVAudioSessionCategoryPlayback |
AVAudioSessionModeMeasurement | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback |
Audio Session 默认行为
- 支持音频播放,但不允许录音
- iOS中,将"铃声/静音"开关设置为静音模式会使应用播放的任何音频静音
- iOS中,将设备锁定时,应用程序的音频将静音
- 当你的应用播放音频时,其他任何背景音频(如音乐应用正在播放的音频)都将静音
多路由Category扩展
多路由Category使应用程序可以使用所有连接的输出端口,而不仅仅使用最后的使用端口,例如,你正在通过HDMI输出路径收听音频并插入耳机,则你的应用将继续通过HDMI输出路径输出音频,同时还通过耳机播放音频
还可以将不同的音频流发送到不同的输出路由,例如,应用可以将一个音频发送到左耳机,将另一个音频发送到右耳机,将第三个音频流发送到HDMI路由
有效输出路径组合
- USB和耳机
- HDMI和耳机
- LineOut和耳机
注:仅当未连接任何其他输出端口(USB,HDMI,LineOut)时,才可以使用内置扬声器
选择AirPlay的Category和mode
仅特定Category和mode支持AirPlay,以下类别通知支持AirPlay的镜像和非镜像版本
- AVAudioSessionCategorySoloAmbient
- AVAudioSessionCategoryAmbient
- AVAudioSessionCategoryPlayback
AVAudioSessionCategoryPlayAndRecord 类别和以下mode仅支持AirPlay镜像版本 - AVAudioSessionModeDefault
- AVAudioSessionModeVideoChat
- AVAudioSessionModeGameChat
从iOS 10开始,您可以通过使用选项AVAudioSessionCategoryPlayAndRecord
激活会话来启用使用类别时的非镜像AirPlay输出AVAudioSessionCategoryOptionAllowAirPlay
背景音频
Capabilities 打开 Background Modes 的 Audio,AirPlay,and Picture in Picture
2、激活 Audio Session
let session = AVAudioSession.sharedInstance()
do {
//1)configure your audio session category, options, and mode
//2)active your audio session to enable your custom configuration
try session.setActive(true)
} catch {
print("Unable to active audio session: \(error.localizedDescription)")
}
使用AVFoundation对象(AVPlayer,AVAudioRecoder)播放或录制音频时,系统会在中断结束时重新激活音频会话,但是如果注册了通知消息并显式重新激活音频会话,则可以验证重新激活成功,还可以更新应用程序的状态和用户界面
检测是否正在播放其他音频
func setupNotification() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleSecondaryAudio(notification:)),
name: AVAudioSession.silenceSecondaryAudioHintNotification,
object: AVAudioSession.sharedInstance())
}
@objc func handleSecondaryAudio(notification: Notification) {
guard let userinfo = notification.userInfo ,
let typeValue = userinfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey] as? UInt,
let type = AVAudioSession.SilenceSecondaryAudioHintType(rawValue: typeValue) else {
return
}
if type == .begin {
//其他应用音频开始播放 - 将辅助音频静音
} else {
//其他应用音频停止播放 - 重新启动辅助音频
}
}
响应中断
中断生命周期
- 应用处于活跃状态,正在播放音频
- FaceTime请求到达,系统激活FaceTime的音频会话
- 系统将停用你的音频会话,此时你的应用播放停止
- 系统发布通知,通知你的会话已被停用
- 你的应用处理通知,如更新界面保存停止播放点继续播放所需的信息
- 如果用户取消中断(忽略FaceTime请求),系统发送通知我们应用中断结束
- 你的应用处理中断结束操作,如更新界面重新激活音频会话并恢复播放
- 如果没有6的中断,而是接听了电话,你的应用将被暂停
音频中断处理
中断开始后:保存状态和上下文,更新用户界面
中断结束后:恢复状态和上下文,更新用户界面,重新激活音频会话观察音频中断
func registerForNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption(notification:)),
name: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance())
}
@objc func handleInterruption(notification: Notification) {
guard let userinfo = notification.userInfo,
let typeValue = userinfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
//中断开始 采取适当措施(保存状态更新界面)
} else if type == .ended {
guard let optionsValue = userinfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
//中断结束 恢复播放
}
}
}
响应路由变更
当用户插入或拔出耳机时,系统会自动更改音频硬件路由
你的应用启动后,系统会首先确定音频路由,应用运行时,它将继续监听活动路由
录制期间,用户可以插入和拔出耳机,作为响应,系统发送包含更改原因和先前路由的路由更改通知,应用停止录制
- 观察音频路由变更
func setupNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(handleRouteChange(notification:)),
name: AVAudioSession.routeChangeNotification,
object: AVAudioSession.sharedInstance())
}
@objc func handleRouteChange(notification: Notification) {
guard let userinfo = notification.userInfo,
let reasonValue = userinfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
return
}
switch reason {
case .newDeviceAvailable:
print("处理可用新设备")
case .oldDeviceUnavailable:
print("处理旧设备")
default:
()
}
}
当有新设备时,可以查询音频会话currentRoute属性,返回AVAudioSessionRouteDescription对象,其中列出了音频会话的所有输入和输出
如果路由更改原因是原因AVAudioSessionRouteChangeReasonOldDeviceUnavailable
,则媒体播放应用应暂停播放,但如果原因是,则不应暂停播放AVAudioSessionRouteChangeReasonOverride
配置设备硬件
设置 | 首选采样率 | 首选I/O缓冲区持续时间 |
---|---|---|
High value | 示例:48Hz +高音质 -大文件或缓冲区大小 | 示例:500ms +较少的文件访问 -更长的延迟 |
Low value | 示例:8Hz +大文件或缓冲区大小 -低音频质量 | 示例:5ms +低延迟 -频繁的文件访问 |
如果音频质量在你应用中非常重要,并且文件或缓冲区的大小不是主要问题,则可以指定高采样率的首选项
默认音频I/O缓存持续时间(44.1kHz约0.02s)为大多数应用提供了足够的响应速度,可以对延迟有严格要求的应用(如现场乐器监控)设置较低的I/O持续时间,但对大多数应用,无需修改此设置
//Category and mode
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.record, mode: .default, options: [])
} catch {
print("Unable to set Category: \(error.localizedDescription)")
}
//Set preferred sample rate
do {
try session.setPreferredSampleRate(44_100)
} catch {
print("Unable to set preferred sample rate \(error.localizedDescription)")
}
//Set preferred I/O buffer duration
do {
try session.setPreferredIOBufferDuration(0.005)
} catch {
print("Unable to set preferred I/O bufferr duration \(error.localizedDescription)")
}
//Active the audio session
do {
try session.setActive(true)
} catch {
print("Unable to active session \(error.localizedDescription)")
}
选择和配置麦克风
设置首选输入
要发现内置或已连接的输入端口,使用音频会话的availableInputs
属性,返回一个AVAudioSessionPortDescription
对象数组,这些对象描述设备的可用输入端口,可用通过端口protType
属性标识端口,要设置首选输入端口(内置麦克风,有线麦克风,USB输入等)使用音频会话的setPreferredInput:error:
方法设置首选数据源
某些端口(如内置麦克风和某些USB附件)支持数据源,可用通过查询端口描述的DataSource
属性发现可用数据源。
对于内置麦克风,返回的数据源描述对象代表每个单独的麦克风,不同设备的内置麦克风返回不同的值。如iPhone4
和iPhone4s
有两个麦克风:底部和顶部
可通过数据源描述的location
属性(上部下部)和orientation
属性(正面背面)的组合来标识各个内置麦克风,使用setPreferredDataSource:error:
方法设置首选数据源AVAudioSessionPortDescription
设置首选极性图案
某些iOS设备支持为某些内置麦克风配置麦克风极性模式,麦克风的极性模式定义了其对声音相对于声源方向的灵敏度
supportedPolarPatterns
数据源描述对象的属性返回可用模式。此属性返回数据源支持的极性图案的数组,例如心形或全向,或者nil
在没有可用的可选图案时返回。如果数据源具有许多受支持的极性图案,则可以使用数据源描述的[setPreferredPolarPattern:error:
方法来设置首选的极性图案