Audio Unit是一个音频处理插件。它是为应用程序添加复杂的音频操作和处理的底层的API。本文主要认识Audio Unit的原理和简单使用
主要内容:
- 音频单元
- Audio Unit使用
- audio processing graph
- 回调函数
1. 认识
Audio Unit是一个音频处理插件。它是为应用程序添加复杂的音频操作和处理的底层的API。所有的音频技术都是构建在这个框架之上的。支持混合、均衡、格式转换,用于录制、回放、离线渲染和实时对话的实时输入输出,流入VoIP(互联网协议语言)。
功能介绍:
- 具有低延时的实时I/O,1)如VoIP(互联网语音协议)应用程序
- 合成声音,进行回放,例如音乐游戏或合成乐器
- 一些特殊的功能,比如回声消除,均衡、混音
- 处理链架构,可将音频处理模块组装到灵活的网络中,
优势:
- 出色的响应能力:音频单元渲染的回调函数中使用的是实时优先线程,所以响应能力非常出色
- 动态重新配置:音频处理图的API允许我们以线程安全的方式动态组装,重新配置和重新排列复杂的音频处理链,同时处理音频
音频单元的生命周期:
- 运行时,获取对动态可链接库的引用
- 实例化音频单元
- 配置音频单元
- 初始化音频单元以使用
- 操作音频单元
- 取消分配音频单元
2. 音频单元
Audio Unit都是有音频单元组成的,这里先认识音频单元。
2.1 分类
共有七个音频单元,分为四大类。
- Effect Unit
- 效果单元,也就是iPod均衡器,对音频数据进行音效处理
- Mixing Unit
- 混音器单元,可以将不同的音频混合到一起输出
- 3D Minxer Unit
- 可以实现3D混音效果功能,是构建OpenAL的基础,一般实现3D混音都是使用OpenAL
- MutilChannel Minxer Unit
- 实现多通道混音效果
- 为任意数量的多通道或立体声流提供立体声输出
- I/O Unit
- Remote I/O 远程I/O单元
- 连接输入输出硬件设备的
- voice-processing I/O Unit语音处理I/O单元
- 对语音进行处理,比如可以进行回声消除,提供自动增益校正,语音处理质量调整和静音。
- Generic Output Unit 通用输出单元
- 这个输出不连接设备,在处理链中直接发到应用程序中,一般用来进行离线的音频操作
- Remote I/O 远程I/O单元
- Format conversion Unit
- 格式转换组件,使用在Remote I/O Unit中,将硬件的音频格式转换成应用程序的音频格式
2.2 组件的基本特征
说明:
- I/O音频单元包含输入音频组件和输出音频组件
- 包括输入输出scope,包括输入输出element
3. 简单使用
在iOS中提供了两种API,都可以进行操作,一种是直接操作Audio Unit,一种是操作Audio Processing Graph(在下文讲解)
Audio Unit是在Audio Processing Graph的上下文环境中工作的
过程示例:
3.1 创建音频组件描述
说明:
- 它是用来创建不同类型的音频组件的
- 音频组件描述中的参数可以在官网中查找
3.2 创建音频单元
说明:
- 初始化音频组件
- 定义音频组件描述
- 获取音频组件的引用
- 实例化音频组件
3.3 设置音频单元的属性
给音频组件设置scope、element设置属性,比如音频流的格式,比如回调函数设置等。
属性是一个键值对,键是系统已经定义好的数值,值是相应的特定类型
常用方法:
- AudioUnitGetPropertyInfo,检查属性是否可用
- AudioUnitGetProperty,得到该属性
- AudioUnitSetProperty,设置属性
- AudioUnitAddPropertyListener,增加监听
- AudioUnitRemovePropertyListnerWithUserData,移除监听
[图片上传失败...(image-dfb5d9-1661917557622)]
说明:
- 这里表示给scope的element设置了一个流格式属性,属性的值是&audioFormat
- AudioUnitSetPropety一次只能设置一个属性
- 设置属性可以设置给scope,也可以设置给element
指定音频单元的部件:
音频单元是由scope和element组成的,所以想要操作音频单元,需要指定标识符
说明:
- scope
- kAudioUnitScope_Global :1)作用于整个音频单元,不作用音频流;2)只有一个element0元素
- kAudioUnitScope_Input和kAudioUnitScope_Output:作用在输入输出scope的元素称为bus,总线
- element
- element是作用在scope的,是嵌套在scope的编程上下文
- 关系
- 属性可以作用于element,也可以作用于scope
- 一个scope可以有多个element,同一个element可以存在于多个scope
注意:
千万千万要注意的一点是:这里是通过element是否是输入还是输出来决定这个I/O单元是用来输入还是输出的,而不是通过scope来决定的。
3.4 设置音频单元的参数
参数是用户可调节的设置,可以在音频单元正在产生音频时改变的参数。
参数也是一个键值对,key是系统已经定义好的数值,value也是数值
常用方法:
- AudioUnitGetParameter得到参数
- AudioUnitSetParameter设置参数
4. audio processing graph
音频处理图可以用来构建和管理音频单元处理链,它可以使用多个音频单元,和实现多个渲染回调函数,允许我们创建几乎任何我们能想象的音频处理解决方案。
特点:
- 线程安全
- 我们重新配置处理图时,音频处理图的API会将配置的音频单元添加到稍后要执行的更改列表中,当指定完所有的更改集后,就可以请求Graph配置他们了,这样就线程安全了。
- 音频处理图总是会有一个完整的I/O单元
- 能够动态的重新分配处理链
- 可以安全的插入AUNode
- 可以在播放音频时交换不同的渲染回调函数
AUNode的认识:
- 音频处理图是管理AUNode的,AUNode是链的节点,作为音频单元的代理
- 它可以看做是音频单元,但是如果要设置音频单元,还必须直接配置音频单元,AUNode本身是不可以配置的
简单过程:
- 将node增加到Graph中
- 直接配置由node配置的音频单元
- 互联node
代码实现:
互联Node的常用方法:
- 添加或者删除音频单元node(AUGraphAddNode, AUGraphRemoveNode)
- 添加或者删除node之间的连接(AUGraphConnectNodeInput, AUGraphDisconnectNodeInput)
- 将渲染回调函数连接到音频单元的input bus(AUGraphSetNodeInputCallback)
5. 回调函数
5.1 音频流数据的获取
音频流程图:
说明:
- 灰色箭头是调用函数的方向
- 橙色箭头是函数返回的音频数据的方向
- 初始的音频数据是音频帧(frame)
- 函数回调执行完返回的音频数据是切片(slice)
过程详解:
- AUGraphStart函数去获取音频时,首先会直接在输出element的缓冲区中找音频数据
- 如果有,则直接输出,如果没有则调用连接到输入的回调函数,也就是它会调用这个绑定输入的函数
- 此时进入到均衡器组件,获取数据,发现也没有,就再调用输入的回调函数
- 此时就从应用内部获取到音频帧
- 拿到音频帧后就一步一步的执行回调函数并返回音频数据,也就是切片(slice)
5.2 回调函数代码实现
注释已足够详细
注意:
- 千万要注意,回调函数必须遵守严格的性能要求
- 回调函数是异步执行的,如果这个函数执行耗时太长,下一个函数已经到达,这个函数尚未完成执行,那么就会导致声音产生间隙
参考文档: