AudioUnit conversion/effects with RenderCallback

I am trying to use AudioUnits to take the Mic input, convert it to a different sample rate, and filter the any frequencies above 4000 hz. To do this, I try to have the following connection:


RemoteIO -> Convert to Effects formats -> Filter frequencies using kAudioUnitSubType_LowPassFilter -> Convert to final format


I will then take the output and send it as a network stream over RTP. Because of this, I want to use the RenderCallback.


My confusion is what do I set the callback on and what do I call AudioUnitRender on? From what I can tell, I have to set the callback on the RemoteIO unit. But then, do I call AudioUnitRender on that RemoteIO unit or do I call it on one of the other units? When I call it on other units, I get errors. When I call it on the RemoteIO unit, I do get audio but it's not clear to me if anything is getting filtered or not.


Here is my code:


//This is the component for the Mic input
    AudioComponentDescription desc;
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_RemoteIO;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

  
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
    CheckError(AudioComponentInstanceNew(inputComponent, &theAudioUnit), "Instance AU");
    UInt32 flag = 1;
    CheckError(AudioUnitSetProperty(theAudioUnit,
                                  kAudioOutputUnitProperty_EnableIO,
                                  kAudioUnitScope_Input,
                                  1,
                                  &flag,
                                  1), "EnableIO");

//This is the final audio format I will use
    AudioStreamBasicDescription audioFormat;
    audioFormat.mSampleRate = 8000; //Need a sample rate of 8000
    audioFormat.mChannelsPerFrame = numberOfChannels;
    audioFormat.mFormatID = kAudioFormatLinearPCM;
    audioFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
    audioFormat.mBitsPerChannel = 16;
    audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame = (audioFormat.mBitsPerChannel / 8) * audioFormat.mChannelsPerFrame;
    audioFormat.mFramesPerPacket = 1;

//Here is the effect filter for frequencies above 4000
    AudioComponentDescription lowPass;
    lowPass.componentType = kAudioUnitType_Effect;
    lowPass.componentSubType = kAudioUnitSubType_LowPassFilter;
    lowPass.componentManufacturer = kAudioUnitManufacturer_Apple;
    lowPass.componentFlags = 0;
    lowPass.componentFlagsMask = 0;

//This is the converter from Mic input to effect format
    AudioComponentDescription converter;
    converter.componentType = kAudioUnitType_FormatConverter;
    converter.componentSubType = kAudioUnitSubType_AUConverter;
    converter.componentManufacturer = kAudioUnitManufacturer_Apple;
    converter.componentFlags = 0;
    converter.componentFlagsMask = 0;

    AudioComponent converterComponent = AudioComponentFindNext(nil, &converter);
    CheckError(AudioComponentInstanceNew(converterComponent, &converterUnit), "Converter inst");

    AudioComponent effectComponent = AudioComponentFindNext(nil, &lowPass);
    CheckError(AudioComponentInstanceNew(effectComponent, &lowPassUnit), "Effect Inst");

//Here is the audio format from the Mic
    AudioStreamBasicDescription theFormat;
    memset (&theFormat, 0, sizeof (theFormat));
    UInt32 sizeofDesc = sizeof(AudioStreamBasicDescription);
    CheckError(AudioUnitGetProperty(theAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &theFormat, &sizeofDesc), "Get IO Format");

//Now I set it as the input for the converter
    CheckError(AudioUnitSetProperty(converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &theFormat, sizeof(AudioStreamBasicDescription)), "Set Converter In fomrat");

//Here is the audio format from the Effect
    AudioStreamBasicDescription effectFormat;
    memset(&effectFormat, 0, sizeof(effectFormat));
    CheckError(AudioUnitGetProperty(lowPassUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &effectFormat,&sizeofDesc), "Get Lowpass Format");

//Now i set that as the output for the converter
    CheckError(AudioUnitSetProperty(converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &effectFormat, sizeof(AudioStreamBasicDescription)), "Set cConverter out format");

    AudioUnitConnection connection;
    connection.sourceAudioUnit = theAudioUnit;
    connection.sourceOutputNumber = 1;
    connection.destInputNumber = 0;

