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