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