Smoothing IIR Filter Response

Hi everyone,

I am experiencing a problem where if I have the gain turned up on an IIR peak filter, and reset the frequency to 20 Hz, from 10kHz - 20kHz, a loud pop that occurs. I’m guessing that the pop is a product of some built-in smoothing, that is trying to happen too quickly over one buffer length. I don’t know how to solve this issue. I’m also not sure what code I would share to address the problem. Do you all have some ideas of where to start?

Thank you,
Andy

Last time I checked, there was no coefficient smoothing in the dsp::IIR filters, which might be your problem. Aprupt changes in the coefficients are known to cause discontinuities in the filtered signal.

Gotcha, no worries. Thank you!

dsp::StateVariableFilter and dsp::StateVariableTPTFilter have automatic cutoff frequency change smoothing however :wink:

1 Like

Nice!

Have there been some changes to this since JUCE 6? I’m still having issues with this. Should I try something like SmoothedValue for this?

If you want to use IIRFilter or dsp::IIR classes without artefacts at cutoff frequency changes, you need to use SmoothedValue classes yes.

1 Like

Hi, thank you for your help. Do you know of a tutorial for how to implement SmoothedValue? I am stuck in how to integrate SmoothedValue into my existing code. I am trying to smooth out band gain changes in my IIR filters.

You create your SmoothedValue for a floating type (float, double) and a smoothing type. Linear smoothing makes ramps. Multiplicative smoothing is linear in log -that is, if you use it for frequency it will be linear in pitch, if you use it for amplitude it will be linear in level (dB).

SmoothedValue<float /*, ValueSmoothingTypes::Linear */> yourValue;  // linear by default
SmoothedValue yourValue{ 0.5f }; // floating type deduced from initial value

To set the length of your ramp, call reset. You can set a length in samples directly, or a sample rate and a length in seconds.

yourValue.reset (4410); // in samples
yourValue.reset (44100.0, 0.1); // in seconds

To set a new value to smooth, call setTargetValue.

yourValue.setTargetValue (newValue);

Once per sample in processBlock, call getNextValue to get your current smoothed value.

auto yourValueNow{ yourValue.getNextValue() };

If you need to jump many samples into the future, call skip. This effectively advances the smoother -you can’t go back.

auto yourValueAfter200Samples{ yourValue.skip (200) };

If you need to get the current value many times for each sample, call getNextValue only once, because it advances the smoother. Every subsequent time call getCurrentValue.

auto yourValueNowAgain{ yourValue.getCurrentValue() };

To know if you’re in the middle of a ramp, call isSmoothing(). To know the last target value (which may or not have been reached yet), call getTargetValue(). To make the smoother jump to a new value (that is, to bypass the smoothing), call setCurrentAndTargetValue (newValue). If your value represents a gain (an amplitude), you can advance it for a whole buffer and apply the smoothed gain to it calling applyGain(). There are three versions: for an AudioBuffer, for a pointer, and for two pointers (one for output, one for input).

2 Likes

Hey, I am still getting intermittent pops in my gain knobs, and I think I am doing something wrong in my process block, here’s an example for what I have for one of the bands

ban1Gain.reset(getSampleRate(), 1.5f);
ban1Gain.setTargetValue(targetValueGain);
auto ban1GainNow{ ban1Gain.getNextValue() };
ban1Gain.setTargetValue(ban1GainNow);
auto ban1GainAfter200Samples{ ban1Gain.skip (200) };
ban1Gain.setTargetValue(ban1GainAfter200Samples);
auto ban1GainNowAgain{ ban1Gain.getCurrentValue() };
ban1Gain.setTargetValue(ban1GainNowAgain);

How does this look to you all?

Also, I’m not totally sure how this smoothing connects to my IIR filter. Maybe I’m missing something.

Thank you,
Andy Bainton

Well, those were just examples of how to use the class. You have to connect the pieces in the proper places. Reset should be called where you manage sample rate changes, usually prepareToPlay. If these gains come from parameters, you should call setTargetValue from the parameter change callback. Then in processBlock, for each sample you should check if the smoother is in the middle of a ramp, and in that case update your filter:

if (band1gain.isSmoothing()) /* update your filter with band1gain.getNextValue() */;

Anyway, going back through the post I see your problem was with modulating frequency, not gain. If you want to fix that, you should smooth your cutoff parameter. The mechanics are the same -reset in prepareToPlay, setTargetValue from parameter changes, check the smoother in processBlock and update the filter if needed. You may want to set the smoother to Multiplicative instead of Linear. In that case, remember to never give it a target of zero.

Bear in mind that fast, drastic changes of cutoff are not very welcome by most filter topologies. Even with smoothing, you may get a slight discontinuity, unless you make the smoother very slow.

Thank you. This is all useful. Just to ask about updating the filter, would that function in the process block look like this?

if (ban1GainSmoothed.isSmoothing())
{
    ban1GainSmoothed.getNextValue();
    updateBan1();
}

I get what you mean about the discontinuity. I’ll just have to make it work.

Thanks,
Andy

getNextValue returns the updated value. You have to set the filter’s parameter to it.

Gotcha! Will do. Thank you