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 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 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 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