IIR::Filter. Is per sample modulation possible?


#1

I do not get how to use the new IIR::Filter in a plugin which is able to modulate it with an LFO or envelope.

If I want to change the filter cutoff each, let’s say, 20 samples, using dsp::IIR::Coefficients::makeLowPass(), it seems pretty expensive. And they do a new and I cannot do it in the audio thread, I would have to create my own function to modify the coefficients coping it from the original makeLowPass(), which points me to think that I’m doing it in the wrong way.

The problem increases if you would try to modulate a Chebyshev filter, since its static functions return an Array!

Are such classes suited to being modulated per sample? If they are, how should it be done?


#2

OMG, why is the new dsp module’s IIR Filter implementation allocating (calling new, using an Array<NumericType>, etc) when creating new coefficients? The old IIRFilter class in juce_audio_basics uses a simple 5 element C-array as member object, which makes it usable in the audio thread (so you can modulate the coefficients like you suggested).

I am sure I must be overlooking something very simple, but this is a bit irritating…


#3

The reason might be (I didn’t write it), because in the old IIRFilter you were in charge for the life cycle of the coefficients.
In the DSP module the dsp::IIR::Coefficients are reference counted objects, so they can easily be swapped atomically and are safe in case the other reference is deleted outside the processing.

I think you can use it like before using the assignment (copy) operator.
Sure, there is still the allocation of the reference counted object in the makeXXXfilter calls, that will go out of scope immediately afterwards…


#4

But how would you achieve modulation of the coefficients if they can’t be created in the audio thread?

I know this class is rather designed for static usage (the state variable filters are more suited for fast modulation), but nonetheless you might want to modulate a peak EQ with a slow LFO or whatever and removing this possibility seems like a bad idea.

Sure, there is still the allocation of the reference counted object in the makeXXXfilter calls, that will go out of scope immediately afterwards…

Yes. Why is that? I just don’t understant why the coefficients are stored on the heap, I mean it’s not that much of data (for FIR it’s different because the impulse response can get big and it might make sense).


#5

Yes, but I think you shouldn’t be creating in each sample a new coefficient object, that’s quite expensive. There should be a better method, maybe creating your own function that just modify or creates a stack coefficient object.

But I still do not know if this would be the way you modify a filter values in each sample.


#6

Yes, obviously you need to downsample the control rate that creates the coefficients, but this has nothing to do with the problem that the makeXXXPass() functions are allocating.


#7

The response you’re going to get is "use the StateVariableFilter"

There are problems with that of course, because it’s fixed to 2nd order and is a single output instead of 3 outputs (like the system it implements is designed), preventing you from using it to build higher order IIRs that can be modulated (@IvanC I think I mentioned this like a year ago).

What you can do is copy the StateVariableFilter code and create a filter “core” that implements an s-plane biquad transfer function, and then use good old fashioned filter design equations calculate the correct coefficients. If you look at the code, the only difference between LPF, HPF, and BPF SVF filters is where the output is pulled in the difference equation. If you add a gain to all of those outputs (say a0, a1, a2) you get the transfer function:

(a0) * s^2 + (a1) * wc * s + (a2) * wc^2 
----------------------------------------
        s^2 + (wc/Q) * s + wc^2

Which you can use to create any classic IIR you want, and all the coefficients can be modulated at audio rate (no need to downsample).

And I agree, any coefficient calculation function should not allocate if it doesn’t have to, but coefficients being stored on the heap isn’t bad. Especially since the filter order isn’t necessarily known at compile time.


#8

Thank you for the information, very helpful!