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).
435 lines
24 KiB
Text
435 lines
24 KiB
Text
// mod-pool.lib — Shared modulation pool (ADSRs + LFOs) for the "Modular" mode.
|
|
//
|
|
// Provides 16 ADSR slots + 32 LFO slots that can be routed through a
|
|
// per-engine modulation matrix to any parameter in the host .dsp.
|
|
// Engines import this via:
|
|
//
|
|
// mp = library("mod-pool.lib");
|
|
// sources = mp.sources(gate); // 48-wide parallel bus
|
|
// mod_x = sum(s, 48, sources : ba.selector(s, 48) * amount(s));
|
|
//
|
|
// PUBLIC API
|
|
// ----------
|
|
//
|
|
// mp.N_ADSR integer — 16
|
|
// mp.N_LFO integer — 32
|
|
// mp.N_SOURCES integer — 48 (= N_ADSR + N_LFO)
|
|
//
|
|
// mp.adsrs(gate) bus — 16-wide parallel bus, each element [0,1]
|
|
// mp.lfos bus — 32-wide parallel bus, each element [-1,+1]
|
|
// mp.sources(gate) bus — 48-wide parallel bus (adsrs(gate), lfos)
|
|
//
|
|
// PARAMETER NAMESPACE CONVENTION
|
|
// ------------------------------
|
|
// All mod-pool sliders live under groups prefixed with "MM_" so that the
|
|
// paramMeta parser can segregate them from engine sound params:
|
|
//
|
|
// MM_ADSR/NN_adsrNN_{attack,decay,sustain,release,enable} (16 slots)
|
|
// MM_LFO /NN_lfoNN_{rate,morph,enable} (32 slots)
|
|
//
|
|
// Engine-side matrix sliders (declared in the engine .dsp, not in this file)
|
|
// should follow:
|
|
//
|
|
// MM_Matrix/sNN_dNN_<destname> (48 * n_dst)
|
|
//
|
|
// where sNN = source index (0..47; 0..15 = ADSRs, 16..47 = LFOs),
|
|
// dNN = destination index, and <destname> = short destination label.
|
|
//
|
|
// MUTING
|
|
// ------
|
|
// Each slot has an "enable" hslider (0 or 1). The slot's output is multiplied
|
|
// by the enable value so a muted slot contributes 0 at the matrix input. This
|
|
// is cheap but does NOT let Faust eliminate the underlying env/LFO compute.
|
|
// Compile-time dead-code elimination of unused slots is a future concern.
|
|
//
|
|
// LFO WAVEMORPH
|
|
// -------------
|
|
// A single "morph" slider (0..1) crossfades through four shapes:
|
|
//
|
|
// [0.000, 0.333) : sine -> triangle
|
|
// [0.333, 0.666) : triangle -> square
|
|
// [0.666, 1.000] : square -> saw
|
|
//
|
|
// Implemented via nested ba.if so the chosen pair is a linear crossfade.
|
|
// The output is always in [-1,+1].
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
import("stdfaust.lib");
|
|
|
|
// Counts (engine code can reference mp.N_SOURCES etc.)
|
|
N_ADSR = 16;
|
|
N_LFO = 32;
|
|
N_SOURCES = 48;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Per-slot hslider helpers. Unique label per slot index.
|
|
// Slot indices are 1-based in labels (adsr01..adsr16, lfo01..lfo32) to keep
|
|
// alphabetical ordering stable in the Faust JSON output.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// ADSR param factories — declared per-slot because Faust slider labels must
|
|
// be string literals known at compile time. One function per slot index.
|
|
|
|
adsr01_a = hslider("MM_ADSR/00_adsr01_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr01_d = hslider("MM_ADSR/00_adsr01_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr01_s = hslider("MM_ADSR/00_adsr01_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr01_r = hslider("MM_ADSR/00_adsr01_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr01_e = hslider("MM_ADSR/00_adsr01_enable", 1.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr02_a = hslider("MM_ADSR/01_adsr02_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr02_d = hslider("MM_ADSR/01_adsr02_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr02_s = hslider("MM_ADSR/01_adsr02_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr02_r = hslider("MM_ADSR/01_adsr02_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr02_e = hslider("MM_ADSR/01_adsr02_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr03_a = hslider("MM_ADSR/02_adsr03_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr03_d = hslider("MM_ADSR/02_adsr03_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr03_s = hslider("MM_ADSR/02_adsr03_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr03_r = hslider("MM_ADSR/02_adsr03_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr03_e = hslider("MM_ADSR/02_adsr03_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr04_a = hslider("MM_ADSR/03_adsr04_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr04_d = hslider("MM_ADSR/03_adsr04_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr04_s = hslider("MM_ADSR/03_adsr04_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr04_r = hslider("MM_ADSR/03_adsr04_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr04_e = hslider("MM_ADSR/03_adsr04_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr05_a = hslider("MM_ADSR/04_adsr05_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr05_d = hslider("MM_ADSR/04_adsr05_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr05_s = hslider("MM_ADSR/04_adsr05_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr05_r = hslider("MM_ADSR/04_adsr05_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr05_e = hslider("MM_ADSR/04_adsr05_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr06_a = hslider("MM_ADSR/05_adsr06_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr06_d = hslider("MM_ADSR/05_adsr06_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr06_s = hslider("MM_ADSR/05_adsr06_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr06_r = hslider("MM_ADSR/05_adsr06_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr06_e = hslider("MM_ADSR/05_adsr06_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr07_a = hslider("MM_ADSR/06_adsr07_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr07_d = hslider("MM_ADSR/06_adsr07_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr07_s = hslider("MM_ADSR/06_adsr07_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr07_r = hslider("MM_ADSR/06_adsr07_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr07_e = hslider("MM_ADSR/06_adsr07_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr08_a = hslider("MM_ADSR/07_adsr08_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr08_d = hslider("MM_ADSR/07_adsr08_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr08_s = hslider("MM_ADSR/07_adsr08_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr08_r = hslider("MM_ADSR/07_adsr08_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr08_e = hslider("MM_ADSR/07_adsr08_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr09_a = hslider("MM_ADSR/08_adsr09_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr09_d = hslider("MM_ADSR/08_adsr09_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr09_s = hslider("MM_ADSR/08_adsr09_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr09_r = hslider("MM_ADSR/08_adsr09_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr09_e = hslider("MM_ADSR/08_adsr09_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr10_a = hslider("MM_ADSR/09_adsr10_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr10_d = hslider("MM_ADSR/09_adsr10_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr10_s = hslider("MM_ADSR/09_adsr10_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr10_r = hslider("MM_ADSR/09_adsr10_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr10_e = hslider("MM_ADSR/09_adsr10_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr11_a = hslider("MM_ADSR/10_adsr11_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr11_d = hslider("MM_ADSR/10_adsr11_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr11_s = hslider("MM_ADSR/10_adsr11_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr11_r = hslider("MM_ADSR/10_adsr11_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr11_e = hslider("MM_ADSR/10_adsr11_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr12_a = hslider("MM_ADSR/11_adsr12_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr12_d = hslider("MM_ADSR/11_adsr12_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr12_s = hslider("MM_ADSR/11_adsr12_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr12_r = hslider("MM_ADSR/11_adsr12_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr12_e = hslider("MM_ADSR/11_adsr12_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr13_a = hslider("MM_ADSR/12_adsr13_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr13_d = hslider("MM_ADSR/12_adsr13_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr13_s = hslider("MM_ADSR/12_adsr13_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr13_r = hslider("MM_ADSR/12_adsr13_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr13_e = hslider("MM_ADSR/12_adsr13_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr14_a = hslider("MM_ADSR/13_adsr14_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr14_d = hslider("MM_ADSR/13_adsr14_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr14_s = hslider("MM_ADSR/13_adsr14_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr14_r = hslider("MM_ADSR/13_adsr14_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr14_e = hslider("MM_ADSR/13_adsr14_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr15_a = hslider("MM_ADSR/14_adsr15_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr15_d = hslider("MM_ADSR/14_adsr15_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr15_s = hslider("MM_ADSR/14_adsr15_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr15_r = hslider("MM_ADSR/14_adsr15_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr15_e = hslider("MM_ADSR/14_adsr15_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
adsr16_a = hslider("MM_ADSR/15_adsr16_attack[scale:log]", 0.010, 0.001, 5.0, 0.001);
|
|
adsr16_d = hslider("MM_ADSR/15_adsr16_decay", 0.200, 0.001, 10.0, 0.001);
|
|
adsr16_s = hslider("MM_ADSR/15_adsr16_sustain", 0.700, 0.0, 1.0, 0.001);
|
|
adsr16_r = hslider("MM_ADSR/15_adsr16_release", 0.300, 0.01, 10.0, 0.001);
|
|
adsr16_e = hslider("MM_ADSR/15_adsr16_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
// Single-slot ADSR expression. The enable multiplier zeros muted slots.
|
|
adsr_slot(a, d, s, r, e, gate) = en.adsr(a, d, s, r, gate) : *(e);
|
|
|
|
// 16-wide parallel bus of ADSR outputs. Takes the global gate as an argument
|
|
// so engines can bind their own gate signal (noteOn/noteOff).
|
|
adsrs(gate) =
|
|
adsr_slot(adsr01_a, adsr01_d, adsr01_s, adsr01_r, adsr01_e, gate),
|
|
adsr_slot(adsr02_a, adsr02_d, adsr02_s, adsr02_r, adsr02_e, gate),
|
|
adsr_slot(adsr03_a, adsr03_d, adsr03_s, adsr03_r, adsr03_e, gate),
|
|
adsr_slot(adsr04_a, adsr04_d, adsr04_s, adsr04_r, adsr04_e, gate),
|
|
adsr_slot(adsr05_a, adsr05_d, adsr05_s, adsr05_r, adsr05_e, gate),
|
|
adsr_slot(adsr06_a, adsr06_d, adsr06_s, adsr06_r, adsr06_e, gate),
|
|
adsr_slot(adsr07_a, adsr07_d, adsr07_s, adsr07_r, adsr07_e, gate),
|
|
adsr_slot(adsr08_a, adsr08_d, adsr08_s, adsr08_r, adsr08_e, gate),
|
|
adsr_slot(adsr09_a, adsr09_d, adsr09_s, adsr09_r, adsr09_e, gate),
|
|
adsr_slot(adsr10_a, adsr10_d, adsr10_s, adsr10_r, adsr10_e, gate),
|
|
adsr_slot(adsr11_a, adsr11_d, adsr11_s, adsr11_r, adsr11_e, gate),
|
|
adsr_slot(adsr12_a, adsr12_d, adsr12_s, adsr12_r, adsr12_e, gate),
|
|
adsr_slot(adsr13_a, adsr13_d, adsr13_s, adsr13_r, adsr13_e, gate),
|
|
adsr_slot(adsr14_a, adsr14_d, adsr14_s, adsr14_r, adsr14_e, gate),
|
|
adsr_slot(adsr15_a, adsr15_d, adsr15_s, adsr15_r, adsr15_e, gate),
|
|
adsr_slot(adsr16_a, adsr16_d, adsr16_s, adsr16_r, adsr16_e, gate);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// LFO slots (32)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
lfo01_r = hslider("MM_LFO/00_lfo01_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo01_m = hslider("MM_LFO/00_lfo01_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo01_e = hslider("MM_LFO/00_lfo01_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo02_r = hslider("MM_LFO/01_lfo02_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo02_m = hslider("MM_LFO/01_lfo02_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo02_e = hslider("MM_LFO/01_lfo02_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo03_r = hslider("MM_LFO/02_lfo03_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo03_m = hslider("MM_LFO/02_lfo03_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo03_e = hslider("MM_LFO/02_lfo03_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo04_r = hslider("MM_LFO/03_lfo04_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo04_m = hslider("MM_LFO/03_lfo04_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo04_e = hslider("MM_LFO/03_lfo04_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo05_r = hslider("MM_LFO/04_lfo05_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo05_m = hslider("MM_LFO/04_lfo05_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo05_e = hslider("MM_LFO/04_lfo05_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo06_r = hslider("MM_LFO/05_lfo06_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo06_m = hslider("MM_LFO/05_lfo06_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo06_e = hslider("MM_LFO/05_lfo06_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo07_r = hslider("MM_LFO/06_lfo07_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo07_m = hslider("MM_LFO/06_lfo07_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo07_e = hslider("MM_LFO/06_lfo07_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo08_r = hslider("MM_LFO/07_lfo08_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo08_m = hslider("MM_LFO/07_lfo08_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo08_e = hslider("MM_LFO/07_lfo08_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo09_r = hslider("MM_LFO/08_lfo09_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo09_m = hslider("MM_LFO/08_lfo09_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo09_e = hslider("MM_LFO/08_lfo09_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo10_r = hslider("MM_LFO/09_lfo10_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo10_m = hslider("MM_LFO/09_lfo10_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo10_e = hslider("MM_LFO/09_lfo10_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo11_r = hslider("MM_LFO/10_lfo11_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo11_m = hslider("MM_LFO/10_lfo11_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo11_e = hslider("MM_LFO/10_lfo11_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo12_r = hslider("MM_LFO/11_lfo12_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo12_m = hslider("MM_LFO/11_lfo12_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo12_e = hslider("MM_LFO/11_lfo12_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo13_r = hslider("MM_LFO/12_lfo13_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo13_m = hslider("MM_LFO/12_lfo13_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo13_e = hslider("MM_LFO/12_lfo13_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo14_r = hslider("MM_LFO/13_lfo14_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo14_m = hslider("MM_LFO/13_lfo14_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo14_e = hslider("MM_LFO/13_lfo14_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo15_r = hslider("MM_LFO/14_lfo15_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo15_m = hslider("MM_LFO/14_lfo15_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo15_e = hslider("MM_LFO/14_lfo15_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo16_r = hslider("MM_LFO/15_lfo16_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo16_m = hslider("MM_LFO/15_lfo16_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo16_e = hslider("MM_LFO/15_lfo16_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo17_r = hslider("MM_LFO/16_lfo17_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo17_m = hslider("MM_LFO/16_lfo17_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo17_e = hslider("MM_LFO/16_lfo17_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo18_r = hslider("MM_LFO/17_lfo18_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo18_m = hslider("MM_LFO/17_lfo18_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo18_e = hslider("MM_LFO/17_lfo18_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo19_r = hslider("MM_LFO/18_lfo19_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo19_m = hslider("MM_LFO/18_lfo19_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo19_e = hslider("MM_LFO/18_lfo19_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo20_r = hslider("MM_LFO/19_lfo20_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo20_m = hslider("MM_LFO/19_lfo20_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo20_e = hslider("MM_LFO/19_lfo20_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo21_r = hslider("MM_LFO/20_lfo21_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo21_m = hslider("MM_LFO/20_lfo21_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo21_e = hslider("MM_LFO/20_lfo21_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo22_r = hslider("MM_LFO/21_lfo22_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo22_m = hslider("MM_LFO/21_lfo22_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo22_e = hslider("MM_LFO/21_lfo22_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo23_r = hslider("MM_LFO/22_lfo23_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo23_m = hslider("MM_LFO/22_lfo23_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo23_e = hslider("MM_LFO/22_lfo23_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo24_r = hslider("MM_LFO/23_lfo24_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo24_m = hslider("MM_LFO/23_lfo24_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo24_e = hslider("MM_LFO/23_lfo24_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo25_r = hslider("MM_LFO/24_lfo25_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo25_m = hslider("MM_LFO/24_lfo25_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo25_e = hslider("MM_LFO/24_lfo25_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo26_r = hslider("MM_LFO/25_lfo26_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo26_m = hslider("MM_LFO/25_lfo26_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo26_e = hslider("MM_LFO/25_lfo26_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo27_r = hslider("MM_LFO/26_lfo27_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo27_m = hslider("MM_LFO/26_lfo27_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo27_e = hslider("MM_LFO/26_lfo27_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo28_r = hslider("MM_LFO/27_lfo28_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo28_m = hslider("MM_LFO/27_lfo28_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo28_e = hslider("MM_LFO/27_lfo28_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo29_r = hslider("MM_LFO/28_lfo29_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo29_m = hslider("MM_LFO/28_lfo29_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo29_e = hslider("MM_LFO/28_lfo29_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo30_r = hslider("MM_LFO/29_lfo30_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo30_m = hslider("MM_LFO/29_lfo30_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo30_e = hslider("MM_LFO/29_lfo30_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo31_r = hslider("MM_LFO/30_lfo31_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo31_m = hslider("MM_LFO/30_lfo31_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo31_e = hslider("MM_LFO/30_lfo31_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
lfo32_r = hslider("MM_LFO/31_lfo32_rate[scale:log]", 1.0, 0.01, 20.0, 0.001);
|
|
lfo32_m = hslider("MM_LFO/31_lfo32_morph", 0.0, 0.0, 1.0, 0.001);
|
|
lfo32_e = hslider("MM_LFO/31_lfo32_enable", 0.0, 0.0, 1.0, 1.0);
|
|
|
|
// Wavemorph — 0..1 crossfades sine -> triangle -> square -> saw.
|
|
// Uses nested ba.if. Each shape is already in [-1,+1].
|
|
wavemorph(rate, morph) =
|
|
ba.if(morph < 0.333,
|
|
(1.0 - morph*3.0) * os.osc(rate) + (morph*3.0) * os.lf_triangle(rate),
|
|
ba.if(morph < 0.666,
|
|
(1.0 - (morph-0.333)*3.0) * os.lf_triangle(rate) + ((morph-0.333)*3.0) * os.lf_squarewave(rate),
|
|
(1.0 - (morph-0.666)*3.0) * os.lf_squarewave(rate) + ((morph-0.666)*3.0) * os.lf_saw(rate)));
|
|
|
|
lfo_slot(rate, morph, en_flag) = wavemorph(rate, morph) : *(en_flag);
|
|
|
|
lfos =
|
|
lfo_slot(lfo01_r, lfo01_m, lfo01_e),
|
|
lfo_slot(lfo02_r, lfo02_m, lfo02_e),
|
|
lfo_slot(lfo03_r, lfo03_m, lfo03_e),
|
|
lfo_slot(lfo04_r, lfo04_m, lfo04_e),
|
|
lfo_slot(lfo05_r, lfo05_m, lfo05_e),
|
|
lfo_slot(lfo06_r, lfo06_m, lfo06_e),
|
|
lfo_slot(lfo07_r, lfo07_m, lfo07_e),
|
|
lfo_slot(lfo08_r, lfo08_m, lfo08_e),
|
|
lfo_slot(lfo09_r, lfo09_m, lfo09_e),
|
|
lfo_slot(lfo10_r, lfo10_m, lfo10_e),
|
|
lfo_slot(lfo11_r, lfo11_m, lfo11_e),
|
|
lfo_slot(lfo12_r, lfo12_m, lfo12_e),
|
|
lfo_slot(lfo13_r, lfo13_m, lfo13_e),
|
|
lfo_slot(lfo14_r, lfo14_m, lfo14_e),
|
|
lfo_slot(lfo15_r, lfo15_m, lfo15_e),
|
|
lfo_slot(lfo16_r, lfo16_m, lfo16_e),
|
|
lfo_slot(lfo17_r, lfo17_m, lfo17_e),
|
|
lfo_slot(lfo18_r, lfo18_m, lfo18_e),
|
|
lfo_slot(lfo19_r, lfo19_m, lfo19_e),
|
|
lfo_slot(lfo20_r, lfo20_m, lfo20_e),
|
|
lfo_slot(lfo21_r, lfo21_m, lfo21_e),
|
|
lfo_slot(lfo22_r, lfo22_m, lfo22_e),
|
|
lfo_slot(lfo23_r, lfo23_m, lfo23_e),
|
|
lfo_slot(lfo24_r, lfo24_m, lfo24_e),
|
|
lfo_slot(lfo25_r, lfo25_m, lfo25_e),
|
|
lfo_slot(lfo26_r, lfo26_m, lfo26_e),
|
|
lfo_slot(lfo27_r, lfo27_m, lfo27_e),
|
|
lfo_slot(lfo28_r, lfo28_m, lfo28_e),
|
|
lfo_slot(lfo29_r, lfo29_m, lfo29_e),
|
|
lfo_slot(lfo30_r, lfo30_m, lfo30_e),
|
|
lfo_slot(lfo31_r, lfo31_m, lfo31_e),
|
|
lfo_slot(lfo32_r, lfo32_m, lfo32_e);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// sources(gate) — concatenated 48-wide mod-source bus.
|
|
// Order: adsr01..adsr16, lfo01..lfo32
|
|
// ---------------------------------------------------------------------------
|
|
|
|
sources(gate) = adsrs(gate), lfos;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Individual named source functions — 48 total, in canonical order.
|
|
// Engines can reference these directly in matrix sums, since each hslider
|
|
// inside a matrix amount must have a unique compile-time label.
|
|
//
|
|
// Canonical source ordering (source index 0..47):
|
|
// 0..15 : adsr01..adsr16
|
|
// 16..47 : lfo01..lfo32
|
|
// ---------------------------------------------------------------------------
|
|
|
|
src00(gate) = adsr_slot(adsr01_a, adsr01_d, adsr01_s, adsr01_r, adsr01_e, gate);
|
|
src01(gate) = adsr_slot(adsr02_a, adsr02_d, adsr02_s, adsr02_r, adsr02_e, gate);
|
|
src02(gate) = adsr_slot(adsr03_a, adsr03_d, adsr03_s, adsr03_r, adsr03_e, gate);
|
|
src03(gate) = adsr_slot(adsr04_a, adsr04_d, adsr04_s, adsr04_r, adsr04_e, gate);
|
|
src04(gate) = adsr_slot(adsr05_a, adsr05_d, adsr05_s, adsr05_r, adsr05_e, gate);
|
|
src05(gate) = adsr_slot(adsr06_a, adsr06_d, adsr06_s, adsr06_r, adsr06_e, gate);
|
|
src06(gate) = adsr_slot(adsr07_a, adsr07_d, adsr07_s, adsr07_r, adsr07_e, gate);
|
|
src07(gate) = adsr_slot(adsr08_a, adsr08_d, adsr08_s, adsr08_r, adsr08_e, gate);
|
|
src08(gate) = adsr_slot(adsr09_a, adsr09_d, adsr09_s, adsr09_r, adsr09_e, gate);
|
|
src09(gate) = adsr_slot(adsr10_a, adsr10_d, adsr10_s, adsr10_r, adsr10_e, gate);
|
|
src10(gate) = adsr_slot(adsr11_a, adsr11_d, adsr11_s, adsr11_r, adsr11_e, gate);
|
|
src11(gate) = adsr_slot(adsr12_a, adsr12_d, adsr12_s, adsr12_r, adsr12_e, gate);
|
|
src12(gate) = adsr_slot(adsr13_a, adsr13_d, adsr13_s, adsr13_r, adsr13_e, gate);
|
|
src13(gate) = adsr_slot(adsr14_a, adsr14_d, adsr14_s, adsr14_r, adsr14_e, gate);
|
|
src14(gate) = adsr_slot(adsr15_a, adsr15_d, adsr15_s, adsr15_r, adsr15_e, gate);
|
|
src15(gate) = adsr_slot(adsr16_a, adsr16_d, adsr16_s, adsr16_r, adsr16_e, gate);
|
|
|
|
src16(gate) = lfo_slot(lfo01_r, lfo01_m, lfo01_e);
|
|
src17(gate) = lfo_slot(lfo02_r, lfo02_m, lfo02_e);
|
|
src18(gate) = lfo_slot(lfo03_r, lfo03_m, lfo03_e);
|
|
src19(gate) = lfo_slot(lfo04_r, lfo04_m, lfo04_e);
|
|
src20(gate) = lfo_slot(lfo05_r, lfo05_m, lfo05_e);
|
|
src21(gate) = lfo_slot(lfo06_r, lfo06_m, lfo06_e);
|
|
src22(gate) = lfo_slot(lfo07_r, lfo07_m, lfo07_e);
|
|
src23(gate) = lfo_slot(lfo08_r, lfo08_m, lfo08_e);
|
|
src24(gate) = lfo_slot(lfo09_r, lfo09_m, lfo09_e);
|
|
src25(gate) = lfo_slot(lfo10_r, lfo10_m, lfo10_e);
|
|
src26(gate) = lfo_slot(lfo11_r, lfo11_m, lfo11_e);
|
|
src27(gate) = lfo_slot(lfo12_r, lfo12_m, lfo12_e);
|
|
src28(gate) = lfo_slot(lfo13_r, lfo13_m, lfo13_e);
|
|
src29(gate) = lfo_slot(lfo14_r, lfo14_m, lfo14_e);
|
|
src30(gate) = lfo_slot(lfo15_r, lfo15_m, lfo15_e);
|
|
src31(gate) = lfo_slot(lfo16_r, lfo16_m, lfo16_e);
|
|
src32(gate) = lfo_slot(lfo17_r, lfo17_m, lfo17_e);
|
|
src33(gate) = lfo_slot(lfo18_r, lfo18_m, lfo18_e);
|
|
src34(gate) = lfo_slot(lfo19_r, lfo19_m, lfo19_e);
|
|
src35(gate) = lfo_slot(lfo20_r, lfo20_m, lfo20_e);
|
|
src36(gate) = lfo_slot(lfo21_r, lfo21_m, lfo21_e);
|
|
src37(gate) = lfo_slot(lfo22_r, lfo22_m, lfo22_e);
|
|
src38(gate) = lfo_slot(lfo23_r, lfo23_m, lfo23_e);
|
|
src39(gate) = lfo_slot(lfo24_r, lfo24_m, lfo24_e);
|
|
src40(gate) = lfo_slot(lfo25_r, lfo25_m, lfo25_e);
|
|
src41(gate) = lfo_slot(lfo26_r, lfo26_m, lfo26_e);
|
|
src42(gate) = lfo_slot(lfo27_r, lfo27_m, lfo27_e);
|
|
src43(gate) = lfo_slot(lfo28_r, lfo28_m, lfo28_e);
|
|
src44(gate) = lfo_slot(lfo29_r, lfo29_m, lfo29_e);
|
|
src45(gate) = lfo_slot(lfo30_r, lfo30_m, lfo30_e);
|
|
src46(gate) = lfo_slot(lfo31_r, lfo31_m, lfo31_e);
|
|
src47(gate) = lfo_slot(lfo32_r, lfo32_m, lfo32_e);
|