1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.audio; 18 19 import android.util.Log; 20 21 22 public class Correlation { 23 24 private int mBlockSize = 4096; 25 private int mSamplingRate = 44100; 26 private double [] mDataDownsampled = new double [mBlockSize]; 27 private double [] mDataAutocorrelated = new double[mBlockSize]; 28 29 public double mEstimatedLatencySamples = 0; 30 public double mEstimatedLatencyMs = 0; 31 public double mEstimatedLatencyConfidence = 0.0; 32 33 private double mAmplitudeThreshold = 0.001; // 0.001 = -60 dB noise 34 init(int blockSize, int samplingRate)35 public void init(int blockSize, int samplingRate) { 36 mBlockSize = blockSize; 37 mSamplingRate = samplingRate; 38 } 39 computeCorrelation(double [] data, int samplingRate)40 public boolean computeCorrelation(double [] data, int samplingRate) { 41 boolean status = false; 42 log("Started Auto Correlation for data with " + data.length + " points"); 43 mSamplingRate = samplingRate; 44 45 downsampleData(data, mDataDownsampled, mAmplitudeThreshold); 46 47 //correlation vector 48 autocorrelation(mDataDownsampled, mDataAutocorrelated); 49 50 int N = data.length; //all samples available 51 double groupSize = (double) N / mBlockSize; //samples per downsample point. 52 53 double maxValue = 0; 54 int maxIndex = -1; 55 56 double minLatencyMs = 8; //min latency expected. This algorithm should be improved. 57 int minIndex = (int)(0.5 + minLatencyMs * mSamplingRate / (groupSize*1000)); 58 59 double average = 0; 60 double rms = 0; 61 //find max 62 for (int i=minIndex; i<mDataAutocorrelated.length; i++) { 63 average += mDataAutocorrelated[i]; 64 rms += mDataAutocorrelated[i]*mDataAutocorrelated[i]; 65 if (mDataAutocorrelated[i] > maxValue) { 66 maxValue = mDataAutocorrelated[i]; 67 maxIndex = i; 68 } 69 } 70 71 rms = Math.sqrt(rms/mDataAutocorrelated.length); 72 average = average/mDataAutocorrelated.length; 73 log(String.format(" Maxvalue %f, max Index : %d/%d (%d) minIndex=%d",maxValue, maxIndex, 74 mDataAutocorrelated.length, data.length, minIndex)); 75 76 log(String.format(" average : %.3f rms: %.3f", average, rms)); 77 78 mEstimatedLatencyConfidence = 0.0; 79 if (average>0) { 80 double factor = 3.0; 81 82 double raw = (rms-average) /(factor*average); 83 log(String.format("Raw: %.3f",raw)); 84 mEstimatedLatencyConfidence = Math.max(Math.min(raw, 1.0),0.0); 85 } 86 87 log(String.format(" ****Confidence: %.2f",mEstimatedLatencyConfidence)); 88 89 mEstimatedLatencySamples = maxIndex*groupSize; 90 91 mEstimatedLatencyMs = mEstimatedLatencySamples *1000/mSamplingRate; 92 93 log(String.format(" latencySamples: %.2f %.2f ms", mEstimatedLatencySamples, 94 mEstimatedLatencyMs)); 95 96 status = true; 97 return status; 98 } 99 downsampleData(double [] data, double [] dataDownsampled, double threshold)100 private boolean downsampleData(double [] data, double [] dataDownsampled, double threshold) { 101 102 boolean status = false; 103 // mDataDownsampled = new double[mBlockSize]; 104 for (int i=0; i<mBlockSize; i++) { 105 dataDownsampled[i] = 0; 106 } 107 108 int N = data.length; //all samples available 109 double groupSize = (double) N / mBlockSize; 110 111 int ignored = 0; 112 113 int currentIndex = 0; 114 double nextGroup = groupSize; 115 for (int i = 0; i<N && currentIndex<mBlockSize; i++) { 116 117 if (i> nextGroup) { //advanced to next group. 118 currentIndex++; 119 nextGroup += groupSize; 120 } 121 122 if (currentIndex>=mBlockSize) { 123 break; 124 } 125 126 double value = Math.abs(data[i]); 127 if (value >= threshold) { 128 dataDownsampled[currentIndex] += value; 129 } else { 130 ignored++; 131 } 132 } 133 134 log(String.format(" Threshold: %.3f, ignored:%d/%d (%%.2f)", threshold, ignored, N, 135 (double) ignored/(double)N)); 136 137 status = true; 138 return status; 139 } 140 autocorrelation(double [] data, double [] dataOut)141 private boolean autocorrelation(double [] data, double [] dataOut) { 142 boolean status = false; 143 144 double sumsquared = 0; 145 int N = data.length; 146 for (int i=0; i<N; i++) { 147 double value = data[i]; 148 sumsquared += value*value; 149 } 150 151 if (sumsquared>0) { 152 for (int i = 0; i < N; i++) { 153 dataOut[i] = 0; 154 for (int j = 0; j < N - i; j++) { 155 156 dataOut[i] += data[j] * data[i + j]; 157 } 158 dataOut[i] = dataOut[i] / sumsquared; 159 } 160 status = true; 161 } 162 163 return status; 164 } 165 log(String msg)166 private static void log(String msg) { 167 Log.v("Recorder", msg); 168 } 169 } 170