Audit acoustic graph DSP model

This commit is contained in:
Meta Vens 2026-05-28 12:07:05 +01:00
commit 450321d10f
3 changed files with 319 additions and 1 deletions

View file

@ -0,0 +1,315 @@
# Acoustic Graph DSP Audit
This audit asks the blunt question for each live graph-voice component:
- What is this simulating?
- Does the implementation still make sense?
- If not, what authority should own the correction?
The answer is mixed. The graph is no longer pure rubble, but several pieces are
still standing because they recently helped a metric rather than because they
have a clean physical contract.
## Pipeline Map
`PatchScript` builds neutral acoustic records:
1. `AcousticPath` owns tract or branch length, rest area, propagation speed,
loss, and optional live area controls.
2. `AcousticTerminal` marks topology, source, branch, and radiation positions
on paths.
3. `AcousticConnection` connects terminals into scatter groups.
4. `AcousticSourcePort` owns glottal, turbulence, reed, labial, click, or
synthetic excitation.
5. `AcousticRadiationPort` owns output boundary behavior.
6. `AcousticPortNetwork` names which paths, terminals, connections, sources,
radiation ports, and wave clock form one graph.
`FaustExport` lowers that graph into bidirectional delay state:
1. Terminals become graph nodes.
2. Adjacent nodes on each path become bidirectional segments.
3. Each segment stores right-going and left-going traveling waves.
4. Nodes scatter incoming waves into outgoing waves.
5. Source ports add energy at node context.
6. Radiation ports read boundary flow.
7. Segment delays propagate outgoing waves into the next sample state.
That is the right high-level architecture. The weak parts are inside the
lowering laws.
## Component Audit
### Topology Construction
What it simulates: fixed graph discretization of acoustic paths, with topology
terminals defining scattering locations.
Current state: mostly coherent. Interior source terminals no longer create
topology nodes; they attach to existing graph nodes. That fixed a real authority
leak.
Smells:
- Interior sources attach to the nearest node, not an interpolated point inside
a segment. Generated tract source banks compensate by creating many fixed
source terminals. That is expressive but heavy.
- A node can effectively belong to one connection through `nodeConnection`.
More complex graph topologies may need a first-class multi-connection node
model instead of assuming one branch connection owns the node.
- `terminalConnection` is built but not used. That is small rot.
Verdict: keep the model, but source placement wants an interpolated injection
primitive before this becomes a clean alien-tract substrate.
### Area And Geometry
What it simulates: time-varying tract cross-sectional area derived from rest
diameter plus tongue, constriction, and lip controls.
Current state: useful but simplistic. Diameter is squared into area, which is
the correct broad convention. Generated lip opening now owns path-end geometry
instead of also owning radiation opening.
Smells:
- Tongue/lip shaping is a hand-authored Gaussian blend, and constriction is a
min-like clamp. This is a serviceable control surface, not a principled
articulatory model.
- Graph propagation does not use `tract_motion` slew. Utterance fixtures smooth
controls externally, but generic graph voices can move morphology abruptly.
- Node area sums co-located non-source terminal areas. That is right for some
admittance joins and suspicious for ordinary labels sharing a location.
Verdict: acceptable as a compact morphology surface, but motion/smoothing must
move into graph area ownership instead of living only in fixture curves or the
old tract proxy.
### Wave Clock And Delay
What it simulates: travel time through each path segment.
Current state: conceptually correct. Segment delay follows physical length,
speed of sound, and the selected fractional-delay strategy.
Smells:
- Audio-rate delay modulation from live morphology can hit Faust fractional
delay primitives directly. That is flexible, but the graph has no explicit
policy for smoothing delay changes or preserving passivity during fast
topology-scale motion.
- Minimum-delay policy is hidden in `WaveClockMinimumDelay`; it deserves clearer
documentation per strategy.
Verdict: keep fractional delay. Add explicit delay-smoothing/passivity policy
before trusting violent morphology animation.
### Two-Port Path Scattering
What it simulates: Kelly-Lochbaum area-discontinuity scattering between two
adjacent tube sections.
Current state: coherent. The graph now uses the PT/Kelly-Lochbaum traveling-wave
convention:
`r = (A_left - A_right) / (A_left + A_right)`.
Smells:
- Source injection is added equally to both outgoing directions at a two-port
node. That is plausible as a symmetric pressure/flow source only when the
source is actually centered at the discontinuity. It is not yet a general
source impedance law.
Verdict: keep the scatter law. Revisit source injection weighting separately.
### Three-Port Branch Scattering
What it simulates: PT-style oral/nasal side branch scattering.
Current state: coherent for the one common case: two same-path ports plus one
side branch. Removing the hidden `0.15` velum damper fixed a real nasal failure.
Smells:
- The implementation recognizes one special topology shape. It is not yet a
general N-port passive junction.
- Source injection is added per port inside the branch law. That needs a clearer
impedance story if sources can live at branch nodes.
Verdict: good enough for nasal branches, not enough for arbitrary alien tract
graphs. General N-port scattering should replace the shape-specific path.
### Generic Connection Fallback
What it simulates: arbitrary connection scattering when a connection is not the
recognized three-port branch.
Current state: suspect. It computes a pressure-like expression, applies a
handmade port admittance blend, mixes with bypassed incoming waves, and injects
sources. This is exactly the kind of helper that survives because it is useful,
not because it has a single physical invariant.
Smells:
- `0.6 + 0.4 * sqrt(...)` is an unexplained compensator.
- It mixes pressure-continuity language into a graph whose main convention is
KL/PT traveling waves.
- It has no passivity proof.
Verdict: highest-priority Jenga. Replace with a general passive scattering law
or restrict supported connections until that exists.
### Glottal Source
What it simulates: voiced excitation at a source boundary.
Current state: better after local `tanh` normalization. Before that, the source
could dominate the tract as an overdriven buzzer.
Smells:
- The waveform is still a hand-shaped proxy, not an LF/Rosenberg model with
named open quotient, speed quotient, and return phase.
- `opening` is used as waveform skew in generated glottis records. That field
name is too generic for what it does here.
- Source injection bypasses boundary impedance; it is simply added into node
outgoing waves.
Verdict: acceptable as a bounded placeholder. Rename or split glottal shape
fields, then implement a real glottal flow source with impedance coupling.
### Turbulence And Release Source
What it simulates: pressure-driven constriction noise and a short opening
transient.
Current state: partially coherent. Sustained turbulence now has a local
pressure-drive term, and release pressure can observe node incident pressure.
Smells:
- The aperture gate constants `0.85`, `0.04`, source gains `0.62`, `1.2`,
`0.58`, and pressure-drive `0.50 + 1.50 * pressure` are not owned by a named
physical model.
- Release still detects local control opening, not stored upstream pressure
behind a sealed constriction.
- There is no graph-native reservoir state.
Verdict: keep as a pressure-informed source, but do not call this plosive
modeling yet. The next real primitive is closure reservoir state connected to
the scattering graph.
### Segment Loss
What it simulates: propagation loss plus extra loss in narrow/contact-like
sections.
Current state: directionally correct and audibly important. It stopped closed
sections from preserving wave energy as immortal buzz.
Smells:
- The `0.02` area constant is an exposed scar. It says "area-dependent loss"
but not whose units or morphology it belongs to.
- Loss is amplitude-only. There is no frequency-dependent wall/radiation loss
model.
Verdict: keep the authority, parameterize it. Path or material records should
own contact loss scale and frequency-dependent wall loss.
### Radiation
What it simulates: boundary flow emitted from lip, nostril, beak, or other
openings.
Current state: less wrong after cutting generated lip double gates. Radiation
now reads boundary flow and generated tract lip opening is path geometry, not
also output aperture.
Smells:
- The 30/70 raw/high-pass blend is a voicing scar. It approximates radiation
color, but it is not a clear radiation impedance model.
- The admittance expression `sqrt(area / (area + 1.0))` contains an unexplained
unit constant.
- `RadiationBoundaryReflectionExpression` is useful for standalone radiation
openings, but dangerous when the path end area already represents aperture.
Verdict: keep boundary-flow readout. Replace color/admittance constants with a
named radiation impedance primitive.
### Generated PT-Style Tract
What it simulates: a compact human vocal tract morphology expressed through
neutral graph records.
Current state: much healthier. It no longer relies on PT-shaped runtime modules,
and the accepted utterance smoke now passes without cross-word collapse.
Smells:
- It still generates many area terminals and many source terminals. That is a
compiled approximation of continuous morphology, not a beautiful abstraction.
- Nasal and lip reflection both bind to `lip_reflection`, matching PT pressure
but not a general anatomy model.
- `tract_motion` is not applied to graph area controls.
Verdict: good pressure fixture, not final architecture. Continuous/interpolated
source and area sampling would make it less PT-grid-shaped.
### Tests And Metrics
What they simulate: falsification pressure, not hearing.
Current state: better. Cross-utterance separability caught the metric lie.
Static closure-release no longer pretends a sealed static constriction has
time-history release energy.
Smells:
- Smoke floors are still survival rails, not acceptance criteria.
- Artifact WAVs are generated, but the harness has no durable listening verdict.
- Speech-band/articulation metrics are still weak proxies.
Verdict: keep the new separability check. Add windowed phone/gesture reports as
first-class artifacts because they exposed the nasal and rounded-vowel failures
faster than global scores.
## Cut List
Cut or replace these before piling on more tuning:
1. Generic connection fallback pressure/admittance blend.
2. Unowned radiation constants: 30/70 high-pass blend and `area + 1.0`.
3. Unowned segment contact-loss constant `0.02`.
4. Turbulence/release scalar constants without a named pressure-flow model.
5. Nearest-node source attachment as the only interior source placement model.
## Keep List
These foundations deserve to stay:
1. Path graph with typed terminals.
2. KL/PT traveling-wave convention for two-port and nasal branch scatter.
3. Source terminals not owning topology.
4. Velum as side-port area, not global branch damping.
5. Generated lip opening as path-end geometry, not output double gate.
6. Area-dependent segment loss as a named owner, once parameterized.
7. Cross-utterance separability as a hard guardrail.
## Next Coherent Build
The next implementation should not be another gain pass. Build a general
passive N-port junction and a closure reservoir primitive:
- N-port junction: one law for any connection group, using each connected port's
admittance/area, with a passivity probe and no shape-specific fallback.
- Closure reservoir: a stateful pressure store attached to a constriction owner,
charged while local area is sealed and released through the graph when area
opens.
- Radiation impedance: replace output coloring constants with a named local
radiation model.
Until those exist, the graph can imitate some Pink Trombone pressure, but it is
not yet a clean general vocal-acoustic machine.

