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