构造Audio Unit应用
选择设计模式
有6种设计模式可以用于iOS应用audio unit。首先选择一种最接近你的app处理音频的模式。
其共同特性:
- 仅有一个I/O unit
- 在整个
audio processing graph
中使用单一音频流格式,尽管格式可能会有变化 - 要求在特定位置设置流的格式或者部分流的格式
正确设置流格式对于建立音频数据流至关重要
I/O传递
将传入的音频直接发送到输出硬件,不需要处理audio data。
音频硬件强制Remote I/O unit
输入元素的朝外一侧的流格式,而可以在朝内侧指定期望的格式。audio unit执行需要的格式转换。为避免不必要的采样率转换,请定义采样率为硬件设备采样率
利用两个remote I/O元素间的audio unit connection
,不需要设置output元素的input scope格式。connection
会为传播在输入元素指定的格式
在output 元素的外侧采用硬件流格式,其会自动执行需要的格式转换
input 元素默认是禁用的,因此在使用时 需要确保启用
使用这个模式 无需配置任何音频数据缓冲区
没有渲染回调的I/O
在remote I/O
元素间 添加一个或多个audio unit。
这种模式 仍然没有回调函数,就说明了,无法直接操作音频
无论何时,使用
I/O unit
外的任何unit,都必须要指定kAudioUnitProperty_MaximumFramesPerSlice
属性要设置
Multichannel Mixer unit
就必须在混音器的输出上设置流格式的采样率使用这个模式 类似直通模式 也无需配置任何音频数据缓冲区
具有渲染回调的I/O
这种模式,在Remote I/O
的输入和输出元素间放置渲染回调函数,可以在音频达到输出硬件前对其进行操作。简单可以使用render回调调整音量,复杂的话,可以通过Accelerate framework
Accelerate中的Fourier transforms
和卷积函数对声音做处理
将回调函数附加到输出元素的输入范围,回调函数通过调用Remote I/O
的input元素的渲染回调函数获取新的音频数据
此时,当使用渲染回调函数建立从一个audio unit到另一个unit的路径,回调函数将代替
audio unit connection
仅有渲染回调函数的 output
常为音乐游戏和合成器选择这种模式,此时app需要生成声音并最大程度的响应。
简单来说,这个模式将渲染回调函数直接连接到远程I/O unit的输出元素的输入范围
可以使用相同模式构造更复杂结构的app
iPod EQ要求您在输入和输出上都设置完整的流格式
多通道混频器只需要在其输出上设置正确的采样率即可
Multichannel Mixer unit inputs
通常需要单独考虑每个音频单元的流格式
其它设计模式
还有两种其他设计模式:
录制或分析音频,创建具有渲染回调功能的仅input app
一般,更好的选择是使用AudioQueue
对象(AudioQueueRef
),此时将会有更大灵活性,因为其渲染回调并不在实时线程上执行离线音频处理,使用
Generic Output unit
,此unit并未连接到音频硬件。当使用它向应用程序发送音频时,取决于您app来调用其render方法
构建你的app
构建步骤:
- 配置
audio session
- 指定
audio unit
- 创建一个
audio processing graph
然后获取audio unit
- 配置
audio unit
- 连接
audio unit node
- 提供用户界面
- 初始化 并启动音频处理图
配置audio session
与配置所有iOS音频相同,第一步就是配置audio session
。
首先指定在app中采用的采样率:self.graphSampleRate = 44100.0; // Hertz
接着使用audio session
对象 请求系统将您的首选采样率用作设备硬件采样率,来避免app间的采样率转换,这样可以最大化CPU性能和声音质量,并最大程度地减少电池消耗。
NSError *audioSessionError = nil;
//获取对应用程序的单例音频回话
AVAudioSession *mySession = [AVAudioSession sharedInstance]; // 1
//请求硬件的采样率,根据设备上其它音频活动,系统可能不会通过请求
[mySession setPreferredHardwareSampleRate: graphSampleRate // 2
error: &audioSessionError];
//请求音频回话类别,指定"录制和播放"支持音频输入和输出
[mySession setCategory: AVAudioSessionCategoryPlayAndRecord // 3
error: &audioSessionError];
//请求激活音频回话
[mySession setActive: YES // 4
error: &audioSessionError];
//激活后 根据系统提供采样率 更新自己采样率
self.graphSampleRate = [mySession currentHardwareSampleRate]; // 5
还有另外一项硬件特性:音频硬件I/O缓冲区持续时间。在44.1kHZ采样率下,默认持续时间为23ms,相当于1024个采样的切片大小。如果I/O延迟在应用中很重要,可以请求更短的持续时间,大约0.005ms(相当于256个样本)
self.ioBufferDuration = 0.005;
[mySession setPreferredIOBufferDuration: ioBufferDuration
error: &audioSessionError];
参阅Audio Session Programming Guide
指定所需音频单元
使用AudioComponentDescription
结构指定所需的每个audio unit
.
构建Audio Processing Graph
这一步将创建前面所说的设计模式框架。
//如何对包含Remote I/O单元和Multichannel Mixer unit的图形执行这些步骤
AUGraph processingGraph;
NewAUGraph (&processingGraph);
AUNode ioNode;
AUNode mixerNode;
//
AUGraphAddNode (processingGraph, &ioUnitDesc, &ioNode);
AUGraphAddNode (processingGraph, &mixerDesc, &mixerNode);
此时该graph已实例化,并拥有您将在应用程序中使用的nodes。
然后open graph并初始化audio unit
AUGraphOpen (processingGraph);
然后获取audio unit实例的引用
AudioUnit ioUnit;
AudioUnit mixerUnit;
AUGraphNodeInfo (processingGraph, ioNode, NULL, &ioUnit);
AUGraphNodeInfo (processingGraph, mixerNode, NULL, &mixerUnit);
获取了unit对象的引用,就可以配置或者互连这些audio unit
配置audio unit
每个unit都有自己的配置,参阅Using Specific Audio Units
默认 Remote I/O
已启用输出并禁用输入。
除了Remote I/O
和Voice-processing I/O
单元外,所有iOS音频单元都需要配置kAudioUnitProperty_MaximumFramesPerSlice
属性。此属性可确保audio unit准备好响应渲染调用而产生足够数量的音频数据帧。参阅Audio Unit Properties
所有的audio unit
都需要在输入或者输出上定义其频流格式。
编写和渲染回调函数
有关示例,请在iOS库中查看各种音频单元示例项目包括MixerHost、aurioTouch、SynthHost
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = &renderCallback;
callbackStruct.inputProcRefCon = soundStructArray;
AudioUnitSetProperty (
myIOUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0, // output element
&callbackStruct,
sizeof (callbackStruct)
);
//使用AUGraph 以线程安全的方式
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = &renderCallback;
callbackStruct.inputProcRefCon = soundStructArray;
AUGraphSetNodeInputCallback (
processingGraph,
myIONode,
0, // output element
&callbackStruct
);
// ... some time later
Boolean graphUpdated;
AUGraphUpdate (processingGraph, &graphUpdated);
连接 Audio Unit Nodes
大多数情况下,使用AUdio processing Graph
中的API AUGraphConnectNodeInput、AUGraphDisconnectNodeInput
函数在audio unit间建立和断开连接,这些函数是线程安全的,而且避免显式定义连接的编码开销
AudioUnitElement mixerUnitOutputBus = 0;
AudioUnitElement ioUnitOutputElement = 0;
AUGraphConnectNodeInput (
processingGraph,
mixerNode, // source node
mixerUnitOutputBus, // source node bus
iONode, // destination node
ioUnitOutputElement // desinatation node element
);
不推荐:可以直接使用音频单元属性机制在音频单元之间建立和断开连接。为此,请结合使用AudioUnitSetProperty
函数和kAudioUnitProperty_MakeConnection
属性。此方法要求您为每个连接定义一个AudioUnitConnection
结构,以用作其属性值
AudioUnitElement mixerUnitOutputBus = 0;
AudioUnitElement ioUnitOutputElement = 0;
AudioUnitConnection mixerOutToIoUnitIn;
mixerOutToIoUnitIn.sourceAudioUnit = mixerUnitInstance;
mixerOutToIoUnitIn.sourceOutputNumber = mixerUnitOutputBus;
mixerOutToIoUnitIn.destInputNumber = ioUnitOutputElement;
AudioUnitSetProperty (
ioUnitInstance, // connection destination
kAudioUnitProperty_MakeConnection, // property key
kAudioUnitScope_Input, // destination scope
ioUnitOutputElement, // destination element
&mixerOutToIoUnitIn, // connection definition
sizeof (mixerOutToIoUnitIn)
);
提供用户界面
参阅Use Parameters and UIKit to Give Users Control
初始化并且开始Audio Processing Graph
开始音频流之前,必须通过调用AUGraphInitialize
函数来初始化音频处理图。
这一步执行的步骤:
- 通过自动为每个unit单独调用
AudioUnitInitialize
函数初始化所有audio unit。 - 验证图表的连接和音频数据流格式。
- 在音频单元连接之间传播流格式。
OSStatus result = AUGraphInitialize (processingGraph);
// Check for error. On successful initialization, start the graph...
AUGraphStart (processingGraph);
// Some time later
AUGraphStop (processingGraph)
如果graph 初始化失败,可以使用
CAShow
函数,将graph
的状态输出到Xcode控制台
调试
打印ASBD信息 查看问题
- (void) printASBD: (AudioStreamBasicDescription) asbd {
char formatIDString[5];
UInt32 formatID = CFSwapInt32HostToBig (asbd.mFormatID);
bcopy (&formatID, formatIDString, 4);
formatIDString[4] = '\0';
NSLog (@" Sample Rate: %10.0f", asbd.mSampleRate);
NSLog (@" Format ID: %10s", formatIDString);
NSLog (@" Format Flags: %10X", asbd.mFormatFlags);
NSLog (@" Bytes per Packet: %10d", asbd.mBytesPerPacket);
NSLog (@" Frames per Packet: %10d", asbd.mFramesPerPacket);
NSLog (@" Bytes per Frame: %10d", asbd.mBytesPerFrame);
NSLog (@" Channels per Frame: %10d", asbd.mChannelsPerFrame);
NSLog (@" Bits per Channel: %10d", asbd.mBitsPerChannel);
}
Using Specific Audio Units
Using I/O Units
Remote I/O Unit
参见示例代码项目aurioTouch
Using Mixer Units
Multichannel Mixer Unit
默认情况下,kAudioUnitProperty_MaximumFramesPerSlice
属性设置为值1024,这在屏幕锁定和显示器休眠时是不够的。如果您的应用在锁定屏幕的情况下播放音频,则除非音频输入处于活动状态,否则您必须增加此属性的值。进行如下操作:
- 如果音频输入处于活动状态,则无需为
kAudioUnitProperty_MaximumFramesPerSlice
属性设置值。 - 如果未激活音频输入,则将此属性设置为4096
Using Effect Units
Mixer iPodEQ AUGraph Test示例代码项目