Survey worst-case Zyn feature pressure
This commit is contained in:
parent
9735ef0bdd
commit
8426ca50f4
9 changed files with 234 additions and 20 deletions
|
|
@ -129,6 +129,11 @@ license/provenance is explicit enough for this repo. Use project-authored
|
|||
fixtures for parser shape and only promote external fixtures with clean source
|
||||
notes.
|
||||
|
||||
The current external-bank pressure survey is in
|
||||
[zynaddsubfx-pressure-survey.md](zynaddsubfx-pressure-survey.md). Its strongest
|
||||
signal is that named kit/layer routing should probably precede syntax golf for
|
||||
PAD, additive banks, or formant motion.
|
||||
|
||||
Sources:
|
||||
|
||||
- [ZynAddSubFX](https://github.com/zynaddsubfx/zynaddsubfx)
|
||||
|
|
|
|||
63
docs/zynaddsubfx-pressure-survey.md
Normal file
63
docs/zynaddsubfx-pressure-survey.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# ZynAddSubFX Pressure Survey
|
||||
|
||||
## Objective
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
## Scoring
|
||||
|
||||
`ZynInstrumentSurvey.ComplexityScore` ranks instruments by enabled kit items,
|
||||
layering, active ADD/SUB/PAD engines, envelope count, free-envelope count, LFO
|
||||
count, filter count, formant-filter count, and effect count.
|
||||
|
||||
The score is deliberately crude. It is a triage knife, not a musicological
|
||||
thesis.
|
||||
|
||||
## Top Pressure Targets
|
||||
|
||||
| Score | Path | Name | Why It Hurts |
|
||||
| ---: | --- | --- | --- |
|
||||
| 666 | `olivers-100/0032-Drum Kit.xiz` | Drum Kit | 16 enabled kit items, ADD/SUB/PAD mixed engines, 106 envelopes, 25 free envelopes, 73 LFOs, 37 filters, 3 formant filters. This is the named-layer/kit-routing boss fight. |
|
||||
| 398 | `Companion/0056-Wide Bass.xiz` | Wide Bass | Only 2 enabled kit items, but huge modulation/formant pressure: 86 envelopes, 54 LFOs, 18 filters, 18 formant filters. This is vowel/formant motion pressure wearing bass clothes. |
|
||||
| 357 | `Cris Owl Alvarez/0160-DRUMKIT toy drummer 2.xiz` | DRUMKIT toy drummer 2 | 12 enabled ADD kit items with 77 envelopes, 9 free envelopes, 38 LFOs, and 32 filters. Layered percussion without PAD escape hatches. |
|
||||
| 260 | `olivers-other/0038-Deep Zyn.xiz` | Deep Zyn | 4 ADD layers and 36 free envelopes. This is the free-envelope case without relying on mixed engines. |
|
||||
| 251 | `Companion/0121-Ghost Ensemble.xiz` | Ghost Ensemble | ADD/SUB/PAD all active, layered, 50 envelopes, 30 LFOs, 11 filters, 11 formant filters. Good broad-spectrum stress target. |
|
||||
| 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. |
|
||||
|
||||
## Pressure Conclusions
|
||||
|
||||
- **Named layers are real.** Anonymous Aquarium voices can sketch small Zyn
|
||||
layers, but drum kits and clonewheel-style instruments need stable layer names,
|
||||
key ranges, engine identity, and per-layer routing.
|
||||
- **Free envelopes are not optional forever.** Zyn's worst cases use dozens of
|
||||
free envelopes. ADSR plus staged operator envelopes will not remain honest for
|
||||
normal voice-layer work.
|
||||
- **Formant motion is bigger than static formant filters.** Wide Bass and Ghost
|
||||
Ensemble show that static formant banks are only the foothold.
|
||||
- **PAD is both source and layer pressure.** PADsynth-style patches are not just
|
||||
saw pads with slow attack; they need a spectral/wavetable source authority.
|
||||
- **Drum kits should not be first golf targets.** They are the best stress test
|
||||
for the model, but probably too broad for the first new syntax slice.
|
||||
|
||||
## Recommended Next Cut
|
||||
|
||||
Start with **named layer routing**, not PAD synthesis.
|
||||
|
||||
Reason: the worst Zyn instruments repeatedly combine multiple kit items and
|
||||
engines. A named layer surface would give additive banks, PAD sources, free
|
||||
envelopes, and formant motion somewhere coherent to live later. Without it, the
|
||||
next abstractions risk becoming loose fields on anonymous voices.
|
||||
|
|
@ -64,6 +64,12 @@ public sealed record ZynKitItem(
|
|||
int FormantFilterCount,
|
||||
int EffectCount);
|
||||
|
||||
public sealed record ZynInstrumentSurveyItem(
|
||||
string Path,
|
||||
string Name,
|
||||
int ComplexityScore,
|
||||
IReadOnlyList<ReferenceFeature> Features);
|
||||
|
||||
public enum ZynEngine
|
||||
{
|
||||
AddSynth,
|
||||
|
|
@ -78,8 +84,7 @@ public static class ZynInstrumentReader
|
|||
|
||||
public static ZynInstrument Parse(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
using var xml = OpenXmlStream(bytes);
|
||||
var document = XDocument.Load(xml, LoadOptions.None);
|
||||
var document = XDocument.Parse(XmlText(bytes).TrimStart(), LoadOptions.None);
|
||||
var instrument = document.Descendants("INSTRUMENT").FirstOrDefault()
|
||||
?? throw new ArgumentException("Zyn instrument XML is missing INSTRUMENT");
|
||||
var info = instrument.Element("INFO");
|
||||
|
|
@ -115,12 +120,12 @@ public static class ZynInstrumentReader
|
|||
.Count(element => element.Name.LocalName.EndsWith("_ENVELOPE", StringComparison.OrdinalIgnoreCase) &&
|
||||
BoolParam(element, "free_mode")),
|
||||
LfoCount: CountElements(item, name => name.EndsWith("_LFO", StringComparison.OrdinalIgnoreCase)),
|
||||
FilterCount: CountElements(item, name => name.Contains("FILTER", StringComparison.OrdinalIgnoreCase)),
|
||||
FormantFilterCount: CountElements(item, name => name.Contains("FORMANT", StringComparison.OrdinalIgnoreCase)),
|
||||
FilterCount: CountElements(item, name => name.Equals("FILTER", StringComparison.OrdinalIgnoreCase)),
|
||||
FormantFilterCount: CountElements(item, name => name.Equals("FORMANT_FILTER", StringComparison.OrdinalIgnoreCase)),
|
||||
EffectCount: CountElements(item, name => name.Contains("EFFECT", StringComparison.OrdinalIgnoreCase)));
|
||||
}
|
||||
|
||||
private static MemoryStream OpenXmlStream(ReadOnlySpan<byte> bytes)
|
||||
private static string XmlText(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
if (bytes.Length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b)
|
||||
{
|
||||
|
|
@ -128,11 +133,10 @@ public static class ZynInstrumentReader
|
|||
using var gzip = new GZipStream(compressed, CompressionMode.Decompress);
|
||||
var decompressed = new MemoryStream();
|
||||
gzip.CopyTo(decompressed);
|
||||
decompressed.Position = 0;
|
||||
return decompressed;
|
||||
return System.Text.Encoding.UTF8.GetString(decompressed.ToArray());
|
||||
}
|
||||
|
||||
return new MemoryStream(bytes.ToArray());
|
||||
return System.Text.Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
private static int CountElements(XElement root, Func<string, bool> predicate) =>
|
||||
|
|
@ -167,3 +171,44 @@ public static class ZynInstrumentReader
|
|||
private static string? AttributeValue(XElement element, string name) =>
|
||||
element.Attribute(name)?.Value;
|
||||
}
|
||||
|
||||
public static class ZynInstrumentSurvey
|
||||
{
|
||||
public static IReadOnlyList<ZynInstrumentSurveyItem> RankDirectory(string root, int take = 25)
|
||||
{
|
||||
return Directory.GetFiles(root, "*.xiz", SearchOption.AllDirectories)
|
||||
.Select(file =>
|
||||
{
|
||||
var instrument = ZynInstrumentReader.ParseFile(file);
|
||||
var features = instrument.Features();
|
||||
return new ZynInstrumentSurveyItem(
|
||||
Path.GetRelativePath(root, file),
|
||||
instrument.Name,
|
||||
ComplexityScore(features),
|
||||
features);
|
||||
})
|
||||
.OrderByDescending(item => item.ComplexityScore)
|
||||
.ThenBy(item => item.Path, StringComparer.OrdinalIgnoreCase)
|
||||
.Take(take)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static int ComplexityScore(IReadOnlyList<ReferenceFeature> features)
|
||||
{
|
||||
var byName = features.ToDictionary(feature => feature.Name, feature => feature.Value, StringComparer.OrdinalIgnoreCase);
|
||||
var layered = byName.GetValueOrDefault("layered_instrument") == "yes" ? 8 : 0;
|
||||
return Int("enabled_kit_items") * 4 +
|
||||
layered +
|
||||
Int("engine_add") * 4 +
|
||||
Int("engine_sub") * 4 +
|
||||
Int("engine_pad") * 7 +
|
||||
Int("envelope_count") +
|
||||
Int("free_envelope_count") * 4 +
|
||||
Int("lfo_count") * 2 +
|
||||
Int("filter_count") * 2 +
|
||||
Int("formant_filter_count") * 8 +
|
||||
Int("effect_count") * 3;
|
||||
|
||||
int Int(string name) => int.TryParse(byName.GetValueOrDefault(name), out var value) ? value : 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,3 +59,4 @@
|
|||
{"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":"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_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"}
|
||||
|
|
|
|||
|
|
@ -81,9 +81,9 @@
|
|||
"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": {
|
||||
"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.",
|
||||
"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 current classifier identifies active ADD/SUB/PAD engines, enabled kit items, layering, envelopes, free envelopes, LFOs, filters, formants, and effects so rebuild targets can pressure Aquarium's DSL without pretending parser success is expressivity. The first rebuilds show Aquarium can sketch these fixtures with existing voices/macros/formants/noise, but the missing abstractions are 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.",
|
||||
"next": "Choose one Zyn abstraction candidate to promote only after mapping ownership and tests; do not add the whole shopping list at once."
|
||||
"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.",
|
||||
"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 hardened reader now 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, but the upstream survey says named kit/layer routing is the owner abstraction needed before syntax golf for additive harmonic banks, PAD/spectral source generation, free envelopes, or richer formant/vowel morphing.",
|
||||
"next": "Map named kit/layer routing first; do not add additive/PAD/free-envelope/formant syntax until layer authority is clear."
|
||||
},
|
||||
"note_and_envelope_model": {
|
||||
"rule": "Notes own frequency, gate duration, and host/MIDI source. Envelopes own ADSR level shape.",
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
},
|
||||
"last_verified": {
|
||||
"command": "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore",
|
||||
"result": "83 passed; Zyn rebuild catalog added without regressing DX7 parity",
|
||||
"commit": "pending Zyn rebuild catalog"
|
||||
"result": "86 passed; Zyn worst-case survey added without regressing DX7 parity",
|
||||
"commit": "pending Zyn pressure survey"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,25 @@ Doctrine update:
|
|||
|
||||
Completed this slice:
|
||||
|
||||
- Hardened `ZynInstrumentReader` against the real upstream Zyn instrument bank:
|
||||
it now tolerates leading whitespace before XML declarations and counts only
|
||||
actual `FORMANT_FILTER`/`FILTER` blocks instead of all internal formant/vowel
|
||||
nodes.
|
||||
- Added `ZynInstrumentSurvey` to rank `.xiz` directories by feature pressure.
|
||||
The non-vendored upstream bank scan parsed `1358/1358` files at
|
||||
`zynaddsubfx/instruments` commit `e9f64a9`.
|
||||
- Added `docs/zynaddsubfx-pressure-survey.md`. Worst observed target:
|
||||
`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.
|
||||
- Survey conclusion: named kit/layer routing should probably precede syntax
|
||||
golf for additive banks, PAD sources, or formant motion.
|
||||
- Verified with bundled Python/dexed-py still wired:
|
||||
`AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore`:
|
||||
86 passed.
|
||||
|
||||
Previous slice:
|
||||
|
||||
- Added first Zyn-style Aquarium rebuilds for the project-authored additive
|
||||
lead, PAD texture, and vocal/formant layer fixtures. They live in
|
||||
`BuiltInScripts` and `ReferenceRebuildCatalog.ZynRebuilds`.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ current_mechanism:
|
|||
tests: tests/AquariumSynth.Dsl.Tests/PatchScriptTests.cs
|
||||
doctrine: docs/audio-doctrine.md
|
||||
dx7_calibration_plan: docs/dx7-calibration-plan.md
|
||||
zyn_pressure_survey: docs/zynaddsubfx-pressure-survey.md
|
||||
roadmap: docs/reference-synth-roadmap.md
|
||||
parameter_contracts: src/AquariumSynth.Dsl/Model.cs
|
||||
|
||||
|
|
@ -93,9 +94,9 @@ 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."
|
||||
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. A non-vendored upstream bank survey parsed 1358/1358 .xiz files and identified named kit/layer routing as the next likely owner abstraction."
|
||||
first_slice:
|
||||
- "Choose the first Zyn abstraction to promote only if repeated rebuilds justify it: additive harmonic bank, PAD spectral source, free envelope, named layer routing, or richer formant morphing."
|
||||
- "Map named kit/layer routing first; the upstream worst-case survey says it is the owner needed before additive harmonic bank, PAD spectral source, free envelope, or richer formant morphing syntax."
|
||||
- "Do not add all of them at once."
|
||||
faust:
|
||||
priority: 3
|
||||
|
|
@ -126,11 +127,11 @@ next_agent_start_here:
|
|||
Continue the ZynAddSubFX phase. The first parser/classifier slice exists in
|
||||
`src/AquariumSynth.Dsl/ZynInstrument.cs` with project-authored fixtures
|
||||
under `tests/AquariumSynth.Dsl.Tests/Fixtures/ZynAddSubFX/ProjectAuthored`.
|
||||
Next pressure is to choose one Zyn abstraction candidate from the first
|
||||
rebuild evidence: additive harmonic bank, PAD spectral source, free
|
||||
envelope, named layer routing, or richer formant morphing. Promote only one
|
||||
after mapping ownership and tests. Leave DX7 alone unless a regression test
|
||||
fails.
|
||||
Next pressure is to map named kit/layer routing from the Zyn survey evidence
|
||||
in `docs/zynaddsubfx-pressure-survey.md`. The worst upstream targets combine
|
||||
many enabled kit items and engines, so layer authority should come before
|
||||
syntax golf for additive banks, PAD source generation, free envelopes, or
|
||||
formant morphing. Leave DX7 alone unless a regression test fails.
|
||||
must_read:
|
||||
- AGENTS.md
|
||||
- docs/audio-doctrine.md
|
||||
|
|
@ -143,6 +144,19 @@ next_agent_start_here:
|
|||
- "dotnet test AquariumSynthCSharp.slnx --no-restore"
|
||||
|
||||
completed_slices:
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Hardened the Zyn reader against the real upstream instrument bank and
|
||||
added `ZynInstrumentSurvey`. The reader now tolerates leading whitespace
|
||||
before XML declarations and counts actual `FILTER`/`FORMANT_FILTER`
|
||||
blocks instead of internal formant/vowel nodes. A non-vendored scan of
|
||||
`zynaddsubfx/instruments` commit `e9f64a9` parsed 1358/1358 `.xiz`
|
||||
files. The top pressure 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 survey points
|
||||
to named kit/layer routing as the next owner abstraction before syntax
|
||||
golf.
|
||||
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore: 86 passed"
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Added first Zyn-style Aquarium rebuilds for the project-authored additive
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
</AMPLITUDE_ENVELOPE>
|
||||
</AMPLITUDE_PARAMETERS>
|
||||
<FILTER_PARAMETERS>
|
||||
<FILTER>
|
||||
<par name="category" value="2"/>
|
||||
</FILTER>
|
||||
<par name="category" value="2"/>
|
||||
</FILTER_PARAMETERS>
|
||||
</PAD_SYNTH_PARAMETERS>
|
||||
|
|
|
|||
|
|
@ -83,6 +83,70 @@ public sealed class ZynInstrumentTests
|
|||
AssertFeature(instrument, "engine_pad", "0");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToleratesLeadingWhitespaceBeforeXmlDeclaration()
|
||||
{
|
||||
var instrument = ZynInstrumentReader.Parse(Encoding.UTF8.GetBytes("""
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ZynAddSubFX-data>
|
||||
<INSTRUMENT>
|
||||
<INFO><string name="name">Loose XML</string></INFO>
|
||||
<INSTRUMENT_KIT>
|
||||
<INSTRUMENT_KIT_ITEM id="0">
|
||||
<par_bool name="enabled" value="yes"/>
|
||||
<par_bool name="add_enabled" value="yes"/>
|
||||
</INSTRUMENT_KIT_ITEM>
|
||||
</INSTRUMENT_KIT>
|
||||
</INSTRUMENT>
|
||||
</ZynAddSubFX-data>
|
||||
"""));
|
||||
|
||||
Assert.Equal("Loose XML", instrument.Name);
|
||||
AssertFeature(instrument, "engine_add", "1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CountsOnlyActualFormantFilterBlocks()
|
||||
{
|
||||
var instrument = ZynInstrumentReader.Parse(Encoding.UTF8.GetBytes("""
|
||||
<ZynAddSubFX-data>
|
||||
<INSTRUMENT>
|
||||
<INFO><string name="name">Formant Counting</string></INFO>
|
||||
<INSTRUMENT_KIT>
|
||||
<INSTRUMENT_KIT_ITEM id="0">
|
||||
<par_bool name="enabled" value="yes"/>
|
||||
<par_bool name="add_enabled" value="yes"/>
|
||||
<ADD_SYNTH_PARAMETERS>
|
||||
<FILTER_PARAMETERS>
|
||||
<FORMANT_FILTER>
|
||||
<par name="num_formants" value="3"/>
|
||||
<VOWEL id="0"><FORMANT id="0"/><FORMANT id="1"/></VOWEL>
|
||||
</FORMANT_FILTER>
|
||||
</FILTER_PARAMETERS>
|
||||
</ADD_SYNTH_PARAMETERS>
|
||||
</INSTRUMENT_KIT_ITEM>
|
||||
</INSTRUMENT_KIT>
|
||||
</INSTRUMENT>
|
||||
</ZynAddSubFX-data>
|
||||
"""));
|
||||
|
||||
AssertFeature(instrument, "formant_filter_count", "1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SurveyRanksWorstFeaturePressure()
|
||||
{
|
||||
var root = Path.Combine(AppContext.BaseDirectory, "Fixtures", "ZynAddSubFX", "ProjectAuthored");
|
||||
|
||||
var survey = ZynInstrumentSurvey.RankDirectory(root, take: 3);
|
||||
|
||||
Assert.Equal(3, survey.Count);
|
||||
Assert.Equal("vocal-layer.xiz", survey[0].Path);
|
||||
Assert.True(survey[0].ComplexityScore > survey[^1].ComplexityScore);
|
||||
Assert.Contains(survey[0].Features, feature => feature.Name == "formant_filter_count" && feature.Value == "1");
|
||||
}
|
||||
|
||||
private static void AssertFeature(ZynInstrument instrument, string name, string value) =>
|
||||
Assert.Contains(instrument.Features(), feature => feature.Name == name && feature.Value == value);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue