VAR long cog, tract, pace, index, attenuation, sample '6 longs ...must long dira_, dirb_, ctra_, ctrb_, zzzzz, cnt_ '6 longs ...be long frames[4] 'many longs ...contiguous PUB start(parameters, pos_pin, neg_pin) dira_ |= (|< pos_pin + |< neg_pin) ctrb_ := $18000000 + pos_pin & $3F ctrb_ += $04000000 + (neg_pin & $3F) << 9 tract := parameters cnt_ := clkfreq / 20_000 return cog := cognew(@entry, @attenuation) PUB set_attenuation(level) attenuation := level PUB set_pace(percentage) pace := percentage PUB sample_ptr '' Returns the address of the long which receives the audio samples in real-time '' (signed 32-bit values updated at 20KHz) return @sample PUB go(time) repeat while frames[0] bytemove(@frames[0] + 3, tract, 13) frames[0] |= $01000000 / (time * 100 / pace #> 2) DAT entry org call #initial jmp #loop 'return here retadd long :wait 'pointer to next frame handler routine :wait jmpret retadd,#loop '(6 or 17.5 cycles) mov f_ptr,par 'check for next frame add f_ptr,#8*4 'point past miscellaneous data rdlong step_size,f_ptr 'get stepsize and step_size,h00FFFFFF wz 'isolate stepsize and check if not 0 if_z jmp #:wait 'if not 0, next frame ready add step_size,#1 'next frame ready, insure accurate accumulation mov step_acc,step_size 'initialize step accumulator movs :set1,#par_next 'ready to get parameters and steps for aa..ff movd :set2,#par_curr movd :set3,#par_next movd :set4,#par_step add f_ptr,#3 'point to first parameter mov f_cnt,#13 'iterate aa..ff :set jmpret retadd,#loop '(19.5 or 46.5 cycles) rdbyte t1,f_ptr 'get new parameter shl t1,#24 'msb justify :set1 mov t2,par_next 'get next parameter :set2 mov par_curr,t2 'current parameter = next parameter :set3 mov par_next,t1 'next parameter = new parameter sub t1,t2 wc 'get next-current delta with sign in c negc t1,t1 'make delta absolute (by c, not msb) rcl vscl,#1 wz,nr 'save sign into nz (vscl unaffected) mov t2,#8 'multiply delta by step size :mult shl t1,#1 wc if_c add t1,step_size djnz t2,#:mult :set4 negnz par_step,t1 'set signed step add :set1,#1 'update pointers for next parameter+step add :set2,d0 add :set3,d0 add :set4,d0 add f_ptr,#1 djnz f_cnt,#:set 'another parameter? :stepframe jmpret retadd,#loop '(47.5 or 8 cycles) mov :step1,:stepi 'ready to step parameters mov f_cnt,#13 'iterate aa..ff :step jmpret retadd,#loop '(3 or 4 cycles) :step1 add par_curr,par_step 'step parameter add :step1,d0s0 'update pointers for next parameter+step djnz f_cnt,#:step 'another parameter? add step_acc,step_size 'accumulate frame steps test step_acc,h01000000 wc 'check for frame steps done if_nc jmp #:stepframe 'another frame step? sub f_ptr,#16 'zero stepsize in frame to signal frame done wrlong vscl,f_ptr jmp #:wait 'check for next frame :stepi add par_curr,par_step 'instruction used to step parameters ' %%%%%%%%%%%%%%%%%%%%% ' % Vocal Tract Job % ' %%%%%%%%%%%%%%%%%%%%% loop waitcnt cnt_value,cnt_ticks 'wait for sample period rdlong t1,par 'perform master attenuation sar x,t1 mov t1,x 'update duty cycle output for pin driving add t1,h80000000 mov frqb,t1 mov t1,par 'update sample receiver in main memory add t1,#1*4 wrlong x,t1 :White_noise_source test lfsr,lfsr_taps wc 'iterate lfsr three times rcl lfsr,#1 test lfsr,lfsr_taps wc rcl lfsr,#1 test lfsr,lfsr_taps wc rcl lfsr,#1 :Aspiration mov t1,aa 'aspiration amplitude mov t2,lfsr call #mult sar t1,#8 'set x mov x,t1 :Vibrato mov t1,vr 'vibrato rate shr t1,#10 add vphase,t1 mov t1,vp 'vibrato pitch mov t2,vphase call #sine add t1,gp 'sum glottal pitch (+) into vibrato pitch (+/-) :Glottal_pulse shr t1,#2 'divide final pitch by 3 to mesh with mov t2,t1 '...12 notes/octave musical scale shr t2,#2 '(multiply by %0.0101010101010101) add t1,t2 mov t2,t1 shr t2,#4 add t1,t2 mov t2,t1 shr t2,#8 add t1,t2 add t1,tune 'tune scale so that gp=100 produces 110.00Hz (A2) call #antilog 'convert pitch (log frequency) to phase delta add gphase,t2 mov t1,gphase 'convert phase to glottal pulse sample call #antilog sub t2,h40000000 mov t1,ga call #sine sar t1,#6 'add to x add x,t1 :Vocal_tract_formants mov y,#0 'reset y mov a,f1 'formant1, sum and rotate (x,y) add x,f1x add y,f1y call #cordic mov f1x,x mov f1y,y mov a,f2 'formant2, sum and rotate (x,y) add x,f2x add y,f2y call #cordic mov f2x,x mov f2y,y mov a,f3 'formant3, sum and rotate (x,y) add x,f3x add y,f3y call #cordic mov f3x,x mov f3y,y mov a,f4 'formant4, sum and rotate (x,y) add x,f4x add y,f4y call #cordic mov f4x,x mov f4y,y :Nasal_anti_formant add nx,x 'subtract from x (nx negated) mov a,nf 'nasal frequency call #cordic mov t1,na 'nasal amplitude mov t2,x call #mult mov x,nx 'restore x neg nx,t1 'negate nx :Frication mov t1,lfsr 'phase noise sar t1,#3 add fphase,t1 sar t1,#1 add fphase,t1 mov t1,ff 'frication frequency shr t1,#1 add fphase,t1 mov t1,fa 'frication amplitude mov t2,fphase call #sine add x,t1 'add to x jmp retadd 'run segment of frame handler, return to loop ' %%%%%%%%%%%%%%%%%%%%%% ' % Math Subroutines % ' %%%%%%%%%%%%%%%%%%%%%% ' Antilog ' in: t1 = log (top 4 bits = whole number, next 11 bits = fraction) ' out: t2 = antilog ($00010000..$FFEA0000) antilog mov t2,t1 shr t2,#16 'position 11-bit fraction shr t1,#16+12 'position 4-bit whole number and t2,h00000FFE 'get table offset or t2,h0000D000 'get table base rdword t2,t2 'lookup fractional antilog or t2,h00010000 'insert leading bit shl t2,t1 'shift up by whole number antilog_ret ret ' Scaled sine ' in: t1 = unsigned scale (15 top bits used) ' t2 = angle (13 top bits used) ' out: t1 = 17-bit * 15-bit scaled sine ($80014000..$7FFEC000) sine shr t2,#32-13 'get 13-bit angle test t2,h00001000 wz 'get sine quadrant 3|4 into nz test t2,h00000800 wc 'get sine quadrant 2|4 into c negc t2,t2 'if sine quadrant 2|4, negate table offset or t2,h00007000 'insert sine table base address >> 1 shl t2,#1 'shift left to get final word address rdword t2,t2 'read sine word from table negnz t2,t2 'if quadrant 3|4, negate word shl t2,#15 'msb-justify result 'multiply follows... ' Multiply ' in: t1 = unsigned multiplier (15 top bits used) ' t2 = signed multiplicand (17 top bits used) ' out: t1 = 32-bit signed product mult shr t1,#32-15 'position unsigned multiplier sar t2,#15 'position signed multiplicand shl t2,#15-1 jmp #mult_steps 'do multiply steps mult_step sar t1,#1 wc 'multiply step that gets assembled into reserves (x15) if_c add t1,t2 ' Cordic rotation ' in: a = 0 to <90 degree angle (~13 top bits used) ' x,y = signed coordinates ' out: x,y = scaled and rotated signed coordinates cordic sar x,#1 'multiply (x,y) by %0.10011001 (0.60725 * 0.984) mov t1,x '...for cordic pre-scaling and slight damping sar t1,#3 add x,t1 mov t1,x sar t1,#4 add x,t1 sar y,#1 mov t1,y sar t1,#3 add y,t1 mov t1,y sar t1,#4 add y,t1 mov t1,x 'do first cordic step sub x,y add y,t1 sub a,h80000000 wc jmp #cordic_steps+1 'do subsequent cordic steps (skip first instruction) cordic_step mov a,a wc 'cordic step that gets assembled into reserves (x13) mov t1,y cordic_dx sar t1,#1 '(source incremented for each step) mov t2,x cordic_dy sar t2,#1 '(source incremented for each step) sumnc x,t1 sumc y,t2 cordic_a sumnc a,cordic_delta '(source incremented for each step) ' %%%%%%%%%%%%%%%% ' % Initialize % ' %%%%%%%%%%%%%%%% initial mov reserves,#0 'zero all reserved data add initial,d0 djnz clear_cnt,#initial mov t1,#2*15 'assemble 15 multiply steps into reserves :minst mov mult_steps,mult_step '(saves hub memory) add :minst,d0s0 test t1,#1 wc if_c sub :minst,#2 djnz t1,#:minst mov mult_ret,antilog_ret 'write 'ret' after last instruction mov t1,#13 'assemble 13 cordic steps into reserves :cstep mov t2,#8 '(saves hub memory) :cinst mov cordic_steps,cordic_step add :cinst,d0s0 djnz t2,#:cinst sub :cinst,#8 add cordic_dx,#1 add cordic_dy,#1 add cordic_a,#1 djnz t1,#:cstep mov cordic_ret,antilog_ret 'write 'ret' over last instruction mov t1,par 'get dira/dirb/ctra/ctrb add t1,#2*4 mov t2,#4 :regs rdlong dira,t1 add t1,#4 add :regs,d0 djnz t2,#:regs add t1,#4 'get cnt ticks rdlong cnt_ticks,t1 mov cnt_value,cnt 'prepare for initial waitcnt add cnt_value,cnt_ticks initial_ret ret tune long $66920000 'scale tuned to 110.00Hz at gp=100 (manually calibrated) lfsr long 1 'linear feedback shift register for noise generation lfsr_taps long $80061000 cordic_delta long $4B901476, $27ECE16D 'cordic angle deltas (first is h80000000) long $14444750, $0A2C350C, $05175F85, $028BD879, $0145F154 long $00A2F94D, $00517CBB, $0028BE60, $00145F30, $000A2F98 h80000000 long $80000000 'miscellaneous constants greater than 9 bits h40000000 long $40000000 h01000000 long $01000000 h00FFFFFF long $00FFFFFF h00010000 long $00010000 h0000D000 long $0000D000 h00007000 long $00007000 h00001000 long $00001000 h00000FFE long $00000FFE h00000800 long $00000800 d0 long $00000200 'destination/source field increments d0s0 long $00000201 clear_cnt long $1F0 - reserves 'number of reserved registers to clear on startup reserves 'reserved registers that get cleared on startup frqa_center res 1 cnt_ticks res 1 cnt_value res 1 f_ptr res 1 f_cnt res 1 step_size res 1 step_acc res 1 vphase res 1 gphase res 1 fphase res 1 f1x res 1 f1y res 1 f2x res 1 f2y res 1 f3x res 1 f3y res 1 f4x res 1 f4y res 1 nx res 1 a res 1 x res 1 y res 1 t1 res 1 t2 res 1 par_curr '*** current parameters aa res 1 'aspiration amplitude ga res 1 'glottal amplitude gp res 1 'glottal pitch vp res 1 'vibrato pitch vr res 1 'vibrato rate f1 res 1 'formant1 frequency f2 res 1 'formant2 frequency f3 res 1 'formant3 frequency f4 res 1 'formant4 frequency na res 1 'nasal amplitude nf res 1 'nasal frequency fa res 1 'frication amplitude ff res 1 'frication frequency par_next res 13 '*** next parameters par_step res 13 '*** parameter steps mult_steps res 2 * 15 'assembly area for multiply steps w/ret mult_ret sine_ret res 1 cordic_steps res 8 * 13 - 1 'assembly area for cordic steps w/ret cordic_ret res 1