AVAudioEngine
是 iOS/macOS 中 AVFoundation 框架 提供的一款强大的 音频处理工具,允许开发者构建自定义音频信号流程和实现高效的音频处理管道。它由一组音频节点组成,能够实时处理音频,包括录制、播放、混音、效果添加等。AVAudioEngine
的实现基于 CoreAudio 的底层技术,但提供了更简单易用的接口。
下面按照功能分类介绍 AVAudioEngine
的重要特性,并提供代码示例。
1. 核心组件与架构 #
AVAudioEngine
的音频处理管道由以下核心概念组成:
组件 | 说明 |
---|---|
AVAudioEngine | 引擎主体,管理所有音频节点和连接关系。 |
AVAudioNode | 引擎中的基础构建块,代表一个音频处理器节点(如输入、输出、效果器、混音器等)。 |
AVAudioInputNode | 声音输入节点,常用于从麦克风录音或捕获外部音频信号。 |
AVAudioOutputNode | 声音输出节点,常用于设备扬声器、耳机或其他输出设备进行播放。 |
AVAudioPlayerNode | 用来播放音频的节点,可以播放本地音频文件或音频缓冲数据。 |
AVAudioMixerNode | 混音节点,用于合并多个音频信号通道。 |
AVAudioUnitEffect | 音效节点,用于在音频信号流中添加效果(如混响、延迟等)。 |
AVAudioPCMBuffer | 用于存储音频数据的缓冲区。 |
2. AVAudioEngine 核心方法 #
方法名 | 说明 | 示例 |
---|---|---|
start() | 启动引擎。(需要配置所有节点和连接后调用) | audioEngine.start() |
stop() | 停止引擎,清空当前的音频信号管道。 | audioEngine.stop() |
reset() | 重置引擎,移除所有未提交的音频处理链配置。 | audioEngine.reset() |
pause() | 暂停音频引擎(保持当前状态)。 | audioEngine.pause() |
attach(_:) | 将节点附加到引擎中,但尚未连接到其他节点。 | audioEngine.attach(playerNode) |
connect(_:to:format:) | 将一个节点连接到另一个节点,并指定音频信号的数据格式(可选)。 | audioEngine.connect(playerNode, to: mixerNode) |
3. AVAudioNode 的类型与功能 #
节点类型 | 说明 | 示例 |
---|---|---|
AVAudioInputNode | 声音输入节点,用于捕获外部音频输入信号(如麦克风)。 | let inputNode = audioEngine.inputNode |
AVAudioOutputNode | 输出节点,用于发送音频至设备的输出(如扬声器或耳机)。 | let outputNode = audioEngine.outputNode |
AVAudioPlayerNode | 播放音频文件或缓冲区内容的节点,独立管理播放音频流。 | playerNode.scheduleFile(audioFile, at: nil) |
AVAudioMixerNode | 混音节点,用于合并多个音频输入源和输出一个音频流。 | mixerNode.volume = 0.5 |
AVAudioUnitEffect | 音效节点(如混响、均衡器等),对音频流实时添加效果。 | let reverbEffect = AVAudioUnitReverb() |
AVAudioSession | 配置音频会话(如音频播放类型、后台模式等)。 | AVAudioSession.sharedInstance().setCategory(.playback) |
4. 配置与启动引擎 #
创建与链接音频节点 #
使用 AVAudioEngine
配置音频信号管道必须经过以下步骤:
- 创建音频节点(如
AVAudioPlayerNode
、AVAudioOutputNode
)。 - 将节点
attach
到引擎中。 - 使用
connect
连接节点并指定信号流方向。 - 配置并启动引擎。
import AVFoundation
let audioEngine = AVAudioEngine()
let playerNode = AVAudioPlayerNode()
let mixerNode = AVAudioMixerNode()
do {
// 添加节点到引擎
audioEngine.attach(playerNode)
audioEngine.attach(mixerNode)
// 连接节点
audioEngine.connect(playerNode, to: mixerNode, format: nil)
audioEngine.connect(mixerNode, to: audioEngine.outputNode, format: nil)
// 启动引擎
try audioEngine.start()
} catch {
print("Failed to start audio engine: \(error)")
}
5. 播放音频 #
为了播放音频,可以使用 AVAudioPlayerNode
配合 AVAudioFile
或 AVAudioPCMBuffer
:
播放音频文件 #
import AVFoundation
let audioEngine = AVAudioEngine()
let playerNode = AVAudioPlayerNode()
do {
// 设置播放器节点
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: audioEngine.outputNode, format: nil)
// 加载音频文件
let audioFile = try AVAudioFile(forReading: URL(fileURLWithPath: "/path/to/audio.mp3"))
// 调度音频文件并播放
playerNode.scheduleFile(audioFile, at: nil) {
print("Playback completed")
}
try audioEngine.start()
playerNode.play()
} catch {
print("Error during audio playback: \(error)")
}
使用 Audio Buffer 播放 #
import AVFoundation
let audioEngine = AVAudioEngine()
let playerNode = AVAudioPlayerNode()
do {
audioEngine.attach(playerNode)
audioEngine.connect(playerNode, to: audioEngine.outputNode, format: nil)
// 设置 PCM 缓冲区
let buffer = AVAudioPCMBuffer(pcmFormat: audioEngine.outputNode.inputFormat(forBus: 0), frameCapacity: 1024)!
// 填充音频数据到 buffer...
playerNode.scheduleBuffer(buffer, at: nil)
try audioEngine.start()
playerNode.play()
} catch {
print("Error during buffer playback: \(error)")
}
6. 添加音效 #
AVAudioEngine
支持在音频信号流中插入音效单位(AVAudioUnitEffect
)。以下示例展示如何添加混响效果:
import AVFoundation
let audioEngine = AVAudioEngine()
let playerNode = AVAudioPlayerNode()
let reverbEffect = AVAudioUnitReverb()
do {
// 配置混响效果
reverbEffect.loadFactoryPreset(.mediumHall)
reverbEffect.wetDryMix = 50 // 混混比
audioEngine.attach(playerNode)
audioEngine.attach(reverbEffect)
// 设置音频管道:player -> reverb -> output
audioEngine.connect(playerNode, to: reverbEffect, format: nil)
audioEngine.connect(reverbEffect, to: audioEngine.outputNode, format: nil)
// 播放音频
let audioFile = try AVAudioFile(forReading: URL(fileURLWithPath: "/path/to/audio.mp3"))
playerNode.scheduleFile(audioFile, at: nil)
try audioEngine.start()
playerNode.play()
} catch {
print("Failed to apply reverb effect: \(error)")
}
7. 混音与音量控制 #
通过 AVAudioMixerNode
可以调节多个音频输入源的混音效果。例如控制独立音源的音量:
import AVFoundation
let audioEngine = AVAudioEngine()
let playerNode1 = AVAudioPlayerNode()
let playerNode2 = AVAudioPlayerNode()
let mixerNode = AVAudioMixerNode()
do {
audioEngine.attach(playerNode1)
audioEngine.attach(playerNode2)
audioEngine.attach(mixerNode)
// 连接到混音器
audioEngine.connect(playerNode1, to: mixerNode, format: nil)
audioEngine.connect(playerNode2, to: mixerNode, format: nil)
audioEngine.connect(mixerNode, to: audioEngine.outputNode, format: nil)
// 设置音量
playerNode1.volume = 0.8
playerNode2.volume = 0.5
// 播放两个音频文件
let file1 = try AVAudioFile(forReading: URL(fileURLWithPath: "/path/to/audio1.mp3"))
let file2 = try AVAudioFile(forReading: URL(fileURLWithPath: "/path/to/audio2.mp3"))
playerNode1.scheduleFile(file1, at: nil)
playerNode2.scheduleFile(file2, at: nil)
try audioEngine.start()
playerNode1.play()
playerNode2.play()
} catch {
print("Failed to mix audio: \(error)")
}
8. 注意事项 #
音频格式匹配:
如果节点间的格式不匹配,需要在连接时指定合适的音频格式。实时处理:
AVAudioEngine
支持低延迟和实时音频处理,但需要正确配置音频会话(AVAudioSession
)。动态连接:
可以在运行时动态连接或移除节点,但需要调用audioEngine.stop()
然后重新启动。
总结 #
常用功能: #
- 播放: 使用
AVAudioPlayerNode
播放音频文件或缓冲数据。 - 音效: 通过
AVAudioUnitEffect
添加实时音效(如混响、延迟)。 - 混音: 使用
AVAudioMixerNode
合并多个音频流。 - 音频输入: 使用
AVAudioInputNode
捕获麦克风输入。
适用场景: #
- 自定义音频播放(支持文件和流)。
- 实时音频处理(如变声、混音、音效)。
- 音频可视化或音频分析开发。