diff --git a/README.md b/README.md index 1aba37e..8077a5b 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,9 @@ dotnet test - `FaustCompiler.ValidateAsync(source)` compile-checks with Faust when present. - `FaustCompiler.CompileAsync(source, options)` writes generated backend code. - `BuiltInScripts.ReferenceScripts()` carries readable SFXR, BFXR-flavored, - 808, FM bell, and wobble bass patches. They are stable references for testing - and for judging whether the DSL can express useful sound designs cleanly. + 808, FM bell, wobble bass, and advanced layered patches. They are stable + references for testing and for judging whether the DSL can express useful + sound designs cleanly. - `SfxrParams`, `PatchScriptScoring`, `AudioAnalyzer`, and `Presets` carry the reusable Rust-side analysis, scoring, SFXR, and preset tools. diff --git a/docs/migration-checklist.md b/docs/migration-checklist.md index c3c1f98..7cf408a 100644 --- a/docs/migration-checklist.md +++ b/docs/migration-checklist.md @@ -18,7 +18,7 @@ there. - Modulation routing in both direct style (`mod n=wob g=.4 l=.5`) and routed style (`bus n=sway to=g:.1,l:-.2,fmix:.3`). - Built-in reference script catalog for classic SFXR, BFXR-flavored effects, - classic 808, FM bell, and wobble bass patches. + classic 808, FM bell, wobble bass, and advanced layered patches. - Abstract SFXR reference script using defaults and templates. - SFXR parameter presets, mutation, shorthand atoms, and SFXR-to-patch mapping. - Aquarium preset patches: pluck, heartbeat, voice, and SFXR named bridge. diff --git a/src/AquariumSynth.Dsl/BuiltInScripts.cs b/src/AquariumSynth.Dsl/BuiltInScripts.cs index 6993f29..9b28845 100644 --- a/src/AquariumSynth.Dsl/BuiltInScripts.cs +++ b/src/AquariumSynth.Dsl/BuiltInScripts.cs @@ -600,6 +600,190 @@ public static class BuiltInScripts """) ]; + public static readonly IReadOnlyList AdvancedNames = ["aurora-pad", "machine-breath", "glass-creature", "ritual-sequence"]; + + public static readonly IReadOnlyList<(string Name, string Script)> AdvancedReferenceScripts = + [ + ("aurora-pad", """ + patch + gain=0.58 + soft_clip=true + + defaults + wave=sine + attack=0.018 + sustain=1.15 + decay=1.6 + lpf=0.86 + hpf=0.018 + drive=0.05 + + template name=GlassPartial + fm=2.414 + fm_index=1.4 + fm_decay=0.8 + formants=420:80:0.25,1180:180:0.75,2450:360:0.42 + formant_mix=0.18 + + mod name=tide + wave=sine + hz=0.21 + gain=0.08 + pitch=0.012 + lpf=0.09 + formant_mix=0.16 + fm_index=0.35 + + mod name=shiver + wave=triangle + hz=4.6 + pitch=0.004 + drive=0.035 + + voice use=GlassPartial freq=220 gain=0.12 + voice use=GlassPartial freq=330 gain=0.08 fm=3.01 fm_index=0.9 + voice use=GlassPartial freq=440 gain=0.06 fm=1.618 fm_index=0.7 formant_mix=0.26 + voice wave=noise freq=1600 gain=0.018 sustain=1.4 decay=2.0 noise=0.72 hpf=0.62 lpf=0.38 + """), + ("machine-breath", """ + patch + gain=0.7 + soft_clip=true + + defaults + wave=saw + freq=72 + gain=0.1 + attack=0.004 + sustain=0.72 + decay=0.55 + lpf=0.28 + hpf=0.025 + drive=0.34 + fold=0.12 + fm=1.5 + fm_index=0.8 + fm_decay=0.45 + + mod name=piston + wave=sample_hold + hz=5.5 + gain=0.16 + pitch=0.028 + lpf=0.24 + drive=0.18 + fold=0.16 + + mod name=valve + wave=square + hz=2.75 + noise=0.18 + hpf=0.12 + formant_mix=0.25 + + voice + formants=290:55:0.55,760:120:1,1780:260:0.5 + formant_mix=0.22 + + voice + wave=square + freq=36 + gain=0.13 + duty=0.37 + pitch_ramp=-0.18 + + voice + wave=noise + freq=700 + gain=0.055 + sustain=0.5 + decay=0.35 + noise=0.8 + hpf=0.42 + lpf=0.45 + + voice + wave=triangle + freq=144 + gain=0.045 + sustain=0.62 + decay=0.48 + fm=2.25 + fm_index=1.1 + fm_decay=0.5 + formants=510:90:0.35,1320:170:0.9,2100:300:0.45 + formant_mix=0.18 + """), + ("glass-creature", """ + patch + gain=0.52 + soft_clip=true + + defaults + wave=triangle + attack=0.002 + sustain=0.18 + decay=1.05 + lpf=0.92 + hpf=0.03 + drive=0.08 + + template name=Ping + fm=3.5 + fm_index=2.8 + fm_decay=0.5 + vibrato=0.018 + vibrato_hz=6.4 + + mod name=blink + wave=sine + hz=1.8 + pitch=0.02 + gain=0.18 + fm_index=0.8 + + voice use=Ping freq=523.25 gain=0.12 punch=0.12 + voice use=Ping freq=659.25 gain=0.08 sustain=0.11 decay=0.84 fm=4.2 + voice use=Ping freq=783.99 gain=0.07 sustain=0.08 decay=0.72 fm_index=1.9 + voice wave=sine freq=261.63 gain=0.045 sustain=0.7 decay=1.3 fm=1.5 fm_index=0.65 fm_decay=1.2 + voice wave=noise freq=2400 gain=0.012 sustain=0.04 decay=0.55 noise=0.9 hpf=0.72 lpf=0.5 + """), + ("ritual-sequence", """ + patch + gain=0.62 + repeat=0.42 + soft_clip=true + + defaults + wave=sine + gain=0.13 + attack=0.001 + sustain=0.045 + decay=0.5 + punch=0.18 + lpf=0.82 + hpf=0.035 + drive=0.1 + + template name=Step + fm=2 + fm_index=1.8 + fm_decay=0.32 + arp_delay=0.065 + arp_mult=1.4983 + + bus name=cycle + wave=triangle + hz=2.38 + to=gain:0.2,pitch:0.018,lpf:0.12,fm_index:0.75 + + voice use=Step freq=196 + voice use=Step freq=293.66 gain=0.09 sustain=0.035 decay=0.38 arp_mult=1.3348 + voice use=Step freq=392 gain=0.07 sustain=0.028 decay=0.32 arp_mult=1.2599 + voice wave=noise freq=3100 gain=0.025 sustain=0.018 decay=0.16 noise=1 hpf=0.68 lpf=0.46 + """) + ]; + public static IEnumerable<(string Family, string Name, string Script)> ReferenceScripts() { foreach (var item in ClassicSfxrPrimitiveGolfScripts) yield return ("sfxr", item.Name, item.Script); @@ -607,6 +791,7 @@ public static class BuiltInScripts foreach (var item in Classic808PrimitiveGolfScripts) yield return ("808", item.Name, item.Script); foreach (var item in FmBellPrimitiveGolfScripts) yield return ("fm-bell", item.Name, item.Script); foreach (var item in WobbleBassPrimitiveGolfScripts) yield return ("wobble-bass", item.Name, item.Script); + foreach (var item in AdvancedReferenceScripts) yield return ("advanced", item.Name, item.Script); } public static IEnumerable<(string Family, string Name, string Script)> PrimitiveGolfScripts() => ReferenceScripts(); diff --git a/state/evidence.jsonl b/state/evidence.jsonl index a17d024..6a3eb10 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -3,3 +3,4 @@ {"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":"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"} diff --git a/tests/AquariumSynth.Dsl.Tests/PatchScriptTests.cs b/tests/AquariumSynth.Dsl.Tests/PatchScriptTests.cs index fec7de3..5ee4797 100644 --- a/tests/AquariumSynth.Dsl.Tests/PatchScriptTests.cs +++ b/tests/AquariumSynth.Dsl.Tests/PatchScriptTests.cs @@ -151,6 +151,19 @@ public sealed class PatchScriptTests } } + [Fact] + public void AdvancedReferenceScriptsExerciseLayeredPatchFeatures() + { + foreach (var (name, script) in BuiltInScripts.AdvancedReferenceScripts) + { + var patch = PatchScript.Parse(script); + + Assert.True(patch.Voices.Count >= 4, $"{name} should demonstrate layered voices."); + Assert.True(patch.Controls.Count >= 1, $"{name} should demonstrate modulation."); + Assert.Contains(patch.Voices, voice => voice.Fm.Index > 0 || voice.Formants.Count > 0 || voice.Color.NoiseMix > 0); + } + } + [Fact] public void FaustEmitterProducesWobbleSource() {