filter_design.h
Filters steeper than one biquad.
#include <sonore/filter_design.h>- 5types
- 8other declarations
- Guides: Getting started, MIDI and MPE
On this page
Design notes
dsp.h has the RBJ cookbook, which is every shape a SINGLE biquad can be. A 24 dB/octave lowpass is not one of those shapes, and neither is a 48 dB crossover or an anti-alias filter worth the name. Getting there means more than one section, and the only interesting question is what Q each section takes: cascading identical biquads does NOT give a Butterworth, it gives a droopy passband and a -6 dB point that has wandered off the cutoff.
What decides it: the poles of an Nth-order Butterworth sit at equal angles on a semicircle, and each conjugate pair becomes one biquad whose Q is set by where that pair sits. Order 4 is the familiar 0.54 and 1.31: two very different sections that are flat together and wrong separately.
Odd orders need a real pole as well, which is a FIRST-order section: not a biquad with something zeroed by accident, but a deliberate one-pole stage, because a 5th-order filter that quietly rounds to 4th or 6th is a filter whose slope is not what the user asked for.
Types 5
One section of a Chebyshev Type I design: where its pole pair sits.
Butterworth puts its poles on a circle; Chebyshev puts them on an ELLIPSE, squashed towards the imaginary axis by an amount the ripple decides. That squashing is the whole trade: the poles crowd nearer the passband edge, the transition gets steeper, and the passband stops being flat and starts to ripple by exactly the amount that was asked for.
frequencyScale is the part Butterworth does not have. Each section sits at a DIFFERENT frequency -- the pole's distance from the origin -- rather than all of them at the cutoff, and ignoring it gives a filter that looks plausible and has the wrong corner.
The bilinear substitution's normalised coefficients, shared by both kinds of section below. ok is false for a degenerate denominator.
One section of a Chebyshev Type II design.
Type I ripples in the PASSBAND and is flat in the stopband; Type II is the other way round, and that is usually the trade you want. An anti-alias filter's stopband is thrown away, so ripple there costs nothing, while ripple in the passband is ripple in the signal you are keeping.
It is not an all-pole design. Type I gets its steepness from where its poles sit; Type II gets it from ZEROS -- notches placed up the stopband, which is why its rejection is equiripple rather than falling away for ever, and why each section needs a numerator of its own.
The poles are the reciprocals of Type I's, which is the whole construction: inverting the frequency axis turns a rippling passband into a rippling stopband.
template <int MaxOrder = 8> class CascadedIir
A cascade of biquads, plus at most one first-order section.
MaxOrder bounds the storage, so a plugin pays for the steepest filter it might ask for and nothing more. Designing past it is REFUSED rather than clamped: a caller that asked for 12th order and silently got 8th has a crossover that leaks, and would never find out from here.
template <int MaxOrder = 8> class LinkwitzRileyN
A Linkwitz-Riley crossover of any even order.
Two Butterworths of half the order in cascade, which is the whole definition: doubling the poles is what puts both bands 6 dB down at the crossover instead of 3, and what makes them sum FLAT. A pair of plain Butterworths leaves a 3 dB bump there, which is audible on anything with energy near the corner and is the reason multiband processors use this and not the obvious thing.
effects.h has a fixed 4th-order version, which stays: it is the common case and it is smaller. This is for a caller that wants to CHOOSE: 12, 24 or 48 dB per octave is a control a multiband plugin should be able to offer.
ODD orders are refused. An odd-order Linkwitz-Riley does not exist: the construction is two identical halves, and half of an odd number is not a filter order.
Functions 8
- fn
constexpr int butterworthBiquads(int order)How many biquad sections an order needs, not counting the odd first-order one. Order 4 -> 2, order 5 -> 2 (plus a one-pole), order 1 -> 0.filter_design.h:31 - fn
inline double butterworthQ(int order, int section)The Q of one section of an Nth-order Butterworth.filter_design.h:41 - fn
inline double chebyshevEpsilon(double rippleDb)The ripple as an amplitude ratio: 1 dB of ripple is eps = 0.5088.filter_design.h:82 - fn
inline ChebyshevSection chebyshevSectionFor(int order, int section, double rippleDb)Where pole pairfilter_design.h:92section(0-based, 0 .. order / 2 - 1) of an order-orderType I design withrippleDbof passband ripple sits. An order below 2 gives the defaults (Q 0.7071, scale 1), and a ripple of 0 or less is taken as 0.01 dB. An odd order's real pole is not a pair and is not returned here: CascadedIir builds it apart. - fn
inline BilinearCoefficients bilinearCoefficients(double b2, double b1, double b0, double a2, double a1, double a0, double cutoffHz, double sampleRate)One second-order analog section, turned into a digital biquad.filter_design.h:161 - fn
inline void bilinearSection(double b2, double b1, double b0, double a2, double a1, double a0, double cutoffHz, double sampleRate, Biquad& out)bilinearCoefficients() written into a float Biquad. A degenerate result leavesfilter_design.h:188outas it was. Its state is not cleared. - fn
inline void bilinearFirstOrder(double b1, double b0, double a1, double a0, double cutoffHz, double sampleRate, Biquad& out)The same substitution for a FIRST-order section, H(S) = (b1 S + b0)/(a1 S + a0).filter_design.h:215 - fn
inline Chebyshev2Section chebyshev2SectionFor(int order, int section, double stopbandDb)Where pole pairfilter_design.h:266section(0-based, 0 .. order / 2 - 1) of an order-orderType II design withstopbandDbof rejection sits, on the prototype normalised to the stopband edge. An order below 2 gives the defaults; a rejection under 1 dB is taken as 1.