Attachment 'pitchshifter_chromatic.asm'
Download 1 ; program: pitch_shifter-16b-fading.asm
2 ; UID = 000039 - this is a unique id so variables dont conflict
3 ; 16b address space (1.5s sample time)
4 ; mono data in on left channel, mono out on both left and right
5 ; rotary encoder (MOD2) controlled playback speed
6 ; pot (MOD1) controlled buffer size
7
8 ; program overview
9 ;
10 ; data is sent out and taken in from the codec. data is taken in on the
11 ; left channel, and played out on both left and right. a buffer of the
12 ; past n seconds is kept and the output is the result of sampling this
13 ; buffer at varying playback speeds. the speed at which it plays through
14 ; the memory is controlled by the rotary encoder (MOD2). turning the
15 ; encoder to the right speeds playback up, and turning it the left slows
16 ; playback down. this playback speed is limited to chromatic steps by
17 ; using a lookup table in program memory to determine playback speed. the
18 ; audio is kept clean over fractional sample periods by interpolating
19 ; between the two closest samples. the output is a mix of the current
20 ; sample, and a sample from the opposite side of the buffer, with the
21 ; relative mix being determined by the distance to the buffer boundary.
22 ; in this way, the audio is faded down as it crosses the buffer boundary.
23 ; the pot (MOD1) controls the buffer size. the adc samples the pot 256
24 ; times and deadbands the signal to remove glitches.
25
26 ; constant definitions
27 ;
28 .equ buffer_min_000039 = $0200 ; minimum buffer size
29 .equ delay_mem_000039 = $0200 ; memory position for desired delay time
30 ;
31 ;.equ step-12_000039 = $0080 ; these are the playback speeds used
32 ;.equ step-11_000039 = $0088 ; they are stored in program memory
33 ;.equ step-10_000039 = $0090 ; and not used here
34 ;.equ step-9_000039 = $0098
35 ;.equ step-8_000039 = $00A1
36 ;.equ step-7_000039 = $00AB
37 ;.equ step-6_000039 = $00B5
38 ;.equ step-5_000039 = $00C0
39 ;.equ step-4_000039 = $00CB
40 ;.equ step-3_000039 = $00D7
41 ;.equ step-2_000039 = $00E4
42 ;.equ step-1_000039 = $00F2
43 ;.equ step00_000039 = $0100
44 ;.equ step01_000039 = $010F
45 ;.equ step02_000039 = $011F
46 ;.equ step03_000039 = $0130
47 ;.equ step04_000039 = $0143
48 ;.equ step05_000039 = $0156
49 ;.equ step06_000039 = $016A
50 ;.equ step07_000039 = $0180
51 ;.equ step08_000039 = $0196
52 ;.equ step09_000039 = $01AF
53 ;.equ step10_000039 = $01C8
54 ;.equ step11_000039 = $01E3
55 ;.equ step12_000039 = $0200
56
57 ; register usage - may be redefined in other sections
58 ;
59 ; r0 multiply result lsb
60 ; r1 multiply result msb
61 ; r2 sample 3/4 lsb
62 ; r3 sample 3/4 msb
63 ; r4 left/right lsb out
64 ; r5 left/right msb out
65 ; r6 left lsb in/temporary swap register
66 ; r7 left msb in/temporary swap register
67 ; r8 rotary encoder position counter
68 ; r9 adc msb accumulator
69 ; r10 adc fractional byte accumulator
70 ; r11 adc lsb accumulator
71 ; r12 playback speed increment lsb value ($0100 is normal speed)
72 ; r13 playback speed increment msb value
73 ; r14 rotary encoder counter
74 ; r15 switch\adc counter
75 ; r16 temporary swap register
76 ; r17 temporary swap register
77 ; r18 signed multiply register
78 ; r19 signed multiply register
79 ; r20 unsigned multiply register
80 ; r21 unsigned multiply register
81 ; r22 write address third byte/null register
82 ; r23 read address fractional byte
83 ; r24 write address lsb
84 ; r25 write address msb
85 ; r26 buffer length lsb
86 ; r27 buffer length msb
87 ; r28 read address lsb
88 ; r29 read address msb
89 ; r30 jump location for interrupt lsb
90 ; r31 jump location for interrupt msb
91 ; t rotary encoder edge indicator
92
93 ;program starts here first time
94 ; intialize registers
95 ldi r30,$29 ; set jump location to program start
96 clr r24 ; clear write register
97 clr r25
98 ldi r22,$00 ; setup write address high byte
99 clr r18 ; setup r18 as null register for carry addition and ddr setting
100 ldi r17,$ff ; setup r17 for ddr setting
101
102 clear_000039: ; clear delay buffer
103 ; eliminates static when first switching to the delay setting
104
105 adiw r25:r24,$01 ; increment write register
106 adc r22,r18 ; increment write third byte
107 cpi r22,$01 ; check if 16b memory space has been cleared
108 breq cleardone_000039 ; continue until end of buffer reached
109 out portd,r24 ; set address
110 sts porth,r25
111 out portg,r22 ; pull ce low,we low,and set high bits of address
112 out ddra,r17 ; set porta as output for data write
113 out ddrc,r17 ; set portc as output for data write
114 out porta,r18 ; set data
115 out portc,r18 ; r18 is cleared above
116 sbi portg,portg2 ; pull we high to write
117 out ddra,r18 ; set porta as input for data lines
118 out ddrc,r18 ; set portc as input for data lines
119 rjmp clear_000039 ; continue clearing
120
121 cleardone_000039: ; reset registers
122
123 ldi r24,$00 ; initialize write register
124 ldi r25,$00
125 clr r22 ; setup null register
126 ldi r28,$00 ; set read address to minimum delay
127 ldi r29,$fd
128 clr r4 ; initialize data output registers
129 clr r5
130 ldi r26,$00 ; initialize buffer size
131 ldi r27,$06
132 sts delay_mem_000039,r26 ; store desired buffer size
133 sts (delay_mem_000039 + 1),r27 ; i ran out of registers
134 clr r12 ; initialize playback speed
135 ldi r16,$01
136 mov r13,r16
137 ldi r16,$0c ; initialize playback speed pointer
138 mov r8,r16
139 reti ; return and wait for next interrupt
140
141 ;program begins here every time but first
142 ; initiate data transfer to codec
143 sbi portb,portb0 ; toggle slave select pin
144 out spdr,r5 ; send out left channel msb
145 cbi portb,portb0
146
147 ;increment write address
148 adiw r25:r24,$01 ; increment write address
149 cp r24,r26 ; check if at end of buffer
150 cpc r25,r27
151 brlo wait1_000039 ; do nothing if not at end of buffer
152 clr r24 ; reset buffer to bottom
153 clr r25
154
155 wait1_000039: ; check if byte has been sent
156
157 in r17,spsr
158 sbrs r17,spif
159 rjmp wait1_000039
160 in r7,spdr ; recieve in left channel msb
161 out spdr,r4 ; send out left channel lsb
162
163 ;increment read address
164 add r23,r12 ; increment read register
165 adc r28,r13
166 adc r29,r22 ; r22 is cleared above
167 cp r28,r26 ; check if at end of buffer
168 cpc r29,r27
169 brlo wait2_000039 ; do nothing if not at end of buffer
170 clr r28 ; reset buffer to bottom
171 clr r29
172
173 wait2_000039: ; check if byte has been sent
174
175 in r17,spsr
176 sbrs r17,spif
177 rjmp wait2_000039
178 in r6,spdr ; recieve in left channel lsb
179 out spdr,r5 ; send out right channel msb
180
181 ;write left channel data to sram
182 out portd,r24 ; set address
183 sts porth,r25
184 out portg,r22 ; pull ce low,we low,and set high bits of address
185 ldi r17,$ff
186 out ddra,r17 ; set porta as output for data write
187 out ddrc,r17 ; set portc as output for data write
188 out porta,r6 ; set data
189 out portc,r7
190 sbi portg,portg2 ; pull we high to write
191 out ddra,r22 ; set porta as input for data lines
192 out ddrc,r22 ; set portc as input for data lines
193
194 wait3_000039: ; check if byte has been sent
195
196 in r17,spsr
197 sbrs r17,spif
198 rjmp wait3_000039
199 in r17,spdr ; recieve in right channel msb
200 out spdr,r4 ; send out right channel lsb
201
202 ;get left channel sample 1 data from sram
203 movw r17:r16,r29:r28 ; move read address to temporary register
204 out portd,r16 ; set address
205 sts porth,r17
206 ldi r21,$01 ; increment read address
207 add r16,r21 ; placed here to use 2 cycle wait
208 adc r17,r22 ; r22 is cleared above
209 in r6,pina ; get data
210 in r18,pinc ; get data
211 cp r16,r26 ; check if at end of buffer
212 cpc r17,r27
213 brlo wait4_000039 ; do nothing if not at end of buffer
214 clr r16 ; reset buffer to bottom
215 clr r17
216
217 wait4_000039: ; check if byte has been sent
218
219 in r19,spsr
220 sbrs r19,spif
221 rjmp wait4_000039
222 in r19,spdr ; recieve in right channel lsb
223
224 ;get left channel sample 2 data from sram
225 out portd,r16 ; set address
226 sts porth,r17
227 nop ; wait 2 cycle setup time
228 nop
229 in r7,pina ; get data
230 in r19,pinc ; get data
231
232 ;multiply sample 1 by distance
233 mov r20,r23 ; get distance from sample 1
234 com r20
235 mulsu r18,r20 ; (signed)Ah * (unsigned)B
236 movw r5:r4,r1:r0
237 mul r6,r20 ; (unsigned)Al * (unsigned)B
238 add r4,r1
239 adc r5,r22 ; r22 is cleared above
240 mov r17,r0
241
242 ;multiply and accumulate sample 2 by distance
243 mulsu r19,r23 ; (signed)Ah * (unsigned)B
244 add r4,r0 ; accumulate result
245 adc r5,r1
246 mul r7,r23 ; (unsigned)Al * (unsigned)B
247 add r17,r0 ; accumulate result
248 adc r4,r1
249 adc r5,r22 ; r22 is cleared above
250
251 ;get sample from other side of buffer
252 movw r17:r16,r29:r28 ; move current position to temporary register
253 movw r7:r6,r27:r26 ; move buffer size to temporary register
254 lsr r7 ; divide buffer size by 2
255 ror r6
256 cp r16,r6 ; check if in lower or upper half of buffer
257 cpc r17,r7
258 brsh buffer_flip_000039 ; subtract half buffer if in upper half
259 add r16,r6 ; add half buffer size if in lower half
260 adc r17,r7
261 rjmp getsample3_000039 ; continue
262
263 buffer_flip_000039: ; adjust to opposite side of memory
264
265 sub r16,r6 ; subtract half buffer size if in upper half
266 sbc r17,r7
267
268 getsample3_000039: ;get left channel sample 3 data from sram
269
270 out portd,r16 ; set address
271 sts porth,r17
272 add r16,r21 ; increment read address - r21 set to $01 above
273 adc r17,r22 ; r22 is cleared above
274 in r6,pina ; get data
275 in r18,pinc ; get data
276 cp r16,r26 ; check if at end of buffer
277 cpc r17,r27
278 brlo getsample4_000039 ; do nothing if not at end of buffer
279 clr r16 ; reset buffer to bottom
280 clr r17
281
282 getsample4_000039: ;get left channel sample 4 data from sram
283
284 out portd,r16 ; set address
285 sts porth,r17
286 nop ; wait 2 cycle setup time
287 nop
288 in r7,pina ; get data
289 in r19,pinc ; get data
290
291 ;multiply sample 3 by distance
292 mulsu r18,r20 ; (signed)ah * b
293 movw r3:r2,r1:r0
294 mul r6,r20 ; al * b
295 add r2,r1
296 adc r3,r22 ; r22 is cleared above
297 mov r17,r0
298
299 ;multiply sample 4 by distance
300 mulsu r19,r23 ; (signed)ah * b
301 add r2,r0 ; accumulate result
302 adc r3,r1
303 mul r7,r23 ; al * b
304 add r17,r0 ; accumulate result
305 adc r2,r1
306 adc r3,r22 ; r22 is cleared above
307
308 ;get distance to boundary
309 movw r17:r16,r29:r28 ; move read address to temporary register
310 mov r18,r23
311 sub r16,r24 ; find distance to loop boundary
312 sbc r17,r25
313 brcc half_000039 ; check if result is negative
314 com r16 ; invert distance if negative
315 com r17
316 com r18
317 add r18,r21 ; r21 set to $01 above
318 adc r16,r22 ; r22 cleared above
319 adc r17,r22
320
321 half_000039: ; check if result is greater than half the buffer size
322
323 movw r7:r6,r27:r26 ; move buffer size to temporary register
324 lsr r7 ; divide buffer size by 2
325 ror r6
326 cp r16,r6 ; check if result is greater than half the buffer size
327 cpc r17,r7
328 brlo scale_000039 ; skip flip if not
329 sub r16,r26 ; flip result around boundary
330 sbc r17,r27
331 com r16
332 com r17
333 com r18
334 add r18,r21 ; r21 set to $01 above
335 adc r16,r22
336 adc r17,r22
337
338 scale_000039: ; scale distance to match buffer size - 50% accurate
339
340 movw r7:r6,r27:r26 ; move buffer size to temporary register
341 sbrc r7,$07 ; check if msb of buffer size is set
342 rjmp attenuate_000039 ; attenuate signal if 16b value
343
344 shift_000039: ; shift buffer size till it occupies full 16b
345
346 lsl r6 ; multiply buffer size by 2
347 rol r7
348 lsl r18 ; multiply distance by 2
349 rol r16
350 rol r17
351 sbrs r7,$07 ; check if msb of buffer size is set
352 rjmp shift_000039 ; keep checking if not set
353
354 attenuate_000039: ; multiply sample 1/2 by distance
355
356 lsl r18 ; multiply distance by 2 since max value is 1/2 buffer size
357 rol r16
358 rol r17
359 sub r6,r16 ; find complementary distance of sample 3/4
360 sbc r7,r17 ; only 1 bit error for not subtracting r18 as well
361 movw r21:r20,r7:r6 ; move distance to signed multiply register
362 movw r19:r18,r5:r4 ; move value to signed multiply register
363 mulsu r19,r17 ; (signed)ah * bh
364 movw r5:r4,r1:r0
365 mul r18,r16 ; al * bl
366 movw r7:r6,r1:r0
367 mulsu r19,r16 ; (signed)ah * bl
368 sbc r5,r22 ; r22 is cleared above
369 add r7,r0
370 adc r4,r1
371 adc r5,r22
372 mul r17,r18 ; bh * al
373 add r7,r0
374 adc r4,r1
375 adc r5,r22
376
377 ;multiply and accumulate sample 3/4 with result from above
378 movw r19:r18,r3:r2 ; move value to signed multiply register
379 mulsu r19,r21 ; (signed)ah * bh
380 add r4,r0 ; accumulate result
381 adc r5,r1
382 mul r18,r20 ; al * bl
383 add r6,r0 ; accumulate result
384 adc r7,r1
385 adc r4,r22 ; r22 is cleared above
386 adc r5,r22
387 mulsu r19,r20 ; (signed)ah * bl
388 sbc r5,r22 ; accumulate result
389 add r7,r0
390 adc r4,r1
391 adc r5,r22
392 mul r21,r18 ; bh * al
393 add r7,r0
394 adc r4,r1
395 adc r5,r22
396
397 rotary_000039: ; check rotary encoder and adjust playback rate
398 ; rotary encoder is externally debounced, so that is not done here.
399 ; pin1 is sampled on a transition from high to low on pin0. if pin1 is
400 ; high, a left turn occured, if pin1 is low, a right turn occured.
401 dec r14 ; reduce the sampling rate to help with debounce
402 brne check_000039 ; continue if not ready yet
403 ldi r17,$40 ; adjust sample frequency to catch all rising edges (1.5ms)
404 mov r14,r17
405 lds r17,pinj ; get switch data
406 sbrs r17,$00 ; check if pin0 is low
407 rjmp edge_000039 ; check if pin0 was low on previous sample
408 clt ; clear state register if back high
409 rjmp check_000039 ; finish off
410
411 edge_000039: ; check for falling edge
412
413 brts check_000039 ; do nothing if the edge was already detected
414 set ; set state register to indicate a falling edge occured
415 sbrs r17,$01 ; check if pin1 is high
416 rjmp increment_000039 ; increment playback if right rotation
417 ldi r16,$01 ; check if pitch at min
418 cp r8,r16
419 brlo check_000039 ; do nothing it at bottom
420 dec r8 ; decrement rotary encoder position counter
421 movw r17:r16,z ; store z register
422 ldi zh,$4c ; setup z pointer to fetch tone from lookup table
423 mov zl,r8
424 lsl zl
425 lpm r12,z+ ; move tone to pitch register
426 lpm r13,z
427 movw z,r17:r16 ; restore z register
428 rjmp check_000039 ; finish off
429
430 increment_000039: ; increment playback speed
431
432 ldi r16,$18 ; check if pitch at max
433 cp r8,r16
434 brsh reset1_000039 ; do nothing if at max already
435 inc r8 ; increment rotary encoder position counter
436 movw r17:r16,z ; store z register
437 ldi zh,$4c ; setup z pointer to fetch tone from lookup table
438 mov zl,r8
439 lsl zl
440 lpm r12,z+ ; move tone to pitch register
441 lpm r13,z
442 movw z,r17:r16 ; restore z register
443 rjmp check_000039 ; finish off
444
445 reset1_000039: ; reset tone register in case it goes too high
446
447 mov r8,r16 ; set tone register to max
448
449 check_000039: ; check if buffer size is correct
450
451 lds r16,delay_mem_000039 ; fetch desired buffer size
452 lds r17,(delay_mem_000039 + 1) ; i ran out of registers
453 cp r26,r16 ; compare current delay to desired delay
454 cpc r27,r17
455 brlo upcount_000039 ; increment if smaller than
456 breq adcsample_000039 ; do nothing if they are same size
457 sbiw r27:r26,$02 ; decrement buffer size
458 rjmp adcsample_000039 ; finish off
459
460 upcount_000039: ; increment buffer size register
461
462 adiw r27:r26,$02 ; increment buffer size
463
464 adcsample_000039: ; get loop setting
465
466 lds r17,adcsra ; get adc control register
467 sbrs r17,adif ; check if adc conversion is complete
468 rjmp done_000039 ; skip adc sampling
469 lds r16,adcl ; get low byte adc value
470 lds r17,adch ; get high byte adc value
471 add r10,r16 ; accumulate adc samples
472 adc r11,r17
473 adc r9,r22 ; r22 is cleared above
474 ldi r17,$f7
475 sts adcsra,r17 ; clear interrupt flag
476 dec r15 ; countdown adc sample clock
477 brne done_000039 ; move adc value to loop setting after 256 samples
478 lsr r9 ; divide accumulated value by 4 to make a 16b value
479 ror r11
480 ror r10
481 lsr r9
482 ror r11
483 ror r10
484 ldi r16,low(buffer_min_000039) ; fetch min buffer size
485 ldi r17,high(buffer_min_000039)
486 cp r10,r16 ; compare adc value to min buffer size
487 cpc r11,r17
488 brsh compare_000039 ; skip if above minimum buffer size
489 movw r11:r10,r17:r16 ; else set to minimum buffer size
490
491 compare_000039: ; compare to previous value
492
493 lds r16,delay_mem_000039 ; fetch desired delay time
494 lds r17,(delay_mem_000039 + 1) ; i ran out of registers
495 sub r16,r10 ; find difference between adc value and desired buffer size
496 sbc r17,r11
497 brcc deadband_000039 ; check for magnitude of change if positive
498 neg r16 ; else invert difference if negative
499 adc r17,r22 ; r22 is cleared above
500 neg r17
501
502 deadband_000039: ; see if pot has moved or if its just noise
503
504 cpi r16,$40 ; see if difference is greater than 1 lsb
505 cpc r17,r22 ; r22 is cleared above
506 brlo nochange_000039 ; dont update loop time if difference is not large enough
507 ldi r16,$fe ; make sure buffer size is even
508 and r10,r16
509 sts delay_mem_000039,r10 ; store new desired buffer size
510 sts (delay_mem_000039 + 1),r11 ; i ran out of registers
511
512 nochange_000039: ; clear accumulation registers
513
514 clr r10 ; empty accumulation registers
515 clr r11
516 clr r9
517
518 switchsample_000039: ; check rotary switch state
519
520 lds r16,pinj ; get switch data
521 andi r16,$78 ; mask off rotary switch
522 lsr r16 ; adjust switch position to program memory location
523 lsr r16
524 ldi r17,$02
525 add r16,r17
526 cpse r16,r31 ; check if location has changed
527 clr r30 ; reset jump register to intial state
528 mov r31,r16
529
530 done_000039:
531
532 reti ; return to waiting
533
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.