code wiki / _hdl_build / nx_squeaker.nx
nx_squeaker.nx
buildroot/runtime/_hdl_build/nx_squeaker.nx
about
nx_squeaker.nx -- THE TOY. A dog-toy ball that makes its noise because a dog's jaw compresses it.
Operator 2026-07-25: "a dog toy ball that is designed to make the noise when compressed by a dog's
mouth". This is the physical model of that object, from the bite down:
jaw force -> shell compression -> air displaced -> chamber pressure -> reed valve flutters
-> flow through the neck -> radiated sound
Every stage is a real physical quantity in manufacturable units (newtons, millimetres, cubic
millimetres), because the whole point of rung R5 is to INVERT this model and hand back a geometry
somebody can actually mould. A model in arbitrary units cannot produce a spec.
WHY THE PITCH GLIDES, AND WHY THAT IS THE PRODUCT. The chamber is a Helmholtz resonator:
w = c * sqrt(A_neck / (V_chamber * L_neck))
A dog's bite REMOVES volume from the chamber, and w rises as V falls. So the sound a dog produces
by biting is not a fixed beep -- it is a rising squeal whose contour is drawn by the dog's own jaw.
Prey distress calls rise in pitch too. That correspondence is the reason this object can sound like
something worth catching instead of like a rubber toy, and it is measurable: the gate checks that a
harder bite raises the pitch and that a bigger chamber lowers it, both in the direction the physics
demands rather than the direction I would like.
THE REED IS THE SAME PHYSICS AS A VOCAL FOLD. A squeaker tongue and a larynx are both elastic
valves driven unstably by the flow past them, so this organ does not implement a second oscillator:
it drives nx_vox_source with the chamber pressure and takes its flow back. That reuse is not just
DRY, it is why the toy inherits nonlinear regimes -- the roughness, the pitch breaks and the
cycle-to-cycle variation that stop a dog habituating -- from a model already gated at 13/13.
Integer fixed-point, deterministic. license_tier: ORIGINAL expect_exit: 0
dependencies 3 imports · 1 importers
imports: nx_syscalls.nxnx_isqrt.nxnx_vox_source.nx
imported by: nx_squeaker_design.nx
structs
| none |
consts
| 34 | const SQ_Q: i64 = 65536 |
| 35 | const SQ_C: i64 = 343000 // speed of sound, mm/s |
| 38 | const SQ_P_V0: i64 = 0 // mm^3 chamber volume at rest |
| 39 | const SQ_P_AN_X100: i64 = 1 // mm^2 neck cross-section, x100 |
| 40 | const SQ_P_LN_X100: i64 = 2 // mm neck length, x100 |
| 41 | const SQ_P_DVDF_X100: i64 = 3 // mm^3/N volume displaced per newton of bite, x100 (shell durometer) |
| 42 | const SQ_P_BITE_N: i64 = 4 // N peak bite force |
| 43 | const SQ_P_RISE_MS: i64 = 5 // ms jaw closing time |
| 44 | const SQ_P_HOLD_MS: i64 = 6 // ms |
| 45 | const SQ_P_REL_MS: i64 = 7 // ms |
| 46 | const SQ_P_ZETA: i64 = 8 // Q16 acoustic damping of the resonator |
| 47 | const SQ_P_REED_F0: i64 = 9 // Hz reed natural frequency |
| 48 | const SQ_P_REED_DET: i64 = 10 // Hz second-reed detune -> nonlinear regimes |
| 49 | const SQ_P_REED_ZETA: i64 = 11 // Q16 |
| 50 | const SQ_P_FLOWGAIN: i64 = 12 // Q16 reed opening -> neck conductance |
| 51 | const SQ_P_PGAIN: i64 = 13 // Q16 chamber pressure -> reed drive |
| 52 | const SQ_P_OVS: i64 = 14 |
| 53 | const SQ_P_SEED: i64 = 15 |
| 54 | const SQ_P_JITTER: i64 = 16 // Q16 |
| 55 | const SQ_P_N: i64 = 17 |
| 84 | const SQ_S_P: i64 = 0 // chamber overpressure (Q16, normalised) |
| 85 | const SQ_S_U: i64 = 1 // neck volume flow (Q16) |
| 86 | const SQ_S_VOL: i64 = 2 // current chamber volume, mm^3 |
| 87 | const SQ_S_N: i64 = 3 |
| 133 | const SQ_D_PPEAK: i64 = 0 |
| 134 | const SQ_D_VEND: i64 = 1 |
| 135 | const SQ_D_CYC: i64 = 2 |
| 136 | const SQ_D_N: i64 = 3 |
functions
| 57 | func sq_defaults(p: *i64) -> i64 called by 1: sqd_to_params |
| 93 | func sq_omega(p: *i64, vol: i64, sr: i64) -> i64 |
| 110 | func sq_bite(p: *i64, i: i64, sr: i64) -> i64 called by 1: sq_render |
| 138 | func sq_render(out: *i64, n: i64, rate: i64, p: *i64, diag: *i64) -> i64 |
| 250 | func sq_render_audio(out: *i64, n: i64, rate: i64, p: *i64, amp: i64, diag: *i64) -> i64 calls 1: sq_render |
| 275 | func sq_f_at(p: *i64, vol: i64) -> i64 |
| 283 | func sq_vol_bitten(p: *i64) -> i64 called by 1: sq_f_bitten |
| 289 | func sq_f_rest(p: *i64) -> i64 { return sq_f_at(p, p[SQ_P_V0]) } |
| 290 | func sq_f_bitten(p: *i64) -> i64 { return sq_f_at(p, sq_vol_bitten(p)) } |