Audio Unit 基础

如图所示,全部 iOS 音频技术都是基于 audio units。此处显示的更高级别的技术,如 Media Player,AV Foundation,OpenAL,AudioToolbox,是对 audio units 的封装,为特定的任务提供专用且简化的 API。ios

Figure 1

如在可控性、性能、灵活性有很是高的需求,或者须要实现特定的功能(例如回音消除),直接使用 audio unit 是一个正确的选择。编程

Audio Units 提供高效,模块化音频处理方案

当你须要实现如下需求时,不使用高级 API,直接使用 audio unitsapi

  • 低延时同步音频输入输出,例如 VoIP 应用
  • 响应回放合成声音,例如音乐游戏或合成乐器
  • 使用特定的 audio unit 特征,例如回声消除,混音,色调均衡
  • 处理链结构让你能够将音频处理模块组装到灵活的网络中。这是 iOS 中惟一提供此功能的音频 API

iOS 中的 Audio Units

根据不一样功能分类,iOS 提供了七种 audio units缓存

  • Effect - iPod Equalizer
  • Mixing - 3D Mixer
  • Mixing - Multichannel Mixer
  • I/O - Remote I/O
  • I/O - Voice-Processing I/O
  • I/O - Generic Output
  • Format conversion - Format Converter

注意:iOS 动态插件结构不支持第三方 audio units,也就是说,动态加载的 audio units 仅能经过操做系统提供。安全

Effect Unit

iOS 4 提供了一个效果单元,iPod Equalizer,与 iPod 内置应用使用相同的均衡器。查看这个 audio unit 的 iPod 应用用户界面,进入设置 -> iPod -> EQ。当使用此 audio unit,必须提供本身的用户界面。此 audio unit 提供了一组预设的均衡曲线,例如低音加强,Pop 和 Spoken Word。网络

Mixer Units

iOS 提供两个 mixer units。3D Mixer unit 是 OpenAL 的基础,若是须要实现 3D Mixer unit 的特征,能够优先使用 OpenAL,它提供了高级 API,而且很是适合游戏应用程序。关于如何使用 OpenAL,见示例代码: oalTouch。数据结构

Multichannel Mixer unit 为任意数量的单声道或立体声提供混音,立体声输出。能够打开和关闭每个输入,设置输入增益,并设置立体声平移位置。见示例代码:MixerHost。app

I/O Units

iOS 提供了三个 I/O units,其中 Remote I/O unit 是最经常使用的。链接输入输出音频硬件,对传入和传出的样本值低延迟访问,提供硬件音频格式和应用音频格式之间的格式转化。见示例代码:aurioTouch。异步

Voice-Processing I/O unit 是对 Remote I/O unit 的拓展,添加了语音聊天中的回声消除,还提供了自动增益矫正,语音质量调整,静音等特性。模块化

Generic Output unit 不链接音频硬件,而是提供了一种将处理链的输出发送到应用程序的机制。一般会使用作离线音频处理

Format Converter Unit

iOS 4 提供了 Format Converter Unit,一般经过 I/O unit 间接使用。

两个 Audio Unit API

iOS 中两个和 audio units 相关的 API,一个 API 直接处理 audio units ,另外一个处理 audio processing graphs,在应用中能够同时使用两个 API。

这两个 API 之间有一些功能重叠,能够根据本身编程风格自由组合和搭配,提供的功能以下:

  • 获取对定义音频单元动态连接库的引用
  • 实例化 audio unit
  • audio units 互联和附件回调函数
  • 开始和中止音频流

Audio Unit 获取

首先须要在音频组件描述数据结构中肯定其类型、子类型和制造商密匙。下面指定了一个特定的 audio Unit,Remote I/O unit,对 componentManufacturer 字段,全部的 iOS audio units 使用 kAudioUnitManufacturer_Apple。如需建立一个通用描述,能够将类型或者子类型设置为0,例如为了匹配全部的 I/O unit,能够将 componentSubType 设置为0。

AudioComponentDescription ioUnitDescription;
    ioUnitDescription.componentType = kAudioUnitType_Output;
    ioUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
    ioUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
    ioUnitDescription.componentFlags = 0;
    ioUnitDescription.componentFlagsMask = 0;

使用 audio unit API 获取 audio unit 实例,对于 AudioComponentFindNext ,若是第一个参数传空,按照系统定义的排序,找到第一个符合的 audio unit ,若是该参数为先前找到的音频单元,则该功能找到与描述匹配的下一个 audio unit,例如此用法能够经过重复调用 AudioComponentFindNext 来获取全部 I/O 单元的引用。

