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