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