Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2017 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 /**
     18  * Tools for measuring latency and for detecting glitches.
     19  * These classes are pure math and can be used with any audio system.
     20  */
     21 
     22 #ifndef AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
     23 #define AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
     24 
     25 #include <algorithm>
     26 #include <assert.h>
     27 #include <cctype>
     28 #include <math.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 
     33 #include <audio_utils/sndfile.h>
     34 
     35 // Tag for machine readable results as property = value pairs
     36 #define LOOPBACK_RESULT_TAG      "RESULT: "
     37 
     38 constexpr int32_t kDefaultSampleRate = 48000;
     39 constexpr int32_t kMillisPerSecond   = 1000;
     40 constexpr int32_t kMinLatencyMillis  = 4;    // arbitrary and very low
     41 constexpr int32_t kMaxLatencyMillis  = 400;  // arbitrary and generous
     42 constexpr double  kMaxEchoGain       = 10.0; // based on experiments, otherwise too noisy
     43 constexpr double  kMinimumConfidence = 0.5;
     44 
     45 static void printAudioScope(float sample) {
     46     const int maxStars = 80; // arbitrary, fits on one line
     47     char c = '*';
     48     if (sample < -1.0) {
     49         sample = -1.0;
     50         c = '$';
     51     } else if (sample > 1.0) {
     52         sample = 1.0;
     53         c = '$';
     54     }
     55     int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
     56     for (int i = 0; i < numSpaces; i++) {
     57         putchar(' ');
     58     }
     59     printf("%c\n", c);
     60 }
     61 
     62 /*
     63 
     64 FIR filter designed with
     65 http://t-filter.appspot.com
     66 
     67 sampling frequency: 48000 Hz
     68 
     69 * 0 Hz - 8000 Hz
     70   gain = 1.2
     71   desired ripple = 5 dB
     72   actual ripple = 5.595266169703693 dB
     73 
     74 * 12000 Hz - 20000 Hz
     75   gain = 0
     76   desired attenuation = -40 dB
     77   actual attenuation = -37.58691566571914 dB
     78 
     79 */
     80 
     81 #define FILTER_TAP_NUM 11
     82 
     83 static const float sFilterTaps8000[FILTER_TAP_NUM] = {
     84         -0.05944219353343189f,
     85         -0.07303434839503208f,
     86         -0.037690487672689066f,
     87         0.1870480506596512f,
     88         0.3910337357836833f,
     89         0.5333672385425637f,
     90         0.3910337357836833f,
     91         0.1870480506596512f,
     92         -0.037690487672689066f,
     93         -0.07303434839503208f,
     94         -0.05944219353343189f
     95 };
     96 
     97 class LowPassFilter {
     98 public:
     99 
    100     /*
    101      * Filter one input sample.
    102      * @return filtered output
    103      */
    104     float filter(float input) {
    105         float output = 0.0f;
    106         mX[mCursor] = input;
    107         // Index backwards over x.
    108         int xIndex = mCursor + FILTER_TAP_NUM;
    109         // Write twice so we avoid having to wrap in the middle of the convolution.
    110         mX[xIndex] = input;
    111         for (int i = 0; i < FILTER_TAP_NUM; i++) {
    112             output += sFilterTaps8000[i] * mX[xIndex--];
    113         }
    114         if (++mCursor >= FILTER_TAP_NUM) {
    115             mCursor = 0;
    116         }
    117         return output;
    118     }
    119 
    120     /**
    121      * @return true if PASSED
    122      */
    123     bool test() {
    124         // Measure the impulse of the filter at different phases so we exercise
    125         // all the wraparound cases in the FIR.
    126         for (int offset = 0; offset < (FILTER_TAP_NUM * 2); offset++ ) {
    127             // printf("LowPassFilter: cursor = %d\n", mCursor);
    128             // Offset by one each time.
    129             if (filter(0.0f) != 0.0f) {
    130                 printf("ERROR: filter should return 0.0 before impulse response\n");
    131                 return false;
    132             }
    133             for (int i = 0; i < FILTER_TAP_NUM; i++) {
    134                 float output = filter((i == 0) ? 1.0f : 0.0f); // impulse
    135                 if (output != sFilterTaps8000[i]) {
    136                     printf("ERROR: filter should return impulse response\n");
    137                     return false;
    138                 }
    139             }
    140             for (int i = 0; i < FILTER_TAP_NUM; i++) {
    141                 if (filter(0.0f) != 0.0f) {
    142                     printf("ERROR: filter should return 0.0 after impulse response\n");
    143                     return false;
    144                 }
    145             }
    146         }
    147         return true;
    148     }
    149 
    150 private:
    151     float   mX[FILTER_TAP_NUM * 2]{}; // twice as big as needed to avoid wrapping
    152     int32_t mCursor = 0;
    153 };
    154 
    155 // A narrow impulse seems to have better immunity against over estimating the
    156 // latency due to detecting subharmonics by the auto-correlator.
    157 static const float s_Impulse[] = {
    158         0.0f, 0.0f, 0.0f, 0.0f, 0.3f, // silence on each side of the impulse
    159         0.99f, 0.0f, -0.99f, // bipolar with one zero crossing in middle
    160         -0.3f, 0.0f, 0.0f, 0.0f, 0.0f
    161 };
    162 
    163 constexpr int32_t kImpulseSizeInFrames = (int32_t)(sizeof(s_Impulse) / sizeof(s_Impulse[0]));
    164 
    165 class PseudoRandom {
    166 public:
    167     PseudoRandom() {}
    168     PseudoRandom(int64_t seed)
    169             :    mSeed(seed)
    170     {}
    171 
    172     /**
    173      * Returns the next random double from -1.0 to 1.0
    174      *
    175      * @return value from -1.0 to 1.0
    176      */
    177      double nextRandomDouble() {
    178         return nextRandomInteger() * (0.5 / (((int32_t)1) << 30));
    179     }
    180 
    181     /** Calculate random 32 bit number using linear-congruential method. */
    182     int32_t nextRandomInteger() {
    183         // Use values for 64-bit sequence from MMIX by Donald Knuth.
    184         mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407;
    185         return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence.
    186     }
    187 
    188 private:
    189     int64_t mSeed = 99887766;
    190 };
    191 
    192 
    193 typedef struct LatencyReport_s {
    194     double latencyInFrames;
    195     double confidence;
    196 } LatencyReport;
    197 
    198 static double calculateCorrelation(const float *a,
    199                                    const float *b,
    200                                    int windowSize)
    201 {
    202     double correlation = 0.0;
    203     double sumProducts = 0.0;
    204     double sumSquares = 0.0;
    205 
    206     // Correlate a against b.
    207     for (int i = 0; i < windowSize; i++) {
    208         float s1 = a[i];
    209         float s2 = b[i];
    210         // Use a normalized cross-correlation.
    211         sumProducts += s1 * s2;
    212         sumSquares += ((s1 * s1) + (s2 * s2));
    213     }
    214 
    215     if (sumSquares >= 0.00000001) {
    216         correlation = (float) (2.0 * sumProducts / sumSquares);
    217     }
    218     return correlation;
    219 }
    220 
    221 static int measureLatencyFromEchos(const float *data,
    222                                    int32_t numFloats,
    223                                    int32_t sampleRate,
    224                                    LatencyReport *report) {
    225     // Allocate results array
    226     const int minReasonableLatencyFrames = sampleRate * kMinLatencyMillis / kMillisPerSecond;
    227     const int maxReasonableLatencyFrames = sampleRate * kMaxLatencyMillis / kMillisPerSecond;
    228     int32_t maxCorrelationSize = maxReasonableLatencyFrames * 3;
    229     int numCorrelations = std::min(numFloats, maxCorrelationSize);
    230     float *correlations = new float[numCorrelations]{};
    231     float *harmonicSums = new float[numCorrelations]{};
    232 
    233     // Perform sliding auto-correlation.
    234     // Skip first frames to avoid huge peak at zero offset.
    235     for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
    236         int32_t remaining = numFloats - i;
    237         float correlation = (float) calculateCorrelation(&data[i], data, remaining);
    238         correlations[i] = correlation;
    239         // printf("correlation[%d] = %f\n", ic, correlation);
    240     }
    241 
    242     // Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
    243     // Add higher harmonics mapped onto lower harmonics. This reinforces the "fundamental" echo.
    244     const int numEchoes = 8;
    245     for (int partial = 1; partial < numEchoes; partial++) {
    246         for (int i = minReasonableLatencyFrames; i < numCorrelations; i++) {
    247             harmonicSums[i / partial] += correlations[i] / partial;
    248         }
    249     }
    250 
    251     // Find highest peak in correlation array.
    252     float maxCorrelation = 0.0;
    253     int peakIndex = 0;
    254     for (int i = 0; i < numCorrelations; i++) {
    255         if (harmonicSums[i] > maxCorrelation) {
    256             maxCorrelation = harmonicSums[i];
    257             peakIndex = i;
    258             // printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
    259         }
    260     }
    261     report->latencyInFrames = peakIndex;
    262 /*
    263     {
    264         int32_t topPeak = peakIndex * 7 / 2;
    265         for (int i = 0; i < topPeak; i++) {
    266             float sample = harmonicSums[i];
    267             printf("%4d: %7.5f ", i, sample);
    268             printAudioScope(sample);
    269         }
    270     }
    271 */
    272 
    273     // Calculate confidence.
    274     if (maxCorrelation < 0.001) {
    275         report->confidence = 0.0;
    276     } else {
    277         // Compare peak to average value around peak.
    278         int32_t numSamples = std::min(numCorrelations, peakIndex * 2);
    279         if (numSamples <= 0) {
    280             report->confidence = 0.0;
    281         } else {
    282             double sum = 0.0;
    283             for (int i = 0; i < numSamples; i++) {
    284                 sum += harmonicSums[i];
    285             }
    286             const double average = sum / numSamples;
    287             const double ratio = average / maxCorrelation; // will be < 1.0
    288             report->confidence = 1.0 - sqrt(ratio);
    289         }
    290     }
    291 
    292     delete[] correlations;
    293     delete[] harmonicSums;
    294     return 0;
    295 }
    296 
    297 class AudioRecording
    298 {
    299 public:
    300     AudioRecording() {
    301     }
    302     ~AudioRecording() {
    303         delete[] mData;
    304     }
    305 
    306     void allocate(int maxFrames) {
    307         delete[] mData;
    308         mData = new float[maxFrames];
    309         mMaxFrames = maxFrames;
    310     }
    311 
    312     // Write SHORT data from the first channel.
    313     int32_t write(int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
    314         // stop at end of buffer
    315         if ((mFrameCounter + numFrames) > mMaxFrames) {
    316             numFrames = mMaxFrames - mFrameCounter;
    317         }
    318         for (int i = 0; i < numFrames; i++) {
    319             mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
    320         }
    321         return numFrames;
    322     }
    323 
    324     // Write FLOAT data from the first channel.
    325     int32_t write(float *inputData, int32_t inputChannelCount, int32_t numFrames) {
    326         // stop at end of buffer
    327         if ((mFrameCounter + numFrames) > mMaxFrames) {
    328             numFrames = mMaxFrames - mFrameCounter;
    329         }
    330         for (int i = 0; i < numFrames; i++) {
    331             mData[mFrameCounter++] = inputData[i * inputChannelCount];
    332         }
    333         return numFrames;
    334     }
    335 
    336     int32_t size() {
    337         return mFrameCounter;
    338     }
    339 
    340     float *getData() {
    341         return mData;
    342     }
    343 
    344     void setSampleRate(int32_t sampleRate) {
    345         mSampleRate = sampleRate;
    346     }
    347 
    348     int32_t getSampleRate() {
    349         return mSampleRate;
    350     }
    351 
    352     int save(const char *fileName, bool writeShorts = true) {
    353         SNDFILE *sndFile = nullptr;
    354         int written = 0;
    355         SF_INFO info = {
    356                 .frames = mFrameCounter,
    357                 .samplerate = mSampleRate,
    358                 .channels = 1,
    359                 .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT)
    360         };
    361 
    362         sndFile = sf_open(fileName, SFM_WRITE, &info);
    363         if (sndFile == nullptr) {
    364             printf("AudioRecording::save(%s) failed to open file\n", fileName);
    365             return -errno;
    366         }
    367 
    368         written = sf_writef_float(sndFile, mData, mFrameCounter);
    369 
    370         sf_close(sndFile);
    371         return written;
    372     }
    373 
    374     int load(const char *fileName) {
    375         SNDFILE *sndFile = nullptr;
    376         SF_INFO info;
    377 
    378         sndFile = sf_open(fileName, SFM_READ, &info);
    379         if (sndFile == nullptr) {
    380             printf("AudioRecording::load(%s) failed to open file\n", fileName);
    381             return -errno;
    382         }
    383 
    384         assert(info.channels == 1);
    385         assert(info.format == SF_FORMAT_FLOAT);
    386 
    387         setSampleRate(info.samplerate);
    388         allocate(info.frames);
    389         mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
    390 
    391         sf_close(sndFile);
    392         return mFrameCounter;
    393     }
    394 
    395     /**
    396      * Square the samples so they are all positive and so the peaks are emphasized.
    397      */
    398     void square() {
    399         for (int i = 0; i < mFrameCounter; i++) {
    400             const float sample = mData[i];
    401             mData[i] = sample * sample;
    402         }
    403     }
    404 
    405     /**
    406      * Low pass filter the recording using a simple FIR filter.
    407      * Note that the lowpass filter cutoff tracks the sample rate.
    408      * That is OK because the impulse width is a fixed number of samples.
    409      */
    410     void lowPassFilter() {
    411         for (int i = 0; i < mFrameCounter; i++) {
    412             mData[i] = mLowPassFilter.filter(mData[i]);
    413         }
    414     }
    415 
    416     /**
    417      * Remove DC offset using a one-pole one-zero IIR filter.
    418      */
    419     void dcBlocker() {
    420         const float R = 0.996; // narrow notch at zero Hz
    421         float x1 = 0.0;
    422         float y1 = 0.0;
    423         for (int i = 0; i < mFrameCounter; i++) {
    424             const float x = mData[i];
    425             const float y = x - x1 + (R * y1);
    426             mData[i] = y;
    427             y1 = y;
    428             x1 = x;
    429         }
    430     }
    431 
    432 private:
    433     float        *mData = nullptr;
    434     int32_t       mFrameCounter = 0;
    435     int32_t       mMaxFrames = 0;
    436     int32_t       mSampleRate = kDefaultSampleRate; // common default
    437     LowPassFilter mLowPassFilter;
    438 };
    439 
    440 // ====================================================================================
    441 class LoopbackProcessor {
    442 public:
    443     virtual ~LoopbackProcessor() = default;
    444 
    445 
    446     enum process_result {
    447         PROCESS_RESULT_OK,
    448         PROCESS_RESULT_GLITCH
    449     };
    450 
    451     virtual void reset() {}
    452 
    453     virtual process_result process(float *inputData, int inputChannelCount,
    454                  float *outputData, int outputChannelCount,
    455                  int numFrames) = 0;
    456 
    457 
    458     virtual void report() = 0;
    459 
    460     virtual void printStatus() {};
    461 
    462     int32_t getResult() {
    463         return mResult;
    464     }
    465 
    466     void setResult(int32_t result) {
    467         mResult = result;
    468     }
    469 
    470     virtual bool isDone() {
    471         return false;
    472     }
    473 
    474     virtual int save(const char *fileName) {
    475         (void) fileName;
    476         return AAUDIO_ERROR_UNIMPLEMENTED;
    477     }
    478 
    479     virtual int load(const char *fileName) {
    480         (void) fileName;
    481         return AAUDIO_ERROR_UNIMPLEMENTED;
    482     }
    483 
    484     virtual void setSampleRate(int32_t sampleRate) {
    485         mSampleRate = sampleRate;
    486     }
    487 
    488     int32_t getSampleRate() {
    489         return mSampleRate;
    490     }
    491 
    492     // Measure peak amplitude of buffer.
    493     static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
    494         float peak = 0.0f;
    495         for (int i = 0; i < numFrames; i++) {
    496             const float pos = fabs(*inputData);
    497             if (pos > peak) {
    498                 peak = pos;
    499             }
    500             inputData += inputChannelCount;
    501         }
    502         return peak;
    503     }
    504 
    505 
    506 private:
    507     int32_t mSampleRate = kDefaultSampleRate;
    508     int32_t mResult = 0;
    509 };
    510 
    511 class PeakDetector {
    512 public:
    513     float process(float input) {
    514         float output = mPrevious * mDecay;
    515         if (input > output) {
    516             output = input;
    517         }
    518         mPrevious = output;
    519         return output;
    520     }
    521 
    522 private:
    523     float  mDecay = 0.99f;
    524     float  mPrevious = 0.0f;
    525 };
    526 
    527 // ====================================================================================
    528 /**
    529  * Measure latency given a loopback stream data.
    530  * Uses a state machine to cycle through various stages including:
    531  *
    532  */
    533 class EchoAnalyzer : public LoopbackProcessor {
    534 public:
    535 
    536     EchoAnalyzer() : LoopbackProcessor() {
    537         mAudioRecording.allocate(2 * getSampleRate());
    538         mAudioRecording.setSampleRate(getSampleRate());
    539     }
    540 
    541     void setSampleRate(int32_t sampleRate) override {
    542         LoopbackProcessor::setSampleRate(sampleRate);
    543         mAudioRecording.setSampleRate(sampleRate);
    544     }
    545 
    546     void reset() override {
    547         mDownCounter = getSampleRate() / 2;
    548         mLoopCounter = 0;
    549         mMeasuredLoopGain = 0.0f;
    550         mEchoGain = 1.0f;
    551         mState = STATE_INITIAL_SILENCE;
    552     }
    553 
    554     virtual bool isDone() {
    555         return mState == STATE_DONE || mState == STATE_FAILED;
    556     }
    557 
    558     void setGain(float gain) {
    559         mEchoGain = gain;
    560     }
    561 
    562     float getGain() {
    563         return mEchoGain;
    564     }
    565 
    566     bool testLowPassFilter() {
    567         LowPassFilter filter;
    568         return filter.test();
    569     }
    570 
    571     void report() override {
    572         printf("EchoAnalyzer ---------------\n");
    573         if (getResult() != 0) {
    574             printf(LOOPBACK_RESULT_TAG "result          = %d\n", getResult());
    575             return;
    576         }
    577 
    578         // printf("LowPassFilter test %s\n", testLowPassFilter() ? "PASSED" : "FAILED");
    579 
    580         printf(LOOPBACK_RESULT_TAG "measured.gain          = %8f\n", mMeasuredLoopGain);
    581         printf(LOOPBACK_RESULT_TAG "echo.gain              = %8f\n", mEchoGain);
    582         printf(LOOPBACK_RESULT_TAG "test.state             = %8d\n", mState);
    583         printf(LOOPBACK_RESULT_TAG "test.state.name        = %8s\n", convertStateToText(mState));
    584 
    585         if (mState == STATE_WAITING_FOR_SILENCE) {
    586             printf("WARNING - Stuck waiting for silence. Input may be too noisy!\n");
    587             setResult(ERROR_NOISY);
    588         } else if (mMeasuredLoopGain >= 0.9999) {
    589             printf("   ERROR - clipping, turn down volume slightly\n");
    590             setResult(ERROR_CLIPPING);
    591         } else if (mState != STATE_DONE && mState != STATE_GATHERING_ECHOS) {
    592             printf("WARNING - Bad state. Check volume on device.\n");
    593             setResult(ERROR_INVALID_STATE);
    594         } else {
    595             // Cleanup the signal to improve the auto-correlation.
    596             mAudioRecording.dcBlocker();
    597             mAudioRecording.square();
    598             mAudioRecording.lowPassFilter();
    599 
    600             printf("Please wait several seconds for auto-correlation to complete.\n");
    601             measureLatencyFromEchos(mAudioRecording.getData(),
    602                                     mAudioRecording.size(),
    603                                     getSampleRate(),
    604                                     &mLatencyReport);
    605 
    606             double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
    607                                    / getSampleRate();
    608             printf(LOOPBACK_RESULT_TAG "latency.frames         = %8.2f\n",
    609                    mLatencyReport.latencyInFrames);
    610             printf(LOOPBACK_RESULT_TAG "latency.msec           = %8.2f\n",
    611                    latencyMillis);
    612             printf(LOOPBACK_RESULT_TAG "latency.confidence     = %8.6f\n",
    613                    mLatencyReport.confidence);
    614             if (mLatencyReport.confidence < kMinimumConfidence) {
    615                 printf("   ERROR - confidence too low!\n");
    616                 setResult(ERROR_CONFIDENCE);
    617             }
    618         }
    619     }
    620 
    621     void printStatus() override {
    622         printf("st = %d, echo gain = %f ", mState, mEchoGain);
    623     }
    624 
    625     void sendImpulses(float *outputData, int outputChannelCount, int numFrames) {
    626         while (numFrames-- > 0) {
    627             float sample = s_Impulse[mSampleIndex++];
    628             if (mSampleIndex >= kImpulseSizeInFrames) {
    629                 mSampleIndex = 0;
    630             }
    631 
    632             *outputData = sample;
    633             outputData += outputChannelCount;
    634         }
    635     }
    636 
    637     void sendOneImpulse(float *outputData, int outputChannelCount) {
    638         mSampleIndex = 0;
    639         sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
    640     }
    641 
    642     // @return number of frames for a typical block of processing
    643     int32_t getBlockFrames() {
    644         return getSampleRate() / 8;
    645     }
    646 
    647     process_result process(float *inputData, int inputChannelCount,
    648                  float *outputData, int outputChannelCount,
    649                  int numFrames) override {
    650         int channelsValid = std::min(inputChannelCount, outputChannelCount);
    651         float peak = 0.0f;
    652         int numWritten;
    653         int numSamples;
    654 
    655         echo_state nextState = mState;
    656 
    657         switch (mState) {
    658             case STATE_INITIAL_SILENCE:
    659                 // Output silence at the beginning.
    660                 numSamples = numFrames * outputChannelCount;
    661                 for (int i = 0; i < numSamples; i++) {
    662                     outputData[i] = 0;
    663                 }
    664                 mDownCounter -= numFrames;
    665                 if (mDownCounter <= 0) {
    666                     nextState = STATE_MEASURING_GAIN;
    667                     //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
    668                     mDownCounter = getBlockFrames() * 2;
    669                 }
    670                 break;
    671 
    672             case STATE_MEASURING_GAIN:
    673                 sendImpulses(outputData, outputChannelCount, numFrames);
    674                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    675                 // If we get several in a row then go to next state.
    676                 if (peak > mPulseThreshold) {
    677                     mDownCounter -= numFrames;
    678                     if (mDownCounter <= 0) {
    679                         //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
    680                         //       mLoopCounter, peak);
    681                         mDownCounter = getBlockFrames();
    682                         mMeasuredLoopGain = peak;  // assumes original pulse amplitude is one
    683                         mSilenceThreshold = peak * 0.1; // scale silence to measured pulse
    684                         // Calculate gain that will give us a nice decaying echo.
    685                         mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
    686                         if (mEchoGain > kMaxEchoGain) {
    687                             printf("ERROR - loop gain too low. Increase the volume.\n");
    688                             nextState = STATE_FAILED;
    689                         } else {
    690                             nextState = STATE_WAITING_FOR_SILENCE;
    691                         }
    692                     }
    693                 } else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
    694                     mDownCounter = getBlockFrames();
    695                 }
    696                 break;
    697 
    698             case STATE_WAITING_FOR_SILENCE:
    699                 // Output silence and wait for the echos to die down.
    700                 numSamples = numFrames * outputChannelCount;
    701                 for (int i = 0; i < numSamples; i++) {
    702                     outputData[i] = 0;
    703                 }
    704                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    705                 // If we get several in a row then go to next state.
    706                 if (peak < mSilenceThreshold) {
    707                     mDownCounter -= numFrames;
    708                     if (mDownCounter <= 0) {
    709                         nextState = STATE_SENDING_PULSE;
    710                         //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
    711                         mDownCounter = getBlockFrames();
    712                     }
    713                 } else {
    714                     mDownCounter = getBlockFrames();
    715                 }
    716                 break;
    717 
    718             case STATE_SENDING_PULSE:
    719                 mAudioRecording.write(inputData, inputChannelCount, numFrames);
    720                 sendOneImpulse(outputData, outputChannelCount);
    721                 nextState = STATE_GATHERING_ECHOS;
    722                 //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter);
    723                 break;
    724 
    725             case STATE_GATHERING_ECHOS:
    726                 numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames);
    727                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    728                 if (peak > mMeasuredLoopGain) {
    729                     mMeasuredLoopGain = peak;  // AGC might be raising gain so adjust it on the fly.
    730                     // Recalculate gain that will give us a nice decaying echo.
    731                     mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
    732                 }
    733                 // Echo input to output.
    734                 for (int i = 0; i < numFrames; i++) {
    735                     int ic;
    736                     for (ic = 0; ic < channelsValid; ic++) {
    737                         outputData[ic] = inputData[ic] * mEchoGain;
    738                     }
    739                     for (; ic < outputChannelCount; ic++) {
    740                         outputData[ic] = 0;
    741                     }
    742                     inputData += inputChannelCount;
    743                     outputData += outputChannelCount;
    744                 }
    745                 if (numWritten  < numFrames) {
    746                     nextState = STATE_DONE;
    747                 }
    748                 break;
    749 
    750             case STATE_DONE:
    751             case STATE_FAILED:
    752             default:
    753                 break;
    754         }
    755 
    756         mState = nextState;
    757         mLoopCounter++;
    758         return PROCESS_RESULT_OK;
    759     }
    760 
    761     int save(const char *fileName) override {
    762         return mAudioRecording.save(fileName);
    763     }
    764 
    765     int load(const char *fileName) override {
    766         int result = mAudioRecording.load(fileName);
    767         setSampleRate(mAudioRecording.getSampleRate());
    768         mState = STATE_DONE;
    769         return result;
    770     }
    771 
    772 private:
    773 
    774     enum error_code {
    775         ERROR_OK = 0,
    776         ERROR_NOISY = -99,
    777         ERROR_CLIPPING,
    778         ERROR_CONFIDENCE,
    779         ERROR_INVALID_STATE
    780     };
    781 
    782     enum echo_state {
    783         STATE_INITIAL_SILENCE,
    784         STATE_MEASURING_GAIN,
    785         STATE_WAITING_FOR_SILENCE,
    786         STATE_SENDING_PULSE,
    787         STATE_GATHERING_ECHOS,
    788         STATE_DONE,
    789         STATE_FAILED
    790     };
    791 
    792     const char *convertStateToText(echo_state state) {
    793         const char *result = "Unknown";
    794         switch(state) {
    795             case STATE_INITIAL_SILENCE:
    796                 result = "INIT";
    797                 break;
    798             case STATE_MEASURING_GAIN:
    799                 result = "GAIN";
    800                 break;
    801             case STATE_WAITING_FOR_SILENCE:
    802                 result = "SILENCE";
    803                 break;
    804             case STATE_SENDING_PULSE:
    805                 result = "PULSE";
    806                 break;
    807             case STATE_GATHERING_ECHOS:
    808                 result = "ECHOS";
    809                 break;
    810             case STATE_DONE:
    811                 result = "DONE";
    812                 break;
    813             case STATE_FAILED:
    814                 result = "FAILED";
    815                 break;
    816         }
    817         return result;
    818     }
    819 
    820 
    821     int32_t         mDownCounter = 500;
    822     int32_t         mLoopCounter = 0;
    823     int32_t         mSampleIndex = 0;
    824     float           mPulseThreshold = 0.02f;
    825     float           mSilenceThreshold = 0.002f;
    826     float           mMeasuredLoopGain = 0.0f;
    827     float           mDesiredEchoGain = 0.95f;
    828     float           mEchoGain = 1.0f;
    829     echo_state      mState = STATE_INITIAL_SILENCE;
    830 
    831     AudioRecording  mAudioRecording; // contains only the input after the gain detection burst
    832     LatencyReport   mLatencyReport;
    833     // PeakDetector    mPeakDetector;
    834 };
    835 
    836 
    837 // ====================================================================================
    838 /**
    839  * Output a steady sinewave and analyze the return signal.
    840  *
    841  * Use a cosine transform to measure the predicted magnitude and relative phase of the
    842  * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
    843  */
    844 class SineAnalyzer : public LoopbackProcessor {
    845 public:
    846 
    847     void report() override {
    848         printf("SineAnalyzer ------------------\n");
    849         printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %8f\n", mPeakAmplitude);
    850         printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %8f\n", mMagnitude);
    851         printf(LOOPBACK_RESULT_TAG "peak.noise         = %8f\n", mPeakNoise);
    852         printf(LOOPBACK_RESULT_TAG "rms.noise          = %8f\n", mRootMeanSquareNoise);
    853         float amplitudeRatio = mMagnitude / mPeakNoise;
    854         float signalToNoise = amplitudeRatio * amplitudeRatio;
    855         printf(LOOPBACK_RESULT_TAG "signal.to.noise    = %8.2f\n", signalToNoise);
    856         float signalToNoiseDB = 10.0 * log(signalToNoise);
    857         printf(LOOPBACK_RESULT_TAG "signal.to.noise.db = %8.2f\n", signalToNoiseDB);
    858         if (signalToNoiseDB < MIN_SNRATIO_DB) {
    859             printf("ERROR - signal to noise ratio is too low! < %d dB. Adjust volume.\n", MIN_SNRATIO_DB);
    860             setResult(ERROR_NOISY);
    861         }
    862         printf(LOOPBACK_RESULT_TAG "frames.accumulated = %8d\n", mFramesAccumulated);
    863         printf(LOOPBACK_RESULT_TAG "sine.period        = %8d\n", mSinePeriod);
    864         printf(LOOPBACK_RESULT_TAG "test.state         = %8d\n", mState);
    865         printf(LOOPBACK_RESULT_TAG "frame.count        = %8d\n", mFrameCounter);
    866         // Did we ever get a lock?
    867         bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
    868         if (!gotLock) {
    869             printf("ERROR - failed to lock on reference sine tone\n");
    870             setResult(ERROR_NO_LOCK);
    871         } else {
    872             // Only print if meaningful.
    873             printf(LOOPBACK_RESULT_TAG "glitch.count       = %8d\n", mGlitchCount);
    874             printf(LOOPBACK_RESULT_TAG "max.glitch         = %8f\n", mMaxGlitchDelta);
    875             if (mGlitchCount > 0) {
    876                 printf("ERROR - number of glitches > 0\n");
    877                 setResult(ERROR_GLITCHES);
    878             }
    879         }
    880     }
    881 
    882     void printStatus() override {
    883         printf("st = %d, #gl = %3d,", mState, mGlitchCount);
    884     }
    885 
    886     double calculateMagnitude(double *phasePtr = NULL) {
    887         if (mFramesAccumulated == 0) {
    888             return 0.0;
    889         }
    890         double sinMean = mSinAccumulator / mFramesAccumulated;
    891         double cosMean = mCosAccumulator / mFramesAccumulated;
    892         double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
    893         if( phasePtr != NULL )
    894         {
    895             double phase = M_PI_2 - atan2( sinMean, cosMean );
    896             *phasePtr = phase;
    897         }
    898         return magnitude;
    899     }
    900 
    901     /**
    902      * @param inputData contains microphone data with sine signal feedback
    903      * @param outputData contains the reference sine wave
    904      */
    905     process_result process(float *inputData, int inputChannelCount,
    906                  float *outputData, int outputChannelCount,
    907                  int numFrames) override {
    908         process_result result = PROCESS_RESULT_OK;
    909         mProcessCount++;
    910 
    911         float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    912         if (peak > mPeakAmplitude) {
    913             mPeakAmplitude = peak;
    914         }
    915 
    916         for (int i = 0; i < numFrames; i++) {
    917             bool sineEnabled = true;
    918             float sample = inputData[i * inputChannelCount];
    919 
    920             float sinOut = sinf(mPhase);
    921 
    922             switch (mState) {
    923                 case STATE_IDLE:
    924                     sineEnabled = false;
    925                     mDownCounter--;
    926                     if (mDownCounter <= 0) {
    927                         mState = STATE_MEASURE_NOISE;
    928                         mDownCounter = NOISE_FRAME_COUNT;
    929                     }
    930                     break;
    931                 case STATE_MEASURE_NOISE:
    932                     sineEnabled = false;
    933                     mPeakNoise = std::max(abs(sample), mPeakNoise);
    934                     mNoiseSumSquared += sample * sample;
    935                     mDownCounter--;
    936                     if (mDownCounter <= 0) {
    937                         mState = STATE_WAITING_FOR_SIGNAL;
    938                         mRootMeanSquareNoise = sqrt(mNoiseSumSquared / NOISE_FRAME_COUNT);
    939                         mTolerance = std::max(MIN_TOLERANCE, mPeakNoise * 2.0f);
    940                         mPhase = 0.0; // prevent spike at start
    941                     }
    942                     break;
    943 
    944                 case STATE_IMMUNE:
    945                     mDownCounter--;
    946                     if (mDownCounter <= 0) {
    947                         mState = STATE_WAITING_FOR_SIGNAL;
    948                     }
    949                     break;
    950 
    951                 case STATE_WAITING_FOR_SIGNAL:
    952                     if (peak > mThreshold) {
    953                         mState = STATE_WAITING_FOR_LOCK;
    954                         //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
    955                         resetAccumulator();
    956                     }
    957                     break;
    958 
    959                 case STATE_WAITING_FOR_LOCK:
    960                     mSinAccumulator += sample * sinOut;
    961                     mCosAccumulator += sample * cosf(mPhase);
    962                     mFramesAccumulated++;
    963                     // Must be a multiple of the period or the calculation will not be accurate.
    964                     if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
    965                         mPhaseOffset = 0.0;
    966                         mMagnitude = calculateMagnitude(&mPhaseOffset);
    967                         if (mMagnitude > mThreshold) {
    968                             if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) {
    969                                 mState = STATE_LOCKED;
    970                                 //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter);
    971                             }
    972                             mPreviousPhaseOffset = mPhaseOffset;
    973                         }
    974                         resetAccumulator();
    975                     }
    976                     break;
    977 
    978                 case STATE_LOCKED: {
    979                     // Predict next sine value
    980                     float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude;
    981                     // printf("    predicted = %f, actual = %f\n", predicted, sample);
    982 
    983                     float diff = predicted - sample;
    984                     float absDiff = fabs(diff);
    985                     mMaxGlitchDelta = std::max(mMaxGlitchDelta, absDiff);
    986                     if (absDiff > mTolerance) {
    987                         mGlitchCount++;
    988                         result = PROCESS_RESULT_GLITCH;
    989                         //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
    990                         //       mFrameCounter, mGlitchCount, predicted, sample);
    991                         mState = STATE_IMMUNE;
    992                         mDownCounter = mSinePeriod * PERIODS_IMMUNE;
    993                     }
    994 
    995                     // Track incoming signal and slowly adjust magnitude to account
    996                     // for drift in the DRC or AGC.
    997                     mSinAccumulator += sample * sinOut;
    998                     mCosAccumulator += sample * cosf(mPhase);
    999                     mFramesAccumulated++;
   1000                     // Must be a multiple of the period or the calculation will not be accurate.
   1001                     if (mFramesAccumulated == mSinePeriod) {
   1002                         const double coefficient = 0.1;
   1003                         double phaseOffset = 0.0;
   1004                         double magnitude = calculateMagnitude(&phaseOffset);
   1005                         // One pole averaging filter.
   1006                         mMagnitude = (mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient);
   1007                         resetAccumulator();
   1008                     }
   1009                 } break;
   1010             }
   1011 
   1012             float output = 0.0f;
   1013             // Output sine wave so we can measure it.
   1014             if (sineEnabled) {
   1015                 output = (sinOut * mOutputAmplitude)
   1016                          + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
   1017                 // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut,  mPhaseIncrement);
   1018                 // advance and wrap phase
   1019                 mPhase += mPhaseIncrement;
   1020                 if (mPhase > M_PI) {
   1021                     mPhase -= (2.0 * M_PI);
   1022                 }
   1023             }
   1024             outputData[i * outputChannelCount] = output;
   1025 
   1026 
   1027             mFrameCounter++;
   1028         }
   1029         return result;
   1030     }
   1031 
   1032     void resetAccumulator() {
   1033         mFramesAccumulated = 0;
   1034         mSinAccumulator = 0.0;
   1035         mCosAccumulator = 0.0;
   1036     }
   1037 
   1038     void reset() override {
   1039         mGlitchCount = 0;
   1040         mState = STATE_IDLE;
   1041         mDownCounter = IDLE_FRAME_COUNT;
   1042         mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
   1043         printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
   1044         resetAccumulator();
   1045         mProcessCount = 0;
   1046         mPeakNoise = 0.0f;
   1047         mNoiseSumSquared = 0.0;
   1048         mRootMeanSquareNoise = 0.0;
   1049         mPhase = 0.0f;
   1050         mMaxGlitchDelta = 0.0;
   1051     }
   1052 
   1053 private:
   1054 
   1055     enum error_code {
   1056         OK,
   1057         ERROR_NO_LOCK = -80,
   1058         ERROR_GLITCHES,
   1059         ERROR_NOISY
   1060     };
   1061 
   1062     enum sine_state_t {
   1063         STATE_IDLE,
   1064         STATE_MEASURE_NOISE,
   1065         STATE_IMMUNE,
   1066         STATE_WAITING_FOR_SIGNAL,
   1067         STATE_WAITING_FOR_LOCK,
   1068         STATE_LOCKED
   1069     };
   1070 
   1071     enum constants {
   1072         // Arbitrary durations, assuming 48000 Hz
   1073         IDLE_FRAME_COUNT = 48 * 100,
   1074         NOISE_FRAME_COUNT = 48 * 600,
   1075         PERIODS_NEEDED_FOR_LOCK = 8,
   1076         PERIODS_IMMUNE = 2,
   1077         MIN_SNRATIO_DB = 65
   1078     };
   1079 
   1080     static constexpr float MIN_TOLERANCE = 0.01;
   1081 
   1082     int     mSinePeriod = 79;
   1083     double  mPhaseIncrement = 0.0;
   1084     double  mPhase = 0.0;
   1085     double  mPhaseOffset = 0.0;
   1086     double  mPreviousPhaseOffset = 0.0;
   1087     double  mMagnitude = 0.0;
   1088     double  mThreshold = 0.005;
   1089     double  mTolerance = MIN_TOLERANCE;
   1090     int32_t mFramesAccumulated = 0;
   1091     int32_t mProcessCount = 0;
   1092     double  mSinAccumulator = 0.0;
   1093     double  mCosAccumulator = 0.0;
   1094     float   mMaxGlitchDelta = 0.0f;
   1095     int32_t mGlitchCount = 0;
   1096     double  mPeakAmplitude = 0.0;
   1097     int     mDownCounter = IDLE_FRAME_COUNT;
   1098     int32_t mFrameCounter = 0;
   1099     float   mOutputAmplitude = 0.75;
   1100 
   1101     // measure background noise
   1102     float   mPeakNoise = 0.0f;
   1103     double  mNoiseSumSquared = 0.0;
   1104     double  mRootMeanSquareNoise = 0.0;
   1105 
   1106     PseudoRandom  mWhiteNoise;
   1107     float   mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
   1108 
   1109     sine_state_t  mState = STATE_IDLE;
   1110 };
   1111 
   1112 #undef LOOPBACK_RESULT_TAG
   1113 
   1114 #endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */
   1115