memlnaut-nisps/playground/faust/eoc-saturation.json
monkey-w1n5t0n afff406d92 feat(playground): add Modular audio mode with shared mod pool
New "Modular" engine in a-immersive with three hot-swappable Faust
sub-engines (subtractive/additive/fm) sharing a common modulation pool:
16 ADSR slots + 32 LFO slots (single-knob sine->tri->square->saw
wavemorph) routed through a 48-source x 10-destination matrix per
engine. Per-connection scalar amounts in [-1, 1], summed at each
destination. Default MLP output count is 512 (32 mod-source params +
480 matrix cells); model reinits on sub-engine swap, count change, or
engine-param exposure toggle.

Faust layer:
- mod-pool.lib: shared ADSR/LFO/source-bus library
- gen-modular-dsp.py: byte-reproducible generator (source of truth)
- modular-subtractive: faithful Minimoog (3 osc, ladder filter, no envs)
- modular-additive: 64-partial, spectral shape + formants, no envs/LFOs
- modular-fm: 4-op matrix + self-feedback, no envs
- All three share d08=amp, d09=pan conventions
- MODULAR_DESTINATIONS.md: authoritative destination table

JS layer:
- ModularEngine: self-contained SynthEngine with getState/setState,
  setSubEngine, setModSourceCount, setExposeEngineParam
- modular-ui: drawer with sub-engine toggle, ADSR/LFO count steppers,
  per-slot enable switches, matrix grid editor (tap-cycle, long-press
  precise, right-click menu, negative amounts), preset overlay
- modular-presets: 6 named presets (Slow pad, Plucky bass, Crystal,
  DX bell, Morphing drone + default)
- a-app.js: Modular mode registered, paramMeta:change -> resizeMLP,
  modular DSP state persisted under modularDspState, window.__nisps
  debug hooks for programmatic control

Tests: tests/e2e/modular-mode.spec.js (11 Playwright tests, all passing
including DSP state survives reload, sub-engine swap keeps paramCount,
preset apply verification).

Also fixes a pre-existing build.sh bug where the -e flag caused faust
to overwrite .wasm outputs with expanded DSP source text, leaving
additive/fm-matrix/eoc-* committed as invalid WebAssembly. Rebuilt all
affected engines with the corrected script. Added an early-message
buffer to faust-worklet-processor.js so setParam calls arriving before
wasm instantiation are queued rather than dropped (needed when the user
configures modular state before clicking Start Audio).
2026-04-11 07:34:41 +02:00

1 line
No EOL
6.4 KiB
JSON

