welcome: please sign in

Upload page content

You can upload content for the page named below. If you change the page name, you can also upload content for another page. If the page name is empty, we derive the page name from the file name.

File to load page content from
Page name
Comment
What is the opposite of 'day'?

location: TremoloStereo

MICrODEC: Stock Functions

Stereo Tremolo

This function implements a tremolo, and is stereo in and out. The pot (MOD1) controls the LFO depth, and the pushbutton on the rotary encoder (MOD2) changes the phasing between the left and right channels.

Tremolo is a simple amplitude modulation of the input signal, so it becomes louder and quieter at a regular rate. This differs from ring-modulation in that the polarity of the input signal is always preserved. With ring-modulation, since it is a true multiplication, you can have a negative input signal inverting the polarity for some period of time. This function uses a sinusoid from a lookup table to vary the amplitude of the input signal. This LFO can be varied from .17Hz to 43Hz with the pot (MOD1), and pressing the pushbutton on MOD2 changes whether the left and right signals change amplitude together, or alternate, so as one is increasing in volume, the other is decreasing (panning from left to right).

To decrease the amount of effect, mix in some of the original signal with the MIX knob.

tremolo_stereo.asm


   1 ; program: tremolo-stereo.asm
   2 ; UID = 000057 - unique id to eliminate conflicts between variables
   3 ; memory is not used
   4 ; stereo in and out
   5 ; pot (MOD1) controls the lfo frequency
   6 ; pushbutton (on MOD2) controls phase between left and right output
   7 
   8 ; program overview
   9 ;
  10 ; data is read in from the codec, and multiplied by an lfo that is internally
  11 ; generated using a 512s/16b sinewave lookup table.  the frequency of this
  12 ; lfo is determined by the adc input, which is oversampled 256 times and
  13 ; compared to the previous value with a deadband.  this helps reduce jitter.
  14 ; the left and right channels are mutiplied by opposite signals, so the
  15 ; sound bounces back and forth between left and right.  depressing the
  16 ; pushbutton (on MOD2) changes this so that the left and right are in phase.
  17 
  18 ; register usage - may be redefined in other sections
  19 ;
  20 ; r0  multiply result lsb
  21 ; r1  multiply result msb
  22 ; r2  accumulation lsb
  23 ; r3  accumulation mlb
  24 ; r4  left lsb out/accumulation mhb
  25 ; r5  left msb out/accumulation msb
  26 ; r6  right in/out lsb
  27 ; r7  right in/out msb
  28 ; r8  adc accumulator fractional byte
  29 ; r9  adc accumulator lsb
  30 ; r10 adc accumulator msb
  31 ; r11 lfo control signal fractional byte
  32 ; r12 lfo control signal lsb
  33 ; r13 lfo control signal msb
  34 ; r14 null register
  35 ; r15 switch/adc sample counter
  36 ; r16 temporary swap register
  37 ; r17 temporary swap register
  38 ; r18 sine wave buffer/multiply lsb
  39 ; r19 sine wave buffer/multiply msb
  40 ; r20 multiply swap register
  41 ; r21 multiply swap register
  42 ; r22 sinetable lookup address lsb
  43 ; r23 sinetable lookup address mlb
  44 ; r24 left data in lsb
  45 ; r25 left data in msb
  46 ; r26 sinetable lookup address mhb
  47 ; r27 sinetable lookup address msb
  48 ; r28 phase indicator
  49 ; r29 
  50 ; r30 jump location for interrupt lsb
  51 ; r31 jump location for interrupt msb
  52 ; t   pushbutton state indicator
  53 
  54 ;program starts here first time
  55 ; intialize registers
  56 ldi r30,$07 ; increment z pointer to new jump location
  57 clr r14 ; clear null register
  58 ldi r16,$10 ; initialize lfo rate
  59 ldi r17,$00
  60 movw r13:r12,r17:r16 ;  move to lfo rate register
  61 clr r28 ; initialize phase mode register
  62 reti ; finish with initialization and wait for next interrupt
  63 
  64 ;program starts here every time but first
  65 ; initiate data transfer to codec
  66 sbi portb,portb0 ; toggle slave select pin
  67 out spdr,r5 ; send out left channel msb
  68 cbi portb,portb0
  69 
  70 wait1_000057: ; check if byte has been sent
  71 
  72 in r17,spsr
  73 sbrs r17,spif
  74 rjmp wait1_000057
  75 in r25,spdr ; recieve in left channel msb
  76 out spdr,r4 ; send out left channel lsb
  77 
  78 wait2_000057: ; check if byte has been sent
  79 
  80 in r17,spsr
  81 sbrs r17,spif
  82 rjmp wait2_000057
  83 in r24,spdr ; recieve in left channel lsb
  84 out spdr,r7 ; send out right channel msb
  85 
  86 wait3_000057: ; check if byte has been sent
  87 
  88 in r17,spsr
  89 sbrs r17,spif
  90 rjmp wait3_000057
  91 in r7,spdr ; recieve in right channel msb
  92 out spdr,r6 ; send out right channel lsb
  93 
  94 wait4_000057: ; check if byte has been sent
  95 
  96 in r17,spsr
  97 sbrs r17,spif
  98 rjmp wait4_000057
  99 in r6,spdr ; recieve in right channel lsb
 100 
 101 ;vco generation
 102 movw r17:r16,r31:r30 ; store z register
 103 ;get sample 1
 104 add r22,r11 ; increment sinetable address
 105 adc r23,r12
 106 adc r26,r13
 107 adc r27,r14 ; r14 is cleared above
 108 movw r31:r30,r27:r26 ; move to z register for data fetch
 109 lsl r30 ; adjust pointer for 16b fetch
 110 rol r31
 111 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
 112 ori r31,$48 ; set to memory address location where table is stored
 113 lpm r18,z+ ; get sine value lsb, increment z register
 114 lpm r19,z ; get sine value msb
 115 sbrc r27,$01 ; flip sign for half of the values
 116 rjmp interpolate_000057 ; interpolate if even
 117 neg r18 ; invert if odd
 118 adc r19,r14 ; r14 is cleared above
 119 neg r19
 120 
 121 interpolate_000057: ; multiply sample 1 by distance
 122 
 123 movw r21:r20,r23:r22 ; get distance from sample 1
 124 com r20 ; invert distance
 125 com r21
 126 mulsu r19,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
 127 movw r5:r4,r1:r0 ; store high bytes result for later
 128 mul     r18,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
 129 movw r3:r2,r1:r0 ; store low byets for later
 130 mulsu r19,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
 131 sbc     r5,r14 ; r14 is cleared above - subtract sign bit
 132 add     r3,r0 ; accumulate result
 133 adc     r4,r1
 134 adc     r5,r14 ; r14 is cleared above
 135 mul r21,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
 136 add     r3,r0 ; accumulate result
 137 adc     r4,r1
 138 adc     r5,r14 ; r14 is cleared above
 139 
 140 ;get sample 2
 141 adiw r27:r26,$01 ; set to next sample
 142 movw r31:r30,r27:r26 ; move to z register for data fetch
 143 lsl r30 ; adjust pointer for 16b fetch
 144 rol r31
 145 andi r31,$03 ; limit value to 10b (512 samples x 2 bytes)
 146 ori r31,$48 ; set to memory address location where table is stored
 147 lpm r18,z+ ; get sine value lsb, increment z register
 148 lpm r19,z ; get sine value msb
 149 sbrc r27,$01 ; flip sign for half of the values
 150 rjmp interpolate1_000057
 151 neg r18
 152 adc r19,r14 ; r14 is cleared above
 153 neg r19
 154 
 155 interpolate1_000057: ; multiply sample 2 by distance and accumulate
 156 
 157 sbiw r27:r26,$01 ; reset address
 158 movw r31:r30,r17:r16 ; restore z register
 159 mulsu r19,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
 160 add r4,r0 ; accumulate result
 161 adc r5,r1
 162 mul     r18,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
 163 add r2,r0 ; accumulate result
 164 adc r3,r1
 165 adc r4,r14 ; r14 is cleared above
 166 adc r5,r14
 167 mulsu r19,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
 168 sbc     r5,r14 ; r14 is cleared above - subtract sign bit
 169 add     r3,r0 ; accumulate result
 170 adc     r4,r1
 171 adc     r5,r14 ; r14 is cleared above
 172 mul r23,r18 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
 173 add     r3,r0 ; accumulate result
 174 adc     r4,r1
 175 adc     r5,r14 ; r14 is cleared above
 176 
 177 ;add in offset so lfo is always positive
 178 ; (the difference between tremolo and ring modulation)
 179 ldi r16,$80 ; turn signed value to unsigned value
 180 add r5,r16
 181 
 182 ;multiply left data by lfo
 183 movw r21:r20,r5:r4 ; move lfo to multiply register
 184 movw r19:r18,r25:r24 ; move left data to multiply register
 185 mulsu r19,r21 ; (signed)ah * (unsigned)bh
 186 movw r5:r4,r1:r0
 187 mul     r18,r20 ; (unsigned)al * (unsigned)bl
 188 movw r3:r2,r1:r0
 189 mulsu r19,r20 ; (signed)ah * (unsigned)bl
 190 sbc     r5,r14 ; r14 is cleared above
 191 add     r3,r0 ; accumulate result
 192 adc     r4,r1
 193 adc     r5,r14 ; r14 is cleared above
 194 mul r21,r18 ; (unsigned)bh * (unsigned)al
 195 add     r3,r0 ; accumulate result
 196 adc     r4,r1
 197 adc     r5,r14 ; r14 is cleared above
 198 
 199 ;check if in or out of phase mode
 200 sbrs r28,$00 ; check if phase inversion bit is set
 201 rjmp rightmultiply_000057 ; skip inversion if in phase
 202 com r20 ; else invert lfo signal
 203 com r21 ; ones complement used to avoid $0000 problem
 204 
 205 rightmultiply_000057: ; multiply right data by lfo
 206 
 207 movw r19:r18,r7:r6 ; move right data to multiply register
 208 mulsu r19,r21 ; (signed)ah * (unsigned)bh
 209 movw r7:r6,r1:r0
 210 mul     r18,r20 ; (unsigned)al * (unsigned)bl
 211 movw r3:r2,r1:r0
 212 mulsu r19,r20 ; (signed)ah * (unsigned)bl
 213 sbc     r7,r14 ; r14 is cleared above
 214 add     r3,r0 ; accumulate result
 215 adc     r6,r1
 216 adc     r7,r14 ; r14 is cleared above
 217 mul r21,r18 ; (unsigned)bh * (unsigned)al
 218 add     r3,r0 ; accumulate result
 219 adc     r6,r1
 220 adc     r7,r14 ; r14 is cleared above
 221 
 222 adcsample_000057: ; sample adc for lfo rate
 223 
 224 lds r17,adcsra ; get adc control register
 225 sbrs r17,adif ; check if adc conversion is complete
 226 rjmp done_000057 ; skip adc sampling
 227 lds r16,adcl ; get low byte adc value
 228 lds r17,adch ; get high byte adc value
 229 add r8,r16 ; accumulate adc samples
 230 adc r9,r17
 231 adc r10,r14 ; r14 is cleared above
 232 ldi r17,$f7
 233 sts adcsra,r17 ; clear interrupt flag
 234 dec r15 ; countdown adc sample clock
 235 brne done_000057 ; get delay time if its been long enough
 236 lsr r10 ; divide adc value by 4 to make 16b value
 237 ror r9
 238 ror r8
 239 lsr r10
 240 ror r9
 241 ror r8
 242 movw r17:r16,r9:r8 ; move adc sample to temporary register
 243 clr r20
 244 ldi r21,$01 ; add in offset of min lfo rate ($000100)
 245 add r17,r21
 246 adc r20,r14 ; r14 is cleared above
 247 sub r16,r11 ; find difference between adc sample and current lfo rate
 248 sbc r17,r12
 249 sbc r20,r13
 250 brsh check_000057 ; check for deadband if positive
 251 com r16 ; else invert if negative
 252 com r17 ; only 1 lsb error doing it this way
 253 com r20
 254 
 255 check_000057: ; check if difference is greater than deadband
 256 
 257 cpi r16,$40 ; check if difference is less than 1 adc lsb
 258 cpc r17,r14 ; r14 cleared above
 259 cpc r20,r14
 260 brlo empty_000057 ; do nothing if less than 1 adc lsb
 261 mov r11,r8 ; else move to lfo rate register
 262 mov r12,r9
 263 mov r13,r10
 264 add r12,r21 ; add in offset
 265 adc r13,r14 ; r14 is cleared above
 266 
 267 empty_000057: ; empty accumulation registers and finish off
 268 
 269 clr r8 ; empty accumulation registers
 270 clr r9
 271 clr r10
 272 
 273 ;check if phase mode should change
 274 lds r16,pinj ; get switch data
 275 sbrs r16,$02 ; check if pushbutton is released
 276 rjmp edge_000057 ; check if pushbutton was low on previous sample
 277 clt ;  clear state register if back high
 278 rjmp switchsample_000057 ; finish off
 279 
 280 edge_000057: ; check for falling edge
 281 
 282 brts adcsample_000057 ; do nothing if edge was already detected
 283 set ; set t register to indicate edge detected
 284 ldi r17,$01 ; toggle phase inversion mode
 285 eor r28,r17
 286 
 287 switchsample_000057: ; check rotary switch for new function
 288 
 289 andi r16,$78 ; mask off rotary switch
 290 lsr r16 ; adjust switch position to program memory location
 291 lsr r16
 292 ldi r17,$02
 293 add r16,r17
 294 cpse r16,r31 ; check if location has changed
 295 clr r30 ; reset jump register to intial state
 296 mov r31,r16
 297 
 298 done_000057:
 299 
 300 reti ; return to waiting
 301