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