View file

@ -195,3 +195,4 @@
{"evidence":"Added utterance separability diagnostics after user listening reported that mama and papa still sound like nearly identical phone vibrations despite flattering individual log-mel scores. The utterance harness now renders all accepted fixtures, writes `separability.txt`, and fails when candidate pairs are more mutually similar than reference pairs by more than 0.20 log-mel similarity. Latest artifact `artifacts/parity/pink-trombone-utterance-logmel/20260527T225833392` catches mama/papa collapse: referenceLogMel 0.3791, candidateLogMel 0.6069, referenceEnvelope 0.8670, candidateEnvelope 0.8885. Interior source terminals also stopped creating topology nodes or contributing to node area/reflection; they now attach to existing graph nodes and inject energy while geometry terminals own impedance.","claim":"Log-mel cosine can lie by rewarding shared filtered buzz. The parity harness must reject cross-utterance collapse, and graph source ports must inject into wave nodes without mutating the tract topology they are meant to excite.","timestamp":"2026-05-27T23:05:00+01:00","topic":"acoustic-graph-utterance-collapse-source-topology"}
{"evidence":"Generated tract lip radiation terminals no longer set terminal `area_scale` from the first frame's lip opening. Lip opening already drives live oral path area and radiation aperture, so the old terminal scale let a word starting with closed lips carry closed-lip admittance through later open vowels. Focused structural tests passed, and static PT graph artifact `artifacts/parity/pink-trombone-logmel/20260527T231611586` passed smoke. The utterance artifact `artifacts/parity/pink-trombone-utterance-logmel/20260527T231056242` still fails: thrombosis remains too silent and mama/papa candidate similarity is 0.6343 versus reference 0.3791.","claim":"Generated radiation terminal area scale is not a lip-opening owner. Removing the baked first-frame scale is a coherent authority cut even though it exposes the remaining source/reservoir/radiation coupling failure more harshly.","timestamp":"2026-05-27T23:20:00+01:00","topic":"acoustic-graph-lip-radiation-authority"}
{"evidence":"Repaired graph voice sample-flow ownership: bounded the glottal source with local tanh normalization, added area-dependent segment propagation loss, removed the hidden 0.15 generated nasal side-port damper so velum diameter squared owns nasal admittance, stopped generated tract lips from binding lip opening into radiation-port opening, and stopped multiplying graph boundary flow by aperture after boundary reflection/path geometry already handle opening. Focused structural tests, utterance smoke, and static smoke passed. Latest utterance artifact `artifacts/parity/pink-trombone-utterance-logmel/20260528T070759312`: mama/papa/thrombosis cosine 0.7918/0.8540/0.4366, candidate mama/papa separability 0.3651 versus reference 0.3791. Latest static artifact `artifacts/parity/pink-trombone-logmel/20260528T071240442` treats closure-release as sealed static with peak 0.000005.","claim":"The phone-vibration collapse came from multiple model ownership leaks, not one missing gain knob: unbounded source dominance, immortal closed-section wave storage, damped nasal admittance, and lip radiation double-gating. The graph is still quiet and under-articulated, but different articulations now drive different sample-flow paths. Next fix is true closure reservoir/release through the scattering graph.","timestamp":"2026-05-28T07:15:00+01:00","topic":"acoustic-graph-voice-model-repair"}
{"evidence":"Added `docs/acoustic-graph-dsp-audit.md`, auditing each graph-voice component by asking what it simulates and whether the implementation makes sense. The audit keeps path graphs with typed terminals, KL/PT two-port and nasal branch scatter, source/topology separation, velum side-port area, generated lip geometry ownership, area-dependent segment loss, and utterance separability. It marks the generic connection fallback pressure/admittance blend, radiation color/admittance constants, the segment loss constant, turbulence/release scalar constants, and nearest-node source attachment as Jenga. It recommends replacing the generic fallback with a passive general N-port junction, adding graph-native closure reservoir state, and replacing radiation constants with a named radiation impedance primitive.","claim":"The current graph voice is viable but not clean. Further tuning should start by cutting unowned fallback laws and constants, not by adding more source gain or metric-specific compensation.","timestamp":"2026-05-28T07:45:00+01:00","topic":"acoustic-graph-dsp-audit"}

