From 6f6a334529f652ab93a6cdf25f154f9fd6aa64eb Mon Sep 17 00:00:00 2001 From: w1n5t0n Date: Sat, 21 Mar 2026 22:26:37 +0200 Subject: [PATCH] feat(playground): add C15 synth mode with NISPS-controlled parameters and arpeggiator Adds a secondary Synth mode (toggled via collapsible left side panel) where the NISPS ML engine's 20 outputs control curated C15 synthesizer parameters (oscillator PM, shapers, filters, reverb, echo, flanger, output mixer) through a WASM AudioWorklet bridge. Includes a chord progression arpeggiator with controls for tempo (BPM), octave range, octave offset, and 4 selectable progressions. The C15 engine runs in an AudioWorklet with lock-free SharedArrayBuffer ring buffer communication. New files: c15/ (WASM assets), js/synth/ (bridge, arpeggiator, param map), serve-coop.py (COOP/COEP headers for SharedArrayBuffer support). --- playground/c15/c15_engine.wasm | Bin 0 -> 269526 bytes playground/c15/parameters.json | 5433 +++++++++++++++++++++++++++ playground/c15/worklet-processor.js | 592 +++ playground/css/style.css | 242 ++ playground/index.html | 98 +- playground/js/app.js | 184 +- playground/js/synth/arpeggiator.js | 121 + playground/js/synth/c15-bridge.js | 210 ++ playground/js/synth/param-map.js | 38 + playground/js/ui/param-display.js | 23 +- playground/serve-coop.py | 18 + playground/serve.sh | 6 +- 12 files changed, 6940 insertions(+), 25 deletions(-) create mode 100755 playground/c15/c15_engine.wasm create mode 100644 playground/c15/parameters.json create mode 100644 playground/c15/worklet-processor.js create mode 100644 playground/js/synth/arpeggiator.js create mode 100644 playground/js/synth/c15-bridge.js create mode 100644 playground/js/synth/param-map.js create mode 100755 playground/serve-coop.py diff --git a/playground/c15/c15_engine.wasm b/playground/c15/c15_engine.wasm new file mode 100755 index 0000000000000000000000000000000000000000..00ea237ec54718d60dd63235fd6dc1e87acebbf6 GIT binary patch literal 269526 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 A). At zero, the output signal of ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_PM_B_Shp", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Shaper" + }, + { + "id": 68, + "valid": true, + "name": "Osc_A_PM_FB", + "displayName": "PM FB", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -0.5, + "max": 0.5, + "bipolar": true, + "description": "Amount of phase modulation by the Feedback signal.", + "category": "Local_Modulateables", + "pid": "Osc_A_PM_FB", + "scaleFactor": 0.5, + "scaleOffset": 0, + "shortName": "PM FB" + }, + { + "id": 70, + "valid": true, + "name": "Osc_A_PM_FB_Env_C", + "displayName": "PM FB - Env C", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (C) amount for the phase modulation by the Feedback signal. At zero, the modulation stays constant, ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_PM_FB_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 71, + "valid": true, + "name": "Shp_A_Drive", + "displayName": "Drive", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0.2, + "scalingType": "Expon_Shaper_Drive", + "min": 0.18, + "max": 50.18, + "bipolar": false, + "description": "Input gain [in dB] of the sine shaper stage. Higher gains will create more distortion and harmonics.", + "category": "Local_Modulateables", + "pid": "Shp_A_Drive", + "scaleFactor": 50, + "scaleOffset": 0.18, + "shortName": "Drive" + }, + { + "id": 73, + "valid": true, + "name": "Shp_A_Drive_Env_A", + "displayName": "Drive - Env A", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (A) amount for the Drive factor. At zero, the gain stays constant, at higher values, the envelope ", + "category": "Local_Unmodulateables", + "pid": "Shp_A_Drive_Env_A", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env A" + }, + { + "id": 74, + "valid": true, + "name": "Shp_A_Fold", + "displayName": "Fold", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of folding back of the shaper curve for high input amplitudes.\\n0.0 %: flat saturation, no ", + "category": "Local_Unmodulateables", + "pid": "Shp_A_Fold", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Fold" + }, + { + "id": 75, + "valid": true, + "name": "Shp_A_Asym", + "displayName": "Asymmetry", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Asymmetry of the shaper curve, generating even (2nd, 4th, ...) harmonics. At higher values it becomes a ", + "category": "Local_Unmodulateables", + "pid": "Shp_A_Asym", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Asym" + }, + { + "id": 76, + "valid": true, + "name": "Shp_A_Mix", + "displayName": "Mix", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Mix amount of Shaper A in the signal sent to the Filters and to the Output Mixer. At zero, it is the input ", + "category": "Local_Modulateables", + "pid": "Shp_A_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 78, + "valid": true, + "name": "Shp_A_FB_Mix", + "displayName": "FB Mix", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0, + "scalingType": "Cubic", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between Oscillator & Shaper A (at zero) and the Feedback signal for the signal A.", + "category": "Local_Modulateables", + "pid": "Shp_A_FB_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "FB Mix" + }, + { + "id": 80, + "valid": true, + "name": "Shp_A_FB_Env_C", + "displayName": "FB - Env C", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (C) amount for the Feedback Mix. At zero, only a Gate signal is applied, at higher values, Envelope ", + "category": "Local_Unmodulateables", + "pid": "Shp_A_FB_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 81, + "valid": true, + "name": "Shp_A_Ring_Mod", + "displayName": "Ring Mod", + "groupName": "Shaper A", + "groupShortName": "Shaper A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Mix amount of the ring modulation between both Oscillators & Shapers.", + "category": "Local_Modulateables", + "pid": "Shp_A_Ring_Mod", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Ring Mod" + }, + { + "id": 83, + "valid": true, + "name": "Osc_B_Pitch", + "displayName": "Pitch", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0.5333333333333333, + "scalingType": "Linear", + "min": -20, + "max": 130, + "bipolar": false, + "description": "Pitch (logarithmic frequency) of Oscillator B at C3 (MIDI note 60) [in semitones, based on MIDI note ", + "category": "Local_Modulateables", + "pid": "Osc_B_Pitch", + "scaleFactor": 150, + "scaleOffset": -20, + "shortName": "Pitch" + }, + { + "id": 85, + "valid": true, + "name": "Osc_B_Pitch_KT", + "displayName": "Pitch - Key Trk", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0.9523809523809523, + "scalingType": "Linear", + "min": 0, + "max": 1.05, + "bipolar": false, + "description": "Key tracking of the oscillator pitch. It's the scaling factor between the key position of a received MIDI ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_Pitch_KT", + "scaleFactor": 1.05, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 86, + "valid": true, + "name": "Osc_B_Pitch_Env_C", + "displayName": "Pitch - Env C", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -80, + "max": 80, + "bipolar": true, + "description": "Amount of pitch modulation by Envelope C [in semitones].", + "category": "Local_Unmodulateables", + "pid": "Osc_B_Pitch_Env_C", + "scaleFactor": 80, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 87, + "valid": true, + "name": "Osc_B_Fluct", + "displayName": "Fluctuation", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": 0, + "max": 0.95, + "bipolar": false, + "description": "Fluctuation of the oscillator frequency. If the parameter is set larger than 0.0 %, the frequency is changed ", + "category": "Local_Modulateables", + "pid": "Osc_B_Fluct", + "scaleFactor": 0.95, + "scaleOffset": 0, + "shortName": "Fluctuation" + }, + { + "id": 89, + "valid": true, + "name": "Osc_B_Fluct_Env_C", + "displayName": "Fluct - Env C", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of modulation of the frequency fluctuation by Envelope C. At zero, the modulation stays constant, at ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_Fluct_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 90, + "valid": true, + "name": "Osc_B_PM_Self", + "displayName": "PM Self", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -0.5, + "max": 0.5, + "bipolar": true, + "description": "Amount of phase modulation by Oscillator & Shaper B (local feedback).", + "category": "Local_Modulateables", + "pid": "Osc_B_PM_Self", + "scaleFactor": 0.5, + "scaleOffset": 0, + "shortName": "PM Self" + }, + { + "id": 92, + "valid": true, + "name": "Osc_B_PM_Self_Env_B", + "displayName": "PM Self - Env B", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (B) amount for the phase modulation by Oscillator & Shaper B (local feedback). At zero, the ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_PM_Self_Env_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env B" + }, + { + "id": 93, + "valid": true, + "name": "Osc_B_PM_Self_Shp", + "displayName": "PM Self - Shaper", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Mix amount of Shaper B in the signal being used for phase modulation (self). At zero, the output signal of ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_PM_Self_Shp", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Shaper" + }, + { + "id": 94, + "valid": true, + "name": "Osc_B_PM_A", + "displayName": "PM A", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Amount of phase modulation by Oscillator & Shaper A (cross feedback).", + "category": "Local_Modulateables", + "pid": "Osc_B_PM_A", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "PM A" + }, + { + "id": 96, + "valid": true, + "name": "Osc_B_PM_A_Env_A", + "displayName": "PM A - Env A", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (A) amount for the pahse modulation by Oscillator & Shaper A (cross feedback). At zero, the ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_PM_A_Env_A", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env A" + }, + { + "id": 97, + "valid": true, + "name": "Osc_B_PM_A_Shp", + "displayName": "PM A - Shaper", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Mix amount of Shaper A in the signal being used for phase modulation (A -> B). At zero, the output signal of ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_PM_A_Shp", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Shaper" + }, + { + "id": 98, + "valid": true, + "name": "Osc_B_PM_FB", + "displayName": "PM FB", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -0.5, + "max": 0.5, + "bipolar": true, + "description": "Amount of phase modulation by the Feedback signal.", + "category": "Local_Modulateables", + "pid": "Osc_B_PM_FB", + "scaleFactor": 0.5, + "scaleOffset": 0, + "shortName": "PM FB" + }, + { + "id": 100, + "valid": true, + "name": "Osc_B_PM_FB_Env_C", + "displayName": "PM FB - Env C", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (C) amount for the phase modulation by the Feedback signal. At zero, the modulation stays constant, ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_PM_FB_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 101, + "valid": true, + "name": "Shp_B_Drive", + "displayName": "Drive", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0.2, + "scalingType": "Expon_Shaper_Drive", + "min": 0.18, + "max": 50.18, + "bipolar": false, + "description": "Input gain [in dB] of the sine shaper stage. Higher gains will create more distortion and harmonics.", + "category": "Local_Modulateables", + "pid": "Shp_B_Drive", + "scaleFactor": 50, + "scaleOffset": 0.18, + "shortName": "Drive" + }, + { + "id": 103, + "valid": true, + "name": "Shp_B_Drive_Env_B", + "displayName": "Drive - Env B", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (B) amount for the Drive factor. At zero, the gain stays constant, at higher values, the envelope ", + "category": "Local_Unmodulateables", + "pid": "Shp_B_Drive_Env_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env B" + }, + { + "id": 104, + "valid": true, + "name": "Shp_B_Fold", + "displayName": "Fold", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of folding back of the shaper curve for high input amplitudes.\\n0.0 %: flat saturation, no ", + "category": "Local_Unmodulateables", + "pid": "Shp_B_Fold", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Fold" + }, + { + "id": 105, + "valid": true, + "name": "Shp_B_Asym", + "displayName": "Asymmetry", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Asymmetry of the shaper curve, generating even (2nd, 4th, ...) harmonics. At higher values it becomes a ", + "category": "Local_Unmodulateables", + "pid": "Shp_B_Asym", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Asym" + }, + { + "id": 106, + "valid": true, + "name": "Shp_B_Mix", + "displayName": "Mix", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Mix amount of Shaper B in the signal sent to the Filters and to the Output Mixer. At zero, it is the input ", + "category": "Local_Modulateables", + "pid": "Shp_B_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 108, + "valid": true, + "name": "Shp_B_FB_Mix", + "displayName": "FB Mix", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0, + "scalingType": "Cubic", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between Oscillator & Shaper B (at zero) and the Feedback signal for the signal B.", + "category": "Local_Modulateables", + "pid": "Shp_B_FB_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "FB Mix" + }, + { + "id": 110, + "valid": true, + "name": "Shp_B_FB_Env_C", + "displayName": "FB - Env C", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (C) amount for the Feedback Mix. At zero, only a Gate signal is applied, at higher values, Envelope ", + "category": "Local_Unmodulateables", + "pid": "Shp_B_FB_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 111, + "valid": true, + "name": "Shp_B_Ring_Mod", + "displayName": "Ring Mod", + "groupName": "Shaper B", + "groupShortName": "Shaper B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Mix amount of the ring modulation between both Oscillators & Shapers.", + "category": "Local_Modulateables", + "pid": "Shp_B_Ring_Mod", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Ring Mod" + }, + { + "id": 113, + "valid": true, + "name": "Comb_Flt_In_A_B", + "displayName": "A - B", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "S_Curve", + "min": 0, + "max": 1, + "bipolar": false, + "description": "The signal for the Comb Filter as a crossfade between the outputs of Oscillator & Shaper A and Oscillator & ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_In_A_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "A - B" + }, + { + "id": 115, + "valid": true, + "name": "Comb_Flt_Pitch", + "displayName": "Pitch", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0.5, + "scalingType": "Expon_Lin_Pitch", + "min": 0, + "max": 120, + "bipolar": false, + "description": "Coarse pitch of the Comb Filter (delay) at C3 (MIDI note 60) [in semitones, based on MIDI note numbers].", + "category": "Local_Modulateables", + "pid": "Comb_Flt_Pitch", + "scaleFactor": 120, + "scaleOffset": 0, + "shortName": "Pitch" + }, + { + "id": 117, + "valid": true, + "name": "Comb_Flt_Pitch_KT", + "displayName": "Pitch - Key Trk", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0.9523809523809523, + "scalingType": "Linear", + "min": 0, + "max": 1.05, + "bipolar": false, + "description": "Key scaling of the tuning of the Comb Filter (delay).\\n0.0 %: same tuning for all keys\\n100.0 %: full ", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_Pitch_KT", + "scaleFactor": 1.05, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 118, + "valid": true, + "name": "Comb_Flt_Pitch_Env_C", + "displayName": "Pitch - Env C", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -80, + "max": 80, + "bipolar": true, + "description": "Amount of modulation of the Comb Filter pitch by Envelope C [in semitones].", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_Pitch_Env_C", + "scaleFactor": 80, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 119, + "valid": true, + "name": "Comb_Flt_Decay", + "displayName": "Decay", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -100, + "max": 100, + "bipolar": true, + "description": "Amount of the internal feedback noticeable as the decay time of the impules response [logarithmic scaling]. ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_Decay", + "scaleFactor": 100, + "scaleOffset": 0, + "shortName": "Decay" + }, + { + "id": 121, + "valid": true, + "name": "Comb_Flt_Decay_KT", + "displayName": "Decay - Key Trk", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0.33, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key scaling of the decay time.\\n0.0 %: equal time for all keys\\n100.0 %: shortening to half time per octave, ", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_Decay_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 123, + "valid": true, + "name": "Comb_Flt_AP_Tune", + "displayName": "Allpass - Tune", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 1, + "scalingType": "Expon_Lin_Pitch", + "min": 0, + "max": 140, + "bipolar": false, + "description": "Center frequency of the 2-pole allpass filter [in semitones]. It is the frequency where the phase is shifted ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_AP_Tune", + "scaleFactor": 140, + "scaleOffset": 0, + "shortName": "AP Tune" + }, + { + "id": 125, + "valid": true, + "name": "Comb_Flt_AP_KT", + "displayName": "Allpass - Key Trk", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key scaling of the allpass filter frequency.\\n0.0 %: same tuning for all keys\\n100.0 %: full tracking with ", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_AP_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 126, + "valid": true, + "name": "Comb_Flt_AP_Env_C", + "displayName": "Allpass - Env C", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -80, + "max": 80, + "bipolar": true, + "description": "Amount of modulation of the allpass center frequency by Envelope C [in semitones].", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_AP_Env_C", + "scaleFactor": 80, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 127, + "valid": true, + "name": "Comb_Flt_AP_Res", + "displayName": "Allpass - Reson", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Resonance of the 2-pole allpass. Determines how much the phase shift increases around the center ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_AP_Res", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "AP Reson" + }, + { + "id": 129, + "valid": true, + "name": "Comb_Flt_LP_Tune", + "displayName": "Hi Cut", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 1, + "scalingType": "Expon_Lin_Pitch", + "min": 40, + "max": 140, + "bipolar": false, + "description": "Cutoff frequency [in semitones] of the lowpass filter that damps the higher frequencies in the comb filter's ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_LP_Tune", + "scaleFactor": 100, + "scaleOffset": 40, + "shortName": "Hi Cut" + }, + { + "id": 131, + "valid": true, + "name": "Comb_Flt_LP_KT", + "displayName": "Hi Cut - Key Trk", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key scaling of the lowpass cutoff frequency.\\n0.0 %: same cutoff for all keys\\n100.0 %: full tracking with ", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_LP_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 132, + "valid": true, + "name": "Comb_Flt_LP_Env_C", + "displayName": "Hi Cut - Env C ", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -100, + "max": 100, + "bipolar": true, + "description": "Amount of modulation of the cutoff frequency of the lowpass filter by Envelope C [in semitones].", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_LP_Env_C", + "scaleFactor": 100, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 133, + "valid": true, + "name": "Comb_Flt_PM", + "displayName": "PM", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -0.9, + "max": 0.9, + "bipolar": true, + "description": "Amount of (phase) modulation of the comb filter pitch (delay time) by the output signals of Oscillator & ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_PM", + "scaleFactor": 0.9, + "scaleOffset": 0, + "shortName": "PM" + }, + { + "id": 135, + "valid": true, + "name": "Comb_Flt_PM_A_B", + "displayName": "PM - A-B", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "The (phase) modulation signal for the Comb Filter as a crossfade between the outputs of Oscillator & Shaper A ", + "category": "Local_Unmodulateables", + "pid": "Comb_Flt_PM_A_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "A - B" + }, + { + "id": 136, + "valid": true, + "name": "SV_Flt_In_A_B", + "displayName": "A - B", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "The signal for the State Variable Filter as a crossfade between the outputs of Oscillator & Shaper A and ", + "category": "Local_Modulateables", + "pid": "SV_Flt_In_A_B", + "shortName": "A - B" + }, + { + "id": 138, + "valid": true, + "name": "SV_Flt_Comb_Mix", + "displayName": "Comb Mix", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "The input signal for the State Variable Filter as mix of the signals from the A-B mixer and from the Comb ", + "category": "Local_Modulateables", + "pid": "SV_Flt_Comb_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Comb Mix" + }, + { + "id": 140, + "valid": true, + "name": "SV_Flt_Cut", + "displayName": "Cutoff", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0.5, + "scalingType": "Expon_Lin_Pitch", + "min": 20, + "max": 140, + "bipolar": false, + "description": "Static value of the filter cutoff frequency at C3 [in semitones], applies to both stages of the filter. The ", + "category": "Local_Modulateables", + "pid": "SV_Flt_Cut", + "scaleFactor": 120, + "scaleOffset": 20, + "shortName": "Cutoff" + }, + { + "id": 142, + "valid": true, + "name": "SV_Flt_Cut_KT", + "displayName": "Cutoff - Key Trk", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key scaling of the filter cutoffs.\\n0.0 %: no influence\\n100.0 %: full tracking with the keys, origin at C3 = ", + "category": "Local_Unmodulateables", + "pid": "SV_Flt_Cut_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 143, + "valid": true, + "name": "SV_Flt_Cut_Env_C", + "displayName": "Cutoff - Env C", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -100, + "max": 100, + "bipolar": true, + "description": "Amount of cutoff modulation by Envelope C [in semitones].", + "category": "Local_Unmodulateables", + "pid": "SV_Flt_Cut_Env_C", + "scaleFactor": 100, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 144, + "valid": true, + "name": "SV_Flt_Res", + "displayName": "Resonance", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of filter resonance, creating peaks at the cutoff frequencies.", + "category": "Local_Modulateables", + "pid": "SV_Flt_Res", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Reson" + }, + { + "id": 146, + "valid": true, + "name": "SV_Flt_Res_KT", + "displayName": "Reson - Key Trk", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Key scaling of the filter resonance.\\n0.0 %: no influence\\n100.0 %: full tracking with the keys, origin at C3 ", + "category": "Local_Unmodulateables", + "pid": "SV_Flt_Res_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 147, + "valid": true, + "name": "SV_Flt_Res_Env_C", + "displayName": "Reson - Env C ", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Amount of resonance modulation by Envelope C.", + "category": "Local_Unmodulateables", + "pid": "SV_Flt_Res_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 148, + "valid": true, + "name": "SV_Flt_Spread", + "displayName": "Spread", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0.2, + "scalingType": "Linear", + "min": -30, + "max": 30, + "bipolar": true, + "description": "Amount of splitting of the cutoffs of the two 2-pole filters. Half of the value is applied as a positive ", + "category": "Local_Modulateables", + "pid": "SV_Flt_Spread", + "scaleFactor": 30, + "scaleOffset": 0, + "shortName": "Spread" + }, + { + "id": 150, + "valid": true, + "name": "SV_Flt_LBH", + "displayName": "L - B - H", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between the lowpass, bandpass and highpass outputs of the two filter stages.\\n(first stage -> ", + "category": "Local_Modulateables", + "pid": "SV_Flt_LBH", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "L-B-H" + }, + { + "id": 152, + "valid": true, + "name": "SV_Flt_Par", + "displayName": "Parallel", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "At zero, the two 2-pole filter stages are in series forming a 4-pole filter. Positive values crossfade from ", + "category": "Local_Unmodulateables", + "pid": "SV_Flt_Par", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Parallel " + }, + { + "id": 153, + "valid": true, + "name": "SV_Flt_FM", + "displayName": "FM", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -5, + "max": 5, + "bipolar": true, + "description": "Amount of modulation of the cutoff frequencies by the output signals of Oscillator & Shaper A and B, with the ", + "category": "Local_Modulateables", + "pid": "SV_Flt_FM", + "scaleFactor": 5, + "scaleOffset": 0, + "shortName": "FM" + }, + { + "id": 155, + "valid": true, + "name": "SV_Flt_FM_A_B", + "displayName": "FM - A-B", + "groupName": "State Variable Filter", + "groupShortName": "SV Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "The signal for the FM (cutoff frequency modulation) of the State Variable Filter as a crossfade between the ", + "category": "Local_Unmodulateables", + "pid": "SV_Flt_FM_A_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "A - B" + }, + { + "id": 156, + "valid": true, + "name": "FB_Mix_Comb", + "displayName": "Comb Filter", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Feedback mix factor for the output of the Comb Filter.", + "category": "Local_Modulateables", + "pid": "FB_Mix_Comb", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Comb" + }, + { + "id": 158, + "valid": true, + "name": "FB_Mix_SVF", + "displayName": "SV Filter", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Feedback mix factor for the output of the State Variable Filter.", + "category": "Local_Modulateables", + "pid": "FB_Mix_SVF", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "SV Filter" + }, + { + "id": 160, + "valid": true, + "name": "FB_Mix_FX", + "displayName": "Effects", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Feedback mix factor for the output of the Effects chain. The reverb amount in the feedback can be set by the ", + "category": "Local_Modulateables", + "pid": "FB_Mix_FX", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Effects" + }, + { + "id": 162, + "valid": true, + "name": "FB_Mix_Rvb", + "displayName": "Reverb Mix", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Controls the amount of reverb in the feedback independantly from the Mix in the Reverb section.\\n0.0 %: dry, ", + "category": "Local_Modulateables", + "pid": "FB_Mix_Rvb", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Rev Mix" + }, + { + "id": 164, + "valid": true, + "name": "FB_Mix_Drive", + "displayName": "Drive", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0.2857142857142857, + "scalingType": "Expon_Mix_Drive", + "min": 0.25, + "max": 70.25, + "bipolar": false, + "description": "Input gain [in dB] of the sine shaper stage. Higher gains will create more distortion and harmonics.", + "category": "Local_Modulateables", + "pid": "FB_Mix_Drive", + "scaleFactor": 70, + "scaleOffset": 0.25, + "shortName": "Drive" + }, + { + "id": 166, + "valid": true, + "name": "FB_Mix_Fold", + "displayName": "Fold", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of folding back of the shaper curve for high input amplitudes.\\n0.0 %: flat saturation, no ", + "category": "Local_Unmodulateables", + "pid": "FB_Mix_Fold", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Fold" + }, + { + "id": 167, + "valid": true, + "name": "FB_Mix_Asym", + "displayName": "Asymmetry", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Asymmetry of the shaper curve, generating even (2nd, 4th, ...) harmonics. At higher values, it becomes a ", + "category": "Local_Unmodulateables", + "pid": "FB_Mix_Asym", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Asym" + }, + { + "id": 168, + "valid": true, + "name": "FB_Mix_Lvl_KT", + "displayName": "Level - Key Trk", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Key scaling of the feedback level [in dB per semitone].\\npositive values: higher level for higher notes (+ ", + "category": "Local_Unmodulateables", + "pid": "FB_Mix_Lvl_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 169, + "valid": true, + "name": "Out_Mix_A_Lvl", + "displayName": "A - Level", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0.75, + "scalingType": "Linear", + "min": -2, + "max": 2, + "bipolar": true, + "description": "Output mix factor for the signal from Oscillator & Shaper A.", + "category": "Local_Modulateables", + "pid": "Out_Mix_A_Lvl", + "scaleFactor": 2, + "scaleOffset": 0, + "shortName": "A" + }, + { + "id": 171, + "valid": true, + "name": "Out_Mix_A_Pan", + "displayName": "A - Pan", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": true, + "description": "Pan position of the signal from Oscillator & Shaper A in the output mix.", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_A_Pan", + "scaleFactor": 0.5, + "scaleOffset": 0.5, + "shortName": "Pan" + }, + { + "id": 172, + "valid": true, + "name": "Out_Mix_B_Lvl", + "displayName": "B - Level", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -2, + "max": 2, + "bipolar": true, + "description": "Output mix factor for the signal from Oscillator & Shaper B.", + "category": "Local_Modulateables", + "pid": "Out_Mix_B_Lvl", + "scaleFactor": 2, + "scaleOffset": 0, + "shortName": "B" + }, + { + "id": 174, + "valid": true, + "name": "Out_Mix_B_Pan", + "displayName": "B - Pan", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": true, + "description": "Pan position of the signal from Oscillator & Shaper B in the output mix.", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_B_Pan", + "scaleFactor": 0.5, + "scaleOffset": 0.5, + "shortName": "Pan" + }, + { + "id": 175, + "valid": true, + "name": "Out_Mix_Comb_Lvl", + "displayName": "Comb - Level", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -2, + "max": 2, + "bipolar": true, + "description": "Output mix factor for the signal from the Comb Filter.", + "category": "Local_Modulateables", + "pid": "Out_Mix_Comb_Lvl", + "scaleFactor": 2, + "scaleOffset": 0, + "shortName": "Comb" + }, + { + "id": 177, + "valid": true, + "name": "Out_Mix_Comb_Pan", + "displayName": "Comb - Pan", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": true, + "description": "Pan position of the signal from the Comb Filter in the output mix.", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_Comb_Pan", + "scaleFactor": 0.5, + "scaleOffset": 0.5, + "shortName": "Pan" + }, + { + "id": 178, + "valid": true, + "name": "Out_Mix_SVF_Lvl", + "displayName": "SV Filter - Level", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -2, + "max": 2, + "bipolar": true, + "description": "Output mix factor for the signal from the State Variable Filter.", + "category": "Local_Modulateables", + "pid": "Out_Mix_SVF_Lvl", + "scaleFactor": 2, + "scaleOffset": 0, + "shortName": "SV Filter" + }, + { + "id": 180, + "valid": true, + "name": "Out_Mix_SVF_Pan", + "displayName": "SV Filter - Pan", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": true, + "description": "Pan position of the signal from the State Variable Filter in the output mix.", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_SVF_Pan", + "scaleFactor": 0.5, + "scaleOffset": 0.5, + "shortName": "Pan" + }, + { + "id": 181, + "valid": true, + "name": "Out_Mix_Drive", + "displayName": "Drive", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Expon_Mix_Drive", + "min": 0.25, + "max": 50.25, + "bipolar": false, + "description": "Input gain [in dB] of the sine shaper stage. Higher gains will create more distortion and harmonics.", + "category": "Local_Modulateables", + "pid": "Out_Mix_Drive", + "scaleFactor": 50, + "scaleOffset": 0.25, + "shortName": "Drive" + }, + { + "id": 183, + "valid": true, + "name": "Out_Mix_Fold", + "displayName": "Fold", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of folding back of the shaper curve for high input amplitudes.\\n0.0 %: flat saturation, no ", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_Fold", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Fold" + }, + { + "id": 184, + "valid": true, + "name": "Out_Mix_Asym", + "displayName": "Asymmetry", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Asymmetry of the shaper curve, generating even (2nd, 4th, ...) harmonics. At higher values, it becomes a ", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_Asym", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Asym" + }, + { + "id": 185, + "valid": true, + "name": "Out_Mix_Lvl", + "displayName": "Level", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0.38, + "scalingType": "Parabolic", + "min": 0, + "max": 2.56, + "bipolar": false, + "description": "Master output level [in dB] of the synth. The output signal will be processed by the (monophonic) chain of ", + "category": "Local_Modulateables", + "pid": "Out_Mix_Lvl", + "scaleFactor": 2.56, + "scaleOffset": 0, + "shortName": "Level" + }, + { + "id": 187, + "valid": true, + "name": "Out_Mix_Key_Pan", + "displayName": "Key Pan", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of key panning for each note (referring to the key position relative to the center at C3 = 60 ", + "category": "Local_Unmodulateables", + "pid": "Out_Mix_Key_Pan", + "shortName": "Key Pan" + }, + { + "id": 188, + "valid": true, + "name": "Cabinet_Drive", + "displayName": "Drive", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.4, + "scalingType": "Expon_Gain", + "min": 0, + "max": 50, + "bipolar": false, + "description": "Gain [in dB] for the input signal. Higher gains will increase the amount of distortion/saturation.", + "category": "Local_Modulateables", + "pid": "Cabinet_Drive", + "scaleFactor": 50, + "scaleOffset": 0, + "shortName": "Drive" + }, + { + "id": 190, + "valid": true, + "name": "Cabinet_Fold", + "displayName": "Fold", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.25, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of folding back of the shaper curve for high input amplitudes.\\n0.0 %: flat saturation, no ", + "category": "Local_Unmodulateables", + "pid": "Cabinet_Fold", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Fold" + }, + { + "id": 191, + "valid": true, + "name": "Cabinet_Asym", + "displayName": "Asymmetry", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.25, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Asymmetry of the shaper curve, generating even (2nd, 4th, ...) harmonics. At higher values, it becomes a ", + "category": "Local_Unmodulateables", + "pid": "Cabinet_Asym", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Asym" + }, + { + "id": 192, + "valid": true, + "name": "Cabinet_Tilt", + "displayName": "Tilt", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": -100, + "max": 100, + "bipolar": true, + "description": "Controls two inverted shelving EQs before and after the distortion stage.\\nNegative: more distortions at high ", + "category": "Local_Modulateables", + "pid": "Cabinet_Tilt", + "scaleFactor": 100, + "scaleOffset": 0, + "shortName": "Tilt" + }, + { + "id": 194, + "valid": true, + "name": "Cabinet_Hi_Cut", + "displayName": "Hi Cut", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.625, + "scalingType": "Expon_Lin_Pitch", + "min": 60, + "max": 140, + "bipolar": false, + "description": "Cutoff frequency [in semitones] of the lowpass filter at the output.", + "category": "Local_Modulateables", + "pid": "Cabinet_Hi_Cut", + "scaleFactor": 80, + "scaleOffset": 60, + "shortName": "Hi Cut" + }, + { + "id": 196, + "valid": true, + "name": "Cabinet_Lo_Cut", + "displayName": "Lo Cut", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.125, + "scalingType": "Expon_Lin_Pitch", + "min": 20, + "max": 100, + "bipolar": false, + "description": "Cutoff frequency [in semitones] of the highpass filter at the input.", + "category": "Local_Unmodulateables", + "pid": "Cabinet_Lo_Cut", + "scaleFactor": 80, + "scaleOffset": 20, + "shortName": "Lo Cut" + }, + { + "id": 197, + "valid": true, + "name": "Cabinet_Cab_Lvl", + "displayName": "Cab Level", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0.72, + "scalingType": "Expon_Gain", + "min": -50, + "max": 0, + "bipolar": false, + "description": "Output level [in dB] of the saturation effect before it is mixed with the dry signal.", + "category": "Local_Modulateables", + "pid": "Cabinet_Cab_Lvl", + "scaleFactor": 50, + "scaleOffset": -50, + "shortName": "Cab Level" + }, + { + "id": 199, + "valid": true, + "name": "Cabinet_Mix", + "displayName": "Mix", + "groupName": "Cabinet", + "groupShortName": "Cabinet", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between the dry signal and the saturated signal.", + "category": "Local_Modulateables", + "pid": "Cabinet_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 201, + "valid": true, + "name": "Gap_Flt_Center", + "displayName": "Center", + "groupName": "Gap Filter", + "groupShortName": "Gap Filter", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 24, + "max": 120, + "bipolar": false, + "description": "Shifts the mean cutoff frequency of both 4-pole filters on both channels up or down [in semitones].", + "category": "Local_Modulateables", + "pid": "Gap_Flt_Center", + "scaleFactor": 96, + "scaleOffset": 24, + "shortName": "Center" + }, + { + "id": 203, + "valid": true, + "name": "Gap_Flt_Stereo", + "displayName": "Stereo", + "groupName": "Gap Filter", + "groupShortName": "Gap Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -36, + "max": 36, + "bipolar": true, + "description": "Sets the difference between the center frequencies of the left and of the right channel [in semitones].", + "category": "Local_Unmodulateables", + "pid": "Gap_Flt_Stereo", + "scaleFactor": 36, + "scaleOffset": 0, + "shortName": "Stereo" + }, + { + "id": 204, + "valid": true, + "name": "Gap_Flt_Gap", + "displayName": "Gap", + "groupName": "Gap Filter", + "groupShortName": "Gap Filter", + "defaultValue": 0.125, + "scalingType": "Linear", + "min": 0, + "max": 48, + "bipolar": false, + "description": "Offset between the cutoffs of the lowpass and the highpass [in semitones]. When the filters are running in ", + "category": "Local_Modulateables", + "pid": "Gap_Flt_Gap", + "scaleFactor": 48, + "scaleOffset": 0, + "shortName": "Gap" + }, + { + "id": 206, + "valid": true, + "name": "Gap_Flt_Res", + "displayName": "Resonance", + "groupName": "Gap Filter", + "groupShortName": "Gap Filter", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 0.9, + "bipolar": false, + "description": "Resonance of both filters. Higher values create two resonance peaks at the upper and lower end of the ", + "category": "Local_Unmodulateables", + "pid": "Gap_Flt_Res", + "scaleFactor": 0.9, + "scaleOffset": 0, + "shortName": "Reson" + }, + { + "id": 207, + "valid": true, + "name": "Gap_Flt_Bal", + "displayName": "Balance", + "groupName": "Gap Filter", + "groupShortName": "Gap Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Balance between the levels of the ranges above and below the gap.\\nIn the center position both ranges are ", + "category": "Local_Modulateables", + "pid": "Gap_Flt_Bal", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Balance" + }, + { + "id": 209, + "valid": true, + "name": "Gap_Flt_Mix", + "displayName": "Mix", + "groupName": "Gap Filter", + "groupShortName": "Gap Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Crossfades between the dry signal and the filtered signal. At positive values, the filter runs in parallel ", + "category": "Local_Modulateables", + "pid": "Gap_Flt_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 211, + "valid": true, + "name": "Flanger_Time_Mod", + "displayName": "Time Mod", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Relative amount of the modulation of the delay times by the LFO and/or the envelope.", + "category": "Local_Modulateables", + "pid": "Flanger_Time_Mod", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Time Mod" + }, + { + "id": 213, + "valid": true, + "name": "Flanger_Phase", + "displayName": "Phase", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 180, + "bipolar": false, + "description": "Phase offset between the LFO signals modulating the delay times in the left and the right channel.", + "category": "Local_Unmodulateables", + "pid": "Flanger_Phase", + "scaleFactor": 180, + "scaleOffset": 0, + "shortName": "Phase" + }, + { + "id": 214, + "valid": true, + "name": "Flanger_Rate", + "displayName": "Rate", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0.317, + "scalingType": "Parabolic", + "min": 0, + "max": 10, + "bipolar": false, + "description": "Frequency of the LFO and rate of the envelope. Both can be modulation sources for the delay times and the ", + "category": "Local_Modulateables", + "pid": "Flanger_Rate", + "scaleFactor": 10, + "scaleOffset": 0, + "shortName": "Rate" + }, + { + "id": 216, + "valid": true, + "name": "Flanger_Time", + "displayName": "Time", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0.317, + "scalingType": "Parabolic", + "min": 0, + "max": 50, + "bipolar": false, + "description": "Mean value of the delay times in the left and right channel.\\nThe \\", + "category": "Local_Modulateables", + "pid": "Flanger_Time", + "scaleFactor": 50, + "scaleOffset": 0, + "shortName": "Time" + }, + { + "id": 218, + "valid": true, + "name": "Flanger_Stereo", + "displayName": "Stereo", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Linear", + "min": -50, + "max": 50, + "bipolar": true, + "description": "Sets the ratio between the delay times of the left and of the right channel [the value shows the offset to ", + "category": "Local_Unmodulateables", + "pid": "Flanger_Stereo", + "scaleFactor": 50, + "scaleOffset": 0, + "shortName": "Stereo" + }, + { + "id": 219, + "valid": true, + "name": "Flanger_Feedback", + "displayName": "Feedback", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": true, + "description": "Amount of the internal feedback. At negative values, the feedback is inverted and will emphasize other ", + "category": "Local_Modulateables", + "pid": "Flanger_Feedback", + "scaleFactor": 0.5, + "scaleOffset": 0.5, + "shortName": "Feedback" + }, + { + "id": 221, + "valid": true, + "name": "Flanger_Cross_FB", + "displayName": "Cross FB", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Amount of the cross feedback between the left and the right channel, increasing the complexity of the ", + "category": "Local_Unmodulateables", + "pid": "Flanger_Cross_FB", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Cross FB" + }, + { + "id": 222, + "valid": true, + "name": "Flanger_Hi_Cut", + "displayName": "Hi Cut", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0.75, + "scalingType": "Expon_Lin_Pitch", + "min": 60, + "max": 140, + "bipolar": false, + "description": "Cutoff frequency [in semitones] of the filter that damps the higher frequencies of the delayed signal.", + "category": "Local_Unmodulateables", + "pid": "Flanger_Hi_Cut", + "scaleFactor": 80, + "scaleOffset": 60, + "shortName": "Hi Cut" + }, + { + "id": 223, + "valid": true, + "name": "Flanger_Mix", + "displayName": "Mix", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Crossfades between the dry signal and the delayed signal. At negative values, the delayed signal is ", + "category": "Local_Modulateables", + "pid": "Flanger_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 225, + "valid": true, + "name": "Echo_Time", + "displayName": "Time", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 0.433, + "scalingType": "Parabolic", + "min": 0, + "max": 2, + "bipolar": false, + "description": "Mean delay time [in milliseconds]. (As there can be an offset between the left and right channel, this ", + "category": "Local_Modulateables", + "pid": "Echo_Time", + "scaleFactor": 2, + "scaleOffset": 0, + "shortName": "Time" + }, + { + "id": 227, + "valid": true, + "name": "Echo_Stereo", + "displayName": "Stereo", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 0, + "scalingType": "Linear", + "min": -33, + "max": 33, + "bipolar": true, + "description": "Sets the ratio between the delay times of the left and of the right channel [the value shows the offset to ", + "category": "Local_Modulateables", + "pid": "Echo_Stereo", + "scaleFactor": 33, + "scaleOffset": 0, + "shortName": "Stereo" + }, + { + "id": 229, + "valid": true, + "name": "Echo_Feedback", + "displayName": "Feedback", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of internal feedback.", + "category": "Local_Modulateables", + "pid": "Echo_Feedback", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Feedback" + }, + { + "id": 231, + "valid": true, + "name": "Echo_Cross_FB", + "displayName": "Cross FB", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of the cross feedback between the left and right channel.\\n0.0 %: no cross feedback\\n100.0 %: all ", + "category": "Local_Unmodulateables", + "pid": "Echo_Cross_FB", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Cross FB" + }, + { + "id": 232, + "valid": true, + "name": "Echo_Hi_Cut", + "displayName": "Hi Cut", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 0.75, + "scalingType": "Expon_Lin_Pitch", + "min": 60, + "max": 140, + "bipolar": false, + "description": "Cutoff frequency [in semitones] of the filter that damps the higher frequencies of the delayed signal.", + "category": "Local_Unmodulateables", + "pid": "Echo_Hi_Cut", + "scaleFactor": 80, + "scaleOffset": 60, + "shortName": "Hi Cut" + }, + { + "id": 233, + "valid": true, + "name": "Echo_Mix", + "displayName": "Mix", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between the dry signal and the delayed signal.", + "category": "Local_Modulateables", + "pid": "Echo_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 235, + "valid": true, + "name": "Reverb_Size", + "displayName": "Size", + "groupName": "Reverb", + "groupShortName": "Reverb", + "defaultValue": 0.33, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "The room size and reverb time are set here.", + "category": "Local_Modulateables", + "pid": "Reverb_Size", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Size" + }, + { + "id": 237, + "valid": true, + "name": "Reverb_Pre_Dly", + "displayName": "Pre Delay", + "groupName": "Reverb", + "groupShortName": "Reverb", + "defaultValue": 0.33, + "scalingType": "Cubic", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Pre delay time, shifting the late reflections. This has a profound effect on the perceived room size.", + "category": "Local_Unmodulateables", + "pid": "Reverb_Pre_Dly", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Pre Delay" + }, + { + "id": 238, + "valid": true, + "name": "Reverb_Color", + "displayName": "Color", + "groupName": "Reverb", + "groupShortName": "Reverb", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Cutoff of the filter that damps the lower frequencies of the reverberation signal.", + "category": "Local_Modulateables", + "pid": "Reverb_Color", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Color" + }, + { + "id": 240, + "valid": true, + "name": "Reverb_Chorus", + "displayName": "Chorus", + "groupName": "Reverb", + "groupShortName": "Reverb", + "defaultValue": 0.25, + "scalingType": "Parabolic", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This controls the internal modulation of the reverberation delays. At higher amounts, there is more movement ", + "category": "Local_Unmodulateables", + "pid": "Reverb_Chorus", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Chorus" + }, + { + "id": 241, + "valid": true, + "name": "Reverb_Mix", + "displayName": "Mix", + "groupName": "Reverb", + "groupShortName": "Reverb", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between the dry signal and the reverberation signal.", + "category": "Local_Modulateables", + "pid": "Reverb_Mix", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Mix" + }, + { + "id": 243, + "valid": true, + "name": "MC_A", + "displayName": "\ue000", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "", + "category": "Macro_Controls", + "pid": "MC_A", + "shortName": "\ue000" + }, + { + "id": 244, + "valid": true, + "name": "MC_B", + "displayName": "\ue001", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "", + "category": "Macro_Controls", + "pid": "MC_B", + "shortName": "\ue001" + }, + { + "id": 245, + "valid": true, + "name": "MC_C", + "displayName": "\ue002", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "", + "category": "Macro_Controls", + "pid": "MC_C", + "shortName": "\ue002" + }, + { + "id": 246, + "valid": true, + "name": "MC_D", + "displayName": "\ue003", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "", + "category": "Macro_Controls", + "pid": "MC_D", + "shortName": "\ue003" + }, + { + "id": 247, + "valid": true, + "name": "Master_Volume", + "displayName": "Master Volume", + "groupName": "Master", + "groupShortName": "Master", + "defaultValue": 0.5, + "scalingType": "Parabolic", + "min": 0, + "max": 4, + "bipolar": false, + "description": "Master volume [in dB], applied at the end of the effect chain, before the soft clipper.", + "category": "Global_Modulateables", + "pid": "Master_Volume", + "scaleFactor": 4, + "scaleOffset": 0, + "shortName": "Volume" + }, + { + "id": 248, + "valid": true, + "name": "Master_Tune", + "displayName": "Master Tune", + "groupName": "Master", + "groupShortName": "Master", + "defaultValue": 0, + "scalingType": "Linear", + "min": -48, + "max": 48, + "bipolar": true, + "description": "Global pitch transposition [in semitones].", + "category": "Global_Modulateables", + "pid": "Master_Tune", + "scaleFactor": 48, + "scaleOffset": 0, + "shortName": "Tune" + }, + { + "id": 249, + "valid": true, + "name": "Unison_Voices", + "displayName": "Voices", + "groupName": "Unison", + "groupShortName": "Unison", + "defaultValue": 0, + "scalingType": "Linear", + "min": 1, + "max": 24, + "bipolar": false, + "description": "Number of unison voices that will be assigned to a key. At 1, the unison effect is disabled.", + "category": "Local_Unmodulateables", + "pid": "Unison_Voices", + "scaleFactor": 23, + "scaleOffset": 1, + "shortName": "Voices" + }, + { + "id": 250, + "valid": true, + "name": "Unison_Detune", + "displayName": "Detune", + "groupName": "Unison", + "groupShortName": "Unison", + "defaultValue": 0.004166666666666667, + "scalingType": "Linear", + "min": 0, + "max": 12, + "bipolar": false, + "description": "Spreading of the pitches of the unison voices [in semitones]. The Fine mode allows for adjustments in steps ", + "category": "Local_Modulateables", + "pid": "Unison_Detune", + "scaleFactor": 12, + "scaleOffset": 0, + "shortName": "Detune" + }, + { + "id": 252, + "valid": true, + "name": "Unison_Phase", + "displayName": "Phase", + "groupName": "Unison", + "groupShortName": "Unison", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Spreading the (Oscillators') start phases of the unison voices to get different phase cancellations at the ", + "category": "Local_Unmodulateables", + "pid": "Unison_Phase", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Phase" + }, + { + "id": 253, + "valid": true, + "name": "Unison_Pan", + "displayName": "Pan", + "groupName": "Unison", + "groupShortName": "Unison", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Voice panning inside of a group of unison voices. At 100.0 %, the voices are spread over the full stereo ", + "category": "Local_Unmodulateables", + "pid": "Unison_Pan", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Pan" + }, + { + "id": 254, + "valid": true, + "name": "Pedal_1", + "displayName": "Pedal 1", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Pedal_1", + "shortName": "Pedal 1" + }, + { + "id": 255, + "valid": true, + "name": "Pedal_1_to_MC_A", + "displayName": "Pedal 1 to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 1 on Macro Control A. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_1_to_MC_A", + "shortName": "Ped 1 to \ue000" + }, + { + "id": 256, + "valid": true, + "name": "Pedal_1_to_MC_B", + "displayName": "Pedal 1 to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 1 on Macro Control B. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_1_to_MC_B", + "shortName": "Ped 1 to \ue001" + }, + { + "id": 257, + "valid": true, + "name": "Pedal_1_to_MC_C", + "displayName": "Pedal 1 to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 1 on Macro Control C. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_1_to_MC_C", + "shortName": "Ped 1 to \ue002" + }, + { + "id": 258, + "valid": true, + "name": "Pedal_1_to_MC_D", + "displayName": "Pedal 1 to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 1 on Macro Control D. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_1_to_MC_D", + "shortName": "Ped 1 to \ue003" + }, + { + "id": 259, + "valid": true, + "name": "Pedal_2", + "displayName": "Pedal 2", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Pedal_2", + "shortName": "Pedal 2" + }, + { + "id": 260, + "valid": true, + "name": "Pedal_2_to_MC_A", + "displayName": "Pedal 2 to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 2 on Macro Control A. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_2_to_MC_A", + "shortName": "Ped 2 to \ue000" + }, + { + "id": 261, + "valid": true, + "name": "Pedal_2_to_MC_B", + "displayName": "Pedal 2 to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 2 on Macro Control B. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_2_to_MC_B", + "shortName": "Ped 2 to \ue001" + }, + { + "id": 262, + "valid": true, + "name": "Pedal_2_to_MC_C", + "displayName": "Pedal 2 to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 2 on Macro Control C. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_2_to_MC_C", + "shortName": "Ped 2 to \ue002" + }, + { + "id": 263, + "valid": true, + "name": "Pedal_2_to_MC_D", + "displayName": "Pedal 2 to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 2 on Macro Control D. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_2_to_MC_D", + "shortName": "Ped 2 to \ue003" + }, + { + "id": 264, + "valid": true, + "name": "Pedal_3", + "displayName": "Pedal 3", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Pedal_3", + "shortName": "Pedal 3" + }, + { + "id": 265, + "valid": true, + "name": "Pedal_3_to_MC_A", + "displayName": "Pedal 3 to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 3 on Macro Control A. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_3_to_MC_A", + "shortName": "Ped 3 to \ue000" + }, + { + "id": 266, + "valid": true, + "name": "Pedal_3_to_MC_B", + "displayName": "Pedal 3 to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 3 on Macro Control B. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_3_to_MC_B", + "shortName": "Ped 3 to \ue001" + }, + { + "id": 267, + "valid": true, + "name": "Pedal_3_to_MC_C", + "displayName": "Pedal 3 to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 3 on Macro Control C. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_3_to_MC_C", + "shortName": "Ped 3 to \ue002" + }, + { + "id": 268, + "valid": true, + "name": "Pedal_3_to_MC_D", + "displayName": "Pedal 3 to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 3 on Macro Control D. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_3_to_MC_D", + "shortName": "Ped 3 to \ue003" + }, + { + "id": 269, + "valid": true, + "name": "Pedal_4", + "displayName": "Pedal 4", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Pedal_4", + "shortName": "Pedal 4" + }, + { + "id": 270, + "valid": true, + "name": "Pedal_4_to_MC_A", + "displayName": "Pedal 4 to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 4 on Macro Control A. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_4_to_MC_A", + "shortName": "Ped 4 to \ue000" + }, + { + "id": 271, + "valid": true, + "name": "Pedal_4_to_MC_B", + "displayName": "Pedal 4 to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 4 on Macro Control B. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_4_to_MC_B", + "shortName": "Ped 4 to \ue001" + }, + { + "id": 272, + "valid": true, + "name": "Pedal_4_to_MC_C", + "displayName": "Pedal 4 to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 4 on Macro Control C. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_4_to_MC_C", + "shortName": "Ped 4 to \ue002" + }, + { + "id": 273, + "valid": true, + "name": "Pedal_4_to_MC_D", + "displayName": "Pedal 4 to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 4 on Macro Control D. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_4_to_MC_D", + "shortName": "Ped 4 to \ue003" + }, + { + "id": 274, + "valid": true, + "name": "Bender", + "displayName": "Bender", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Bender", + "shortName": "Bender" + }, + { + "id": 275, + "valid": true, + "name": "Bender_to_MC_A", + "displayName": "Bender to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Bender on Macro Control A. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Bender_to_MC_A", + "shortName": "Bend to \ue000" + }, + { + "id": 276, + "valid": true, + "name": "Bender_to_MC_B", + "displayName": "Bender to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Bender on Macro Control B. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Bender_to_MC_B", + "shortName": "Bend to \ue001" + }, + { + "id": 277, + "valid": true, + "name": "Bender_to_MC_C", + "displayName": "Bender to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Bender on Macro Control C. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Bender_to_MC_C", + "shortName": "Bend to \ue002" + }, + { + "id": 278, + "valid": true, + "name": "Bender_to_MC_D", + "displayName": "Bender to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Bender on Macro Control D. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Bender_to_MC_D", + "shortName": "Bend to \ue003" + }, + { + "id": 279, + "valid": true, + "name": "Aftertouch", + "displayName": "Aftertouch", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Aftertouch", + "shortName": "Aftertouch" + }, + { + "id": 280, + "valid": true, + "name": "Aftertouch_to_MC_A", + "displayName": "Aftertouch to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Aftertouch on Macro Control A. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Aftertouch_to_MC_A", + "shortName": "AT to \ue000" + }, + { + "id": 281, + "valid": true, + "name": "Aftertouch_to_MC_B", + "displayName": "Aftertouch to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Aftertouch on Macro Control B. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Aftertouch_to_MC_B", + "shortName": "AT to \ue001" + }, + { + "id": 282, + "valid": true, + "name": "Aftertouch_to_MC_C", + "displayName": "Aftertouch to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Aftertouch on Macro Control C. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Aftertouch_to_MC_C", + "shortName": "AT to \ue002" + }, + { + "id": 283, + "valid": true, + "name": "Aftertouch_to_MC_D", + "displayName": "Aftertouch to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Aftertouch on Macro Control D. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Aftertouch_to_MC_D", + "shortName": "AT to \ue003" + }, + { + "id": 284, + "valid": true, + "name": "Ribbon_1", + "displayName": "Ribbon 1", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Ribbon_1", + "shortName": "Ribbon 1" + }, + { + "id": 285, + "valid": true, + "name": "Ribbon_1_to_MC_A", + "displayName": "Ribbon 1 to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 1 on Macro Control A. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_1_to_MC_A", + "shortName": "Rib 1 to \ue000" + }, + { + "id": 286, + "valid": true, + "name": "Ribbon_1_to_MC_B", + "displayName": "Ribbon 1 to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 1 on Macro Control B. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_1_to_MC_B", + "shortName": "Rib 1 to \ue001" + }, + { + "id": 287, + "valid": true, + "name": "Ribbon_1_to_MC_C", + "displayName": "Ribbon 1 to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 1 on Macro Control C. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_1_to_MC_C", + "shortName": "Rib 1 to \ue002" + }, + { + "id": 288, + "valid": true, + "name": "Ribbon_1_to_MC_D", + "displayName": "Ribbon 1 to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 1 on Macro Control D. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_1_to_MC_D", + "shortName": "Rib 1 to \ue003" + }, + { + "id": 289, + "valid": true, + "name": "Ribbon_2", + "displayName": "Ribbon 2", + "groupName": "Hardware Source", + "groupShortName": "HW Source", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "This control is the parameter representation of the \\", + "category": "Hardware_Sources", + "pid": "Ribbon_2", + "shortName": "Ribbon 2" + }, + { + "id": 290, + "valid": true, + "name": "Ribbon_2_to_MC_A", + "displayName": "Ribbon 2 to \ue000", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 2 on Macro Control A. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_2_to_MC_A", + "shortName": "Rib 2 to \ue000" + }, + { + "id": 291, + "valid": true, + "name": "Ribbon_2_to_MC_B", + "displayName": "Ribbon 2 to \ue001", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 2 on Macro Control B. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_2_to_MC_B", + "shortName": "Rib 2 to \ue001" + }, + { + "id": 292, + "valid": true, + "name": "Ribbon_2_to_MC_C", + "displayName": "Ribbon 2 to \ue002", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 2 on Macro Control C. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_2_to_MC_C", + "shortName": "Rib 2 to \ue002" + }, + { + "id": 293, + "valid": true, + "name": "Ribbon_2_to_MC_D", + "displayName": "Ribbon 2 to \ue003", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 2 on Macro Control D. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_2_to_MC_D", + "shortName": "Rib 2 to \ue003" + }, + { + "id": 294, + "valid": true, + "name": "Env_A_Att_Curve", + "displayName": "Attack Curve", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Curvature of the (polynomial) Attack segment. (negative: decelerating slope, zero: linear, positive: ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Att_Curve", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Curve" + }, + { + "id": 295, + "valid": true, + "name": "Env_B_Att_Curve", + "displayName": "Attack Curve", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Curvature of the (polynomial) Attack segment. (negative: decelerating slope, zero: linear, positive: ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Att_Curve", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Curve" + }, + { + "id": 296, + "valid": true, + "name": "Env_C_Att_Curve", + "displayName": "Attack Curve", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Curvature of the (polynomial) Attack segment. (negative: decelerating slope, zero: linear, positive: ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Att_Curve", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Curve" + }, + { + "id": 297, + "valid": true, + "name": "Env_C_Sus", + "displayName": "Sustain Level", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Sustain level (bipolar, target of the second Decay segment).", + "category": "Local_Modulateables", + "pid": "Env_C_Sus", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Sustain" + }, + { + "id": 299, + "valid": true, + "name": "FB_Mix_Lvl", + "displayName": "Level", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0.38, + "scalingType": "Parabolic", + "min": 0, + "max": 4, + "bipolar": false, + "description": "Level of the (global) Feedback mix [in dB].", + "category": "Local_Modulateables", + "pid": "FB_Mix_Lvl", + "scaleFactor": 4, + "scaleOffset": 0, + "shortName": "Level" + }, + { + "id": 301, + "valid": true, + "name": "Osc_A_Phase", + "displayName": "Phase", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -0.5, + "max": 0.5, + "bipolar": true, + "description": "Start phase [in degrees]. The Oscillator will be set to this phase position with each Note-On.", + "category": "Local_Modulateables", + "pid": "Osc_A_Phase", + "scaleFactor": 0.5, + "scaleOffset": 0, + "shortName": "Phase" + }, + { + "id": 302, + "valid": true, + "name": "Osc_B_Phase", + "displayName": "Phase", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -0.5, + "max": 0.5, + "bipolar": true, + "description": "Start phase [in degrees]. The Oscillator will be set to this phase position with each Note-On.", + "category": "Local_Modulateables", + "pid": "Osc_B_Phase", + "scaleFactor": 0.5, + "scaleOffset": 0, + "shortName": "Phase" + }, + { + "id": 303, + "valid": true, + "name": "Osc_A_Chirp", + "displayName": "Chirp", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0.6666666666666666, + "scalingType": "Expon_Lin_Pitch", + "min": 80, + "max": 140, + "bipolar": false, + "description": "Cutoff [in semitones] of the lowpass in the phase modulation signal path that can be applied to reduce the ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_Chirp", + "scaleFactor": 60, + "scaleOffset": 80, + "shortName": "Chirp" + }, + { + "id": 304, + "valid": true, + "name": "Osc_B_Chirp", + "displayName": "Chirp", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 0.6666666666666666, + "scalingType": "Expon_Lin_Pitch", + "min": 80, + "max": 140, + "bipolar": false, + "description": "Cutoff [in semitones] of the lowpass in the phase modulation signal path that can be applied to reduce the ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_Chirp", + "scaleFactor": 60, + "scaleOffset": 80, + "shortName": "Chirp" + }, + { + "id": 305, + "valid": true, + "name": "Comb_Flt_Decay_Gate", + "displayName": "Decay - Gate", + "groupName": "Comb Filter", + "groupShortName": "Comb Filter", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Amount of gating applied to the decay time. It reduces the decay time when the key is released ", + "category": "Local_Modulateables", + "pid": "Comb_Flt_Decay_Gate", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Gate" + }, + { + "id": 307, + "valid": true, + "name": "Flanger_Envelope", + "displayName": "Envelope", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between the signals of the LFO and the envelope as modulation sources for the delay times and the ", + "category": "Local_Unmodulateables", + "pid": "Flanger_Envelope", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Envelope" + }, + { + "id": 308, + "valid": true, + "name": "Flanger_AP_Mod", + "displayName": "Allpass Mod", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Linear", + "min": -70, + "max": 70, + "bipolar": true, + "description": "Relative amount of the modulation of the allpass center frequencies by the LFO and/or the envelope. The ", + "category": "Local_Modulateables", + "pid": "Flanger_AP_Mod", + "scaleFactor": 70, + "scaleOffset": 0, + "shortName": "AP Mod" + }, + { + "id": 310, + "valid": true, + "name": "Flanger_AP_Tune", + "displayName": "Allpass Tune", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 140, + "bipolar": false, + "description": "Mean center frequency of the 4-pole allpass filters which are in series with the delays. Their ", + "category": "Local_Modulateables", + "pid": "Flanger_AP_Tune", + "scaleFactor": 140, + "scaleOffset": 0, + "shortName": "AP Tune" + }, + { + "id": 312, + "valid": true, + "name": "Scale_Base_Key", + "displayName": "Base Key", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 11, + "bipolar": false, + "description": "Sets the base key for the custom scale. The scale is defined for the eleven keys above the base key and will ", + "category": "Global_Unmodulateables", + "pid": "Scale_Base_Key", + "scaleFactor": 11, + "scaleOffset": 0, + "shortName": "Base Key" + }, + { + "id": 313, + "valid": true, + "name": "Scale_Offset_1", + "displayName": "Offset +1", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the first key following the base key [in cents]. At zero, the interval to the base key would be the ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_1", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +1" + }, + { + "id": 314, + "valid": true, + "name": "Scale_Offset_2", + "displayName": "Offset +2", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the second key following the base key [in cents]. At zero, the interval to the base key would be ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_2", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +2" + }, + { + "id": 315, + "valid": true, + "name": "Scale_Offset_3", + "displayName": "Offset +3", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the third key following the base key [in cents]. At zero, the interval to the base key would be the ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_3", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +3" + }, + { + "id": 316, + "valid": true, + "name": "Scale_Offset_4", + "displayName": "Offset +4", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the fourth key following the base key [in cents]. At zero, the interval to the base key would be ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_4", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +4" + }, + { + "id": 317, + "valid": true, + "name": "Scale_Offset_5", + "displayName": "Offset +5", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the fifth key following the base key [in cents]. At zero, the interval to the base key would be ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_5", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +5" + }, + { + "id": 318, + "valid": true, + "name": "Scale_Offset_6", + "displayName": "Offset +6", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the sixth key following the base key [in cents]. At zero, the interval to the base key would be the ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_6", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +6" + }, + { + "id": 319, + "valid": true, + "name": "Scale_Offset_7", + "displayName": "Offset +7", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the seventh key following the base key [in cents]. At zero,the interval to the base key would be ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_7", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +7" + }, + { + "id": 320, + "valid": true, + "name": "Scale_Offset_8", + "displayName": "Offset +8", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the eighth key following the base key [in cents]. At zero, the interval to the base key would be ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_8", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +8" + }, + { + "id": 321, + "valid": true, + "name": "Scale_Offset_9", + "displayName": "Offset +9", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the ninth key following the base key [in cents]. At zero, the interval to the base key would be the ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_9", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +9" + }, + { + "id": 322, + "valid": true, + "name": "Scale_Offset_10", + "displayName": "Offset +10", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the tenth key following the base key [in cents]. At zero, the interval to the base key would be the ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_10", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +10" + }, + { + "id": 323, + "valid": true, + "name": "Scale_Offset_11", + "displayName": "Offset +11", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the eleventh key following the base key [in cents]. At zero, the interval to the base key would be ", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_11", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +11" + }, + { + "id": 324, + "valid": true, + "name": "MC_Time_A", + "displayName": "\ue000 Smoothing", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.4, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Sets the smoothing time [in milliseconds] for modulations on target parameters assigned to Macro Control A. ", + "category": "Macro_Times", + "pid": "MC_Time_A", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Smoothing" + }, + { + "id": 325, + "valid": true, + "name": "MC_Time_B", + "displayName": "\ue001 Smoothing", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.4, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Sets the smoothing time [in milliseconds] for modulations on target parameters assigned to Macro Control B. ", + "category": "Macro_Times", + "pid": "MC_Time_B", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Smoothing" + }, + { + "id": 326, + "valid": true, + "name": "MC_Time_C", + "displayName": "\ue002 Smoothing", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.4, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Sets the smoothing time [in milliseconds] for modulations on target parameters assigned to Macro Control C. ", + "category": "Macro_Times", + "pid": "MC_Time_C", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Smoothing" + }, + { + "id": 327, + "valid": true, + "name": "MC_Time_D", + "displayName": "\ue003 Smoothing", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.4, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Sets the smoothing time [in milliseconds] for modulations on target parameters assigned to Macro Control D. ", + "category": "Macro_Times", + "pid": "MC_Time_D", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Smoothing" + }, + { + "id": 328, + "valid": true, + "name": "Env_A_Dec_1_Vel", + "displayName": "Decay 1 Velocity", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Velocity influence on the Decay 1 time. The value represents the logarithmic amount of the reduction ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Dec_1_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 330, + "valid": true, + "name": "Env_A_Dec_2_Vel", + "displayName": "Decay 2 Velocity", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Velocity influence on the Decay 2 time. The value represents the logarithmic amount of the reduction ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Dec_2_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 332, + "valid": true, + "name": "Env_A_Elevate", + "displayName": "Elevate T - A", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Transforms the breakpoints of the Envelope signal, becoming more and more a flat organ-like curve towards the ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Elevate", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Elevate" + }, + { + "id": 334, + "valid": true, + "name": "Env_B_Dec_1_Vel", + "displayName": "Decay 1 Velocity", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Velocity influence on the Decay 1 time. The value represents the logarithmic amount of the reduction ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Dec_1_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 336, + "valid": true, + "name": "Env_B_Dec_2_Vel", + "displayName": "Decay 2 Velocity", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Velocity influence on the Decay 2 time. The value represents the logarithmic amount of the reduction ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Dec_2_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 338, + "valid": true, + "name": "Env_B_Elevate", + "displayName": "Elevate T - A", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Transforms the breakpoints of the Envelope signal, becoming more and more a flat organ-like curve towards the ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Elevate", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Elevate" + }, + { + "id": 340, + "valid": true, + "name": "Env_C_Retr_H", + "displayName": "Retrig Hardness", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines how much the Envelope signal is forced to start at zero instead of the current level (usually from ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Retr_H", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Retrig" + }, + { + "id": 342, + "valid": true, + "name": "Echo_Send", + "displayName": "Send", + "groupName": "Echo", + "groupShortName": "Echo", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Level control at the input of the effect signal path. In contrast to the Echo Mix parameter, the Delay ", + "category": "Local_Modulateables", + "pid": "Echo_Send", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Send" + }, + { + "id": 344, + "valid": true, + "name": "Reverb_Send", + "displayName": "Send", + "groupName": "Reverb", + "groupShortName": "Reverb", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Level control at the input of the effect signal path. In contrast to the Reverb Mix parameter, the Reverb ", + "category": "Local_Modulateables", + "pid": "Reverb_Send", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Send" + }, + { + "id": 346, + "valid": true, + "name": "FB_Mix_Osc", + "displayName": "A/B @VG", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Level of the signal from branch A or B of the other Part. (Only available when using Layer mode.)", + "category": "Local_Modulateables", + "pid": "FB_Mix_Osc", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "A/B @VG" + }, + { + "id": 348, + "valid": true, + "name": "FB_Mix_Osc_Src", + "displayName": "A - B", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades between branch A and B of the other Part. (Only available when using Layer mode.)", + "category": "Local_Modulateables", + "pid": "FB_Mix_Osc_Src", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "A - B" + }, + { + "id": 350, + "valid": true, + "name": "FB_Mix_Comb_Src", + "displayName": "Comb - From @VG", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades to the Comb Filter signal of the other Part. (Only available when using Layer mode.)", + "category": "Local_Modulateables", + "pid": "FB_Mix_Comb_Src", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "From @VG" + }, + { + "id": 352, + "valid": true, + "name": "FB_Mix_SVF_Src", + "displayName": "SV Filter - From @VG", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades to the State Variable Filter signal of the other Part. (Only available when using Layer mode.)", + "category": "Local_Modulateables", + "pid": "FB_Mix_SVF_Src", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "From @VG" + }, + { + "id": 354, + "valid": true, + "name": "FB_Mix_FX_Src", + "displayName": "Effects - From @VG", + "groupName": "Feedback Mixer", + "groupShortName": "FB Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Crossfades to the Effects signal of the other Part. (Only available when using Layer mode.)", + "category": "Local_Modulateables", + "pid": "FB_Mix_FX_Src", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "From @VG" + }, + { + "id": 356, + "valid": true, + "name": "Split_Split_Point", + "displayName": "Split Point", + "groupName": "Split", + "groupShortName": "Split", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0.05, + "max": 60.05, + "bipolar": false, + "description": "Determines the split position of a sound as a key number. Keys from the lower end up to the Split Point will ", + "category": "Local_Modulateables", + "pid": "Split_Split_Point", + "scaleFactor": 60, + "scaleOffset": 0.05, + "shortName": "Split Pnt" + }, + { + "id": 358, + "valid": true, + "name": "Voice_Grp_Volume", + "displayName": "Part Volume", + "groupName": "Voices", + "groupShortName": "Voices", + "defaultValue": 0.5, + "scalingType": "Parabolic", + "min": 0, + "max": 4, + "bipolar": false, + "description": "Part volume [in dB], available when using Split or Layer mode. Applied at the end of the corresponding effect ", + "category": "Local_Modulateables", + "pid": "Voice_Grp_Volume", + "scaleFactor": 4, + "scaleOffset": 0, + "shortName": "Volume" + }, + { + "id": 360, + "valid": true, + "name": "Voice_Grp_Tune", + "displayName": "Part Tune", + "groupName": "Voices", + "groupShortName": "Voices", + "defaultValue": 0, + "scalingType": "Linear", + "min": -48, + "max": 48, + "bipolar": true, + "description": "Part pitch transposition [in semitones], available when using Split or Layer mode. Applied for the ", + "category": "Local_Modulateables", + "pid": "Voice_Grp_Tune", + "scaleFactor": 48, + "scaleOffset": 0, + "shortName": "Tune" + }, + { + "id": 362, + "valid": true, + "name": "Out_Mix_To_FX", + "displayName": "To FX @VG", + "groupName": "Output Mixer", + "groupShortName": "Output Mixer", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines how much of the Output Mixer signal is sent to the Effects section of the other Part. At 100.0 % ", + "category": "Local_Modulateables", + "pid": "Out_Mix_To_FX", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "To FX @VG" + }, + { + "id": 364, + "valid": true, + "name": "Mono_Grp_Enable", + "displayName": "Enable", + "groupName": "Mono", + "groupShortName": "Mono", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "When enabled, only one voice will be assigned, even when multiple keys are pressed.", + "category": "Local_Unmodulateables", + "pid": "Mono_Grp_Enable", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Enable" + }, + { + "id": 365, + "valid": true, + "name": "Mono_Grp_Prio", + "displayName": "Key Priority", + "groupName": "Mono", + "groupShortName": "Mono", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 2, + "bipolar": false, + "description": "When pressing multiple keys in Mono mode, they are sorted according to the priority set by this parameter. ", + "category": "Local_Unmodulateables", + "pid": "Mono_Grp_Prio", + "scaleFactor": 2, + "scaleOffset": 0, + "shortName": "Priority" + }, + { + "id": 366, + "valid": true, + "name": "Mono_Grp_Legato", + "displayName": "Legato", + "groupName": "Mono", + "groupShortName": "Mono", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 3, + "bipolar": false, + "description": "When a new key matches the Priority, it will take over. When the priorized key is released the second ", + "category": "Local_Unmodulateables", + "pid": "Mono_Grp_Legato", + "scaleFactor": 3, + "scaleOffset": 0, + "shortName": "Legato" + }, + { + "id": 367, + "valid": true, + "name": "Mono_Grp_Glide", + "displayName": "Glide Time", + "groupName": "Mono", + "groupShortName": "Mono", + "defaultValue": 0, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "In Legato mode this time can be used to smooth the transitions of the pitches.", + "category": "Local_Modulateables", + "pid": "Mono_Grp_Glide", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Glide" + }, + { + "id": 369, + "valid": true, + "name": "MC_E", + "displayName": "\ue200", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "", + "category": "Macro_Controls", + "pid": "MC_E", + "shortName": "\ue200" + }, + { + "id": 370, + "valid": true, + "name": "MC_Time_E", + "displayName": "\ue200 Smoothing", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.4, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Sets the smoothing time [in milliseconds] for modulations on target parameters assigned to Macro Control E. ", + "category": "Macro_Times", + "pid": "MC_Time_E", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Smoothing" + }, + { + "id": 371, + "valid": true, + "name": "MC_F", + "displayName": "\ue201", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "", + "category": "Macro_Controls", + "pid": "MC_F", + "shortName": "\ue201" + }, + { + "id": 372, + "valid": true, + "name": "MC_Time_F", + "displayName": "\ue201 Smoothing", + "groupName": "Macro Control", + "groupShortName": "Macro Control", + "defaultValue": 0.4, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Sets the smoothing time [in milliseconds] for modulations on target parameters assigned to Macro Control F. ", + "category": "Macro_Times", + "pid": "MC_Time_F", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Smoothing" + }, + { + "id": 373, + "valid": true, + "name": "Pedal_1_to_MC_E", + "displayName": "Pedal 1 to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 1 on Macro Control E. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_1_to_MC_E", + "shortName": "Ped 1 to \ue200" + }, + { + "id": 374, + "valid": true, + "name": "Pedal_1_to_MC_F", + "displayName": "Pedal 1 to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 1 on Macro Control F. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_1_to_MC_F", + "shortName": "Ped 1 to \ue201" + }, + { + "id": 375, + "valid": true, + "name": "Pedal_2_to_MC_E", + "displayName": "Pedal 2 to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 2 on Macro Control E. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_2_to_MC_E", + "shortName": "Ped 2 to \ue200" + }, + { + "id": 376, + "valid": true, + "name": "Pedal_2_to_MC_F", + "displayName": "Pedal 2 to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 2 on Macro Control F. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_2_to_MC_F", + "shortName": "Ped 2 to \ue201" + }, + { + "id": 377, + "valid": true, + "name": "Pedal_3_to_MC_E", + "displayName": "Pedal 3 to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 3 on Macro Control E. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_3_to_MC_E", + "shortName": "Ped 3 to \ue200" + }, + { + "id": 378, + "valid": true, + "name": "Pedal_3_to_MC_F", + "displayName": "Pedal 3 to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 3 on Macro Control F. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_3_to_MC_F", + "shortName": "Ped 3 to \ue201" + }, + { + "id": 379, + "valid": true, + "name": "Pedal_4_to_MC_E", + "displayName": "Pedal 4 to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 4 on Macro Control E. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_4_to_MC_E", + "shortName": "Ped 4 to \ue200" + }, + { + "id": 380, + "valid": true, + "name": "Pedal_4_to_MC_F", + "displayName": "Pedal 4 to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Pedal 4 on Macro Control F. If the pedal is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Pedal_4_to_MC_F", + "shortName": "Ped 4 to \ue201" + }, + { + "id": 381, + "valid": true, + "name": "Bender_to_MC_E", + "displayName": "Bender to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Bender on Macro Control E. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Bender_to_MC_E", + "shortName": "Bend to \ue200" + }, + { + "id": 382, + "valid": true, + "name": "Bender_to_MC_F", + "displayName": "Bender to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Bender on Macro Control F. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Bender_to_MC_F", + "shortName": "Bend to \ue201" + }, + { + "id": 383, + "valid": true, + "name": "Aftertouch_to_MC_E", + "displayName": "Aftertouch to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Aftertouch on Macro Control E. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Aftertouch_to_MC_E", + "shortName": "AT to \ue200" + }, + { + "id": 384, + "valid": true, + "name": "Aftertouch_to_MC_F", + "displayName": "Aftertouch to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of the Aftertouch on Macro Control F. The amount can be adjusted continuously [in ", + "category": "Hardware_Amounts", + "pid": "Aftertouch_to_MC_F", + "shortName": "AT to \ue201" + }, + { + "id": 385, + "valid": true, + "name": "Ribbon_1_to_MC_E", + "displayName": "Ribbon 1 to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 1 on Macro Control E. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_1_to_MC_E", + "shortName": "Rib 1 to \ue200" + }, + { + "id": 386, + "valid": true, + "name": "Ribbon_1_to_MC_F", + "displayName": "Ribbon 1 to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 1 on Macro Control F. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_1_to_MC_F", + "shortName": "Rib 1 to \ue201" + }, + { + "id": 387, + "valid": true, + "name": "Ribbon_2_to_MC_E", + "displayName": "Ribbon 2 to \ue200", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 2 on Macro Control E. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_2_to_MC_E", + "shortName": "Rib 2 to \ue200" + }, + { + "id": 388, + "valid": true, + "name": "Ribbon_2_to_MC_F", + "displayName": "Ribbon 2 to \ue201", + "groupName": "Hardware Amount", + "groupShortName": "HW Amount", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the influence of Ribbon 2 on Macro Control F. If the ribbon is set to a returning mode, the amount ", + "category": "Hardware_Amounts", + "pid": "Ribbon_2_to_MC_F", + "shortName": "Rib 2 to \ue201" + }, + { + "id": 389, + "valid": true, + "name": "Flanger_Tremolo", + "displayName": "Tremolo", + "groupName": "Flanger", + "groupShortName": "Flanger", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Determines the amount of amplitude modulation by the Flanger's stereo LFO. It is applied after the Mix stage ", + "category": "Local_Modulateables", + "pid": "Flanger_Tremolo", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Tremolo" + }, + { + "id": 391, + "valid": true, + "name": "Scale_Offset_0", + "displayName": "Offset +0", + "groupName": "Scale", + "groupShortName": "Scale", + "defaultValue": 0, + "scalingType": "Linear", + "min": -8, + "max": 8, + "bipolar": true, + "description": "Offset of the base key [in cents]. At zero, the interval to the base key would be perfect identity.", + "category": "Global_Unmodulateables", + "pid": "Scale_Offset_0", + "scaleFactor": 8, + "scaleOffset": 0, + "shortName": "Offset +0" + }, + { + "id": 393, + "valid": true, + "name": "Osc_A_Reset", + "displayName": "Reset", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "If enabled, the Oscillator will reset its phase everytime the envelopes start (in the corresponding voice). ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_Reset", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Reset" + }, + { + "id": 394, + "valid": true, + "name": "Osc_B_Reset", + "displayName": "Reset", + "groupName": "Oscillator B", + "groupShortName": "Oscillator B", + "defaultValue": 1, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "If enabled, every pressed key will cause the Oscillator to reset its phase (for the corresponding voice). If ", + "category": "Local_Unmodulateables", + "pid": "Osc_B_Reset", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Reset" + }, + { + "id": 395, + "valid": true, + "name": "Voice_Grp_Mute", + "displayName": "Part Mute", + "groupName": "Voices", + "groupShortName": "Voices", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Part mute, available when usig Split or Layer mode. When enabled, the corresponding Part will be muted.", + "category": "Local_Unmodulateables", + "pid": "Voice_Grp_Mute", + "shortName": "Part Mute" + }, + { + "id": 396, + "valid": true, + "name": "Voice_Grp_Fade_From", + "displayName": "Fade From", + "groupName": "Voices", + "groupShortName": "Voices", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Available when using Layer mode. For Part I the full level range spans from the lowest key (C1) to the key ", + "category": "Local_Unmodulateables", + "pid": "Voice_Grp_Fade_From", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Fade From" + }, + { + "id": 397, + "valid": true, + "name": "Voice_Grp_Fade_Range", + "displayName": "Fade Range", + "groupName": "Voices", + "groupShortName": "Voices", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Available when using Layer mode. This parameter adjusts the width of a key range [in semitones] where the ", + "category": "Local_Unmodulateables", + "pid": "Voice_Grp_Fade_Range", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Fade Range" + } + ], + "groups": [ + { + "name": "Cabinet", + "parameterCount": 8 + }, + { + "name": "Comb Filter", + "parameterCount": 16 + }, + { + "name": "Echo", + "parameterCount": 7 + }, + { + "name": "Envelope A", + "parameterCount": 16 + }, + { + "name": "Envelope B", + "parameterCount": 16 + }, + { + "name": "Envelope C", + "parameterCount": 13 + }, + { + "name": "Feedback Mixer", + "parameterCount": 14 + }, + { + "name": "Flanger", + "parameterCount": 13 + }, + { + "name": "Gap Filter", + "parameterCount": 6 + }, + { + "name": "Hardware Amount", + "parameterCount": 48 + }, + { + "name": "Hardware Source", + "parameterCount": 8 + }, + { + "name": "Macro Control", + "parameterCount": 12 + }, + { + "name": "Master", + "parameterCount": 2 + }, + { + "name": "Mono", + "parameterCount": 4 + }, + { + "name": "Oscillator A", + "parameterCount": 16 + }, + { + "name": "Oscillator B", + "parameterCount": 16 + }, + { + "name": "Output Mixer", + "parameterCount": 14 + }, + { + "name": "Reverb", + "parameterCount": 6 + }, + { + "name": "Scale", + "parameterCount": 13 + }, + { + "name": "Shaper A", + "parameterCount": 8 + }, + { + "name": "Shaper B", + "parameterCount": 8 + }, + { + "name": "Split", + "parameterCount": 1 + }, + { + "name": "State Variable Filter", + "parameterCount": 13 + }, + { + "name": "Unison", + "parameterCount": 4 + }, + { + "name": "Voices", + "parameterCount": 5 + } + ] +} \ No newline at end of file diff --git a/playground/c15/worklet-processor.js b/playground/c15/worklet-processor.js new file mode 100644 index 0000000..640f43b --- /dev/null +++ b/playground/c15/worklet-processor.js @@ -0,0 +1,592 @@ +/** + * C15 Audio Engine - AudioWorklet Processor + * + * This processor loads the C15 WASM module and calls render() each audio frame. + * It runs in AudioWorkletGlobalScope and connects to the Web Audio graph with + * 0 inputs and 2 outputs (stereo). + * + * WASM API: + * - engineInit(sampleRate, polyphony) -> int + * - render(numFrames) -> float* (interleaved stereo) + * - noteOn(note, velocity) + * - noteOff(note, velocity) + * - setParameter(paramId, value) + * - reset() + * + * Ring Buffer Protocol (SharedArrayBuffer): + * - Lock-free SPSC ring buffer for main thread -> worklet communication + * - Message types: 0=parameter, 1=noteOn, 2=noteOff + * - Message format: [type, id/note, value/velocity, reserved] + * + * @file worklet-processor.js + */ + +// WASM module state (shared across all processor instances) +let wasmInstance = null; +let wasmMemory = null; +let wasmReady = false; + +// Ring buffer state +let ringBufferReader = null; + +// Message type constants (must match ring-buffer.js) +const MessageType = { + PARAMETER: 0, + NOTE_ON: 1, + NOTE_OFF: 2 +}; + +/** + * Ring buffer layout constants + */ +const HEADER_SIZE = 3; +const MESSAGE_SIZE = 4; +const RING_CAPACITY = 512; + +/** + * RingBufferReader - Reads messages from SharedArrayBuffer ring buffer + * + * This is the consumer side of the SPSC ring buffer, designed for use + * in the AudioWorklet's process() callback. + * + * @class RingBufferReader + */ +class RingBufferReader { + constructor(sharedBuffer) { + this._buffer = new Float32Array(sharedBuffer); + this._capacity = RING_CAPACITY; + this._messageSize = MESSAGE_SIZE; + this._headerSize = HEADER_SIZE; + } + + /** + * Get current write index (main thread updates this) + * @private + */ + _getWriteIndex() { + return Atomics.load(new Int32Array(this._buffer.buffer), 0); + } + + /** + * Get current read index + * @private + */ + _getReadIndex() { + return Atomics.load(new Int32Array(this._buffer.buffer), 1); + } + + /** + * Advance read index with atomic store + * @private + */ + _advanceReadIndex(currentIdx) { + const nextIdx = (currentIdx + 1) % this._capacity; + Atomics.store(new Int32Array(this._buffer.buffer), 1, nextIdx); + return nextIdx; + } + + /** + * Read and process messages with callbacks + * + * This is the preferred method for use in the AudioWorklet process() call. + * It avoids creating arrays and directly calls the appropriate callback. + * + * @param {Object} callbacks - Callback handlers + * @param {Function} callbacks.onParameter - Called for parameter updates (paramId, value) + * @param {Function} callbacks.onNoteOn - Called for note on events (note, velocity) + * @param {Function} callbacks.onNoteOff - Called for note off events (note, velocity) + * @param {number} maxMessages - Maximum messages to process per call + * @returns {number} Number of messages processed + */ + processMessages(callbacks, maxMessages = 32) { + const writeIdx = this._getWriteIndex(); + let readIdx = this._getReadIndex(); + + let count = 0; + + while (readIdx !== writeIdx && count < maxMessages) { + // Read message from buffer + const msgOffset = this._headerSize + (readIdx * this._messageSize); + + const type = this._buffer[msgOffset + 0]; + const id = this._buffer[msgOffset + 1]; + const value = this._buffer[msgOffset + 2]; + + // Dispatch to callback based on type + switch (type) { + case MessageType.PARAMETER: + if (callbacks.onParameter) { + callbacks.onParameter(id, value); + } + break; + + case MessageType.NOTE_ON: + if (callbacks.onNoteOn) { + callbacks.onNoteOn(id, value); + } + break; + + case MessageType.NOTE_OFF: + if (callbacks.onNoteOff) { + callbacks.onNoteOff(id, value); + } + break; + + default: + console.warn('[RingBufferReader] Unknown message type:', type); + } + + // Advance read index + readIdx = this._advanceReadIndex(readIdx); + count++; + } + + return count; + } + + /** + * Get number of available messages (for debugging/monitoring) + */ + getAvailableCount() { + const writeIdx = this._getWriteIndex(); + const readIdx = this._getReadIndex(); + + if (writeIdx >= readIdx) { + return writeIdx - readIdx; + } else { + return this._capacity - readIdx + writeIdx; + } + } +} + +/** + * Initialize WASM module from compiled WebAssembly.Module and memory + * This is called when the main thread sends the 'init-wasm' message + */ +function initWasmFromModule(wasmModule, memory) { + return WebAssembly.instantiate(wasmModule, { + // Import object - Emscripten typically uses 'a' for the main import namespace + a: { + // Memory import if needed + d: () => { throw new Error('abort'); }, + b: () => 1, // nowIsMonotonic + a: () => performance.now(), // _emscripten_get_now + c: (size) => { // _emscripten_resize_heap - not typically needed with fixed memory + return 0; + } + } + }).then(instance => { + wasmInstance = instance; + wasmMemory = memory; + wasmReady = true; + + // Call the constructors + if (instance.exports.f) { + instance.exports.f(); + } + + return true; + }); +} + +/** + * C15Processor - AudioWorklet processor for the C15 synth engine + * + * Fulfills validation assertions: + * - VAL-M2-001: WASM module loads in AudioWorkletGlobalScope + * - VAL-M2-002: registerProcessor() succeeds with 0 inputs, 2 outputs + * - VAL-M2-003: AudioWorkletNode connects to AudioContext destination + * - VAL-M2-004: WASM render produces valid stereo float32 output + * - VAL-M2-005: Ring buffer enables lock-free parameter updates without corruption + * - VAL-M2-006: Note on/off via ring buffer triggers audio start/release + * + * NOTE: WebAssembly.Module cannot be sent via MessagePort.postMessage() in Chrome. + * The WASM module must be passed via processorOptions in the AudioWorkletNode constructor. + * See: https://issues.chromium.org/issues/40855462 + */ +class C15Processor extends AudioWorkletProcessor { + constructor(options) { + super(options); + + // Processor configuration + this._initialized = false; + this._sampleRate = 48000; + this._polyphony = 24; + this._bufferSize = 128; + + // Default configuration + const processorOptions = options.processorOptions || {}; + this._sampleRate = processorOptions.sampleRate || 48000; + this._polyphony = processorOptions.polyphony || 24; + + // Listen for messages from main thread + this.port.onmessage = this._handleMessage.bind(this); + + // Debug: verify message handler is bound + this.port.postMessage({ type: 'status', status: 'handler-bound', test: true }); + + // Log that processor was created + this._postStatus('created', { + sampleRate: this._sampleRate, + polyphony: this._polyphony + }); + + // Initialize WASM from processorOptions if provided + // Chrome requires this approach - WebAssembly.Module cannot be sent via postMessage + // See: https://issues.chromium.org/issues/40855462 + if (processorOptions.wasmModule instanceof WebAssembly.Module) { + this._postStatus('wasm-module-received-via-options', { + hasModule: true, + moduleType: 'WebAssembly.Module' + }); + // Initialize WASM asynchronously + this._initFromWasmModule(processorOptions.wasmModule, null); + } else if (processorOptions.wasmModule) { + this._postError(new Error('wasmModule in processorOptions is not a WebAssembly.Module'), 'constructor'); + } else { + this._postStatus('no-wasm-in-options', { + hint: 'WASM module should be passed via processorOptions.wasmModule' + }); + } + + // Initialize ring buffer if provided + if (processorOptions.ringBuffer) { + ringBufferReader = new RingBufferReader(processorOptions.ringBuffer); + this._postStatus('ring-buffer-ready', { + hasRingBuffer: true, + capacity: RING_CAPACITY + }); + } + } + + /** + * Post status message to main thread + * @private + */ + _postStatus(status, data = {}) { + this.port.postMessage({ + type: 'status', + status: status, + ...data + }); + } + + /** + * Post error message to main thread + * @private + */ + _postError(error, context = '') { + this.port.postMessage({ + type: 'error', + error: error.toString(), + context: context + }); + } + + /** + * Handle messages from main thread + * @private + */ + _handleMessage(event) { + const data = event.data; // event.data contains the message + + // Debug log all messages (except high-frequency ones) + if (data.type !== 'setParameter' && data.type !== 'tick') { + this._postStatus('message-received', { msgType: data.type }); + } + + switch (data.type) { + case 'test': + this._postStatus('test-received', { value: data.value }); + break; + + case 'init-wasm': + // Initialize WASM from compiled module sent by main thread + // NOTE: This may not work in Chrome due to cross-origin issues + // See: https://issues.chromium.org/issues/40855462 + // The WASM module should be passed via processorOptions instead + if (this._initialized) { + this._postStatus('wasm-already-initialized', { + hint: 'WASM was already initialized via processorOptions' + }); + return; + } + + // Check if wasmModule is valid + if (!data.wasmModule) { + this._postError(new Error('No wasmModule in init-wasm message'), 'init-wasm'); + return; + } + + if (!(data.wasmModule instanceof WebAssembly.Module)) { + this._postError(new Error('wasmModule is not a WebAssembly.Module: ' + typeof data.wasmModule), 'init-wasm'); + return; + } + + this._postStatus('starting-wasm-init', { + hasModule: true, + moduleType: 'WebAssembly.Module', + note: 'Using postMessage (may fail in Chrome)' + }); + this._initFromWasmModule(data.wasmModule, data.memory); + break; + + case 'init-ring-buffer': + // Initialize ring buffer from SharedArrayBuffer sent by main thread + if (this._initialized && ringBufferReader) { + this._postStatus('ring-buffer-already-initialized', { + hint: 'Ring buffer was already initialized via processorOptions' + }); + return; + } + + if (data.ringBuffer) { + ringBufferReader = new RingBufferReader(data.ringBuffer); + this._postStatus('ring-buffer-ready', { + hasRingBuffer: true, + capacity: RING_CAPACITY + }); + } else { + this._postError(new Error('No SharedArrayBuffer provided'), 'ring buffer init'); + } + break; + + case 'noteOn': + if (this._initialized && wasmInstance) { + wasmInstance.exports.o(data.note, data.velocity); // _noteOn + } + break; + + case 'noteOff': + if (this._initialized && wasmInstance) { + wasmInstance.exports.p(data.note, data.velocity); // _noteOff + } + break; + + case 'setParameter': + if (this._initialized && wasmInstance) { + wasmInstance.exports.q(data.paramId, data.value); // _setParameter + } + break; + + case 'reset': + if (this._initialized && wasmInstance) { + wasmInstance.exports.t(); // _reset + } + break; + + case 'getConfig': + this._postStatus('config', { + sampleRate: this._sampleRate, + polyphony: this._polyphony, + initialized: this._initialized + }); + break; + + default: + console.warn('[C15Processor] Unknown message type:', data.type); + } + } + + /** + * Initialize from pre-compiled WASM module + * @private + */ + async _initFromWasmModule(wasmModule, memory) { + try { + this._postStatus('loading'); + + // Lazy reference to WASM memory — needed by _clock_time_get before we can + // assign wasmMemory (which only exists after WebAssembly.instantiate returns). + const memRef = [null]; + + // Define the WASM imports matching Emscripten's expected structure. + // Mapping (namespace 'a') as of the current build: + // a -> _emscripten_get_now + // b -> _proc_exit + // c -> __emscripten_runtime_keepalive_clear + // d -> __setitimer_js + // e -> _clock_time_get (WASI clock; writes i64 nanoseconds into WASM heap) + // f -> _emscripten_resize_heap + // g -> __abort_js + const imports = { + a: { + a: () => performance.now(), + b: (code) => { throw new Error('WASM exit: ' + code); }, + c: () => {}, + d: (_which, _timeout_ms) => 0, + e: (clk_id, _ignored_precision, ptime) => { + const now = clk_id === 0 ? Date.now() : performance.now(); + const nsec = BigInt(Math.round(now * 1e6)); + if (memRef[0]) { + new BigInt64Array(memRef[0].buffer)[ptime >> 3] = nsec; + } + return 0; + }, + f: (requestedSize) => { + console.warn('[C15Processor] Heap resize requested but not supported'); + return 0; + }, + g: () => { throw new Error('WASM abort called'); } + } + }; + + // Instantiate the compiled module + const instance = await WebAssembly.instantiate(wasmModule, imports); + + wasmInstance = instance; + + // Get memory from exports (export 'h' is memory) + wasmMemory = instance.exports.h; + memRef[0] = wasmMemory; + + // Call runtime init (export 'i' is initRuntime/__wasm_call_ctors) + if (instance.exports.i) { + instance.exports.i(); + } + + // Initialize the engine (export 'j' is _engineInit) + const initResult = instance.exports.j(this._sampleRate, this._polyphony); + + if (initResult !== 1) { + throw new Error('engineInit failed with result: ' + initResult); + } + + this._initialized = true; + wasmReady = true; + + // Get the default frames per render call (export 'v' is _getDefaultFrames) + this._bufferSize = instance.exports.v(); + + this._postStatus('ready', { + sampleRate: this._sampleRate, + polyphony: this._polyphony, + bufferSize: this._bufferSize + }); + + } catch (error) { + this._postError(error, 'WASM load/init'); + console.error('[C15Processor] Failed to init WASM:', error); + } + } + + /** + * Process audio frames + * + * Called by the browser for each audio render quantum (typically 128 frames). + * + * VAL-M2-005: Processes ring buffer messages lock-free from main thread + * VAL-M2-006: Note on/off via ring buffer triggers audio + * + * @param {Float32Array[][]} inputs - Input audio buffers (unused) + * @param {Float32Array[][]} outputs - Output audio buffers (stereo) + * @param {Object} parameters - Automatable parameters (unused) + * @returns {boolean} - True to keep processor alive + */ + process(inputs, outputs, parameters) { + const output = outputs[0]; + + if (!output || output.length < 2) { + return true; // Keep alive but no output + } + + const leftChannel = output[0]; + const rightChannel = output[1]; + const numFrames = leftChannel.length; + + // If not initialized, output silence + if (!this._initialized || !wasmInstance) { + for (let i = 0; i < numFrames; i++) { + leftChannel[i] = 0; + rightChannel[i] = 0; + } + return true; + } + + // Process ring buffer messages first (VAL-M2-005, VAL-M2-006) + if (ringBufferReader) { + ringBufferReader.processMessages({ + onParameter: (paramId, value) => { + wasmInstance.exports.q(paramId, value); // _setParameter + }, + onNoteOn: (note, velocity) => { + this._dbgNoteOns = (this._dbgNoteOns || 0) + 1; + wasmInstance.exports.o(note, velocity); // _noteOn + }, + onNoteOff: (note, velocity) => { + wasmInstance.exports.p(note, velocity); // _noteOff + } + }, 64); // Process up to 64 messages per audio frame + } + + // Cache HEAPF32 view (recreate if memory grew) + if (!this._heapF32 || this._heapF32.buffer !== wasmMemory.buffer) { + this._heapF32 = new Float32Array(wasmMemory.buffer); + } + + try { + // Render audio using WASM + // export 'm' is _render - returns pointer to interleaved stereo buffer + const bufferPtr = wasmInstance.exports.m(numFrames); + + if (!bufferPtr) { + // Render failed, output silence + for (let i = 0; i < numFrames; i++) { + leftChannel[i] = 0; + rightChannel[i] = 0; + } + return true; + } + + const heapF32 = this._heapF32; + const bufferOffset = bufferPtr >> 2; // Convert byte offset to float32 index + + // Deinterleave stereo data from WASM buffer to output channels + let frameMax = 0; + for (let i = 0; i < numFrames; i++) { + const sampleIndex = bufferOffset + (i * 2); + const l = heapF32[sampleIndex]; + const r = heapF32[sampleIndex + 1]; + leftChannel[i] = l; + rightChannel[i] = r; + const v = Math.abs(l) > Math.abs(r) ? Math.abs(l) : Math.abs(r); + if (v > frameMax) frameMax = v; + } + + // Periodic diagnostics (every ~2 seconds = 750 frames at 128 frames/quantum) + this._dbgFrameCount = (this._dbgFrameCount || 0) + 1; + this._dbgMaxSample = Math.max(this._dbgMaxSample || 0, frameMax); + if (this._dbgFrameCount % 750 === 0) { + this.port.postMessage({ + type: 'diag', + frames: this._dbgFrameCount, + maxSample: this._dbgMaxSample, + noteOns: this._dbgNoteOns || 0 + }); + this._dbgMaxSample = 0; + } + + } catch (error) { + // On error, output silence + console.error('[C15Processor] Render error:', error); + for (let i = 0; i < numFrames; i++) { + leftChannel[i] = 0; + rightChannel[i] = 0; + } + } + + return true; // Keep processor alive + } + + /** + * Static getter for parameter descriptors (for automatable parameters) + * Currently not used but required for proper AudioWorklet interface. + */ + static get parameterDescriptors() { + return []; + } +} + +// Register the processor with the AudioWorkletGlobalScope +// VAL-M2-002: This registration must succeed for the processor to be usable +registerProcessor('c15-processor', C15Processor); diff --git a/playground/css/style.css b/playground/css/style.css index 66c8731..fab1d66 100644 --- a/playground/css/style.css +++ b/playground/css/style.css @@ -11,6 +11,7 @@ --danger: #ff3366; --good: #00ff88; --bad: #ff6644; + --synth-accent: #ff6b35; } * { box-sizing: border-box; margin: 0; padding: 0; } @@ -35,6 +36,7 @@ html, body { height: 100dvh; max-width: 600px; margin: 0 auto; + position: relative; } .header, @@ -62,6 +64,12 @@ html, body { color: var(--accent); } +.header-right { + display: flex; + align-items: center; + gap: 8px; +} + .header button { background: none; border: 1px solid #333; @@ -73,6 +81,235 @@ html, body { font-size: 14px; } +.mode-badge { + font-size: 10px; + padding: 3px 8px; + border-radius: 10px; + background: rgba(0, 255, 136, 0.12); + border: 1px solid var(--accent-dim); + color: var(--accent); + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; +} + +.mode-badge.synth { + background: rgba(255, 107, 53, 0.12); + border-color: var(--synth-accent); + color: var(--synth-accent); +} + +/* ==================== Side Panel ==================== */ +.side-panel-toggle { + position: fixed; + left: 0; + top: 50%; + transform: translateY(-50%); + z-index: 50; + background: var(--bg-surface); + border: 1px solid #333; + border-left: none; + border-radius: 0 8px 8px 0; + color: var(--text-dim); + font-size: 16px; + padding: 12px 6px; + cursor: pointer; + line-height: 1; + transition: background 0.15s; +} + +.side-panel-toggle:hover { + background: var(--bg-elevated); + color: var(--text); +} + +.side-panel { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 260px; + background: var(--bg-surface); + border-right: 1px solid #333; + z-index: 60; + transform: translateX(-100%); + transition: transform 0.25s ease; + display: flex; + flex-direction: column; + overflow-y: auto; +} + +.side-panel.open { + transform: translateX(0); +} + +.side-panel-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 55; +} + +.side-panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 16px 10px; + border-bottom: 1px solid #222; +} + +.side-panel-title { + font-size: 14px; + font-weight: 600; + color: var(--text); + letter-spacing: 0.5px; +} + +.side-panel-close { + background: none; + border: none; + color: var(--text-dim); + font-size: 22px; + cursor: pointer; + line-height: 1; + padding: 0 4px; +} + +.side-panel-tabs { + display: flex; + gap: 0; + padding: 12px 16px; + border-bottom: 1px solid #222; +} + +.sp-tab { + flex: 1; + padding: 8px 0; + background: var(--bg-elevated); + border: 1px solid #333; + color: var(--text-dim); + font-size: 12px; + font-weight: 600; + cursor: pointer; + transition: all 0.15s; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.sp-tab:first-child { + border-radius: 6px 0 0 6px; +} + +.sp-tab:last-child { + border-radius: 0 6px 6px 0; + border-left: none; +} + +.sp-tab.active[data-mode="visual"] { + background: rgba(0, 255, 136, 0.12); + border-color: var(--accent-dim); + color: var(--accent); +} + +.sp-tab.active[data-mode="synth"] { + background: rgba(255, 107, 53, 0.12); + border-color: var(--synth-accent); + color: var(--synth-accent); +} + +.sp-hint { + padding: 16px; + font-size: 12px; + color: var(--text-dim); + line-height: 1.5; +} + +/* Synth controls */ +.synth-controls { + padding: 12px 16px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.synth-section { + display: flex; + flex-direction: column; + gap: 6px; +} + +.synth-label { + font-size: 11px; + color: var(--text-dim); + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; +} + +.synth-label span { + color: var(--synth-accent); + font-weight: 700; +} + +.synth-slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 6px; + background: #333; + border-radius: 3px; + outline: none; +} + +.synth-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + background: var(--synth-accent); + border-radius: 50%; + cursor: pointer; +} + +.synth-slider::-moz-range-thumb { + width: 16px; + height: 16px; + background: var(--synth-accent); + border-radius: 50%; + cursor: pointer; + border: none; +} + +.synth-select { + background: var(--bg-elevated); + border: 1px solid #333; + color: var(--text); + padding: 6px 8px; + border-radius: 6px; + font-size: 12px; + cursor: pointer; +} + +.synth-start-btn { + font-size: 12px; +} + +.synth-arp-btn { + font-size: 12px; +} + +.synth-arp-btn.playing { + background: rgba(255, 107, 53, 0.15); + border-color: var(--synth-accent); + color: var(--synth-accent); +} + +.synth-status { + font-size: 10px; + color: var(--text-dim); + font-family: monospace; +} + /* Visual canvas */ .visual-container { position: relative; @@ -160,6 +397,11 @@ html, body { flex-shrink: 0; } +.param-container.synth-mode .param-label { + width: 72px; + color: #886644; +} + .param-track { flex: 1; height: 8px; diff --git a/playground/index.html b/playground/index.html index e8ef002..f30410d 100644 --- a/playground/index.html +++ b/playground/index.html @@ -11,16 +11,84 @@
+ +
+
+ Mode + +
+ +
+ + +
+ + + + + +
+

Move the joystick and train the neural network to map positions to visual parameters.

+
+
+ + + +

NISPS Playground

- +
+ Visual + +
-
+
@@ -37,35 +105,43 @@
+ + + diff --git a/playground/js/app.js b/playground/js/app.js index 47d753b..8d3c688 100644 --- a/playground/js/app.js +++ b/playground/js/app.js @@ -1,15 +1,22 @@ // NISPS Playground - Main application -// Wires IML engine to visual system with joystick input and dual learning modes +// Wires IML engine to visual system OR C15 synth with joystick input and dual learning modes import { IML } from './nisps/iml.js'; import { FlowFieldVisualizer } from './ui/visualizer.js'; import { VirtualJoystick } from './ui/joystick.js'; import { Controls } from './ui/controls.js'; import { ParamDisplay } from './ui/param-display.js'; +import { C15Bridge } from './synth/c15-bridge.js'; +import { Arpeggiator } from './synth/arpeggiator.js'; +import { SYNTH_PARAM_MAP, SYNTH_PARAM_NAMES, SYNTH_PARAM_COLORS } from './synth/param-map.js'; const N_INPUTS = 2; const N_OUTPUTS = 20; +// Visual mode param display config +const VISUAL_PARAM_NAMES = ['Flow', 'Scale', 'Speed', 'Hue', 'Spread', 'Size', 'Trail', 'Turb', 'Attract', 'Radius', 'DispRate', 'DispAmt', 'Lifetime', 'Respawn', 'Advection', 'Inertia', 'Drag', 'Repulse', 'RepCnt', 'RepRate']; +const VISUAL_PARAM_COLORS = ['#00ff88', '#00ccff', '#ff6600', '#ff00cc', '#ffcc00', '#88ff00', '#0088ff', '#ff3366', '#9bff5f', '#59d3ff', '#ff8f3f', '#a0b7ff', '#f4ff7a', '#ffa8db', '#7dffc8', '#ffd166', '#8ad4ff', '#ff5f5f', '#ffc15f', '#ff8a3d']; + // --- State --- let iml; let visualizer; @@ -17,6 +24,7 @@ let joystick; let controls; let paramDisplay; let learningMode = 'examples'; // 'examples' | 'rl' +let outputMode = 'visual'; // 'visual' | 'synth' let noiseLevel = 0.05; let rlExplorationDecay = 0.97; let animating = true; @@ -29,6 +37,10 @@ let visualExpanded = false; let appRoot; let expandVisualBtn; +// Synth state +let c15 = null; +let arpeggiator = null; + // --- Init --- function init() { iml = new IML(N_INPUTS, N_OUTPUTS, [10, 10, 14], 1000, 1.0, 0.00001); @@ -85,6 +97,21 @@ function init() { helpOverlay.addEventListener('click', () => helpOverlay.classList.add('hidden')); } + // Side panel + initSidePanel(); + + // Init synth + c15 = new C15Bridge(); + c15.onStatusChange = (msg) => { + const el = document.getElementById('synth-status'); + if (el) el.textContent = msg; + }; + c15.loadParams(); + arpeggiator = new Arpeggiator(c15); + + // Wire synth controls + initSynthControls(); + window.addEventListener('gamepadconnected', () => refreshDashboard()); window.addEventListener('gamepaddisconnected', () => refreshDashboard()); window.addEventListener('keydown', onKeyDown); @@ -106,6 +133,149 @@ function init() { loadState(); } +// --- Side Panel --- +function initSidePanel() { + const panel = document.getElementById('side-panel'); + const toggle = document.getElementById('side-panel-toggle'); + const close = document.getElementById('side-panel-close'); + const backdrop = document.getElementById('side-panel-backdrop'); + + const openPanel = () => { + panel.classList.add('open'); + backdrop.classList.remove('hidden'); + }; + const closePanel = () => { + panel.classList.remove('open'); + backdrop.classList.add('hidden'); + }; + + toggle.addEventListener('click', openPanel); + close.addEventListener('click', closePanel); + backdrop.addEventListener('click', closePanel); + + // Tab switching + panel.querySelectorAll('.sp-tab').forEach(tab => { + tab.addEventListener('click', () => { + const mode = tab.dataset.mode; + setOutputMode(mode); + + panel.querySelectorAll('.sp-tab').forEach(t => t.classList.toggle('active', t === tab)); + }); + }); +} + +function setOutputMode(mode) { + outputMode = mode; + + const synthControls = document.getElementById('synth-controls'); + const visualInfo = document.getElementById('visual-info'); + const presetsVisual = document.getElementById('presets-visual'); + const modeBadge = document.getElementById('mode-badge'); + const paramContainer = document.getElementById('param-display'); + + if (mode === 'synth') { + synthControls.classList.remove('hidden'); + visualInfo.classList.add('hidden'); + presetsVisual.classList.add('hidden'); + modeBadge.textContent = 'Synth'; + modeBadge.classList.add('synth'); + paramContainer.classList.add('synth-mode'); + + // Rebuild param display with synth labels/colors + paramDisplay.setNamesAndColors(SYNTH_PARAM_NAMES, SYNTH_PARAM_COLORS); + } else { + synthControls.classList.add('hidden'); + visualInfo.classList.remove('hidden'); + presetsVisual.classList.remove('hidden'); + modeBadge.textContent = 'Visual'; + modeBadge.classList.remove('synth'); + paramContainer.classList.remove('synth-mode'); + + // Restore visual param labels/colors + paramDisplay.setNamesAndColors(VISUAL_PARAM_NAMES, VISUAL_PARAM_COLORS); + } + + // Re-run inference and route outputs + routeOutputs(iml.getOutputs()); +} + +function routeOutputs(outputs) { + // Always update visualizer (it's always visible) + visualizer.setParams(outputs); + + // If in synth mode, also send to C15 + if (outputMode === 'synth' && c15 && c15.running) { + for (let i = 0; i < outputs.length && i < SYNTH_PARAM_MAP.length; i++) { + c15.setParameter(SYNTH_PARAM_MAP[i].id, outputs[i]); + } + } +} + +// --- Synth Controls --- +function initSynthControls() { + const startBtn = document.getElementById('synth-start'); + const volumeSlider = document.getElementById('synth-volume'); + const arpToggle = document.getElementById('arp-toggle'); + const arpProgression = document.getElementById('arp-progression'); + const arpTempo = document.getElementById('arp-tempo'); + const arpOctaves = document.getElementById('arp-octaves'); + const arpOffset = document.getElementById('arp-offset'); + + startBtn.addEventListener('click', async () => { + if (c15.running) { + arpeggiator.stop(); + arpToggle.textContent = 'Play'; + arpToggle.classList.remove('playing'); + await c15.stop(); + startBtn.textContent = 'Start Audio'; + } else { + await c15.start(); + startBtn.textContent = 'Stop Audio'; + // Send current NISPS outputs to synth + routeOutputs(iml.getOutputs()); + } + }); + + volumeSlider.addEventListener('input', (e) => { + c15.setMasterVolume(parseFloat(e.target.value)); + }); + + arpToggle.addEventListener('click', () => { + if (!c15.running) return; + if (arpeggiator.playing) { + arpeggiator.stop(); + arpToggle.textContent = 'Play'; + arpToggle.classList.remove('playing'); + } else { + arpeggiator.start(); + arpToggle.textContent = 'Stop'; + arpToggle.classList.add('playing'); + } + }); + + arpProgression.addEventListener('change', (e) => { + arpeggiator.progression = e.target.value; + }); + + arpTempo.addEventListener('input', (e) => { + const val = parseInt(e.target.value); + arpeggiator.bpm = val; + document.getElementById('tempo-val').textContent = val; + }); + + arpOctaves.addEventListener('input', (e) => { + const val = parseInt(e.target.value); + arpeggiator.octaves = val; + document.getElementById('octaves-val').textContent = val; + }); + + arpOffset.addEventListener('input', (e) => { + const val = parseInt(e.target.value); + arpeggiator.octaveOffset = val; + document.getElementById('offset-val').textContent = val; + }); +} + // --- Animation loop --- function animate() { if (!animating) return; @@ -121,7 +291,7 @@ function onJoystickMove(x, y) { iml.process(); const outputs = iml.getOutputs(); - visualizer.setParams(outputs); + routeOutputs(outputs); // Only update param display from network in inference (not when user is dragging) if (learningMode !== 'examples' || paramDisplay.activeBar < 0) { @@ -147,7 +317,7 @@ function onTrain() { if (loss !== null) { // After training, switch back to inference and update display const outputs = iml.getOutputs(); - visualizer.setParams(outputs); + routeOutputs(outputs); paramDisplay.update(outputs); controls.updateStatus(iml.exampleCount, loss, noiseLevel); controls.updateLossPlot(iml.lossHistory); @@ -159,7 +329,7 @@ function onTrain() { function onRandomize() { iml.randomiseWeights(); const outputs = iml.getOutputs(); - visualizer.setParams(outputs); + routeOutputs(outputs); paramDisplay.update(outputs); noiseLevel = 0.05; // reset noise controls.updateStatus(iml.exampleCount, iml.lastLoss, noiseLevel); @@ -206,7 +376,7 @@ function onThumbsDown() { iml.moveWeights(noiseLevel); const outputs = iml.getOutputs(); - visualizer.setParams(outputs); + routeOutputs(outputs); paramDisplay.update(outputs); controls.updateStatus(iml.exampleCount, iml.lastLoss, noiseLevel); refreshDashboard(); @@ -292,7 +462,7 @@ window.loadPreset = function(name) { const loss = trainModel(); const outputs = iml.getOutputs(); - visualizer.setParams(outputs); + routeOutputs(outputs); paramDisplay.update(outputs); controls.updateStatus(iml.exampleCount, loss, noiseLevel); controls.updateLossPlot(iml.lossHistory); @@ -319,7 +489,7 @@ function loadState() { } trainModel(); const outputs = iml.getOutputs(); - visualizer.setParams(outputs); + routeOutputs(outputs); paramDisplay.update(outputs); controls.updateStatus(iml.exampleCount, iml.lastLoss, noiseLevel); controls.updateLossPlot(iml.lossHistory); diff --git a/playground/js/synth/arpeggiator.js b/playground/js/synth/arpeggiator.js new file mode 100644 index 0000000..c555b16 --- /dev/null +++ b/playground/js/synth/arpeggiator.js @@ -0,0 +1,121 @@ +// Arpeggiator — plays chord progressions through the C15 engine +// Supports tempo, octave range, octave offset, and multiple chord progressions + +// Chord progressions as arrays of arrays of intervals (semitones from root) +const PROGRESSIONS = { + 'I-vi-IV-V': [ + [0, 4, 7], // C major + [9, 12, 16], // A minor + [5, 9, 12], // F major + [7, 11, 14], // G major + ], + 'I-IV-vi-V': [ + [0, 4, 7], + [5, 9, 12], + [9, 12, 16], + [7, 11, 14], + ], + 'i-VI-III-VII': [ + [0, 3, 7], // C minor + [8, 12, 15], // Ab major + [3, 7, 10], // Eb major + [10, 14, 17], // Bb major + ], + 'I-V-vi-IV': [ + [0, 4, 7], + [7, 11, 14], + [9, 12, 16], + [5, 9, 12], + ], +}; + +export class Arpeggiator { + constructor(bridge) { + this.bridge = bridge; + this.bpm = 120; + this.octaves = 2; // how many octaves to span + this.octaveOffset = 0; // base octave shift (-2 to +2) + this.progression = 'I-vi-IV-V'; + this.playing = false; + this._timer = null; + this._chordIndex = 0; + this._noteIndex = 0; + this._currentNotes = []; + this._lastNote = -1; + } + + get progressionNames() { + return Object.keys(PROGRESSIONS); + } + + start() { + if (this.playing) return; + this.playing = true; + this._chordIndex = 0; + this._noteIndex = 0; + this._scheduleNext(); + } + + stop() { + this.playing = false; + if (this._timer) { + clearTimeout(this._timer); + this._timer = null; + } + // Release current note + if (this._lastNote >= 0) { + this.bridge.noteOff(this._lastNote); + this._lastNote = -1; + } + } + + _scheduleNext() { + if (!this.playing) return; + + const msPerBeat = 60000 / this.bpm; + // Each note gets a 16th-note duration, chord changes every bar (4 beats) + const noteDuration = msPerBeat / 4; + + this._playNextNote(); + + this._timer = setTimeout(() => this._scheduleNext(), noteDuration); + } + + _playNextNote() { + const chords = PROGRESSIONS[this.progression] || PROGRESSIONS['I-vi-IV-V']; + + // Release previous note + if (this._lastNote >= 0) { + this.bridge.noteOff(this._lastNote); + } + + // Build arpeggiated note sequence from current chord across octaves + const chord = chords[this._chordIndex]; + const baseNote = 48 + (this.octaveOffset * 12); // C3 as base + offset + + // Build notes across octave range + const notes = []; + for (let oct = 0; oct < this.octaves; oct++) { + for (const interval of chord) { + const note = baseNote + interval + (oct * 12); + if (note >= 0 && note <= 127) { + notes.push(note); + } + } + } + + if (notes.length === 0) return; + + // Play the next note in sequence + const note = notes[this._noteIndex % notes.length]; + this.bridge.noteOn(note, 0.6 + Math.random() * 0.2); + this._lastNote = note; + + // Advance + this._noteIndex++; + if (this._noteIndex >= notes.length) { + this._noteIndex = 0; + this._chordIndex = (this._chordIndex + 1) % chords.length; + } + } +} diff --git a/playground/js/synth/c15-bridge.js b/playground/js/synth/c15-bridge.js new file mode 100644 index 0000000..76c1b8d --- /dev/null +++ b/playground/js/synth/c15-bridge.js @@ -0,0 +1,210 @@ +// C15 WASM synth engine bridge +// Handles WASM loading, AudioWorklet setup, ring buffer communication + +const CONFIG = { + WASM_PATH: 'c15/c15_engine.wasm', + WORKLET_PATH: 'c15/worklet-processor.js', + PARAMS_PATH: 'c15/parameters.json', + SAMPLE_RATE: 48000, + POLYPHONY: 24, +}; + +const MESSAGE_TYPE = { PARAMETER: 0, NOTE_ON: 1, NOTE_OFF: 2 }; +const HEADER_SIZE = 3; +const MESSAGE_SIZE = 4; +const RING_CAPACITY = 512; + +class RingBufferWriter { + constructor(sharedBuffer) { + this._buffer = new Float32Array(sharedBuffer); + this._int32 = new Int32Array(sharedBuffer); + } + + write(type, id, value) { + const writeIdx = Atomics.load(this._int32, 0); + const readIdx = Atomics.load(this._int32, 1); + const next = (writeIdx + 1) % RING_CAPACITY; + if (next === readIdx) return false; + + const off = HEADER_SIZE + writeIdx * MESSAGE_SIZE; + this._buffer[off] = type; + this._buffer[off + 1] = id; + this._buffer[off + 2] = value; + this._buffer[off + 3] = 0; + + Atomics.store(this._int32, 0, next); + Atomics.add(this._int32, 2, 1); + return true; + } + + writeParameter(paramId, value) { + return this.write(MESSAGE_TYPE.PARAMETER, paramId, value); + } + writeNoteOn(note, velocity) { + return this.write(MESSAGE_TYPE.NOTE_ON, note, velocity); + } + writeNoteOff(note, velocity) { + return this.write(MESSAGE_TYPE.NOTE_OFF, note, velocity || 0); + } +} + +export class C15Bridge { + constructor() { + this.audioContext = null; + this.workletNode = null; + this.masterGain = null; + this.ringWriter = null; + this.running = false; + this.ready = false; + this.allParams = null; + this.activeNotes = new Set(); + this._onStatusChange = null; + } + + set onStatusChange(fn) { this._onStatusChange = fn; } + + _status(msg) { + console.log('[C15]', msg); + this._onStatusChange?.(msg); + } + + async loadParams() { + if (this.allParams) return this.allParams; + try { + const res = await fetch(CONFIG.PARAMS_PATH); + if (!res.ok) throw new Error(`${res.status}`); + const data = await res.json(); + this.allParams = data.parameters; + } catch (err) { + console.warn('[C15] Failed to load parameters.json:', err.message); + this.allParams = []; + } + return this.allParams; + } + + async start() { + if (this.running) return; + + try { + if (typeof SharedArrayBuffer === 'undefined') { + throw new Error('SharedArrayBuffer not available — serve with COOP/COEP headers (use serve.sh)'); + } + + this._status('Compiling WASM...'); + const resp = await fetch(CONFIG.WASM_PATH); + if (!resp.ok) throw new Error(`Failed to fetch WASM: ${resp.status}`); + const wasmBytes = await resp.arrayBuffer(); + const wasmModule = await WebAssembly.compile(wasmBytes); + + const AC = window.AudioContext || window.webkitAudioContext; + this.audioContext = new AC({ + sampleRate: CONFIG.SAMPLE_RATE, + latencyHint: 'interactive', + }); + + // Ring buffer (passed via processorOptions — worklet reads it immediately) + const sabSize = (HEADER_SIZE + RING_CAPACITY * MESSAGE_SIZE) * 4; + const sab = new SharedArrayBuffer(sabSize); + new Float32Array(sab).fill(0); + this.ringWriter = new RingBufferWriter(sab); + + // Load worklet + this._status('Loading AudioWorklet...'); + await this.audioContext.audioWorklet.addModule(CONFIG.WORKLET_PATH); + + this.workletNode = new AudioWorkletNode(this.audioContext, 'c15-processor', { + processorOptions: { + sampleRate: CONFIG.SAMPLE_RATE, + polyphony: CONFIG.POLYPHONY, + wasmModule, + ringBuffer: sab, + }, + numberOfInputs: 0, + numberOfOutputs: 1, + outputChannelCount: [2], + }); + + this.workletNode.port.onmessage = (e) => this._handleWorkletMsg(e.data); + + this.masterGain = this.audioContext.createGain(); + this.masterGain.gain.value = 0.5; + + this.workletNode.connect(this.masterGain); + this.masterGain.connect(this.audioContext.destination); + + if (this.audioContext.state === 'suspended') { + await this.audioContext.resume(); + } + + this.running = true; + this._status('Running'); + } catch (err) { + this._status(`Error: ${err.message}`); + console.error('[C15] Start failed:', err); + } + } + + async stop() { + if (!this.running) return; + this.panic(); + if (this.audioContext) { + await this.audioContext.suspend(); + } + this.running = false; + this.ready = false; + this._status('Stopped'); + } + + _handleWorkletMsg(data) { + if (data.type === 'status' && data.status === 'ready') { + this.ready = true; + this._status('WASM ready'); + this._sendAllDefaults(); + } + } + + _sendAllDefaults() { + if (!this.allParams || !this.ringWriter) return; + for (const p of this.allParams) { + if (!p.valid) continue; + this.ringWriter.writeParameter(p.id, Math.max(0, Math.min(1, p.defaultValue))); + } + // Set some initial sound: Osc A output level up, envelope sustain + this.ringWriter.writeParameter(169, 0.75); // Out_Mix_A_Lvl + this.ringWriter.writeParameter(8, 0.4); // Env_A_Sus + this.ringWriter.writeParameter(0, 0.1); // Env_A_Att (quick) + this.ringWriter.writeParameter(10, 0.5); // Env_A_Rel + this.ringWriter.writeParameter(241, 0.15); // Reverb_Mix + this.ringWriter.writeParameter(233, 0.08); // Echo_Mix + } + + setParameter(paramId, value) { + if (!this.ringWriter || !this.running) return; + this.ringWriter.writeParameter(paramId, Math.max(0, Math.min(1, value))); + } + + noteOn(note, velocity = 0.7) { + if (!this.ringWriter || !this.running) return; + this.ringWriter.writeNoteOn(note, velocity); + this.activeNotes.add(note); + } + + noteOff(note) { + if (!this.ringWriter || !this.running) return; + this.ringWriter.writeNoteOff(note, 0); + this.activeNotes.delete(note); + } + + panic() { + for (const note of this.activeNotes) { + this.ringWriter?.writeNoteOff(note, 0); + } + this.activeNotes.clear(); + } + + setMasterVolume(value) { + if (this.masterGain) { + this.masterGain.gain.value = value; + } + } +} diff --git a/playground/js/synth/param-map.js b/playground/js/synth/param-map.js new file mode 100644 index 0000000..3027879 --- /dev/null +++ b/playground/js/synth/param-map.js @@ -0,0 +1,38 @@ +// Maps 20 NISPS outputs (0-1) to curated C15 synth parameters +// Each entry: { id, name, label, defaultValue, bipolar } +// Chosen for maximum sonic impact and exploration potential + +export const SYNTH_PARAM_MAP = [ + { id: 60, name: 'Osc_A_PM_Self', label: 'OscA Self-PM', defaultValue: 0.00, bipolar: true }, + { id: 64, name: 'Osc_A_PM_B', label: 'OscA PM-B', defaultValue: 0.00, bipolar: true }, + { id: 90, name: 'Osc_B_PM_Self', label: 'OscB Self-PM', defaultValue: 0.00, bipolar: true }, + { id: 94, name: 'Osc_B_PM_A', label: 'OscB PM-A', defaultValue: 0.00, bipolar: true }, + { id: 71, name: 'Shp_A_Drive', label: 'ShpA Drive', defaultValue: 0.20, bipolar: false }, + { id: 74, name: 'Shp_A_Fold', label: 'ShpA Fold', defaultValue: 0.50, bipolar: false }, + { id: 101, name: 'Shp_B_Drive', label: 'ShpB Drive', defaultValue: 0.20, bipolar: false }, + { id: 140, name: 'SV_Flt_Cut', label: 'SVF Cutoff', defaultValue: 0.50, bipolar: false }, + { id: 144, name: 'SV_Flt_Res', label: 'SVF Reso', defaultValue: 0.50, bipolar: false }, + { id: 148, name: 'SV_Flt_Spread', label: 'SVF Spread', defaultValue: 0.20, bipolar: true }, + { id: 115, name: 'Comb_Flt_Pitch', label: 'Comb Pitch', defaultValue: 0.50, bipolar: false }, + { id: 119, name: 'Comb_Flt_Decay', label: 'Comb Decay', defaultValue: 0.00, bipolar: true }, + { id: 127, name: 'Comb_Flt_AP_Res', label: 'Comb AP Res', defaultValue: 0.50, bipolar: false }, + { id: 235, name: 'Reverb_Size', label: 'Reverb Size', defaultValue: 0.33, bipolar: false }, + { id: 238, name: 'Reverb_Color', label: 'Reverb Color', defaultValue: 0.50, bipolar: false }, + { id: 225, name: 'Echo_Time', label: 'Echo Time', defaultValue: 0.43, bipolar: false }, + { id: 229, name: 'Echo_Feedback', label: 'Echo FB', defaultValue: 0.50, bipolar: false }, + { id: 219, name: 'Flanger_Feedback', label: 'Flanger FB', defaultValue: 0.00, bipolar: true }, + { id: 169, name: 'Out_Mix_A_Lvl', label: 'Out A Level', defaultValue: 0.75, bipolar: true }, + { id: 172, name: 'Out_Mix_B_Lvl', label: 'Out B Level', defaultValue: 0.00, bipolar: true }, +]; + +// Parameter display names for the param bar UI +export const SYNTH_PARAM_NAMES = SYNTH_PARAM_MAP.map(p => p.label); + +// Colors for synth mode param bars (warm/synth palette) +export const SYNTH_PARAM_COLORS = [ + '#ff6b35', '#ff9a3c', '#e85d04', '#ffb703', + '#fb5607', '#ff006e', '#8338ec', '#3a86ff', + '#06d6a0', '#118ab2', '#073b4c', '#ef476f', + '#ffd166', '#06d6a0', '#118ab2', '#8ecae6', + '#219ebc', '#ffb4a2', '#e5989b', '#b5838d', +]; diff --git a/playground/js/ui/param-display.js b/playground/js/ui/param-display.js index 9f7c3c0..550b824 100644 --- a/playground/js/ui/param-display.js +++ b/playground/js/ui/param-display.js @@ -1,8 +1,8 @@ // Parameter bar display // Shows output parameters as horizontal bars, draggable in examples mode -const PARAM_NAMES = ['Flow', 'Scale', 'Speed', 'Hue', 'Spread', 'Size', 'Trail', 'Turb', 'Attract', 'Radius', 'DispRate', 'DispAmt', 'Lifetime', 'Respawn', 'Advection', 'Inertia', 'Drag', 'Repulse', 'RepCnt', 'RepRate']; -const PARAM_COLORS = ['#00ff88', '#00ccff', '#ff6600', '#ff00cc', '#ffcc00', '#88ff00', '#0088ff', '#ff3366', '#9bff5f', '#59d3ff', '#ff8f3f', '#a0b7ff', '#f4ff7a', '#ffa8db', '#7dffc8', '#ffd166', '#8ad4ff', '#ff5f5f', '#ffc15f', '#ff8a3d']; +const DEFAULT_PARAM_NAMES = ['Flow', 'Scale', 'Speed', 'Hue', 'Spread', 'Size', 'Trail', 'Turb', 'Attract', 'Radius', 'DispRate', 'DispAmt', 'Lifetime', 'Respawn', 'Advection', 'Inertia', 'Drag', 'Repulse', 'RepCnt', 'RepRate']; +const DEFAULT_PARAM_COLORS = ['#00ff88', '#00ccff', '#ff6600', '#ff00cc', '#ffcc00', '#88ff00', '#0088ff', '#ff3366', '#9bff5f', '#59d3ff', '#ff8f3f', '#a0b7ff', '#f4ff7a', '#ffa8db', '#7dffc8', '#ffd166', '#8ad4ff', '#ff5f5f', '#ffc15f', '#ff8a3d']; export class ParamDisplay { constructor(container, numParams = 20) { @@ -12,6 +12,8 @@ export class ParamDisplay { this.draggable = false; this.onChange = null; this.activeBar = -1; + this.paramNames = [...DEFAULT_PARAM_NAMES]; + this.paramColors = [...DEFAULT_PARAM_COLORS]; this.build(); } @@ -25,7 +27,7 @@ export class ParamDisplay { const label = document.createElement('span'); label.className = 'param-label'; - label.textContent = PARAM_NAMES[i] || `p${i}`; + label.textContent = this.paramNames[i] || `p${i}`; const track = document.createElement('div'); track.className = 'param-track'; @@ -33,7 +35,7 @@ export class ParamDisplay { const fill = document.createElement('div'); fill.className = 'param-fill'; - fill.style.background = PARAM_COLORS[i] || '#888'; + fill.style.background = this.paramColors[i] || '#888'; fill.style.width = '50%'; const val = document.createElement('span'); @@ -106,4 +108,17 @@ export class ParamDisplay { this.draggable = draggable; this.container.classList.toggle('draggable', draggable); } + + setNamesAndColors(names, colors) { + this.paramNames = names || DEFAULT_PARAM_NAMES; + this.paramColors = colors || DEFAULT_PARAM_COLORS; + // Update existing bars in-place + for (let i = 0; i < this.numParams; i++) { + if (!this.bars[i]) continue; + const row = this.bars[i].fill.closest('.param-row'); + const label = row?.querySelector('.param-label'); + if (label) label.textContent = this.paramNames[i] || `p${i}`; + this.bars[i].fill.style.background = this.paramColors[i] || '#888'; + } + } } diff --git a/playground/serve-coop.py b/playground/serve-coop.py new file mode 100755 index 0000000..0573f10 --- /dev/null +++ b/playground/serve-coop.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""HTTP server with COOP/COEP headers for SharedArrayBuffer support.""" +import http.server +import sys +import os + +PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8000 + +class COOPHandler(http.server.SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header('Cross-Origin-Opener-Policy', 'same-origin') + self.send_header('Cross-Origin-Embedder-Policy', 'require-corp') + super().end_headers() + +os.chdir(os.path.dirname(os.path.abspath(__file__))) +with http.server.HTTPServer(('', PORT), COOPHandler) as httpd: + print(f'Serving playground at http://localhost:{PORT} (COOP/COEP enabled)') + httpd.serve_forever() diff --git a/playground/serve.sh b/playground/serve.sh index 320d623..e72ffa9 100755 --- a/playground/serve.sh +++ b/playground/serve.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Serve the NISPS playground, finding an open port if needed. +# Serve the NISPS playground with COOP/COEP headers (required for SharedArrayBuffer/synth mode). PORT="${1:-8000}" MAX_ATTEMPTS=20 @@ -8,8 +8,8 @@ DIR="$(cd "$(dirname "$0")" && pwd)" for ((i = 0; i < MAX_ATTEMPTS; i++)); do candidate=$((PORT + i)) if ! ss -tlnp 2>/dev/null | grep -q ":${candidate} "; then - echo "Serving playground at http://localhost:${candidate}" - exec python3 -m http.server "$candidate" -d "$DIR" + echo "Serving playground at http://localhost:${candidate} (COOP/COEP enabled)" + exec python3 "$DIR/serve-coop.py" "$candidate" fi echo "Port ${candidate} in use, trying next..." done