Hello Jules !
I can see in the processSamples class of the juce::IIRFilter class that the filter structure used is the Direct Form I. I think it is a bad idea, and that this class should use at least the Transposed Direct Form II instead. The resulting structure would be canonic (using less delay lines and less state variables), with a better numerical robustness, and its output would be a little smoothed when one of the filter parameters changes (and we will have of course exactly the same transfer function). I propose to replace “float x1, x2, y1, y2;” in the header with “float v1, v2;”. And then, I propose the following new code :
[code]//==============================================================================
void IIRFilter::reset() noexcept
{
const ScopedLock sl (processLock);
v1 = 0;
v2 = 0;
}
float IIRFilter::processSingleSampleRaw (const float in) noexcept
{
float out = coefficients[0] * in + v1;
JUCE_SNAP_TO_ZERO (out);
v1 = coefficients[1] * in + v2 - coefficients[4] * out;
v2 = coefficients[2] * in - coefficients[5] * out;
return out;
}
void IIRFilter::processSamples (float* const samples,
const int numSamples) noexcept
{
const ScopedLock sl (processLock);
if (active)
{
for (int i = 0; i < numSamples; ++i)
{
const float in = samples[i];
float out = coefficients[0] * in + v1;
JUCE_SNAP_TO_ZERO (out);
v1 = coefficients[1] * in + v2 - coefficients[4] * out;
v2 = coefficients[2] * in - coefficients[5] * out;
samples[i] = out;
}
}
}[/code]
Some people on KVRaudio are suggesting even better approaches (the topology-preserving structures for example), but this one is working a lot better than the DFI yet What do you think of this change ?