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