mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 01:30:22 +00:00
737 lines
49 KiB
Plaintext
737 lines
49 KiB
Plaintext
{{
|
|
┌───────────────────────────────────────────┬────────────────┬───────────────────────────────────┬─────────────────┐
|
|
│ Vocal Tract v1.1 │ by Chip Gracey │ Copyright (c) 2006 Parallax, Inc. │ 28 October 2006 │
|
|
├───────────────────────────────────────────┴────────────────┴───────────────────────────────────┴─────────────────┤
|
|
│ │
|
|
│ This object synthesizes a human vocal tract in real-time. It requires one cog and at least 80 MHz. │
|
|
│ │
|
|
│ The vocal tract is controlled via 13 single-byte parameters which must reside in the parent object: │
|
|
│ │
|
|
│ VAR byte aa,ga,gp,vp,vr,f1,f2,f3,f4,na,nf,fa,ff 'vocal tract parameters │
|
|
│ │
|
|
│ │
|
|
│ aa │
|
|
│ ┌────────────┐ │
|
|
│ │ ASPIRATION ├──┐ │
|
|
│ └────────────┘ │ f1 f2 f3 f4 na nf │
|
|
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌───────┐ │
|
|
│ +┣──┤ F1 ├──┤ F2 ├──┤ F3 ├──┤ F4 ├──┤ NASAL ├──┐ │
|
|
│ ga gp └────┘ └────┘ └────┘ └────┘ └───────┘ │ │
|
|
│ ┌─────────┐ │ │
|
|
│ │ GLOTTAL ├──┘ +┣── OUTPUT │
|
|
│ └────┬────┘ fa ff │
|
|
│ ┌───────────┐ │ │
|
|
│ vp │ vr │ FRICATION ├──┘ │
|
|
│ ┌────┴────┐ └───────────┘ │
|
|
│ │ VIBRATO │ │
|
|
│ └─────────┘ │
|
|
│ │
|
|
│ │
|
|
│ ┌───────────┬──────────────────────┬─────────────┬────────────────────────────────────────────────┐ │
|
|
│ │ parameter │ description │ unit │ notes │ │
|
|
│ ├───────────┼──────────────────────┼─────────────┼────────────────────────────────────────────────┤ │
|
|
│ │ aa │ aspiration amplitude │ 0..255 │ breath volume: silent..loud, linear │ │
|
|
│ │ ga │ glottal amplitude │ 0..255 │ voice volume: silent..loud, linear │ │
|
|
│ │ gp │ glottal pitch │ 1/48 octave │ voice pitch: 100 ─ 110.00Hz (musical note A2) │ │
|
|
│ │ vp │ vibrato pitch │ 1/48 octave │ voice vibrato pitch: 48 ─ ± 1/2 octave swing │ │
|
|
│ │ vr │ vibrato rate │ 0.0763 Hz │ voice vibrato rate: 52 ─ 4 Hz │ │
|
|
│ │ f1 │ formant1 frequency │ 19.53 Hz │ 1st resonator frequency: 40 ─ 781 Hz │ │
|
|
│ │ f2 │ formant2 frequency │ 19.53 Hz │ 2nd resonator frequency: 56 ─ 1094 Hz │ │
|
|
│ │ f3 │ formant3 frequency │ 19.53 Hz │ 3rd resonator frequency: 128 ─ 2500 Hz │ │
|
|
│ │ f4 │ formant4 frequency │ 19.53 Hz │ 4th resonator frequency: 179 ─ 3496 Hz │ │
|
|
│ │ na │ nasal amplitude │ 0..255 │ anti-resonator level: off..on, linear │ │
|
|
│ │ nf │ nasal frequency │ 19.53 Hz │ anti-resonator frequency: 102 ─ 1992 Hz │ │
|
|
│ │ fa │ frication amplitude │ 0..255 │ white noise volume: silent..loud, linear │ │
|
|
│ │ ff │ frication frequency │ 39.06 Hz │ white noise frequency: 60 ─ 2344 Hz ("Sh") │ │
|
|
│ └───────────┴──────────────────────┴─────────────┴────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ The parent object alternately modifies one or more of these parameters and then calls the go(time) method to │
|
|
│ queue the entire 13-parameter frame for feeding to the vocal tract. The vocal tract will load one queued frame │
|
|
│ after another and smoothly interpolate between them over specified amounts of time without interruption. Up to │
|
|
│ eight frames will be queued in order to relax the frame-generation timing requirement of the parent object. If │
|
|
│ eight frames are queued, the parent must then wait to queue another frame. If the vocal tract runs out of │
|
|
│ frames, it will continue generating samples based on the last frame. When a new frame is queued, it will │
|
|
│ immediately load it and begin inter-polating towards it. │
|
|
│ │
|
|
│ The vocal tract generates audio samples at a continuous rate of 20KHz. These samples can be output to pins via │
|
|
│ delta-modulation for RC filtering or direct transducer driving. An FM aural subcarrier can also be generated for │
|
|
│ inclusion into a TV broadcast controlled by another cog. Regardless of any output mode, samples are always │
|
|
│ streamed into a special variable so that other objects can access them in real-time. │
|
|
│ │
|
|
│ In order to achieve optimal sound quality, it is worthwhile to maximize amplitudes such as 'ga' to the point │
|
|
│ just shy of numerical overflow. Numerical overflow results in high-amplitude noise bursts which are quite │
|
|
│ disruptive. The closeness of 'f1'-'f4' and their relationship to 'gp' can greatly influence the amount of 'ga' │
|
|
│ that can be applied before overflow occurs. You must determine through experimentation what the limits are. By │
|
|
│ pushing 'ga' close to the overflow point, you will maximize the signal-to-noise ratio of the vocal tract, │
|
|
│ resulting in the highest quality sound. Once your vocal tract programming is complete, the attenuation level │
|
|
│ can then be used to reduce the overall output in 3dB steps while preserving the signal-to-noise ratio. │
|
|
│ │
|
|
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
│ Revision History v1.0 released 26 October 2006 │
|
|
│ │
|
|
│ v1.1 If the vocal tract runs out of frames, its internal parameters will now be brought all the way to the │
|
|
│ last frame's values. Before, they were left one interpolation point shy, and then set to the last frame's │
|
|
│ values at the start of the next frame. For continuous frames this was trivial, but it posed a problem │
|
|
│ during frame gaps because the internal parameters would get stalled at transition points just shy of the │
|
|
│ last frame's values. This change makes the vocal tract behave more sensibly during frame gaps. │
|
|
│ │
|
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
}}
|
|
CON
|
|
|
|
frame_buffers = 8 'frame buffers (2n)
|
|
|
|
frame_bytes = 3 {for stepsize} + 13 {for aa..ff} '16 bytes per frame
|
|
frame_longs = frame_bytes / 4 '4 longs per frame
|
|
|
|
frame_buffer_bytes = frame_bytes * frame_buffers
|
|
frame_buffer_longs = frame_longs * frame_buffers
|
|
|
|
|
|
VAR
|
|
|
|
long cog, tract, pace
|
|
|
|
long index, attenuation, sample '3 longs ...must
|
|
long dira_, dirb_, ctra_, ctrb_, frqa_, cnt_ '6 longs ...be
|
|
long frames[frame_buffer_longs] 'many longs ...contiguous
|
|
|
|
|
|
PUB start(tract_ptr, pos_pin, neg_pin, fm_offset) : okay
|
|
|
|
'' Start vocal tract driver - starts a cog
|
|
'' returns false if no cog available
|
|
''
|
|
'' tract_ptr = pointer to vocal tract parameters (13 bytes)
|
|
'' pos_pin = positive delta-modulation pin (-1 to disable)
|
|
'' neg_pin = negative delta-modulation pin (pos_pin must also be enabled, -1 to disable)
|
|
'' fm_offset = offset frequency for fm aural subcarrier generation (-1 to disable, 4_500_000 for NTSC)
|
|
|
|
'Reset driver
|
|
stop
|
|
|
|
'Remember vocal tract parameters pointer
|
|
tract := tract_ptr
|
|
|
|
'Initialize pace to 100%
|
|
pace := 100
|
|
|
|
'If delta-modulation pin(s) enabled, ready output(s) and ready ctrb for duty mode
|
|
if pos_pin > -1
|
|
dira_[pos_pin >> 5 & 1] |= |< pos_pin
|
|
ctrb_ := $18000000 + pos_pin & $3F
|
|
if neg_pin > -1
|
|
dira_[neg_pin >> 5 & 1] |= |< neg_pin
|
|
ctrb_ += $04000000 + (neg_pin & $3F) << 9
|
|
|
|
'If fm offset is valid, ready ctra for pll mode with divide-by-16 (else disabled)
|
|
if fm_offset > -1
|
|
ctra_ := $05800000
|
|
|
|
'Ready frqa value for fm offset
|
|
repeat 33
|
|
frqa_ <<= 1
|
|
if fm_offset => clkfreq
|
|
fm_offset -= clkfreq
|
|
frqa_++
|
|
fm_offset <<= 1
|
|
|
|
'Ready 20KHz sample period
|
|
cnt_ := clkfreq / 20_000
|
|
|
|
'Launch vocal tract cog
|
|
return cog := cognew(@entry, @attenuation) + 1
|
|
|
|
|
|
PUB stop
|
|
|
|
'' Stop vocal tract driver - frees a cog
|
|
|
|
'If already running, stop vocal tract cog
|
|
if cog
|
|
cogstop(cog~ - 1)
|
|
|
|
'Reset variables and buffers
|
|
longfill(@index, 0, constant(3 + 6 + frame_buffer_longs))
|
|
|
|
|
|
PUB set_attenuation(level)
|
|
|
|
'' Set master attenuation level (0..7, initially 0)
|
|
|
|
attenuation := level
|
|
|
|
|
|
PUB set_pace(percentage)
|
|
|
|
'' Set pace to some percentage (initially 100)
|
|
|
|
pace := percentage
|
|
|
|
|
|
PUB go(time)
|
|
|
|
'' Queue current parameters to transition over time
|
|
''
|
|
'' actual time = integer(time * 100 / pace) #> 2 * 700µs (at least 1400µs, see set_pace)
|
|
|
|
'Wait until frame available (first long will be zeroed)
|
|
repeat while frames[index]
|
|
|
|
'Load parameters into frame
|
|
bytemove(@frames[index] + 3, tract, 13)
|
|
|
|
'Write stepsize into frame (non-0 alerts vocal tract that frame is ready)
|
|
frames[index] |= $01000000 / (time * 100 / pace #> 2)
|
|
|
|
'Increment frame index
|
|
index := (index + frame_longs) & constant(frame_buffer_longs - 1)
|
|
|
|
|
|
PUB full : status
|
|
|
|
'' Returns true if the parameter queue is full
|
|
'' (useful for checking if "go" would have to wait)
|
|
|
|
return frames[index]
|
|
|
|
|
|
PUB empty : status | i
|
|
|
|
'' Returns true if the parameter queue is empty
|
|
'' (useful for detecting when the vocal tract is finished)
|
|
|
|
repeat i from 0 to constant(frame_buffers - 1)
|
|
if frames[i * frame_longs]
|
|
return {false}
|
|
return true
|
|
|
|
|
|
PUB sample_ptr : 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 aural_id : id
|
|
|
|
'' Returns the id of the cog executing the vocal tract algorithm
|
|
'' (for connecting a broadcast tv driver with the aural subcarrier)
|
|
|
|
return cog - 1
|
|
|
|
|
|
DAT
|
|
|
|
' ┌──────────────────┐
|
|
' │ Initialization │
|
|
' └──────────────────┘
|
|
|
|
entry org
|
|
|
|
:zero mov reserves,#0 'zero all reserved data
|
|
add :zero,d0
|
|
djnz clear_cnt,#:zero
|
|
|
|
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
|
|
|
|
rdlong frqa_center,t1 'get frqa center
|
|
|
|
add t1,#4 'get cnt ticks
|
|
rdlong cnt_ticks,t1
|
|
|
|
mov cnt_value,cnt 'prepare for initial waitcnt
|
|
add cnt_value,cnt_ticks
|
|
|
|
|
|
' ┌────────────────────┐
|
|
' │ Vocal Tract Loop │
|
|
' └────────────────────┘
|
|
|
|
' Wait for next sample period, then output sample
|
|
|
|
loop waitcnt cnt_value,cnt_ticks 'wait for sample period
|
|
|
|
rdlong t1,par 'perform master attenuation
|
|
sar x,t1
|
|
|
|
mov t1,x 'update fm aural subcarrier for tv broadcast
|
|
sar t1,#10
|
|
add t1,frqa_center
|
|
mov frqa,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
|
|
|
|
' Handle frame
|
|
|
|
jmp :ret 'run segment of frame handler, return to loop
|
|
|
|
|
|
' ┌─────────────────┐
|
|
' │ Frame Handler │
|
|
' └─────────────────┘
|
|
|
|
:ret long :wait 'pointer to next frame handler routine
|
|
|
|
|
|
:wait jmpret :ret,#loop '(6 or 17.5 cycles)
|
|
mov frame_ptr,par 'check for next frame
|
|
add frame_ptr,#8*4 'point past miscellaneous data
|
|
add frame_ptr,frame_index 'point to start of frame
|
|
rdlong step_size,frame_ptr 'get stepsize
|
|
and step_size,h00FFFFFF wz 'isolate stepsize and check if not 0
|
|
if_nz jmp #:next 'if not 0, next frame ready
|
|
|
|
|
|
mov :final1,:finali 'no frame ready, ready to finalize parameters
|
|
mov frame_cnt,#13 'iterate aa..ff
|
|
|
|
:final jmpret :ret,#loop '(13.5 or 4 cycles)
|
|
:final1 mov par_curr,par_next 'current parameter = next parameter
|
|
add :final1,d0s0 'update pointers
|
|
djnz frame_cnt,#:final 'another parameter?
|
|
|
|
jmp #:wait 'check for next frame
|
|
|
|
|
|
:next 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 frame_ptr,#3 'point to first parameter
|
|
mov frame_cnt,#13 'iterate aa..ff
|
|
|
|
:set jmpret :ret,#loop '(19.5 or 46.5 cycles)
|
|
rdbyte t1,frame_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 frame_ptr,#1
|
|
djnz frame_cnt,#:set 'another parameter?
|
|
|
|
|
|
:stepframe jmpret :ret,#loop '(47.5 or 8 cycles)
|
|
mov :step1,:stepi 'ready to step parameters
|
|
mov frame_cnt,#13 'iterate aa..ff
|
|
|
|
:step jmpret :ret,#loop '(3 or 4 cycles)
|
|
:step1 add par_curr,par_step 'step parameter
|
|
add :step1,d0s0 'update pointers for next parameter+step
|
|
djnz frame_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 frame_ptr,#frame_bytes 'zero stepsize in frame to signal frame done
|
|
wrlong vscl,frame_ptr
|
|
|
|
add frame_index,#frame_bytes'point to next frame
|
|
and frame_index,#frame_buffer_bytes - 1
|
|
|
|
jmp #:wait 'check for next frame
|
|
|
|
|
|
:finali mov par_curr,par_next 'instruction used to finalize parameters
|
|
:stepi add par_curr,par_step 'instruction used to step parameters
|
|
|
|
|
|
' ┌────────────────────┐
|
|
' │ 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)
|
|
|
|
|
|
' ┌────────────────┐
|
|
' │ Defined Data │
|
|
' └────────────────┘
|
|
|
|
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 'cordic angle deltas (first is h80000000)
|
|
long $27ECE16D
|
|
long $14444750
|
|
long $0A2C350C
|
|
long $05175F85
|
|
long $028BD879
|
|
long $0145F154
|
|
long $00A2F94D
|
|
long $00517CBB
|
|
long $0028BE60
|
|
long $00145F30
|
|
long $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
|
|
|
|
|
|
' ┌──────────────────────────────────────────────────┐
|
|
' │ Undefined Data (zeroed by initialization code) │
|
|
' └──────────────────────────────────────────────────┘
|
|
|
|
reserves
|
|
|
|
frqa_center res 1 'reserved registers that get cleared on startup
|
|
|
|
cnt_ticks res 1
|
|
cnt_value res 1
|
|
|
|
frame_index res 1
|
|
frame_ptr res 1
|
|
frame_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
|
|
|
|
{{
|
|
|
|
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
|
│ TERMS OF USE: MIT License │
|
|
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
|
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
|
|
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
|
|
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
|
|
│is furnished to do so, subject to the following conditions: │
|
|
│ │
|
|
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
|
|
│ │
|
|
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
|
|
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
|
|
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
|
|
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
|
|
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
}} |