Home | History | Annotate | Download | only in examples
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // Test program to record from default audio input and playback to default audio output.
     18 // It will generate feedback (Larsen effect) if played through on-device speakers,
     19 // or acts as a delay if played through headset.
     20 
     21 #include <SLES/OpenSLES.h>
     22 #include <SLES/OpenSLES_Android.h>
     23 #include <assert.h>
     24 #include <pthread.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <string.h>
     28 #include <unistd.h>
     29 
     30 #include <audio_utils/fifo.h>
     31 #include <audio_utils/sndfile.h>
     32 
     33 #define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else { fprintf(stderr, "0x%x != 0x%x\n", \
     34     (unsigned) (x), (unsigned) (y)); assert((x) == (y)); } } while (0)
     35 
     36 // default values
     37 static SLuint32 rxBufCount = 2;     // -r#
     38 static SLuint32 txBufCount = 2;     // -t#
     39 static SLuint32 bufSizeInFrames = 240;  // -f#
     40 static SLuint32 channels = 1;       // -c#
     41 static SLuint32 sampleRate = 48000; // -s#
     42 static SLuint32 exitAfterSeconds = 60; // -e#
     43 static SLuint32 freeBufCount = 0;   // calculated
     44 static SLuint32 bufSizeInBytes = 0; // calculated
     45 
     46 // Storage area for the buffer queues
     47 static char **rxBuffers;
     48 static char **txBuffers;
     49 static char **freeBuffers;
     50 
     51 // Buffer indices
     52 static SLuint32 rxFront;    // oldest recording
     53 static SLuint32 rxRear;     // next to be recorded
     54 static SLuint32 txFront;    // oldest playing
     55 static SLuint32 txRear;     // next to be played
     56 static SLuint32 freeFront;  // oldest free
     57 static SLuint32 freeRear;   // next to be freed
     58 
     59 static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
     60 static SLBufferQueueItf playerBufferQueue;
     61 
     62 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     63 
     64 static audio_utils_fifo *fifo;
     65 static audio_utils_fifo_reader *fifoReader;
     66 static audio_utils_fifo_writer *fifoWriter;
     67 
     68 static audio_utils_fifo *fifo2;
     69 static short *fifo2Buffer = NULL;
     70 static audio_utils_fifo_reader *fifo2Reader;
     71 static audio_utils_fifo_writer *fifo2Writer;
     72 
     73 static int injectImpulse;
     74 
     75 // Called after audio recorder fills a buffer with data
     76 static void recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused, void *context __unused)
     77 {
     78     SLresult result;
     79 
     80     pthread_mutex_lock(&mutex);
     81 
     82     // We should only be called when a recording buffer is done
     83     assert(rxFront <= rxBufCount);
     84     assert(rxRear <= rxBufCount);
     85     assert(rxFront != rxRear);
     86     char *buffer = rxBuffers[rxFront];
     87 
     88     // Remove buffer from record queue
     89     if (++rxFront > rxBufCount) {
     90         rxFront = 0;
     91     }
     92 
     93 #if 1
     94     ssize_t actual = fifoWriter->write(buffer, (size_t) bufSizeInFrames);
     95     if (actual != (ssize_t) bufSizeInFrames) {
     96         write(1, "?", 1);
     97     }
     98 
     99     // This is called by a realtime (SCHED_FIFO) thread,
    100     // and it is unsafe to do I/O as it could block for unbounded time.
    101     // Flash filesystem is especially notorious for blocking.
    102     if (fifo2Buffer != NULL) {
    103         actual = fifo2Writer->write(buffer, (size_t) bufSizeInFrames);
    104         if (actual != (ssize_t) bufSizeInFrames) {
    105             write(1, "?", 1);
    106         }
    107     }
    108 
    109     // Enqueue this same buffer for the recorder to fill again.
    110     result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes);
    111     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    112 
    113     // Update our model of the record queue
    114     SLuint32 rxRearNext = rxRear+1;
    115     if (rxRearNext > rxBufCount) {
    116         rxRearNext = 0;
    117     }
    118     assert(rxRearNext != rxFront);
    119     rxBuffers[rxRear] = buffer;
    120     rxRear = rxRearNext;
    121 
    122 #else
    123     // Enqueue the just-filled buffer for the player
    124     result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes);
    125     if (SL_RESULT_SUCCESS == result) {
    126 
    127         // There was room in the play queue, update our model of it
    128         assert(txFront <= txBufCount);
    129         assert(txRear <= txBufCount);
    130         SLuint32 txRearNext = txRear+1;
    131         if (txRearNext > txBufCount) {
    132             txRearNext = 0;
    133         }
    134         assert(txRearNext != txFront);
    135         txBuffers[txRear] = buffer;
    136         txRear = txRearNext;
    137 
    138     } else {
    139 
    140         // Here if record has a filled buffer to play, but play queue is full.
    141         assert(SL_RESULT_BUFFER_INSUFFICIENT == result);
    142         write(1, "?", 1);
    143 
    144         // We could either try again later, or discard. For now we discard and re-use buffer.
    145         // Enqueue this same buffer for the recorder to fill again.
    146         result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes);
    147         ASSERT_EQ(SL_RESULT_SUCCESS, result);
    148 
    149         // Update our model of the record queue
    150         SLuint32 rxRearNext = rxRear+1;
    151         if (rxRearNext > rxBufCount) {
    152             rxRearNext = 0;
    153         }
    154         assert(rxRearNext != rxFront);
    155         rxBuffers[rxRear] = buffer;
    156         rxRear = rxRearNext;
    157 
    158     }
    159 #endif
    160 
    161     pthread_mutex_unlock(&mutex);
    162 }
    163 
    164 
    165 // Called after audio player empties a buffer of data
    166 static void playerCallback(SLBufferQueueItf caller __unused, void *context __unused)
    167 {
    168     SLresult result;
    169 
    170     pthread_mutex_lock(&mutex);
    171 
    172     // Get the buffer that just finished playing
    173     assert(txFront <= txBufCount);
    174     assert(txRear <= txBufCount);
    175     assert(txFront != txRear);
    176     char *buffer = txBuffers[txFront];
    177     if (++txFront > txBufCount) {
    178         txFront = 0;
    179     }
    180 
    181 #if 1
    182     ssize_t actual = fifoReader->read(buffer, bufSizeInFrames);
    183     if (actual != (ssize_t) bufSizeInFrames) {
    184         write(1, "/", 1);
    185         // on underrun from pipe, substitute silence
    186         memset(buffer, 0, bufSizeInFrames * channels * sizeof(short));
    187     }
    188 
    189     if (injectImpulse == -1) {
    190         // Experimentally, a single frame impulse was insufficient to trigger feedback.
    191         // Also a Nyquist frequency signal was also insufficient, probably because
    192         // the response of output and/or input path was not adequate at high frequencies.
    193         // This short burst of a few cycles of square wave at Nyquist/4 was found to work well.
    194         for (unsigned i = 0; i < bufSizeInFrames / 8; i += 8) {
    195             for (int j = 0; j < 8; j++) {
    196                 for (unsigned k = 0; k < channels; k++) {
    197                     ((short *)buffer)[(i+j)*channels+k] = j < 4 ? 0x7FFF : 0x8000;
    198                 }
    199             }
    200         }
    201         injectImpulse = 0;
    202     }
    203 
    204     // Enqueue the filled buffer for playing
    205     result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes);
    206     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    207 
    208     // Update our model of the player queue
    209     assert(txFront <= txBufCount);
    210     assert(txRear <= txBufCount);
    211     SLuint32 txRearNext = txRear+1;
    212     if (txRearNext > txBufCount) {
    213         txRearNext = 0;
    214     }
    215     assert(txRearNext != txFront);
    216     txBuffers[txRear] = buffer;
    217     txRear = txRearNext;
    218 
    219 #else
    220     // First try to enqueue the free buffer for recording
    221     result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes);
    222     if (SL_RESULT_SUCCESS == result) {
    223 
    224         // There was room in the record queue, update our model of it
    225         assert(rxFront <= rxBufCount);
    226         assert(rxRear <= rxBufCount);
    227         SLuint32 rxRearNext = rxRear+1;
    228         if (rxRearNext > rxBufCount) {
    229             rxRearNext = 0;
    230         }
    231         assert(rxRearNext != rxFront);
    232         rxBuffers[rxRear] = buffer;
    233         rxRear = rxRearNext;
    234 
    235     } else {
    236 
    237         // Here if record queue is full
    238         assert(SL_RESULT_BUFFER_INSUFFICIENT == result);
    239 
    240         // Instead enqueue the free buffer on the free queue
    241         assert(freeFront <= freeBufCount);
    242         assert(freeRear <= freeBufCount);
    243         SLuint32 freeRearNext = freeRear+1;
    244         if (freeRearNext > freeBufCount) {
    245             freeRearNext = 0;
    246         }
    247         // There must always be room in the free queue
    248         assert(freeRearNext != freeFront);
    249         freeBuffers[freeRear] = buffer;
    250         freeRear = freeRearNext;
    251 
    252     }
    253 #endif
    254 
    255     pthread_mutex_unlock(&mutex);
    256 }
    257 
    258 // Main program
    259 int main(int argc, char **argv)
    260 {
    261     const char *outFileName = NULL;
    262     // process command-line options
    263     int i;
    264     for (i = 1; i < argc; ++i) {
    265         char *arg = argv[i];
    266         if (arg[0] != '-') {
    267             break;
    268         }
    269         // -r# number of slots in receive buffer queue
    270         if (!strncmp(arg, "-r", 2)) {
    271             rxBufCount = atoi(&arg[2]);
    272             if (rxBufCount < 1 || rxBufCount > 16) {
    273                 fprintf(stderr, "%s: unusual receive buffer queue size (%u buffers)\n", argv[0],
    274                     (unsigned) rxBufCount);
    275             }
    276         // -t# number of slots in transmit buffer queue
    277         } else if (!strncmp(arg, "-t", 2)) {
    278             txBufCount = atoi(&arg[2]);
    279             if (txBufCount < 1 || txBufCount > 16) {
    280                 fprintf(stderr, "%s: unusual transmit buffer queue size (%u buffers)\n", argv[0],
    281                     (unsigned) txBufCount);
    282             }
    283         // -f# size of each buffer in frames
    284         } else if (!strncmp(arg, "-f", 2)) {
    285             bufSizeInFrames = atoi(&arg[2]);
    286             if (bufSizeInFrames == 0) {
    287                 fprintf(stderr, "%s: unusual buffer size (%u frames)\n", argv[0],
    288                     (unsigned) bufSizeInFrames);
    289             }
    290         // -c1 mono or -c2 stereo
    291         } else if (!strncmp(arg, "-c", 2)) {
    292             channels = atoi(&arg[2]);
    293             if (channels < 1 || channels > 2) {
    294                 fprintf(stderr, "%s: unusual channel count ignored (%u)\n", argv[0],
    295                     (unsigned) channels);
    296                 channels = 2;
    297             }
    298         // -s# sample rate in Hz
    299         } else if (!strncmp(arg, "-s", 2)) {
    300             sampleRate = atoi(&arg[2]);
    301             switch (sampleRate) {
    302             case 8000:
    303             case 11025:
    304             case 12000:
    305             case 16000:
    306             case 22050:
    307             case 24000:
    308             case 32000:
    309             case 44100:
    310             case 48000:
    311                 break;
    312             default:
    313                 fprintf(stderr, "%s: unusual sample rate (%u Hz)\n", argv[0],
    314                     (unsigned) sampleRate);
    315                 break;
    316             }
    317         // -e# exit after this many seconds
    318         } else if (!strncmp(arg, "-e", 2)) {
    319             exitAfterSeconds = atoi(&arg[2]);
    320         // -ofile log to output file also
    321         } else if (!strncmp(arg, "-o", 2)) {
    322             outFileName = &arg[2];
    323         // -i# inject an impulse after # milliseconds
    324         } else if (!strncmp(arg, "-i", 2)) {
    325             injectImpulse = atoi(&arg[2]);
    326         } else
    327             fprintf(stderr, "%s: unknown option %s\n", argv[0], arg);
    328     }
    329     // no other arguments allowed
    330     if (i < argc) {
    331         fprintf(stderr, "usage: %s -r# -t# -f# -s# -c# -i# -ofile\n", argv[0]);
    332         fprintf(stderr, "  -r# receive buffer queue count for microphone input, default 1\n");
    333         fprintf(stderr, "  -t# transmit buffer queue count for speaker output, default 2\n");
    334         fprintf(stderr, "  -f# number of frames per buffer, default 512\n");
    335         fprintf(stderr, "  -s# sample rate in Hz, default 44100\n");
    336         fprintf(stderr, "  -c1 mono\n");
    337         fprintf(stderr, "  -c2 stereo, default\n");
    338         fprintf(stderr, "  -i# inject impulse after # milliseconds\n");
    339         fprintf(stderr, "  -ofile log input to specified .wav file also\n");
    340     }
    341 
    342     // compute total free buffers as -r plus -t
    343     freeBufCount = rxBufCount + txBufCount;
    344     // compute buffer size
    345     bufSizeInBytes = channels * bufSizeInFrames * sizeof(short);
    346 
    347     // Initialize free buffers
    348     freeBuffers = (char **) calloc(freeBufCount+1, sizeof(char *));
    349     unsigned j;
    350     for (j = 0; j < freeBufCount; ++j) {
    351         freeBuffers[j] = (char *) malloc(bufSizeInBytes);
    352     }
    353     freeFront = 0;
    354     freeRear = freeBufCount;
    355     freeBuffers[j] = NULL;
    356 
    357     // Initialize record queue
    358     rxBuffers = (char **) calloc(rxBufCount+1, sizeof(char *));
    359     rxFront = 0;
    360     rxRear = 0;
    361 
    362     // Initialize play queue
    363     txBuffers = (char **) calloc(txBufCount+1, sizeof(char *));
    364     txFront = 0;
    365     txRear = 0;
    366 
    367     size_t frameSize = channels * sizeof(short);
    368 #define FIFO_FRAMES 1024
    369     short *fifoBuffer = new short[FIFO_FRAMES * channels];
    370     fifo = new audio_utils_fifo(FIFO_FRAMES, frameSize, fifoBuffer);
    371     fifoReader = new audio_utils_fifo_reader(*fifo, true /*throttlesWriter*/);
    372     fifoWriter = new audio_utils_fifo_writer(*fifo);
    373 
    374     SNDFILE *sndfile;
    375     if (outFileName != NULL) {
    376         // create .wav writer
    377         SF_INFO info;
    378         info.frames = 0;
    379         info.samplerate = sampleRate;
    380         info.channels = channels;
    381         info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    382         sndfile = sf_open(outFileName, SFM_WRITE, &info);
    383         if (sndfile != NULL) {
    384 #define FIFO2_FRAMES 65536
    385             fifo2Buffer = new short[FIFO2_FRAMES * channels];
    386             fifo2 = new audio_utils_fifo(FIFO2_FRAMES, frameSize, fifo2Buffer);
    387             fifo2Reader = new audio_utils_fifo_reader(*fifo2, true /*throttlesWriter*/);
    388             fifo2Writer = new audio_utils_fifo_writer(*fifo2);
    389         } else {
    390             fprintf(stderr, "sf_open failed\n");
    391         }
    392     } else {
    393         sndfile = NULL;
    394     }
    395 
    396     SLresult result;
    397 
    398     // create engine
    399     SLObjectItf engineObject;
    400     result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    401     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    402     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    403     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    404     SLEngineItf engineEngine;
    405     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    406     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    407 
    408     // create output mix
    409     SLObjectItf outputmixObject;
    410     result = (*engineEngine)->CreateOutputMix(engineEngine, &outputmixObject, 0, NULL, NULL);
    411     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    412     result = (*outputmixObject)->Realize(outputmixObject, SL_BOOLEAN_FALSE);
    413     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    414 
    415     // create an audio player with buffer queue source and output mix sink
    416     SLDataSource audiosrc;
    417     SLDataSink audiosnk;
    418     SLDataFormat_PCM pcm;
    419     SLDataLocator_OutputMix locator_outputmix;
    420     SLDataLocator_BufferQueue locator_bufferqueue_tx;
    421     locator_bufferqueue_tx.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
    422     locator_bufferqueue_tx.numBuffers = txBufCount;
    423     locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
    424     locator_outputmix.outputMix = outputmixObject;
    425     pcm.formatType = SL_DATAFORMAT_PCM;
    426     pcm.numChannels = channels;
    427     pcm.samplesPerSec = sampleRate * 1000;
    428     pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
    429     pcm.containerSize = 16;
    430     pcm.channelMask = channels == 1 ? SL_SPEAKER_FRONT_CENTER :
    431         (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
    432     pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
    433     audiosrc.pLocator = &locator_bufferqueue_tx;
    434     audiosrc.pFormat = &pcm;
    435     audiosnk.pLocator = &locator_outputmix;
    436     audiosnk.pFormat = NULL;
    437     SLObjectItf playerObject = NULL;
    438     SLObjectItf recorderObject = NULL;
    439     SLInterfaceID ids_tx[1] = {SL_IID_BUFFERQUEUE};
    440     SLboolean flags_tx[1] = {SL_BOOLEAN_TRUE};
    441     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, &audiosnk,
    442         1, ids_tx, flags_tx);
    443     if (SL_RESULT_CONTENT_UNSUPPORTED == result) {
    444         fprintf(stderr, "Could not create audio player (result %x), check sample rate\n", result);
    445         goto cleanup;
    446     }
    447     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    448     result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
    449     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    450     SLPlayItf playerPlay;
    451     result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);
    452     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    453     result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue);
    454     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    455     result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, playerCallback, NULL);
    456     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    457 
    458     // Enqueue some zero buffers for the player
    459     for (j = 0; j < txBufCount; ++j) {
    460 
    461         // allocate a free buffer
    462         assert(freeFront != freeRear);
    463         char *buffer = freeBuffers[freeFront];
    464         if (++freeFront > freeBufCount) {
    465             freeFront = 0;
    466         }
    467 
    468         // put on play queue
    469         SLuint32 txRearNext = txRear + 1;
    470         if (txRearNext > txBufCount) {
    471             txRearNext = 0;
    472         }
    473         assert(txRearNext != txFront);
    474         txBuffers[txRear] = buffer;
    475         txRear = txRearNext;
    476         result = (*playerBufferQueue)->Enqueue(playerBufferQueue,
    477             buffer, bufSizeInBytes);
    478         ASSERT_EQ(SL_RESULT_SUCCESS, result);
    479     }
    480 
    481     result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
    482     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    483 
    484     // Create an audio recorder with microphone device source and buffer queue sink.
    485     // The buffer queue as sink is an Android-specific extension.
    486 
    487     SLDataLocator_IODevice locator_iodevice;
    488     SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_rx;
    489     locator_iodevice.locatorType = SL_DATALOCATOR_IODEVICE;
    490     locator_iodevice.deviceType = SL_IODEVICE_AUDIOINPUT;
    491     locator_iodevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
    492     locator_iodevice.device = NULL;
    493     audiosrc.pLocator = &locator_iodevice;
    494     audiosrc.pFormat = NULL;
    495     locator_bufferqueue_rx.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
    496     locator_bufferqueue_rx.numBuffers = rxBufCount;
    497     audiosnk.pLocator = &locator_bufferqueue_rx;
    498     audiosnk.pFormat = &pcm;
    499     {
    500     SLInterfaceID ids_rx[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
    501     SLboolean flags_rx[1] = {SL_BOOLEAN_TRUE};
    502     result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audiosrc,
    503         &audiosnk, 1, ids_rx, flags_rx);
    504     if (SL_RESULT_SUCCESS != result) {
    505         fprintf(stderr, "Could not create audio recorder (result %x), "
    506                 "check sample rate and channel count\n", result);
    507         goto cleanup;
    508     }
    509     }
    510     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    511     result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
    512     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    513     SLRecordItf recorderRecord;
    514     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
    515     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    516     result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
    517         &recorderBufferQueue);
    518     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    519     result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, recorderCallback, NULL);
    520     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    521 
    522     // Enqueue some empty buffers for the recorder
    523     for (j = 0; j < rxBufCount; ++j) {
    524 
    525         // allocate a free buffer
    526         assert(freeFront != freeRear);
    527         char *buffer = freeBuffers[freeFront];
    528         if (++freeFront > freeBufCount) {
    529             freeFront = 0;
    530         }
    531 
    532         // put on record queue
    533         SLuint32 rxRearNext = rxRear + 1;
    534         if (rxRearNext > rxBufCount) {
    535             rxRearNext = 0;
    536         }
    537         assert(rxRearNext != rxFront);
    538         rxBuffers[rxRear] = buffer;
    539         rxRear = rxRearNext;
    540         result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue,
    541             buffer, bufSizeInBytes);
    542         ASSERT_EQ(SL_RESULT_SUCCESS, result);
    543     }
    544 
    545     // Kick off the recorder
    546     result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
    547     ASSERT_EQ(SL_RESULT_SUCCESS, result);
    548 
    549 #if 0
    550     // give recorder a head start so that the pipe is initially filled
    551     sleep(1);
    552 #endif
    553 
    554     // Wait patiently
    555     do {
    556         for (int i = 0; i < 10; i++) {
    557             usleep(100000);
    558             if (fifo2Buffer != NULL) {
    559                 for (;;) {
    560                     short buffer[bufSizeInFrames * channels];
    561                     ssize_t actual = fifo2Reader->read(buffer, bufSizeInFrames);
    562                     if (actual <= 0)
    563                         break;
    564                     (void) sf_writef_short(sndfile, buffer, (sf_count_t) actual);
    565                 }
    566             }
    567             if (injectImpulse > 0) {
    568                 if (injectImpulse <= 100) {
    569                     injectImpulse = -1;
    570                     write(1, "I", 1);
    571                 } else {
    572                     if ((injectImpulse % 1000) < 100) {
    573                         write(1, "i", 1);
    574                     }
    575                     injectImpulse -= 100;
    576                 }
    577             } else if (i == 9) {
    578                 write(1, ".", 1);
    579             }
    580         }
    581         SLBufferQueueState playerBQState;
    582         result = (*playerBufferQueue)->GetState(playerBufferQueue, &playerBQState);
    583         ASSERT_EQ(SL_RESULT_SUCCESS, result);
    584         SLAndroidSimpleBufferQueueState recorderBQState;
    585         result = (*recorderBufferQueue)->GetState(recorderBufferQueue, &recorderBQState);
    586         ASSERT_EQ(SL_RESULT_SUCCESS, result);
    587     } while (--exitAfterSeconds);
    588 
    589     // Tear down the objects and exit
    590 cleanup:
    591     delete fifoWriter;
    592     fifoWriter = NULL;
    593     delete fifoReader;
    594     fifoReader = NULL;
    595     delete fifo;
    596     fifo = NULL;
    597     delete[] fifoBuffer;
    598     fifoBuffer = NULL;
    599 
    600     if (sndfile != NULL) {
    601         delete fifo2Writer;
    602         fifo2Writer = NULL;
    603         delete fifo2Reader;
    604         fifo2Reader = NULL;
    605         delete fifo2;
    606         fifo2 = NULL;
    607         delete[] fifo2Buffer;
    608         fifo2Buffer = NULL;
    609         sf_close(sndfile);
    610         sndfile = NULL;
    611     }
    612     if (NULL != playerObject) {
    613         (*playerObject)->Destroy(playerObject);
    614     }
    615     if (NULL != recorderObject) {
    616         (*recorderObject)->Destroy(recorderObject);
    617     }
    618     (*outputmixObject)->Destroy(outputmixObject);
    619     (*engineObject)->Destroy(engineObject);
    620 
    621     return EXIT_SUCCESS;
    622 }
    623