loopback.cpp revision 4485d41bcded0eceec7ec97d50aa2b0e702397a0
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
41class AudioRecorder
42{
43public:
44    AudioRecorder() {
45    }
46    ~AudioRecorder() {
47        delete[] mData;
48    }
49
50    void allocate(int maxFrames) {
51        delete[] mData;
52        mData = new float[maxFrames];
53        mMaxFrames = maxFrames;
54    }
55
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
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
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
86private:
87    float *mData = NULL;
88    int32_t mFrameCounter = 0;
89    int32_t mMaxFrames = 0;
90};
91
92// ====================================================================================
93// ========================= Loopback Processor =======================================
94// ====================================================================================
95class LoopbackProcessor {
96public:
97
98    // Calculate mean and standard 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
117    float getMaxAmplitude() const { return mMaxAmplitude; }
118    int   getMeasurementCount() const { return mLatencyCount; }
119    float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; }
120
121    // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period.
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
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    }
194private:
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
215const 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
218struct 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
232static 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.
245static 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
299static 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
310static 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
332int 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_audio_format_t requestedInputFormat = AAUDIO_FORMAT_PCM_I16;
343    const aaudio_audio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT;
344    aaudio_audio_format_t actualInputFormat;
345    aaudio_audio_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
517finish:
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