AudioComponent foundIoUnitReference = AudioComponentFindNext(NULL, &ioUnitDescription);
    AudioUnit ioUnitInstance;
    AudioComponentInstanceNew(foundIoUnitReference, &ioUnitInstance);

使用 audio processing graph API 获取 audio unit

AUGraph processingGraph;
    NewAUGraph(&processingGraph);
    
    AUNode ioNode;
    AUGraphAddNode(processingGraph, &ioUnitDescription, &ioNode);
    AUGraphOpen(processingGraph);
    
    AudioUnit ioUnit;
    AUGraphNodeInfo(processingGraph, ioNode, NULL, &ioUnit);

Audio Units 结构

如图所示,Audio Unit 中有一个 input element,一个 output element,这种结构比较常见,但这并不适合全部情况。例如在 mixer unit 中,会存在多个 input element,一个 output element的状况。

element 1 能够理解为 input element(bus),element 0 理解为output element。Input scope 和 Output scope,直接参与音频流的流程,音频从 Input scope 输入,从 Output scope 输出,一些参数或属性适用于 Input scope 或 Output scope。Global scope,应用于整个 audio unit,不与音频流关联,它有一个 element,命名为 element 0,一些属性,像 kAudioUnitProperty_MaximumFramesPerSlice 应用于 Global scope。

Audio Units 属性

设置属性,可使用函数 AudioUnitSetProperty

UInt32 busCount = 2;
    OSStatus result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount));

下面是一些经常使用的属性:

  • kAudioOutputUnitProperty_EnableIO 启用或禁止 I/O,默认输出开启,输入禁止。
  • kAudioUnitProperty_ElementCount 配置元素个数
  • kAudioUnitProperty_MaximumFramesPerSlice 设置 audio unit 的最大帧数
  • kAudioUnitProperty_StreamFormat 指定输入 audio unit 输入输出元素的数据格式

大部分属性能够在 audio unit 未初始化的时候设置,由于这些属性通常不会发生改变,有些属性像 iPod EQ unit 中的 kAudioUnitProperty_PresentPreset和 Voice-Processing I/O unit 中的 kAUVoiceIOProperty_MuteOutput 这些属性会在播放音频的时候也会发生改变

查找属性是否可得,访问属性值,监听改变,可使用一下函数:

  • AudioUnitGetPropertyInfo 查看属性是否可得,若是能够,会获得值大小和值是否能够改变
  • AudioUnitGetProperty, AudioUnitSetProperty 获取或设置属性
  • AudioUnitAddPropertyListener, AudioUnitRemovePropertyListenerWithUserData 安装或者移除监听属性变化回调函数

Audio Units 参数

audio unit 参数是用户能够在音频生成的过程当中更改,事实上,大部分参数能够在 audio unit 正在执行时实时调整的,例如音量。

  • AudioUnitGetParameter
  • AudioUnitSetParameter

I/O Units 的基本特性

  • Input element 和 Output element 都是 I/O unit 的一部分,能够将它们视为一个独立的个体,单独启动或禁止每个 Element,默认状况下,Element 1 禁用,Element 0 开启。
  • 音频输入硬件麦克风直接连着 Element 1, Element 1 的 Input scope 对你是不可见的,你首次访问硬件输入的音频数据是位于 Element 1 的 Output scope。
  • 音频输出硬件扬声器直接连着 Element 0,Element 0 的 Output scope 对你是不可见的,数据从 Element 1 的 Output scope 传递到 Element 0 的 Input scope。

每个 Element都有本身的 input scope 和 output scope,当描述 I/O unit 的时候可能会有困惑,至关于这样描述,你收到收据来自 input element 的 output scope,发送数据到 output element 的 input scope。
I/O unit 是惟一可以在 audio processing graph 中启动和中止音频流的 audio unit。I/O unit 负责在音频单元APP中的音频流。

Audio Processing Graphs 管理 Audio Units

AUGraph 用于构建和管理 audio units 处理链,能够利用多个 audio units 和多个回调函数功能,建立几乎任何你能够想象的音频处理解决方案。
AUGraph 增长了线程安全,让你能够即时从新配置处理链,例如你能够安全插入一个均衡器,甚至在音频播放时能够交换混音器输入的其它回调函数。实际上,AUGraph 提供了 iOS 中惟一能够在音频应用程序中执行这种动态从新配置的 API 。
Audio Processing Graph 使用了 AUNode 表示上 graph 中 一个单独的 audio unit ,当使用 Audio Processing Graphs,一般与包含 audio units 的代理 AUNode 交互,而不是直接使用 audio unit。
当将 graph 组合时,须要配置每个 audio unit ,而且必须经过 audio unit API 直接与 audio unit 交互,节点单元自己是不能够配置的,经过这种方式,须要使用这两种 API。
一般状况下,Audio Processing Graphs 一般须要三个任务,将节点添加到 Graph 中,直接配置由节点表示的 audio unit,互连节点。

