Search code examples
pythonfilterscipyfftfrequency

Create a band-pass filter via Scipy in Python?


Is there a way to create a quick bandpass filter via scipy or librosa in Python 3.6 for a 16KHz wav file to filter noise outside of human voice band of 300-3400Hz ? Here is a sample wav file with background noise at low frequency.

UPDATE: Yes, I have already seen/tried How to implement band-pass Butterworth filter with Scipy.signal.butter . Unfortunately, the filtered sound is horribly deformed. Essentially, the whole code does this:

lo,hi=300,3400
sr,y=wavfile.read(wav_file)
b,a=butter(N=6, Wn=[2*lo/sr, 2*hi/sr], btype='band')
x = lfilter(b,a,y)
sounddevice.play(x, sr)  # playback

What am I doing wrong or how can this be improved so that the background noise is filtered out correctly.

Here is the visualization of the original and filtered file using the link above. The visualization looks reasonable, but it sounds horrible :( How can this be fixed?

enter image description here


Solution

  • Apparently the problem occurs when writing unnormalized 64 bit floating point data. I get an output file that sounds reasonable by either converting x to 16 bit or 32 bit integers, or by normalizing x to the range [-1, 1] and converting to 32 floating point.

    I'm not using sounddevice; instead, I'm saving the filtered data to a new WAV file and playing that. Here are the variations that worked for me:

    # Convert to 16 integers
    wavfile.write('off_plus_noise_filtered.wav', sr, x.astype(np.int16))
    

    or...

    # Convert to 32 bit integers
    wavfile.write('off_plus_noise_filtered.wav', sr, x.astype(np.int32))
    

    or...

    # Convert to normalized 32 bit floating point
    normalized_x = x / np.abs(x).max()
    wavfile.write('off_plus_noise_filtered.wav', sr, normalized_x.astype(np.float32))
    

    When outputting integers, you could scale up the values to minimize the loss of precision that results from truncating the floating point values:

    x16 = (normalized_x * (2**15-1)).astype(np.int16)
    wavfile.write('off_plus_noise_filtered.wav', sr, x16)