Add community DX7 voice parity survey

This commit is contained in:
Meta Vens 2026-05-14 15:10:30 +01:00
commit ecae695fe2
6 changed files with 163 additions and 8 deletions

View file

@ -106,6 +106,19 @@ math is still fake in the places that matter perceptually.
`<= 0.145`, envelope distance at `<= 0.08`, zero-crossing at `>= 0.75`,
RMS ratio near unity, and score at `>= 0.75`.
9. **Community voice survey**
- Use public-domain community SysEx fixtures as broader behavioral probes
after each isolated calibration rung.
- `analog1.syx` from Musical Artifacts artifact 152 now has a broad rendered
parity test for four actual community voices: `ANLGSYN 1`, `{ Mooger }`,
`Piano Bass`, and `RES SYNTH1`. The test writes listening artifacts under
`artifacts/parity/dx7-community-analog1/` and gates log-mel at `<= 0.3`,
envelope distance at `<= 0.16`, and score at `>= 0.5`.
- A first survey also tried `DX1 LEAD B` and `MELLOWSOLO`; they were left
out of the passing gate because their log-mel/zero-crossing mismatch is
larger. Keep those failures as pressure for oscillator phase, feedback,
and transient detail rather than padding the test with weak thresholds.
## Rejected Path
Do not keep tuning `PRC SYNTH1` with branch-specific magic constants. That patch

View file

@ -46,3 +46,4 @@
{"kind":"patch_library","lesson":"Repo-owned Aquarium .aqua stock now lives under patches/ with library.yaml; every .aqua must parse/export in tests and the patch library must remain out of AquariumSynth.Dsl.nupkg unless intentionally promoted.","source":"Patch library folder slice on 2026-05-14"}
{"kind":"dx7_algorithm8_split_route_parity","lesson":"The DX7 summed/cascaded route scale 6.0 is now backed by split project-authored algorithm-8 rendered probes, not only PRC or one combined stack. Latest real-Dexed run: combined stack log-mel 0.04029555 score 0.8664546, summed pair log-mel 0.042429477 score 0.870778, cascade log-mel 0.03811625 score 0.9157809. Keep isolated direct branches at 6.275 and summed/cascaded branches at 6.0 until broader evidence contradicts it.","source":"Split algorithm-8 parity probes on 2026-05-14"}
{"kind":"patch_library_taste","lesson":"Not every parity probe belongs in the patch library. Harsh project-authored DX7 calibration probes should stay in tests and ignored listening artifacts unless they are musically useful stock; the navigable .aqua library is for reusable inspiration, not measurement torture signals.","source":"User listening correction on 2026-05-14"}
{"kind":"dx7_community_voice_parity","lesson":"Actual public-domain analog1.syx community voices now have a broad rendered parity gate. ANLGSYN 1, { Mooger }, Piano Bass, and RES SYNTH1 pass log-mel <= 0.3, envelope <= 0.16, score >= 0.5 using the current DX7 lowering. The first survey excluded DX1 LEAD B and MELLOWSOLO because their log-mel/zero-crossing mismatch is too large; remaining pressure is fine waveform/phase/feedback behavior, not only broad envelope shape.","source":"Community DX7 analog1.syx survey on 2026-05-14"}

View file

