Home | History | Annotate | Download | only in audioquality
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 /* This test accepts a collection of N speech waveforms collected as
     18    part of N recognition attempts.  The waveforms are ordered by
     19    increasing presentation level.  The test determines the extent to
     20    which the peak amplitudes in the waveforms track the change in
     21    presentation level.  Failure to track the presentation level within
     22    some reasonable margin is an indication of clipping or of automatic
     23    gain control in the signal path.
     24 
     25    The speech stimuli that are used for this test should simply be
     26    replications of exactly the same speech signal presented at
     27    different levels.  It is expected that all recognition attempts on
     28    this signal result in correct recognition.  A warning, but not a
     29    hard failure, should be issued if any of the attempts fail.  The
     30    hard failure criterion for this test should be only based on the
     31    amplitude linearity tracking. */
     32 
     33 #include <stdlib.h>
     34 #include <stdio.h>
     35 #include <math.h>
     36 
     37 /* Keep a record of the top N absolute values found using a slow
     38    bubble-sort.  This is OK, since the use of this program is not time
     39    critical, and N is usually small.  Note that the argument n = N-1. */
     40 static void bubbleUp(int* store, int n, short val) {
     41     if (val < store[n])
     42         return;
     43     for (int i = 0; i <= n; ++i) {
     44         if (val >= store[i]) {
     45             for (int j = n; j > i ; j--)
     46                 store[j] = store[j-1];
     47             store[i] = val;
     48             return;
     49         }
     50     }
     51 }
     52 
     53 /* Make two measurements on the signal of length numSamples sampled at
     54    sampleRate in pcm: the RMS of the highest amplitude 30ms segment
     55    (returned in peakRms), and the RMS of the top 50 peak absolute
     56    values found (returned in peakAverage).  If the signal is too
     57    short to make reasonable measurements, the function returns 0, else
     58    it returns 1. */
     59 static int peakLevels(short* pcm, int numSamples, float sampleRate,
     60                       float* peakAverage, float* peakRms) {
     61     float rmsFrameSize = 0.03;
     62     float rmsFrameStep = 0.01;
     63     int frameStep = int(0.5 + (sampleRate * rmsFrameStep));
     64     int frameSize = int(0.5 + (sampleRate * rmsFrameSize));
     65     int numFrames = 1 + ((numSamples - frameSize) / frameStep);
     66 
     67     if (numFrames < 10) {
     68         return 0; // failure for too short signal
     69     }
     70 
     71     // Peak RMS calculation
     72     double maxEnergy = 0.0;
     73     for (int frame = 0; frame < numFrames; ++frame) {
     74         double energy = 0.0;
     75         int limit = (frame * frameStep) + frameSize;
     76         for (int i = frame * frameStep; i < limit; ++i) {
     77             double s = pcm[i];
     78             energy += s * s;
     79         }
     80         if (energy > maxEnergy) {
     81             maxEnergy = energy;
     82         }
     83     }
     84     *peakRms = sqrt(maxEnergy / frameSize);
     85 
     86     // Find the absolute highest topN peaks in the signal and compute
     87     // the RMS of their values.
     88     int topN = 50; // The number of highest peaks over which to average.
     89     int topM = topN - 1;
     90     int* maxVal = new int[topN];
     91     for (int i = 0; i < topN; ++i) {
     92         maxVal[i] = 0;
     93     }
     94     for (int i = 0; i < numSamples; ++i) {
     95         if (pcm[i] >= 0) {
     96             bubbleUp(maxVal, topM, pcm[i]);
     97         } else {
     98             bubbleUp(maxVal, topM, -pcm[i]);
     99         }
    100     }
    101     float sum = 0.0;
    102     // The RMS is taken bacause we want the values of the highest peaks
    103     // to dominate.
    104     for (int i = 0; i < topN; ++i) {
    105         float fval = maxVal[i];
    106         sum += (fval * fval);
    107     }
    108     delete [] maxVal;
    109     *peakAverage = sqrt(sum/topN);
    110     return 1; // success
    111 }
    112 
    113 /* There are numSignals int16 signals in pcms.  sampleCounts is an
    114    integer array of length numSignals containing their respective
    115    lengths in samples.  They are all sampled at sampleRate.  The pcms
    116    are ordered by increasing stimulus level.  The level steps between
    117    successive stimuli were of size dbStepSize dB.  The signal with
    118    index referenceStim (0 <= referenceStim < numSignals) should be in
    119    an amplitude range that is reasonably certain to be linear (e.g. at
    120    normal speaking levels).  The maximum deviation in linearity found
    121    (in dB) is returned in maxDeviation.  The function returns 1 if
    122    the measurements could be made, or a negative number that
    123    indicates the error, as follows:
    124       -1 The input signals or sample counts are missing.
    125       -2 The number of input signals is < 2.
    126       -3 The specified sample rate is <= 4000.0
    127       -4 The dB step size for the increase in stimulus level is <= 0.0
    128       -5 The specified reverence stimulus number is out of range.
    129       -6 One or more of the stimuli is too short in duration. */
    130 int linearityTest(short** pcms, int* sampleCounts, int numSignals,
    131                   float sampleRate, float dbStepSize, int referenceStim,
    132                   float* maxDeviation) {
    133     if (!(pcms && sampleCounts)) {
    134         return -1; // Input signals or sample counts are missing
    135     }
    136     if (numSignals < 2) {
    137         return -2; // the number of input signals must be >= 2;
    138     }
    139     if (sampleRate <= 4000.0) {
    140         return -3; // The sample rate must be > 4000 Hz.
    141     }
    142     if (dbStepSize <= 0.0) {
    143         return -4; // The dB step size must be > 0.0
    144     }
    145     if (!((referenceStim >= 0) && (referenceStim < numSignals))) {
    146         return -5; // (0 <= referenceStim < numSignals) must be true
    147     }
    148     float* peakAverage = new float[numSignals];
    149     float* peakRms = new float[numSignals];
    150     for (int sig = 0; sig < numSignals; ++sig) {
    151         if (!peakLevels(pcms[sig], sampleCounts[sig],
    152              sampleRate, peakAverage + sig, peakRms + sig)) {
    153             return -6; // failure because a signal is too short.
    154         }
    155     }
    156     float peakAverageRef = peakAverage[referenceStim];
    157     float peakRmsRef = peakRms[referenceStim];
    158     float maxDev = 0.0;
    159     for (int i = 0; i < numSignals; ++i) {
    160         float dbAverage = 20.0 * log10(peakAverage[i]/peakAverageRef);
    161         float dbRms = 20.0 * log10(peakRms[i]/peakRmsRef);
    162         float reference = dbStepSize * (i - referenceStim);
    163         float average_level = 0.5 * (dbAverage + dbRms);
    164         float dev = fabs(average_level - reference);
    165         // fprintf(stderr,"dbAverage:%f dbRms:%f reference:%f dev:%f\n",
    166         //         dbAverage, dbRms, reference, dev);
    167         if (dev > maxDev)
    168             maxDev = dev;
    169     }
    170     *maxDeviation = maxDev;
    171     return 1;
    172 }
    173