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