1

MICrODEC: Stock Functions

Reverser with Crossfade

This function implements a reverser, so all input sounds are played backwards. It takes mono data in on the left channel, and presents mono data out on both the left and right channels. The pot (MOD1) controls 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 buffer size used for sample playback, from 6ms to 1.5s. Smaller buffer sizes give a more subtle effect, as you are playing through only very small samples at a time, but has the advantage of not having much of a delay. If the buffer is too small, you begin to hear the rate at which you are moving through the buffer, almost like a slight tremolo. For large buffer sizes, the samples sound more like reversed audio, but the delay becomes larger as well.

reverser_crossfade.asm


   1 ; program: reverser-16b-pot-crossfade.asm
   2 ; UID = 000044 - unique id to eliminate conflicts between variables
   3 ; 16b address space (1.5s sample time)
   4 ; mono data in on left channel, mono data out on left and right
   5 ; pot (MOD1) controlled buffer size
   6 
   7 ; program overview
   8 ;
   9 ; data is read in from memory and written out the codec at the same time
  10 ; new data is written to the memory from the codec.  left channel data
  11 ; is read in, and presented on both right and left out.  the write address
  12 ; increments until it reaches the top of the buffer and then starts at
  13 ; zero.  the read address does the same thing, but in reverse.  the adc
  14 ; value sets the buffer size, and is determined by the pot (MOD1) value,
  15 ; which is multiplied up to a 16b value.  the buffer size is adjusted at a
  16 ; rate of 2 lsb per sample period, until it matches to what the pot says
  17 ; it should be.  the samples are played normally, until within a fixed
  18 ; distance of the buffer boundary.  at this point, the samples are
  19 ; crossfaded with the first samples on the other side of the boundary.  this
  20 ; reduces the appearance of clicks at buffer transistions, without giving
  21 ; the reverb effect of the fading method, and with a little less tremolo
  22 ; than the ducking method.
  23 
  24 ; constant definitions
  25 ;
  26 .equ stepsize_000044 = $0080 ; 65536/(stepsize * 44.1) = crossfade time (ms)
  27 .equ buffer_min_000044 = (4 * ($10000 / stepsize_000044))
  28 ; minimum sample buffer size to accomodate crossfade time
  29 
  30 ; register usage - may be redefined in other sections
  31 ;
  32 ; r0  multiply result lsb
  33 ; r1  multiply result msb
  34 ; r2  multiply accumulate lsb
  35 ; r3  multiply accumulate mlb
  36 ; r4  left/right lsb out / sample 1 lsb / multiply accumulate mhb
  37 ; r5  left/right msb out / smaple 2 msb / multiply accumulate msb
  38 ; r6  left lsb in / sample 2 lsb
  39 ; r7  left msb in / sample 2 msb
  40 ; r8  
  41 ; r9  adc msb accumulator
  42 ; r10 adc fractional byte accumulator
  43 ; r11 adc lsb accumulator
  44 ; r12 desired buffer size lsb
  45 ; r13 desired buffer size msb
  46 ; r14 null register
  47 ; r15 switch/adc counter
  48 ; r16 temporary swap register
  49 ; r17 temporary swap register
  50 ; r18 crossfade address temporary lsb
  51 ; r19 crossfade address temporary msb
  52 ; r20 crossfade distance lsb
  53 ; r21 crossfade distance msb
  54 ; r22 multiplicand lsb 
  55 ; r23 multiplicand msb
  56 ; r24 write address lsb
  57 ; r25 write address msb
  58 ; r26 buffer size lsb
  59 ; r27 buffer size msb
  60 ; r28 read address lsb
  61 ; r29 read address msb
  62 ; r30 jump location for interrupt lsb
  63 ; r31 jump location for interrupt msb
  64 ; t   crossfading indicator
  65 
  66 ; program starts here first time
  67 ; initialze z pointer for correct jump
  68 ; this assumes a less than 256 word jump
  69 ldi r30,$20 ; set jump location to program start
  70 clr r24 ; clear write register
  71 clr r25
  72 ldi r22,$00 ; setup write address high byte
  73 clr r18 ; setup r18 as null register for carry addition and ddr setting
  74 ldi r17,$ff ; setup r17 for ddr setting
  75 
  76 clear_000044: ; clear delay buffer
  77 ; eliminates static when first switching to the delay setting
  78 
  79 adiw r25:r24,$01 ; increment write register
  80 adc r22,r18 ; increment write third byte
  81 cpi r22,$01 ; check if 16b memory space has been cleared
  82 breq cleardone_000044 ; continue until end of buffer reached
  83 out portd,r24 ; set address
  84 sts porth,r25
  85 out portg,r22 ; pull ce low,we low,and set high bits of address
  86 out ddra,r17 ; set porta as output for data write
  87 out ddrc,r17 ; set portc as output for data write
  88 out porta,r18 ; set data
  89 out portc,r18 ; r18 is cleared above
  90 sbi portg,portg2 ; pull we high to write
  91 out ddra,r18 ; set porta as input for data lines
  92 out ddrc,r18 ; set portc as input for data lines
  93 rjmp clear_000044 ; continue clearing
  94 
  95 cleardone_000044: ; reset registers
  96 
  97 ldi r24,$00 ; initialize write register
  98 ldi r25,$00
  99 clr r14 ; setup null register
 100 ldi r28,$00 ; set read address to minimum delay
 101 ldi r29,$fd
 102 clr r4 ; initialize data output registers
 103 clr r5
 104 ldi r26,$00 ; initialize buffer size
 105 ldi r27,$06
 106 reti ; finish with initialization and wait for next interrupt
 107 
 108 ; program starts here every time but first
 109 ; initiate data transfer to codec
 110 sbi portb,portb0 ; toggle slave select pin
 111 out spdr,r5 ; send out left channel msb
 112 cbi portb,portb0
 113 
 114 ;increment write address
 115 adiw r25:r24,$01 ; increment write address
 116 cp r24,r26 ; check if at buffer boundary
 117 cpc r25,r27
 118 brlo wait1_000044 ; continue if not
 119 clr r24 ; set write address to bottom
 120 clr r25
 121 
 122 wait1_000044: ; check if byte has been sent
 123 
 124 in r17,spsr
 125 sbrs r17,spif
 126 rjmp wait1_000044
 127 in r7,spdr ; recieve in left channel msb
 128 out spdr,r4 ; send out left channel lsb
 129 
 130 ;decrement read address
 131 sbiw r29:r28,$01 ; decrement read address
 132 brsh wait2_000044 ; check if at bottom of buffer
 133 movw r29:r28,r27:r26 ; set counter to top
 134 sbiw r29:r28,$01 ; decrement read address
 135 
 136 wait2_000044: ; check if byte has been sent
 137 
 138 in r17,spsr
 139 sbrs r17,spif
 140 rjmp wait2_000044
 141 in r6,spdr ; recieve in left channel lsb
 142 out spdr,r5 ; send out right channel msb
 143 
 144 ;write left channel data to sram
 145 out portd,r24 ; set address
 146 sts porth,r25
 147 out portg,r14 ; pull ce low,we low,and set high bits of address
 148 ldi r17,$ff
 149 out ddra,r17 ; set porta as output for data write
 150 out ddrc,r17 ; set portc as output for data write
 151 out porta,r6 ; set data
 152 out portc,r7
 153 sbi portg,portg2 ; pull we high to write
 154 out ddra,r14 ; set porta as input for data lines
 155 out ddrc,r14 ; set portc as input for data lines
 156 
 157 wait3_000044: ; check if byte has been sent
 158 
 159 in r17,spsr
 160 sbrs r17,spif
 161 rjmp wait3_000044
 162 in r17,spdr ; recieve in right channel msb
 163 out spdr,r4 ; send out right channel lsb
 164 
 165 ;get left/right channel data from sram
 166 out portd,r28 ; set address
 167 sts porth,r29
 168 nop ; wait input latch time of 2 clock cycles
 169 nop
 170 in r4,pina ; get data
 171 in r5,pinc ; get data
 172 
 173 wait4_000044: ; check if byte has been sent
 174 
 175 in r17,spsr
 176 sbrs r17,spif
 177 rjmp wait4_000044
 178 in r17,spdr ; recieve in left channel lsb
 179 
 180 ;get crossfade distance
 181 movw r17:r16,r29:r28 ; move read address to temporary register
 182 sub r16,r24 ; find distance to loop boundary
 183 sbc r17,r25
 184 brcc attenuate_000044 ; check if within crossfade distance if positive
 185 add r16,r26 ; flip result around boundary if negative
 186 adc r17,r27
 187 
 188 attenuate_000044: ; multiply signal by distance to boundary
 189 
 190 brts crossfade1_000044 ; skip if already crossfading
 191 ldi r18,low(2 * ($10000 / stepsize_000044)) ; get crossfade distance
 192 ldi r19,high(2 * ($10000 / stepsize_000044))
 193 cp r16,r18 ; check if less than crossfade distance
 194 cpc r17,r19
 195 brsh check_000044 ; do nothing if not
 196 set ; set t register to indicate crossfade and downcounting
 197 clr r20 ; set crossfade counter to top
 198 clr r21
 199 subi r20,low(stepsize_000044) ; decrement for first sample
 200 sbci r21,high(stepsize_000044)
 201 
 202 crossfade1_000044: ; crossfade the signal
 203 
 204 ;setup sample 2 read address
 205 movw r19:r18,r29:r28 ; move read address to temporary register
 206 subi r18,low(2 * ($10000 / stepsize_000044)) ; get sample from other side of boundary
 207 sbci r19,high(2 * ($10000 / stepsize_000044))
 208 brcc crossfade2_000044 ; continue if no buffer underflow
 209 add r18,r26 ; wrap read address around buffer
 210 adc r19,r27
 211 
 212 crossfade2_000044: ; continue crossfading signal
 213 
 214 ;get sample 2 from sram
 215 out portd,r18 ; set address
 216 sts porth,r19
 217 nop ; wait input latch time of 2 clock cycles
 218 nop
 219 in r6,pina ; get data
 220 in r7,pinc ; get data
 221 
 222 ;multiply sample 1
 223 movw r17:r16,r5:r4 ; move data to signed multiply register
 224 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
 225 movw r5:r4,r1:r0 ; store high bytes result for later
 226 mul     r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
 227 movw r3:r2,r1:r0 ; store low byets for later
 228 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
 229 sbc     r5,r14 ; r14 is cleared above - subtract sign bit
 230 add     r3,r0 ; accumulate result
 231 adc     r4,r1
 232 adc     r5,r14 ; r14 is cleared above
 233 mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
 234 add     r3,r0 ; accumulate result
 235 adc     r4,r1
 236 adc     r5,r14 ; r14 is cleared above
 237 
 238 ;multiply and accumulate sample 2
 239 movw r17:r16,r7:r6 ; move data to signed multiply register
 240 movw r23:r22,r21:r20 ; move distance counter to temporary register
 241 com r22 ; invert distance for sample 2
 242 com r23
 243 mulsu r17,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
 244 add r4,r0 ; accumulate result
 245 adc r5,r1
 246 mul     r16,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
 247 add r2,r0 ; accumulate result
 248 adc r3,r1
 249 adc r4,r14
 250 adc r5,r14
 251 mulsu r17,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
 252 sbc     r5,r14 ; r14 is cleared above - subtract sign bit
 253 add     r3,r0 ; accumulate result
 254 adc     r4,r1
 255 adc     r5,r14 ; r14 is cleared above
 256 mul r23,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
 257 add     r3,r0 ; accumulate result
 258 adc     r4,r1
 259 adc     r5,r14 ; r14 is cleared above
 260 
 261 ;check if done crossfading
 262 subi r20,low(stepsize_000044) ; decrement crossfade
 263 sbci r21,high(stepsize_000044)
 264 brcc check1_000044 ; check if crossfade time is negative
 265 movw r29:r28,r19:r18 ; move crossfade address to current address
 266 clt ; clear the t register to indicate done
 267 rjmp check_000044 ; finish off
 268 
 269 check1_000044: ; continue checking crossfade time
 270 
 271 brne check_000044 ; check if crossfade time is zero
 272 movw r29:r28,r19:r18 ; move crossfade address to current address
 273 clt ; clear the t register to indicate done
 274 
 275 check_000044: ; check if buffer size is correct
 276 
 277 cp r26,r12 ; compare current delay to desired delay
 278 cpc r27,r13
 279 brlo upcount2_000044 ; increment if smaller than
 280 breq adcsample_000044 ; do nothing if they are same size
 281 sbiw r27:r26,$02 ; decrement buffer size
 282 rjmp adcsample_000044 ; finish off
 283 
 284 upcount2_000044: ; increment buffer size register
 285 
 286 adiw r27:r26,$02 ; increment buffer size
 287 
 288 adcsample_000044: ; get loop setting
 289 
 290 lds r17,adcsra ; get adc control register
 291 sbrs r17,adif ; check if adc conversion is complete
 292 rjmp done_000044 ; skip adc sampling
 293 lds r16,adcl ; get low byte adc value
 294 lds r17,adch ; get high byte adc value
 295 add r10,r16
 296 adc r11,r17 ; accumulate adc samples
 297 adc r9,r14 ; accumulate adc samples - r14 is cleared above
 298 ldi r17,$f7
 299 sts adcsra,r17 ; clear interrupt flag
 300 dec r15 ; countdown adc sample clock
 301 brne done_000044 ; move adc value to loop setting after 256 samples
 302 lsr r9 ; divide accumulated value by 4 to get 16b value
 303 ror r11
 304 ror r10
 305 lsr r9
 306 ror r11
 307 ror r10
 308 ldi r16,low(buffer_min_000044) ; load minimum buffer size
 309 ldi r17,high(buffer_min_000044)
 310 cp r10,r16 ; check if less than minimum
 311 cpc r11,r17
 312 brsh compare_000044 ; compare to previous value if above min
 313 movw r11:r10,r17:r16 ; set buffer size to minimum
 314 
 315 compare_000044: ; compare to previous value
 316 
 317 movw r17:r16,r13:r12 ; make a copy of current loop time for comparison
 318 sub r16,r10 ; find difference between current loop time and last loop time
 319 sbc r17,r11
 320 brcc deadband_000044 ; see if difference is large enough to indicate a change
 321 neg r16 ; invert difference if negative
 322 adc r17,r14 ; r14 is cleared above
 323 neg r17
 324 
 325 deadband_000044: ; see if pot has moved or if its just noise
 326 
 327 cpi r16,$40 ; see if difference is greater than 1 lsb
 328 cpc r17,r14 ; r14 is cleared above
 329 brlo nochange_000044 ; dont update loop time if difference is not large enough
 330 ldi r16,$fe ; make sure buffer size is even
 331 and r10,r16
 332 movw r13:r12,r11:r10 ; move adc value to loop time register
 333 
 334 nochange_000044: ; clear accumulation registers
 335 
 336 clr r10 ; empty accumulation registers
 337 clr r11
 338 clr r9
 339 
 340 ;check rotary switch state
 341 lds r16,pinj ; get switch data
 342 andi r16,$78 ; mask off rotary switch
 343 lsr r16 ; adjust switch position to program memory location
 344 lsr r16
 345 ldi r17,$02
 346 add r16,r17
 347 cpse r16,r31 ; check if location has changed
 348 clr r30 ; reset jump register to intial state
 349 mov r31,r16
 350 
 351 done_000044:
 352 
 353 reti ; return to waiting
 354