DelayPingPong

MICrODEC: Stock Functions

3s Stereo Ping-Pong Delay

This program performs a ping-pong delay routine. It stores the incoming data from the codec, and plays back a delayed sample, but reverses the orientation of the channels on playback. In this way, the audio coming in on the left channel comes out on the right after a delay. With a bit of feedback, the signals chase each other from left to right. The input data is stereo, and is taken from the left and right channels. The output is also stereo, and is presented on both the left and right channels.

The pot (MOD1) controls the delay time. It goes from 3ms to 3s. MOD2 does nothing for this program. Most analog delay pedals change the rate at which data is being clocked in and out, to change the delay time. This gives a smooth change in audio when the delay is varied, although it also changes the frequency resolution of the signal. To change the delay time in the MICrODEC, the easiest way is to just jump to the new delay time, although this gives an audible pop as the data abruptly switches. In this program, we increment the delay time (or decrement it) at a rate of one sample per sample, until it matches what you want it to be. This gives the effect of frequency doubling (or reversing) during delay transitions, which can be interesting sonically.

delay_ping_pong.asm


   1 ; program: ping_pong-18b-pot.asm
   2 ; UID = 000030 - unique id to eliminate conflicts between variables
   3 ; 18b address space (3s delay time)
   4 ; stereo data - left and right swap to create ping-pong effect
   5 ; pot (MOD1) controlled delay time (3ms - 3s)
   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.  ADC0 (MOD1) is read
  11 ; and averaged over 256 samples to reduce jitter.  this value is subtracted
  12 ; from the write address to create the desired read address.  if the actual
  13 ; read address doesnt match the desired read address, it is either
  14 ; incremented or decremented by one sample each sample period until it
  15 ; matches.  this reduces noise during delay time transitions.  the left
  16 ; channel input is recorded to the right channel output buffer (and vice
  17 ; versa), so the sound bounces back and forth between the two channels.
  18 
  19 ; register usage - may be redefined in other sections
  20 ;
  21 ; r0  deisred delay time fractional byte
  22 ; r1  
  23 ; r2  left lsb out
  24 ; r3  left msb out
  25 ; r4  right lsb out
  26 ; r5  right msb out
  27 ; r6  left lsb in
  28 ; r7  left msb in
  29 ; r8  right lsb in
  30 ; r9  right msb in
  31 ; r10 adc accumulator lsb
  32 ; r11 adc accumulator msb
  33 ; r12 actual delay lsb
  34 ; r13 actual delay msb
  35 ; r14 adc sample counter
  36 ; r15 switch sample counter
  37 ; r16 temporary swap register
  38 ; r17 temporary swap register
  39 ; r18 null register
  40 ; r19 adc accumulator fractional byte
  41 ; r20 temporary swap register
  42 ; r21 actual delay fractional byte
  43 ; r22 write address third byte
  44 ; r23 read address third byte
  45 ; r24 write address lsb
  46 ; r25 write address msb
  47 ; r26 desired delay lsb
  48 ; r27 desired delay msb
  49 ; r28 read address lsb
  50 ; r29 read address msb
  51 ; r30 jump location for interrupt lsb
  52 ; r31 jump location for interrupt msb
  53 ; t   
  54 
  55 ;program starts here first time
  56 ldi r30,$25 ; set jump location to program start
  57 clr r24 ; clear write register
  58 clr r25
  59 ldi r22,$00 ; setup write address high byte
  60 clr r18 ; setup r18 as null register for carry addition and ddr setting
  61 ldi r17,$ff ; setup r17 for ddr setting
  62 
  63 clear_000030: ; clear delay buffer
  64 ; eliminates static when first switching to the delay setting
  65 
  66 adiw r25:r24,$01 ; increment write register
  67 adc r22,r18 ; increment write third byte
  68 cpi r22,$04 ; check if full memory space has been cleared
  69 breq cleardone_000030 ; continue until end of buffer reached
  70 out portd,r24 ; set address
  71 sts porth,r25
  72 out portg,r22 ; pull ce low,we low,and set high bits of address
  73 out ddra,r17 ; set porta as output for data write
  74 out ddrc,r17 ; set portc as output for data write
  75 out porta,r18 ; set data
  76 out portc,r18 ; r18 is cleared above
  77 sbi portg,portg2 ; pull we high to write
  78 out ddra,r18 ; set porta as input for data lines
  79 out ddrc,r18 ; set portc as input for data lines
  80 rjmp clear_000030 ; continue clearing
  81 
  82 cleardone_000030: ; reset registers
  83 
  84 clr r24 ; clear write register
  85 clr r25
  86 ldi r22,$00 ; setup write address high byte
  87 clr r28 ; set read address to minimum delay
  88 ldi r29,$ff
  89 ldi r23,$07 ; setup read address high byte
  90 clr r21 ; set actual delay time to minimum delay
  91 ldi r16,$01
  92 mov r12,r16
  93 clr r13
  94 clr r2 ; initialize data output registers
  95 clr r3
  96 clr r4
  97 clr r5
  98 reti ; finish with initialization and wait for next interrupt
  99 
 100 ; program starts here every time but first
 101 ; initiate data transfer to codec
 102 sbi portb,portb0 ; toggle slave select pin
 103 out spdr,r3 ; send out left channel msb
 104 cbi portb,portb0
 105 
 106 ;increment sram addresses
 107 adiw r25:r24,$01 ; increment write address
 108 adc r22,r18 ; increment write third byte
 109 andi r22,$03 ; mask off unsed bits
 110 adiw r29:r28,$01 ; increment read address
 111 adc r23,r18 ; increment read third byte
 112 andi r23,$03 ; mask off unsed bits
 113 ori r23,$04 ; set we bit for reading
 114 
 115 wait1_000030: ; check if byte has been sent
 116 
 117 in r17,spsr
 118 sbrs r17,spif
 119 rjmp wait1_000030
 120 in r7,spdr ; recieve in left channel msb
 121 out spdr,r2 ; send out left channel lsb
 122 
 123 ;get left channel data from sram
 124 out portg,r23 ; pull ce low, we high, and set high bits of register
 125 out portd,r28 ; set address
 126 sts porth,r29
 127 adiw r29:r28,$01 ; increment read address
 128 adc r23,r18 ; placed here to use setup time efficiently
 129 andi r23,$03 ; mask off unsed bits
 130 ori r23,$04 ; set we bit for reading
 131 in r2,pina ; get data
 132 in r3,pinc ; get data
 133 
 134 wait2_000030: ; check if byte has been sent
 135 
 136 in r17,spsr
 137 sbrs r17,spif
 138 rjmp wait2_000030
 139 in r6,spdr ; recieve in left channel lsb
 140 out spdr,r5 ; send out right channel msb
 141 
 142 ;write left channel data to sram
 143 out portd,r24 ; set address
 144 sts porth,r25
 145 out portg,r22 ; pull ce low,we low,and set high bits of address
 146 ldi r17,$ff
 147 out ddra,r17 ; set porta as output for data write
 148 out ddrc,r17 ; set portc as output for data write
 149 out porta,r6 ; set data
 150 out portc,r7
 151 sbi portg,portg2 ; pull we high to write
 152 out ddra,r18 ; set porta as input for data lines
 153 out ddrc,r18 ; set portc as input for data lines
 154 
 155 wait3_000030: ; check if byte has been sent
 156 
 157 in r17,spsr
 158 sbrs r17,spif
 159 rjmp wait3_000030
 160 in r9,spdr ; recieve in right channel msb
 161 out spdr,r4 ; send out right channel lsb
 162 
 163 ;get right channel data from sram
 164 out portg,r23 ; pull ce low,we high,oe low, and set high bits of address
 165 out portd,r28 ; set address
 166 sts porth,r29
 167 adiw r25:r24,$01 ; increment write address
 168 adc r22,r18 ; placed here for efficient use of setup time
 169 andi r22,$03 ; mask off unsed bits
 170 in r4,pina ; get data
 171 in r5,pinc ; get data
 172 
 173 wait4_000030: ; check if byte has been sent
 174 
 175 in r17,spsr
 176 sbrs r17,spif
 177 rjmp wait4_000030
 178 in r8,spdr ; recieve in left channel lsb
 179 
 180 ;write right channel data to sram
 181 out portd,r24 ; set address
 182 sts porth,r25
 183 out portg,r22 ; pull ce low,we low,and set high bits of address
 184 ldi r17,$ff
 185 out ddra,r17 ; set porta as output for data write
 186 out ddrc,r17 ; set portc as output for data write
 187 out porta,r8 ; set data
 188 out portc,r9
 189 sbi portg,portg2 ; pull we high to write
 190 out ddra,r18 ; set porta as input for data lines
 191 out ddrc,r18 ; set portc as input for data lines
 192 
 193 ; get delay settings
 194 lds r17,adcsra ; get adc control register
 195 sbrs r17,adif ; check if adc conversion is complete
 196 rjmp shift_000030 ; skip adc sampling
 197 lds r16,adcl ; get low byte adc value
 198 lds r17,adch ; get high byte adc value
 199 add r19,r16 ; accumulate adc samples
 200 adc r10,r17
 201 adc r11,r18 ; r18 is cleared above
 202 ldi r17,$f7
 203 sts adcsra,r17 ; clear interrupt flag
 204 dec r14 ; countdown adc sample clock
 205 brne shift_000030 ; get delay time if its been long enough
 206 ldi r17,$01 ; check if adc value is less than $000100
 207 cp r19,r18 ; r18 is cleared above
 208 cpc r10,r17
 209 cpc r11,r18
 210 brsh deadband_000030 ; check if adc value changed enough to update delay
 211 inc r10 ; set minimum delay to $000100 = 3ms
 212 clr r19
 213 
 214 deadband_000030: ; check for change in adc value
 215 
 216 movw r17:r16,r11:r10 ; move adc sample to temporary register
 217 mov r20,r19
 218 sub r20,r0 ; find difference between adc sample and desired delay time
 219 sbc r16,r26
 220 sbc r17,r27
 221 brsh check_000030 ; check for deadband if positive
 222 com r20 ; invert if negative
 223 com r16 ; using ones complement as it is faster, and only has 1 bit error
 224 com r17
 225 
 226 check_000030: ; check if difference is greater than deadband
 227 
 228 cpi r16,$01 ; check if difference is less than 1 lsb
 229 cpc r17,r18 ; r18 cleared above
 230 brlo empty_000030 ; do nothing if less than 1 lsb
 231 movw r27:r26,r11:r10 ; move adc sample to delay time if large enough change
 232 andi r19,$fc ; make sure delay time is a multiple of 4
 233 mov r0,r19
 234 
 235 empty_000030: ; empty accumulation registers and finish off
 236 
 237 clr r10 ; empty accumulation registers
 238 clr r11
 239 clr r19
 240 
 241 shift_000030: ; check if delay time is correct
 242 
 243 cp r0,r21 ; compare desired delay to actual delay
 244 cpc r26,r12
 245 cpc r27,r13
 246 breq switchsample_000030 ; do nothing if the same
 247 brlo indexdown_000030
 248 ldi r17,$04 ; increment delay register
 249 add r21,r17
 250 adc r12,r18 ; r18 is cleared above
 251 adc r13,r18
 252 ldi r17,$03
 253 and r13,r17 ; mask off unused bits
 254 rjmp switchsample_000030
 255 
 256 indexdown_000030:
 257 
 258 subi r21,$02 ; decrement delay register
 259 sbc r12,r18 ; r18 is cleared above
 260 sbc r13,r18
 261 ldi r17,$03
 262 and r13,r17 ; mask off unused bits
 263 
 264 switchsample_000030: ; check state of rotary switch
 265 
 266 dec r15
 267 brne done_000030
 268 lds r16,pinj ; get switch data
 269 andi r16,$78 ; mask off rotary switch
 270 lsr r16 ; adjust switch position to program memory location
 271 lsr r16
 272 ldi r17,$02
 273 add r16,r17 ; move to jump register
 274 cp r16,r31 ; check if location has changed
 275 breq done_000030 ; finish off if no change
 276 clr r30 ; reset jump register to intial state
 277 mov r31,r16
 278 
 279 done_000030:
 280 
 281 movw r29:r28,r25:r24 ; move write address to read destination register
 282 mov r23,r22 ; move write third byte to read third byte
 283 sub r28,r21 ; subtract delay time
 284 sbc r29,r12
 285 sbc r23,r13
 286 andi r23,$03 ; mask off unsed bits
 287 ori r23,$04 ; set we bit for reading
 288 movw r17:r16,r3:r2 ; swap left and right channels
 289 movw r3:r2,r5:r4
 290 movw r5:r4,r17:r16
 291 reti
 292 

DelayPingPong (last edited 2010-08-21 02:05:32 by guest)