2

I'm writing an iOS app that captures audio from the microphone, filters it with a high-pass filter, and plays it back through the speakers.

I'm getting a -50 OSStatus error when I call AudioUnitRender on the render callback function when I run it on an iPhone 4S, but it runs fine on the simulator.

I'm using an AUGraph, which has a RemoteIO unit, a HighPassFilter effect unit, and an AUConverter unit to make the ASBDs between the HPF and the output match. The converter AudioUnit instance is called converterUnit.

Here's the code.

static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{

    AudioController *THIS = (AudioController*)inRefCon;

    AudioBuffer buffer;

    AudioStreamBasicDescription converterOutputASBD;
    UInt32 converterOutputASBDSize = sizeof(converterOutputASBD);
    AudioUnitGetProperty([THIS converterUnit], kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &converterOutputASBD, &converterOutputASBDSize);

    buffer.mDataByteSize = inNumberFrames * converterOutputASBD.mBytesPerFrame;
    buffer.mNumberChannels = converterOutputASBD.mChannelsPerFrame;
    buffer.mData = malloc(buffer.mDataByteSize);

    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0] = buffer;

    OSStatus result = AudioUnitRender([THIS converterUnit], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);

    ...

}

I think -50 error means one of the parameters is wrong. The only parameters that can be wrong are [THIS converterUnit] and &bufferList, given that all the rest are handed to me as arguments. I've checked the converterUnit instance and it is correctly allocated and initialized (what's more, if that was the problem, it wouldn't run on the simulator either). The only parameter left to check is the bufferList. What I could make out so far from debugging is that both the RemoteIO's output element's input ASBD, and the inNumberFrames are different in the phone and on the simulator. But still, I think that to me that doesn't change things, given that I create and allocate memory for the AudioBuffer buffer based on an ASBD resulting from a AudioUnitGetProperty([THIS ioUnit], kAudioUnitProperty_StreamFormat, ...) call.

Any help will be much appreciated, I'm kind of running desperate here..

You guys rock.

Cheers.

UPDATE:

Here's the audio controller class' definition:

@interface AudioController : NSObject
{
    AUGraph mGraph;
    AudioUnit mEffects;
    AudioUnit ioUnit;
    AudioUnit converterUnit;
}

@property (readonly, nonatomic) AudioUnit mEffects;
@property (readonly, nonatomic) AudioUnit ioUnit;
@property (readonly, nonatomic) AudioUnit converterUnit;

@property (nonatomic) float* volumenPromedio;

-(void)initializeAUGraph;
-(void)startAUGraph;
-(void)stopAUGraph;

@end

, and here's the initialization code for the AUGraph (defined in AudioController.mm):

- (void)initializeAUGraph
{
    NSError *audioSessionError = nil;
    AVAudioSession *mySession = [AVAudioSession sharedInstance];
    [mySession setPreferredHardwareSampleRate: kGraphSampleRate
                                        error: &audioSessionError];
    [mySession setCategory: AVAudioSessionCategoryPlayAndRecord
                     error: &audioSessionError];
    [mySession setActive: YES error: &audioSessionError];

    OSStatus result = noErr;

    // create a new AUGraph
    result = NewAUGraph(&mGraph);

    AUNode outputNode;
    AUNode effectsNode;
    AUNode converterNode;

    // effects component
    AudioComponentDescription effects_desc;
    effects_desc.componentType = kAudioUnitType_Effect;
    effects_desc.componentSubType = kAudioUnitSubType_HighPassFilter;
    effects_desc.componentFlags = 0;
    effects_desc.componentFlagsMask = 0;
    effects_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    //  output component
    AudioComponentDescription output_desc;
    output_desc.componentType = kAudioUnitType_Output;
    output_desc.componentSubType = kAudioUnitSubType_RemoteIO;
    output_desc.componentFlags = 0;
    output_desc.componentFlagsMask = 0;
    output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    // stream format converter component
    AudioComponentDescription converter_desc;
    converter_desc.componentType = kAudioUnitType_FormatConverter;
    converter_desc.componentSubType = kAudioUnitSubType_AUConverter;
    converter_desc.componentFlags = 0;
    converter_desc.componentFlagsMask = 0;
    converter_desc.componentManufacturer = kAudioUnitManufacturer_Apple;

    // Add nodes to the graph
    result = AUGraphAddNode(mGraph, &output_desc, &outputNode);
    [self hasError:result:__FILE__:__LINE__];
    result = AUGraphAddNode(mGraph, &effects_desc, &effectsNode);
    [self hasError:result:__FILE__:__LINE__];
    result = AUGraphAddNode(mGraph, &converter_desc, &converterNode);

    // manage connections in the graph    

    // Connect the io unit node's input element's output to the effectsNode input
    result = AUGraphConnectNodeInput(mGraph, outputNode, 1, effectsNode, 0);

    // Connect the effects node's output to the converter node's input
    result = AUGraphConnectNodeInput(mGraph, effectsNode, 0, converterNode, 0);

    // open the graph
    result = AUGraphOpen(mGraph);

    // Get references to the audio units
    result = AUGraphNodeInfo(mGraph, effectsNode, NULL, &mEffects);
    result = AUGraphNodeInfo(mGraph, outputNode, NULL, &ioUnit);
    result = AUGraphNodeInfo(mGraph, converterNode, NULL, &converterUnit);

    // Enable input on remote io unit
    UInt32 flag = 1;
    result = AudioUnitSetProperty(ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &flag, sizeof(flag));

    // Setup render callback struct
    AURenderCallbackStruct renderCallbackStruct;
    renderCallbackStruct.inputProc = &renderInput;
    renderCallbackStruct.inputProcRefCon = self;

    result = AUGraphSetNodeInputCallback(mGraph, outputNode, 0, &renderCallbackStruct);

    // Get fx unit's input current stream format...
    AudioStreamBasicDescription fxInputASBD;
    UInt32 sizeOfASBD = sizeof(AudioStreamBasicDescription);

    result = AudioUnitGetProperty(mEffects, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fxInputASBD, &sizeOfASBD);

    // ...and set it on the io unit's input scope's output 
    result = AudioUnitSetProperty(ioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Output,
                                  1,
                                  &fxInputASBD,
                                  sizeof(fxInputASBD));

    // Set fx unit's output sample rate, just in case
    Float64 sampleRate = 44100.0;

    result = AudioUnitSetProperty(mEffects,
                                  kAudioUnitProperty_SampleRate,
                                  kAudioUnitScope_Output,
                                  0,
                                  &sampleRate,
                                  sizeof(sampleRate));

    AudioStreamBasicDescription fxOutputASBD;

    // get fx audio unit's output ASBD...
    result = AudioUnitGetProperty(mEffects, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &fxOutputASBD, &sizeOfASBD);

    // ...and set it to the converter audio unit's input
    result = AudioUnitSetProperty(converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fxOutputASBD, sizeof(fxOutputASBD));

    AudioStreamBasicDescription ioUnitsOutputElementInputASBD;

    // now get io audio unit's output element's input ASBD...
    result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ioUnitsOutputElementInputASBD, &sizeOfASBD);

    // ...set the sample rate...
    ioUnitsOutputElementInputASBD.mSampleRate = 44100.0;

    // ...and set it to the converter audio unit's output
    result = AudioUnitSetProperty(converterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &ioUnitsOutputElementInputASBD, sizeof(ioUnitsOutputElementInputASBD));

    // initialize graph
    result = AUGraphInitialize(mGraph);

}

