code wiki / _hdl_build / nx_vox_tract.nx

nx_vox_tract.nx

buildroot/runtime/_hdl_build/nx_vox_tract.nx

12782 B254 linesdepth 4pulls 4 transitivereach 0 importersview sourcekind orphan library
docsdependenciesstructsconstsfunctions

about

nx_vox_tract.nx -- the TIMBRE half of animal-sound synthesis: a vocal tract as a physical tube. nx_vox_source produces a glottal flow. That flow sounds like a buzz until it passes through a throat, and the shape of the throat is most of what makes a rabbit sound like a rabbit and a dog sound like a dog. This organ is that throat, modelled as a chain of cylindrical sections. WHAT IS GENUINELY NEW HERE, AND WHAT IS DELIBERATELY NOT. A tree-wide audit ran before a line was written (operator: "make sure we arent duplicating stuff"), and it found that most of this problem is already solved: * nx_lpc_levinson ALREADY emits PARCOR reflection coefficients in Q30. Reflection coefficients are exactly what a tube chain is made of, so this organ adopts that representation verbatim rather than inventing a second one -- a tract built here is interchangeable with the voice stack's. * nx_lpc_synth ALREADY implements the all-pole filter in direct form. It is imported and USED. What did not exist anywhere in 16000+ files, and is therefore all this organ adds: * vt_area_to_k -- tube GEOMETRY (cross-sectional areas) -> reflection coefficients. The whole point: the existing coefficients are FITTED to a recording, these are DERIVED from a shape you can state, so a species can be described instead of sampled. * vt_k_to_a -- the step-up recursion, so a geometry can drive the existing direct-form filter. * vt_ladder -- the Kelly-Lochbaum scattering ladder. WHY A LADDER AT ALL, IF THE DIRECT FORM ALREADY EXISTS. They compute the same transfer function, so the ladder has to earn its place: it is the PHYSICAL structure. Each coefficient is one tube junction, forward and backward travelling waves are separately available, |k|<1 guarantees stability by construction, and a section can be moved while the filter runs (which is what articulation is). The direct form has none of that. To prove this is a second REALISATION and not a second IMPLEMENTATION, nx_vox_tract_gate asserts the ladder and nx_lpc_synth agree sample-for-sample on the same geometry. If they ever diverge, one of them is wrong and the gate says so. TRACT LENGTH SETS THE FORMANTS, and that is the species signature. For a uniform tube the resonances sit at (2n-1)*c/(4L), so a 150 mm dog throat puts its first formant near 570 Hz and a 15 mm mouse throat puts it near 5700 Hz. Section count is NOT free: a waveguide advances one section per sample, so N = 2*L*rate/c. The organ derives N rather than letting a caller pick a number that silently means a different tube than the one they asked for. license_tier: ORIGINAL expect_exit: 0

dependencies 2 imports · 0 importers

nx_syscalls.nx nx_lpc_synth.nx nx_vox_tract.nx

imports: nx_syscalls.nxnx_lpc_synth.nx

imported by: nobody (leaf or entry point)

structs

none

consts

40const VT_Q30: i64 = 1073741824 // 2^30 -- the PARCOR scale nx_lpc_levinson already uses
41const VT_C: i64 = 343000 // speed of sound, mm/s
42const VT_MAXSEC: i64 = 96 // sections; 96 covers a 170 mm tract at 96 kHz
43const VT_SHAPE_N: i64 = 8 // control points in a species shape profile
138const VT_SP_LEN: i64 = 0 // mm, glottis to lips
139const VT_SP_F0: i64 = 1 // Hz, typical fundamental for the source driving it
140const VT_SP_SHAPE: i64 = 2 // VT_SHAPE_N relative areas follow
141const VT_SP_N: i64 = 10 // 2 + VT_SHAPE_N
143const VT_NSPECIES: i64 = 5
144const VT_SP_RABBIT: i64 = 0
145const VT_SP_RODENT: i64 = 1
146const VT_SP_BIRD: i64 = 2
147const VT_SP_DOG: i64 = 3
148const VT_SP_UNIFORM: i64 = 4 // a plain tube: the control case, no shaping at all

functions

48func vt_nsec(len_mm: i64, rate: i64) -> i64
called by 1: vt_speak
60func vt_area_to_k(areas: *i64, nsec: i64, k_out: *i64) -> i64
called by 1: vt_speak
86func vt_k_to_a(k: *i64, order: i64, a_out: *i64) -> i64
calls 1: sys_mmap
110func vt_ladder(exc: *i64, n: i64, k: *i64, order: i64, out: *i64) -> i64
called by 1: vt_speak calls 1: sys_mmap
157func vt_species_defaults(tbl: *i64) -> i64
218func vt_species_areas(tbl: *i64, sp: i64, nsec: i64, areas: *i64) -> i64
called by 1: vt_speak
239func vt_f1_uniform(len_mm: i64) -> i64
245func vt_speak(tbl: *i64, sp: i64, exc: *i64, n: i64, rate: i64, out: *i64) -> i64