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 // Play an impulse and then record it.
18 // Measure the round trip latency.
19
20 #include <assert.h>
21 #include <cctype>
22 #include <math.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27
28 #include <aaudio/AAudio.h>
29
30 #define INPUT_PEAK_THRESHOLD 0.1f
31 #define SILENCE_FRAMES 10000
32 #define SAMPLE_RATE 48000
33 #define NUM_SECONDS 7
34 #define FILENAME "/data/oboe_input.raw"
35
36 #define NANOS_PER_MICROSECOND ((int64_t)1000)
37 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
38 #define MILLIS_PER_SECOND 1000
39 #define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND)
40
41 class AudioRecorder
42 {
43 public:
AudioRecorder()44 AudioRecorder() {
45 }
~AudioRecorder()46 ~AudioRecorder() {
47 delete[] mData;
48 }
49
allocate(int maxFrames)50 void allocate(int maxFrames) {
51 delete[] mData;
52 mData = new float[maxFrames];
53 mMaxFrames = maxFrames;
54 }
55
record(int16_t * inputData,int inputChannelCount,int numFrames)56 void record(int16_t *inputData, int inputChannelCount, int numFrames) {
57 // stop at end of buffer
58 if ((mFrameCounter + numFrames) > mMaxFrames) {
59 numFrames = mMaxFrames - mFrameCounter;
60 }
61 for (int i = 0; i < numFrames; i++) {
62 mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
63 }
64 }
65
record(float * inputData,int inputChannelCount,int numFrames)66 void record(float *inputData, int inputChannelCount, int numFrames) {
67 // stop at end of buffer
68 if ((mFrameCounter + numFrames) > mMaxFrames) {
69 numFrames = mMaxFrames - mFrameCounter;
70 }
71 for (int i = 0; i < numFrames; i++) {
72 mData[mFrameCounter++] = inputData[i * inputChannelCount];
73 }
74 }
75
save(const char * fileName)76 int save(const char *fileName) {
77 FILE *fid = fopen(fileName, "wb");
78 if (fid == NULL) {
79 return errno;
80 }
81 int written = fwrite(mData, sizeof(float), mFrameCounter, fid);
82 fclose(fid);
83 return written;
84 }
85
86 private:
87 float *mData = NULL;
88 int32_t mFrameCounter = 0;
89 int32_t mMaxFrames = 0;
90 };
91
92 // ====================================================================================
93 // ========================= Loopback Processor =======================================
94 // ====================================================================================
95 class LoopbackProcessor {
96 public:
97
98 // Calculate mean and standard deviation.
calculateAverageLatency(double * deviation)99 double calculateAverageLatency(double *deviation) {
100 if (mLatencyCount <= 0) {
101 return -1.0;
102 }
103 double sum = 0.0;
104 for (int i = 0; i < mLatencyCount; i++) {
105 sum += mLatencyArray[i];
106 }
107 double average = sum / mLatencyCount;
108 sum = 0.0;
109 for (int i = 0; i < mLatencyCount; i++) {
110 double error = average - mLatencyArray[i];
111 sum += error * error; // squared
112 }
113 *deviation = sqrt(sum / mLatencyCount);
114 return average;
115 }
116
getMaxAmplitude() const117 float getMaxAmplitude() const { return mMaxAmplitude; }
getMeasurementCount() const118 int getMeasurementCount() const { return mLatencyCount; }
getAverageAmplitude() const119 float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; }
120
121 // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period.
process(float * inputData,int inputChannelCount,float * outputData,int outputChannelCount,int numFrames)122 void process(float *inputData, int inputChannelCount,
123 float *outputData, int outputChannelCount,
124 int numFrames) {
125 (void) outputChannelCount;
126
127 // Measure peak and average amplitude.
128 for (int i = 0; i < numFrames; i++) {
129 float sample = inputData[i * inputChannelCount];
130 if (sample > mMaxAmplitude) {
131 mMaxAmplitude = sample;
132 }
133 if (sample < 0) {
134 sample = 0 - sample;
135 }
136 mAmplitudeTotal += sample;
137 mAmplitudeCount++;
138 }
139
140 // Clear output.
141 memset(outputData, 0, numFrames * outputChannelCount * sizeof(float));
142
143 // Wait a while between hearing the pulse and starting a new one.
144 if (mState == STATE_SILENT) {
145 mCounter += numFrames;
146 if (mCounter > SILENCE_FRAMES) {
147 //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter);
148 // copy impulse
149 for (float sample : mImpulse) {
150 *outputData = sample;
151 outputData += outputChannelCount;
152 }
153 mState = STATE_LISTENING;
154 mCounter = 0;
155 }
156 }
157 // Start listening as soon as we send the impulse.
158 if (mState == STATE_LISTENING) {
159 for (int i = 0; i < numFrames; i++) {
160 float sample = inputData[i * inputChannelCount];
161 if (sample >= INPUT_PEAK_THRESHOLD) {
162 mLatencyArray[mLatencyCount++] = mCounter;
163 if (mLatencyCount >= MAX_LATENCY_VALUES) {
164 mState = STATE_DONE;
165 } else {
166 mState = STATE_SILENT;
167 }
168 mCounter = 0;
169 break;
170 } else {
171 mCounter++;
172 }
173 }
174 }
175 }
176
echo(float * inputData,int inputChannelCount,float * outputData,int outputChannelCount,int numFrames)177 void echo(float *inputData, int inputChannelCount,
178 float *outputData, int outputChannelCount,
179 int numFrames) {
180 int channelsValid = (inputChannelCount < outputChannelCount)
181 ? inputChannelCount : outputChannelCount;
182 for (int i = 0; i < numFrames; i++) {
183 int ic;
184 for (ic = 0; ic < channelsValid; ic++) {
185 outputData[ic] = inputData[ic];
186 }
187 for (ic = 0; ic < outputChannelCount; ic++) {
188 outputData[ic] = 0;
189 }
190 inputData += inputChannelCount;
191 outputData += outputChannelCount;
192 }
193 }
194 private:
195 enum {
196 STATE_SILENT,
197 STATE_LISTENING,
198 STATE_DONE
199 };
200
201 enum {
202 MAX_LATENCY_VALUES = 64
203 };
204
205 int mState = STATE_SILENT;
206 int32_t mCounter = 0;
207 int32_t mLatencyArray[MAX_LATENCY_VALUES];
208 int32_t mLatencyCount = 0;
209 float mMaxAmplitude = 0;
210 float mAmplitudeTotal = 0;
211 int32_t mAmplitudeCount = 0;
212 static const float mImpulse[5];
213 };
214
215 const float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f};
216
217 // TODO make this a class that manages its own buffer allocation
218 struct LoopbackData {
219 AAudioStream *inputStream = nullptr;
220 int32_t inputFramesMaximum = 0;
221 int16_t *inputData = nullptr;
222 float *conversionBuffer = nullptr;
223 int32_t actualInputChannelCount = 0;
224 int32_t actualOutputChannelCount = 0;
225 int32_t inputBuffersToDiscard = 10;
226
227 aaudio_result_t inputError;
228 LoopbackProcessor loopbackProcessor;
229 AudioRecorder audioRecorder;
230 };
231
convertPcm16ToFloat(const int16_t * source,float * destination,int32_t numSamples)232 static void convertPcm16ToFloat(const int16_t *source,
233 float *destination,
234 int32_t numSamples) {
235 const float scaler = 1.0f / 32768.0f;
236 for (int i = 0; i < numSamples; i++) {
237 destination[i] = source[i] * scaler;
238 }
239 }
240
241 // ====================================================================================
242 // ========================= CALLBACK =================================================
243 // ====================================================================================
244 // Callback function that fills the audio output buffer.
MyDataCallbackProc(AAudioStream * outputStream,void * userData,void * audioData,int32_t numFrames)245 static aaudio_data_callback_result_t MyDataCallbackProc(
246 AAudioStream *outputStream,
247 void *userData,
248 void *audioData,
249 int32_t numFrames
250 ) {
251 (void) outputStream;
252 LoopbackData *myData = (LoopbackData *) userData;
253 float *outputData = (float *) audioData;
254
255 // Read audio data from the input stream.
256 int32_t framesRead;
257
258 if (numFrames > myData->inputFramesMaximum) {
259 myData->inputError = AAUDIO_ERROR_OUT_OF_RANGE;
260 return AAUDIO_CALLBACK_RESULT_STOP;
261 }
262
263 if (myData->inputBuffersToDiscard > 0) {
264 // Drain the input.
265 do {
266 framesRead = AAudioStream_read(myData->inputStream, myData->inputData,
267 numFrames, 0);
268 if (framesRead < 0) {
269 myData->inputError = framesRead;
270 } else if (framesRead > 0) {
271 myData->inputBuffersToDiscard--;
272 }
273 } while(framesRead > 0);
274 } else {
275 framesRead = AAudioStream_read(myData->inputStream, myData->inputData,
276 numFrames, 0);
277 if (framesRead < 0) {
278 myData->inputError = framesRead;
279 } else if (framesRead > 0) {
280 // Process valid input data.
281 myData->audioRecorder.record(myData->inputData,
282 myData->actualInputChannelCount,
283 framesRead);
284
285 int32_t numSamples = framesRead * myData->actualInputChannelCount;
286 convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
287
288 myData->loopbackProcessor.process(myData->conversionBuffer,
289 myData->actualInputChannelCount,
290 outputData,
291 myData->actualOutputChannelCount,
292 framesRead);
293 }
294 }
295
296 return AAUDIO_CALLBACK_RESULT_CONTINUE;
297 }
298
usage()299 static void usage() {
300 printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n");
301 printf(" -b{burstsPerBuffer} for example 2 for double buffered\n");
302 printf(" -p{outputPerfMode} set output AAUDIO_PERFORMANCE_MODE*\n");
303 printf(" -P{inputPerfMode} 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("For example: loopback -b2 -pl -Pn\n");
308 }
309
parsePerformanceMode(char c)310 static aaudio_performance_mode_t parsePerformanceMode(char c) {
311 aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE;
312 c = tolower(c);
313 switch (c) {
314 case 'n':
315 mode = AAUDIO_PERFORMANCE_MODE_NONE;
316 break;
317 case 'l':
318 mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
319 break;
320 case 'p':
321 mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
322 break;
323 default:
324 printf("ERROR invalue performance mode %c\n", c);
325 break;
326 }
327 return mode;
328 }
329
330 // ====================================================================================
331 // TODO break up this large main() function into smaller functions
main(int argc,const char ** argv)332 int main(int argc, const char **argv)
333 {
334 aaudio_result_t result = AAUDIO_OK;
335 LoopbackData loopbackData;
336 AAudioStream *outputStream = nullptr;
337
338 const int requestedInputChannelCount = 1;
339 const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED;
340 const int requestedSampleRate = SAMPLE_RATE;
341 int actualSampleRate = 0;
342 const aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_PCM_I16;
343 const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT;
344 aaudio_format_t actualInputFormat;
345 aaudio_format_t actualOutputFormat;
346
347 const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
348 //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
349 aaudio_sharing_mode_t actualSharingMode;
350
351 AAudioStreamBuilder *builder = nullptr;
352 aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
353 int32_t framesPerBurst = 0;
354 float *outputData = NULL;
355 double deviation;
356 double latency;
357 aaudio_performance_mode_t outputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
358 aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
359
360 int32_t burstsPerBuffer = 1; // single buffered
361
362 for (int i = 1; i < argc; i++) {
363 const char *arg = argv[i];
364 if (arg[0] == '-') {
365 char option = arg[1];
366 switch (option) {
367 case 'b':
368 burstsPerBuffer = atoi(&arg[2]);
369 break;
370 case 'p':
371 outputPerformanceLevel = parsePerformanceMode(arg[2]);
372 break;
373 case 'P':
374 inputPerformanceLevel = parsePerformanceMode(arg[2]);
375 break;
376 default:
377 usage();
378 break;
379 }
380 } else {
381 break;
382 }
383 }
384
385 loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
386
387 // Make printf print immediately so that debug info is not stuck
388 // in a buffer if we hang or crash.
389 setvbuf(stdout, NULL, _IONBF, (size_t) 0);
390
391 printf("%s - Audio loopback using AAudio\n", argv[0]);
392
393 // Use an AAudioStreamBuilder to contain requested parameters.
394 result = AAudio_createStreamBuilder(&builder);
395 if (result < 0) {
396 goto finish;
397 }
398
399 // Request common stream properties.
400 AAudioStreamBuilder_setSampleRate(builder, requestedSampleRate);
401 AAudioStreamBuilder_setFormat(builder, requestedInputFormat);
402 AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode);
403
404 // Open the input stream.
405 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
406 AAudioStreamBuilder_setPerformanceMode(builder, inputPerformanceLevel);
407 AAudioStreamBuilder_setChannelCount(builder, requestedInputChannelCount);
408
409 result = AAudioStreamBuilder_openStream(builder, &loopbackData.inputStream);
410 printf("AAudioStreamBuilder_openStream(input) returned %d = %s\n",
411 result, AAudio_convertResultToText(result));
412 if (result < 0) {
413 goto finish;
414 }
415
416 // Create an output stream using the Builder.
417 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
418 AAudioStreamBuilder_setFormat(builder, requestedOutputFormat);
419 AAudioStreamBuilder_setPerformanceMode(builder, outputPerformanceLevel);
420 AAudioStreamBuilder_setChannelCount(builder, requestedOutputChannelCount);
421 AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &loopbackData);
422
423 result = AAudioStreamBuilder_openStream(builder, &outputStream);
424 printf("AAudioStreamBuilder_openStream(output) returned %d = %s\n",
425 result, AAudio_convertResultToText(result));
426 if (result != AAUDIO_OK) {
427 goto finish;
428 }
429
430 printf("Stream INPUT ---------------------\n");
431 loopbackData.actualInputChannelCount = AAudioStream_getChannelCount(loopbackData.inputStream);
432 printf(" channelCount: requested = %d, actual = %d\n", requestedInputChannelCount,
433 loopbackData.actualInputChannelCount);
434 printf(" framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream));
435
436 actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream);
437 printf(" dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat);
438 assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
439
440 printf("Stream OUTPUT ---------------------\n");
441 // Check to see what kind of stream we actually got.
442 actualSampleRate = AAudioStream_getSampleRate(outputStream);
443 printf(" sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
444
445 loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream);
446 printf(" channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount,
447 loopbackData.actualOutputChannelCount);
448
449 actualSharingMode = AAudioStream_getSharingMode(outputStream);
450 printf(" sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode);
451
452 // This is the number of frames that are read in one chunk by a DMA controller
453 // or a DSP or a mixer.
454 framesPerBurst = AAudioStream_getFramesPerBurst(outputStream);
455 printf(" framesPerBurst = %d\n", framesPerBurst);
456
457 printf(" bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
458
459 actualOutputFormat = AAudioStream_getFormat(outputStream);
460 printf(" dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat);
461 assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
462
463 // Allocate a buffer for the audio data.
464 loopbackData.inputFramesMaximum = 32 * framesPerBurst;
465
466 loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount];
467 loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
468 loopbackData.actualInputChannelCount];
469
470 result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst);
471 if (result < 0) { // may be positive buffer size
472 fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result);
473 goto finish;
474 }
475 printf("AAudioStream_setBufferSize() actual = %d\n",result);
476
477 // Start output first so input stream runs low.
478 result = AAudioStream_requestStart(outputStream);
479 if (result != AAUDIO_OK) {
480 fprintf(stderr, "ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
481 result, AAudio_convertResultToText(result));
482 goto finish;
483 }
484
485 result = AAudioStream_requestStart(loopbackData.inputStream);
486 if (result != AAUDIO_OK) {
487 fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
488 result, AAudio_convertResultToText(result));
489 goto finish;
490 }
491
492 printf("------- sleep while the callback runs --------------\n");
493 fflush(stdout);
494 sleep(NUM_SECONDS);
495
496
497 printf("input error = %d = %s\n",
498 loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
499
500 printf("AAudioStream_getXRunCount %d\n", AAudioStream_getXRunCount(outputStream));
501 printf("framesRead = %d\n", (int) AAudioStream_getFramesRead(outputStream));
502 printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream));
503
504 latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation);
505 printf("measured peak = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude());
506 printf("threshold = %8.5f\n", INPUT_PEAK_THRESHOLD);
507 printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude());
508 printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount());
509 printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation);
510 printf("measured latency = %8.2f msec <===== !!\n", (1000.0 * latency / actualSampleRate));
511
512 {
513 int written = loopbackData.audioRecorder.save(FILENAME);
514 printf("wrote %d samples to %s\n", written, FILENAME);
515 }
516
517 finish:
518 AAudioStream_close(outputStream);
519 AAudioStream_close(loopbackData.inputStream);
520 delete[] loopbackData.conversionBuffer;
521 delete[] loopbackData.inputData;
522 delete[] outputData;
523 AAudioStreamBuilder_delete(builder);
524
525 printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
526 return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
527 }
528
529