iOS的CoreAudio分为三层:应用服务层、驱动层、硬件层。其中,应用服务层包括:AudioQueue Service、AudioPlayer Service、AudioSession Service、AudioFile Service、AudioUnit等。
一、CoreAudio整体架构
CoreAudio的整体架构自顶向下是Service层、Driver层、Hardware层,如下图所示:
二、Service层级架构
Service层按照等级分层,自顶向下是High-Level、Mid-Level、Low-Level。其中,高级别Service有AVAudioPlayer、AudioQueue、OpenAL等;中级别Service有AudioConverter、AudioFile、AudioUnit等;低级别Service有IOKit、Auido HAL等。如下图所示:
三、 AudioQueue
AudioQueue可用于音频播放、录音。我们不用了解硬件接口,就可以使用硬件录制和播放设备
1、播放
AudioQueue播放的数据源采用回调方式获取,提供缓冲队列,最终输出给扬声器播放。如下图所示:
2、录音
AudioQueue录音的数据源来自麦克风,内部提供缓冲队列,最终回调给业务层。如下图所示:
四、AudioConverter
AudioConverter提供音频格式转换。以mp3转aac为例,输入mp3解码得到pcn,然后pcm编码成aac。转换流程如下图所示:
五、AudioSession
AudioSession用于访问麦克风、扬声器的会话,提供管理音频服务、监听Route变化,如下图所示:
1、配置AudioSession
关于AudioSession的配置,示例代码如下:
let session = AVAudioSession.sharedInstance()
do {
// Configure the audio session for playback
try session.setCategory(AVAudioSessionCategoryPlayback,
mode: AVAudioSessionModeMoviePlayback,
options: [])
// Activate audio session to enable your custom configuration
try session.setActive(true)
} catch let error as NSError {
print("Unable to activate audio session: \(error.localizedDescription)")
}
2、后台播放
如果要后台播放音乐,或者Airplay投屏、画中画播放,需要把后台模式打开:
3、处理中断
不同session之间会互相中断。比如当前正在播放音乐,有个语音电话打进来,那么就会中断音乐播放,保存状态和上下文。等到通话结束,然后使用状态和上下文恢复音乐播放。示意图如下:
监听中断通知以及处理中断,示例代码如下:
func registerForNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleInterruption),
name: .AVAudioSessionInterruption,
object: AVAudioSession.sharedInstance())
}
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 {
// save state and context
}
else if type == .ended {
guard let optionsValue =
userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended, resume to playback
}
}
}
5、监听Route变化
通知注册AVAudioSessionRouteChangeNotification
来监听。以监听连接耳机为例,代码示例如下:
func setupNotification() {
NotificationCenter.default.addObserver(self,
selector: #selector(handleRouteChange),
name: .AVAudioSessionRouteChange,
object: AVAudioSession.sharedInstance())
}
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 {
// headphone connected
}
case .oldDeviceUnavailable:
if let previousRoute =
userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
for output in previousRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
// headphone disconnected
}
}
default: ()
}
}