* * * PITCH.ASM - pitch change effect for TI DSK module. * * * * Knobs - adjust these values within suggested range for * variations on the effect. * * SWEEP_STEP and UP are set according to the following tables: * * Semitones Up (UP=1) Down (UP=0) * 1 1948 -1839 * 2 4012 -3575 * 3 6199 -5213 * 4 8517 -6760 * 5 10972 -8219 * 6 13572 -9597 * 7 16328 -10897 * 8 19247 -12125 * 9 22340 -13284 * 10 25617 -14377 * 11 29089 -15409 * 12 32767 -16384 * * Cents Up (UP=1) Down (UP=0) * 2 37 -37 * 4 75 -75 * 6 113 -113 * 8 151 -151 * 10 189 -188 * 12 227 -226 * 14 266 -263 * 16 304 -301 * 18 342 -338 * 20 380 -376 * SWEEP_STEP .set 16328 ; (sets amnt of pitch change, see above tables) UP .set 1 ; (0 = down, 1 = up) MIX .set 1 ; (0 = no dry, 1 = mix dry signal in) * * Misc. defines * BUF .set 0420h ; main circular buffer start, middle, and end BUF+1 .set 0421h BUFMID .set 0500h BUFMID+1 .set 0501h BUFEND .set 0800h BSZ .set 03e0h ; total buffer size XFADE .set 508 ; # of samples in crossfade zone XMAJOR .set 91 ; fast part of crossfade XMINOR .set 38 ; slow part of crossfade * * Another faster crossfade option, sounds better for certain things * XFADE .set 128 ; # of samples in crossfade zone * XMAJOR .set 362 ; fast part of crossfade * XMINOR .set 150 ; slow part of crossfade * * * Data storage * SWEEPCNT .set 00h ; primary interp factor SWEEPCOMP .set 01h ; complement of interp factor STEP .set 02h ; inc/dec value for sweepcnt TMP .set 03h ; temporary storage reg INPVAL .set 04h ; input value OUTVAL .set 05h ; output value BLENDA .set 06h ; blend for delay A BLENDB .set 07h ; blend for delay B BL_STEPA .set 08h ; blend step A BL_STEPB .set 09h ; blend step B ACTIVE_FLG .set 0ah ; active delay channel, 0=delay A, 1=delay B SWEEP .set 0bh ; active sweep length .include "setup.asm" * * main * main: ssxm ; set sign extension mode spm 1 ; set P shift for Q15 (1 bit left shift ; from P => accum) ldpk 8 lrlk AR0,BUFEND ; permanently point to end of mem buf lrlk AR1,BUF ; write ptr, point to start of mem buf lrlk AR2,BUF+1 ; delay A 1st read ptr lrlk AR3,BUF ; delay A 2nd read ptr lrlk AR4,BUFMID+1 ; delay B 1st read ptr lrlk AR5,BUFMID ; delay B 2nd read ptr lalk 04000h ; half most positive value sacl SWEEPCNT ; set initial fractional sweep to midrange val lalk 07fffh ; initial blend value for A sacl BLENDA lack 0 ; initial blend value for B sacl BLENDB lalk XMINOR ; initial xfade rate for A sacl BL_STEPA lalk XMAJOR ; initial xfade rate for B neg sacl BL_STEPB ; set amount of pitch change lalk SWEEP_STEP ; install sweep rate sacl STEP abs sacl TMP ; usable portion of sweep before crossfade lt TMP ; |step| * xfade mpyk XFADE pac neg sach TMP lalk BSZ ; subtract from buffer size add TMP subk 2 ; just to be sure sacl SWEEP ; this is main sweep length counter value lar AR6,SWEEP ; load xfade trigger counter lark AR7,0 ; this counter is inactive until loaded lack 0 sacl ACTIVE_FLG ; show delay A active first lack 014h ; enable AIC recv interrupts ldpk 0 sacl IMR ldpk 8 ; loop here forever processing interrupts loop: idle b loop * * rint * * Recv interrupt handler performs all the work. Since there is * no main thread, there is no need to save or restore regs. * We can assume: * * AR0 - points to end of buffer * AR1 - current write ptr * AR2 - delay A 1st read ptr * AR3 - delay A 2nd read ptr * AR4 - delay B 1st read ptr * AR5 - delay B 2nd read ptr * AR6 - sign counter (indicates when to swap blend signs and reload) * AR7 - size counter (indicates when to swap blend step sizes) * rint: ; note: no need to save/restore processor state since ; main thread does absolutely nothing sovm ; set clipping overflow mode for the ; "analog" processing ldpk 0 lac DRR ; read in new input value ldpk 8 sfr ; dump 1 junk bit .if MIX sacl INPVAL ; temporary storage .endif larp AR1 ; use store ptr AR1 sacl *+ ; store to circular buffer cmpr 0 bbz nowrap1,*,AR2 lrlk AR1,BUF nowrap1: ; interpolate A ouput value from the delay A read ptrs lalk 32767 ; develop complement of fractional sub SWEEPCNT ; sweep position sacl SWEEPCOMP lt SWEEPCNT ; get fractional sweep position mpy *,AR3 ; scale 1st read value ltp SWEEPCOMP mpy *,AR4 ; scale 2nd read value apac sach TMP ; apply crossfade factor to A lt BLENDA mpy TMP sph TMP ; keep xfaded A val handy ; interpolate B output value from the delay B read ptrs lt SWEEPCNT mpy *,AR5 ; scale 1st read value ltp SWEEPCOMP mpy *,AR2 ; scale 2nd read value apac sach OUTVAL ; apply crossfade factor to B lt BLENDB mpy OUTVAL pac addh TMP ; bring in A value .if MIX addh INPVAL .endif sach OUTVAL ; this will be our "wet" output value lac OUTVAL ; prepare final output for AIC andk 0fffch ; clear lowest 2 bits ldpk 0 sacl DXR ; do output ldpk 8 ; adjust crossfade blend zac addh BLENDA ; adjust A crossfade blend addh BL_STEPA bgez not_neg1 ; clamp at zero zac not_neg1: sach BLENDA zac addh BLENDB ; adjust B crossface blend addh BL_STEPB bgez not_neg2,*,AR7 ; clamp at zero zac not_neg2: sach BLENDB ; see if size of crossfade steps need changing banz chk_ar7,* ; if AR7 == 0, don't decrement (idle) b do_sweep,* chk_ar7: mar *- ; dec AR7 banz do_sweep,* lac BL_STEPA ; simply exchange A and B sacl TMP lac BL_STEPB neg ; but swapping signs to preserve direction sacl BL_STEPA lac TMP neg sacl BL_STEPB ; ; now update our sweep stuff ; do_sweep: rovm ; normal overflow operation for arithmetic bv next,*,AR2 next: .if UP ; ; UPWARD SWEEP (delay decreasing, pitch goes up) ; ; always move forward at least one notch call inc_delay_ptrs ; now update fractional delay zac ; clear accum addh SWEEPCNT ; use high accum for calc addh STEP sach SWEEPCNT bnv done,*,AR2 ; fractional part didn't overflow, ; no need to advance sweep further ; fractional portion overflowed, adjust sweeps A & B forward call inc_delay_ptrs lac SWEEPCNT ; mask off sign stuff from overflow andk 07fffh sacl SWEEPCNT .else ; ; DOWNWARD SWEEP (delay increasing, pitch goes down) ; ; see if we need to update pointers lac SWEEPCNT add STEP ; step is negative here so we're actually sacl SWEEPCNT ; subtracting blz underflow,*,AR2 ; cnt went less than zero, skip ptr advance ; no underflow, advance ptrs but skip rest of sweep updates call inc_delay_ptrs b done,* ; fractional portion underflowed, update sweep stuff underflow: lac SWEEPCNT ; mask off sign stuff from underflow andk 07fffh sacl SWEEPCNT .endif ; see if signs of crossfade steps need changing larp AR6 mar *- ; dec AR6 banz done,* lar AR6,SWEEP ; counter expired, reload lac BL_STEPA ; swap signs neg sacl BL_STEPA lac BL_STEPB neg sacl BL_STEPB ; load halfway crossfade counter lalk XFADE sfr sacl TMP lrlk AR7,TMP ; reset idle delay channel to beginning of sweep by grabbing store ptr sar AR1,TMP ; stash store ptr in TMP lac ACTIVE_FLG ; see which channel needs restart bz reset_B ; A has been active, go reload B ; reload delay channel A lar AR2,TMP lack 0 sacl ACTIVE_FLG ; indicate A going active b done,* reset_B: ; reload delay channel B lar AR4,TMP lack 1 sacl ACTIVE_FLG ; indicate B going active ; ; DONE ; done: eint ret * * inc_delay_ptrs * * Increment the both read ptrs for both delays A and B. * Must always be called with arp=>AR2. * inc_delay_ptrs: sar AR2,TMP ; copy AR2 to AR3 lar AR3,TMP mar *+ ; inc AR2 cmpr 0 ; end of buffer? bbz nowrap2,*,AR4 lrlk AR2,BUF ; reload AR2 nowrap2: sar AR4,TMP ; copy AR4 to AR5 lar AR5,TMP mar *+ ; inc AR4 cmpr 0 ; end of buffer? bbz nowrap3,* lrlk AR4,BUF ; reload AR4 nowrap3: ret * * tint * * Timer interrupt - not used. * tint: eint ret * * xint * * AIC xmit interrupt - not used. * xint: eint ret