views:

76

answers:

1

For some reason, it seems that stopping at a breakpoint during debugging will kill my audio queue playback.

  1. AudioQueue will be playing audio output.
  2. Trigger a breakpoint to pause my iPhone app.
  3. Subsequent resume, audio no longer gets played.
  4. ( However, AudioQueue callback functions are still getting called.)
  5. ( No AudioSession or AudioQueue errors are found.)

Since the debugger pauses the application (rather than an incoming phone call, for example) , it's not a typical iPhone interruption, so AudioSession interruption callbacks do not get triggered like in this solution.

I am using three AudioQueue buffers at 4096 samples at 22kHz and filling them in a circular manner.

Problem occurs for both multi-threaded and single-threaded mode.

  • Is there some known problem that you can't pause and resume AudioSessions or AudioQueues during a debugging session?
  • Is it running out of "queued buffers" and it's destroying/killing the AudioQueue object (but then my AQ callback shouldn't trigger).

Anyone have insight into inner workings of iPhone AudioQueues?

A: 

After playing around with it for the last several days, before posting to StackOverflow, I figured out the answer just today. Go figure!

Just recreate the AudioQueue again by calling my "preparation functions"

SetupNewQueue(mDataFormat.mSampleRate, mDataFormat.mChannelsPerFrame);
StartQueue(true);

So detect when your AudioQueue may have "died". In my case, I would be writing data into an input buffer to be "pulled" by AudioQueue callback. If it doesn't occur in a certain time, or after X number of bytes of input buffer have been filled, I then recreate the AudioQueue.

This seems to solve the issue where "halts/fails" audio when you hit a debugging breakpoint.

The simplified versions of these functions are the following:

void AQPlayer::SetupNewQueue(double inSampleRate, UInt32 inChannelsPerFrame)
{
    //Prep AudioStreamBasicDescription
    mDataFormat.mSampleRate = inSampleRate;
    mDataFormat.SetCanonical(inChannelsPerFrame, YES);

    XThrowIfError(AudioQueueNewOutput(&mDataFormat, AQPlayer::AQBufferCallback, this, 
                                    NULL, kCFRunLoopCommonModes, 0, &mQueue), "AudioQueueNew failed");

    // adjust buffer size to represent about a half second of audio based on this format
    CalculateBytesForTime(mDataFormat, kBufferDurationSeconds, &mBufferByteSize, &mNumPacketsToRead);
    ctl->cmsg(CMSG_INFO, VERB_NOISY, "AQPlayer Buffer Byte Size: %d, Num Packets to Read: %d\n", (int)mBufferByteSize, (int)mNumPacketsToRead);

    mBufferWaitTime = mNumPacketsToRead / mDataFormat.mSampleRate * 0.9;

    XThrowIfError(AudioQueueAddPropertyListener(mQueue, kAudioQueueProperty_IsRunning, isRunningProc, this), "adding property listener");

    //Allocate AQ buffers (assume we are using CBR (constant bitrate)
    for (int i = 0; i < kNumberBuffers; ++i) {
        XThrowIfError(AudioQueueAllocateBuffer(mQueue, mBufferByteSize, &mBuffers[i]), "AudioQueueAllocateBuffer failed");
    }
...
}

OSStatus AQPlayer::StartQueue(BOOL inResume)
{   
// if we are not resuming, we also should restart the file read index
    if (!inResume)
        mCurrentPacket = 0; 

    // prime the queue with some data before starting
    for (int i = 0; i < kNumberBuffers; ++i) {
        mBuffers[i]->mAudioDataByteSize = mBuffers[i]->mAudioDataBytesCapacity;
        memset( mBuffers[i]->mAudioData, 0, mBuffers[i]->mAudioDataByteSize );

        XThrowIfError(AudioQueueEnqueueBuffer( mQueue,
                                mBuffers[i],
                                0,
                                NULL ),"AudioQueueEnqueueBuffer failed");
    }

    OSStatus status;
    status = AudioSessionSetActive( true );
    XThrowIfError(status, "\n\n*** AudioSession failed to become active *** \n\n");

    status = AudioQueueStart(mQueue, NULL);
    XThrowIfError(status, "\n\n*** AudioQueue failed to start *** \n\n");

    return status;
}
zeroinverse