• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <math.h>
18 
19 /* Return the median of the n values in "values".
20    Uses a stupid bubble sort, but is only called once on small array. */
getMedian(float * values,int n)21 float getMedian(float* values, int n) {
22     if (n <= 0)
23         return 0.0;
24     if (n == 1)
25         return values[0];
26     if (n == 2)
27         return 0.5 * (values[0] + values[1]);
28     for (int i = 1; i < n; ++i)
29         for (int j = i; j < n; ++j) {
30             if (values[j] < values[i-1]) {
31                 float tmp = values[i-1];
32                 values[i-1] = values[j];
33                 values[j] = tmp;
34             }
35         }
36     int ind = int(0.5 + (0.5 * n)) - 1;
37     return values[ind];
38 }
39 
computeAndRemoveMean(short * pcm,int numSamples)40 float computeAndRemoveMean(short* pcm, int numSamples) {
41     float sum = 0.0;
42 
43     for (int i = 0; i < numSamples; ++i)
44         sum += pcm[i];
45     short mean;
46     if (sum >= 0.0)
47         mean = (short)(0.5 + (sum / numSamples));
48     else
49         mean = (short)((sum / numSamples) - 0.5);
50     for (int i = 0; i < numSamples; ++i)
51         pcm[i] -= mean;
52     return sum / numSamples;
53 }
54 
measureRms(short * pcm,int numSamples,float sampleRate,float onsetThresh,float * rms,float * stdRms,float * mean,float * duration)55 void measureRms(short* pcm, int numSamples, float sampleRate, float onsetThresh,
56                 float* rms, float* stdRms, float* mean, float* duration) {
57     *rms = 0.0;
58     *stdRms = 0.0;
59     *duration = 0.0;
60     float frameDur = 0.025;    // Both the duration and interval of the
61                                 // analysis frames.
62     float calInterval = 0.250; // initial part of signal used to
63                                 // establish background level (seconds).
64     double sumFrameRms = 1.0;
65     float sumSampleSquares = 0.0;
66     double sumFrameSquares = 1.0; // init. to small number to avoid
67                                     // log and divz problems.
68     int frameSize = (int)(0.5 + (sampleRate * frameDur));
69     int numFrames = numSamples / frameSize;
70     int numCalFrames = int(0.5 + (calInterval / frameDur));
71     if (numCalFrames < 1)
72         numCalFrames = 1;
73     int frame = 0;
74 
75     *mean = computeAndRemoveMean(pcm, numSamples);
76 
77     if (onsetThresh < 0.0) { // Handle the case where we want to
78                               // simply measure the RMS of the entire
79                               // input sequence.
80         for (frame = 0; frame < numFrames; ++frame) {
81             short* p_data = pcm + (frame * frameSize);
82             int i;
83             for (i = 0, sumSampleSquares = 0.0; i < frameSize; ++i) {
84                 float samp = p_data[i];
85                 sumSampleSquares += samp * samp;
86             }
87             sumSampleSquares /= frameSize;
88             sumFrameSquares += sumSampleSquares;
89             double localRms = sqrt(sumSampleSquares);
90             sumFrameRms += localRms;
91         }
92         *rms = sumFrameRms / numFrames;
93         *stdRms = sqrt((sumFrameSquares / numFrames) - (*rms * *rms));
94         *duration = frameSize * numFrames / sampleRate;
95         return;
96     }
97 
98     /* This handles the case where we look for a target signal against a
99        background, and expect the signal to start some time after the
100        beginning, and to finish some time before the end of the input
101        samples. */
102     if (numFrames < (3 * numCalFrames)) {
103         return;
104     }
105     float* calValues = new float[numCalFrames];
106     float calMedian = 0.0;
107     int onset = -1;
108     int offset = -1;
109 
110     for (frame = 0; frame < numFrames; ++frame) {
111         short* p_data = pcm + (frame * frameSize);
112         int i;
113         for (i = 0, sumSampleSquares = 1.0; i < frameSize; ++i) {
114             float samp = p_data[i];
115             sumSampleSquares += samp * samp;
116         }
117         sumSampleSquares /= frameSize;
118         /* We handle three states: (1) before the onset of the signal; (2)
119            within the signal; (3) following the signal.  The signal is
120            assumed to be at least onsetThresh dB above the background
121            noise, and that at least one frame of silence/background
122            precedes the onset of the signal. */
123         if (onset < 0) { // (1)
124             sumFrameSquares += sumSampleSquares;
125             if (frame < numCalFrames ) {
126                 calValues[frame] = sumSampleSquares;
127                 continue;
128             }
129             if (frame == numCalFrames) {
130                 calMedian = getMedian(calValues, numCalFrames);
131                 if (calMedian < 10.0)
132                     calMedian = 10.0; // avoid divz, etc.
133             }
134             float ratio = 10.0 * log10(sumSampleSquares / calMedian);
135             if (ratio > onsetThresh) {
136                 onset = frame;
137                 sumFrameSquares = 1.0;
138                 sumFrameRms = 1.0;
139             }
140             continue;
141         }
142         if ((onset > 0) && (offset < 0)) { // (2)
143             int sig_frame = frame - onset;
144             if (sig_frame < numCalFrames) {
145                 calValues[sig_frame] = sumSampleSquares;
146             } else {
147                 if (sig_frame == numCalFrames) {
148                     calMedian = getMedian(calValues, numCalFrames);
149                     if (calMedian < 10.0)
150                         calMedian = 10.0; // avoid divz, etc.
151                 }
152                 float ratio = 10.0 * log10(sumSampleSquares / calMedian);
153                 int denFrames = frame - onset - 1;
154                 if (ratio < (-onsetThresh)) { // found signal end
155                     *rms = sumFrameRms / denFrames;
156                     *stdRms = sqrt((sumFrameSquares / denFrames) - (*rms * *rms));
157                     *duration = frameSize * (frame - onset) / sampleRate;
158                     offset = frame;
159                     continue;
160                 }
161             }
162             sumFrameSquares += sumSampleSquares;
163             double localRms = sqrt(sumSampleSquares);
164             sumFrameRms += localRms;
165             continue;
166         }
167         if (offset > 0) { // (3)
168             /* If we have found the real signal end, the level should stay
169                low till data end.  If not, flag this anomaly by increasing the
170                reported duration. */
171             float localRms = 1.0 + sqrt(sumSampleSquares);
172             float localSnr = 20.0 * log10(*rms / localRms);
173             if (localSnr < onsetThresh)
174                 *duration = frameSize * (frame - onset) / sampleRate;
175             continue;
176         }
177     }
178     delete [] calValues;
179 }
180 
181