Add GPL Zyn pad survey fixtures
This commit is contained in:
parent
19210ca33f
commit
2c1684e7a7
7 changed files with 186 additions and 14 deletions
|
|
@ -6,16 +6,17 @@ Find the ZynAddSubFX instruments most likely to break Aquarium's current patch
|
|||
language before spending time on syntax polish.
|
||||
|
||||
This is a feature-pressure survey, not an audio parity claim. The upstream Zyn
|
||||
instrument bank is not vendored in this repo because preset-level license and
|
||||
provenance are not explicit enough at the bank root. The survey records derived
|
||||
metadata only.
|
||||
instrument bank is GPL reference feedstock through the pinned Zyn submodule
|
||||
tree. It is development/test material only: useful for reports, parity renders,
|
||||
and pressure selection, never package content or Aquarium patch-library stock.
|
||||
|
||||
## Source Checked
|
||||
|
||||
- Repository: `https://github.com/zynaddsubfx/instruments`
|
||||
- Observed commit: `e9f64a9 Add files via upload`
|
||||
- Files scanned: `1358` `.xiz` instruments
|
||||
- Reader result after hardening: `1358` parsed, `0` errors
|
||||
- Observed commit: `c5c912131b31df5fdf372d2f06a25aaf2375837f`
|
||||
- Files scanned: `1318` `.xiz` instruments
|
||||
- Reader result after hardening: `1318` parsed, `0` errors
|
||||
- Local path: `external/zynaddsubfx/instruments/banks`
|
||||
|
||||
## Scoring
|
||||
|
||||
|
|
@ -38,6 +39,21 @@ thesis.
|
|||
| 220 | `Drums/0001-Drums Kit1.xiz` | Drums Kit | 11 enabled kit items, ADD/SUB mix, 30 envelopes, 11 free envelopes, 15 LFOs, 12 filters. A smaller drum-kit target than Oliver's monster. |
|
||||
| 181 | `olivers-100/0124-Simple Clonewheel.xiz` | Simple Clonewheel | 8 enabled kit items with 7 PAD engines. Best early pressure for layered PAD routing without drum semantics. |
|
||||
|
||||
## Upstream PAD Listening Batch
|
||||
|
||||
`ZynPadReferenceRendererSurveysUpstreamGplPadFixturesWhenBuilt` renders a small
|
||||
GPL PAD batch through the real Zyn oracle when the renderer is built:
|
||||
|
||||
- `Pads/0002-sin2x pad.xiz`
|
||||
- `Pads/0065-Soft Pad.xiz`
|
||||
- `Dual/0008-Organ Choir Pad2.xiz`
|
||||
- `Laba170bank/0098-DoublePadBass.xiz`
|
||||
- `Companion/0121-Ghost Ensemble.xiz`
|
||||
|
||||
The WAVs and table previews land under
|
||||
`artifacts/parity/zyn-upstream-pad-fixtures/`. These are listening and parity
|
||||
fixtures, not library candidates.
|
||||
|
||||
## Pressure Conclusions
|
||||
|
||||
- **Named layers are real.** Anonymous Aquarium voices can sketch small Zyn
|
||||
|
|
|
|||
|
|
@ -71,3 +71,4 @@
|
|||
{"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-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-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."}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ surfaces:
|
|||
reference_roadmap: docs/reference-synth-roadmap.md
|
||||
dx7_public_domain_fixtures: tests/AquariumSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain
|
||||
zyn_reference_source: external/zynaddsubfx
|
||||
zyn_upstream_instrument_bank: external/zynaddsubfx/instruments
|
||||
zyn_reference_build_script: tools/zyn-reference/build-zyn-reference.ps1
|
||||
zyn_pad_reference_renderer: tools/zyn-reference/ZynPadReference.cpp
|
||||
zyn_reference_build_patches: tools/zyn-reference/patches
|
||||
|
|
@ -60,3 +61,4 @@ checks:
|
|||
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
|
||||
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
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
},
|
||||
"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. 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, not translation. Upstream Zyn presets live in a separate bank submodule and should not be vendored until license/provenance is explicit. 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 observed upstream instrument bank at commit e9f64a9: 1358/1358 .xiz files. 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, not translation. 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. 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.",
|
||||
"next": "Use the Zyn PAD oracle report to isolate whether the main mismatch is source table generation, note playback/gain, envelope shape, or fixture mapping before adding more Zyn DSL surface."
|
||||
},
|
||||
"note_and_envelope_model": {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ current_mechanism:
|
|||
dx7_reference_fixtures: tests/AquariumSynth.Dsl.Tests/Fixtures/Dx7/PublicDomain
|
||||
dx7_reference_renderer_tests: tests/AquariumSynth.Dsl.Tests/Dx7ReferenceParityTests.cs
|
||||
zyn_reference_source: external/zynaddsubfx
|
||||
zyn_upstream_instrument_bank: external/zynaddsubfx/instruments
|
||||
zyn_reference_build_script: tools/zyn-reference/build-zyn-reference.ps1
|
||||
zyn_pad_reference_renderer: tools/zyn-reference/ZynPadReference.cpp
|
||||
zyn_reference_reader: src/AquariumSynth.Dsl/ZynInstrument.cs
|
||||
|
|
@ -74,8 +75,8 @@ live_invariants:
|
|||
- 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.
|
||||
- Reference fixtures must preserve provenance, license status, source hash, and feature pressure.
|
||||
- Public-domain reference fixtures, dexed-py helpers, and pinned ZynAddSubFX source are test/development material only; the NuGet package must ship only AquariumSynth.Dsl.
|
||||
- ZynAddSubFX import starts as feature extraction, not translation. Use project-authored `.xiz` fixtures until external preset-bank license/provenance is explicit. The Zyn engine source is pinned under `external/zynaddsubfx` for parity oracles only, not Aquarium runtime code.
|
||||
- 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.
|
||||
- ZynAddSubFX import starts as feature extraction, not translation. 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
|
@ -101,7 +102,7 @@ reference_targets:
|
|||
priority: 2
|
||||
purpose: "Additive, subtractive, PAD, formant, kit, and layered-instrument pressure test."
|
||||
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. 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`. The optional PAD reports emit note and source-table WAV/listening artifacts. After cutting unearned noise/drive/soft-clip, adding interpolated table reads, raising Aquarium spectral tables to 131072 frames, splitting table root from playback frequency, removing gratuitous spread/vibrato, collapsing same-note PAD voices into one coherent table, and trimming gain, the project-authored PAD report is close by ear and still not formally passing: log-mel 0.35669, envelope 0.347572, RMS ratio 1.001704, centroid ratio 1.004072, score 0.6211. The PAD fixture survey shows `pad-pure` and `pad-texture` render identically through Zyn, while `pad-harmonic` changes the source and becomes the next real rung. A non-vendored upstream bank survey parsed 1358/1358 .xiz files and identified named kit/layer routing as the first owner abstraction."
|
||||
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. 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`. The optional PAD reports emit note and source-table WAV/listening artifacts. After cutting unearned noise/drive/soft-clip, adding interpolated table reads, raising Aquarium spectral tables to 131072 frames, splitting table root from playback frequency, removing gratuitous spread/vibrato, collapsing same-note PAD voices into one coherent table, and trimming gain, the project-authored PAD report is close by ear and still not formally passing: log-mel 0.35669, envelope 0.347572, RMS ratio 1.001704, centroid ratio 1.004072, score 0.6211. The PAD fixture survey shows `pad-pure` and `pad-texture` render identically through Zyn, while `pad-harmonic` changes the source and becomes the next real rung. 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:
|
||||
- "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."
|
||||
|
|
@ -167,6 +168,18 @@ next_agent_start_here:
|
|||
- "dotnet test AquariumSynthCSharp.slnx --no-restore"
|
||||
|
||||
completed_slices:
|
||||
- date: 2026-05-15
|
||||
summary: >
|
||||
Initialized the upstream GPL ZynAddSubFX instrument bank submodule and
|
||||
made it explicit test-only feedstock. Added an optional survey test that
|
||||
parses 1318 `.xiz` instruments and writes
|
||||
`artifacts/parity/zyn-upstream-bank-survey/report.txt`, plus an optional
|
||||
oracle render batch for five upstream PAD instruments under
|
||||
`artifacts/parity/zyn-upstream-pad-fixtures/`. The batch includes
|
||||
`Pads/0002-sin2x pad.xiz`, `Pads/0065-Soft Pad.xiz`,
|
||||
`Dual/0008-Organ Choir Pad2.xiz`, `Laba170bank/0098-DoublePadBass.xiz`,
|
||||
and `Companion/0121-Ghost Ensemble.xiz`.
|
||||
verification: "dotnet test AquariumSynthCSharp.slnx --no-restore --filter FullyQualifiedName~Zyn: 14 passed"
|
||||
- date: 2026-05-15
|
||||
summary: >
|
||||
Added more project-authored PAD fixtures and a Zyn-only PAD fixture
|
||||
|
|
|
|||
|
|
@ -147,9 +147,61 @@ public sealed class ZynInstrumentTests
|
|||
Assert.Contains(survey[0].Features, feature => feature.Name == "formant_filter_count" && feature.Value == "1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SurveysUpstreamGplInstrumentBankWhenAvailable()
|
||||
{
|
||||
var root = RepositoryRoot();
|
||||
var bankRoot = Path.Combine(root, "external", "zynaddsubfx", "instruments", "banks");
|
||||
if (!Directory.Exists(bankRoot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var files = Directory.GetFiles(bankRoot, "*.xiz", SearchOption.AllDirectories);
|
||||
if (files.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var survey = ZynInstrumentSurvey.RankDirectory(bankRoot, take: 20);
|
||||
Assert.NotEmpty(survey);
|
||||
Assert.Contains(survey, item => item.Features.Any(feature => feature.Name == "engine_pad" && feature.Value != "0"));
|
||||
|
||||
var artifactDir = Path.Combine(root, "artifacts", "parity", "zyn-upstream-bank-survey");
|
||||
Directory.CreateDirectory(artifactDir);
|
||||
var report = new List<string>
|
||||
{
|
||||
"ZynAddSubFX upstream GPL instrument bank survey",
|
||||
"source: https://github.com/zynaddsubfx/instruments",
|
||||
"license: GPL test/development corpus via upstream ZynAddSubFX instruments submodule",
|
||||
"usage: feature pressure and parity fixtures only; never NuGet/package content",
|
||||
$"bank_root: {bankRoot}",
|
||||
$"xiz_count: {files.Length}",
|
||||
""
|
||||
};
|
||||
foreach (var item in survey)
|
||||
{
|
||||
var features = item.Features.ToDictionary(feature => feature.Name, feature => feature.Value);
|
||||
report.Add($"{item.ComplexityScore}\t{item.Path}\t{item.Name}\tpad={features["engine_pad"]} add={features["engine_add"]} sub={features["engine_sub"]} layers={features["enabled_kit_items"]} env={features["envelope_count"]} free={features["free_envelope_count"]} lfo={features["lfo_count"]} filter={features["filter_count"]} formant={features["formant_filter_count"]} fx={features["effect_count"]}");
|
||||
}
|
||||
|
||||
await File.WriteAllLinesAsync(Path.Combine(artifactDir, "report.txt"), report);
|
||||
}
|
||||
|
||||
private static void AssertFeature(ZynInstrument instrument, string name, string value) =>
|
||||
Assert.Contains(instrument.Features(), feature => feature.Name == name && feature.Value == value);
|
||||
|
||||
private static string FixturePath(params string[] parts) =>
|
||||
Path.Combine([AppContext.BaseDirectory, "Fixtures", .. parts]);
|
||||
|
||||
private static string RepositoryRoot()
|
||||
{
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AquariumSynthCSharp.slnx")))
|
||||
{
|
||||
directory = directory.Parent;
|
||||
}
|
||||
|
||||
return directory?.FullName ?? throw new InvalidOperationException("could not find repository root");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,9 +146,9 @@ public sealed class ZynReferenceParityTests
|
|||
var tableRaw = Path.Combine(artifactDir, $"{name}-table0.f32");
|
||||
var tableWav = Path.Combine(artifactDir, $"{name}-table0.wav");
|
||||
|
||||
var note = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, noteRaw, "note", "261.6256", "1.5")]);
|
||||
var note = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, noteRaw, 0, "note", "261.6256", "1.5")]);
|
||||
Assert.Equal(0, note.ExitCode);
|
||||
var table = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, tableRaw, "0")]);
|
||||
var table = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, tableRaw, 0, "0")]);
|
||||
Assert.Equal(0, table.ExitCode);
|
||||
|
||||
var noteSamples = await ReadFloat32Async(noteRaw);
|
||||
|
|
@ -174,6 +174,88 @@ public sealed class ZynReferenceParityTests
|
|||
await File.WriteAllLinesAsync(Path.Combine(artifactDir, "report.txt"), report);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ZynPadReferenceRendererSurveysUpstreamGplPadFixturesWhenBuilt()
|
||||
{
|
||||
var root = RepositoryRoot();
|
||||
var bash = @"C:\msys64\usr\bin\bash.exe";
|
||||
var renderer = Path.Combine(root, "artifacts", "zyn-reference-build-msys", "src", "Tests", "ZynPadReference.exe");
|
||||
var bankRoot = Path.Combine(root, "external", "zynaddsubfx", "instruments", "banks");
|
||||
if (!File.Exists(bash) || !File.Exists(renderer) || !Directory.Exists(bankRoot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var selected = new[]
|
||||
{
|
||||
Path.Combine("Pads", "0002-sin2x pad.xiz"),
|
||||
Path.Combine("Pads", "0065-Soft Pad.xiz"),
|
||||
Path.Combine("Dual", "0008-Organ Choir Pad2.xiz"),
|
||||
Path.Combine("Laba170bank", "0098-DoublePadBass.xiz"),
|
||||
Path.Combine("Companion", "0121-Ghost Ensemble.xiz")
|
||||
};
|
||||
var artifactDir = Path.Combine(root, "artifacts", "parity", "zyn-upstream-pad-fixtures");
|
||||
Directory.CreateDirectory(artifactDir);
|
||||
|
||||
var report = new List<string>
|
||||
{
|
||||
"Zyn upstream GPL PAD fixture survey",
|
||||
"source: https://github.com/zynaddsubfx/instruments",
|
||||
"license: GPL test/development corpus via upstream ZynAddSubFX instruments submodule",
|
||||
$"renderer: {renderer}"
|
||||
};
|
||||
|
||||
foreach (var relative in selected)
|
||||
{
|
||||
var input = Path.Combine(bankRoot, relative);
|
||||
if (!File.Exists(input))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var instrument = ZynInstrumentReader.ParseFile(input);
|
||||
var kitIndex = instrument.KitItems.FirstOrDefault(item => item.Enabled && item.Engines.Contains(ZynEngine.PadSynth))?.Id ?? -1;
|
||||
if (kitIndex < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = SanitizeArtifactName(Path.GetFileNameWithoutExtension(input));
|
||||
var noteRaw = Path.Combine(artifactDir, $"{name}-zyn.f32");
|
||||
var noteWav = Path.Combine(artifactDir, $"{name}-zyn.wav");
|
||||
var tableRaw = Path.Combine(artifactDir, $"{name}-table0.f32");
|
||||
var tableWav = Path.Combine(artifactDir, $"{name}-table0.wav");
|
||||
|
||||
var note = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, noteRaw, kitIndex, "note", "261.6256", "1.5")]);
|
||||
Assert.Equal(0, note.ExitCode);
|
||||
var table = await RunAsync(bash, ["-lc", ZynPadCommand(root, input, tableRaw, kitIndex, "0")]);
|
||||
Assert.Equal(0, table.ExitCode);
|
||||
|
||||
var noteSamples = await ReadFloat32Async(noteRaw);
|
||||
var tableSamples = await ReadFloat32Async(tableRaw);
|
||||
Assert.True(noteSamples.Length > 0);
|
||||
Assert.True(tableSamples.Length > noteSamples.Length);
|
||||
WriteWav(noteWav, noteSamples, 44100, 0.9f);
|
||||
WriteWav(tableWav, tableSamples, 44100, 0.9f);
|
||||
|
||||
var features = AudioAnalyzer.AnalyzeAudio(noteSamples).Features;
|
||||
report.Add("");
|
||||
report.Add($"fixture: {relative}");
|
||||
report.Add($"instrument: {instrument.Name}");
|
||||
report.Add($"kit_index: {kitIndex}");
|
||||
report.Add($"note_stdout: {note.Stdout.Trim()}");
|
||||
report.Add($"table_stdout: {table.Stdout.Trim()}");
|
||||
report.Add($"peak: {Peak(noteSamples):0.######}");
|
||||
report.Add($"rms: {Rms(noteSamples):0.######}");
|
||||
report.Add($"duration: {features.DurationSeconds:0.######}");
|
||||
report.Add($"centroid: {features.SpectralCentroidHz:0.######}");
|
||||
report.Add($"table_peak: {Peak(tableSamples):0.######}");
|
||||
report.Add($"table_rms: {Rms(tableSamples):0.######}");
|
||||
}
|
||||
|
||||
await File.WriteAllLinesAsync(Path.Combine(artifactDir, "report.txt"), report);
|
||||
}
|
||||
|
||||
private static string RepositoryRoot()
|
||||
{
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
|
|
@ -217,17 +299,23 @@ public sealed class ZynReferenceParityTests
|
|||
|
||||
private static string BashQuote(string value) => "'" + value.Replace("'", "'\\''") + "'";
|
||||
|
||||
private static string ZynPadCommand(string root, string input, string output, params string[] modeArguments) =>
|
||||
private static string ZynPadCommand(string root, string input, string output, int kitIndex, params string[] modeArguments) =>
|
||||
string.Join(' ', [
|
||||
"export PATH=/mingw64/bin:/usr/bin:/bin:$PATH;",
|
||||
$"cd {BashQuote(ToMsysPath(root))};",
|
||||
"./artifacts/zyn-reference-build-msys/src/Tests/ZynPadReference.exe",
|
||||
BashQuote(ToMsysPath(input)),
|
||||
BashQuote(ToMsysPath(output)),
|
||||
"0",
|
||||
kitIndex.ToString(System.Globalization.CultureInfo.InvariantCulture),
|
||||
..modeArguments
|
||||
]);
|
||||
|
||||
private static string SanitizeArtifactName(string name)
|
||||
{
|
||||
var chars = name.Select(ch => char.IsLetterOrDigit(ch) ? char.ToLowerInvariant(ch) : '-').ToArray();
|
||||
return string.Join('-', new string(chars).Split('-', StringSplitOptions.RemoveEmptyEntries));
|
||||
}
|
||||
|
||||
private static async Task<float[]> ReadFloat32Async(string path)
|
||||
{
|
||||
var bytes = await File.ReadAllBytesAsync(path);
|
||||
|
|
|
|||
Loading…
Reference in a new issue