在iOS平台上,所有的音频框架都是基于AudioUnit实现的,使用AudioUnit会给你带来最大的自由度。
使用场景
- Simultaneous audio I/O (input and output) with low latency, such as for a VoIP (Voice over Internet Protocol) application
- 低延迟的音频I/O场景,比如VoIP(俗称网络电话)
- Responsive playback of synthesized sounds, such as for musical games or synthesized musical instruments
- 多路声音合成回放,比如游戏、音乐合成器
- Use of a specific audio unit feature such as acoustic echo cancelation, mixing, or tonal equalization
- 使用AudioUnit特有功能,比如回声消除、混合、均衡器等
- A processing-chain architecture that lets you assemble audio processing modules into flexible networks. This is the only audio API in iOS offering this capability.
- 想使用音频处理模块组成一个处理链网络(一种图状的数据结构),在iOS平台中AudioUnit是唯一提供这种功能的API
理论
AudioUnit类型
Effect
kAudioUnitType_Effect
用于声音特效处理常用子类型如下:
- 均衡效果器:
kAudioUnitSubType_NBandEQ
- 压缩效果器:
kAudioUnitSubType_DynamicsProcessor
- 混响效果器:
kAudioUnitSubType_Reverb2
- 高通:
kAudioUnitSubType_HighPassFilter
- 低通:
kAudioUnitSubType_LowPassFilter
- 带通:
kAudioUnitSubType_BandPassFilter
- 延迟:
kAudioUnitSubType_Delay
- 压限:
kAudioUnitSubType_PeakLimiter
- 均衡效果器:
Mixing
kAudioUnitType_Mixer
用于合成多路音频流常用子类型:
- MultiChannelMixer:
kAudioUnitSubType_MultiChannelMixer
混合多路声音的效果器,接收多路声音的输入,可以单独调整每一路声音的增益与开关,将多路声音混合成一路输出。
- MultiChannelMixer:
I/O
kAudioUnitType_Output
提供I/O
功能常用子类型:
- RemoteIO:
kAudioUnitSubType_RemoteIO
用来采集音频与播放音频,使用麦克风和扬声器。 - Generic Output:
kAudioUnitSubType_GenericOutput
离线处理,不需要使用扬声器播放,将音频数据放入内存队列或文件中时使用此类型。
- RemoteIO:
Format conversion
kAudioUnitType_FormatConverter
用于格式转换功能,如采样格式Float
到SInt16
、交错和平铺格式转换、单双声道转换。常用子类型:
- AUConverter:
kAudioUnitSubType_AUConverter
格式转换效果器 - Time Pitch:
kAudioUnitSubType_TimePitch
变速变调效果器
- AUConverter:
Generator Unit
kAudioUnitType_Generator
常用它来提供播放器功能常用子类型:
- AudioFilePlayer:
kAudioUnitSubType_AudioFilePlayer
如果输入不是麦克风是一个媒体文件使用此类型。
- AudioFilePlayer:
AudioUnit概念
一个AudioUnit
包含1~2条Element
,Element
是音频数据处理的上下文,也称为Bus
。每一个Element
分为输入和输出两部分,分别称为Input scope
和Output scope
。
官网提供的I/O
类型的AudioUnit
的数据处理流程图。I/O Unit
将从麦克风收集来的音频数据通过Element 1
的Input scope
输入经过Element 1
的Output scope
输出到我们的App
中。经过我们的处理逻辑后在通过Element 0
的Input scope
输入,最后通过Element 0
的Output scope
输出到扬声器中。
一个将多个AudioUnit
连接起来处理音频的流程图
播放音频实践
创建AudioUnit
要创建AudioUnit
首先要创建一个AudioUnit
描述结构体AudioComponentDescription
构造方法如下
1 | public init() |
- componentType:AudioUnit的类型, 例如:
kAudioUnitType_Output
- componentSubType:AudioUnit的子类型,例如:
kAudioUnitSubType_RemoteIO
- componentManufacturer:厂商 直接写
kAudioUnitManufacturer_Apple
就可以了 - componentFlags:文档中写明必须为0
- componentFlagsMask: 同样必须为0
创建好AudioComponentDescription
结构体就可以使用它来创建AudioUnit
了,创建AudioUnit有两种方式:
直接创建
1
2
3
4//使用AudioComponentFindNext方法根据AudioComponentDescription中的描述取得符合条件的AudioComponent对象
let component = AudioComponentFindNext(nil, &componentDesc)
//根据这个AudioComponent对象创建出AudioUnit
AudioComponentInstanceNew(component, &audioUnit)使用AUGraph创建
1
2
3
4
5
6
7
8
9//首先创建一个AUGraph
var playerGraph: AUGraph
NewAUGraph(&playerGraph)
//使用AudioComponentDescription的描述在AUGraph对象中添加一个AUNode
AUGraphAddNode(playerGraph, &playerDesc, &playerNode)
//打开AUGraph 这个方法就会实例化其中添加的所有AUNode,然后根据AUNode取得AudioUnit
AUGraphOpen(playerGraph)
//从playerNode中取得playerUnit实例
AUGraphNodeInfo(playerGraph, playerNode, nil, &playerUnit)
设置AudioUnit的参数
拿到AudioUnit
实例后就可以这是AudioUnit
中的参数,下面是以一个Remote I/O
类型的AudioUnit
为例的参数设置代码:
1 | let busZero: UInt32 = 0 // Element 0 |
创建AudioStreamBasicDescription
这是一个音频数据的描述结构体,可以将这个描述设置到AudioUnit
的输入或输出上,因为AudioUnit
可以是多个连接在一起的,一个AudioUnit
的输入是另一个AudioUnit
的输出,所以分为输入和输出两部分。
1 | let bytesPerSample: UInt32 = UInt32(MemoryLayout<Float32>.size) |
- mSampleRate:采样率
- mFormatFlags:指定每个采样的数据格式,这里设置为
Float
,kAudioFormatFlagIsNonInterleaved
表示非交错存储,音频数据实际存储在AudioBufferList
结构中的mBuffers
中,如果是非交错存储,左右声道音频数据就会分别存储在mBuffers[0]
和mBuffers[1]
中,如果是Interleaved
交错存储,那么左右声道的音频数据就会交错存储在mBuffers[0]
中。 - mBytesPerPacket:根据
mFormatFlags
指定的Float
类型非交错存储,就设置为bytesPerSample
表示每个采样的字节数。但如果是Interleaved
交错存储的,就应该设置为bytesPerSample * mChannelsPerFrame
因为左右声道数据是交错存在一起的。 - mBytesPerFrame:同
mBytesPerPacket
- mBitsPerChannel:表示每个声道的音频数据要多少位,一个字节是8位,所以用8 * 每个采样的字节数
接下来就可以将AudioStreamBasicDescription
设置给AudioUnit
了
1 | //varAudioDesc是一个AudioStreamBasicDescription的实例设置给了audioUnit的Element0的Input输入端 |
回调结构AURenderCallbackStruct
当AudioUnit
启动后,就会调用AURenderCallbackStruct
结构中指定的函数,取得数据
1 | var callbackStruct = AURenderCallbackStruct(inputProc: { (inRefCon, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData) -> OSStatus in |
AURenderCallbackStruct
有两个参数,一个是AURenderCallback
的函数指针,在Swift中使用一个闭包传递进去,因为C的函数指针不允许捕获外部对象,不能使用self
调用方法,所以AURenderCallbackStruct
第二个参数可以把self
的指针传递到AURenderCallback
函数中,以备使用。