welcome: please sign in
location: Diff for "Flanger"
Differences between revisions 4 and 6 (spanning 2 versions)
Revision 4 as of 2010-08-13 09:05:40
Size: 14616
Editor: guest
Comment:
Revision 6 as of 2010-08-21 02:08:43
Size: 14639
Editor: guest
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
=== MICrODEC: Stock Functions ===
Line 7: Line 9:
Both the depth of the low frequency oscillator (LFO), and the offset delay time can be modified to change the quality of the sound. Unfortunately, there is only one other knob on the MICrODEC (although you could add more), so we had to cram them both onto that knob. By pressing the pushbutton on MOD2, the functionality of the rotary encoder on MOD2 changes. In one state, it modifies the LFO depth, with rotations to the right increasing its amplitude (from 0ms to 6ms). In the other state, it modifies the offset delay, with rotations to the right increasing the delay time (from 0ms to 24ms). In both cases, the rotary encoder sets an 8b value, so it takes quite a few turns to go from one end to the other, although this also gives finer resolution. Both the depth of the low frequency oscillator (LFO), and the offset delay time can be modified to change the quality of the sound. Since there is only one other built-in knob on the MICrODEC we crammed both functions onto MOD2. By pressing the pushbutton on MOD2, the functionality of the rotary encoder on MOD2 changes. In one state, it modifies the LFO depth, with rotations to the right increasing its amplitude (from 0ms to 6ms). In the other state, it modifies the offset delay, with rotations to the right increasing the delay time (from 0ms to 24ms). In both cases, the rotary encoder changes an 8b value in increments of 1, so it takes quite a few turns to go from one end to the other, although this also gives finer resolution.  

MICrODEC: Stock Functions

Flanger

This function implements a flanger. It is mono, taking in data from the left channel, and presenting the mono output on both the left and right channels.

Flanging is based on analog tape and reel recording techniques, where slight pressure is applied to the flange of the reel, slowing down the playback. This creates a time-varying delayed signal, which, when mixed with the original signal, has a swept filter effect. Any time-varying signal can be used, but in this case, we are using a sinusoidal signal, as it introduces the least amount of added frequency content (ramp waves are also common). This sinusoid is created by taking values from a look-up table stored in program memory. The frequency of the sinusoid is set by the pot (MOD1), and is variable from .084Hz to 10.84Hz.

Both the depth of the low frequency oscillator (LFO), and the offset delay time can be modified to change the quality of the sound. Since there is only one other built-in knob on the MICrODEC we crammed both functions onto MOD2. By pressing the pushbutton on MOD2, the functionality of the rotary encoder on MOD2 changes. In one state, it modifies the LFO depth, with rotations to the right increasing its amplitude (from 0ms to 6ms). In the other state, it modifies the offset delay, with rotations to the right increasing the delay time (from 0ms to 24ms). In both cases, the rotary encoder changes an 8b value in increments of 1, so it takes quite a few turns to go from one end to the other, although this also gives finer resolution.

Try the flanger with large LFO depth and some feedback, and you'll get what we've started calling "drunken sailor mode". It will make your guitar strings sound like rubber bands. And apologies for the awful user interface, this one is a good candidate for an extra mod knob, or MIDI control.

