=== MICrODEC: Stock Functions === ==== 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. The pot (MOD1) controls the delay time, and subsequently the buffer size. A reverser is created by playing all the samples in reverse order. Unfortunately, you end up hitting the beginning of your buffer at some point, and need to start over again. This creates an audible click in the sample playback. To eliminate this click, we are using a cross-fading method. This is the same as cross-fading between records when DJing. As one sample gets close to the buffer boundary, its volume is faded down, and a sample from the other side of the boundary is faded up. This continues as the sample moves forward in the buffer, with the volume of the first sample being reduced to zero by the time it gets to the buffer boundary. At this point, the sample on the other side is playing full volume, and takes over. This gives a relatively smooth transition across the buffer boundary, with only a slight dipping noticeable, but also keeps the playback very true to the original signal during the majority of playback, which is not near the boundary. The crossfade time is preset at the begining of the code, which also determines the minimum sample size, as there has to be enough data for both fading up and down on either side of the boundary. The pot (MOD1) varies the delay time the function waits after the input crosses the threshold, before it starts reversing (from 6ms to 3s). This lets you trigger the device, and have it play the following notes backwards. There is also a backtrack time, which is a fixed amount of time that the function will continue playing after it has reached the beginning of the buffer (the trigger point). This allows the sampler to catch the rising edge of notes, before they cross the treshold. After this backtrack time, the reverser then crossfades to the currently forward moving signal. In this implementation, only input audio on the left channel (TRS Connector: Tip) can trigger the gate. [[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 }}}