SmoothedValue, IIRFilter and ProcessorChain

Hey Hey,

So Ive been having some issues with zippering artifacts with my IIR filters. To preface, I have 2 low shelf and 2 high shelf filters which get adjusted at different parts in my processorChain, however none of the filters ever move their frequency just the gain.
These filters realistically won’t change too often, but when my tone slider moves I get some pretty bad zippering unless the slider is moved pretty slowly. It is my understanding that dsp::stateVariableFilter would probably be better to use, however that filter doesn’t support shelves.

I have been trying to get a SmoothValue hooked up to these filters gain parameter for a while now, but since I am using a ProcessorChain, and not iterating over the buffer manually, Im not sure how I would appropriately change the filter parameters for each new smoothed value. I can update the parameters each processBlock call, but this is only fast enough that my smoothValue ramp speed must be at 1.5 seconds to remove the zippering (far too slow).

To summarize, my question is, how can I use a smoothValue to adjust IIR filter coefficients that live inside a processorChain? And if this is not possible, what other routes could I take?

It’s a big question which has lots ramifications. The IIR filters are in particular heavy as they call new when you set their cutoff & gain.

The basic principle though is to free yourself from processing at the same block size as the host, or your plugin will sound different depending on the host block size.

Once you have some system to do that, you should be able to tune the parameters to update as often as you want – and you can tune it to balance the audio quality vs CPU.

What you’re seeking is the concept of a fixed control rate.


The basic principle though is to free yourself from processing at the same block size as the host, or your plugin will sound different depending on the host block size.

This is an interesting point, I never thought about, but it totally makes sense. Do you maybe have any more insight / resources of how it would change in sound with different block sizes?

Thanks in advance !

It’s basically an aliasing issue. If you have fast modulation and a large block size, and you’re processing per blocks, your control signals can start to alias as they’re not being sampled fast enough for the frequency they’re running at.

In the case of simple knobs, it’s still an issue because when you consider how large block sizes can get. If you’re only updating your filter coefficients every 2048 samples because the block size of the host told you to, you’re gonna have to run with some pretty slow smoothing so that you don’t hop too far in your coefficients between blocks. Now you’ve optimized for worst case, so you’ve dropped your sound quality for the people running 128 sized blocks.

1 Like

Thanks for the reply. However it seems like to get something like this working would realistically not be worth it just to reduce some zippering for one knob ! Is a fixed control rate commonly used though?

It’s a great question, and one that I’ve been thinking about when considering trying use processorChain in plugins. Once you call processorChain.process, the block of audio data gets processed all in one go, and there’s not an opportunity from outside the chain to change control values.

I’m not sure the best way around it, but smoothing the control values from inside the chain was one idea I had. For instance, maybe a templated wrapper around the juce::dsp classes, that combined the dsp classes with a SmoothedValue. The ProcessorChain could still call the process method, but internally, the wrapper would go sample-by-sample, calling SmoothedValue::getNextValue, recalculating dsp coefficients if necessary, and then calling the processSample method of the dsp object.

But at what point is that just super convoluted, rather than handling the parameter smoothing sample-by-sample in your main processor code, and calling those processSample methods yourself… begging the question, when is it worth it to use processorChain?