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 37static SLuint32 rxBufCount = 2; // -r# 38static SLuint32 txBufCount = 2; // -t# 39static SLuint32 bufSizeInFrames = 240; // -f# 40static SLuint32 channels = 1; // -c# 41static SLuint32 sampleRate = 48000; // -s# 42static SLuint32 exitAfterSeconds = 60; // -e# 43static SLuint32 freeBufCount = 0; // calculated 44static SLuint32 bufSizeInBytes = 0; // calculated 45 46// Storage area for the buffer queues 47static char **rxBuffers; 48static char **txBuffers; 49static char **freeBuffers; 50 51// Buffer indices 52static SLuint32 rxFront; // oldest recording 53static SLuint32 rxRear; // next to be recorded 54static SLuint32 txFront; // oldest playing 55static SLuint32 txRear; // next to be played 56static SLuint32 freeFront; // oldest free 57static SLuint32 freeRear; // next to be freed 58 59static SLAndroidSimpleBufferQueueItf recorderBufferQueue; 60static SLBufferQueueItf playerBufferQueue; 61 62static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 63 64static audio_utils_fifo fifo; 65 66static audio_utils_fifo fifo2; 67static short *fifo2Buffer = NULL; 68 69static int injectImpulse; 70 71// Called after audio recorder fills a buffer with data 72static 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 162static 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 255int 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 582cleanup: 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