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
|
|
|
{"name": "fm-matrix","filename": "fm-matrix.dsp","version": "2.79.3","compile_options": "-lang wasm -ct 1 -cn fm_matrix -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/signals.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/maths.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/platform.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/envelopes.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/oscillators.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/filters.lib","/nix/store/yjqpc2a6b2m08zwwrgsdlqsi40m5wnpp-faust-2.79.3/share/faust/basics.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": 344892,"code": "Ly8gZm0tbWF0cml4LmRzcCDigJQgRnVsbCA1Ni1wYXJhbWV0ZXIgNC1vcGVyYXRvciBGTSBzeW50aGVzaXplciB3aXRoIGNvbnRpbnVvdXMgcm91dGluZyBtYXRyaXgKLy8KLy8gRGVzaWduOiA0IG9wZXJhdG9ycyB3aXRoIGZ1bGx5IGNvbnRpbnVvdXMgTsOXTiBjcm9zcy1tb2R1bGF0aW9uIG1hdHJpeC4KLy8gTm8gZml4ZWQgYWxnb3JpdGhtIHNsb3RzIOKAlCB0aGUgYWxnb3JpdGhtIGVtZXJnZXMgZnJvbSBtb2R1bGF0aW9uIGluZGV4IHZhbHVlcy4KLy8gV2hlbiBhbGwgcm91dGluZyBtYXRyaXggaW5kaWNlcyBhcmUgbmVhciAwOiBhZGRpdGl2ZSBzeW50aGVzaXMuCi8vIEFzIGluZGljZXMgZ3JvdzogaW5jcmVhc2luZ2x5IGNvbXBsZXggRk0gdGltYnJlcyBlbWVyZ2UuCi8vCi8vIFBhcmFtZXRlciBvcmRlcmluZyAoNTYgdG90YWwsIG1hcHMgdG8gTUxQIG91dHB1dCBpbmRpY2VzIDDigJM1NSk6Ci8vICAgMOKAkzIzOiAgT3BlcmF0b3IgQ29yZSDDlyA0IChyYXRpbywgbGV2ZWwsIGF0dGFjaywgZGVjYXksIHN1c3RhaW4sIHJlbGVhc2UpCi8vICAgMjTigJMzNTogQ3Jvc3MtTW9kdWxhdGlvbiBNYXRyaXggKDEyIGRpcmVjdGVkIHBhaXJzKQovLyAgIDM24oCTMzk6IFNlbGYtRmVlZGJhY2sgw5cgNAovLyAgIDQw4oCTNDc6IEdsb2JhbCBNb2R1bGF0aW9uIChMRk8sIHBpdGNoIGVudiwgdmVsb2NpdHkpCi8vICAgNDjigJM1NTogTWFzdGVyIChsZXZlbCwgdmVsIHNlbnMsIGdsaWRlLCBmaW5lIHR1bmUsIHdhdmVmb3JtIGJsZW5kLCBzdGVyZW8gc3ByZWFkLAovLyAgICAgICAgICAgb3V0cHV0IHNhdHVyYXRpb24sIG91dHB1dCBIUCkKLy8KLy8gSGlkZGVuIHBhcmFtcyAobm90IGluIHRoZSA1Ni1wYXJhbSBOSVNQUyBsaXN0KToKLy8gICBmcmVxICDigJQgc2V0IGJ5IG5vdGVPbiAoSHopCi8vICAgZ2F0ZSAg4oCUIHNldCBieSBub3RlT24vbm90ZU9mZiAoMC8xKQovLyAgIF92ZWwgIOKAlCBzZXQgYnkgbm90ZU9uICh2ZWxvY2l0eSAw4oCTMSkKLy8KLy8gRk0gaW1wbGVtZW50YXRpb246IGVhY2ggb3BlcmF0b3IgcGhhc2UtbW9kdWxhdGVzIG90aGVycyB2aWEgYSAxLXNhbXBsZSBkZWxheWVkCi8vIGZlZWRiYWNrIGJ1cy4gVGhlIDQgb3BlcmF0b3Igb3V0cHV0cyBhcmUgY29sbGVjdGVkIGludG8gYSA0LWNoYW5uZWwgYnVzIGFuZAovLyBsb29wZWQgdmlhIH4uIFRoaXMgaXMgdGhlIHN0YW5kYXJkIEZhdXN0IEZNIHBhdHRlcm4uCi8vCi8vIEJ1aWxkOgovLyAgIG5peC1zaGVsbCAtcCBmYXVzdCAtLXJ1biBcCi8vICAgICAiZmF1c3QgLWxhbmcgd2FzbSAtY24gZm1fbWF0cml4IGZtLW1hdHJpeC5kc3AgLW8gZm0tbWF0cml4Lndhc20iCgppbXBvcnQoInN0ZGZhdXN0LmxpYiIpOwoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIEhpZGRlbiBjb250cm9sIHBhcmFtcyAoZHJpdmVuIGJ5IG5vdGVPbi9ub3RlT2ZmLCBub3QgTklTUFMpCi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKZnJlcSA9IGhzbGlkZXIoIk1hc3Rlci9mcmVxW2hpZGRlbjoxXVt1bml0Okh6XSIsIDIyMCwgMjAsIDQwMDAsIDAuMDEpOwpnYXRlID0gYnV0dG9uKCJNYXN0ZXIvZ2F0ZVtoaWRkZW46MV0iKTsKdmVsICA9IGhzbGlkZXIoIk1hc3Rlci9fdmVsW2hpZGRlbjoxXSIsIDAuNywgMC4wLCAxLjAsIDAuMDAxKSA6IHNpLnNtb287CgovLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gR3JvdXAgMSDigJQgT3BlcmF0b3IgQ29yZSDDlyA0ICAocGFyYW1zIDDigJMyMykKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpvcDFfcmF0aW8gICA9IGhzbGlkZXIoIk9wZXJhdG9ycy9PcCAxL29wMV9yYXRpbyIsICAgMS4wLCAwLjEyNSwgMTYuMCwgMC4wMDEpIDogc2kuc21vbzsKb3AxX2xldmVsICAgPSBoc2xpZGVyKCJPcGVyYXRvcnMvT3AgMS9vcDFfbGV2ZWwiLCAgIDAuOCwgMC4wLCAgIDEuMCwgIDAuMDAxKSA6IHNpLnNtb287Cm9wMV9hdHRhY2sgID0gaHNsaWRlcigiT3BlcmF0b3JzL09wIDEvb3AxX2F0dGFjayIsICAwLjAxLCAwLjAwMSwgNS4wLCAwLjAwMSk7Cm9wMV9kZWNheSAgID0gaHNsaWRlcigiT3BlcmF0b3JzL09wID
|