mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50: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.                         │
 | |
| └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
 | |
| }} |