Foundation — AVAudioEngine

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 配置音频信号管道必须经过以下步骤:

  1. 创建音频节点(如 AVAudioPlayerNodeAVAudioOutputNode)。
  2. 将节点 attach 到引擎中。
  3. 使用 connect 连接节点并指定信号流方向。
  4. 配置并启动引擎。
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 配合 AVAudioFileAVAudioPCMBuffer

播放音频文件 #

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. 注意事项 #

  1. 音频格式匹配:
    如果节点间的格式不匹配,需要在连接时指定合适的音频格式。

  2. 实时处理:
    AVAudioEngine 支持低延迟和实时音频处理,但需要正确配置音频会话(AVAudioSession)。

  3. 动态连接:
    可以在运行时动态连接或移除节点,但需要调用 audioEngine.stop() 然后重新启动。


总结 #

常用功能: #

  1. 播放: 使用 AVAudioPlayerNode 播放音频文件或缓冲数据。
  2. 音效: 通过 AVAudioUnitEffect 添加实时音效(如混响、延迟)。
  3. 混音: 使用 AVAudioMixerNode 合并多个音频流。
  4. 音频输入: 使用 AVAudioInputNode 捕获麦克风输入。

适用场景: #

  • 自定义音频播放(支持文件和流)。
  • 实时音频处理(如变声、混音、音效)。
  • 音频可视化或音频分析开发。
本文共 1631 字,上次修改于 Jan 1, 2025