diff --git a/playground/c15/c15_engine.wasm b/playground/c15/c15_engine.wasm new file mode 100755 index 0000000..00ea237 Binary files /dev/null and b/playground/c15/c15_engine.wasm differ diff --git a/playground/c15/parameters.json b/playground/c15/parameters.json new file mode 100644 index 0000000..16c5221 --- /dev/null +++ b/playground/c15/parameters.json @@ -0,0 +1,5433 @@ +{ + "version": "1.7B-8", + "generated": "2026-03-09T06:29:52.786Z", + "totalSlots": 400, + "validParameters": 287, + "parameters": [ + { + "id": 0, + "valid": true, + "name": "Env_A_Att", + "displayName": "Attack Time", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the (polynomial) Attack segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_A_Att", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Attack" + }, + { + "id": 2, + "valid": true, + "name": "Env_A_Dec_1", + "displayName": "Decay 1 Time", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0.59, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the first (linear) Decay segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_A_Dec_1", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Decay 1" + }, + { + "id": 4, + "valid": true, + "name": "Env_A_BP", + "displayName": "Breakpoint Level", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Level of the Breakpoint between the two Decay segments.", + "category": "Local_Modulateables", + "pid": "Env_A_BP", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Breakpoint" + }, + { + "id": 6, + "valid": true, + "name": "Env_A_Dec_2", + "displayName": "Decay 2 Time", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0.79, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the second (exponential) Decay segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_A_Dec_2", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Decay 2" + }, + { + "id": 8, + "valid": true, + "name": "Env_A_Sus", + "displayName": "Sustain Level", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Sustain level (target of the second Decay segment).", + "category": "Local_Modulateables", + "pid": "Env_A_Sus", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Sustain" + }, + { + "id": 10, + "valid": true, + "name": "Env_A_Rel", + "displayName": "Release Time", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0.53, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -18.99, + "bipolar": false, + "description": "Time of the (exponential) Release segment in milliseconds (infinite at maximum).", + "category": "Local_Modulateables", + "pid": "Env_A_Rel", + "scaleFactor": 1.01, + "scaleOffset": -20, + "shortName": "Release" + }, + { + "id": 12, + "valid": true, + "name": "Env_A_Gain", + "displayName": "Gain", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Expon_Gain", + "min": -24, + "max": 24, + "bipolar": true, + "description": "Gain [in dB] of the envelope signal. As this signal modulates the Oscillator and Shaper A, the Gain ", + "category": "Local_Modulateables", + "pid": "Env_A_Gain", + "scaleFactor": 24, + "scaleOffset": 0, + "shortName": "Gain" + }, + { + "id": 14, + "valid": true, + "name": "Env_A_Lvl_Vel", + "displayName": "Level Velocity", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Influence of the key velocity on the peak, breakpoint and sustain levels of the envelope [maximum dynamic ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Lvl_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Level Vel" + }, + { + "id": 15, + "valid": true, + "name": "Env_A_Att_Vel", + "displayName": "Attack Velocity", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Velocity influence on the Attack time. The value represents the logarithmic amount of the reduction of the ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Att_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 16, + "valid": true, + "name": "Env_A_Rel_Vel", + "displayName": "Release Velocity", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Velocity influence on the Release time. The value represents the logarithmic amount of the reduction of the ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Rel_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 17, + "valid": true, + "name": "Env_A_Lvl_KT", + "displayName": "Level Key Trk", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Key tracking of the envelope's peak, breakpoint and sustain levels [dB per semitone].\\nPositive values: ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Lvl_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Level KT" + }, + { + "id": 18, + "valid": true, + "name": "Env_A_Time_KT", + "displayName": "Time Key Trk", + "groupName": "Envelope A", + "groupShortName": "Envelope A", + "defaultValue": 0.05, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key tracking of the attack, decay and release times. The value determines how much shorter the times get for ", + "category": "Local_Unmodulateables", + "pid": "Env_A_Time_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Time KT" + }, + { + "id": 19, + "valid": true, + "name": "Env_B_Att", + "displayName": "Attack Time", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the (polynomial) Attack segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_B_Att", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Attack" + }, + { + "id": 21, + "valid": true, + "name": "Env_B_Dec_1", + "displayName": "Decay 1 Time", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0.59, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the first (linear) Decay segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_B_Dec_1", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Decay 1" + }, + { + "id": 23, + "valid": true, + "name": "Env_B_BP", + "displayName": "Breakpoint Level", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Level of the Breakpoint between the two Decay segments.", + "category": "Local_Modulateables", + "pid": "Env_B_BP", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Breakpoint" + }, + { + "id": 25, + "valid": true, + "name": "Env_B_Dec_2", + "displayName": "Decay 2 Time", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0.79, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the second (exponential) Decay segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_B_Dec_2", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Decay 2" + }, + { + "id": 27, + "valid": true, + "name": "Env_B_Sus", + "displayName": "Sustain Level", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Sustain level (target of the second Decay segment).", + "category": "Local_Modulateables", + "pid": "Env_B_Sus", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Sustain" + }, + { + "id": 29, + "valid": true, + "name": "Env_B_Rel", + "displayName": "Release Time", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0.53, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -18.99, + "bipolar": false, + "description": "Time of the (exponential) Release segment in milliseconds (infinite at maximum).", + "category": "Local_Modulateables", + "pid": "Env_B_Rel", + "scaleFactor": 1.01, + "scaleOffset": -20, + "shortName": "Release" + }, + { + "id": 31, + "valid": true, + "name": "Env_B_Gain", + "displayName": "Gain", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Expon_Gain", + "min": -24, + "max": 24, + "bipolar": true, + "description": "Gain [in dB] of the envelope signal. As this signal modulates the Oscillator and Shaper B, the Gain ", + "category": "Local_Modulateables", + "pid": "Env_B_Gain", + "scaleFactor": 24, + "scaleOffset": 0, + "shortName": "Gain" + }, + { + "id": 33, + "valid": true, + "name": "Env_B_Lvl_Vel", + "displayName": "Level Velocity", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Influence of the key velocity on the peak, breakpoint and sustain levels of the envelope [maximum dynamic ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Lvl_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Level Vel" + }, + { + "id": 34, + "valid": true, + "name": "Env_B_Att_Vel", + "displayName": "Attack Velocity", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Velocity influence on the Attack time. The value represents the logarithmic amount of the reduction of the ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Att_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 35, + "valid": true, + "name": "Env_B_Rel_Vel", + "displayName": "Release Velocity", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Velocity influence on the Release time. The value represents the logarithmic amount of the reduction of the ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Rel_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 36, + "valid": true, + "name": "Env_B_Lvl_KT", + "displayName": "Level Key Trk", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Key tracking of the envelope's peak, breakpoint and sustain levels [dB per semitone].\\nPositive values: ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Lvl_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Level KT" + }, + { + "id": 37, + "valid": true, + "name": "Env_B_Time_KT", + "displayName": "Time Key Trk", + "groupName": "Envelope B", + "groupShortName": "Envelope B", + "defaultValue": 0.05, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key tracking of the attack, decay and release times. The value determines how much shorter the times get for ", + "category": "Local_Unmodulateables", + "pid": "Env_B_Time_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Time KT" + }, + { + "id": 38, + "valid": true, + "name": "Env_C_Att", + "displayName": "Attack Time", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the (polynomial) Attack segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_C_Att", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Attack" + }, + { + "id": 40, + "valid": true, + "name": "Env_C_Dec_1", + "displayName": "Decay 1 Time", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0.59, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the first (linear) Decay segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_C_Dec_1", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Decay 1" + }, + { + "id": 42, + "valid": true, + "name": "Env_C_BP", + "displayName": "Breakpoint Level", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Level of the (bipolar) Breakpoint between the two Decay segments.", + "category": "Local_Modulateables", + "pid": "Env_C_BP", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Breakpoint" + }, + { + "id": 44, + "valid": true, + "name": "Env_C_Dec_2", + "displayName": "Decay 2 Time", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0.79, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -19, + "bipolar": false, + "description": "Time of the second (exponential) Decay segment in milliseconds.", + "category": "Local_Modulateables", + "pid": "Env_C_Dec_2", + "scaleFactor": 1, + "scaleOffset": -20, + "shortName": "Decay 2" + }, + { + "id": 46, + "valid": true, + "name": "Env_C_Rel", + "displayName": "Release Time", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0.53, + "scalingType": "Expon_Env_Time", + "min": -20, + "max": -18.99, + "bipolar": false, + "description": "Time of the (exponential) Release segment in milliseconds (infinite at maximum).", + "category": "Local_Modulateables", + "pid": "Env_C_Rel", + "scaleFactor": 1.01, + "scaleOffset": -20, + "shortName": "Release" + }, + { + "id": 48, + "valid": true, + "name": "Env_C_Lvl_Vel", + "displayName": "Level Velocity", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0.5, + "scalingType": "Linear", + "min": -60, + "max": 60, + "bipolar": true, + "description": "Influence of the key velocity on the peak, breakpoint and sustain levels of the envelope [maximum dynamic ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Lvl_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Level Vel" + }, + { + "id": 49, + "valid": true, + "name": "Env_C_Att_Vel", + "displayName": "Attack Velocity", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Velocity influence on the Attack time. The value represents the logarithmic amount of the reduction of the ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Att_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 50, + "valid": true, + "name": "Env_C_Rel_Vel", + "displayName": "Release Velocity", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 60, + "bipolar": false, + "description": "Velocity influence on the Release time. The value represents the logarithmic amount of the reduction of the ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Rel_Vel", + "scaleFactor": 60, + "scaleOffset": 0, + "shortName": "Velocity" + }, + { + "id": 51, + "valid": true, + "name": "Env_C_Lvl_KT", + "displayName": "Level Key Trk", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Key tracking of the envelope's peak, breakpoint and sustain levels [dB per semitone].\\nPositive values: ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Lvl_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Level KT" + }, + { + "id": 52, + "valid": true, + "name": "Env_C_Time_KT", + "displayName": "Time Key Trk", + "groupName": "Envelope C", + "groupShortName": "Envelope C", + "defaultValue": 0.05, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Key tracking of the attack, decay and release times. The value determines how much shorter the times get for ", + "category": "Local_Unmodulateables", + "pid": "Env_C_Time_KT", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Time KT" + }, + { + "id": 53, + "valid": true, + "name": "Osc_A_Pitch", + "displayName": "Pitch", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0.5333333333333333, + "scalingType": "Linear", + "min": -20, + "max": 130, + "bipolar": false, + "description": "Pitch (logarithmic frequency) of Oscillator A at C3 (MIDI note 60) [in semitones, based on MIDI note ", + "category": "Local_Modulateables", + "pid": "Osc_A_Pitch", + "scaleFactor": 150, + "scaleOffset": -20, + "shortName": "Pitch" + }, + { + "id": 55, + "valid": true, + "name": "Osc_A_Pitch_KT", + "displayName": "Pitch - Key Trk", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "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_A_Pitch_KT", + "scaleFactor": 1.05, + "scaleOffset": 0, + "shortName": "Key Trk" + }, + { + "id": 56, + "valid": true, + "name": "Osc_A_Pitch_Env_C", + "displayName": "Pitch - Env C", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "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_A_Pitch_Env_C", + "scaleFactor": 80, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 57, + "valid": true, + "name": "Osc_A_Fluct", + "displayName": "Fluctuation", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "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_A_Fluct", + "scaleFactor": 0.95, + "scaleOffset": 0, + "shortName": "Fluctuation" + }, + { + "id": 59, + "valid": true, + "name": "Osc_A_Fluct_Env_C", + "displayName": "Fluct - Env C", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "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_A_Fluct_Env_C", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env C" + }, + { + "id": 60, + "valid": true, + "name": "Osc_A_PM_Self", + "displayName": "PM Self", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -0.5, + "max": 0.5, + "bipolar": true, + "description": "Amount of phase modulation by Oscillator & Shaper A (local feedback).", + "category": "Local_Modulateables", + "pid": "Osc_A_PM_Self", + "scaleFactor": 0.5, + "scaleOffset": 0, + "shortName": "PM Self" + }, + { + "id": 62, + "valid": true, + "name": "Osc_A_PM_Self_Env_A", + "displayName": "PM Self - Env A", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (A) amount for the phase modulation by Oscillator & Shaper A (local feedback). At zero, the ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_PM_Self_Env_A", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env A" + }, + { + "id": 63, + "valid": true, + "name": "Osc_A_PM_Self_Shp", + "displayName": "PM Self - Shaper", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Mix amount of Shaper A in the signal being used for phase modulation (self). At zero, the output signal of ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_PM_Self_Shp", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Shaper" + }, + { + "id": 64, + "valid": true, + "name": "Osc_A_PM_B", + "displayName": "PM B", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Parabolic", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Amount of phase modulation by Oscillator & Shaper B (cross feedback).", + "category": "Local_Modulateables", + "pid": "Osc_A_PM_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "PM B" + }, + { + "id": 66, + "valid": true, + "name": "Osc_A_PM_B_Env_B", + "displayName": "PM B - Env B", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Linear", + "min": 0, + "max": 1, + "bipolar": false, + "description": "Envelope (B) amount for the pahse modulation by Oscillator & Shaper B (cross feedback). At zero, the ", + "category": "Local_Unmodulateables", + "pid": "Osc_A_PM_B_Env_B", + "scaleFactor": 1, + "scaleOffset": 0, + "shortName": "Env B" + }, + { + "id": 67, + "valid": true, + "name": "Osc_A_PM_B_Shp", + "displayName": "PM B - Shaper", + "groupName": "Oscillator A", + "groupShortName": "Oscillator A", + "defaultValue": 0, + "scalingType": "Linear", + "min": -1, + "max": 1, + "bipolar": true, + "description": "Mix amount of Shaper B in the signal being used for phase modulation (B -> 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