iOS 混音实现

作者: 飛天江郎 | 来源:发表于2019-10-09 19:58 被阅读0次

    原定假设需要混音的时候,就需要两个不同的unit,一个是输入输出的Remote i/o Unit 另一个是混合用的MixUnit

    image.png

    上图中,MixUnit的作用主要是将两个文件流合成一个,然后输出

    MixUnit可以拥有多个Bus

    AuGraph 连接一组audio Unit 之间的输入和输出,构成一个Unit,同时也为audio unit的输入提供了回调。AUGraph抽象了音频流的处理过程,子结构可以作为一个AUNode潜入到更大的结构里面进行处理。AUGraph可以遍历整个图的信息,每个节点都是一个或者多个AUNode,音频数据在点与点之间流通,并且每个图都有一个输出节点。输出节点可以用来启动、停止整个处理过程。

    AUGraph的调用过程如下:

    //新建并打开
    NewAUGraph(&auGraph)
    AUGraphOpen(auGraph)
    //定义AUNode
    AUNode outputNode;
    
    //加入新的节点1
    AUGraphAddNode(auGraph, &outputAudioDesc, &outputNode) //这里的outputAudioDesc为对音频流的描述
    AUGraphNodeInfo(auGraph, outputNode, NULL, &outputUnit)//AudioUnit outputUnit
    
    //加入新节点2
    AUGraphAddNode(auGraph, &mixAudioDesc, &mixNode) //这里的mixAudioDesc为对音频流的描述
    AUGraphNodeInfo(auGraph, mixNode, NULL, &mixUnit) //AudioUnit mixUnit 另一个Unit
    
    //连接到对应
    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))
    
    //设置一个回调
    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");
    

    对应的音频内容混合后回调

    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”); 
    
    //文件流读取回调
    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”); 
    
    //麦克风采麦回调
    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");
    

    音频输入的回调

    #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; 
    } 
    

    写入的函数

    - (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); 
    
    }
    

    检查函数

    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);
    }
    

    相关文章

      网友评论

        本文标题:iOS 混音实现

        本文链接:https://www.haomeiwen.com/subject/hjfnpctx.html