• 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 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
recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused,void * context __unused)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
playerCallback(SLBufferQueueItf caller __unused,void * context __unused)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
main(int argc,char ** argv)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