Search code examples
iosxcodedebuggingplaybackaudioqueue

iPhone playback AudioQueue stops/fails to continue after pausing at a breakpoint during debugging


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?


Solution

  • 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;
    }