The reason I make the connection between the converter's output and the remote io unit's output element's input with a render callback function (rather than with the AUGraphConnectNodeInput method) is because I need to make some calculations on the samples right after they've been processed by the high-pass filter. The render callback gives me the opportunity to look into the samples buffer right after the AudioUnitRender call, and do said calculations there.

UPDATE 2:

By debugging, I found differences in the Remote IO output bus' input ASBD on the device and on the simulator. It shouldn't make a difference (I allocate and initialize the AudioBufferList based on data coming from a previous AudioUnitGetProperty([THIS ioUnit], kAudioUnitProperty_StreamFormat, ...) call), but it's the only thing I can see different in the device and the simulator.

Here's the Remote IO output bus' input ASBD on the device:

Float64 mSampleRate        44100
UInt32  mFormatID          1819304813
UInt32  mFormatFlags       41
UInt32  mBytesPerPacket    4
UInt32  mFramesPerPacket   1
UInt32  mBytesPerFrame     4
UInt32  mChannelsPerFrame  2
UInt32  mBitsPerChannel    32
UInt32  mReserved          0

, and here it is on the simulator:

Float64 mSampleRate        44100
UInt32  mFormatID          1819304813
UInt32  mFormatFlags       12
UInt32  mBytesPerPacket    4
UInt32  mFramesPerPacket   1
UInt32  mBytesPerFrame     4
UInt32  mChannelsPerFrame  2
UInt32  mBitsPerChannel    16
UInt32  mReserved          0
Carlos Vega
  • 211
  • 3
  • 9
  • it isn't clear how your units are connected, where your render callback is installed, etc. – hooleyhoop Sep 03 '12 at 22:08
  • Thanks a lot. I edited the post to include more code. I hope it's not too much.. Thanks again! – Carlos Vega Sep 04 '12 at 00:57
  • Your ABSD could be incompatible with this audio unit. Where, how did you configure it. – hotpaw2 Sep 04 '12 at 14:33
  • 1
    As far as I know, the Remote IO's output bus' input ASBD cannot be configured, so I took it (with `AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, ... , &sizeOfASBD);`) and set it to be the converter unit's output ASBD. Then I took the fx unit's output ASBD and set it to be the converter unit's input ASBD. Finally, I took the fx unit's input ASBD and set it to be the Remote IO unit's input bus' output ASBD. All in all, the AUGraph goes as follows: RemoteIO Input -> Effect unit -> AU format converter unit -> RemoteIO Output – Carlos Vega Sep 04 '12 at 18:03
  • Hey Carlos, Do you get any solution for this? I am facing the same issue. – Sumit Sharma Jan 15 '14 at 01:18

0 Answers0