1/* 2 * Copyright (C) 2016 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// Audio loopback tests to measure the round trip latency and glitches. 18 19#include <algorithm> 20#include <assert.h> 21#include <cctype> 22#include <errno.h> 23#include <math.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <stdlib.h> 27#include <string.h> 28#include <unistd.h> 29 30#include <aaudio/AAudio.h> 31#include <aaudio/AAudioTesting.h> 32 33#include "AAudioSimplePlayer.h" 34#include "AAudioSimpleRecorder.h" 35#include "AAudioExampleUtils.h" 36#include "LoopbackAnalyzer.h" 37 38// Tag for machine readable results as property = value pairs 39#define RESULT_TAG "RESULT: " 40#define NUM_SECONDS 5 41#define PERIOD_MILLIS 1000 42#define NUM_INPUT_CHANNELS 1 43#define FILENAME_ALL "/data/loopback_all.wav" 44#define FILENAME_ECHOS "/data/loopback_echos.wav" 45#define APP_VERSION "0.2.04" 46 47constexpr int kNumCallbacksToDrain = 20; 48constexpr int kNumCallbacksToDiscard = 20; 49 50struct LoopbackData { 51 AAudioStream *inputStream = nullptr; 52 int32_t inputFramesMaximum = 0; 53 int16_t *inputShortData = nullptr; 54 float *inputFloatData = nullptr; 55 aaudio_format_t actualInputFormat = AAUDIO_FORMAT_INVALID; 56 int32_t actualInputChannelCount = 0; 57 int32_t actualOutputChannelCount = 0; 58 int32_t numCallbacksToDrain = kNumCallbacksToDrain; 59 int32_t numCallbacksToDiscard = kNumCallbacksToDiscard; 60 int32_t minNumFrames = INT32_MAX; 61 int32_t maxNumFrames = 0; 62 int32_t insufficientReadCount = 0; 63 int32_t insufficientReadFrames = 0; 64 int32_t framesReadTotal = 0; 65 int32_t framesWrittenTotal = 0; 66 bool isDone = false; 67 68 aaudio_result_t inputError = AAUDIO_OK; 69 aaudio_result_t outputError = AAUDIO_OK; 70 71 SineAnalyzer sineAnalyzer; 72 EchoAnalyzer echoAnalyzer; 73 AudioRecording audioRecording; 74 LoopbackProcessor *loopbackProcessor; 75}; 76 77static void convertPcm16ToFloat(const int16_t *source, 78 float *destination, 79 int32_t numSamples) { 80 constexpr float scaler = 1.0f / 32768.0f; 81 for (int i = 0; i < numSamples; i++) { 82 destination[i] = source[i] * scaler; 83 } 84} 85 86// ==================================================================================== 87// ========================= CALLBACK ================================================= 88// ==================================================================================== 89// Callback function that fills the audio output buffer. 90 91static int32_t readFormattedData(LoopbackData *myData, int32_t numFrames) { 92 int32_t framesRead = AAUDIO_ERROR_INVALID_FORMAT; 93 if (myData->actualInputFormat == AAUDIO_FORMAT_PCM_I16) { 94 framesRead = AAudioStream_read(myData->inputStream, myData->inputShortData, 95 numFrames, 96 0 /* timeoutNanoseconds */); 97 } else if (myData->actualInputFormat == AAUDIO_FORMAT_PCM_FLOAT) { 98 framesRead = AAudioStream_read(myData->inputStream, myData->inputFloatData, 99 numFrames, 100 0 /* timeoutNanoseconds */); 101 } else { 102 printf("ERROR actualInputFormat = %d\n", myData->actualInputFormat); 103 assert(false); 104 } 105 if (framesRead < 0) { 106 myData->inputError = framesRead; 107 printf("ERROR in read = %d = %s\n", framesRead, 108 AAudio_convertResultToText(framesRead)); 109 } else { 110 myData->framesReadTotal += framesRead; 111 } 112 return framesRead; 113} 114 115static aaudio_data_callback_result_t MyDataCallbackProc( 116 AAudioStream *outputStream, 117 void *userData, 118 void *audioData, 119 int32_t numFrames 120) { 121 (void) outputStream; 122 aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_CONTINUE; 123 LoopbackData *myData = (LoopbackData *) userData; 124 float *outputData = (float *) audioData; 125 126 // Read audio data from the input stream. 127 int32_t actualFramesRead; 128 129 if (numFrames > myData->inputFramesMaximum) { 130 myData->inputError = AAUDIO_ERROR_OUT_OF_RANGE; 131 return AAUDIO_CALLBACK_RESULT_STOP; 132 } 133 134 if (numFrames > myData->maxNumFrames) { 135 myData->maxNumFrames = numFrames; 136 } 137 if (numFrames < myData->minNumFrames) { 138 myData->minNumFrames = numFrames; 139 } 140 141 // Silence the output. 142 int32_t numBytes = numFrames * myData->actualOutputChannelCount * sizeof(float); 143 memset(audioData, 0 /* value */, numBytes); 144 145 if (myData->numCallbacksToDrain > 0) { 146 // Drain the input. 147 int32_t totalFramesRead = 0; 148 do { 149 actualFramesRead = readFormattedData(myData, numFrames); 150 if (actualFramesRead) { 151 totalFramesRead += actualFramesRead; 152 } 153 // Ignore errors because input stream may not be started yet. 154 } while (actualFramesRead > 0); 155 // Only counts if we actually got some data. 156 if (totalFramesRead > 0) { 157 myData->numCallbacksToDrain--; 158 } 159 160 } else if (myData->numCallbacksToDiscard > 0) { 161 // Ignore. Allow the input to fill back up to equilibrium with the output. 162 actualFramesRead = readFormattedData(myData, numFrames); 163 if (actualFramesRead < 0) { 164 result = AAUDIO_CALLBACK_RESULT_STOP; 165 } 166 myData->numCallbacksToDiscard--; 167 168 } else { 169 170 int32_t numInputBytes = numFrames * myData->actualInputChannelCount * sizeof(float); 171 memset(myData->inputFloatData, 0 /* value */, numInputBytes); 172 173 // Process data after equilibrium. 174 int64_t inputFramesWritten = AAudioStream_getFramesWritten(myData->inputStream); 175 int64_t inputFramesRead = AAudioStream_getFramesRead(myData->inputStream); 176 int64_t framesAvailable = inputFramesWritten - inputFramesRead; 177 actualFramesRead = readFormattedData(myData, numFrames); 178 if (actualFramesRead < 0) { 179 result = AAUDIO_CALLBACK_RESULT_STOP; 180 } else { 181 182 if (actualFramesRead < numFrames) { 183 if(actualFramesRead < (int32_t) framesAvailable) { 184 printf("insufficient but numFrames = %d" 185 ", actualFramesRead = %d" 186 ", inputFramesWritten = %d" 187 ", inputFramesRead = %d" 188 ", available = %d\n", 189 numFrames, 190 actualFramesRead, 191 (int) inputFramesWritten, 192 (int) inputFramesRead, 193 (int) framesAvailable); 194 } 195 myData->insufficientReadCount++; 196 myData->insufficientReadFrames += numFrames - actualFramesRead; // deficit 197 } 198 199 int32_t numSamples = actualFramesRead * myData->actualInputChannelCount; 200 201 if (myData->actualInputFormat == AAUDIO_FORMAT_PCM_I16) { 202 convertPcm16ToFloat(myData->inputShortData, myData->inputFloatData, numSamples); 203 } 204 // Save for later. 205 myData->audioRecording.write(myData->inputFloatData, 206 myData->actualInputChannelCount, 207 numFrames); 208 // Analyze the data. 209 myData->loopbackProcessor->process(myData->inputFloatData, 210 myData->actualInputChannelCount, 211 outputData, 212 myData->actualOutputChannelCount, 213 numFrames); 214 myData->isDone = myData->loopbackProcessor->isDone(); 215 if (myData->isDone) { 216 result = AAUDIO_CALLBACK_RESULT_STOP; 217 } 218 } 219 } 220 myData->framesWrittenTotal += numFrames; 221 222 return result; 223} 224 225static void MyErrorCallbackProc( 226 AAudioStream *stream __unused, 227 void *userData __unused, 228 aaudio_result_t error) { 229 printf("Error Callback, error: %d\n",(int)error); 230 LoopbackData *myData = (LoopbackData *) userData; 231 myData->outputError = error; 232} 233 234static void usage() { 235 printf("Usage: aaudio_loopback [OPTION]...\n\n"); 236 AAudioArgsParser::usage(); 237 printf(" -B{frames} input capacity in frames\n"); 238 printf(" -C{channels} number of input channels\n"); 239 printf(" -F{0,1,2} input format, 1=I16, 2=FLOAT\n"); 240 printf(" -g{gain} recirculating loopback gain\n"); 241 printf(" -P{inPerf} set input AAUDIO_PERFORMANCE_MODE*\n"); 242 printf(" n for _NONE\n"); 243 printf(" l for _LATENCY\n"); 244 printf(" p for _POWER_SAVING\n"); 245 printf(" -t{test} select test mode\n"); 246 printf(" m for sine magnitude\n"); 247 printf(" e for echo latency (default)\n"); 248 printf(" f for file latency, analyzes %s\n\n", FILENAME_ECHOS); 249 printf(" -X use EXCLUSIVE mode for input\n"); 250 printf("Example: aaudio_loopback -n2 -pl -Pl -x\n"); 251} 252 253static aaudio_performance_mode_t parsePerformanceMode(char c) { 254 aaudio_performance_mode_t mode = AAUDIO_ERROR_ILLEGAL_ARGUMENT; 255 c = tolower(c); 256 switch (c) { 257 case 'n': 258 mode = AAUDIO_PERFORMANCE_MODE_NONE; 259 break; 260 case 'l': 261 mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 262 break; 263 case 'p': 264 mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING; 265 break; 266 default: 267 printf("ERROR in value performance mode %c\n", c); 268 break; 269 } 270 return mode; 271} 272 273enum { 274 TEST_SINE_MAGNITUDE = 0, 275 TEST_ECHO_LATENCY, 276 TEST_FILE_LATENCY, 277}; 278 279static int parseTestMode(char c) { 280 int testMode = TEST_ECHO_LATENCY; 281 c = tolower(c); 282 switch (c) { 283 case 'm': 284 testMode = TEST_SINE_MAGNITUDE; 285 break; 286 case 'e': 287 testMode = TEST_ECHO_LATENCY; 288 break; 289 case 'f': 290 testMode = TEST_FILE_LATENCY; 291 break; 292 default: 293 printf("ERROR in value test mode %c\n", c); 294 break; 295 } 296 return testMode; 297} 298 299void printAudioGraph(AudioRecording &recording, int numSamples) { 300 int32_t start = recording.size() / 2; 301 int32_t end = start + numSamples; 302 if (end >= recording.size()) { 303 end = recording.size() - 1; 304 } 305 float *data = recording.getData(); 306 // Normalize data so we can see it better. 307 float maxSample = 0.01; 308 for (int32_t i = start; i < end; i++) { 309 float samplePos = fabs(data[i]); 310 if (samplePos > maxSample) { 311 maxSample = samplePos; 312 } 313 } 314 float gain = 0.98f / maxSample; 315 316 for (int32_t i = start; i < end; i++) { 317 float sample = data[i]; 318 printf("%6d: %7.4f ", i, sample); // actual value 319 sample *= gain; 320 printAudioScope(sample); 321 } 322} 323 324 325// ==================================================================================== 326// TODO break up this large main() function into smaller functions 327int main(int argc, const char **argv) 328{ 329 330 AAudioArgsParser argParser; 331 AAudioSimplePlayer player; 332 AAudioSimpleRecorder recorder; 333 LoopbackData loopbackData; 334 AAudioStream *inputStream = nullptr; 335 AAudioStream *outputStream = nullptr; 336 337 aaudio_result_t result = AAUDIO_OK; 338 aaudio_sharing_mode_t requestedInputSharingMode = AAUDIO_SHARING_MODE_SHARED; 339 int requestedInputChannelCount = NUM_INPUT_CHANNELS; 340 aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_UNSPECIFIED; 341 int32_t requestedInputCapacity = -1; 342 aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY; 343 344 int32_t outputFramesPerBurst = 0; 345 346 aaudio_format_t actualOutputFormat = AAUDIO_FORMAT_INVALID; 347 int32_t actualSampleRate = 0; 348 int written = 0; 349 350 int testMode = TEST_ECHO_LATENCY; 351 double gain = 1.0; 352 353 // Make printf print immediately so that debug info is not stuck 354 // in a buffer if we hang or crash. 355 setvbuf(stdout, NULL, _IONBF, (size_t) 0); 356 357 printf("%s - Audio loopback using AAudio V" APP_VERSION "\n", argv[0]); 358 359 for (int i = 1; i < argc; i++) { 360 const char *arg = argv[i]; 361 if (argParser.parseArg(arg)) { 362 // Handle options that are not handled by the ArgParser 363 if (arg[0] == '-') { 364 char option = arg[1]; 365 switch (option) { 366 case 'B': 367 requestedInputCapacity = atoi(&arg[2]); 368 break; 369 case 'C': 370 requestedInputChannelCount = atoi(&arg[2]); 371 break; 372 case 'F': 373 requestedInputFormat = atoi(&arg[2]); 374 break; 375 case 'g': 376 gain = atof(&arg[2]); 377 break; 378 case 'P': 379 inputPerformanceLevel = parsePerformanceMode(arg[2]); 380 break; 381 case 'X': 382 requestedInputSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE; 383 break; 384 case 't': 385 testMode = parseTestMode(arg[2]); 386 break; 387 default: 388 usage(); 389 exit(EXIT_FAILURE); 390 break; 391 } 392 } else { 393 usage(); 394 exit(EXIT_FAILURE); 395 break; 396 } 397 } 398 399 } 400 401 if (inputPerformanceLevel < 0) { 402 printf("illegal inputPerformanceLevel = %d\n", inputPerformanceLevel); 403 exit(EXIT_FAILURE); 404 } 405 406 int32_t requestedDuration = argParser.getDurationSeconds(); 407 int32_t requestedDurationMillis = requestedDuration * MILLIS_PER_SECOND; 408 int32_t timeMillis = 0; 409 int32_t recordingDuration = std::min(60 * 5, requestedDuration); 410 411 switch(testMode) { 412 case TEST_SINE_MAGNITUDE: 413 loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer; 414 break; 415 case TEST_ECHO_LATENCY: 416 loopbackData.echoAnalyzer.setGain(gain); 417 loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer; 418 break; 419 case TEST_FILE_LATENCY: { 420 loopbackData.echoAnalyzer.setGain(gain); 421 422 loopbackData.loopbackProcessor = &loopbackData.echoAnalyzer; 423 int read = loopbackData.loopbackProcessor->load(FILENAME_ECHOS); 424 printf("main() read %d mono samples from %s on Android device\n", read, FILENAME_ECHOS); 425 loopbackData.loopbackProcessor->report(); 426 return 0; 427 } 428 break; 429 default: 430 exit(1); 431 break; 432 } 433 434 printf("OUTPUT stream ----------------------------------------\n"); 435 result = player.open(argParser, MyDataCallbackProc, MyErrorCallbackProc, &loopbackData); 436 if (result != AAUDIO_OK) { 437 fprintf(stderr, "ERROR - player.open() returned %d\n", result); 438 exit(1); 439 } 440 outputStream = player.getStream(); 441 442 actualOutputFormat = AAudioStream_getFormat(outputStream); 443 if (actualOutputFormat != AAUDIO_FORMAT_PCM_FLOAT) { 444 fprintf(stderr, "ERROR - only AAUDIO_FORMAT_PCM_FLOAT supported\n"); 445 exit(1); 446 } 447 448 actualSampleRate = AAudioStream_getSampleRate(outputStream); 449 loopbackData.audioRecording.allocate(recordingDuration * actualSampleRate); 450 loopbackData.audioRecording.setSampleRate(actualSampleRate); 451 outputFramesPerBurst = AAudioStream_getFramesPerBurst(outputStream); 452 453 argParser.compareWithStream(outputStream); 454 455 printf("INPUT stream ----------------------------------------\n"); 456 // Use different parameters for the input. 457 argParser.setNumberOfBursts(AAUDIO_UNSPECIFIED); 458 argParser.setFormat(requestedInputFormat); 459 argParser.setPerformanceMode(inputPerformanceLevel); 460 argParser.setChannelCount(requestedInputChannelCount); 461 argParser.setSharingMode(requestedInputSharingMode); 462 463 // Make sure the input buffer has plenty of capacity. 464 // Extra capacity on input should not increase latency if we keep it drained. 465 int32_t inputBufferCapacity = requestedInputCapacity; 466 if (inputBufferCapacity < 0) { 467 int32_t outputBufferCapacity = AAudioStream_getBufferCapacityInFrames(outputStream); 468 inputBufferCapacity = 2 * outputBufferCapacity; 469 } 470 argParser.setBufferCapacity(inputBufferCapacity); 471 472 result = recorder.open(argParser); 473 if (result != AAUDIO_OK) { 474 fprintf(stderr, "ERROR - recorder.open() returned %d\n", result); 475 goto finish; 476 } 477 inputStream = loopbackData.inputStream = recorder.getStream(); 478 479 { 480 int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream); 481 result = AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity); 482 if (result < 0) { 483 fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames() returned %d\n", result); 484 goto finish; 485 } else {} 486 } 487 488 argParser.compareWithStream(inputStream); 489 490 // If the input stream is too small then we cannot satisfy the output callback. 491 { 492 int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream); 493 if (actualCapacity < 2 * outputFramesPerBurst) { 494 fprintf(stderr, "ERROR - input capacity < 2 * outputFramesPerBurst\n"); 495 goto finish; 496 } 497 } 498 499 // ------- Setup loopbackData ----------------------------- 500 loopbackData.actualInputFormat = AAudioStream_getFormat(inputStream); 501 502 loopbackData.actualInputChannelCount = recorder.getChannelCount(); 503 loopbackData.actualOutputChannelCount = player.getChannelCount(); 504 505 // Allocate a buffer for the audio data. 506 loopbackData.inputFramesMaximum = 32 * AAudioStream_getFramesPerBurst(inputStream); 507 508 if (loopbackData.actualInputFormat == AAUDIO_FORMAT_PCM_I16) { 509 loopbackData.inputShortData = new int16_t[loopbackData.inputFramesMaximum 510 * loopbackData.actualInputChannelCount]{}; 511 } 512 loopbackData.inputFloatData = new float[loopbackData.inputFramesMaximum * 513 loopbackData.actualInputChannelCount]{}; 514 515 loopbackData.loopbackProcessor->reset(); 516 517 // Start OUTPUT first so INPUT does not overflow. 518 result = player.start(); 519 if (result != AAUDIO_OK) { 520 printf("ERROR - AAudioStream_requestStart(output) returned %d = %s\n", 521 result, AAudio_convertResultToText(result)); 522 goto finish; 523 } 524 525 result = recorder.start(); 526 if (result != AAUDIO_OK) { 527 printf("ERROR - AAudioStream_requestStart(input) returned %d = %s\n", 528 result, AAudio_convertResultToText(result)); 529 goto finish; 530 } 531 532 printf("------- sleep and log while the callback runs --------------\n"); 533 while (timeMillis <= requestedDurationMillis) { 534 if (loopbackData.inputError != AAUDIO_OK) { 535 printf(" ERROR on input stream\n"); 536 break; 537 } else if (loopbackData.outputError != AAUDIO_OK) { 538 printf(" ERROR on output stream\n"); 539 break; 540 } else if (loopbackData.isDone) { 541 printf(" Test says it is DONE!\n"); 542 break; 543 } else { 544 // Log a line of stream data. 545 printf("%7.3f: ", 0.001 * timeMillis); // display in seconds 546 loopbackData.loopbackProcessor->printStatus(); 547 printf(" insf %3d,", (int) loopbackData.insufficientReadCount); 548 549 int64_t inputFramesWritten = AAudioStream_getFramesWritten(inputStream); 550 int64_t inputFramesRead = AAudioStream_getFramesRead(inputStream); 551 int64_t outputFramesWritten = AAudioStream_getFramesWritten(outputStream); 552 int64_t outputFramesRead = AAudioStream_getFramesRead(outputStream); 553 static const int textOffset = strlen("AAUDIO_STREAM_STATE_"); // strip this off 554 printf(" | INPUT: wr %7lld - rd %7lld = %5lld, st %8s, oruns %3d", 555 (long long) inputFramesWritten, 556 (long long) inputFramesRead, 557 (long long) (inputFramesWritten - inputFramesRead), 558 &AAudio_convertStreamStateToText( 559 AAudioStream_getState(inputStream))[textOffset], 560 AAudioStream_getXRunCount(inputStream)); 561 562 printf(" | OUTPUT: wr %7lld - rd %7lld = %5lld, st %8s, uruns %3d\n", 563 (long long) outputFramesWritten, 564 (long long) outputFramesRead, 565 (long long) (outputFramesWritten - outputFramesRead), 566 &AAudio_convertStreamStateToText( 567 AAudioStream_getState(outputStream))[textOffset], 568 AAudioStream_getXRunCount(outputStream) 569 ); 570 } 571 int32_t periodMillis = (timeMillis < 2000) ? PERIOD_MILLIS / 4 : PERIOD_MILLIS; 572 usleep(periodMillis * 1000); 573 timeMillis += periodMillis; 574 } 575 576 result = player.stop(); 577 if (result != AAUDIO_OK) { 578 printf("ERROR - player.stop() returned %d = %s\n", 579 result, AAudio_convertResultToText(result)); 580 goto finish; 581 } 582 583 result = recorder.stop(); 584 if (result != AAUDIO_OK) { 585 printf("ERROR - recorder.stop() returned %d = %s\n", 586 result, AAudio_convertResultToText(result)); 587 goto finish; 588 } 589 590 printf("input error = %d = %s\n", 591 loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError)); 592 593 if (loopbackData.inputError == AAUDIO_OK) { 594 if (testMode == TEST_SINE_MAGNITUDE) { 595 printAudioGraph(loopbackData.audioRecording, 200); 596 } 597 // Print again so we don't have to scroll past waveform. 598 printf("OUTPUT Stream ----------------------------------------\n"); 599 argParser.compareWithStream(outputStream); 600 printf("INPUT Stream ----------------------------------------\n"); 601 argParser.compareWithStream(inputStream); 602 603 loopbackData.loopbackProcessor->report(); 604 } 605 606 { 607 int32_t framesRead = AAudioStream_getFramesRead(inputStream); 608 int32_t framesWritten = AAudioStream_getFramesWritten(inputStream); 609 printf("Callback Results ---------------------------------------- INPUT\n"); 610 printf(" input overruns = %d\n", AAudioStream_getXRunCount(inputStream)); 611 printf(" framesWritten = %8d\n", framesWritten); 612 printf(" framesRead = %8d\n", framesRead); 613 printf(" myFramesRead = %8d\n", (int) loopbackData.framesReadTotal); 614 printf(" written - read = %8d\n", (int) (framesWritten - framesRead)); 615 printf(" insufficient # = %8d\n", (int) loopbackData.insufficientReadCount); 616 if (loopbackData.insufficientReadCount > 0) { 617 printf(" insufficient frames = %8d\n", (int) loopbackData.insufficientReadFrames); 618 } 619 } 620 { 621 int32_t framesRead = AAudioStream_getFramesRead(outputStream); 622 int32_t framesWritten = AAudioStream_getFramesWritten(outputStream); 623 printf("Callback Results ---------------------------------------- OUTPUT\n"); 624 printf(" output underruns = %d\n", AAudioStream_getXRunCount(outputStream)); 625 printf(" myFramesWritten = %8d\n", (int) loopbackData.framesWrittenTotal); 626 printf(" framesWritten = %8d\n", framesWritten); 627 printf(" framesRead = %8d\n", framesRead); 628 printf(" min numFrames = %8d\n", (int) loopbackData.minNumFrames); 629 printf(" max numFrames = %8d\n", (int) loopbackData.maxNumFrames); 630 } 631 632 written = loopbackData.loopbackProcessor->save(FILENAME_ECHOS); 633 if (written > 0) { 634 printf("main() wrote %8d mono samples to \"%s\" on Android device\n", 635 written, FILENAME_ECHOS); 636 } 637 638 written = loopbackData.audioRecording.save(FILENAME_ALL); 639 if (written > 0) { 640 printf("main() wrote %8d mono samples to \"%s\" on Android device\n", 641 written, FILENAME_ALL); 642 } 643 644 if (loopbackData.loopbackProcessor->getResult() < 0) { 645 printf("ERROR: LOOPBACK PROCESSING FAILED. Maybe because the volume was too low.\n"); 646 result = loopbackData.loopbackProcessor->getResult(); 647 } 648 if (loopbackData.insufficientReadCount > 3) { 649 printf("ERROR: LOOPBACK PROCESSING FAILED. insufficientReadCount too high\n"); 650 result = AAUDIO_ERROR_UNAVAILABLE; 651 } 652 653finish: 654 player.close(); 655 recorder.close(); 656 delete[] loopbackData.inputFloatData; 657 delete[] loopbackData.inputShortData; 658 659 printf(RESULT_TAG "result = %d \n", result); // machine readable 660 printf("result is %s\n", AAudio_convertResultToText(result)); // human readable 661 if (result != AAUDIO_OK) { 662 printf("FAILURE\n"); 663 return EXIT_FAILURE; 664 } else { 665 printf("SUCCESS\n"); 666 return EXIT_SUCCESS; 667 } 668} 669 670