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