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 #define LOOPBACK_SAMPLE_RATE     48000
     38 
     39 #define MILLIS_PER_SECOND        1000
     40 
     41 #define MAX_ZEROTH_PARTIAL_BINS  40
     42 constexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy
     43 
     44 // A narrow impulse seems to have better immunity against over estimating the
     45 // latency due to detecting subharmonics by the auto-correlator.
     46 static const float s_Impulse[] = {
     47         0.0f, 0.0f, 0.0f, 0.0f, 0.3f, // silence on each side of the impulse
     48         0.99f, 0.0f, -0.99f, // bipolar with one zero crossing in middle
     49         -0.3f, 0.0f, 0.0f, 0.0f, 0.0f
     50 };
     51 
     52 constexpr int32_t kImpulseSizeInFrames = (int32_t)(sizeof(s_Impulse) / sizeof(s_Impulse[0]));
     53 
     54 class PseudoRandom {
     55 public:
     56     PseudoRandom() {}
     57     PseudoRandom(int64_t seed)
     58             :    mSeed(seed)
     59     {}
     60 
     61     /**
     62      * Returns the next random double from -1.0 to 1.0
     63      *
     64      * @return value from -1.0 to 1.0
     65      */
     66      double nextRandomDouble() {
     67         return nextRandomInteger() * (0.5 / (((int32_t)1) << 30));
     68     }
     69 
     70     /** Calculate random 32 bit number using linear-congruential method. */
     71     int32_t nextRandomInteger() {
     72         // Use values for 64-bit sequence from MMIX by Donald Knuth.
     73         mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407;
     74         return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence.
     75     }
     76 
     77 private:
     78     int64_t mSeed = 99887766;
     79 };
     80 
     81 static double calculateCorrelation(const float *a,
     82                                    const float *b,
     83                                    int windowSize)
     84 {
     85     double correlation = 0.0;
     86     double sumProducts = 0.0;
     87     double sumSquares = 0.0;
     88 
     89     // Correlate a against b.
     90     for (int i = 0; i < windowSize; i++) {
     91         float s1 = a[i];
     92         float s2 = b[i];
     93         // Use a normalized cross-correlation.
     94         sumProducts += s1 * s2;
     95         sumSquares += ((s1 * s1) + (s2 * s2));
     96     }
     97 
     98     if (sumSquares >= 0.00000001) {
     99         correlation = (float) (2.0 * sumProducts / sumSquares);
    100     }
    101     return correlation;
    102 }
    103 
    104 static int calculateCorrelations(const float *haystack, int haystackSize,
    105                                  const float *needle, int needleSize,
    106                                  float *results, int resultSize)
    107 {
    108     int maxCorrelations = haystackSize - needleSize;
    109     int numCorrelations = std::min(maxCorrelations, resultSize);
    110 
    111     for (int ic = 0; ic < numCorrelations; ic++) {
    112         double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
    113         results[ic] = correlation;
    114     }
    115 
    116     return numCorrelations;
    117 }
    118 
    119 /*==========================================================================================*/
    120 /**
    121  * Scan until we get a correlation of a single scan that goes over the tolerance level,
    122  * peaks then drops back down.
    123  */
    124 static double findFirstMatch(const float *haystack, int haystackSize,
    125                              const float *needle, int needleSize, double threshold  )
    126 {
    127     int ic;
    128     // How many correlations can we calculate?
    129     int numCorrelations = haystackSize - needleSize;
    130     double maxCorrelation = 0.0;
    131     int peakIndex = -1;
    132     double location = -1.0;
    133     const double backThresholdScaler = 0.5;
    134 
    135     for (ic = 0; ic < numCorrelations; ic++) {
    136         double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
    137 
    138         if( (correlation > maxCorrelation) ) {
    139             maxCorrelation = correlation;
    140             peakIndex = ic;
    141         }
    142 
    143         //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
    144         //    ic, correlation, maxSum );
    145         // Are we past what we were looking for?
    146         if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) {
    147             location = peakIndex;
    148             break;
    149         }
    150     }
    151 
    152     return location;
    153 }
    154 
    155 typedef struct LatencyReport_s {
    156     double latencyInFrames;
    157     double confidence;
    158 } LatencyReport;
    159 
    160 // Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
    161 // Using first echo instead of the original impulse for a better match.
    162 static int measureLatencyFromEchos(const float *haystack, int haystackSize,
    163                             const float *needle, int needleSize,
    164                             LatencyReport *report) {
    165     const double threshold = 0.1;
    166     printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n",
    167            haystackSize, needleSize);
    168 
    169     // Find first peak
    170     int first = (int) (findFirstMatch(haystack,
    171                                       haystackSize,
    172                                       needle,
    173                                       needleSize,
    174                                       threshold) + 0.5);
    175 
    176     // Use first echo as the needle for the other echos because
    177     // it will be more similar.
    178     needle = &haystack[first];
    179     int again = (int) (findFirstMatch(haystack,
    180                                       haystackSize,
    181                                       needle,
    182                                       needleSize,
    183                                       threshold) + 0.5);
    184 
    185     printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again);
    186     first = again;
    187 
    188     // Allocate results array
    189     int remaining = haystackSize - first;
    190     const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value
    191     int numCorrelations = std::min(remaining, maxReasonableLatencyFrames);
    192     float *correlations = new float[numCorrelations];
    193     float *harmonicSums = new float[numCorrelations](); // set to zero
    194 
    195     // Generate correlation for every position.
    196     numCorrelations = calculateCorrelations(&haystack[first], remaining,
    197                                             needle, needleSize,
    198                                             correlations, numCorrelations);
    199 
    200     // Add higher harmonics mapped onto lower harmonics.
    201     // This reinforces the "fundamental" echo.
    202     const int numEchoes = 10;
    203     for (int partial = 1; partial < numEchoes; partial++) {
    204         for (int i = 0; i < numCorrelations; i++) {
    205             harmonicSums[i / partial] += correlations[i] / partial;
    206         }
    207     }
    208 
    209     // Find highest peak in correlation array.
    210     float maxCorrelation = 0.0;
    211     float sumOfPeaks = 0.0;
    212     int peakIndex = 0;
    213     const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
    214     for (int i = skip; i < numCorrelations; i++) {
    215         if (harmonicSums[i] > maxCorrelation) {
    216             maxCorrelation = harmonicSums[i];
    217             sumOfPeaks += maxCorrelation;
    218             peakIndex = i;
    219             printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
    220         }
    221     }
    222 
    223     report->latencyInFrames = peakIndex;
    224     if (sumOfPeaks < 0.0001) {
    225         report->confidence = 0.0;
    226     } else {
    227         report->confidence = maxCorrelation / sumOfPeaks;
    228     }
    229 
    230     delete[] correlations;
    231     delete[] harmonicSums;
    232     return 0;
    233 }
    234 
    235 class AudioRecording
    236 {
    237 public:
    238     AudioRecording() {
    239     }
    240     ~AudioRecording() {
    241         delete[] mData;
    242     }
    243 
    244     void allocate(int maxFrames) {
    245         delete[] mData;
    246         mData = new float[maxFrames];
    247         mMaxFrames = maxFrames;
    248     }
    249 
    250     // Write SHORT data from the first channel.
    251     int write(int16_t *inputData, int inputChannelCount, int numFrames) {
    252         // stop at end of buffer
    253         if ((mFrameCounter + numFrames) > mMaxFrames) {
    254             numFrames = mMaxFrames - mFrameCounter;
    255         }
    256         for (int i = 0; i < numFrames; i++) {
    257             mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
    258         }
    259         return numFrames;
    260     }
    261 
    262     // Write FLOAT data from the first channel.
    263     int write(float *inputData, int inputChannelCount, int numFrames) {
    264         // stop at end of buffer
    265         if ((mFrameCounter + numFrames) > mMaxFrames) {
    266             numFrames = mMaxFrames - mFrameCounter;
    267         }
    268         for (int i = 0; i < numFrames; i++) {
    269             mData[mFrameCounter++] = inputData[i * inputChannelCount];
    270         }
    271         return numFrames;
    272     }
    273 
    274     int size() {
    275         return mFrameCounter;
    276     }
    277 
    278     float *getData() {
    279         return mData;
    280     }
    281 
    282     void setSampleRate(int32_t sampleRate) {
    283         mSampleRate = sampleRate;
    284     }
    285 
    286     int32_t getSampleRate() {
    287         return mSampleRate;
    288     }
    289 
    290     int save(const char *fileName, bool writeShorts = true) {
    291         SNDFILE *sndFile = nullptr;
    292         int written = 0;
    293         SF_INFO info = {
    294                 .frames = mFrameCounter,
    295                 .samplerate = mSampleRate,
    296                 .channels = 1,
    297                 .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT)
    298         };
    299 
    300         sndFile = sf_open(fileName, SFM_WRITE, &info);
    301         if (sndFile == nullptr) {
    302             printf("AudioRecording::save(%s) failed to open file\n", fileName);
    303             return -errno;
    304         }
    305 
    306         written = sf_writef_float(sndFile, mData, mFrameCounter);
    307 
    308         sf_close(sndFile);
    309         return written;
    310     }
    311 
    312     int load(const char *fileName) {
    313         SNDFILE *sndFile = nullptr;
    314         SF_INFO info;
    315 
    316         sndFile = sf_open(fileName, SFM_READ, &info);
    317         if (sndFile == nullptr) {
    318             printf("AudioRecording::load(%s) failed to open file\n", fileName);
    319             return -errno;
    320         }
    321 
    322         assert(info.channels == 1);
    323 
    324         allocate(info.frames);
    325         mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
    326 
    327         sf_close(sndFile);
    328         return mFrameCounter;
    329     }
    330 
    331 private:
    332     float  *mData = nullptr;
    333     int32_t mFrameCounter = 0;
    334     int32_t mMaxFrames = 0;
    335     int32_t mSampleRate = 48000; // common default
    336 };
    337 
    338 // ====================================================================================
    339 class LoopbackProcessor {
    340 public:
    341     virtual ~LoopbackProcessor() = default;
    342 
    343 
    344     virtual void reset() {}
    345 
    346     virtual void process(float *inputData, int inputChannelCount,
    347                  float *outputData, int outputChannelCount,
    348                  int numFrames) = 0;
    349 
    350 
    351     virtual void report() = 0;
    352 
    353     virtual void printStatus() {};
    354 
    355     virtual int getResult() {
    356         return -1;
    357     }
    358 
    359     virtual bool isDone() {
    360         return false;
    361     }
    362 
    363     virtual int save(const char *fileName) {
    364         (void) fileName;
    365         return AAUDIO_ERROR_UNIMPLEMENTED;
    366     }
    367 
    368     virtual int load(const char *fileName) {
    369         (void) fileName;
    370         return AAUDIO_ERROR_UNIMPLEMENTED;
    371     }
    372 
    373     virtual void setSampleRate(int32_t sampleRate) {
    374         mSampleRate = sampleRate;
    375     }
    376 
    377     int32_t getSampleRate() {
    378         return mSampleRate;
    379     }
    380 
    381     // Measure peak amplitude of buffer.
    382     static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
    383         float peak = 0.0f;
    384         for (int i = 0; i < numFrames; i++) {
    385             float pos = fabs(*inputData);
    386             if (pos > peak) {
    387                 peak = pos;
    388             }
    389             inputData += inputChannelCount;
    390         }
    391         return peak;
    392     }
    393 
    394 
    395 private:
    396     int32_t mSampleRate = LOOPBACK_SAMPLE_RATE;
    397 };
    398 
    399 class PeakDetector {
    400 public:
    401     float process(float input) {
    402         float output = mPrevious * mDecay;
    403         if (input > output) {
    404             output = input;
    405         }
    406         mPrevious = output;
    407         return output;
    408     }
    409 
    410 private:
    411     float  mDecay = 0.99f;
    412     float  mPrevious = 0.0f;
    413 };
    414 
    415 
    416 static void printAudioScope(float sample) {
    417     const int maxStars = 80; // arbitrary, fits on one line
    418     char c = '*';
    419     if (sample < -1.0) {
    420         sample = -1.0;
    421         c = '$';
    422     } else if (sample > 1.0) {
    423         sample = 1.0;
    424         c = '$';
    425     }
    426     int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
    427     for (int i = 0; i < numSpaces; i++) {
    428         putchar(' ');
    429     }
    430     printf("%c\n", c);
    431 }
    432 
    433 // ====================================================================================
    434 /**
    435  * Measure latency given a loopback stream data.
    436  * Uses a state machine to cycle through various stages including:
    437  *
    438  */
    439 class EchoAnalyzer : public LoopbackProcessor {
    440 public:
    441 
    442     EchoAnalyzer() : LoopbackProcessor() {
    443         mAudioRecording.allocate(2 * getSampleRate());
    444         mAudioRecording.setSampleRate(getSampleRate());
    445     }
    446 
    447     void setSampleRate(int32_t sampleRate) override {
    448         LoopbackProcessor::setSampleRate(sampleRate);
    449         mAudioRecording.setSampleRate(sampleRate);
    450     }
    451 
    452     void reset() override {
    453         mDownCounter = 200;
    454         mLoopCounter = 0;
    455         mMeasuredLoopGain = 0.0f;
    456         mEchoGain = 1.0f;
    457         mState = STATE_INITIAL_SILENCE;
    458     }
    459 
    460     virtual int getResult() {
    461         return mState == STATE_DONE ? 0 : -1;
    462     }
    463 
    464     virtual bool isDone() {
    465         return mState == STATE_DONE || mState == STATE_FAILED;
    466     }
    467 
    468     void setGain(float gain) {
    469         mEchoGain = gain;
    470     }
    471 
    472     float getGain() {
    473         return mEchoGain;
    474     }
    475 
    476     void report() override {
    477 
    478         printf("EchoAnalyzer ---------------\n");
    479         printf(LOOPBACK_RESULT_TAG "measured.gain          = %f\n", mMeasuredLoopGain);
    480         printf(LOOPBACK_RESULT_TAG "echo.gain              = %f\n", mEchoGain);
    481         printf(LOOPBACK_RESULT_TAG "test.state             = %d\n", mState);
    482         if (mMeasuredLoopGain >= 0.9999) {
    483             printf("   ERROR - clipping, turn down volume slightly\n");
    484         } else {
    485             const float *needle = s_Impulse;
    486             int needleSize = (int) (sizeof(s_Impulse) / sizeof(float));
    487             float *haystack = mAudioRecording.getData();
    488             int haystackSize = mAudioRecording.size();
    489             measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport);
    490             if (mLatencyReport.confidence < 0.01) {
    491                 printf("   ERROR - confidence too low = %f\n", mLatencyReport.confidence);
    492             } else {
    493                 double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
    494                 printf(LOOPBACK_RESULT_TAG "latency.frames        = %8.2f\n", mLatencyReport.latencyInFrames);
    495                 printf(LOOPBACK_RESULT_TAG "latency.msec          = %8.2f\n", latencyMillis);
    496                 printf(LOOPBACK_RESULT_TAG "latency.confidence    = %8.6f\n", mLatencyReport.confidence);
    497             }
    498         }
    499     }
    500 
    501     void printStatus() override {
    502         printf("st = %d, echo gain = %f ", mState, mEchoGain);
    503     }
    504 
    505     void sendImpulses(float *outputData, int outputChannelCount, int numFrames) {
    506         while (numFrames-- > 0) {
    507             float sample = s_Impulse[mSampleIndex++];
    508             if (mSampleIndex >= kImpulseSizeInFrames) {
    509                 mSampleIndex = 0;
    510             }
    511 
    512             *outputData = sample;
    513             outputData += outputChannelCount;
    514         }
    515     }
    516 
    517     void sendOneImpulse(float *outputData, int outputChannelCount) {
    518         mSampleIndex = 0;
    519         sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
    520     }
    521 
    522     void process(float *inputData, int inputChannelCount,
    523                  float *outputData, int outputChannelCount,
    524                  int numFrames) override {
    525         int channelsValid = std::min(inputChannelCount, outputChannelCount);
    526         float peak = 0.0f;
    527         int numWritten;
    528         int numSamples;
    529 
    530         echo_state_t nextState = mState;
    531 
    532         switch (mState) {
    533             case STATE_INITIAL_SILENCE:
    534                 // Output silence at the beginning.
    535                 numSamples = numFrames * outputChannelCount;
    536                 for (int i = 0; i < numSamples; i++) {
    537                     outputData[i] = 0;
    538                 }
    539                 if (mDownCounter-- <= 0) {
    540                     nextState = STATE_MEASURING_GAIN;
    541                     //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
    542                     mDownCounter = 8;
    543                 }
    544                 break;
    545 
    546             case STATE_MEASURING_GAIN:
    547                 sendImpulses(outputData, outputChannelCount, numFrames);
    548                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    549                 // If we get several in a row then go to next state.
    550                 if (peak > mPulseThreshold) {
    551                     if (mDownCounter-- <= 0) {
    552                         //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
    553                         //       mLoopCounter, peak);
    554                         mDownCounter = 8;
    555                         mMeasuredLoopGain = peak;  // assumes original pulse amplitude is one
    556                         // Calculate gain that will give us a nice decaying echo.
    557                         mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
    558                         if (mEchoGain > MAX_ECHO_GAIN) {
    559                             printf("ERROR - loop gain too low. Increase the volume.\n");
    560                             nextState = STATE_FAILED;
    561                         } else {
    562                             nextState = STATE_WAITING_FOR_SILENCE;
    563                         }
    564                     }
    565                 } else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
    566                     mDownCounter = 8;
    567                 }
    568                 break;
    569 
    570             case STATE_WAITING_FOR_SILENCE:
    571                 // Output silence and wait for the echos to die down.
    572                 numSamples = numFrames * outputChannelCount;
    573                 for (int i = 0; i < numSamples; i++) {
    574                     outputData[i] = 0;
    575                 }
    576                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    577                 // If we get several in a row then go to next state.
    578                 if (peak < mSilenceThreshold) {
    579                     if (mDownCounter-- <= 0) {
    580                         nextState = STATE_SENDING_PULSE;
    581                         //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
    582                         mDownCounter = 8;
    583                     }
    584                 } else {
    585                     mDownCounter = 8;
    586                 }
    587                 break;
    588 
    589             case STATE_SENDING_PULSE:
    590                 mAudioRecording.write(inputData, inputChannelCount, numFrames);
    591                 sendOneImpulse(outputData, outputChannelCount);
    592                 nextState = STATE_GATHERING_ECHOS;
    593                 //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter);
    594                 break;
    595 
    596             case STATE_GATHERING_ECHOS:
    597                 numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames);
    598                 peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    599                 if (peak > mMeasuredLoopGain) {
    600                     mMeasuredLoopGain = peak;  // AGC might be raising gain so adjust it on the fly.
    601                     // Recalculate gain that will give us a nice decaying echo.
    602                     mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
    603                 }
    604                 // Echo input to output.
    605                 for (int i = 0; i < numFrames; i++) {
    606                     int ic;
    607                     for (ic = 0; ic < channelsValid; ic++) {
    608                         outputData[ic] = inputData[ic] * mEchoGain;
    609                     }
    610                     for (; ic < outputChannelCount; ic++) {
    611                         outputData[ic] = 0;
    612                     }
    613                     inputData += inputChannelCount;
    614                     outputData += outputChannelCount;
    615                 }
    616                 if (numWritten  < numFrames) {
    617                     nextState = STATE_DONE;
    618                     //printf("%5d: switch to STATE_DONE\n", mLoopCounter);
    619                 }
    620                 break;
    621 
    622             case STATE_DONE:
    623             default:
    624                 break;
    625         }
    626 
    627         mState = nextState;
    628         mLoopCounter++;
    629     }
    630 
    631     int save(const char *fileName) override {
    632         return mAudioRecording.save(fileName);
    633     }
    634 
    635     int load(const char *fileName) override {
    636         return mAudioRecording.load(fileName);
    637     }
    638 
    639 private:
    640 
    641     enum echo_state_t {
    642         STATE_INITIAL_SILENCE,
    643         STATE_MEASURING_GAIN,
    644         STATE_WAITING_FOR_SILENCE,
    645         STATE_SENDING_PULSE,
    646         STATE_GATHERING_ECHOS,
    647         STATE_DONE,
    648         STATE_FAILED
    649     };
    650 
    651     int32_t         mDownCounter = 500;
    652     int32_t         mLoopCounter = 0;
    653     int32_t         mSampleIndex = 0;
    654     float           mPulseThreshold = 0.02f;
    655     float           mSilenceThreshold = 0.002f;
    656     float           mMeasuredLoopGain = 0.0f;
    657     float           mDesiredEchoGain = 0.95f;
    658     float           mEchoGain = 1.0f;
    659     echo_state_t    mState = STATE_INITIAL_SILENCE;
    660 
    661     AudioRecording  mAudioRecording; // contains only the input after the gain detection burst
    662     LatencyReport   mLatencyReport;
    663     // PeakDetector    mPeakDetector;
    664 };
    665 
    666 
    667 // ====================================================================================
    668 /**
    669  * Output a steady sinewave and analyze the return signal.
    670  *
    671  * Use a cosine transform to measure the predicted magnitude and relative phase of the
    672  * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
    673  */
    674 class SineAnalyzer : public LoopbackProcessor {
    675 public:
    676 
    677     virtual int getResult() {
    678         return mState == STATE_LOCKED ? 0 : -1;
    679     }
    680 
    681     void report() override {
    682         printf("SineAnalyzer ------------------\n");
    683         printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %7.5f\n", mPeakAmplitude);
    684         printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %7.5f\n", mMagnitude);
    685         printf(LOOPBACK_RESULT_TAG "phase.offset       = %7.5f\n", mPhaseOffset);
    686         printf(LOOPBACK_RESULT_TAG "ref.phase          = %7.5f\n", mPhase);
    687         printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
    688         printf(LOOPBACK_RESULT_TAG "sine.period        = %6d\n", mSinePeriod);
    689         printf(LOOPBACK_RESULT_TAG "test.state         = %6d\n", mState);
    690         printf(LOOPBACK_RESULT_TAG "frame.count        = %6d\n", mFrameCounter);
    691         // Did we ever get a lock?
    692         bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
    693         if (!gotLock) {
    694             printf("ERROR - failed to lock on reference sine tone\n");
    695         } else {
    696             // Only print if meaningful.
    697             printf(LOOPBACK_RESULT_TAG "glitch.count       = %6d\n", mGlitchCount);
    698         }
    699     }
    700 
    701     void printStatus() override {
    702         printf("st = %d, #gl = %3d,", mState, mGlitchCount);
    703     }
    704 
    705     double calculateMagnitude(double *phasePtr = NULL) {
    706         if (mFramesAccumulated == 0) {
    707             return 0.0;
    708         }
    709         double sinMean = mSinAccumulator / mFramesAccumulated;
    710         double cosMean = mCosAccumulator / mFramesAccumulated;
    711         double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
    712         if( phasePtr != NULL )
    713         {
    714             double phase = M_PI_2 - atan2( sinMean, cosMean );
    715             *phasePtr = phase;
    716         }
    717         return magnitude;
    718     }
    719 
    720     /**
    721      * @param inputData contains microphone data with sine signal feedback
    722      * @param outputData contains the reference sine wave
    723      */
    724     void process(float *inputData, int inputChannelCount,
    725                  float *outputData, int outputChannelCount,
    726                  int numFrames) override {
    727         mProcessCount++;
    728 
    729         float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
    730         if (peak > mPeakAmplitude) {
    731             mPeakAmplitude = peak;
    732         }
    733 
    734         for (int i = 0; i < numFrames; i++) {
    735             float sample = inputData[i * inputChannelCount];
    736 
    737             float sinOut = sinf(mPhase);
    738 
    739             switch (mState) {
    740                 case STATE_IDLE:
    741                 case STATE_IMMUNE:
    742                 case STATE_WAITING_FOR_SIGNAL:
    743                     break;
    744                 case STATE_WAITING_FOR_LOCK:
    745                     mSinAccumulator += sample * sinOut;
    746                     mCosAccumulator += sample * cosf(mPhase);
    747                     mFramesAccumulated++;
    748                     // Must be a multiple of the period or the calculation will not be accurate.
    749                     if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
    750                         mPhaseOffset = 0.0;
    751                         mMagnitude = calculateMagnitude(&mPhaseOffset);
    752                         if (mMagnitude > mThreshold) {
    753                             if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) {
    754                                 mState = STATE_LOCKED;
    755                                 //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter);
    756                             }
    757                             mPreviousPhaseOffset = mPhaseOffset;
    758                         }
    759                         resetAccumulator();
    760                     }
    761                     break;
    762 
    763                 case STATE_LOCKED: {
    764                     // Predict next sine value
    765                     float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude;
    766                     // printf("    predicted = %f, actual = %f\n", predicted, sample);
    767 
    768                     float diff = predicted - sample;
    769                     if (fabs(diff) > mTolerance) {
    770                         mGlitchCount++;
    771                         //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
    772                         //       mFrameCounter, mGlitchCount, predicted, sample);
    773                         mState = STATE_IMMUNE;
    774                         //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
    775                         mDownCounter = mSinePeriod;  // Set duration of IMMUNE state.
    776                     }
    777 
    778                     // Track incoming signal and slowly adjust magnitude to account
    779                     // for drift in the DRC or AGC.
    780                     mSinAccumulator += sample * sinOut;
    781                     mCosAccumulator += sample * cosf(mPhase);
    782                     mFramesAccumulated++;
    783                     // Must be a multiple of the period or the calculation will not be accurate.
    784                     if (mFramesAccumulated == mSinePeriod) {
    785                         const double coefficient = 0.1;
    786                         double phaseOffset = 0.0;
    787                         double magnitude = calculateMagnitude(&phaseOffset);
    788                         // One pole averaging filter.
    789                         mMagnitude = (mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient);
    790                         resetAccumulator();
    791                     }
    792                 } break;
    793             }
    794 
    795             // Output sine wave so we can measure it.
    796             outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
    797                     + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
    798             // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut,  mPhaseIncrement);
    799 
    800             // advance and wrap phase
    801             mPhase += mPhaseIncrement;
    802             if (mPhase > M_PI) {
    803                 mPhase -= (2.0 * M_PI);
    804             }
    805 
    806             mFrameCounter++;
    807         }
    808 
    809         // Do these once per buffer.
    810         switch (mState) {
    811             case STATE_IDLE:
    812                 mState = STATE_IMMUNE; // so we can tell when
    813                 break;
    814             case STATE_IMMUNE:
    815                 mDownCounter -= numFrames;
    816                 if (mDownCounter <= 0) {
    817                     mState = STATE_WAITING_FOR_SIGNAL;
    818                     //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
    819                 }
    820                 break;
    821             case STATE_WAITING_FOR_SIGNAL:
    822                 if (peak > mThreshold) {
    823                     mState = STATE_WAITING_FOR_LOCK;
    824                     //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
    825                     resetAccumulator();
    826                 }
    827                 break;
    828             case STATE_WAITING_FOR_LOCK:
    829             case STATE_LOCKED:
    830                 break;
    831         }
    832 
    833     }
    834 
    835     void resetAccumulator() {
    836         mFramesAccumulated = 0;
    837         mSinAccumulator = 0.0;
    838         mCosAccumulator = 0.0;
    839     }
    840 
    841     void reset() override {
    842         mGlitchCount = 0;
    843         mState = STATE_IMMUNE;
    844         mDownCounter = IMMUNE_FRAME_COUNT;
    845         mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
    846         printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
    847         resetAccumulator();
    848         mProcessCount = 0;
    849     }
    850 
    851 private:
    852 
    853     enum sine_state_t {
    854         STATE_IDLE,
    855         STATE_IMMUNE,
    856         STATE_WAITING_FOR_SIGNAL,
    857         STATE_WAITING_FOR_LOCK,
    858         STATE_LOCKED
    859     };
    860 
    861     enum constants {
    862         IMMUNE_FRAME_COUNT = 48 * 500,
    863         PERIODS_NEEDED_FOR_LOCK = 8
    864     };
    865 
    866     int     mSinePeriod = 79;
    867     double  mPhaseIncrement = 0.0;
    868     double  mPhase = 0.0;
    869     double  mPhaseOffset = 0.0;
    870     double  mPreviousPhaseOffset = 0.0;
    871     double  mMagnitude = 0.0;
    872     double  mThreshold = 0.005;
    873     double  mTolerance = 0.01;
    874     int32_t mFramesAccumulated = 0;
    875     int32_t mProcessCount = 0;
    876     double  mSinAccumulator = 0.0;
    877     double  mCosAccumulator = 0.0;
    878     int32_t mGlitchCount = 0;
    879     double  mPeakAmplitude = 0.0;
    880     int     mDownCounter = IMMUNE_FRAME_COUNT;
    881     int32_t mFrameCounter = 0;
    882     float   mOutputAmplitude = 0.75;
    883 
    884     PseudoRandom  mWhiteNoise;
    885     float   mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
    886 
    887     sine_state_t  mState = STATE_IDLE;
    888 };
    889 
    890 
    891 #undef LOOPBACK_SAMPLE_RATE
    892 #undef LOOPBACK_RESULT_TAG
    893 
    894 #endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */
    895