AudioUnit学习笔记

AudioUnit简介

在iOS中,所有的上层音频技术都是基于AudioUnit和CoreMedia来实现的。
下图可以直观的反应出AudioUnit的层级关系。

对于普通的音频播放功能可能不需要直接使用AudioUnit来实现,但是如果你需要实现比较高级的功能(比如录音,混音等),就需要使用AudioUnit了。

根据不同的音频目的,iOS将AudioUnit共分为7类。如下图所示

  • effect units 这个是用在ipod类app上的,比较少使用到。

  • mixer units 做混音的,有2个audio unit,3D mixer比较适合用来做游戏的3d音效。multichannel mixer提供混合任意数量的单声道或立体声流,立体声输出。

  • i/o units 远程I/O单元是最常用的。它连接到输入和输出音频硬件和给你对传入和传出的低延迟访问音频样本值。它提供了硬件音频格式和应用程序之间的格式转换音频格式。声音处理I/O单元扩展了远程I/O单元,通过添加声学回声取消使用网络电话或者语音聊天应用程序。通用输出装置不连接到音频硬件,而是提供了一个机制来处理链的输出发送到您的应用程序。

  • Format Converter Unit 用来处理音频格式的转换。

iOS提供了2种创建AudioUnit方式,一种是直接创建AudioUnit,另一种是通过AUGraph来使用AudioUnit.

通过AudioComponentDescription&AudioComponent音频组件创建AudioUnit

在创建一个AudioUnit实例之前,需要先创建一个audiocomponent 创建方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//组件描述
AudioComponentDescription ioUnitDescription;
//组件类型
ioUnitDescription.componentType = kAudioUnitType_Output;
//组件子类型
ioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
//默认值
ioUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
ioUnitDescription.componentFlags = 0;
ioUnitDescription.componentFlagsMask = 0;
AudioComponent foundIoUnitReference = AudioComponentFindNext (NULL,&ioUnitDescription);
AudioUnit ioUnitInstance;
AudioComponentInstanceNew (
foundIoUnitReference,
&ioUnitInstance
);

使用AUGraph创建AudioUnit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
AUGraph processingGraph;
NewAUGraph (&processingGraph);
// Add an audio unit node to the graph, then instantiate the audio unit
AUNode ioNode;
AUGraphAddNode (
processingGraph,
&ioUnitDescription,
&ioNode
);
AUGraphOpen (processingGraph); // indirectly performs audio unit instantiation
// Obtain a reference to the newly-instantiated I/O unit
AudioUnit ioUnit;
AUGraphNodeInfo (
processingGraph,
ioNode,
NULL,
&ioUnit
);

使用Scopes和Elements来指定AudioUnit

Scope和Elements的关系如下图所示:

对于通用的audioUnit,可以有1-2条输入输出流,输入和输出不一定相等,比如mixer,可以两个音频输入,混音合成一个音频流输出。每个element表示一个音频处理上下文(context), 也称为bus。每个element有输出和输出部分,称为scope,分别是input scope和Output scope。Global scope确定只有一个element,就是element0,有些属性只能在Global scope上设置。

inputbus = Element1 = 1,outputbus = Element0 = 0

使用Property来config AudioUnit

1
2
3
4
5
6
7
8
9
10
UInt32 busCount = 2;
OSStatus result = AudioUnitSetProperty (
mixerUnit,
kAudioUnitProperty_ElementCount, // the property key
kAudioUnitScope_Input, // the scope to set the property on
0, // the element to set the property on
&busCount, // the property value
sizeof (busCount)
);

一个普通的播放行为可能需要设置的属性有 kAudioOutputUnitProperty_EnableIOkAudioUnitProperty_StreamFormatkAudioUnitProperty_SetRenderCallback 三个,不同行为需要设置不同的属性。

I/O Unit的基本特征

i/o unit是最常见的一种AudioUnit

对于remote_IO类型audioUnit,即从硬件采集和输出到硬件的audioUnit,它的逻辑是固定的:固定2个element,麦克风经过element1到APP,APP经element0到扬声器。

我们能把控的是中间的“APP内处理”部分,结合上图,淡黄色的部分就是APP可控的,Element1这个组件负责链接麦克风和APP,它的输入部分是系统控制,输出部分是APP控制;Element0负责连接APP和扬声器,输入部分APP控制,输出部分系统控制。

使用AUGraph来管理AudioUnit

Render Callback Functions Feed Audio to Audio Units

当音频数据是由内存或者磁盘提供的话,数据都需要从渲染回调函数这里提供给inputbus,当你想要对音频数据进行自定义的处理的话,就在这里进行吧。

渲染回调函数格式如下

1
2
3
4
5
6
7
8
static OSStatus MyAURenderCallback (
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData
) { /* callback body */ }

各参数的关系如下图:

Working with the AudioStreamBasicDescription structure

AudioStreamBasicDescription 这个结构体表示当前音频的格式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
struct AudioStreamBasicDescription {
Float64 mSampleRate;//采样率
UInt32 mFormatID;//格式ID
UInt32 mFormatFlags;//存储标识
UInt32 mBytesPerPacket;//每个packet的字节数
UInt32 mFramesPerPacket;//每个packet包含几个frame
UInt32 mBytesPerFrame;//每个frame的字节数
UInt32 mChannelsPerFrame;//每个frame的声道数
UInt32 mBitsPerChannel;//每个声道的位数
UInt32 mReserved;
};
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;

Understanding Where and How to Set Stream Formats

这个图展示了一个完整的录音+混音+播放的流程,在组件两边设置stream的格式,在代码里的概念是scope。

在使用AUGraph时选择不同的设计模式

I/O Pass Through

简单的音频输入输出场景,直接使用AudioUnit链接两端,不需要设置渲染回调.

I/O Without a Render Callback Function

需要多个AudioUnit来处理的场景,使用AudioUnit链接各部分,不需要设置回调。

I/O with a Render Callback Function

设置渲染回调的方式来处理输入输出的链接。

Output-Only with a Render Callback Function

只有输出没有输入的场景,设置渲染回调来链接输出端。

复杂的场景,需要设置多个渲染回调。同时需要多个AudioUnit来处理链接。

构建AudioUnit app的流程

无论选择上面的哪种设计,构建app的基本流程如下:

  • Configure your audio session.

  • Specify audio units.

  • Create an audio processing graph, then obtain the
    audio units.

  • Configure the audio units.

  • Connect the audio unit nodes.

  • Provide a user interface.

  • Initialize and then start the audio processing graph.

使用指定的AudioUnit

Using I/O Units

  • Remote I/O Unit

  • Voice-Processing I/O Unit

  • Generic Output Unit

Using Mixer Units

  • Multichannel Mixer Unit

  • 3D Mixer Unit

Using Effect Units

Identifier Keys for Audio Units

参考文档