Rename project to AquaSynth

This commit is contained in:
Meta Vens 2026-05-16 05:49:44 +01:00
commit 64d0a6bf4f
47 changed files with 516 additions and 515 deletions

View file

@ -1,14 +1,14 @@
# AquariumSynthCSharp Instructions # AquaSynth Instructions
## Purpose ## Purpose
AquariumSynthCSharp is the C# authoring and compiler front-end for Aquarium AquaSynth is the C# authoring and compiler front-end for AquaSynth
synth patches. It owns patch graph models, terse patch scripting, SFXR-derived synth patches. It owns patch graph models, terse patch scripting, SFXR-derived
patch mapping, analysis/scoring tools, preset catalogs, and Faust source/code patch mapping, analysis/scoring tools, preset catalogs, and Faust source/code
generation for the Aquarium/Vortice engine path. generation for the AquaSynth/Vortice engine path.
The Rust crate may remain a reference lab and audio renderer. This repo should The AquaSynth-rs branch may remain a reference lab and audio renderer. This repo should
not depend on the Rust branch for reusable authoring, analysis, preset, or Faust not depend on AquaSynth-rs for reusable authoring, analysis, preset, or Faust
compiler behavior. compiler behavior.
## Persistent State ## Persistent State

8
AquaSynth.slnx Normal file
View file

@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/AquaSynth.Dsl/AquaSynth.Dsl.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/AquaSynth.Dsl.Tests/AquaSynth.Dsl.Tests.csproj" />
</Folder>
</Solution>

View file

@ -1,8 +0,0 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/AquariumSynth.Dsl/AquariumSynth.Dsl.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/AquariumSynth.Dsl.Tests/AquariumSynth.Dsl.Tests.csproj" />
</Folder>
</Solution>

View file

@ -1,12 +1,12 @@
# Aquarium Synth CSharp # AquaSynth
C# front-end for Aquarium Synth patch scripts. It parses the Aquarium patch DSL C# front-end for AquaSynth patch scripts. It parses the AquaSynth patch DSL
into a serializable patch graph, emits Faust `.dsp`, and can ask an installed into a serializable patch graph, emits Faust `.dsp`, and can ask an installed
Faust compiler to generate backend code such as C#, C++, C, or Rust. Faust compiler to generate backend code such as C#, C++, C, or Rust.
This repo is the C# bridge for Aquarium/Vortice-land. Rust remains the reference This repo is the C# bridge for AquaSynth/Vortice-land. Rust remains the reference
lab; this project is the tool that lets the engine script Faust without dragging lab; this project is the tool that lets the engine script Faust without dragging
the Rust crate into the room by the ankle. the AquaSynth-rs crate into the room by the ankle.
## Run ## Run
@ -15,7 +15,7 @@ dotnet test
``` ```
DX7 reference-render tests use `dexed-py` when a Python with `dexed` and DX7 reference-render tests use `dexed-py` when a Python with `dexed` and
`numpy` is available. Set `AQUARIUM_DX7_PYTHON` to that interpreter to force a `numpy` is available. Set `AQUASYNTH_DX7_PYTHON` to that interpreter to force a
specific runtime; otherwise the tests skip the render path and still parse the specific runtime; otherwise the tests skip the render path and still parse the
vendored public-domain fixture. vendored public-domain fixture.
@ -26,23 +26,23 @@ ZynAddSubFX is pinned as a GPL test/development reference under
git submodule update --init --recursive git submodule update --init --recursive
``` ```
That source is a parity oracle only. Aquarium does not ship Zyn code, link it That source is a parity oracle only. AquaSynth does not ship Zyn code, link it
from `AquariumSynth.Dsl`, or treat it as runtime machinery. from `AquaSynth.Dsl`, or treat it as runtime machinery.
## Package Boundary ## Package Boundary
Aquarium consumes this library as a pinned `AquariumSynth.Dsl` NuGet package, Downstream consumers should pin this library as an `AquaSynth.Dsl` NuGet
not as a live project reference. Breaking synth-library work should happen here package, not use it as a live project reference. Breaking synth-library work
freely, then Aquarium should intentionally update only after a new package should happen here freely, then consumers should intentionally update only after
version is packed and tested. a new package version is packed and tested.
```powershell ```powershell
dotnet pack src\AquariumSynth.Dsl\AquariumSynth.Dsl.csproj -c Release dotnet pack src\AquaSynth.Dsl\AquaSynth.Dsl.csproj -c Release
``` ```
The test suite verifies that development fixtures, Python render helpers, The test suite verifies that development fixtures, Python render helpers,
external reference synth sources, and SysEx banks do not ship in external reference synth sources, and SysEx banks do not ship in
`AquariumSynth.Dsl.nupkg`. `AquaSynth.Dsl.nupkg`.
## Shape ## Shape
@ -55,7 +55,7 @@ external reference synth sources, and SysEx banks do not ship in
references for testing and for judging whether the DSL can express useful references for testing and for judging whether the DSL can express useful
sound designs cleanly. sound designs cleanly.
- `SfxrParams`, `PatchScriptScoring`, `AudioAnalyzer`, and `Presets` carry the - `SfxrParams`, `PatchScriptScoring`, `AudioAnalyzer`, and `Presets` carry the
reusable Rust-side analysis, scoring, SFXR, and preset tools. reusable AquaSynth-rs-side analysis, scoring, SFXR, and preset tools.
Faust `2.85.5` supports `-lang csharp`, so the intended hot path is: Faust `2.85.5` supports `-lang csharp`, so the intended hot path is:

View file

@ -1,8 +1,8 @@
# Aquarium Synth Audio Doctrine # AquaSynth Audio Doctrine
## Mission ## Mission
AquariumSynthCSharp is the authoring and compiler surface for Aquarium synth AquaSynth is the authoring and compiler surface for AquaSynth
patches. It should let us sketch sound quickly, compile to Faust, inspect the patches. It should let us sketch sound quickly, compile to Faust, inspect the
graph, compare results, and hand stable contracts to the C#/Vortice side. graph, compare results, and hand stable contracts to the C#/Vortice side.
@ -64,7 +64,7 @@ That separation is the model for this repo:
- The Faust emitter owns pure signal expression. - The Faust emitter owns pure signal expression.
- The engine owns scheduling, buffers, device I/O, threading, and presentation. - The engine owns scheduling, buffers, device I/O, threading, and presentation.
- Patch parameters are runtime controls, not compile-time constants. A compiled - Patch parameters are runtime controls, not compile-time constants. A compiled
DSP must be able to expose stable parameter paths so Aquarium can vary a sound DSP must be able to expose stable parameter paths so AquaSynth can vary a sound
without recompiling the patch. without recompiling the patch.
- Host parameters should be smoothed where they cross into signal-rate behavior. - Host parameters should be smoothed where they cross into signal-rate behavior.
@ -110,7 +110,7 @@ authorities.
as a general envelope field. as a general envelope field.
- Host/MIDI note mode should expose stable note frequency and note gate controls - Host/MIDI note mode should expose stable note frequency and note gate controls
that the engine can wire to MIDI note-on/note-off behavior. that the engine can wire to MIDI note-on/note-off behavior.
- Faust-managed polyphony is the preferred MIDI path. Aquarium should describe a - Faust-managed polyphony is the preferred MIDI path. AquaSynth should describe a
single voice graph and emit Faust's standard `freq`, `gain`, and `gate` single voice graph and emit Faust's standard `freq`, `gain`, and `gate`
controls with `[midi:on][nvoices:n]` options, leaving MIDI decoding and voice controls with `[midi:on][nvoices:n]` options, leaving MIDI decoding and voice
allocation to Faust architectures unless a concrete target proves that allocation to Faust architectures unless a concrete target proves that
@ -122,7 +122,7 @@ authorities.
## Metrics ## Metrics
Analysis exists to catch regressions and support search, not to crown winners. Analysis exists to catch regressions and support search, not to crown winners.
Aquarium Faust output can be rendered through Faust-generated C# in authoring AquaSynth Faust output can be rendered through Faust-generated C# in authoring
tests, which gives us candidate audio buffers without depending on the old Rust tests, which gives us candidate audio buffers without depending on the old Rust
renderer. That is only half of parity: reference targets still need a real renderer. That is only half of parity: reference targets still need a real
external render or a captured, lawful fixture before the comparison means external render or a captured, lawful fixture before the comparison means

View file

@ -2,7 +2,7 @@
## Objective ## Objective
Make DX7 reference rebuilds prove Aquarium can emulate the machine, not merely Make DX7 reference rebuilds prove AquaSynth can emulate the machine, not merely
draw a similar operator graph. The hard `PRC SYNTH1` target showed the current draw a similar operator graph. The hard `PRC SYNTH1` target showed the current
DSL surface is expressive enough to describe the topology, but the importer DSL surface is expressive enough to describe the topology, but the importer
math is still fake in the places that matter perceptually. math is still fake in the places that matter perceptually.
@ -10,7 +10,7 @@ math is still fake in the places that matter perceptually.
## Current Mechanism ## Current Mechanism
- `dexed-py` renders lawful public-domain or project-authored DX7 references. - `dexed-py` renders lawful public-domain or project-authored DX7 references.
- Aquarium renders candidate patches through Faust-generated C#. - AquaSynth renders candidate patches through Faust-generated C#.
- Tests compare rendered audio with log-mel, envelope, duration, RMS, - Tests compare rendered audio with log-mel, envelope, duration, RMS,
zero-crossing, centroid, and listening WAV artifacts. zero-crossing, centroid, and listening WAV artifacts.
- `OperatorGraph` owns topology: operators, routes, carriers, feedback, and - `OperatorGraph` owns topology: operators, routes, carriers, feedback, and
@ -33,11 +33,11 @@ math is still fake in the places that matter perceptually.
- Render project-authored DX7 algorithm-32 patches with one audible carrier. - Render project-authored DX7 algorithm-32 patches with one audible carrier.
- Sweep operator output levels. - Sweep operator output levels.
- Fit output level to linear carrier amplitude. - Fit output level to linear carrier amplitude.
- Verify Aquarium single-carrier renders match relative RMS. - Verify AquaSynth single-carrier renders match relative RMS.
2. **Two-operator modulation index** 2. **Two-operator modulation index**
- Render a carrier plus one modulator across modulator output levels. - Render a carrier plus one modulator across modulator output levels.
- Fit DX7 output level to Aquarium route/index scaling using log-mel and - Fit DX7 output level to AquaSynth route/index scaling using log-mel and
harmonic energy, not RMS. harmonic energy, not RMS.
- Keep carrier amplitude fixed while calibrating modulation index. - Keep carrier amplitude fixed while calibrating modulation index.
@ -46,7 +46,7 @@ math is still fake in the places that matter perceptually.
- Fit feedback amount separately from route/index scaling. - Fit feedback amount separately from route/index scaling.
4. **Ratio detune** 4. **Ratio detune**
- Lower DX7 ratio-mode detune into Aquarium operator ratios. - Lower DX7 ratio-mode detune into AquaSynth operator ratios.
- Keep detune as frequency behavior, not a modulation or chorus feature. - Keep detune as frequency behavior, not a modulation or chorus feature.
5. **Algorithm output compensation** 5. **Algorithm output compensation**
@ -70,14 +70,14 @@ math is still fake in the places that matter perceptually.
unlike the current linear `env=rl` approximation. unlike the current linear `env=rl` approximation.
- Dexed's rendered audio does not hear that raw state directly. The operator - Dexed's rendered audio does not hear that raw state directly. The operator
path samples EG once per 64-sample block and linearly interpolates the path samples EG once per 64-sample block and linearly interpolates the
applied gain across the block, so a DX7-capable Aquarium envelope must applied gain across the block, so a DX7-capable AquaSynth envelope must
model both the rate/level state machine and its block-interpolated output. model both the rate/level state machine and its block-interpolated output.
- Aquarium staged operator envelopes now support per-segment curves, e.g. - AquaSynth staged operator envelopes now support per-segment curves, e.g.
`env=rl rates=.00145,.0508,.5268,.35 levels=2,.297,.0156,0 curves=lin,exp,exp,exp`. `env=rl rates=.00145,.0508,.5268,.35 levels=2,.297,.0156,0 curves=lin,exp,exp,exp`.
This is a general staged-contour feature, not a DX7-specific syntax escape. This is a general staged-contour feature, not a DX7-specific syntax escape.
- `Dx7SysEx.ApproximateAppliedRateLevelEnvelope` lowers a DX7 EG into a - `Dx7SysEx.ApproximateAppliedRateLevelEnvelope` lowers a DX7 EG into a
curved staged contour by tracing Dexed-style block-interpolated applied curved staged contour by tracing Dexed-style block-interpolated applied
gain and normalizing it against Aquarium's separate operator-output level. gain and normalizing it against AquaSynth's separate operator-output level.
On `PRC SYNTH1`, this improves log-mel from `0.24476814` to `0.17186502`, On `PRC SYNTH1`, this improves log-mel from `0.24476814` to `0.17186502`,
envelope distance from `0.43700194` to `0.13057204`, and zero-crossing envelope distance from `0.43700194` to `0.13057204`, and zero-crossing
ratio from `0.7931764` to `0.7815517` while keeping RMS at unity. ratio from `0.7931764` to `0.7815517` while keeping RMS at unity.
@ -117,11 +117,11 @@ math is still fake in the places that matter perceptually.
envelope distance at `<= 0.14`, zero-crossing ratio in `0.8..1.11`, and envelope distance at `<= 0.14`, zero-crossing ratio in `0.8..1.11`, and
score at `>= 0.6`. score at `>= 0.6`.
- `Piano Bass` exposed a real pitch bug: the candidate ignored the DX7 voice - `Piano Bass` exposed a real pitch bug: the candidate ignored the DX7 voice
transpose byte, so `transpose=12` rendered one octave too high. Aquarium transpose byte, so `transpose=12` rendered one octave too high. AquaSynth
now uses `Dx7SysEx.NoteFrequencyHz(midiNote, transpose)`, where `24` is now uses `Dx7SysEx.NoteFrequencyHz(midiNote, transpose)`, where `24` is
neutral. neutral.
- `ANLGSYN 1` exposed a second real pitch/frequency bug: DX7 fixed-frequency - `ANLGSYN 1` exposed a second real pitch/frequency bug: DX7 fixed-frequency
operators were being lowered as fake note ratios. Aquarium now maps fixed operators were being lowered as fake note ratios. AquaSynth now maps fixed
operators through the Dexed log-frequency formula and divides the absolute operators through the Dexed log-frequency formula and divides the absolute
Hz by the graph base frequency. This restores the low fixed-frequency Hz by the graph base frequency. This restores the low fixed-frequency
carrier/modulation buzz for op1/op3 instead of flattening the voice into carrier/modulation buzz for op1/op3 instead of flattening the voice into
@ -129,13 +129,13 @@ math is still fake in the places that matter perceptually.
- Community listening then exposed two note-dependent lowering gaps: - Community listening then exposed two note-dependent lowering gaps:
operator key-scaling was computed against hardcoded MIDI note 60, and DX7 operator key-scaling was computed against hardcoded MIDI note 60, and DX7
operator rate scaling was parsed but not applied to the block-interpolated operator rate scaling was parsed but not applied to the block-interpolated
envelope trace. Aquarium now lowers both against the effective played envelope trace. AquaSynth now lowers both against the effective played
MIDI note after voice transpose. This makes `RES SYNTH1` attack much MIDI note after voice transpose. This makes `RES SYNTH1` attack much
closer and improves `Piano Bass` log-mel, with graph gain rebalanced to closer and improves `Piano Bass` log-mel, with graph gain rebalanced to
`0.72` for `Piano Bass`. `0.72` for `Piano Bass`.
- `ANLGSYN 1` also exposed that the previous max-feedback value was too tame - `ANLGSYN 1` also exposed that the previous max-feedback value was too tame
for audible community patches. DX7 feedback value `7` now lowers to for audible community patches. DX7 feedback value `7` now lowers to
Aquarium feedback `2.2`, while feedback value `5` remains at `0.19` for AquaSynth feedback `2.2`, while feedback value `5` remains at `0.19` for
the hard PRC target. The hotter value restores much more of the 1.2-5 kHz the hard PRC target. The hotter value restores much more of the 1.2-5 kHz
buzzing-harmonic band, with envelope/RMS still imperfect. buzzing-harmonic band, with envelope/RMS still imperfect.
- Listening then caught that the max-feedback candidate still lost punch - Listening then caught that the max-feedback candidate still lost punch
@ -145,7 +145,7 @@ math is still fake in the places that matter perceptually.
gates `ANLGSYN 1` with a sustained 2.5-5 kHz band-energy check so the gates `ANLGSYN 1` with a sustained 2.5-5 kHz band-energy check so the
old drifting version cannot pass by global log-mel score alone. old drifting version cannot pass by global log-mel score alone.
- Later listening caught `ANLGSYN 1` and `RES SYNTH1` blowing out into a - Later listening caught `ANLGSYN 1` and `RES SYNTH1` blowing out into a
drone at the modulator peak. Until Aquarium has a more exact DX7 EG/output drone at the modulator peak. Until AquaSynth has a more exact DX7 EG/output
model, algorithm-2 non-carrier applied envelopes cap only the first staged model, algorithm-2 non-carrier applied envelopes cap only the first staged
peak at `0.92`. This leaves `Piano Bass` unchanged, improves `RES SYNTH1`, peak at `0.92`. This leaves `Piano Bass` unchanged, improves `RES SYNTH1`,
and keeps `ANLGSYN 1` judged primarily by log-mel plus sustained high-band and keeps `ANLGSYN 1` judged primarily by log-mel plus sustained high-band

View file

@ -1,7 +1,7 @@
# Migration Checklist # Migration Checklist
This repo is now the authoring-side home for Aquarium Synth patch scripts and This repo is now the authoring-side home for AquaSynth patch scripts and
Faust generation. The Rust crate remains useful as a reference lab, but the Faust generation. The AquaSynth-rs crate remains useful as a reference lab, but the
current C# surface should not depend on somebody remembering what got built current C# surface should not depend on somebody remembering what got built
there. there.
@ -21,7 +21,7 @@ there.
classic 808, FM bell, wobble bass, and advanced layered patches. classic 808, FM bell, wobble bass, and advanced layered patches.
- Abstract SFXR reference script using defaults and templates. - Abstract SFXR reference script using defaults and templates.
- SFXR parameter presets, mutation, shorthand atoms, and SFXR-to-patch mapping. - SFXR parameter presets, mutation, shorthand atoms, and SFXR-to-patch mapping.
- Aquarium preset patches: pluck, heartbeat, voice, and SFXR named bridge. - AquaSynth preset patches: pluck, heartbeat, voice, and SFXR named bridge.
- Patch script scoring: terse, readability, and balanced metrics. - Patch script scoring: terse, readability, and balanced metrics.
- Audio analysis and comparison: envelope, log-mel spectrogram, spectral - Audio analysis and comparison: envelope, log-mel spectrogram, spectral
features, distance metrics, and score. features, distance metrics, and score.

View file

@ -1,10 +1,10 @@
# Reference Synth Roadmap # Reference Synth Roadmap
AquariumSynthCSharp needs external synthesis references for the same reason the AquaSynth needs external synthesis references for the same reason the
SFXR path was useful: a real target prevents the DSL from becoming a pile of SFXR path was useful: a real target prevents the DSL from becoming a pile of
locally plausible knobs. The goal is not to clone every synth's internals. The locally plausible knobs. The goal is not to clone every synth's internals. The
goal is to use known engines and patch formats as pressure tests, then rebuild goal is to use known engines and patch formats as pressure tests, then rebuild
selected sounds through Aquarium's own graph until the missing abstractions are selected sounds through AquaSynth's own graph until the missing abstractions are
obvious. obvious.
## Objective ## Objective
@ -13,18 +13,18 @@ Build a reference-driven synth library workflow:
- Parse or describe external synth patches as structured reference models. - Parse or describe external synth patches as structured reference models.
- Render references when legally and technically practical. - Render references when legally and technically practical.
- Rebuild selected references in Aquarium patch DSL. - Rebuild selected references in AquaSynth patch DSL.
- Compare audio with analysis metrics and listening notes. - Compare audio with analysis metrics and listening notes.
- Promote only the abstractions that make repeated reference rebuilds simpler. - Promote only the abstractions that make repeated reference rebuilds simpler.
- Reject reference targets we cannot turn into parity tests. Parsing a format is - Reject reference targets we cannot turn into parity tests. Parsing a format is
inventory; proving Aquarium can reproduce the behavior is the point. inventory; proving AquaSynth can reproduce the behavior is the point.
The DSL should grow because a reference target demanded a missing invariant, not The DSL should grow because a reference target demanded a missing invariant, not
because a field name seemed maybe useful at 2 AM. because a field name seemed maybe useful at 2 AM.
## Live Invariants ## Live Invariants
- Aquarium owns its patch graph. External synths are reference targets, not - AquaSynth owns its patch graph. External synths are reference targets, not
runtime dependencies. runtime dependencies.
- Expressive parameters are part of the patch contract. Callers must be able to - Expressive parameters are part of the patch contract. Callers must be able to
change exposed values at runtime through the hosted DSP parameter API instead change exposed values at runtime through the hosted DSP parameter API instead
@ -32,10 +32,10 @@ runtime dependencies.
- Reference importers must preserve source provenance, license status, and the - Reference importers must preserve source provenance, license status, and the
source format hash. source format hash.
- Tests must compare against rendered fixtures, extracted parameter topology, or - Tests must compare against rendered fixtures, extracted parameter topology, or
known structural expectations that prove Aquarium can express the same behavior. known structural expectations that prove AquaSynth can express the same behavior.
Weak "it parsed" tests do not prove expressivity, and references without a Weak "it parsed" tests do not prove expressivity, and references without a
credible parity test path should not be consumed. credible parity test path should not be consumed.
- Aquarium Faust output can be rendered through Faust-generated C# for candidate - AquaSynth Faust output can be rendered through Faust-generated C# for candidate
audio comparison. Reference audio still needs a real source fixture or renderer; audio comparison. Reference audio still needs a real source fixture or renderer;
do not compare a rebuild only against itself and call that parity. do not compare a rebuild only against itself and call that parity.
- DX7 reference audio uses `dexed-py` when available, with public-domain SysEx - DX7 reference audio uses `dexed-py` when available, with public-domain SysEx
@ -43,7 +43,7 @@ credible parity test path should not be consumed.
package content. package content.
- ZynAddSubFX source is pinned under `external/zynaddsubfx` as GPL - ZynAddSubFX source is pinned under `external/zynaddsubfx` as GPL
test/development material. It may be compiled or wrapped only for parity test/development material. It may be compiled or wrapped only for parity
tests; Aquarium must not ship, link, or runtime-depend on it. tests; AquaSynth must not ship, link, or runtime-depend on it.
- Do not add a generic graph escape hatch until a specific family of references - Do not add a generic graph escape hatch until a specific family of references
forces it. forces it.
- When a reference target exceeds the current voice-centric DSL, document the - When a reference target exceeds the current voice-centric DSL, document the
@ -62,7 +62,7 @@ the Yamaha DX7 and accepts SysEx cartridge data. Its `msfa` synth engine remains
Apache-2.0, which may matter if we use code as a reference rather than only Apache-2.0, which may matter if we use code as a reference rather than only
format notes. format notes.
Pressure on Aquarium DSL: Pressure on AquaSynth DSL:
- Operator graph representation. - Operator graph representation.
- Multi-stage envelopes beyond ADSR. - Multi-stage envelopes beyond ADSR.
@ -76,9 +76,9 @@ First deliverable:
- Add a `Dx7SysEx` parser for single voice and cartridge payloads. - Add a `Dx7SysEx` parser for single voice and cartridge payloads.
- Convert one or two public-domain or project-authored DX7-style voices into - Convert one or two public-domain or project-authored DX7-style voices into
`ReferencePatch` records. `ReferencePatch` records.
- Build an Aquarium DSL approximation using explicit FM templates. - Build an AquaSynth DSL approximation using explicit FM templates.
- Add mel/envelope/spectral comparison against rendered Dexed output from the - Add mel/envelope/spectral comparison against rendered Dexed output from the
public-domain fixture. Aquarium candidate rendering and `dexed-py` reference public-domain fixture. AquaSynth candidate rendering and `dexed-py` reference
rendering exist, and `analog1.syx` voice 13 has the first thresholded parity rendering exist, and `analog1.syx` voice 13 has the first thresholded parity
rebuild. The next pressure is a harder voice that actually needs audible rebuild. The next pressure is a harder voice that actually needs audible
operator routing rather than a sine-like behavioral match. Staged operator operator routing rather than a sine-like behavioral match. Staged operator
@ -103,7 +103,7 @@ synthesis engines. It ships a large instrument library and exposes formant
filters, state-variable filters, free envelope shapes, layered kits, modulation, filters, state-variable filters, free envelope shapes, layered kits, modulation,
and effects routing. and effects routing.
Pressure on Aquarium DSL: Pressure on AquaSynth DSL:
- Additive harmonic banks. - Additive harmonic banks.
- Non-ADSR envelope curves. - Non-ADSR envelope curves.
@ -131,7 +131,7 @@ First deliverable:
The upstream ZynAddSubFX engine source is pinned as `external/zynaddsubfx` The upstream ZynAddSubFX engine source is pinned as `external/zynaddsubfx`
for parity tests, currently at `3ab608c432996ba4d582176572c0b0f82328c825`. for parity tests, currently at `3ab608c432996ba4d582176572c0b0f82328c825`.
That checkout is GPL reference machinery, not Aquarium implementation. Do not That checkout is GPL reference machinery, not AquaSynth implementation. Do not
vendor the upstream Zyn instrument-bank submodule until its preset vendor the upstream Zyn instrument-bank submodule until its preset
license/provenance is explicit enough for this repo. Use project-authored license/provenance is explicit enough for this repo. Use project-authored
fixtures for parser shape and only promote external fixtures with clean source fixtures for parser shape and only promote external fixtures with clean source
@ -144,7 +144,7 @@ PAD, additive banks, or formant motion.
The first language-golf response is `layer`: a named layer declaration owns The first language-golf response is `layer`: a named layer declaration owns
engine tags, optional MIDI key range metadata, default gain, and an effect-send engine tags, optional MIDI key range metadata, default gain, and an effect-send
label while lowering to ordinary Aquarium voices. It is intentionally only an label while lowering to ordinary AquaSynth voices. It is intentionally only an
ownership/routing scaffold for now; it does not add PAD synthesis, free ownership/routing scaffold for now; it does not add PAD synthesis, free
envelopes, or effect buses by stealth. envelopes, or effect buses by stealth.
@ -184,7 +184,7 @@ PAD engine parity across all harmonic profiles, randomness controls, or
multi-table pitch-zone behavior. `root`, `spread`, and `partials` are multi-table pitch-zone behavior. `root`, `spread`, and `partials` are
authoring-time table shape, not runtime controls. authoring-time table shape, not runtime controls.
The canonical Aquarium spelling for richer spectral-table shaping is neutral: The canonical AquaSynth spelling for richer spectral-table shaping is neutral:
`pad_mode`, `pad_bandwidth`, `pad_bwscale`, `pad_profile`, and `pad_position`. `pad_mode`, `pad_bandwidth`, `pad_bwscale`, `pad_profile`, and `pad_position`.
The Zyn importer may populate those fields from Zyn's PAD controls, and the The Zyn importer may populate those fields from Zyn's PAD controls, and the
parser still accepts older `zyn_*` names in generated parity artifacts, but parser still accepts older `zyn_*` names in generated parity artifacts, but
@ -204,7 +204,7 @@ repo contains categorized examples, and the standard libraries include
oscillators, filters, delays, reverbs, physical models, DX7, virtual analog oscillators, filters, delays, reverbs, physical models, DX7, virtual analog
effects, wave-digital models, spatialization, and more. effects, wave-digital models, spatialization, and more.
Pressure on Aquarium DSL: Pressure on AquaSynth DSL:
- General signal graph expression. - General signal graph expression.
- Multichannel routing. - Multichannel routing.
@ -219,7 +219,7 @@ First deliverable:
license/provenance, graph family, and expected compile behavior. license/provenance, graph family, and expected compile behavior.
- Select small fixtures from official examples first: comb string, additive - Select small fixtures from official examples first: comb string, additive
drum, stereo reverb, physical model, WDF filter. drum, stereo reverb, physical model, WDF filter.
- Use Faust references to decide whether Aquarium needs a lower-level graph DSL - Use Faust references to decide whether AquaSynth needs a lower-level graph DSL
beneath the current instrument patch surface. beneath the current instrument patch surface.
Sources: Sources:
@ -235,7 +235,7 @@ Sources:
Surge XT is a modern GPL-3.0 hybrid synth with a large real-world patch corpus. Surge XT is a modern GPL-3.0 hybrid synth with a large real-world patch corpus.
It is a strong long-term target, but it is too broad for the first import pass. It is a strong long-term target, but it is too broad for the first import pass.
Pressure on Aquarium DSL: Pressure on AquaSynth DSL:
- Multi-scene patch structure. - Multi-scene patch structure.
- Multiple oscillator algorithms. - Multiple oscillator algorithms.
@ -256,10 +256,10 @@ Source: [Surge XT](https://github.com/surge-synthesizer/surge)
### Tier 5: Cardinal/VCV-Style Modular Patches ### Tier 5: Cardinal/VCV-Style Modular Patches
Cardinal is a GPL-3.0 self-contained open-source modular synth based on VCV Rack Cardinal is a GPL-3.0 self-contained open-source modular synth based on VCV Rack
code. It is the right target once Aquarium needs to prove arbitrary modular graph code. It is the right target once AquaSynth needs to prove arbitrary modular graph
expression. expression.
Pressure on Aquarium DSL: Pressure on AquaSynth DSL:
- Module graph representation. - Module graph representation.
- Cables as first-class edges. - Cables as first-class edges.
@ -288,7 +288,7 @@ public sealed record ReferencePatch(
ReferenceSource Source, ReferenceSource Source,
IReadOnlyList<ReferenceFeature> Features, IReadOnlyList<ReferenceFeature> Features,
IReadOnlyList<PatchParameter> Parameters, IReadOnlyList<PatchParameter> Parameters,
string? AquariumScript); string? AquaSynthScript);
public sealed record ReferenceSource( public sealed record ReferenceSource(
string Kind, string Kind,
@ -310,8 +310,8 @@ public sealed record PatchParameter(
``` ```
This keeps source authority and runtime control authority visible. A reference This keeps source authority and runtime control authority visible. A reference
can be "known target, not yet translated" without pretending it is an Aquarium can be "known target, not yet translated" without pretending it is an AquaSynth
patch, and an Aquarium patch can say which values callers may vary without patch, and an AquaSynth patch can say which values callers may vary without
changing graph shape. changing graph shape.
## Runtime Parameter Surface ## Runtime Parameter Surface
@ -319,10 +319,10 @@ changing graph shape.
Faust supports this directly through UI controls such as `hslider`, `vslider`, Faust supports this directly through UI controls such as `hslider`, `vslider`,
`nentry`, `button`, and `checkbox`. Hosted DSP code can expose a parameter map `nentry`, `button`, and `checkbox`. Hosted DSP code can expose a parameter map
and update control zones with `setParamValue`/`getParamValue` style APIs, so and update control zones with `setParamValue`/`getParamValue` style APIs, so
Aquarium should compile once for a graph shape and then push parameter values at AquaSynth should compile once for a graph shape and then push parameter values at
runtime. runtime.
Aquarium DSL needs an explicit parameter declaration surface, likely separate AquaSynth DSL needs an explicit parameter declaration surface, likely separate
from modulation buses: from modulation buses:
```text ```text
@ -346,7 +346,7 @@ Lowering rules:
1. Create reference model contracts, parameter contracts, and tests. 1. Create reference model contracts, parameter contracts, and tests.
2. Add explicit patch parameter declarations and Faust parameter emission. 2. Add explicit patch parameter declarations and Faust parameter emission.
3. Add DX7 SysEx parser and feature extraction. 3. Add DX7 SysEx parser and feature extraction.
4. Add two DX7-derived Aquarium DSL rebuilds with exposed macro controls. 4. Add two DX7-derived AquaSynth DSL rebuilds with exposed macro controls.
5. Add audio fixture workflow for rendered references. 5. Add audio fixture workflow for rendered references.
6. Add Zyn XML feature extraction. 6. Add Zyn XML feature extraction.
7. Add Faust reference catalog entries and compile checks. 7. Add Faust reference catalog entries and compile checks.
@ -361,7 +361,7 @@ Lowering rules:
of fields. of fields.
- Do not compare only parse success. That proves the parser is lenient, not that - Do not compare only parse success. That proves the parser is lenient, not that
the DSL can express the sound. the DSL can express the sound.
- Do not use Faust as a hidden escape hatch for Aquarium patches. If a reference - Do not use Faust as a hidden escape hatch for AquaSynth patches. If a reference
requires raw Faust, the DSL is missing an abstraction or the reference belongs requires raw Faust, the DSL is missing an abstraction or the reference belongs
in a lower-level graph layer. in a lower-level graph layer.
@ -370,5 +370,5 @@ Lowering rules:
- A new agent can pick one target family and know where to start. - A new agent can pick one target family and know where to start.
- Every reference fixture says what feature pressure it applies. - Every reference fixture says what feature pressure it applies.
- The DSL roadmap is driven by reference failures, not taste fumes. - The DSL roadmap is driven by reference failures, not taste fumes.
- Aquarium can keep using readable reference patches while the synth library - AquaSynth can keep using readable reference patches while the synth library
grows toward serious DSP graph expression. grows toward serious DSP graph expression.

View file

@ -2,13 +2,13 @@
## Objective ## Objective
Find the ZynAddSubFX instruments most likely to break Aquarium's current patch Find the ZynAddSubFX instruments most likely to break AquaSynth's current patch
language before spending time on syntax polish. language before spending time on syntax polish.
This is a feature-pressure survey, not an audio parity claim. The upstream Zyn This is a feature-pressure survey, not an audio parity claim. The upstream Zyn
instrument bank is GPL reference feedstock through the pinned Zyn submodule instrument bank is GPL reference feedstock through the pinned Zyn submodule
tree. It is development/test material only: useful for reports, parity renders, tree. It is development/test material only: useful for reports, parity renders,
and pressure selection, never package content or Aquarium patch-library stock. and pressure selection, never package content or AquaSynth patch-library stock.
## Source Checked ## Source Checked
@ -53,7 +53,7 @@ GPL PAD batch through the real Zyn oracle when the renderer is built:
Each render lands in a per-synth timestamped folder under Each render lands in a per-synth timestamped folder under
`artifacts/parity/zyn-upstream-pad-fixtures/`, with Zyn reference WAVs, source `artifacts/parity/zyn-upstream-pad-fixtures/`, with Zyn reference WAVs, source
table WAVs, `report.txt`, and `candidate.aqua`. The candidate is now generated table WAVs, `report.txt`, and `candidate.aqua`. The candidate is now generated
per preset by `ZynInstrumentReader.RebuildFirstPadAsAquariumScript`: first per preset by `ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript`: first
enabled PAD kit item, OSCIL harmonic magnitudes, oracle-reported table enabled PAD kit item, OSCIL harmonic magnitudes, oracle-reported table
basefreq, and basic volume/envelope scaffolding. The report names missing basefreq, and basic volume/envelope scaffolding. The report names missing
semantics such as Zyn harmonic profile shaping, bandwidth/profile behavior, and semantics such as Zyn harmonic profile shaping, bandwidth/profile behavior, and
@ -62,7 +62,7 @@ library candidates.
## Pressure Conclusions ## Pressure Conclusions
- **Named layers are real.** Anonymous Aquarium voices can sketch small Zyn - **Named layers are real.** Anonymous AquaSynth voices can sketch small Zyn
layers, but drum kits and clonewheel-style instruments need stable layer names, layers, but drum kits and clonewheel-style instruments need stable layer names,
key ranges, engine identity, and per-layer routing. key ranges, engine identity, and per-layer routing.
- **Free envelopes are not optional forever.** Zyn's worst cases use dozens of - **Free envelopes are not optional forever.** Zyn's worst cases use dozens of
@ -86,7 +86,7 @@ next abstractions risk becoming loose fields on anonymous voices.
## First Responses ## First Responses
Aquarium now has a minimal `layer` command: AquaSynth now has a minimal `layer` command:
```text ```text
layer name=pad_low engine=pad min_key=36 max_key=84 gain=.07 layer name=pad_low engine=pad min_key=36 max_key=84 gain=.07

View file

@ -1,8 +1,8 @@
# Aquarium Patch Library # AquaSynth Patch Library
This folder is the development patch library for Aquarium `.aqua` scripts. It is where reusable stock, reference rebuilds, calibration probes, and inspiration patches live as files instead of being trapped inside C# string constants or ignored render artifacts. This folder is the development patch library for AquaSynth `.aqua` scripts. It is where reusable stock, reference rebuilds, calibration probes, and inspiration patches live as files instead of being trapped inside C# string constants or ignored render artifacts.
The library is intentionally outside `src/` and is not part of the shipped `AquariumSynth.Dsl` NuGet package. Treat it as repo-owned source material for future synth consumers, examples, and parity work. The library is intentionally outside `src/` and is not part of the shipped `AquaSynth.Dsl` NuGet package. Treat it as repo-owned source material for future synth consumers, examples, and parity work.
## Layout ## Layout

View file

@ -1,6 +1,6 @@
version: 1 version: 1
purpose: Development patch library for Aquarium DSL examples, parity rebuilds, and stock inspiration. purpose: Development patch library for AquaSynth DSL examples, parity rebuilds, and stock inspiration.
package_boundary: Not included in AquariumSynth.Dsl.nupkg unless explicitly promoted later. package_boundary: Not included in AquaSynth.Dsl.nupkg unless explicitly promoted later.
entries: entries:
- path: patches/808/clap.aqua - path: patches/808/clap.aqua
family: 808 family: 808

View file

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PackageId>AquariumSynth.Dsl</PackageId> <PackageId>AquaSynth.Dsl</PackageId>
<Version>0.1.0-aquarium.1</Version> <Version>0.1.0-aquasynth.1</Version>
<Authors>GameCult</Authors> <Authors>GameCult</Authors>
<Company>GameCult</Company> <Company>GameCult</Company>
<Description>Aquarium synth patch DSL model, parser, analysis helpers, and Faust source emitter.</Description> <Description>AquaSynth patch DSL model, parser, analysis helpers, and Faust source emitter.</Description>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/GameCult/AquariumSynthCSharp</RepositoryUrl> <RepositoryUrl>https://github.com/GameCult/AquaSynth</RepositoryUrl>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -1,6 +1,6 @@
using System.Numerics; using System.Numerics;
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed record AudioAnalysisConfig( public sealed record AudioAnalysisConfig(
float SampleRate = 44100, float SampleRate = 44100,

View file

@ -1,4 +1,4 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public static class BuiltInScripts public static class BuiltInScripts
{ {

View file

@ -1,6 +1,6 @@
using System.Security.Cryptography; using System.Security.Cryptography;
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public enum Dx7FrequencyMode public enum Dx7FrequencyMode
{ {
@ -316,7 +316,7 @@ public static class Dx7SysEx
return new Dx7RateLevelEnvelopeApproximation( return new Dx7RateLevelEnvelopeApproximation(
new RateLevelEnvelope(r1, l1, r2, l2, r3, l3, r4, l4), new RateLevelEnvelope(r1, l1, r2, l2, r3, l3, r4, l4),
Math.Max(r1 + r2 + r3, 0.02f), Math.Max(r1 + r2 + r3, 0.02f),
"Approximation: DX7 EG uses four rate/level segments; Aquarium staged envelopes preserve the intermediate target levels."); "Approximation: DX7 EG uses four rate/level segments; AquaSynth staged envelopes preserve the intermediate target levels.");
} }
public static Dx7RateLevelEnvelopeApproximation ApproximateAppliedRateLevelEnvelope( public static Dx7RateLevelEnvelopeApproximation ApproximateAppliedRateLevelEnvelope(
@ -359,7 +359,7 @@ public static class Dx7SysEx
RateLevelCurve.Exponential, RateLevelCurve.Exponential,
RateLevelCurve.Exponential), RateLevelCurve.Exponential),
gateSeconds, gateSeconds,
"Approximation: DX7 applied EG gain sampled per 64-sample block and normalized for Aquarium operator-level scaling."); "Approximation: DX7 applied EG gain sampled per 64-sample block and normalized for AquaSynth operator-level scaling.");
} }
public static IReadOnlyList<Dx7EnvelopeTracePoint> TraceEnvelope( public static IReadOnlyList<Dx7EnvelopeTracePoint> TraceEnvelope(

View file

@ -2,9 +2,9 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed record FaustExportOptions(string Name = "aquarium_patch", bool Stereo = false); public sealed record FaustExportOptions(string Name = "aquasynth_patch", bool Stereo = false);
public sealed record FaustExport(string Source, IReadOnlyList<string> Warnings); public sealed record FaustExport(string Source, IReadOnlyList<string> Warnings);
@ -655,7 +655,7 @@ public static class FaustCompiler
faustPath ??= FindFaust(); faustPath ??= FindFaust();
if (faustPath is null) return null; if (faustPath is null) return null;
var sourcePath = Path.Combine(Path.GetTempPath(), $"aquarium-synth-{Guid.NewGuid():N}.dsp"); var sourcePath = Path.Combine(Path.GetTempPath(), $"aquasynth-{Guid.NewGuid():N}.dsp");
await File.WriteAllTextAsync(sourcePath, source, cancellationToken); await File.WriteAllTextAsync(sourcePath, source, cancellationToken);
try try
{ {
@ -691,7 +691,7 @@ public static class FaustCompiler
if (faustPath is null) return null; if (faustPath is null) return null;
var frames = Math.Max(1, (int)MathF.Round(options.SampleRate * Math.Max(options.DurationSeconds, 1f / options.SampleRate))); var frames = Math.Max(1, (int)MathF.Round(options.SampleRate * Math.Max(options.DurationSeconds, 1f / options.SampleRate)));
var tempDir = Path.Combine(Path.GetTempPath(), $"aquarium-synth-render-{Guid.NewGuid():N}"); var tempDir = Path.Combine(Path.GetTempPath(), $"aquasynth-render-{Guid.NewGuid():N}");
Directory.CreateDirectory(tempDir); Directory.CreateDirectory(tempDir);
try try
{ {

View file

@ -1,4 +1,4 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public enum Waveform public enum Waveform
{ {
@ -309,7 +309,7 @@ public sealed record ReferencePatch(
ReferenceSource Source, ReferenceSource Source,
IReadOnlyList<ReferenceFeature> Features, IReadOnlyList<ReferenceFeature> Features,
IReadOnlyList<PatchParameter> Parameters, IReadOnlyList<PatchParameter> Parameters,
string? AquariumScript = null); string? AquaSynthScript = null);
public sealed record Repeat(float IntervalSeconds); public sealed record Repeat(float IntervalSeconds);

View file

@ -1,6 +1,6 @@
using System.Numerics; using System.Numerics;
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
internal static class PadSynthWaveform internal static class PadSynthWaveform
{ {

View file

@ -1,6 +1,6 @@
using System.Globalization; using System.Globalization;
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed class PatchScriptException(int line, string message) : Exception($"line {line}: {message}") public sealed class PatchScriptException(int line, string message) : Exception($"line {line}: {message}")
{ {

View file

@ -1,4 +1,4 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
internal readonly record struct PatchScriptStatement(string Text, int Line); internal readonly record struct PatchScriptStatement(string Text, int Line);

View file

@ -1,8 +1,8 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public static class Presets public static class Presets
{ {
public static SynthPatch AquariumPluck() public static SynthPatch AquaSynthPluck()
{ {
var envelope = new Envelope(0.002f, 0, 0.7246377f, 0.43f); var envelope = new Envelope(0.002f, 0, 0.7246377f, 0.43f);
return new SynthPatch return new SynthPatch
@ -18,7 +18,7 @@ public static class Presets
}; };
} }
public static SynthPatch AquariumHeartbeat() public static SynthPatch AquaSynthHeartbeat()
{ {
var envelope = new Envelope(0.004f, 0, 0.617284f, 0.2f); var envelope = new Envelope(0.004f, 0, 0.617284f, 0.2f);
return new SynthPatch return new SynthPatch
@ -33,7 +33,7 @@ public static class Presets
}; };
} }
public static SynthPatch AquariumVoice() public static SynthPatch AquaSynthVoice()
{ {
var voice = Simple(new Oscillator(Waveform.Triangle, 220), new Envelope(0.018f, 0, 0.9259259f, 0.28f), 0.34f, 0.1944f) with var voice = Simple(new Oscillator(Waveform.Triangle, 220), new Envelope(0.018f, 0, 0.9259259f, 0.28f), 0.34f, 0.1944f) with
{ {

View file

@ -1,4 +1,4 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed record ReferenceRebuild( public sealed record ReferenceRebuild(
string Id, string Id,
@ -15,7 +15,7 @@ public static class ReferenceRebuildCatalog
public static readonly IReadOnlyList<ReferenceRebuild> ZynRebuilds = public static readonly IReadOnlyList<ReferenceRebuild> ZynRebuilds =
[ [
new( new(
"zyn/project-additive-lead/aquarium", "zyn/project-additive-lead/aquasynth",
"zynaddsubfx", "zynaddsubfx",
"Project additive lead", "Project additive lead",
"zyn/project/additive-lead", "zyn/project/additive-lead",
@ -24,17 +24,17 @@ public static class ReferenceRebuildCatalog
new("engine_add", "1", "Represented as additive sine partials under named layer authority."), new("engine_add", "1", "Represented as additive sine partials under named layer authority."),
new("named_layers", "body,shine", "Layer declarations group partial voices under reusable ADD engine intent."), new("named_layers", "body,shine", "Layer declarations group partial voices under reusable ADD engine intent."),
new("additive_harmonic_bank", "2 banks / 4 partials", "Harmonic-bank syntax records authored ADD partial sets before lowering to ordinary voices."), new("additive_harmonic_bank", "2 banks / 4 partials", "Harmonic-bank syntax records authored ADD partial sets before lowering to ordinary voices."),
new("partial_voice_count", "4", "The additive shape lowers to four Aquarium partial voices."), new("partial_voice_count", "4", "The additive shape lowers to four AquaSynth partial voices."),
new("envelope_count", "2", "Amplitude and pitch contour pressure maps to ADSR plus vibrato in the current patch surface."), new("envelope_count", "2", "Amplitude and pitch contour pressure maps to ADSR plus vibrato in the current patch surface."),
new("lfo_count", "1", "The fixture's amplitude/frequency motion is represented as patch-level vibrato/macro motion.") new("lfo_count", "1", "The fixture's amplitude/frequency motion is represented as patch-level vibrato/macro motion.")
], ],
[ [
new("zyn_addsynth_oscillator_detail", "Zyn ADDsynth harmonic phase/bandwidth details", "Aquarium records harmonic ratios and gains, but not Zyn's full ADDsynth oscillator table semantics."), new("zyn_addsynth_oscillator_detail", "Zyn ADDsynth harmonic phase/bandwidth details", "AquaSynth records harmonic ratios and gains, but not Zyn's full ADDsynth oscillator table semantics."),
new("zyn_free_envelope_exactness", "Zyn envelope parameter semantics", "The rebuild uses Aquarium ADSR/motion controls rather than Zyn's exact envelope timing model.") new("zyn_free_envelope_exactness", "Zyn envelope parameter semantics", "The rebuild uses AquaSynth ADSR/motion controls rather than Zyn's exact envelope timing model.")
], ],
"This is the easy Zyn pressure case: Aquarium can now say the sound as named harmonic banks while keeping the emitted voice graph plain enough to inspect."), "This is the easy Zyn pressure case: AquaSynth can now say the sound as named harmonic banks while keeping the emitted voice graph plain enough to inspect."),
new( new(
"zyn/project-pad-texture/aquarium", "zyn/project-pad-texture/aquasynth",
"zynaddsubfx", "zynaddsubfx",
"Project PAD texture", "Project PAD texture",
"zyn/project/pad-texture", "zyn/project/pad-texture",
@ -49,12 +49,12 @@ public static class ReferenceRebuildCatalog
new("slow_modulation", "patch_lfo", "The rebuild adds slow patch modulation because PAD texture wants motion even though the minimal fixture only marks PAD/filter/free-envelope pressure.") new("slow_modulation", "patch_lfo", "The rebuild adds slow patch modulation because PAD texture wants motion even though the minimal fixture only marks PAD/filter/free-envelope pressure.")
], ],
[ [
new("padsynth_wavetable_exactness", "Full Zyn PAD engine behavior", "Aquarium now uses frequency-domain spreading, deterministic random phase, and one inverse FFT, but does not cover every Zyn harmonic profile, randomness control, or pitch-zone table behavior."), new("padsynth_wavetable_exactness", "Full Zyn PAD engine behavior", "AquaSynth now uses frequency-domain spreading, deterministic random phase, and one inverse FFT, but does not cover every Zyn harmonic profile, randomness control, or pitch-zone table behavior."),
new("free_envelope_shape", "arbitrary Zyn envelope points", "Aquarium has staged voice envelopes now, but not arbitrary point/free-mode Zyn envelope curves.") new("free_envelope_shape", "arbitrary Zyn envelope points", "AquaSynth has staged voice envelopes now, but not arbitrary point/free-mode Zyn envelope curves.")
], ],
"This is pressure, not a claim of full Zyn PAD parity. Aquarium can now state a layered FFT spectral cloud, while exact Zyn PAD profile coverage remains separate pressure."), "This is pressure, not a claim of full Zyn PAD parity. AquaSynth can now state a layered FFT spectral cloud, while exact Zyn PAD profile coverage remains separate pressure."),
new( new(
"zyn/project-vocal-layer/aquarium", "zyn/project-vocal-layer/aquasynth",
"zynaddsubfx", "zynaddsubfx",
"Project vocal layer", "Project vocal layer",
"zyn/project/vocal-layer", "zyn/project/vocal-layer",
@ -62,15 +62,15 @@ public static class ReferenceRebuildCatalog
[ [
new("engine_add", "1", "The air/formant layer is represented as a formant-filtered oscillator voice."), new("engine_add", "1", "The air/formant layer is represented as a formant-filtered oscillator voice."),
new("engine_sub", "1", "The body layer is represented as a lower triangle voice."), new("engine_sub", "1", "The body layer is represented as a lower triangle voice."),
new("layered_instrument", "yes", "Multiple Zyn kit items map to multiple Aquarium voices."), new("layered_instrument", "yes", "Multiple Zyn kit items map to multiple AquaSynth voices."),
new("named_layers", "air,body,breath", "Layer declarations preserve source identity before voice expansion."), new("named_layers", "air,body,breath", "Layer declarations preserve source identity before voice expansion."),
new("voice_rate_level_envelope", "3", "Each vocal layer uses a staged rate/level voice envelope for asymmetric onset and release shape."), new("voice_rate_level_envelope", "3", "Each vocal layer uses a staged rate/level voice envelope for asymmetric onset and release shape."),
new("formant_filter_count", "1", "Represented with Aquarium formant filters and a vowel macro."), new("formant_filter_count", "1", "Represented with AquaSynth formant filters and a vowel macro."),
new("effect_count", "1", "Approximated with local drive/soft clipping rather than a Zyn effect slot.") new("effect_count", "1", "Approximated with local drive/soft clipping rather than a Zyn effect slot.")
], ],
[ [
new("kit_item_authority", "Zyn per-kit-item engine/effect routing", "Aquarium has layered voices but no named kit-item/layer contract with per-layer effect sends."), new("kit_item_authority", "Zyn per-kit-item engine/effect routing", "AquaSynth has layered voices but no named kit-item/layer contract with per-layer effect sends."),
new("formant_motion", "Zyn formant/vowel morph details", "Aquarium can mix static formants, but richer vowel morphing remains pressure.") new("formant_motion", "Zyn formant/vowel morph details", "AquaSynth can mix static formants, but richer vowel morphing remains pressure.")
], ],
"This rebuild keeps the useful part: layered source plus formant intent. It also exposes that repeated Zyn-style kit targets will need explicit layer naming and routing rather than anonymous voice piles.") "This rebuild keeps the useful part: layered source plus formant intent. It also exposes that repeated Zyn-style kit targets will need explicit layer naming and routing rather than anonymous voice piles.")
]; ];
@ -78,55 +78,55 @@ public static class ReferenceRebuildCatalog
public static readonly IReadOnlyList<ReferenceRebuild> Dx7Rebuilds = public static readonly IReadOnlyList<ReferenceRebuild> Dx7Rebuilds =
[ [
new( new(
"dx7/algo32-additive-organ/aquarium", "dx7/algo32-additive-organ/aquasynth",
"dx7", "dx7",
"Algorithm 32 additive organ", "Algorithm 32 additive organ",
"dx7/algo32-additive-organ", "dx7/algo32-additive-organ",
BuiltInScripts.Dx7StyleAlgorithm32AdditiveOrgan, BuiltInScripts.Dx7StyleAlgorithm32AdditiveOrgan,
[ [
new("carrier_operators", "1,2,3,4,5,6", "Six independent carriers map cleanly to six Aquarium voices."), new("carrier_operators", "1,2,3,4,5,6", "Six independent carriers map cleanly to six AquaSynth voices."),
new("modulation_edge_count", "0", "No operator-to-operator modulation is needed for the additive shape."), new("modulation_edge_count", "0", "No operator-to-operator modulation is needed for the additive shape."),
new("self_feedback_operators", "6", "Approximated with fold/drive on the highest partial."), new("self_feedback_operators", "6", "Approximated with fold/drive on the highest partial."),
new("operator_envelope_approximation", "DX7 EG -> ADSR", "DX7 four-rate/four-level envelopes can now be lowered to labeled ADSR approximations.") new("operator_envelope_approximation", "DX7 EG -> ADSR", "DX7 four-rate/four-level envelopes can now be lowered to labeled ADSR approximations.")
], ],
[ [
new("operator_envelope_exactness", "per-operator four-stage DX7 EG", "Aquarium has an ADSR approximation helper, not exact DX7 rate/level envelope execution."), new("operator_envelope_exactness", "per-operator four-stage DX7 EG", "AquaSynth has an ADSR approximation helper, not exact DX7 rate/level envelope execution."),
new("output_compensation", "algorithm ROM COM", "Aquarium voice gains are explicit and not derived from DX7 COM scaling.") new("output_compensation", "algorithm ROM COM", "AquaSynth voice gains are explicit and not derived from DX7 COM scaling.")
], ],
"This is the honest easy case: DX7 algorithm 32 is mostly additive synthesis, so the current voice DSL can represent the carrier layout without pretending to own a full operator graph."), "This is the honest easy case: DX7 algorithm 32 is mostly additive synthesis, so the current voice DSL can represent the carrier layout without pretending to own a full operator graph."),
new( new(
"dx7/algo8-bright-pair/aquarium", "dx7/algo8-bright-pair/aquasynth",
"dx7", "dx7",
"Algorithm 8 bright pair", "Algorithm 8 bright pair",
"dx7/algo8-bright-pair", "dx7/algo8-bright-pair",
BuiltInScripts.Dx7StyleAlgorithm8BrightPair, BuiltInScripts.Dx7StyleAlgorithm8BrightPair,
[ [
new("carrier_operators", "1,3", "Represented as carriers in a first-class Aquarium operator graph."), new("carrier_operators", "1,3", "Represented as carriers in a first-class AquaSynth operator graph."),
new("modulation_edges", "6->5,4+5->3,2->1", "Represented as explicit operator edges in the graph."), new("modulation_edges", "6->5,4+5->3,2->1", "Represented as explicit operator edges in the graph."),
new("feedback_sources", "4", "Represented as operator-local feedback approximation on operator 4."), new("feedback_sources", "4", "Represented as operator-local feedback approximation on operator 4."),
new("runtime_macros", "brightness,strike", "Field-site parameters expose useful sound controls without recompilation."), new("runtime_macros", "brightness,strike", "Field-site parameters expose useful sound controls without recompilation."),
new("operator_envelope_approximation", "DX7 EG -> ADSR", "DX7 four-rate/four-level envelopes can now be lowered to labeled ADSR approximations.") new("operator_envelope_approximation", "DX7 EG -> ADSR", "DX7 four-rate/four-level envelopes can now be lowered to labeled ADSR approximations.")
], ],
[ [
new("dx7_feedback_register", "DX7 feedback scaling/register details", "Aquarium operator feedback is now renderable as a one-sample delayed self-reference, but not calibrated to exact DX7 feedback scaling."), new("dx7_feedback_register", "DX7 feedback scaling/register details", "AquaSynth operator feedback is now renderable as a one-sample delayed self-reference, but not calibrated to exact DX7 feedback scaling."),
new("operator_envelope_exactness", "independent DX7 rate/level envelopes", "Aquarium has per-operator ADSR approximation, not exact DX7 EG execution.") new("operator_envelope_exactness", "independent DX7 rate/level envelopes", "AquaSynth has per-operator ADSR approximation, not exact DX7 EG execution.")
], ],
"This patch is deliberately a pressure test. The new operator graph owns the topology; exact DX7 envelope and feedback-register behavior remain pressure for later refinement."), "This patch is deliberately a pressure test. The new operator graph owns the topology; exact DX7 envelope and feedback-register behavior remain pressure for later refinement."),
new( new(
"dx7/public-domain-mc-mm-5-3/aquarium", "dx7/public-domain-mc-mm-5-3/aquasynth",
"dx7", "dx7",
"Public-domain MC-MM 5-3", "Public-domain MC-MM 5-3",
"dx7/public-domain/analog1/13", "dx7/public-domain/analog1/13",
BuiltInScripts.Dx7StylePublicDomainMcMm53, BuiltInScripts.Dx7StylePublicDomainMcMm53,
[ [
new("reference_fixture", "analog1.syx#13", "Public-domain DX7 SysEx fixture from Musical Artifacts artifact 152."), new("reference_fixture", "analog1.syx#13", "Public-domain DX7 SysEx fixture from Musical Artifacts artifact 152."),
new("audio_parity", "score>=0.75 log_mel<0.12 envelope<0.10", "First rendered DX7 parity target using dexed-py reference audio and Faust-rendered Aquarium audio."), new("audio_parity", "score>=0.75 log_mel<0.12 envelope<0.10", "First rendered DX7 parity target using dexed-py reference audio and Faust-rendered AquaSynth audio."),
new("dominant_pitch", "654.0639Hz", "The rendered reference behaves as a short C3-derived 5x-ratio sine-like tone."), new("dominant_pitch", "654.0639Hz", "The rendered reference behaves as a short C3-derived 5x-ratio sine-like tone."),
new("operator_envelope_approximation", "DX7 EG -> ADSR", "ADSR timing is fitted to the rendered reference envelope.") new("operator_envelope_approximation", "DX7 EG -> ADSR", "ADSR timing is fitted to the rendered reference envelope.")
], ],
[ [
new("operator_topology_exactness", "2->1 FM pair", "The first parity candidate matches the rendered behavior as a sine-like tone; it does not claim exact DX7 operator-level execution."), new("operator_topology_exactness", "2->1 FM pair", "The first parity candidate matches the rendered behavior as a sine-like tone; it does not claim exact DX7 operator-level execution."),
new("operator_envelope_exactness", "DX7 rate/level EG", "The candidate uses Aquarium ADSR timing matched to rendered audio.") new("operator_envelope_exactness", "DX7 rate/level EG", "The candidate uses AquaSynth ADSR timing matched to rendered audio.")
], ],
"This is the first thresholded audio parity rung. It proves the render/compare loop on a lawful fixture without pretending the DX7 internals are solved.") "This is the first thresholded audio parity rung. It proves the render/compare loop on a lawful fixture without pretending the DX7 internals are solved.")
]; ];

View file

@ -1,4 +1,4 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed record PatchScriptMetrics( public sealed record PatchScriptMetrics(
int ByteCount, int ByteCount,

View file

@ -1,4 +1,4 @@
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed record SfxrParams public sealed record SfxrParams
{ {

View file

@ -5,7 +5,7 @@ using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
namespace AquariumSynth.Dsl; namespace AquaSynth.Dsl;
public sealed record ZynInstrument( public sealed record ZynInstrument(
string Name, string Name,
@ -117,25 +117,25 @@ public static class ZynInstrumentReader
public static ReferenceSource SourceForBytes(string uri, string license, ReadOnlySpan<byte> bytes, string notes = "") => public static ReferenceSource SourceForBytes(string uri, string license, ReadOnlySpan<byte> bytes, string notes = "") =>
new("zyn-xiz", uri, license, Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant(), notes); new("zyn-xiz", uri, license, Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant(), notes);
public static ZynPadRebuild RebuildFirstPadAsAquariumScript( public static ZynPadRebuild RebuildFirstPadAsAquaSynthScript(
string path, string path,
float tableRootFrequencyHz, float tableRootFrequencyHz,
float playbackFrequencyHz = 261.6256f) => float playbackFrequencyHz = 261.6256f) =>
RebuildFirstPadAsAquariumScript(File.ReadAllBytes(path), tableRootFrequencyHz, playbackFrequencyHz); RebuildFirstPadAsAquaSynthScript(File.ReadAllBytes(path), tableRootFrequencyHz, playbackFrequencyHz);
public static ZynPadRebuild RebuildEnabledPadsAsAquariumScript( public static ZynPadRebuild RebuildEnabledPadsAsAquaSynthScript(
string path, string path,
IReadOnlyDictionary<int, float> tableRootFrequencyHzByKitItem, IReadOnlyDictionary<int, float> tableRootFrequencyHzByKitItem,
float playbackFrequencyHz = 261.6256f) => float playbackFrequencyHz = 261.6256f) =>
RebuildEnabledPadsAsAquariumScript(File.ReadAllBytes(path), tableRootFrequencyHzByKitItem, playbackFrequencyHz); RebuildEnabledPadsAsAquaSynthScript(File.ReadAllBytes(path), tableRootFrequencyHzByKitItem, playbackFrequencyHz);
public static ZynPadRebuild RebuildEnabledPadsAsAquariumScript( public static ZynPadRebuild RebuildEnabledPadsAsAquaSynthScript(
string path, string path,
IReadOnlyDictionary<int, ZynPadTableRoot> tableRootFrequencyHzByKitItem, IReadOnlyDictionary<int, ZynPadTableRoot> tableRootFrequencyHzByKitItem,
float playbackFrequencyHz = 261.6256f) => float playbackFrequencyHz = 261.6256f) =>
RebuildEnabledPadsAsAquariumScript(File.ReadAllBytes(path), tableRootFrequencyHzByKitItem, playbackFrequencyHz); RebuildEnabledPadsAsAquaSynthScript(File.ReadAllBytes(path), tableRootFrequencyHzByKitItem, playbackFrequencyHz);
public static ZynPadRebuild RebuildFirstPadAsAquariumScript( public static ZynPadRebuild RebuildFirstPadAsAquaSynthScript(
ReadOnlySpan<byte> bytes, ReadOnlySpan<byte> bytes,
float tableRootFrequencyHz, float tableRootFrequencyHz,
float playbackFrequencyHz = 261.6256f) float playbackFrequencyHz = 261.6256f)
@ -150,19 +150,19 @@ public static class ZynInstrumentReader
.Descendants("INSTRUMENT_KIT_ITEM") .Descendants("INSTRUMENT_KIT_ITEM")
.FirstOrDefault(IsEnabledPadItem) .FirstOrDefault(IsEnabledPadItem)
?? throw new ArgumentException("Zyn instrument XML has no enabled PAD kit item"); ?? throw new ArgumentException("Zyn instrument XML has no enabled PAD kit item");
return RebuildPadItemsAsAquariumScript( return RebuildPadItemsAsAquaSynthScript(
document, document,
[item], [item],
new Dictionary<int, ZynPadTableRoot> { [IntAttribute(item, "id")] = new(tableRootFrequencyHz) }, new Dictionary<int, ZynPadTableRoot> { [IntAttribute(item, "id")] = new(tableRootFrequencyHz) },
playbackFrequencyHz); playbackFrequencyHz);
} }
public static ZynPadRebuild RebuildEnabledPadsAsAquariumScript( public static ZynPadRebuild RebuildEnabledPadsAsAquaSynthScript(
ReadOnlySpan<byte> bytes, ReadOnlySpan<byte> bytes,
IReadOnlyDictionary<int, float> tableRootFrequencyHzByKitItem, IReadOnlyDictionary<int, float> tableRootFrequencyHzByKitItem,
float playbackFrequencyHz = 261.6256f) float playbackFrequencyHz = 261.6256f)
{ {
return RebuildEnabledPadsAsAquariumScript( return RebuildEnabledPadsAsAquaSynthScript(
bytes, bytes,
tableRootFrequencyHzByKitItem.ToDictionary( tableRootFrequencyHzByKitItem.ToDictionary(
pair => pair.Key, pair => pair.Key,
@ -170,7 +170,7 @@ public static class ZynInstrumentReader
playbackFrequencyHz); playbackFrequencyHz);
} }
public static ZynPadRebuild RebuildEnabledPadsAsAquariumScript( public static ZynPadRebuild RebuildEnabledPadsAsAquaSynthScript(
ReadOnlySpan<byte> bytes, ReadOnlySpan<byte> bytes,
IReadOnlyDictionary<int, ZynPadTableRoot> tableRootFrequencyHzByKitItem, IReadOnlyDictionary<int, ZynPadTableRoot> tableRootFrequencyHzByKitItem,
float playbackFrequencyHz = 261.6256f) float playbackFrequencyHz = 261.6256f)
@ -190,10 +190,10 @@ public static class ZynInstrumentReader
throw new ArgumentException("Zyn instrument XML has no enabled PAD kit item"); throw new ArgumentException("Zyn instrument XML has no enabled PAD kit item");
} }
return RebuildPadItemsAsAquariumScript(document, items, tableRootFrequencyHzByKitItem, playbackFrequencyHz); return RebuildPadItemsAsAquaSynthScript(document, items, tableRootFrequencyHzByKitItem, playbackFrequencyHz);
} }
private static ZynPadRebuild RebuildPadItemsAsAquariumScript( private static ZynPadRebuild RebuildPadItemsAsAquaSynthScript(
XDocument document, XDocument document,
IReadOnlyList<XElement> items, IReadOnlyList<XElement> items,
IReadOnlyDictionary<int, ZynPadTableRoot> tableRootFrequencyHzByKitItem, IReadOnlyDictionary<int, ZynPadTableRoot> tableRootFrequencyHzByKitItem,
@ -234,7 +234,7 @@ public static class ZynInstrumentReader
throw new ArgumentException($"Missing PAD table root for kit item {kitId}.", nameof(tableRootFrequencyHzByKitItem)); throw new ArgumentException($"Missing PAD table root for kit item {kitId}.", nameof(tableRootFrequencyHzByKitItem));
} }
var tableRootFrequencyHz = ZynPadRootForAquarium(item, tableRoot); var tableRootFrequencyHz = ZynPadRootForAquaSynth(item, tableRoot);
if (tableRootFrequencyHz <= 0) if (tableRootFrequencyHz <= 0)
{ {
throw new ArgumentException($"Invalid PAD table root for kit item {kitId}.", nameof(tableRootFrequencyHzByKitItem)); throw new ArgumentException($"Invalid PAD table root for kit item {kitId}.", nameof(tableRootFrequencyHzByKitItem));
@ -255,7 +255,7 @@ public static class ZynInstrumentReader
return new ZynPadRebuild(name, items.Count == 1 ? IntAttribute(items[0], "id") : -1, script.ToString(), matched, missing); return new ZynPadRebuild(name, items.Count == 1 ? IntAttribute(items[0], "id") : -1, script.ToString(), matched, missing);
} }
private static float ZynPadRootForAquarium(XElement item, ZynPadTableRoot tableRoot) private static float ZynPadRootForAquaSynth(XElement item, ZynPadTableRoot tableRoot)
{ {
var oscillator = item var oscillator = item
.Element("PAD_SYNTH_PARAMETERS") .Element("PAD_SYNTH_PARAMETERS")
@ -319,7 +319,7 @@ public static class ZynInstrumentReader
var partialText = string.Join(",", harmonics.Select(partial => $"{F(partial.Ratio)}:{F(partial.Gain)}")); var partialText = string.Join(",", harmonics.Select(partial => $"{F(partial.Ratio)}:{F(partial.Gain)}"));
var featurePrefix = includeKitSuffix ? $"pad_kit_{kitId}_" : "pad_"; var featurePrefix = includeKitSuffix ? $"pad_kit_{kitId}_" : "pad_";
matched.Add(new($"{featurePrefix}oscillator_harmonics", harmonics.Count.ToString(CultureInfo.InvariantCulture), "Mapped OSCIL/HARMONICS magnitudes into Aquarium spectrum partials.")); matched.Add(new($"{featurePrefix}oscillator_harmonics", harmonics.Count.ToString(CultureInfo.InvariantCulture), "Mapped OSCIL/HARMONICS magnitudes into AquaSynth spectrum partials."));
matched.Add(new($"{featurePrefix}oscillator_base_function", IntParam(oscillator, "base_function", 0).ToString(CultureInfo.InvariantCulture), "Expanded Zyn OscilGen base function into harmonic partials before PAD synthesis.")); matched.Add(new($"{featurePrefix}oscillator_base_function", IntParam(oscillator, "base_function", 0).ToString(CultureInfo.InvariantCulture), "Expanded Zyn OscilGen base function into harmonic partials before PAD synthesis."));
if (IntParam(oscillator, "base_function_modulation", 0) != 0) if (IntParam(oscillator, "base_function_modulation", 0) != 0)
{ {
@ -331,30 +331,30 @@ public static class ZynInstrumentReader
} }
matched.Add(new($"{featurePrefix}oscillator_spectrum_adjust", $"{IntParam(oscillator, "spectrum_adjust_type", 0)}:{IntParam(oscillator, "spectrum_adjust_par", 64)}", "Applied Zyn OscilGen spectrum adjustment to generated harmonic partials.")); matched.Add(new($"{featurePrefix}oscillator_spectrum_adjust", $"{IntParam(oscillator, "spectrum_adjust_type", 0)}:{IntParam(oscillator, "spectrum_adjust_par", 64)}", "Applied Zyn OscilGen spectrum adjustment to generated harmonic partials."));
matched.Add(new($"{featurePrefix}table_root", F(tableRootFrequencyHz), "Root frequency comes from the Zyn oracle generated sample basefreq.")); matched.Add(new($"{featurePrefix}table_root", F(tableRootFrequencyHz), "Root frequency comes from the Zyn oracle generated sample basefreq."));
matched.Add(new($"{featurePrefix}note_frequency", F(noteFrequencyHz), "Mapped Zyn PAD coarse/fine detune into Aquarium table playback frequency.")); matched.Add(new($"{featurePrefix}note_frequency", F(noteFrequencyHz), "Mapped Zyn PAD coarse/fine detune into AquaSynth table playback frequency."));
matched.Add(new($"{featurePrefix}volume", volume.ToString(CultureInfo.InvariantCulture), "Mapped PAD amplitude volume into Aquarium layer gain.")); matched.Add(new($"{featurePrefix}volume", volume.ToString(CultureInfo.InvariantCulture), "Mapped PAD amplitude volume into AquaSynth layer gain."));
matched.Add(new($"{featurePrefix}bandwidth", bandwidth.ToString(CultureInfo.InvariantCulture), "Mapped Zyn PAD bandwidth into Aquarium spectral table generation.")); matched.Add(new($"{featurePrefix}bandwidth", bandwidth.ToString(CultureInfo.InvariantCulture), "Mapped Zyn PAD bandwidth into AquaSynth spectral table generation."));
matched.Add(new($"{featurePrefix}bandwidth_scale", bandwidthScale.ToString(CultureInfo.InvariantCulture), "Mapped Zyn PAD bandwidth scaling exponent selection.")); matched.Add(new($"{featurePrefix}bandwidth_scale", bandwidthScale.ToString(CultureInfo.InvariantCulture), "Mapped Zyn PAD bandwidth scaling exponent selection."));
matched.Add(new($"{featurePrefix}harmonic_profile", profile, "Mapped Zyn PAD harmonic profile shape into Aquarium spectral table generation.")); matched.Add(new($"{featurePrefix}harmonic_profile", profile, "Mapped Zyn PAD harmonic profile shape into AquaSynth spectral table generation."));
matched.Add(new($"{featurePrefix}harmonic_position", position, "Mapped Zyn PAD harmonic position warp into Aquarium spectral table generation.")); matched.Add(new($"{featurePrefix}harmonic_position", position, "Mapped Zyn PAD harmonic position warp into AquaSynth spectral table generation."));
if (lowPass < 1) if (lowPass < 1)
{ {
matched.Add(new($"{featurePrefix}filter_lpf", F(lowPass), "Mapped static Zyn PAD global low-pass frequency into Aquarium layer filtering.")); matched.Add(new($"{featurePrefix}filter_lpf", F(lowPass), "Mapped static Zyn PAD global low-pass frequency into AquaSynth layer filtering."));
matched.Add(new($"{featurePrefix}filter_lpf_q", F(lowPassQ), "Mapped Zyn analog low-pass Q into Aquarium filter damping.")); matched.Add(new($"{featurePrefix}filter_lpf_q", F(lowPassQ), "Mapped Zyn analog low-pass Q into AquaSynth filter damping."));
matched.Add(new($"{featurePrefix}filter_lpf_order", lowPassOrder.ToString(CultureInfo.InvariantCulture), "Mapped Zyn analog LP1/LP2 and stage count into Aquarium low-pass order.")); matched.Add(new($"{featurePrefix}filter_lpf_order", lowPassOrder.ToString(CultureInfo.InvariantCulture), "Mapped Zyn analog LP1/LP2 and stage count into AquaSynth low-pass order."));
} }
if (highPass > 0) if (highPass > 0)
{ {
matched.Add(new($"{featurePrefix}filter_hpf", F(highPass), "Mapped static Zyn PAD global high-pass frequency into Aquarium layer filtering.")); matched.Add(new($"{featurePrefix}filter_hpf", F(highPass), "Mapped static Zyn PAD global high-pass frequency into AquaSynth layer filtering."));
matched.Add(new($"{featurePrefix}filter_hpf_order", highPassOrder.ToString(CultureInfo.InvariantCulture), "Mapped Zyn analog HP1/HP2 and stage count into Aquarium high-pass order.")); matched.Add(new($"{featurePrefix}filter_hpf_order", highPassOrder.ToString(CultureInfo.InvariantCulture), "Mapped Zyn analog HP1/HP2 and stage count into AquaSynth high-pass order."));
} }
if (lowPassEnvelope is not null) if (lowPassEnvelope is not null)
{ {
matched.Add(new($"{featurePrefix}filter_lpf_envelope", "ratelevel", "Mapped Zyn PAD filter envelope octave offsets into Aquarium low-pass cutoff motion.")); matched.Add(new($"{featurePrefix}filter_lpf_envelope", "ratelevel", "Mapped Zyn PAD filter envelope octave offsets into AquaSynth low-pass cutoff motion."));
} }
if (highPassEnvelope is not null) if (highPassEnvelope is not null)
{ {
matched.Add(new($"{featurePrefix}filter_hpf_envelope", "ratelevel", "Mapped Zyn PAD filter envelope octave offsets into Aquarium high-pass cutoff motion.")); matched.Add(new($"{featurePrefix}filter_hpf_envelope", "ratelevel", "Mapped Zyn PAD filter envelope octave offsets into AquaSynth high-pass cutoff motion."));
} }
if (filter?.Element("FILTER_LFO") is { } lfo && IntParam(lfo, "intensity", 0) != 0) if (filter?.Element("FILTER_LFO") is { } lfo && IntParam(lfo, "intensity", 0) != 0)
{ {

View file

@ -1,92 +1,93 @@
{"kind":"migration","lesson":"The C# repo must own reusable authoring, analysis, preset, scoring, and Faust-generation behavior; only the Rust renderer/runtime and experimental compiler branch are intentionally left behind.","source":"user direction and migration pass through commit 3246672"} {"kind":"migration","lesson":"The C# repo must own reusable authoring, analysis, preset, scoring, and Faust-generation behavior; only the Rust renderer/runtime and experimental compiler branch are intentionally left behind.","source":"user direction and migration pass through commit 3246672"}
{"kind":"realtime","lesson":"Runtime-facing audio code must avoid unbounded operations: allocation, locks, I/O, console output, OS calls, and context switches do not belong in audio callbacks.","source":"PortAudio callback documentation"} {"kind":"realtime","lesson":"Runtime-facing audio code must avoid unbounded operations: allocation, locks, I/O, console output, OS calls, and context switches do not belong in audio callbacks.","source":"PortAudio callback documentation"}
{"kind":"dsp_graph","lesson":"Treat patch scripts as deterministic dataflow graphs with explicit rate and block semantics; terse syntax is acceptable only when lowering remains inspectable.","source":"Pure Data block/subpatch model and Faust block-diagram model"} {"kind":"dsp_graph","lesson":"Treat patch scripts as deterministic dataflow graphs with explicit rate and block semantics; terse syntax is acceptable only when lowering remains inspectable.","source":"Pure Data block/subpatch model and Faust block-diagram model"}
{"kind":"sfxr_parity","lesson":"SFXR normalized filter values are not linear cutoff frequencies. In Faust export, small SFXR high-pass values must remain very low; mapping hpf directly to thousands of Hz removes the body of pickup, jump, powerup, and blip presets while still leaving them audible enough for weak tests to pass.","source":"Aquarium engine native Faust parity test against C# SFXR reference renderer"} {"kind":"sfxr_parity","lesson":"SFXR normalized filter values are not linear cutoff frequencies. In Faust export, small SFXR high-pass values must remain very low; mapping hpf directly to thousands of Hz removes the body of pickup, jump, powerup, and blip presets while still leaving them audible enough for weak tests to pass.","source":"AquaSynth engine native Faust parity test against C# SFXR reference renderer"}
{"kind":"dsl_reference_corpus","lesson":"BFXR, SFXR, 808, FM, and wobble presets are reference patches for testing and DSL design, not terseness trophies. Built-ins should be readable enough to teach the patch language, and the parser should support multi-line commands instead of forcing all expressive patches into one-line golf.","source":"user clarified preset purpose for patch DSL iteration"} {"kind":"dsl_reference_corpus","lesson":"BFXR, SFXR, 808, FM, and wobble presets are reference patches for testing and DSL design, not terseness trophies. Built-ins should be readable enough to teach the patch language, and the parser should support multi-line commands instead of forcing all expressive patches into one-line golf.","source":"user clarified preset purpose for patch DSL iteration"}
{"kind":"advanced_reference_patches","lesson":"The synth reference catalog needs complex examples that combine defaults, templates, multiple voices, modulation buses, FM, formants, noise layers, repeat, and soft clipping. These patches expose DSL readability and composition pressure better than short one-shot SFX presets.","source":"user asked for sophisticated patch references"} {"kind":"advanced_reference_patches","lesson":"The synth reference catalog needs complex examples that combine defaults, templates, multiple voices, modulation buses, FM, formants, noise layers, repeat, and soft clipping. These patches expose DSL readability and composition pressure better than short one-shot SFX presets.","source":"user asked for sophisticated patch references"}
{"kind":"reference_synth_roadmap","lesson":"AquariumSynthCSharp should use external synth and DSP corpora as reference targets, not just internal demo patches. Start with DX7/Dexed SysEx for structured FM, then ZynAddSubFX, Faust examples/libraries, Surge XT, and Cardinal as progressively broader pressure tests. Keep provenance and license state explicit before vendoring fixtures.","source":"user clarified any synth can be broken down and rebuilt via Aquarium DSL"} {"kind":"reference_synth_roadmap","lesson":"AquaSynth should use external synth and DSP corpora as reference targets, not just internal demo patches. Start with DX7/Dexed SysEx for structured FM, then ZynAddSubFX, Faust examples/libraries, Surge XT, and Cardinal as progressively broader pressure tests. Keep provenance and license state explicit before vendoring fixtures.","source":"user clarified any synth can be broken down and rebuilt via AquaSynth DSL"}
{"kind":"runtime_patch_parameters","lesson":"Synth patches must expose expressive runtime parameters. Faust supports this through UI controls and hosted parameter APIs, so Aquarium should compile a graph once and vary declared parameter values without recompilation. Recompile only for structural graph changes.","source":"user asked whether Faust can vary one number without recompiling"} {"kind":"runtime_patch_parameters","lesson":"Synth patches must expose expressive runtime parameters. Faust supports this through UI controls and hosted parameter APIs, so AquaSynth should compile a graph once and vary declared parameter values without recompilation. Recompile only for structural graph changes.","source":"user asked whether Faust can vary one number without recompiling"}
{"kind":"synth_test_ownership","lesson":"AquariumSynthCSharp owns reference-patch correctness. Aquarium should only smoke-test the engine synth host and bundled Faust boundary; SFXR/BFXR/reference parity belongs here so the engine can keep moving while the synth library is under active surgery.","source":"user questioned synth tests living in Aquarium"} {"kind":"synth_test_ownership","lesson":"AquaSynth owns reference-patch correctness. AquaSynth should only smoke-test the engine synth host and bundled Faust boundary; SFXR/BFXR/reference parity belongs here so the engine can keep moving while the synth library is under active surgery.","source":"user questioned synth tests living in AquaSynth"}
{"kind":"package_boundary","lesson":"Aquarium consumes AquariumSynth.Dsl through a pinned package, not a live project reference. Breaking changes in AquariumSynthCSharp are allowed during synth construction as long as a new package version is packed and Aquarium is updated deliberately when the boundary should move.","source":"user wanted Aquarium builds insulated from synth-agent work"} {"kind":"package_boundary","lesson":"AquaSynth consumes AquaSynth.Dsl through a pinned package, not a live project reference. Breaking changes in AquaSynth are allowed during synth construction as long as a new package version is packed and AquaSynth is updated deliberately when the boundary should move.","source":"user wanted AquaSynth builds insulated from synth-agent work"}
{"kind":"runtime_patch_parameters","lesson":"Aquarium patch scripts now have first-class `param` declarations and patch/reference model contracts. Faust emission exposes declared parameters as smoothed hslider controls, but target binding is intentionally still absent and warned about; do not claim parameters shape sound until binding is designed.","source":"parameter contract implementation on 2026-05-13"} {"kind":"runtime_patch_parameters","lesson":"AquaSynth patch scripts now have first-class `param` declarations and patch/reference model contracts. Faust emission exposes declared parameters as smoothed hslider controls, but target binding is intentionally still absent and warned about; do not claim parameters shape sound until binding is designed.","source":"parameter contract implementation on 2026-05-13"}
{"kind":"runtime_patch_parameters","lesson":"Aquarium parameter binding uses Option B: numeric field values may reference declared parameters with `@/path`, producing exact field bindings such as `/voices/0/filter/lpf`. The field owns the meaning; the parameter owns the runtime value. Avoid target-routing parameter semantics unless a new authority is deliberately mapped.","source":"user rejected target binding as a landmine; field-site binding implemented on 2026-05-13"} {"kind":"runtime_patch_parameters","lesson":"AquaSynth parameter binding uses Option B: numeric field values may reference declared parameters with `@/path`, producing exact field bindings such as `/voices/0/filter/lpf`. The field owns the meaning; the parameter owns the runtime value. Avoid target-routing parameter semantics unless a new authority is deliberately mapped.","source":"user rejected target binding as a landmine; field-site binding implemented on 2026-05-13"}
{"kind":"dx7_reference_path","lesson":"DX7 reference elaboration starts with structural extraction, not translation. The repo now parses 155-byte voice edit buffers, 128-byte packed voices, and 4104-byte packed 32-voice SysEx banks into neutral DX7 records with checksum validation and ReferencePatch features. Next pressure point is explicit algorithm topology metadata for carriers, modulation edges, and feedback edge location.","source":"DX7 SysEx parser slice on 2026-05-13"} {"kind":"dx7_reference_path","lesson":"DX7 reference elaboration starts with structural extraction, not translation. The repo now parses 155-byte voice edit buffers, 128-byte packed voices, and 4104-byte packed 32-voice SysEx banks into neutral DX7 records with checksum validation and ReferencePatch features. Next pressure point is explicit algorithm topology metadata for carriers, modulation edges, and feedback edge location.","source":"DX7 SysEx parser slice on 2026-05-13"}
{"kind":"dx7_reference_path","lesson":"DX7 algorithm topology is now derived from the reverse-engineered OPS algorithm ROM table, not hand-drawn diagrams. Topology exposes carrier operators, modulation edges, feedback-register writers, self-feedback operators, delayed-feedback targets, and raw ROM steps. Use this as pressure evidence before adding an Aquarium operator graph layer.","source":"DX7 algorithm topology slice on 2026-05-13; Ken Shirriff OPS ROM table"} {"kind":"dx7_reference_path","lesson":"DX7 algorithm topology is now derived from the reverse-engineered OPS algorithm ROM table, not hand-drawn diagrams. Topology exposes carrier operators, modulation edges, feedback-register writers, self-feedback operators, delayed-feedback targets, and raw ROM steps. Use this as pressure evidence before adding an AquaSynth operator graph layer.","source":"DX7 algorithm topology slice on 2026-05-13; Ken Shirriff OPS ROM table"}
{"kind":"dx7_reference_rebuilds","lesson":"Two DX7-style Aquarium rebuilds now make the pressure explicit: algorithm 32 maps cleanly to layered additive voices, but algorithm 8 exposes missing graph structure (operator modulation edges, summed modulators, and operator-local self-feedback). Do not patch this with more Voice fields; decide whether an operator graph model owns this invariant.","source":"DX7 rebuild catalog slice on 2026-05-13"} {"kind":"dx7_reference_rebuilds","lesson":"Two DX7-style AquaSynth rebuilds now make the pressure explicit: algorithm 32 maps cleanly to layered additive voices, but algorithm 8 exposes missing graph structure (operator modulation edges, summed modulators, and operator-local self-feedback). Do not patch this with more Voice fields; decide whether an operator graph model owns this invariant.","source":"DX7 rebuild catalog slice on 2026-05-13"}
{"kind":"operator_graph_model","lesson":"DX7 algorithm-8 pressure justified a first-class OperatorGraph abstraction. OperatorGraph owns FM topology: operators, modulation edges, carriers, and feedback. Do not hide this authority inside Voice fields. Remaining DX7 pressure is exact feedback-register timing and rate/level envelopes.","source":"operator graph implementation slice on 2026-05-13"} {"kind":"operator_graph_model","lesson":"DX7 algorithm-8 pressure justified a first-class OperatorGraph abstraction. OperatorGraph owns FM topology: operators, modulation edges, carriers, and feedback. Do not hide this authority inside Voice fields. Remaining DX7 pressure is exact feedback-register timing and rate/level envelopes.","source":"operator graph implementation slice on 2026-05-13"}
{"kind":"operator_graph_authoring","lesson":"Operator graph authoring should use readable `operator`, `route`, and `carrier` declarations with `env=ad:...` or `env=adsr:...`. Compact `ops=`/`edges=` tuples are acceptable as importer/interchange scaffolding only, not the face of the DSL.","source":"user rejected number-soup operator syntax; readable operator graph syntax slice on 2026-05-13"} {"kind":"operator_graph_authoring","lesson":"Operator graph authoring should use readable `operator`, `route`, and `carrier` declarations with `env=ad:...` or `env=adsr:...`. Compact `ops=`/`edges=` tuples are acceptable as importer/interchange scaffolding only, not the face of the DSL.","source":"user rejected number-soup operator syntax; readable operator graph syntax slice on 2026-05-13"}
{"kind":"note_envelope_model","lesson":"Aquarium's general envelope model is ADSR: attack, decay, sustain level, release. Note timing belongs to `Note` records: frequency, gate duration, and host/MIDI source. SFXR sustain maps to note gate duration; SFXR punch maps to lower sustain level plus voice gain compensation so ADSR peak remains implicit 1 and the held level sits below the transient.","source":"user identified SFXR sustain as note duration and Punch as transient above sustain; punch mapping correction on 2026-05-13"} {"kind":"note_envelope_model","lesson":"AquaSynth's general envelope model is ADSR: attack, decay, sustain level, release. Note timing belongs to `Note` records: frequency, gate duration, and host/MIDI source. SFXR sustain maps to note gate duration; SFXR punch maps to lower sustain level plus voice gain compensation so ADSR peak remains implicit 1 and the held level sits below the transient.","source":"user identified SFXR sustain as note duration and Punch as transient above sustain; punch mapping correction on 2026-05-13"}
{"kind":"faust_polyphony","lesson":"Prefer Faust-managed MIDI polyphony over Aquarium graph inflation. Aquarium should emit a single voice graph with standard `freq`, `gain`, and `gate` controls plus `[midi:on][nvoices:n]`; Faust architectures own MIDI decoding and voice allocation unless a concrete target proves the boundary insufficient.","source":"Faust official MIDI/polyphony docs and user preference for Faust support on 2026-05-13"} {"kind":"faust_polyphony","lesson":"Prefer Faust-managed MIDI polyphony over AquaSynth graph inflation. AquaSynth should emit a single voice graph with standard `freq`, `gain`, and `gate` controls plus `[midi:on][nvoices:n]`; Faust architectures own MIDI decoding and voice allocation unless a concrete target proves the boundary insufficient.","source":"Faust official MIDI/polyphony docs and user preference for Faust support on 2026-05-13"}
{"kind":"operator_graph_parameters","lesson":"Readable operator graph fields bind runtime parameters at exact `/opgraphs/...` field paths: graph gain/note fields, operator ratio/level/feedback/envelope fields, and route index fields. Keep compact `ops=`/`edges=` as importer scaffolding and avoid graph-wide macro routing.","source":"operator graph field-site binding slice on 2026-05-13"} {"kind":"operator_graph_parameters","lesson":"Readable operator graph fields bind runtime parameters at exact `/opgraphs/...` field paths: graph gain/note fields, operator ratio/level/feedback/envelope fields, and route index fields. Keep compact `ops=`/`edges=` as importer scaffolding and avoid graph-wide macro routing.","source":"operator graph field-site binding slice on 2026-05-13"}
{"kind":"dx7_envelopes","lesson":"DX7 four-rate/four-level operator envelopes can be lowered to labeled Aquarium ADSR approximations plus gate duration, but this is not exact DX7 EG execution. Reference rebuilds should record approximation as matched pressure and exactness as still missing.","source":"DX7 envelope approximation slice on 2026-05-13; Yamaha DX7 manual describes EGs as four rates and four levels"} {"kind":"dx7_envelopes","lesson":"DX7 four-rate/four-level operator envelopes can be lowered to labeled AquaSynth ADSR approximations plus gate duration, but this is not exact DX7 EG execution. Reference rebuilds should record approximation as matched pressure and exactness as still missing.","source":"DX7 envelope approximation slice on 2026-05-13; Yamaha DX7 manual describes EGs as four rates and four levels"}
{"kind":"render_parity_harness","lesson":"Aquarium Faust output can now be compiled to Faust-generated C# and rendered into mono float samples for AudioAnalyzer comparisons. This proves the candidate side of parity; DX7 parity still requires a real Dexed/reference render or captured licensed fixture, not self-comparison.","source":"Faust render parity harness slice on 2026-05-13"} {"kind":"render_parity_harness","lesson":"AquaSynth Faust output can now be compiled to Faust-generated C# and rendered into mono float samples for AudioAnalyzer comparisons. This proves the candidate side of parity; DX7 parity still requires a real Dexed/reference render or captured licensed fixture, not self-comparison.","source":"Faust render parity harness slice on 2026-05-13"}
{"kind":"reference_parity_doctrine","lesson":"External synth references exist to exercise Aquarium DSL expressiveness, not to clone foreign internals. Consume only references that can be backed by parity tests proving Aquarium can reproduce the behavior in terse, readable syntax; parsing a format is inventory, not evidence.","source":"user clarified reference-synth purpose and parity-test requirement on 2026-05-13"} {"kind":"reference_parity_doctrine","lesson":"External synth references exist to exercise AquaSynth DSL expressiveness, not to clone foreign internals. Consume only references that can be backed by parity tests proving AquaSynth can reproduce the behavior in terse, readable syntax; parsing a format is inventory, not evidence.","source":"user clarified reference-synth purpose and parity-test requirement on 2026-05-13"}
{"kind":"dx7_reference_rendering","lesson":"DX7 reference rendering now uses dexed-py as optional test tooling against a public-domain SysEx fixture. Keep fixtures and renderer helpers under the test project, preserve provenance and SHA-256 beside vendored banks, and keep AquariumSynth.Dsl.nupkg free of SysEx/Python/test assets.","source":"dexed-py public-domain fixture harness slice on 2026-05-13"} {"kind":"dx7_reference_rendering","lesson":"DX7 reference rendering now uses dexed-py as optional test tooling against a public-domain SysEx fixture. Keep fixtures and renderer helpers under the test project, preserve provenance and SHA-256 beside vendored banks, and keep AquaSynth.Dsl.nupkg free of SysEx/Python/test assets.","source":"dexed-py public-domain fixture harness slice on 2026-05-13"}
{"kind":"dx7_rendered_parity","lesson":"The first thresholded rendered DX7 parity target is analog1.syx voice 13, MC-MM 5-3. Aquarium matches the rendered behavior with a short sine-like patch and explicit score/log-mel/envelope/duration/RMS/zero-crossing/centroid thresholds. Treat this as proof of the parity loop, not proof of exact DX7 operator execution; next target should audibly require operator routing.","source":"first thresholded DX7 rendered parity slice on 2026-05-13"} {"kind":"dx7_rendered_parity","lesson":"The first thresholded rendered DX7 parity target is analog1.syx voice 13, MC-MM 5-3. AquaSynth matches the rendered behavior with a short sine-like patch and explicit score/log-mel/envelope/duration/RMS/zero-crossing/centroid thresholds. Treat this as proof of the parity loop, not proof of exact DX7 operator execution; next target should audibly require operator routing.","source":"first thresholded DX7 rendered parity slice on 2026-05-13"}
{"kind":"operator_feedback","lesson":"A harder DX7 algorithm-8 target exposed direct operator self-reference as non-renderable Faust. Operator feedback now emits as delayed Faust recursion, which keeps the render path alive and is closer to DX7 feedback pressure; exact DX7 feedback scaling remains unsolved.","source":"PRC SYNTH1 parity probe and delayed feedback render fix on 2026-05-13"} {"kind":"operator_feedback","lesson":"A harder DX7 algorithm-8 target exposed direct operator self-reference as non-renderable Faust. Operator feedback now emits as delayed Faust recursion, which keeps the render path alive and is closer to DX7 feedback pressure; exact DX7 feedback scaling remains unsolved.","source":"PRC SYNTH1 parity probe and delayed feedback render fix on 2026-05-13"}
{"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":"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 AquaSynth 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":"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_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"} {"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"}
{"kind":"dx7_prc_logmel","lesson":"Listening and log-mel agreed that the aggregate PRC SYNTH1 score was misleading. The Dexed reference has strong low/body partials around 131 Hz and 393 Hz; Aquarium's earlier candidate lacked the low body despite a better aggregate score. The probe now gates on log-mel <=0.255 with a loose aggregate floor, and remaining mismatch points at DX7 output/index scaling and algorithm compensation.","source":"PRC SYNTH1 perceptual/log-mel calibration on 2026-05-14"} {"kind":"dx7_prc_logmel","lesson":"Listening and log-mel agreed that the aggregate PRC SYNTH1 score was misleading. The Dexed reference has strong low/body partials around 131 Hz and 393 Hz; AquaSynth's earlier candidate lacked the low body despite a better aggregate score. The probe now gates on log-mel <=0.255 with a loose aggregate floor, and remaining mismatch points at DX7 output/index scaling and algorithm compensation.","source":"PRC SYNTH1 perceptual/log-mel calibration on 2026-05-14"}
{"kind":"dx7_output_level_curve","lesson":"A project-authored Dexed single-carrier sweep shows DX7 carrier amplitude follows 2^((outputLevel - 99) / 8). The previous internal-level proxy made mid-level operators far too loud. `Dx7SysEx.OperatorOutputAmplitude` now owns the measured curve; next calibration rung is two-operator modulation index.","source":"DX7 single-carrier calibration rung on 2026-05-14"} {"kind":"dx7_output_level_curve","lesson":"A project-authored Dexed single-carrier sweep shows DX7 carrier amplitude follows 2^((outputLevel - 99) / 8). The previous internal-level proxy made mid-level operators far too loud. `Dx7SysEx.OperatorOutputAmplitude` now owns the measured curve; next calibration rung is two-operator modulation index.","source":"DX7 single-carrier calibration rung on 2026-05-14"}
{"kind":"dx7_modulation_index","lesson":"Isolated two-operator Dexed FM fits full-level modulation at about 12.55 radians. Under Aquarium's current Faust phase formula this maps to route index 6.275 for a full-level modulator. Apply this to isolated two-op branches, but do not blindly apply it to cascaded algorithm stacks before algorithm output compensation is modeled.","source":"DX7 two-operator modulation calibration rung on 2026-05-14"} {"kind":"dx7_modulation_index","lesson":"Isolated two-operator Dexed FM fits full-level modulation at about 12.55 radians. Under AquaSynth's current Faust phase formula this maps to route index 6.275 for a full-level modulator. Apply this to isolated two-op branches, but do not blindly apply it to cascaded algorithm stacks before algorithm output compensation is modeled.","source":"DX7 two-operator modulation calibration rung on 2026-05-14"}
{"kind":"dx7_feedback_scale","lesson":"Isolated Dexed feedback sweeps fit a nonlinear Aquarium feedback table: 0, .01, .02, .05, .10, .19, .38, .66 for DX7 feedback values 0-7. PRC feedback value 5 was already accidentally close via feedback*0.04; the table moves that knowledge into Dx7SysEx.OperatorFeedbackAmount.","source":"DX7 feedback calibration rung on 2026-05-14"} {"kind":"dx7_feedback_scale","lesson":"Isolated Dexed feedback sweeps fit a nonlinear AquaSynth feedback table: 0, .01, .02, .05, .10, .19, .38, .66 for DX7 feedback values 0-7. PRC feedback value 5 was already accidentally close via feedback*0.04; the table moves that knowledge into Dx7SysEx.OperatorFeedbackAmount.","source":"DX7 feedback calibration rung on 2026-05-14"}
{"kind":"dx7_detune_and_com","lesson":"PRC exposed two separate DX7 facts: ratio-mode detune must lower into operator frequency ratios, and ROM COM output compensation must be applied relative to the algorithm-32 six-output baseline used by the carrier-amplitude calibration. This cut the PRC carrier scale table; the remaining cascaded-route constant is still a fenced hard-target scar needing summed-modulator calibration.","source":"DX7 PRC parity rung on 2026-05-14"} {"kind":"dx7_detune_and_com","lesson":"PRC exposed two separate DX7 facts: ratio-mode detune must lower into operator frequency ratios, and ROM COM output compensation must be applied relative to the algorithm-32 six-output baseline used by the carrier-amplitude calibration. This cut the PRC carrier scale table; the remaining cascaded-route constant is still a fenced hard-target scar needing summed-modulator calibration.","source":"DX7 PRC parity rung on 2026-05-14"}
{"kind":"dx7_topology_aware_route_index","lesson":"The PRC hard target proved DX7 route index cannot key only on ROM edge kind: a direct edge feeding a later summed branch behaves like part of the cascaded/summed stack, not like the isolated two-op branch. `Dx7SysEx.OperatorRouteIndex(topology, edge)` now owns this distinction; PRC no longer has a private route function.","source":"DX7 summed/cascaded route lowering on 2026-05-14"} {"kind":"dx7_topology_aware_route_index","lesson":"The PRC hard target proved DX7 route index cannot key only on ROM edge kind: a direct edge feeding a later summed branch behaves like part of the cascaded/summed stack, not like the isolated two-op branch. `Dx7SysEx.OperatorRouteIndex(topology, edge)` now owns this distinction; PRC no longer has a private route function.","source":"DX7 summed/cascaded route lowering on 2026-05-14"}
{"kind":"dx7_project_authored_patch_rendering","lesson":"Project-authored Dexed patches can be generated directly inside tests from explicit operator specs. Use this for lawful calibration rungs instead of squeezing every DX7 fact out of public-domain hard patches.","source":"DX7 project-authored summed-stack parity on 2026-05-14"} {"kind":"dx7_project_authored_patch_rendering","lesson":"Project-authored Dexed patches can be generated directly inside tests from explicit operator specs. Use this for lawful calibration rungs instead of squeezing every DX7 fact out of public-domain hard patches.","source":"DX7 project-authored summed-stack parity on 2026-05-14"}
{"kind":"dx7_envelope_curve_cut","lesson":"Reusing the operator output amplitude curve for DX7 EG levels is not ready: it can improve isolated envelope shape metrics with shorter timing, but it failed the hard PRC log-mel target. Keep EG levels linear until isolated envelope parity passes without regressing PRC.","source":"DX7 envelope level experiment on 2026-05-14"} {"kind":"dx7_envelope_curve_cut","lesson":"Reusing the operator output amplitude curve for DX7 EG levels is not ready: it can improve isolated envelope shape metrics with shorter timing, but it failed the hard PRC log-mel target. Keep EG levels linear until isolated envelope parity passes without regressing PRC.","source":"DX7 envelope level experiment on 2026-05-14"}
{"kind":"dx7_envelope_trace","lesson":"Direct DX7 EG tracing shows the current `env=rl` abstraction is the wrong shape for some DX7 envelopes: the traced EG can spike above unit gain and drop below .02 within about 9 ms while `env=rl` is still near 1. A runtime `env=dx7` surface was attempted and cut because matching the local trace did not yet match Dexed plugin audio.","source":"DX7 envelope trace artifact on 2026-05-14"} {"kind":"dx7_envelope_trace","lesson":"Direct DX7 EG tracing shows the current `env=rl` abstraction is the wrong shape for some DX7 envelopes: the traced EG can spike above unit gain and drop below .02 within about 9 ms while `env=rl` is still near 1. A runtime `env=dx7` surface was attempted and cut because matching the local trace did not yet match Dexed plugin audio.","source":"DX7 envelope trace artifact on 2026-05-14"}
{"kind":"dx7_envelope_applied_gain","lesson":"Dexed-style operator audio does not use the raw DX7 EG state directly. It samples the EG once per 64-sample block and linearly interpolates applied gain across the block. A DX7-capable Aquarium envelope must model that applied-gain timebase; matching raw EG trace alone can make a candidate far too quiet or otherwise wrong.","source":"DX7 interpolated envelope trace artifact on 2026-05-14"} {"kind":"dx7_envelope_applied_gain","lesson":"Dexed-style operator audio does not use the raw DX7 EG state directly. It samples the EG once per 64-sample block and linearly interpolates applied gain across the block. A DX7-capable AquaSynth envelope must model that applied-gain timebase; matching raw EG trace alone can make a candidate far too quiet or otherwise wrong.","source":"DX7 interpolated envelope trace artifact on 2026-05-14"}
{"kind":"curved_rate_level_envelope","lesson":"Aquarium staged operator envelopes now support per-segment curves and deliberate levels above unity. Keep this authority in `RateLevelEnvelope`: it protects the invariant that multi-target operator contours can model exponential synth envelopes without inventing one-off DX7 syntax.","source":"Curved rate/level envelope implementation on 2026-05-14"} {"kind":"curved_rate_level_envelope","lesson":"AquaSynth staged operator envelopes now support per-segment curves and deliberate levels above unity. Keep this authority in `RateLevelEnvelope`: it protects the invariant that multi-target operator contours can model exponential synth envelopes without inventing one-off DX7 syntax.","source":"Curved rate/level envelope implementation on 2026-05-14"}
{"kind":"dx7_applied_envelope_lowering","lesson":"`Dx7SysEx.ApproximateAppliedRateLevelEnvelope` improves the hard PRC target by tracing Dexed-style block-interpolated EG gain, normalizing it against Aquarium operator output level, and emitting curved `env=rl`. Latest PRC metrics: log-mel 0.21931955, envelope distance 0.10271241, RMS ratio 1.0003664, score 0.5026183. Zero-crossing worsened to 0.28642866, so contour parity exposed a remaining oscillator/phase mismatch rather than solving DX7 parity outright.","source":"DX7 applied-envelope PRC probe on 2026-05-14"} {"kind":"dx7_applied_envelope_lowering","lesson":"`Dx7SysEx.ApproximateAppliedRateLevelEnvelope` improves the hard PRC target by tracing Dexed-style block-interpolated EG gain, normalizing it against AquaSynth operator output level, and emitting curved `env=rl`. Latest PRC metrics: log-mel 0.21931955, envelope distance 0.10271241, RMS ratio 1.0003664, score 0.5026183. Zero-crossing worsened to 0.28642866, so contour parity exposed a remaining oscillator/phase mismatch rather than solving DX7 parity outright.","source":"DX7 applied-envelope PRC probe on 2026-05-14"}
{"kind":"dx7_applied_envelope_peak_picker","lesson":"Listening caught a real lowering bug: the first applied-envelope approximation treated the first 64-sample block as the attack peak, which made slower rising op2 in PRC swell late (`0.014 -> 0.613`) instead of hitting early and decaying. Picking the first near-peak before gate gives op2 `0.981 -> 0.843 -> 0.660` and improves PRC to log-mel 0.17186502, RMS 1.0000212, zero-crossing 0.7815517, score 0.6501268.","source":"PRC SYNTH1 listening-guided envelope fix on 2026-05-14"} {"kind":"dx7_applied_envelope_peak_picker","lesson":"Listening caught a real lowering bug: the first applied-envelope approximation treated the first 64-sample block as the attack peak, which made slower rising op2 in PRC swell late (`0.014 -> 0.613`) instead of hitting early and decaying. Picking the first near-peak before gate gives op2 `0.981 -> 0.843 -> 0.660` and improves PRC to log-mel 0.17186502, RMS 1.0000212, zero-crossing 0.7815517, score 0.6501268.","source":"PRC SYNTH1 listening-guided envelope fix on 2026-05-14"}
{"kind":"dx7_summed_route_scale","lesson":"Listening identified missing harsh stacked-modulation brightness. Raising `Dx7SysEx.SummedOperatorModulationRouteIndex` from 1.6 to 2.4 improves PRC to log-mel 0.16794802, envelope distance 0.09510617, centroid ratio 1.1254044, RMS ratio 1.00095, score 0.68491656, and also tightens the project-authored algorithm-8 summed-stack log-mel when graph gain is normalized. Keep isolated direct branches at 6.275; the stronger 2.4 scale belongs only to summed/cascaded DX7 branches.","source":"DX7 stacked modulation brightness calibration on 2026-05-14"} {"kind":"dx7_summed_route_scale","lesson":"Listening identified missing harsh stacked-modulation brightness. Raising `Dx7SysEx.SummedOperatorModulationRouteIndex` from 1.6 to 2.4 improves PRC to log-mel 0.16794802, envelope distance 0.09510617, centroid ratio 1.1254044, RMS ratio 1.00095, score 0.68491656, and also tightens the project-authored algorithm-8 summed-stack log-mel when graph gain is normalized. Keep isolated direct branches at 6.275; the stronger 2.4 scale belongs only to summed/cascaded DX7 branches.","source":"DX7 stacked modulation brightness calibration on 2026-05-14"}
{"kind":"dx7_summed_route_scale_hotter","lesson":"Follow-up listening and band analysis showed the previous summed/cascaded scale still sounded underwater. Raising `Dx7SysEx.SummedOperatorModulationRouteIndex` to 6.0 and normalizing PRC graph gain to 0.25 improves PRC to log-mel 0.13874426, envelope distance 0.067291506, RMS ratio 0.9912135, zero-crossing ratio 0.95325506, centroid ratio 1.1156512, score 0.7546445. A hotter 7.0 scale did not buy enough log-mel improvement and worsened RMS/score, so 6.0 is the current coherent stop.","source":"DX7 stacked modulation brightness hot sweep on 2026-05-14"} {"kind":"dx7_summed_route_scale_hotter","lesson":"Follow-up listening and band analysis showed the previous summed/cascaded scale still sounded underwater. Raising `Dx7SysEx.SummedOperatorModulationRouteIndex` to 6.0 and normalizing PRC graph gain to 0.25 improves PRC to log-mel 0.13874426, envelope distance 0.067291506, RMS ratio 0.9912135, zero-crossing ratio 0.95325506, centroid ratio 1.1156512, score 0.7546445. A hotter 7.0 scale did not buy enough log-mel improvement and worsened RMS/score, so 6.0 is the current coherent stop.","source":"DX7 stacked modulation brightness hot sweep on 2026-05-14"}
{"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":"patch_library","lesson":"Repo-owned AquaSynth .aqua stock now lives under patches/ with library.yaml; every .aqua must parse/export in tests and the patch library must remain out of AquaSynth.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":"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":"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 are useful broader probes, but listening rejected the first loose gate. ANLGSYN 1, RES SYNTH1, DX1 LEAD B, and MELLOWSOLO should remain pressure until their timbre/zero-crossing mismatch is understood. Only Mooger and fixed Piano Bass are currently defensible rendered community parity targets.","source":"Community DX7 analog1.syx survey on 2026-05-14"} {"kind":"dx7_community_voice_parity","lesson":"Actual public-domain analog1.syx community voices are useful broader probes, but listening rejected the first loose gate. ANLGSYN 1, RES SYNTH1, DX1 LEAD B, and MELLOWSOLO should remain pressure until their timbre/zero-crossing mismatch is understood. Only Mooger and fixed Piano Bass are currently defensible rendered community parity targets.","source":"Community DX7 analog1.syx survey on 2026-05-14"}
{"kind":"dx7_voice_transpose","lesson":"Piano Bass in analog1.syx proved imported DX7 voices must lower the voice transpose byte into graph frequency. Transpose 24 is neutral; transpose 12 lowers MIDI note 60 to 130.8128 Hz. Hardcoding 261.6256 made Piano Bass a full octave too high. After the fix, Piano Bass improves to log-mel 0.16180529, zero-crossing 0.905511, score 0.64267576. Do not call the broader analog1.syx survey passing; only Mooger and fixed Piano Bass are currently defensible.","source":"Piano Bass listening correction on 2026-05-14"} {"kind":"dx7_voice_transpose","lesson":"Piano Bass in analog1.syx proved imported DX7 voices must lower the voice transpose byte into graph frequency. Transpose 24 is neutral; transpose 12 lowers MIDI note 60 to 130.8128 Hz. Hardcoding 261.6256 made Piano Bass a full octave too high. After the fix, Piano Bass improves to log-mel 0.16180529, zero-crossing 0.905511, score 0.64267576. Do not call the broader analog1.syx survey passing; only Mooger and fixed Piano Bass are currently defensible.","source":"Piano Bass listening correction on 2026-05-14"}
{"kind":"dx7_fixed_frequency","lesson":"ANLGSYN 1 proved DX7 fixed-frequency operators must lower as absolute Hz divided by the graph base frequency, not as note ratios. Dexed fixed mode uses a log-frequency formula; op1 fixed 0/0 is about 1 Hz and op3 fixed 0/8 detune 14 is about 1.207 Hz, yielding ratios 0.007615 and 0.009227 against the transposed 130.8128 Hz graph base. This restores the low buzzing modulation and improves ANLGSYN 1 to log-mel 0.19427659, zero-crossing 0.94662774, score 0.659334.","source":"ANLGSYN 1 listening correction on 2026-05-14"} {"kind":"dx7_fixed_frequency","lesson":"ANLGSYN 1 proved DX7 fixed-frequency operators must lower as absolute Hz divided by the graph base frequency, not as note ratios. Dexed fixed mode uses a log-frequency formula; op1 fixed 0/0 is about 1 Hz and op3 fixed 0/8 detune 14 is about 1.207 Hz, yielding ratios 0.007615 and 0.009227 against the transposed 130.8128 Hz graph base. This restores the low buzzing modulation and improves ANLGSYN 1 to log-mel 0.19427659, zero-crossing 0.94662774, score 0.659334.","source":"ANLGSYN 1 listening correction on 2026-05-14"}
{"kind":"dx7_max_feedback","lesson":"ANLGSYN 1 still sounded too 'woooh' after fixed-frequency lowering because high-band buzz was missing. Route-index sweeps did not help; increasing only DX7 feedback value 7 from 0.66 to 2.2 restored much more 1.2-5 kHz energy while leaving feedback value 5 at 0.19 for PRC. Latest ANLGSYN 1: log-mel 0.17553389, centroid 0.9519112, zero-crossing 0.90904, score 0.6448498. Envelope/RMS worsened, so this is a timbre correction, not final ANLGSYN parity.","source":"ANLGSYN 1 max-feedback listening correction on 2026-05-14"} {"kind":"dx7_max_feedback","lesson":"ANLGSYN 1 still sounded too 'woooh' after fixed-frequency lowering because high-band buzz was missing. Route-index sweeps did not help; increasing only DX7 feedback value 7 from 0.66 to 2.2 restored much more 1.2-5 kHz energy while leaving feedback value 5 at 0.19 for PRC. Latest ANLGSYN 1: log-mel 0.17553389, centroid 0.9519112, zero-crossing 0.90904, score 0.6448498. Envelope/RMS worsened, so this is a timbre correction, not final ANLGSYN parity.","source":"ANLGSYN 1 max-feedback listening correction on 2026-05-14"}
{"kind":"dx7_output_nonlinearity","lesson":"ANLGSYN 1 listening caught a remaining 'deeooh' failure: the max-feedback candidate had a close attack but lost sustained high-band punch halfway through. Enabling Aquarium's existing output soft clip only for max-feedback self-feedback DX7 voices improved ANLGSYN to score 0.70946133, log-mel 0.18456098, envelope 0.10628832, RMS 0.9816304 while PRC stayed passing with soft clip disabled at score 0.7546429 and log-mel 0.13874851. Do not apply the nonlinearity globally; PRC proves that would be a compensator.","source":"ANLGSYN 1 sustained-buzz parity correction on 2026-05-14"} {"kind":"dx7_output_nonlinearity","lesson":"ANLGSYN 1 listening caught a remaining 'deeooh' failure: the max-feedback candidate had a close attack but lost sustained high-band punch halfway through. Enabling AquaSynth's existing output soft clip only for max-feedback self-feedback DX7 voices improved ANLGSYN to score 0.70946133, log-mel 0.18456098, envelope 0.10628832, RMS 0.9816304 while PRC stayed passing with soft clip disabled at score 0.7546429 and log-mel 0.13874851. Do not apply the nonlinearity globally; PRC proves that would be a compensator.","source":"ANLGSYN 1 sustained-buzz parity correction on 2026-05-14"}
{"kind":"dx7_max_feedback_envelope_floor","lesson":"Output soft clip alone did not fix ANLGSYN 1's mid-note drift. The self-feedback source operator was under-driven after the attack; preserving a 0.9 applied-envelope sustain floor for max-feedback source operators lifted the sustained 2.5-5 kHz band ratio from about 0.175 to above 1.0 and improved ANLGSYN 1 to score 0.7245976, log-mel 0.18460114, envelope 0.119950555, zero-crossing 0.97149533. Add sustained band gates when broad log-mel allows audible drift through.","source":"ANLGSYN 1 sustained high-band parity gate on 2026-05-14"} {"kind":"dx7_max_feedback_envelope_floor","lesson":"Output soft clip alone did not fix ANLGSYN 1's mid-note drift. The self-feedback source operator was under-driven after the attack; preserving a 0.9 applied-envelope sustain floor for max-feedback source operators lifted the sustained 2.5-5 kHz band ratio from about 0.175 to above 1.0 and improved ANLGSYN 1 to score 0.7245976, log-mel 0.18460114, envelope 0.119950555, zero-crossing 0.97149533. Add sustained band gates when broad log-mel allows audible drift through.","source":"ANLGSYN 1 sustained high-band parity gate on 2026-05-14"}
{"kind":"dx7_com_compensation_cut","lesson":"ROM COM carrier compensation was louder but harmonically wrong for the analog1 community pass. User listening preferred the no-COM renders across patches; the real issue was that they were too quiet. Cut hidden per-carrier COM boosts: `OperatorOutputCompensation` returns unity and graph gain owns loudness recovery. Passing community gains with no COM are Mooger 0.75, Piano Bass 0.90, RES SYNTH1 0.75. DX1 LEAD B and MELLOWSOLO sound closer by ear with no COM but remain numeric pressure targets.","source":"community DX7 listening correction on 2026-05-14"} {"kind":"dx7_com_compensation_cut","lesson":"ROM COM carrier compensation was louder but harmonically wrong for the analog1 community pass. User listening preferred the no-COM renders across patches; the real issue was that they were too quiet. Cut hidden per-carrier COM boosts: `OperatorOutputCompensation` returns unity and graph gain owns loudness recovery. Passing community gains with no COM are Mooger 0.75, Piano Bass 0.90, RES SYNTH1 0.75. DX1 LEAD B and MELLOWSOLO sound closer by ear with no COM but remain numeric pressure targets.","source":"community DX7 listening correction on 2026-05-14"}
{"kind":"dx7_effective_note_scaling","lesson":"DX7 operator key-scaling and rate-scaling must lower against the effective played MIDI note after voice transpose, not hardcoded MIDI 60. `Dx7SysEx.OperatorRateScaling` now feeds the block-interpolated applied envelope trace, and imported operator levels use the effective note. This improves RES SYNTH1 attack/log-mel and Piano Bass harmonic metrics; Piano Bass graph gain rebalances from 0.90 to 0.72. MELLOWSOLO remains the clearest harmonic mismatch and now has a pressure artifact test rather than a fake parity pass. A trial output-level-dependent applied-envelope trace was cut because it worsened MELLOWSOLO and ANLGSYN 1.","source":"community DX7 listening correction on 2026-05-14"} {"kind":"dx7_effective_note_scaling","lesson":"DX7 operator key-scaling and rate-scaling must lower against the effective played MIDI note after voice transpose, not hardcoded MIDI 60. `Dx7SysEx.OperatorRateScaling` now feeds the block-interpolated applied envelope trace, and imported operator levels use the effective note. This improves RES SYNTH1 attack/log-mel and Piano Bass harmonic metrics; Piano Bass graph gain rebalances from 0.90 to 0.72. MELLOWSOLO remains the clearest harmonic mismatch and now has a pressure artifact test rather than a fake parity pass. A trial output-level-dependent applied-envelope trace was cut because it worsened MELLOWSOLO and ANLGSYN 1.","source":"community DX7 listening correction on 2026-05-14"}
{"kind":"dx7_modulator_peak_headroom","lesson":"Listening found ANLGSYN 1 and RES SYNTH1 blowing out into a drone at the modulator-envelope peak while Piano Bass was spot on. The surviving DX7 probe change caps only algorithm-2 non-carrier applied-envelope Level1 at 0.92, preserving Piano Bass and improving RES SYNTH1 while keeping ANLGSYN judged by log-mel and sustained high-band evidence rather than aggregate score. A wider summed-route headroom experiment for Mooger fan-in was tested and cut because it worsened Mooger metrics and did not isolate the overtone-emphasis issue.","source":"community DX7 listening correction on 2026-05-14"} {"kind":"dx7_modulator_peak_headroom","lesson":"Listening found ANLGSYN 1 and RES SYNTH1 blowing out into a drone at the modulator-envelope peak while Piano Bass was spot on. The surviving DX7 probe change caps only algorithm-2 non-carrier applied-envelope Level1 at 0.92, preserving Piano Bass and improving RES SYNTH1 while keeping ANLGSYN judged by log-mel and sustained high-band evidence rather than aggregate score. A wider summed-route headroom experiment for Mooger fan-in was tested and cut because it worsened Mooger metrics and did not isolate the overtone-emphasis issue.","source":"community DX7 listening correction on 2026-05-14"}
{"kind":"dx7_mellowsolo_tail_window","lesson":"MELLOWSOLO pressure artifacts need a longer render window than 1.0s because the Aquarium candidate emits a 0.6s release from a 0.65s gate, so the old WAV cut the tail before it could finish. Extending only the MELLOWSOLO pressure render to 1.25s improves log-mel from about 0.429 to 0.347 and makes the tail audible without changing the synth lowering or passing parity targets. A global traced-release-duration experiment was tested and cut because it shortened releases too aggressively and broke PRC, Piano Bass, RES SYNTH1, and ANLGSYN 1. RES SYNTH1 has oscillator sync enabled, so its phase-like attack issue is more likely relative operator attack/peak shape than free-running oscillator phase.","source":"community DX7 listening correction on 2026-05-14"} {"kind":"dx7_mellowsolo_tail_window","lesson":"MELLOWSOLO pressure artifacts need a longer render window than 1.0s because the AquaSynth candidate emits a 0.6s release from a 0.65s gate, so the old WAV cut the tail before it could finish. Extending only the MELLOWSOLO pressure render to 1.25s improves log-mel from about 0.429 to 0.347 and makes the tail audible without changing the synth lowering or passing parity targets. A global traced-release-duration experiment was tested and cut because it shortened releases too aggressively and broke PRC, Piano Bass, RES SYNTH1, and ANLGSYN 1. RES SYNTH1 has oscillator sync enabled, so its phase-like attack issue is more likely relative operator attack/peak shape than free-running oscillator phase.","source":"community DX7 listening correction on 2026-05-14"}
{"kind":"dx7_graph_pitch_lfo","lesson":"ANLGSYN 1's missing beat is DX7 pitch LFO, not amplitude LFO: raw analog1.syx has AMD=71 but all operator AMS values are 0, while PMD=32/PMS=1/sine speed 38/delay 33 are active. Aquarium operator graphs now own graph-level vibrato (`vibrato`, `vibrato_hz`, `vibrato_delay`) and the DX7 probe lowers that LFO to about depth 0.003694 at 5.993 Hz with a 0.233s fade-in. A hard delay gate was tested and cut because it pushed log-mel over the ANLGSYN gate; fade-in keeps ANLGSYN passing at log-mel about 0.19497 and score about 0.6979.","source":"ANLGSYN 1 pitch-LFO beat correction on 2026-05-14"} {"kind":"dx7_graph_pitch_lfo","lesson":"ANLGSYN 1's missing beat is DX7 pitch LFO, not amplitude LFO: raw analog1.syx has AMD=71 but all operator AMS values are 0, while PMD=32/PMS=1/sine speed 38/delay 33 are active. AquaSynth operator graphs now own graph-level vibrato (`vibrato`, `vibrato_hz`, `vibrato_delay`) and the DX7 probe lowers that LFO to about depth 0.003694 at 5.993 Hz with a 0.233s fade-in. A hard delay gate was tested and cut because it pushed log-mel over the ANLGSYN gate; fade-in keeps ANLGSYN passing at log-mel about 0.19497 and score about 0.6979.","source":"ANLGSYN 1 pitch-LFO beat correction on 2026-05-14"}
{"kind":"zyn_reference_start","lesson":"The ZynAddSubFX phase should start with structural feature extraction, not translation. Upstream Zyn instruments live in a separate bank submodule and the preset provenance is not clear enough to vendor casually, so the first parser slice uses project-authored .xiz fixtures. `ZynInstrumentReader` reads plain or gzip-compressed XML and classifies active ADD/SUB/PAD engines, kit layering, envelopes, free envelopes, LFOs, filters, formants, and effects into ReferencePatch features.","source":"ZynAddSubFX feature extraction kickoff on 2026-05-14"} {"kind":"zyn_reference_start","lesson":"The ZynAddSubFX phase should start with structural feature extraction, not translation. Upstream Zyn instruments live in a separate bank submodule and the preset provenance is not clear enough to vendor casually, so the first parser slice uses project-authored .xiz fixtures. `ZynInstrumentReader` reads plain or gzip-compressed XML and classifies active ADD/SUB/PAD engines, kit layering, envelopes, free envelopes, LFOs, filters, formants, and effects into ReferencePatch features.","source":"ZynAddSubFX feature extraction kickoff on 2026-05-14"}
{"kind":"zyn_reference_rebuilds","lesson":"The first Zyn-style Aquarium rebuilds can sketch additive lead, PAD texture, and vocal/formant layer fixtures with existing voices, macros, formants, filters, noise, and modulation, but the missing abstractions are now concrete: additive harmonic-bank syntax, PAD/spectral source generation, free envelopes for normal voices, named kit/layer routing with per-layer effect sends, and richer formant/vowel morphing. Keep these as named pressure before adding new DSL surface.","source":"ZynAddSubFX rebuild catalog slice on 2026-05-14"} {"kind":"zyn_reference_rebuilds","lesson":"The first Zyn-style AquaSynth rebuilds can sketch additive lead, PAD texture, and vocal/formant layer fixtures with existing voices, macros, formants, filters, noise, and modulation, but the missing abstractions are now concrete: additive harmonic-bank syntax, PAD/spectral source generation, free envelopes for normal voices, named kit/layer routing with per-layer effect sends, and richer formant/vowel morphing. Keep these as named pressure before adding new DSL surface.","source":"ZynAddSubFX rebuild catalog slice on 2026-05-14"}
{"kind":"zyn_worst_case_survey","lesson":"Scanning the upstream ZynAddSubFX instrument bank at commit e9f64a9 found 1358 .xiz files and the hardened reader now parses all of them. The nastiest target is `olivers-100/0032-Drum Kit.xiz`: 16 enabled kit items, mixed ADD/SUB/PAD engines, 106 envelopes, 25 free envelopes, 73 LFOs, 37 filters, and 3 formant filters. The broader top list says named kit/layer routing should come before syntax golf for additive banks, PAD sources, or formant motion; otherwise new abstractions will have no coherent owner.","source":"ZynAddSubFX pressure survey on 2026-05-14"} {"kind":"zyn_worst_case_survey","lesson":"Scanning the upstream ZynAddSubFX instrument bank at commit e9f64a9 found 1358 .xiz files and the hardened reader now parses all of them. The nastiest target is `olivers-100/0032-Drum Kit.xiz`: 16 enabled kit items, mixed ADD/SUB/PAD engines, 106 envelopes, 25 free envelopes, 73 LFOs, 37 filters, and 3 formant filters. The broader top list says named kit/layer routing should come before syntax golf for additive banks, PAD sources, or formant motion; otherwise new abstractions will have no coherent owner.","source":"ZynAddSubFX pressure survey on 2026-05-14"}
{"kind":"layer_language_golf","lesson":"The first Zyn-driven language-golf abstraction is `layer`: named layer declarations own engine tags, optional MIDI key range metadata, default gain, and effect-send labels, while lowering to ordinary Aquarium voices for now. The abstraction generalizes across additive, PAD-texture, and vocal/formant rebuilds and gives future additive-bank, PAD-source, free-envelope, and formant-motion syntax a coherent owner. It deliberately does not implement those later features yet.","source":"Zyn named layer DSL slice on 2026-05-14"} {"kind":"layer_language_golf","lesson":"The first Zyn-driven language-golf abstraction is `layer`: named layer declarations own engine tags, optional MIDI key range metadata, default gain, and effect-send labels, while lowering to ordinary AquaSynth voices for now. The abstraction generalizes across additive, PAD-texture, and vocal/formant rebuilds and gives future additive-bank, PAD-source, free-envelope, and formant-motion syntax a coherent owner. It deliberately does not implement those later features yet.","source":"Zyn named layer DSL slice on 2026-05-14"}
{"kind":"harmonic_bank_language_golf","lesson":"Additive `harmonics` syntax now records authored ratio/gain partial banks under named layers and lowers them to ordinary Aquarium voices. This generalizes additive lead and future drawbar/partial-bank targets without hiding Zyn ADDsynth phase, bandwidth, oscillator shaping, or free-envelope semantics behind a fake parity claim.","source":"Zyn harmonic-bank DSL slice on 2026-05-14"} {"kind":"harmonic_bank_language_golf","lesson":"Additive `harmonics` syntax now records authored ratio/gain partial banks under named layers and lowers them to ordinary AquaSynth voices. This generalizes additive lead and future drawbar/partial-bank targets without hiding Zyn ADDsynth phase, bandwidth, oscillator shaping, or free-envelope semantics behind a fake parity claim.","source":"Zyn harmonic-bank DSL slice on 2026-05-14"}
{"kind":"voice_rate_level_envelopes","lesson":"The existing readable `env=rl rates=... levels=... curves=... gate=...` contour now applies to normal voices and therefore to named layer defaults, not only operator graphs. This gives PAD and vocal Zyn rebuilds staged non-ADSR onset/release shape while preserving the invariant that Note owns gate timing and envelope data owns level shape. Arbitrary Zyn free-envelope point curves remain missing pressure.","source":"Zyn staged voice envelope DSL slice on 2026-05-14"} {"kind":"voice_rate_level_envelopes","lesson":"The existing readable `env=rl rates=... levels=... curves=... gate=...` contour now applies to normal voices and therefore to named layer defaults, not only operator graphs. This gives PAD and vocal Zyn rebuilds staged non-ADSR onset/release shape while preserving the invariant that Note owns gate timing and envelope data owns level shape. Arbitrary Zyn free-envelope point curves remain missing pressure.","source":"Zyn staged voice envelope DSL slice on 2026-05-14"}
{"kind":"pad_spectral_cloud","lesson":"PAD-style layers now have `spectrum layer=... root=... spread=... partials=ratio:gain,...`, recorded as `SpectralBank` and generated as PADsynth-style FFT tables: frequency-domain harmonic spreading, deterministic random phase, one inverse FFT, normalization, then Faust waveform/rdtable playback before ordinary voice treatment. This gives PAD texture rebuilds a real spectral source authority without inflating the voice graph; roots/spreads/partials are authoring-time table shape, not runtime controls. It still does not claim full Zyn PAD harmonic-profile/randomness/pitch-zone behavior.","source":"Zyn PAD spectral-cloud DSL slice on 2026-05-14"} {"kind":"pad_spectral_cloud","lesson":"PAD-style layers now have `spectrum layer=... root=... spread=... partials=ratio:gain,...`, recorded as `SpectralBank` and generated as PADsynth-style FFT tables: frequency-domain harmonic spreading, deterministic random phase, one inverse FFT, normalization, then Faust waveform/rdtable playback before ordinary voice treatment. This gives PAD texture rebuilds a real spectral source authority without inflating the voice graph; roots/spreads/partials are authoring-time table shape, not runtime controls. It still does not claim full Zyn PAD harmonic-profile/randomness/pitch-zone behavior.","source":"Zyn PAD spectral-cloud DSL slice on 2026-05-14"}
{"kind":"zyn_reference_oracle","lesson":"ZynAddSubFX source is pinned as GPL test/development material under `external/zynaddsubfx` at 3ab608c432996ba4d582176572c0b0f82328c825. Treat it like dexed-py: a parity oracle for tests and artifacts, not Aquarium implementation, runtime dependency, or NuGet package content. Before further Zyn DSL golf, build a test-only PAD renderer/wrapper from this source and compare Aquarium rebuilds against actual Zyn behavior.","source":"Zyn source pinning for parity on 2026-05-14"} {"kind":"zyn_reference_oracle","lesson":"ZynAddSubFX source is pinned as GPL test/development material under `external/zynaddsubfx` at 3ab608c432996ba4d582176572c0b0f82328c825. Treat it like dexed-py: a parity oracle for tests and artifacts, not AquaSynth implementation, runtime dependency, or NuGet package content. Before further Zyn DSL golf, build a test-only PAD renderer/wrapper from this source and compare AquaSynth rebuilds against actual Zyn behavior.","source":"Zyn source pinning for parity on 2026-05-14"}
{"date":"2026-05-14","area":"zyn-reference","lesson":"Pinned Zyn can build on Windows through MSYS2/MinGW when GUI/plugins are disabled, tests are enabled, Werror is off, and the stale non-async MiddleWare Part constructor call is patched. Apply Zyn compatibility patches to ignored artifact source copies, not the clean pinned submodule.","evidence":"tools/zyn-reference/build-zyn-reference.ps1 copies external/zynaddsubfx to artifacts/zyn-source-msys, applies tools/zyn-reference/patches/0001-msys-part-constructor.patch, builds PadNoteTest, and ctest -R PadNoteTest passes."} {"date":"2026-05-14","area":"zyn-reference","lesson":"Pinned Zyn can build on Windows through MSYS2/MinGW when GUI/plugins are disabled, tests are enabled, Werror is off, and the stale non-async MiddleWare Part constructor call is patched. Apply Zyn compatibility patches to ignored artifact source copies, not the clean pinned submodule.","evidence":"tools/zyn-reference/build-zyn-reference.ps1 copies external/zynaddsubfx to artifacts/zyn-source-msys, applies tools/zyn-reference/patches/0001-msys-part-constructor.patch, builds PadNoteTest, and ctest -R PadNoteTest passes."}
{"date":"2026-05-15","area":"zyn-pad-parity","lesson":"The Zyn PAD oracle now emits real reference material through Zyn PADnoteParameters/PADnote, and the first project-authored PAD comparison is not close: log-mel 0.552296, envelope 0.774501, RMS ratio 0.234763, centroid ratio 1.025735, score 0.236117. Treat this as evidence to isolate source table generation, playback/gain, envelope, and fixture mapping before adding more Zyn DSL syntax.","evidence":"tools/zyn-reference/ZynPadReference.cpp plus tests/AquariumSynth.Dsl.Tests/ZynReferenceParityTests.cs write artifacts/parity/zyn-pad-reference/pad-texture-zyn.wav, pad-texture-aqua.wav, and report.txt when the Zyn artifact renderer is built."} {"date":"2026-05-15","area":"zyn-pad-parity","lesson":"The Zyn PAD oracle now emits real reference material through Zyn PADnoteParameters/PADnote, and the first project-authored PAD comparison is not close: log-mel 0.552296, envelope 0.774501, RMS ratio 0.234763, centroid ratio 1.025735, score 0.236117. Treat this as evidence to isolate source table generation, playback/gain, envelope, and fixture mapping before adding more Zyn DSL syntax.","evidence":"tools/zyn-reference/ZynPadReference.cpp plus tests/AquaSynth.Dsl.Tests/ZynReferenceParityTests.cs write artifacts/parity/zyn-pad-reference/pad-texture-zyn.wav, pad-texture-aqua.wav, and report.txt when the Zyn artifact renderer is built."}
{"date":"2026-05-15","area":"zyn-pad-parity","lesson":"User listening correctly identified the first Aquarium PAD rebuild as too noisy. The fix was architectural cleanup before new syntax: cut unearned noise/drive/soft-clip from the rebuild, interpolate spectral table reads, and use Zyn-sized 131072-frame PAD tables. Score improved from 0.236117 to 0.487325, with RMS ratio 1.008516 and centroid ratio 1.083082. Remaining mismatch is source profile/table generation and envelope semantics, not decorative layers.","evidence":"artifacts/parity/zyn-pad-reference/report.txt after the de-bee pass; full dotnet test passed 96/96."} {"date":"2026-05-15","area":"zyn-pad-parity","lesson":"User listening correctly identified the first AquaSynth PAD rebuild as too noisy. The fix was architectural cleanup before new syntax: cut unearned noise/drive/soft-clip from the rebuild, interpolate spectral table reads, and use Zyn-sized 131072-frame PAD tables. Score improved from 0.236117 to 0.487325, with RMS ratio 1.008516 and centroid ratio 1.083082. Remaining mismatch is source profile/table generation and envelope semantics, not decorative layers.","evidence":"artifacts/parity/zyn-pad-reference/report.txt after the de-bee pass; full dotnet test passed 96/96."}
{"date":"2026-05-15","area":"zyn-pad-parity","lesson":"The octave-low Zyn PAD mismatch came from conflating spectral table root with note playback frequency. `spectrum root` must own table/base frequency, while `freq` owns the played note. Using Zyn's sample basefreq 77.7813 with playback freq 261.6256, and removing gratuitous spread/vibrato, improved the PAD report to log-mel 0.353365, envelope 0.348752, RMS ratio 0.97283, centroid ratio 0.999243, score 0.612832.","evidence":"PatchScript now preserves spectrum freq as voice note frequency; PatchScriptTests.SpectralBankSeparatesTableRootFromPlaybackFrequency covers the invariant; artifacts/parity/zyn-pad-reference/report.txt records the improved score."} {"date":"2026-05-15","area":"zyn-pad-parity","lesson":"The octave-low Zyn PAD mismatch came from conflating spectral table root with note playback frequency. `spectrum root` must own table/base frequency, while `freq` owns the played note. Using Zyn's sample basefreq 77.7813 with playback freq 261.6256, and removing gratuitous spread/vibrato, improved the PAD report to log-mel 0.353365, envelope 0.348752, RMS ratio 0.97283, centroid ratio 0.999243, score 0.612832.","evidence":"PatchScript now preserves spectrum freq as voice note frequency; PatchScriptTests.SpectralBankSeparatesTableRootFromPlaybackFrequency covers the invariant; artifacts/parity/zyn-pad-reference/report.txt records the improved score."}
{"date":"2026-05-15","area":"zyn-pad-parity","lesson":"Aqua's remaining detuned impression was caused by stacking same-note PAD spectral voices. Collapse body/high content into one coherent spectral table before adding layers for richness. A simple gain trim then brought loudness into line: RMS ratio 1.001704, centroid ratio 1.004072, score 0.6211.","evidence":"BuiltInScripts.ZynStylePadTexture now uses one active PAD spectrum with partials 1, 1.5, and 2 plus patch gain 1.1; artifacts/parity/zyn-pad-reference/report.txt records the focused render."} {"date":"2026-05-15","area":"zyn-pad-parity","lesson":"Aqua's remaining detuned impression was caused by stacking same-note PAD spectral voices. Collapse body/high content into one coherent spectral table before adding layers for richness. A simple gain trim then brought loudness into line: RMS ratio 1.001704, centroid ratio 1.004072, score 0.6211.","evidence":"BuiltInScripts.ZynStylePadTexture now uses one active PAD spectrum with partials 1, 1.5, and 2 plus patch gain 1.1; artifacts/parity/zyn-pad-reference/report.txt records the focused render."}
{"date":"2026-05-15","area":"zyn-pad-fixtures","lesson":"More PAD fixtures immediately exposed which pressure is real: `pad-pure` and old `pad-texture` render identically through Zyn, so the filter/category XML in `pad-texture` was not live oracle behavior. `pad-harmonic` changes table and note output through explicit OSCIL harmonics and should be the next PAD parity rung.","evidence":"tests/AquariumSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored/pad-pure.xiz and pad-harmonic.xiz plus artifacts/parity/zyn-pad-fixtures/report.txt."} {"date":"2026-05-15","area":"zyn-pad-fixtures","lesson":"More PAD fixtures immediately exposed which pressure is real: `pad-pure` and old `pad-texture` render identically through Zyn, so the filter/category XML in `pad-texture` was not live oracle behavior. `pad-harmonic` changes table and note output through explicit OSCIL harmonics and should be the next PAD parity rung.","evidence":"tests/AquaSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored/pad-pure.xiz and pad-harmonic.xiz plus artifacts/parity/zyn-pad-fixtures/report.txt."}
{"date":"2026-05-15","area":"zyn-upstream-gpl-bank","lesson":"GPL Zyn presets are acceptable as test-only feedstock. The upstream instrument submodule at `external/zynaddsubfx/instruments` commit c5c912131b31df5fdf372d2f06a25aaf2375837f provides 1318 `.xiz` files; survey and PAD render tests write ignored reports/WAVs while the NuGet boundary still excludes `.xiz`, `external/`, and artifacts.","evidence":"ZynInstrumentTests.SurveysUpstreamGplInstrumentBankWhenAvailable and ZynReferenceParityTests.ZynPadReferenceRendererSurveysUpstreamGplPadFixturesWhenBuilt; focused Zyn tests passed 14/14."} {"date":"2026-05-15","area":"zyn-upstream-gpl-bank","lesson":"GPL Zyn presets are acceptable as test-only feedstock. The upstream instrument submodule at `external/zynaddsubfx/instruments` commit c5c912131b31df5fdf372d2f06a25aaf2375837f provides 1318 `.xiz` files; survey and PAD render tests write ignored reports/WAVs while the NuGet boundary still excludes `.xiz`, `external/`, and artifacts.","evidence":"ZynInstrumentTests.SurveysUpstreamGplInstrumentBankWhenAvailable and ZynReferenceParityTests.ZynPadReferenceRendererSurveysUpstreamGplPadFixturesWhenBuilt; focused Zyn tests passed 14/14."}
{"date":"2026-05-15","area":"zyn-artifact-layout","lesson":"Zyn PAD listening artifacts now follow the DX7 parity shape: per-synth timestamped run folders with Zyn reference WAVs, source-table WAVs, `report.txt`, and `candidate.aqua`. Upstream GPL PAD runs write `candidate-aquarium.wav`; this was initially the current ZynStylePadTexture baseline and is now superseded by generated per-preset PAD rebuilds.","evidence":"ZynReferenceParityTests focused run passed 4/4 and wrote folders such as artifacts/parity/zyn-upstream-pad-fixtures/0121-ghost-ensemble-20260515T072330036."} {"date":"2026-05-15","area":"zyn-artifact-layout","lesson":"Zyn PAD listening artifacts now follow the DX7 parity shape: per-synth timestamped run folders with Zyn reference WAVs, source-table WAVs, `report.txt`, and `candidate.aqua`. Upstream GPL PAD runs write `candidate-aquasynth.wav`; this was initially the current ZynStylePadTexture baseline and is now superseded by generated per-preset PAD rebuilds.","evidence":"ZynReferenceParityTests focused run passed 4/4 and wrote folders such as artifacts/parity/zyn-upstream-pad-fixtures/0121-ghost-ensemble-20260515T072330036."}
{"date":"2026-05-15","area":"zyn-pad-translation","lesson":"Zyn PAD artifacts now use per-preset generated `.aqua` instead of the generic baseline. `ZynInstrumentReader.RebuildFirstPadAsAquariumScript` translates the first enabled PAD kit item into one named PAD layer plus a `spectrum` bank using OSCIL harmonic magnitudes, Zyn oracle sample `basefreq` as table root, PAD volume as layer gain, and simple rate/level envelope scaffolding. Reports explicitly list missing Zyn semantics: bandwidth/profile spreading, harmonic profile shaping, and filter envelope/LFO lowering.","evidence":"ZynInstrumentTests.RebuildsProjectAuthoredPadAsAquariumSpectrumScript plus focused Zyn tests passed 13/13; latest upstream PAD reports include generated candidate scripts and log-mel scores from ~0.276 to ~0.637."} {"date":"2026-05-15","area":"zyn-pad-translation","lesson":"Zyn PAD artifacts now use per-preset generated `.aqua` instead of the generic baseline. `ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript` translates the first enabled PAD kit item into one named PAD layer plus a `spectrum` bank using OSCIL harmonic magnitudes, Zyn oracle sample `basefreq` as table root, PAD volume as layer gain, and simple rate/level envelope scaffolding. Reports explicitly list missing Zyn semantics: bandwidth/profile spreading, harmonic profile shaping, and filter envelope/LFO lowering.","evidence":"ZynInstrumentTests.RebuildsProjectAuthoredPadAsAquaSynthSpectrumScript plus focused Zyn tests passed 13/13; latest upstream PAD reports include generated candidate scripts and log-mel scores from ~0.276 to ~0.637."}
{"date":"2026-05-15","topic":"zyn-pad-profile-table-semantics","lesson":"Zyn PAD bandwidth/profile/position fields belong on SpectralBank as table-generation data, not in parity side channels. Mapping those fields and Zyn's exponential PAD volume curve improved gain/envelope evidence, but profile math alone did not solve log-mel parity.","evidence":"Focused Zyn tests passed 13/13 after adding PadSpectrumProfile and zyn_* script fields; upstream PAD batch still has log-mel roughly 0.34-0.69 depending on fixture."} {"date":"2026-05-15","topic":"zyn-pad-profile-table-semantics","lesson":"Zyn PAD bandwidth/profile/position fields belong on SpectralBank as table-generation data, not in parity side channels. Mapping those fields and Zyn's exponential PAD volume curve improved gain/envelope evidence, but profile math alone did not solve log-mel parity.","evidence":"Focused Zyn tests passed 13/13 after adding PadSpectrumProfile and zyn_* script fields; upstream PAD batch still has log-mel roughly 0.34-0.69 depending on fixture."}
{"date":"2026-05-15","topic":"zyn-oscilgen-approximation","lesson":"Expanding a subset of Zyn OscilGen base_function plus spectrum_adjust is conceptually the right owner for one-harmonic PAD presets, but the first approximation is mixed and must not be treated as solved parity.","evidence":"Soft Pad log-mel improved to 0.342738 and Organ Choir Pad2 to 0.573216, while sin2x pad regressed to 0.524421, DoublePadBass to 0.689315, and Ghost Ensemble to 0.482155. Next cut needs more OscilGen semantics or gating."} {"date":"2026-05-15","topic":"zyn-oscilgen-approximation","lesson":"Expanding a subset of Zyn OscilGen base_function plus spectrum_adjust is conceptually the right owner for one-harmonic PAD presets, but the first approximation is mixed and must not be treated as solved parity.","evidence":"Soft Pad log-mel improved to 0.342738 and Organ Choir Pad2 to 0.573216, while sin2x pad regressed to 0.524421, DoublePadBass to 0.689315, and Ghost Ensemble to 0.482155. Next cut needs more OscilGen semantics or gating."}
{"date":"2026-05-15","topic":"zyn-oscilgen-stage-order","lesson":"Zyn OscilGen partial extraction should only run the sampled waveform pipeline when oscillator processing stages are live; otherwise direct harmonics stay readable and capped. A broad base-function expansion made some presets worse without earning its complexity.","evidence":"After adding sampled filter/waveshape/modulation stage order and restoring a capped direct path, sin2x pad improved to log-mel 0.455193, while Soft Pad stayed 0.342738 and Ghost stayed 0.40922. DoublePadBass and Organ Choir remain poor, so this is an incremental rung, not parity."} {"date":"2026-05-15","topic":"zyn-oscilgen-stage-order","lesson":"Zyn OscilGen partial extraction should only run the sampled waveform pipeline when oscillator processing stages are live; otherwise direct harmonics stay readable and capped. A broad base-function expansion made some presets worse without earning its complexity.","evidence":"After adding sampled filter/waveshape/modulation stage order and restoring a capped direct path, sin2x pad improved to log-mel 0.455193, while Soft Pad stayed 0.342738 and Ghost stayed 0.40922. DoublePadBass and Organ Choir remain poor, so this is an incremental rung, not parity."}
{"date":"2026-05-15","topic":"zyn-pad-kit-layering","lesson":"Do not treat first enabled PAD kit item as a Zyn instrument when kit_mode is multi-kit. Multi-kit PAD presets need per-kit table roots and layered Aqua spectrum banks, or parity artifacts compare against an amputated preset.","evidence":"DoublePadBass has kit_mode=1 with enabled PAD kit items 0 and 1. Rebuilding/summing both moved log-mel from about 0.655 to 0.493 and generated doublepadbass_0 plus doublepadbass_1. Raising the volume cap to 3.0 worsened log-mel to 0.521 and was cut."} {"date":"2026-05-15","topic":"zyn-pad-kit-layering","lesson":"Do not treat first enabled PAD kit item as a Zyn instrument when kit_mode is multi-kit. Multi-kit PAD presets need per-kit table roots and layered Aqua spectrum banks, or parity artifacts compare against an amputated preset.","evidence":"DoublePadBass has kit_mode=1 with enabled PAD kit items 0 and 1. Rebuilding/summing both moved log-mel from about 0.655 to 0.493 and generated doublepadbass_0 plus doublepadbass_1. Raising the volume cap to 3.0 worsened log-mel to 0.521 and was cut."}
{"date":"2026-05-15","topic":"zyn-ghost-base-function","lesson":"A direct-harmonic readability gate must not erase source identity. Ghost Ensemble needed power-base OscilGen expansion even without downstream oscillator stages; otherwise it collapsed toward a generic two-partial pad.","evidence":"Ghost Ensemble has base_function=4 with two explicit harmonics. Expanding power-base multi-harmonic sources changed the candidate from two partials to 32 partials and improved score to 0.446977, with log-mel 0.402789."} {"date":"2026-05-15","topic":"zyn-ghost-base-function","lesson":"A direct-harmonic readability gate must not erase source identity. Ghost Ensemble needed power-base OscilGen expansion even without downstream oscillator stages; otherwise it collapsed toward a generic two-partial pad.","evidence":"Ghost Ensemble has base_function=4 with two explicit harmonics. Expanding power-base multi-harmonic sources changed the candidate from two partials to 32 partials and improved score to 0.446977, with log-mel 0.402789."}
{"date":"2026-05-15","topic":"zyn-pad-static-filter","lesson":"Zyn PAD static global filter frequency is audible layer-balance authority, not optional decoration. Read nested FILTER params under FILTER_PARAMETERS and lower old Zyn cutoff values through the exponential FilterParams curve, not a linear freq/127 guess.","evidence":"DoublePadBass kit0 filter freq=12 and kit1 freq=61. Linear mapping gave lpf=0.094488/0.480315 and still sounded like all kazoo. Zyn's curve maps them to lpf=0.003325/0.047225, improving log-mel to 0.334546 and making the generated patch deep-bass-first. Filter envelopes/LFOs and level recovery remain missing pressure."} {"date":"2026-05-15","topic":"zyn-pad-static-filter","lesson":"Zyn PAD static global filter frequency is audible layer-balance authority, not optional decoration. Read nested FILTER params under FILTER_PARAMETERS and lower old Zyn cutoff values through the exponential FilterParams curve, not a linear freq/127 guess.","evidence":"DoublePadBass kit0 filter freq=12 and kit1 freq=61. Linear mapping gave lpf=0.094488/0.480315 and still sounded like all kazoo. Zyn's curve maps them to lpf=0.003325/0.047225, improving log-mel to 0.334546 and making the generated patch deep-bass-first. Filter envelopes/LFOs and level recovery remain missing pressure."}
{"date":"2026-05-15","topic":"zyn-pad-detune-and-filter-order","lesson":"Zyn PAD note frequency is not just the requested note plus table root; FREQUENCY_PARAMETERS coarse/fine detune are live playback authority, and analog LP2 needs a steeper low-pass than Aquarium's old one-pole default.","evidence":"DoublePadBass has coarse_detune=15360, which Zyn getdetune wraps from octave 15 to -1 octave. Emitting spectrum freq=130.8128 instead of 261.6256 and lpf_order=2 moved DoublePadBass log-mel from 0.334546 after static cutoff mapping to 0.259298; remaining gap is RMS/envelope."} {"date":"2026-05-15","topic":"zyn-pad-detune-and-filter-order","lesson":"Zyn PAD note frequency is not just the requested note plus table root; FREQUENCY_PARAMETERS coarse/fine detune are live playback authority, and analog LP2 needs a steeper low-pass than AquaSynth's old one-pole default.","evidence":"DoublePadBass has coarse_detune=15360, which Zyn getdetune wraps from octave 15 to -1 octave. Emitting spectrum freq=130.8128 instead of 261.6256 and lpf_order=2 moved DoublePadBass log-mel from 0.334546 after static cutoff mapping to 0.259298; remaining gap is RMS/envelope."}
{"date":"2026-05-15","topic":"zyn-pad-filter-envelope","lesson":"Zyn PAD FILTER_ENVELOPE is audible transient authority. Static cutoff parity can get the tone right while still missing the opening hit; Aquarium needs explicit low-pass cutoff envelopes with nonzero start levels.","evidence":"DoublePadBass sounded tonally right but softer at onset. Mapping Zyn ADSR-filter octave offsets into lpf_env=rl with lpf_start improved log-mel from 0.259298 to 0.241993, envelope distance from 0.631427 to 0.554658, RMS ratio from 0.390177 to 0.456173, and score from 0.370218 to 0.404382."} {"date":"2026-05-15","topic":"zyn-pad-filter-envelope","lesson":"Zyn PAD FILTER_ENVELOPE is audible transient authority. Static cutoff parity can get the tone right while still missing the opening hit; AquaSynth needs explicit low-pass cutoff envelopes with nonzero start levels.","evidence":"DoublePadBass sounded tonally right but softer at onset. Mapping Zyn ADSR-filter octave offsets into lpf_env=rl with lpf_start improved log-mel from 0.259298 to 0.241993, envelope distance from 0.631427 to 0.554658, RMS ratio from 0.390177 to 0.456173, and score from 0.370218 to 0.404382."}
{"date":"2026-05-15","topic":"zyn-pad-output-calibration","lesson":"After PAD table roots, detune, filter order, filter envelope, and logarithmic envelope times are mapped, remaining Zyn PAD loudness can be a generated-rebuild output calibration rather than another per-layer fixture tweak.","evidence":"Current PAD fixture RMS ratios before calibration were consistently low: DoublePadBass 0.511669, Soft Pad 0.619347, Organ Choir 0.588127, Ghost 0.730155, sin2x 0.775653. Emitting patch gain=1.6 moved DoublePadBass candidate WAV peak to 0.979 against clipped Zyn reference peak 1.0, envelope distance to 0.226563, RMS ratio to 0.818669, and score to 0.631168 while keeping batch RMS plausible."} {"date":"2026-05-15","topic":"zyn-pad-output-calibration","lesson":"After PAD table roots, detune, filter order, filter envelope, and logarithmic envelope times are mapped, remaining Zyn PAD loudness can be a generated-rebuild output calibration rather than another per-layer fixture tweak.","evidence":"Current PAD fixture RMS ratios before calibration were consistently low: DoublePadBass 0.511669, Soft Pad 0.619347, Organ Choir 0.588127, Ghost 0.730155, sin2x 0.775653. Emitting patch gain=1.6 moved DoublePadBass candidate WAV peak to 0.979 against clipped Zyn reference peak 1.0, envelope distance to 0.226563, RMS ratio to 0.818669, and score to 0.631168 while keeping batch RMS plausible."}
{"date":"2026-05-15","topic":"zyn-pad-filter-q","lesson":"Zyn PAD analog filter Q is real filter damping authority and belongs as a general Aquarium filter surface (`lpf_q`), not as a fixture-specific cutoff/gain fudge.","evidence":"Ghost Ensemble has FILTER freq_track=97 and q=28. Adding note-tracked cutoff lowered Ghost lpf from 0.369706 to 0.282776 and adding explicit lpf_q=0.499021 moved log-mel from 0.38424 to 0.375637 and RMS ratio from 1.155288 to 1.09995. DoublePadBass remained acceptable and improved to log-mel 0.247729 / score 0.650069 after Q."} {"date":"2026-05-15","topic":"zyn-pad-filter-q","lesson":"Zyn PAD analog filter Q is real filter damping authority and belongs as a general AquaSynth filter surface (`lpf_q`), not as a fixture-specific cutoff/gain fudge.","evidence":"Ghost Ensemble has FILTER freq_track=97 and q=28. Adding note-tracked cutoff lowered Ghost lpf from 0.369706 to 0.282776 and adding explicit lpf_q=0.499021 moved log-mel from 0.38424 to 0.375637 and RMS ratio from 1.155288 to 1.09995. DoublePadBass remained acceptable and improved to log-mel 0.247729 / score 0.650069 after Q."}
{"date":"2026-05-15","topic":"zyn-pad-q-order-cascade","lesson":"Explicit low-pass Q must not erase low-pass order. When a reference filter has both damping and slope, Faust lowering should cascade resonant stages according to `lpf_order` rather than replacing the filter with one generic resonant low-pass.","evidence":"Ghost Ensemble still sounded bright in the middle after lpf_q. Cascading resonlp stages for lpf_order=4 moved Ghost log-mel from 0.375637 to 0.369402 and RMS ratio from 1.09995 to 1.054875 while keeping DoublePadBass near its accepted score: log-mel 0.247748, score 0.650029. ZynPadReference harmonic dumps showed Ghost's first 32 imported OscilGen partial ratios already matched Zyn, so this was filter slope, not first-page harmonic extraction."} {"date":"2026-05-15","topic":"zyn-pad-q-order-cascade","lesson":"Explicit low-pass Q must not erase low-pass order. When a reference filter has both damping and slope, Faust lowering should cascade resonant stages according to `lpf_order` rather than replacing the filter with one generic resonant low-pass.","evidence":"Ghost Ensemble still sounded bright in the middle after lpf_q. Cascading resonlp stages for lpf_order=4 moved Ghost log-mel from 0.375637 to 0.369402 and RMS ratio from 1.09995 to 1.054875 while keeping DoublePadBass near its accepted score: log-mel 0.247748, score 0.650029. ZynPadReference harmonic dumps showed Ghost's first 32 imported OscilGen partial ratios already matched Zyn, so this was filter slope, not first-page harmonic extraction."}
{"date":"2026-05-15","topic":"zyn-oscil-filter-index","lesson":"Zyn OscilGen oscillator filter functions use one-based harmonic indices. Do not translate them through a zero-based Aquarium index; that moves spectral features to the wrong partial and can make a preset sound like the wrong overtone is being emphasized.","evidence":"sin2x uses oscillator filter type 13. With zero-based indexing, Aquarium put the spike on h3 and had centroid ratio 1.227953. After preserving Zyn's one-based index, candidate partials are h2-dominant, centroid ratio improves to 0.998145, log-mel moves from 0.491437 to 0.479782, and UpstreamSin2xOscilFilterKeepsSpikeOnSecondHarmonicWhenAvailable asserts h2 > h3 * 10."} {"date":"2026-05-15","topic":"zyn-oscil-filter-index","lesson":"Zyn OscilGen oscillator filter functions use one-based harmonic indices. Do not translate them through a zero-based AquaSynth index; that moves spectral features to the wrong partial and can make a preset sound like the wrong overtone is being emphasized.","evidence":"sin2x uses oscillator filter type 13. With zero-based indexing, AquaSynth put the spike on h3 and had centroid ratio 1.227953. After preserving Zyn's one-based index, candidate partials are h2-dominant, centroid ratio improves to 0.998145, log-mel moves from 0.491437 to 0.479782, and UpstreamSin2xOscilFilterKeepsSpikeOnSecondHarmonicWhenAvailable asserts h2 > h3 * 10."}
{"date":"2026-05-15","topic":"zyn-organ-highpass-selected-root","lesson":"Organ Choir's coherent octave blast needs real Zyn PAD high-pass ownership and selected table roots for OscilGen shift-base sources, but selected roots must not become the global default. The source rule is narrow: keep sample-0 roots unless the PAD OscilGen base function is the shift source (`base_function=7`) and the oracle provides the selected note table root.","evidence":"Organ Choir kit0 has analog HP2 with stages and FILTER_ENVELOPE, while kit1 uses base_function=7. Mapping hpf_order/hpf_env and using the selected 622.25 Hz table root only for kit1 moved the candidate's dominant peak to 522.67 Hz with 261 Hz at -1.29 dB, close to Zyn's 523.33 Hz lead and 261 Hz at -2.23 dB. Latest Organ log-mel is 0.499122 / score 0.51966, while accepted Soft Pad, Ghost, and DoublePadBass remain on sample-0 roots; global selected roots had regressed DoublePadBass."} {"date":"2026-05-15","topic":"zyn-organ-highpass-selected-root","lesson":"Organ Choir's coherent octave blast needs real Zyn PAD high-pass ownership and selected table roots for OscilGen shift-base sources, but selected roots must not become the global default. The source rule is narrow: keep sample-0 roots unless the PAD OscilGen base function is the shift source (`base_function=7`) and the oracle provides the selected note table root.","evidence":"Organ Choir kit0 has analog HP2 with stages and FILTER_ENVELOPE, while kit1 uses base_function=7. Mapping hpf_order/hpf_env and using the selected 622.25 Hz table root only for kit1 moved the candidate's dominant peak to 522.67 Hz with 261 Hz at -1.29 dB, close to Zyn's 523.33 Hz lead and 261 Hz at -2.23 dB. Latest Organ log-mel is 0.499122 / score 0.51966, while accepted Soft Pad, Ghost, and DoublePadBass remain on sample-0 roots; global selected roots had regressed DoublePadBass."}
{"date":"2026-05-15","topic":"zyn-organ-basefunc-mod-adaptive","lesson":"Organ Choir's violin-vs-wind mismatch was OscilGen source-table ownership, not pitch, envelope, or gain. Zyn `base_function_modulation` warps the phase before base-function sampling, and `adaptive_harmonics` remaps the prepared oscillator spectrum at the PAD table root; both must happen before PAD bandwidth/profile spreading.","evidence":"Before the fix, Aqua kit1 partials formed a smooth ramp (h3 -3.5 dB, h4 -5.1 dB, h8 -10.2 dB relative to h1) while Zyn's harmonic dump was sparse (h3 -27.1 dB, h4 -70.3 dB, h8 -50.2 dB). Applying base_function_modulation=1 and adaptive_harmonics=1 moved kit1 table energy to 400-800 Hz 0.2713 vs Zyn 0.2780 and 8-16 kHz 0.1471 vs Zyn 0.1355; Organ log-mel improved to 0.471483 and score to 0.541933 without changing accepted DoublePadBass, Ghost, or Soft Pad."} {"date":"2026-05-15","topic":"zyn-organ-basefunc-mod-adaptive","lesson":"Organ Choir's violin-vs-wind mismatch was OscilGen source-table ownership, not pitch, envelope, or gain. Zyn `base_function_modulation` warps the phase before base-function sampling, and `adaptive_harmonics` remaps the prepared oscillator spectrum at the PAD table root; both must happen before PAD bandwidth/profile spreading.","evidence":"Before the fix, Aqua kit1 partials formed a smooth ramp (h3 -3.5 dB, h4 -5.1 dB, h8 -10.2 dB relative to h1) while Zyn's harmonic dump was sparse (h3 -27.1 dB, h4 -70.3 dB, h8 -50.2 dB). Applying base_function_modulation=1 and adaptive_harmonics=1 moved kit1 table energy to 400-800 Hz 0.2713 vs Zyn 0.2780 and 8-16 kHz 0.1471 vs Zyn 0.1355; Organ log-mel improved to 0.471483 and score to 0.541933 without changing accepted DoublePadBass, Ghost, or Soft Pad."}
{"date":"2026-05-15","topic":"zyn-pad-ear-vs-logmel","lesson":"For sustained Zyn PAD fixtures, log-mel distance can remain pessimistic after the perceptual target has been reached. Keep it as a regression and triage signal, but do not keep grinding accepted fixtures solely to chase a DX7-like numeric threshold when listening and structural source-table evidence agree.","evidence":"After mapping selected roots, HP filter motion, Q/order cascades, one-based OscilGen filter indices, base-function phase modulation, and adaptive harmonics, the user accepted DoublePadBass, Ghost Ensemble, Soft Pad, Organ Choir, and the other recent community PAD fixtures by ear. Organ Choir still reports log-mel 0.471483 despite direct kit1 table energy matching the Zyn body/air balance and the listener judging all fixtures equivalent."} {"date":"2026-05-15","topic":"zyn-pad-ear-vs-logmel","lesson":"For sustained Zyn PAD fixtures, log-mel distance can remain pessimistic after the perceptual target has been reached. Keep it as a regression and triage signal, but do not keep grinding accepted fixtures solely to chase a DX7-like numeric threshold when listening and structural source-table evidence agree.","evidence":"After mapping selected roots, HP filter motion, Q/order cascades, one-based OscilGen filter indices, base-function phase modulation, and adaptive harmonics, the user accepted DoublePadBass, Ghost Ensemble, Soft Pad, Organ Choir, and the other recent community PAD fixtures by ear. Organ Choir still reports log-mel 0.471483 despite direct kit1 table energy matching the Zyn body/air balance and the listener judging all fixtures equivalent."}
{"date":"2026-05-16","topic":"pad-syntax-generalization","lesson":"Reference-synth names must not become canonical Aquarium syntax or public patch-model vocabulary. Importers may translate Zyn controls, and parser aliases may keep old parity artifacts readable, but generated/authored `.aqua` should use Aquarium-native `pad_*` terms for general spectral-table intent.","evidence":"Zyn PAD parity had produced generated scripts with `zyn_mode`, `zyn_bandwidth`, `zyn_bwscale`, `zyn_profile`, and `zyn_position`, plus public model names like `ZynHarmonicProfile`. The generalization pass changed generated scripts to `pad_mode`, `pad_bandwidth`, `pad_bwscale`, `pad_profile`, and `pad_position`, renamed the public profile/position records and enums to PAD-native names, preserved legacy `zyn_*` parser aliases, and passed `dotnet test AquariumSynthCSharp.slnx --no-restore` with 108 tests."} {"date":"2026-05-16","topic":"pad-syntax-generalization","lesson":"Reference-synth names must not become canonical AquaSynth syntax or public patch-model vocabulary. Importers may translate Zyn controls, and parser aliases may keep old parity artifacts readable, but generated/authored `.aqua` should use AquaSynth-native `pad_*` terms for general spectral-table intent.","evidence":"Zyn PAD parity had produced generated scripts with `zyn_mode`, `zyn_bandwidth`, `zyn_bwscale`, `zyn_profile`, and `zyn_position`, plus public model names like `ZynHarmonicProfile`. The generalization pass changed generated scripts to `pad_mode`, `pad_bandwidth`, `pad_bwscale`, `pad_profile`, and `pad_position`, renamed the public profile/position records and enums to PAD-native names, preserved legacy `zyn_*` parser aliases, and passed `dotnet test AquaSynth.slnx --no-restore` with 108 tests."}
{"date":"2026-05-16","topic":"project-rename-aquasynth","lesson":"The C# synth repo is now AquaSynth and the Rust reference branch is AquaSynth-rs. Keep repo wiring, package metadata, namespaces, solution/project names, env vars, docs, and state aligned to AquaSynth; do not reintroduce AquariumSynthCSharp/AquariumSynth.Dsl names.","evidence":"Renamed solution to AquaSynth.slnx, projects to src/AquaSynth.Dsl and tests/AquaSynth.Dsl.Tests, namespaces/package to AquaSynth.Dsl, package version suffix to 0.1.0-aquasynth.1, DX7 env var to AQUASYNTH_DX7_PYTHON, and origin to https://github.com/GameCult/AquaSynth.git. Verified `dotnet build AquaSynth.slnx --no-restore`, `dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-build -v minimal` with 108 passed, and `dotnet pack src\\AquaSynth.Dsl\\AquaSynth.Dsl.csproj -c Release --no-restore` producing AquaSynth.Dsl.0.1.0-aquasynth.1.nupkg."}

View file

@ -1,7 +1,7 @@
project: AquariumSynthCSharp project: AquaSynth
mission: > mission: >
C# authoring, analysis, and Faust generation layer for Aquarium synth patches, C# authoring, analysis, and Faust generation layer for AquaSynth patches,
targeting the Aquarium/Vortice engine path without dragging the Rust renderer targeting the AquaSynth/Vortice engine path without dragging the Rust renderer
into runtime dependencies. into runtime dependencies.
surfaces: surfaces:
instructions: AGENTS.md instructions: AGENTS.md
@ -10,32 +10,32 @@ surfaces:
scratch: state/scratch.md scratch: state/scratch.md
evidence: state/evidence.jsonl evidence: state/evidence.jsonl
spine: state/spine.yaml spine: state/spine.yaml
solution: AquariumSynthCSharp.slnx solution: AquaSynth.slnx
library: src/AquariumSynth.Dsl library: src/AquaSynth.Dsl
patch_library: patches patch_library: patches
tests: tests/AquariumSynth.Dsl.Tests tests: tests/AquaSynth.Dsl.Tests
reference_roadmap: docs/reference-synth-roadmap.md reference_roadmap: docs/reference-synth-roadmap.md
dx7_public_domain_fixtures: tests/AquariumSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain dx7_public_domain_fixtures: tests/AquaSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain
zyn_reference_source: external/zynaddsubfx zyn_reference_source: external/zynaddsubfx
zyn_upstream_instrument_bank: external/zynaddsubfx/instruments zyn_upstream_instrument_bank: external/zynaddsubfx/instruments
zyn_reference_build_script: tools/zyn-reference/build-zyn-reference.ps1 zyn_reference_build_script: tools/zyn-reference/build-zyn-reference.ps1
zyn_pad_reference_renderer: tools/zyn-reference/ZynPadReference.cpp zyn_pad_reference_renderer: tools/zyn-reference/ZynPadReference.cpp
zyn_reference_build_patches: tools/zyn-reference/patches zyn_reference_build_patches: tools/zyn-reference/patches
zyn_project_fixtures: tests/AquariumSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored zyn_project_fixtures: tests/AquaSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored
package: AquariumSynth.Dsl 0.1.0-aquarium.1 package: AquaSynth.Dsl 0.1.0-aquasynth.1
modules: modules:
model: src/AquariumSynth.Dsl/Model.cs model: src/AquaSynth.Dsl/Model.cs
parser: src/AquariumSynth.Dsl/PatchScript.cs parser: src/AquaSynth.Dsl/PatchScript.cs
faust: src/AquariumSynth.Dsl/FaustExport.cs faust: src/AquaSynth.Dsl/FaustExport.cs
faust_rendering: src/AquariumSynth.Dsl/FaustExport.cs faust_rendering: src/AquaSynth.Dsl/FaustExport.cs
dx7_reference_render_tests: tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs dx7_reference_render_tests: tests/AquaSynth.Dsl.Tests/Dx7ReferenceParityTests.cs
zyn_reference_reader: src/AquariumSynth.Dsl/ZynInstrument.cs zyn_reference_reader: src/AquaSynth.Dsl/ZynInstrument.cs
package_boundary_tests: tests/AquariumSynth.Dsl.Tests/PackageBoundaryTests.cs package_boundary_tests: tests/AquaSynth.Dsl.Tests/PackageBoundaryTests.cs
sfxr: src/AquariumSynth.Dsl/SfxrParams.cs sfxr: src/AquaSynth.Dsl/SfxrParams.cs
analysis: src/AquariumSynth.Dsl/AudioAnalysis.cs analysis: src/AquaSynth.Dsl/AudioAnalysis.cs
scoring: src/AquariumSynth.Dsl/ScriptMetrics.cs scoring: src/AquaSynth.Dsl/ScriptMetrics.cs
presets: src/AquariumSynth.Dsl/Presets.cs presets: src/AquaSynth.Dsl/Presets.cs
built_in_scripts: src/AquariumSynth.Dsl/BuiltInScripts.cs built_in_scripts: src/AquaSynth.Dsl/BuiltInScripts.cs
boundaries: boundaries:
leave_in_rust: leave_in_rust:
- sample renderer - sample renderer
@ -57,8 +57,8 @@ boundaries:
- structured .aqua patch library - structured .aqua patch library
checks: checks:
default: dotnet test default: dotnet test
package: dotnet pack src/AquariumSynth.Dsl/AquariumSynth.Dsl.csproj -c Release package: dotnet pack src/AquaSynth.Dsl/AquaSynth.Dsl.csproj -c Release
faust_validation: automatic in tests when Faust is installed faust_validation: automatic in tests when Faust is installed
dexed_py_reference: optional in tests when AQUARIUM_DX7_PYTHON or a Python with dexed-py is available dexed_py_reference: optional in tests when AQUASYNTH_DX7_PYTHON or a Python with dexed-py is available
zyn_reference_source: pinned submodule used only for parity tests; never package content zyn_reference_source: pinned submodule used only for parity tests; never package content
zyn_upstream_instrument_bank: nested GPL submodule used only for test/development pressure surveys and oracle renders; never package content zyn_upstream_instrument_bank: nested GPL submodule used only for test/development pressure surveys and oracle renders; never package content

View file

@ -1,6 +1,6 @@
{ {
"project": "AquariumSynthCSharp", "project": "AquaSynth",
"mission": "Own Aquarium Synth patch authoring, analysis, preset mapping, and Faust generation in C# for the Aquarium/Vortice engine path.", "mission": "Own AquaSynth patch authoring, analysis, preset mapping, and Faust generation in C# for the AquaSynth/Vortice engine path.",
"current_boundary": { "current_boundary": {
"leave_in_rust": [ "leave_in_rust": [
"Rust sample renderer", "Rust sample renderer",
@ -69,7 +69,7 @@
"state/spine.yaml" "state/spine.yaml"
], ],
"reference_strategy": { "reference_strategy": {
"purpose": "Use external synths and DSP corpora as parity pressure for Aquarium's patch DSL and Faust emitter: the DSL must reproduce behavior in terse, readable Aquarium syntax, not clone foreign internals.", "purpose": "Use external synths and DSP corpora as parity pressure for AquaSynth's patch DSL and Faust emitter: the DSL must reproduce behavior in terse, readable AquaSynth syntax, not clone foreign internals.",
"priority_order": [ "priority_order": [
"DX7/Dexed SysEx for FM operator topology", "DX7/Dexed SysEx for FM operator topology",
"ZynAddSubFX instruments for additive, subtractive, PAD, formant, and layered patches", "ZynAddSubFX instruments for additive, subtractive, PAD, formant, and layered patches",
@ -80,13 +80,13 @@
"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." "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": { "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, voice transpose lowering, fixed-frequency operator lowering, ROM COM output compensation cut, operator key/rate scaling at the effective played note, topology-aware route lowering, staged operator envelopes with per-segment curves, raw and block-interpolated DX7 envelope tracing, applied-envelope curved lowering with optional sustain floor, two Aquarium DSL rebuild attempts, first-class OperatorGraph support with graph-level vibrato, 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.145, project-authored algorithm-8 summed-stack/cascade/summed-pair parity, rendered community parity for Mooger, Piano Bass, RES SYNTH1, and ANLGSYN 1 with a sustained high-band gate, a MELLOWSOLO pressure artifact test, and renderable delayed operator feedback.", "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, voice transpose lowering, fixed-frequency operator lowering, ROM COM output compensation cut, operator key/rate scaling at the effective played note, topology-aware route lowering, staged operator envelopes with per-segment curves, raw and block-interpolated DX7 envelope tracing, applied-envelope curved lowering with optional sustain floor, two AquaSynth DSL rebuild attempts, first-class OperatorGraph support with graph-level vibrato, public-domain SysEx fixture coverage, dexed-py reference rendering, project-authored Dexed patch rendering, AquaSynth 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.145, project-authored algorithm-8 summed-stack/cascade/summed-pair parity, rendered community parity for Mooger, Piano Bass, RES SYNTH1, and ANLGSYN 1 with a sustained high-band gate, a MELLOWSOLO pressure artifact test, 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, feedback, and graph-level pitch motion 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; ANLGSYN 1 showed max feedback value 7 needs a hotter Aquarium amount 2.2 to recover buzzy high bands, while value 5 remains 0.19 for PRC. ANLGSYN 1 then exposed a sustained-buzz gap: output soft clip helped but did not stop mid-note drift; max-feedback source operators need an applied-envelope sustain floor so the feedback loop stays driven after the attack. ANLGSYN 1's remaining beat is pitch LFO: AMD is nonzero but all operator AMS values are 0, while PMD/PMS lower cleanly to operator-graph vibrato with faded delay. Ratio-mode detune belongs in operator frequency lowering. DX7 voice transpose belongs in graph base frequency: transpose 24 is neutral, transpose 12 lowers MIDI note 60 to 130.8128 Hz. DX7 fixed-frequency operators are absolute Hz, not note ratios; ANLGSYN 1 proved fixed op1/op3 must lower to ratios around 0.007615/0.009227 against a 130.8128 Hz graph base to preserve low buzzing modulation. ROM COM carrier compensation was cut: user listening found no-COM renders harmonically closer and mainly too quiet, so `OperatorOutputCompensation` returns unity and graph gain owns loudness recovery. Operator key-scaling and rate-scaling must lower against the effective played MIDI note after transpose; hardcoded MIDI 60 made RES SYNTH1 too soft on attack and Piano Bass less harmonically accurate. 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; later output-level-dependent and naive traced-release applied-envelope experiments were also cut because they worsened broader parity. 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; it can also preserve a floor for max-feedback source sustain. Community gains with no COM and played-note scaling: Mooger 0.75, Piano Bass 0.72, RES SYNTH1 0.75. Algorithm-2 non-carrier applied envelopes currently cap the first peak at 0.92 to prevent ANLGSYN 1/RES SYNTH1 peak blowout while preserving Piano Bass. MELLOWSOLO is a pressure artifact test, not passing parity; its render window is 1.25s so the candidate release is audible instead of cut at the artifact boundary. Mooger needs a more aggressive attack contour, and RES SYNTH1's phase-like attack issue is likely relative operator attack/peak shape because oscillator sync is enabled. PRC remains passing on log-mel/envelope/RMS, with score floor lowered to 0.74 after rate scaling changed the aggregate score.", "lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six AquaSynth voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, feedback, and graph-level pitch motion 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 AquaSynth 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; ANLGSYN 1 showed max feedback value 7 needs a hotter AquaSynth amount 2.2 to recover buzzy high bands, while value 5 remains 0.19 for PRC. ANLGSYN 1 then exposed a sustained-buzz gap: output soft clip helped but did not stop mid-note drift; max-feedback source operators need an applied-envelope sustain floor so the feedback loop stays driven after the attack. ANLGSYN 1's remaining beat is pitch LFO: AMD is nonzero but all operator AMS values are 0, while PMD/PMS lower cleanly to operator-graph vibrato with faded delay. Ratio-mode detune belongs in operator frequency lowering. DX7 voice transpose belongs in graph base frequency: transpose 24 is neutral, transpose 12 lowers MIDI note 60 to 130.8128 Hz. DX7 fixed-frequency operators are absolute Hz, not note ratios; ANLGSYN 1 proved fixed op1/op3 must lower to ratios around 0.007615/0.009227 against a 130.8128 Hz graph base to preserve low buzzing modulation. ROM COM carrier compensation was cut: user listening found no-COM renders harmonically closer and mainly too quiet, so `OperatorOutputCompensation` returns unity and graph gain owns loudness recovery. Operator key-scaling and rate-scaling must lower against the effective played MIDI note after transpose; hardcoded MIDI 60 made RES SYNTH1 too soft on attack and Piano Bass less harmonically accurate. 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; later output-level-dependent and naive traced-release applied-envelope experiments were also cut because they worsened broader parity. 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; it can also preserve a floor for max-feedback source sustain. Community gains with no COM and played-note scaling: Mooger 0.75, Piano Bass 0.72, RES SYNTH1 0.75. Algorithm-2 non-carrier applied envelopes currently cap the first peak at 0.92 to prevent ANLGSYN 1/RES SYNTH1 peak blowout while preserving Piano Bass. MELLOWSOLO is a pressure artifact test, not passing parity; its render window is 1.25s so the candidate release is audible instead of cut at the artifact boundary. Mooger needs a more aggressive attack contour, and RES SYNTH1's phase-like attack issue is likely relative operator attack/peak shape because oscillator sync is enabled. PRC remains passing on log-mel/envelope/RMS, with score floor lowered to 0.74 after rate scaling changed the aggregate score.",
"next": "Use the now-validated algorithm-8 route scale, transpose lowering, and fixed-frequency lowering as fixed inputs while isolating remaining community-voice timbre mismatch, then expand to broader public-domain banks." "next": "Use the now-validated algorithm-8 route scale, transpose lowering, and fixed-frequency lowering as fixed inputs while isolating remaining community-voice timbre mismatch, then expand to broader public-domain banks."
}, },
"zyn_reference_path": { "zyn_reference_path": {
"status": "Initial ZynInstrumentReader parses plain XML and gzip-compressed .xiz-shaped XML from project-authored fixtures into ZynInstrument records and ReferencePatch features. First Zyn-style Aquarium rebuilds exist for additive lead, PAD texture, and vocal/formant layer. `ZynInstrumentSurvey` can rank .xiz directories by feature pressure. Named `layer` declarations provide the first Zyn-driven DSL abstraction; additive `harmonics`, PAD `spectrum`, and staged `env=rl` voice envelopes now attach to those layers. Zyn PAD `spectrum` banks can now carry explicit `zyn_mode`, `zyn_bandwidth`, `zyn_bwscale`, `zyn_profile`, and `zyn_position` table-generation fields, and `PadSynthWaveform` lowers those through Zyn-style profile/bandwidth math. The pinned Zyn source now builds as a test-only MSYS2/MinGW oracle through `tools/zyn-reference/build-zyn-reference.ps1`, which patches an ignored artifact source copy, proves `PadNoteTest`, and builds `ZynPadReference`.", "status": "Initial ZynInstrumentReader parses plain XML and gzip-compressed .xiz-shaped XML from project-authored fixtures into ZynInstrument records and ReferencePatch features. First Zyn-style AquaSynth rebuilds exist for additive lead, PAD texture, and vocal/formant layer. `ZynInstrumentSurvey` can rank .xiz directories by feature pressure. Named `layer` declarations provide the first Zyn-driven DSL abstraction; additive `harmonics`, PAD `spectrum`, and staged `env=rl` voice envelopes now attach to those layers. Zyn PAD `spectrum` banks can now carry explicit `zyn_mode`, `zyn_bandwidth`, `zyn_bwscale`, `zyn_profile`, and `zyn_position` table-generation fields, and `PadSynthWaveform` lowers those through Zyn-style profile/bandwidth math. The pinned Zyn source now builds as a test-only MSYS2/MinGW oracle through `tools/zyn-reference/build-zyn-reference.ps1`, which patches an ignored artifact source copy, proves `PadNoteTest`, and builds `ZynPadReference`.",
"lesson": "Start Zyn as structural classification, then move into explicit scoped rebuilds. Upstream Zyn presets live in a nested GPL instrument-bank submodule and may be used as test-only parity/pressure feedstock once provenance is recorded. The ZynAddSubFX engine source is pinned as GPL test/development material under `external/zynaddsubfx` at 3ab608c432996ba4d582176572c0b0f82328c825; use it as a parity oracle only, never as Aquarium runtime or NuGet package content. Zyn build fixes belong in `tools/zyn-reference/patches/` and apply to ignored artifact copies, not the clean pinned submodule. The PAD oracle now renders both generated PAD sample tables and note audio through Zyn `PADnoteParameters`/`PADnote`; optional parity artifacts live under `artifacts/parity/zyn-pad-reference` and `artifacts/parity/zyn-pad-fixtures`. The first project-authored PAD comparison proved the harness and showed Aquarium `spectrum` was not a close Zyn PAD emulation. User listening identified Zyn as pure wineglass and Aquarium as angry bees. Cutting unearned air/noise/drive/soft-clip from the rebuild, adding interpolated wavetable reads, and raising Aquarium spectral tables to Zyn's 131072-frame PAD size improved the report. A later pitch/drone pass split PAD table root from playback note frequency: `spectrum root=...` owns table/base frequency and `freq=...` owns the played note. Using Zyn's reported sample basefreq 77.7813 while playing 261.6256, removing gratuitous spread/vibrato, collapsing two same-note PAD voices into one coherent spectral table, and trimming gain improved the report to log-mel 0.35669, envelope 0.347572, RMS ratio 1.001704, centroid ratio 1.004072, score 0.6211. The first PAD fixture survey shows `pad-pure` and the old `pad-texture` render identically through Zyn, so the filter/category XML in `pad-texture` is not live pressure for this oracle path. `pad-harmonic` changes the source table and note output through explicit OSCIL harmonics, making it the next real PAD reference rung. The remaining gap is source-table/profile and envelope semantics, not more decorative patch layers. The report now also emits source-table WAVs for listening. The hardened reader parses the upstream GPL instrument bank at commit c5c912131b31df5fdf372d2f06a25aaf2375837f: 1318/1318 .xiz files; optional tests write a ranked report and render five upstream PAD listening fixtures under ignored artifacts. `RebuildFirstPadAsAquariumScript` now generates first-pass per-preset PAD `.aqua` candidates from the first enabled PAD kit item, OSCIL harmonic magnitudes, oracle-reported table base frequency, basic volume, and simple rate/level envelope scaffolding while reporting missing bandwidth/profile/filter semantics. The worst target is `olivers-100/0032-Drum Kit.xiz`, with 16 enabled kit items, mixed ADD/SUB/PAD engines, 106 envelopes, 25 free envelopes, 73 LFOs, 37 filters, and 3 formant filters. The first rebuilds show Aquarium can sketch small Zyn fixtures with existing voices/macros/formants/noise, and the upstream survey correctly pushed named kit/layer routing first. `layer` owns name, engine tag, optional key range metadata, default gain, and effect-send labels while lowering to ordinary voices. `harmonics` owns authored ratio/gain partial banks under those layers. `spectrum` owns PAD-style root/spread/partial clouds under named layers and generated table base frequency; playback `freq` remains note authority. Staged `env=rl` now applies to voices as well as operators, preserving Note as gate authority and envelope data as level-shape authority. Aquarium still does not claim Zyn ADDsynth phase/bandwidth details, full Zyn PAD harmonic-profile/randomness/pitch-zone behavior, arbitrary free-envelope point curves, or richer formant morphing.", "lesson": "Start Zyn as structural classification, then move into explicit scoped rebuilds. Upstream Zyn presets live in a nested GPL instrument-bank submodule and may be used as test-only parity/pressure feedstock once provenance is recorded. The ZynAddSubFX engine source is pinned as GPL test/development material under `external/zynaddsubfx` at 3ab608c432996ba4d582176572c0b0f82328c825; use it as a parity oracle only, never as AquaSynth runtime or NuGet package content. Zyn build fixes belong in `tools/zyn-reference/patches/` and apply to ignored artifact copies, not the clean pinned submodule. The PAD oracle now renders both generated PAD sample tables and note audio through Zyn `PADnoteParameters`/`PADnote`; optional parity artifacts live under `artifacts/parity/zyn-pad-reference` and `artifacts/parity/zyn-pad-fixtures`. The first project-authored PAD comparison proved the harness and showed AquaSynth `spectrum` was not a close Zyn PAD emulation. User listening identified Zyn as pure wineglass and AquaSynth as angry bees. Cutting unearned air/noise/drive/soft-clip from the rebuild, adding interpolated wavetable reads, and raising AquaSynth spectral tables to Zyn's 131072-frame PAD size improved the report. A later pitch/drone pass split PAD table root from playback note frequency: `spectrum root=...` owns table/base frequency and `freq=...` owns the played note. Using Zyn's reported sample basefreq 77.7813 while playing 261.6256, removing gratuitous spread/vibrato, collapsing two same-note PAD voices into one coherent spectral table, and trimming gain improved the report to log-mel 0.35669, envelope 0.347572, RMS ratio 1.001704, centroid ratio 1.004072, score 0.6211. The first PAD fixture survey shows `pad-pure` and the old `pad-texture` render identically through Zyn, so the filter/category XML in `pad-texture` is not live pressure for this oracle path. `pad-harmonic` changes the source table and note output through explicit OSCIL harmonics, making it the next real PAD reference rung. The remaining gap is source-table/profile and envelope semantics, not more decorative patch layers. The report now also emits source-table WAVs for listening. The hardened reader parses the upstream GPL instrument bank at commit c5c912131b31df5fdf372d2f06a25aaf2375837f: 1318/1318 .xiz files; optional tests write a ranked report and render five upstream PAD listening fixtures under ignored artifacts. `RebuildFirstPadAsAquaSynthScript` now generates first-pass per-preset PAD `.aqua` candidates from the first enabled PAD kit item, OSCIL harmonic magnitudes, oracle-reported table base frequency, basic volume, and simple rate/level envelope scaffolding while reporting missing bandwidth/profile/filter semantics. The worst target is `olivers-100/0032-Drum Kit.xiz`, with 16 enabled kit items, mixed ADD/SUB/PAD engines, 106 envelopes, 25 free envelopes, 73 LFOs, 37 filters, and 3 formant filters. The first rebuilds show AquaSynth can sketch small Zyn fixtures with existing voices/macros/formants/noise, and the upstream survey correctly pushed named kit/layer routing first. `layer` owns name, engine tag, optional key range metadata, default gain, and effect-send labels while lowering to ordinary voices. `harmonics` owns authored ratio/gain partial banks under those layers. `spectrum` owns PAD-style root/spread/partial clouds under named layers and generated table base frequency; playback `freq` remains note authority. Staged `env=rl` now applies to voices as well as operators, preserving Note as gate authority and envelope data as level-shape authority. AquaSynth still does not claim Zyn ADDsynth phase/bandwidth details, full Zyn PAD harmonic-profile/randomness/pitch-zone behavior, arbitrary free-envelope point curves, or richer formant morphing.",
"next": "Continue Zyn PAD parity from the sampled OscilGen stage-order, multi-kit PAD, Zyn-curve static global-filter, low-pass/high-pass order, PAD frequency-detune, low-pass/high-pass filter-envelope, logarithmic Zyn envelope-time, generated Zyn PAD output-calibration, low-pass note tracking, explicit low-pass Q, Q+order cascade, one-based OscilGen oscillator-filter index, base-function-7 selected-table-root, base-function phase modulation, and adaptive-harmonics paths. Ghost Ensemble now expands its power-base multi-harmonic OscilGen source, applies Zyn filter note tracking from `freq_track`, emits `lpf_q` from the Zyn analog filter `q`, and preserves low-pass slope by cascading resonant Faust stages according to `lpf_order`; latest Ghost log-mel is 0.369402, envelope distance is 0.363685, RMS ratio is 1.054875, centroid ratio is 1.006808, and score is 0.57229. DoublePadBass is accepted by ear and latest survey reports log-mel 0.247674, envelope distance 0.208351, RMS ratio 0.888011, score 0.650319. Soft Pad is accepted by ear. Organ Choir now maps kit0 HP2/stage/filter-envelope motion and kit1's selected table root, `base_function_modulation=1`, and `adaptive_harmonics=1`; latest Organ log-mel is 0.471483, envelope distance 0.302978, RMS ratio 0.878, centroid ratio 1.011328, and score 0.541933. Its kit1 PAD table energy now matches the wind-body shape much more closely: 400-800 Hz 0.2713 vs Zyn 0.2780 and 8-16 kHz 0.1471 vs Zyn 0.1355. ZynPadReference has test-only `harmonics` and `selected` modes; selected roots are diagnostic/source-specific, not a global default, because global selected roots regressed accepted DoublePadBass. sin2x remains loud/concentrated after the one-based filter-index fix, so do not claim full Zyn PAD parity until broader OscilGen filtering/waveshaping/adaptive-harmonic behavior and note/global filter LFO ownership are mapped or deliberately excluded." "next": "Continue Zyn PAD parity from the sampled OscilGen stage-order, multi-kit PAD, Zyn-curve static global-filter, low-pass/high-pass order, PAD frequency-detune, low-pass/high-pass filter-envelope, logarithmic Zyn envelope-time, generated Zyn PAD output-calibration, low-pass note tracking, explicit low-pass Q, Q+order cascade, one-based OscilGen oscillator-filter index, base-function-7 selected-table-root, base-function phase modulation, and adaptive-harmonics paths. Ghost Ensemble now expands its power-base multi-harmonic OscilGen source, applies Zyn filter note tracking from `freq_track`, emits `lpf_q` from the Zyn analog filter `q`, and preserves low-pass slope by cascading resonant Faust stages according to `lpf_order`; latest Ghost log-mel is 0.369402, envelope distance is 0.363685, RMS ratio is 1.054875, centroid ratio is 1.006808, and score is 0.57229. DoublePadBass is accepted by ear and latest survey reports log-mel 0.247674, envelope distance 0.208351, RMS ratio 0.888011, score 0.650319. Soft Pad is accepted by ear. Organ Choir now maps kit0 HP2/stage/filter-envelope motion and kit1's selected table root, `base_function_modulation=1`, and `adaptive_harmonics=1`; latest Organ log-mel is 0.471483, envelope distance 0.302978, RMS ratio 0.878, centroid ratio 1.011328, and score 0.541933. Its kit1 PAD table energy now matches the wind-body shape much more closely: 400-800 Hz 0.2713 vs Zyn 0.2780 and 8-16 kHz 0.1471 vs Zyn 0.1355. ZynPadReference has test-only `harmonics` and `selected` modes; selected roots are diagnostic/source-specific, not a global default, because global selected roots regressed accepted DoublePadBass. sin2x remains loud/concentrated after the one-based filter-index fix, so do not claim full Zyn PAD parity until broader OscilGen filtering/waveshaping/adaptive-harmonic behavior and note/global filter LFO ownership are mapped or deliberately excluded."
}, },
"note_and_envelope_model": { "note_and_envelope_model": {
@ -97,12 +97,12 @@
"runtime_parameters": { "runtime_parameters": {
"rule": "Patch parameters are runtime controls, not compile-time constants.", "rule": "Patch parameters are runtime controls, not compile-time constants.",
"faust_surface": "Faust UI controls expose parameter zones that hosts can update with setParamValue/getParamValue-style APIs.", "faust_surface": "Faust UI controls expose parameter zones that hosts can update with setParamValue/getParamValue-style APIs.",
"aquarium_requirement": "Aquarium DSL patches declare stable exposed parameters with path/default/min/max/step metadata so callers can vary sound without recompiling.", "aquasynth_requirement": "AquaSynth DSL patches declare stable exposed parameters with path/default/min/max/step metadata so callers can vary sound without recompiling.",
"binding_rule": "Parameter binding uses field-site references such as `lpf=@/macro/brightness` and `/opgraphs/0/operators/2/level`; the field owns meaning and the parameter owns the runtime value. Avoid target-routing semantics unless a new graph authority is deliberately mapped." "binding_rule": "Parameter binding uses field-site references such as `lpf=@/macro/brightness` and `/opgraphs/0/operators/2/level`; the field owns meaning and the parameter owns the runtime value. Avoid target-routing semantics unless a new graph authority is deliberately mapped."
}, },
"last_verified": { "last_verified": {
"command": "dotnet test AquariumSynthCSharp.slnx --no-restore", "command": "dotnet build AquaSynth.slnx --no-restore; dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-build -v minimal; dotnet pack src\\AquaSynth.Dsl\\AquaSynth.Dsl.csproj -c Release --no-restore",
"result": "Mapped Zyn base-function modulation and adaptive harmonics for Organ Choir; C# suite 107 passed", "result": "Renamed local repo, namespaces, package metadata, docs, state, and remote wiring to AquaSynth/AquaSynth-rs; test assembly passed 108 and pack produced AquaSynth.Dsl.0.1.0-aquasynth.1.nupkg",
"commit": "pending Zyn Organ Choir OscilGen source-table mapping" "commit": "pending AquaSynth rename wiring"
} }
} }

View file

@ -1,18 +1,18 @@
# Scratch # Scratch
Current standing order: grow AquariumSynthCSharp through external reference Current standing order: grow AquaSynth through external reference
targets, not through speculative field sprawl. `state/spine.yaml` and targets, not through speculative field sprawl. `state/spine.yaml` and
`docs/reference-synth-roadmap.md` are the handoff surfaces for agents working on `docs/reference-synth-roadmap.md` are the handoff surfaces for agents working on
the synth library while Aquarium work continues elsewhere. the synth library while AquaSynth work continues elsewhere.
Doctrine update: Doctrine update:
- External synths are parity pressure for Aquarium's DSL, not internals to - External synths are parity pressure for AquaSynth's DSL, not internals to
clone. Only consume targets that can be backed by parity tests proving clone. Only consume targets that can be backed by parity tests proving
Aquarium can reproduce the behavior in terse, readable syntax. AquaSynth can reproduce the behavior in terse, readable syntax.
- Parsing a synth format is inventory. Rebuilding and testing behavior is the - Parsing a synth format is inventory. Rebuilding and testing behavior is the
proof. proof.
- Pinned reference synths are test-only oracles, not Aquarium organs. ZynAddSubFX - Pinned reference synths are test-only oracles, not AquaSynth organs. ZynAddSubFX
now lives under `external/zynaddsubfx` as GPL development material pinned at now lives under `external/zynaddsubfx` as GPL development material pinned at
`3ab608c432996ba4d582176572c0b0f82328c825`; it must not enter the NuGet `3ab608c432996ba4d582176572c0b0f82328c825`; it must not enter the NuGet
package or runtime dependency graph. package or runtime dependency graph.
@ -44,7 +44,7 @@ Current slice:
same as the references. Treat the remaining high log-mel values as regression same as the references. Treat the remaining high log-mel values as regression
pressure and diagnostic smoke, not as a mandate to keep sanding sounds that pressure and diagnostic smoke, not as a mandate to keep sanding sounds that
have already landed perceptually. have already landed perceptually.
- Verification: `dotnet test AquariumSynthCSharp.slnx --no-restore`: 107 - Verification: `dotnet test AquaSynth.slnx --no-restore`: 107
passed. passed.
- Next pressure can leave this Zyn PAD batch and move to the roadmap's next - Next pressure can leave this Zyn PAD batch and move to the roadmap's next
synth/abstraction rung; keep sin2x and broader OscilGen/PAD source-table synth/abstraction rung; keep sin2x and broader OscilGen/PAD source-table
@ -58,12 +58,12 @@ Current slice:
bandwidth, bandwidth scale, harmonic profile, and harmonic-position warp. bandwidth, bandwidth scale, harmonic profile, and harmonic-position warp.
`PadSynthWaveform` uses the Zyn PAD profile/bandwidth formulas for those `PadSynthWaveform` uses the Zyn PAD profile/bandwidth formulas for those
banks and keeps the old generic PADsynth path for ordinary `spectrum`. banks and keeps the old generic PADsynth path for ordinary `spectrum`.
- `ZynInstrumentReader.RebuildFirstPadAsAquariumScript` now emits readable - `ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript` now emits readable
neutral `pad_mode=`, `pad_bandwidth=`, `pad_bwscale=`, `pad_profile=...`, neutral `pad_mode=`, `pad_bandwidth=`, `pad_bwscale=`, `pad_profile=...`,
and `pad_position=...` fields, and maps Zyn PAD volume with the source and `pad_position=...` fields, and maps Zyn PAD volume with the source
exponential gain curve instead of the old tiny linear `/500` guess. The exponential gain curve instead of the old tiny linear `/500` guess. The
parser still accepts older `zyn_*` aliases for parity artifacts, but authored parser still accepts older `zyn_*` aliases for parity artifacts, but authored
`.aqua` should use Aquarium-native PAD terms. `.aqua` should use AquaSynth-native PAD terms.
- The translator now expands a useful subset of Zyn OscilGen base-function - The translator now expands a useful subset of Zyn OscilGen base-function
harmonic content plus spectrum adjustment before feeding PAD synthesis. This harmonic content plus spectrum adjustment before feeding PAD synthesis. This
is a coherent source-table owner, but current parity is mixed: it improves is a coherent source-table owner, but current parity is mixed: it improves
@ -71,7 +71,7 @@ Current slice:
regress. Treat it as live pressure on OscilGen coverage, not solved Zyn regress. Treat it as live pressure on OscilGen coverage, not solved Zyn
oscillator parity. oscillator parity.
- Latest focused verification: - Latest focused verification:
`dotnet test tests\AquariumSynth.Dsl.Tests\AquariumSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~ZynInstrumentTests|FullyQualifiedName~ZynReferenceParityTests"`: `dotnet test tests\AquaSynth.Dsl.Tests\AquaSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~ZynInstrumentTests|FullyQualifiedName~ZynReferenceParityTests"`:
13 passed. 13 passed.
- Latest upstream PAD batch after OscilGen subset: - Latest upstream PAD batch after OscilGen subset:
`Soft Pad` log-mel `0.342738`, score `0.443083`; `Soft Pad` log-mel `0.342738`, score `0.443083`;
@ -119,7 +119,7 @@ Current user-ear correction:
with two enabled PAD kit items across the full key range. The old parity path with two enabled PAD kit items across the full key range. The old parity path
rendered and rebuilt only kit item 0. The upstream PAD test now sums enabled rendered and rebuilt only kit item 0. The upstream PAD test now sums enabled
PAD kit notes and passes per-kit table roots into PAD kit notes and passes per-kit table roots into
`RebuildEnabledPadsAsAquariumScript`; generated Aqua emits `RebuildEnabledPadsAsAquaSynthScript`; generated Aqua emits
`doublepadbass_0` and `doublepadbass_1`. `doublepadbass_0` and `doublepadbass_1`.
- Latest DoublePadBass after multi-kit rebuild: log-mel `0.493451`, score - Latest DoublePadBass after multi-kit rebuild: log-mel `0.493451`, score
`0.376854`, RMS ratio `0.826864`. Raising the volume cap to `3.0` worsened it `0.376854`, RMS ratio `0.826864`. Raising the volume cap to `3.0` worsened it
@ -152,7 +152,7 @@ Current user-ear correction:
- User then heard the tone as right but the Zyn reference hitting harder at the - User then heard the tone as right but the Zyn reference hitting harder at the
beginning. The missing authority was Zyn's PAD filter envelope: Aqua had only beginning. The missing authority was Zyn's PAD filter envelope: Aqua had only
the static cutoff, while Zyn adds ADSR-filter octave offsets before the the static cutoff, while Zyn adds ADSR-filter octave offsets before the
global filter. Aquarium now has a general low-pass rate/level envelope global filter. AquaSynth now has a general low-pass rate/level envelope
surface (`lpf_env=rl`, `lpf_start`, `lpf_rates`, `lpf_levels`) and the Zyn surface (`lpf_env=rl`, `lpf_start`, `lpf_rates`, `lpf_levels`) and the Zyn
importer maps `FILTER_ENVELOPE` into it. Latest DoublePadBass after this importer maps `FILTER_ENVELOPE` into it. Latest DoublePadBass after this
pass: log-mel `0.241993`, envelope distance `0.554658`, RMS ratio `0.456173`, pass: log-mel `0.241993`, envelope distance `0.554658`, RMS ratio `0.456173`,
@ -179,7 +179,7 @@ Current user-ear correction:
`log2(note/440) * tracking` rule. Aqua now applies that when importing PAD `log2(note/440) * tracking` rule. Aqua now applies that when importing PAD
global filters. global filters.
- Ghost still had too much upper tail, so the next cut was not a Ghost-only - Ghost still had too much upper tail, so the next cut was not a Ghost-only
brightness knob. Aquarium now has explicit low-pass Q via `lpf_q`/`lpq` on brightness knob. AquaSynth now has explicit low-pass Q via `lpf_q`/`lpq` on
the filter model. Zyn PAD analog low-pass `q` imports through the filter model. Zyn PAD analog low-pass `q` imports through
`exp((q/127)^2 * ln(1000)) - 0.9`; Ghost emits `lpf_q=0.499021`, which Faust `exp((q/127)^2 * ln(1000)) - 0.9`; Ghost emits `lpf_q=0.499021`, which Faust
lowers through `fi.resonlp(..., max(0.1, lpf_q), 1.0)`. lowers through `fi.resonlp(..., max(0.1, lpf_q), 1.0)`.
@ -219,7 +219,7 @@ Current user-ear correction:
by ear, with Organ Choir still a bit tinny and sin2x still overdoing a high by ear, with Organ Choir still a bit tinny and sin2x still overdoing a high
harmonic. The sin2x issue was a real OscilGen indexing bug, not a fixture harmonic. The sin2x issue was a real OscilGen indexing bug, not a fixture
brightness preference: Zyn oscillator filters receive one-based harmonic brightness preference: Zyn oscillator filters receive one-based harmonic
indices (`filter(1)` is the first harmonic), while Aquarium had translated indices (`filter(1)` is the first harmonic), while AquaSynth had translated
them through a zero-based index and moved the type-13 spike filter from h2 to them through a zero-based index and moved the type-13 spike filter from h2 to
h3. `ZynOscilFilterGain` now preserves Zyn's one-based index, and the h3. `ZynOscilFilterGain` now preserves Zyn's one-based index, and the
upstream sin2x regression asserts h2 dominates h3 by more than 10x. upstream sin2x regression asserts h2 dominates h3 by more than 10x.
@ -252,16 +252,16 @@ Completed this slice:
randomness control, or pitch-zone behavior. It is a source-level FFT randomness control, or pitch-zone behavior. It is a source-level FFT
partial-cloud authority for readable PAD rebuilds. partial-cloud authority for readable PAD rebuilds.
- Focused verification before wavetable replacement passed: - Focused verification before wavetable replacement passed:
`dotnet test tests\AquariumSynth.Dsl.Tests\AquariumSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~SpectralBank|FullyQualifiedName~ZynStyleReferenceRebuildsParseExportAndDeclarePressure|FullyQualifiedName~BuiltInReferenceScriptsParseAndExportFaust"`: `dotnet test tests\AquaSynth.Dsl.Tests\AquaSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~SpectralBank|FullyQualifiedName~ZynStyleReferenceRebuildsParseExportAndDeclarePressure|FullyQualifiedName~BuiltInReferenceScriptsParseAndExportFaust"`:
4 passed. 4 passed.
- Wavetable replacement verification: - Wavetable replacement verification:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
94 passed. 94 passed.
- FFT table generator focused verification: - FFT table generator focused verification:
`dotnet test tests\AquariumSynth.Dsl.Tests\AquariumSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~SpectralBank|FullyQualifiedName~FaustCompilerValidatesSpectralBank|FullyQualifiedName~ZynStyleReferenceRebuildsParseExportAndDeclarePressure"`: `dotnet test tests\AquaSynth.Dsl.Tests\AquaSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~SpectralBank|FullyQualifiedName~FaustCompilerValidatesSpectralBank|FullyQualifiedName~ZynStyleReferenceRebuildsParseExportAndDeclarePressure"`:
4 passed. 4 passed.
- Full verification after FFT table generator: - Full verification after FFT table generator:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
94 passed. 94 passed.
Previous slice: Previous slice:
@ -276,10 +276,10 @@ Previous slice:
- Arbitrary Zyn free-envelope point curves remain missing pressure. This slice - Arbitrary Zyn free-envelope point curves remain missing pressure. This slice
is the coherent staged-envelope rung, not a free-mode clone. is the coherent staged-envelope rung, not a free-mode clone.
- Focused verification passed: - Focused verification passed:
`dotnet test tests\AquariumSynth.Dsl.Tests\AquariumSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~LayeredVoiceRateLevelEnvelope|FullyQualifiedName~ZynStyleReferenceRebuildsParseExportAndDeclarePressure|FullyQualifiedName~BuiltInReferenceScriptsParseAndExportFaust"`: `dotnet test tests\AquaSynth.Dsl.Tests\AquaSynth.Dsl.Tests.csproj --no-restore --filter "FullyQualifiedName~LayeredVoiceRateLevelEnvelope|FullyQualifiedName~ZynStyleReferenceRebuildsParseExportAndDeclarePressure|FullyQualifiedName~BuiltInReferenceScriptsParseAndExportFaust"`:
3 passed. 3 passed.
- Full verification with bundled Python/dexed-py: - Full verification with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
91 passed. 91 passed.
Previous slice: Previous slice:
@ -294,7 +294,7 @@ Previous slice:
- This is not Zyn ADDsynth parity theater. Phase, bandwidth, oscillator - This is not Zyn ADDsynth parity theater. Phase, bandwidth, oscillator
shaping, and exact free-envelope behavior remain explicit pressure. shaping, and exact free-envelope behavior remain explicit pressure.
- Verified with bundled Python/dexed-py still wired: - Verified with bundled Python/dexed-py still wired:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
90 passed. 90 passed.
Previous slice: Previous slice:
@ -309,7 +309,7 @@ Previous slice:
Future additive-bank, PAD-source, free-envelope, and formant-motion syntax Future additive-bank, PAD-source, free-envelope, and formant-motion syntax
now has somewhere coherent to attach. now has somewhere coherent to attach.
- Verified with bundled Python/dexed-py still wired: - Verified with bundled Python/dexed-py still wired:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
88 passed. 88 passed.
Previous slice: Previous slice:
@ -328,12 +328,12 @@ Previous slice:
- Survey conclusion: named kit/layer routing should probably precede syntax - Survey conclusion: named kit/layer routing should probably precede syntax
golf for additive banks, PAD sources, or formant motion. golf for additive banks, PAD sources, or formant motion.
- Verified with bundled Python/dexed-py still wired: - Verified with bundled Python/dexed-py still wired:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
86 passed. 86 passed.
Previous slice: Previous slice:
- Added first Zyn-style Aquarium rebuilds for the project-authored additive - Added first Zyn-style AquaSynth rebuilds for the project-authored additive
lead, PAD texture, and vocal/formant layer fixtures. They live in lead, PAD texture, and vocal/formant layer fixtures. They live in
`BuiltInScripts` and `ReferenceRebuildCatalog.ZynRebuilds`. `BuiltInScripts` and `ReferenceRebuildCatalog.ZynRebuilds`.
- The rebuild tests parse/export every Zyn script and compare matched feature - The rebuild tests parse/export every Zyn script and compare matched feature
@ -343,7 +343,7 @@ Previous slice:
normal voices, named kit/layer routing with per-layer effect sends, and richer normal voices, named kit/layer routing with per-layer effect sends, and richer
formant/vowel morphing. No new runtime surface was added yet. formant/vowel morphing. No new runtime surface was added yet.
- Verified with bundled Python/dexed-py still wired: - Verified with bundled Python/dexed-py still wired:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
83 passed. 83 passed.
Previous slice: Previous slice:
@ -353,13 +353,13 @@ Previous slice:
gzip-compressed XML, extracts instrument metadata and kit items, and emits gzip-compressed XML, extracts instrument metadata and kit items, and emits
neutral `ReferencePatch` features. neutral `ReferencePatch` features.
- Added project-authored Zyn fixtures under - Added project-authored Zyn fixtures under
`tests/AquariumSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored` instead `tests/AquaSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored` instead
of vendoring upstream preset-bank files with unclear root provenance. of vendoring upstream preset-bank files with unclear root provenance.
- Current Zyn classifier detects active ADD/SUB/PAD engines, enabled kit items, - Current Zyn classifier detects active ADD/SUB/PAD engines, enabled kit items,
layering, envelopes, free envelopes, LFOs, filters, formant filters, and layering, envelopes, free envelopes, LFOs, filters, formant filters, and
effects. This is inventory for choosing rebuild targets, not translation yet. effects. This is inventory for choosing rebuild targets, not translation yet.
- Verified with bundled Python/dexed-py still wired: - Verified with bundled Python/dexed-py still wired:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
81 passed. 81 passed.
Previous slice: Previous slice:
@ -392,7 +392,7 @@ Previous slice:
too aggressively and broke PRC, `Piano Bass`, `RES SYNTH1`, and `ANLGSYN 1`; too aggressively and broke PRC, `Piano Bass`, `RES SYNTH1`, and `ANLGSYN 1`;
release duration remains part of the broader DX7 envelope model pressure. release duration remains part of the broader DX7 envelope model pressure.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
76 passed. 76 passed.
Previous slice: Previous slice:
@ -414,7 +414,7 @@ Previous slice:
and does not deserve to live. Mooger's "too hard" harmonics remain unresolved and does not deserve to live. Mooger's "too hard" harmonics remain unresolved
overtone-emphasis pressure, not a solved high-band-energy problem. overtone-emphasis pressure, not a solved high-band-energy problem.
- Focused verification with bundled Python/dexed-py: - Focused verification with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter "Dx7SysExTests|PublicDomainDx7MoogerAndPianoBassMeetRenderedParityWhenInstalled|PublicDomainDx7AnlgSyn1KeepsBuzzingModulationWhenInstalled|PublicDomainDx7MellowSoloWritesPressureWavsWhenInstalled|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled"`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter "Dx7SysExTests|PublicDomainDx7MoogerAndPianoBassMeetRenderedParityWhenInstalled|PublicDomainDx7AnlgSyn1KeepsBuzzingModulationWhenInstalled|PublicDomainDx7MellowSoloWritesPressureWavsWhenInstalled|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled"`:
28 passed. 28 passed.
Previous slice: Previous slice:
@ -436,7 +436,7 @@ Previous slice:
more Dexed-shaped on paper, but worsened `MELLOWSOLO` and `ANLGSYN 1`, so it more Dexed-shaped on paper, but worsened `MELLOWSOLO` and `ANLGSYN 1`, so it
does not belong in the live machine yet. does not belong in the live machine yet.
- Focused verification with bundled Python/dexed-py: - Focused verification with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter "Dx7SysExTests|PublicDomainDx7MoogerAndPianoBassMeetRenderedParityWhenInstalled|PublicDomainDx7AnlgSyn1KeepsBuzzingModulationWhenInstalled|PublicDomainDx7MellowSoloWritesPressureWavsWhenInstalled|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled"`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter "Dx7SysExTests|PublicDomainDx7MoogerAndPianoBassMeetRenderedParityWhenInstalled|PublicDomainDx7AnlgSyn1KeepsBuzzingModulationWhenInstalled|PublicDomainDx7MellowSoloWritesPressureWavsWhenInstalled|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled"`:
28 passed. 28 passed.
Previous slice: Previous slice:
@ -456,7 +456,7 @@ Previous slice:
`DX1 LEAD B` score `.45156077`, log-mel `.43533683`, RMS `.8193191`; `DX1 LEAD B` score `.45156077`, log-mel `.43533683`, RMS `.8193191`;
`MELLOWSOLO` score `.5359293`, log-mel `.39420125`, RMS `.99559265`. `MELLOWSOLO` score `.5359293`, log-mel `.39420125`, RMS `.99559265`.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
74 passed. 74 passed.
Previous slice: Previous slice:
@ -476,7 +476,7 @@ Previous slice:
- Fresh `ANLGSYN 1`: score `.7245976`, log-mel `.18460114`, envelope - Fresh `ANLGSYN 1`: score `.7245976`, log-mel `.18460114`, envelope
`.119950555`, RMS `.97143364`, zero-crossing `.97149533`. `.119950555`, RMS `.97143364`, zero-crossing `.97149533`.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
74 passed. 74 passed.
Earlier slice: Earlier slice:
@ -505,7 +505,7 @@ Earlier slice:
- 1.2-2.5 kHz candidate/reference energy: `.008 -> .656` - 1.2-2.5 kHz candidate/reference energy: `.008 -> .656`
- 2.5-5 kHz candidate/reference energy: near zero -> `.321` - 2.5-5 kHz candidate/reference energy: near zero -> `.321`
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
73 passed. 73 passed.
Earlier slice: Earlier slice:
@ -523,13 +523,13 @@ Earlier slice:
focused gates. Latest metrics: log-mel `.19427659`, envelope `.14924917`, focused gates. Latest metrics: log-mel `.19427659`, envelope `.14924917`,
zero-crossing `.94662774`, centroid `.9182808`, score `.659334`. zero-crossing `.94662774`, centroid `.9182808`, score `.659334`.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
73 passed. 73 passed.
Previous slice: Previous slice:
- Fixed the `Piano Bass` octave bug. The parsed voice has `transpose=12`; the - Fixed the `Piano Bass` octave bug. The parsed voice has `transpose=12`; the
old generated Aquarium script hardcoded `freq=261.6256`, so it rendered an old generated AquaSynth script hardcoded `freq=261.6256`, so it rendered an
octave too high. `Dx7SysEx.NoteFrequencyHz(midiNote, transpose)` now treats octave too high. `Dx7SysEx.NoteFrequencyHz(midiNote, transpose)` now treats
transpose `24` as neutral and transpose `12` as one octave down. transpose `24` as neutral and transpose `12` as one octave down.
- `Dx7VoiceProbeScript` now uses the voice transpose for graph frequency and - `Dx7VoiceProbeScript` now uses the voice transpose for graph frequency and
@ -540,14 +540,14 @@ Previous slice:
- Fixed `Piano Bass` metrics: log-mel `.16180529`, envelope `.13544041`, - Fixed `Piano Bass` metrics: log-mel `.16180529`, envelope `.13544041`,
zero-crossing `.905511`, score `.64267576`. zero-crossing `.905511`, score `.64267576`.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
71 passed. 71 passed.
Previous slice: Previous slice:
- Added `PublicDomainDx7AnalogCommunityVoicesMeetBroadRenderedParityWhenInstalled`, - Added `PublicDomainDx7AnalogCommunityVoicesMeetBroadRenderedParityWhenInstalled`,
which renders actual public-domain `analog1.syx` community voices through which renders actual public-domain `analog1.syx` community voices through
Dexed and the current Aquarium DX7 lowering. Kept four broad parity voices: Dexed and the current AquaSynth DX7 lowering. Kept four broad parity voices:
`ANLGSYN 1`, `{ Mooger }`, `Piano Bass`, and `RES SYNTH1`. `ANLGSYN 1`, `{ Mooger }`, `Piano Bass`, and `RES SYNTH1`.
- The test writes WAVs/reports under - The test writes WAVs/reports under
`artifacts/parity/dx7-community-analog1/<voice>/` and gates log-mel `artifacts/parity/dx7-community-analog1/<voice>/` and gates log-mel
@ -561,7 +561,7 @@ Previous slice:
from the passing gate because log-mel and zero-crossing mismatch were too from the passing gate because log-mel and zero-crossing mismatch were too
large. That is pressure, not library stock. large. That is pressure, not library stock.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
70 passed. 70 passed.
Previous slice: Previous slice:
@ -582,7 +582,7 @@ Previous slice:
intentionally not part of the patch library: they are useful measuring tools, intentionally not part of the patch library: they are useful measuring tools,
not pleasant or reusable stock. not pleasant or reusable stock.
- Verified with bundled Python/dexed-py: - Verified with bundled Python/dexed-py:
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`: `AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore`:
69 passed. 69 passed.
Previous slice: Previous slice:
@ -600,7 +600,7 @@ Previous slice:
- Added a test contract: every `.aqua` under `patches/` must parse through - Added a test contract: every `.aqua` under `patches/` must parse through
`PatchScript.Parse` and export Faust, while package-boundary tests keep `PatchScript.Parse` and export Faust, while package-boundary tests keep
`patches/` and `.aqua` files out of the NuGet package. `patches/` and `.aqua` files out of the NuGet package.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 67 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 67 passed.
Previous slice: Previous slice:
@ -614,7 +614,7 @@ Previous slice:
`0.9912135`, zero-crossing ratio `0.95325506`, centroid ratio `1.1156512`, `0.9912135`, zero-crossing ratio `0.95325506`, centroid ratio `1.1156512`,
score `0.7546445`. score `0.7546445`.
- Added `Dx7SysEx.ApproximateAppliedRateLevelEnvelope`, which traces the - Added `Dx7SysEx.ApproximateAppliedRateLevelEnvelope`, which traces the
Dexed-style block-interpolated applied EG gain and lowers it into Aquarium's Dexed-style block-interpolated applied EG gain and lowers it into AquaSynth's
curved staged envelope surface, normalized so operator output level remains a curved staged envelope surface, normalized so operator output level remains a
separate authority. separate authority.
- Retried hard `PRC SYNTH1` with the applied-envelope lowering. Latest focused - Retried hard `PRC SYNTH1` with the applied-envelope lowering. Latest focused
@ -638,8 +638,8 @@ Previous slice:
`artifacts/parity/dx7-envelope-trace/egstep.csv`. The first rows show both `artifacts/parity/dx7-envelope-trace/egstep.csv`. The first rows show both
DX7 raw state and applied gain: raw jumps to `2` immediately, while applied DX7 raw state and applied gain: raw jumps to `2` immediately, while applied
gain ramps from `.03125` to `2` over the first 64-sample block and remains gain ramps from `.03125` to `2` over the first 64-sample block and remains
near `1` around 20 ms. Aquarium `env=rl` is a different contour entirely. near `1` around 20 ms. AquaSynth `env=rl` is a different contour entirely.
The artifact now also includes a curved Aquarium staged-envelope candidate The artifact now also includes a curved AquaSynth staged-envelope candidate
that tracks the applied-gain contour far more closely. that tracks the applied-gain contour far more closely.
- Tried adding a first `env=dx7` runtime lowering, then cut it. The trace - Tried adding a first `env=dx7` runtime lowering, then cut it. The trace
matched the Python `graph.py` envelope but not Dexed plugin audio, so the matched the Python `graph.py` envelope but not Dexed plugin audio, so the
@ -690,7 +690,7 @@ Previous slice:
Previous slice: Previous slice:
- Completed the feedback calibration rung. Isolated Dexed feedback sweeps fit a - Completed the feedback calibration rung. Isolated Dexed feedback sweeps fit a
nonlinear Aquarium feedback table: `0, .01, .02, .05, .10, .19, .38, .66` nonlinear AquaSynth feedback table: `0, .01, .02, .05, .10, .19, .38, .66`
for DX7 feedback values `0..7`. for DX7 feedback values `0..7`.
- Added `Dx7SysEx.OperatorFeedbackAmount` and moved the PRC feedback lowering - Added `Dx7SysEx.OperatorFeedbackAmount` and moved the PRC feedback lowering
off the old `voice.Feedback * 0.04` constant. PRC barely changed because off the old `voice.Feedback * 0.04` constant. PRC barely changed because
@ -701,7 +701,7 @@ Previous slice:
- Completed the second calibration rung for isolated two-operator FM. A Dexed - Completed the second calibration rung for isolated two-operator FM. A Dexed
sweep fits full-scale DX7 modulation at about `12.55` radians. Given the sweep fits full-scale DX7 modulation at about `12.55` radians. Given the
current Aquarium Faust formula, that maps to an Aquarium route index of current AquaSynth Faust formula, that maps to an AquaSynth route index of
`6.275` for a full-level modulator. `6.275` for a full-level modulator.
- Added `Dx7SysEx.OperatorModulationRouteIndex` and a regression test for the - Added `Dx7SysEx.OperatorModulationRouteIndex` and a regression test for the
calibrated phase-deviation scale. The hard PRC probe applies that scale only calibrated phase-deviation scale. The hard PRC probe applies that scale only
@ -737,7 +737,7 @@ Previous slice:
`<= 0.255` plus a loose aggregate score floor instead of treating aggregate `<= 0.255` plus a loose aggregate score floor instead of treating aggregate
score as the main judge. score as the main judge.
- Spectral diagnosis showed the Dexed reference has dominant peaks around - Spectral diagnosis showed the Dexed reference has dominant peaks around
`393`, `131`, `524`, and `656` Hz. The prior Aquarium candidate had too `393`, `131`, `524`, and `656` Hz. The prior AquaSynth candidate had too
little `80-160` Hz body when based at `392`, while the corrected DX7 note little `80-160` Hz body when based at `392`, while the corrected DX7 note
basis restored the low body but exposed weak mid harmonic structure and high basis restored the low body but exposed weak mid harmonic structure and high
centroid/zero-crossing mismatch. centroid/zero-crossing mismatch.
@ -769,7 +769,7 @@ Previous slice:
- Added a hard DX7 `PRC SYNTH1` rendered parity probe that writes listening - Added a hard DX7 `PRC SYNTH1` rendered parity probe that writes listening
artifacts when `dexed-py` and Faust are available: artifacts when `dexed-py` and Faust are available:
`artifacts/parity/dx7-prc-synth1/reference-dexed.wav`, `artifacts/parity/dx7-prc-synth1/reference-dexed.wav`,
`candidate-aquarium.wav`, `candidate.aqua`, and `report.txt`. `candidate-aquasynth.wav`, `candidate.aqua`, and `report.txt`.
- The probe rebuilds `analog1.syx` voice 17 with readable operator graph syntax - The probe rebuilds `analog1.syx` voice 17 with readable operator graph syntax
and staged `env=rl` operator envelopes. It asserts a modest hard-target score and staged `env=rl` operator envelopes. It asserts a modest hard-target score
floor instead of pretending exact DX7 parity is solved. floor instead of pretending exact DX7 parity is solved.
@ -792,7 +792,7 @@ Previous slice:
that emits staged envelope script specs from four-rate/four-level operator EG that emits staged envelope script specs from four-rate/four-level operator EG
data. data.
- Verified the surface structurally and through the render path: - Verified the surface structurally and through the render path:
`dotnet test AquariumSynthCSharp.slnx --no-restore`: 53 passed. `dotnet test AquaSynth.slnx --no-restore`: 53 passed.
- Retried the hard `PRC SYNTH1` probe with direct DX7 rate/level lowering. The - Retried the hard `PRC SYNTH1` probe with direct DX7 rate/level lowering. The
best quick score was only ~0.316, worse than the hand-tweaked ADSR candidate. best quick score was only ~0.316, worse than the hand-tweaked ADSR candidate.
The DSL can now express the missing contour shape, but DX7 EG timing/gain The DSL can now express the missing contour shape, but DX7 EG timing/gain
@ -801,33 +801,33 @@ Previous slice:
Previous slice: Previous slice:
- Added `Dx7OperatorLevelApproximation`, which distills DX7 operator output - Added `Dx7OperatorLevelApproximation`, which distills DX7 operator output
level plus key/velocity scaling into a normal Aquarium `level=`-style value. level plus key/velocity scaling into a normal AquaSynth `level=`-style value.
This remains DX7 reference-import knowledge, not a generic DSL feature. This remains DX7 reference-import knowledge, not a generic DSL feature.
- Retried `PRC SYNTH1` with effective levels and envelope tweaks. Static level - Retried `PRC SYNTH1` with effective levels and envelope tweaks. Static level
mapping alone did not rescue the hard target: quick probes moved from ~0.24 mapping alone did not rescue the hard target: quick probes moved from ~0.24
to ~0.34, and envelope tweaks reached ~0.42. The next missing pressure is to ~0.34, and envelope tweaks reached ~0.42. The next missing pressure is
DX7-style operator envelope/gain evolution, not just static operator levels. DX7-style operator envelope/gain evolution, not just static operator levels.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 50 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 50 passed.
Previous slice: Previous slice:
- Tried the harder `analog1.syx` voice 17, `PRC SYNTH1` (algorithm 8). The - Tried the harder `analog1.syx` voice 17, `PRC SYNTH1` (algorithm 8). The
initial routed Aquarium candidate scored badly (~0.23), which exposed a real initial routed AquaSynth candidate scored badly (~0.23), which exposed a real
missing invariant rather than a threshold-tuning problem. missing invariant rather than a threshold-tuning problem.
- Reworked operator feedback emission from a cyclic smoothed self-reference to - Reworked operator feedback emission from a cyclic smoothed self-reference to
a renderable Faust feedback expression using delayed recursion. Added a renderable Faust feedback expression using delayed recursion. Added
`FaustCompilerRendersOperatorFeedbackWhenInstalled`. `FaustCompilerRendersOperatorFeedbackWhenInstalled`.
- Exact DX7 feedback scaling and EG behavior are still not solved, but feedback - Exact DX7 feedback scaling and EG behavior are still not solved, but feedback
no longer makes the Faust render path fall over. no longer makes the Faust render path fall over.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 49 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 49 passed.
Previous slice: Previous slice:
- Added the first thresholded DX7 rendered-audio parity rebuild: - Added the first thresholded DX7 rendered-audio parity rebuild:
`BuiltInScripts.Dx7StylePublicDomainMcMm53` maps public-domain `BuiltInScripts.Dx7StylePublicDomainMcMm53` maps public-domain
`analog1.syx` voice 13, `MC-MM 5-3`, to a terse Aquarium sine patch. `analog1.syx` voice 13, `MC-MM 5-3`, to a terse AquaSynth sine patch.
- Added `PublicDomainDx7McMm53MeetsFirstRenderedParityThresholdWhenInstalled`, - Added `PublicDomainDx7McMm53MeetsFirstRenderedParityThresholdWhenInstalled`,
which renders the DX7 reference through `dexed-py`, renders the Aquarium which renders the DX7 reference through `dexed-py`, renders the AquaSynth
candidate through Faust-generated C#, and asserts score, log-mel distance, candidate through Faust-generated C#, and asserts score, log-mel distance,
envelope distance, duration/RMS ratios, zero-crossing ratio, and centroid envelope distance, duration/RMS ratios, zero-crossing ratio, and centroid
ratio. ratio.
@ -835,43 +835,43 @@ Previous slice:
DX7 operator execution. The next DX7 pressure should be a harder voice that DX7 operator execution. The next DX7 pressure should be a harder voice that
audibly needs the operator graph. audibly needs the operator graph.
- Verified with bundled Python plus `dexed-py`: parity test passed. - Verified with bundled Python plus `dexed-py`: parity test passed.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 48 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 48 passed.
Previous slice: Previous slice:
- Added a public-domain DX7 SysEx fixture from Musical Artifacts artifact 152: - Added a public-domain DX7 SysEx fixture from Musical Artifacts artifact 152:
`tests/AquariumSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain/analog1.syx`, with `tests/AquaSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain/analog1.syx`, with
provenance and SHA-256 recorded beside it. provenance and SHA-256 recorded beside it.
- Added a test-only `dexed-py` reference renderer. It uses - Added a test-only `dexed-py` reference renderer. It uses
`AQUARIUM_DX7_PYTHON` when set, otherwise probes `py`, `python`, and `AQUASYNTH_DX7_PYTHON` when set, otherwise probes `py`, `python`, and
`python3`; if `dexed-py` is absent, the render test returns without turning `python3`; if `dexed-py` is absent, the render test returns without turning
optional tooling into a hard dependency. optional tooling into a hard dependency.
- Added a `.nupkg` boundary test that packs `AquariumSynth.Dsl` and asserts - Added a `.nupkg` boundary test that packs `AquaSynth.Dsl` and asserts
test fixtures, SysEx banks, and Python helpers are not shipped. test fixtures, SysEx banks, and Python helpers are not shipped.
- Verified with bundled Python plus `dexed-py`: the DX7 fixture renders through - Verified with bundled Python plus `dexed-py`: the DX7 fixture renders through
Dexed successfully. Dexed successfully.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 47 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 47 passed.
Previous slice: Previous slice:
- Added `FaustCompiler.RenderAsync`, which compiles Aquarium Faust output to - Added `FaustCompiler.RenderAsync`, which compiles AquaSynth Faust output to
Faust-generated C#, runs it in a temp .NET project, and returns a mono float Faust-generated C#, runs it in a temp .NET project, and returns a mono float
sample buffer for analysis. sample buffer for analysis.
- Added a render test that proves a generated Aquarium patch produces non-silent - Added a render test that proves a generated AquaSynth patch produces non-silent
audio and can be compared through `AudioAnalyzer`. audio and can be compared through `AudioAnalyzer`.
- DX7 audio parity is still not claimed: the candidate renderer exists, but the - DX7 audio parity is still not claimed: the candidate renderer exists, but the
reference side needs Dexed output or a captured licensed fixture. reference side needs Dexed output or a captured licensed fixture.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 44 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 44 passed.
Previous slice: Previous slice:
- Added `Dx7EnvelopeApproximation`, which lowers a DX7 four-rate/four-level EG - Added `Dx7EnvelopeApproximation`, which lowers a DX7 four-rate/four-level EG
to a labeled Aquarium ADSR approximation plus gate duration. to a labeled AquaSynth ADSR approximation plus gate duration.
- Reference rebuilds now record `operator_envelope_approximation` as matched - Reference rebuilds now record `operator_envelope_approximation` as matched
pressure and `operator_envelope_exactness` as still missing. pressure and `operator_envelope_exactness` as still missing.
- Doctrine now states that DX7 EG approximation is not exact DX7 envelope - Doctrine now states that DX7 EG approximation is not exact DX7 envelope
execution. execution.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 43 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 43 passed.
Previous slice: Previous slice:
@ -882,7 +882,7 @@ Previous slice:
the exact `/opgraphs/...` field paths. the exact `/opgraphs/...` field paths.
- Fixed AD operator envelope binding to use `/env/decay` for the second AD - Fixed AD operator envelope binding to use `/env/decay` for the second AD
value, matching the ADSR model instead of calling it release by accident. value, matching the ADSR model instead of calling it release by accident.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 42 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 42 passed.
Previous slice: Previous slice:
@ -892,7 +892,7 @@ Previous slice:
`[midi:on][nvoices:8]` option and `freq`, `gain`, `gate` controls. `[midi:on][nvoices:8]` option and `freq`, `gain`, `gate` controls.
- Host/MIDI playback no longer emits per-voice `/voices/0/note/frequency` and - Host/MIDI playback no longer emits per-voice `/voices/0/note/frequency` and
`/voices/0/note/gate` controls; Faust architecture owns allocation. `/voices/0/note/gate` controls; Faust architecture owns allocation.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 41 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 41 passed.
Previous slice: Previous slice:
@ -905,7 +905,7 @@ Previous slice:
- Added host note mode for MIDI-oriented patches through stable note frequency - Added host note mode for MIDI-oriented patches through stable note frequency
and note gate controls in generated Faust. and note gate controls in generated Faust.
- Moved built-in authoring examples off `punch=` and onto `sustain_level=`. - Moved built-in authoring examples off `punch=` and onto `sustain_level=`.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 40 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 40 passed.
Previous slice: Previous slice:
@ -915,7 +915,7 @@ Previous slice:
envelope forms for operator declarations. envelope forms for operator declarations.
- Kept compact `ops=`/`edges=` syntax as parser/interchange scaffolding, but it - Kept compact `ops=`/`edges=` syntax as parser/interchange scaffolding, but it
is no longer the built-in authoring example. is no longer the built-in authoring example.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 36 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 36 passed.
Previous slice: Previous slice:
@ -927,21 +927,21 @@ Previous slice:
- Updated the DX7 algorithm-8 rebuild to use a real operator graph for the - Updated the DX7 algorithm-8 rebuild to use a real operator graph for the
topology. The remaining missing feature is exact DX7 feedback-register timing topology. The remaining missing feature is exact DX7 feedback-register timing
and DX7 rate/level envelopes, not graph ownership. and DX7 rate/level envelopes, not graph ownership.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 35 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 35 passed.
Earlier rebuild slice: Earlier rebuild slice:
- Added `ReferenceRebuild` and `ReferenceRebuildCatalog` for explicit - Added `ReferenceRebuild` and `ReferenceRebuildCatalog` for explicit
reference-target rebuild attempts. reference-target rebuild attempts.
- Added two DX7-style Aquarium DSL rebuilds: - Added two DX7-style AquaSynth DSL rebuilds:
- `dx7/algo32-additive-organ`: algorithm 32's six-carrier additive shape, - `dx7/algo32-additive-organ`: algorithm 32's six-carrier additive shape,
which maps cleanly to six Aquarium voices. which maps cleanly to six AquaSynth voices.
- `dx7/algo8-bright-pair`: algorithm 8's two-carrier FM shape, which is only - `dx7/algo8-bright-pair`: algorithm 8's two-carrier FM shape, which is only
an approximation because the current voice DSL cannot express an approximation because the current voice DSL cannot express
`6->5`, `4+5->3`, `2->1`, or operator-local self-feedback. `6->5`, `4+5->3`, `2->1`, or operator-local self-feedback.
- Added matched/missing feature records so topology mismatch becomes evidence - Added matched/missing feature records so topology mismatch becomes evidence
instead of being hidden inside prose. instead of being hidden inside prose.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 34 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 34 passed.
Earlier topology slice: Earlier topology slice:
@ -954,7 +954,7 @@ Earlier topology slice:
`modulation_edge_count`, `feedback_sources`, and `modulation_edge_count`, `feedback_sources`, and
`self_feedback_operators`. `self_feedback_operators`.
- Representative tests cover algorithm 8, algorithm 16, and algorithm 32. - Representative tests cover algorithm 8, algorithm 16, and algorithm 32.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 31 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 31 passed.
Earlier slice: Earlier slice:
@ -965,7 +965,7 @@ Earlier slice:
- Added `Dx7Voice.ToReferencePatch` and structural feature extraction so DX7 - Added `Dx7Voice.ToReferencePatch` and structural feature extraction so DX7
voices can pressure the reference model before translation exists. voices can pressure the reference model before translation exists.
- Checksum validation rejects bad wrapped SysEx payloads. - Checksum validation rejects bad wrapped SysEx payloads.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 28 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 28 passed.
Parameter slice: Parameter slice:
@ -982,7 +982,7 @@ Parameter slice:
parameter default as the graph value. parameter default as the graph value.
- Faust emission substitutes the parameter expression only at the bound field - Faust emission substitutes the parameter expression only at the bound field
site. Unbound parameters still emit a warning. site. Unbound parameters still emit a warning.
- Verified with `dotnet test AquariumSynthCSharp.slnx --no-restore`: 23 passed. - Verified with `dotnet test AquaSynth.slnx --no-restore`: 23 passed.
Next likely slice: Next likely slice:

View file

@ -1,4 +1,4 @@
project: AquariumSynthCSharp project: AquaSynth
spine_version: 1 spine_version: 1
updated: 2026-05-15T14:15:00+01:00 updated: 2026-05-15T14:15:00+01:00
@ -7,58 +7,58 @@ prime_directive: >
not to justify piling parameters onto the current patch DSL. not to justify piling parameters onto the current patch DSL.
current_objective: > current_objective: >
Turn AquariumSynthCSharp into a reference-driven patch authoring and Faust Turn AquaSynth into a reference-driven patch authoring and Faust
generation library. Use external synths and DSP corpora as pressure tests so generation library. Use external synths and DSP corpora as pressure tests so
Aquarium's DSL grows only when a real target proves the abstraction belongs. AquaSynth's DSL grows only when a real target proves the abstraction belongs.
current_mechanism: current_mechanism:
parser: src/AquariumSynth.Dsl/PatchScript.cs parser: src/AquaSynth.Dsl/PatchScript.cs
model: src/AquariumSynth.Dsl/Model.cs model: src/AquaSynth.Dsl/Model.cs
faust_emitter: src/AquariumSynth.Dsl/FaustExport.cs faust_emitter: src/AquaSynth.Dsl/FaustExport.cs
faust_renderer: src/AquariumSynth.Dsl/FaustExport.cs faust_renderer: src/AquaSynth.Dsl/FaustExport.cs
operator_graph_model: src/AquariumSynth.Dsl/Model.cs operator_graph_model: src/AquaSynth.Dsl/Model.cs
analysis: src/AquariumSynth.Dsl/AudioAnalysis.cs analysis: src/AquaSynth.Dsl/AudioAnalysis.cs
built_ins: src/AquariumSynth.Dsl/BuiltInScripts.cs built_ins: src/AquaSynth.Dsl/BuiltInScripts.cs
patch_library: patches patch_library: patches
dx7_references: src/AquariumSynth.Dsl/Dx7SysEx.cs dx7_references: src/AquaSynth.Dsl/Dx7SysEx.cs
dx7_reference_fixtures: tests/AquariumSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain dx7_reference_fixtures: tests/AquaSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain
dx7_reference_renderer_tests: tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs dx7_reference_renderer_tests: tests/AquaSynth.Dsl.Tests/Dx7ReferenceParityTests.cs
zyn_reference_source: external/zynaddsubfx zyn_reference_source: external/zynaddsubfx
zyn_upstream_instrument_bank: external/zynaddsubfx/instruments zyn_upstream_instrument_bank: external/zynaddsubfx/instruments
zyn_reference_build_script: tools/zyn-reference/build-zyn-reference.ps1 zyn_reference_build_script: tools/zyn-reference/build-zyn-reference.ps1
zyn_pad_reference_renderer: tools/zyn-reference/ZynPadReference.cpp zyn_pad_reference_renderer: tools/zyn-reference/ZynPadReference.cpp
zyn_reference_reader: src/AquariumSynth.Dsl/ZynInstrument.cs zyn_reference_reader: src/AquaSynth.Dsl/ZynInstrument.cs
zyn_project_fixtures: tests/AquariumSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored zyn_project_fixtures: tests/AquaSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored
reference_rebuilds: src/AquariumSynth.Dsl/ReferenceRebuilds.cs reference_rebuilds: src/AquaSynth.Dsl/ReferenceRebuilds.cs
tests: tests/AquariumSynth.Dsl.Tests/PatchScriptTests.cs tests: tests/AquaSynth.Dsl.Tests/PatchScriptTests.cs
doctrine: docs/audio-doctrine.md doctrine: docs/audio-doctrine.md
dx7_calibration_plan: docs/dx7-calibration-plan.md dx7_calibration_plan: docs/dx7-calibration-plan.md
zyn_pressure_survey: docs/zynaddsubfx-pressure-survey.md zyn_pressure_survey: docs/zynaddsubfx-pressure-survey.md
roadmap: docs/reference-synth-roadmap.md roadmap: docs/reference-synth-roadmap.md
parameter_contracts: src/AquariumSynth.Dsl/Model.cs parameter_contracts: src/AquaSynth.Dsl/Model.cs
live_invariants: live_invariants:
- Aquarium owns its patch graph; external synths are references, not runtime dependencies. - AquaSynth owns its patch graph; external synths are references, not runtime dependencies.
- Patch scripts lower into explicit graph records before Faust emission. - Patch scripts lower into explicit graph records before Faust emission.
- Operator graphs own FM topology: operators, modulation edges, carriers, and feedback. Do not hide this authority inside Voice fields. - Operator graphs own FM topology: operators, modulation edges, carriers, and feedback. Do not hide this authority inside Voice fields.
- Readable operator graph authoring uses `operator`, `route`, and `carrier` declarations; compact `ops=`/`edges=` is only scaffolding/interchange. - Readable operator graph authoring uses `operator`, `route`, and `carrier` declarations; compact `ops=`/`edges=` is only scaffolding/interchange.
- Notes own frequency, gate duration, and host/MIDI source; envelopes own ADSR level shape. Do not put note duration or SFXR punch back into the general envelope model. - Notes own frequency, gate duration, and host/MIDI source; envelopes own ADSR level shape. Do not put note duration or SFXR punch back into the general envelope model.
- Faust-managed MIDI polyphony is preferred: emit one voice graph with standard `freq`, `gain`, and `gate` controls plus `[midi:on][nvoices:n]` instead of inflating Aquarium graphs for local allocation. - Faust-managed MIDI polyphony is preferred: emit one voice graph with standard `freq`, `gain`, and `gate` controls plus `[midi:on][nvoices:n]` instead of inflating AquaSynth graphs for local allocation.
- Generated Faust should remain inspectable and deterministic. - Generated Faust should remain inspectable and deterministic.
- Aquarium Faust output may be rendered through Faust-generated C# for candidate audio comparison; reference parity still needs a real external render or captured licensed fixture. - AquaSynth Faust output may be rendered through Faust-generated C# for candidate audio comparison; reference parity still needs a real external render or captured licensed fixture.
- Reference-patch correctness and synth parity tests live in AquariumSynthCSharp, not Aquarium. - Reference-patch correctness and synth parity tests live in AquaSynth, not AquaSynth.
- Exposed patch parameters are runtime controls, not compile-time constants. - Exposed patch parameters are runtime controls, not compile-time constants.
- Parameter binding uses field-site references like `lpf=@/macro/brightness`; do not add target-routing semantics unless a later map proves a new authority is needed. - Parameter binding uses field-site references like `lpf=@/macro/brightness`; do not add target-routing semantics unless a later map proves a new authority is needed.
- Operator graph parameter binding also uses exact field-site paths under `/opgraphs/...`; do not add graph-wide macro routing. - Operator graph parameter binding also uses exact field-site paths under `/opgraphs/...`; do not add graph-wide macro routing.
- Operator feedback must render through Faust as delayed recursion, not as a direct cyclic self-reference. - Operator feedback must render through Faust as delayed recursion, not as a direct cyclic self-reference.
- DX7 operator output level approximation belongs in reference import/rebuild helpers, not the generic DSL model. - DX7 operator output level approximation belongs in reference import/rebuild helpers, not the generic DSL model.
- DX7 carrier output amplitude follows the measured `2^((outputLevel - 99) / 8)` curve; do not restore the old internal-level proxy. - DX7 carrier output amplitude follows the measured `2^((outputLevel - 99) / 8)` curve; do not restore the old internal-level proxy.
- DX7 isolated two-operator modulation uses Aquarium route index `6.275` for full-level modulators; do not apply that blindly to cascaded stacks. - DX7 isolated two-operator modulation uses AquaSynth route index `6.275` for full-level modulators; do not apply that blindly to cascaded stacks.
- DX7 feedback lowers through `Dx7SysEx.OperatorFeedbackAmount`; feedback value 5 remains 0.19 for PRC, while feedback value 7 is much hotter at 2.2 to restore community-patch buzz. Do not restore PRC-local `feedback * constant` folklore. - DX7 feedback lowers through `Dx7SysEx.OperatorFeedbackAmount`; feedback value 5 remains 0.19 for PRC, while feedback value 7 is much hotter at 2.2 to restore community-patch buzz. Do not restore PRC-local `feedback * constant` folklore.
- DX7 ratio-mode detune lowers through `Dx7SysEx.OperatorFrequencyRatio`; do not silently drop detune from operator graph scripts. - DX7 ratio-mode detune lowers through `Dx7SysEx.OperatorFrequencyRatio`; do not silently drop detune from operator graph scripts.
- DX7 voice transpose lowers through `Dx7SysEx.NoteFrequencyHz`; transpose 24 is neutral, 12 is one octave down. Do not hardcode `freq=261.6256` for imported voices. - DX7 voice transpose lowers through `Dx7SysEx.NoteFrequencyHz`; transpose 24 is neutral, 12 is one octave down. Do not hardcode `freq=261.6256` for imported voices.
- DX7 fixed-frequency operators lower through `Dx7SysEx.FixedOperatorFrequencyHz` as absolute Hz divided by the graph base frequency; do not treat fixed mode as a note ratio. - DX7 fixed-frequency operators lower through `Dx7SysEx.FixedOperatorFrequencyHz` as absolute Hz divided by the graph base frequency; do not treat fixed mode as a note ratio.
- DX7 ROM `COM` output compensation is not a carrier-balance authority in Aquarium lowering; `OperatorOutputCompensation` returns unity and graph gain owns loudness recovery. - DX7 ROM `COM` output compensation is not a carrier-balance authority in AquaSynth lowering; `OperatorOutputCompensation` returns unity and graph gain owns loudness recovery.
- DX7 operator key-scaling and rate-scaling lower against the effective played MIDI note after voice transpose; do not hardcode MIDI 60 inside imported operator level or envelope timing. - DX7 operator key-scaling and rate-scaling lower against the effective played MIDI note after voice transpose; do not hardcode MIDI 60 inside imported operator level or envelope timing.
- DX7 route lowering is topology-aware: isolated direct branches use `OperatorModulationRouteIndex` (`6.275`); summed edges and direct edges feeding summed modulation use `SummedOperatorModulationRouteIndex` (`6.0`). - DX7 route lowering is topology-aware: isolated direct branches use `OperatorModulationRouteIndex` (`6.275`); summed edges and direct edges feeding summed modulation use `SummedOperatorModulationRouteIndex` (`6.0`).
- `SummedOperatorModulationRouteIndex` is backed by the hard PRC target, a project-authored algorithm-8 stack probe, and split cascade/summed-pair probes; do not restore the older under-bright `1.6` scale. - `SummedOperatorModulationRouteIndex` is backed by the hard PRC target, a project-authored algorithm-8 stack probe, and split cascade/summed-pair probes; do not restore the older under-bright `1.6` scale.
@ -75,15 +75,15 @@ live_invariants:
- Do not replace applied-envelope release with a naive traced threshold duration; it shortened releases too aggressively and broke broader DX7 parity. - Do not replace applied-envelope release with a naive traced threshold duration; it shortened releases too aggressively and broke broader DX7 parity.
- Recompile only for graph shape changes; vary declared parameters through the hosted DSP control API. - Recompile only for graph shape changes; vary declared parameters through the hosted DSP control API.
- Reference fixtures must preserve provenance, license status, source hash, and feature pressure. - Reference fixtures must preserve provenance, license status, source hash, and feature pressure.
- Public-domain reference fixtures, GPL Zyn instrument-bank fixtures, dexed-py helpers, and pinned ZynAddSubFX source are test/development material only; the NuGet package must ship only AquariumSynth.Dsl. - Public-domain reference fixtures, GPL Zyn instrument-bank fixtures, dexed-py helpers, and pinned ZynAddSubFX source are test/development material only; the NuGet package must ship only AquaSynth.Dsl.
- ZynAddSubFX import starts as feature extraction and then explicit, scoped rebuilds. Upstream GPL `.xiz` instruments may be used as test-only pressure/parity feedstock with provenance reports, while project-authored `.xiz` fixtures remain the clean baseline. The Zyn engine source and instrument bank are pinned under `external/zynaddsubfx` for parity oracles only, not Aquarium runtime code. - ZynAddSubFX import starts as feature extraction and then explicit, scoped rebuilds. Upstream GPL `.xiz` instruments may be used as test-only pressure/parity feedstock with provenance reports, while project-authored `.xiz` fixtures remain the clean baseline. The Zyn engine source and instrument bank are pinned under `external/zynaddsubfx` for parity oracles only, not AquaSynth runtime code.
- Named `layer` declarations own Zyn-style kit/layer identity: engine tag, optional key range metadata, default gain, and effect-send labels. They lower to ordinary voices for now; do not smuggle PAD/free-envelope/effect behavior into them. - Named `layer` declarations own Zyn-style kit/layer identity: engine tag, optional key range metadata, default gain, and effect-send labels. They lower to ordinary voices for now; do not smuggle PAD/free-envelope/effect behavior into them.
- Additive `harmonics` declarations own authored ratio/gain partial banks under named layers. They lower to ordinary voices for now; do not pretend this covers Zyn ADDsynth phase, bandwidth, oscillator shaping, or free-envelope semantics. - Additive `harmonics` declarations own authored ratio/gain partial banks under named layers. They lower to ordinary voices for now; do not pretend this covers Zyn ADDsynth phase, bandwidth, oscillator shaping, or free-envelope semantics.
- Normal voices may use staged `env=rl` rate/level envelopes. Notes still own gate timing; envelopes own level shape. This is not arbitrary Zyn free-envelope point editing. - Normal voices may use staged `env=rl` rate/level envelopes. Notes still own gate timing; envelopes own level shape. This is not arbitrary Zyn free-envelope point editing.
- PAD `spectrum` declarations own authored partial clouds under named layers and split table root from playback pitch: `root` is source/base frequency, while `freq` is the note played by the spectral voice. They generate PADsynth-style FFT wavetables with frequency-domain spreading and deterministic random phase, then emit Faust table readers before ordinary voice treatment. Richer PAD table shaping uses neutral `pad_mode`, `pad_bandwidth`, `pad_bwscale`, `pad_profile`, and `pad_position` syntax. The parser may accept older `zyn_*` parity-artifact aliases, but generated/authored `.aqua` should not expose reference-synth names for general DSP intent. Do not pretend this covers every Zyn OscilGen modulation/waveshaping/filter/adaptive-harmonic path yet. - PAD `spectrum` declarations own authored partial clouds under named layers and split table root from playback pitch: `root` is source/base frequency, while `freq` is the note played by the spectral voice. They generate PADsynth-style FFT wavetables with frequency-domain spreading and deterministic random phase, then emit Faust table readers before ordinary voice treatment. Richer PAD table shaping uses neutral `pad_mode`, `pad_bandwidth`, `pad_bwscale`, `pad_profile`, and `pad_position` syntax. The parser may accept older `zyn_*` parity-artifact aliases, but generated/authored `.aqua` should not expose reference-synth names for general DSP intent. Do not pretend this covers every Zyn OscilGen modulation/waveshaping/filter/adaptive-harmonic path yet.
- Zyn OscilGen oscillator filters use one-based harmonic indices: first harmonic is `1`, not `0`. Preserve that index when translating filter types or spectral peaks move to the wrong partial. - Zyn OscilGen oscillator filters use one-based harmonic indices: first harmonic is `1`, not `0`. Preserve that index when translating filter types or spectral peaks move to the wrong partial.
- Zyn PAD analog high-pass filters are filter authority too: HP1/HP2 type, stages, and FILTER_ENVELOPE motion lower through `hpf_order` and `hpf_env=rl`, not ad hoc layer gain or brightness compensation. - Zyn PAD analog high-pass filters are filter authority too: HP1/HP2 type, stages, and FILTER_ENVELOPE motion lower through `hpf_order` and `hpf_env=rl`, not ad hoc layer gain or brightness compensation.
- Zyn PAD selected table roots are not a global default in Aquarium lowering. Keep sample-0 roots unless a source rule earns the selected root; currently OscilGen shift-base sources (`base_function=7`) may use the selected note table root because Organ Choir proved sample-0 roots leave the octave voice misaligned. - Zyn PAD selected table roots are not a global default in AquaSynth lowering. Keep sample-0 roots unless a source rule earns the selected root; currently OscilGen shift-base sources (`base_function=7`) may use the selected note table root because Organ Choir proved sample-0 roots leave the octave voice misaligned.
- Zyn OscilGen base-function modulation is source-table authority: apply it as phase modulation before base-function sampling, not as later oscillator modulation or filter/EQ compensation. - Zyn OscilGen base-function modulation is source-table authority: apply it as phase modulation before base-function sampling, not as later oscillator modulation or filter/EQ compensation.
- Zyn adaptive harmonics remap the prepared oscillator spectrum at the PAD table root frequency before PAD bandwidth/profile spreading. Do not skip it for sources whose wind/body character depends on sparse remapped harmonics. - Zyn adaptive harmonics remap the prepared oscillator spectrum at the PAD table root frequency before PAD bandwidth/profile spreading. Do not skip it for sources whose wind/body character depends on sparse remapped harmonics.
- Filter damping is first-class: `lpf_q`/`lpq` owns explicit low-pass Q when a reference synth exposes analog filter damping. Low-pass order remains slope authority even when Q is present; Faust lowering cascades resonant low-pass stages instead of replacing order with one generic resonant filter. Do not fake Q by secretly moving cutoff, slope, or layer gain. - Filter damping is first-class: `lpf_q`/`lpq` owns explicit low-pass Q when a reference synth exposes analog filter damping. Low-pass order remains slope authority even when Q is present; Faust lowering cascades resonant low-pass stages instead of replacing order with one generic resonant filter. Do not fake Q by secretly moving cutoff, slope, or layer gain.
@ -91,7 +91,7 @@ live_invariants:
- 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. - 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. - 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. - 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. - Do not consume external synth targets that cannot be backed by parity tests. Parsing is inventory; reproducing behavior through terse, readable AquaSynth DSL is the proof.
- Audio metrics are evidence, not final judgment. - Audio metrics are evidence, not final judgment.
- Do not add a generic graph escape hatch until specific references force it. - Do not add a generic graph escape hatch until specific references force it.
- Do not hide missing graph structure behind unrelated voice fields. - Do not hide missing graph structure behind unrelated voice fields.
@ -101,14 +101,14 @@ reference_targets:
priority: 1 priority: 1
purpose: "Structured FM/operator graph pressure test." purpose: "Structured FM/operator graph pressure test."
source: "https://github.com/asb2m10/dexed" source: "https://github.com/asb2m10/dexed"
status: "DX7 SysEx parser, public-domain SysEx fixture, dexed-py reference rendering, project-authored Dexed patch rendering, first thresholded rendered parity test for analog1.syx voice 13, hard PRC SYNTH1 probe with listening WAV artifacts, project-authored algorithm-8 summed-stack parity, renderable delayed operator feedback, DX7 effective operator-level approximation, measured carrier output amplitude, isolated route index, feedback table, ratio detune lowering, voice transpose lowering, fixed-frequency lowering, ROM COM compensation cut, operator key/rate scaling at the effective played note, staged operator envelopes, DX7 envelope tracing, 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, project-authored Dexed patch rendering, first thresholded rendered parity test for analog1.syx voice 13, hard PRC SYNTH1 probe with listening WAV artifacts, project-authored algorithm-8 summed-stack parity, renderable delayed operator feedback, DX7 effective operator-level approximation, measured carrier output amplitude, isolated route index, feedback table, ratio detune lowering, voice transpose lowering, fixed-frequency lowering, ROM COM compensation cut, operator key/rate scaling at the effective played note, staged operator envelopes, DX7 envelope tracing, algorithm topology extraction, envelope-to-ADSR approximation, two AquaSynth DSL rebuild attempts, first-class operator graph support, and AquaSynth candidate rendering through Faust-generated C# exist."
first_slice: first_slice:
- "Add a harder public-domain DX7 parity target that audibly needs operator routing." - "Add a harder public-domain DX7 parity target that audibly needs operator routing."
zynaddsubfx: zynaddsubfx:
priority: 2 priority: 2
purpose: "Additive, subtractive, PAD, formant, kit, and layered-instrument pressure test." purpose: "Additive, subtractive, PAD, formant, kit, and layered-instrument pressure test."
source: "https://github.com/zynaddsubfx/zynaddsubfx" source: "https://github.com/zynaddsubfx/zynaddsubfx"
status: "Initial ZynInstrumentReader reads plain or gzip-compressed .xiz XML from project-authored fixtures and classifies active ADD/SUB/PAD engines, enabled kit items, layering, envelopes, free envelopes, LFOs, filters, formants, and effects as ReferencePatch features. First Aquarium rebuilds exist for additive lead, PAD texture, and vocal/formant layer and now use named `layer` declarations. Additive rebuilds can use `harmonics` banks attached to layers, PAD rebuilds can use `spectrum` PADsynth-style FFT wavetable banks attached to layers, and PAD/vocal rebuilds use staged voice `env=rl` envelopes through layer defaults. `ZynInstrumentReader.RebuildFirstPadAsAquariumScript` now emits per-preset PAD `.aqua` rebuilds for enabled PAD kit items with OSCIL harmonic expansion, oracle-reported table base frequency, PAD detune, Zyn PAD profile/bandwidth/position semantics, static/filter-envelope low-pass mapping, low-pass note tracking, explicit low-pass Q, logarithmic Zyn envelope times, and calibrated PAD output gain. ZynAddSubFX source is pinned under `external/zynaddsubfx` at 3ab608c432996ba4d582176572c0b0f82328c825 for test-only parity work, and `tools/zyn-reference/build-zyn-reference.ps1` now builds an ignored patched source copy with MSYS2/MinGW, proves `PadNoteTest`, and builds `ZynPadReference`, including a test-only `harmonics` mode for dumping normalized OscilGen amplitudes before PAD generation. The optional PAD reports emit note and source-table WAV/listening artifacts. Latest accepted upstream PAD pressure has DoublePadBass close enough by ear with log-mel 0.247748 / score 0.650029 after Q/order cascade, while Ghost Ensemble improves to log-mel 0.369402 / score 0.57229. The remaining Ghost gap is no longer first-32 OscilGen harmonic extraction; oracle harmonic dumps match those ratios. The upstream GPL instrument bank is initialized at `external/zynaddsubfx/instruments` commit c5c912131b31df5fdf372d2f06a25aaf2375837f; survey tests parse 1318/1318 .xiz files, write a pressure report, and render a five-patch upstream PAD listening batch under ignored artifacts." status: "Initial ZynInstrumentReader reads plain or gzip-compressed .xiz XML from project-authored fixtures and classifies active ADD/SUB/PAD engines, enabled kit items, layering, envelopes, free envelopes, LFOs, filters, formants, and effects as ReferencePatch features. First AquaSynth rebuilds exist for additive lead, PAD texture, and vocal/formant layer and now use named `layer` declarations. Additive rebuilds can use `harmonics` banks attached to layers, PAD rebuilds can use `spectrum` PADsynth-style FFT wavetable banks attached to layers, and PAD/vocal rebuilds use staged voice `env=rl` envelopes through layer defaults. `ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript` now emits per-preset PAD `.aqua` rebuilds for enabled PAD kit items with OSCIL harmonic expansion, oracle-reported table base frequency, PAD detune, Zyn PAD profile/bandwidth/position semantics, static/filter-envelope low-pass mapping, low-pass note tracking, explicit low-pass Q, logarithmic Zyn envelope times, and calibrated PAD output gain. ZynAddSubFX source is pinned under `external/zynaddsubfx` at 3ab608c432996ba4d582176572c0b0f82328c825 for test-only parity work, and `tools/zyn-reference/build-zyn-reference.ps1` now builds an ignored patched source copy with MSYS2/MinGW, proves `PadNoteTest`, and builds `ZynPadReference`, including a test-only `harmonics` mode for dumping normalized OscilGen amplitudes before PAD generation. The optional PAD reports emit note and source-table WAV/listening artifacts. Latest accepted upstream PAD pressure has DoublePadBass close enough by ear with log-mel 0.247748 / score 0.650029 after Q/order cascade, while Ghost Ensemble improves to log-mel 0.369402 / score 0.57229. The remaining Ghost gap is no longer first-32 OscilGen harmonic extraction; oracle harmonic dumps match those ratios. The upstream GPL instrument bank is initialized at `external/zynaddsubfx/instruments` commit c5c912131b31df5fdf372d2f06a25aaf2375837f; survey tests parse 1318/1318 .xiz files, write a pressure report, and render a five-patch upstream PAD listening batch under ignored artifacts."
first_slice: first_slice:
- "Use the Zyn PAD oracle report to isolate source-table, playback/gain, envelope, and fixture-mapping mismatch before further Zyn DSL golf." - "Use the Zyn PAD oracle report to isolate source-table, playback/gain, envelope, and fixture-mapping mismatch before further Zyn DSL golf."
- "Use named layers as the owner before promoting richer formant morphing syntax." - "Use named layers as the owner before promoting richer formant morphing syntax."
@ -123,7 +123,7 @@ reference_targets:
first_slice: first_slice:
- "Add ExternalFaustReference catalog entries." - "Add ExternalFaustReference catalog entries."
- "Compile selected official examples as external references." - "Compile selected official examples as external references."
- "Use failures to decide whether Aquarium needs a lower-level graph DSL." - "Use failures to decide whether AquaSynth needs a lower-level graph DSL."
surge_xt: surge_xt:
priority: 4 priority: 4
purpose: "Modern hybrid synth and modulation matrix pressure test." purpose: "Modern hybrid synth and modulation matrix pressure test."
@ -141,8 +141,8 @@ reference_targets:
next_agent_start_here: next_agent_start_here:
recommended_first_task: > recommended_first_task: >
Continue the ZynAddSubFX phase. The first parser/classifier slice exists in Continue the ZynAddSubFX phase. The first parser/classifier slice exists in
`src/AquariumSynth.Dsl/ZynInstrument.cs` with project-authored fixtures `src/AquaSynth.Dsl/ZynInstrument.cs` with project-authored fixtures
under `tests/AquariumSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored`. under `tests/AquaSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored`.
Named `layer` declarations own source identity, additive `harmonics` banks Named `layer` declarations own source identity, additive `harmonics` banks
attach to those layers, PAD `spectrum` banks attach to those layers as attach to those layers, PAD `spectrum` banks attach to those layers as
PADsynth-style FFT wavetable sources, and staged `env=rl` envelopes now PADsynth-style FFT wavetable sources, and staged `env=rl` envelopes now
@ -151,7 +151,7 @@ next_agent_start_here:
patched artifact copy and builds `ZynPadReference`, which emits PAD table patched artifact copy and builds `ZynPadReference`, which emits PAD table
samples or note audio from Zyn. The first optional project-authored PAD samples or note audio from Zyn. The first optional project-authored PAD
comparison writes WAV/report artifacts under `artifacts/parity/zyn-pad-reference`. comparison writes WAV/report artifacts under `artifacts/parity/zyn-pad-reference`.
User listening called the reference wineglass-pure and Aquarium angry bees. User listening called the reference wineglass-pure and AquaSynth angry bees.
The current de-bee/pitch pass cuts unearned noise/drive/soft-clip, The current de-bee/pitch pass cuts unearned noise/drive/soft-clip,
interpolates spectral wavetable reads, uses 131072-frame PAD tables, splits interpolates spectral wavetable reads, uses 131072-frame PAD tables, splits
table `root` from playback `freq`, removes gratuitous spread/vibrato, table `root` from playback `freq`, removes gratuitous spread/vibrato,
@ -171,7 +171,7 @@ next_agent_start_here:
- state/memory.json - state/memory.json
- state/evidence.jsonl - state/evidence.jsonl
verification: verification:
- "dotnet test AquariumSynthCSharp.slnx --no-restore" - "dotnet test AquaSynth.slnx --no-restore"
completed_slices: completed_slices:
- date: 2026-05-15 - date: 2026-05-15
@ -186,7 +186,7 @@ completed_slices:
(400-800 Hz 0.2713 vs 0.2780; 8-16 kHz 0.1471 vs 0.1355), and Organ (400-800 Hz 0.2713 vs 0.2780; 8-16 kHz 0.1471 vs 0.1355), and Organ
improves to log-mel 0.471483 / score 0.541933 without changing accepted improves to log-mel 0.471483 / score 0.541933 without changing accepted
DoublePadBass, Ghost, or Soft Pad. DoublePadBass, Ghost, or Soft Pad.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 107 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 107 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Mapped Organ Choir's remaining PAD voice-alignment issue to two real Zyn Mapped Organ Choir's remaining PAD voice-alignment issue to two real Zyn
@ -199,20 +199,20 @@ completed_slices:
Latest Organ candidate is led by the 523 Hz choir voice, with 261 Hz body Latest Organ candidate is led by the 523 Hz choir voice, with 261 Hz body
at about -1.29 dB versus Zyn's -2.23 dB. Soft Pad, Ghost, and at about -1.29 dB versus Zyn's -2.23 dB. Soft Pad, Ghost, and
DoublePadBass remain on the accepted sample-0 root path. DoublePadBass remain on the accepted sample-0 root path.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 107 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 107 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Fixed Zyn OscilGen oscillator-filter harmonic indexing after user listening Fixed Zyn OscilGen oscillator-filter harmonic indexing after user listening
accepted Ghost, DoublePadBass, and Soft Pad but still heard sin2x accepted Ghost, DoublePadBass, and Soft Pad but still heard sin2x
over-emphasizing the wrong high harmonic. Zyn filter functions receive over-emphasizing the wrong high harmonic. Zyn filter functions receive
one-based harmonic numbers, so Aquarium's zero-based translation moved one-based harmonic numbers, so AquaSynth's zero-based translation moved
sin2x's type-13 spike from h2 to h3. Preserving the Zyn index makes the sin2x's type-13 spike from h2 to h3. Preserving the Zyn index makes the
generated sin2x table h2-dominant, improves centroid ratio from generated sin2x table h2-dominant, improves centroid ratio from
`1.227953` to `0.998145`, and moves log-mel from `0.491437` to `1.227953` to `0.998145`, and moves log-mel from `0.491437` to
`0.479782`. Organ Choir remains slightly tinny by ear and sin2x is still `0.479782`. Organ Choir remains slightly tinny by ear and sin2x is still
louder/concentrated, so the next pressure is output/profile shape rather louder/concentrated, so the next pressure is output/profile shape rather
than the wrong overtone index. than the wrong overtone index.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 106 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 106 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Added generated Zyn PAD output calibration after user listening found the Added generated Zyn PAD output calibration after user listening found the
@ -225,7 +225,7 @@ completed_slices:
improves to `0.226563`, RMS ratio to `0.818669`, and score to `0.631168`. improves to `0.226563`, RMS ratio to `0.818669`, and score to `0.631168`.
The rest of the current PAD batch stays in a plausible RMS range The rest of the current PAD batch stays in a plausible RMS range
(`0.94..1.24` except DoublePadBass at `0.82`). (`0.94..1.24` except DoublePadBass at `0.82`).
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 104 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 104 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Corrected Zyn PAD envelope timing after user listening reported that Corrected Zyn PAD envelope timing after user listening reported that
@ -237,7 +237,7 @@ completed_slices:
multiplier remains cut because it overdrives the whole fixture batch. multiplier remains cut because it overdrives the whole fixture batch.
DoublePadBass improved to log-mel `0.211646`, envelope distance DoublePadBass improved to log-mel `0.211646`, envelope distance
`0.462822`, RMS ratio `0.511669`, score `0.450507`. `0.462822`, RMS ratio `0.511669`, score `0.450507`.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 104 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 104 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Added a general low-pass rate/level envelope surface and mapped Zyn PAD Added a general low-pass rate/level envelope surface and mapped Zyn PAD
@ -250,11 +250,11 @@ completed_slices:
Zyn cutoff, preserving the immediate open-filter transient. DoublePadBass Zyn cutoff, preserving the immediate open-filter transient. DoublePadBass
improved from log-mel `0.259298` / envelope distance `0.631427` to improved from log-mel `0.259298` / envelope distance `0.631427` to
`0.241993` / `0.554658`; remaining pressure is mostly level/RMS. `0.241993` / `0.554658`; remaining pressure is mostly level/RMS.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 104 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 104 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Mapped two missing Zyn PAD authorities exposed by DoublePadBass listening: Mapped two missing Zyn PAD authorities exposed by DoublePadBass listening:
analog low-pass slope and PAD frequency detune. Aquarium now has a general analog low-pass slope and PAD frequency detune. AquaSynth now has a general
`lpf_order` field which emits Faust `fi.lowpass(order, ...)`; Zyn analog `lpf_order` field which emits Faust `fi.lowpass(order, ...)`; Zyn analog
`LP2` filters lower to `lpf_order=2` and include Zyn stage count. The Zyn `LP2` filters lower to `lpf_order=2` and include Zyn stage count. The Zyn
PAD importer also applies Zyn `getdetune` semantics to PAD importer also applies Zyn `getdetune` semantics to
@ -263,7 +263,7 @@ completed_slices:
to `130.8128`. DoublePadBass log-mel moved from `0.334546` after static to `130.8128`. DoublePadBass log-mel moved from `0.334546` after static
cutoff mapping to `0.259298`; the remaining gap is mostly envelope/RMS, cutoff mapping to `0.259298`; the remaining gap is mostly envelope/RMS,
not wrong pitch or high-band balance. not wrong pitch or high-band balance.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 103 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 103 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Mapped static Zyn PAD global low-pass filters into generated Aqua layers Mapped static Zyn PAD global low-pass filters into generated Aqua layers
@ -275,7 +275,7 @@ completed_slices:
`lpf=0.003325`, while kit1's `freq=61` lowers to `lpf=0.047225`. The `lpf=0.003325`, while kit1's `freq=61` lowers to `lpf=0.047225`. The
louder gain-cap experiment stayed cut. Filter envelopes and LFOs remain louder gain-cap experiment stayed cut. Filter envelopes and LFOs remain
explicit missing pressure. explicit missing pressure.
verification: "dotnet test tests\\AquariumSynth.Dsl.Tests\\AquariumSynth.Dsl.Tests.csproj --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed" verification: "dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Addressed the two user-identified Zyn PAD offenders separately. Ghost Addressed the two user-identified Zyn PAD offenders separately. Ghost
@ -288,7 +288,7 @@ completed_slices:
sums enabled PAD kit-note references, gathers per-kit table roots, and sums enabled PAD kit-note references, gathers per-kit table roots, and
rebuilds enabled PAD kit layers into one Aqua script. A louder gain-cap rebuilds enabled PAD kit layers into one Aqua script. A louder gain-cap
experiment worsened DoublePadBass and was cut. experiment worsened DoublePadBass and was cut.
verification: "dotnet test tests\\AquariumSynth.Dsl.Tests\\AquariumSynth.Dsl.Tests.csproj --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed" verification: "dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Reworked the first Zyn OscilGen approximation into a sampled stage-order Reworked the first Zyn OscilGen approximation into a sampled stage-order
@ -300,10 +300,10 @@ completed_slices:
improves the processed `sin2x pad` target versus the previous subset improves the processed `sin2x pad` target versus the previous subset
while keeping the pass honest: DoublePadBass and Organ Choir Pad2 still while keeping the pass honest: DoublePadBass and Organ Choir Pad2 still
miss badly, so Zyn PAD parity remains open pressure. miss badly, so Zyn PAD parity remains open pressure.
verification: "dotnet test tests\\AquariumSynth.Dsl.Tests\\AquariumSynth.Dsl.Tests.csproj --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed" verification: "dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Promoted explicit Zyn PAD table semantics into the Aquarium model and Promoted explicit Zyn PAD table semantics into the AquaSynth model and
parser. `SpectralBank` now carries `PadSpectrumProfile`; patch scripts can parser. `SpectralBank` now carries `PadSpectrumProfile`; patch scripts can
express Zyn PAD mode, bandwidth, bandwidth scale, harmonic profile, and express Zyn PAD mode, bandwidth, bandwidth scale, harmonic profile, and
harmonic-position warp through readable `zyn_*` fields, and harmonic-position warp through readable `zyn_*` fields, and
@ -315,7 +315,7 @@ completed_slices:
Organ Choir Pad2 improve, while sin2x, DoublePadBass, and Ghost Ensemble Organ Choir Pad2 improve, while sin2x, DoublePadBass, and Ghost Ensemble
regress, so the next cut must either complete more OscilGen semantics or regress, so the next cut must either complete more OscilGen semantics or
gate the approximation to the cases it owns. gate the approximation to the cases it owns.
verification: "dotnet test tests\\AquariumSynth.Dsl.Tests\\AquariumSynth.Dsl.Tests.csproj --no-restore --filter \"FullyQualifiedName~ZynInstrumentTests|FullyQualifiedName~ZynReferenceParityTests\": 13 passed" verification: "dotnet test tests\\AquaSynth.Dsl.Tests\\AquaSynth.Dsl.Tests.csproj --no-restore --filter \"FullyQualifiedName~ZynInstrumentTests|FullyQualifiedName~ZynReferenceParityTests\": 13 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Initialized the upstream GPL ZynAddSubFX instrument bank submodule and Initialized the upstream GPL ZynAddSubFX instrument bank submodule and
@ -326,12 +326,12 @@ completed_slices:
`artifacts/parity/zyn-upstream-pad-fixtures/`. Zyn PAD artifacts now use `artifacts/parity/zyn-upstream-pad-fixtures/`. Zyn PAD artifacts now use
per-synth timestamped folders with reference WAVs, table WAVs, per-synth timestamped folders with reference WAVs, table WAVs,
`candidate.aqua`, and `report.txt`; upstream GPL runs now render `candidate.aqua`, and `report.txt`; upstream GPL runs now render
per-preset first-pass PAD rebuilds as `candidate-aquarium.wav`, with per-preset first-pass PAD rebuilds as `candidate-aquasynth.wav`, with
reports naming which Zyn PAD semantics are still missing. The reports naming which Zyn PAD semantics are still missing. The
batch includes `Pads/0002-sin2x pad.xiz`, `Pads/0065-Soft Pad.xiz`, batch includes `Pads/0002-sin2x pad.xiz`, `Pads/0065-Soft Pad.xiz`,
`Dual/0008-Organ Choir Pad2.xiz`, `Laba170bank/0098-DoublePadBass.xiz`, `Dual/0008-Organ Choir Pad2.xiz`, `Laba170bank/0098-DoublePadBass.xiz`,
and `Companion/0121-Ghost Ensemble.xiz`. and `Companion/0121-Ghost Ensemble.xiz`.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed" verification: "dotnet test AquaSynth.slnx --no-restore --filter FullyQualifiedName~ZynReferenceParityTests: 4 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Added more project-authored PAD fixtures and a Zyn-only PAD fixture Added more project-authored PAD fixtures and a Zyn-only PAD fixture
@ -342,7 +342,7 @@ completed_slices:
through the oracle, so the `pad-texture` filter/category XML was not live through the oracle, so the `pad-texture` filter/category XML was not live
pressure; `pad-harmonic` changes both table and note output and is the pressure; `pad-harmonic` changes both table and note output and is the
next useful PAD rung. next useful PAD rung.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 98 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 98 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Removed the detuned impression in the Zyn PAD rebuild by collapsing the Removed the detuned impression in the Zyn PAD rebuild by collapsing the
@ -351,7 +351,7 @@ completed_slices:
loud. Latest report: log-mel 0.35669, envelope 0.347572, RMS ratio loud. Latest report: log-mel 0.35669, envelope 0.347572, RMS ratio
1.001704, centroid ratio 1.004072, score 0.6211. This preserves the 1.001704, centroid ratio 1.004072, score 0.6211. This preserves the
pitch/root split and avoids treating voice stacking as PAD richness. pitch/root split and avoids treating voice stacking as PAD richness.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 97 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 97 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Fixed the octave-low Zyn PAD rebuild by splitting spectral table root Fixed the octave-low Zyn PAD rebuild by splitting spectral table root
@ -362,11 +362,11 @@ completed_slices:
Removing gratuitous spread/vibrato reduced the drone the user heard. Removing gratuitous spread/vibrato reduced the drone the user heard.
Latest report: log-mel 0.353365, envelope 0.348752, RMS ratio 0.97283, Latest report: log-mel 0.353365, envelope 0.348752, RMS ratio 0.97283,
centroid ratio 0.999243, score 0.612832. centroid ratio 0.999243, score 0.612832.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 97 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 97 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
De-beed the project-authored Zyn PAD rebuild after listening identified De-beed the project-authored Zyn PAD rebuild after listening identified
Aquarium as a noisy swarm while Zyn sounded like a pure blown wineglass. AquaSynth as a noisy swarm while Zyn sounded like a pure blown wineglass.
Cut the unearned `air` noise layer, drive, and soft clipping from the PAD Cut the unearned `air` noise layer, drive, and soft clipping from the PAD
rebuild; added interpolated Faust wavetable reads for spectral banks; and rebuild; added interpolated Faust wavetable reads for spectral banks; and
raised spectral PAD tables to Zyn's 131072-frame sample size. The optional raised spectral PAD tables to Zyn's 131072-frame sample size. The optional
@ -376,7 +376,7 @@ completed_slices:
This is a real improvement from 0.236117 but not passing; remaining This is a real improvement from 0.236117 but not passing; remaining
pressure is exact source profile/table generation and Zyn amplitude pressure is exact source profile/table generation and Zyn amplitude
semantics. semantics.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 96 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 96 passed"
- date: 2026-05-15 - date: 2026-05-15
summary: > summary: >
Added `ZynPadReference`, a test-only executable copied into the ignored Added `ZynPadReference`, a test-only executable copied into the ignored
@ -385,12 +385,12 @@ completed_slices:
`PADnoteParameters`/`PADnote` path, and writes either generated PAD table `PADnoteParameters`/`PADnote` path, and writes either generated PAD table
samples or rendered note audio as raw float32. Added an optional C# parity samples or rendered note audio as raw float32. Added an optional C# parity
harness that runs the renderer when the artifact executable exists and harness that runs the renderer when the artifact executable exists and
writes Zyn/Aquarium WAVs plus a report under writes Zyn/AquaSynth WAVs plus a report under
`artifacts/parity/zyn-pad-reference`. First report: log-mel 0.552296, `artifacts/parity/zyn-pad-reference`. First report: log-mel 0.552296,
envelope 0.774501, RMS ratio 0.234763, centroid ratio 1.025735, score envelope 0.774501, RMS ratio 0.234763, centroid ratio 1.025735, score
0.236117, so the harness works and current Aquarium `spectrum` is not yet 0.236117, so the harness works and current AquaSynth `spectrum` is not yet
a close PAD emulation. a close PAD emulation.
verification: "powershell -NoProfile -ExecutionPolicy Bypass -File tools\\zyn-reference\\build-zyn-reference.ps1; dotnet test AquariumSynthCSharp.slnx --no-restore: 96 passed" verification: "powershell -NoProfile -ExecutionPolicy Bypass -File tools\\zyn-reference\\build-zyn-reference.ps1; dotnet test AquaSynth.slnx --no-restore: 96 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Built the pinned ZynAddSubFX source as a test-only MSYS2/MinGW parity Built the pinned ZynAddSubFX source as a test-only MSYS2/MinGW parity
@ -399,7 +399,7 @@ completed_slices:
repo-owned compatibility patch for the stale `Part` constructor call, repo-owned compatibility patch for the stale `Part` constructor call,
configures GUI/plugins off with tests on and `Werror=OFF`, builds configures GUI/plugins off with tests on and `Werror=OFF`, builds
`PadNoteTest`, and runs the CTest target. The next rung is a small `PadNoteTest`, and runs the CTest target. The next rung is a small
reference renderer/wrapper that emits PAD audio or tables for Aquarium reference renderer/wrapper that emits PAD audio or tables for AquaSynth
`spectrum` parity. `spectrum` parity.
verification: "powershell -NoProfile -ExecutionPolicy Bypass -File tools\\zyn-reference\\build-zyn-reference.ps1: PadNoteTest passed" verification: "powershell -NoProfile -ExecutionPolicy Bypass -File tools\\zyn-reference\\build-zyn-reference.ps1: PadNoteTest passed"
- date: 2026-05-14 - date: 2026-05-14
@ -409,9 +409,9 @@ completed_slices:
Added guard tests proving the source, license, PAD implementation files, Added guard tests proving the source, license, PAD implementation files,
and commit are present, and tightened the package-boundary test so and commit are present, and tightened the package-boundary test so
`external/`, C++ sources, and headers do not ship in `external/`, C++ sources, and headers do not ship in
`AquariumSynth.Dsl.nupkg`. This establishes Zyn as a parity oracle, not `AquaSynth.Dsl.nupkg`. This establishes Zyn as a parity oracle, not
Aquarium implementation. AquaSynth implementation.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 95 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 95 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added PAD spectral-cloud syntax for named layers. `spectrum Added PAD spectral-cloud syntax for named layers. `spectrum
@ -422,7 +422,7 @@ completed_slices:
playback before ordinary voice treatment. The Zyn PAD texture rebuild now playback before ordinary voice treatment. The Zyn PAD texture rebuild now
uses two spectral banks under `pad_low` and `pad_high`; full Zyn PAD uses two spectral banks under `pad_low` and `pad_high`; full Zyn PAD
profile/randomness/pitch-zone behavior remains missing pressure. profile/randomness/pitch-zone behavior remains missing pressure.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 94 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 94 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Promoted staged rate/level envelopes from operator graphs to normal Promoted staged rate/level envelopes from operator graphs to normal
@ -433,7 +433,7 @@ completed_slices:
envelopes through named layers, proving the surface beyond a single envelopes through named layers, proving the surface beyond a single
fixture while leaving arbitrary Zyn free-envelope point curves as missing fixture while leaving arbitrary Zyn free-envelope point curves as missing
pressure. pressure.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 91 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 91 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added additive harmonic-bank syntax for named layers. `harmonics Added additive harmonic-bank syntax for named layers. `harmonics
@ -443,17 +443,17 @@ completed_slices:
banks under `body` and `shine`, while the catalog marks harmonic-bank banks under `body` and `shine`, while the catalog marks harmonic-bank
support as matched and keeps Zyn ADDsynth phase/bandwidth/oscillator support as matched and keeps Zyn ADDsynth phase/bandwidth/oscillator
detail as missing pressure. detail as missing pressure.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 90 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 90 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added the first Zyn-driven language-golf abstraction: `layer`. Added the first Zyn-driven language-golf abstraction: `layer`.
Layer declarations own name, engine tag, optional MIDI key range metadata, Layer declarations own name, engine tag, optional MIDI key range metadata,
default gain, and effect-send labels while lowering to ordinary Aquarium default gain, and effect-send labels while lowering to ordinary AquaSynth
voices. The three Zyn-style rebuilds now use named layers, proving the voices. The three Zyn-style rebuilds now use named layers, proving the
abstraction generalizes across additive, PAD-texture, and vocal/formant abstraction generalizes across additive, PAD-texture, and vocal/formant
targets without adding fixture-specific shortcuts or hidden PAD/effect targets without adding fixture-specific shortcuts or hidden PAD/effect
behavior. behavior.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 88 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 88 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Hardened the Zyn reader against the real upstream instrument bank and Hardened the Zyn reader against the real upstream instrument bank and
@ -466,10 +466,10 @@ completed_slices:
envelopes, 73 LFOs, 37 filters, and 3 formant filters. The survey points envelopes, 73 LFOs, 37 filters, and 3 formant filters. The survey points
to named kit/layer routing as the next owner abstraction before syntax to named kit/layer routing as the next owner abstraction before syntax
golf. golf.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 86 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 86 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added first Zyn-style Aquarium rebuilds for the project-authored additive Added first Zyn-style AquaSynth rebuilds for the project-authored additive
lead, PAD texture, and vocal/formant layer fixtures. The rebuild catalog lead, PAD texture, and vocal/formant layer fixtures. The rebuild catalog
now records matched and missing features for each target, while tests now records matched and missing features for each target, while tests
parse/export the scripts and verify matched feature claims against source parse/export the scripts and verify matched feature claims against source
@ -477,7 +477,7 @@ completed_slices:
additive harmonic-bank syntax, PAD/spectral source generation, free additive harmonic-bank syntax, PAD/spectral source generation, free
envelopes for normal voices, named kit/layer routing with per-layer effect envelopes for normal voices, named kit/layer routing with per-layer effect
sends, and richer formant/vowel morphing. sends, and richer formant/vowel morphing.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 83 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 83 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Started the ZynAddSubFX phase after closing the current DX7 community Started the ZynAddSubFX phase after closing the current DX7 community
@ -488,7 +488,7 @@ completed_slices:
kit items, layering, envelopes, free envelopes, LFOs, filters, formants, kit items, layering, envelopes, free envelopes, LFOs, filters, formants,
and effects. Upstream Zyn bank files were inspected but not vendored and effects. Upstream Zyn bank files were inspected but not vendored
because preset provenance was not explicit enough for this first slice. because preset provenance was not explicit enough for this first slice.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 81 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 81 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Reframed the latest community listening as envelope pressure rather than Reframed the latest community listening as envelope pressure rather than
@ -499,7 +499,7 @@ completed_slices:
to 1.25s, improving pressure log-mel to 0.3472474 and score to 0.5722256. to 1.25s, improving pressure log-mel to 0.3472474 and score to 0.5722256.
Tested and cut a global traced-release-duration change because it broke Tested and cut a global traced-release-duration change because it broke
PRC, `Piano Bass`, `RES SYNTH1`, and `ANLGSYN 1`. PRC, `Piano Bass`, `RES SYNTH1`, and `ANLGSYN 1`.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 76 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 76 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added scoped modulator peak headroom after listening found `ANLGSYN 1` Added scoped modulator peak headroom after listening found `ANLGSYN 1`
@ -513,7 +513,7 @@ completed_slices:
0.42906323. Tested and cut a three-source summed-route headroom rule for 0.42906323. Tested and cut a three-source summed-route headroom rule for
`{ Mooger }`; it worsened Mooger metrics and did not isolate the overtone `{ Mooger }`; it worsened Mooger metrics and did not isolate the overtone
issue. issue.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter Dx7... focused pass: 28 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter Dx7... focused pass: 28 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Lowered DX7 operator key-scaling and rate-scaling against the effective Lowered DX7 operator key-scaling and rate-scaling against the effective
@ -526,7 +526,7 @@ completed_slices:
and should not be promoted as passing stock. A trial that made envelope and should not be promoted as passing stock. A trial that made envelope
tracing depend on operator output level was cut because it worsened tracing depend on operator output level was cut because it worsened
`MELLOWSOLO` and `ANLGSYN 1`. `MELLOWSOLO` and `ANLGSYN 1`.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter Dx7... focused pass: 28 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter Dx7... focused pass: 28 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Removed DX7 ROM COM carrier compensation after listening found the Removed DX7 ROM COM carrier compensation after listening found the
@ -538,7 +538,7 @@ completed_slices:
0.10764466, RMS 0.941083. `DX1 LEAD B` and `MELLOWSOLO` sound closer by 0.10764466, RMS 0.941083. `DX1 LEAD B` and `MELLOWSOLO` sound closer by
ear under no-COM/gain-restored lowering but still fail numeric gates, so ear under no-COM/gain-restored lowering but still fail numeric gates, so
they remain pressure targets. they remain pressure targets.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 74 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 74 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Fixed the remaining `ANLGSYN 1` drift after listening found the Fixed the remaining `ANLGSYN 1` drift after listening found the
@ -551,18 +551,18 @@ completed_slices:
graph gain moved to 0.30, bringing its RMS ratio to 0.9909909. Latest graph gain moved to 0.30, bringing its RMS ratio to 0.9909909. Latest
`ANLGSYN 1`: score 0.7245976, log-mel 0.18460114, envelope 0.119950555, `ANLGSYN 1`: score 0.7245976, log-mel 0.18460114, envelope 0.119950555,
zero-crossing 0.97149533. zero-crossing 0.97149533.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 74 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 74 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Tightened the `ANLGSYN 1` parity rung after listening caught the remaining Tightened the `ANLGSYN 1` parity rung after listening caught the remaining
`deeooh` failure: the hotter feedback attack was close, but sustained `deeooh` failure: the hotter feedback attack was close, but sustained
high-band buzz collapsed midway through the note. The DX7 probe lowering high-band buzz collapsed midway through the note. The DX7 probe lowering
now enables Aquarium's existing output soft clip only for max-feedback now enables AquaSynth's existing output soft clip only for max-feedback
voices with a self-feedback topology. PRC remains `soft_clip=false`. voices with a self-feedback topology. PRC remains `soft_clip=false`.
Latest `ANLGSYN 1`: score 0.70946133, log-mel 0.18456098, envelope Latest `ANLGSYN 1`: score 0.70946133, log-mel 0.18456098, envelope
0.10628832, RMS ratio 0.9816304, zero-crossing 0.8657629. Latest PRC: 0.10628832, RMS ratio 0.9816304, zero-crossing 0.8657629. Latest PRC:
score 0.7546429, log-mel 0.13874851, envelope 0.067291394. score 0.7546429, log-mel 0.13874851, envelope 0.067291394.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 73 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 73 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Retuned DX7 max-feedback lowering after listening found `ANLGSYN 1` still Retuned DX7 max-feedback lowering after listening found `ANLGSYN 1` still
@ -575,7 +575,7 @@ completed_slices:
rises from 0.8% to 65.6%, and 2.5-5 kHz from near zero to 32.1%. rises from 0.8% to 65.6%, and 2.5-5 kHz from near zero to 32.1%.
Envelope/RMS are still imperfect, so this is a timbre correction rather Envelope/RMS are still imperfect, so this is a timbre correction rather
than the final word on ANLGSYN. than the final word on ANLGSYN.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 73 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 73 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Fixed DX7 fixed-frequency operator lowering after listening caught Fixed DX7 fixed-frequency operator lowering after listening caught
@ -588,7 +588,7 @@ completed_slices:
`ANLGSYN 1`; latest metrics improve to log-mel 0.19427659, envelope `ANLGSYN 1`; latest metrics improve to log-mel 0.19427659, envelope
0.14924917, zero-crossing 0.94662774, centroid 0.9182808, score 0.14924917, zero-crossing 0.94662774, centroid 0.9182808, score
0.659334. 0.659334.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 73 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 73 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Fixed DX7 voice transpose lowering after listening caught `Piano Bass` Fixed DX7 voice transpose lowering after listening caught `Piano Bass`
@ -600,31 +600,31 @@ completed_slices:
`RES SYNTH1`, `DX1 LEAD B`, and `MELLOWSOLO` remain pressure, not passing `RES SYNTH1`, `DX1 LEAD B`, and `MELLOWSOLO` remain pressure, not passing
parity. Fixed `Piano Bass` improves to log-mel 0.16180529, envelope parity. Fixed `Piano Bass` improves to log-mel 0.16180529, envelope
0.13544041, zero-crossing 0.905511, and score 0.64267576. 0.13544041, zero-crossing 0.905511, and score 0.64267576.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 71 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 71 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added a rendered public-domain community DX7 parity test against four Added a rendered public-domain community DX7 parity test against four
actual `analog1.syx` voices from Musical Artifacts artifact 152: actual `analog1.syx` voices from Musical Artifacts artifact 152:
`ANLGSYN 1`, `{ Mooger }`, `Piano Bass`, and `RES SYNTH1`. The test `ANLGSYN 1`, `{ Mooger }`, `Piano Bass`, and `RES SYNTH1`. The test
renders Dexed references, lowers the same voices through the current renders Dexed references, lowers the same voices through the current
Aquarium DX7 operator-graph path, writes listening WAV/report artifacts AquaSynth DX7 operator-graph path, writes listening WAV/report artifacts
under `artifacts/parity/dx7-community-analog1/`, and gates broad parity under `artifacts/parity/dx7-community-analog1/`, and gates broad parity
at log-mel <= 0.3, envelope <= 0.16, score >= 0.5. First survey also at log-mel <= 0.3, envelope <= 0.16, score >= 0.5. First survey also
tried `DX1 LEAD B` and `MELLOWSOLO`; they remain pressure because tried `DX1 LEAD B` and `MELLOWSOLO`; they remain pressure because
log-mel/zero-crossing mismatch is too large for the broad gate. 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" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 70 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added split project-authored DX7 algorithm-8 parity probes for the Added split project-authored DX7 algorithm-8 parity probes for the
cascaded `6 -> 5 -> 3` branch and summed `4 + 5 -> 3` branch. The tests cascaded `6 -> 5 -> 3` branch and summed `4 + 5 -> 3` branch. The tests
render Dexed references, render Aquarium operator-graph candidates, write render Dexed references, render AquaSynth operator-graph candidates, write
listening WAV/report artifacts under `artifacts/parity/dx7-project-authored/`, listening WAV/report artifacts under `artifacts/parity/dx7-project-authored/`,
and gate log-mel at <= 0.06. Latest real-Dexed run: combined stack and gate log-mel at <= 0.06. Latest real-Dexed run: combined stack
log-mel 0.04029555/score 0.8664546, summed pair 0.042429477/0.870778, log-mel 0.04029555/score 0.8664546, summed pair 0.042429477/0.870778,
cascade 0.03811625/0.9157809. The split probes remain test/artifact cascade 0.03811625/0.9157809. The split probes remain test/artifact
material rather than patch-library stock because they are calibration material rather than patch-library stock because they are calibration
signals, not usable sounds. signals, not usable sounds.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 69 passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore: 69 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added a structured development patch library under `patches/` with a Added a structured development patch library under `patches/` with a
@ -632,8 +632,8 @@ completed_slices:
existing stock/reference catalogs plus the calibrated PRC SYNTH1 parity existing stock/reference catalogs plus the calibrated PRC SYNTH1 parity
candidate. Added tests so every library `.aqua` parses and exports Faust, candidate. Added tests so every library `.aqua` parses and exports Faust,
while package-boundary tests assert `patches/` and `.aqua` files do not while package-boundary tests assert `patches/` and `.aqua` files do not
ship in the AquariumSynth.Dsl NuGet package. ship in the AquaSynth.Dsl NuGet package.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 67 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 67 passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Increased `Dx7SysEx.SummedOperatorModulationRouteIndex` from 1.6 to 6.0 Increased `Dx7SysEx.SummedOperatorModulationRouteIndex` from 1.6 to 6.0
@ -642,27 +642,27 @@ completed_slices:
to 0.067291506, centroid ratio to 1.1156512, and score to 0.7546445 with to 0.067291506, centroid ratio to 1.1156512, and score to 0.7546445 with
RMS ratio 0.9912135. The independent project-authored algorithm-8 RMS ratio 0.9912135. The independent project-authored algorithm-8
summed-stack test now uses graph gain 0.218 and tighter log-mel/RMS gates. summed-stack test now uses graph gain 0.218 and tighter log-mel/RMS gates.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added `Dx7SysEx.ApproximateAppliedRateLevelEnvelope` and used it in the Added `Dx7SysEx.ApproximateAppliedRateLevelEnvelope` and used it in the
hard PRC SYNTH1 probe. The helper traces Dexed-style block-interpolated hard PRC SYNTH1 probe. The helper traces Dexed-style block-interpolated
applied EG gain, normalizes it against Aquarium operator-output scaling, applied EG gain, normalizes it against AquaSynth operator-output scaling,
and emits curved `env=rl` syntax. PRC log-mel improved from 0.24476814 to and emits curved `env=rl` syntax. PRC log-mel improved from 0.24476814 to
0.21931955 and envelope distance from 0.43700194 to 0.10271241, with RMS 0.21931955 and envelope distance from 0.43700194 to 0.10271241, with RMS
ratio near unity. A follow-up fixed the peak picker for slower rising ratio near unity. A follow-up fixed the peak picker for slower rising
envelopes after listening exposed op2's false late swell; PRC now reaches envelopes after listening exposed op2's false late swell; PRC now reaches
log-mel 0.17186502, envelope distance 0.13057204, zero-crossing ratio log-mel 0.17186502, envelope distance 0.13057204, zero-crossing ratio
0.7815517, and score 0.6501268. 0.7815517, and score 0.6501268.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled|ApproximatesDx7AppliedEnvelopeAsCurvedRateLevelEnvelope\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled|ApproximatesDx7AppliedEnvelopeAsCurvedRateLevelEnvelope\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added per-segment curves to staged operator envelopes. `env=rl` remains Added per-segment curves to staged operator envelopes. `env=rl` remains
the same rate/level contour surface, but can now express linear or the same rate/level contour surface, but can now express linear or
exponential segments with deliberate levels above unity. The DX7 envelope exponential segments with deliberate levels above unity. The DX7 envelope
trace artifact includes a curved Aquarium candidate that is much closer trace artifact includes a curved AquaSynth candidate that is much closer
to Dexed's block-interpolated applied gain than the old linear contour. to Dexed's block-interpolated applied gain than the old linear contour.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"OperatorGraphSyntaxParsesReadableRateLevelEnvelope|OperatorGraphSyntaxParsesCurvedRateLevelEnvelope|ProjectAuthoredDx7EnvelopeTraceWritesComparisonArtifact\": passed" verification: "dotnet test AquaSynth.slnx --no-restore --filter \"OperatorGraphSyntaxParsesReadableRateLevelEnvelope|OperatorGraphSyntaxParsesCurvedRateLevelEnvelope|ProjectAuthoredDx7EnvelopeTraceWritesComparisonArtifact\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added `Dx7SysEx.TraceInterpolatedEnvelope` and extended the DX7 envelope Added `Dx7SysEx.TraceInterpolatedEnvelope` and extended the DX7 envelope
@ -670,7 +670,7 @@ completed_slices:
rendering samples the EG once per 64-sample block and interpolates the rendering samples the EG once per 64-sample block and interpolates the
applied gain across the block, so matching the raw EG state alone is not applied gain across the block, so matching the raw EG state alone is not
enough for audio parity. enough for audio parity.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"TracesDx7EnvelopeAppliedBlockInterpolatedGain|TracesDx7EnvelopeInternalGainShape|ProjectAuthoredDx7EnvelopeTraceWritesComparisonArtifact\": passed" verification: "dotnet test AquaSynth.slnx --no-restore --filter \"TracesDx7EnvelopeAppliedBlockInterpolatedGain|TracesDx7EnvelopeInternalGainShape|ProjectAuthoredDx7EnvelopeTraceWritesComparisonArtifact\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added `Dx7SysEx.TraceEnvelope` and an envelope trace comparison artifact. Added `Dx7SysEx.TraceEnvelope` and an envelope trace comparison artifact.
@ -678,7 +678,7 @@ completed_slices:
an initial gain spike above 1 followed by millisecond-scale collapse to a an initial gain spike above 1 followed by millisecond-scale collapse to a
tiny sustain. A first `env=dx7` runtime lowering attempt was cut because tiny sustain. A first `env=dx7` runtime lowering attempt was cut because
matching the local trace did not match Dexed plugin audio. matching the local trace did not match Dexed plugin audio.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"ProjectAuthoredDx7EnvelopeTraceWritesComparisonArtifact|TracesDx7EnvelopeInternalGainShape\": passed" verification: "dotnet test AquaSynth.slnx --no-restore --filter \"ProjectAuthoredDx7EnvelopeTraceWritesComparisonArtifact|TracesDx7EnvelopeInternalGainShape\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added test-only project-authored Dexed patch rendering and an independent Added test-only project-authored Dexed patch rendering and an independent
@ -687,7 +687,7 @@ completed_slices:
exponential DX7 EG level curve was tested and cut: it improved some exponential DX7 EG level curve was tested and cut: it improved some
envelope metrics but failed PRC log-mel, so EG levels remain linear until envelope metrics but failed PRC log-mel, so EG levels remain linear until
isolated envelope parity can pass without harming PRC. isolated envelope parity can pass without harming PRC.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|Dx7SysExTests\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|Dx7SysExTests\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Moved the remaining PRC cascaded-route scalar into Moved the remaining PRC cascaded-route scalar into
@ -695,7 +695,7 @@ completed_slices:
use the two-op route scale 6.275; summed edges and direct edges feeding a use the two-op route scale 6.275; summed edges and direct edges feeding a
summed branch use the summed/cascaded route scale 1.6. The PRC probe no summed branch use the summed/cascaded route scale 1.6. The PRC probe no
longer owns a private route-index function. longer owns a private route-index function.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added DX7 ratio-mode detune and algorithm output compensation to central Added DX7 ratio-mode detune and algorithm output compensation to central
@ -704,23 +704,23 @@ completed_slices:
output counts relative to the algorithm-32 six-output amplitude baseline. output counts relative to the algorithm-32 six-output amplitude baseline.
The PRC probe no longer carries a manual carrier-level scale table; its The PRC probe no longer carries a manual carrier-level scale table; its
remaining cascaded-route value is fenced as a hard-target probe scar. remaining cascaded-route value is fenced as a hard-target probe scar.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Completed the DX7 feedback calibration rung. Isolated feedback sweeps fit Completed the DX7 feedback calibration rung. Isolated feedback sweeps fit
Aquarium feedback amounts `0, .01, .02, .05, .10, .19, .38, .66` for DX7 AquaSynth feedback amounts `0, .01, .02, .05, .10, .19, .38, .66` for DX7
feedback values 0-7. `Dx7SysEx.OperatorFeedbackAmount` now owns that feedback values 0-7. `Dx7SysEx.OperatorFeedbackAmount` now owns that
table, and the PRC probe no longer uses a local `feedback * constant`. table, and the PRC probe no longer uses a local `feedback * constant`.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"CalibratesDx7FeedbackToAquariumFeedbackAmount|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"CalibratesDx7FeedbackToAquaSynthFeedbackAmount|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Completed the isolated two-operator modulation-index rung. Dexed sweeps Completed the isolated two-operator modulation-index rung. Dexed sweeps
fit full-level modulation at about 12.55 radians, which maps to Aquarium fit full-level modulation at about 12.55 radians, which maps to AquaSynth
route index 6.275 under the current Faust phase formula. The PRC probe route index 6.275 under the current Faust phase formula. The PRC probe
applies that calibrated route only to the isolated `op2 -> op1` branch; applies that calibrated route only to the isolated `op2 -> op1` branch;
applying it to the cascaded stack made metrics worse, leaving algorithm applying it to the cascaded stack made metrics worse, leaving algorithm
output compensation as explicit pressure. output compensation as explicit pressure.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Added `docs/dx7-calibration-plan.md` and completed the first calibration Added `docs/dx7-calibration-plan.md` and completed the first calibration
@ -729,7 +729,7 @@ completed_slices:
exposes `OperatorOutputAmplitude` and `ApproximateOperatorLevel` uses that exposes `OperatorOutputAmplitude` and `ApproximateOperatorLevel` uses that
measured curve instead of the old internal-level proxy. The PRC log-mel measured curve instead of the old internal-level proxy. The PRC log-mel
gate is temporarily 0.26 while the next rung calibrates modulation index. gate is temporarily 0.26 while the next rung calibrates modulation index.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Reoriented the hard DX7 `PRC SYNTH1` probe around log-mel distance after Reoriented the hard DX7 `PRC SYNTH1` probe around log-mel distance after
@ -738,25 +738,25 @@ completed_slices:
dominant 393 Hz partial; the candidate now uses the DX7 note basis and dominant 393 Hz partial; the candidate now uses the DX7 note basis and
scaled high modulators to reach log-mel 0.25093183, with remaining scaled high modulators to reach log-mel 0.25093183, with remaining
zero-crossing/centroid mismatch left as explicit pressure. zero-crossing/centroid mismatch left as explicit pressure.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed"
- date: 2026-05-14 - date: 2026-05-14
summary: > summary: >
Tightened the hard DX7 `PRC SYNTH1` probe to require score >= 0.60 and Tightened the hard DX7 `PRC SYNTH1` probe to require score >= 0.60 and
improved the candidate to 0.6057491. The useful calibration cuts were improved the candidate to 0.6057491. The useful calibration cuts were
removing Aquarium soft clipping, setting the graph base frequency to 392, removing AquaSynth soft clipping, setting the graph base frequency to 392,
and boosting the op3 carrier branch for algorithm-8 output/body balance. 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 Global detune and per-edge route damping were tested and cut because they
made the metrics worse. made the metrics worse.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added a hard DX7 `PRC SYNTH1` rendered parity probe that emits listening Added a hard DX7 `PRC SYNTH1` rendered parity probe that emits listening
WAV artifacts under ignored `artifacts/parity/dx7-prc-synth1` when WAV artifacts under ignored `artifacts/parity/dx7-prc-synth1` when
dexed-py and Faust are available. The Aquarium candidate uses readable dexed-py and Faust are available. The AquaSynth candidate uses readable
operator graph syntax and staged `env=rl` envelopes, asserts a modest operator graph syntax and staged `env=rl` envelopes, asserts a modest
hard-target score floor, and currently scores 0.4581856 with duration hard-target score floor, and currently scores 0.4581856 with duration
close to the Dexed reference but remaining gain/timbre mismatch. close to the Dexed reference but remaining gain/timbre mismatch.
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed" verification: "AQUASYNTH_DX7_PYTHON=<bundled python> dotnet test AquaSynth.slnx --no-restore --filter PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled: passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added staged operator envelopes for operator graphs through Added staged operator envelopes for operator graphs through
@ -766,15 +766,15 @@ completed_slices:
lowering on `PRC SYNTH1` only reached ~0.316 in quick probes, so the DSL lowering on `PRC SYNTH1` only reached ~0.316 in quick probes, so the DSL
surface exists but DX7 EG timing/gain calibration remains the next surface exists but DX7 EG timing/gain calibration remains the next
pressure. pressure.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 53 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 53 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added `Dx7OperatorLevelApproximation`, which maps DX7 operator output Added `Dx7OperatorLevelApproximation`, which maps DX7 operator output
level plus key/velocity scaling into normal Aquarium operator levels for level plus key/velocity scaling into normal AquaSynth operator levels for
reference rebuild work. Retried `PRC SYNTH1`; static level mapping alone reference rebuild work. Retried `PRC SYNTH1`; static level mapping alone
did not produce parity, and envelope tweaks only reached ~0.42 score. The did not produce parity, and envelope tweaks only reached ~0.42 score. The
next pressure is DX7-style operator envelope/gain evolution. next pressure is DX7-style operator envelope/gain evolution.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 50 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 50 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Probed the harder `analog1.syx` voice 17 (`PRC SYNTH1`) and found the Probed the harder `analog1.syx` voice 17 (`PRC SYNTH1`) and found the
@ -782,53 +782,53 @@ completed_slices:
cut was feedback: operator feedback now emits as Faust delayed recursion cut was feedback: operator feedback now emits as Faust delayed recursion
instead of a direct cyclic self-reference, and a render test covers it. instead of a direct cyclic self-reference, and a render test covers it.
Exact DX7 feedback scaling and EG behavior remain pressure. Exact DX7 feedback scaling and EG behavior remain pressure.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 49 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 49 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added the first thresholded rendered DX7 parity rebuild for Added the first thresholded rendered DX7 parity rebuild for
`analog1.syx` voice 13, `MC-MM 5-3`. The candidate is a terse Aquarium `analog1.syx` voice 13, `MC-MM 5-3`. The candidate is a terse AquaSynth
sine-like behavioral match and the test compares dexed-py reference audio sine-like behavioral match and the test compares dexed-py reference audio
against Faust-rendered Aquarium audio using score, log-mel, envelope, against Faust-rendered AquaSynth audio using score, log-mel, envelope,
duration, RMS, zero-crossing, and centroid thresholds. It does not claim duration, RMS, zero-crossing, and centroid thresholds. It does not claim
exact DX7 operator execution. exact DX7 operator execution.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 48 passed; focused dexed-py parity test passed with bundled Python" verification: "dotnet test AquaSynth.slnx --no-restore: 48 passed; focused dexed-py parity test passed with bundled Python"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added a public-domain DX7 SysEx fixture from Musical Artifacts artifact Added a public-domain DX7 SysEx fixture from Musical Artifacts artifact
152, provenance metadata, and a test-only dexed-py renderer. The render 152, provenance metadata, and a test-only dexed-py renderer. The render
test uses `AQUARIUM_DX7_PYTHON` when set and otherwise stays optional. test uses `AQUASYNTH_DX7_PYTHON` when set and otherwise stays optional.
Added a package-boundary test that packs `AquariumSynth.Dsl` and asserts Added a package-boundary test that packs `AquaSynth.Dsl` and asserts
fixtures, SysEx banks, and Python helpers do not ship in the NuGet package. fixtures, SysEx banks, and Python helpers do not ship in the NuGet package.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 47 passed; dexed-py render test passed with bundled Python" verification: "dotnet test AquaSynth.slnx --no-restore: 47 passed; dexed-py render test passed with bundled Python"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added `FaustCompiler.RenderAsync`, which compiles Aquarium Faust source to Added `FaustCompiler.RenderAsync`, which compiles AquaSynth Faust source to
Faust-generated C#, runs it in a temp .NET project, and returns mono float Faust-generated C#, runs it in a temp .NET project, and returns mono float
samples. The first render test checks non-silent generated audio and feeds samples. The first render test checks non-silent generated audio and feeds
the buffer through `AudioAnalyzer`; DX7 audio parity remains blocked on a the buffer through `AudioAnalyzer`; DX7 audio parity remains blocked on a
chosen reference renderer or captured fixture. chosen reference renderer or captured fixture.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 44 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 44 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added `Dx7EnvelopeApproximation`, a labeled helper that lowers DX7 Added `Dx7EnvelopeApproximation`, a labeled helper that lowers DX7
four-rate/four-level envelopes to Aquarium ADSR plus gate duration. four-rate/four-level envelopes to AquaSynth ADSR plus gate duration.
Reference rebuilds now distinguish approximated envelope lowering from Reference rebuilds now distinguish approximated envelope lowering from
still-missing exact DX7 EG execution. still-missing exact DX7 EG execution.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 43 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 43 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added field-site parameter binding for readable operator graphs. Graph Added field-site parameter binding for readable operator graphs. Graph
gain/note fields, operator ratio/level/feedback/envelope fields, and route gain/note fields, operator ratio/level/feedback/envelope fields, and route
index fields now bind through exact `/opgraphs/...` paths, and Faust index fields now bind through exact `/opgraphs/...` paths, and Faust
emission substitutes those bound expressions in operator graph output. emission substitutes those bound expressions in operator graph output.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 42 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 42 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added a patch-level Playback contract and `instrument` authoring surface Added a patch-level Playback contract and `instrument` authoring surface
for Faust-managed MIDI mono/polyphony. `instrument midi=true polyphony=8` for Faust-managed MIDI mono/polyphony. `instrument midi=true polyphony=8`
emits `[midi:on][nvoices:8]` and standard `freq`, `gain`, and `gate` emits `[midi:on][nvoices:8]` and standard `freq`, `gain`, and `gate`
controls instead of Aquarium-owned voice allocation. controls instead of AquaSynth-owned voice allocation.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 41 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 41 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Standardized the live envelope model as ADSR and added explicit Note Standardized the live envelope model as ADSR and added explicit Note
@ -837,14 +837,14 @@ completed_slices:
ADSR sustain level plus voice gain compensation as compatibility import ADSR sustain level plus voice gain compensation as compatibility import
behavior. Generated Faust can emit one-shot ADSR envelopes or host behavior. Generated Faust can emit one-shot ADSR envelopes or host
gate/frequency controls. gate/frequency controls.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 40 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 40 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added readable operator graph authoring syntax with `operator`, `route`, Added readable operator graph authoring syntax with `operator`, `route`,
and `carrier` declarations, plus `env=ad:attack:decay` and and `carrier` declarations, plus `env=ad:attack:decay` and
`env=adsr:attack:decay:sustain:release` forms. Converted the DX7 `env=adsr:attack:decay:sustain:release` forms. Converted the DX7
algorithm-8 rebuild away from compact number soup. algorithm-8 rebuild away from compact number soup.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 36 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 36 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added first-class OperatorGraph support with operators, modulation edges, Added first-class OperatorGraph support with operators, modulation edges,
@ -852,14 +852,14 @@ completed_slices:
normal voices. Updated the DX7 algorithm-8 rebuild to use the operator normal voices. Updated the DX7 algorithm-8 rebuild to use the operator
graph; remaining pressure is exact DX7 feedback-register timing and graph; remaining pressure is exact DX7 feedback-register timing and
rate/level envelopes. rate/level envelopes.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 35 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 35 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added ReferenceRebuild catalog entries and two DX7-style Aquarium DSL Added ReferenceRebuild catalog entries and two DX7-style AquaSynth DSL
rebuilds. Algorithm 32 maps cleanly to six additive voices; algorithm 8 rebuilds. Algorithm 32 maps cleanly to six additive voices; algorithm 8
exposes missing operator graph concepts including explicit modulation exposes missing operator graph concepts including explicit modulation
edges, summed modulators, and operator-local self-feedback. edges, summed modulators, and operator-local self-feedback.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 34 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 34 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added DX7 algorithm topology metadata for algorithms 1-32 from the Added DX7 algorithm topology metadata for algorithms 1-32 from the
@ -867,14 +867,14 @@ completed_slices:
operators, modulation edges, feedback-register writers, self-feedback operators, modulation edges, feedback-register writers, self-feedback
operators, delayed-feedback targets, raw ROM steps, and ReferencePatch operators, delayed-feedback targets, raw ROM steps, and ReferencePatch
feature pressure. feature pressure.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 31 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 31 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added DX7 SysEx records and parsing for 155-byte voice edit buffers, Added DX7 SysEx records and parsing for 155-byte voice edit buffers,
128-byte packed voices, and 4104-byte packed 32-voice banks. Extracted 128-byte packed voices, and 4104-byte packed 32-voice banks. Extracted
operators, envelopes, algorithm, feedback, oscillator sync, LFO, operators, envelopes, algorithm, feedback, oscillator sync, LFO,
transpose, voice name, checksum validation, and ReferencePatch features. transpose, voice name, checksum validation, and ReferencePatch features.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 28 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 28 passed"
- date: 2026-05-13 - date: 2026-05-13
summary: > summary: >
Added PatchParameter and ReferencePatch contracts, `param` script parsing, Added PatchParameter and ReferencePatch contracts, `param` script parsing,
@ -882,7 +882,7 @@ completed_slices:
controls. Parameter binding uses field-site references such as controls. Parameter binding uses field-site references such as
`lpf=@/macro/brightness`; the emitter substitutes the parameter only at `lpf=@/macro/brightness`; the emitter substitutes the parameter only at
the exact bound field site and warns for unbound parameters. the exact bound field site and warns for unbound parameters.
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore: 23 passed" verification: "dotnet test AquaSynth.slnx --no-restore: 23 passed"
cut_line: cut_line:
- "Cut any importer that cannot state source provenance and license." - "Cut any importer that cannot state source provenance and license."

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
@ -23,7 +23,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\AquariumSynth.Dsl\AquariumSynth.Dsl.csproj" /> <ProjectReference Include="..\..\src\AquaSynth.Dsl\AquaSynth.Dsl.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -2,7 +2,7 @@ using System.Diagnostics;
using System.Text.Json; using System.Text.Json;
using System.Text; using System.Text;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class Dx7ReferenceParityTests public sealed class Dx7ReferenceParityTests
{ {
@ -319,8 +319,8 @@ public sealed class Dx7ReferenceParityTests
var dx7 = Dx7SysEx.TraceEnvelope(envelope, gateSeconds: 0.75f, durationSeconds: 1.1f); var dx7 = Dx7SysEx.TraceEnvelope(envelope, gateSeconds: 0.75f, durationSeconds: 1.1f);
var dx7Applied = Dx7SysEx.TraceInterpolatedEnvelope(envelope, gateSeconds: 0.75f, durationSeconds: 1.1f); var dx7Applied = Dx7SysEx.TraceInterpolatedEnvelope(envelope, gateSeconds: 0.75f, durationSeconds: 1.1f);
var approximation = ScaledEnvelope(Dx7SysEx.ApproximateRateLevelEnvelope(envelope), 0.62f, 0.75f); var approximation = ScaledEnvelope(Dx7SysEx.ApproximateRateLevelEnvelope(envelope), 0.62f, 0.75f);
var aquarium = TraceRateLevelEnvelope(approximation.Envelope, approximation.GateSeconds, durationSeconds: 1.1f); var aquasynth = TraceRateLevelEnvelope(approximation.Envelope, approximation.GateSeconds, durationSeconds: 1.1f);
var aquariumCurved = TraceRateLevelEnvelope( var aquasynthCurved = TraceRateLevelEnvelope(
new RateLevelEnvelope( new RateLevelEnvelope(
64f / 44100, 64f / 44100,
2f, 2f,
@ -341,27 +341,27 @@ public sealed class Dx7ReferenceParityTests
var path = Path.Combine(artifactDir, "egstep.csv"); var path = Path.Combine(artifactDir, "egstep.csv");
using var writer = new StreamWriter(path); using var writer = new StreamWriter(path);
writer.WriteLine("time_seconds,dx7_raw_gain,dx7_applied_gain,aquarium_rl_gain,aquarium_curved_gain,dx7_stage"); writer.WriteLine("time_seconds,dx7_raw_gain,dx7_applied_gain,aquasynth_rl_gain,aquasynth_curved_gain,dx7_stage");
for (var i = 0; i < Math.Min(dx7.Count, aquarium.Length); i += 64) for (var i = 0; i < Math.Min(dx7.Count, aquasynth.Length); i += 64)
{ {
writer.WriteLine(string.Join( writer.WriteLine(string.Join(
",", ",",
F(dx7[i].TimeSeconds), F(dx7[i].TimeSeconds),
F(dx7[i].Gain), F(dx7[i].Gain),
F(dx7Applied[i].Gain), F(dx7Applied[i].Gain),
F(aquarium[i]), F(aquasynth[i]),
F(aquariumCurved[i]), F(aquasynthCurved[i]),
dx7[i].Stage.ToString(System.Globalization.CultureInfo.InvariantCulture))); dx7[i].Stage.ToString(System.Globalization.CultureInfo.InvariantCulture)));
} }
var linearShape = NormalizedDistance( var linearShape = NormalizedDistance(
dx7Applied.Select(point => point.Gain).ToArray(), dx7Applied.Select(point => point.Gain).ToArray(),
aquarium); aquasynth);
var curvedShape = NormalizedDistance( var curvedShape = NormalizedDistance(
dx7Applied.Select(point => point.Gain).ToArray(), dx7Applied.Select(point => point.Gain).ToArray(),
aquariumCurved); aquasynthCurved);
Assert.True(linearShape > 0.55f, $"expected current Aquarium envelope to visibly differ; shape distance {linearShape}, artifact: {path}"); Assert.True(linearShape > 0.55f, $"expected current AquaSynth envelope to visibly differ; shape distance {linearShape}, artifact: {path}");
Assert.True(curvedShape < linearShape * 0.6f, $"expected curved Aquarium envelope to improve shape; linear {linearShape}, curved {curvedShape}, artifact: {path}"); Assert.True(curvedShape < linearShape * 0.6f, $"expected curved AquaSynth envelope to improve shape; linear {linearShape}, curved {curvedShape}, artifact: {path}");
} }
private static string FixturePath(params string[] parts) => private static string FixturePath(params string[] parts) =>
@ -370,7 +370,7 @@ public sealed class Dx7ReferenceParityTests
private static string ArtifactPath(params string[] parts) private static string ArtifactPath(params string[] parts)
{ {
var current = new DirectoryInfo(AppContext.BaseDirectory); var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current is not null && !File.Exists(Path.Combine(current.FullName, "AquariumSynthCSharp.slnx"))) while (current is not null && !File.Exists(Path.Combine(current.FullName, "AquaSynth.slnx")))
{ {
current = current.Parent; current = current.Parent;
} }
@ -771,7 +771,7 @@ public sealed class Dx7ReferenceParityTests
var peak = Math.Max(Peak(reference), Peak(candidate)); var peak = Math.Max(Peak(reference), Peak(candidate));
var scale = peak <= 0 ? 1 : 0.98f / peak; var scale = peak <= 0 ? 1 : 0.98f / peak;
WriteWav(Path.Combine(directory, "reference-dexed.wav"), reference, sampleRate, scale); WriteWav(Path.Combine(directory, "reference-dexed.wav"), reference, sampleRate, scale);
WriteWav(Path.Combine(directory, "candidate-aquarium.wav"), candidate, sampleRate, scale); WriteWav(Path.Combine(directory, "candidate-aquasynth.wav"), candidate, sampleRate, scale);
File.WriteAllText(Path.Combine(directory, "candidate.aqua"), script); File.WriteAllText(Path.Combine(directory, "candidate.aqua"), script);
File.WriteAllText( File.WriteAllText(
Path.Combine(directory, "report.txt"), Path.Combine(directory, "report.txt"),
@ -780,7 +780,7 @@ public sealed class Dx7ReferenceParityTests
ParityReport(comparison), ParityReport(comparison),
$"wav normalization scale: {scale}", $"wav normalization scale: {scale}",
$"reference: {Path.Combine(directory, "reference-dexed.wav")}", $"reference: {Path.Combine(directory, "reference-dexed.wav")}",
$"candidate: {Path.Combine(directory, "candidate-aquarium.wav")}")); $"candidate: {Path.Combine(directory, "candidate-aquasynth.wav")}"));
} }
private static void WriteWav(string path, IReadOnlyList<float> samples, int sampleRate, float scale) private static void WriteWav(string path, IReadOnlyList<float> samples, int sampleRate, float scale)
@ -893,7 +893,7 @@ internal static class DexedPyRenderer
var python = await FindPythonWithDexedAsync(cancellationToken); var python = await FindPythonWithDexedAsync(cancellationToken);
if (python is null) return null; if (python is null) return null;
var tempDir = Path.Combine(Path.GetTempPath(), $"aquarium-dx7-render-{Guid.NewGuid():N}"); var tempDir = Path.Combine(Path.GetTempPath(), $"aquasynth-dx7-render-{Guid.NewGuid():N}");
Directory.CreateDirectory(tempDir); Directory.CreateDirectory(tempDir);
try try
{ {
@ -945,7 +945,7 @@ internal static class DexedPyRenderer
var python = await FindPythonWithDexedAsync(cancellationToken); var python = await FindPythonWithDexedAsync(cancellationToken);
if (python is null) return null; if (python is null) return null;
var tempDir = Path.Combine(Path.GetTempPath(), $"aquarium-dx7-patch-render-{Guid.NewGuid():N}"); var tempDir = Path.Combine(Path.GetTempPath(), $"aquasynth-dx7-patch-render-{Guid.NewGuid():N}");
Directory.CreateDirectory(tempDir); Directory.CreateDirectory(tempDir);
try try
{ {
@ -988,7 +988,7 @@ internal static class DexedPyRenderer
private static async Task<PythonCommand?> FindPythonWithDexedAsync(CancellationToken cancellationToken) private static async Task<PythonCommand?> FindPythonWithDexedAsync(CancellationToken cancellationToken)
{ {
var configuredPython = Environment.GetEnvironmentVariable("AQUARIUM_DX7_PYTHON"); var configuredPython = Environment.GetEnvironmentVariable("AQUASYNTH_DX7_PYTHON");
var candidates = new List<PythonCommand>(); var candidates = new List<PythonCommand>();
if (!string.IsNullOrWhiteSpace(configuredPython)) if (!string.IsNullOrWhiteSpace(configuredPython))
{ {

View file

@ -1,6 +1,6 @@
using AquariumSynth.Dsl; using AquaSynth.Dsl;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class Dx7SysExTests public sealed class Dx7SysExTests
{ {
@ -221,7 +221,7 @@ public sealed class Dx7SysExTests
} }
[Fact] [Fact]
public void CalibratesDx7OutputLevelToAquariumRouteIndex() public void CalibratesDx7OutputLevelToAquaSynthRouteIndex()
{ {
var fullScalePhaseDeviationRadians = 2 * Dx7SysEx.OperatorOutputAmplitude(99) * Dx7SysEx.OperatorModulationRouteIndex; var fullScalePhaseDeviationRadians = 2 * Dx7SysEx.OperatorOutputAmplitude(99) * Dx7SysEx.OperatorModulationRouteIndex;
@ -251,7 +251,7 @@ public sealed class Dx7SysExTests
} }
[Fact] [Fact]
public void CalibratesDx7FeedbackToAquariumFeedbackAmount() public void CalibratesDx7FeedbackToAquaSynthFeedbackAmount()
{ {
Assert.Equal(0, Dx7SysEx.OperatorFeedbackAmount(0)); Assert.Equal(0, Dx7SysEx.OperatorFeedbackAmount(0));
Assert.Equal(0.19f, Dx7SysEx.OperatorFeedbackAmount(5)); Assert.Equal(0.19f, Dx7SysEx.OperatorFeedbackAmount(5));

View file

@ -10,4 +10,4 @@ Artifacts artifact 152, "DX7 patches 1".
- SHA-256: `5AC120DF3A538DFB9A4B17A218F71B1D136269D2C30A33E676C1D6DDE2AC3AD2` - SHA-256: `5AC120DF3A538DFB9A4B17A218F71B1D136269D2C30A33E676C1D6DDE2AC3AD2`
This fixture is test/development material only. It must not be packed into This fixture is test/development material only. It must not be packed into
`AquariumSynth.Dsl.nupkg`. `AquaSynth.Dsl.nupkg`.

View file

@ -1,6 +1,6 @@
# Project-Authored ZynAddSubFX Fixtures # Project-Authored ZynAddSubFX Fixtures
These `.xiz` files are tiny Aquarium-authored XML fixtures shaped like These `.xiz` files are tiny AquaSynth-authored XML fixtures shaped like
ZynAddSubFX instrument files. They are not copied from an upstream Zyn bank. ZynAddSubFX instrument files. They are not copied from an upstream Zyn bank.
Purpose: Purpose:
@ -19,4 +19,4 @@ Current fixtures:
- `pad-harmonic.xiz` - `pad-harmonic.xiz`
- `vocal-layer.xiz` - `vocal-layer.xiz`
License: project-authored for AquariumSynthCSharp tests. License: project-authored for AquaSynth tests.

View file

@ -4,7 +4,7 @@
<INSTRUMENT> <INSTRUMENT>
<INFO> <INFO>
<string name="name">AQ Additive Lead</string> <string name="name">AQ Additive Lead</string>
<string name="author">AquariumSynthCSharp tests</string> <string name="author">AquaSynth tests</string>
<string name="comments">Project-authored additive fixture.</string> <string name="comments">Project-authored additive fixture.</string>
<par name="type" value="0"/> <par name="type" value="0"/>
</INFO> </INFO>

View file

@ -4,7 +4,7 @@
<INSTRUMENT> <INSTRUMENT>
<INFO> <INFO>
<string name="name">AQ Pad Harmonic</string> <string name="name">AQ Pad Harmonic</string>
<string name="author">AquariumSynthCSharp tests</string> <string name="author">AquaSynth tests</string>
<string name="comments">Project-authored PAD fixture with explicit oscillator harmonics.</string> <string name="comments">Project-authored PAD fixture with explicit oscillator harmonics.</string>
<par name="type" value="0"/> <par name="type" value="0"/>
</INFO> </INFO>

View file

@ -4,7 +4,7 @@
<INSTRUMENT> <INSTRUMENT>
<INFO> <INFO>
<string name="name">AQ Pad Pure</string> <string name="name">AQ Pad Pure</string>
<string name="author">AquariumSynthCSharp tests</string> <string name="author">AquaSynth tests</string>
<string name="comments">Project-authored default PAD fixture for pure source-table parity.</string> <string name="comments">Project-authored default PAD fixture for pure source-table parity.</string>
<par name="type" value="0"/> <par name="type" value="0"/>
</INFO> </INFO>

View file

@ -4,7 +4,7 @@
<INSTRUMENT> <INSTRUMENT>
<INFO> <INFO>
<string name="name">AQ Pad Texture</string> <string name="name">AQ Pad Texture</string>
<string name="author">AquariumSynthCSharp tests</string> <string name="author">AquaSynth tests</string>
<string name="comments">Project-authored PAD fixture.</string> <string name="comments">Project-authored PAD fixture.</string>
<par name="type" value="0"/> <par name="type" value="0"/>
</INFO> </INFO>

View file

@ -4,7 +4,7 @@
<INSTRUMENT> <INSTRUMENT>
<INFO> <INFO>
<string name="name">AQ Vocal Layer</string> <string name="name">AQ Vocal Layer</string>
<string name="author">AquariumSynthCSharp tests</string> <string name="author">AquaSynth tests</string>
<string name="comments">Project-authored layered vocal/formant fixture.</string> <string name="comments">Project-authored layered vocal/formant fixture.</string>
<par name="type" value="0"/> <par name="type" value="0"/>
</INFO> </INFO>

View file

@ -1,7 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO.Compression; using System.IO.Compression;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class PackageBoundaryTests public sealed class PackageBoundaryTests
{ {
@ -9,20 +9,20 @@ public sealed class PackageBoundaryTests
public async Task NuGetPackageContainsOnlyPublishedSynthSurface() public async Task NuGetPackageContainsOnlyPublishedSynthSurface()
{ {
var root = RepositoryRoot(); var root = RepositoryRoot();
var outputDir = Path.Combine(Path.GetTempPath(), $"aquarium-pack-{Guid.NewGuid():N}"); var outputDir = Path.Combine(Path.GetTempPath(), $"aquasynth-pack-{Guid.NewGuid():N}");
Directory.CreateDirectory(outputDir); Directory.CreateDirectory(outputDir);
try try
{ {
var result = await RunAsync( var result = await RunAsync(
"dotnet", "dotnet",
["pack", Path.Combine(root, "src", "AquariumSynth.Dsl", "AquariumSynth.Dsl.csproj"), "-c", "Release", "--no-restore", "-o", outputDir]); ["pack", Path.Combine(root, "src", "AquaSynth.Dsl", "AquaSynth.Dsl.csproj"), "-c", "Release", "--no-restore", "-o", outputDir]);
Assert.Equal(0, result.ExitCode); Assert.Equal(0, result.ExitCode);
var package = Assert.Single(Directory.GetFiles(outputDir, "AquariumSynth.Dsl.*.nupkg")); var package = Assert.Single(Directory.GetFiles(outputDir, "AquaSynth.Dsl.*.nupkg"));
using var archive = ZipFile.OpenRead(package); using var archive = ZipFile.OpenRead(package);
var entries = archive.Entries.Select(entry => entry.FullName).ToArray(); var entries = archive.Entries.Select(entry => entry.FullName).ToArray();
Assert.Contains(entries, entry => entry.StartsWith("lib/net10.0/AquariumSynth.Dsl.dll", StringComparison.Ordinal)); Assert.Contains(entries, entry => entry.StartsWith("lib/net10.0/AquaSynth.Dsl.dll", StringComparison.Ordinal));
Assert.Contains("README.md", entries); Assert.Contains("README.md", entries);
Assert.DoesNotContain(entries, entry => entry.StartsWith("tests/", StringComparison.OrdinalIgnoreCase)); Assert.DoesNotContain(entries, entry => entry.StartsWith("tests/", StringComparison.OrdinalIgnoreCase));
Assert.DoesNotContain(entries, entry => entry.StartsWith("Fixtures/", StringComparison.OrdinalIgnoreCase)); Assert.DoesNotContain(entries, entry => entry.StartsWith("Fixtures/", StringComparison.OrdinalIgnoreCase));
@ -44,7 +44,7 @@ public sealed class PackageBoundaryTests
private static string RepositoryRoot() private static string RepositoryRoot()
{ {
var directory = new DirectoryInfo(AppContext.BaseDirectory); var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquariumSynthCSharp.slnx"))) while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquaSynth.slnx")))
{ {
directory = directory.Parent; directory = directory.Parent;
} }

View file

@ -1,6 +1,6 @@
using AquariumSynth.Dsl; using AquaSynth.Dsl;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class PatchScriptTests public sealed class PatchScriptTests
{ {
@ -161,9 +161,9 @@ public sealed class PatchScriptTests
} }
[Fact] [Fact]
public void AquariumPresetsExportFaust() public void AquaSynthPresetsExportFaust()
{ {
foreach (var patch in new[] { Presets.AquariumPluck(), Presets.AquariumHeartbeat(), Presets.AquariumVoice(), Presets.Sfxr("pickup") }) foreach (var patch in new[] { Presets.AquaSynthPluck(), Presets.AquaSynthHeartbeat(), Presets.AquaSynthVoice(), Presets.Sfxr("pickup") })
{ {
var export = FaustEmitter.Emit(patch); var export = FaustEmitter.Emit(patch);
Assert.Contains("process =", export.Source); Assert.Contains("process =", export.Source);
@ -832,7 +832,7 @@ public sealed class PatchScriptTests
public async Task FaustCompilerCanEmitCSharpWhenInstalled() public async Task FaustCompilerCanEmitCSharpWhenInstalled()
{ {
var export = FaustEmitter.EmitScript("v w=sin f=440 g=.2 s=.1 d=.2"); var export = FaustEmitter.EmitScript("v w=sin f=440 g=.2 s=.1 d=.2");
var output = Path.Combine(Path.GetTempPath(), $"aquarium-synth-{Guid.NewGuid():N}.cs"); var output = Path.Combine(Path.GetTempPath(), $"aquasynth-{Guid.NewGuid():N}.cs");
try try
{ {
var validation = await FaustCompiler.CompileAsync( var validation = await FaustCompiler.CompileAsync(
@ -916,7 +916,7 @@ public sealed class PatchScriptTests
private static string RepositoryRoot() private static string RepositoryRoot()
{ {
var directory = new DirectoryInfo(AppContext.BaseDirectory); var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquariumSynthCSharp.slnx"))) while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquaSynth.slnx")))
{ {
directory = directory.Parent; directory = directory.Parent;
} }

View file

@ -1,6 +1,6 @@
using AquariumSynth.Dsl; using AquaSynth.Dsl;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class SfxrPatchReferenceTests public sealed class SfxrPatchReferenceTests
{ {

View file

@ -1,8 +1,8 @@
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
using AquariumSynth.Dsl; using AquaSynth.Dsl;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class ZynInstrumentTests public sealed class ZynInstrumentTests
{ {
@ -18,7 +18,7 @@ public sealed class ZynInstrumentTests
ZynInstrumentReader.SourceForBytes("fixture://zyn/project/additive-lead.xiz", "project-authored", bytes)); ZynInstrumentReader.SourceForBytes("fixture://zyn/project/additive-lead.xiz", "project-authored", bytes));
Assert.Equal("AQ Additive Lead", instrument.Name); Assert.Equal("AQ Additive Lead", instrument.Name);
Assert.Equal("AquariumSynthCSharp tests", instrument.Author); Assert.Equal("AquaSynth tests", instrument.Author);
Assert.Contains(reference.Features, feature => feature.Name == "engine_add" && feature.Value == "1"); Assert.Contains(reference.Features, feature => feature.Name == "engine_add" && feature.Value == "1");
Assert.Contains(reference.Features, feature => feature.Name == "engine_sub" && feature.Value == "0"); Assert.Contains(reference.Features, feature => feature.Name == "engine_sub" && feature.Value == "0");
Assert.Contains(reference.Features, feature => feature.Name == "engine_pad" && feature.Value == "0"); Assert.Contains(reference.Features, feature => feature.Name == "engine_pad" && feature.Value == "0");
@ -148,11 +148,11 @@ public sealed class ZynInstrumentTests
} }
[Fact] [Fact]
public void RebuildsProjectAuthoredPadAsAquariumSpectrumScript() public void RebuildsProjectAuthoredPadAsAquaSynthSpectrumScript()
{ {
var path = FixturePath("ZynAddSubFX", "ProjectAuthored", "pad-harmonic.xiz"); var path = FixturePath("ZynAddSubFX", "ProjectAuthored", "pad-harmonic.xiz");
var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquariumScript(path, tableRootFrequencyHz: 77.7813f); var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript(path, tableRootFrequencyHz: 77.7813f);
var patch = PatchScript.Parse(rebuild.Script); var patch = PatchScript.Parse(rebuild.Script);
Assert.Equal("AQ Pad Harmonic", rebuild.InstrumentName); Assert.Equal("AQ Pad Harmonic", rebuild.InstrumentName);
@ -181,7 +181,7 @@ public sealed class ZynInstrumentTests
return; return;
} }
var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquariumScript(path, tableRootFrequencyHz: 109.999f); var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript(path, tableRootFrequencyHz: 109.999f);
var patch = PatchScript.Parse(rebuild.Script); var patch = PatchScript.Parse(rebuild.Script);
var bank = Assert.Single(patch.SpectralBanks); var bank = Assert.Single(patch.SpectralBanks);
@ -200,7 +200,7 @@ public sealed class ZynInstrumentTests
return; return;
} }
var rebuild = ZynInstrumentReader.RebuildEnabledPadsAsAquariumScript( var rebuild = ZynInstrumentReader.RebuildEnabledPadsAsAquaSynthScript(
path, path,
new Dictionary<int, ZynPadTableRoot> new Dictionary<int, ZynPadTableRoot>
{ {
@ -275,7 +275,7 @@ public sealed class ZynInstrumentTests
private static string RepositoryRoot() private static string RepositoryRoot()
{ {
var directory = new DirectoryInfo(AppContext.BaseDirectory); var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquariumSynthCSharp.slnx"))) while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquaSynth.slnx")))
{ {
directory = directory.Parent; directory = directory.Parent;
} }

View file

@ -1,6 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
namespace AquariumSynth.Dsl.Tests; namespace AquaSynth.Dsl.Tests;
public sealed class ZynReferenceParityTests public sealed class ZynReferenceParityTests
{ {
@ -12,8 +12,8 @@ public sealed class ZynReferenceParityTests
Assert.True(Directory.Exists(zynRoot), "ZynAddSubFX reference source submodule is missing. Run `git submodule update --init --recursive`."); Assert.True(Directory.Exists(zynRoot), "ZynAddSubFX reference source submodule is missing. Run `git submodule update --init --recursive`.");
Assert.True(File.Exists(Path.Combine(zynRoot, "COPYING")), "ZynAddSubFX reference source should keep its GPL license file."); Assert.True(File.Exists(Path.Combine(zynRoot, "COPYING")), "ZynAddSubFX reference source should keep its GPL license file.");
Assert.True(File.Exists(Path.Combine(zynRoot, "src", "Params", "PADnoteParameters.cpp")), "PAD parameter implementation is the parity source, not Aquarium runtime code."); Assert.True(File.Exists(Path.Combine(zynRoot, "src", "Params", "PADnoteParameters.cpp")), "PAD parameter implementation is the parity source, not AquaSynth runtime code.");
Assert.True(File.Exists(Path.Combine(zynRoot, "src", "Synth", "PADnote.cpp")), "PAD note implementation is the parity source, not Aquarium runtime code."); Assert.True(File.Exists(Path.Combine(zynRoot, "src", "Synth", "PADnote.cpp")), "PAD note implementation is the parity source, not AquaSynth runtime code.");
Assert.Contains("GNU GENERAL PUBLIC LICENSE", await File.ReadAllTextAsync(Path.Combine(zynRoot, "COPYING"))); Assert.Contains("GNU GENERAL PUBLIC LICENSE", await File.ReadAllTextAsync(Path.Combine(zynRoot, "COPYING")));
var revision = await RunAsync("git", ["-C", zynRoot, "rev-parse", "HEAD"]); var revision = await RunAsync("git", ["-C", zynRoot, "rev-parse", "HEAD"]);
@ -35,12 +35,12 @@ public sealed class ZynReferenceParityTests
var artifactDir = Path.Combine(root, "artifacts", "parity", "zyn-pad-reference", RunFolderName("pad-texture")); var artifactDir = Path.Combine(root, "artifacts", "parity", "zyn-pad-reference", RunFolderName("pad-texture"));
Directory.CreateDirectory(artifactDir); Directory.CreateDirectory(artifactDir);
var input = Path.Combine(root, "tests", "AquariumSynth.Dsl.Tests", "Fixtures", "ZynAddSubFX", "ProjectAuthored", "pad-texture.xiz"); var input = Path.Combine(root, "tests", "AquaSynth.Dsl.Tests", "Fixtures", "ZynAddSubFX", "ProjectAuthored", "pad-texture.xiz");
var zynRaw = Path.Combine(artifactDir, "reference-zyn.f32"); var zynRaw = Path.Combine(artifactDir, "reference-zyn.f32");
var zynWav = Path.Combine(artifactDir, "reference-zyn.wav"); var zynWav = Path.Combine(artifactDir, "reference-zyn.wav");
var zynTableRaw = Path.Combine(artifactDir, "reference-zyn-table0.f32"); var zynTableRaw = Path.Combine(artifactDir, "reference-zyn-table0.f32");
var zynTableWav = Path.Combine(artifactDir, "reference-zyn-table0.wav"); var zynTableWav = Path.Combine(artifactDir, "reference-zyn-table0.wav");
var aquaWav = Path.Combine(artifactDir, "candidate-aquarium.wav"); var aquaWav = Path.Combine(artifactDir, "candidate-aquasynth.wav");
var reportPath = Path.Combine(artifactDir, "report.txt"); var reportPath = Path.Combine(artifactDir, "report.txt");
var command = string.Join(' ', [ var command = string.Join(' ', [
@ -93,7 +93,7 @@ public sealed class ZynReferenceParityTests
$"zyn_table_rms: {Rms(zynTableSamples):0.######}" $"zyn_table_rms: {Rms(zynTableSamples):0.######}"
}; };
var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquariumScript(input, ParseBaseFrequency(tableReference.Stdout)); var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript(input, ParseBaseFrequency(tableReference.Stdout));
report.AddRange(rebuild.MatchedFeatures.Select(feature => $"matched_{feature.Name}: {feature.Value}")); report.AddRange(rebuild.MatchedFeatures.Select(feature => $"matched_{feature.Name}: {feature.Value}"));
report.AddRange(rebuild.MissingFeatures.Select(feature => $"missing_{feature.Name}: {feature.Value} - {feature.Notes}")); report.AddRange(rebuild.MissingFeatures.Select(feature => $"missing_{feature.Name}: {feature.Value} - {feature.Notes}"));
var aquaExport = FaustEmitter.EmitScript(rebuild.Script, new FaustExportOptions("zyn_pad_texture")); var aquaExport = FaustEmitter.EmitScript(rebuild.Script, new FaustExportOptions("zyn_pad_texture"));
@ -129,7 +129,7 @@ public sealed class ZynReferenceParityTests
return; return;
} }
var fixtureDir = Path.Combine(root, "tests", "AquariumSynth.Dsl.Tests", "Fixtures", "ZynAddSubFX", "ProjectAuthored"); var fixtureDir = Path.Combine(root, "tests", "AquaSynth.Dsl.Tests", "Fixtures", "ZynAddSubFX", "ProjectAuthored");
var artifactDir = Path.Combine(root, "artifacts", "parity", "zyn-pad-fixtures"); var artifactDir = Path.Combine(root, "artifacts", "parity", "zyn-pad-fixtures");
Directory.CreateDirectory(artifactDir); Directory.CreateDirectory(artifactDir);
@ -156,7 +156,7 @@ public sealed class ZynReferenceParityTests
Assert.Equal(0, note.ExitCode); Assert.Equal(0, note.ExitCode);
var table = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, tableRaw, 0, "0")]); var table = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, tableRaw, 0, "0")]);
Assert.Equal(0, table.ExitCode); Assert.Equal(0, table.ExitCode);
var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquariumScript(input, ParseBaseFrequency(table.Stdout)); var rebuild = ZynInstrumentReader.RebuildFirstPadAsAquaSynthScript(input, ParseBaseFrequency(table.Stdout));
await File.WriteAllTextAsync(Path.Combine(runDir, "candidate.aqua"), rebuild.Script); await File.WriteAllTextAsync(Path.Combine(runDir, "candidate.aqua"), rebuild.Script);
var noteSamples = await ReadFloat32Async(noteRaw); var noteSamples = await ReadFloat32Async(noteRaw);
@ -254,7 +254,7 @@ public sealed class ZynReferenceParityTests
var noteWav = Path.Combine(runDir, "reference-zyn.wav"); var noteWav = Path.Combine(runDir, "reference-zyn.wav");
var tableRaw = Path.Combine(runDir, "reference-zyn-table0.f32"); var tableRaw = Path.Combine(runDir, "reference-zyn-table0.f32");
var tableWav = Path.Combine(runDir, "reference-zyn-table0.wav"); var tableWav = Path.Combine(runDir, "reference-zyn-table0.wav");
var aquaWav = Path.Combine(runDir, "candidate-aquarium.wav"); var aquaWav = Path.Combine(runDir, "candidate-aquasynth.wav");
var noteStdouts = new List<string>(); var noteStdouts = new List<string>();
var noteLayers = new List<float[]>(); var noteLayers = new List<float[]>();
@ -290,7 +290,7 @@ public sealed class ZynReferenceParityTests
} }
var noteSamples = SumSamples(noteLayers); var noteSamples = SumSamples(noteLayers);
var rebuild = ZynInstrumentReader.RebuildEnabledPadsAsAquariumScript(input, tableRoots); var rebuild = ZynInstrumentReader.RebuildEnabledPadsAsAquaSynthScript(input, tableRoots);
await File.WriteAllTextAsync(Path.Combine(runDir, "candidate.aqua"), rebuild.Script); await File.WriteAllTextAsync(Path.Combine(runDir, "candidate.aqua"), rebuild.Script);
Assert.True(noteSamples.Length > 0); Assert.True(noteSamples.Length > 0);
@ -361,7 +361,7 @@ public sealed class ZynReferenceParityTests
private static string RepositoryRoot() private static string RepositoryRoot()
{ {
var directory = new DirectoryInfo(AppContext.BaseDirectory); var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquariumSynthCSharp.slnx"))) while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquaSynth.slnx")))
{ {
directory = directory.Parent; directory = directory.Parent;
} }

View file

@ -1,8 +1,8 @@
/* /*
Test-only AquariumSynthCSharp ZynAddSubFX PAD oracle. Test-only AquaSynth ZynAddSubFX PAD oracle.
This file links against the pinned ZynAddSubFX reference source in an ignored This file links against the pinned ZynAddSubFX reference source in an ignored
artifact build. It is development/parity tooling, not Aquarium runtime code. artifact build. It is development/parity tooling, not AquaSynth runtime code.
*/ */
#include "../globals.h" #include "../globals.h"