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