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