diff --git a/docs/reference-synth-roadmap.md b/docs/reference-synth-roadmap.md index d9c506a..03991bf 100644 --- a/docs/reference-synth-roadmap.md +++ b/docs/reference-synth-roadmap.md @@ -83,7 +83,10 @@ First deliverable: direct DX7 rate/level lowering on `PRC SYNTH1` still fails hard enough to make EG timing/gain calibration the live pressure. The hard PRC probe now writes ignored listening artifacts under `artifacts/parity/dx7-prc-synth1` - so metrics and ears can steer the next calibration pass together. + so metrics and ears can steer the next calibration pass together. It now + clears a modest hard-target floor above `0.60`; remaining mismatch points at + DX7 output/index scaling and algorithm output compensation rather than a + missing authoring syntax. Source: [Dexed](https://github.com/asb2m10/dexed) diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 68fdc79..eb929e3 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -27,3 +27,4 @@ {"kind":"dx7_operator_levels","lesson":"DX7 operator output level is nonlinear and includes key/velocity offsets before envelope gain. Added a reference-import helper to approximate effective Aquarium operator levels, but PRC SYNTH1 still only improved to ~0.34 with static levels and ~0.42 with envelope tweaks; hard parity now points at DX7-style operator envelope/gain evolution rather than static level mapping alone.","source":"DX7 effective operator level probe on 2026-05-13"} {"kind":"staged_operator_envelopes","lesson":"Operator graphs now have a readable staged envelope surface, env=rl rates=... levels=..., backed by RateLevelEnvelope and Faust rl4_env rendering. This exercises the DSL with DX7-style multi-target contours without changing the general ADSR voice envelope. Direct DX7 rate/level lowering on PRC SYNTH1 peaked around ~0.316 in quick probes, so the next parity pressure is calibrated DX7 EG timing/gain rather than more syntax.","source":"staged operator envelope slice and PRC SYNTH1 probe on 2026-05-13"} {"kind":"dx7_prc_listening_artifacts","lesson":"The hard PRC SYNTH1 parity probe now writes ignored WAV artifacts for ear checks under artifacts/parity/dx7-prc-synth1. A calibrated readable operator-graph candidate with staged env=rl envelopes scores ~0.458: duration is close, while RMS and timbre remain off. Use listening plus metrics to choose the next calibration target instead of blindly adding DSL surface.","source":"PRC SYNTH1 listening WAV probe on 2026-05-13"} +{"kind":"dx7_prc_calibration","lesson":"PRC SYNTH1 improved to a modest passing score of ~0.606 without new DSL surface. Removing soft clipping, using a 392 Hz graph base, and boosting the op3 carrier branch helped; global detune and per-edge route damping worsened metrics and were cut. Remaining pressure is DX7 output/index scaling or algorithm output compensation, not more envelope syntax.","source":"PRC SYNTH1 calibration pass on 2026-05-14"} diff --git a/state/memory.json b/state/memory.json index afa0d3c..4d68e01 100644 --- a/state/memory.json +++ b/state/memory.json @@ -60,9 +60,9 @@ "rule": "Add DSL abstractions only when reference rebuilds prove the missing invariant. Do not consume references that cannot be backed by parity tests; parsing alone is inventory, not evidence of expressivity." }, "dx7_reference_path": { - "status": "DX7 SysEx parser extracts voice edit buffers, packed voices, packed 32-voice banks, six operators, envelopes, algorithm, feedback, oscillator sync, LFO, transpose, voice name, checksum validation, ReferencePatch features, algorithm topology from the reverse-engineered OPS algorithm ROM table, envelope-to-ADSR approximations, effective operator-level approximations, staged operator envelopes, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, Aquarium candidate rendering through Faust-generated C#, one thresholded rendered parity test for analog1.syx voice 13, a hard PRC SYNTH1 parity probe with listening WAV artifacts, and renderable delayed operator feedback.", - "lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six Aquarium voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, and feedback belong to a graph authority, not Voice fields. The first rendered parity rung is behavioral and sine-like, not exact DX7 operator execution. A harder algorithm-8 target exposed that feedback must be delayed recursion in Faust, not a direct cyclic self-reference. Static DX7 output-level approximation helps import/rebuild math but does not solve hard parity alone. Staged operator envelopes now let Aquarium express four-rate/four-level contours in readable DSL. A calibrated PRC SYNTH1 probe now writes WAVs and scores ~0.458; duration is close, while gain/timbre mismatch remains.", - "next": "Listen to artifacts/parity/dx7-prc-synth1 and use the audible mismatch to choose the next calibration target: output gain/index scaling, ratio/detune, or EG curve shape." + "status": "DX7 SysEx parser extracts voice edit buffers, packed voices, packed 32-voice banks, six operators, envelopes, algorithm, feedback, oscillator sync, LFO, transpose, voice name, checksum validation, ReferencePatch features, algorithm topology from the reverse-engineered OPS algorithm ROM table, envelope-to-ADSR approximations, effective operator-level approximations, staged operator envelopes, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, Aquarium candidate rendering through Faust-generated C#, one thresholded rendered parity test for analog1.syx voice 13, a hard PRC SYNTH1 parity probe with listening WAV artifacts and score >= 0.60, and renderable delayed operator feedback.", + "lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six Aquarium voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, and feedback belong to a graph authority, not Voice fields. The first rendered parity rung is behavioral and sine-like, not exact DX7 operator execution. A harder algorithm-8 target exposed that feedback must be delayed recursion in Faust, not a direct cyclic self-reference. Static DX7 output-level approximation helps import/rebuild math but does not solve hard parity alone. Staged operator envelopes now let Aquarium express four-rate/four-level contours in readable DSL. PRC SYNTH1 now passes a modest hard-target floor around 0.606 after removing soft clipping, centering the graph at 392 Hz, and boosting the op3 carrier branch; log-mel/timbre still lag.", + "next": "Use PRC SYNTH1 listening artifacts and metrics to calibrate DX7 output/index scaling or algorithm output compensation before adding new DSL surface." }, "note_and_envelope_model": { "rule": "Notes own frequency, gate duration, and host/MIDI source. Envelopes own ADSR level shape.", @@ -77,7 +77,7 @@ }, "last_verified": { "command": "dotnet test AquariumSynthCSharp.slnx --no-restore", - "result": "54 passed", - "commit": "pending hard PRC SYNTH1 listening artifacts" + "result": "54 passed with AQUARIUM_DX7_PYTHON set to bundled Python", + "commit": "pending PRC SYNTH1 parity calibration" } } diff --git a/state/scratch.md b/state/scratch.md index 0341cbd..eb971ee 100644 --- a/state/scratch.md +++ b/state/scratch.md @@ -15,6 +15,21 @@ Doctrine update: Completed this slice: +- Tightened the hard DX7 `PRC SYNTH1` probe from a "writes WAVs" smoke test to + a modest passing parity target: threshold now requires score >= `0.60`. +- The useful cuts were not new syntax. The candidate improved by removing + patch soft clipping, setting the graph base frequency to `392`, and boosting + the `op3` carrier branch to compensate the algorithm-8 output/body balance. + Global detune and per-edge route damping both made the score worse and were + cut. +- Current best run with bundled Python and Faust writes timestamped artifacts + under `artifacts/parity/dx7-prc-synth1//`: score `0.6057491`, + log-mel distance `0.27658066`, envelope distance `0.38489524`, duration + ratio `0.98714787`, RMS ratio `0.9756519`, zero-crossing ratio `1.0458903`, + centroid ratio `1.093785`. + +Previous slice: + - Added a hard DX7 `PRC SYNTH1` rendered parity probe that writes listening artifacts when `dexed-py` and Faust are available: `artifacts/parity/dx7-prc-synth1/reference-dexed.wav`, diff --git a/state/spine.yaml b/state/spine.yaml index 609e23f..304371a 100644 --- a/state/spine.yaml +++ b/state/spine.yaml @@ -1,6 +1,6 @@ project: AquariumSynthCSharp spine_version: 1 -updated: 2026-05-13T19:38:00+01:00 +updated: 2026-05-14T00:48:00+01:00 prime_directive: > Build coherent synth machinery. References exist to reveal missing structure, @@ -59,7 +59,7 @@ reference_targets: priority: 1 purpose: "Structured FM/operator graph pressure test." source: "https://github.com/asb2m10/dexed" - status: "DX7 SysEx parser, public-domain SysEx fixture, dexed-py reference rendering, first thresholded rendered parity test for analog1.syx voice 13, hard PRC SYNTH1 probe with listening WAV artifacts, renderable delayed operator feedback, DX7 effective operator-level approximation, staged operator envelopes, algorithm topology extraction, envelope-to-ADSR approximation, two Aquarium DSL rebuild attempts, first-class operator graph support, and Aquarium candidate rendering through Faust-generated C# exist." + status: "DX7 SysEx parser, public-domain SysEx fixture, dexed-py reference rendering, first thresholded rendered parity test for analog1.syx voice 13, hard PRC SYNTH1 probe with listening WAV artifacts and score >= 0.60, renderable delayed operator feedback, DX7 effective operator-level approximation, staged operator envelopes, algorithm topology extraction, envelope-to-ADSR approximation, two Aquarium DSL rebuild attempts, first-class operator graph support, and Aquarium candidate rendering through Faust-generated C# exist." first_slice: - "Add a harder public-domain DX7 parity target that audibly needs operator routing." zynaddsubfx: @@ -95,10 +95,11 @@ reference_targets: next_agent_start_here: recommended_first_task: > - Listen to the generated PRC SYNTH1 WAVs under - `artifacts/parity/dx7-prc-synth1`, then use the audible mismatch plus the - score report to decide whether the next calibration pass should target - output gain/index scaling, frequency ratio/detune, or EG curve shape. + Listen to the latest generated PRC SYNTH1 WAVs under + `artifacts/parity/dx7-prc-synth1/`. The current candidate passes the + modest hard-target metric floor, but log-mel/timbre still lag; the next + calibration should target DX7 output/index scaling or algorithm output + compensation before adding any new DSL surface. must_read: - AGENTS.md - docs/audio-doctrine.md @@ -110,6 +111,15 @@ next_agent_start_here: - "dotnet test AquariumSynthCSharp.slnx --no-restore" completed_slices: + - date: 2026-05-14 + summary: > + Tightened the hard DX7 `PRC SYNTH1` probe to require score >= 0.60 and + improved the candidate to 0.6057491. The useful calibration cuts were + removing Aquarium soft clipping, setting the graph base frequency to 392, + and boosting the op3 carrier branch for algorithm-8 output/body balance. + Global detune and per-edge route damping were tested and cut because they + made the metrics worse. + verification: "AQUARIUM_DX7_PYTHON= dotnet test AquariumSynthCSharp.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed" - date: 2026-05-13 summary: > Added a hard DX7 `PRC SYNTH1` rendered parity probe that emits listening diff --git a/tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs b/tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs index c05b791..71b4835 100644 --- a/tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs +++ b/tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs @@ -113,7 +113,7 @@ public sealed class Dx7ReferenceParityTests var comparison = new AudioAnalyzer(new AudioAnalysisConfig(SampleRate: reference.SampleRate)) .Compare(reference.Samples, candidate.Samples); - var artifactDir = ArtifactPath("parity", "dx7-prc-synth1"); + var artifactDir = ArtifactPath("parity", "dx7-prc-synth1", DateTimeOffset.UtcNow.ToString("yyyyMMddTHHmmssfff", System.Globalization.CultureInfo.InvariantCulture)); WriteListeningArtifacts( artifactDir, reference.Samples, @@ -122,7 +122,7 @@ public sealed class Dx7ReferenceParityTests script, comparison); - Assert.True(comparison.Score >= 0.40f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}"); + Assert.True(comparison.Score >= 0.60f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}"); } private static string FixturePath(params string[] parts) => @@ -146,17 +146,17 @@ public sealed class Dx7ReferenceParityTests var builder = new StringBuilder(); builder.AppendLine("patch"); builder.AppendLine(" gain=2.0"); - builder.AppendLine(" soft_clip=true"); + builder.AppendLine(" soft_clip=false"); builder.AppendLine(); builder.AppendLine("opgraph"); builder.AppendLine(" name=dx7_prc_synth1_probe"); - builder.AppendLine(" freq=440"); + builder.AppendLine(" freq=392"); builder.AppendLine(" gain=0.55"); builder.AppendLine(); foreach (var op in voice.Operators.OrderByDescending(op => op.Number)) { - var level = Dx7SysEx.ApproximateOperatorLevel(op).LinearLevel; + var level = Dx7SysEx.ApproximateOperatorLevel(op).LinearLevel * CarrierLevelScale(op.Number); var envelope = ScaledEnvelope(Dx7SysEx.ApproximateRateLevelEnvelope(op.Envelope), 0.62f); builder.AppendLine($"operator name=op{op.Number}"); builder.AppendLine($" ratio={F(Dx7Ratio(op))}"); @@ -196,6 +196,12 @@ public sealed class Dx7ReferenceParityTests return coarse * (1 + op.FrequencyFine / 100f); } + private static float CarrierLevelScale(int operatorNumber) => operatorNumber switch + { + 3 => 1.45f, + _ => 1 + }; + private static Dx7RateLevelEnvelopeApproximation ScaledEnvelope(Dx7RateLevelEnvelopeApproximation approximation, float scale) { var envelope = approximation.Envelope;