[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