iOS音频播放(三)AudioUnit介绍与实战
在iOS平台上,所有的音频框架底层都是基于AudioUnit实现的。较高层次的音频框架包括: Media Player、 AV Foundation、OpenAL和Audio Toolbox,这些框架都封装了AudioUnit,然后提供了更高层次的API(功能更少,职责更单一的接口)。
![](https://img-blog.csdnimg.cn/img_convert/34561c4265b553bb670412c834c7f357.webp?x-oss-process=image/format,png)
当开发者在开发音视频相关产品的时候,如果对音视频需要更高程度的控制、性能以及灵活性,或者想要使用一些特殊功能(回声消除)的时候,可以直接使用AudioUnit API。苹果官方文档中描述,AudioUnit提供了音频快速的模块化处理,如果是在以下场景中,更适合使用AudioUnit而不是使用高层次的音频框架。
想使用低延迟的音频I/O(input或者output),比如说在VoIP的应用场景下。
多路声音的合成并且回放,比如游戏或者音乐合成乐器的应用。
使用AudioUnit里面提供的特有功能,比如:回声消除、Mix两轨音频,以及均衡器、压缩器、混响器等效果器。
需要图状结构来处理音频,可以将音频处理模块组装到灵活的图状结构中,苹果公司为音频开发者提供了这种API。
构建AudioUnit的时候需要制定类型(Type)、子类型(subtype)以及厂商(Manufacture).类型(Type)就是四大类型的AudioUnit的Type;而子类型(subtype)就是该大类型下面的子类型(比如Effect该大类型下面有EQ、Compressor、limiter等子类型);厂商(Manufacture)一般情况下比较固定,直接写成kAudioUnitManufacturer_Apple就可以了。利用以上这三个变量开发者可以完整描述出一个AudioUnit了,比如使用下面的代码创建一个RemoteIO类型的AudioUnit:
AudioComponentDescription ioUnitDescription;
ioUnitDescription.componentType = kAudioUnitType_Output;
ioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
ioUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
ioUnitDescription.componentFlags = 0;
ioUnitDescription.componentFlagsMask = 0;复制代码
上诉代码构造了RemoteIO类型的AudioUnit描述的结构体,那么如何使用这个描述来构造真正的AudioUnit呢?有两种方式:第一种方式是直接使用AudioUnit裸的创建方式;第二种方式是使用AUGraph和AUNode(其实一个AUNode就是对AudioUnit的封装)来构建。下面就来分别介绍这两种方式。
(1) 裸创建方式
首先根据AudioUnit的描述,找出实际的AudioUnit类型:
AudioComponent ioUnitRef = AudioComponentFindNext(NULL,&ioUnitDescription);复制代码
然后声明一个AudioUnit引用:
AudioUnit ioUnitInstance;复制代码
最后根据类型创建这个AudioUnit实例:
AudioConponentInstanceNew(isUnitRef,&ioUnitInstance);复制代码
(2) AUGraph创建方式
首先声明并且实例化一个AUGraph:
AUGraph processingGraph;
NewAUGraph(&processingGraph);复制代码
然后按照AudioUnit的描述在AUGraph中添加了一个AUNode:
AUNode ioNode;
AUGraphAddNode(processingGraph,&ioUnitDescription,&isNode);复制代码
接下来打开AUGraph,其实打开AUGraph的过程也是间接实例化AUGraph中所有的AUNode。注意,必须在获取AudioUnit之前打开整个AUGraph,否则,我们将不能从对应的AUNode中获取正确的AudioUnit:
AUGraphOpen(processingGraph);复制代码
最后在AUGraph中的某个Node里获得AudioUnit的应用:
AudioUnit ioUnit;
AUGraphNodeInfo(processingGraph,ioNode,NULL,&ioUnit);复制代码
AudioUnit的通用参数设置
本节将以RemoteIO这个AudioUnit为例来讲解AudioUnit的参数设置,RemoteIO这个AudioUnit是与硬件IO相关的一个Unit,它分为输入端和输出端(I代表Input,O代表Output)。输入端一般是指麦克风,输出端一般是指扬声器(Speaker)或者耳机。如果需要同时使用输入输出,即K歌应用中的耳返功能(用户在唱歌或者说话的同时,耳机会将麦克风收录的声音播放出来,让用户能够听到自己的声音),则需要开发者做一些设置将它们连起来。
![](https://img-blog.csdnimg.cn/img_convert/c60925cd2314191b337496f1193fd956.webp?x-oss-process=image/format,png)
上图中的RemoteIO Unit分为Element0和Element1,其中Element0控制输出端,Element1控制输入端,同时每个Element又分为Input Scope和Output Scope。如果开发者想要使用扬声器的声音播放功能,那么必须将这个Unit的Element0的OutputScope和Speaker进行连接。而开发者想要使用麦克风的录音功能,那么必须将这个Unit的Element1的InputScope和麦克风进行连接。使用扬声器的代码如下:
OSStatus status = noErr;
UInt32 oneFlag = 1;
UInt32 busZero = 0;// Element 0
status = AudioUnitSetProperty(remoteIOUnit,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_output,busZero,&oneFlag,sizeof(oneFlag));
CheckStatus(status,@"Could not Connect To Speaker",YES);复制代码
上面这段代码就是把RemoteIOUnit的Element0的OutputScope连接到Speaker上,连接过程会返回一个OSStatus类型的值,可以使用自定义的CheckStatus函数来判断错误并且输出Could not Connect To Speaker的提示。具体的CheakStatus函数如下:
static void CheckStatus(OSStatus status,NSString *message,BOOL fatal)
{
if(status != noErr)
{
char fourCC[16];
*(UInt32 *)fourCC = CFSwapInt32HostToBig(status);
fourCC[4]='