• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
66 static audio_utils_fifo fifo2;
67 static short *fifo2Buffer = NULL;
68 
69 static int injectImpulse;
70 
71 // Called after audio recorder fills a buffer with data
recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused,void * context __unused)72 static 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
playerCallback(SLBufferQueueItf caller __unused,void * context __unused)162 static 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
main(int argc,char ** argv)255 int 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
582 cleanup:
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