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