slesTestFeedback.cpp revision 086a6f51a7b12880ed114962136972f89ed70da2
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/sndfile.h> 31#include <media/nbaio/MonoPipe.h> 32#include <media/nbaio/MonoPipeReader.h> 33 34#define ASSERT_EQ(x, y) do { if ((x) == (y)) ; else { fprintf(stderr, "0x%x != 0x%x\n", \ 35 (unsigned) (x), (unsigned) (y)); assert((x) == (y)); } } while (0) 36 37// default values 38static SLuint32 rxBufCount = 2; // -r# 39static SLuint32 txBufCount = 2; // -t# 40static SLuint32 bufSizeInFrames = 240; // -f# 41static SLuint32 channels = 1; // -c# 42static SLuint32 sampleRate = 48000; // -s# 43static SLuint32 exitAfterSeconds = 60; // -e# 44static SLuint32 freeBufCount = 0; // calculated 45static SLuint32 bufSizeInBytes = 0; // calculated 46 47// Storage area for the buffer queues 48static char **rxBuffers; 49static char **txBuffers; 50static char **freeBuffers; 51 52// Buffer indices 53static SLuint32 rxFront; // oldest recording 54static SLuint32 rxRear; // next to be recorded 55static SLuint32 txFront; // oldest playing 56static SLuint32 txRear; // next to be played 57static SLuint32 freeFront; // oldest free 58static SLuint32 freeRear; // next to be freed 59 60static SLAndroidSimpleBufferQueueItf recorderBufferQueue; 61static SLBufferQueueItf playerBufferQueue; 62 63static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 64 65static android::MonoPipeReader *pipeReader; 66static android::MonoPipe *pipeWriter; 67 68static android::MonoPipeReader *pipeReader2; 69static android::MonoPipe *pipeWriter2; 70 71static int injectImpulse; 72 73// Called after audio recorder fills a buffer with data 74static void recorderCallback(SLAndroidSimpleBufferQueueItf caller __unused, void *context __unused) 75{ 76 SLresult result; 77 78 pthread_mutex_lock(&mutex); 79 80 // We should only be called when a recording buffer is done 81 assert(rxFront <= rxBufCount); 82 assert(rxRear <= rxBufCount); 83 assert(rxFront != rxRear); 84 char *buffer = rxBuffers[rxFront]; 85 86 // Remove buffer from record queue 87 if (++rxFront > rxBufCount) { 88 rxFront = 0; 89 } 90 91#if 1 92 ssize_t actual = pipeWriter->write(buffer, (size_t) bufSizeInFrames); 93 if (actual != (ssize_t) bufSizeInFrames) { 94 write(1, "?", 1); 95 } 96 97 // This is called by a realtime (SCHED_FIFO) thread, 98 // and it is unsafe to do I/O as it could block for unbounded time. 99 // Flash filesystem is especially notorious for blocking. 100 if (pipeWriter2 != NULL) { 101 actual = pipeWriter2->write(buffer, (size_t) bufSizeInFrames); 102 if (actual != (ssize_t) bufSizeInFrames) { 103 write(1, "?", 1); 104 } 105 } 106 107 // Enqueue this same buffer for the recorder to fill again. 108 result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes); 109 ASSERT_EQ(SL_RESULT_SUCCESS, result); 110 111 // Update our model of the record queue 112 SLuint32 rxRearNext = rxRear+1; 113 if (rxRearNext > rxBufCount) { 114 rxRearNext = 0; 115 } 116 assert(rxRearNext != rxFront); 117 rxBuffers[rxRear] = buffer; 118 rxRear = rxRearNext; 119 120#else 121 // Enqueue the just-filled buffer for the player 122 result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes); 123 if (SL_RESULT_SUCCESS == result) { 124 125 // There was room in the play queue, update our model of it 126 assert(txFront <= txBufCount); 127 assert(txRear <= txBufCount); 128 SLuint32 txRearNext = txRear+1; 129 if (txRearNext > txBufCount) { 130 txRearNext = 0; 131 } 132 assert(txRearNext != txFront); 133 txBuffers[txRear] = buffer; 134 txRear = txRearNext; 135 136 } else { 137 138 // Here if record has a filled buffer to play, but play queue is full. 139 assert(SL_RESULT_BUFFER_INSUFFICIENT == result); 140 write(1, "?", 1); 141 142 // We could either try again later, or discard. For now we discard and re-use buffer. 143 // Enqueue this same buffer for the recorder to fill again. 144 result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes); 145 ASSERT_EQ(SL_RESULT_SUCCESS, result); 146 147 // Update our model of the record queue 148 SLuint32 rxRearNext = rxRear+1; 149 if (rxRearNext > rxBufCount) { 150 rxRearNext = 0; 151 } 152 assert(rxRearNext != rxFront); 153 rxBuffers[rxRear] = buffer; 154 rxRear = rxRearNext; 155 156 } 157#endif 158 159 pthread_mutex_unlock(&mutex); 160} 161 162 163// Called after audio player empties a buffer of data 164static void playerCallback(SLBufferQueueItf caller __unused, void *context __unused) 165{ 166 SLresult result; 167 168 pthread_mutex_lock(&mutex); 169 170 // Get the buffer that just finished playing 171 assert(txFront <= txBufCount); 172 assert(txRear <= txBufCount); 173 assert(txFront != txRear); 174 char *buffer = txBuffers[txFront]; 175 if (++txFront > txBufCount) { 176 txFront = 0; 177 } 178 179#if 1 180 ssize_t actual = pipeReader->read(buffer, bufSizeInFrames, (int64_t) -1); 181 if (actual != (ssize_t) bufSizeInFrames) { 182 write(1, "/", 1); 183 // on underrun from pipe, substitute silence 184 memset(buffer, 0, bufSizeInFrames * channels * sizeof(short)); 185 } 186 187 if (injectImpulse == -1) { 188 // Experimentally, a single frame impulse was insufficient to trigger feedback. 189 // Also a Nyquist frequency signal was also insufficient, probably because 190 // the response of output and/or input path was not adequate at high frequencies. 191 // This short burst of a few cycles of square wave at Nyquist/4 was found to work well. 192 for (unsigned i = 0; i < bufSizeInFrames / 8; i += 8) { 193 for (int j = 0; j < 8; j++) { 194 for (unsigned k = 0; k < channels; k++) { 195 ((short *)buffer)[(i+j)*channels+k] = j < 4 ? 0x7FFF : 0x8000; 196 } 197 } 198 } 199 injectImpulse = 0; 200 } 201 202 // Enqueue the filled buffer for playing 203 result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, bufSizeInBytes); 204 ASSERT_EQ(SL_RESULT_SUCCESS, result); 205 206 // Update our model of the player queue 207 assert(txFront <= txBufCount); 208 assert(txRear <= txBufCount); 209 SLuint32 txRearNext = txRear+1; 210 if (txRearNext > txBufCount) { 211 txRearNext = 0; 212 } 213 assert(txRearNext != txFront); 214 txBuffers[txRear] = buffer; 215 txRear = txRearNext; 216 217#else 218 // First try to enqueue the free buffer for recording 219 result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, buffer, bufSizeInBytes); 220 if (SL_RESULT_SUCCESS == result) { 221 222 // There was room in the record queue, update our model of it 223 assert(rxFront <= rxBufCount); 224 assert(rxRear <= rxBufCount); 225 SLuint32 rxRearNext = rxRear+1; 226 if (rxRearNext > rxBufCount) { 227 rxRearNext = 0; 228 } 229 assert(rxRearNext != rxFront); 230 rxBuffers[rxRear] = buffer; 231 rxRear = rxRearNext; 232 233 } else { 234 235 // Here if record queue is full 236 assert(SL_RESULT_BUFFER_INSUFFICIENT == result); 237 238 // Instead enqueue the free buffer on the free queue 239 assert(freeFront <= freeBufCount); 240 assert(freeRear <= freeBufCount); 241 SLuint32 freeRearNext = freeRear+1; 242 if (freeRearNext > freeBufCount) { 243 freeRearNext = 0; 244 } 245 // There must always be room in the free queue 246 assert(freeRearNext != freeFront); 247 freeBuffers[freeRear] = buffer; 248 freeRear = freeRearNext; 249 250 } 251#endif 252 253 pthread_mutex_unlock(&mutex); 254} 255 256// Main program 257int main(int argc, char **argv) 258{ 259 const char *outFileName = NULL; 260 // process command-line options 261 int i; 262 for (i = 1; i < argc; ++i) { 263 char *arg = argv[i]; 264 if (arg[0] != '-') { 265 break; 266 } 267 // -r# number of slots in receive buffer queue 268 if (!strncmp(arg, "-r", 2)) { 269 rxBufCount = atoi(&arg[2]); 270 if (rxBufCount < 1 || rxBufCount > 16) { 271 fprintf(stderr, "%s: unusual receive buffer queue size (%u buffers)\n", argv[0], 272 (unsigned) rxBufCount); 273 } 274 // -t# number of slots in transmit buffer queue 275 } else if (!strncmp(arg, "-t", 2)) { 276 txBufCount = atoi(&arg[2]); 277 if (txBufCount < 1 || txBufCount > 16) { 278 fprintf(stderr, "%s: unusual transmit buffer queue size (%u buffers)\n", argv[0], 279 (unsigned) txBufCount); 280 } 281 // -f# size of each buffer in frames 282 } else if (!strncmp(arg, "-f", 2)) { 283 bufSizeInFrames = atoi(&arg[2]); 284 if (bufSizeInFrames == 0) { 285 fprintf(stderr, "%s: unusual buffer size (%u frames)\n", argv[0], 286 (unsigned) bufSizeInFrames); 287 } 288 // -c1 mono or -c2 stereo 289 } else if (!strncmp(arg, "-c", 2)) { 290 channels = atoi(&arg[2]); 291 if (channels < 1 || channels > 2) { 292 fprintf(stderr, "%s: unusual channel count ignored (%u)\n", argv[0], 293 (unsigned) channels); 294 channels = 2; 295 } 296 // -s# sample rate in Hz 297 } else if (!strncmp(arg, "-s", 2)) { 298 sampleRate = atoi(&arg[2]); 299 switch (sampleRate) { 300 case 8000: 301 case 11025: 302 case 12000: 303 case 16000: 304 case 22050: 305 case 24000: 306 case 32000: 307 case 44100: 308 case 48000: 309 break; 310 default: 311 fprintf(stderr, "%s: unusual sample rate (%u Hz)\n", argv[0], 312 (unsigned) sampleRate); 313 break; 314 } 315 // -e# exit after this many seconds 316 } else if (!strncmp(arg, "-e", 2)) { 317 exitAfterSeconds = atoi(&arg[2]); 318 // -ofile log to output file also 319 } else if (!strncmp(arg, "-o", 2)) { 320 outFileName = &arg[2]; 321 // -i# inject an impulse after # milliseconds 322 } else if (!strncmp(arg, "-i", 2)) { 323 injectImpulse = atoi(&arg[2]); 324 } else 325 fprintf(stderr, "%s: unknown option %s\n", argv[0], arg); 326 } 327 // no other arguments allowed 328 if (i < argc) { 329 fprintf(stderr, "usage: %s -r# -t# -f# -s# -c# -i# -ofile\n", argv[0]); 330 fprintf(stderr, " -r# receive buffer queue count for microphone input, default 1\n"); 331 fprintf(stderr, " -t# transmit buffer queue count for speaker output, default 2\n"); 332 fprintf(stderr, " -f# number of frames per buffer, default 512\n"); 333 fprintf(stderr, " -s# sample rate in Hz, default 44100\n"); 334 fprintf(stderr, " -c1 mono\n"); 335 fprintf(stderr, " -c2 stereo, default\n"); 336 fprintf(stderr, " -i# inject impulse after # milliseconds\n"); 337 fprintf(stderr, " -ofile log input to specified .wav file also\n"); 338 } 339 340 // compute total free buffers as -r plus -t 341 freeBufCount = rxBufCount + txBufCount; 342 // compute buffer size 343 bufSizeInBytes = channels * bufSizeInFrames * sizeof(short); 344 345 // Initialize free buffers 346 freeBuffers = (char **) calloc(freeBufCount+1, sizeof(char *)); 347 unsigned j; 348 for (j = 0; j < freeBufCount; ++j) { 349 freeBuffers[j] = (char *) malloc(bufSizeInBytes); 350 } 351 freeFront = 0; 352 freeRear = freeBufCount; 353 freeBuffers[j] = NULL; 354 355 // Initialize record queue 356 rxBuffers = (char **) calloc(rxBufCount+1, sizeof(char *)); 357 rxFront = 0; 358 rxRear = 0; 359 360 // Initialize play queue 361 txBuffers = (char **) calloc(txBufCount+1, sizeof(char *)); 362 txFront = 0; 363 txRear = 0; 364 365 const android::NBAIO_Format nbaio_format = android::Format_from_SR_C(sampleRate, channels, 366 AUDIO_FORMAT_PCM_16_BIT); 367 pipeWriter = new android::MonoPipe(1024, nbaio_format, false /*writeCanBlock*/); 368 android::NBAIO_Format offer = nbaio_format; 369 size_t numCounterOffers = 0; 370 ssize_t neg = pipeWriter->negotiate(&offer, 1, NULL, numCounterOffers); 371 assert(0 == neg); 372 pipeReader = new android::MonoPipeReader(pipeWriter); 373 numCounterOffers = 0; 374 neg = pipeReader->negotiate(&offer, 1, NULL, numCounterOffers); 375 assert(0 == neg); 376 377 SNDFILE *sndfile; 378 if (outFileName != NULL) { 379 // create .wav writer 380 SF_INFO info; 381 info.frames = 0; 382 info.samplerate = sampleRate; 383 info.channels = channels; 384 info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; 385 sndfile = sf_open(outFileName, SFM_WRITE, &info); 386 if (sndfile != NULL) { 387 pipeWriter2 = new android::MonoPipe(65536, nbaio_format, false /*writeCanBlock*/); 388 offer = nbaio_format; 389 numCounterOffers = 0; 390 neg = pipeWriter2->negotiate(&offer, 1, NULL, numCounterOffers); 391 assert(0 == neg); 392 pipeReader2 = new android::MonoPipeReader(pipeWriter2); 393 numCounterOffers = 0; 394 neg = pipeReader2->negotiate(&offer, 1, NULL, numCounterOffers); 395 assert(0 == neg); 396 } else { 397 fprintf(stderr, "sf_open failed\n"); 398 } 399 } else { 400 sndfile = NULL; 401 } 402 403 SLresult result; 404 405 // create engine 406 SLObjectItf engineObject; 407 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); 408 ASSERT_EQ(SL_RESULT_SUCCESS, result); 409 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); 410 ASSERT_EQ(SL_RESULT_SUCCESS, result); 411 SLEngineItf engineEngine; 412 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); 413 ASSERT_EQ(SL_RESULT_SUCCESS, result); 414 415 // create output mix 416 SLObjectItf outputmixObject; 417 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputmixObject, 0, NULL, NULL); 418 ASSERT_EQ(SL_RESULT_SUCCESS, result); 419 result = (*outputmixObject)->Realize(outputmixObject, SL_BOOLEAN_FALSE); 420 ASSERT_EQ(SL_RESULT_SUCCESS, result); 421 422 // create an audio player with buffer queue source and output mix sink 423 SLDataSource audiosrc; 424 SLDataSink audiosnk; 425 SLDataFormat_PCM pcm; 426 SLDataLocator_OutputMix locator_outputmix; 427 SLDataLocator_BufferQueue locator_bufferqueue_tx; 428 locator_bufferqueue_tx.locatorType = SL_DATALOCATOR_BUFFERQUEUE; 429 locator_bufferqueue_tx.numBuffers = txBufCount; 430 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 431 locator_outputmix.outputMix = outputmixObject; 432 pcm.formatType = SL_DATAFORMAT_PCM; 433 pcm.numChannels = channels; 434 pcm.samplesPerSec = sampleRate * 1000; 435 pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; 436 pcm.containerSize = 16; 437 pcm.channelMask = channels == 1 ? SL_SPEAKER_FRONT_CENTER : 438 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); 439 pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; 440 audiosrc.pLocator = &locator_bufferqueue_tx; 441 audiosrc.pFormat = &pcm; 442 audiosnk.pLocator = &locator_outputmix; 443 audiosnk.pFormat = NULL; 444 SLObjectItf playerObject = NULL; 445 SLObjectItf recorderObject = NULL; 446 SLInterfaceID ids_tx[1] = {SL_IID_BUFFERQUEUE}; 447 SLboolean flags_tx[1] = {SL_BOOLEAN_TRUE}; 448 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audiosrc, &audiosnk, 449 1, ids_tx, flags_tx); 450 if (SL_RESULT_CONTENT_UNSUPPORTED == result) { 451 fprintf(stderr, "Could not create audio player (result %x), check sample rate\n", result); 452 goto cleanup; 453 } 454 ASSERT_EQ(SL_RESULT_SUCCESS, result); 455 result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE); 456 ASSERT_EQ(SL_RESULT_SUCCESS, result); 457 SLPlayItf playerPlay; 458 result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay); 459 ASSERT_EQ(SL_RESULT_SUCCESS, result); 460 result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, &playerBufferQueue); 461 ASSERT_EQ(SL_RESULT_SUCCESS, result); 462 result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, playerCallback, NULL); 463 ASSERT_EQ(SL_RESULT_SUCCESS, result); 464 465 // Enqueue some zero buffers for the player 466 for (j = 0; j < txBufCount; ++j) { 467 468 // allocate a free buffer 469 assert(freeFront != freeRear); 470 char *buffer = freeBuffers[freeFront]; 471 if (++freeFront > freeBufCount) { 472 freeFront = 0; 473 } 474 475 // put on play queue 476 SLuint32 txRearNext = txRear + 1; 477 if (txRearNext > txBufCount) { 478 txRearNext = 0; 479 } 480 assert(txRearNext != txFront); 481 txBuffers[txRear] = buffer; 482 txRear = txRearNext; 483 result = (*playerBufferQueue)->Enqueue(playerBufferQueue, 484 buffer, bufSizeInBytes); 485 ASSERT_EQ(SL_RESULT_SUCCESS, result); 486 } 487 488 result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); 489 ASSERT_EQ(SL_RESULT_SUCCESS, result); 490 491 // Create an audio recorder with microphone device source and buffer queue sink. 492 // The buffer queue as sink is an Android-specific extension. 493 494 SLDataLocator_IODevice locator_iodevice; 495 SLDataLocator_AndroidSimpleBufferQueue locator_bufferqueue_rx; 496 locator_iodevice.locatorType = SL_DATALOCATOR_IODEVICE; 497 locator_iodevice.deviceType = SL_IODEVICE_AUDIOINPUT; 498 locator_iodevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; 499 locator_iodevice.device = NULL; 500 audiosrc.pLocator = &locator_iodevice; 501 audiosrc.pFormat = NULL; 502 locator_bufferqueue_rx.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; 503 locator_bufferqueue_rx.numBuffers = rxBufCount; 504 audiosnk.pLocator = &locator_bufferqueue_rx; 505 audiosnk.pFormat = &pcm; 506 { 507 SLInterfaceID ids_rx[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; 508 SLboolean flags_rx[1] = {SL_BOOLEAN_TRUE}; 509 result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audiosrc, 510 &audiosnk, 1, ids_rx, flags_rx); 511 if (SL_RESULT_SUCCESS != result) { 512 fprintf(stderr, "Could not create audio recorder (result %x), " 513 "check sample rate and channel count\n", result); 514 goto cleanup; 515 } 516 } 517 ASSERT_EQ(SL_RESULT_SUCCESS, result); 518 result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE); 519 ASSERT_EQ(SL_RESULT_SUCCESS, result); 520 SLRecordItf recorderRecord; 521 result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord); 522 ASSERT_EQ(SL_RESULT_SUCCESS, result); 523 result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, 524 &recorderBufferQueue); 525 ASSERT_EQ(SL_RESULT_SUCCESS, result); 526 result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, recorderCallback, NULL); 527 ASSERT_EQ(SL_RESULT_SUCCESS, result); 528 529 // Enqueue some empty buffers for the recorder 530 for (j = 0; j < rxBufCount; ++j) { 531 532 // allocate a free buffer 533 assert(freeFront != freeRear); 534 char *buffer = freeBuffers[freeFront]; 535 if (++freeFront > freeBufCount) { 536 freeFront = 0; 537 } 538 539 // put on record queue 540 SLuint32 rxRearNext = rxRear + 1; 541 if (rxRearNext > rxBufCount) { 542 rxRearNext = 0; 543 } 544 assert(rxRearNext != rxFront); 545 rxBuffers[rxRear] = buffer; 546 rxRear = rxRearNext; 547 result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, 548 buffer, bufSizeInBytes); 549 ASSERT_EQ(SL_RESULT_SUCCESS, result); 550 } 551 552 // Kick off the recorder 553 result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING); 554 ASSERT_EQ(SL_RESULT_SUCCESS, result); 555 556#if 0 557 // give recorder a head start so that the pipe is initially filled 558 sleep(1); 559#endif 560 561 // Wait patiently 562 do { 563 for (int i = 0; i < 10; i++) { 564 usleep(100000); 565 if (pipeReader2 != NULL) { 566 for (;;) { 567 short buffer[bufSizeInFrames * channels]; 568 ssize_t actual = pipeReader2->read(buffer, bufSizeInFrames, (int64_t) -1); 569 if (actual <= 0) 570 break; 571 (void) sf_writef_short(sndfile, buffer, (sf_count_t) actual); 572 } 573 } 574 if (injectImpulse > 0) { 575 if (injectImpulse <= 100) { 576 injectImpulse = -1; 577 write(1, "I", 1); 578 } else { 579 if ((injectImpulse % 1000) < 100) { 580 write(1, "i", 1); 581 } 582 injectImpulse -= 100; 583 } 584 } else if (i == 9) { 585 write(1, ".", 1); 586 } 587 } 588 SLBufferQueueState playerBQState; 589 result = (*playerBufferQueue)->GetState(playerBufferQueue, &playerBQState); 590 ASSERT_EQ(SL_RESULT_SUCCESS, result); 591 SLAndroidSimpleBufferQueueState recorderBQState; 592 result = (*recorderBufferQueue)->GetState(recorderBufferQueue, &recorderBQState); 593 ASSERT_EQ(SL_RESULT_SUCCESS, result); 594 } while (--exitAfterSeconds); 595 596 // Tear down the objects and exit 597cleanup: 598 if (sndfile != NULL) { 599 sf_close(sndfile); 600 } 601 if (NULL != playerObject) { 602 (*playerObject)->Destroy(playerObject); 603 } 604 if (NULL != recorderObject) { 605 (*recorderObject)->Destroy(recorderObject); 606 } 607 (*outputmixObject)->Destroy(outputmixObject); 608 (*engineObject)->Destroy(engineObject); 609 610 return EXIT_SUCCESS; 611} 612