Audio rate modulation of filters at high resonance - how can I do it without things getting out of control?

Pselodux's icon

Hi all, I'm building a modal synth engine using mc versions of resonant filters, and everything is sounding great so far, but I tried adding some modulation to the filter frequencies for a bit of extra dimension. However, due to the high resonance, any modulation above a certain frequency tends to cause the filter to explode.

What is happening here, and how can I prevent it? So far I've tried biquad~, lores~, as well as rolling my own in gen~ from the examples provided in Max, and all of them have the same issue. The only filter seemingly without such issue is the Moog ladder filter example in gen~, but I'd rather not use that as I'd like to keep things open for potentially porting to other platforms.

I could probably get away with changing the resonant filters into sine waves and treat it more like an additive synth, but ideally I'd like to use this technique in a more typical subtractive synth format as well, as filter FM at high resonance is one of my favourite things to do with analog synths (and even with digital synthesis back when I had a Nord Modular).

Roman Thilenius's icon

biquad kernels usually explode at one point, the opposite behaviour can be found in SFV.

and limit yourself to sub warp-speeds. a ladder filter or sfv might be stable, but fast modulations will still cause aliasing artefacts which makes the process pointless.

Pselodux's icon

Interesting. Thanks for the information, I’ll give sfv a try. I’m fine with aliasing - in fact, my patch sounds rather pleasant to my ears when the filter spread increases to the point that aliasing occurs - it’s just the overload that I can’t deal with.

edit: ah, did you meant aliasing due to the input being a float rather than a signal? Perhaps I’ll have to just deal with that or build my own svf in gen.

Graham Wakefield's icon

I would highly recommend working with SVFs rather than biquads -- the SVFs that are variously called "trapezoidal" or "zero delay" or "TPT" etc., usually based on math in Vadim Zavalishin's incredible book (https://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_2.1.0.pdf?srsltid=AfmBOoo4SwMzbGmq2PBrzJX4eykSwShInHCF20f9fgQITb4wzQ8PgmbU) or Andy Simper's code (see https://cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf).

Here's Andy Simper's SVF as genexpr (drop this into a codebox in gen~):

// Ported directly from Andy Simper's trapezoidal SVF here:
// http://www.cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf

History c1, c2;

input, cutoff, q = in1, in2, in3;

// coefficients:
g = tan(pi * cutoff/samplerate);
k = 1/(q); // = 2 - 2*res

a1 = 1/(1 + g*(g + k));
a2 = g*a1;
a3 = g*a2; // g*g*a1

// process:
v3 = input - c2;
v1 = a1*c1 + a2*v3;
v2 = c2 + a2*c1 + a3*v3;

c1 = 2*v1 - c1;
c2 = 2*v2 - c2;

lp = v2;
bp = v1;
ubp = k*bp;

hp = input - ubp - lp; // = np - lp = lp - pk
np = lp + hp; // = input - ubp
pk = lp - hp; // = input - ubp - 2*lp;
ap = lp + hp - ubp; // = input - 2*ubp;
bshelf = input + ubp; // input + hp + lp - ap

out1 = lp;
out2 = bp;
out3 = hp;
out4 = ap;
out5 = np;
out6 = pk;
out7 = ubp;
out8 = bshelf;


The behaviour of these SVFs is mostly equivalent to what you can do with biquads, but they do not blow up under audio-rate modulation. Also they give you lots of different filter shapes at once, rather than having different sets of coefficients for each filter shape. They are not more expensive than biquads. So basically in most cases, this SVF is a better choice than the biquad. :-)

Graham Wakefield's icon

That said -- for a modal synthesis engine, I'd also consider working with a complex resonator -- I find those are also stable to modulation, and might be more interesting to explore for a complex modal model:

Max Patch
Copy patch and select New From Clipboard in Max.

We'll be talking a lot about these in the 2nd GO book :-)

Pselodux's icon

Ah that is excellent, thanks so much for the code. I’ll give it a try!

Roman Thilenius's icon

no, i meant aliasing just because of the high modulation speed. (the vanilla svf~ object takes signals, too)

Pselodux's icon

Update: the gen~ patch above worked perfectly with some minor modifications to my code. I only barely understand how it works but it’s a much nicer result. Thanks again to both of you for helping.

vojko v's icon

why a complex resonator, what is the difference?

the imaginary output sounds duller, what is it exactly?

Graham Wakefield's icon

It may sound duller because the ping doesn't go into the imaginary axis. Instead of a sudden level jump (a click) you get a sudden slope change (a thump, which has more spectral falloff). That may be preferable in some cases. Ping into the imaginary side instead and the two output sounds will flip around. If instead you want to feed continuous audio in, you probably want to add it to the magnitude.

Why complex? There's a bunch of things you can do with complex signals that you can't do with real-only signals, like single sideband modulation etc, and working with instantaneous phase and magnitude.

BTW you do also get near-complex (90 degree phase shifted) outputs from the SVF lp, bp, hp outputs. They get closer to being true complex signals as the Q increases.

Instead, the complex resonator gives two 90-degree shifted bandpasses.

Actually on a certain level, they are equivalent to each other; both implement second order feedback systems of complex variables. But they just do the intermediate calculations in slightly different ways (different intermediate matrices), which leads to different affordances and responses at those afforded inputs.

ward de jager's icon

Is there a difference between gen~ codebox instantiated in the patcher , versus the codebox inside gen~ itself ?

Excellent sounding filter btw , just make sure resonance is not < 0

vojko v's icon

"If instead you want to feed continuous audio in, you probably want to add it to the magnitude."

understood everything except this, where is the magnitude??

ward de jager's icon

Also , for those interested and since we 're talking filters anyway .

Vadim Zavalishin is now working at U-he ( I asked Urs permission if I could share this info and got green light )
So , imho Reaktor is now buried and dead since it's main developer ( and inventor of the fabulous core language ) has now left .

Let's keep the legacy alive an port all those fantastic core filters to gen ~