flanger_sine.asm


   1 ; program: flanger-16b-sine.asm
   2 ; UID = 000045 - unique id to eliminate conflicts between variables
   3 ; 16b address space
   4 ; mono data in on left channel, mono data out on both channels
   5 ; pot (MOD1) controls lfo frequency
   6 ; rotary encoder (MOD2) controls lfo depth and offset delay
   7 ; pushbutton (on MOD2) controls wether depth or offset is being modded
   8 ; offset delay variable from 90us to 23ms
   9 
  10 ; program overview
  11 ;
  12 ; data is read in from the codec and stored to sram.  data is read out of
  13 ; sram at a variable delay set by an lfo and an offset.  the lfo is
  14 ; generated from a 16b/512s half sinewave lookup table.  this is incremented
  15 ; with a 24b number to get very low frequencies.  the lfo rate is set via
  16 ; the adc, which is oversampled 256 times and deadbanded to get rid of
  17 ; glitches.  the lfo depth is created by multiplying the lfo signal with
  18 ; an 8b depth value, which is set via the rotary encoder.  the offset delay
  19 ; is an 8b value, which is also set with the rotary encoder, with the
  20 ; pushbutton selecting which function is currently active.
  21 
  22 ; register usage - may be redefined in other sections
  23 ;
  24 ; r0  multiply result lsb
  25 ; r1  multiply result msb
  26 ; r2  accumulation lsb
  27 ; r3  accumulation mlb
  28 ; r4  right/left lsb out/accumulation mhb
  29 ; r5  right/left msb out/accumulation msb
  30 ; r6  pushbutton state register
  31 ; r7  
  32 ; r8  adc accumulator fractional byte
  33 ; r9  adc accumulator lsb
  34 ; r10 adc accumulator msb
  35 ; r11 rotary encoder counter
  36 ; r12 lfo rate lsb
  37 ; r13 lfo rate msb
  38 ; r14 null register
  39 ; r15 switch sample counter
  40 ; r16 temporary swap register
  41 ; r17 temporary swap register
  42 ; r18 sine wave buffer/multiply msb
  43 ; r19 sine wave buffer/multiply msb
  44 ; r20 multiply swap register
  45 ; r21 multiply swap register
  46 ; r22 sinetable lookup address lsb
  47 ; r23 sinetable lookup address mlb
  48 ; r24 write address lsb
  49 ; r25 write address msb
  50 ; r26 sinetable lookup address mhb
  51 ; r27 sinetable lookup address msb
  52 ; r28 delay offset
  53 ; r29 lfo depth
  54 ; r30 jump location for interrupt lsb
  55 ; r31 jump location for interrupt msb
  56 ; t   rotary encoder edge detect indicator
  57 
  58 ; program starts here first time
  59 ; intialize registers
  60 ldi r30,$05 ; increment z pointer to new jump location
  61 clr r14 ; clear null register
  62 ldi r28,$09 ; initialize delay offset
  63 ldi r29,$06 ; intiialize lfo depth
  64 reti ; finish with initialization and wait for next interrupt
  65 
  66 ; program starts here every time but first
  67 ; initiate data transfer to codec
  68 sbi portb,portb0 ; toggle slave select pin
  69 out spdr,r5 ; send out left channel msb
  70 cbi portb,portb0
  71 
  72 adiw r25:r24,$01 ; increment write address
  73 
  74 wait1_000045: ; check if byte has been sent
  75 
  76 in r17,spsr
  77 sbrs r17,spif
  78 rjmp wait1_000045
  79 in r19,spdr ; recieve in left channel msb
  80 out spdr,r4 ; send out left channel lsb
  81 
  82 wait2_000045: ; check if byte has been sent
  83 
  84 in r17,spsr
  85 sbrs r17,spif
  86 rjmp wait2_000045
  87 in r18,spdr ; recieve in left channel lsb
  88 out spdr,r5 ; send out right channel msb
  89 
  90 ;write left channel data to sram
  91 out portd,r24 ; set address
  92 sts porth,r25
  93 out portg,r14 ; pull ce low,we low,and set high bits of address
  94 ldi r17,$ff
  95 out ddra,r17 ; set porta as output for data write
  96 out ddrc,r17 ; set portc as output for data write
  97 out porta,r18 ; set data
  98 out portc,r19
  99 sbi portg,portg2 ; pull we high to write
 100 out ddra,r14 ; set porta as input for data lines
 101 out ddrc,r14 ; set portc as input for data lines
 102 
 103 wait3_000045: ; check if byte has been sent
 104 
 105 in r17,spsr
 106 sbrs r17,spif
 107 rjmp wait3_000045
 108 in r17,spdr ; recieve in right channel msb
 109 out spdr,r4 ; send out right channel lsb
 110 
 111 wait4_000045: ; check if byte has been sent
 112 
 113 in r17,spsr
 114 sbrs r17,spif
 115 rjmp wait4_000045
 116 in r17,spdr ; recieve in right channel lsb
 117 
 118 ; vco generation
 119 movw r17:r16,r31:r30 ; store z register
 120 ;get sample 1
 121 add r22,r12 ; increment sinetable address
 122 adc r23,r13
 123 adc r26,r14 ; r14 is cleared above
 124 adc r27,r14
 125 movw r31:r30,r27:r26 ; move to z register for data fetch
 126 lsl r30 ; adjust pointer for 16b fetch
 127 rol r31
 128 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
 129 ori r31,$48 ; set to memory address location where table is stored
 130 lpm r18,z+ ; get sine value lsb, increment z register
 131 lpm r19,z ; get sine value msb
 132 sbrc r27,$01 ; flip sign for half of the values
 133 rjmp interpolate_000045
 134 neg r18
 135 adc r19,r14 ; r14 is cleared above
 136 neg r19
 137 
 138 interpolate_000045: ; multiply sample 1 by distance
 139 
 140 movw r21:r20,r23:r22 ; get distance from sample 1
 141 com r20 ; invert distance
 142 com r21
 143 mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
 144 movw r5:r4,r1:r0 ; store high bytes result for later
 145 mul     r18,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
 146 movw r3:r2,r1:r0 ; store low byets for later
 147 mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
 148 sbc     r5,r14 ; r14 is cleared above - subtract sign bit
 149 add     r3,r0 ; accumulate result
 150 adc     r4,r1
 151 adc     r5,r14 ; r14 is cleared above
 152 mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
 153 add     r3,r0 ; accumulate result
 154 adc     r4,r1
 155 adc     r5,r14 ; r14 is cleared above
 156 
 157 ;get sample 2
 158 adiw r27:r26,$01 ; set to next sample
 159 movw r31:r30,r27:r26 ; move to z register for data fetch
 160 lsl r30 ; adjust pointer for 16b fetch
 161 rol r31
 162 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
 163 ori r31,$48 ; set to memory address location where table is stored
 164 lpm r18,z+ ; get sine value lsb, increment z register
 165 lpm r19,z ; get sine value msb
 166 sbrc r27,$01 ; flip sign for half of the values
 167 rjmp interpolate1_000045
 168 neg r18
 169 adc r19,r14 ; r14 is cleared above
 170 neg r19
 171 
 172 interpolate1_000045: ; multiply sample 2 by distance
 173 
 174 sbiw r27:r26,$01 ; reset address
 175 movw r31:r30,r17:r16 ; restore z register
 176 mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
 177 add r4,r0 ; accumulate result
 178 adc r5,r1
 179 mul     r18,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
 180 add r2,r0 ; accumulate result
 181 adc r3,r1
 182 adc r4,r14 ; r14 is cleared above
 183 adc r5,r14
 184 mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
 185 sbc     r5,r14 ; r14 is cleared above - subtract sign bit
 186 add     r3,r0 ; accumulate result
 187 adc     r4,r1
 188 adc     r5,r14 ; r14 is cleared above
 189 mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
 190 add     r3,r0 ; accumulate result
 191 adc     r4,r1
 192 adc     r5,r14 ; r14 is cleared above
 193 
 194 ;set lfo depth - 8b value
 195 ldi r16,$80 ; convert lfo to unsigned number
 196 add r5,r16
 197 movw r19:r18,r5:r4 ; move lfo signal to multiply register
 198 mov r21,r29 ; move lfo depth to multiply register
 199 mul r19,r21 ; (unsigned)ah * (unsigned)b
 200 movw r5:r4,r1:r0
 201 mul r21,r18 ; (unsigned)b * (unsigned)al
 202 add     r4,r1
 203 adc     r5,r14 ; r14 is cleared above
 204 
 205 ;add lfo to delay
 206 movw r17:r16,r25:r24 ; move current location to read address
 207 mov r20,r28 ; move delay offset to temporary register
 208 clr r21 ; clear temporary high byte
 209 lsl r20 ; multiply delay time by 4
 210 rol r21
 211 lsl r20
 212 rol r21
 213 sub r16,r20 ; remove delay offset
 214 sbc r17,r21
 215 sec ; set the carry bit so all values are reduced by 1 lsb
 216 sbc r16,r5 ; remove lfo time
 217 sbc r17,r14 ; r14 is cleared above
 218 
 219 ;get left channel sample 1 from sram
 220 out portd,r16 ; set address
 221 sts porth,r17
 222 nop ; wait setup period of two cycles
 223 nop
 224 in r18,pina ; get data
 225 in r19,pinc ; get data
 226 
 227 ;multiply sample 1 by distance
 228 mov r20,r4 ; get distance from sample 1
 229 mulsu r19,r20 ; (signed)ah * b
 230 movw r5:r4,r1:r0
 231 mul r18,r20 ; al * b
 232 add r4,r1
 233 adc r5,r14 ; r14 is cleared above
 234 mov r3,r0
 235 
 236 ;get left channel sample 2 from sram
 237 subi r16,$ff ; set to next sample
 238 sbci r17,$ff ; done this way because there is no addi or adci
 239 out portd,r16 ; set address
 240 sts porth,r17
 241 nop ; wait setup period of two cycles
 242 nop
 243 in r18,pina ; get data
 244 in r19,pinc ; get data
 245 
 246 ;multiply sample 2 by distance
 247 com r20 ; get distance to sample 2
 248 mulsu r19,r20 ; (signed)ah * b
 249 add r4,r0 ; accumulate result
 250 adc r5,r1
 251 mul r18,r20 ; al * b
 252 add r3,r0 ; accumulate result
 253 add r4,r1
 254 adc r5,r14 ; r14 is cleared above
 255 
 256 ;check rotary encoder and adjust lfo depth
 257 ; although rotary encoder is externally debounced, it is done here again.
 258 ; pin1 is sampled on a transition from high to low on pin0.  if pin1 is
 259 ; high, a left turn occured, if pin1 is low, a right turn occured.
 260 dec r11 ; check if time to sample rotary encoder
 261 brne adcsample_000045 ; continue if not
 262 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
 263 mov r11,r17
 264 lds r17,pinj ; get encoder/pushbutton data
 265 mov r16,r17 ; store pushbutton data to temporary register
 266 eor r16,r6 ; check if pushbutton changed value
 267 and r16,r17 ; check if rising edge
 268 sbrc r16,$02 ; continue if not
 269 rjmp pushbutton_000045 ; go to pushbutton routine if pressed
 270 ldi r20,$80 ; mask off the high bit of the pushbutton state register
 271 and r6,r20 ; pinj7 should be 0, as there is no i/o attached to it
 272 or r6,r17 ; store pushbutton data for next sample comparison
 273 sbrs r17,$02 ; check if pushbutton pressed
 274 rjmp adcsample_000045 ; do not allow any parameter change while button pressed
 275 sbrs r17,$00 ; check if pin0 is low
 276 rjmp edge_000045 ; check if pin0 was low on previous sample
 277 clt ;  clear state register if back high
 278 rjmp adcsample_000045 ; finish off
 279 
 280 edge_000045: ; check for falling edge
 281 
 282 brts adcsample_000045 ; do nothing if edge was already detected
 283 set ; set t register to indicate edge detected
 284 ldi r21,$01 ; prepare for addition or subtraction
 285 sbrs r6,$07 ; check which function is being modded
 286 rjmp lfo_000045 ; do lfo function
 287 sbrs r17,$01 ; check if pin1 is high
 288 rjmp increment_000045 ; increment desired delay if right rotation
 289 dec r28 ; decrement delay register else
 290 cp r28,r21 ; r21 set to $01 above
 291 brsh adcsample_000045 ; continue if not
 292 mov r28,r16 ; set delay to min
 293 rjmp adcsample_000045 ; finish off
 294 
 295 increment_000045: ; increment desired delay register
 296 
 297 add r28,r21 ; increment delay register
 298 brcc adcsample_000045 ; check if overflow occured
 299 ser r28 ; set delay to max
 300 
 301 adcsample_000045: ; sample adc for lfo rate
 302 
 303 lds r17,adcsra ; get adc control register
 304 sbrs r17,adif ; check if adc conversion is complete
 305 rjmp done_000045 ; skip adc sampling
 306 lds r16,adcl ; get low byte adc value
 307 lds r17,adch ; get high byte adc value
 308 add r8,r16 ; accumulate adc samples
 309 adc r9,r17
 310 adc r10,r14 ; r14 is cleared above
 311 ldi r17,$f7
 312 sts adcsra,r17 ; clear interrupt flag
 313 dec r15 ; countdown adc sample clock
 314 brne done_000045 ; get delay time if its been long enough
 315 
 316 deadband_000045: ; set the low value of the delay
 317 
 318 lsr r10 ; divide adc value by 16
 319 ror r9
 320 ror r8
 321 lsr r10
 322 ror r9
 323 ror r8
 324 lsr r9 ; r10 is now empty
 325 ror r8
 326 lsr r9
 327 ror r8
 328 movw r17:r16,r9:r8 ; move adc sample to temporary register
 329 ldi r21,$80 ; add in offset of min lfo rate ($0080)
 330 add r16,r21
 331 adc r17,r14 ; r14 is cleared above
 332 sub r16,r12 ; find difference between adc sample and current lfo rate
 333 sbc r17,r13
 334 brsh check_000045 ; check for deadband if positive
 335 neg r16 ; invert if negative
 336 adc r17,r14 ; r14 is cleared above
 337 neg r17
 338 
 339 check_000045: ; check if difference is greater than deadband
 340 
 341 cpi r16,$10 ; check if difference is less than 1 adc lsb
 342 cpc r17,r14 ; r14 cleared above
 343 brlo empty_000045 ; do nothing if less than 1 adc lsb
 344 movw r13:r12,r9:r8 ; move adc sample to lfo rate register
 345 add r12,r21 ; add in offset of min lfo rate ($0080)
 346 adc r13,r14 ; r14 is cleared above
 347 
 348 empty_000045: ; empty accumulation registers and finish off
 349 
 350 clr r8 ; empty accumulation registers
 351 clr r9
 352 clr r10
 353 
 354 switchsample_000045: ; check rotary switch
 355 
 356 lds r16,pinj ; get switch data
 357 andi r16,$78 ; mask off rotary switch
 358 lsr r16 ; adjust switch position to program memory location
 359 lsr r16
 360 ldi r17,$02
 361 add r16,r17
 362 cpse r16,r31 ; check if location has changed
 363 clr r30 ; reset jump register to intial state
 364 mov r31,r16
 365 
 366 done_000045:
 367 
 368 reti ; return to waiting
 369 
 370 lfo_000045: ; modify lfo depth parameter
 371 
 372 sbrs r17,$01 ; check if pin1 is high
 373 rjmp increment1_000045 ; increment desired delay if right rotation
 374 sub r29,r21 ; decrement lfo depth register else
 375 brcc adcsample_000045 ; check if underflow
 376 clr r29 ; set depth to min
 377 rjmp adcsample_000045 ; finish off
 378 
 379 increment1_000045: ; increment desired delay register
 380 
 381 add r29,r21 ; increment lfo depth register
 382 brcc adcsample_000045 ; check if overflow occured
 383 ser r29 ; set depth to max
 384 rjmp adcsample_000045 ; finish off
 385 
 386 pushbutton_000045: ; edge detect
 387 
 388 ldi r20,$80 ; toggle msb to inidicate function change
 389 eor r6,r20
 390 and r6,r20 ; mask off the high bit
 391 or r6,r17 ; store pushbutton data for next sample comparison
 392 rjmp adcsample_000045 ; finish off
 393 

Flanger (last edited 2010-08-21 02:08:43 by guest)