1fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/*
2fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Copyright (C) 2017 The Android Open Source Project
3fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *
4fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Licensed under the Apache License, Version 2.0 (the "License");
5fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * you may not use this file except in compliance with the License.
6fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * You may obtain a copy of the License at
7fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *
8fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *      http://www.apache.org/licenses/LICENSE-2.0
9fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *
10fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Unless required by applicable law or agreed to in writing, software
11fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * distributed under the License is distributed on an "AS IS" BASIS,
12fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * See the License for the specific language governing permissions and
14fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * limitations under the License.
15fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */
16fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
17fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/**
18fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Tools for measuring latency and for detecting glitches.
19fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * These classes are pure math and can be used with any audio system.
20fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */
21fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
22fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#ifndef AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
23fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#define AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H
24fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
25fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <algorithm>
26fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <assert.h>
27fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <cctype>
28fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <math.h>
29fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <stdio.h>
30fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <stdlib.h>
31fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#include <unistd.h>
32fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
334a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk#include <audio_utils/sndfile.h>
344a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
35fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// Tag for machine readable results as property = value pairs
36fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#define LOOPBACK_RESULT_TAG      "RESULT: "
37fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#define LOOPBACK_SAMPLE_RATE     48000
38fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
39fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#define MILLIS_PER_SECOND        1000
40fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
41fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#define MAX_ZEROTH_PARTIAL_BINS  40
424a764a3b450095cef05b6025a72c3876c95b6a14Phil Burkconstexpr double MAX_ECHO_GAIN = 10.0; // based on experiments, otherwise autocorrelation too noisy
43fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
44a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk// A narrow impulse seems to have better immunity against over estimating the
45a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk// latency due to detecting subharmonics by the auto-correlator.
46fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic const float s_Impulse[] = {
47a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk        0.0f, 0.0f, 0.0f, 0.0f, 0.3f, // silence on each side of the impulse
48a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk        0.99f, 0.0f, -0.99f, // bipolar with one zero crossing in middle
49a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk        -0.3f, 0.0f, 0.0f, 0.0f, 0.0f
50fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
51fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
52a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burkconstexpr int32_t kImpulseSizeInFrames = (int32_t)(sizeof(s_Impulse) / sizeof(s_Impulse[0]));
53a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk
54fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass PseudoRandom {
55fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic:
56fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    PseudoRandom() {}
57fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    PseudoRandom(int64_t seed)
58fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            :    mSeed(seed)
59fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    {}
60fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
61fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    /**
62fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     * Returns the next random double from -1.0 to 1.0
63fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     *
64fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     * @return value from -1.0 to 1.0
65fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     */
66fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     double nextRandomDouble() {
67fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return nextRandomInteger() * (0.5 / (((int32_t)1) << 30));
68fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
69fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
70fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    /** Calculate random 32 bit number using linear-congruential method. */
71fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t nextRandomInteger() {
72fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // Use values for 64-bit sequence from MMIX by Donald Knuth.
73fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407;
74fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence.
75fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
76fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
77fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate:
78fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int64_t mSeed = 99887766;
79fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
80fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
81fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic double calculateCorrelation(const float *a,
82fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                   const float *b,
83fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                   int windowSize)
84fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{
85fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double correlation = 0.0;
86fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double sumProducts = 0.0;
87fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double sumSquares = 0.0;
88fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
89fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Correlate a against b.
90fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    for (int i = 0; i < windowSize; i++) {
91fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        float s1 = a[i];
92fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        float s2 = b[i];
93fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // Use a normalized cross-correlation.
94fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        sumProducts += s1 * s2;
95fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        sumSquares += ((s1 * s1) + (s2 * s2));
96fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
97fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
98fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    if (sumSquares >= 0.00000001) {
99fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        correlation = (float) (2.0 * sumProducts / sumSquares);
100fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
101fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    return correlation;
102fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}
103fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
104fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic int calculateCorrelations(const float *haystack, int haystackSize,
105fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                 const float *needle, int needleSize,
106fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                 float *results, int resultSize)
107fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{
108fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int maxCorrelations = haystackSize - needleSize;
109fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int numCorrelations = std::min(maxCorrelations, resultSize);
110fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
111fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    for (int ic = 0; ic < numCorrelations; ic++) {
112fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
113fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        results[ic] = correlation;
114fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
115fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
116fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    return numCorrelations;
117fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}
118fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
119fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/*==========================================================================================*/
120fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/**
121fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Scan until we get a correlation of a single scan that goes over the tolerance level,
122fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * peaks then drops back down.
123fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */
124fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic double findFirstMatch(const float *haystack, int haystackSize,
125fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                             const float *needle, int needleSize, double threshold  )
126fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{
127fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int ic;
128fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // How many correlations can we calculate?
129fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int numCorrelations = haystackSize - needleSize;
130fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double maxCorrelation = 0.0;
131fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int peakIndex = -1;
132fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double location = -1.0;
133fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    const double backThresholdScaler = 0.5;
134fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
135fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    for (ic = 0; ic < numCorrelations; ic++) {
136fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        double correlation = calculateCorrelation(&haystack[ic], needle, needleSize);
137fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
138fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if( (correlation > maxCorrelation) ) {
139fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            maxCorrelation = correlation;
140fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            peakIndex = ic;
141fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
142fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
143fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n",
144fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        //    ic, correlation, maxSum );
145fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // Are we past what we were looking for?
146fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) {
147fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            location = peakIndex;
148fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            break;
149fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
150fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
151fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
152fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    return location;
153fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}
154fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
155fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burktypedef struct LatencyReport_s {
156fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double latencyInFrames;
157fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double confidence;
158fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} LatencyReport;
159fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
160fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental.
161fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// Using first echo instead of the original impulse for a better match.
162fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic int measureLatencyFromEchos(const float *haystack, int haystackSize,
163fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                            const float *needle, int needleSize,
164fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                            LatencyReport *report) {
165fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    const double threshold = 0.1;
1664a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n",
1674a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk           haystackSize, needleSize);
168fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
169fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Find first peak
170fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int first = (int) (findFirstMatch(haystack,
171fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      haystackSize,
172fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      needle,
173fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      needleSize,
174fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      threshold) + 0.5);
175fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
176fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Use first echo as the needle for the other echos because
177fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // it will be more similar.
178fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    needle = &haystack[first];
179fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int again = (int) (findFirstMatch(haystack,
180fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      haystackSize,
181fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      needle,
182fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      needleSize,
183fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                      threshold) + 0.5);
184fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
1854a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again);
186fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    first = again;
187fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
188fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Allocate results array
189fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int remaining = haystackSize - first;
190fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value
191fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int numCorrelations = std::min(remaining, maxReasonableLatencyFrames);
192fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float *correlations = new float[numCorrelations];
193fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float *harmonicSums = new float[numCorrelations](); // set to zero
194fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
195fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Generate correlation for every position.
196fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    numCorrelations = calculateCorrelations(&haystack[first], remaining,
197fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                            needle, needleSize,
198fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                            correlations, numCorrelations);
199fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
200fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Add higher harmonics mapped onto lower harmonics.
201fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // This reinforces the "fundamental" echo.
202fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    const int numEchoes = 10;
203fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    for (int partial = 1; partial < numEchoes; partial++) {
204fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        for (int i = 0; i < numCorrelations; i++) {
205fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            harmonicSums[i / partial] += correlations[i] / partial;
206fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
207fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
208fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
209fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Find highest peak in correlation array.
210fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float maxCorrelation = 0.0;
211fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float sumOfPeaks = 0.0;
212fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int peakIndex = 0;
213fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins
214fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    for (int i = skip; i < numCorrelations; i++) {
215fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if (harmonicSums[i] > maxCorrelation) {
216fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            maxCorrelation = harmonicSums[i];
217fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            sumOfPeaks += maxCorrelation;
218fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            peakIndex = i;
219fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex);
220fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
221fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
222fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
223fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    report->latencyInFrames = peakIndex;
224fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    if (sumOfPeaks < 0.0001) {
225fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        report->confidence = 0.0;
226fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    } else {
227fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        report->confidence = maxCorrelation / sumOfPeaks;
228fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
229fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
230fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    delete[] correlations;
231fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    delete[] harmonicSums;
232fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    return 0;
233fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}
234fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
235fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass AudioRecording
236fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{
237fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic:
238fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    AudioRecording() {
239fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
240fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    ~AudioRecording() {
241fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        delete[] mData;
242fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
243fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
244fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void allocate(int maxFrames) {
245fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        delete[] mData;
246fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mData = new float[maxFrames];
247fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mMaxFrames = maxFrames;
248fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
249fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
250fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Write SHORT data from the first channel.
251fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int write(int16_t *inputData, int inputChannelCount, int numFrames) {
252fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // stop at end of buffer
253fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if ((mFrameCounter + numFrames) > mMaxFrames) {
254fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            numFrames = mMaxFrames - mFrameCounter;
255fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
256fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        for (int i = 0; i < numFrames; i++) {
257fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768);
258fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
259fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return numFrames;
260fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
261fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
262fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Write FLOAT data from the first channel.
263fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int write(float *inputData, int inputChannelCount, int numFrames) {
264fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // stop at end of buffer
265fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if ((mFrameCounter + numFrames) > mMaxFrames) {
266fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            numFrames = mMaxFrames - mFrameCounter;
267fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
268fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        for (int i = 0; i < numFrames; i++) {
269fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            mData[mFrameCounter++] = inputData[i * inputChannelCount];
270fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
271fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return numFrames;
272fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
273fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
274fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int size() {
275fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return mFrameCounter;
276fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
277fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
278fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float *getData() {
279fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return mData;
280fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
281fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
2824a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    void setSampleRate(int32_t sampleRate) {
2834a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        mSampleRate = sampleRate;
2844a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
2854a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
2864a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    int32_t getSampleRate() {
2874a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mSampleRate;
2884a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
2894a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
290fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int save(const char *fileName, bool writeShorts = true) {
2914a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        SNDFILE *sndFile = nullptr;
292fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        int written = 0;
2934a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        SF_INFO info = {
2944a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                .frames = mFrameCounter,
2954a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                .samplerate = mSampleRate,
2964a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                .channels = 1,
2974a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT)
2984a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        };
2994a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3004a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        sndFile = sf_open(fileName, SFM_WRITE, &info);
3014a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        if (sndFile == nullptr) {
3024a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            printf("AudioRecording::save(%s) failed to open file\n", fileName);
303fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            return -errno;
304fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
305fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
3064a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        written = sf_writef_float(sndFile, mData, mFrameCounter);
3074a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3084a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        sf_close(sndFile);
309fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return written;
310fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
311fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
3124a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    int load(const char *fileName) {
3134a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        SNDFILE *sndFile = nullptr;
3144a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        SF_INFO info;
3154a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3164a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        sndFile = sf_open(fileName, SFM_READ, &info);
3174a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        if (sndFile == nullptr) {
3184a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            printf("AudioRecording::load(%s) failed to open file\n", fileName);
3194a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            return -errno;
3204a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        }
3214a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3224a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        assert(info.channels == 1);
3234a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3244a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        allocate(info.frames);
3254a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        mFrameCounter = sf_readf_float(sndFile, mData, info.frames);
3264a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3274a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        sf_close(sndFile);
3284a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mFrameCounter;
3294a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
3304a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
331fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate:
332fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float  *mData = nullptr;
333fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t mFrameCounter = 0;
334fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t mMaxFrames = 0;
3354a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    int32_t mSampleRate = 48000; // common default
336fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
337fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
338fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// ====================================================================================
339fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass LoopbackProcessor {
340fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic:
341fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual ~LoopbackProcessor() = default;
342fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
343fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
344fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual void reset() {}
345fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
346fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual void process(float *inputData, int inputChannelCount,
347fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                 float *outputData, int outputChannelCount,
348fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                 int numFrames) = 0;
349fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
350fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
351fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual void report() = 0;
352fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
353fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual void printStatus() {};
354fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
3554a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    virtual int getResult() {
3564a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return -1;
3574a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
3584a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
359fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual bool isDone() {
360fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return false;
361fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
362fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
3634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    virtual int save(const char *fileName) {
3644a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        (void) fileName;
3654a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return AAUDIO_ERROR_UNIMPLEMENTED;
3664a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
3674a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3684a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    virtual int load(const char *fileName) {
3694a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        (void) fileName;
3704a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return AAUDIO_ERROR_UNIMPLEMENTED;
3714a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
3724a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
3734a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    virtual void setSampleRate(int32_t sampleRate) {
374fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mSampleRate = sampleRate;
375fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
376fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
377fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t getSampleRate() {
378fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return mSampleRate;
379fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
380fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
381fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    // Measure peak amplitude of buffer.
382fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) {
383fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        float peak = 0.0f;
384fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        for (int i = 0; i < numFrames; i++) {
385fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            float pos = fabs(*inputData);
386fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            if (pos > peak) {
387fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                peak = pos;
388fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            }
389fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            inputData += inputChannelCount;
390fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
391fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return peak;
392fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
393fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
394fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
395fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate:
396fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t mSampleRate = LOOPBACK_SAMPLE_RATE;
397fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
398fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
399fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass PeakDetector {
400fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic:
401fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float process(float input) {
402fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        float output = mPrevious * mDecay;
403fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if (input > output) {
404fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            output = input;
405fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
406fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mPrevious = output;
407fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return output;
408fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
409fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
410fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate:
411fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float  mDecay = 0.99f;
412fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float  mPrevious = 0.0f;
413fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
414fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
415fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
416fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic void printAudioScope(float sample) {
4174e98efa3b18ba1d72d28358da86f9beb5596588dPhil Burk    const int maxStars = 80; // arbitrary, fits on one line
418fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    char c = '*';
419fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    if (sample < -1.0) {
420fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        sample = -1.0;
421fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        c = '$';
422fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    } else if (sample > 1.0) {
423fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        sample = 1.0;
424fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        c = '$';
425fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
426fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars);
427fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    for (int i = 0; i < numSpaces; i++) {
428fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        putchar(' ');
429fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
430fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    printf("%c\n", c);
431fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}
432fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
433fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// ====================================================================================
434fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/**
435fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Measure latency given a loopback stream data.
436fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Uses a state machine to cycle through various stages including:
437fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *
438fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */
439fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass EchoAnalyzer : public LoopbackProcessor {
440fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic:
441fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
442fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    EchoAnalyzer() : LoopbackProcessor() {
4434a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        mAudioRecording.allocate(2 * getSampleRate());
4444a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        mAudioRecording.setSampleRate(getSampleRate());
4454a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
4464a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
4474a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    void setSampleRate(int32_t sampleRate) override {
4484a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        LoopbackProcessor::setSampleRate(sampleRate);
4494a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        mAudioRecording.setSampleRate(sampleRate);
450fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
451fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
452fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void reset() override {
453fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mDownCounter = 200;
454fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mLoopCounter = 0;
455fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mMeasuredLoopGain = 0.0f;
456fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mEchoGain = 1.0f;
457fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mState = STATE_INITIAL_SILENCE;
458fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
459fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
4604a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    virtual int getResult() {
4614a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mState == STATE_DONE ? 0 : -1;
4624a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
4634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
464fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    virtual bool isDone() {
4654a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mState == STATE_DONE || mState == STATE_FAILED;
466fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
467fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
468fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void setGain(float gain) {
469fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mEchoGain = gain;
470fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
471fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
472fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float getGain() {
473fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return mEchoGain;
474fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
475fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
476fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void report() override {
477fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
478fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf("EchoAnalyzer ---------------\n");
479fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "measured.gain          = %f\n", mMeasuredLoopGain);
480fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "echo.gain              = %f\n", mEchoGain);
481fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "test.state             = %d\n", mState);
482fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if (mMeasuredLoopGain >= 0.9999) {
483fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            printf("   ERROR - clipping, turn down volume slightly\n");
484fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        } else {
485fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            const float *needle = s_Impulse;
486fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            int needleSize = (int) (sizeof(s_Impulse) / sizeof(float));
4874a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            float *haystack = mAudioRecording.getData();
4884a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            int haystackSize = mAudioRecording.size();
4894a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport);
4904a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk            if (mLatencyReport.confidence < 0.01) {
4914a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                printf("   ERROR - confidence too low = %f\n", mLatencyReport.confidence);
492fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            } else {
4934a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate();
4944a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                printf(LOOPBACK_RESULT_TAG "latency.frames        = %8.2f\n", mLatencyReport.latencyInFrames);
495fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                printf(LOOPBACK_RESULT_TAG "latency.msec          = %8.2f\n", latencyMillis);
4964a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                printf(LOOPBACK_RESULT_TAG "latency.confidence    = %8.6f\n", mLatencyReport.confidence);
497fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            }
498fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
499fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
500fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
501fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void printStatus() override {
50274ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        printf("st = %d, echo gain = %f ", mState, mEchoGain);
503fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
504fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
505a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk    void sendImpulses(float *outputData, int outputChannelCount, int numFrames) {
506a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk        while (numFrames-- > 0) {
507a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk            float sample = s_Impulse[mSampleIndex++];
508a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk            if (mSampleIndex >= kImpulseSizeInFrames) {
509a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk                mSampleIndex = 0;
510a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk            }
511a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk
512fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            *outputData = sample;
513fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            outputData += outputChannelCount;
514fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
515fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
516fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
517a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk    void sendOneImpulse(float *outputData, int outputChannelCount) {
518a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk        mSampleIndex = 0;
519a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk        sendImpulses(outputData, outputChannelCount, kImpulseSizeInFrames);
520a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk    }
521a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk
522fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void process(float *inputData, int inputChannelCount,
523fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                 float *outputData, int outputChannelCount,
524fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                 int numFrames) override {
525fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        int channelsValid = std::min(inputChannelCount, outputChannelCount);
526fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        float peak = 0.0f;
527fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        int numWritten;
528fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        int numSamples;
529fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
530fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        echo_state_t nextState = mState;
531fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
532fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        switch (mState) {
533fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_INITIAL_SILENCE:
534fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                // Output silence at the beginning.
535fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                numSamples = numFrames * outputChannelCount;
536fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                for (int i = 0; i < numSamples; i++) {
537fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    outputData[i] = 0;
538fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
539fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (mDownCounter-- <= 0) {
540fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    nextState = STATE_MEASURING_GAIN;
541fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter);
542fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mDownCounter = 8;
543fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
544fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
545fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
546fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_MEASURING_GAIN:
547a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk                sendImpulses(outputData, outputChannelCount, numFrames);
548fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
549fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                // If we get several in a row then go to next state.
550fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (peak > mPulseThreshold) {
551fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    if (mDownCounter-- <= 0) {
552fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n",
553fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        //       mLoopCounter, peak);
554fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mDownCounter = 8;
555fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mMeasuredLoopGain = peak;  // assumes original pulse amplitude is one
556fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        // Calculate gain that will give us a nice decaying echo.
557fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
5584a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                        if (mEchoGain > MAX_ECHO_GAIN) {
5594a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                            printf("ERROR - loop gain too low. Increase the volume.\n");
5604a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                            nextState = STATE_FAILED;
5614a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                        } else {
5624a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                            nextState = STATE_WAITING_FOR_SILENCE;
5634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                        }
564fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    }
565a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk                } else if (numFrames > kImpulseSizeInFrames){ // ignore short callbacks
566fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mDownCounter = 8;
567fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
568fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
569fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
570fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_WAITING_FOR_SILENCE:
5714e98efa3b18ba1d72d28358da86f9beb5596588dPhil Burk                // Output silence and wait for the echos to die down.
572fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                numSamples = numFrames * outputChannelCount;
573fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                for (int i = 0; i < numSamples; i++) {
574fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    outputData[i] = 0;
575fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
576fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
577fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                // If we get several in a row then go to next state.
578fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (peak < mSilenceThreshold) {
579fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    if (mDownCounter-- <= 0) {
580fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        nextState = STATE_SENDING_PULSE;
581fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter);
582fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mDownCounter = 8;
583fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    }
584fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                } else {
585fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mDownCounter = 8;
586fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
587fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
588fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
589fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_SENDING_PULSE:
5904a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                mAudioRecording.write(inputData, inputChannelCount, numFrames);
591a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk                sendOneImpulse(outputData, outputChannelCount);
592fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                nextState = STATE_GATHERING_ECHOS;
593fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter);
594fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
595fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
596fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_GATHERING_ECHOS:
5974a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk                numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames);
598fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
599fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (peak > mMeasuredLoopGain) {
600fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mMeasuredLoopGain = peak;  // AGC might be raising gain so adjust it on the fly.
601fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    // Recalculate gain that will give us a nice decaying echo.
602fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mEchoGain = mDesiredEchoGain / mMeasuredLoopGain;
603fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
604fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                // Echo input to output.
605fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                for (int i = 0; i < numFrames; i++) {
606fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    int ic;
607fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    for (ic = 0; ic < channelsValid; ic++) {
608fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        outputData[ic] = inputData[ic] * mEchoGain;
609fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    }
610fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    for (; ic < outputChannelCount; ic++) {
611fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        outputData[ic] = 0;
612fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    }
613fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    inputData += inputChannelCount;
614fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    outputData += outputChannelCount;
615fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
616fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (numWritten  < numFrames) {
617fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    nextState = STATE_DONE;
618fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    //printf("%5d: switch to STATE_DONE\n", mLoopCounter);
619fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
620fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
621fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
622fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_DONE:
623fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            default:
624fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
625fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
626fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
627fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mState = nextState;
628fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mLoopCounter++;
629fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
630fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
6314a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    int save(const char *fileName) override {
6324a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mAudioRecording.save(fileName);
6334a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
6344a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
6354a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    int load(const char *fileName) override {
6364a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mAudioRecording.load(fileName);
6374a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
6384a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
639fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate:
640fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
641fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    enum echo_state_t {
642fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_INITIAL_SILENCE,
643fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_MEASURING_GAIN,
644fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_WAITING_FOR_SILENCE,
645fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_SENDING_PULSE,
646fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_GATHERING_ECHOS,
6474a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        STATE_DONE,
6484a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        STATE_FAILED
649fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    };
650fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
651a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk    int32_t         mDownCounter = 500;
652a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk    int32_t         mLoopCounter = 0;
653a2951c5943172b122cfbfd4098b4f6738eab207dPhil Burk    int32_t         mSampleIndex = 0;
6544a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    float           mPulseThreshold = 0.02f;
6554a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    float           mSilenceThreshold = 0.002f;
6564a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    float           mMeasuredLoopGain = 0.0f;
6574a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    float           mDesiredEchoGain = 0.95f;
6584a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    float           mEchoGain = 1.0f;
6594a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    echo_state_t    mState = STATE_INITIAL_SILENCE;
6604a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
6614a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    AudioRecording  mAudioRecording; // contains only the input after the gain detection burst
6624a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    LatencyReport   mLatencyReport;
6634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    // PeakDetector    mPeakDetector;
664fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
665fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
666fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
667fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// ====================================================================================
668fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/**
669fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Output a steady sinewave and analyze the return signal.
670fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *
671fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Use a cosine transform to measure the predicted magnitude and relative phase of the
672fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * looped back sine wave. Then generate a predicted signal and compare with the actual signal.
673fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */
674fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass SineAnalyzer : public LoopbackProcessor {
675fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic:
676fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
6774a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    virtual int getResult() {
6784a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk        return mState == STATE_LOCKED ? 0 : -1;
6794a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk    }
6804a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk
681fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void report() override {
682fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf("SineAnalyzer ------------------\n");
683fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "peak.amplitude     = %7.5f\n", mPeakAmplitude);
684fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "sine.magnitude     = %7.5f\n", mMagnitude);
685fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "phase.offset       = %7.5f\n", mPhaseOffset);
686fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "ref.phase          = %7.5f\n", mPhase);
687fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated);
68874ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        printf(LOOPBACK_RESULT_TAG "sine.period        = %6d\n", mSinePeriod);
689fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "test.state         = %6d\n", mState);
690fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        printf(LOOPBACK_RESULT_TAG "frame.count        = %6d\n", mFrameCounter);
691fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // Did we ever get a lock?
692fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0);
693fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if (!gotLock) {
694fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            printf("ERROR - failed to lock on reference sine tone\n");
695fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        } else {
696fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            // Only print if meaningful.
697fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            printf(LOOPBACK_RESULT_TAG "glitch.count       = %6d\n", mGlitchCount);
698fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
699fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
700fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
701fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void printStatus() override {
70274ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        printf("st = %d, #gl = %3d,", mState, mGlitchCount);
703fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
704fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
705fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double calculateMagnitude(double *phasePtr = NULL) {
706fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if (mFramesAccumulated == 0) {
707fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            return 0.0;
708fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
709fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        double sinMean = mSinAccumulator / mFramesAccumulated;
710fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        double cosMean = mCosAccumulator / mFramesAccumulated;
711fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean ));
712fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if( phasePtr != NULL )
713fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        {
714fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            double phase = M_PI_2 - atan2( sinMean, cosMean );
715fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            *phasePtr = phase;
716fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
717fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        return magnitude;
718fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
719fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
720fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    /**
721fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     * @param inputData contains microphone data with sine signal feedback
722fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     * @param outputData contains the reference sine wave
723fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk     */
724fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void process(float *inputData, int inputChannelCount,
725fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                 float *outputData, int outputChannelCount,
726fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                 int numFrames) override {
72774ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        mProcessCount++;
72874ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk
729fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames);
730fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        if (peak > mPeakAmplitude) {
731fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            mPeakAmplitude = peak;
732fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
733fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
734fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        for (int i = 0; i < numFrames; i++) {
735fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            float sample = inputData[i * inputChannelCount];
736fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
737fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            float sinOut = sinf(mPhase);
738fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
739fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            switch (mState) {
74074ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                case STATE_IDLE:
741fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                case STATE_IMMUNE:
742fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                case STATE_WAITING_FOR_SIGNAL:
743fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    break;
744fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                case STATE_WAITING_FOR_LOCK:
745fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mSinAccumulator += sample * sinOut;
746fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mCosAccumulator += sample * cosf(mPhase);
747fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mFramesAccumulated++;
748fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    // Must be a multiple of the period or the calculation will not be accurate.
74974ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    if (mFramesAccumulated == mSinePeriod * PERIODS_NEEDED_FOR_LOCK) {
750fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mPhaseOffset = 0.0;
751fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mMagnitude = calculateMagnitude(&mPhaseOffset);
752fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        if (mMagnitude > mThreshold) {
753fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                            if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) {
754fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                mState = STATE_LOCKED;
755fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                                //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter);
756fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                            }
757fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                            mPreviousPhaseOffset = mPhaseOffset;
758fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        }
759fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        resetAccumulator();
760fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    }
761fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    break;
762fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
763fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                case STATE_LOCKED: {
764fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    // Predict next sine value
765fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude;
766fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    // printf("    predicted = %f, actual = %f\n", predicted, sample);
767fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
768fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    float diff = predicted - sample;
769fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    if (fabs(diff) > mTolerance) {
770fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mGlitchCount++;
771fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n",
772fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        //       mFrameCounter, mGlitchCount, predicted, sample);
773fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        mState = STATE_IMMUNE;
774fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                        //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter);
77574ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        mDownCounter = mSinePeriod;  // Set duration of IMMUNE state.
77674ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    }
77774ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk
77874ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    // Track incoming signal and slowly adjust magnitude to account
77974ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    // for drift in the DRC or AGC.
78074ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    mSinAccumulator += sample * sinOut;
78174ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    mCosAccumulator += sample * cosf(mPhase);
78274ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    mFramesAccumulated++;
78374ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    // Must be a multiple of the period or the calculation will not be accurate.
78474ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                    if (mFramesAccumulated == mSinePeriod) {
78574ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        const double coefficient = 0.1;
78674ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        double phaseOffset = 0.0;
78774ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        double magnitude = calculateMagnitude(&phaseOffset);
78874ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        // One pole averaging filter.
78974ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        mMagnitude = (mMagnitude * (1.0 - coefficient)) + (magnitude * coefficient);
79074ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                        resetAccumulator();
791fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    }
792fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                } break;
793fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            }
794fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
795fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            // Output sine wave so we can measure it.
796fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude)
797fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude);
798fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut,  mPhaseIncrement);
799fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
800fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            // advance and wrap phase
801fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            mPhase += mPhaseIncrement;
802fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            if (mPhase > M_PI) {
803fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                mPhase -= (2.0 * M_PI);
804fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            }
805fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
806fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            mFrameCounter++;
807fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
808fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
809fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        // Do these once per buffer.
810fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        switch (mState) {
81174ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk            case STATE_IDLE:
81274ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                mState = STATE_IMMUNE; // so we can tell when
81374ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk                break;
814fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_IMMUNE:
815fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                mDownCounter -= numFrames;
816fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (mDownCounter <= 0) {
817fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mState = STATE_WAITING_FOR_SIGNAL;
818fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter);
819fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
820fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
821fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_WAITING_FOR_SIGNAL:
822fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                if (peak > mThreshold) {
823fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    mState = STATE_WAITING_FOR_LOCK;
824fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter);
825fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                    resetAccumulator();
826fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                }
827fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
828fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_WAITING_FOR_LOCK:
829fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk            case STATE_LOCKED:
830fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk                break;
831fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        }
832fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
833fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
834fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
835fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void resetAccumulator() {
836fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mFramesAccumulated = 0;
837fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mSinAccumulator = 0.0;
838fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mCosAccumulator = 0.0;
839fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
840fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
841fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    void reset() override {
842fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mGlitchCount = 0;
843fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        mState = STATE_IMMUNE;
84474ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        mDownCounter = IMMUNE_FRAME_COUNT;
84574ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        mPhaseIncrement = 2.0 * M_PI / mSinePeriod;
84674ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        printf("phaseInc = %f for period %d\n", mPhaseIncrement, mSinePeriod);
847fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        resetAccumulator();
84874ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        mProcessCount = 0;
849fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    }
850fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
851fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate:
852fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
853fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    enum sine_state_t {
85474ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        STATE_IDLE,
855fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_IMMUNE,
856fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_WAITING_FOR_SIGNAL,
857fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_WAITING_FOR_LOCK,
858fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk        STATE_LOCKED
859fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    };
860fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
86174ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk    enum constants {
86274ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        IMMUNE_FRAME_COUNT = 48 * 500,
86374ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk        PERIODS_NEEDED_FOR_LOCK = 8
86474ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk    };
86574ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk
86674ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk    int     mSinePeriod = 79;
867fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mPhaseIncrement = 0.0;
868fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mPhase = 0.0;
869fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mPhaseOffset = 0.0;
870fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mPreviousPhaseOffset = 0.0;
871fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mMagnitude = 0.0;
872fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mThreshold = 0.005;
873fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mTolerance = 0.01;
874fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t mFramesAccumulated = 0;
87574ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk    int32_t mProcessCount = 0;
876fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mSinAccumulator = 0.0;
877fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mCosAccumulator = 0.0;
878fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t mGlitchCount = 0;
879fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    double  mPeakAmplitude = 0.0;
88074ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk    int     mDownCounter = IMMUNE_FRAME_COUNT;
881fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    int32_t mFrameCounter = 0;
882fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float   mOutputAmplitude = 0.75;
883fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
884fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    PseudoRandom  mWhiteNoise;
885fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk    float   mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC.
886fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
88774ce5e8273a1c3f6ea14ab087f66f7d32ad106d7Phil Burk    sine_state_t  mState = STATE_IDLE;
888fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk};
889fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
890fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
891fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#undef LOOPBACK_SAMPLE_RATE
892fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#undef LOOPBACK_RESULT_TAG
893fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk
894fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */
895