{"name": "eoc-saturation","filename": "eoc-saturation.dsp","version": "2.79.3","compile_options": "-lang wasm -ct 1 -cn eoc_saturation -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0","library_list": ["/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/stdfaust.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/maths.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/filters.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/platform.lib"],"include_pathnames": ["/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust","/usr/local/share/faust","/usr/share/faust",".","/home/w1n5t0n/src/MEMLNaut-NISPS/playground/faust"],"size": 56,"code": "Ly8gZW9jLXNhdHVyYXRpb24uZHNwIOKAlCBTdGVyZW8gc2F0dXJhdGlvbiBlZmZlY3QgZm9yIHRoZSBFT0MgcmFjay4KLy8KLy8gUGFyYW1ldGVycyAoNCk6Ci8vICAgZHJpdmUgICAgIFswLjAsIDAtMV0gICAwPWNsZWFuLCAxPWZ1bGwgZHJpdmUKLy8gICBjaGFyYWN0ZXIgWzAuMCwgMC0xXSAgIDA9c29mdC1jbGlwL3RhbmgsIDAuNT10YXBlL2FzeW1tZXRyaWMsIDE9aGFyZC1jbGlwCi8vICAgdG9uZSAgICAgIFswLjUsIDAtMV0gICBwb3N0LXNhdHVyYXRpb24gdG9uZTogMD1kYXJrIChMUCksIDE9YnJpZ2h0IChIUCBibGVuZCkKLy8gICBtaXggICAgICAgWzEuMCwgMC0xXSAgIGRyeS93ZXQgZm9yIHBhcmFsbGVsIHNhdHVyYXRpb24KCmltcG9ydCgic3RkZmF1c3QubGliIik7CgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gVUkKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpkcml2ZSAgICAgPSBoc2xpZGVyKCJkcml2ZSIsICAgICAwLjAsIDAsIDEsIDAuMDAxKTsKY2hhcmFjdGVyID0gaHNsaWRlcigiY2hhcmFjdGVyIiwgMC4wLCAwLCAxLCAwLjAwMSk7CnRvbmUgICAgICA9IGhzbGlkZXIoInRvbmUiLCAgICAgIDAuNSwgMCwgMSwgMC4wMDEpOwptaXggICAgICAgPSBoc2xpZGVyKCJtaXgiLCAgICAgICAxLjAsIDAsIDEsIDAuMDAxKTsKCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBTYXR1cmF0aW9uIHNoYXBlcwovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCmRyaXZlR2FpbiA9IDEuMCArIGRyaXZlICogMTUuMDsgICAvLyAxeCB0byAxNnggZ2FpbiBiZWZvcmUgY2xpcHBpbmcKCi8vIFNvZnQgY2xpcDogdGFuaApzb2Z0Q2xpcCh4KSA9IG1hLnRhbmgoeCk7CgovLyBUYXBlOiBhc3ltbWV0cmljIHdhdmVzaGFwZXIg4oCUIHNsaWdodGx5IGhhcmRlciBjbGlwcGluZyBvbiBwb3NpdGl2ZSBwZWFrcwovLyBCbGVuZHMgdGFuaCB3aXRoIGEgZ2VudGxlIHNlY29uZC1oYXJtb25pYyBiaWFzCnRhcGVDbGlwKHgpID0gc29mdENsaXAoeCAqIDEuMikgKiAwLjU1ICsgc29mdENsaXAoeCkgKiAwLjQ1ICsgeCAqIHggKiAwLjA0ICogKDEuMCAtIHNvZnRDbGlwKGFicyh4KSkpOwoKLy8gSGFyZCBjbGlwOiBzaW1wbGUgc2F0dXJhdGUKaGFyZENsaXAoeCkgPSBtYXgoLTEuMCwgbWluKDEuMCwgeCkpOwoKLy8gQ2hhcmFjdGVyIGNyb3NzZmFkZSBiZXR3ZWVuIHRoZSB0aHJlZSBzaGFwZXM6Ci8vICAgYz0wLjAgIOKGkiBzb2Z0ICh0YW5oKQovLyAgIGM9MC41ICDihpIgdGFwZSAoYXN5bW1ldHJpYykKLy8gICBjPTEuMCAg4oaSIGhhcmQgY2xpcApzYXR1cmF0ZShjLCB4KSA9CiAgICBzb2Z0Q2xpcCh4KSAqIChtYXgoMC4wLCAxLjAgLSBjICogMi4wKSkgKwogICAgdGFwZUNsaXAoeCkgKiAoMS4wIC0gYWJzKGMgLSAwLjUpICogMi4wKSArCiAgICBoYXJkQ2xpcCh4KSAqIG1heCgwLjAsIChjIC0gMC41KSAqIDIuMCk7CgovLyBBcHBseSBkcml2ZSwgc2F0dXJhdGUsIGFuZCBub3JtYWxpc2Ugb3V0cHV0IGxldmVsCnByb2Nlc3NDaGFubmVsKHgpID0gc2F0dXJhdGUoY2hhcmFjdGVyLCB4ICogZHJpdmVHYWluKSAvIG1heCgwLjAwMSwgc3FydChkcml2ZUdhaW4pKTsKCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBUb25lIGNvbnRyb2w6IExQL0hQIGJsZW5kIHZpYSBvbmUtcG9sZSBmaWx0ZXJzCi8vCi8vIHRvbmU9MC4wICDihpIgZGFyayAoNDAwSHogTFApCi8vIHRvbmU9MC41ICDihpIgZmxhdCAocGFzc3Rocm91Z2gpCi8vIHRvbmU9MS4wICDihpIgYnJpZ2h0ICg4a0h6IEhQIGJsZW5kKQovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCnRvbmVGcmVxTFAgPSA0MDAuMCArIHRvbmUgKiAxOTYwMC4wOyAgIC8vIDQwMEh6IHRvIDIwa0h6IChmdWxseSBvcGVuIGF0IHRvbmU9MSkKdG9uZUZyZXFIUCA9IDIwMC4wICsgdG9uZSAqIDc4MDAuMDsgICAgLy8gMjAwSHogdG8gOGtIegoKYXBwbHlUb25lKHgpID0gZmkubG93cGFzcygxLCB0b25lRnJlcUxQLCB4KTsKCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQovLyBNYWluIHByb2Nlc3M6IHN0ZXJlbyBzYXR1cmF0aW9uIHdpdGggcGFyYWxsZWwgZHJ5L3dldCBtaXgKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpwcm9jZXNzKGluTCwgaW5SKSA9IG91dEwsIG91dFIKd2l0aCB7CiAgICB3ZXRMID0gYXBwbHlUb25lKHByb2Nlc3NDaGFubmVsKGluTCkpOwogICAgd2V0UiA9IGFwcGx5VG9uZShwcm9jZXNzQ2hhbm5lbChpblIpKTsKICAgIG91dEwgPSBpbkwgKiAoMS4wIC0gbWl4KSArIHdldEwgKiBtaXg7CiAgICBvdXRSID0gaW5SICogKDEuMCAtIG1peCkgKyB3ZXRSICogbWl4Owp9Owo=","inputs": 2,"outputs": 2,"meta": [ { "compile_options": "-lang wasm -ct 1 -cn eoc_saturation -es 1 -mcd 16 -mdd 1024 -mdy 33 -single -ftz 0" },{ "filename": "eoc-saturation.dsp" },{ "filters.lib/lowpass0_highpass1": "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>" },{ "filters.lib/lowpass0_highpass1:author": "Julius O. Smith III" },{ "filters.lib/lowpass:author": "Julius O. Smith III" },{ "filters.lib/lowpass:copyright": "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>" },{ "filters.lib/lowpass:license": "MIT-style STK-4.3 license" },{ "filters.lib/name": "Faust Filters Library" },{ "filters.lib/tf1:author": "Julius O. Smith III" },{ "filters.lib/tf1:copyright": "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>" },{ "filters.lib/tf1:license": "MIT-style STK-4.3 license" },{ "filters.lib/tf1s:author": "Julius O. Smith III" },{ "filters.lib/tf1s:copyright": "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>" },{ "filters.lib/tf1s:license": "MIT-style STK-4.3 license" },{ "filters.lib/version": "1.7.1" },{ "maths.lib/author": "GRAME" },{ "maths.lib/copyright": "GRAME" },{ "maths.lib/license": "LGPL with exception" },{ "maths.lib/name": "Faust Math Library" },{ "maths.lib/version": "2.8.1" },{ "name": "eoc-saturation" },{ "platform.lib/name": "Generic Platform Library" },{ "platform.lib/version": "1.3.0" }],"ui": [ {"type": "vgroup","label": "eoc-saturation","items": [ {"type": "hslider","label": "character","varname": "fHslider1","shortname": "character","address": "/eoc-saturation/character","index": 4,"init": 0,"min": 0,"max": 1,"step": 0.001},{"type": "hslider","label": "drive","varname": "fHslider0","shortname": "drive","address": "/eoc-saturation/drive","index": 0,"init": 0,"min": 0,"max": 1,"step": 0.001},{"type": "hslider","label": "mix","varname": "fHslider3","shortname": "mix","address": "/eoc-saturation/mix","index": 36,"init": 1,"min": 0,"max": 1,"step": 0.001},{"type": "hslider","label": "tone","varname": "fHslider2","shortname": "tone","address": "/eoc-saturation/tone","index": 16,"init": 0.5,"min": 0,"max": 1,"step": 0.001}]}]}