Implementation of basic 2-pole low-pass filter

I know this question has been asked to hell and back from many people, and that it will probably get downvoted until it's deleted, but please bear with me.

I am designing a sound engine for one of my projects, which needs to support low-pass filtering (no other filtering required, so no need for extremely robust equalization or high-pass filters).

Unfortunately, I'm not exactly well-versed in DSP math (as I'm more a programmer than anything else), so this has been getting tricky.

What I've found by searching on the internet is a "1-RC and C filter" which is implemented as:

// Coefficient computation
// Cutoff and reso are [0,128) integers
c = 0.5^[(128-Cutoff) / 16.0]
r = 0.5^[( 24+Reso)   / 16.0]

v0 = (1.0 - r*c)*v0 - c*v1 + c*input
v1 = (1.0 - r*c)*v1 + c*v0


On the same website, I also found that c can be written frequency-wise with the formula

c = 2.0 * sin(Freq * pi/SampRate)


Since I'm only doing variable low-pass filtering with no resonance, this leads to

c = 2.0 * sin(Freq * pi/SampRate)
r = pow(0.5, 24 / 16.0) = 1 / sqrt(8) = ~0.3536


Since the sound engine is mostly MIDI-related, I've used the frequency cutoffs I've found elsewhere on the internet, which led to a relatively simple formula of

CutoffFreq = 250.0 * 32^x


Where x is bound to [0.0,1.0], which leads to a frequency range of 250Hz~8kHz.

However, this is where my issues start.

Let's assume that we're using a maximum cutoff frequency (that is, there should be no filtering on the data) and that I'm mixing at 44.1kHz.

c   = 2.0 * sin(8000.0 * pi/44100.0) = ~1.0791
r   = 8^(-0.5)                       = ~0.3536
1.0 - r*c                      = ~0.6184


Now let's assume the input signal is a basic triangle:

Input[] = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 0.8, 0.6, 0.4, 0.2}


Applying the code above leads to

// Beforehand...
v0 = 0.0
v1 = 0.0

// 1st Sample [works okay]
v0 = 0.6184*0.0 - 1.0791*0.0 + 1.0791*0.0 = 0.0
v1 = 0.6184*0.0 + 1.0791*0.0              = 0.0

// 2nd Sample [incorrect]
v0 = 0.6184*0.0 - 1.0791*0.0 + 1.0791*0.2 = ~0.2158
v1 = 0.6184*0.0 + 1.0791*0.2158           = ~0.2329 [residual 0.0329]

// 3rd Sample [more than doubly incorrect]
v0 = 0.6184*0.2158 - 1.0791*0.2329 + 1.0791*0.4 = ~0.3138
v1 = 0.6184*0.2329 + 1.0791*0.3138              = ~0.4826 [residual 0.0826]


As you can see, this doesn't exactly work correctly. In fact, judging from the sound of it, it's as though I had introduced a slight high-pass filter as well as increased resonance slightly.

As such, I was wondering if anyone could offer an explanation a more programming-related person could understand for computing low-pass filter coefficients and equations, as well as shed some light on how to properly implement this.

First of all, your filter is a non-typical lowpass filter. The transfer function of your filter is

$H(z) = \frac{\beta^2}{1+(\beta^2-2\alpha)z^{-1}+\alpha^2z^{-2}}$

where $\alpha = 1-rc$, $\beta=c$.

In the example you use these values

$c = 2\sin(8000\pi/44100)$, $r = 8^{-0.5}$.

They give the following frequency response.

In the example where you step-by-step process the first samples of your triangular waveform it seems that you expect that input to pass through undistorted. Is that correct? if so, why do you expect this?

As an alternative you can use a 2nd order butterworth low-pass filter. Here http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt you find the expressions for the coefficients as well as the difference equation you have to implement.

• Well, I would've expected that the highest frequency value I can get from the equation I found for MIDI cutoff would result in an undistorted output, as that's what it implies. [EDIT: sorry, I didn't realize hitting Enter would stop <_<]. What's the difference between a Butterworth filter, a typical low-pass filter, and the filter I'm implementing a the moment? – Ruben Nunez May 23 '13 at 9:36
• The Butterworth magnitude response is monotonically decreasing from DC. There are no ripples nor overshoots. Furthermore, the Butterworth filter puts two zeros right at nyquist (fs/2). It costs a little extra but not much. – niaren May 23 '13 at 10:27
• I'm not sure I understand your explanation of why you expect to see an undistorted output. – niaren May 23 '13 at 10:28
• I forgot to mention that the Butterworth filter also has a sharper cutoff (steeper roll-off) than your filter. – niaren May 23 '13 at 11:33
• The Butterworth filter has two zeros at fs/2. The numerator can be put on the form $k(1-z^{-1})(1-z^{-1})=k(1-2z^{-1}+z^{-2})$. Therefore, only three multiplies are required in the direct form. – niaren May 23 '13 at 20:02