# Apply butterworth filter (lowpass) to a signal

I'm working on a DSP lab and I can't figure out how to apply the filter I've created to a sound. The only thing I found online that did something to my sound was signal.filtfilt but that seems to just output silence for some reason.

import matplotlib.pyplot as plt
import numpy as np
from scipy import signal
import librosa

sf = 44100
t = np.arange(0., 0.5+1./sf, 1./sf)
totalTime = 0.5

mysound = totalTime * np.sin(2*np.pi*697*t) + totalTime * np.sin(2*np.pi*1477*t)

#Creation of the filter
N  = 6    # Filter order
fc = 1000/3000 # Cutoff frequency, normalized
b, a = signal.butter(N, fc)

#Apply the filter
tempf = signal.filtfilt(b,a, mysound)
librosa.output.write_wav('3mod.wav',tempf,sf)


I think I'm either making a mistake in the filter creation, or I just can't find the right way to apply it. Since I'm very new to DSP I can't really understand all the documentation material, so I might just be overlooking something simple in one of the documents I've been reading, but I can't figure out what.

EDIT: it seems to work when I change the filter creation code to

#Creation of the filter
cutOff = 1000 # Cutoff frequency
nyq = 0.5 * sf
N  = 6    # Filter order
fc = cutOff / nyq # Cutoff frequency normal
b, a = signal.butter(N, fc)


So it seems that having the wrong calculation for the Nyquist frequency gave me silence. I'm not 100% sure that .filtfilt is the right (or only) way to deploy the filter.

Leaving the question here in case it helps others.

• The difference between filtfilt and lfilter is explained here: dsp.stackexchange.com/a/19086/29 Basically one delays/shifts the signal and the other does not. Feb 2 '18 at 19:23
• I work on this software, btw, so if there's anything we can do to make it easier to use or improve the documentation, let me know. I've thought we should add an fs= parameter, for instance, so you'd call it as signal.butter(N, 1000, fs=44100) instead of signal.butter(N, 1000/(44100/2)). Another idea is to make filter objects that you can apply directly to signals instead of generating b, a or sos and applying them as different steps. Feb 2 '18 at 19:47
• Thank you so much for all the explanations! As a naive user I found it hard to understand the difference between filtfilt and lfilter from the documentation, while your comment here is very clear (maybe I didn't find the right docs online). I would also have found it easier if the features you mention in the second comment (having a fs parameter and being able to apply the filter directly to a sound) had been available. Bear in mind that I'm literally just starting out and have very little coding experience though. Feb 5 '18 at 10:12
• Well I have a lot of experience and I still think it's annoying that I have to remember fc/(fs/2) in some functions and fc/(fs/pi) (I think?) in others, because they use different definitions of normalized frequency. Feb 5 '18 at 16:41
• What do you think of this modified version? Added fs parameter and a realistic example. Feb 11 '18 at 17:59

I found a way to fix the issue by changing the calculations, so it seems like I had the wrong Nyquist frequency and therefore the wrong cutoff frequency. I hadn't realised that the Nyquist frequency in this case didn't need to apply to the highest frequency present in the sound, but rather to the sampling frequency used to generate the sound (it sounds silly now that I know).

The answer (also in the EDIT section of the question) is:

#Creation of the filter
cutOff = 1000 # Cutoff frequency
nyq = 0.5 * sf
N  = 6    # Filter order
fc = cutOff / nyq # Cutoff frequency normal
b, a = signal.butter(N, fc)


The .filtfilt method didn't work because I had the wrong filter.

• Yes, that's the correct way to set the cutoff frequency. Unfortunately "nyquist rate" and "nyquist frequency" mean similar, but different things. Note that higher-order filters should use output='sos' instead of b, a, and then use sosfilt or sosfiltfilt. Feb 2 '18 at 19:27