iOS 混音實(shí)現(xiàn)

原定假設(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);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Linear PCM 在介紹Core Audio之前,先介紹一下最常用的非壓縮數(shù)字音頻格式Linear PCM(線...
    huangjun0閱讀 4,995評(píng)論 0 2
  • 前言 相關(guān)文章:使用VideoToolbox硬編碼H.264使用VideoToolbox硬解碼H.264使用Aud...
    落影l(fā)oyinglin閱讀 8,531評(píng)論 8 33
  • 教程一:視頻截圖(Tutorial 01: Making Screencaps) 首先我們需要了解視頻文件的一些基...
    90后的思維閱讀 4,987評(píng)論 0 3
  • 我們小區(qū)的大媽走出去都有種趾高氣昂的勢(shì)頭,無(wú)他,在本市的廣場(chǎng)舞江湖中,我們小區(qū)組建了最有實(shí)力的幫派。從服裝...
    我的喜歡是藍(lán)色的閱讀 588評(píng)論 2 5
  • 這是個(gè)隨心所欲的世界 我愿意聽從自己的內(nèi)心 當(dāng)呼吸急促的時(shí)候 應(yīng)該是聞到了你的氣息 長(zhǎng)大了就不會(huì)痛苦流涕 回頭就可...
    掛居寶閱讀 280評(píng)論 0 0

友情鏈接更多精彩內(nèi)容