Clarify graph nasal coupling authority
This commit is contained in:
parent
67581dd2fa
commit
efa425483f
7 changed files with 15 additions and 11 deletions
|
|
@ -224,8 +224,9 @@ terminal-specific admittance in connection pressure instead of collapsing every
|
|||
co-located terminal into one node area, and branch output uses a floored
|
||||
area-ratio pressure normalization so weak side ports no longer receive full
|
||||
junction pressure. The generated tract graph also stopped treating the first
|
||||
nasal tube diameter as the velum aperture; velum now owns branch coupling and
|
||||
aperture. Two tempting compensators were cut: raw per-port outgoing coupling
|
||||
nasal tube diameter as the velum aperture; velum now owns branch entrance area
|
||||
and nasal aperture, while connection coupling remains a structural admittance
|
||||
limit. Two tempting compensators were cut: raw per-port outgoing coupling
|
||||
made supposedly oral fixtures nearly silent, and lip/beak radiation gain did
|
||||
not restore oral vowel RMS. The internal-node scatter fix made unconnected
|
||||
injection/source terminals transmit pressure between adjacent segments instead
|
||||
|
|
|
|||
|
|
@ -154,8 +154,10 @@ Trombone anatomy:
|
|||
controls consumed by tract voices; graph injection maps burst into source
|
||||
`transient` rather than a PT-specific side path.
|
||||
- `nasal_branch` adds a second diameter/area tube and generated oral/nasal
|
||||
connection terminals. Velum owns branch coupling and nasal aperture; the first
|
||||
nasal tube sample is not allowed to masquerade as the velum opening.
|
||||
connection terminals. Velum owns branch entrance area and nasal aperture;
|
||||
connection coupling is a structural admittance limit, not a second velum
|
||||
writer. The first nasal tube sample is not allowed to masquerade as the velum
|
||||
opening.
|
||||
- generated graph records mirror live `tract` parameter bindings into acoustic
|
||||
source, terminal, connection, radiation, and path-area fields so `/pink/...`
|
||||
controls exercise the actual graph.
|
||||
|
|
|
|||
|
|
@ -849,7 +849,7 @@ public static class FaustEmitter
|
|||
var detune = port.Kind == AcousticSourceKind.Labial ? $" * (1.0 + ({balance} - 0.5) * 0.018)" : "";
|
||||
var phase = $"os.phasor(1.0, {frequency}{detune})";
|
||||
var closure = $"clip01((0.12 - ({opening})) / 0.12)";
|
||||
var release = $"(max(0.0, (({closure}) : mem) - ({closure})) : + ~ *(0.90))";
|
||||
var release = $"(max(0.0, (({closure}) : mem) - ({closure})) : + ~ *(0.995))";
|
||||
return port.Kind switch
|
||||
{
|
||||
AcousticSourceKind.Glottal =>
|
||||
|
|
@ -859,7 +859,7 @@ public static class FaustEmitter
|
|||
AcousticSourceKind.Reed =>
|
||||
$"(ma.tanh(sin(2.0 * ma.PI * {phase}) * (1.0 + {pressure} * 8.0)) * {opening} + no.noise * {noise} * 0.2) * {balance}",
|
||||
AcousticSourceKind.TurbulenceJet =>
|
||||
$"((no.noise : fi.highpass(2, 900.0 + 2600.0 * clip01(1.0 - {opening}))) * {noise} * {pressure} * min(clip01((0.8 - {opening}) / 0.8), clip01(8.0 * (0.7 - {opening})) * clip01(30.0 * ({opening} - 0.3))) * 0.62 + (no.noise : fi.highpass(1, 450.0)) * {transient} * {pressure} * {release} * (0.8 + 0.8 * clip01({opening} / 0.8))) * {balance}",
|
||||
$"((no.noise : fi.highpass(2, 900.0 + 2600.0 * clip01(1.0 - {opening}))) * {noise} * {pressure} * min(clip01((0.8 - {opening}) / 0.8), clip01(8.0 * (0.7 - {opening})) * clip01(30.0 * ({opening} - 0.3))) * 0.62 + {transient} * {pressure} * {release} * 0.58 * (0.8 + 0.8 * clip01({opening} / 0.8))) * {balance}",
|
||||
AcousticSourceKind.Click =>
|
||||
$"no.noise * {pressure} * max({opening}, {transient}) * exp(0.0 - age * 120.0) * {balance}",
|
||||
AcousticSourceKind.Synthetic =>
|
||||
|
|
|
|||
|
|
@ -1417,7 +1417,7 @@ public static class PatchScript
|
|||
0,
|
||||
AcousticBranchKind.Nasal,
|
||||
nasal.Velum,
|
||||
1,
|
||||
0.15f,
|
||||
true);
|
||||
AddAcousticBranchRecord(branch);
|
||||
AddAcousticTerminalRecord(new AcousticTerminal(
|
||||
|
|
@ -1434,13 +1434,11 @@ public static class PatchScript
|
|||
AcousticTerminalKind.Junction,
|
||||
branch.Name,
|
||||
MathF.Pow(Math.Max(0, branch.Opening), 2) / Math.Max(0.000001f, nasalArea.Diameters[0] * nasalArea.Diameters[0])));
|
||||
var branchConnectionIndex = _acousticConnections.Count;
|
||||
AddAcousticConnectionRecord(new AcousticConnection(
|
||||
BranchConnectionName(branch),
|
||||
[BranchFromTerminalName(branch), BranchToTerminalName(branch)],
|
||||
AcousticConnectionLaw.AreaScattering,
|
||||
branch.Coupling));
|
||||
MirrorParameterBinding(OwnerField(tractPath, "velum"), $"/acoustic/connections/{branchConnectionIndex}/coupling");
|
||||
var nasalRadiation = new AcousticRadiationPort(
|
||||
$"{nasalPathName}_radiation",
|
||||
nasalPathName,
|
||||
|
|
|
|||
|
|
@ -177,3 +177,4 @@
|
|||
{"evidence":"After switching graph radiation from raw pressure to boundary flow, recalibrated the graph radiation gain from 3.0 to 4.0. A 4.5 trial improved utterance RMS but pushed static fixtures closer to soft-clip influence, so 4.0 was kept. Focused utterance parity passed with artifacts/parity/pink-trombone-utterance-logmel/20260527T124230804: mama cosine 0.5712 RMS 0.4431, papa 0.8659 RMS 0.1768, thrombosis 0.4379 RMS 0.3324. Static parity passed with artifacts/parity/pink-trombone-logmel/20260527T124311305: open/front/nasal/ma/sibilant/closure cosine 0.5942/0.6710/0.6369/0.5955/0.4201/0.6143.","claim":"Radiation gain is part of the post-flow normalization contract, not a substitute for articulation. The kept value improves underpowered utterance output while preserving static guardrails; pressure storage/release remains the actual consonant problem.","timestamp":"2026-05-27T12:45:00+01:00","topic":"acoustic-radiation-flow-normalization"}
|
||||
{"evidence":"Added `AudioArticulationComparison` to `AudioComparison` and updated Pink Trombone parity reports with envelope cosine, active-frame ratio, silence mismatch, envelope/spectral flux ratios, motor-band ratio, speech-band ratio, articulation score, and utterance verdicts. Focused tests passed. Latest utterance artifact artifacts/parity/pink-trombone-utterance-logmel/20260527T130026316 reports mama/papa/thrombosis as `not-accepted-articulation` with articulation scores 0.1930/0.1829/0.1848 despite log-mel cosine 0.5712/0.8659/0.4379. Static reports also expose weak speech-band ratios instead of hiding behind cosine.","claim":"Log-mel cosine is a spectral witness, not a speech-articulation authority. The parity harness must surface silence-map, envelope, flux, motor-band, and speech-band failures so filtered buzz cannot forge a listening receipt.","timestamp":"2026-05-27T13:05:00+01:00","topic":"voice-articulation-metric-witness"}
|
||||
{"evidence":"Segment delay lowering no longer clamps physical acoustic graph segments to one sample before wave-clock lowering; `WaveClockPolicy` now owns the legal delay floor. Generated `tract propagation=graph` patches use `HalfSampleGrid` instead of first-order Thiran, matching the PT/Kelly-Lochbaum half-sample regime. Focused graph, Faust validation, utterance, and static parity tests passed. Latest utterance artifact artifacts/parity/pink-trombone-utterance-logmel/20260527T133539554: mama cosine/articulation 0.5982/0.1952, papa 0.8615/0.1979, thrombosis 0.5217/0.2640. Latest static artifact artifacts/parity/pink-trombone-logmel/20260527T133424894. Re-testing the literal SndKit reflection sign made thrombosis loud/unstable; adding a broader glottal harmonic tail worsened voiced-band behavior, so both were cut.","claim":"Physical tract length must lower to sub-sample graph delays; forcing each section to one sample made the tract acoustically overlong and hid failures behind a low resonance crutch. Half-sample graph timing is the correct next foundation, but articulation still fails because body/radiation and pressure-release behavior are incomplete.","timestamp":"2026-05-27T13:40:00+01:00","topic":"acoustic-graph-half-sample-clock"}
|
||||
{"evidence":"Focused PT tests passed. Utterance artifact artifacts/parity/pink-trombone-utterance-logmel/20260527T150836912: mama/papa/thrombosis cosine 0.8337/0.8893/0.5317, rmsRatio 1.0768/1.5626/0.4004, articulation 0.2118/0.1890/0.2594. Static artifact artifacts/parity/pink-trombone-logmel/20260527T150749857: open/front/nasal/ma/sibilant/closure cosine 0.6942/0.6732/0.7386/0.7067/0.4548/0.5449. A long noisy release transient was cut because it drove papa motor-band ratio to 47.0313.","claim":"Generated graph nasal branches no longer mirror velum into connection coupling. Velum owns nasal entrance area and nostril aperture; `AcousticConnection.Coupling` is a structural max-admittance limit. A fixed 0.15 generated nasal coupling avoids the previous double gate without flooding the graph. `TurbulenceJet` transient lowering now emits a short pressure pulse rather than filtered noise, but true plosive intelligibility still needs graph pressure storage/release around severe closures.","timestamp":"2026-05-27T15:03:00+01:00","topic":"pink-trombone-nasal-coupling-authority"}
|
||||
|
|
|
|||
|
|
@ -119,8 +119,8 @@ live_invariants:
|
|||
- The physical voice research packet now includes a practical implementation/researcher map (`research/physical-voice-modeling/PEOPLE.md`) and talk/transcript ledger (`research/physical-voice-modeling/talks/TALKS.md`). Birkholz/VocalTractLab pressures multi-level articulatory control and gestures; Story/TubeTalker pressures continuous parametric area functions and length/coupling; Smith pressures bidirectional delay-line scattering and fractional-delay waveguides; Fels/ArtiSynth pressures separation between biomechanics, acoustics, timeline controls, and diagnostics. These are reusable DSL semantics, not modules to clone.
|
||||
- Physical voice implementation has started moving PT pressure into neutral acoustic owners: `AcousticPath`, `AcousticTerminal`, `AcousticConnection`, `AcousticSourcePort`, `AcousticBranch`, `AcousticRadiationPort`, `WaveClockPolicy`, and `AcousticPortNetwork`. Parser commands `path`, `terminal`, `connect`, `source_port`, `branch`, `radiation_port`, `wave_clock`, and `acoustic_network` now exist. Existing `tract_shape`, `glottis`, `tract_injection`, `nasal_branch`, and `tract` commands populate acoustic records as compatibility aliases while the current `Voice.Tract` Faust renderer still owns audio.
|
||||
- `acoustic` / `acoustic_voice` can now render an `AcousticPortNetwork` through a compact Faust response proxy or, when typed terminals are present, a compiled bidirectional Faust segment graph. Graph lowering splits paths at terminal positions, aggregates co-located terminals into graph nodes, orders connection ports deterministically, scatters area-weighted/pressure/bypass connection groups, injects source terminals even at connected nodes, and sums radiation terminals. Segment delay follows physical segment length and the network `wave_clock` strategy through unit-grid, half-sample, linear fractional, Lagrange, or Thiran/allpass Faust delay primitives. Source, branch, radiation, terminal, and connection controls can bind through ordinary Aqua parameters into emitted Faust. PT-shaped `tract` authoring now routes accepted parity through `propagation=graph`; the old waveguide parity lane is not a comparison baseline or historical pressure lane.
|
||||
- Pink Trombone log-mel parity now writes graph candidates only. Generated tract graphs mirror live `/pink/...` tract bindings into acoustic source, terminal, connection, radiation, and path-area fields. Graph generation stopped baking first-frame tongue/constriction/lip morphology into the path; `AcousticPath.AreaControl` now turns tongue/constriction/lip controls into live area expressions at graph sample points. Velum owns nasal branch coupling/aperture, and the first nasal tube sample is no longer allowed to decide the velum opening. Graph radiation now uses aperture as a boundary impedance owner: lip/beak opening blends boundary reflection from near-closed termination to declared open-end reflection, and radiation reads boundary flow (`incoming - outgoing`) through a stronger radiation slope instead of raw incoming pressure. Latest static graph evidence: open/front/nasal/ma/sibilant/closure cosine 0.6088/0.6826/0.6468/0.6153/0.4158/0.6198.
|
||||
- PT expressive parity should decompose into reusable Aqua primitives before audio golf: `tract_shape` owns continuous diameter/area geometry over normalized position, physical length (`length_cm`), interpolation/resampling, derived reflection coefficients, and per-cell acoustic delay; `AcousticAreaControl` owns live sampled morphology controls on a path; `WaveClockPolicy` owns the legal delay floor instead of forcing every segment to one sample; generated graph tracts now use `HalfSampleGrid` to match the PT/Kelly-Lochbaum half-sample regime; ordinary same-path graph interiors now own signed two-port area-discontinuity scattering; `glottis` owns excitation controls with mild harmonic waveshaping and a small higher-harmonic brightness term for graph sources; `tract_injection` owns positioned frication/burst controls; `AcousticSourcePort.Transient` owns graph-native release/click energy; sustained `TurbulenceJet` lowering owns a narrow-open aperture gate so closed constrictions no longer hiss; `AcousticSourcePositionControl` owns differentiable runtime weights over fixed source terminals so moving articulators can move source energy without recompiling the graph; `AcousticRadiationPort` owns aperture-dependent boundary reflection, flow radiation, and current post-flow normalization; `nasal_branch` owns a second diameter/area tube and branch aperture; `tract_motion` owns control slew and obstruction threshold; `AcousticPortNetwork` owns graph lowering. `PinkTromboneReferenceRenderer` ports the pinned MIT `chdh/pink-trombone-mod` DSP source (`external/pink-trombone-mod` at 359c2d3b42b10280404c1650dc601902112b4c90), and reference WAVs before `artifacts/parity/pink-trombone-utterances/20260526T211902375` are invalid. The accepted PT utterance curriculum is mama, papa, and thrombosis; lulek was cut because its control sketch taught a bad artifact. Latest graph-only utterance smoke under `artifacts/parity/pink-trombone-utterance-logmel/20260527T133539554`: mama/papa/thrombosis remain `not-accepted-articulation` with articulation scores 0.1952/0.1979/0.2640, but the half-sample clock improves `thrombosis` motor-band behavior and removes an overlong-tract crutch. `AudioComparison.Articulation` now reports envelope cosine, active-frame ratio, silence mismatch, envelope/spectral flux ratios, motor-band ratio, speech-band ratio, and an articulation score so log-mel cannot forge a listening receipt. A direct LF coefficient source was tested and cut because it overdrove the graph and broke static open vowel; a broader harmonic-tail source was tested and cut because it worsened voiced-band behavior. A naive midpoint admittance scatter was tested and cut because it collapsed `mama`; the kept area-discontinuity scatter needed the opposite reflection sign for this graph state convention, and re-testing the literal SndKit sign after the clock fix made `thrombosis` loud/unstable. Next pressure is graph-native storage/release around severe constrictions plus nasal/oral branch coupling; output coloring alone is not allowed to impersonate articulation.
|
||||
- Pink Trombone log-mel parity now writes graph candidates only. Generated tract graphs mirror live `/pink/...` tract bindings into acoustic source, terminal, radiation, and path-area fields. Graph generation stopped baking first-frame tongue/constriction/lip morphology into the path; `AcousticPath.AreaControl` now turns tongue/constriction/lip controls into live area expressions at graph sample points. Velum owns nasal branch entrance area/aperture, while connection coupling is a structural admittance limit and no longer mirrors velum as a second gate. The first nasal tube sample is no longer allowed to decide the velum opening. Graph radiation now uses aperture as a boundary impedance owner: lip/beak opening blends boundary reflection from near-closed termination to declared open-end reflection, and radiation reads boundary flow (`incoming - outgoing`) through a stronger radiation slope instead of raw incoming pressure. Latest static graph evidence after structural nasal coupling is under `artifacts/parity/pink-trombone-logmel/20260527T150749857`: open/front/nasal/ma/sibilant/closure cosine 0.6942/0.6732/0.7386/0.7067/0.4548/0.5449.
|
||||
- PT expressive parity should decompose into reusable Aqua primitives before audio golf: `tract_shape` owns continuous diameter/area geometry over normalized position, physical length (`length_cm`), interpolation/resampling, derived reflection coefficients, and per-cell acoustic delay; `AcousticAreaControl` owns live sampled morphology controls on a path; `WaveClockPolicy` owns the legal delay floor instead of forcing every segment to one sample; generated graph tracts now use `HalfSampleGrid` to match the PT/Kelly-Lochbaum half-sample regime; ordinary same-path graph interiors now own signed two-port area-discontinuity scattering; `glottis` owns excitation controls with mild harmonic waveshaping and a small higher-harmonic brightness term for graph sources; `tract_injection` owns positioned frication/burst controls; `AcousticSourcePort.Transient` owns graph-native release/click energy as a pressure pulse rather than filtered noise; sustained `TurbulenceJet` lowering owns a narrow-open aperture gate so closed constrictions no longer hiss; `AcousticSourcePositionControl` owns differentiable runtime weights over fixed source terminals so moving articulators can move source energy without recompiling the graph; `AcousticRadiationPort` owns aperture-dependent boundary reflection, flow radiation, and current post-flow normalization; `nasal_branch` owns a second diameter/area tube and branch aperture while `AcousticConnection.Coupling` owns structural max admittance; `tract_motion` owns control slew and obstruction threshold; `AcousticPortNetwork` owns graph lowering. `PinkTromboneReferenceRenderer` ports the pinned MIT `chdh/pink-trombone-mod` DSP source (`external/pink-trombone-mod` at 359c2d3b42b10280404c1650dc601902112b4c90), and reference WAVs before `artifacts/parity/pink-trombone-utterances/20260526T211902375` are invalid. The accepted PT utterance curriculum is mama, papa, and thrombosis; lulek was cut because its control sketch taught a bad artifact. Latest graph-only utterance smoke under `artifacts/parity/pink-trombone-utterance-logmel/20260527T150836912`: mama/papa/thrombosis remain `not-accepted-articulation` with cosine 0.8337/0.8893/0.5317 and articulation scores 0.2118/0.1890/0.2594. `AudioComparison.Articulation` reports envelope cosine, active-frame ratio, silence mismatch, envelope/spectral flux ratios, motor-band ratio, speech-band ratio, and an articulation score so log-mel cannot forge a listening receipt. A direct LF coefficient source was tested and cut because it overdrove the graph and broke static open vowel; a broader harmonic-tail source was tested and cut because it worsened voiced-band behavior. A naive midpoint admittance scatter was tested and cut because it collapsed `mama`; the kept area-discontinuity scatter needed the opposite reflection sign for this graph state convention, and re-testing the literal SndKit sign after the clock fix made `thrombosis` loud/unstable. A long noisy release transient was tested and cut because it turned papa into motor-band energy; deterministic pressure release with PT-scale decay is structurally kept but still not sufficient. Next pressure is true graph pressure storage/release at severe closures; output coloring alone is not allowed to impersonate articulation.
|
||||
- If dotnet builds hang in this workspace, stop orphan dotnet workers and retry with `--disable-build-servers -p:UseSharedCompilation=false -p:BuildInParallel=false`; plain parallel/shared builds can stall without useful diagnostics.
|
||||
- eSpeak NG is an early parity reference for timing, IPA coverage, and log-mel pressure, not an anatomy oracle or final target.
|
||||
- Do not expand into prosody, emotional context, personality, or broad Weksa expressivity until the tiny IPA utterance harness can show useful loss movement.
|
||||
|
|
|
|||
|
|
@ -638,6 +638,8 @@ public sealed class PatchScriptTests
|
|||
Assert.Contains(patch.AcousticPaths, path => path.Name == "voices_0_oral" && path.AreaControl is not null);
|
||||
Assert.Contains(patch.AcousticSourcePorts, port => port.Name.StartsWith("voices_0_inj_", StringComparison.Ordinal) && port.Transient > 0 && port.PositionControl is not null);
|
||||
Assert.Contains(patch.AcousticTerminals, terminal => terminal.Name.StartsWith("voices_0_area_", StringComparison.Ordinal));
|
||||
Assert.Contains(patch.AcousticConnections, connection => connection.Name.Contains("_nose_connection", StringComparison.Ordinal) && MathF.Abs(connection.Coupling - 0.15f) < 0.0001f);
|
||||
Assert.DoesNotContain(patch.ParameterBindings, binding => binding.FieldPath.StartsWith("/acoustic/connections/", StringComparison.OrdinalIgnoreCase) && binding.FieldPath.EndsWith("/coupling", StringComparison.OrdinalIgnoreCase));
|
||||
Assert.Contains("acoustic_graph_radiated", export.Source);
|
||||
Assert.Contains("graph_terminal_area_voices_0_area_", export.Source);
|
||||
Assert.DoesNotContain("wg_diameter_target_", export.Source);
|
||||
|
|
|
|||
Loading…
Reference in a new issue