Commit graph

10 commits

Author SHA1 Message Date
monkey-w1n5t0n
b290144670 fix(modular): base_amp floor + opt-in matrix to keep voice audible
Modular sub-engines computed amp_val as a pure function of mod_amp (the
matrix d08_amp destination sum), so once the MLP drove the matrix cells
every joystick movement had a chance to silence the voice: matrix cells
have signed range [-1, 1], sigmoid outputs near 0.5 denormalise to 0,
and the amp gate collapsed. Additive survived in scattered regions
because it only has one kill-switch (d08_amp); subtractive and fm were
almost always dead because they also have d05_cutoff and d01_op1_level.

Two changes:

1. DSP: each sub-engine gets a base_amp hslider (default 1.0) so
   amp_val = clamp(base_amp + mod_amp) * level * vel_gain. At the
   default the voice is always fully open and d08_amp modulation is
   purely additive decoration; drop base_amp to 0 for classic
   ADSR-gated VCA behaviour.

2. ModularEngine._rebuildParamMeta: restore the _exposedMatrixCells
   gate (default empty). paramCount drops from 512 to 32 (4 ADSR * 4
   + 8 LFO * 2); matrix cells are opt-in via setExposeMatrixCell.
   _applyDefaultPatch no longer writes s00_d08_amp since base_amp
   keeps the voice audible without routing.

Tests updated for the new 32-param baseline; matrix-cell persistence
test now calls setExposeMatrixCell(1, 5, true) before asserting the
cell lands in paramMeta. Drive-by: engine-switching test bumped from
3 to 4 engine cards (stale since the modular engine was added).
2026-04-11 09:27:19 +02:00
monkey-w1n5t0n
e6938ebb8b fix(playground): modular worklet base-class loading + live matrix display
Two fixes for Modular mode:

1. faust-worklet-processor.js: explicitly attach FaustWorkletProcessor
   to globalThis. Class declarations at the top of a classic script are
   lexically scoped to that script's evaluation context and do not
   propagate across separate addModule() calls, so the base class was
   invisible to subclass processors when they loaded in AudioWorklet-
   GlobalScope. Symptom: "FaustWorkletProcessor is not defined" at the
   extends clause of modular-subtractive-processor.js.

2. modular-ui.js + a-app.js: wire live matrix cell updates. Phase C
   wired matrix cells for writes (click cycles, precise editor) but
   not for reads — the MLP-driven values never propagated to the DOM.
   modular-ui now exposes updateLive(outputs), called from routeOutputs
   on every inference tick. Throttled to ~20 fps internally to avoid
   DOM thrashing. Only visible sources (within adsrCount/lfoCount) are
   updated; muted slots stay dark.
2026-04-11 08:08:29 +02:00
monkey-w1n5t0n
0de64463fd build(faust): auto re-exec under nix-shell when faust is missing
If faust isn't in PATH but nix-shell is, transparently re-exec the
script via `nix-shell -p faust --run` instead of erroring out with a
hint. NISPS_BUILD_IN_NIX_SHELL guards against infinite recursion if the
shell somehow still lacks faust.
2026-04-11 07:58:43 +02:00
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
w1n5t0n
5ac67df699 Merge EOC Delay + Saturation + Master Bus modules (meml-2sl, meml-2zm)
Resolved conflicts: merged all 6 module imports in eoc-chain-ui.js and index.js.
All moduleFactory cases now return real Faust WASM implementations — no stubs remain.
2026-04-03 18:22:00 +01:00
w1n5t0n
a9e2b5a8f9 feat(playground/eoc): Delay + Saturation + Master Bus modules (meml-2sl, meml-2zm)
Faust DSP sources, compiled WASM + JSON descriptors, AudioWorklet processors,
and EOCModule subclasses for all three effects.
2026-04-03 18:19:32 +01:00
w1n5t0n
0a5f2544ca feat(playground/eoc): EQ + Compressor + Reverb modules (meml-4b4, meml-cpe, meml-wwc)
Faust DSP sources, compiled WASM + JSON descriptors, AudioWorklet processors,
and EOCModule subclasses for all three effects.

- meml-4b4: 4-band parametric EQ (fi.low_shelf, fi.peak_eq × 2, fi.high_shelf)
  10 params: freq/gain per band, Q for the two mid bell bands
- meml-cpe: stereo feed-forward compressor (co.compressor_mono × 2)
  7 params: threshold, ratio, attack, release, knee, makeup, mix (parallel)
- meml-wwc: zita reverb (re.zita_rev1_stereo) with pre-delay and M/S width
  8 active params + mod_rate placeholder (zita internal mod not yet exposed)

All three DSPs compile cleanly under Faust 2.83.1. moduleFactory in
eoc-chain-ui.js now returns real instances for eq/compressor/reverb.
2026-04-03 17:57:36 +01:00
w1n5t0n
80f95b8db7 feat(playground/faust): full 48-param additive synth DSP (meml-pj4)
Replace placeholder stub with a complete additive synthesiser:
- 64 harmonics, 5 parameter groups (Spectral Shape, Temporal, Phase,
  Modulation, Master), 48 NISPS-mapped params in exact spec order
- Spectral descriptors: per-harmonic amplitudes, tilt, inharmonicity,
  odd/even balance, formant Gaussian shaping
- Temporal: dual ADSR (global + brightness), spectral flux LFO
- Phase: randomisation, walk, inter-partial beating, stereo spread,
  noise floor with colour, sub-harmonic
- Modulation: vibrato (delayed onset), tremolo, harmonic drift,
  formant resonance peaks
- Master: level, vel coupling, portamento, saturation waveshaper,
  fine-tune cents
- additive-processor.js: AudioWorklet processor extending
  FaustWorkletProcessor with dynamic zone-table construction at init,
  _onNoteOn/Off, _renderBlock, _onSetParam
- additive.json: Faust descriptor with 48 params in spec-ordered groups
  (numeric prefixes force alphabetical sort = spec order)
- additive.wasm: compiled with faust 2.83.1 -lang wasm -cn additive
2026-04-03 17:52:07 +01:00
w1n5t0n
67d2230ba8 feat(playground/faust): full 56-param FM matrix synth DSP (meml-wgg)
- Full 4-operator FM with continuous 4×4 routing matrix (12 cross-mod pairs)
- Compiles to 46KB WASM via faust -lang wasm
- 55 visible NISPS params (pitch_glide missing from DSP, tracked)
- Includes fm-matrix-processor.js AudioWorklet + JSON param descriptor
2026-04-03 17:41:54 +01:00
w1n5t0n
55bf1b5d48 feat(playground): Faust WASM build pipeline + param auto-discovery (meml-5s3)
Adds the Faust DSP toolchain infrastructure: placeholder additive and FM DSP
files, build.sh (faust -lang wasm per .dsp), faustJsonToParamMeta() to convert
Faust JSON UI trees into the standard paramMeta format, FaustEngineBase
(SynthEngine subclass wiring init/setParam/noteOn/noteOff through AudioWorklet
messages), and FaustWorkletProcessor base class for concrete engine processors.
2026-04-03 17:30:23 +01:00