MICrODEC: Stock Functions
Reverser with Crossfade
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 are using a cross-fading method. This is the same as cross-fading between records when DJing. As one sample gets close to the buffer boundary, its volume is faded down, and a sample from the other side of the boundary is faded up. This continues as the sample moves forward in the buffer, with the volume of the first sample being reduced to zero by the time it gets to the buffer boundary. At this point, the sample on the other side is playing full volume, and takes over. This gives a relatively smooth transition across the buffer boundary, with only a slight dipping noticeable, but also keeps the playback very true to the original signal during the majority of playback, which is not near the boundary. The crossfade time is preset at the begining of the code, which also determines the minimum sample size, as there has to be enough data for both fading up and down on either side of the boundary.
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.
1 ; program: reverser-16b-pot-crossfade.asm
2 ; UID = 000044 - unique id to eliminate conflicts between variables
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 read in from memory and written out the codec at the same time
10 ; new data is written to the memory from the codec. left channel data
11 ; is read in, and presented on both right and left out. the write address
12 ; increments until it reaches the top of the buffer and then starts at
13 ; zero. the read address does the same thing, but in reverse. the adc
14 ; value sets the buffer size, and is determined by the pot (MOD1) value,
15 ; which is multiplied up to a 16b value. the buffer size is adjusted at a
16 ; rate of 2 lsb per sample period, until it matches to what the pot says
17 ; it should be. the samples are played normally, until within a fixed
18 ; distance of the buffer boundary. at this point, the samples are
19 ; crossfaded with the first samples on the other side of the boundary. this
20 ; reduces the appearance of clicks at buffer transistions, without giving
21 ; the reverb effect of the fading method, and with a little less tremolo
22 ; than the ducking method.
23
24 ; constant definitions
25 ;
26 .equ stepsize_000044 = $0080 ; 65536/(stepsize * 44.1) = crossfade time (ms)
27 .equ buffer_min_000044 = (4 * ($10000 / stepsize_000044))
28 ; minimum sample buffer size to accomodate crossfade time
29
30 ; register usage - may be redefined in other sections
31 ;
32 ; r0 multiply result lsb
33 ; r1 multiply result msb
34 ; r2 multiply accumulate lsb
35 ; r3 multiply accumulate mlb
36 ; r4 left/right lsb out / sample 1 lsb / multiply accumulate mhb
37 ; r5 left/right msb out / smaple 2 msb / multiply accumulate msb
38 ; r6 left lsb in / sample 2 lsb
39 ; r7 left msb in / sample 2 msb
40 ; r8
41 ; r9 adc msb accumulator
42 ; r10 adc fractional byte accumulator
43 ; r11 adc lsb accumulator
44 ; r12 desired buffer size lsb
45 ; r13 desired buffer size msb
46 ; r14 null register
47 ; r15 switch/adc counter
48 ; r16 temporary swap register
49 ; r17 temporary swap register
50 ; r18 crossfade address temporary lsb
51 ; r19 crossfade address temporary msb
52 ; r20 crossfade distance lsb
53 ; r21 crossfade distance msb
54 ; r22 multiplicand lsb
55 ; r23 multiplicand msb
56 ; r24 write address lsb
57 ; r25 write address msb
58 ; r26 buffer size lsb
59 ; r27 buffer size msb
60 ; r28 read address lsb
61 ; r29 read address msb
62 ; r30 jump location for interrupt lsb
63 ; r31 jump location for interrupt msb
64 ; t crossfading indicator
65
66 ; program starts here first time
67 ; initialze z pointer for correct jump
68 ; this assumes a less than 256 word jump
69 ldi r30,$20 ; set jump location to program start
70 clr r24 ; clear write register
71 clr r25
72 ldi r22,$00 ; setup write address high byte
73 clr r18 ; setup r18 as null register for carry addition and ddr setting
74 ldi r17,$ff ; setup r17 for ddr setting
75
76 clear_000044: ; clear delay buffer
77 ; eliminates static when first switching to the delay setting
78
79 adiw r25:r24,$01 ; increment write register
80 adc r22,r18 ; increment write third byte
81 cpi r22,$01 ; check if 16b memory space has been cleared
82 breq cleardone_000044 ; continue until end of buffer reached
83 out portd,r24 ; set address
84 sts porth,r25
85 out portg,r22 ; pull ce low,we low,and set high bits of address
86 out ddra,r17 ; set porta as output for data write
87 out ddrc,r17 ; set portc as output for data write
88 out porta,r18 ; set data
89 out portc,r18 ; r18 is cleared above
90 sbi portg,portg2 ; pull we high to write
91 out ddra,r18 ; set porta as input for data lines
92 out ddrc,r18 ; set portc as input for data lines
93 rjmp clear_000044 ; continue clearing
94
95 cleardone_000044: ; reset registers
96
97 ldi r24,$00 ; initialize write register
98 ldi r25,$00
99 clr r14 ; setup null register
100 ldi r28,$00 ; set read address to minimum delay
101 ldi r29,$fd
102 clr r4 ; initialize data output registers
103 clr r5
104 ldi r26,$00 ; initialize buffer size
105 ldi r27,$06
106 reti ; finish with initialization and wait for next interrupt
107
108 ; program starts here every time but first
109 ; initiate data transfer to codec
110 sbi portb,portb0 ; toggle slave select pin
111 out spdr,r5 ; send out left channel msb
112 cbi portb,portb0
113
114 ;increment write address
115 adiw r25:r24,$01 ; increment write address
116 cp r24,r26 ; check if at buffer boundary
117 cpc r25,r27
118 brlo wait1_000044 ; continue if not
119 clr r24 ; set write address to bottom
120 clr r25
121
122 wait1_000044: ; check if byte has been sent
123
124 in r17,spsr
125 sbrs r17,spif
126 rjmp wait1_000044
127 in r7,spdr ; recieve in left channel msb
128 out spdr,r4 ; send out left channel lsb
129
130 ;decrement read address
131 sbiw r29:r28,$01 ; decrement read address
132 brsh wait2_000044 ; check if at bottom of buffer
133 movw r29:r28,r27:r26 ; set counter to top
134 sbiw r29:r28,$01 ; decrement read address
135
136 wait2_000044: ; check if byte has been sent
137
138 in r17,spsr
139 sbrs r17,spif
140 rjmp wait2_000044
141 in r6,spdr ; recieve in left channel lsb
142 out spdr,r5 ; send out right channel msb
143
144 ;write left channel data to sram
145 out portd,r24 ; set address
146 sts porth,r25
147 out portg,r14 ; pull ce low,we low,and set high bits of address
148 ldi r17,$ff
149 out ddra,r17 ; set porta as output for data write
150 out ddrc,r17 ; set portc as output for data write
151 out porta,r6 ; set data
152 out portc,r7
153 sbi portg,portg2 ; pull we high to write
154 out ddra,r14 ; set porta as input for data lines
155 out ddrc,r14 ; set portc as input for data lines
156
157 wait3_000044: ; check if byte has been sent
158
159 in r17,spsr
160 sbrs r17,spif
161 rjmp wait3_000044
162 in r17,spdr ; recieve in right channel msb
163 out spdr,r4 ; send out right channel lsb
164
165 ;get left/right channel data from sram
166 out portd,r28 ; set address
167 sts porth,r29
168 nop ; wait input latch time of 2 clock cycles
169 nop
170 in r4,pina ; get data
171 in r5,pinc ; get data
172
173 wait4_000044: ; check if byte has been sent
174
175 in r17,spsr
176 sbrs r17,spif
177 rjmp wait4_000044
178 in r17,spdr ; recieve in left channel lsb
179
180 ;get crossfade distance
181 movw r17:r16,r29:r28 ; move read address to temporary register
182 sub r16,r24 ; find distance to loop boundary
183 sbc r17,r25
184 brcc attenuate_000044 ; check if within crossfade distance if positive
185 add r16,r26 ; flip result around boundary if negative
186 adc r17,r27
187
188 attenuate_000044: ; multiply signal by distance to boundary
189
190 brts crossfade1_000044 ; skip if already crossfading
191 ldi r18,low(2 * ($10000 / stepsize_000044)) ; get crossfade distance
192 ldi r19,high(2 * ($10000 / stepsize_000044))
193 cp r16,r18 ; check if less than crossfade distance
194 cpc r17,r19
195 brsh check_000044 ; do nothing if not
196 set ; set t register to indicate crossfade and downcounting
197 clr r20 ; set crossfade counter to top
198 clr r21
199 subi r20,low(stepsize_000044) ; decrement for first sample
200 sbci r21,high(stepsize_000044)
201
202 crossfade1_000044: ; crossfade the signal
203
204 ;setup sample 2 read address
205 movw r19:r18,r29:r28 ; move read address to temporary register
206 subi r18,low(2 * ($10000 / stepsize_000044)) ; get sample from other side of boundary
207 sbci r19,high(2 * ($10000 / stepsize_000044))
208 brcc crossfade2_000044 ; continue if no buffer underflow
209 add r18,r26 ; wrap read address around buffer
210 adc r19,r27
211
212 crossfade2_000044: ; continue crossfading signal
213
214 ;get sample 2 from sram
215 out portd,r18 ; set address
216 sts porth,r19
217 nop ; wait input latch time of 2 clock cycles
218 nop
219 in r6,pina ; get data
220 in r7,pinc ; get data
221
222 ;multiply sample 1
223 movw r17:r16,r5:r4 ; move data to signed multiply register
224 mulsu r17,r21 ; (signed)Ah * (unsigned)Bh - multiply high bytes
225 movw r5:r4,r1:r0 ; store high bytes result for later
226 mul r16,r20 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
227 movw r3:r2,r1:r0 ; store low byets for later
228 mulsu r17,r20 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
229 sbc r5,r14 ; r14 is cleared above - subtract sign bit
230 add r3,r0 ; accumulate result
231 adc r4,r1
232 adc r5,r14 ; r14 is cleared above
233 mul r21,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
234 add r3,r0 ; accumulate result
235 adc r4,r1
236 adc r5,r14 ; r14 is cleared above
237
238 ;multiply and accumulate sample 2
239 movw r17:r16,r7:r6 ; move data to signed multiply register
240 movw r23:r22,r21:r20 ; move distance counter to temporary register
241 com r22 ; invert distance for sample 2
242 com r23
243 mulsu r17,r23 ; (signed)Ah * (unsigned)Bh - multiply high bytes
244 add r4,r0 ; accumulate result
245 adc r5,r1
246 mul r16,r22 ; (unsigned)Al * (unsigned)Bl ; multiply low bytes
247 add r2,r0 ; accumulate result
248 adc r3,r1
249 adc r4,r14
250 adc r5,r14
251 mulsu r17,r22 ; (signed)Ah * (unsigned)Bl - multiply middle bytes
252 sbc r5,r14 ; r14 is cleared above - subtract sign bit
253 add r3,r0 ; accumulate result
254 adc r4,r1
255 adc r5,r14 ; r14 is cleared above
256 mul r23,r16 ; (unsigned)Bh * (unsigned)Al - multiply middle bytes
257 add r3,r0 ; accumulate result
258 adc r4,r1
259 adc r5,r14 ; r14 is cleared above
260
261 ;check if done crossfading
262 subi r20,low(stepsize_000044) ; decrement crossfade
263 sbci r21,high(stepsize_000044)
264 brcc check1_000044 ; check if crossfade time is negative
265 movw r29:r28,r19:r18 ; move crossfade address to current address
266 clt ; clear the t register to indicate done
267 rjmp check_000044 ; finish off
268
269 check1_000044: ; continue checking crossfade time
270
271 brne check_000044 ; check if crossfade time is zero
272 movw r29:r28,r19:r18 ; move crossfade address to current address
273 clt ; clear the t register to indicate done
274
275 check_000044: ; check if buffer size is correct
276
277 cp r26,r12 ; compare current delay to desired delay
278 cpc r27,r13
279 brlo upcount2_000044 ; increment if smaller than
280 breq adcsample_000044 ; do nothing if they are same size
281 sbiw r27:r26,$02 ; decrement buffer size
282 rjmp adcsample_000044 ; finish off
283
284 upcount2_000044: ; increment buffer size register
285
286 adiw r27:r26,$02 ; increment buffer size
287
288 adcsample_000044: ; get loop setting
289
290 lds r17,adcsra ; get adc control register
291 sbrs r17,adif ; check if adc conversion is complete
292 rjmp done_000044 ; skip adc sampling
293 lds r16,adcl ; get low byte adc value
294 lds r17,adch ; get high byte adc value
295 add r10,r16
296 adc r11,r17 ; accumulate adc samples
297 adc r9,r14 ; accumulate adc samples - r14 is cleared above
298 ldi r17,$f7
299 sts adcsra,r17 ; clear interrupt flag
300 dec r15 ; countdown adc sample clock
301 brne done_000044 ; move adc value to loop setting after 256 samples
302 lsr r9 ; divide accumulated value by 4 to get 16b value
303 ror r11
304 ror r10
305 lsr r9
306 ror r11
307 ror r10
308 ldi r16,low(buffer_min_000044) ; load minimum buffer size
309 ldi r17,high(buffer_min_000044)
310 cp r10,r16 ; check if less than minimum
311 cpc r11,r17
312 brsh compare_000044 ; compare to previous value if above min
313 movw r11:r10,r17:r16 ; set buffer size to minimum
314
315 compare_000044: ; compare to previous value
316
317 movw r17:r16,r13:r12 ; make a copy of current loop time for comparison
318 sub r16,r10 ; find difference between current loop time and last loop time
319 sbc r17,r11
320 brcc deadband_000044 ; see if difference is large enough to indicate a change
321 neg r16 ; invert difference if negative
322 adc r17,r14 ; r14 is cleared above
323 neg r17
324
325 deadband_000044: ; see if pot has moved or if its just noise
326
327 cpi r16,$40 ; see if difference is greater than 1 lsb
328 cpc r17,r14 ; r14 is cleared above
329 brlo nochange_000044 ; dont update loop time if difference is not large enough
330 ldi r16,$fe ; make sure buffer size is even
331 and r10,r16
332 movw r13:r12,r11:r10 ; move adc value to loop time register
333
334 nochange_000044: ; clear accumulation registers
335
336 clr r10 ; empty accumulation registers
337 clr r11
338 clr r9
339
340 ;check rotary switch state
341 lds r16,pinj ; get switch data
342 andi r16,$78 ; mask off rotary switch
343 lsr r16 ; adjust switch position to program memory location
344 lsr r16
345 ldi r17,$02
346 add r16,r17
347 cpse r16,r31 ; check if location has changed
348 clr r30 ; reset jump register to intial state
349 mov r31,r16
350
351 done_000044:
352
353 reti ; return to waiting
354