==== Stereo Panning Flanger ==== This function implements a flanger which varies the delay between the left and right outputs. It takes in mono audio on the left channel, and outputs a stereo signal on both left and right channels. the pot (MOD1) adjusts the LFO frequency, and the rotary encoder (MOD2) adjusts the LFO depth. [[attachment:flanger_stereo_pan.asm|flanger_stereo_pan.asm]] ---- {{{#!highlight nasm ; program: stereo_flanger-16b.asm ; UID = 000053 - unique id to eliminate conflicts between variables ; 16b address space ; mono data in on left channel, stereo data out (opposite phasing) ; pot (MOD1) controls lfo frequency ; rotary encoder (MOD2) controls lfo depth ; program overview ; ; data is read in from the codec and stored to sram. data is read out of ; sram at a variable delay set by an lfo. the lfo is generated from a ; 16b/512s half sinewave lookup table. this is incremented with a 24b ; number to get very low frequencies. the lfo rate is set via the adc, ; which is oversampled 256 times and deadbanded to get rid of glitches. ; the lfo depth is created by multiplying the lfo signal with an 8b depth ; value, which is set via the rotary encoder. ; 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 out lsb ; r7 right out msb ; r8 adc accumulator fractional byte ; r9 adc accumulator lsb ; r10 adc accumulator msb ; r11 rotary encoder counter ; r12 lfo rate lsb ; r13 lfo rate msb ; r14 null register ; r15 switch sample counter ; r16 temporary swap register ; r17 temporary swap register ; r18 sine wave buffer/multiply msb ; 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 write address lsb ; r25 write address msb ; r26 sinetable lookup address mhb ; r27 sinetable lookup address msb ; r28 temporary swap register ; r29 lfo depth ; r30 jump location for interrupt lsb ; r31 jump location for interrupt msb ; t rotary encoder edge detect indicator ; program starts here first time ; intialize registers ldi r30,$04 ; increment z pointer to new jump location clr r14 ; clear null register ldi r29,$0d ; intiialize lfo depth 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 adiw r25:r24,$01 ; increment write address wait1_000053: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000053 in r19,spdr ; recieve in left channel msb out spdr,r4 ; send out left channel lsb wait2_000053: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000053 in r18,spdr ; recieve in left channel lsb out spdr,r7 ; send out right channel msb ;write left channel to sram out portd,r24 ; set address sts porth,r25 out portg,r14 ; pull ce low,we low,and set high bits of address ldi r17,$ff out ddra,r17 ; set porta as output for data write out ddrc,r17 ; set portc as output for data write out porta,r18 ; set data out portc,r19 sbi portg,portg2 ; pull we high to write out ddra,r14 ; set porta as input for data lines out ddrc,r14 ; set portc as input for data lines wait3_000053: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000053 in r17,spdr ; recieve in right channel msb out spdr,r6 ; send out right channel lsb wait4_000053: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000053 in r17,spdr ; recieve in right channel lsb ; vco generation movw r17:r16,r31:r30 ; store z register ;get sample 1 add r22,r12 ; increment sinetable address adc r23,r13 adc r26,r14 ; r14 is cleared above adc r27,r14 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_000053 neg r18 adc r19,r14 ; r14 is cleared above neg r19 interpolate_000053: ; 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_000053 neg r18 adc r19,r14 ; r14 is cleared above neg r19 interpolate1_000053: ; multiply sample 2 by distance 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 ;set lfo depth - 8b value ldi r16,$80 ; convert lfo to unsigned number add r5,r16 movw r19:r18,r5:r4 ; move lfo signal to multiply register mov r21,r29 ; move lfo depth to multiply register mul r19,r21 ; (unsigned)ah * (unsigned)b movw r5:r4,r1:r0 mul r21,r18 ; (unsigned)b * (unsigned)al add r4,r1 adc r5,r14 ; r14 is cleared above mov r28,r5 ; store lfo for later mov r21,r4 ;right channel data preperation ;add lfo to delay movw r17:r16,r25:r24 ; move current location to read address sec ; set the carry bit so all values are reduced by 1 lsb for fractional byte sbc r16,r5 ; remove lfo time sbc r17,r14 ; r14 is cleared above ;get right channel sample 1 from sram out portd,r16 ; set address sts porth,r17 nop ; wait setup period of two cycles nop in r18,pina ; get data in r19,pinc ; get data ;multiply sample 1 by distance mov r20,r4 ; get distance from sample 1 mulsu r19,r20 ; (signed)ah * b movw r5:r4,r1:r0 mul r18,r20 ; al * b add r4,r1 adc r5,r14 ; r14 is cleared above mov r3,r0 ;get right channel sample 2 from sram subi r16,$ff ; set to next sample sbci r17,$ff ; done this way because there is no addi or adci out portd,r16 ; set address sts porth,r17 nop ; wait setup period of two cycles nop in r18,pina ; get data in r19,pinc ; get data ;multiply sample 2 by distance com r20 ; get distance to sample 2 mulsu r19,r20 ; (signed)ah * b add r4,r0 ; accumulate result adc r5,r1 mul r18,r20 ; al * b add r3,r0 ; accumulate result add r4,r1 adc r5,r14 ; r14 is cleared above movw r7:r6,r5:r4 ; move right channel to output register ;left channel data preperation ;add lfo to delay clr r4 ; get max depth value mov r5,r29 sub r4,r21 ; subtract lfo to make inverse sbc r5,r28 movw r17:r16,r25:r24 ; move current location to read address sec ; set the carry bit so all values are reduced by 1 lsb for fractional byte sbc r16,r5 ; remove lfo time sbc r17,r14 ; r14 is cleared above ;get left channel sample 1 from sram out portd,r16 ; set address sts porth,r17 nop ; wait setup period of two cycles nop in r18,pina ; get data in r19,pinc ; get data ;multiply sample 1 by distance mov r20,r4 ; get distance from sample 1 mulsu r19,r20 ; (signed)ah * b movw r5:r4,r1:r0 mul r18,r20 ; al * b add r4,r1 adc r5,r14 ; r14 is cleared above mov r3,r0 ;get left channel sample 2 from sram subi r16,$ff ; set to next sample sbci r17,$ff ; done this way because there is no addi or adci out portd,r16 ; set address sts porth,r17 nop ; wait setup period of two cycles nop in r18,pina ; get data in r19,pinc ; get data ;multiply sample 2 by distance com r20 ; get distance to sample 2 mulsu r19,r20 ; (signed)ah * b add r4,r0 ; accumulate result adc r5,r1 mul r18,r20 ; al * b add r3,r0 ; accumulate result add r4,r1 adc r5,r14 ; r14 is cleared above ;check rotary encoder and adjust lfo depth ; although rotary encoder is externally debounced, it is done here again. ; pin1 is sampled on a transition from high to low on pin0. if pin1 is ; high, a left turn occured, if pin1 is low, a right turn occured. dec r11 ; count down sample clock brne adcsample_000053 ; continue if not ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms) mov r11,r17 ; reload sample clock lds r17,pinj ; get switch data sbrs r17,$00 ; check if pin0 is low rjmp edge_000053 ; check if pin0 was low on previous sample clt ; clear state register if back high rjmp adcsample_000053 ; finish off edge_000053: ; check for falling edge brts adcsample_000053 ; do nothing if edge was already detected set ; set t register to indicate edge detected ldi r21,$01 ; prepare for addition or subtraction sbrs r17,$01 ; check if pin1 is high rjmp increment_000053 ; increment desired delay if right rotation sub r29,r21 ; decrement lfo depth register else brcc adcsample_000053 ; check if underflow clr r29 ; set depth to min rjmp adcsample_000053 ; finish off increment_000053: ; increment desired delay register add r29,r21 ; increment lfo depth register brcc adcsample_000053 ; check if overflow occured ser r29 ; set depth to max adcsample_000053: ; sample adc for lfo rate lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp done_000053 ; 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_000053 ; get delay time if its been long enough deadband_000053: ; set the low value of the delay lsr r10 ; divide adc value by 16 ror r9 ror r8 lsr r10 ror r9 ror r8 lsr r9 ; r10 now empty ror r8 lsr r9 ror r8 movw r17:r16,r9:r8 ; move adc sample to temporary register ldi r21,$80 ; add in offset of min lfo rate ($0080) add r16,r21 adc r17,r14 ; r14 is cleared above sub r16,r12 ; find difference between adc sample and current lfo rate sbc r17,r13 brsh check_000053 ; check for deadband if positive neg r16 ; invert if negative adc r17,r14 ; r14 is cleared above neg r17 check_000053: ; check if difference is greater than deadband cpi r16,$10 ; check if difference is less than 1 adc lsb cpc r17,r14 ; r14 cleared above brlo empty_000053 ; do nothing if less than 1 adc lsb movw r13:r12,r9:r8 ; move adc sample to lfo rate register add r12,r21 ; add in offset of min lfo rate ($0080) adc r13,r14 ; r14 is cleared above empty_000053: ; empty accumulation registers and finish off clr r8 ; empty accumulation registers clr r9 clr r10 switchsample_000053: ; check rotary switch lds r16,pinj ; get switch data 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_000053: reti ; return to waiting }}}