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.
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