Home | History | Annotate | Download | only in audio
      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 
     35     public void init(int blockSize, int samplingRate) {
     36         mBlockSize = blockSize;
     37         mSamplingRate = samplingRate;
     38     }
     39 
     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 
    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 
    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 
    166     private static void log(String msg) {
    167         Log.v("Recorder", msg);
    168     }
    169 }
    170