Filtering a wav file using python

前端 未结 2 554
甜味超标
甜味超标 2020-12-24 10:04

So i recently successfully built a system which will record, plot, and playback an audio wav file entirely with python. Now, I\'m trying to put some filtering and audio mixi

2条回答
  •  借酒劲吻你
    2020-12-24 10:52

    First step : What kind of audio filter do you need ?

    Choose the filtered band

    • Low-pass Filter : remove highest frequency from your audio signal
    • High-pass Filter : remove lowest frequencies from your audio signal
    • Band-pass Filter : remove both highest and lowest frequencies from your audio signal

    For the following steps, i assume you need a Low-pass Filter.

    Choose your cutoff frequency

    The Cutoff frequency is the frequency where your signal will be attenuated by -3dB.

    Your example signal is 440Hz, so let's choose a Cutoff frequency of 400Hz. Then your 440Hz-signal is attenuated (more than -3dB), by the Low-pass 400Hz filter.

    Choose your filter type

    According to this other stackoverflow answer

    Filter design is beyond the scope of Stack Overflow - that's a DSP problem, not a programming problem. Filter design is covered by any DSP textbook - go to your library. I like Proakis and Manolakis' Digital Signal Processing. (Ifeachor and Jervis' Digital Signal Processing isn't bad either.)

    To go inside a simple example, I suggest to use a moving average filter (for a simple low-pass filter).

    See Moving average

    Mathematically, a moving average is a type of convolution and so it can be viewed as an example of a low-pass filter used in signal processing

    This Moving average Low-pass Filter is a basic filter, and it is quite easy to use and to understand.

    The parameter of the moving average is the window length.

    The relationship between moving average window length and Cutoff frequency needs little bit mathematics and is explained here

    The code will be

    import math
    
    sampleRate = 11025.0 
    cutOffFrequency = 400.0
    freqRatio = cutOffFrequency / sampleRate
    
    N = int(math.sqrt(0.196201 + freqRatio**2) / freqRatio)
    

    So, in the example, the window length will be 12

    Second step : coding the filter

    Hand-made moving average

    see specific discussion on how to create a moving average in python

    Solution from Alleo is

    def running_mean(x, windowSize):
       cumsum = numpy.cumsum(numpy.insert(x, 0, 0)) 
       return (cumsum[windowSize:] - cumsum[:-windowSize]) / windowSize 
    
    filtered = running_mean(signal, N)
    

    Using lfilter

    Alternatively, as suggested by dpwilson, we can also use lfilter

    win = numpy.ones(N)
    win *= 1.0/N
    filtered = scipy.signal.lfilter(win, [1], signal).astype(channels.dtype)
    

    Third step : Let's Put It All Together

    import matplotlib.pyplot as plt
    import numpy as np
    import wave
    import sys
    import math
    import contextlib
    
    fname = 'test.wav'
    outname = 'filtered.wav'
    
    cutOffFrequency = 400.0
    
    # from http://stackoverflow.com/questions/13728392/moving-average-or-running-mean
    def running_mean(x, windowSize):
      cumsum = np.cumsum(np.insert(x, 0, 0)) 
      return (cumsum[windowSize:] - cumsum[:-windowSize]) / windowSize
    
    # from http://stackoverflow.com/questions/2226853/interpreting-wav-data/2227174#2227174
    def interpret_wav(raw_bytes, n_frames, n_channels, sample_width, interleaved = True):
    
        if sample_width == 1:
            dtype = np.uint8 # unsigned char
        elif sample_width == 2:
            dtype = np.int16 # signed 2-byte short
        else:
            raise ValueError("Only supports 8 and 16 bit audio formats.")
    
        channels = np.fromstring(raw_bytes, dtype=dtype)
    
        if interleaved:
            # channels are interleaved, i.e. sample N of channel M follows sample N of channel M-1 in raw data
            channels.shape = (n_frames, n_channels)
            channels = channels.T
        else:
            # channels are not interleaved. All samples from channel M occur before all samples from channel M-1
            channels.shape = (n_channels, n_frames)
    
        return channels
    
    with contextlib.closing(wave.open(fname,'rb')) as spf:
        sampleRate = spf.getframerate()
        ampWidth = spf.getsampwidth()
        nChannels = spf.getnchannels()
        nFrames = spf.getnframes()
    
        # Extract Raw Audio from multi-channel Wav File
        signal = spf.readframes(nFrames*nChannels)
        spf.close()
        channels = interpret_wav(signal, nFrames, nChannels, ampWidth, True)
    
        # get window size
        # from http://dsp.stackexchange.com/questions/9966/what-is-the-cut-off-frequency-of-a-moving-average-filter
        freqRatio = (cutOffFrequency/sampleRate)
        N = int(math.sqrt(0.196196 + freqRatio**2)/freqRatio)
    
        # Use moviung average (only on first channel)
        filtered = running_mean(channels[0], N).astype(channels.dtype)
    
        wav_file = wave.open(outname, "w")
        wav_file.setparams((1, ampWidth, sampleRate, nFrames, spf.getcomptype(), spf.getcompname()))
        wav_file.writeframes(filtered.tobytes('C'))
        wav_file.close()
    

提交回复
热议问题