To gain familiarity with the Audio Queue API, I took a look at \Developer\Examples\CoreAudio\SimpleSDK\AudioQueueTools\aqrecord.c. I'm using MacOS 10.5.4 with Xcode 3.1.
The AQ record example fails 10% of the time. The failure is in the 'buffer full' callback: AudioFileWritePackets() fails with code = -40.
In the current design, when the user hits return to request that recording halt, the run loop issues a AudioQueueStop() (synchronous) and then disposes of the audio queue, closes the audio file, etc. For the failure to occur, it appears that a buffer full callback is issued after the synchronous call to AudioQueueStop() has returned and enough further processing has occurred to close the audio file. Why does this only happen sometimes? It may have something to do with how much data was in the buffer currently being filled when the user requests that recording stop, but this is only speculation on my part.
There are (at least) three possible ways that Apple could have implemented a synchronous input AQ stop: 1. Stop the queue, and discard any data in the buffer currently filling, or 2. Stop the queue, and immediately Issue one more 'buffer full' callback with the data currently in the buffer (don't record until the current buffer is full), or 3. Stop the queue, and issue a 'buffer full' callback only after the current buffer is completely filled with recorded data. I haven't encountered any Apple documentation that explains which option was implemented. The flaky behavior I'm seeing with synchronous stop would certainly be explained by option #3 and might be explained by option #2.
My fix for this problem was pretty simple: + attach a listener to the audio queue + after issuing the synchronous stop, the run loop falls into a new 1/4 second polling loop that waits for an indication that the queue has actually been stopped (signaled by the listener callback) + then (and only then) continue with disposing of the audio queue, closing the audio file, etc. Summary: the new 1/4 second polling loop ensures that the 'final' buffer full callback completes *before* the run loop closes the audio file.
My current design should be robust for asynchronous AQ stop, so I gave this a try. My visualization was that all of the empty buffers enqueued waiting to be filled with audio data would be filled, and then the audio queue would stop. However, when I tried this, my application crashed. It's not clear whether I did something wrong, or the failure was in Apple's code. The synchronous AQ stop satisfies my needs, so I don't have any plans to investigate the asynch stop problem further.
It might be useful for Apple to implement a version of synchronous AudioQueueStop() that discards the data in the partially recorded current buffer. There would thus be no callbacks after the function returns and no need for the 1/4 second polling loop I implemented. This may be what most people visualize a 'true' synchronous recording stop would look like.
- Steve
|