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