#Audio Processing Graph 有一个 I/O Unit

每个 audio processing graph 有一个 I/O unit,不管你是录音,播放,同步 I/O。Graphs 经过 AUGraphStart 和 AUGraphStop 启动和中止音频流,经过函数 AudioOutputUnitStart 和 AudioOutputUnitStop 传递开始和中止消息到 I/O unit。

Audio Processing Graphs 提供线程安全

audio processing graph 使用“待办事项列表”提供线程安全,API 的一些函数添加工做单元到稍后执行的更改列表中,在你指定完整的更改好,让 graph 实现他们。
这是一些 audio processing graph 支持的重配置函数

  • 添加或删除音频单元节点(AUGraphAddNode,AUGraphRemoveNode)
  • 添加或删除节点间的链接(AUGraphConnectNodeInput,AUGraphDisconnectNodeInput)
  • 渲染回调函数链接到 aduio unit 的输入总线(AUGraphSetNodeInputCallback)

下面看一个重配置 audio processing graph 的例子,构建一个 graph 包含 Multichannel Mixer unit 和 Remote I/O unit,将声音输入到混频器的两个输入总线上。从混合器输出数据到 I/O unit 的 Output element 上。

如今用户想插入均衡器到其中一个音频流上,如何完成动态配置

  • 使用 AUGraphDisconnectNodeInput 断掉 input 1 到 mixer unit 的回调
  • 添加一个 iPod EQ unit 到 graph 中,须要使用 AudioComponentDescription 指定 iPod EQ unit 的结构,接着调用 AUGraphAddNode,至此,iPod EQ unit 被安装可是没有初始化,被 graph 拥有可是没有参与到音频流中
  • 重配置和初始化 iPod EQ unit,详情以下:

调用 AudioUnitGetProperty 函数获得 mixer input 的流格式(kAudioUnitProperty_StreamForamt)
调用 AudioUnitSetProperty 函数两次,一次设置 iPod EQ unit 的输入格式,一次设置它的输出格式
调用 AudioUnitInitialize 函数,给 iPod EQ 分配资源和准备处理音频,这个函数调用时线程安全的
调用 AUGraphSetNodeInputCallback 函数,设置鼓的回调函数到 iPod EQ unit 的输入

回调函数提供数据给 Audio Units

为了给 audio unit 的输入总线提供数据,使用听从 AURenderCallback 原型的回调函数,音频输入单元须要一帧数据的时候触发回调。在处理 audio unit 应用中,写回调函数多是最具备创意的工做,你能根据你的意愿产生和改变声音。
回调函数有严格的性能要求,回调存在于实时线程上,随后回调异步到达,回调函数内部全部的工做发生在时间有限的环境中,当下一帧数据到达,你仍在处理以前的回调产生的帧,声音则会产生间隙,出于这个缘由,不得在回调函数主体中执行耗时操做,例如锁定,分配内存,访问文件系统,网络链接等。

理解音频单元的回调函数

回调函数头部

static OSStatus MyAURenderCallBack(void                                    *inRefCon,
                                                      AudioUnitRenderActionFlags  *ioActionFlags,
                                                      const AudioTimeStamp         *inTimeStamp,
                                                      UInt32                                inBusNumber,
                                                      UInt32                                inNumberFrames,
                                                      AudioBufferList                   *ioData);
  • inRefCon,参数指向回调附加到 audio unit 输入时指定的编程上下文

  • ioActionFlags,参数容许提供提示当没有音频数据处理时,例如当你的应用程序是合成吉他,用户当面没有播放,请执行此操做。当你想要输出静音,能够在回调主体中使用以下语句:*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence,而且你必须明确地将 ioData 参数指向的缓冲区设置为0。
  • inTimeStamp,回调函数被触发时间,包含一个 AudioTimeStamp 结构体。
  • inBusNumber,参数指示调用回调的音频单元总线
  • inNumberFrames,当前调用的音频采样数
  • ioData,指向音频数据缓存区

相关文章
相关标签/搜索