• 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;
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     /**
432      * Some analyzers may only look at one channel.
433      * You can optionally specify that channel here.
434      *
435      * @param inputChannel
436      */
setInputChannel(int inputChannel)437     void setInputChannel(int inputChannel) {
438         mInputChannel = inputChannel;
439     }
440 
getInputChannel()441     int getInputChannel() const {
442         return mInputChannel;
443     }
444 
445     /**
446      * Some analyzers may only generate one channel.
447      * You can optionally specify that channel here.
448      *
449      * @param outputChannel
450      */
setOutputChannel(int outputChannel)451     void setOutputChannel(int outputChannel) {
452         mOutputChannel = outputChannel;
453     }
454 
getOutputChannel()455     int getOutputChannel() const {
456         return mOutputChannel;
457     }
458 
459 protected:
460     int32_t   mResetCount = 0;
461 
462 private:
463 
464     int32_t mInputChannel = 0;
465     int32_t mOutputChannel = 0;
466     int32_t mSampleRate = kDefaultSampleRate;
467     int32_t mResult = 0;
468 };
469 
470 class LatencyAnalyzer : public LoopbackProcessor {
471 public:
472 
LatencyAnalyzer()473     LatencyAnalyzer() : LoopbackProcessor() {}
474     virtual ~LatencyAnalyzer() = default;
475 
476     /**
477      * Call this after the constructor because it calls other virtual methods.
478      */
479     virtual void setup() = 0;
480 
481     virtual int32_t getProgress() const = 0;
482 
483     virtual int getState() const = 0;
484 
485     // @return latency in frames
486     virtual int32_t getMeasuredLatency() const = 0;
487 
488     /**
489      * This is an overall confidence in the latency result based on correlation, SNR, etc.
490      * @return probability value between 0.0 and 1.0
491      */
getMeasuredConfidence()492     double getMeasuredConfidence() const {
493         // Limit the ratio and prevent divide-by-zero.
494         double noiseSignalRatio = getSignalRMS() <= getBackgroundRMS()
495                                   ? 1.0 : getBackgroundRMS() / getSignalRMS();
496         // Prevent high background noise and low signals from generating false matches.
497         double adjustedConfidence = getMeasuredCorrelation() - noiseSignalRatio;
498         return std::max(0.0, adjustedConfidence);
499     }
500 
501     /**
502      * Cross correlation value for the noise pulse against
503      * the corresponding position in the normalized recording.
504      *
505      * @return value between -1.0 and 1.0
506      */
507     virtual double getMeasuredCorrelation() const = 0;
508 
509     virtual double getBackgroundRMS() const = 0;
510 
511     virtual double getSignalRMS() const = 0;
512 
513     virtual bool hasEnoughData() const = 0;
514 };
515 
516 // ====================================================================================
517 /**
518  * Measure latency given a loopback stream data.
519  * Use an encoded bit train as the sound source because it
520  * has an unambiguous correlation value.
521  * Uses a state machine to cycle through various stages.
522  *
523  */
524 class PulseLatencyAnalyzer : public LatencyAnalyzer {
525 public:
526 
setup()527     void setup() override {
528         int32_t pulseLength = calculatePulseLength();
529         int32_t maxLatencyFrames = getSampleRate() * kMaxLatencyMillis / kMillisPerSecond;
530         mFramesToRecord = pulseLength + maxLatencyFrames;
531         mAudioRecording.allocate(mFramesToRecord);
532         mAudioRecording.setSampleRate(getSampleRate());
533     }
534 
getState()535     int getState() const override {
536         return mState;
537     }
538 
setSampleRate(int32_t sampleRate)539     void setSampleRate(int32_t sampleRate) override {
540         LoopbackProcessor::setSampleRate(sampleRate);
541         mAudioRecording.setSampleRate(sampleRate);
542     }
543 
reset()544     void reset() override {
545         LoopbackProcessor::reset();
546         mState = STATE_MEASURE_BACKGROUND;
547         mDownCounter = (int32_t) (getSampleRate() * kBackgroundMeasurementLengthSeconds);
548         mLoopCounter = 0;
549 
550         mPulseCursor = 0;
551         mBackgroundSumSquare = 0.0f;
552         mBackgroundSumCount = 0;
553         mBackgroundRMS = 0.0f;
554         mSignalRMS = 0.0f;
555 
556         generatePulseRecording(calculatePulseLength());
557         mAudioRecording.clear();
558         mLatencyReport.reset();
559     }
560 
hasEnoughData()561     bool hasEnoughData() const override {
562         return mAudioRecording.isFull();
563     }
564 
isDone()565     bool isDone() override {
566         return mState == STATE_DONE;
567     }
568 
getProgress()569     int32_t getProgress() const override {
570         return mAudioRecording.size();
571     }
572 
analyze()573     std::string analyze() override {
574         std::stringstream report;
575         report << "PulseLatencyAnalyzer ---------------\n";
576         report << LOOPBACK_RESULT_TAG "test.state             = "
577                 << std::setw(8) << mState << "\n";
578         report << LOOPBACK_RESULT_TAG "test.state.name        = "
579                 << convertStateToText(mState) << "\n";
580         report << LOOPBACK_RESULT_TAG "background.rms         = "
581                 << std::setw(8) << mBackgroundRMS << "\n";
582 
583         int32_t newResult = RESULT_OK;
584         if (mState != STATE_GOT_DATA) {
585             report << "WARNING - Bad state. Check volume on device.\n";
586             // setResult(ERROR_INVALID_STATE);
587         } else {
588             float gain = mAudioRecording.normalize(1.0f);
589             measureLatency();
590 
591             // Calculate signalRMS even if it is bogus.
592             // Also it may be used in the confidence calculation below.
593             mSignalRMS = calculateRootMeanSquare(
594                     &mAudioRecording.getData()[mLatencyReport.latencyInFrames], mPulse.size())
595                          / gain;
596             if (getMeasuredConfidence() < getMinimumConfidence()) {
597                 report << "   ERROR - confidence too low!";
598                 newResult = ERROR_CONFIDENCE;
599             }
600 
601             double latencyMillis = kMillisPerSecond * (double) mLatencyReport.latencyInFrames
602                                    / getSampleRate();
603             report << LOOPBACK_RESULT_TAG "latency.frames         = " << std::setw(8)
604                    << mLatencyReport.latencyInFrames << "\n";
605             report << LOOPBACK_RESULT_TAG "latency.msec           = " << std::setw(8)
606                    << latencyMillis << "\n";
607             report << LOOPBACK_RESULT_TAG "latency.confidence     = " << std::setw(8)
608                    << getMeasuredConfidence() << "\n";
609             report << LOOPBACK_RESULT_TAG "latency.correlation    = " << std::setw(8)
610                    << getMeasuredCorrelation() << "\n";
611         }
612         mState = STATE_DONE;
613         if (getResult() == RESULT_OK) {
614             setResult(newResult);
615         }
616 
617         return report.str();
618     }
619 
getMeasuredLatency()620     int32_t getMeasuredLatency() const override {
621         return mLatencyReport.latencyInFrames;
622     }
623 
getMeasuredCorrelation()624     double getMeasuredCorrelation() const override {
625         return mLatencyReport.correlation;
626     }
627 
getBackgroundRMS()628     double getBackgroundRMS() const override {
629         return mBackgroundRMS;
630     }
631 
getSignalRMS()632     double getSignalRMS() const override {
633         return mSignalRMS;
634     }
635 
isRecordingComplete()636     bool isRecordingComplete() {
637         return mState == STATE_GOT_DATA;
638     }
639 
printStatus()640     void printStatus() override {
641         ALOGD("latency: st = %d = %s", mState, convertStateToText(mState));
642     }
643 
processInputFrame(const float * frameData,int)644     result_code processInputFrame(const float *frameData, int /* channelCount */) override {
645         echo_state nextState = mState;
646         mLoopCounter++;
647         float input = frameData[0];
648 
649         switch (mState) {
650             case STATE_MEASURE_BACKGROUND:
651                 // Measure background RMS on channel 0
652                 mBackgroundSumSquare += static_cast<double>(input) * input;
653                 mBackgroundSumCount++;
654                 mDownCounter--;
655                 if (mDownCounter <= 0) {
656                     mBackgroundRMS = sqrtf(mBackgroundSumSquare / mBackgroundSumCount);
657                     nextState = STATE_IN_PULSE;
658                     mPulseCursor = 0;
659                 }
660                 break;
661 
662             case STATE_IN_PULSE:
663                 // Record input until the mAudioRecording is full.
664                 mAudioRecording.write(input);
665                 if (hasEnoughData()) {
666                     nextState = STATE_GOT_DATA;
667                 }
668                 break;
669 
670             case STATE_GOT_DATA:
671             case STATE_DONE:
672             default:
673                 break;
674         }
675 
676         mState = nextState;
677         return RESULT_OK;
678     }
679 
processOutputFrame(float * frameData,int channelCount)680     result_code processOutputFrame(float *frameData, int channelCount) override {
681         switch (mState) {
682             case STATE_IN_PULSE:
683                 if (mPulseCursor < mPulse.size()) {
684                     float pulseSample = mPulse.getData()[mPulseCursor++];
685                     for (int i = 0; i < channelCount; i++) {
686                         frameData[i] = pulseSample;
687                     }
688                 } else {
689                     for (int i = 0; i < channelCount; i++) {
690                         frameData[i] = 0;
691                     }
692                 }
693                 break;
694 
695             case STATE_MEASURE_BACKGROUND:
696             case STATE_GOT_DATA:
697             case STATE_DONE:
698             default:
699                 for (int i = 0; i < channelCount; i++) {
700                     frameData[i] = 0.0f; // silence
701                 }
702                 break;
703         }
704 
705         return RESULT_OK;
706     }
707 
708 protected:
709 
710     virtual int32_t calculatePulseLength() const = 0;
711 
712     virtual void generatePulseRecording(int32_t pulseLength) = 0;
713 
714     virtual void measureLatency() = 0;
715 
getMinimumConfidence()716     virtual double getMinimumConfidence() const {
717         return 0.5;
718     }
719 
720     AudioRecording     mPulse;
721     AudioRecording     mAudioRecording; // contains only the input after starting the pulse
722     LatencyReport      mLatencyReport;
723 
724     static constexpr int32_t kPulseLengthMillis = 500;
725     float              mPulseAmplitude = 0.5f;
726     double             mBackgroundRMS = 0.0;
727     double             mSignalRMS = 0.0;
728 
729 private:
730 
731     enum echo_state {
732         STATE_MEASURE_BACKGROUND,
733         STATE_IN_PULSE,
734         STATE_GOT_DATA, // must match RoundTripLatencyActivity.java
735         STATE_DONE,
736     };
737 
convertStateToText(echo_state state)738     const char *convertStateToText(echo_state state) {
739         switch (state) {
740             case STATE_MEASURE_BACKGROUND:
741                 return "INIT";
742             case STATE_IN_PULSE:
743                 return "PULSE";
744             case STATE_GOT_DATA:
745                 return "GOT_DATA";
746             case STATE_DONE:
747                 return "DONE";
748         }
749         return "UNKNOWN";
750     }
751 
752     int32_t         mDownCounter = 500;
753     int32_t         mLoopCounter = 0;
754     echo_state      mState = STATE_MEASURE_BACKGROUND;
755 
756     static constexpr double  kBackgroundMeasurementLengthSeconds = 0.5;
757 
758     int32_t            mPulseCursor = 0;
759 
760     double             mBackgroundSumSquare = 0.0;
761     int32_t            mBackgroundSumCount = 0;
762     int32_t            mFramesToRecord = 0;
763 
764 };
765 
766 /**
767  * This algorithm uses a series of random bits encoded using the
768  * Manchester encoder. It works well for wired loopback but not very well for
769  * through the air loopback.
770  */
771 class EncodedRandomLatencyAnalyzer : public PulseLatencyAnalyzer {
772 
773 protected:
774 
calculatePulseLength()775     int32_t calculatePulseLength() const override {
776         // Calculate integer number of bits.
777         int32_t numPulseBits = getSampleRate() * kPulseLengthMillis
778                                / (kFramesPerEncodedBit * kMillisPerSecond);
779         return numPulseBits * kFramesPerEncodedBit;
780     }
781 
generatePulseRecording(int32_t pulseLength)782     void generatePulseRecording(int32_t pulseLength) override {
783         mPulse.allocate(pulseLength);
784         RandomPulseGenerator pulser(kFramesPerEncodedBit);
785         for (int i = 0; i < pulseLength; i++) {
786             mPulse.write(pulser.nextFloat() * mPulseAmplitude);
787         }
788     }
789 
getMinimumConfidence()790     double getMinimumConfidence() const override {
791         return 0.2;
792     }
793 
measureLatency()794     void measureLatency() override {
795         measureLatencyFromPulse(mAudioRecording,
796                                 mPulse,
797                                 &mLatencyReport);
798     }
799 
800 private:
801     static constexpr int32_t kFramesPerEncodedBit = 8; // multiple of 2
802 };
803 
804 /**
805  * This algorithm uses White Noise sent in a short burst pattern.
806  * The original signal and the recorded signal are then run through
807  * an envelope follower to convert the fine detail into more of
808  * a rectangular block before the correlation phase.
809  */
810 class WhiteNoiseLatencyAnalyzer : public PulseLatencyAnalyzer {
811 
812 protected:
813 
calculatePulseLength()814     int32_t calculatePulseLength() const override {
815         return getSampleRate() * kPulseLengthMillis / kMillisPerSecond;
816     }
817 
generatePulseRecording(int32_t pulseLength)818     void generatePulseRecording(int32_t pulseLength) override {
819         mPulse.allocate(pulseLength);
820         // Turn the noise on and off to sharpen the correlation peak.
821         // Use more zeros than ones so that the correlation will be less than 0.5 even when there
822         // is a strong background noise.
823         int8_t pattern[] = {1, 0, 0,
824                             1, 1, 0, 0, 0,
825                             1, 1, 1, 0, 0, 0, 0,
826                             1, 1, 1, 1, 0, 0, 0, 0, 0
827                             };
828         PseudoRandom random;
829         const int32_t numSections = sizeof(pattern);
830         const int32_t framesPerSection = pulseLength / numSections;
831         for (int section = 0; section < numSections; section++) {
832             if (pattern[section]) {
833                 for (int i = 0; i < framesPerSection; i++) {
834                     mPulse.write((float) (random.nextRandomDouble() * mPulseAmplitude));
835                 }
836             } else {
837                 for (int i = 0; i < framesPerSection; i++) {
838                     mPulse.write(0.0f);
839                 }
840             }
841         }
842         // Write any remaining frames.
843         int32_t framesWritten = framesPerSection * numSections;
844         for (int i = framesWritten; i < pulseLength; i++) {
845             mPulse.write(0.0f);
846         }
847     }
848 
measureLatency()849     void measureLatency() override {
850         // Smooth out the noise so we see rectangular blocks.
851         // This improves immunity against phase cancellation and distortion.
852         static constexpr float decay = 0.99f; // just under 1.0, lower numbers decay faster
853         mAudioRecording.detectPeaks(decay);
854         mPulse.detectPeaks(decay);
855         measureLatencyFromPulse(mAudioRecording,
856                                 mPulse,
857                                 &mLatencyReport);
858     }
859 
860 };
861 
862 #endif // ANALYZER_LATENCY_ANALYZER_H
863