[music-dsp] Re: Revisiting C++ Filtering Classes
robert bristow-johnson
rbj at audioimagination.com
Wed Jun 3 20:07:07 EDT 2009
On Jun 3, 2009, at 6:45 PM, Vinnie wrote:
>
>> From: robert bristow-johnson <rbj at audioimagination.com>
>> i'm trying to understand what your "bottom line" is.
>
> I want to make this a comprehensive C++ library for IIR filters. I
> would like to keep the single-header design. The goals:
>
laudable goal.
okay, i will let you worry about this stuff:
>> - Arbitrary order, changable at runtime
>>
>> - Flexibility to control the floating point types used for
>> calculation and output
>>
>> - Robust class hierarchy that encapsulates each concept neatly
>>
>> - No repeated formulas or code
>>
>> - Fast parameter changes using cached calculations
>>
>> - Functional programming style that allows easy assembly of
>> filters from primitives using templates
>>
about this stuff:
> - Butterworth, Chebyshev Type I, Chebyshev Type II, Elliptic,
> Bessel response types
there are nice closed form formulae for the s-plane coefficients (as
"normalized s" LPF prototypes) for the first three (perhaps that is
what you're trying to figure out, you might want to learn some neat
stuff about the Tchebyshev polynomials to do that). not so nice for
the latter two, but you might be able to dig up someone's C code for
the Jacobi elliptical function and the Bessel functions.
now, with the Chebyshev/Elliptical responses, i would recommend to
restrict the passband ripple to be less than 3.0103 dB, the max
stopband gain to be always lower than -3.0103 dB, so that on the
transition slope, there is always a unique -3.0103 dB point which
will always be the "corner frequency" when switching between response
types.
> - All Biquad variants
by this, i think you mean Direct Forms 1&2 and their transposes,
Lattice and normalized ladder, i think there is a Rader/Gold form but
i forget what it is. maybe a "state-variable" form that has discrete-
time integrators, 1/(z-1), as elements instead of 1/z. you might
want to look into stuff that Duane Wise has done with different
forms. sometimes there are numerical issues. i think if you first
go after the Direct Form coefs, you'll be able to map to the other
forms straight-forwardly. that's something to worry about later in
the venture.
> - Low Pass, High Pass, Band Pass, Band Stop, Low Shelf, High Shelf,
> Peaking, Notch of every response type
i think that Band Stop and Notch might be the same thing, no? what i
might call "Band Reject Filter". a Band Stop or Notch that does not
go down to -inf dB at the resonant frequency would be a Peaking
filter with a negative dB gain parameter.
okay, for this, you want to always bring in the *normalized*
frequency (that is that the corner is at w=1) LPF prototype of the
chosen response type above. then use the following s-plane
substitutions for the first four shapes:
LPF: s <- s
HPF: s <- 1/s
BPF: s <- Q*(s + 1/s)
BRF: s <- Q/(s + 1/s)
note that the order of the filter will double when starting with an
Nth-order Butterworth/Chebyshev/whatever LPF prototype and mapping
that to a BPF or BRF.
here, the "Q" is not necessarily the biquad Q unless you started with
a 1st-order LPF prototype. think of it as the approximate reciprocal
of the bandwidth parameter. i think the good mapping should be (from
the cookbook):
1/Q = 2*sinh( ln(2)/2 * BW * w0/sin(w0) )
where BW is the bandwidth in octaves and w0 = 2*pi*f0/Fs .
now, you're gonna need to think about what are the best definitions
for your Peaking and Shelf filters. even though i would *not*
necessarily recommend taking exactly the definitions in the cookbook,
i think what you *do* want are definitions so that leaving the Q and
resonant frequency parameters untouched, a cut of N dB will look
exactly like a upside-down mirrored reflection of the same with a
boost of N dB. in series, the aggregate two should be equivalent to
a "wire". if you simply define the Peaking, Low Shelf, or High Shelf
to be the unscaled input added to the scaled output of a BPF, LPF, or
HPF (respectively), you may very well get into a situation where the
boost and cut are not complementary. so you need to think about this
a bit. BW or slope parameters may need to be "fudged" a bit in some
cases.
also, if you define your Shelves as a scaled LPF/HPF added to a wire
(with the LPF/HPF as perhaps a higher-order Butterworth/Chebyshev/
whatever), then you don't have, in the definition, a shelf-slope
control since there is no "Q" in either the LPF or HPF (to LPF)
mapping above. you might not like it that way.
finally, after you've done that substitution, you have an analog
prototype with the filter shape (Low Pass, etc.) and response type
(Butterworth, etc.), but it is *still* happening at a *normalized*
frequency, w=1. to move it to the desired frequency, w0 = 2*pi*f0/Fs
(where f0 is expressed in the same units as the sampling frequency
Fs), you take your normalized s-plane prototype and make this
Bilinear Transform substitution:
s <- ( 1/tan(w0/2) )*(1 - z^-1)/(1 + z^-1)
that takes care of the BLT substitution *and* compensating the BLT
frequency warping effect on the resonant frequency at the same time.
the compensation of the effect that frequency warping has on
bandwidth is done approximately (but pretty goodly) by the w0/sin(w0)
stretching factor applied to the octave BW above.
--
r b-j rbj at audioimagination.com
"Imagination is more important than knowledge."
More information about the music-dsp
mailing list