[sdiy] Accuracy with integer maths

rburnett at richieburnett.co.uk rburnett at richieburnett.co.uk
Tue Feb 4 17:49:31 CET 2014


Hi Tom,

Only just read your Email quickly at work,  but I _THINK_ I would do it 
like this:

Read your two adjacent entries from the lookup table to 8-bit accuracy.  
Do your linear interpolation between them using two 8x8 multiplies which 
will give you two 16-bit results.  Add these together.  They won't 
overflow 16-bits because linear interpolation cannot go outside the 
bounds of the original points.  I'd keep this interpolation result as a 
16-bit word as it fits neatly into one byte.  I'd then multiply this by 
the amplitude CV information using an 8x16 multiply.  This will give you 
a 24-bit result, of which I'd only keep the top 10-bits if you are 
outputting to a 10-bit PWM or DAC or something like that.

It's generally good practice to do all your truncation (and any rounding 
if you feel the need) right in the last step before the value is output 
at whatever resolution the hardware permits.  If you make a bad choice 
rounding earlier on then there's nothing you can do to recover the lost 
resolution afterwards.

...But then again, i'd probably do the ADSR envelope using a first-order 
IIR digital filter.  You can get this down to one multiply instruction 
if you code it carefully, albeit with quite a long wordlength though 
because the coefficients will get close to 0 and 1 for loooooong attack, 
decay or release times!  (Of course you can't do any fancy reverse-expo 
attack curves or anything like that though using an IIR filter.)

I hope this helps,

-Richie,

PS. You can also program your linear interpolation to just require one 
multiply if you re-arrange:

y = (1-a) * y[n] + a * y[n+1]

into the form

y = y[n] + a * ( y[n+1] - y[n] )

That might speed things up a bit if you're stuck with S/W multiply.  
You'll need the multiply to deal with a negative input from (y[n+1] - 
y[n]) though if you're interpolating on a downward slope!







On 2014-02-04 16:11, Tom Wiltshire wrote:
>> Tom, if you are going to truncate a 16-bit result to 8-bits accuracy 
>> you really should preserve the 9th bit so that you can test it and 
>> decide whether to round the value up or down.
> 
> What, you mean like *actual rounding* instead of brutal truncation?!
> <Gasps!> How modern!
> 
>> Think what you would do if you were going to round a decimal number 
>> like "317" to the nearest ten.  Although you're only going to keep the 
>> top two digits (the '3' and the '1',) you need to test the units digit 
>> to see if the result should be rounded up to 320 or down to 310.  (I 
>> usually implement rounding by adding in a half to produce a carry if 
>> the fractional part that you are going to chop off was greater than 
>> 0.5, then do a plain truncate.  This eliminates any branching in the 
>> code so the execution time is always constant, and you don't incurr 
>> any pipeline stalls, etc.)
> 
> Yeah, that makes sense. Nice trick. Better than doing a bit test on
> the 9th bit and then conditionally adding one, for sure.
> 
>> Most hardware multipliers will give you all of the bits of the result 
>> automatically, so unless you are hard-coding your own 
>> software-multiply routine I'm not sure where you would make 
>> optimisations if you don't need all the bits of the result.
>> What are you actually using this multiply for?!?!?
> 
> It's doing the "Level VCA" calculation in one of my envelope generator 
> programs.
> 
> The process is this:
> You start with a phase accumulator value that represents how far
> through your envelope curve you are. You use the top X bits (8 in my
> case) to read the required value out of the envelope curve lookup
> table, and then read out the next value in the table too. Then you use
> some of the next lowest bits of the phase accumulator to interpolate
> between those two, to give you a smoother curve, especially at slow
> rates. I also use that interp to boost the resolution of the data from
> 8-bit to 10-bit.
> Finally, that 10-bit value is multiplied by the 8-bit Level CV value
> to produce a 10-bit output.
> 
> So you see why I'm asking about the accuracy. I've got lots of bits of
> phase accumulator I could use for the interp, but I don't need all of
> them, and I've got an 10x8 = 18-bit multiplication result of which I
> only need 10-bits.
> 
> I've done more experiments (running the algorithms in PHP at various
> accuracies and spitting out the results to HTML tables) and found that
> if I need ten bits from the interpolation of two 8-bit numbers, I only
> need to use two extra bits for the interp. It doesn't look like any
> extra bits are required to guard against error - although I'm
> truncating everything at the moment, not rounding, so that might be
> why. Rounding would need the next bit to be accurate too, thereby
> requiring one guard bit.
> Similarly with the multiply. For a 10-bit output I only need 2 bits
> beyond the 8 I've got already, but the problem here is that I'm
> reluctant to reduce the resolution of the Level CV or of the curve
> data. But then if I'm reducing the 18-bit output down to 10-bits at
> the end, that's what I'm doing anyway, right?
> 
>> Are you trying to do some clever DSP stuff on a general purpose CPU?
> 
> I don't know about "clever", but I'm still trying to do "serious" DSP
> on a "comedy" chip! (PIC 16F - no hardware multiply - only nutters
> need apply)
> 
> Thanks,
> Tom



More information about the Synth-diy mailing list