Audio Processing Graphs 管理 Audio Units
Audio Processing Graphs 管理 Audio Units
audio processing graph(AUGraph)是一种基于 Core Foundation 风格的数据结构,常用来管理 audio unit 处理链。audio processing graph 可以利用多个 audio unit 与回调函数,以用来解决任意音频处理方法。
AUGraph 类型保证了音频单元的线程安全性,它使得程序员能够实时重新配置处理链。例如,在音频播放时,可以安全地插入均衡器,甚至为混音器输入交换不同的渲染回调函数。事实上,AUGraph 类型提供了 iOS 中唯一用于在音频应用程序中执行此类动态重新配置的 API。
audio processing graph API 使用另一种不透明类型 AUNode 来表示图形上下文中的单个 Audio Unit。使用 audio processing graph 时,通常与 AUNode 交互,作为其包含音频单元的代理,而不是直接与Audio Unit 交互。
然而,当我们将 graph 放在一起时,必须使用 audio unit 的 API 配置每个 audio unit,而 nodes 则不能直接配置audio unit。因此,使用 graph 必须同时使用这两套 API。
还可以通过定义 AUNode 来表示一个完整的 audio processing subgraph(音频处理子图),将 AUNode 实例用作复杂 graph 中的 element。在这种情况下,子图末尾的 I/O 单元必须是 Generic Output unit(一种不连接到设备硬件的 I/O 单元)。
宽泛地说,构建 Audio Processing Graph 需要三步:
- 向 graph 中添加 nodes。
- 通过 nodes 直接配置 audio units。
- 互相连接 nodes。
Audio Processing Graph 拥有精确的 I/O Unit
无论正在录制、播放或是同步,每个 audio processing graph 都有一个 I/O unit。
通过 AUGraphStart
与 AUGraphStop
可以开启或停止音频流。通过 AudioOutputUnitStart
与 AudioOutputUnitStop
可以开启或停止 I/O unit。通过这种方式,graph 的 I/O 单元负责 graph 中的音频流。
Audio Processing Graph 提供线程安全
audio processing graph API 采用“待办事项列表”比喻来提供线程安全。此 API 中的某些功能将工作单元添加到稍后执行的更改列表中。指定完整的更改集后,要求 graph 去实现它们。
以下是 audio processing graph API 支持的一些常见重新配置及其相关功能:
- 添加、移除 audio unit nodes(AUGraphAddNode, AUGraphRemoveNode)
- 添加、移除 nodes 间的连接(AUGraphConnectNodeInput, AUGraphDisconnectNodeInput)
- 连接 audio unit input bus 的回调函数(AUGraphSetNodeInputCallback)
下图描绘了一种架构:构建了一个包含多通道混音器单元和远程 I/O 单元的 graph,用于混合播放两个合成的声音。在输入端,把声音反馈给混音器的两个输入总线。混音器输出进入 I/O 单元的输出元素,然后进入输出音频硬件。
现在,假设用户想将均衡器插入两个音频流之一。要做到这一点,就在其中一个声音和它的混音器输入之间添加一个 iPod EQ 单元,如下图所示。
完成此实时重新配置的步骤如下:
通过调用
AUGraphDisconnectNodeInput
断开 mixer unit input 1 的 “beats sound” 回调。将包含 iPod EQ 单元的音频单元节点添加到 graph 中。通过指定具有 AudioComponentDescription 结构的 iPod EQ 单元,然后调用 AUGraphAddNode 来做到这一点。此时,iPod EQ 单元已实例化,但未初始化。它由 graph 拥有,但尚未参与音频流。
配置和初始化 iPod EQ 单元。在这个例子中,这需要一些步骤:
- 调用
AudioUnitGetProperty
从 mixer 的输入端检索kAudioUnitProperty_StreamFormat
流格式。 - 调用两次
AudioUnitSetProperty
,一次设置 iPod EQ 单元的输入流格式,一次设置输出流格式。 - 调用
AudioUnitInitialize
以分配内存准备使用。这个函数是线程不安全的,但是,当 iPod EQ 单元尚未主动参与 audio processing graph 时,必须在序列时执行它,因为此时没有调用AUGraphUpdate 函数。
- 调用
通过调用 AUGraphSetNodeInputCallback,将 “beats sound” 回调函数添加到 iPod EQ 的输入端。
在前面的步骤中,步骤1、2 和 4 都是 AUGraph* 函数调用,它们都被添加到 graph 的“待办事项”列表中。调用 AUGraphUpdate 执行这些待处理的任务。AUGraphUpdate 功能成功返回后,graph 已被动态重新配置,iPod EQ 已到位并处理音频。
通过 graph “pull” 音频流
在 audio processing graph 中可以使用类似生产者消费者模式,消费者在需要更多音频数据时通知生产者。请求音频数据流的方向与音频流提供的方向正好相反,如下图所示:
对一组数据的每个请求都被称为渲染调用(render call),或者非正式地称为 pull。该图将渲染调用表示为灰色的“控制流”箭头。渲染调用请求的数据更恰当地称为一组音频样本帧(audio sample frames)。反过来,响应拉流而提供的一组音频样本帧被称为 slice。提供 slice 的代码称为渲染回调函数(render callback function)。
图 1-6 的 pull 实现步骤:
- 调用 AUGraphStart 函数后,虚拟输出设备会调用远程 I/O 单元输出元素的渲染回调。此调用要求一片已处理的音频数据帧。
- 远程 I/O 单元的渲染回调功能在其输入缓冲区中查找要处理的音频数据,以满足渲染调用。如果有等待处理的数据,远程I/O单元会使用它。否则,如图所示,它会调用应用程序连接到其输入的任何内容的渲染回调。在本例中,远程 I/O 单元的输入连接到效果单元的输出。因此,I/O 单元 pull 效果单元,要求提供一段音频帧。
- 效果单元的行为与远程 I/O 单元一样。当它需要音频数据时,它会从输入连接中获取。在本例中,效果单元拉动应用程序的渲染回调函数。
- 应用程序的渲染回调函数是 pull 的最终接收者。它向效果单元提供所需的帧。
- 效果单元处理应用程序渲染回调提供的 slice。然后,效果单元将之前请求的处理数据(在步骤 2 中)提供给远程 I/O 单元。
- 远程 I/O 单元处理效果单元提供的切片。然后,远程 I/O 单元将最初请求的已处理切片(在步骤 1 中)提供给虚拟输出设备。这完成了一个 pull 周期。