Search code examples
iphonecocoaaudiosdkaudioqueueservices

How to start writing out an existing AudioQueue in response to an event?


I am writing a class that opens an AudioQueue and analyzes its characteristics, and then under certain conditions can begin or end writing out a file from that AudioQueue that is already instantiated. This is my code (entirely based on SpeakHere) that opens the AudioQueue without writing anything out to tmp:

void AQRecorder::StartListen() {
int i, bufferByteSize;
UInt32 size;

try {       

    SetupAudioFormat(kAudioFormatLinearPCM);

    XThrowIfError(AudioQueueNewInput(&mRecordFormat,
                    MyInputBufferHandler,
                    this,
                    NULL, NULL,
                    0, &mQueue), "AudioQueueNewInput failed");

    mRecordPacket = 0;

    size = sizeof(mRecordFormat);
    XThrowIfError(AudioQueueGetProperty(mQueue, kAudioQueueProperty_StreamDescription,  
            &mRecordFormat, &size), "couldn't get queue's format");

    bufferByteSize = ComputeRecordBufferSize(&mRecordFormat, kBufferDurationSeconds);
    for (i = 0; i < kNumberRecordBuffers; ++i) {
        XThrowIfError(AudioQueueAllocateBuffer(mQueue, bufferByteSize, &mBuffers[i]),
                      "AudioQueueAllocateBuffer failed");
        XThrowIfError(AudioQueueEnqueueBuffer(mQueue, mBuffers[i], 0, NULL),
                      "AudioQueueEnqueueBuffer failed");
    }

    mIsRunning = true;
    XThrowIfError(AudioQueueStart(mQueue, NULL), "AudioQueueStart failed");
}
catch (CAXException &e) {
    char buf[256];
    fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
}
catch (...) {
    fprintf(stderr, "An unknown error occurred\n");
}
}

But I'm a little unclear on how to write a function that will tell this queue "from now until the stop signal, start writing out this queue to tmp as a file". I understand how to tell an AudioQueue to write out as a file at the time that it's created, how to set files format, etc, but not how to tell it to start and stop midstream. Much appreciative of any pointers, thanks.


Solution

  • You can pause/start audioqueue anytime with AudioQueuePause(AudioQueueRef inAQ) and AudioQueueStart (AudioQueueRef inAQ, const AudioTimeStamp *inStartTime);

    And there is more possibilities. Because you write audiofile in callback function you can choose there when and what data should be saved.

    To control write data or not, create bool flag for example "doWrite" in your recorder class and write only if it set to YES. The queue begin to receive data from start and continually fills buffers, but you just inore these data in callback until flag remains NO. Then at any moment set doWrite=YES and next buffer will be saved to file. This approach is especialy useful when you change recording based on sound itself (for example record only if sound is louder than some thredhold value).

    void AQRecorder::MyInputBufferHandler(  void * inUserData, AudioQueueRef    inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp * inStartTime, UInt32  inNumPackets, const  AudioStreamPacketDescription* inPacketDesc)
    {
        AQRecorder *aqr = (AQRecorder *)inUserData;
        try {
            if (inNumPackets > 0) {
    
                        if (aqr.doWrite){
                                // write packets to file
                                XThrowIfError(AudioFileWritePackets(aqr->mRecordFile, FALSE, inBuffer->mAudioDataByteSize, inPacketDesc, aqr->mRecordPacket, &inNumPackets, inBuffer->mAudioData), "AudioFileWritePackets failed");
                                aqr->mRecordPacket += inNumPackets;
                         }
            }
    
            // if we're not stopping, re-enqueue the buffe so that it gets filled again
            if (aqr->IsRunning())
                XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL), "AudioQueueEnqueueBuffer failed");
        } catch (CAXException e) {
            char buf[256];
            fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
        }
    }