@ -74,9 +74,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, measured carrier output amplitude, isolated two-op modulation route scale, summed/cascaded route scale, calibrated feedback amount table, ratio detune lowering, ROM COM output compensation, topology-aware route lowering, staged operator envelopes with per-segment curves, raw and block-interpolated DX7 envelope tracing, applied-envelope curved lowering, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, project-authored Dexed patch 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 log-mel <= 0.18, project-authored algorithm-8 summed-stack/cascade/summed-pair parity, 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 helped import/rebuild math but was mathematically wrong for carrier amplitude. A single-carrier Dexed sweep shows carrier amplitude follows 2^((outputLevel - 99) / 8). Isolated two-op DX7 FM maps full-level modulation to Aquarium route index 6.275; summed/cascaded algorithm-8 branches now use 6.0 after PRC listening, a project-authored combined stack probe, and split cascade/summed-pair probes showed lower values were under-bright. Feedback lowers through a fitted table instead of PRC-local constants. Ratio-mode detune belongs in operator frequency lowering. ROM COM values carry output-count compensation; because the amplitude curve was fitted against algorithm 32, DX7 carriers scale by 6/(COM+1). Project-authored Dexed patch rendering now gives lawful calibration targets. Reusing the operator output curve for EG levels was tested and cut because it failed hard-target log-mel. Direct envelope tracing shows current linear `env=rl` cannot express DX7 EG spike/drop behavior. Dexed-style audio samples EG once per 64-sample block and interpolates applied gain, so raw EG parity alone is not audio parity. Staged operator envelopes now support linear/exponential segments and supranormal levels. `ApproximateAppliedRateLevelEnvelope` uses that surface and now picks the first near-peak before gate so slower rising modulators hit early instead of swelling late. PRC log-mel is down to 0.13874426 with RMS at unity; remaining mismatch is smaller high-air/timbre detail, not broad envelope shape.",
"next": "Use the now-validated algorithm-8 route scale as fixed input while isolating remaining PRC high-air/timbre mismatch, then choose broader public-domain voices for import."
"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, measured carrier output amplitude, isolated two-op modulation route scale, summed/cascaded route scale, calibrated feedback amount table, ratio detune lowering, ROM COM output compensation, topology-aware route lowering, staged operator envelopes with per-segment curves, raw and block-interpolated DX7 envelope tracing, applied-envelope curved lowering, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, project-authored Dexed patch 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 log-mel <= 0.18, project-authored algorithm-8 summed-stack/cascade/summed-pair parity, broad rendered community parity for four analog1.syx voices, 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 helped import/rebuild math but was mathematically wrong for carrier amplitude. A single-carrier Dexed sweep shows carrier amplitude follows 2^((outputLevel - 99) / 8). Isolated two-op DX7 FM maps full-level modulation to Aquarium route index 6.275; summed/cascaded algorithm-8 branches now use 6.0 after PRC listening, a project-authored combined stack probe, and split cascade/summed-pair probes showed lower values were under-bright. Feedback lowers through a fitted table instead of PRC-local constants. Ratio-mode detune belongs in operator frequency lowering. ROM COM values carry output-count compensation; because the amplitude curve was fitted against algorithm 32, DX7 carriers scale by 6/(COM+1). Project-authored Dexed patch rendering now gives lawful calibration targets. Reusing the operator output curve for EG levels was tested and cut because it failed hard-target log-mel. Direct envelope tracing shows current linear `env=rl` cannot express DX7 EG spike/drop behavior. Dexed-style audio samples EG once per 64-sample block and interpolates applied gain, so raw EG parity alone is not audio parity. Staged operator envelopes now support linear/exponential segments and supranormal levels. `ApproximateAppliedRateLevelEnvelope` uses that surface and now picks the first near-peak before gate so slower rising modulators hit early instead of swelling late. PRC log-mel is down to 0.13874426 with RMS at unity; broader analog1.syx community voices pass broad log-mel/envelope gates but expose high zero-crossing mismatch, especially on analog synth and piano-bass timbres.",
"next": "Use the now-validated algorithm-8 route scale as fixed input while isolating remaining PRC high-air/timbre mismatch and community-voice zero-crossing mismatch, then expand to broader public-domain banks."
},
"note_and_envelope_model": {
"rule": "Notes own frequency, gate duration, and host/MIDI source. Envelopes own ADSR level shape.",
@ -91,7 +91,7 @@
},
"last_verified": {
"command": "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore",
"result": "69 passed; algorithm-8 stack/cascade/summed-pair parity probes pass with log-mel <= 0.06 and write listening artifacts",
"commit": "pending split DX7 algorithm-8 probes"
"result": "70 passed; four actual public-domain analog1.syx community voices pass broad rendered parity and write listening artifacts",
"commit": "pending community DX7 voices"
}
}

View file

