DSP module IIR Filter causing low freq noise

Hi all,

I’m using the dsp::IIR:Filter together with coefficients from IIRCoefficients. The filter seems to work and the frequency response looks as expected. However, I notice there is a lot of noise in the lower frequencies. See here. I’ve seen it with both the low shelf and peak filter, and it happens with both process() and processSample().

I’m a C++/JUCE newbie so I might be missing something obvious, but I’m also wondering if it could be quantization noise and if there’s anything I can do to avoid it. Here’s my code:

Header:
dsp::IIR::Filter<float> lowShelfLeft;

Constructor:
lowShelfLeft(dsp::IIR::Coefficients<float>::makeLowShelf(44100, 100, 0.5f, 0.5f))

PrepareToPlay:
dsp::ProcessSpec spec;
spec.sampleRate = sampleRate;
spec.maximumBlockSize = samplesPerBlock;
spec.numChannels = getTotalNumOutputChannels();

lowShelfLeft.prepare(spec);
lowShelfLeft.reset();

ProcessBlock:
dsp::AudioBlock<float> block (buffer);
process(dsp::ProcessContextReplacing<float>(block));

Process:
lowShelfLeft.process(context);

Any input would be appreciated. Thanks!

No replies so far…
I am sure the filters in Juce are not this bad, so I am probably just making some rookie mistake. Anyone care to point me in the right direction? Thanks.

If I understand your image correctly, the noise is somewhere in the -175dB range, which isn’t audible. Is there some practical reason why you are concerned about that?

Thanks for your reply. In my image the purple is the unfiltered signal, and the light green is the filtered signal. So the noise is actually somewhere in the -100dB range where it does matter.

Could it be a problem with the audio channel counts? The Juce filters only process in mono, but you apparently pass in the plugin’s main buffer, which could be stereo or more channels, into the filter instance.

I don’t know about your application but are you sure -100dB matters? If I would have to guess, I’d say the problem simply comes down to rounding errors with floating point -> quantization noise.

You could check it by switching to double, or look at it in python or Matlab.

Even if it didn’t matter I would still like to understand what’s going on :slight_smile: Quantization noise was also my first suspicion. I’m embarrassed to ask, but how would I switch to double when using the JUCE dsp IIR filter?

Thanks for helping out!

Looking at the source of juce_IRRFilter I see that it stores the state in an array of SampleType. This means there can be a recursive buildup of noise in the filters. This means the recursive filters lose a few bits of float precision depending on filter settings - which can easily result in -100dB noise.
In my opinion this is a design flaw of the class, it should always use double (or better the largest available floating point type) for the state and use 64 bit multiplications for the recursive part, even when processing 32 bit floats.
Using double SampleType would help with this, but then you’d have to use buffers with double samples which most of the time is a waste of space.

You will have to change each <float> to <double> in the filter itself, the coefficients, and the audioblock.
And then there’s the problem with the input signal: the processBlock method gives you only float data. So you might want to create your signal yourself, with double precision, because you can only get float in and out of your plug-in. However, for testing purposes it’s fine to do so. Make sure your analyzer is also within your plugin, as you can only get float’s out. Maybe writing it to wav will retain the double precision (with 64bit wav files). Not sure if JUCE supports it, though.

Thanks for the replies guys. This is a bit of a bummer though. I assume not a lot of people are using the Juce IIR filter in their products then?

Well, there are only a few people out there who believe they hear -100dB :slight_smile:

1 Like

There are hosts, that support double processing, not many though. You can implement your plugin to return true in the callback AudioProcessor::supportsDoublePrecisionProcessing() and implement your DSP code in AudioProcessor::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midi).

You have to implement the float version as well, with a little trickery, you can call a templated DSP method from the two callbacks.

Yeah, it seems to be a flaw. It’s well known even for a DSP novice like me that even if the input and output samples are floats, filters should do their internal calculations as doubles.

With states represented in double, we would lose the SIMD compatibility of JUCE’s filters. Four channels for the cost of one is pretty nice, and that’s only SSE… there’s also AVX512 :slight_smile:

So I guess it’s a tradeoff between performance and precision (again, -100dB…).

1 Like

Yeah, I am not really that worried about the low level noise as such, but could the inaccuracies in the calculations lead to some kind of run away behaviors in some cases…?

Admittedly, for some applications -100dB could be a problem, especially if there are some kind of gain stages or dynamic processing involved. As a stratocaster player with a high-gain pedal active, I am quite happy about my noise less single coil pickups :wink:

When it comes to IIR filters and resolution, the coefficients are very critical. Quantization of coefficients has to be taken into account when designing filters, as poles could be made unstable by rounding errors. And of course the state also plays a role in the stability game. As long the design ensures stability, and I very much assume it does in JUCE’s implementation, we are all good.

just going to leave this here

Good notes on improving the numerical robustness of a filter and evaluating it using SIMD (not by interleaving channels). TL;DR don’t use direct form biquads.

The juce IIR filter doesn’t use cascaded second order stages iirc. That’s not great for higher order filters.