Audio & Video 进阶 20m
使用 AVAudioSession 录制立体声
Record stereo audio with AVAudioSession
2020年6月25日
一句话判断
如果你在开发录音类 App,iOS 14 终于支持了原生立体声录制——前提是你知道怎么正确配置 AVAudioSession。
这场 Session 讲了什么
这场 Session 介绍了 iOS 14 中 AVAudioSession 的一个重要新特性:立体声录音。在此之前的 iOS 版本中,即使设备有多个麦克风,录音也只能获得单声道数据。iOS 14 打破了这个限制,开发者可以请求立体声输入,利用设备的多个麦克风同时采集左右声道。
演讲者首先讲解了 iOS 设备的麦克风硬件架构——从 iPhone 的多麦克风阵列到 iPad 的立体声麦克风配置。然后详细介绍了如何通过 AVAudioSession 的 preferredInputOrientation 和输入通道配置来请求立体声录音。关键是你要明确告诉系统你需要双声道输入,否则默认还是单声道。
Session 的中间部分讨论了硬件能力检测——不是所有设备都支持立体声录音,你需要用 AVAudioSession 的 availableInputs 来查询当前设备的能力。演讲者还展示了如何利用 AgioVoiceIOProcessing 的空间音频算法来增强录音的立体声效果。
最后,演讲者提供了一系列最佳实践,涵盖录音前的权限检查、采样率选择、以及如何在不同设备上提供一致的录音体验。
值得深挖的点
- 硬件差异的处理:iPhone 的立体声录制主要依赖底部和顶部麦克风的组合,而 iPad Pro 则有更专业的四麦克风阵列。不同设备的立体声效果和频率响应差异很大,你不能假设所有用户的录音质量一致。需要在 App 中做好设备能力检测和降级策略。
- AVAudioSession 的配置顺序:必须先设置 category 和 mode,再设置 preferredInputNumberOfChannels,最后调用 setActive。顺序错了配置可能不会生效。这个坑在 Stack Overflow 上有大量踩坑记录。
代码片段
// 配置立体声录音
import AVFoundation
func configureStereoRecording() throws {
let session = AVAudioSession.sharedInstance()
// 第一步:设置 category 和 mode
try session.setCategory(.record, mode: .measurement)
try session.setPreferredSampleRate(48000.0)
// 第二步:请求双声道输入
try session.setPreferredInputNumberOfChannels(2)
// 第三步:激活 session
try session.setActive(true)
// 验证实际获得的输入通道数
let inputChannels = session.inputNumberOfChannels
print("实际输入通道数: \(inputChannels)")
// 如果设备不支持立体声,这里可能仍然是 1
}
// 检查设备是否支持立体声录音
func supportsStereoRecording() -> Bool {
let session = AVAudioSession.sharedInstance()
guard let currentInput = session.currentRoute.inputs.first else {
return false
}
// 检查输入端口的通道数
return currentInput.channels?.count ?? 0 >= 2
}
// 创建立体声录音引擎
func startStereoRecording() throws -> AVAudioEngine {
let engine = AVAudioEngine()
let inputNode = engine.inputNode
let format = inputNode.outputFormat(forBus: 0)
// 确认格式是立体声
print("录音格式: \(format)")
// AVAudioFormat(44100.0, 2ch) - 双声道
engine.inputNode.installTap(onBus: 0, bufferSize: 1024, format: format) {
buffer, time in
// buffer 现在包含左右声道数据
self.processStereoBuffer(buffer)
}
try engine.start()
return engine
}
// 处理立体声 buffer
func processStereoBuffer(_ buffer: AVAudioPCMBuffer) {
guard let channelData = buffer.floatChannelData else { return }
// channelData[0] = 左声道
// channelData[1] = 右声道
let leftChannel = Array(UnsafeBufferPointer(
start: channelData[0],
count: Int(buffer.frameLength)
))
let rightChannel = Array(UnsafeBufferPointer(
start: channelData[1],
count: Int(buffer.frameLength)
))
// 计算立体声声像(pan)信息
processAudioChannels(left: leftChannel, right: rightChannel)
}
最佳实践
- 录音前检查设备能力,不支持立体声的设备要优雅降级到单声道
- AVAudioSession 的配置操作必须按正确顺序:category -> mode -> channels -> setActive
- 使用 48kHz 采样率,这是 iOS 设备的原生采样率,可以避免额外的采样率转换开销
- 测试时覆盖不同设备型号,iPhone 和 iPad 的麦克风配置差异很大
- 在 App 进入后台时正确处理录音中断,监听
AVAudioSession.interruptionNotification
还有什么值得关注
- 立体声录制与空间音频(Spatial Audio)有密切关系,关注后续的 AirPods Pro 空间音频 API
- 如果你在做专业音频 App,结合 Audio Workgroups 可以进一步降低录音到监听的延迟
- 搭配 “Meet Audio Workgroups” 一起看,可以全面了解 Apple 音频栈的最新改进
WWDC 2020