error after stopping AudioUnit Recording

2

I'm trying to grab audio input from the iPhone's microphone by using this code:

@property (nonatomic) AudioUnit rioUnit;
@property (nonatomic) CAStreamBasicDescription outputCASBD;

(...)

// set our required format - LPCM non-interleaved 32 bit floating point
CAStreamBasicDescription outFormat = CAStreamBasicDescription(44100, // sample rate
                                                              kAudioFormatLinearPCM, // format id
                                                              4, // bytes per packet
                                                              1, // frames per packet
                                                              4, // bytes per frame
                                                              1, // channels per frame
                                                              32, // bits per channel
                                                            kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved);

try {
    // Open the output unit
    AudioComponentDescription desc;
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_RemoteIO;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

    AudioComponent comp = AudioComponentFindNext(NULL, &desc);

    AudioComponentInstanceNew(comp, &_rioUnit);

    UInt32 one = 1;
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof(one)), "couldn't enable input on the remote I/O unit");

    AURenderCallbackStruct callbackStruct;
    callbackStruct.inputProc = renderInput;
    callbackStruct.inputProcRefCon = (__bridge void*)self;
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackStruct, sizeof(callbackStruct)), "couldn't set remote i/o render callback");

    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, sizeof(outFormat)), "couldn't set the remote I/O unit's output client format");
    XThrowIfError(AudioUnitSetProperty(_rioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &outFormat, sizeof(outFormat)), "couldn't set the remote I/O unit's input client format");

    XThrowIfError(AudioUnitInitialize(_rioUnit), "couldn't initialize the remote I/O unit");
    XThrowIfError(AudioOutputUnitStart(_rioUnit), "couldn't start the remote I/O unit");
}
catch (CAXException &e) {
    char buf[256];
    fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
catch (...) {
    fprintf(stderr, "An unknown error occurred\n");
}

// Allocate our own buffers (1 channel, 16 bits per sample, thus 16 bits per frame, thus 2 bytes per frame).
// Practice learns the buffers used contain 512 frames, if this changes it will be fixed in processAudio.
// will need float for accellerate fft (input is 16 bit signed integer) so make space...
_tempBuffer.mNumberChannels = outFormat.mChannelsPerFrame;
_tempBuffer.mDataByteSize = 512 * outFormat.mBytesPerFrame * 2;
_tempBuffer.mData = malloc( self.tempBuffer.mDataByteSize );

All is good, up to the point where I want to stop listening to the Mic input.

I'm doing this:

    AudioOutputUnitStop(_rioUnit);
    AudioUnitUninitialize(_rioUnit);
    AudioComponentInstanceDispose(_rioUnit);
    _rioUnit = nil;

But I get an error from inside the callback function defined for kAudioOutputUnitProperty_SetInputCallback.

The error is: AURemoteIO::IOThread (14): EXC_BAD_ACCESS (code=1, address=0x8000000c)

It appears to me that AudioUnit's InputCallback is still being called, even though I am Stopping it, Unitializing and Disposing of its instance.

Why is this happening and how can I prevent the InputCallback from being called after I Stop, Unitialize and Dispose of its instance?

ios
objective-c
iphone
core-audio
audiounit
asked on Stack Overflow Nov 30, 2014 by cassi.lup • edited Dec 1, 2014 by cassi.lup

2 Answers

2

First, make sure that _rioUnit really has the same value in the stop as in the start.

The audio input callback is being called in another asynchronous thread. So you may need to wait until that thread also stops (which usually happens in less than 2 audio buffer duration times, so that may be a reasonable amount to time to wait) before uninitializing or disposing of the audio unit or any other needed data structures.

answered on Stack Overflow Dec 2, 2014 by hotpaw2
2

The problem was this line:

_rioUnit = nil; // causes EXC_BAD_ACCESS error

Changing nil to NULL solved the issue:

_rioUnit = NULL; // works smoothly

I got the idea from this article: http://teragonaudio.com/article/How-to-do-realtime-recording-with-effect-processing-on-iOS.html

Also, this answer outlines the difference between nil and NULL in Objective-C.

answered on Stack Overflow Dec 2, 2014 by cassi.lup • edited May 23, 2017 by Community

User contributions licensed under CC BY-SA 3.0