Couple of ballistics filter questions

Hey, I’m looking at the ballistics filter for the first time, and I have a couple of basic questions:

A) just checking: I see that the attack time will be snapped to zero if the attack is less than 1 micro second. is that on purpose, or is that supposed to be 1 ms?

B) What’s that -2*PI about here?

My guess is that it is because exp (-2pi) is approximately -60dB? but if that’s the case I would expect std::log(0.001) instead…?

Also, isn’t something like that missing at the beginning of processSample()?

inputValue = std::abs (inputValue);

1 Like

Indeed. Negative peeks will currently be missed by a limiter / compressor.

and this is in dsp::Compressor::processSample():thinking:

// Rectifier
auto env = jmax ((SampleType) 0.0, inputValue);

// Ballistics filter
env = envelopeFilter.processSample (channel, env);


  • For the abs / jmax thing, I’m going to see what we can do
  • The attack time is snapped to prevent divide by zero / very low values, and anyway below 1ms with this design, the time you’ll put will not make a great difference from zero. For very low attack times, a different design would be mandatory
  • the -2*pi is just the DSP formula, it’s the application of the transform being used to go from the analog to the discrete world. Instead of using the bilinear transform, we use the Impulse Invariant transform here to design the low-pass filter. You can get more information about this on the Giannoulis articles about dynamics processors.

Thanks Ivan!

Yes, that’s why it would make sense to snap it if it is less than 1ms? (instead of the actual 1µs)

ok, I didn’t know it was analog based. I was assuming the release time was designed to correspond to a 60db decrease (as T60 is a common measure that would make sense).
Thanks for the reference, I’ll have a look

Oh yes you’re right it’s snapped at 1e-3 ms = 1 µs, well I don’t think it’s a real issue, there is no way it would break anything so…

Thanks for the report!

(while you’re at it, it would be cool if you can add the unit of that snap value in the documentation of setAttackTime and setReleaseTime. thanks!)

I checked the Giannoulis papers and I find no mention of 2pi in this context. He uses the most common definition of the time constant, the time for the step response to reach 63% of the target. For this system (y = x + k (y/z - x)), that would mean k = exp(-1/t). With k = exp(-2pi/t), it’s reaching 99.8%. I mean it’s possible, but it’s a weird choice -I don’t see how 2pi in particular relates to this.

OTOH, using max instead of abs means using half-wave instead of full-wave rectification. But even then, why not placing it in the filter itself, like

if (levelType == LevelCalculationType::RMS)
    inputValue *= inputValue;
    inputValue = std::abs (inputValue); // or jmax ((SampleType) 0.0, inputValue)

Hello! The 2 pi constant came from the definition of the alpha constant = exp (-1/(tau * f_s)). Tau is not exactly a time value, it’s tau = RC in the analog world. And RC = 1/w = 1/(2pif0) = t0/(2*pi).

But anyway, there are tons of ways to set this, whether you want to set the constant time as the 63% or 99% time, there is no ultimate and unique answer, I just took one solution which is the one I see most of the time…

It’s true there’s no agreement on time constants especially for compressors, but I think in this case we’re talking about the same convention. Tau, or RC, is indeed the time constant in seconds, and the group delay at DC. It is also 1/w0 = 1/f0/2pi = t0/2pi, but t0, or the period of the cutoff frequency, is not usually the target of a ballistics filter. That would be tau itself.

1 Like

Any feedback/fix regarding the abs() missing in BallisticsFilter::processSample() and the jmax rectifier that is in the dsp::Compressor ?
(sorry for the bump if you’re already working on this, just wanted to be sure it’s not overflowed by the alpha constant discussion)

So I just pushed a change to the JUCE team with the peak rectifier being in dsp::BallisticsFilter, and the associated change in dsp::Compressor + a documentation fix. For now, I don’t attend to change anything related with the time constant calculus, since it would break some code, and I’m not yet sure at 100% about what would sound more logical. Still, if you need another way to set the time constants, you can multiply the setAttackTime argument by 2*pi or 2*pi / ln(0.33) or whatever :wink:

1 Like

You still dont use the absolute value of inputValue in that test ?

SampleType cte = (inputValue > yold[(size_t) channel] ? cteAT : cteRL);

1 Like