一其障、Audio Unit綜述
1.1镰吆、Audio Unit 概念點(diǎn)
1.2芹助、 AuidoUnit類型
二、構(gòu)建Audio Unit的流程
2.1 挟纱、配置AudioSession
2.2羞酗、指定 Audio Units類型
2.3、創(chuàng)建AudioUnit
2.4紊服、設(shè)置AudioUnit的屬性
三檀轨、數(shù)據(jù)處理
3.1、 AURenderCallbackStruct
3.2欺嗤、串連的Audio node
3.3参萄、數(shù)據(jù)的轉(zhuǎn)換
四、附錄
〖灞4.1讹挎、Audio Unit 示例
一、Audio Unit綜述
相對(duì)于MacOS,Audio Unit在iOS上使用到的幾率很小吆玖,AV Foundation 和Audio Toolbox提供的API已經(jīng)滿足我們平常開(kāi)發(fā)中音視頻的錄制播放的需求點(diǎn)筒溃。Audio Unit幾乎可以認(rèn)為是對(duì)硬件驅(qū)動(dòng)層的封裝,通過(guò)它獲取麥克風(fēng)采集的音頻數(shù)據(jù)或者將音頻數(shù)據(jù)傳輸給揚(yáng)聲器播放沾乘。但是隨著直播熱對(duì)音視頻的傳輸速度高要求怜奖,將PCM音頻轉(zhuǎn)換成AAC主要用到就是Audio Unit。
與AV Foundation 和Audio Toolbox相比較翅阵,Audio Unit主要有兩大優(yōu)勢(shì):
(1)時(shí)效性高歪玲,Audio Unit是接近硬件層導(dǎo)致對(duì)音頻流的采集回調(diào)更加迅速。
(2)動(dòng)態(tài)的配置怎顾,AUGraph可以動(dòng)態(tài)的對(duì)音頻數(shù)據(jù)的組合配置读慎,改變音效。
1.1Audio Unit 概念點(diǎn):
Audio Unit 主要涉及到三個(gè)常用的概念知識(shí):
(1)AUGraph:包含和管理Audio Unit 的組織者槐雾;
(2)AUNode /AudioComponent:是AUGraph音頻處理環(huán)節(jié)中的一個(gè)節(jié)點(diǎn)夭委。
(3)AudioUnit: 音頻處理組件,是對(duì)音頻處理節(jié)點(diǎn)的實(shí)例描述者和操控者募强。
我們不妨想像演唱會(huì)的舞臺(tái)上株灸,有錄制歌聲與樂(lè)器的麥克風(fēng),而從麥克風(fēng)到輸出到音響之間擎值,還串接了大大小小的效果器慌烧,在這個(gè)過(guò)程中,無(wú)論是麥克風(fēng)鸠儿、音響或是效果器屹蚊,都是不同的AUNode厕氨。AUNode 是這些器材的實(shí)體,而我們要操控這些器材汹粤、改變這些器材的效果屬性命斧,就會(huì)需要透過(guò)每個(gè)器材各自的操控界面,這些介面便是AudioUnit嘱兼,最后構(gòu)成整個(gè)舞臺(tái)国葬,便是AUGraph。AUNode 與AudioComponent 的差別在于芹壕,其實(shí)像上面講到的各種器材汇四,除了可以放在AUGraph 使用之外,也可以單獨(dú)使用踢涌,比方說(shuō)我們有臺(tái)音響通孽,我們除了把音響放在舞臺(tái)上使用外,也可以單獨(dú)拿這臺(tái)音響輸出音樂(lè)睁壁。當(dāng)我們要在AUGraph 中使用某個(gè)器材利虫,我們就要使用AUNode 這種形態(tài),單獨(dú)使用時(shí)堡僻,就使用AudioComponent。但無(wú)論是操作AUNode 或AudioComponent疫剃,都還是得透過(guò)AudioUnit 這一層操作界面钉疫。(上述文字摘自KKBOX iOS/Mac OS X 基礎(chǔ)開(kāi)發(fā)教材)
下圖所示兩路音頻數(shù)據(jù)首先經(jīng)過(guò)均衡器單元,然后再經(jīng)過(guò)混音單元組合在一起巢价, 最后經(jīng)由輸入輸出單元傳輸?shù)降綋P(yáng)聲器牲阁。
1.2 AuidoUnit類型
iOS提供了四大類別7種不同的AuidoUnit
AudioComponentDescription對(duì)象來(lái)描述一個(gè)具體的AudioUnit:
typedef struct AudioComponentDescription {
OSType componentType;
OSType componentSubType;
OSType componentManufacturer;
UInt32 componentFlags;
UInt32 componentFlagsMask;
} AudioComponentDescription;
- componentType AuidoUnit主要有四種大類型:均衡器/混音/輸入輸出/格式轉(zhuǎn)換;
- componentSubType 指的四大類型對(duì)應(yīng)的子類型 可以對(duì)照下面的表壤躲;
- componentManufacturer 目前iOS開(kāi)發(fā)中只有: kAudioUnitManufacturer_Apple城菊;
- componentFlags和“componentFlagsMask一般設(shè)置為0。
類型 | componentType | kAudioUnitType_Effect |
---|---|---|
均衡器 | kAudioUnitType_Effect | kAudioUnitSubType_AUiPodEQ |
混音 | kAudioUnitType_Mixer | kAudioUnitSubType_AU3DMixerEmbedded kAudioUnitSubType_MultiChannelMixer多路 混音 |
輸入輸出 | kAudioUnitType_Output | kAudioUnitSubType_RemoteIO遠(yuǎn)端kAudioUnitSubType_VoiceProcessingIO kAudioUnitSubType_GenericOutput |
格式轉(zhuǎn)換 | kAudioUnitType_FormatConverter | kAudioUnitSubType_AUConverter |
二碉克、構(gòu)建Audio Unit的流程
2.1 配置AudioSession
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setPreferredSampleRate:44100 error:&error];
[audioSession setPreferredInputNumberOfChannels:1 error:&error];
[audioSession setPreferredIOBufferDuration:0.05 error:&error];
[audioSession setActive: YES error: nil];
2.2凌唬、指定 Audio Units類型
// multichannel mixer unit
AudioComponentDescription mixer_desc;
mixer_desc.componentType = kAudioUnitType_Mixer;
mixer_desc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixer_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixer_desc.componentFlags = 0;
mixer_desc.componentFlagsMask = 0;
// multichannel mixer unit
AudioComponentDescription eq_desc;
eq_desc.componentType = kAudioUnitType_Effect;
eq_desc.componentSubType = kAudioUnitSubType_AUiPodEQ;
eq_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
eq_desc.componentFlags = 0;
eq_desc.componentFlagsMask = 0;
// output unit
AudioComponentDescription output_desc;
output_desc.componentType = kAudioUnitType_Output;
output_desc.componentSubType = kAudioUnitSubType_RemoteIO;
output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
output_desc.componentFlags = 0;
output_desc.componentFlagsMask = 0;
2.3、創(chuàng)建AudioUnit
(1)通過(guò)AudioComponent創(chuàng)建
AudioComponent outComponent = AudioComponentFindNext(NULL,&output_desc);
AudioUnit outUnit;
AudioComponentInstanceNew(outComponent, &outUnit);
AudioComponentFindNext參數(shù)inComponent一般設(shè)置為NULL漏麦,從系統(tǒng)中找到第一個(gè)符合inDesc描述的Component客税,如果為其賦值,則從其之后進(jìn)行尋找撕贞。
函數(shù)AudioComponentInstanceNew的第二個(gè)參數(shù)類型是AudioComponentInstance更耻,AudioUnit實(shí)際上就是 AudioComponentInstance,在AudioComponent類中有定義:
typedef AudioComponentInstance AudioUnit;
(2)通過(guò)AUNode創(chuàng)建AudioUnit
AUGraph是由AUNode的串聯(lián)而成,首先需要先創(chuàng)建一個(gè) AUGraph:
OSStatus status = NewAUGraph(&audioGraph);
獲取一個(gè) AUNode,第一個(gè)參數(shù)是創(chuàng)建的AUGraph捏膨。第二個(gè)參數(shù)是描述信息AudioComponentDescription秧均,輸出AUNode食侮。
AUNode mixerNode;
status = AUGraphAddNode(audioGraph, &mixerUnitDescription, &mixerNode);
獲取一個(gè) AudioUnit,第一個(gè)和第三個(gè)參數(shù)是還是之前的AUGraph,AudioComponentDescription目胡。第二個(gè)參數(shù)是我們剛才的AUNode锯七,最終輸出的AudioUnit。
status = AUGraphNodeInfo(audioGraph, mixerNode, &mixerUnitDescription, &mixerUnit);
2.4讶隐、設(shè)置AudioUnit的屬性
AudioUnit實(shí)際上就是一個(gè)AudioComponentInstance實(shí)例對(duì)象起胰,一個(gè)AudioUnit由scope(范圍)和element(元素)組成,實(shí)際上開(kāi)發(fā)中主要涉及到輸入輸出的問(wèn)題
CF_ENUM(AudioUnitScope) {
kAudioUnitScope_Global = 0,
kAudioUnitScope_Input = 1,
kAudioUnitScope_Output = 2,
kAudioUnitScope_Group = 3,
kAudioUnitScope_Part = 4,
kAudioUnitScope_Note = 5,
kAudioUnitScope_Layer = 6,
kAudioUnitScope_LayerItem = 7
};
scope主要使用到的輸入kAudioUnitScope_Input和輸出kAudioUnitScope_Output巫延,而在element效五, Input用“1”(和I很像)表示,Output用“0”(和O很像)表示炉峰,
AudioUnitSetProperty( AudioUnit inUnit,
AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
const void * __nullable inData,
UInt32 inDataSize)
- AudioUnitPropertyID 設(shè)置屬性名稱
- AudioUnitScope AudioUnit的Scope 主要用于輸入輸出范圍
- AudioUnitElement AudioUnit的Element 主要用1 輸入總線(bus),0輸出總線(bus);
- inData 輸入值
- inDataSize 輸入值的長(zhǎng)度
AudioUnit 的Remote IO有2個(gè)element畏妖,大部分代碼和文獻(xiàn)都用bus代替element,兩者同義疼阔,bus0就是輸出bus 1代表輸入轴合,播放音頻文件就是在bus 0傳送數(shù)據(jù),bus 1輸入在Remote IO 默認(rèn)是關(guān)閉的客情,在錄音的狀態(tài)下 需要把bus 1設(shè)置成開(kāi)啟狀態(tài)溺森。
UInt32 one = 1;
AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof(one));
三、數(shù)據(jù)處理
3.1淘邻、AURenderCallbackStruct
在Audio Unit存儲(chǔ)輸入的數(shù)據(jù)和提供播放輸出數(shù)據(jù)都是通過(guò)RenderCallback函數(shù)茵典,通過(guò)AudioUnitSetProperty與輸入輸出回調(diào)相關(guān)聯(lián)。
AURenderCallbackStruct callBackStruct;
callBackStruct.inputProc = BXAURenderCallback;
callBackStruct.inputProcRefCon = (__bridge void * _Nullable)(self);
AudioUnitSetProperty(_outAudioUinit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callBackStruct, sizeof(AURenderCallbackStruct));
AURenderCallbackStruct是結(jié)構(gòu)體宾舅,inputProc 就是我們注冊(cè)回調(diào)函數(shù)统阿。inputProcRefCon注冊(cè)者。
typedef struct AURenderCallbackStruct {
AURenderCallback __nullable inputProc;
void * __nullable inputProcRefCon;
} AURenderCallbackStruct;
typedef OSStatus(*AURenderCallback)(void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * __nullable ioData);
- ioData 需要填充的緩存數(shù)據(jù)
- inNumberFrames 需要填充的數(shù)據(jù)幀數(shù)筹我,根據(jù)這個(gè)幀數(shù) 從原始音頻數(shù)據(jù)格式中輸出多少frame的LPCM.
- ioActionFlags 數(shù)據(jù)回調(diào)發(fā)送錯(cuò)誤或者其他情況 上下文傳遞數(shù)據(jù)扶平。
*inBusNumber 是輸出或者輸出的哪個(gè)bus.
3.2、串連的Audio node
AUGraph 中的各個(gè)Audio unit 是傳連接的 需要把各個(gè)unit 通過(guò)AUGraphConnectNodeInput 連接起來(lái) 一般主要用于混音 或者音效改變的時(shí)候 需要用到蔬蕊。
AUGraphConnectNodeInput(_audioGraph, EQNode, 0, outNode, 0);
3.3结澄、數(shù)據(jù)的轉(zhuǎn)換
AudioConverterRef 第一個(gè)參數(shù)是輸入的格式 第二個(gè)是需要輸出的轉(zhuǎn)換格式。
extern OSStatus
AudioConverterNew( const AudioStreamBasicDescription * inSourceFormat,
const AudioStreamBasicDescription * inDestinationFormat,
AudioConverterRef __nullable * __nonnull outAudioConverter)
需要把我們轉(zhuǎn)換的LPCM格式回調(diào)輸入AudioConverterFillComplexBuffer
extern OSStatus
AudioConverterFillComplexBuffer( AudioConverterRef inAudioConverter,
AudioConverterComplexInputDataProc inInputDataProc,
void * __nullable inInputDataProcUserData,
UInt32 * ioOutputDataPacketSize,
AudioBufferList * outOutputData,
AudioStreamPacketDescription * __nullable outPacketDescription)
AudioConverterComplexInputDataProc回調(diào)函數(shù)就是讀取原有數(shù)據(jù)的幀數(shù)據(jù) 放置于ioData中
static OSStatus BXPlayerConverterFiller(AudioConverterRef inAudioConverter, UInt32 * ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription** outDataPacketDescription, void * inUserData)
{
BXAudioUnitEQPlayer *self = (__bridge BXAudioUnitEQPlayer *)(inUserData);
if (self->_readPacketIndex >= self->_packetArray.count) {
*ioNumberDataPackets = 0;
return 'bxnd';
}
NSData *packet = self->_packetArray[self->_readPacketIndex];
ioData->mNumberBuffers = 1;
ioData->mBuffers[0].mData = (void *)packet.bytes;
ioData->mBuffers[0].mDataByteSize = (UInt32)packet.length;
static AudioStreamPacketDescription aspdesc;
aspdesc.mDataByteSize = (UInt32)packet.length;
aspdesc.mStartOffset = 0;
aspdesc.mVariableFramesInPacket = 1;
*outDataPacketDescription = &aspdesc;
self->_readPacketIndex++;
*ioNumberDataPackets = 1;
return noErr;
}
四岸夯、附錄
AudioUnit掌握需要同學(xué)們切合實(shí)際的敲代碼運(yùn)用AudioUnit 使用的簡(jiǎn)單示例