welcome: please sign in

Revision 4 as of 2010-08-14 00:10:37

Clear message
location: ReverserFading

Reverser with Fading

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 use the fading method. This involves having two samples playing back simultaneously, each from a different point in the buffer (spaced a half-buffer's distance from each other). As one sample gets closer to the boundary, its volume is faded down, and the other is faded up. This continues as each sample moves forward in the buffer, with the volume of the sample being determined by its distance from the buffer boundary. This gives very smooth transitions across the buffer boundary, but also has a slight reverb effect, as multiple delayed signals are being mixed together.

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


   1 ; program: reverser-16b-pot-fading.asm
   2 ; UID = 000036 - this is a unique id so variables dont conflict
   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 sent out and taken in from the codec.  data is taken in on the
  10 ; left channel, and played out on both left and right.  a buffer of the
  11 ; past n seconds is kept and the output is the result of sampling this
  12 ; buffer in reverse.  this buffer size is determined by the pot (MOD1) value,
  13 ; which is multiplied up to a 16b value.  the buffer size is adjusted at a
  14 ; rate of 2 lsb per sample period, until it matches to what the pot says
  15 ; it should be. two samples are taken and averaged together by the
  16 ; ratio of their distances to the buffer boundary.  they are 180 degrees
  17 ; out of phase, so as one sample is crossing the buffer boundary, it is
  18 ; silent, and the other plays full volume.  this helps reduce the clicks
  19 ; at buffer boundary transitions.  it also creates a reverb sound.
  20 
  21 ; constant definitions
  22 ;
  23 .equ buffer_min_000036 = $0200 ; minimum sample buffer size
  24 
  25 ; register usage - may be redefined in other sections
  26 ;
  27 ; r0  multiply result lsb
  28 ; r1  multiply result msb
  29 ; r2  sample 2 lsb
  30 ; r3  sample 2 msb
  31 ; r4  left/right lsb out / sample 1 lsb
  32 ; r5  left/right msb out / sample 1 msb
  33 ; r6  left lsb in
  34 ; r7  left msb in
  35 ; r8  
  36 ; r9  adc msb accumulator
  37 ; r10 adc fractional byte accumulator
  38 ; r11 adc lsb accumulator
  39 ; r12 desired buffer size lsb
  40 ; r13 desired buffer size msb
  41 ; r14 
  42 ; r15 switch/adc counter
  43 ; r16 temporary swap register
  44 ; r17 temporary swap register
  45 ; r18 multiplicand lsb
  46 ; r19 multiplicand msb
  47 ; r20 multiplicand lsb
  48 ; r21 multiplicand msb
  49 ; r22 null register
  50 ; r23 
  51 ; r24 write address lsb
  52 ; r25 write address msb
  53 ; r26 buffer length lsb
  54 ; r27 buffer length msb
  55 ; r28 read address lsb
  56 ; r29 read address msb
  57 ; r30 jump location for interrupt lsb
  58 ; r31 jump location for interrupt msb
  59 ; t
  60 
  61 ; program starts here first time
  62 ; initialze z pointer for correct jump
  63 ; this assumes a less than 256 word jump
  64 ldi r30,$20 ; set jump location to program start
  65 clr r24 ; clear write register
  66 clr r25
  67 ldi r22,$00 ; setup write address high byte
  68 clr r18 ; setup r18 as null register for carry addition and ddr setting
  69 ldi r17,$ff ; setup r17 for ddr setting
  70 
  71 clear_000036: ; clear delay buffer
  72 ; eliminates static when first switching to the delay setting
  73 
  74 adiw r25:r24,$01 ; increment write register
  75 adc r22,r18 ; increment write third byte
  76 cpi r22,$01 ; check if 16b memory space has been cleared
  77 breq cleardone_000036 ; continue until end of buffer reached
  78 out portd,r24 ; set address
  79 sts porth,r25
  80 out portg,r22 ; pull ce low,we low,and set high bits of address
  81 out ddra,r17 ; set porta as output for data write
  82 out ddrc,r17 ; set portc as output for data write
  83 out porta,r18 ; set data
  84 out portc,r18 ; r18 is cleared above
  85 sbi portg,portg2 ; pull we high to write
  86 out ddra,r18 ; set porta as input for data lines
  87 out ddrc,r18 ; set portc as input for data lines
  88 rjmp clear_000036 ; continue clearing
  89 
  90 cleardone_000036: ; reset registers
  91 
  92 ldi r24,$00 ; initialize write register
  93 ldi r25,$00
  94 ldi r22,$00 ; setup write address high byte
  95 ldi r28,$00 ; set read address to minimum delay
  96 ldi r29,$fd
  97 clr r4 ; initialize data output registers
  98 clr r5
  99 ldi r26,$00 ; initialize buffer size
 100 ldi r27,$06
 101 reti ; finish with initialization and wait for next interrupt
 102 
 103 ; program begins here
 104 ; initiate data transfer to codec
 105 sbi portb,portb0 ; toggle slave select pin
 106 out spdr,r5 ; send out left channel msb
 107 cbi portb,portb0
 108 
 109 ;increment write address
 110 adiw r25:r24,$01 ; increment write address
 111 cp r24,r26 ; check if at end of buffer
 112 cpc r25,r27
 113 brlo wait1_000036 ; do nothing if not at end of buffer
 114 clr r24 ; reset buffer to bottom
 115 clr r25
 116 
 117 wait1_000036: ; check if byte has been sent
 118 
 119 in r17,spsr
 120 sbrs r17,spif
 121 rjmp wait1_000036
 122 in r7,spdr ; recieve in left channel msb
 123 out spdr,r4 ; send out left channel lsb
 124 
 125 ;decrement read address (for going in reverse)
 126 sbiw r29:r28,$01
 127 brcc wait2_000036 ; do nothing if not at end of buffer
 128 movw r29:r28,r27:r26 ; reset to top of buffer
 129 sbiw r29:r28,$01 ; reset to top of buffer
 130 
 131 wait2_000036: ; check if byte has been sent
 132 
 133 in r17,spsr
 134 sbrs r17,spif
 135 rjmp wait2_000036
 136 in r6,spdr ; recieve in left channel lsb
 137 out spdr,r5 ; send out right channel msb
 138 
 139 ;write left channel data to sram
 140 out portd,r24 ; set address
 141 sts porth,r25
 142 out portg,r22 ; pull ce low,we low,and set high bits of address
 143 ldi r17,$ff
 144 out ddra,r17 ; set porta as output for data write
 145 out ddrc,r17 ; set portc as output for data write
 146 out porta,r6 ; set data
 147 out portc,r7
 148 sbi portg,portg2 ; pull we high to write
 149 out ddra,r22 ; set porta as input for data lines
 150 out ddrc,r22 ; set portc as input for data lines
 151 
 152 wait3_000036: ; check if byte has been sent
 153 
 154 in r17,spsr
 155 sbrs r17,spif
 156 rjmp wait3_000036
 157 in r17,spdr ; recieve in right channel msb
 158 out spdr,r4 ; send out right channel lsb
 159 
 160 ;get sample 1 from sram
 161 out portd,r28 ; set address
 162 sts porth,r29
 163 nop ; wait required 2 cycle setup time
 164 nop
 165 in r4,pina ; get data
 166 in r5,pinc ; get data
 167 
 168 wait4_000036: ; check if byte has been sent
 169 
 170 in r17,spsr
 171 sbrs r17,spif
 172 rjmp wait4_000036
 173 in r17,spdr ; recieve in right channel lsb
 174 
 175 ;get sample 2 from other side of buffer
 176 movw r17:r16,r29:r28 ; move current position to temporary register
 177 movw r7:r6,r27:r26 ; move buffer size to temporary register
 178 lsr r7 ; divide buffer size by 2
 179 ror r6
 180 cp r16,r6 ; check if in lower or upper half of buffer
 181 cpc r17,r7
 182 brsh buffer_flip_000036 ; subtract half buffer if in upper half
 183 add r16,r6 ; add half buffer size if in lower half
 184 adc r17,r7
 185 rjmp getsample2_000036 ; continue
 186 
 187 buffer_flip_000036: ; adjust to opposite side of memory
 188 
 189 sub r16,r6 ; subtract half buffer size if in upper half
 190 sbc r17,r7
 191 
 192 getsample2_000036: ;get left channel sample 3 data from sram
 193 
 194 out portd,r16 ; set address
 195 sts porth,r17
 196 nop ; wait 2 cycle setup time
 197 nop
 198 in r2,pina ; get data
 199 in r3,pinc ; get data
 200 
 201 ;get distance to boundary
 202 movw r17:r16,r29:r28 ; move read address to temporary register
 203 sub r16,r24 ; find distance to loop boundary
 204 sbc r17,r25
 205 brcc half_000036 ; check if result is negative
 206 neg r16 ; invert distance if negative
 207 adc r17,r22 ; r22 is cleared above
 208 neg r17
 209 
 210 half_000036: ; check if result is greater than half the buffer size
 211 
 212 movw r7:r6,r27:r26 ; move buffer size to temporary register
 213 lsr r7 ; divide buffer size by 2
 214 ror r6
 215 cp r16,r6 ; check if result is greater than half the buffer size
 216 cpc r17,r7
 217 brlo scale_000036 ; skip flip if not
 218 sub r16,r26 ; flip result around boundary
 219 sbc r17,r27
 220 neg r16 ; invert distance
 221 adc r17,r22 ; r22 is cleared above
 222 neg r17
 223 
 224 scale_000036: ; scale distance to match buffer size - 50% accurate
 225 
 226 movw r7:r6,r27:r26 ; move buffer size to temporary register
 227 sbrc r7,$07 ; check if msb of buffer size is set
 228 rjmp attenuate_000036 ; attenuate signal if 16b value
 229 
 230 shift_000036: ; shift buffer size till it occupies full 16b
 231 
 232 lsl r6 ; multiply buffer size by 2
 233 rol r7
 234 lsl r16 ; multiply distance by 2
 235 rol r17
 236 sbrs r7,$07 ; check if msb of buffer size is set
 237 rjmp shift_000036 ; keep checking if not set
 238 
 239 attenuate_000036: ; multiply sample 1 by distance
 240 
 241 lsl r16 ; multiply distance by 2 since max value is 1/2 buffer size
 242 rol r17
 243 sub r6,r16 ; find complementary distance of sample 2
 244 sbc r7,r17
 245 movw r21:r20,r7:r6 ; move distance to signed multiply register
 246 movw r19:r18,r5:r4 ; move value to signed multiply register
 247 mulsu r19,r17 ; (signed)ah * bh
 248 movw r5:r4,r1:r0
 249 mul     r18,r16 ; al * bl
 250 movw r7:r6,r1:r0
 251 mulsu r19,r16 ; (signed)ah * bl
 252 sbc     r5,r22 ; r22 is cleared above
 253 add     r7,r0
 254 adc     r4,r1
 255 adc     r5,r22
 256 mul r17,r18 ; bh * al
 257 add     r7,r0
 258 adc     r4,r1
 259 adc     r5,r22
 260 
 261 ;multiply and accumulate opposing sample with result from above
 262 movw r19:r18,r3:r2 ; move value to signed multiply register
 263 mulsu r19,r21 ; (signed)ah * bh
 264 add     r4,r0
 265 adc     r5,r1
 266 mul     r18,r20 ; al * bl
 267 add     r6,r0
 268 adc     r7,r1
 269 adc     r4,r22
 270 adc     r5,r22
 271 mulsu r19,r20 ; (signed)ah * bl
 272 sbc     r5,r22
 273 add     r7,r0
 274 adc     r4,r1
 275 adc     r5,r22
 276 mul r21,r18 ; bh * al
 277 add     r7,r0
 278 adc     r4,r1
 279 adc     r5,r22
 280 
 281 ;check if buffer size is correct
 282 cp r26,r12 ; compare current delay to desired delay
 283 cpc r27,r13
 284 brlo upcount_000036 ; increment if smaller than
 285 breq adcsample_000036 ; do nothing if they are same size
 286 sbiw r27:r26,$02 ; decrement buffer size
 287 rjmp adcsample_000036 ; finish off
 288 
 289 upcount_000036: ; increment buffer size register
 290 
 291 adiw r27:r26,$02 ; increment buffer size
 292 
 293 adcsample_000036: ; get loop setting
 294 
 295 lds r17,adcsra ; get adc control register
 296 sbrs r17,adif ; check if adc conversion is complete
 297 rjmp done_000036 ; skip adc sampling
 298 lds r16,adcl ; get low byte adc value
 299 lds r17,adch ; get high byte adc value
 300 add r10,r16
 301 adc r11,r17 ; accumulate adc samples
 302 adc r9,r22 ; accumulate adc samples - r22 is cleared above
 303 ldi r17,$f7
 304 sts adcsra,r17 ; clear interrupt flag
 305 dec r15 ; countdown adc sample clock
 306 brne done_000036 ; move adc value to loop setting after 256 samples
 307 lsr r9 ; divide accumulated value by 4
 308 ror r11
 309 ror r10
 310 lsr r9
 311 ror r11
 312 ror r10
 313 ldi r16,low(buffer_min_000036) ; load minimum buffer size
 314 ldi r17,high(buffer_min_000036)
 315 cp r10,r16 ; check if less than minimum
 316 cpc r11,r17
 317 brsh compare_000036 ; compare to previous value if above min
 318 movw r11:r10,r17:r16 ; set buffer size to minimum
 319 
 320 compare_000036: ; compare to previous value
 321 
 322 movw r17:r16,r13:r12 ; make a copy of current loop time for comparison
 323 sub r16,r10 ; find difference between current loop time and last loop time
 324 sbc r17,r11
 325 brcc deadband_000036 ; see if difference is large enough to indicate a change
 326 neg r16 ; invert difference if negative
 327 adc r17,r22 ; r22 is cleared above
 328 neg r17
 329 
 330 deadband_000036: ; see if pot has moved or if its just noise
 331 
 332 cpi r16,$40 ; see if difference is greater than 1 lsb
 333 cpc r17,r22 ; r22 is cleared above
 334 brlo nochange_000036 ; dont update loop time if difference is not large enough
 335 ldi r16,$fe ; make sure buffer size is even
 336 and r10,r16
 337 movw r13:r12,r11:r10 ; move adc value to loop time register
 338 
 339 nochange_000036: ; clear accumulation registers
 340 
 341 clr r10 ; empty accumulation registers
 342 clr r11
 343 clr r9
 344 
 345 ;check rotary switch state
 346 lds r16,pinj ; get switch data
 347 andi r16,$78 ; mask off rotary switch
 348 lsr r16 ; adjust switch position to program memory location
 349 lsr r16
 350 ldi r17,$02
 351 add r16,r17
 352 cpse r16,r31 ; check if location has changed
 353 clr r30 ; reset jump register to intial state
 354 mov r31,r16
 355 
 356 done_000036:
 357 
 358 reti ; return to waiting
 359