# Speech SNR estimation

I'm trying to estimate SNR level in db to use it as a switch for an speech enhancement method that i'm working on. The noise level is not known and can be estimated using a posterior SNR algorithm. Considering having noisy speech signal power spectrum and estimated noise power spectrum (using a posterior SNR estimation method) how can one estimate signal SNR level in db? The reason for estimating the SNR in db is that if the SNR level rises above a known value e.g. 10db the enhancement algorithm should be switched to another one. How can i estimate SNR in db accurately?

## 2 Answers

I am not an expert in audio speech processing so this may be a naive approach but an idea to consider:

I would set a dynamic threshold using an N-Sigma algorithm (explained below) to separate spoken speech from the blank intervals that would be expected to exist in between the spoken words. The SNR would be estimated as the ratio of the standard deviation of the signals above the threshold to the standard deviation of the signals below the threshold.

Details on N-Sigma Algorithm: The threshold is dynamically set to be a multiple number of standard deviations above the noise floor ($$N\sigma$$). $$N$$ is a design parameter trading false alarm vs false detection of noise. Setting $$N$$ too high results in low level speech getting included as measured "noise" and setting $$N$$ too low results in high level noise getting included as measured "signal". This should work well in higher SNR applications, such as detecting the 10 dB SNR levels as the OP is intending. The algorithm initializes with a threshold that is mid-level based on the signals present. Samples above threshold are considered signal and excluded from the RMS computation of the noise. From the first measurement the threshold is then moved from the starting position ot be $$N\sigma$$ above the noise level and the process repeats adaptively setting the threshold based on noise levels present. Further optimizations can be made by adding hysteresis or avoiding brief threshold crossings that don't remain crossed for a certain number of samples. • Thanks, I've started to implement and check your idea. I'll post the results here later. – Filthy Man May 11 '20 at 22:01
• @FilthyMan Oh cool I will be very interested! – Dan Boschen May 11 '20 at 22:03
• I implemented your algorithm in cpp and tuned it, gotta say it really satisfies my expectations, still the input must be normalized and if the input range changes the design parameters e.g. window size and N will have to change but overally it works just fine. I will mention your name in my thesis when it's done, if you want the code and visual results just let me know and i will post it here. Thanks! – Filthy Man May 16 '20 at 23:28
• @FilthyMan Oh that's awesome, thank you for letting me know and the mention! (Please use "C. Daniel Boschen" as my name). I would imagine that the levels would be continously adaptive and you would set an adaptation time constant and that way it would always auto-correct. But yes may be of interest for others to see the actual visual results if you had anything brief to post here. – Dan Boschen May 17 '20 at 0:15
• You can also reference these authors and this article: citeseerx.ist.psu.edu/viewdoc/…. These are the folks that taught me. – Dan Boschen May 17 '20 at 0:24

Here is the C++ code of the answer.

#include <algorithm>
#include <queue>
#include <numeric>
#include <vector>
#include <cmath>
const float N = 4;
float noise_th= 0.35;    //noise threshold
float calculate_SNR(queue<float> &signal_fifo)
{
vector<float> noise_vect;
vector<float> speech_vect;
float SNR;
float noise_std;
float speech_std;
auto fast_std = [](vector<float> input) //https://stackoverflow.com/questions/7616511/calculate-mean-and-standard-deviation-from-a-vector-of-samples-in-c-using-boos/12405793#12405793
{
float mean, acc;
mean = accumulate(begin(input), end(input), 0.0)/input.size();
acc = 0.0;
for_each(begin(input), end(input), [&](const float d)
{
acc += (d - mean)*(d - mean);
});
return sqrt(acc/(input.size()-1));
};

while(!signal_fifo.empty()) //Seprate speech and noise samples(fifo has the ABS of input signal)
{
if(signal_fifo.front() < noise_th)
{
noise_vect.push_back(signal_fifo.front());
signal_fifo.pop();
}
else
{
speech_vect.push_back(signal_fifo.front());
signal_fifo.pop();
}
}

noise_std = fast_std(noise_vect);
speech_std = fast_std(speech_vect);
SNR = 20*log10(speech_std/noise_std);
noise_th = N*noise_std;     //Update threshold value
return SNR;
}