原定假設(shè)需要混音的時(shí)候,就需要兩個(gè)不同的unit,一個(gè)是輸入輸出的Remote i/o Unit 另一個(gè)是混合用的MixUnit

image.png
上圖中,MixUnit的作用主要是將兩個(gè)文件流合成一個(gè),然后輸出
MixUnit可以擁有多個(gè)Bus
AuGraph 連接一組audio Unit 之間的輸入和輸出,構(gòu)成一個(gè)Unit,同時(shí)也為audio unit的輸入提供了回調(diào)。AUGraph抽象了音頻流的處理過(guò)程,子結(jié)構(gòu)可以作為一個(gè)AUNode潛入到更大的結(jié)構(gòu)里面進(jìn)行處理。AUGraph可以遍歷整個(gè)圖的信息,每個(gè)節(jié)點(diǎn)都是一個(gè)或者多個(gè)AUNode,音頻數(shù)據(jù)在點(diǎn)與點(diǎn)之間流通,并且每個(gè)圖都有一個(gè)輸出節(jié)點(diǎn)。輸出節(jié)點(diǎn)可以用來(lái)啟動(dòng)、停止整個(gè)處理過(guò)程。
AUGraph的調(diào)用過(guò)程如下:
//新建并打開
NewAUGraph(&auGraph)
AUGraphOpen(auGraph)
//定義AUNode
AUNode outputNode;
//加入新的節(jié)點(diǎn)1
AUGraphAddNode(auGraph, &outputAudioDesc, &outputNode) //這里的outputAudioDesc為對(duì)音頻流的描述
AUGraphNodeInfo(auGraph, outputNode, NULL, &outputUnit)//AudioUnit outputUnit
//加入新節(jié)點(diǎn)2
AUGraphAddNode(auGraph, &mixAudioDesc, &mixNode) //這里的mixAudioDesc為對(duì)音頻流的描述
AUGraphNodeInfo(auGraph, mixNode, NULL, &mixUnit) //AudioUnit mixUnit 另一個(gè)Unit
//連接到對(duì)應(yīng)
AUGraphConnectNodeInput(auGraph, mixNode, MIX_UNIT_OUTPUT_BUS, outputNode, REMOTE_IO_UNIT_OUTPUT_BUS)
//打開錄制
AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, REMOTE_IO_UNIT_INPUT_BUS, &flag,sizeof(1))
//設(shè)置一個(gè)回調(diào)
AURenderCallbackStruct recordCallback;
recordCallback.inputProc = RecordCallback;
recordCallback.inputProcRefCon = (__bridge void *)self;
AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Output, REMOTE_IO_UNIT_INPUT_BUS, &recordCallback, sizeof(recordCallback))
//開始
AUGraphInitialize(auGraph)
AUGraphStart(auGraph)
需要提前初始化的Buffer and Format;
AudioBufferList *bufferList;
Byte *buffer;
AudioStreamBasicDescription audioFormat;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstacne] setPreferredIOBufferDruation:0.02 error:nil];
//buffer
unit32_t numberBuffers = 1;
bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
bufferList->mNumberBuffers = numberBuffers;
bufferList->mBuffers[0].mNumberChannels = 1;
bufferList->mBuffers[0].mDataByteSize = 2048*2*10;
bufferList->mBuffers[0].mData = malloc(2048*2*10);
buffer = malloc(2048*2*10);
//audio format
audioForamt.mSampleRate = 44100;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
audioFormat.mBitsPerChannel = 16;
1、初始化AUGraph
//AUGraph 初始化
CheckError(NewAUGraph(&auGraph), "NewAUGraph error");
CheckError(AUGraphOpen(auGraph), "open graph fail”);
// output audio unit
AudioComponentDescription outputAudioDesc;
outputAudioDesc.componentType = kAudioUnitType_Output;
outputAudioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
outputAudioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
outputAudioDesc.componentFlags = 0;
outputAudioDesc.componentFlagsMask = 0;
AUNode outputNode;
CheckError(AUGraphAddNode(auGraph, &outputAudioDesc, &outputNode), "add node fail");
CheckError(AUGraphNodeInfo(auGraph, outputNode, NULL, &outputUnit), "get audio unit fail”);
// mix audio unit
AudioComponentDescription mixAudioDesc;
mixAudioDesc.componentType = kAudioUnitType_Mixer;
mixAudioDesc.componentSubType = kAudioUnitSubType_MultiChannelMixer;
mixAudioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
mixAudioDesc.componentFlags = 0;
mixAudioDesc.componentFlagsMask = 0;
AUNode mixNode;
CheckError(AUGraphAddNode(auGraph, &mixAudioDesc, &mixNode), "add node fail");
CheckError(AUGraphNodeInfo(auGraph, mixNode, NULL, &mixUnit), "get audio unit fail”);
//connect
CheckError(AUGraphConnectNodeInput(auGraph, mixNode, MIX_UNIT_OUTPUT_BUS, outputNode, REMOTE_IO_UNIT_OUTPUT_BUS), "connect fail");
// set format
CheckError(AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, REMOTE_IO_UNIT_INPUT_BUS, &audioFormat, sizeof(audioFormat)), "set format fail");
CheckError(AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, REMOTE_IO_UNIT_OUTPUT_BUS, &audioFormat, sizeof(audioFormat)), "set fomat fail”);
//enable record
UInt32 flag = 1;
CheckError(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, REMOTE_IO_UNIT_INPUT_BUS, &flag,sizeof(flag)), "set flag fail”);
// set callback
AURenderCallbackStruct recordCallback;
recordCallback.inputProc = RecordCallback;
recordCallback.inputProcRefCon = (__bridge void *)self;
CheckError(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Output, REMOTE_IO_UNIT_INPUT_BUS, &recordCallback, sizeof(recordCallback)), "set property fail");
CheckError(AUGraphInitialize(auGraph), "init augraph fail");
CheckError(AUGraphStart(auGraph), "start graph fail");
對(duì)應(yīng)的音頻內(nèi)容混合后回調(diào)
static OSStatus RecordCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
ViewController *vc = (__bridge ViewController *)inRefCon;
vc->buffList->mNumberBuffers = 1;
OSStatus status = AudioUnitRender(vc->outputUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, vc->buffList);
if (status != noErr) {
NSLog(@"AudioUnitRender error:%d", status);
}
NSLog(@"RecordCallback size = %d", vc->buffList->mBuffers[0].mDataByteSize);
[vc writePCMData:vc->buffList->mBuffers[0].mData size:vc->buffList->mBuffers[0].mDataByteSize];
return noErr;
}
2、初始化MixUnit 和output Unit
UInt32 busCount = 2;
CheckError(AudioUnitSetProperty(mixUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, MIX_UNIT_INPUT_BUS0, &busCount, sizeof(UInt32)), "set property fail”);
UInt32 size = sizeof(UInt32);
CheckError(AudioUnitGetProperty(mixUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, MIX_UNIT_INPUT_BUS0, &busCount, &size), "get property fail");
CheckError(AudioUnitGetProperty(outputUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Global, REMOTE_IO_UNIT_INPUT_BUS, &busCount, &size), "get property fail”);
//文件流讀取回調(diào)
AURenderCallbackStruct callback0;
callback0.inputProc = mixCallback0;
callback0.inputProcRefCon = (__bridge void *)self;
CheckError(AudioUnitSetProperty(mixUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, MIX_UNIT_INPUT_BUS0, &callback0, sizeof(AURenderCallbackStruct)), "add mix callback fail");
CheckError(AudioUnitSetProperty(mixUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MIX_UNIT_INPUT_BUS0, &audioFormat, sizeof(AudioStreamBasicDescription)), "set mix format fail”);
//麥克風(fēng)采麥回調(diào)
AURenderCallbackStruct callback1;
callback1.inputProc = mixCallback1;
callback1.inputProcRefCon = (__bridge void *)self;
CheckError(AudioUnitSetProperty(mixUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, MIX_UNIT_INPUT_BUS1, &callback1, sizeof(AURenderCallbackStruct)), "add mix callback fail”);
CheckError(AudioUnitSetProperty(mixUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MIX_UNIT_INPUT_BUS1, &audioFormat, sizeof(AudioStreamBasicDescription)), "set mix format fail");
音頻輸入的回調(diào)
#pragma mark - callback
static OSStatus mixCallback0(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
ViewController *vc = (__bridge ViewController *)inRefCon;
NSInteger bytes = CONST_BUFFER_SIZE < ioData->mBuffers[0].mDataByteSize * 2 ? CONST_BUFFER_SIZE : ioData->mBuffers[0].mDataByteSize * 2; //
bytes = [vc->inputSteam read:vc->buffer maxLength:bytes];
for (int i = 0; i < bytes; ++i) {
((Byte*)ioData->mBuffers[0].mData)[i/2] = vc->buffer[i];
}
ioData->mBuffers[1].mDataByteSize = (UInt32)bytes / 2;
return noErr;
}
static OSStatus mixCallback1(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
ViewController *vc = (__bridge ViewController *)inRefCon;
memcpy(ioData->mBuffers[0].mData, vc->buffList->mBuffers[0].mData, vc->buffList->mBuffers[0].mDataByteSize);
ioData->mBuffers[0].mDataByteSize = vc->buffList->mBuffers[0].mDataByteSize;
return noErr;
}
寫入的函數(shù)
- (void)writePCMData:(Byte *)buffer size:(int)size {
static FILE *file = NULL;
NSString *path = [NSTemporaryDirectory() stringByAppendingString:@"/record.pcm"];
if (!file) {
file = fopen(path.UTF8String, "w");
}
fwrite(buffer, size, 1, file);
}
檢查函數(shù)
static void CheckError(OSStatus error, const char *operation)
{
if (error == noErr) return;
char str[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
str[0] = str[5] = '\'';
str[6] = '\0';
} else
// no, format it as an integer
sprintf(str, "%d", (int)error);
fprintf(stderr, "Error: %s (%s)\n", operation, str);
exit(1);
}