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.
1 ; program: reverser-18b-gated-delay.asm
2 ; UID = 000050 - unique id to eliminate conflicts between variables
3 ; 18b address space (3s sample time)
4 ; stereo data, in and out
5 ; pot (MOD1) controlled delay
6
7 ; program overview
8 ;
9 ; data is read in from memory and written out to the codec at the same time
10 ; new data is written to the memory from the codec. the left channel input
11 ; data is compared to a predetermined threshold level, and if it crosses
12 ; this level, the reverser function is initiated after a delay. the delay
13 ; is set via the adc reading from the pot (MOD1). this value is sampled
14 ; 256 times and deadbanded to reduce jitter. the codec passes data
15 ; through while in forward mode, and continues to do so for the delay
16 ; period after the input has crossed the threshold. after this time, the
17 ; input data is no longer written to memory, in order to increase the
18 ; available buffer size. the reverse data is played out back to the
19 ; beginning, plus a predetermined backtrack amount to catch the rising
20 ; edge of notes. at this point, the forward data saving is resumed, and
21 ; the reverse data is faded while the current data is faded in. this
22 ; crossfade size is determined via a constant at the beginning of the code.
23 ; there is also a holdoff time after the reverser function has finished,
24 ; before it can be retriggered, in order to reduce constant triggering and
25 ; glitches from not having enough forward data stored.
26
27 ; constant definitions
28 ;
29 .equ backtrack_000050 = $0800 ; past memory used as starting point
30 ; (must be even)
31 .equ holdoff_000050 = $0f00 ; holdoff before retriggering
32 ; must be larger than backtrack (must be even)
33 .equ stepsize_000050 = $0080 ; 65536/(stepsize * 44.1) = crossfade time (ms)
34 .equ threshold_000050 = $3900 ; threshold for turn on of reverser ($7fff max)
35 .equ minbuff_000050 = $02 ; minimum buffer time in multiples of 5.8ms
36 .equ maxbuff_000050 = ($03ff00 - (2 * ($010000 / stepsize_000050)) - holdoff_000050)
37 ; maximum buffer time to keep from overlapping
38 .equ mem_000050 = $0200 ; memory location for storage in internal sram
39
40 ; register usage - may be redefined in other sections
41 ;
42 ; r0 multiply result lsb
43 ; r1 multiply result msb
44 ; r2 left lsb out
45 ; r3 left msb out
46 ; r4 right lsb out
47 ; r5 right msb out
48 ; r6 left lsb in
49 ; r7 left msb in
50 ; r8 right lsb in
51 ; r9 right msb in
52 ; r10 adc accumulation lsb
53 ; r11 adc accumulation msb
54 ; r12 buffer bottom / crossfade lsb
55 ; r13 buffer bottom / crossfade msb
56 ; r14 adc accumulation fractional byte
57 ; r15 switch/adc sample counter
58 ; r16 temporary swap register
59 ; r17 temporary swap register
60 ; r18 null register
61 ; r19 reverse mode indicator
62 ; r20 temporary swap regiser
63 ; r21 buffer bottom third byte
64 ; r22 write address third byte
65 ; r23 read address third byte
66 ; r24 write address lsb
67 ; r25 write address msb
68 ; r26 desired buffer size lsb
69 ; r27 desired buffer size msb
70 ; r28 read address lsb
71 ; r29 read address msb
72 ; r30 jump location for interrupt lsb
73 ; r31 jump location for interrupt msb
74 ; t foward/reverse indicator
75
76 ; program starts here first time
77 ; initialze z pointer for correct jump
78 ; this assumes a less than 256 word jump
79 ldi r30,$23 ; set jump location to program start
80 clr r24 ; clear write register
81 clr r25
82 ldi r22,$00 ; setup write address high byte
83 clr r18 ; setup r18 as null register for carry addition and ddr setting
84 ldi r17,$ff ; setup r17 for ddr setting
85
86 clear_000050: ; clear delay buffer
87 ; eliminates static when first switching to the delay setting
88
89 adiw r25:r24,$01 ; increment write register
90 adc r22,r18 ; increment write third byte
91 cpi r22,$04 ; check if full memory space has been cleared
92 breq cleardone_000050 ; continue until end of buffer reached
93 out portd,r24 ; set address
94 sts porth,r25
95 out portg,r22 ; pull ce low,we low,and set high bits of address
96 out ddra,r17 ; set porta as output for data write
97 out ddrc,r17 ; set portc as output for data write
98 out porta,r18 ; set data
99 out portc,r18 ; r18 is cleared above
100 sbi portg,portg2 ; pull we high to write
101 out ddra,r18 ; set porta as input for data lines
102 out ddrc,r18 ; set portc as input for data lines
103 rjmp clear_000050 ; continue clearing
104
105 cleardone_000050: ; reset registers
106
107 ldi r24,$01 ; initialize write register
108 ldi r25,$00
109 ldi r22,$00 ; setup write address high byte
110 ldi r28,$01 ; set read address to minimum delay
111 ldi r29,$ff
112 ldi r23,$07 ; setup read address high byte
113 clr r19 ; initialize reverse mode register
114 clr r2 ; initialize data output registers
115 clr r3
116 clr r4
117 clr r5
118 clt ; clear t register to start with forward play
119 reti ; finish with initialization and wait for next interrupt
120
121 ; initiate data transfer to codec
122 ; this is the point the z-pointer is incremented to
123 ; program starts here all but first time
124 sbi portb,portb0 ; toggle slave select pin
125 out spdr,r3 ; send out left channel msb
126 cbi portb,portb0
127
128 ;increment write addresses
129 brts wait1_000050 ; dont write data to memory if reversing
130 adiw r25:r24,$01 ; increment write address
131 adc r22,r18 ; increment write third byte
132 andi r22,$03 ; mask off unsed bits
133
134 wait1_000050: ; check if byte has been sent
135
136 in r17,spsr
137 sbrs r17,spif
138 rjmp wait1_000050
139 in r7,spdr ; recieve in left channel msb
140 out spdr,r2 ; send out left channel lsb
141
142 wait2_000050: ; check if byte has been sent
143
144 in r17,spsr
145 sbrs r17,spif
146 rjmp wait2_000050
147 in r6,spdr ; recieve in left channel lsb
148 out spdr,r5 ; send out right channel msb
149
150 ;write left channel data to sram
151 brts wait3_000050 ; dont write data to memory if reversing
152 out portd,r24 ; set address
153 sts porth,r25
154 out portg,r22 ; pull ce low,we low,and set high bits of address
155 ldi r17,$ff
156 out ddra,r17 ; set porta as output for data write
157 out ddrc,r17 ; set portc as output for data write
158 out porta,r6 ; set data
159 out portc,r7
160 sbi portg,portg2 ; pull we high to write
161 out ddra,r18 ; set porta as input for data lines
162 out ddrc,r18 ; set portc as input for data lines
163
164 wait3_000050: ; check if byte has been sent
165
166 in r17,spsr
167 sbrs r17,spif
168 rjmp wait3_000050
169 in r9,spdr ; recieve in right channel msb
170 out spdr,r4 ; send out right channel lsb
171
172 ;increment write address
173 brts wait4_000050 ; dont write data to memory if reversing
174 adiw r25:r24,$01 ; increment write address
175 adc r22,r18 ; increment write third byte
176 andi r22,$03 ; mask off unsed bits
177
178 wait4_000050: ; check if byte has been sent
179
180 in r17,spsr
181 sbrs r17,spif
182 rjmp wait4_000050
183 in r8,spdr ; recieve in left channel lsb
184
185 ;write right channel data to sram
186 brts dataload_000050 ; dont write data to memory if reversing
187 out portd,r24 ; set address
188 sts porth,r25
189 out portg,r22 ; pull ce low,we low,and set high bits of address
190 ldi r17,$ff
191 out ddra,r17 ; set porta as output for data write
192 out ddrc,r17 ; set portc as output for data write
193 out porta,r8 ; set data
194 out portc,r9
195 sbi portg,portg2 ; pull we high to write
196 out ddra,r18 ; set porta as input for data lines
197 out ddrc,r18 ; set portc as input for data lines
198
199 ;check if input signal is above threshold
200 movw r3:r2,r7:r6 ; backup input values as they get modified
201 movw r5:r4,r9:r8 ; move input data to output
202 sbrc r19,$04 ; check if holding off after last trigger
203 rjmp holdtime_000050 ; countdown holdoff timer if waiting
204 sbrc r19,$07 ; check if already in reverse mode
205 rjmp reverse_000050 ; go to reverse function - else check threshold
206 ldi r16,low(threshold_000050) ; get threshold value
207 ldi r17,high(threshold_000050)
208 tst r7 ; check if negative
209 brpl compare_000050 ; compare if positive
210 com r6 ; invert if negative
211 com r7 ; ones complement used for simplicity
212
213 compare_000050: ; compare input value to threshold
214
215 cp r6,r16 ; compare to left channel input
216 cpc r7,r17
217 brsh start_000050 ; start reversing if above threshold
218 rjmp adcsample_000050 ; else finish off
219
220 holdtime_000050: ; decrement holdoff timer
221
222 ldi r17,$02 ; subtract 2 from holdoff timer
223 sub r12,r17 ; 2 is used to have holdoff and backup match scaling
224 sbc r13,r18 ; r18 is cleared above
225 brne finishoff_000050 ; finish off if it not done holding off
226 clr r19 ; set mode to forward and clear hold off bit
227
228 finishoff_000050: ; finish off if not done holding off
229
230 rjmp adcsample_000050 ; finish off - too long for a branch instruction
231
232 start_000050: ; intialize reversing
233
234 ldi r19,$80 ; indicate that reverser is active, waiting for delay
235 movw r17:r16,r25:r24 ; move write address to temporary register
236 mov r21,r22
237 subi r16,low(backtrack_000050) ; remove initial buffer size
238 sbci r17,high(backtrack_000050)
239 sbc r21,r18 ; r18 cleared above
240 andi r21,$03 ; mask off low bits
241 movw r13:r12,r17:r16 ; move buffer bottom to its register
242 rjmp adcsample_000050 ; finish off
243
244 reverse_000050: ; check for reverse function
245
246 sbrc r19,$06 ; check if delay time has been met
247 rjmp dataload_000050 ; get data from memory if delay time has been met
248 ; else check if delay time is up yet
249 movw r17:r16,r13:r12 ; move bottom of buffer to temporary register
250 mov r20,r21
251 add r17,r26 ; add in buffer size
252 adc r20,r27
253 andi r20,$03 ; mask off unused bits
254 cp r24,r16 ; check if write address is at top of buffer
255 cpc r25,r17
256 cpc r22,r20
257 breq bufferset_000050 ; set the read address to the top of the buffer
258 rjmp adcsample_000050 ; else finish off
259
260 bufferset_000050: ; set the read address to the top of the buffer
261
262 movw r29:r28,r25:r24 ; else set read address equal to write address
263 mov r23,r22
264 ori r19,$40 ; set delay time bit in function register
265 set ; set the t register to indicate reversing
266 rjmp dataload1_000050 ; get data from sram
267
268 dataload_000050: ; get data from sram
269
270 sbrc r19,$05 ; check if crossfading
271 rjmp dataload1_000050 ; skip buffer check if crossfading
272 andi r23,$03 ; mask off unused bits
273 cp r28,r12 ; check if at bottom of buffer
274 cpc r29,r13
275 cpc r23,r21
276 brne dataload1_000050 ; continue if not at bottom
277 ori r19,$20 ; set crossfade bit
278 ldi r16,$ff ; set crossfade distance to max value
279 mov r12,r16 ; buffer bottom value no longer needed
280 mov r13,r16
281 clt ; clear the t register to start recording forward again
282
283 dataload1_000050: ; continue getting data from sram
284
285 sbiw r29:r28,$03 ; decrement read address
286 sbc r23,r18 ; r18 is cleared above
287 andi r23,$03 ; mask off unused bits
288 ori r23,$04 ; set we/ bit in high byte register
289
290 ;get left channel data from sram
291 out portg,r23 ; pull ce low, we high, and set high bits of register
292 out portd,r28 ; set address
293 sts porth,r29
294 adiw r29:r28,$01 ; increment read address
295 adc r23,r18 ; increment write third byte
296 andi r23,$03 ; mask off unsed bits
297 ori r23,$04 ; set we/ bit in high byte register
298 in r2,pina ; get data
299 in r3,pinc ; get data
300
301 ;get right channel data from sram
302 out portg,r23 ; pull ce low, we high, and set high bits of register
303 out portd,r28 ; set address
304 sts porth,r29
305 nop ; wait 2 cycle setup time
306 nop
307 in r4,pina ; get data
308 in r5,pinc ; get data
309
310 ;check if crossfading
311 sbrs r19,$05 ; check if crossfading
312 rjmp adcsample_000050 ; finish off if not crossfading
313 ldi r16,low(stepsize_000050) ; get crossfade stepsize
314 ldi r17,high(stepsize_000050)
315 sub r12,r16 ; decrement crossfade distance counter
316 sbc r13,r17
317 breq crossfade_done_000050 ; stop crossfading if done
318 brcs crossfade_done_000050 ; stop crossfading if done
319 rjmp crossfade_000050
320
321 crossfade_done_000050: ; stop crossfading
322
323 movw r3:r2,r7:r6 ; move forward data to output
324 movw r5:r4,r9:r8
325 ldi r16,low(holdoff_000050) ; setup holdoff counter
326 ldi r17,high(holdoff_000050)
327 movw r13:r12,r17:r16 ; r13:r12 no longer needed for crossfading
328 ori r19,$10 ; set reverse mode to holdoff
329 rjmp adcsample_000050 ; finish off
330
331 crossfade_000050: ; crossfade between forward and reverse signals
332
333 sts mem_000050,r6 ; backup data to free a register
334 sts (mem_000050 + 1),r7
335
336 ;multiply left reverse sample by crossfade distance
337 movw r17:r16,r3:r2 ; move left reverse data to multiply register
338 movw r21:r20,r13:r12 ; move crossfade distance to multiply register
339 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
340 movw r3:r2,r1:r0 ; store high bytes result for later
341 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
342 movw r7:r6,r1:r0 ; store low byets for later
343 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
344 sbc r3,r18 ; r18 is cleared above - subtract sign bit
345 add r7,r0 ; accumulate result
346 adc r2,r1
347 adc r3,r18 ; r18 is cleared above
348 mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
349 add r7,r0 ; accumulate result
350 adc r2,r1
351 adc r3,r18 ; r18 is cleared above
352
353 ;multiply and accumulate left forward channel by crossfade distance
354 lds r16,mem_000050 ; fetch left forward channel from memory
355 lds r17,(mem_000050 + 1)
356 com r20 ; invert crossfade distance for forward channel
357 com r21
358 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
359 add r2,r0 ; accumulate result
360 adc r3,r1
361 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
362 add r6,r0 ; accumulate result
363 adc r7,r1
364 adc r2,r18 ; r18 cleared above
365 adc r3,r18
366 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
367 sbc r3,r18 ; r18 is cleared above - subtract sign bit
368 add r7,r0 ; accumulate result
369 adc r2,r1
370 adc r3,r18 ; r18 is cleared above
371 mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
372 add r7,r0 ; accumulate result
373 adc r2,r1
374 adc r3,r18 ; r18 is cleared above
375
376 ;multiply right reverse sample by crossfade distance
377 movw r17:r16,r5:r4 ; move right reverse data to multiply register
378 movw r21:r20,r13:r12 ; move crossfade distance to multiply register
379 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
380 movw r5:r4,r1:r0 ; store high bytes result for later
381 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
382 movw r7:r6,r1:r0 ; store low byets for later
383 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
384 sbc r5,r18 ; r18 is cleared above - subtract sign bit
385 add r7,r0 ; accumulate result
386 adc r4,r1
387 adc r5,r18 ; r18 is cleared above
388 mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
389 add r7,r0 ; accumulate result
390 adc r4,r1
391 adc r5,r18 ; r18 is cleared above
392
393 ;multiply and accumulate right forward channel by crossfade distance
394 movw r17:r16,r9:r8 ; move left forward data to multiply register
395 com r20 ; invert crossfade distance for forward channel
396 com r21
397 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
398 add r4,r0 ; accumulate result
399 adc r5,r1
400 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
401 add r6,r0 ; accumulate result
402 adc r7,r1
403 adc r4,r18 ; r18 cleared above
404 adc r5,r18
405 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
406 sbc r5,r18 ; r18 is cleared above - subtract sign bit
407 add r7,r0 ; accumulate result
408 adc r4,r1
409 adc r5,r18 ; r18 is cleared above
410 mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
411 add r7,r0 ; accumulate result
412 adc r4,r1
413 adc r5,r18 ; r18 is cleared above
414
415 adcsample_000050: ; get buffer size from adc
416
417 lds r17,adcsra ; get adc control register
418 sbrs r17,adif ; check if adc conversion is complete
419 rjmp done_000050 ; skip adc sampling
420 lds r16,adcl ; get low byte adc value
421 lds r17,adch ; get high byte adc value
422 add r14,r16 ; accumulate adc samples
423 adc r10,r17
424 adc r11,r18 ; r18 is cleared above
425 ldi r17,$f7
426 sts adcsra,r17 ; clear interrupt flag
427 dec r15 ; countdown adc sample clock
428 brne done_000050 ; dont get buffer size till its been long enough
429 ldi r17,minbuff_000050 ; fetch minimum buffer size
430 cp r10,r17 ; check if adc value is less than minimum buffer size
431 cpc r11,r18 ; r18 is cleared above
432 brsh high_000050 ; check if above max buffer size if not below min
433 mov r10,r17 ; set to minimum buffer size
434 rjmp deadband_000050 ; check if adc value changed enough
435
436 high_000050: ; check max value of adc
437
438 ldi r16,low(maxbuff_000050) ; fetch max buffer size
439 ldi r17,high(maxbuff_000050)
440 cp r10,r16 ; check if adc value larger than max buffer size
441 cpc r11,r17
442 brlo deadband_000050 ; check for value chnage if lower than max size
443 mov r10,r16 ; set buffer size to max value
444 mov r11,r17
445
446 deadband_000050: ; check if adc value changed enough to warrant update
447
448 movw r17:r16,r11:r10 ; move adc sample to temporary register
449 sub r16,r26 ; find difference between adc sample and desired delay time
450 sbc r17,r27
451 brsh check_000050 ; check for deadband if positive
452 neg r16 ; invert if negative
453 adc r17,r18 ; r18 is cleared above
454 neg r17
455
456 check_000050: ; check if difference is greater than deadband
457
458 cpi r16,$02 ; check if difference is less than 2 lsb
459 cpc r17,r18 ; r18 cleared above
460 brlo empty_000050 ; do nothing if less than $02
461 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change
462
463 empty_000050: ; empty accumulation registers and finish off
464
465 clr r10 ; empty accumulation registers
466 clr r11
467 clr r14
468
469 switchsample_000050: ; check if at same function
470
471 lds r16,pinj ; get switch data
472 andi r16,$78 ; mask off rotary switch
473 lsr r16 ; adjust switch position to program memory location
474 lsr r16
475 ldi r17,$02
476 add r16,r17
477 cpse r16,r31 ; check if location has changed
478 clr r30 ; reset jump register to new value
479 mov r31,r16
480
481 done_000050:
482
483 reti ; return to waiting
484