dsp::IIR::Filter::snapToZero - need help to understand

Hello,
mainly I think I understand what is snapToZero() or ScopedNoDenormals. It helps to avoid number that are smaller than float can express. So for float it is smaller than std::numeric_limits<float>::min().

But actually I don’t understand how and when to use it?

In the JUCE documentation for dsp::IIR::Filter there is sentence for using processSample() method:

Moreover, you might need the function snapToZero after a few calls to avoid potential denormalisation issues.

But please tell me what is exactly “after a few calls”? Should I call snapToZero every 10 calls of processSample() or every 3 calls, or maybe it’s enough if I call one time at whole buffer?
Or maybe I should use it only in some specific situations?

And should I use it if I already have instance of ScopedNoDenormals noDenormals; on the begining of processBlock() method?

For any help great thanks in advance

ScopedNoDenormals deals with subnormal numbers, which is a performance problem because they’re (essentially) zero but take up an order of magnitude more cycles to perform arithmetic with. This is most notable shortly after an input goes to zero, but its a problem for all processing, especially in the presence of noise.

snapToZero deals with limit cycles, TL;DR is that stable IIR filters must have their filter states decay to zero when the input is zero, but because of quantization error, they will bounce between the +/- minimum values of the numeric type. Snap to zero guarantees that the filter states return to zero.

TL;DR

  • ScopedNoDenormals helps when inputs can be tiny. Use it for all DSP.
  • snapToZero eliminates limit cycles in any recursive/IIR processing. Use it in IIR/feedback systems.

And to be pedantic, denormals are a floating point problem, limit cycles occur in any kind of IIR processing (integer/fixed point/float point)

2 Likes

Great thanks for your explanation,
but just for quick purpose, could you just tell me how often should I call snapToZero for my low cut filter? should I call it after each call of processSample() or less? Now I call it on the end of my processBlock() so it is called every buffer block. So it depend on buffer size I set in my host. So maybe I should use it more strictly?

You’ll have to experiment and profile to figure it out. One way to do it is to compute the impulse response of your filter and find how many samples it takes to contain an arbitrary amount of the energy of the signal (say, 99%) and then set that as your interval to snap to zero. Since this changes with the coefficients of the filter, you’d pick the worst case scenario (highest Q factor, lowest cutoff frequency).

2 Likes