blob: 75b04eb7ea4f31f9bd12352c7f3825ee2808725b [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.verifier.audio;
import android.util.Log;
public class Correlation {
private int mBlockSize = 4096;
private int mSamplingRate = 44100;
private double [] mDataDownsampled = new double [mBlockSize];
private double [] mDataAutocorrelated = new double[mBlockSize];
public double mEstimatedLatencySamples = 0;
public double mEstimatedLatencyMs = 0;
public double mEstimatedLatencyConfidence = 0.0;
public void init(int blockSize, int samplingRate) {
mBlockSize = blockSize;
mSamplingRate = samplingRate;
}
public boolean computeCorrelation(double [] data, int samplingRate) {
boolean status = false;
log("Started Auto Correlation for data with " + data.length + " points");
mSamplingRate = samplingRate;
downsampleData(data, mDataDownsampled);
//correlation vector
autocorrelation(mDataDownsampled, mDataAutocorrelated);
int N = data.length; //all samples available
double groupSize = (double) N / mBlockSize; //samples per downsample point.
double maxValue = 0;
int maxIndex = -1;
double minLatencyMs = 8; //min latency expected. This algorithm should be improved.
int minIndex = (int)(0.5 + minLatencyMs * mSamplingRate / (groupSize*1000));
double average = 0;
double rms = 0;
//find max
for (int i=minIndex; i<mDataAutocorrelated.length; i++) {
average += mDataAutocorrelated[i];
rms += mDataAutocorrelated[i]*mDataAutocorrelated[i];
if (mDataAutocorrelated[i] > maxValue) {
maxValue = mDataAutocorrelated[i];
maxIndex = i;
}
}
rms = Math.sqrt(rms/mDataAutocorrelated.length);
average = average/mDataAutocorrelated.length;
log(String.format(" Maxvalue %f, max Index : %d/%d (%d) minIndex=%d",maxValue, maxIndex,
mDataAutocorrelated.length, data.length, minIndex));
log(String.format(" average : %.3f rms: %.3f", average, rms));
mEstimatedLatencyConfidence = 0.0;
if (average>0) {
double factor = 3.0;
double raw = (rms-average) /(factor*average);
log(String.format("Raw: %.3f",raw));
mEstimatedLatencyConfidence = Math.max(Math.min(raw, 1.0),0.0);
}
log(String.format(" ****Confidence: %.2f",mEstimatedLatencyConfidence));
mEstimatedLatencySamples = maxIndex*groupSize;
mEstimatedLatencyMs = mEstimatedLatencySamples *1000/mSamplingRate;
log(String.format(" latencySamples: %.2f %.2f ms", mEstimatedLatencySamples,
mEstimatedLatencyMs));
status = true;
return status;
}
private boolean downsampleData(double [] data, double [] dataDownsampled) {
boolean status = false;
// mDataDownsampled = new double[mBlockSize];
for (int i=0; i<mBlockSize; i++) {
dataDownsampled[i] = 0;
}
int N = data.length; //all samples available
double groupSize = (double) N / mBlockSize;
int currentIndex = 0;
double nextGroup = groupSize;
for (int i = 0; i<N && currentIndex<mBlockSize; i++) {
if (i> nextGroup) { //advanced to next group.
currentIndex++;
nextGroup += groupSize;
}
if (currentIndex>=mBlockSize) {
break;
}
dataDownsampled[currentIndex] += Math.abs(data[i]);
}
status = true;
return status;
}
private boolean autocorrelation(double [] data, double [] dataOut) {
boolean status = false;
double sumsquared = 0;
int N = data.length;
for (int i=0; i<N; i++) {
double value = data[i];
sumsquared += value*value;
}
if (sumsquared>0) {
for (int i = 0; i < N; i++) {
dataOut[i] = 0;
for (int j = 0; j < N - i; j++) {
dataOut[i] += data[j] * data[i + j];
}
dataOut[i] = dataOut[i] / sumsquared;
}
status = true;
}
return status;
}
private static void log(String msg) {
Log.v("Recorder", msg);
}
}