[sdiy] BLIT/BLEP virtual analogue synthesis
Olivier Gillet
ol.gillet at gmail.com
Wed Aug 4 18:38:51 CEST 2010
Another idea from two observations: 1/ you're probably computing the
samples at a faster rate than the control signal modulating
phase_increment. 2/ What really matters is the scaled reciprocal of
phase_increment. Scaled as in 65536 / phase_increment
So here's a way of doing it: whenever you do something that ends up
modify phase_increment, set a "dirty" bit to true.
Now in your blep code:
if (/*wrapping*/ ... ) {
phase = ...
if (dirty) {
phase_increment_reciprocal = ... // Smart code to compute 65536 /
phase_increment efficiently
dirty = 0;
}
fractional_wrap_point = phase * phase_increment_reciprocal >> 8; //
Assuming you want the fractional value in the 0-255 scale.
// Add a blep parametrized by fractional_wrap_point to the playlist
}
The reciprocal computation can be done with a Newton method, or using
the following shift + lookup table idea:
# 256 bytes LUT with reciprocal of scaled large values
reciprocal_lut = [0] * 128
for i in xrange(128):
reciprocal_lut[i] = int(65536 / ((i + 128.5)))
# 512 bytes LUT with reciprocal of small values
small_values_lut = [0] * 256
for i in xrange(256):
small_values_lut[i] = 65536 / i if i > 0 else 1
# The pseudo-code for the reciprocal itself
def FastScaledReciprocal(x):
if x < 256:
return small_values_lut[x]
else:
r = 1
p = x
while p < 32768:
r <<= 1
p <<= 1
return r * reciprocal_lut[(p >> 8) - 128] >> 8
I gave it a test run and the max error is 1, average is 0.0066, and
there's at most 7 iterations in the loop. It probably beats a
division.
Now that I've written it, I recognized the pattern: this is the same
flavour of code as the shift+antilog trick used to convert a
fractional MIDI pitch into a phase increment. For instance:
http://github.com/pichenettes/shruthi-1/blob/master/hardware/shruthi/synthesis_engine.cc#L893
So you already have this kind of lookup table and loop somewhere, and
you use it to compute the phase increment. Thus, in your MIDI >
increment loops, do the exact dual thing, in the opposite direction,
and you'll get the reciprocal for free. With the reciprocal, the
fractional wrapped_phase / phase_increment ratio is only a multiply
and shift away.
Olivier
More information about the Synth-diy
mailing list