LoopbackAnalyzer.h revision 4a764a3b450095cef05b6025a72c3876c95b6a14
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 44fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic const float s_Impulse[] = { 45fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 0.0f, 0.0f, 0.0f, 0.0f, 0.2f, // silence on each side of the impulse 46fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 0.5f, 0.9999f, 0.0f, -0.9999, -0.5f, // bipolar 47fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk -0.2f, 0.0f, 0.0f, 0.0f, 0.0f 48fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 49fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 50fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass PseudoRandom { 51fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic: 52fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk PseudoRandom() {} 53fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk PseudoRandom(int64_t seed) 54fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk : mSeed(seed) 55fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk {} 56fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 57fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk /** 58fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Returns the next random double from -1.0 to 1.0 59fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * 60fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * @return value from -1.0 to 1.0 61fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */ 62fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double nextRandomDouble() { 63fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return nextRandomInteger() * (0.5 / (((int32_t)1) << 30)); 64fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 65fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 66fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk /** Calculate random 32 bit number using linear-congruential method. */ 67fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t nextRandomInteger() { 68fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Use values for 64-bit sequence from MMIX by Donald Knuth. 69fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mSeed = (mSeed * (int64_t)6364136223846793005) + (int64_t)1442695040888963407; 70fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return (int32_t) (mSeed >> 32); // The higher bits have a longer sequence. 71fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 72fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 73fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate: 74fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int64_t mSeed = 99887766; 75fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 76fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 77fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic double calculateCorrelation(const float *a, 78fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const float *b, 79fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int windowSize) 80fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{ 81fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double correlation = 0.0; 82fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double sumProducts = 0.0; 83fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double sumSquares = 0.0; 84fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 85fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Correlate a against b. 86fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < windowSize; i++) { 87fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float s1 = a[i]; 88fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float s2 = b[i]; 89fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Use a normalized cross-correlation. 90fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sumProducts += s1 * s2; 91fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sumSquares += ((s1 * s1) + (s2 * s2)); 92fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 93fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 94fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (sumSquares >= 0.00000001) { 95fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk correlation = (float) (2.0 * sumProducts / sumSquares); 96fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 97fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return correlation; 98fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} 99fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 100fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic int calculateCorrelations(const float *haystack, int haystackSize, 101fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const float *needle, int needleSize, 102fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *results, int resultSize) 103fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{ 104fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int maxCorrelations = haystackSize - needleSize; 105fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numCorrelations = std::min(maxCorrelations, resultSize); 106fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 107fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int ic = 0; ic < numCorrelations; ic++) { 108fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double correlation = calculateCorrelation(&haystack[ic], needle, needleSize); 109fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk results[ic] = correlation; 110fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 111fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 112fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return numCorrelations; 113fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} 114fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 115fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/*==========================================================================================*/ 116fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/** 117fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Scan until we get a correlation of a single scan that goes over the tolerance level, 118fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * peaks then drops back down. 119fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */ 120fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic double findFirstMatch(const float *haystack, int haystackSize, 121fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const float *needle, int needleSize, double threshold ) 122fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{ 123fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int ic; 124fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // How many correlations can we calculate? 125fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numCorrelations = haystackSize - needleSize; 126fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double maxCorrelation = 0.0; 127fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int peakIndex = -1; 128fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double location = -1.0; 129fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const double backThresholdScaler = 0.5; 130fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 131fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (ic = 0; ic < numCorrelations; ic++) { 132fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double correlation = calculateCorrelation(&haystack[ic], needle, needleSize); 133fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 134fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if( (correlation > maxCorrelation) ) { 135fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk maxCorrelation = correlation; 136fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk peakIndex = ic; 137fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 138fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 139fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("PaQa_FindFirstMatch: ic = %4d, correlation = %8f, maxSum = %8f\n", 140fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // ic, correlation, maxSum ); 141fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Are we past what we were looking for? 142fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if((maxCorrelation > threshold) && (correlation < backThresholdScaler * maxCorrelation)) { 143fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk location = peakIndex; 144fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 145fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 146fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 147fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 148fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return location; 149fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} 150fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 151fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burktypedef struct LatencyReport_s { 152fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double latencyInFrames; 153fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double confidence; 154fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} LatencyReport; 155fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 156fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// Apply a technique similar to Harmonic Product Spectrum Analysis to find echo fundamental. 157fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// Using first echo instead of the original impulse for a better match. 158fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic int measureLatencyFromEchos(const float *haystack, int haystackSize, 159fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const float *needle, int needleSize, 160fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk LatencyReport *report) { 161fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const double threshold = 0.1; 1624a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf("measureLatencyFromEchos: haystackSize = %d, needleSize = %d\n", 1634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk haystackSize, needleSize); 164fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 165fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Find first peak 166fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int first = (int) (findFirstMatch(haystack, 167fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk haystackSize, 168fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk needle, 169fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk needleSize, 170fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk threshold) + 0.5); 171fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 172fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Use first echo as the needle for the other echos because 173fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // it will be more similar. 174fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk needle = &haystack[first]; 175fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int again = (int) (findFirstMatch(haystack, 176fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk haystackSize, 177fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk needle, 178fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk needleSize, 179fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk threshold) + 0.5); 180fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 1814a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf("measureLatencyFromEchos: first = %d, again at %d\n", first, again); 182fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk first = again; 183fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 184fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Allocate results array 185fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int remaining = haystackSize - first; 186fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const int maxReasonableLatencyFrames = 48000 * 2; // arbitrary but generous value 187fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numCorrelations = std::min(remaining, maxReasonableLatencyFrames); 188fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *correlations = new float[numCorrelations]; 189fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *harmonicSums = new float[numCorrelations](); // set to zero 190fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 191fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Generate correlation for every position. 192fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk numCorrelations = calculateCorrelations(&haystack[first], remaining, 193fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk needle, needleSize, 194fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk correlations, numCorrelations); 195fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 196fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Add higher harmonics mapped onto lower harmonics. 197fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // This reinforces the "fundamental" echo. 198fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const int numEchoes = 10; 199fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int partial = 1; partial < numEchoes; partial++) { 200fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numCorrelations; i++) { 201fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk harmonicSums[i / partial] += correlations[i] / partial; 202fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 203fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 204fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 205fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Find highest peak in correlation array. 206fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float maxCorrelation = 0.0; 207fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float sumOfPeaks = 0.0; 208fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int peakIndex = 0; 209fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const int skip = MAX_ZEROTH_PARTIAL_BINS; // skip low bins 210fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = skip; i < numCorrelations; i++) { 211fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (harmonicSums[i] > maxCorrelation) { 212fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk maxCorrelation = harmonicSums[i]; 213fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sumOfPeaks += maxCorrelation; 214fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk peakIndex = i; 215fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("maxCorrelation = %f at %d\n", maxCorrelation, peakIndex); 216fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 217fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 218fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 219fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk report->latencyInFrames = peakIndex; 220fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (sumOfPeaks < 0.0001) { 221fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk report->confidence = 0.0; 222fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else { 223fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk report->confidence = maxCorrelation / sumOfPeaks; 224fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 225fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 226fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk delete[] correlations; 227fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk delete[] harmonicSums; 228fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return 0; 229fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} 230fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 231fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass AudioRecording 232fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk{ 233fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic: 234fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk AudioRecording() { 235fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 236fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk ~AudioRecording() { 237fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk delete[] mData; 238fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 239fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 240fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void allocate(int maxFrames) { 241fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk delete[] mData; 242fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mData = new float[maxFrames]; 243fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mMaxFrames = maxFrames; 244fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 245fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 246fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Write SHORT data from the first channel. 247fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int write(int16_t *inputData, int inputChannelCount, int numFrames) { 248fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // stop at end of buffer 249fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if ((mFrameCounter + numFrames) > mMaxFrames) { 250fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk numFrames = mMaxFrames - mFrameCounter; 251fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 252fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numFrames; i++) { 253fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mData[mFrameCounter++] = inputData[i * inputChannelCount] * (1.0f / 32768); 254fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 255fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return numFrames; 256fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 257fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 258fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Write FLOAT data from the first channel. 259fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int write(float *inputData, int inputChannelCount, int numFrames) { 260fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // stop at end of buffer 261fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if ((mFrameCounter + numFrames) > mMaxFrames) { 262fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk numFrames = mMaxFrames - mFrameCounter; 263fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 264fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numFrames; i++) { 265fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mData[mFrameCounter++] = inputData[i * inputChannelCount]; 266fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 267fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return numFrames; 268fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 269fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 270fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int size() { 271fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return mFrameCounter; 272fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 273fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 274fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *getData() { 275fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return mData; 276fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 277fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 2784a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk void setSampleRate(int32_t sampleRate) { 2794a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk mSampleRate = sampleRate; 2804a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 2814a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 2824a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int32_t getSampleRate() { 2834a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mSampleRate; 2844a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 2854a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 286fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int save(const char *fileName, bool writeShorts = true) { 2874a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk SNDFILE *sndFile = nullptr; 288fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int written = 0; 2894a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk SF_INFO info = { 2904a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk .frames = mFrameCounter, 2914a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk .samplerate = mSampleRate, 2924a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk .channels = 1, 2934a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk .format = SF_FORMAT_WAV | (writeShorts ? SF_FORMAT_PCM_16 : SF_FORMAT_FLOAT) 2944a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk }; 2954a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 2964a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk sndFile = sf_open(fileName, SFM_WRITE, &info); 2974a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk if (sndFile == nullptr) { 2984a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf("AudioRecording::save(%s) failed to open file\n", fileName); 299fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return -errno; 300fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 301fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 3024a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk written = sf_writef_float(sndFile, mData, mFrameCounter); 3034a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3044a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk sf_close(sndFile); 305fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return written; 306fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 307fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 3084a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int load(const char *fileName) { 3094a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk SNDFILE *sndFile = nullptr; 3104a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk SF_INFO info; 3114a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3124a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk sndFile = sf_open(fileName, SFM_READ, &info); 3134a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk if (sndFile == nullptr) { 3144a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf("AudioRecording::load(%s) failed to open file\n", fileName); 3154a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return -errno; 3164a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 3174a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3184a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk assert(info.channels == 1); 3194a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3204a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk allocate(info.frames); 3214a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk mFrameCounter = sf_readf_float(sndFile, mData, info.frames); 3224a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3234a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk sf_close(sndFile); 3244a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mFrameCounter; 3254a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 3264a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 327fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate: 328fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *mData = nullptr; 329fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t mFrameCounter = 0; 330fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t mMaxFrames = 0; 3314a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int32_t mSampleRate = 48000; // common default 332fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 333fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 334fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// ==================================================================================== 335fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass LoopbackProcessor { 336fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic: 337fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual ~LoopbackProcessor() = default; 338fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 339fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 340fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual void reset() {} 341fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 342fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual void process(float *inputData, int inputChannelCount, 343fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *outputData, int outputChannelCount, 344fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numFrames) = 0; 345fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 346fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 347fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual void report() = 0; 348fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 349fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual void printStatus() {}; 350fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 3514a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk virtual int getResult() { 3524a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return -1; 3534a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 3544a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 355fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual bool isDone() { 356fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return false; 357fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 358fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 3594a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk virtual int save(const char *fileName) { 3604a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk (void) fileName; 3614a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return AAUDIO_ERROR_UNIMPLEMENTED; 3624a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 3634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3644a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk virtual int load(const char *fileName) { 3654a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk (void) fileName; 3664a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return AAUDIO_ERROR_UNIMPLEMENTED; 3674a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 3684a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 3694a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk virtual void setSampleRate(int32_t sampleRate) { 370fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mSampleRate = sampleRate; 371fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 372fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 373fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t getSampleRate() { 374fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return mSampleRate; 375fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 376fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 377fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Measure peak amplitude of buffer. 378fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk static float measurePeakAmplitude(float *inputData, int inputChannelCount, int numFrames) { 379fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float peak = 0.0f; 380fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numFrames; i++) { 381fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float pos = fabs(*inputData); 382fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (pos > peak) { 383fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk peak = pos; 384fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 385fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk inputData += inputChannelCount; 386fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 387fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return peak; 388fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 389fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 390fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 391fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate: 392fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t mSampleRate = LOOPBACK_SAMPLE_RATE; 393fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 394fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 395fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass PeakDetector { 396fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic: 397fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float process(float input) { 398fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float output = mPrevious * mDecay; 399fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (input > output) { 400fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk output = input; 401fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 402fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPrevious = output; 403fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return output; 404fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 405fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 406fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate: 407fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float mDecay = 0.99f; 408fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float mPrevious = 0.0f; 409fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 410fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 411fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 412fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkstatic void printAudioScope(float sample) { 413fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const int maxStars = 80 414fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk ; // arbitrary, fits on one line 415fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk char c = '*'; 416fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (sample < -1.0) { 417fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sample = -1.0; 418fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk c = '$'; 419fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else if (sample > 1.0) { 420fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sample = 1.0; 421fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk c = '$'; 422fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 423fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numSpaces = (int) (((sample + 1.0) * 0.5) * maxStars); 424fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numSpaces; i++) { 425fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk putchar(' '); 426fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 427fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("%c\n", c); 428fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk} 429fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 430fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// ==================================================================================== 431fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/** 432fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Measure latency given a loopback stream data. 433fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Uses a state machine to cycle through various stages including: 434fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * 435fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */ 436fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass EchoAnalyzer : public LoopbackProcessor { 437fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic: 438fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 439fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk EchoAnalyzer() : LoopbackProcessor() { 4404a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk mAudioRecording.allocate(2 * getSampleRate()); 4414a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk mAudioRecording.setSampleRate(getSampleRate()); 4424a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 4434a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 4444a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk void setSampleRate(int32_t sampleRate) override { 4454a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk LoopbackProcessor::setSampleRate(sampleRate); 4464a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk mAudioRecording.setSampleRate(sampleRate); 447fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 448fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 449fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void reset() override { 450fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = 200; 451fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mLoopCounter = 0; 452fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mMeasuredLoopGain = 0.0f; 453fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mEchoGain = 1.0f; 454fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = STATE_INITIAL_SILENCE; 455fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 456fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 4574a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk virtual int getResult() { 4584a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mState == STATE_DONE ? 0 : -1; 4594a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 4604a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 461fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk virtual bool isDone() { 4624a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mState == STATE_DONE || mState == STATE_FAILED; 463fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 464fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 465fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void setGain(float gain) { 466fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mEchoGain = gain; 467fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 468fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 469fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float getGain() { 470fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return mEchoGain; 471fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 472fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 473fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void report() override { 474fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 475fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("EchoAnalyzer ---------------\n"); 476fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "measured.gain = %f\n", mMeasuredLoopGain); 477fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "echo.gain = %f\n", mEchoGain); 478fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "test.state = %d\n", mState); 479fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mMeasuredLoopGain >= 0.9999) { 480fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(" ERROR - clipping, turn down volume slightly\n"); 481fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else { 482fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk const float *needle = s_Impulse; 483fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int needleSize = (int) (sizeof(s_Impulse) / sizeof(float)); 4844a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk float *haystack = mAudioRecording.getData(); 4854a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int haystackSize = mAudioRecording.size(); 4864a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk measureLatencyFromEchos(haystack, haystackSize, needle, needleSize, &mLatencyReport); 4874a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk if (mLatencyReport.confidence < 0.01) { 4884a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf(" ERROR - confidence too low = %f\n", mLatencyReport.confidence); 489fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else { 4904a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk double latencyMillis = 1000.0 * mLatencyReport.latencyInFrames / getSampleRate(); 4914a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf(LOOPBACK_RESULT_TAG "latency.frames = %8.2f\n", mLatencyReport.latencyInFrames); 492fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "latency.msec = %8.2f\n", latencyMillis); 4934a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf(LOOPBACK_RESULT_TAG "latency.confidence = %8.6f\n", mLatencyReport.confidence); 494fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 495fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 496fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 497fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 498fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void printStatus() override { 499fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("state = %d, echo gain = %f ", mState, mEchoGain); 500fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 501fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 502fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk static void sendImpulse(float *outputData, int outputChannelCount) { 503fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (float sample : s_Impulse) { 504fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *outputData = sample; 505fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData += outputChannelCount; 506fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 507fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 508fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 509fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void process(float *inputData, int inputChannelCount, 510fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *outputData, int outputChannelCount, 511fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numFrames) override { 512fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int channelsValid = std::min(inputChannelCount, outputChannelCount); 513fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float peak = 0.0f; 514fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numWritten; 515fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numSamples; 516fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 517fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk echo_state_t nextState = mState; 518fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 519fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk switch (mState) { 520fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_INITIAL_SILENCE: 521fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Output silence at the beginning. 522fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk numSamples = numFrames * outputChannelCount; 523fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numSamples; i++) { 524fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData[i] = 0; 525fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 526fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mDownCounter-- <= 0) { 527fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk nextState = STATE_MEASURING_GAIN; 528fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_MEASURING_GAIN\n", mLoopCounter); 529fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = 8; 530fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 531fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 532fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 533fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_MEASURING_GAIN: 534fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sendImpulse(outputData, outputChannelCount); 535fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 536fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // If we get several in a row then go to next state. 537fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (peak > mPulseThreshold) { 538fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mDownCounter-- <= 0) { 539fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_WAITING_FOR_SILENCE, measured peak = %f\n", 540fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // mLoopCounter, peak); 541fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = 8; 542fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mMeasuredLoopGain = peak; // assumes original pulse amplitude is one 543fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Calculate gain that will give us a nice decaying echo. 544fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mEchoGain = mDesiredEchoGain / mMeasuredLoopGain; 5454a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk if (mEchoGain > MAX_ECHO_GAIN) { 5464a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk printf("ERROR - loop gain too low. Increase the volume.\n"); 5474a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk nextState = STATE_FAILED; 5484a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } else { 5494a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk nextState = STATE_WAITING_FOR_SILENCE; 5504a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 551fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 552fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else { 553fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = 8; 554fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 555fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 556fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 557fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_WAITING_FOR_SILENCE: 558fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Output silence. 559fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk numSamples = numFrames * outputChannelCount; 560fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numSamples; i++) { 561fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData[i] = 0; 562fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 563fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 564fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // If we get several in a row then go to next state. 565fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (peak < mSilenceThreshold) { 566fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mDownCounter-- <= 0) { 567fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk nextState = STATE_SENDING_PULSE; 568fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_SENDING_PULSE\n", mLoopCounter); 569fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = 8; 570fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 571fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else { 572fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = 8; 573fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 574fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 575fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 576fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_SENDING_PULSE: 5774a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk mAudioRecording.write(inputData, inputChannelCount, numFrames); 578fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sendImpulse(outputData, outputChannelCount); 579fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk nextState = STATE_GATHERING_ECHOS; 580fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_GATHERING_ECHOS\n", mLoopCounter); 581fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 582fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 583fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_GATHERING_ECHOS: 5844a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk numWritten = mAudioRecording.write(inputData, inputChannelCount, numFrames); 585fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 586fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (peak > mMeasuredLoopGain) { 587fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mMeasuredLoopGain = peak; // AGC might be raising gain so adjust it on the fly. 588fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Recalculate gain that will give us a nice decaying echo. 589fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mEchoGain = mDesiredEchoGain / mMeasuredLoopGain; 590fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 591fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Echo input to output. 592fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numFrames; i++) { 593fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int ic; 594fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (ic = 0; ic < channelsValid; ic++) { 595fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData[ic] = inputData[ic] * mEchoGain; 596fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 597fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (; ic < outputChannelCount; ic++) { 598fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData[ic] = 0; 599fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 600fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk inputData += inputChannelCount; 601fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData += outputChannelCount; 602fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 603fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (numWritten < numFrames) { 604fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk nextState = STATE_DONE; 605fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_DONE\n", mLoopCounter); 606fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 607fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 608fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 609fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_DONE: 610fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk default: 611fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 612fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 613fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 614fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = nextState; 615fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mLoopCounter++; 616fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 617fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 6184a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int save(const char *fileName) override { 6194a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mAudioRecording.save(fileName); 6204a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 6214a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 6224a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int load(const char *fileName) override { 6234a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mAudioRecording.load(fileName); 6244a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 6254a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 626fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate: 627fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 628fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk enum echo_state_t { 629fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_INITIAL_SILENCE, 630fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_MEASURING_GAIN, 631fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_WAITING_FOR_SILENCE, 632fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_SENDING_PULSE, 633fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_GATHERING_ECHOS, 6344a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk STATE_DONE, 6354a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk STATE_FAILED 636fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk }; 637fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 6384a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int mDownCounter = 500; 6394a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk int mLoopCounter = 0; 6404a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk float mPulseThreshold = 0.02f; 6414a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk float mSilenceThreshold = 0.002f; 6424a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk float mMeasuredLoopGain = 0.0f; 6434a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk float mDesiredEchoGain = 0.95f; 6444a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk float mEchoGain = 1.0f; 6454a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk echo_state_t mState = STATE_INITIAL_SILENCE; 6464a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 6474a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk AudioRecording mAudioRecording; // contains only the input after the gain detection burst 6484a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk LatencyReport mLatencyReport; 6494a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk // PeakDetector mPeakDetector; 650fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 651fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 652fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 653fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk// ==================================================================================== 654fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk/** 655fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Output a steady sinewave and analyze the return signal. 656fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * 657fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * Use a cosine transform to measure the predicted magnitude and relative phase of the 658fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * looped back sine wave. Then generate a predicted signal and compare with the actual signal. 659fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */ 660fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkclass SineAnalyzer : public LoopbackProcessor { 661fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkpublic: 662fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 6634a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk virtual int getResult() { 6644a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk return mState == STATE_LOCKED ? 0 : -1; 6654a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk } 6664a764a3b450095cef05b6025a72c3876c95b6a14Phil Burk 667fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void report() override { 668fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("SineAnalyzer ------------------\n"); 669fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "peak.amplitude = %7.5f\n", mPeakAmplitude); 670fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "sine.magnitude = %7.5f\n", mMagnitude); 671fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "phase.offset = %7.5f\n", mPhaseOffset); 672fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "ref.phase = %7.5f\n", mPhase); 673fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "frames.accumulated = %6d\n", mFramesAccumulated); 674fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "sine.period = %6d\n", mPeriod); 675fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "test.state = %6d\n", mState); 676fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "frame.count = %6d\n", mFrameCounter); 677fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Did we ever get a lock? 678fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk bool gotLock = (mState == STATE_LOCKED) || (mGlitchCount > 0); 679fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (!gotLock) { 680fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("ERROR - failed to lock on reference sine tone\n"); 681fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } else { 682fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Only print if meaningful. 683fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(LOOPBACK_RESULT_TAG "glitch.count = %6d\n", mGlitchCount); 684fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 685fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 686fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 687fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void printStatus() override { 688fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf(" state = %d, glitches = %d,", mState, mGlitchCount); 689fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 690fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 691fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double calculateMagnitude(double *phasePtr = NULL) { 692fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mFramesAccumulated == 0) { 693fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return 0.0; 694fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 695fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double sinMean = mSinAccumulator / mFramesAccumulated; 696fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double cosMean = mCosAccumulator / mFramesAccumulated; 697fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double magnitude = 2.0 * sqrt( (sinMean * sinMean) + (cosMean * cosMean )); 698fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if( phasePtr != NULL ) 699fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk { 700fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double phase = M_PI_2 - atan2( sinMean, cosMean ); 701fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk *phasePtr = phase; 702fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 703fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk return magnitude; 704fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 705fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 706fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk /** 707fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * @param inputData contains microphone data with sine signal feedback 708fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk * @param outputData contains the reference sine wave 709fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk */ 710fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void process(float *inputData, int inputChannelCount, 711fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float *outputData, int outputChannelCount, 712fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int numFrames) override { 713fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float peak = measurePeakAmplitude(inputData, inputChannelCount, numFrames); 714fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (peak > mPeakAmplitude) { 715fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPeakAmplitude = peak; 716fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 717fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 718fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk for (int i = 0; i < numFrames; i++) { 719fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float sample = inputData[i * inputChannelCount]; 720fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 721fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float sinOut = sinf(mPhase); 722fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 723fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk switch (mState) { 724fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_IMMUNE: 725fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_WAITING_FOR_SIGNAL: 726fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 727fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_WAITING_FOR_LOCK: 728fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mSinAccumulator += sample * sinOut; 729fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mCosAccumulator += sample * cosf(mPhase); 730fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mFramesAccumulated++; 731fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Must be a multiple of the period or the calculation will not be accurate. 732fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mFramesAccumulated == mPeriod * 4) { 733fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPhaseOffset = 0.0; 734fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mMagnitude = calculateMagnitude(&mPhaseOffset); 735fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mMagnitude > mThreshold) { 736fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (fabs(mPreviousPhaseOffset - mPhaseOffset) < 0.001) { 737fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = STATE_LOCKED; 738fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_LOCKED\n", mFrameCounter); 739fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 740fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPreviousPhaseOffset = mPhaseOffset; 741fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 742fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk resetAccumulator(); 743fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 744fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 745fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 746fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_LOCKED: { 747fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Predict next sine value 748fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float predicted = sinf(mPhase + mPhaseOffset) * mMagnitude; 749fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // printf(" predicted = %f, actual = %f\n", predicted, sample); 750fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 751fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float diff = predicted - sample; 752fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (fabs(diff) > mTolerance) { 753fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mGlitchCount++; 754fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: Got a glitch # %d, predicted = %f, actual = %f\n", 755fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // mFrameCounter, mGlitchCount, predicted, sample); 756fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = STATE_IMMUNE; 757fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_IMMUNE\n", mFrameCounter); 758fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter = mPeriod; // Set duration of IMMUNE state. 759fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 760fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } break; 761fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 762fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 763fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Output sine wave so we can measure it. 764fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk outputData[i * outputChannelCount] = (sinOut * mOutputAmplitude) 765fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk + (mWhiteNoise.nextRandomDouble() * mNoiseAmplitude); 766fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // printf("%5d: sin(%f) = %f, %f\n", i, mPhase, sinOut, mPhaseIncrement); 767fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 768fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // advance and wrap phase 769fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPhase += mPhaseIncrement; 770fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mPhase > M_PI) { 771fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPhase -= (2.0 * M_PI); 772fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 773fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 774fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mFrameCounter++; 775fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 776fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 777fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk // Do these once per buffer. 778fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk switch (mState) { 779fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_IMMUNE: 780fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mDownCounter -= numFrames; 781fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (mDownCounter <= 0) { 782fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = STATE_WAITING_FOR_SIGNAL; 783fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_WAITING_FOR_SIGNAL\n", mFrameCounter); 784fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 785fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 786fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_WAITING_FOR_SIGNAL: 787fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk if (peak > mThreshold) { 788fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = STATE_WAITING_FOR_LOCK; 789fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk //printf("%5d: switch to STATE_WAITING_FOR_LOCK\n", mFrameCounter); 790fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk resetAccumulator(); 791fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 792fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 793fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_WAITING_FOR_LOCK: 794fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk case STATE_LOCKED: 795fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk break; 796fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 797fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 798fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 799fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 800fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void resetAccumulator() { 801fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mFramesAccumulated = 0; 802fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mSinAccumulator = 0.0; 803fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mCosAccumulator = 0.0; 804fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 805fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 806fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk void reset() override { 807fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mGlitchCount = 0; 808fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mState = STATE_IMMUNE; 809fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk mPhaseIncrement = 2.0 * M_PI / mPeriod; 810fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk printf("phaseInc = %f for period %d\n", mPhaseIncrement, mPeriod); 811fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk resetAccumulator(); 812fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk } 813fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 814fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burkprivate: 815fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 816fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk enum sine_state_t { 817fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_IMMUNE, 818fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_WAITING_FOR_SIGNAL, 819fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_WAITING_FOR_LOCK, 820fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk STATE_LOCKED 821fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk }; 822fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 823fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int mPeriod = 79; 824fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mPhaseIncrement = 0.0; 825fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mPhase = 0.0; 826fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mPhaseOffset = 0.0; 827fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mPreviousPhaseOffset = 0.0; 828fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mMagnitude = 0.0; 829fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mThreshold = 0.005; 830fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mTolerance = 0.01; 831fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t mFramesAccumulated = 0; 832fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mSinAccumulator = 0.0; 833fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mCosAccumulator = 0.0; 834fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t mGlitchCount = 0; 835fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk double mPeakAmplitude = 0.0; 836fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int mDownCounter = 4000; 837fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk int32_t mFrameCounter = 0; 838fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float mOutputAmplitude = 0.75; 839fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 840fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk PseudoRandom mWhiteNoise; 841fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk float mNoiseAmplitude = 0.00; // Used to experiment with warbling caused by DRC. 842fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 843fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk sine_state_t mState = STATE_IMMUNE; 844fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk}; 845fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 846fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 847fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#undef LOOPBACK_SAMPLE_RATE 848fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#undef LOOPBACK_RESULT_TAG 849fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk 850fcf9efd59e36759ee7df66fdfce4f8eedeb376e4Phil Burk#endif /* AAUDIO_EXAMPLES_LOOPBACK_ANALYSER_H */ 851