[sdiy] Integer exponential envelope generation
Richie Burnett
rburnett at richieburnett.co.uk
Tue Nov 22 22:11:34 CET 2011
This topic seems to come up quite frequently. The two equations Scott
posted are equivalent provided your processor can handle signed arithmetic:
y = a0 * input + b1 * y (where b1 = 1.0 - a0)
y = y + b1 * (input - y) (where b1 is the "time constant")
On a DSP which is geared up to multiplying and accumulating I implement the
calculation like this:
y = y + (b1 * input) - (b1 * output)
In programming terms it reduces to two operations: Multiply the current
output value by b1 and subtract from the running total, then multiply the
current input value by b1 and add to the running total.
...of course we still need to work out what the magic b1 value needs to be
in order to achieve a specific attack or decay time constant. To be
precise:
b1 = 1 - exp (-Ts / Tc)
where Tc is the required time-constant in seconds, and Ts is your sampling
time also in seconds. If your required minimum Tc is always much greater
than the sampling time Ts, you can use the approximation:
b1 = Ts / Tc
This is pretty accurate if your sampling frequency is something like 48kHz
and the minimum attack and decay times are 1ms or more. It also gets around
having to perform an exp() function or a table lookup to calculate the
required b1 coefficient. However if you calculate new updated envelope
values at a lower control rate like 2kHz, this formula will become
increasingly innacurate for short time constants that approach the sampling
time.
The only thing to watch out for with this 1-pole IIR envelope generator is
making sure you store the current running total "y" to sufficient bits of
resolution! Take the case where the required envelope time-constant is 1000
times the sampling time. In this case the constant b1 is going to be
something around 1/1000. You need to make sure that when the current output
value is multiplied by b1 it doesn't get rounded down to zero. If this
product were to get rounded down to zero then the decaying envelope will get
stuck at a low level without falling away smoothly to zero.
As a rough rule of thumb you need to keep track of the current "y" value to
the number of bits of precision you want in the answer plus an extra bit for
every factor of 2 that the envelope time-constant is greater than the
sampling time. So if your sampling time is 1ms, and you want a maximum time
constant of 1 second, thats a factor of 1000. Or roughly 2 to the power of
10. Therefore if you want your envelope to be accurate to 8 bits, you need
to keep track of "y" to at least 18 bits of precision.
Hopefully this summary of digital envelopes will help. Although you put
"Integer" in the title it really helps to think of this more like fixed
point arithmetic rather than Integer, because the coefficient b1 must always
be fractional. All you are really doing with integer arithmetic is shifting
all of the fractional numbers left by a fixed number of bits and thinking of
them as integers. "Fractional" or "Integer"? It's all just a matter of
where you imagine that the binary point is placed.
-Richie,
More information about the Synth-diy
mailing list