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
hangIfRequestedLoopbackData94 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
convertPcm16ToFloat(const int16_t * source,float * destination,int32_t numSamples)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
readFormattedData(LoopbackData * myData,int32_t numFrames)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
MyDataCallbackProc(AAudioStream * outputStream,void * userData,void * audioData,int32_t numFrames)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
MyErrorCallbackProc(AAudioStream * stream __unused,void * userData __unused,aaudio_result_t error)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
usage()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
parsePerformanceMode(char c)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
parseTestMode(char c)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
printAudioGraphRegion(AudioRecording & recording,int32_t start,int32_t end)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
main(int argc,const char ** argv)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