@ -15,6 +15,27 @@ Doctrine update:
Completed this slice:
- Added `PublicDomainDx7AnalogCommunityVoicesMeetBroadRenderedParityWhenInstalled`,
which renders actual public-domain `analog1.syx` community voices through
Dexed and the current Aquarium DX7 lowering. Kept four broad parity voices:
`ANLGSYN 1`, `{ Mooger }`, `Piano Bass`, and `RES SYNTH1`.
- The test writes WAVs/reports under
`artifacts/parity/dx7-community-analog1/<voice>/` and gates log-mel
`<= .3`, envelope distance `<= .16`, and score `>= .5`.
- Latest kept-voice metrics:
- `ANLGSYN 1`: log-mel `.25955316`, envelope `.12271979`, score `.5709625`
- `{ Mooger }`: log-mel `.23776375`, envelope `.07168135`, score `.66662127`
- `Piano Bass`: log-mel `.19985504`, envelope `.14781862`, score `.5205584`
- `RES SYNTH1`: log-mel `.22371109`, envelope `.109231755`, score `.54040927`
- First survey also tried `DX1 LEAD B` and `MELLOWSOLO`; they were excluded
from the passing gate because log-mel and zero-crossing mismatch were too
large. That is pressure, not library stock.
- Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`:
70 passed.
Previous slice:
- Added two split project-authored DX7 algorithm-8 rendered parity probes:
`ProjectAuthoredDx7AlgorithmEightCascadeProbeMeetsParityWhenInstalled` for
`6 -> 5 -> 3`, and

View file

@ -1,6 +1,6 @@
project: AquariumSynthCSharp
spine_version: 1
updated: 2026-05-14T14:00:00+01:00
updated: 2026-05-14T14:09:00+01:00
prime_directive: >
Build coherent synth machinery. References exist to reveal missing structure,
@ -64,6 +64,7 @@ live_invariants:
- Public-domain reference fixtures and dexed-py helpers are test/development material only; the NuGet package must ship only AquariumSynth.Dsl.
- Structured `.aqua` stock lives under `patches/` as development source material. Every library patch must parse/export, and the patch library must stay out of the NuGet package unless intentionally promoted.
- Project-authored Dexed patches may be generated inside tests for calibration; do not ship them in the NuGet package or treat them as runtime dependencies.
- Harsh calibration probes belong in tests and ignored artifacts, not in the patch library unless they are musically useful stock.
- Listening WAV artifacts belong under ignored `artifacts/`; keep them out of NuGet and commits unless explicitly promoted as fixtures.
- Do not consume external synth targets that cannot be backed by parity tests. Parsing is inventory; reproducing behavior through terse, readable Aquarium DSL is the proof.
- Audio metrics are evidence, not final judgment.
@ -122,8 +123,8 @@ next_agent_start_here:
route scale now gives PRC the same character with closer harsh stacked
modulation. Split project-authored algorithm-8 cascade and summed-pair
probes now back the 6.0 summed/cascaded route scale. Next pressure is
residual PRC high-air/timbre mismatch, then broader public-domain voice
selection before broad import.
residual PRC high-air/timbre mismatch and high zero-crossing mismatch on
broader public-domain community voices before broad import.
must_read:
- AGENTS.md
- docs/audio-doctrine.md
@ -136,6 +137,18 @@ next_agent_start_here:
- "dotnet test AquariumSynthCSharp.slnx --no-restore"
completed_slices:
- date: 2026-05-14
summary: >
Added a rendered public-domain community DX7 parity test against four
actual `analog1.syx` voices from Musical Artifacts artifact 152:
`ANLGSYN 1`, `{ Mooger }`, `Piano Bass`, and `RES SYNTH1`. The test
renders Dexed references, lowers the same voices through the current
Aquarium DX7 operator-graph path, writes listening WAV/report artifacts
under `artifacts/parity/dx7-community-analog1/`, and gates broad parity
at log-mel <= 0.3, envelope <= 0.16, score >= 0.5. First survey also
tried `DX1 LEAD B` and `MELLOWSOLO`; they remain pressure because
log-mel/zero-crossing mismatch is too large for the broad gate.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 70 passed"
- date: 2026-05-14
summary: >
Added split project-authored DX7 algorithm-8 parity probes for the

View file

@ -136,6 +136,41 @@ public sealed class Dx7ReferenceParityTests
Assert.True(comparison.Score >= 0.75f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}");
}
[Fact]
public async Task PublicDomainDx7AnalogCommunityVoicesMeetBroadRenderedParityWhenInstalled()
{
var cases = new[]
{
new CommunityDx7VoiceCase(7, "ANLGSYN 1", 0.65f, 1.0f, 0.25f),
new CommunityDx7VoiceCase(19, "{ Mooger }", 0.65f, 1.0f, 0.25f),
new CommunityDx7VoiceCase(22, "Piano Bass", 0.45f, 0.8f, 0.25f),
new CommunityDx7VoiceCase(31, "RES SYNTH1", 0.65f, 1.0f, 0.25f)
};
var comparisons = new List<(CommunityDx7VoiceCase Case, AudioComparison Comparison, string ArtifactDir)>();
foreach (var item in cases)
{
var result = await RenderAndComparePublicDomainDx7VoiceAsync(item);
if (result is null)
{
return;
}
comparisons.Add(result.Value);
}
var report = string.Join(
Environment.NewLine + Environment.NewLine,
comparisons.Select(item =>
$"{item.Case.Index}: {item.Case.ExpectedName}{Environment.NewLine}" +
$"{ParityReport(item.Comparison)}{Environment.NewLine}" +
$"artifacts: {item.ArtifactDir}"));
Assert.All(comparisons, item => Assert.True(item.Comparison.LogMelDistance <= 0.3f, report));
Assert.All(comparisons, item => Assert.True(item.Comparison.EnvelopeDistance <= 0.16f, report));
Assert.All(comparisons, item => Assert.True(item.Comparison.Score >= 0.5f, report));
}
[Fact]
public async Task ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled()
{
@ -443,6 +478,60 @@ public sealed class Dx7ReferenceParityTests
return comparison;
}
private static async Task<(CommunityDx7VoiceCase Case, AudioComparison Comparison, string ArtifactDir)?> RenderAndComparePublicDomainDx7VoiceAsync(
CommunityDx7VoiceCase item)
{
var bankPath = FixturePath("Dx7", "PublicDomain", "analog1.syx");
var reference = await DexedPyRenderer.RenderAsync(
bankPath,
patchIndex: item.Index,
noteDurationSeconds: item.NoteDurationSeconds,
renderDurationSeconds: item.RenderDurationSeconds);
if (reference is null)
{
return null;
}
var bank = Dx7SysEx.ParseBank(File.ReadAllBytes(bankPath));
var voice = bank.Voices[item.Index];
Assert.Equal(item.ExpectedName, voice.Name);
var graphName = $"dx7_analog1_{item.Index}_{SafeName(voice.Name)}";
var script = Dx7VoiceProbeScript(
voice,
graphName,
item.GraphGain,
envelopeScale: 0.62f,
gateSeconds: item.NoteDurationSeconds,
useAppliedEnvelope: true);
var candidateSource = FaustEmitter.EmitScript(script);
var candidate = await FaustCompiler.RenderAsync(
candidateSource.Source,
new FaustRenderOptions(DurationSeconds: item.RenderDurationSeconds));
if (candidate is null)
{
return null;
}
var comparison = new AudioAnalyzer(new AudioAnalysisConfig(SampleRate: reference.SampleRate))
.Compare(reference.Samples, candidate.Samples);
var artifactDir = ArtifactPath(
"parity",
"dx7-community-analog1",
$"{item.Index:00}-{SafeName(voice.Name)}-{DateTimeOffset.UtcNow:yyyyMMddTHHmmssfff}");
WriteListeningArtifacts(
artifactDir,
reference.Samples,
candidate.Samples,
reference.SampleRate,
script,
comparison);
return (item, comparison, artifactDir);
}
private static DexedOperatorSpec SilentOperator(int number) =>
FullOperator(number, outputLevel: 0, coarse: 1);
@ -595,6 +684,17 @@ public sealed class Dx7ReferenceParityTests
private static string F(float value) =>
value.ToString("0.######", System.Globalization.CultureInfo.InvariantCulture);
private static string SafeName(string value)
{
var builder = new StringBuilder(value.Length);
foreach (var ch in value.ToLowerInvariant())
{
builder.Append(char.IsLetterOrDigit(ch) ? ch : '_');
}
return builder.ToString().Trim('_');
}
private static string ParityReport(AudioComparison comparison) =>
string.Join(
Environment.NewLine,
@ -627,6 +727,13 @@ internal sealed record DexedOperatorSpec(
IReadOnlyList<int> Rates,
IReadOnlyList<int> Levels);
internal sealed record CommunityDx7VoiceCase(
int Index,
string ExpectedName,
float NoteDurationSeconds,
float RenderDurationSeconds,
float GraphGain);
internal static class DexedPyRenderer
{
public static async Task<DexedPyRender?> RenderAsync(