Home | History | Annotate | Download | only in src
      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