ReverserGatedDelay

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.

reverser_gated_delay.asm


   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 

ReverserGatedDelay (last edited 2010-08-21 02:06:26 by guest)