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