This function implements a tremolo, and is stereo in and out. The pot (MOD1) controls the LFO depth, and the pushbutton on the rotary encoder (MOD2) changes the phasing between the left and right channels.
Tremolo is a simple amplitude modulation of the input signal, so it becomes louder and quieter at a regular rate. This differs from ring-modulation in that the polarity of the input signal is always preserved. With ring-modulation, since it is a true multiplication, you can have a negative input signal inverting the polarity for some period of time. This function uses a sinusoid from a lookup table to vary the amplitude of the input signal. This LFO can be varied from .17Hz to 43Hz with the pot (MOD1), and pressing the pushbutton on MOD2 changes whether the left and right signals change amplitude together, or alternate, so as one is increasing in volume, the other is decreasing (panning from left to right).
To decrease the amount of effect, mix in some of the original signal with the MIX knob.
; program: tremolo-stereo.asm
; UID = 000057 - unique id to eliminate conflicts between variables
; memory is not used
; stereo in and out
; pot (MOD1) controls the lfo frequency
; pushbutton (on MOD2) controls phase between left and right output
; program overview
;
; data is read in from the codec, and multiplied by an lfo that is internally
; generated using a 512s/16b sinewave lookup table. the frequency of this
; lfo is determined by the adc input, which is oversampled 256 times and
; compared to the previous value with a deadband. this helps reduce jitter.
; the left and right channels are mutiplied by opposite signals, so the
; sound bounces back and forth between left and right. depressing the
; pushbutton (on MOD2) changes this so that the left and right are in phase.
; register usage - may be redefined in other sections
;
; r0 multiply result lsb
; r1 multiply result msb
; r2 accumulation lsb
; r3 accumulation mlb
; r4 left lsb out/accumulation mhb
; r5 left msb out/accumulation msb
; r6 right in/out lsb
; r7 right in/out msb
; r8 adc accumulator fractional byte
; r9 adc accumulator lsb
; r10 adc accumulator msb
; r11 lfo control signal fractional byte
; r12 lfo control signal lsb
; r13 lfo control signal msb
; r14 null register
; r15 switch/adc sample counter
; r16 temporary swap register
; r17 temporary swap register
; r18 sine wave buffer/multiply lsb
; r19 sine wave buffer/multiply msb
; r20 multiply swap register
; r21 multiply swap register
; r22 sinetable lookup address lsb
; r23 sinetable lookup address mlb
; r24 left data in lsb
; r25 left data in msb
; r26 sinetable lookup address mhb
; r27 sinetable lookup address msb
; r28 phase indicator
; r29
; r30 jump location for interrupt lsb
; r31 jump location for interrupt msb
; t pushbutton state indicator
;program starts here first time
; intialize registers
ldi r30,$07 ; increment z pointer to new jump location
clr r14 ; clear null register
ldi r16,$10 ; initialize lfo rate
ldi r17,$00
movw r13:r12,r17:r16 ; move to lfo rate register
clr r28 ; initialize phase mode register
reti ; finish with initialization and wait for next interrupt
;program starts here every time but first
; initiate data transfer to codec
sbi portb,portb0 ; toggle slave select pin
out spdr,r5 ; send out left channel msb
cbi portb,portb0
wait1_000057: ; check if byte has been sent
in r17,spsr
sbrs r17,spif
rjmp wait1_000057
in r25,spdr ; recieve in left channel msb
out spdr,r4 ; send out left channel lsb
wait2_000057: ; check if byte has been sent
in r17,spsr
sbrs r17,spif
rjmp wait2_000057
in r24,spdr ; recieve in left channel lsb
out spdr,r7 ; send out right channel msb
wait3_000057: ; check if byte has been sent
in r17,spsr
sbrs r17,spif
rjmp wait3_000057
in r7,spdr ; recieve in right channel msb
out spdr,r6 ; send out right channel lsb
wait4_000057: ; check if byte has been sent
in r17,spsr
sbrs r17,spif
rjmp wait4_000057
in r6,spdr ; recieve in right channel lsb
;vco generation
movw r17:r16,r31:r30 ; store z register
;get sample 1
add r22,r11 ; increment sinetable address
adc r23,r12
adc r26,r13
adc r27,r14 ; r14 is cleared above
movw r31:r30,r27:r26 ; move to z register for data fetch
lsl r30 ; adjust pointer for 16b fetch
rol r31
andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
ori r31,$48 ; set to memory address location where table is stored
lpm r18,z+ ; get sine value lsb, increment z register
lpm r19,z ; get sine value msb
sbrc r27,$01 ; flip sign for half of the values
rjmp interpolate_000057 ; interpolate if even
neg r18 ; invert if odd
adc r19,r14 ; r14 is cleared above
neg r19
interpolate_000057: ; multiply sample 1 by distance
movw r21:r20,r23:r22 ; get distance from sample 1
com r20 ; invert distance
com r21
mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
movw r5:r4,r1:r0 ; store high bytes result for later
mul r18,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
movw r3:r2,r1:r0 ; store low byets for later
mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc r5,r14 ; r14 is cleared above - subtract sign bit
add r3,r0 ; accumulate result
adc r4,r1
adc r5,r14 ; r14 is cleared above
mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add r3,r0 ; accumulate result
adc r4,r1
adc r5,r14 ; r14 is cleared above
;get sample 2
adiw r27:r26,$01 ; set to next sample
movw r31:r30,r27:r26 ; move to z register for data fetch
lsl r30 ; adjust pointer for 16b fetch
rol r31
andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
ori r31,$48 ; set to memory address location where table is stored
lpm r18,z+ ; get sine value lsb, increment z register
lpm r19,z ; get sine value msb
sbrc r27,$01 ; flip sign for half of the values
rjmp interpolate1_000057
neg r18
adc r19,r14 ; r14 is cleared above
neg r19
interpolate1_000057: ; multiply sample 2 by distance and accumulate
sbiw r27:r26,$01 ; reset address
movw r31:r30,r17:r16 ; restore z register
mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
add r4,r0 ; accumulate result
adc r5,r1
mul r18,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
add r2,r0 ; accumulate result
adc r3,r1
adc r4,r14 ; r14 is cleared above
adc r5,r14
mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
sbc r5,r14 ; r14 is cleared above - subtract sign bit
add r3,r0 ; accumulate result
adc r4,r1
adc r5,r14 ; r14 is cleared above
mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
add r3,r0 ; accumulate result
adc r4,r1
adc r5,r14 ; r14 is cleared above
;add in offset so lfo is always positive
; (the difference between tremolo and ring modulation)
ldi r16,$80 ; turn signed value to unsigned value
add r5,r16
;multiply left data by lfo
movw r21:r20,r5:r4 ; move lfo to multiply register
movw r19:r18,r25:r24 ; move left data to multiply register
mulsu r19,r21 ; (signed)ah * (unsigned)bh
movw r5:r4,r1:r0
mul r18,r20 ; (unsigned)al * (unsigned)bl
movw r3:r2,r1:r0
mulsu r19,r20 ; (signed)ah * (unsigned)bl
sbc r5,r14 ; r14 is cleared above
add r3,r0 ; accumulate result
adc r4,r1
adc r5,r14 ; r14 is cleared above
mul r21,r18 ; (unsigned)bh * (unsigned)al
add r3,r0 ; accumulate result
adc r4,r1
adc r5,r14 ; r14 is cleared above
;check if in or out of phase mode
sbrs r28,$00 ; check if phase inversion bit is set
rjmp rightmultiply_000057 ; skip inversion if in phase
com r20 ; else invert lfo signal
com r21 ; ones complement used to avoid $0000 problem
rightmultiply_000057: ; multiply right data by lfo
movw r19:r18,r7:r6 ; move right data to multiply register
mulsu r19,r21 ; (signed)ah * (unsigned)bh
movw r7:r6,r1:r0
mul r18,r20 ; (unsigned)al * (unsigned)bl
movw r3:r2,r1:r0
mulsu r19,r20 ; (signed)ah * (unsigned)bl
sbc r7,r14 ; r14 is cleared above
add r3,r0 ; accumulate result
adc r6,r1
adc r7,r14 ; r14 is cleared above
mul r21,r18 ; (unsigned)bh * (unsigned)al
add r3,r0 ; accumulate result
adc r6,r1
adc r7,r14 ; r14 is cleared above
adcsample_000057: ; sample adc for lfo rate
lds r17,adcsra ; get adc control register
sbrs r17,adif ; check if adc conversion is complete
rjmp done_000057 ; skip adc sampling
lds r16,adcl ; get low byte adc value
lds r17,adch ; get high byte adc value
add r8,r16 ; accumulate adc samples
adc r9,r17
adc r10,r14 ; r14 is cleared above
ldi r17,$f7
sts adcsra,r17 ; clear interrupt flag
dec r15 ; countdown adc sample clock
brne done_000057 ; get delay time if its been long enough
lsr r10 ; divide adc value by 4 to make 16b value
ror r9
ror r8
lsr r10
ror r9
ror r8
movw r17:r16,r9:r8 ; move adc sample to temporary register
clr r20
ldi r21,$01 ; add in offset of min lfo rate ($000100)
add r17,r21
adc r20,r14 ; r14 is cleared above
sub r16,r11 ; find difference between adc sample and current lfo rate
sbc r17,r12
sbc r20,r13
brsh check_000057 ; check for deadband if positive
com r16 ; else invert if negative
com r17 ; only 1 lsb error doing it this way
com r20
check_000057: ; check if difference is greater than deadband
cpi r16,$40 ; check if difference is less than 1 adc lsb
cpc r17,r14 ; r14 cleared above
cpc r20,r14
brlo empty_000057 ; do nothing if less than 1 adc lsb
mov r11,r8 ; else move to lfo rate register
mov r12,r9
mov r13,r10
add r12,r21 ; add in offset
adc r13,r14 ; r14 is cleared above
empty_000057: ; empty accumulation registers and finish off
clr r8 ; empty accumulation registers
clr r9
clr r10
;check if phase mode should change
lds r16,pinj ; get switch data
sbrs r16,$02 ; check if pushbutton is released
rjmp edge_000057 ; check if pushbutton was low on previous sample
clt ; clear state register if back high
rjmp switchsample_000057 ; finish off
edge_000057: ; check for falling edge
brts adcsample_000057 ; do nothing if edge was already detected
set ; set t register to indicate edge detected
ldi r17,$01 ; toggle phase inversion mode
eor r28,r17
switchsample_000057: ; check rotary switch for new function
andi r16,$78 ; mask off rotary switch
lsr r16 ; adjust switch position to program memory location
lsr r16
ldi r17,$02
add r16,r17
cpse r16,r31 ; check if location has changed
clr r30 ; reset jump register to intial state
mov r31,r16
done_000057:
reti ; return to waiting
Last edited 2010-08-21 by guest · 3 revisions