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