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