[FIXED] 24 dB/oct FIR Filters

How would one go about constructing an FIR filter with a 24 db/oct rolloff a la your standard Linkwitz-Riley? The idea is to create a toggle for linear-phase crossover filters, where the LR filters are swapped out for FIRs that approximate such a frequency response.

In my attempts with trying FIR filter design, I haven’t been able to generate anything that produces that gradual of a rolloff. Is this not possible with the filter design functions available in JUCE, or do I just not know how to use them properly?

The best example I’ve found in searching around elsewhere is this:
(Are digital x-overs flawed?? - Page 10 - diyAudio)

I’m curious what any experts would think of such a method, as well as its plausibility for a real-time implementation.

You can convert an IIR prototype to FIR linear phase using forward-backward filtering.

The idea is to take the IIR prototype, find the impulse response (for a practical IIR, the vast majority of the energy in the impulse response is within the first 150-200 samples, in the worst case). Then create an FIR by creating a mirror of the measured impulse response to use as your FIR coefficients.

You can experiment with how many samples to use based on the cutoff frequency/bandwidth of the filter, window functions applied to the impulse, using type 1/2 or 3/4 linear phase FIRs, etc. The magic is in the tuning.

Sidenote: LR crossovers are not necessarily 24 db/octave. You can go higher or lower.

Sidenote: LR crossovers are not necessarily 24 db/octave. You can go higher or lower.

True! In this case I am shooting for 24 db/oct.

Thanks for your help. So it seems like my main point of difficulty now is figuring out how to generate an impulse response of an IIR that I can use for my FIR. I’d need to be able to generate one in real-time in response to filter cutoff changes.

This should get you started:

template<class IIR>
class FIR
    FIR (IIR i, size_t firOrder)
    : iir(i) 
        firCoeff.resize(firOrder, 0.0f):
    void recordImpulseResponse () {
        for (int i = 0; i < targetCoeff.size(); i++)
            auto input = i == 0 ? 1.0f : 0.0f;
            firCoeff[i] = iir(input);

    IIR iir;
    std::vector<float> firCoeff;

One of the harder parts is updating FIR coefficients in real time. You may need to experiment with interpolating coefficients after updates.

Oh! That’s pretty elegant, and of course…that is exactly what I’d need to do. Thanks again.

Right. And I imagine there will likely be reason to keep the coeffs the same size/length as well?

You probably want to keep FIR order fixed during operation or else you’ll run into real time and thread safety issues (resizing a coefficient vector on the UI thread when a user turns a knob may cause a segfault if it is being processed at the same time, if it causes the vector to be reallocated for example).

Well, it works like an absolute charm on a lowpass. For a highpass, however, the results are a little iffy. It becomes increasingly comb-filtered as the cutoff moves lower – I realize this is expected behavior of FIRs, generally, but I’m curious what can be done to control some of that, other than increasing the filter size (which, although it does help, the results aren’t convincing enough to warrant doubling my filter sizes for.

Additionally, I’m getting a phase shift from the highpass that’s near Nyquist. I get the same thing on the lowpass, but it disappears once you blend even 1% of the dry (delay-compensated) signal back in. Not so with the highpass:

Using a windowing function fixed this, at the expense of making the highpass into a sort of peak-filtered frequency response. :thinking:

Last but not least, I’m trying to figure out how to get these filters to work such that they can produce a variable number of multiband crossovers, i.e. a lowpass, bandpass, highpass. Attempting to cascade a highpass into a lowpass has resulted in a pretty hideously comb-filtered response. I’m guessing my issue is not properly compensating for delay between filters, but I had supposed that they would all be delay compensated with each other if they’re using the same filter lengths. Perhaps I was mistaken?

EDIT: Achieving better results on the bandpass when I produce it simply by subtracting the final band (the highpass) from the previous highpass. Mainly just need to control how to properly compensate the phase against the dry signal as it’s now all out of whack.

DOUBLE EDIT: Had to set the delay to be Filter Length - 1. This thread is now Over.