View file

@ -1,6 +1,6 @@
project: AquaSynth
spine_version: 1
updated: 2026-05-28T07:15:00+01:00
updated: 2026-05-28T07:45:00+01:00
prime_directive: >
Build coherent synth machinery. References exist to reveal missing structure,
@ -46,6 +46,7 @@ current_mechanism:
pink_trombone_parity: docs/pink-trombone-parity.md
pink_trombone_dsl_parity_plan: docs/pink-trombone-dsl-parity-plan.md
physical_voice_modeling_research: docs/physical-voice-modeling-research.md
acoustic_graph_dsp_audit: docs/acoustic-graph-dsp-audit.md
physical_voice_modeling_packet: research/physical-voice-modeling
parameter_contracts: src/AquaSynth.Core/Model.cs
@ -124,6 +125,7 @@ live_invariants:
- PT utterance metrics now include cross-utterance separability because log-mel parity alone hid the user's actual listening failure. Artifact `artifacts/parity/pink-trombone-utterance-logmel/20260527T225833392` shows `mama/papa` candidate collapse: reference log-mel similarity 0.3791 versus candidate 0.6069, with candidate envelope similarity 0.8885. Interior source terminals no longer decide topology or node area/reflection; they attach to existing graph nodes and inject energy, while non-source terminals own geometry. Keep this source/topology cut even if other broken layers get louder.
- Generated lip radiation terminals no longer bake first-frame lip opening into terminal `area_scale`; lip opening owns live path area plus radiation aperture. This removes a stale initial-state authority where words starting with closed lips kept closed-lip admittance. Static PT graph artifact `artifacts/parity/pink-trombone-logmel/20260527T231611586` passes, while utterance artifact `artifacts/parity/pink-trombone-utterance-logmel/20260527T231056242` still fails `mama/papa` collapse and silent `thrombosis`. Next work remains source/reservoir/radiation coupling, not restoring terminal-scale double gates.
- Graph voice repair pass on 2026-05-28 fixed four model-level ownership leaks: glottal excitation is source-normalized with local `tanh`; segment propagation loss is area-dependent so closures absorb instead of ringing forever; generated nasal side-port area maps velum diameter squared directly instead of through the old hidden 0.15 damper; generated tract lips use live path-end geometry as the aperture owner and no longer double-gate radiation opening/boundary flow. Utterance artifact `artifacts/parity/pink-trombone-utterance-logmel/20260528T070759312` passes smoke and no longer collapses `mama/papa` (`referenceLogMel=0.3791`, `candidateLogMel=0.3651`); `thrombosis` improves to cosine 0.4366 but remains weak/quiet. Static artifact `artifacts/parity/pink-trombone-logmel/20260528T071240442` passes with `closure-release` demoted to sealed-static evidence (`peak=0.000005`). Next pressure is true graph-native closure reservoir/release through the scattering path.
- `docs/acoustic-graph-dsp-audit.md` is the current Jenga audit for graph voice DSP. It says the path graph, typed terminals, KL/PT two-port and nasal branch scatter, source/topology separation, velum side-port area, generated lip geometry ownership, area-dependent segment loss, and utterance separability should stay. It flags the generic connection fallback pressure/admittance blend, unowned radiation constants, unowned segment loss constant, turbulence/release scalar constants, and nearest-node source attachment as suspect. Next coherent build is a passive general N-port junction plus graph-native closure reservoir, then a named radiation impedance primitive.
- Graph source lowering now names per-node incident pressure and node-local source sums inside the recursive graph loop. Generated tract injection source banks now use cell-centered positions and `1 / sections` index scaling, so turbulence/release energy is born inside tube cells rather than directly on graph/radiation nodes. Latest source-position artifact under `artifacts/parity/pink-trombone-utterance-logmel/20260527T172949293`: mama/papa/thrombosis cosine 0.6654/0.7746/0.5522, rmsRatio 3.0010/2.5712/2.4943, articulation 0.1444/0.1177/0.1193. Static closure-release RMS improved to 1.0078 under `artifacts/parity/pink-trombone-logmel/20260527T172843945`, but papa articulation worsened; keep the cell-centered source placement because it is the correct graph-level source geometry, not because metrics liked it.
- TurbulenceJet now includes a release-timed burst-noise component in addition to the deterministic pressure pulse, but a stronger trial barely moved papa; the source is not the main remaining articulation bottleneck. Graph radiation now mixes 30% raw boundary flow with 70% high-passed flow after local admittance weighting, down from the over-bright 15/85 blend. Latest radiation/source artifact under `artifacts/parity/pink-trombone-utterance-logmel/20260527T174236049`: mama/papa/thrombosis cosine 0.6578/0.7692/0.5585, rmsRatio 3.0006/2.6153/2.3488, articulation 0.1437/0.1173/0.1191. Static `closure-release` under `artifacts/parity/pink-trombone-logmel/20260527T174139661` has rmsRatio 0.9900 and speechBandRatio 0.0842. This is still not speaking; next pressure is true tract resonance/passive scattering and pressure storage, not more burst gain.
- Same-path two-port area discontinuities now emit `graph_area_energy_in_*` and `graph_area_energy_out_*` probes alongside the three-port branch `graph_connection_energy_*` probes. Use these to inspect passivity and formant-body failures at the scatter sites rather than judging only final output loudness.