Sample rate (Hz)
Fc (Hz)
Q
Gain (dB)

Something useful: a biquad filter coefficient calculator. Set the filter Type, the Sample rate (or 1.0 for “normalized” frequency), and cutoff of center frequency Fc. You can adjust Q for lowpass, highpass, bandpass, notch, and peak filters (use 0.7071–which is 1 divided by the square root of 2–for Butterworth lowpass and highpass), and Gain for peak and shelving filters. View the frequency response with a linear or log scale. You can copy and past the resulting coefficients into your programs.
More info on biquads in previous posts, here and here.
Note that there is an improved version of this calculator here.
Hey Nigel…I guess all that competitive math with you in grade school paid off! LOL
Looks like some cool stuff here.
I think you have your a and b coefficients labeling swapped in the coefficient output window.
Thanks for visiting!
Actually, I prefer the a’s in the numerator (of the zdomain transfer function), and b’s in the denominator—some folks go the other way. (I did an informal survey of the DSP texts on my book shelf when this sort of discussion came up some years back, and the a’sontop were most popular, but some fine authors choose the other way as well.)
Folks also disagree on the sign of the denominator coefficients (I use a minus sign at the summation for the denominator/feedback coefficients, others negate the coefficients and use all adds.) If you start from the z transform, the a’s on top and minuses for the denominator coefficients are most natural; if you start at the block diagram of a direct form II biquad, then you’ll tend towards the other convention.
In any case, I’m consistent—check my other articles on biquads and the bilinear transform. But thanks for bringing this up—it reminds me why I need to post some sample code as well.
Nigel, I’ve really enjoyed your articles and have learned a lot. I have a simple biquad filter working fine and I’d like to cascade it. After cascading it appears I’m getting pretty much the same response as not cascading. I’ve defined variables “current_sample” and “filtered_sample” so current_sample is the output of the ADC and filtered_sample is then the value after filtering. Below is the simple biquad code. My thoughts were initially that I could simply run the BiQuad filter section again changing only the “y” values. I’m not sure it’s that easy and kindly ask your help.
—————————————————————————————————
// Lowpass fc=20, 2000 samples/sec, Q=.707
a0 = 0.0009446914586925257;
a1 = 0.0018893829173850513;
a2 = 0.0009446914586925257;
b1 = 1.911196288237583;
b2 = 0.914975054072353;
b1=b1*1;
b2=b2*1;
current_sample = SampleADCfloat();
/* BiQuad filter */
x[2]=x[1];
x[1]=x[0];
x[0]=current_sample;
y[2]=y[1];
y[1]=y[0];
filtered_sample= a0*x[0] + a1*x[1] + a2*x[2] + b1*y[1] + b2*y[2];
y[0]=filtered_sample;
/* Cascade? */
y[2]=y[1];
y[1]=y[0];
filtered_sample= a0*x[0] + a1*x[1] + a2*x[2] + b1*y[1] + b2*y[2];
y[0]=filtered_sample;
.repeat above?
.repeat above?
.repeat above?
Hi Paul,
I see some problems with your code, but since you’re using floating point, I’d go with transposed direct form II anyway, which requires just two saved states per biquad:
http://www.earlevel.com/main/2003/02/28/biquads/
I’m sure you can figure it out from there, but for those who need a head start: Looking at the diagram there (let’s call the top delay state z1, and the lower one z2), you’ll see that the output is
output = input * a0 + z1;
Now for the new z1 and z2:
z1 = input * a1 + z2 – b1 * output;
z2 = input * a2 – b2 * output;
To cascade, you’d repeat the above, preceded with “input = output;”, and another a new set of delay states (z11, z22). Also note that you’ll need to adjust your filter settings—cascading two filters that are each 3dB at 10 kHz yields a filter with a different 3dB point and shape (it’s now 6dB at 10 kHz). So depending on the exact filter shape you want, you may want to use different frequency and Q setting for each section, or just adjust the Q.
Thank you Nigel! I’ll give this a try.
Thanks for being so nice, I definately fit the “need a little head start” category since I’ve never worked with filters like this. Just understanding that the “z” values end up as intermediate values in the calculations is a jump start. As soon as I have time I should start a beginner’s course or read a book that starts with the simple terminology.
Thanks again!
Hey, Nigel, nice js app! Very handy!
Calculation of a1 for low shelf seems incorrect though (a1 > 2.0 when all coefficients should have magnitude <= 2). It is probably irrelevant, but anyway I am using the app with Chrome running in Ubuntu.
Glad it’s helpful to you, Adriano! Remember, the shelves have gain, so you’ll see the a coefficients go over 2 for the boost case. BTW, the high and low shelves are from Udo Zölzer’s Digital Audio Signal Processing.
Nigel
Nigel, you are absolutely right. I mistook b’s for a’s and applied the stability triangle rule for the nonrecursive coefficients instead of the recursive ones. Sorry for that.
Hi Nigel!
I just wanted to thank you for your excellent articles. It seems everything I need is right here! Thanks!
Fredrik C
Dear Nigel,
Is it possible to explain how to add the SLOPE for shelfing filters?
(In the formulas)
Hi Gábor,
I did some work on slope, late last year, including Q (it’s useful to allow a dip before the shelf transition, as some vintage analog equalizers). It was educational, but at a certain point I realized that the only real purpose would be to develop a highquality EQ, and for that I’d want to make other changes as well—and for what, with so many highquality EQ plugins available. So I stopped—it wasn’t something I wanted for myself, and it wasn’t what I wanted to do here in this blog. I might get back to it at another time, but there were so many other things I want to get done first…
But the basic thing was working out the polezero motion in the sdomain, relating to the controls. From there it’s just the bilinear transform. Sorry, I know that’s not very useful information.
Nigel