• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ANALYZER_LATENCY_ANALYZER_H
23 #define ANALYZER_LATENCY_ANALYZER_H
24 
25 #include <algorithm>
26 #include <assert.h>
27 #include <cctype>
28 #include <iomanip>
29 #include <iostream>
30 #include <math.h>
31 #include <memory>
32 #include <sstream>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <vector>
37 
38 #include "PeakDetector.h"
39 #include "PseudoRandom.h"
40 #include "RandomPulseGenerator.h"
41 
42 // This is used when the code is in not in Android.
43 #ifndef ALOGD
44 #define ALOGD LOGD
45 #define ALOGE LOGE
46 #define ALOGW LOGW
47 #endif
48 
49 #define LOOPBACK_RESULT_TAG  "RESULT: "
50 
51 // Enable or disable the optimized latency calculation.
52 #define USE_FAST_LATENCY_CALCULATION 1
53 
54 static constexpr int32_t kDefaultSampleRate = 48000;
55 static constexpr int32_t kMillisPerSecond   = 1000;  // by definition
56 static constexpr int32_t kMaxLatencyMillis  = 1000;  // arbitrary and generous
57 
58 struct LatencyReport {
59     int32_t latencyInFrames = 0.0;
60     double correlation = 0.0;
61 
resetLatencyReport62     void reset() {
63         latencyInFrames = 0;
64         correlation = 0.0;
65     }
66 };
67 
68 /**
69  * Calculate a normalized cross correlation.
70  * @return value between -1.0 and 1.0
71  */
72 
calculateNormalizedCorrelation(const float * a,const float * b,int windowSize,int stride)73 static float calculateNormalizedCorrelation(const float *a,
74                                              const float *b,
75                                              int windowSize,
76                                              int stride) {
77     float correlation = 0.0;
78     float sumProducts = 0.0;
79     float sumSquares = 0.0;
80 
81     // Correlate a against b.
82     for (int i = 0; i < windowSize; i += stride) {
83         float s1 = a[i];
84         float s2 = b[i];
85         // Use a normalized cross-correlation.
86         sumProducts += s1 * s2;
87         sumSquares += ((s1 * s1) + (s2 * s2));
88     }
89 
90     if (sumSquares >= 1.0e-9) {
91         correlation = 2.0 * sumProducts / sumSquares;
92     }
93     return correlation;
94 }
95 
calculateRootMeanSquare(float * data,int32_t numSamples)96 static double calculateRootMeanSquare(float *data, int32_t numSamples) {
97     double sum = 0.0;
98     for (int32_t i = 0; i < numSamples; i++) {
99         double sample = data[i];
100         sum += sample * sample;
101     }
102     return sqrt(sum / numSamples);
103 }
104 
105 /**
106  * Monophonic recording with processing.
107  * Samples are stored as floats internally.
108  */
109 class AudioRecording
110 {
111 public:
112 
allocate(int maxFrames)113     void allocate(int maxFrames) {
114         mData = std::make_unique<float[]>(maxFrames);
115         mMaxFrames = maxFrames;
116         mFrameCounter = 0;
117     }
118 
119     // Write SHORT data from the first channel.
write(const int16_t * inputData,int32_t inputChannelCount,int32_t numFrames)120     int32_t write(const int16_t *inputData, int32_t inputChannelCount, int32_t numFrames) {
121         // stop at end of buffer
122         if ((mFrameCounter + numFrames) > mMaxFrames) {
123             numFrames = mMaxFrames - mFrameCounter;
124         }
125         for (int i = 0; i < numFrames; i++) {
126             mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
127         }
128         return numFrames;
129     }
130 
131     // Write FLOAT data from the first channel.
write(const float * inputData,int32_t inputChannelCount,int32_t numFrames)132     int32_t write(const float *inputData, int32_t inputChannelCount, int32_t numFrames) {
133         // stop at end of buffer
134         if ((mFrameCounter + numFrames) > mMaxFrames) {
135             numFrames = mMaxFrames - mFrameCounter;
136         }
137         for (int i = 0; i < numFrames; i++) {
138             mData[mFrameCounter++] = inputData[i * inputChannelCount];
139         }
140         return numFrames;
141     }
142 
143     // Write single FLOAT value.
write(float sample)144     int32_t write(float sample) {
145         // stop at end of buffer
146         if (mFrameCounter < mMaxFrames) {
147             mData[mFrameCounter++] = sample;
148             return 1;
149         }
150         return 0;
151     }
152 
clear()153     void clear() {
154         mFrameCounter = 0;
155     }
156 
size()157     int32_t size() const {
158         return mFrameCounter;
159     }
160 
isFull()161     bool isFull() const {
162         return mFrameCounter >= mMaxFrames;
163     }
164 
getData()165     float *getData() const {
166         return mData.get();
167     }
168 
setSampleRate(int32_t sampleRate)169     void setSampleRate(int32_t sampleRate) {
170         mSampleRate = sampleRate;
171     }
172 
getSampleRate()173     int32_t getSampleRate() const {
174         return mSampleRate;
175     }
176 
177     /**
178      * Square the samples so they are all positive and so the peaks are emphasized.
179      */
square()180     void square() {
181         float *x = mData.get();
182         for (int i = 0; i < mFrameCounter; i++) {
183             x[i] *= x[i];
184         }
185     }
186 
187     // Envelope follower that rides over the peak values.
detectPeaks(float decay)188     void detectPeaks(float decay) {
189         float level = 0.0f;
190         float *x = mData.get();
191         for (int i = 0; i < mFrameCounter; i++) {
192             level *= decay; // exponential decay
193             float input = fabs(x[i]);
194             // never fall below the input signal
195             if (input > level) {
196                 level = input;
197             }
198             x[i] = level; // write result back into the array
199         }
200     }
201 
202     /**
203      * Amplify a signal so that the peak matches the specified target.
204      *
205      * @param target final max value
206      * @return gain applied to signal
207      */
normalize(float target)208     float normalize(float target) {
209         float maxValue = 1.0e-9f;
210         for (int i = 0; i < mFrameCounter; i++) {
211             maxValue = std::max(maxValue, fabsf(mData[i]));
212         }
213         float gain = target / maxValue;
214         for (int i = 0; i < mFrameCounter; i++) {
215             mData[i] *= gain;
216         }
217         return gain;
218     }
219 
220 private:
221     std::unique_ptr<float[]> mData;
222     int32_t       mFrameCounter = 0;
223     int32_t       mMaxFrames = 0;
224     int32_t       mSampleRate = kDefaultSampleRate; // common default
225 };
226 
227 /**
228   * Find latency using cross correlation in window of the recorded audio.
229   * The stride is used to skip over samples and reduce the CPU load.
230   */
measureLatencyFromPulsePartial(AudioRecording & recorded,int32_t recordedOffset,int32_t recordedWindowSize,AudioRecording & pulse,LatencyReport * report,int32_t stride)231 static int measureLatencyFromPulsePartial(AudioRecording &recorded,
232                                           int32_t recordedOffset,
233                                           int32_t recordedWindowSize,
234                                           AudioRecording &pulse,
235                                           LatencyReport *report,
236                                           int32_t stride) {
237     report->reset();
238 
239     if (recordedOffset + recordedWindowSize + pulse.size() > recorded.size()) {
240         ALOGE("%s() tried to correlate past end of recording, recordedOffset = %d frames\n",
241               __func__, recordedOffset);
242         return -3;
243     }
244 
245     int32_t numCorrelations = recordedWindowSize / stride;
246     if (numCorrelations < 10) {
247         ALOGE("%s() recording too small = %d frames, numCorrelations = %d\n",
248               __func__, recorded.size(), numCorrelations);
249         return -1;
250     }
251     std::unique_ptr<float[]> correlations= std::make_unique<float[]>(numCorrelations);
252 
253     // Correlate pulse against the recorded data.
254     for (int32_t i = 0; i < numCorrelations; i++) {
255         const int32_t index = (i * stride) + recordedOffset;
256         float correlation = calculateNormalizedCorrelation(&recorded.getData()[index],
257                                                            &pulse.getData()[0],
258                                                            pulse.size(),
259                                                            stride);
260         correlations[i] = correlation;
261     }
262     // Find highest peak in correlation array.
263     float peakCorrelation = 0.0;
264     int32_t peakIndex = -1;
265     for (int32_t i = 0; i < numCorrelations; i++) {
266         float value = fabsf(correlations[i]);
267         if (value > peakCorrelation) {
268             peakCorrelation = value;
269             peakIndex = i;
270         }
271     }
272     if (peakIndex < 0) {
273         ALOGE("%s() no signal for correlation\n", __func__);
274         return -2;
275     }
276 #if 0
277     // Dump correlation data for charting.
278     else {
279         const int32_t margin = 50;
280         int32_t startIndex = std::max(0, peakIndex - margin);
281         int32_t endIndex = std::min(numCorrelations - 1, peakIndex + margin);
282         for (int32_t index = startIndex; index < endIndex; index++) {
283             ALOGD("Correlation, %d, %f", index, correlations[index]);
284         }
285     }
286 #endif
287 
288     report->latencyInFrames = recordedOffset + (peakIndex * stride);
289     report->correlation = peakCorrelation;
290 
291     return 0;
292 }
293 
294 #if USE_FAST_LATENCY_CALCULATION
measureLatencyFromPulse(AudioRecording & recorded,AudioRecording & pulse,LatencyReport * report)295 static int measureLatencyFromPulse(AudioRecording &recorded,
296                                    AudioRecording &pulse,
297                                    LatencyReport *report) {
298     const int32_t coarseStride = 16;
299     const int32_t fineWindowSize = coarseStride * 8;
300     const int32_t fineStride = 1;
301     LatencyReport courseReport;
302     courseReport.reset();
303     // Do a rough search, skipping over most of the samples.
304     int result = measureLatencyFromPulsePartial(recorded,
305                                                 0, // recordedOffset,
306                                                 recorded.size() - pulse.size(),
307                                                 pulse,
308                                                 &courseReport,
309                                                 coarseStride);
310     if (result != 0) {
311         return result;
312     }
313     // Now do a fine resolution search near the coarse latency result.
314     int32_t recordedOffset = std::max(0, courseReport.latencyInFrames - (fineWindowSize / 2));
315     result = measureLatencyFromPulsePartial(recorded,
316                                             recordedOffset,
317                                             fineWindowSize,
318                                             pulse,
319                                             report,
320                                             fineStride );
321     return result;
322 }
323 #else
324 // TODO - When we are confident of the new code we can remove this old code.
measureLatencyFromPulse(AudioRecording & recorded,AudioRecording & pulse,LatencyReport * report)325 static int measureLatencyFromPulse(AudioRecording &recorded,
326                                    AudioRecording &pulse,
327                                    LatencyReport *report) {
328     return measureLatencyFromPulsePartial(recorded,
329                                           0,
330                                           recorded.size() - pulse.size(),
331                                           pulse,
332                                           report,
333                                           1 );
334 }
335 #endif
336 
337 // ====================================================================================
338 class LoopbackProcessor {
339 public:
340     virtual ~LoopbackProcessor() = default;
341 
342     enum result_code {
343         RESULT_OK = 0,
344         ERROR_NOISY = -99,
345         ERROR_VOLUME_TOO_LOW,
346         ERROR_VOLUME_TOO_HIGH,
347         ERROR_CONFIDENCE,
348         ERROR_INVALID_STATE,
349         ERROR_GLITCHES,
350         ERROR_NO_LOCK
351     };
352 
prepareToTest()353     virtual void prepareToTest() {
354         reset();
355     }
356 
reset()357     virtual void reset() {
358         mResult = 0;
359         mResetCount++;
360     }
361 
362     virtual result_code processInputFrame(const float *frameData, int channelCount) = 0;
363     virtual result_code processOutputFrame(float *frameData, int channelCount) = 0;
364 
process(const float * inputData,int inputChannelCount,int numInputFrames,float * outputData,int outputChannelCount,int numOutputFrames)365     void process(const float *inputData, int inputChannelCount, int numInputFrames,
366                  float *outputData, int outputChannelCount, int numOutputFrames) {
367         int numBoth = std::min(numInputFrames, numOutputFrames);
368         // Process one frame at a time.
369         for (int i = 0; i < numBoth; i++) {
370             processInputFrame(inputData, inputChannelCount);
371             inputData += inputChannelCount;
372             processOutputFrame(outputData, outputChannelCount);
373             outputData += outputChannelCount;
374         }
375         // If there is more input than output.
376         for (int i = numBoth; i < numInputFrames; i++) {
377             processInputFrame(inputData, inputChannelCount);
378             inputData += inputChannelCount;
379         }
380         // If there is more output than input.
381         for (int i = numBoth; i < numOutputFrames; i++) {
382             processOutputFrame(outputData, outputChannelCount);
383             outputData += outputChannelCount;
384         }
385     }
386 
387     virtual std::string analyze() = 0;
388 
printStatus()389     virtual void printStatus() {};
390 
getResult()391     int32_t getResult() {
392         return mResult;
393     }
394 
setResult(int32_t result)395     void setResult(int32_t result) {
396         mResult = result;
397     }
398 
isDone()399     virtual bool isDone() {
400         return false;
401     }
402 
save(const char * fileName)403     virtual int save(const char *fileName) {
404         (void) fileName;
405         return -1;
406     }
407 
load(const char * fileName)408     virtual int load(const char *fileName) {
409         (void) fileName;
410         return -1;
411     }
412 
setSampleRate(int32_t sampleRate)413     virtual void setSampleRate(int32_t sampleRate) {
414         mSampleRate = sampleRate;
415     }
416 
getSampleRate()417     int32_t getSampleRate() const {
418         return mSampleRate;
419     }
420 
getResetCount()421     int32_t getResetCount() const {
422         return mResetCount;
423     }
424 
425     /** Called when not enough input frames could be read after synchronization.
426      */
onInsufficientRead()427     virtual void onInsufficientRead() {
428         reset();
429     }
430 
431 protected:
432     int32_t   mResetCount = 0;
433 
434 private:
435     int32_t mSampleRate = kDefaultSampleRate;
436     int32_t mResult = 0;
437 };
438 
439 class LatencyAnalyzer : public LoopbackProcessor {
440 public:
441 
LatencyAnalyzer()442     LatencyAnalyzer() : LoopbackProcessor() {}
443     virtual ~LatencyAnalyzer() = default;
444 
445     /**
446      * Call this after the constructor because it calls other virtual methods.
447      */
448     virtual void setup() = 0;
449 
450     virtual int32_t getProgress() const = 0;
451 
452     virtual int getState() const = 0;
453 
454     // @return latency in frames
455     virtual int32_t getMeasuredLatency() const = 0;
456 
457     /**
458      * This is an overall confidence in the latency result based on correlation, SNR, etc.
459      * @return probability value between 0.0 and 1.0
460      */
getMeasuredConfidence()461     double getMeasuredConfidence() const {
462         // Limit the ratio and prevent divide-by-zero.
463         double noiseSignalRatio = getSignalRMS() <= getBackgroundRMS()
464                                   ? 1.0 : getBackgroundRMS() / getSignalRMS();
465         // Prevent high background noise and low signals from generating false matches.
466         double adjustedConfidence = getMeasuredCorrelation() - noiseSignalRatio;
467         return std::max(0.0, adjustedConfidence);
468     }
469 
470     /**
471      * Cross correlation value for the noise pulse against
472      * the corresponding position in the normalized recording.
473      *
474      * @return value between -1.0 and 1.0
475      */
476     virtual double getMeasuredCorrelation() const = 0;
477 
478     virtual double getBackgroundRMS() const = 0;
479 
480     virtual double getSignalRMS() const = 0;
481 
482     virtual bool hasEnoughData() const = 0;
483 };
484 
485 // ====================================================================================
486 /**
487  * Measure latency given a loopback stream data.
488  * Use an encoded bit train as the sound source because it
489  * has an unambiguous correlation value.
490  * Uses a state machine to cycle through various stages.
491  *
492  */
493 class PulseLatencyAnalyzer : public LatencyAnalyzer {
494 public:
495 
setup()496     void setup() override {
497         int32_t pulseLength = calculatePulseLength();
498         int32_t maxLatencyFrames = getSampleRate() * kMaxLatencyMillis / kMillisPerSecond;
499         mFramesToRecord = pulseLength + maxLatencyFrames;
500         mAudioRecording.allocate(mFramesToRecord);
501         mAudioRecording.setSampleRate(getSampleRate());
502     }
503 
getState()504     int getState() const override {
505         return mState;
506     }
507 
setSampleRate(int32_t sampleRate)508     void setSampleRate(int32_t sampleRate) override {
509         LoopbackProcessor::setSampleRate(sampleRate);
510         mAudioRecording.setSampleRate(sampleRate);
511     }
512 
reset()513     void reset() override {
514         LoopbackProcessor::reset();
515         mState = STATE_MEASURE_BACKGROUND;
516         mDownCounter = (int32_t) (getSampleRate() * kBackgroundMeasurementLengthSeconds);
517         mLoopCounter = 0;
518 
519         mPulseCursor = 0;
520         mBackgroundSumSquare = 0.0f;
521         mBackgroundSumCount = 0;
522         mBackgroundRMS = 0.0f;
523         mSignalRMS = 0.0f;
524 
525         generatePulseRecording(calculatePulseLength());
526         mAudioRecording.clear();
527         mLatencyReport.reset();
528     }
529 
hasEnoughData()530     bool hasEnoughData() const override {
531         return mAudioRecording.isFull();
532     }
533 
isDone()534     bool isDone() override {
535         return mState == STATE_DONE;
536     }
537 
getProgress()538     int32_t getProgress() const override {
539         return mAudioRecording.size();
540     }
541 
analyze()542     std::string analyze() override {
543         std::stringstream report;
544         report << "PulseLatencyAnalyzer ---------------\n";
545         report << LOOPBACK_RESULT_TAG "test.state             = "
546                 << std::setw(8) << mState << "\n";
547         report << LOOPBACK_RESULT_TAG "test.state.name        = "
548                 << convertStateToText(mState) << "\n";
549         report << LOOPBACK_RESULT_TAG "background.rms         = "
550                 << std::setw(8) << mBackgroundRMS << "\n";
551 
552         int32_t newResult = RESULT_OK;
553         if (mState != STATE_GOT_DATA) {
554             report << "WARNING - Bad state. Check volume on device.\n";
555             // setResult(ERROR_INVALID_STATE);
556         } else {
557             float gain = mAudioRecording.normalize(1.0f);
558             measureLatency();
559 
560             // Calculate signalRMS even if it is bogus.
561             // Also it may be used in the confidence calculation below.
562             mSignalRMS = calculateRootMeanSquare(
563                     &mAudioRecording.getData()[mLatencyReport.latencyInFrames], mPulse.size())
564                          / gain;
565             if (getMeasuredConfidence() < getMinimumConfidence()) {
566                 report << "   ERROR - confidence too low!";
567                 newResult = ERROR_CONFIDENCE;
568             }
569 
570             double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
571                                    / getSampleRate();
572             report << LOOPBACK_RESULT_TAG "latency.frames         = " << std::setw(8)
573                    << mLatencyReport.latencyInFrames << "\n";
574             report << LOOPBACK_RESULT_TAG "latency.msec           = " << std::setw(8)
575                    << latencyMillis << "\n";
576             report << LOOPBACK_RESULT_TAG "latency.confidence     = " << std::setw(8)
577                    << getMeasuredConfidence() << "\n";
578             report << LOOPBACK_RESULT_TAG "latency.correlation    = " << std::setw(8)
579                    << getMeasuredCorrelation() << "\n";
580         }
581         mState = STATE_DONE;
582         if (getResult() == RESULT_OK) {
583             setResult(newResult);
584         }
585 
586         return report.str();
587     }
588 
getMeasuredLatency()589     int32_t getMeasuredLatency() const override {
590         return mLatencyReport.latencyInFrames;
591     }
592 
getMeasuredCorrelation()593     double getMeasuredCorrelation() const override {
594         return mLatencyReport.correlation;
595     }
596 
getBackgroundRMS()597     double getBackgroundRMS() const override {
598         return mBackgroundRMS;
599     }
600 
getSignalRMS()601     double getSignalRMS() const override {
602         return mSignalRMS;
603     }
604 
isRecordingComplete()605     bool isRecordingComplete() {
606         return mState == STATE_GOT_DATA;
607     }
608 
printStatus()609     void printStatus() override {
610         ALOGD("latency: st = %d = %s", mState, convertStateToText(mState));
611     }
612 
processInputFrame(const float * frameData,int)613     result_code processInputFrame(const float *frameData, int /* channelCount */) override {
614         echo_state nextState = mState;
615         mLoopCounter++;
616         float input = frameData[0];
617 
618         switch (mState) {
619             case STATE_MEASURE_BACKGROUND:
620                 // Measure background RMS on channel 0
621                 mBackgroundSumSquare += static_cast<double>(input) * input;
622                 mBackgroundSumCount++;
623                 mDownCounter--;
624                 if (mDownCounter <= 0) {
625                     mBackgroundRMS = sqrtf(mBackgroundSumSquare / mBackgroundSumCount);
626                     nextState = STATE_IN_PULSE;
627                     mPulseCursor = 0;
628                 }
629                 break;
630 
631             case STATE_IN_PULSE:
632                 // Record input until the mAudioRecording is full.
633                 mAudioRecording.write(input);
634                 if (hasEnoughData()) {
635                     nextState = STATE_GOT_DATA;
636                 }
637                 break;
638 
639             case STATE_GOT_DATA:
640             case STATE_DONE:
641             default:
642                 break;
643         }
644 
645         mState = nextState;
646         return RESULT_OK;
647     }
648 
processOutputFrame(float * frameData,int channelCount)649     result_code processOutputFrame(float *frameData, int channelCount) override {
650         switch (mState) {
651             case STATE_IN_PULSE:
652                 if (mPulseCursor < mPulse.size()) {
653                     float pulseSample = mPulse.getData()[mPulseCursor++];
654                     for (int i = 0; i < channelCount; i++) {
655                         frameData[i] = pulseSample;
656                     }
657                 } else {
658                     for (int i = 0; i < channelCount; i++) {
659                         frameData[i] = 0;
660                     }
661                 }
662                 break;
663 
664             case STATE_MEASURE_BACKGROUND:
665             case STATE_GOT_DATA:
666             case STATE_DONE:
667             default:
668                 for (int i = 0; i < channelCount; i++) {
669                     frameData[i] = 0.0f; // silence
670                 }
671                 break;
672         }
673 
674         return RESULT_OK;
675     }
676 
677 protected:
678 
679     virtual int32_t calculatePulseLength() const = 0;
680 
681     virtual void generatePulseRecording(int32_t pulseLength) = 0;
682 
683     virtual void measureLatency() = 0;
684 
getMinimumConfidence()685     virtual double getMinimumConfidence() const {
686         return 0.5;
687     }
688 
689     AudioRecording     mPulse;
690     AudioRecording     mAudioRecording; // contains only the input after starting the pulse
691     LatencyReport      mLatencyReport;
692 
693     static constexpr int32_t kPulseLengthMillis = 500;
694     float              mPulseAmplitude = 0.5f;
695     double             mBackgroundRMS = 0.0;
696     double             mSignalRMS = 0.0;
697 
698 private:
699 
700     enum echo_state {
701         STATE_MEASURE_BACKGROUND,
702         STATE_IN_PULSE,
703         STATE_GOT_DATA, // must match RoundTripLatencyActivity.java
704         STATE_DONE,
705     };
706 
convertStateToText(echo_state state)707     const char *convertStateToText(echo_state state) {
708         switch (state) {
709             case STATE_MEASURE_BACKGROUND:
710                 return "INIT";
711             case STATE_IN_PULSE:
712                 return "PULSE";
713             case STATE_GOT_DATA:
714                 return "GOT_DATA";
715             case STATE_DONE:
716                 return "DONE";
717         }
718         return "UNKNOWN";
719     }
720 
721     int32_t         mDownCounter = 500;
722     int32_t         mLoopCounter = 0;
723     echo_state      mState = STATE_MEASURE_BACKGROUND;
724 
725     static constexpr double  kBackgroundMeasurementLengthSeconds = 0.5;
726 
727     int32_t            mPulseCursor = 0;
728 
729     double             mBackgroundSumSquare = 0.0;
730     int32_t            mBackgroundSumCount = 0;
731     int32_t            mFramesToRecord = 0;
732 
733 };
734 
735 /**
736  * This algorithm uses a series of random bits encoded using the
737  * Manchester encoder. It works well for wired loopback but not very well for
738  * through the air loopback.
739  */
740 class EncodedRandomLatencyAnalyzer : public PulseLatencyAnalyzer {
741 
742 protected:
743 
calculatePulseLength()744     int32_t calculatePulseLength() const override {
745         // Calculate integer number of bits.
746         int32_t numPulseBits = getSampleRate() * kPulseLengthMillis
747                                / (kFramesPerEncodedBit * kMillisPerSecond);
748         return numPulseBits * kFramesPerEncodedBit;
749     }
750 
generatePulseRecording(int32_t pulseLength)751     void generatePulseRecording(int32_t pulseLength) override {
752         mPulse.allocate(pulseLength);
753         RandomPulseGenerator pulser(kFramesPerEncodedBit);
754         for (int i = 0; i < pulseLength; i++) {
755             mPulse.write(pulser.nextFloat() * mPulseAmplitude);
756         }
757     }
758 
getMinimumConfidence()759     double getMinimumConfidence() const override {
760         return 0.2;
761     }
762 
measureLatency()763     void measureLatency() override {
764         measureLatencyFromPulse(mAudioRecording,
765                                 mPulse,
766                                 &mLatencyReport);
767     }
768 
769 private:
770     static constexpr int32_t kFramesPerEncodedBit = 8; // multiple of 2
771 };
772 
773 /**
774  * This algorithm uses White Noise sent in a short burst pattern.
775  * The original signal and the recorded signal are then run through
776  * an envelope follower to convert the fine detail into more of
777  * a rectangular block before the correlation phase.
778  */
779 class WhiteNoiseLatencyAnalyzer : public PulseLatencyAnalyzer {
780 
781 protected:
782 
calculatePulseLength()783     int32_t calculatePulseLength() const override {
784         return getSampleRate() * kPulseLengthMillis / kMillisPerSecond;
785     }
786 
generatePulseRecording(int32_t pulseLength)787     void generatePulseRecording(int32_t pulseLength) override {
788         mPulse.allocate(pulseLength);
789         // Turn the noise on and off to sharpen the correlation peak.
790         // Use more zeros than ones so that the correlation will be less than 0.5 even when there
791         // is a strong background noise.
792         int8_t pattern[] = {1, 0, 0,
793                             1, 1, 0, 0, 0,
794                             1, 1, 1, 0, 0, 0, 0,
795                             1, 1, 1, 1, 0, 0, 0, 0, 0
796                             };
797         PseudoRandom random;
798         const int32_t numSections = sizeof(pattern);
799         const int32_t framesPerSection = pulseLength / numSections;
800         for (int section = 0; section < numSections; section++) {
801             if (pattern[section]) {
802                 for (int i = 0; i < framesPerSection; i++) {
803                     mPulse.write((float) (random.nextRandomDouble() * mPulseAmplitude));
804                 }
805             } else {
806                 for (int i = 0; i < framesPerSection; i++) {
807                     mPulse.write(0.0f);
808                 }
809             }
810         }
811         // Write any remaining frames.
812         int32_t framesWritten = framesPerSection * numSections;
813         for (int i = framesWritten; i < pulseLength; i++) {
814             mPulse.write(0.0f);
815         }
816     }
817 
measureLatency()818     void measureLatency() override {
819         // Smooth out the noise so we see rectangular blocks.
820         // This improves immunity against phase cancellation and distortion.
821         static constexpr float decay = 0.99f; // just under 1.0, lower numbers decay faster
822         mAudioRecording.detectPeaks(decay);
823         mPulse.detectPeaks(decay);
824         measureLatencyFromPulse(mAudioRecording,
825                                 mPulse,
826                                 &mLatencyReport);
827     }
828 
829 };
830 
831 #endif // ANALYZER_LATENCY_ANALYZER_H
832