14485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk/*
24485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * Copyright (C) 2016 The Android Open Source Project
34485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk *
44485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * Licensed under the Apache License, Version 2.0 (the "License");
54485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * you may not use this file except in compliance with the License.
64485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * You may obtain a copy of the License at
74485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk *
84485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk *      http://www.apache.org/licenses/LICENSE-2.0
94485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk *
104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * Unless required by applicable law or agreed to in writing, software
114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * distributed under the License is distributed on an "AS IS" BASIS,
124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * See the License for the specific language governing permissions and
144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk * limitations under the License.
154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk */
164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// Play an impulse and then record it.
184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// Measure the round trip latency.
194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <assert.h>
214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <cctype>
224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <math.h>
234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <stdlib.h>
244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <stdio.h>
254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <stdlib.h>
264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <unistd.h>
274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#include <aaudio/AAudio.h>
294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define INPUT_PEAK_THRESHOLD    0.1f
314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define SILENCE_FRAMES          10000
324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define SAMPLE_RATE             48000
334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NUM_SECONDS             7
344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define FILENAME                "/data/oboe_input.raw"
354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NANOS_PER_MICROSECOND ((int64_t)1000)
374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define MILLIS_PER_SECOND     1000
394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk#define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * MILLIS_PER_SECOND)
404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkclass AudioRecorder
424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk{
434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkpublic:
444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AudioRecorder() {
454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    ~AudioRecorder() {
474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        delete[] mData;
484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    void allocate(int maxFrames) {
514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        delete[] mData;
524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        mData = new float[maxFrames];
534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        mMaxFrames = maxFrames;
544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    void record(int16_t *inputData, int inputChannelCount, int numFrames) {
574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // stop at end of buffer
584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if ((mFrameCounter + numFrames) > mMaxFrames) {
594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            numFrames = mMaxFrames - mFrameCounter;
604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        for (int i = 0; i < numFrames; i++) {
624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    void record(float *inputData, int inputChannelCount, int numFrames) {
674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // stop at end of buffer
684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if ((mFrameCounter + numFrames) > mMaxFrames) {
694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            numFrames = mMaxFrames - mFrameCounter;
704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        for (int i = 0; i < numFrames; i++) {
724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mData[mFrameCounter++] = inputData[i * inputChannelCount];
734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int save(const char *fileName) {
774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        FILE *fid = fopen(fileName, "wb");
784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if (fid == NULL) {
794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            return errno;
804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        int written = fwrite(mData, sizeof(float), mFrameCounter, fid);
824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        fclose(fid);
834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        return written;
844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkprivate:
874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float *mData = NULL;
884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t mFrameCounter = 0;
894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t mMaxFrames = 0;
904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk};
914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ====================================================================================
934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ========================= Loopback Processor =======================================
944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ====================================================================================
954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkclass LoopbackProcessor {
964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkpublic:
974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Calculate mean and standard deviation.
994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    double calculateAverageLatency(double *deviation) {
1004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if (mLatencyCount <= 0) {
1014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            return -1.0;
1024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        double sum = 0.0;
1044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        for (int i = 0; i < mLatencyCount; i++) {
1054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            sum += mLatencyArray[i];
1064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        double average = sum /  mLatencyCount;
1084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        sum = 0.0;
1094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        for (int i = 0; i < mLatencyCount; i++) {
1104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            double error = average - mLatencyArray[i];
1114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            sum += error * error; // squared
1124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        *deviation = sqrt(sum / mLatencyCount);
1144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        return average;
1154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
1164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
1174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float getMaxAmplitude() const { return mMaxAmplitude; }
1184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int   getMeasurementCount() const { return mLatencyCount; }
1194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float getAverageAmplitude() const { return mAmplitudeTotal / mAmplitudeCount; }
1204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
1214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // TODO Convert this to a feedback circuit and then use auto-correlation to measure the period.
1224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    void process(float *inputData, int inputChannelCount,
1234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            float *outputData, int outputChannelCount,
1244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            int numFrames) {
1254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        (void) outputChannelCount;
1264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
1274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // Measure peak and average amplitude.
1284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        for (int i = 0; i < numFrames; i++) {
1294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            float sample = inputData[i * inputChannelCount];
1304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            if (sample > mMaxAmplitude) {
1314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                mMaxAmplitude = sample;
1324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
1334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            if (sample < 0) {
1344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                sample = 0 - sample;
1354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
1364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mAmplitudeTotal += sample;
1374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mAmplitudeCount++;
1384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
1404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // Clear output.
1414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        memset(outputData, 0, numFrames * outputChannelCount * sizeof(float));
1424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
1434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // Wait a while between hearing the pulse and starting a new one.
1444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if (mState == STATE_SILENT) {
1454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mCounter += numFrames;
1464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            if (mCounter > SILENCE_FRAMES) {
1474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                //printf("LoopbackProcessor send impulse, burst #%d\n", mBurstCounter);
1484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                // copy impulse
1494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                for (float sample : mImpulse) {
1504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    *outputData = sample;
1514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    outputData += outputChannelCount;
1524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                }
1534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                mState = STATE_LISTENING;
1544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                mCounter = 0;
1554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
1564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // Start listening as soon as we send the impulse.
1584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if (mState ==  STATE_LISTENING) {
1594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            for (int i = 0; i < numFrames; i++) {
1604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                float sample = inputData[i * inputChannelCount];
1614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                if (sample >= INPUT_PEAK_THRESHOLD) {
1624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    mLatencyArray[mLatencyCount++] = mCounter;
1634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    if (mLatencyCount >= MAX_LATENCY_VALUES) {
1644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                        mState = STATE_DONE;
1654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    } else {
1664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                        mState = STATE_SILENT;
1674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    }
1684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    mCounter = 0;
1694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    break;
1704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                } else {
1714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    mCounter++;
1724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                }
1734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
1744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
1764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
1774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    void echo(float *inputData, int inputChannelCount,
1784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            float *outputData, int outputChannelCount,
1794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            int numFrames) {
1804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        int channelsValid = (inputChannelCount < outputChannelCount)
1814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            ? inputChannelCount : outputChannelCount;
1824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        for (int i = 0; i < numFrames; i++) {
1834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            int ic;
1844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            for (ic = 0; ic < channelsValid; ic++) {
1854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                outputData[ic] = inputData[ic];
1864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
1874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            for (ic = 0; ic < outputChannelCount; ic++) {
1884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                outputData[ic] = 0;
1894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
1904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            inputData += inputChannelCount;
1914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            outputData += outputChannelCount;
1924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
1934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
1944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkprivate:
1954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    enum {
1964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        STATE_SILENT,
1974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        STATE_LISTENING,
1984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        STATE_DONE
1994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    };
2004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    enum {
2024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        MAX_LATENCY_VALUES = 64
2034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    };
2044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int     mState = STATE_SILENT;
2064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t mCounter = 0;
2074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t mLatencyArray[MAX_LATENCY_VALUES];
2084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t mLatencyCount = 0;
2094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float   mMaxAmplitude = 0;
2104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float   mAmplitudeTotal = 0;
2114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t mAmplitudeCount = 0;
2124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    static const float mImpulse[5];
2134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk};
2144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkconst float LoopbackProcessor::mImpulse[5] = {0.5f, 0.9f, 0.0f, -0.9f, -0.5f};
2164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// TODO make this a class that manages its own buffer allocation
2184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstruct LoopbackData {
2194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStream     *inputStream = nullptr;
2204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t           inputFramesMaximum = 0;
2214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int16_t          *inputData = nullptr;
2224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float            *conversionBuffer = nullptr;
2234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t           actualInputChannelCount = 0;
2244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t           actualOutputChannelCount = 0;
2254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t           inputBuffersToDiscard = 10;
2264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_result_t   inputError;
2284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    LoopbackProcessor loopbackProcessor;
2294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AudioRecorder     audioRecorder;
2304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk};
2314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic void convertPcm16ToFloat(const int16_t *source,
2334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                float *destination,
2344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                int32_t numSamples) {
2354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    const float scaler = 1.0f / 32768.0f;
2364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    for (int i = 0; i < numSamples; i++) {
2374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        destination[i] = source[i] * scaler;
2384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
2394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}
2404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ====================================================================================
2424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ========================= CALLBACK =================================================
2434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ====================================================================================
2444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// Callback function that fills the audio output buffer.
2454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic aaudio_data_callback_result_t MyDataCallbackProc(
2464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        AAudioStream *outputStream,
2474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        void *userData,
2484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        void *audioData,
2494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        int32_t numFrames
2504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk) {
2514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    (void) outputStream;
2524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    LoopbackData *myData = (LoopbackData *) userData;
2534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float  *outputData = (float  *) audioData;
2544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Read audio data from the input stream.
2564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t framesRead;
2574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (numFrames > myData->inputFramesMaximum) {
2594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        myData->inputError = AAUDIO_ERROR_OUT_OF_RANGE;
2604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        return AAUDIO_CALLBACK_RESULT_STOP;
2614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
2624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (myData->inputBuffersToDiscard > 0) {
2644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        // Drain the input.
2654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        do {
2664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            framesRead = AAudioStream_read(myData->inputStream, myData->inputData,
2674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                       numFrames, 0);
2684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            if (framesRead < 0) {
2694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                myData->inputError = framesRead;
2704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            } else if (framesRead > 0) {
2714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                myData->inputBuffersToDiscard--;
2724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
2734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        } while(framesRead > 0);
2744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    } else {
2754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        framesRead = AAudioStream_read(myData->inputStream, myData->inputData,
2764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                       numFrames, 0);
2774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if (framesRead < 0) {
2784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            myData->inputError = framesRead;
2794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        } else if (framesRead > 0) {
2804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            // Process valid input data.
2814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            myData->audioRecorder.record(myData->inputData,
2824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                         myData->actualInputChannelCount,
2834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                         framesRead);
2844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            int32_t numSamples = framesRead * myData->actualInputChannelCount;
2864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            convertPcm16ToFloat(myData->inputData, myData->conversionBuffer, numSamples);
2874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            myData->loopbackProcessor.process(myData->conversionBuffer,
2894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                              myData->actualInputChannelCount,
2904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                              outputData,
2914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                              myData->actualOutputChannelCount,
2924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                              framesRead);
2934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
2944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
2954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    return AAUDIO_CALLBACK_RESULT_CONTINUE;
2974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}
2984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
2994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic void usage() {
3004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("loopback: -b{burstsPerBuffer} -p{outputPerfMode} -P{inputPerfMode}\n");
3014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("          -b{burstsPerBuffer} for example 2 for double buffered\n");
3024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("          -p{outputPerfMode}  set output AAUDIO_PERFORMANCE_MODE*\n");
3034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("          -P{inputPerfMode}   set input AAUDIO_PERFORMANCE_MODE*\n");
3044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("              n for _NONE\n");
3054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("              l for _LATENCY\n");
3064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("              p for _POWER_SAVING;\n");
3074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("For example:  loopback -b2 -pl -Pn\n");
3084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}
3094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkstatic aaudio_performance_mode_t parsePerformanceMode(char c) {
3114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_performance_mode_t mode = AAUDIO_PERFORMANCE_MODE_NONE;
3124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    c = tolower(c);
3134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    switch (c) {
3144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        case 'n':
3154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mode = AAUDIO_PERFORMANCE_MODE_NONE;
3164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            break;
3174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        case 'l':
3184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
3194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            break;
3204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        case 'p':
3214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            mode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
3224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            break;
3234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        default:
3244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            printf("ERROR invalue performance mode %c\n", c);
3254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            break;
3264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
3274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    return mode;
3284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}
3294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// ====================================================================================
3314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk// TODO break up this large main() function into smaller functions
3324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkint main(int argc, const char **argv)
3334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk{
3344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_result_t result = AAUDIO_OK;
3354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    LoopbackData loopbackData;
3364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStream *outputStream = nullptr;
3374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    const int requestedInputChannelCount = 1;
3394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    const int requestedOutputChannelCount = AAUDIO_UNSPECIFIED;
3404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    const int requestedSampleRate = SAMPLE_RATE;
3414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int actualSampleRate = 0;
3429dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk    const aaudio_format_t requestedInputFormat = AAUDIO_FORMAT_PCM_I16;
3439dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk    const aaudio_format_t requestedOutputFormat = AAUDIO_FORMAT_PCM_FLOAT;
3449dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk    aaudio_format_t actualInputFormat;
3459dca9824da74d50be02bc81f539cc77b7bde678aPhil Burk    aaudio_format_t actualOutputFormat;
3464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
34787c9f646a94259d7c321c3b3d5947fa1778f5ac2Phil Burk    const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
34887c9f646a94259d7c321c3b3d5947fa1778f5ac2Phil Burk    //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
3494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_sharing_mode_t       actualSharingMode;
3504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder  *builder = nullptr;
3524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
3534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t framesPerBurst = 0;
3544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    float *outputData = NULL;
3554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    double deviation;
3564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    double latency;
3574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_performance_mode_t outputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
3584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
3594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    int32_t burstsPerBuffer = 1; // single buffered
3614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    for (int i = 1; i < argc; i++) {
3634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        const char *arg = argv[i];
3644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        if (arg[0] == '-') {
3654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            char option = arg[1];
3664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            switch (option) {
3674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                case 'b':
3684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    burstsPerBuffer = atoi(&arg[2]);
3694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    break;
3704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                case 'p':
3714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    outputPerformanceLevel = parsePerformanceMode(arg[2]);
3724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    break;
3734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                case 'P':
3744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    inputPerformanceLevel = parsePerformanceMode(arg[2]);
3754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    break;
3764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                default:
3774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    usage();
3784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                    break;
3794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            }
3804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        } else {
3814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk            break;
3824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        }
3834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
3844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    loopbackData.audioRecorder.allocate(NUM_SECONDS * SAMPLE_RATE);
3864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Make printf print immediately so that debug info is not stuck
3884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // in a buffer if we hang or crash.
3894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    setvbuf(stdout, NULL, _IONBF, (size_t) 0);
3904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("%s - Audio loopback using AAudio\n", argv[0]);
3924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Use an AAudioStreamBuilder to contain requested parameters.
3944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    result = AAudio_createStreamBuilder(&builder);
3954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (result < 0) {
3964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        goto finish;
3974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
3984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
3994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Request common stream properties.
4004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setSampleRate(builder, requestedSampleRate);
4014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setFormat(builder, requestedInputFormat);
4024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setSharingMode(builder, requestedSharingMode);
4034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Open the input stream.
4054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
4064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setPerformanceMode(builder, inputPerformanceLevel);
4074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setChannelCount(builder, requestedInputChannelCount);
4084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    result = AAudioStreamBuilder_openStream(builder, &loopbackData.inputStream);
4104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("AAudioStreamBuilder_openStream(input) returned %d = %s\n",
4114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk           result, AAudio_convertResultToText(result));
4124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (result < 0) {
4134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        goto finish;
4144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
4154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Create an output stream using the Builder.
4174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
4184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setFormat(builder, requestedOutputFormat);
4194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setPerformanceMode(builder, outputPerformanceLevel);
4204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setChannelCount(builder, requestedOutputChannelCount);
4214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_setDataCallback(builder, MyDataCallbackProc, &loopbackData);
4224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    result = AAudioStreamBuilder_openStream(builder, &outputStream);
4244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("AAudioStreamBuilder_openStream(output) returned %d = %s\n",
4254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk           result, AAudio_convertResultToText(result));
4264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (result != AAUDIO_OK) {
4274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        goto finish;
4284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
4294485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4304485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("Stream INPUT ---------------------\n");
4314485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    loopbackData.actualInputChannelCount = AAudioStream_getChannelCount(loopbackData.inputStream);
4324485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    channelCount: requested = %d, actual = %d\n", requestedInputChannelCount,
4334485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk           loopbackData.actualInputChannelCount);
4344485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    framesPerBurst = %d\n", AAudioStream_getFramesPerBurst(loopbackData.inputStream));
4354485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4364485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    actualInputFormat = AAudioStream_getFormat(loopbackData.inputStream);
4374485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    dataFormat: requested = %d, actual = %d\n", requestedInputFormat, actualInputFormat);
4384485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    assert(actualInputFormat == AAUDIO_FORMAT_PCM_I16);
4394485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4404485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("Stream OUTPUT ---------------------\n");
4414485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Check to see what kind of stream we actually got.
4424485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    actualSampleRate = AAudioStream_getSampleRate(outputStream);
4434485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    sampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
4444485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4454485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    loopbackData.actualOutputChannelCount = AAudioStream_getChannelCount(outputStream);
4464485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    channelCount: requested = %d, actual = %d\n", requestedOutputChannelCount,
4474485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk           loopbackData.actualOutputChannelCount);
4484485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4494485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    actualSharingMode = AAudioStream_getSharingMode(outputStream);
4504485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    sharingMode: requested = %d, actual = %d\n", requestedSharingMode, actualSharingMode);
4514485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4524485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // This is the number of frames that are read in one chunk by a DMA controller
4534485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // or a DSP or a mixer.
4544485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    framesPerBurst = AAudioStream_getFramesPerBurst(outputStream);
4554485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    framesPerBurst = %d\n", framesPerBurst);
4564485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4574485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    bufferCapacity = %d\n", AAudioStream_getBufferCapacityInFrames(outputStream));
4584485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4594485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    actualOutputFormat = AAudioStream_getFormat(outputStream);
4604485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("    dataFormat: requested = %d, actual = %d\n", requestedOutputFormat, actualOutputFormat);
4614485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    assert(actualOutputFormat == AAUDIO_FORMAT_PCM_FLOAT);
4624485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4634485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Allocate a buffer for the audio data.
4644485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    loopbackData.inputFramesMaximum = 32 * framesPerBurst;
4654485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4664485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    loopbackData.inputData = new int16_t[loopbackData.inputFramesMaximum * loopbackData.actualInputChannelCount];
4674485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    loopbackData.conversionBuffer = new float[loopbackData.inputFramesMaximum *
4684485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                                              loopbackData.actualInputChannelCount];
4694485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4704485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    result = AAudioStream_setBufferSizeInFrames(outputStream, burstsPerBuffer * framesPerBurst);
4714485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (result < 0) { // may be positive buffer size
4724485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        fprintf(stderr, "ERROR - AAudioStream_setBufferSize() returned %d\n", result);
4734485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        goto finish;
4744485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
4754485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("AAudioStream_setBufferSize() actual = %d\n",result);
4764485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4774485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    // Start output first so input stream runs low.
4784485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    result = AAudioStream_requestStart(outputStream);
4794485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (result != AAUDIO_OK) {
4804485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        fprintf(stderr, "ERROR - AAudioStream_requestStart(output) returned %d = %s\n",
4814485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                result, AAudio_convertResultToText(result));
4824485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        goto finish;
4834485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
4844485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4854485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    result = AAudioStream_requestStart(loopbackData.inputStream);
4864485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    if (result != AAUDIO_OK) {
4874485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        fprintf(stderr, "ERROR - AAudioStream_requestStart(input) returned %d = %s\n",
4884485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                result, AAudio_convertResultToText(result));
4894485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        goto finish;
4904485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
4914485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4924485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("------- sleep while the callback runs --------------\n");
4934485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    fflush(stdout);
4944485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    sleep(NUM_SECONDS);
4954485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4964485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
4974485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("input error = %d = %s\n",
4984485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk                loopbackData.inputError, AAudio_convertResultToText(loopbackData.inputError));
4994485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
5004485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("AAudioStream_getXRunCount %d\n", AAudioStream_getXRunCount(outputStream));
5014485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("framesRead    = %d\n", (int) AAudioStream_getFramesRead(outputStream));
5024485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(outputStream));
5034485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
5044485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    latency = loopbackData.loopbackProcessor.calculateAverageLatency(&deviation);
5054485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("measured peak    = %8.5f\n", loopbackData.loopbackProcessor.getMaxAmplitude());
5064485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("threshold        = %8.5f\n", INPUT_PEAK_THRESHOLD);
5074485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("measured average = %8.5f\n", loopbackData.loopbackProcessor.getAverageAmplitude());
5084485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("# latency measurements = %d\n", loopbackData.loopbackProcessor.getMeasurementCount());
5094485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("measured latency = %8.2f +/- %4.5f frames\n", latency, deviation);
5104485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("measured latency = %8.2f msec  <===== !!\n", (1000.0 * latency / actualSampleRate));
5114485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
5124485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    {
5134485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        int written = loopbackData.audioRecorder.save(FILENAME);
5144485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk        printf("wrote %d samples to %s\n", written, FILENAME);
5154485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    }
5164485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
5174485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burkfinish:
5184485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStream_close(outputStream);
5194485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStream_close(loopbackData.inputStream);
5204485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    delete[] loopbackData.conversionBuffer;
5214485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    delete[] loopbackData.inputData;
5224485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    delete[] outputData;
5234485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    AAudioStreamBuilder_delete(builder);
5244485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
5254485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
5264485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk    return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
5274485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk}
5284485d41bcded0eceec7ec97d50aa2b0e702397a0Phil Burk
529