Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EXC_BAD_ACCESS in AudioRingBuffer::GetTimeBounds

Okay, here's the scenario: I have a real-time recording app using ExtAudioFileWriteAsync targeted for iOS 4.3. The first time I record with the app, it works perfectly. If I press stop, then record again, better than half the time I will get an EXC_BAD_ACCESS in AudioRingBuffer::GetTimeBounds right when recording starts.

That is to say that ExtAudioFileWriteAsync fails on GetTimeBounds when starting the second recording. Here is the bit of code that is fired when recording starts, which creates the ExtAudioFile reference:

- (void) setActive:(NSString *) file 
{
if (mExtAFRef) {
    ExtAudioFileDispose(mExtAFRef);
    mExtAFRef = nil;
    NSLog(@"mExtAFRef Disposed.");
}

if (mOutputAudioFile)
{
    ExtAudioFileDispose(mOutputAudioFile);
    mOutputAudioFile = nil;
    NSLog(@"mOutputAudioFile Disposed.");
}

NSURL *outUrl = [NSURL fileURLWithPath:file];

OSStatus setupErr = ExtAudioFileCreateWithURL((CFURLRef)outUrl, kAudioFileWAVEType, &mOutputFormat, NULL, kAudioFileFlags_EraseFile, &mOutputAudioFile);  
NSAssert(setupErr == noErr, @"Couldn't create file for writing");

setupErr = ExtAudioFileSetProperty(mOutputAudioFile,       kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &audioFormat);
NSAssert(setupErr == noErr, @"Couldn't create file for format");

setupErr =  ExtAudioFileWriteAsync(mOutputAudioFile, 0, NULL);
NSAssert(setupErr == noErr, @"Couldn't initialize write buffers for audio file");

isActive = TRUE;

}

Does anyone have any thoughts whatsoever on what may be causing this? I assume, given EXC_BAD_ACCESS, that it is a memory leak or something's ref count getting knocked to zero, but I can't for the life of me figure out what it might be, and the Googles are drawing a complete blank. I posted this same thing on the Apple dev forum for CoreAudio, but not a soul took pity on me, even to make a pithy comment. HALP!

EDIT: Found the problem. The error was happening when ExtAudioFileWriteAsync was trying to write a new file before the old file was "optimized." A little mutex love solved the problem.

like image 269
Chris Randall Avatar asked Oct 31 '11 23:10

Chris Randall


2 Answers

I'm having almost the same issue on a recording app, can anyone please explain how to solve it with "A little mutex love"? EDIT

Tnx to Chris Randall I did manage to solve my problems. This is how I implemented the mutex:

#include <pthread.h>
static pthread_mutex_t outputAudioFileLock;

then in my init:

pthread_mutex_init(&outputAudioFileLock,NULL);

and in the callback:

if (THIS.mIsRecording) {
        if (0 == pthread_mutex_trylock(&outputAudioFileLock)) {
        OSStatus err = ExtAudioFileWriteAsync(THIS.mRecordFile, inNumberFrames, THIS.recordingBufferList);
        if (noErr != err) {
            NSLog(@"ExtAudioFileWriteAsync Failed: %ld!!!", err);
        } else {
        }
        pthread_mutex_unlock(&outputAudioFileLock);
    }
    }

finally in the stopRecord method:

if (mRecordFile) {

    pthread_mutex_lock(&outputAudioFileLock);
    OSStatus setupErr;
    setupErr = ExtAudioFileDispose(mRecordFile);
    mRecordFile = NULL;
    pthread_mutex_unlock(&outputAudioFileLock);
    NSAssert(setupErr == noErr, @"Couldn't dispose audio file");
    NSLog(@"Stopping Record");  
    mIsRecording = NO;
}

Tnx again for the help, hope this saves someone's time.

like image 158
Stone Alessandro Avatar answered Oct 20 '22 00:10

Stone Alessandro


Include pthread.h, and define pthread_mutex_t outputAudioFileLock in your constructor. Then, in your audio callback, when you want to write, do something like this (adjusting the variables according to what you're using):

if (0 == pthread_mutex_trylock(&outputAudioFileLock)) {
            OSStatus err = ExtAudioFileWriteAsync(mOutputAudioFile, frames, bufferList);
            if (noErr != err) {
                NSLog(@"ExtAudioFileWriteAsync Failed: %ld!!!", err);
            } else {
            }
            pthread_mutex_unlock(&outputAudioFileLock);
        }

The pthread_mutex_trylock checks to see if the thread is locked (and thus "optimizing"). If it is not, it then allows the write. I then wrap both the audio file setup (as seen above) and the audio file cleanup like so, so that the thread is locked when the file system is doing anything that would cause the AudioRingBuffer BAD_ACCESS error:

pthread_mutex_lock(&outputAudioFileLock);
OSStatus setupErr;
setupErr = ExtAudioFileDispose(mOutputAudioFile);
mOutputAudioFile = NULL;
pthread_mutex_unlock(&outputAudioFileLock);
NSAssert(setupErr == noErr, @"Couldn't dispose audio file");

This locks the setup and cleanup threads so that you can't write to a file that is being "optimized," which is the source of the error. Hope this helps!

EDIT: I do my audio callback in the Obj-C part of the audio controller; if you're doing it in the C++ part, this would be structured quite a bit differently; perhaps someone else can answer that?

like image 40
Chris Randall Avatar answered Oct 20 '22 01:10

Chris Randall