//Here I connect the Mic input to the converter
    CheckError(AudioUnitSetProperty(converterUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, 0, &connection, sizeof(AudioUnitConnection)), "Make Conn audio to convert");

    AudioUnitConnection lowPassConnection;
    lowPassConnection.sourceAudioUnit = converterUnit;
    lowPassConnection.sourceOutputNumber = 0;
    lowPassConnection.destInputNumber = 0;

//Here I connect the converter to the Effect filter
    CheckError(AudioUnitSetProperty(lowPassUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, 0, &lowPassConnection, sizeof(AudioUnitConnection)), "Make Conn conv to low pass");

//Here I set the Frequency cutoff
    Float32 lowPassFreq = 3999;
    CheckError(AudioUnitSetParameter(lowPassUnit,
                          kLowPassParam_CutoffFrequency,
                          kAudioUnitScope_Global,
                          0,
                          lowPassFreq,
                                     sizeof(Float32)), "Set Low Pass freq");

//Here will be the converter from the Filter to the final output format I need
    AudioComponentDescription outConverter;
    outConverter.componentType = kAudioUnitType_FormatConverter;
    outConverter.componentSubType = kAudioUnitSubType_AUConverter;
    outConverter.componentManufacturer = kAudioUnitManufacturer_Apple;
    outConverter.componentFlags = 0;
    outConverter.componentFlagsMask = 0;

    AudioComponent outConverterComponent = AudioComponentFindNext(nil, &outConverter);
    CheckError(AudioComponentInstanceNew(outConverterComponent, &outConverterUnit), "Out Converter inst");

//Set the input of this to be the Effect format
    CheckError(AudioUnitSetProperty(outConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &effectFormat, sizeof(AudioStreamBasicDescription)), "Out Convreter  set input format");

//Set the output of this to be the final audio format I need
    CheckError(AudioUnitSetProperty(outConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &audioFormat, sizeof(AudioStreamBasicDescription)), "Out Converter output format");

    AudioUnitConnection outConnection;
    outConnection.sourceAudioUnit = lowPassUnit;
    outConnection.sourceOutputNumber = 0;
    outConnection.destInputNumber = 0;

//Set the connection between effect and converter
    CheckError(AudioUnitSetProperty(outConverterUnit, kAudioUnitProperty_MakeConnection, kAudioUnitScope_Input, 0, &outConnection, sizeof(AudioUnitConnection)), "Make connection out conv to low pass");

//Initialize everything
    AudioUnitInitialize(theAudioUnit);
    AudioUnitInitialize(converterUnit);
    AudioUnitInitialize(lowPassUnit);
    AudioUnitInitialize(outConverterUnit);

    AURenderCallbackStruct callbackStruct;
    callbackStruct.inputProc = recordingCallback;
    callbackStruct.inputProcRefCon = (__bridge void * _Nullable)(self);

//I need a render callback, so I set it on the Mic input
    CheckError(AudioUnitSetProperty(theAudioUnit,
                                  kAudioOutputUnitProperty_SetInputCallback,
                                  kAudioUnitScope_Global,
                                  1,
                                  &callbackStruct,
                                    sizeof(AURenderCallbackStruct)), "Set input callback");
  //And start the Mic input
    CheckError(AudioOutputUnitStart(theAudioUnit), "Out converter start");


And here is the Render Callback:

    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = NULL;
    NSLog(@"Number frames: %u, bus nubmer: %u", (unsigned int)inBusNumber, (unsigned int)inNumberFrames);

//This is where I'm confused. What should the unit be here? Is it the audio unit? Converter unit?
    OSStatus status = AudioUnitRender(theAudioUnit,
                             ioActionFlags,
                             inTimeStamp,
                             1,
                             inNumberFrames,
                             &bufferList);

Replies

hello are you resolve this problem? and I have the same question about how to get the "after effect" data? the input callback data is the recording data ,not the effect data