From 4a3b16bddd5657b8fbd5c39ef1e7bad08d6b07f4 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 29 May 2026 16:52:03 +0100 Subject: [PATCH] Write PT primitive timeline artifacts --- docs/acoustic-graph-proprioception.md | 6 ++- state/evidence.jsonl | 1 + state/memory.json | 2 +- state/scratch.md | 5 ++ state/spine.yaml | 1 + .../PinkTromboneLogMelParityTests.cs | 52 +++++++++++++++++++ 6 files changed, 65 insertions(+), 2 deletions(-) diff --git a/docs/acoustic-graph-proprioception.md b/docs/acoustic-graph-proprioception.md index 6930a20..d907a51 100644 --- a/docs/acoustic-graph-proprioception.md +++ b/docs/acoustic-graph-proprioception.md @@ -240,10 +240,14 @@ PT/SndKit comparison report: path, branch, contact, and radiation timeline fields from its own arrays, and `PrimitiveReferenceReport.ComparePinkTromboneTimeline` role-normalizes those fields against Aqua primitive timelines. +- `PinkTrombonePrimitiveTimelineComparisonWritesArtifacts` writes per-fixture + Aqua timelines, PT internal timelines, comparison CSVs, and a summary under + `artifacts/parity/primitive-vocal-timeline`. The next implementation pass should harden the primitives further: -- turn the timeline comparison into artifact reports for the PT fixture suite; +- use the artifact reports to choose the first physical-law correction in the + primitive machine; - add optional VTL/ArtiSynth adapters only as artifact comparators, not default CI dependencies; - keep `mama`, `papa`, and `thrombosis` as downstream witnesses until primitive diff --git a/state/evidence.jsonl b/state/evidence.jsonl index bf0cc6c..88c5aac 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -229,3 +229,4 @@ {"date":"2026-05-29","area":"primitive-vocal-rebuild","summary":"First teardown cut implemented. PT-shaped tract authoring now adapts into primitive vocal records and no longer emits generated area/contact/injection acoustic banks. The deleted `EnsureTractAcousticNetwork` path makes the old generated graph structurally unable to own default vocal DSP. `VocalNetwork` lowering emits compact primitive Faust locals with delay-library propagation and debug probes, and `ProbeTimelineReport` writes deterministic primitive CSV telemetry before utterance parity. PatchScriptTests pass 84/84, including optional primitive Faust validation when Faust is installed.","evidence":"src/AquaSynth.Core/Model.cs; src/AquaSynth.Core/PatchScript.cs; src/AquaSynth.Core/FaustExport.cs; src/AquaSynth.Core/ProbeTimelineReport.cs; tests/AquaSynth.Dsl.Tests/PatchScriptTests.cs; docs/acoustic-graph-proprioception.md"} {"date":"2026-05-29","area":"primitive-vocal-telemetry","summary":"Primitive timelines now expose path incoming/outgoing wave variables, area-weighted energy in/out, passivity ratio, and contact resistance. Faust debug probes expose matching path wave/passivity fields. `PrimitiveReferenceReport.ComparePinkTrombone` adds a PT/SndKit-facing static comparison report for area sections/length/reflection count, delay estimate, branch admittance, contact state, and radiation fields. PatchScriptTests pass 86/86.","evidence":"src/AquaSynth.Core/FaustExport.cs; src/AquaSynth.Core/ProbeTimelineReport.cs; src/AquaSynth.Core/PrimitiveReferenceReport.cs; tests/AquaSynth.Dsl.Tests/PatchScriptTests.cs; docs/acoustic-graph-proprioception.md"} {"date":"2026-05-29","area":"primitive-vocal-reference-timeline","summary":"PT/SndKit local reference internals are now observable through `PinkTromboneReferenceRenderer.RenderTimeline`, exposing oral/nasal path waves and energy, velopharynx branch reflections/admittance, obstruction contact state, and lip radiation flow/output from the renderer's own arrays. `PrimitiveReferenceReport.ComparePinkTromboneTimeline` role-normalizes Aqua primitive timeline samples against PT timeline samples. PatchScriptTests pass 88/88.","evidence":"src/AquaSynth.Core/PinkTromboneReferenceRenderer.cs; src/AquaSynth.Core/PrimitiveReferenceReport.cs; tests/AquaSynth.Dsl.Tests/PatchScriptTests.cs; docs/acoustic-graph-proprioception.md"} +{"date":"2026-05-29","area":"primitive-vocal-artifacts","summary":"Added a PT fixture-suite primitive timeline artifact test. Each fixture writes Aqua primitive timeline CSV, PT internal timeline CSV, role-normalized comparison CSV, and a summary under `artifacts/parity/primitive-vocal-timeline/`. The focused artifact test passes without native Faust or external reference binaries.","evidence":"tests/AquaSynth.Dsl.Tests/PinkTromboneLogMelParityTests.cs; src/AquaSynth.Core/PinkTromboneReferenceRenderer.cs; src/AquaSynth.Core/PrimitiveReferenceReport.cs; docs/acoustic-graph-proprioception.md"} diff --git a/state/memory.json b/state/memory.json index 0016401..207bd86 100644 --- a/state/memory.json +++ b/state/memory.json @@ -121,7 +121,7 @@ }, "last_verified": { "command": "dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-restore --disable-build-servers -p:UseSharedCompilation=false -p:BuildInParallel=false --filter \"FullyQualifiedName~PatchScriptTests\" -v minimal", - "result": "Primitive vocal telemetry/reference cut passes PatchScriptTests 88/88. Path timelines now include wave/passivity fields, PT/SndKit static primitive comparison is covered, and the local PT renderer exposes internal primitive timeline samples for role-normalized comparison. Earlier full unfiltered run hit the five-minute wall after leaving orphan dotnet workers; focused suites are the current reliable verification path.", + "result": "Primitive vocal telemetry/reference cut passes PatchScriptTests 88/88 and the focused PT primitive timeline artifact test. Path timelines now include wave/passivity fields, PT/SndKit static primitive comparison is covered, the local PT renderer exposes internal primitive timeline samples, and per-fixture comparison artifacts are written under artifacts/parity/primitive-vocal-timeline. Earlier full unfiltered run hit the five-minute wall after leaving orphan dotnet workers; focused suites are the current reliable verification path.", "commit": "pending primitive vocal teardown" } } diff --git a/state/scratch.md b/state/scratch.md index 3be2b31..3e0d55d 100644 --- a/state/scratch.md +++ b/state/scratch.md @@ -40,6 +40,11 @@ Current slice: can role-normalize Aqua primitive timeline samples against PT timeline samples for oral/nasal path, velopharynx branch, obstruction contact, and lip radiation signals. `PatchScript` tests pass 88/88. +- Added `PinkTrombonePrimitiveTimelineComparisonWritesArtifacts`; it writes + per-fixture `aqua-primitive-timeline.csv`, `pt-internal-timeline.csv`, + `comparison.csv`, and `summary.txt` under + `artifacts/parity/primitive-vocal-timeline/`. Focused artifact + test passes. - Proprioception correction after user rejected baby-word golf as the rebuild owner. Diagnosis is now durable in diff --git a/state/spine.yaml b/state/spine.yaml index 327926a..6744993 100644 --- a/state/spine.yaml +++ b/state/spine.yaml @@ -137,6 +137,7 @@ live_invariants: - First primitive vocal teardown cut is live. `tract`, `glottis`, `tract_injection`, and `nasal_branch` now adapt PT fixtures into `AreaFunction`, `WaveguidePath`, `SourcePort`, `ConstrictionContact`, `BranchPort`, `RadiationLoad`, `ProbeTimeline`, and `VocalNetwork` records instead of emitting generated `voices_0_area_*`, `voices_0_contact_*`, or `voices_0_inj_*` acoustic banks. The obsolete `EnsureTractAcousticNetwork` generator is deleted. Faust lowering for `Voice.VocalNetwork` emits compact primitive path/source/contact/branch/radiation locals using `de.fdelay*` delay families and debug probes under `/debug/...`; `ProbeTimelineReport` writes deterministic primitive CSV samples. Next pressure is passivity/energy timeline samples plus PT/SndKit timeline comparison, not intelligibility tuning. - Primitive path telemetry now reports `incoming_wave`, `outgoing_wave`, `energy_in`, `energy_out`, and `passivity_ratio` in both Faust debug probes and `ProbeTimelineReport`; contact telemetry includes resistance. `PrimitiveReferenceReport.ComparePinkTrombone` compares static primitive area sections, length, reflection count, delay estimate, branch admittance, contact state, and radiation fields against local PT/SndKit fixture controls without adding a native reference dependency. Next pressure is per-block comparison against PT/SndKit internals, not static control matching alone. - `PinkTromboneReferenceRenderer.RenderTimeline` now exposes PT internal oral/nasal path, branch, contact, and radiation samples from the renderer's own diameter/wave/reflection/transient arrays. `PrimitiveReferenceReport.ComparePinkTromboneTimeline` role-normalizes Aqua primitive samples against PT timeline samples for path/branch/contact/radiation signals. This is the first local reference-internal comparison surface for the primitive rebuild; next pressure is fixture-suite artifact reporting and then tightening laws where the timeline drift points. + - `PinkTrombonePrimitiveTimelineComparisonWritesArtifacts` writes per-fixture Aqua primitive timelines, PT internal timelines, comparison CSVs, and a summary under `artifacts/parity/primitive-vocal-timeline/`. This makes primitive drift inspectable across the PT fixture suite without native Faust or external VTL/ArtiSynth dependencies. Use these artifacts to choose the next physical-law correction. - 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. - `FaustExportOptions.DebugProbeUi` wraps graph probe signals in Faust `vbargraph` UI controls under `/debug/...` without changing audio output arity. Native streaming Faust patches now separate writable controls from read-only probe zones and can read/snapshot probe values after `ProcessBlock`; use this for acoustic graph passivity/radiation inspection, leave it off for normal parity renders. diff --git a/tests/AquaSynth.Dsl.Tests/PinkTromboneLogMelParityTests.cs b/tests/AquaSynth.Dsl.Tests/PinkTromboneLogMelParityTests.cs index 6bce5d7..8e75b4d 100644 --- a/tests/AquaSynth.Dsl.Tests/PinkTromboneLogMelParityTests.cs +++ b/tests/AquaSynth.Dsl.Tests/PinkTromboneLogMelParityTests.cs @@ -165,6 +165,40 @@ public sealed class PinkTromboneLogMelParityTests Assert.All(rendered, item => Assert.True(item.CandidateSamples.Any(sample => MathF.Abs(sample) > 0.00001f), item.Id)); } + [Fact] + public void PinkTrombonePrimitiveTimelineComparisonWritesArtifacts() + { + var artifactDir = ArtifactPath("parity", "primitive-vocal-timeline", DateTimeOffset.UtcNow.ToString("yyyyMMddTHHmmssfff", CultureInfo.InvariantCulture)); + var renderer = new PinkTromboneReferenceRenderer(); + var summaries = new List(); + + foreach (var fixture in PinkTromboneParityFixtures.All) + { + var patch = PatchScript.Parse(fixture.AquaScript); + var aqua = ProbeTimelineReport.Build(patch, "voices_0_network", blocks: 2); + var reference = renderer.RenderTimeline(fixture.Controls, durationSeconds: .04f, blockSize: 128); + var comparison = PrimitiveReferenceReport.ComparePinkTromboneTimeline(aqua, reference); + var fixtureDir = Path.Combine(artifactDir, fixture.Id); + Directory.CreateDirectory(fixtureDir); + File.WriteAllText(Path.Combine(fixtureDir, "aqua-primitive-timeline.csv"), ProbeTimelineReport.ToCsv(aqua)); + File.WriteAllText(Path.Combine(fixtureDir, "pt-internal-timeline.csv"), PinkTromboneTimelineCsv(reference)); + File.WriteAllText(Path.Combine(fixtureDir, "comparison.csv"), PrimitiveReferenceReport.ToCsv(comparison)); + + Assert.NotEmpty(comparison); + Assert.Contains(comparison, row => row.Primitive == "path:oral" && row.Signal == "energy_in"); + Assert.Contains(comparison, row => row.Primitive == "radiation:lip" && row.Signal == "flow"); + Assert.All(comparison, row => Assert.True( + float.IsFinite(row.Candidate) && float.IsFinite(row.Expected) && float.IsFinite(row.Error), + $"{fixture.Id} emitted non-finite primitive comparison row")); + + var meanAbsoluteError = comparison.Average(row => MathF.Abs(row.Error)); + summaries.Add($"{fixture.Id}: rows={comparison.Count} meanAbsError={meanAbsoluteError:0.######}"); + } + + Directory.CreateDirectory(artifactDir); + File.WriteAllLines(Path.Combine(artifactDir, "summary.txt"), summaries); + } + [Fact(Skip = "PT native probe diagnostics are parked while the generalized graph vocal core owns acceptance.")] public void PinkTromboneGraphDebugProbesWritePassivityReportWhenNativeFaustIsInstalled() { @@ -633,6 +667,24 @@ public sealed class PinkTromboneLogMelParityTests private static string Escape(string value) => value.Contains(',') || value.Contains('"') ? $"\"{value.Replace("\"", "\"\"", StringComparison.Ordinal)}\"" : value; + private static string PinkTromboneTimelineCsv(IReadOnlyList samples) + { + var builder = new StringBuilder(); + builder.AppendLine("block,primitive,signal,value"); + foreach (var sample in samples) + { + builder.Append(sample.Block); + builder.Append(','); + builder.Append(Escape(sample.Primitive)); + builder.Append(','); + builder.Append(Escape(sample.Signal)); + builder.Append(','); + builder.AppendLine(sample.Value.ToString("0.######", CultureInfo.InvariantCulture)); + } + + return builder.ToString(); + } + private static void WriteFixtureArtifacts( string artifactDir, PinkTromboneParityFixture fixture,