Despite the absence of any documentation, there does appear to be a way of doing this.
It requires ensuring that, after AudioQueueStop
is called asynchronously, any data which is still in the queue is properly dealt with.
So, in the call to stop recording, we have a state variable (recordingState
) to keep track of what's happening. Given that this function and the callback execute in different threads, we try to avoid any possible complications by synchronising to the current class (called Listener
in this example):
-(void) stopRecording
{
@synchronized(self)
{
if(self.recordingState != RecordingStateRunning) {return;}
self.recordingState = RecordingStateStopping;
AudioQueueStop(self.audioQueue, false);
}
}
Experimentation showed that after an asynchronous call to AudioQueueStop
(i.e. parameter inImmediate
is set to false
), the callback will be called a few more times, so we have an opportunity to tidy up:
static void AQInputCallback(void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs)
{
Listener *listener = (__bridge Listener *) inUserData;
@synchronized(listener)
{
if(listener.recordingState == RecordingStateRunning)
{
// Do audio processing here.
// ...
}
else if(listener.recordingState == RecordingStateStopping)
{
listener.recordingState = RecordingStateStopped;
// Do tidying here.
// ...
}
AudioQueueEnqueueBuffer(listener.audioQueue, inBuffer, 0, NULL);
}
}