Crossover Filter Design Video Tutorial

    Building on my previous filter design videos (see below), I use the filterdesign, filterdetail and gen~ objects to make a crossover filter that is perfect for use in multi-band EQ's, compressor/limiters or sound design applications. By using these tools to create a Linkwitz-Riley filter system, join me for a 20-minute trip into examining, testing and ultimately building a filter that doesn't otherwise exist in Max.
    Watch other filter design videos: A Tour of Filtering Tools and Demystifying Filters.

    Crossover Filter Design

    • Sep 15 2016 | 6:05 pm
      these are a great set of tutorials tim. long may they continue. (do you take requests?!).
      i implemented trond's linkwitz-riley in genexpr a while back, and use it for all my multiband processing needs.
      last year we released a few additions to the oscillot package including a crossover based on it. attached is a max patch version of this module from the package, in case anyone else is interested.
      nowhere near as useful as the one in your tutorial as the coefficients are calculated at signal rate (!!), but maybe someone will find it interesting to rip out the genexp code and take a look. if not should at least be compatible with beap. also paired here with graham's 2-pole crossover from the gen~ examples.
      pete .
    • Sep 15 2016 | 6:47 pm
      This is fabulous! Thanks for sharing @STKR!
    • Sep 22 2016 | 3:19 pm
      This filter series is really awesome. I don't understand the maths, but this series explains filters in a way that focuses on the use and outcome instead of the mathematics at work.
      Maybe this is the discussion of a future tutorial, but how could I use the Linkwitz-Riley crossover to divide an audio stream into more than two parts (eg low-mid-high)? Intuitively, it seems like I could again divide one of the streams, like the low pass stream, and divide it again into a low and high stream, resulting in a low pass, a middle bandpass and a high pass. Will my three audio streams remain in phase? Do I need to add delay to the high pass side to compensate for the extra filters on the low-pass side?
      Thanks again!
    • Sep 22 2016 | 9:43 pm
      more than two bands... just chain the crossovers serial.
    • Sep 28 2016 | 4:27 pm
      Nice article. Is there a way to easily implement other type of filters like Bessel (std and norm), Linkwitz-Riley, Elliptical and All-Pass. Chebyshev 1 and 2 are already included in filterdesign~ I see.
    • Jan 29 2017 | 1:29 am
      Your video has been incredibly useful for helping me with my studies! I am creating a crossover simulator at the moment and I wondered if you know of a way to implement a Bessel filter? Any information you have would be incredibly useful. Many thanks.
    • Jan 30 2017 | 2:06 pm
      I haven't implemented a Bessel filter for Max. Depending on your application for the filter you could generate biquad~ coefficients using Octave's besself function ( ).
      The same is true for an elliptical filter.
      If you want to have a filter than can be dynamically modified (as the filters in filterdesign are) then it will take some significant math chops. A good starting place (apart from the Octave besself code) is this:
      Hope this helps!
    • Aug 10 2019 | 2:37 am
      Thank you Tim for this very insightful and helpful tutorial. I tried to run the "3 - crossovers - gen~" tutorial patch for the Linkwitz-Riley crossover but I am getting the wrong frequency response as you see from the attached screenshot. The java script seems to be OK as I get the correct response in the "2 - crossovers - lr" tutorial patch using the filterdetail object. I am running as required at 44100 Hz and have changed the I/O and signal vector sizes to various values in Audio Status but nothing changes and the problem remains. I am puzzled at what could be causing this. Any insight would be appreciated. (Max 7.3.5, Mac OS 10.12.6).
    • Aug 10 2019 | 9:44 pm
      i am posting this question here because it is releated to a similar context: how far away is a "regular" biquad from a butterworth (read: cascades of 2) when it comes to its main property, the absolute frequency flatness at the crossover point / rated frequency?
    • Aug 11 2019 | 2:28 pm
      I updated Max 7.3.5 to 7.3.6 and the problem I reported above went away. The spectral plot now shows the same curves as in the tutorial. Does anyone have a clue why?
    • Aug 20 2019 | 1:11 am
      I found out that the issue with the wrong curves in the spectral plot~ I reported earlier has nothing to do with the version number of Max but rather the signal vector size. For the spectral plot in that tutorial patch to display the correct curves the signal vector size must be 256 or smaller.
      However I now realized that there is another issue, this time with the sound coming out of the gen~ objects in that tutorial (the one about Linkwitz-Riley gen~ based filters). It is best heard with pink noise (but it is detectable with any wideband audio) as the input (as shown in the attached patch). Basically, as you change the XO frequency either continously (e.g. with a flonum object or live.dial) or by entering discrete values, you will hear a thumping noise/click at every change. This thumping noise is most evident for XO frequency values below 400 Hz and gets very loud at the monitors/speakers (when set to regular listening level) when the XO frequency is lowered below 100 Hz. It is also clear from the audio in the attached patch tab the thumping noise is only on the HF band of the 2-band crossover.
      I was able to stop it when the XO frequency is changed by discrete values by setting a gate~ that stops the output of ten~ from going out to ezdac~ for about 15 ms, but that trick does not work well when the XO frequency is changed continuously.
      Tim, do you know what is causing this noise? Is there any way to avoid it? Thanks in advance.
    • Aug 20 2019 | 4:52 am
      using sqared topology is less accurate than using 2*butter.
    • Sep 06 2019 | 6:30 pm
      Lots here to respond too, but first, the Butterworth is not something to be compared to a biquad. In fact, the typical implementation of a Butterworth filter is performed with 1 or more biquads. You can use the [biquad~] object or the [cascade~] object.
      Regarding the spectral plots, yes, the [windowed-fft~] abstraction has some requirements of the vector size. Specifically, your signal vector size must be 1/4 the size of the FFT or smaller. This is documented in the "spectral" tab for the [plot~] object. I am hoping to eventually replace this abstraction with an external object that will remove these undesirable sensitivities.
    • Sep 06 2019 | 6:37 pm
      Regarding the "thumping" or other discontinuities when the frequency of the filters change, this is a known problem with filters, particularly when there is feedback in the filter. Do you clear the history when you change the frequency and induce a pop? Or leave now-wrong data in the history?
      The [biquad~] object has an attribute called "Smooth Coefficients" which implements an approach to helping with this problem. It looks like the [cascade~] object does not, which is unfortunate. I'll make a note to research this further to see if we can add it.
    • Sep 11 2019 | 1:28 am
      Thank you Tim for the reply. I am not sure I understand what you mean by "clearing the history". There is not much documentation on the history object in gen. Could you please clarify how to clear the history. Perhaps in the context of the tutorial "3- Crossover - gen~" that you posted originally. Would clearing the history solve the thumping issue?
    • Sep 12 2019 | 10:28 pm
      "Clearing the history" means setting all variables used for feedback to zero. It will trade the thumping for a popping sound which you probably also don't want. So... yeah... also not ideal...
    • Feb 12 2020 | 3:58 am
      Thanks Timothy! This really helped me make a multiband compressor in gen and learn a great deal about filterdesign. I ported your JS crossover calc into the new event driven [gen] if anyone would like it. it also dynamically adjusts to your samplerate using dspstatus.
      the 'samplerate' and 'SAMPLERATE' constants don't seem to function correctly in event driven [gen] on my system samplerate = '200.' when set to 44100.
    • Feb 13 2020 | 8:08 pm
      Thanks for this report @lysdexic. I can reproduce and have ticketed for further research.
      I had initially thought that perhaps my scheduler was running at a 5ms interval and so 200 was a sensible response. But I see that my scheduler preference setting is 2ms, so I'm not sure where the 200 is coming from.