[sdiy] OT - Linear interpolation in C
Tom Wiltshire
tom at electricdruid.net
Sun Jan 30 15:10:14 CET 2011
Sorry to post off-topic (sort of - this is an LFO I'm working on). Perhaps any replies would be better to me privately if you think C programming isn't sufficiently synthy. There *does* seem to have been a lot of it about recently.
The problem I'm having is in converting the following assembly into C.
I've got a 32-bit phase accumulator PHASE_HI and PHASE_LO. I've got 256-entry waveform tables, and the linear interpolation comes in to make straight line sections between those points.
Now, what's below is not running debugged code, just something I've knocked up to demonstrate how I normally go about this kind of thing. The plan is to use the top 8 bits as an index into the waveform table, whilst the bottom 8 bits indicate the 'sub sample' position between two table entries.
The first section (doing the table lookups) is easy, and I've got that working fine in C. It's the second section I'm struggling with. Specifically, I can't see how the C variable types I've got relate to the 1.15 format accumulator usage that I'm used to. I've got a 1.15 integer type in C (signed int), but it is treated as a full range integer (eg 32767 to -32768) rather than as a fractional number from -1 to nearly 1, which is how I use it with the accumlator.
I could "upgrade" my variables to "float", but that's unnecessary since I already know it can be done efficiently with 16-bit variables, and that I can avoid division by using shifts.
So how do I get C to treat a signed int as if it runs from -1 to 1 rather than -32768 to 32767? Or is there a better way?
Thanks,
Tom
LinearInterp:
; Work out the linear interpolated value
; Value = (THIS * 1-index) + (NEXT * index)
; Deal with waveform look-up
mov PHASE_HI, W2 ; Get high word of phase
lsr W2, #8, W2 ; Shift down so I only have the top 8 bits
sl W2, W2 ; Shift up to make an index (2 bytes per entry)
mov #tblpage(LFO_SineTable), W1 ; Set up TBLPAG
mov W1, TBLPAG
mov #tbloffset(LFO_SineTable), W1 ; Get base position
add W1, W2, W1 ; Add offset
; Get the first lookup result
tblrdl [W1++], W6 ; Get the data(THIS into W6)
; Get the second lookup result
tblrdl [W1], W7 ; Get the data (NEXT into W7)
; Use the 8 lower bits of the high word for the linear interp
mov PHASE_HI, W4 ; Index
and W4, #255, W4 ; Mask off the top word
sl W4, #7, W4 ; Shift up to give range 0-1
com W4, W5 ; 1-Index
bclr W5, #15 ; Clear the sign bit again
mpy W5*W6, A ; (THIS * 1-index)
mac W4*W7, A ; (NEXT * index)
sac.r A, W0 ; Get rounded result into W0
(code is dsPIC ASM)
More information about the Synth-diy
mailing list