==== Gated Reverser with Delay ==== This function implements a reverser which is triggered by sounds above a certain threshold (gating). It takes in stereo data and outputs stereo data. [[attachment:reverser_gated_delay.asm|reverser_gated_delay.asm]] ---- {{{#!highlight nasm ; program: reverser-18b-gated-delay.asm ; UID = 000050 - unique id to eliminate conflicts between variables ; 18b address space (3s sample time) ; stereo data, in and out ; pot (MOD1) controlled delay ; program overview ; ; data is read in from memory and written out to the codec at the same time ; new data is written to the memory from the codec. the left channel input ; data is compared to a predetermined threshold level, and if it crosses ; this level, the reverser function is initiated after a delay. the delay ; is set via the adc reading from the pot (MOD1). this value is sampled ; 256 times and deadbanded to reduce jitter. the codec passes data ; through while in forward mode, and continues to do so for the delay ; period after the input has crossed the threshold. after this time, the ; input data is no longer written to memory, in order to increase the ; available buffer size. the reverse data is played out back to the ; beginning, plus a predetermined backtrack amount to catch the rising ; edge of notes. at this point, the forward data saving is resumed, and ; the reverse data is faded while the current data is faded in. this ; crossfade size is determined via a constant at the beginning of the code. ; there is also a holdoff time after the reverser function has finished, ; before it can be retriggered, in order to reduce constant triggering and ; glitches from not having enough forward data stored. ; constant definitions ; .equ backtrack_000050 = $0800 ; past memory used as starting point ; (must be even) .equ holdoff_000050 = $0f00 ; holdoff before retriggering ; must be larger than backtrack (must be even) .equ stepsize_000050 = $0080 ; 65536/(stepsize * 44.1) = crossfade time (ms) .equ threshold_000050 = $3900 ; threshold for turn on of reverser ($7fff max) .equ minbuff_000050 = $02 ; minimum buffer time in multiples of 5.8ms .equ maxbuff_000050 = ($03ff00 - (2 * ($010000 / stepsize_000050)) - holdoff_000050) ; maximum buffer time to keep from overlapping .equ mem_000050 = $0200 ; memory location for storage in internal sram ; register usage - may be redefined in other sections ; ; r0 multiply result lsb ; r1 multiply result msb ; r2 left lsb out ; r3 left msb out ; r4 right lsb out ; r5 right msb out ; r6 left lsb in ; r7 left msb in ; r8 right lsb in ; r9 right msb in ; r10 adc accumulation lsb ; r11 adc accumulation msb ; r12 buffer bottom / crossfade lsb ; r13 buffer bottom / crossfade msb ; r14 adc accumulation fractional byte ; r15 switch/adc sample counter ; r16 temporary swap register ; r17 temporary swap register ; r18 null register ; r19 reverse mode indicator ; r20 temporary swap regiser ; r21 buffer bottom third byte ; r22 write address third byte ; r23 read address third byte ; r24 write address lsb ; r25 write address msb ; r26 desired buffer size lsb ; r27 desired buffer size msb ; r28 read address lsb ; r29 read address msb ; r30 jump location for interrupt lsb ; r31 jump location for interrupt msb ; t foward/reverse indicator ; program starts here first time ; initialze z pointer for correct jump ; this assumes a less than 256 word jump ldi r30,$23 ; set jump location to program start clr r24 ; clear write register clr r25 ldi r22,$00 ; setup write address high byte clr r18 ; setup r18 as null register for carry addition and ddr setting ldi r17,$ff ; setup r17 for ddr setting clear_000050: ; clear delay buffer ; eliminates static when first switching to the delay setting adiw r25:r24,$01 ; increment write register adc r22,r18 ; increment write third byte cpi r22,$04 ; check if full memory space has been cleared breq cleardone_000050 ; continue until end of buffer reached out portd,r24 ; set address sts porth,r25 out portg,r22 ; pull ce low,we low,and set high bits of address 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,r18 ; r18 is cleared above sbi portg,portg2 ; pull we high to write out ddra,r18 ; set porta as input for data lines out ddrc,r18 ; set portc as input for data lines rjmp clear_000050 ; continue clearing cleardone_000050: ; reset registers ldi r24,$01 ; initialize write register ldi r25,$00 ldi r22,$00 ; setup write address high byte ldi r28,$01 ; set read address to minimum delay ldi r29,$ff ldi r23,$07 ; setup read address high byte clr r19 ; initialize reverse mode register clr r2 ; initialize data output registers clr r3 clr r4 clr r5 clt ; clear t register to start with forward play reti ; finish with initialization and wait for next interrupt ; initiate data transfer to codec ; this is the point the z-pointer is incremented to ; program starts here all but first time sbi portb,portb0 ; toggle slave select pin out spdr,r3 ; send out left channel msb cbi portb,portb0 ;increment write addresses brts wait1_000050 ; dont write data to memory if reversing adiw r25:r24,$01 ; increment write address adc r22,r18 ; increment write third byte andi r22,$03 ; mask off unsed bits wait1_000050: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait1_000050 in r7,spdr ; recieve in left channel msb out spdr,r2 ; send out left channel lsb wait2_000050: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait2_000050 in r6,spdr ; recieve in left channel lsb out spdr,r5 ; send out right channel msb ;write left channel data to sram brts wait3_000050 ; dont write data to memory if reversing out portd,r24 ; set address sts porth,r25 out portg,r22 ; 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,r6 ; set data out portc,r7 sbi portg,portg2 ; pull we high to write out ddra,r18 ; set porta as input for data lines out ddrc,r18 ; set portc as input for data lines wait3_000050: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait3_000050 in r9,spdr ; recieve in right channel msb out spdr,r4 ; send out right channel lsb ;increment write address brts wait4_000050 ; dont write data to memory if reversing adiw r25:r24,$01 ; increment write address adc r22,r18 ; increment write third byte andi r22,$03 ; mask off unsed bits wait4_000050: ; check if byte has been sent in r17,spsr sbrs r17,spif rjmp wait4_000050 in r8,spdr ; recieve in left channel lsb ;write right channel data to sram brts dataload_000050 ; dont write data to memory if reversing out portd,r24 ; set address sts porth,r25 out portg,r22 ; 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,r8 ; set data out portc,r9 sbi portg,portg2 ; pull we high to write out ddra,r18 ; set porta as input for data lines out ddrc,r18 ; set portc as input for data lines ;check if input signal is above threshold movw r3:r2,r7:r6 ; backup input values as they get modified movw r5:r4,r9:r8 ; move input data to output sbrc r19,$04 ; check if holding off after last trigger rjmp holdtime_000050 ; countdown holdoff timer if waiting sbrc r19,$07 ; check if already in reverse mode rjmp reverse_000050 ; go to reverse function - else check threshold ldi r16,low(threshold_000050) ; get threshold value ldi r17,high(threshold_000050) tst r7 ; check if negative brpl compare_000050 ; compare if positive com r6 ; invert if negative com r7 ; ones complement used for simplicity compare_000050: ; compare input value to threshold cp r6,r16 ; compare to left channel input cpc r7,r17 brsh start_000050 ; start reversing if above threshold rjmp adcsample_000050 ; else finish off holdtime_000050: ; decrement holdoff timer ldi r17,$02 ; subtract 2 from holdoff timer sub r12,r17 ; 2 is used to have holdoff and backup match scaling sbc r13,r18 ; r18 is cleared above brne finishoff_000050 ; finish off if it not done holding off clr r19 ; set mode to forward and clear hold off bit finishoff_000050: ; finish off if not done holding off rjmp adcsample_000050 ; finish off - too long for a branch instruction start_000050: ; intialize reversing ldi r19,$80 ; indicate that reverser is active, waiting for delay movw r17:r16,r25:r24 ; move write address to temporary register mov r21,r22 subi r16,low(backtrack_000050) ; remove initial buffer size sbci r17,high(backtrack_000050) sbc r21,r18 ; r18 cleared above andi r21,$03 ; mask off low bits movw r13:r12,r17:r16 ; move buffer bottom to its register rjmp adcsample_000050 ; finish off reverse_000050: ; check for reverse function sbrc r19,$06 ; check if delay time has been met rjmp dataload_000050 ; get data from memory if delay time has been met ; else check if delay time is up yet movw r17:r16,r13:r12 ; move bottom of buffer to temporary register mov r20,r21 add r17,r26 ; add in buffer size adc r20,r27 andi r20,$03 ; mask off unused bits cp r24,r16 ; check if write address is at top of buffer cpc r25,r17 cpc r22,r20 breq bufferset_000050 ; set the read address to the top of the buffer rjmp adcsample_000050 ; else finish off bufferset_000050: ; set the read address to the top of the buffer movw r29:r28,r25:r24 ; else set read address equal to write address mov r23,r22 ori r19,$40 ; set delay time bit in function register set ; set the t register to indicate reversing rjmp dataload1_000050 ; get data from sram dataload_000050: ; get data from sram sbrc r19,$05 ; check if crossfading rjmp dataload1_000050 ; skip buffer check if crossfading andi r23,$03 ; mask off unused bits cp r28,r12 ; check if at bottom of buffer cpc r29,r13 cpc r23,r21 brne dataload1_000050 ; continue if not at bottom ori r19,$20 ; set crossfade bit ldi r16,$ff ; set crossfade distance to max value mov r12,r16 ; buffer bottom value no longer needed mov r13,r16 clt ; clear the t register to start recording forward again dataload1_000050: ; continue getting data from sram sbiw r29:r28,$03 ; decrement read address sbc r23,r18 ; r18 is cleared above andi r23,$03 ; mask off unused bits ori r23,$04 ; set we/ bit in high byte register ;get left channel data from sram out portg,r23 ; pull ce low, we high, and set high bits of register out portd,r28 ; set address sts porth,r29 adiw r29:r28,$01 ; increment read address adc r23,r18 ; increment write third byte andi r23,$03 ; mask off unsed bits ori r23,$04 ; set we/ bit in high byte register in r2,pina ; get data in r3,pinc ; get data ;get right channel data from sram out portg,r23 ; pull ce low, we high, and set high bits of register out portd,r28 ; set address sts porth,r29 nop ; wait 2 cycle setup time nop in r4,pina ; get data in r5,pinc ; get data ;check if crossfading sbrs r19,$05 ; check if crossfading rjmp adcsample_000050 ; finish off if not crossfading ldi r16,low(stepsize_000050) ; get crossfade stepsize ldi r17,high(stepsize_000050) sub r12,r16 ; decrement crossfade distance counter sbc r13,r17 breq crossfade_done_000050 ; stop crossfading if done brcs crossfade_done_000050 ; stop crossfading if done rjmp crossfade_000050 crossfade_done_000050: ; stop crossfading movw r3:r2,r7:r6 ; move forward data to output movw r5:r4,r9:r8 ldi r16,low(holdoff_000050) ; setup holdoff counter ldi r17,high(holdoff_000050) movw r13:r12,r17:r16 ; r13:r12 no longer needed for crossfading ori r19,$10 ; set reverse mode to holdoff rjmp adcsample_000050 ; finish off crossfade_000050: ; crossfade between forward and reverse signals sts mem_000050,r6 ; backup data to free a register sts (mem_000050 + 1),r7 ;multiply left reverse sample by crossfade distance movw r17:r16,r3:r2 ; move left reverse data to multiply register movw r21:r20,r13:r12 ; move crossfade distance to multiply register mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes movw r3:r2,r1:r0 ; store high bytes result for later mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes movw r7:r6,r1:r0 ; store low byets for later mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes sbc r3,r18 ; r18 is cleared above - subtract sign bit add r7,r0 ; accumulate result adc r2,r1 adc r3,r18 ; r18 is cleared above mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes add r7,r0 ; accumulate result adc r2,r1 adc r3,r18 ; r18 is cleared above ;multiply and accumulate left forward channel by crossfade distance lds r16,mem_000050 ; fetch left forward channel from memory lds r17,(mem_000050 + 1) com r20 ; invert crossfade distance for forward channel com r21 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes add r2,r0 ; accumulate result adc r3,r1 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes add r6,r0 ; accumulate result adc r7,r1 adc r2,r18 ; r18 cleared above adc r3,r18 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes sbc r3,r18 ; r18 is cleared above - subtract sign bit add r7,r0 ; accumulate result adc r2,r1 adc r3,r18 ; r18 is cleared above mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes add r7,r0 ; accumulate result adc r2,r1 adc r3,r18 ; r18 is cleared above ;multiply right reverse sample by crossfade distance movw r17:r16,r5:r4 ; move right reverse data to multiply register movw r21:r20,r13:r12 ; move crossfade distance to multiply register mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes movw r5:r4,r1:r0 ; store high bytes result for later mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes movw r7:r6,r1:r0 ; store low byets for later mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes sbc r5,r18 ; r18 is cleared above - subtract sign bit add r7,r0 ; accumulate result adc r4,r1 adc r5,r18 ; r18 is cleared above mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes add r7,r0 ; accumulate result adc r4,r1 adc r5,r18 ; r18 is cleared above ;multiply and accumulate right forward channel by crossfade distance movw r17:r16,r9:r8 ; move left forward data to multiply register com r20 ; invert crossfade distance for forward channel com r21 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes add r4,r0 ; accumulate result adc r5,r1 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes add r6,r0 ; accumulate result adc r7,r1 adc r4,r18 ; r18 cleared above adc r5,r18 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes sbc r5,r18 ; r18 is cleared above - subtract sign bit add r7,r0 ; accumulate result adc r4,r1 adc r5,r18 ; r18 is cleared above mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes add r7,r0 ; accumulate result adc r4,r1 adc r5,r18 ; r18 is cleared above adcsample_000050: ; get buffer size from adc lds r17,adcsra ; get adc control register sbrs r17,adif ; check if adc conversion is complete rjmp done_000050 ; skip adc sampling lds r16,adcl ; get low byte adc value lds r17,adch ; get high byte adc value add r14,r16 ; accumulate adc samples adc r10,r17 adc r11,r18 ; r18 is cleared above ldi r17,$f7 sts adcsra,r17 ; clear interrupt flag dec r15 ; countdown adc sample clock brne done_000050 ; dont get buffer size till its been long enough ldi r17,minbuff_000050 ; fetch minimum buffer size cp r10,r17 ; check if adc value is less than minimum buffer size cpc r11,r18 ; r18 is cleared above brsh high_000050 ; check if above max buffer size if not below min mov r10,r17 ; set to minimum buffer size rjmp deadband_000050 ; check if adc value changed enough high_000050: ; check max value of adc ldi r16,low(maxbuff_000050) ; fetch max buffer size ldi r17,high(maxbuff_000050) cp r10,r16 ; check if adc value larger than max buffer size cpc r11,r17 brlo deadband_000050 ; check for value chnage if lower than max size mov r10,r16 ; set buffer size to max value mov r11,r17 deadband_000050: ; check if adc value changed enough to warrant update movw r17:r16,r11:r10 ; move adc sample to temporary register sub r16,r26 ; find difference between adc sample and desired delay time sbc r17,r27 brsh check_000050 ; check for deadband if positive neg r16 ; invert if negative adc r17,r18 ; r18 is cleared above neg r17 check_000050: ; check if difference is greater than deadband cpi r16,$02 ; check if difference is less than 2 lsb cpc r17,r18 ; r18 cleared above brlo empty_000050 ; do nothing if less than $02 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change empty_000050: ; empty accumulation registers and finish off clr r10 ; empty accumulation registers clr r11 clr r14 switchsample_000050: ; check if at same function 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 new value mov r31,r16 done_000050: reti ; return to waiting }}}