0
votes

I essentially have nothing but an audio recording which I want to lower the signal to noise of. For simplicity, I extracted a specific part of the audio that I want to measure signal to noise, called audio. I have also extracted some background noise from an area that has no signal in it, called backgroundnoise My data is here: http://expirebox.com/download/bb9997de2dc5bae12fc7184dd2a0eb0f.html

Due to a lot of help from here: Proper way to add noise to signal

I have :

originalSNR=snr(audio-backgroundnoise,backgroundnoise) %initial calculation is 6.4762
desiredSNRindB = 5:-1:1;    %what I want to lower the snr down to
for desired_snr_db=desiredSNRindB
    est_signal= audio- backgroundnoise;

    %the original matlab equation for SNR is:
    %signalPow = rssq(x(:)).^2;
    %noisePow = rssq(y(:)).^2;
    %r = 10 * log10(signalPow / noisePow);
    %solving for noisePow we get
    rms_new = rssq(est_signal(:)).^2 / 10^(desired_snr_db / 10);

    %assuming rssq(new_noise).^2 = scale_factor * rssq(old).^2
    scale_factor = rms_new / rssq(backgroundnoise(:)).^2;

    new_noise = scale_factor * backgroundnoise;

    %add noise to our estimated signal
    new_signal = est_signal + new_noise; %you may need to do stretching
    %trimming to get the right sizes

    snr(new_signal-backgroundnoise,backgroundnoise)

end 

but the problem is

  1. my test snr at the end is not the same as desired_snr_db, and
  2. the snr goes up if desired_snr_db is low.

Can anyone help?

2
I doubt audio-background noise is doing what you expect it to do. You can't take noise from one part of a signal and subtract it from another part and expect it to just go away. Noise is non-correlated.jaket
Is that what people mean when they talk about correlated noise? My assumption in doing this was that the distribution of background noise is similar throughout (i.e. it suddenly doesn't get noisier for no reason at the end). Hence with similar distributions, I could subtract them. =============== If I can't do this, my next plan was to use filtering. my region of interest is between 25-35kHz. Noise is represented from 0-125kHz (half sampling rate). By filtering everything but 25-35kHz, I significantly improve signal to noise. I can replace all "audio-background" with this new audio.How's that?CaptainObv
ALSO, since backgroundnoise is from a random section. Does that mean I can subtract the new filtered audio from the original audio to get the background noise data? I.e. I can find the SNR of the original audio using: ... r=snr(filteredAudio,audio - filteredAudio);CaptainObv
Being uncorrelated means that if you subtract one section from another you're not going to cancel it out, you're just adding one set of random values to another set of random values and you'll get noise with more variance. Do you mean 25kHz-35kHz or 25Hz-35kHz? If 25kHz, then a bandpass filter would probably do the trick. Use a similar width notch filter to keep the noise. You can't really subtract it because filtering the signal is going to shift the time of the audio signal.jaket
Yes 25kHz. I'm not familiar with notch filters could you elaborate? Use bandpass then notch? I thought the consequence of using filters to remove noise was altered amplitude values for the frequencies that you want to pass through? I didn't know it had an impact on the time. Is everything going to be shifted evenly (e.g. like introducing a lag time at the beginning), or will this completely ruin the signal?..... I just want to create the same audio file, with different signal to noise ratios (their values being KNOWN). If I can't use r=snr(filteredAudio,audio - filteredAudio); what can I use?CaptainObv

2 Answers

0
votes

An obvious issue is with the lines:

scale_factor = rms_new / rssq(backgroundnoise(:)).^2;
new_noise = scale_factor * backgroundnoise;

More specifically, the first line computes a scaling factor to be applied on the background noise power, whereas the second line applies it to background noise samples. To scale the noise power by scale_factor, you'd actually need to scale the samples by sqrt(scale_factor) as:

scale_factor = sqrt(rms_new / rssq(backgroundnoise(:)).^2);
new_noise = scale_factor * backgroundnoise;

Another problem with the data sample you provide is how you handle the background noise in the first place. A direct subtraction of a noise segment from your noisy signal could in fact be adding noise, unless the subtracted noise estimate vector has been carefully obtained so as to be well correlated with the actual noise on a sample-by-sample basis (and this does not appear to be the case with your sample). Subtracting a typical noise sample with similar characteristics (such as noise power) is simply not enough to remove the noise.

In general noise can be reduced by using one of various noise cancellation techniques. These typically involves filtering your noisy signal with either adaptive filters or fixed filters (less complex but requires a-priori knowledge of the signal and noise characteristics).

0
votes

Assuming the background noise you have removed is effectively constant, or at least periodic throughout your signal, one of the most effective ways to remove noise is using an LMS/NLMS adaptive filter.

Adaptive filters constantly modify their parameters to provide the best estimate possible for a desired signal. For noise cancellation we actually want to model our noise, with the error being our desired signal. Check out: http://en.wikipedia.org/wiki/Echo_suppression_and_cancellation for more on this.

Matlab conveniently has an adaptive LMS filter as part of the DSP toolbox.

Under the documentation for the dsp.LMSFilterObject is an example piece of code which can be used for noise reduction with a given noise signal. You can find the example here: http://au.mathworks.com/help/dsp/ref/dsp.lmsfilter-class.html

We can further improve this example by placing the filter in a for loop, and giving the filter its previously calculated coefficients as its starting coefficients, allowing the filter to converge faster and to a better solution.

One of the most important parameters to your filter is theorder. You want this to be large enough to capture all the features of your signal, but not too large to avoid unnecessary computation. Start with a large order and plot your filter coefficients once the filter is finished. When the coefficients go to 0, thats the maximum order of the filter you need.

Note: In the following code my noise sample vector is the same dimension as my audio signal. You'll have to implement this.

clearvars;
[sigdata,Fs]=audioread('noisy_signal.wav');   %//import my noisy signal
[n1data,Fs1]=audioread('NoiseRef1.wav');      %//import my noise 

stepsize1=0.05;   %//stepsize for my adaptive filter. Smaller is more accurate but longer converging times
maxn=5;   %//how many times do I want to loop my adaptive filter. For smaller stepsize maxn needs to be larger. 
order = 220; %//set this large - maybe 500. Experiment - and see where the coefficients go to 0. That's the order you need. 

initialh = 0;   %//initialize my filter coefficients to 0  

for n=1:maxn
    hlms0 = dsp.LMSFilter('Length',order, ...
       'Method','Normalized LMS',...
       'InitialConditions',initialh,...
       'StepSizeSource','Input port');
    x = n1data; %// Noise
    d = sigdata; % //Noise + Signal   
    mu = stepsize1; % //step size 
[y, out0, wts0] = step(hlms0,x,d,mu);
%//y is the filters best estimate for your noise. You don't usually want this
%//out0 is the residual error. Since the filter is trying to model your noise, the error is your desired signal. 
%//wts0 is your filter coefficients 
release(hlms0);
initialh=wts0;     %//set your initial coefficients to the calculated filter coefficients and re-run
end
desired_signal = out0; 
sound(desired_signal,Fs); 

If you like you can add in your SNR calculation within the loop and make the loop terminate at a certain SNR threshold. Note: you want to make your loop run at least twice, so that you do not have the initial adaptive period in your signal.

For more information look up "Noise Cancellation LMS". Best of luck!