IIRFilter Gain

The documentation has this to say about the gain parameter in the IIRFilter class.

I have a band pass filter with a gain of 2 and was expecting that to boost the centre frequency by 6db. Instead I get a 12dB boost.

The filters designed using the IIRFilter class seem to give a dB gain of 40log(gain parameter). Once I realised this it was easy to change so my gain parameter gives the expected dB gain.

Just wondering if there is a reason this class works this way, filter design is not my strong point. (Or maybe I am using the class wrong)

This is an issue of the formulas. In one of the reference documents about filters, the RBJ Cookbook, we can see that a square-root must be applied to the gain factor, and this has not been done in the JUCE code.

For instance, the Laplace transfer function (continuous) of the peak filter is the following :

with G = sqrt(10 ^ (G dB / 20)) or G = 10^ (GdB / 40). Indeed, if s = j w0, |H(j w0)| = G². And, this problem is also present in the LowShelf and HighShelf filters code. In these three cases, the constant A must be calculated this way :

const double A = jmax (0.0f, sqrt(gainFactor));

instead of

const double A = jmax (0.0f, gainFactor);

Moreover, I would like to say once more that it’s wrong to call this class “BandPass”, since it’s not a band pass filter but a peak filter, the band filter having the following Laplace transfer function :

Cheers, I had already done that square root jobby. Just thought I might be being a bit hacky.

Good to know I wasn’t just being stupid and using the class wrong :D.

:mrgreen:

Thanks Wolfen, now I know why my filters distorted so easily… :smiley:

Hello Jules ! Is it possible to update the IIRFilter code, so that the “BandPass” (sigh), the LowShelf and the HighShelf classes work as they are expected to ? Thanks !

I don’t think I could change that without it breaking existing settings… e.g. in tracktion, it’d make all the existing EQs in people’s edits subtly different!

Not the “breaking existing settings” argument again please ! This is a serious BUG. When you ask the bandpass filter (which is again a parametric peak filter, and not a true bandpass filter, because their behaviour are not the same) to increase the gain of 12 dB, the gain is actually increased with a gain of 24 dB ! That means everybody who wants to use your class needs to know there is a problem, and that he has to add a workaround. You just can’t leave an audio effect doing something wrong just because you have done a bad implementation in the past, and because a lot of people are still using it ! I just don’t want to be forced to use a buggy class that I need to “hack” every time I need it. Or else, that means I must forget the idea to use your IIRFilter classes. And that you should at least tell in the documentation what your class is really doing.

Moreover, you can add some code in Tracktion to help the people that use your EQ to keep the same sound in their edits… For example, you should develop an EQ which does the right thing in Tracktion 4. And then, all the projects done with old versions of Tracktion will have their EQ gains converted before being saved in the T4 format.

Isn’t it better to try to improve the utilisation of JUCE in the future, instead of keeping some old bugs to help past projects to work indefinitely ? If you have followed this logic further, you had add some support in Tracktion 4 for computers with Windows XP and one core :mrgreen:

I hear you, and when I have a moment I’ll try to find a way to make this work. I can probably do it be renaming the method, to force people to be aware that something has changed.

Ok. This was a good opportunity to refactor that class, which was never designed right, and always annoyed me. I’ve separated the coefficients out now into a separate class, and corrected this calculation in doing so. You’ll need to rewrite any code that uses it, but this design is far cleaner than the way it was done before.

Thanks, that’s perfect ! :wink:

Awesome :smiley:

LowPass Filter now behaves wrong ( to loud output signal ), also there is now way to make a IIRFilterAudioSource inactive anymore, imho it would be better if it still take the IIRFilter as a parameter instead of coefficients only.

The low-pass is exactly the same (??)

Good point about being about to disable an IIRFilterAudioSource, I’ll add a method to do that. But the new design is unarguably better than passing around IIRFilter objects when all you really want are their settings - you gotta love that good old single-responsibility-principle!

Low pass seems to be working fine for me.

I do have a suggestion though. It may well be a stupid idea and I haven’t quite thought through the implications fully. Could the IIRFilter class hold a pointer to an IIRCoefficients object rather than just the object?

This way when you have several IIRFilters with the same coefficients, you can easily update all their coefficients in one line of code, without having to call setCoefficients on each filter individually.

I have implemented this in a roundabouts way and it seems to work fine. You may be able to come up with an important reason this should not be done though.

ok, i will check again (currently it clips maybe because the filter aren’t disabled)

currently i use code like this

[code] if (!lowPassFreq.isMaxFrequency())
{
DBG(“Set Filter active”);
double frequency=lowPassFreq.getFrequency();
if (frequency>hostSampleRate/2.001)
{
frequency=hostSampleRate/2.001;
};
audioChain->lowPassFilter.setCoefficients(IIRCoefficients::makeLowPass(hostSampleRate,frequency));
} else
{
DBG(“Set Filter inactiv”)
audioChain->lowPassFilter.makeInactive();
};

		audioChain->lowPassAudioSource->setCoefficients(audioChain->lowPassFilter.getCoefficients());[/code]

No! A few reasons, although there may be more:

  • It’s always better to use value semantics rather than pointers when possible. And in this case, the entire coeffs object is very small and well suited to being used as a by-value object.

  • As soon as you have a shared object, you have to worry about and set policies for its ownership.

  • It’s unsafe to modify coeffs while a filter is using them - you’ll get terrible glitching and maths errors if you modify the values halfway through a calculation, and the filter’s internal state could end up as NaNs or INFs, which doesn’t sound very nice!

Those certainly are some good reasons.

ok anything working fine now!