pythonnumpysoundfile

How can I add gaussian noise with a specified SNR to an audio file with Soundfile in Python?


    print('Processing: ', filepath)
    path, file = os.path.split(filepath)
    noisy_path = path.replace('dev-clean', 'dev-noise-gassian')
    print(path, file)
    if not os.path.exists(noisy_path):
        os.makedirs(noisy_path)

    noisy_filepath = os.path.join(noisy_path, file)
    audio_signal, samplerate = sf.read(filepath)
    noise = np.random.normal(0, 0.1, audio_signal.shape[0])

    noisy_signal = audio_signal + noise

    print(audio_signal)
    print(noisy_signal)
    sf.write(noisy_filepath, noisy_signal, samplerate)

    quit()

That's what I'm doing, and it adds noise, but I don't know what the SNR of the noise is. How do I calibrate the addition of noise to match a specified SNR?

Thanks


Solution

  • First, some theory:

    You can compute the SNR by dividing the average power of the signal by the average power of the noise.

    For any given signal, you can estimate its average power using its power spectral density. In brief, it's the averaged amplitude of its FFT.

    Here's a working example using numpy FFT:

    import numpy as np
    import soundfile as sf
    
    
    sampling_rate = 42000 #42kHz sampling rate is enough for audio
    Nsamples = 100000 # a bit more than 2 seconds of signal at the current sampling rate
    freq = 440 # musical A
    A = 5
    noiseAmplitude = 5
    noiseSigma = 0.1
    
    noise = noiseAmplitude * np.random.normal(0, noiseSigma, Nsamples)
    
    # Generate a pure sound sampled at our sampling_rate for a duration of roughly 2s
    cleanSound = A*np.sin(2*np.pi*freq/sampling_rate*np.arange(Nsamples))
    
    sampleSound = cleanSound + noise
    
    # For a pure sine and a white noise, the theoretical SNR in dB is:
    theoreticalSNR = 20*np.log10(A/(np.sqrt(2)*noiseAmplitude*noiseSigma)) # the sqrt of 2 is because of root-mean square amplitude
    
    ## Experimental measurement using FFT (we use sampling_rate//2 points for Nyquist)
    # power spectrum of the clean sound (averaged spectral density)
    cleanPS = np.sum(np.abs(np.fft.fft(cleanSound,sampling_rate//2)/Nsamples)**2)
    
    # same for noise
    noisePS = np.sum(np.abs(np.fft.fft(noise,sampling_rate//2)/Nsamples)**2)
    
    # 10 instead of 20 because we're using power instead of RMS amplitude
    measuredSNR = 10*np.log10(cleanPS/noisePS)
    
    # write to output sound file
    sf.write('/tmp/sample.wav',sampleSound,sampling_rate)
    

    With the values from above, I get a theoretical SNR of 16.989 dB and a measured SNR of 16.946 dB. Therefore, if you want to add white noise with a given SNR to any given audio signal, you can compute the white noise power by reversing the formula: SNR = 10*np.log10(cleanPS/noisePS) and chose the noiseAmplitude and noiseSigma accordingly.