diff --git a/res/controllers/Soundless_joyMIDI.midi.xml b/res/controllers/Soundless_joyMIDI.midi.xml
new file mode 100644
index 000000000000..cbd47b77d24c
--- /dev/null
+++ b/res/controllers/Soundless_joyMIDI.midi.xml
@@ -0,0 +1,1227 @@
+
+
+
+ Soundless joyMIDI
+ Ozzy Chiu
+ Soundless protable MIDI controller 2019-09-04
+ http://mixxx.org/forums/viewtopic.php?f=7&t=????
+ http://mixxx.org/wiki/doku.php/Soundless_joyMIDI
+
+
+
+
+
+
+
+
+ [Master]
+ crossfader
+ 0xB0
+ 0x01
+
+
+
+
+
+ [Master]
+ joyMIDI.fsr
+ 0xB0
+ 0x02
+
+
+
+
+
+ [Master]
+ joyMIDI.joystick
+ 0xB0
+ 0x10
+
+
+
+
+
+ [Master]
+ joyMIDI.joystick
+ 0xB0
+ 0x11
+
+
+
+
+
+ [Master]
+ joyMIDI.joystick
+ 0xB0
+ 0x12
+
+
+
+
+
+ [Master]
+ joyMIDI.joystick
+ 0xB0
+ 0x13
+
+
+
+
+
+ [Master]
+ joyMIDI.gsensor
+ 0xB0
+ 0x14
+
+
+
+
+
+ [Master]
+ joyMIDI.gsensor
+ 0xB0
+ 0x15
+
+
+
+
+
+ [Master]
+ joyMIDI.gsensor
+ 0xB0
+ 0x16
+
+
+
+
+
+ [Master]
+ joyMIDI.gsensor
+ 0xB0
+ 0x17
+
+
+
+
+
+
+
+ [Channel1]
+ joyMIDI.wheel
+ 0xB0
+ 0x0E
+
+
+
+
+
+
+
+ [Channel2]
+ joyMIDI.wheel
+ 0xB0
+ 0x0F
+
+
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x90
+ 0x24
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x80
+ 0x24
+
+
+
+
+
+ [Channel1]
+ joyMIDI.syncButton
+ 0x90
+ 0x25
+
+
+
+
+
+ [Channel1]
+ joyMIDI.syncButton
+ 0x80
+ 0x25
+
+
+
+
+
+ [Channel1]
+ joyMIDI.cueButton
+ 0x90
+ 0x26
+
+
+
+
+
+ [Channel1]
+ joyMIDI.cueButton
+ 0x80
+ 0x26
+
+
+
+
+
+ [Channel1]
+ joyMIDI.playButton
+ 0x90
+ 0x27
+
+
+
+
+
+ [Channel1]
+ joyMIDI.playButton
+ 0x80
+ 0x27
+
+
+
+
+
+ [Channel1]
+ joyMIDI.scratchButton
+ 0x90
+ 0x2C
+
+
+
+
+
+ [Channel1]
+ joyMIDI.scratchButton
+ 0x80
+ 0x2C
+
+
+
+
+
+ [Channel1]
+ joyMIDI.keyButton
+ 0x90
+ 0x2D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.keyButton
+ 0x80
+ 0x2D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.beatgridButton
+ 0x90
+ 0x2E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.beatgridButton
+ 0x80
+ 0x2E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.volumeButton
+ 0x90
+ 0x2F
+
+
+
+
+
+ [Channel1]
+ joyMIDI.volumeButton
+ 0x80
+ 0x2F
+
+
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x90
+ 0x34
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x80
+ 0x34
+
+
+
+
+
+ [Channel1]
+ joyMIDI.filterLowButton
+ 0x90
+ 0x35
+
+
+
+
+
+ [Channel1]
+ joyMIDI.filterLowButton
+ 0x80
+ 0x35
+
+
+
+
+
+ [Channel1]
+ joyMIDI.filterMiddleButton
+ 0x90
+ 0x36
+
+
+
+
+
+ [Channel1]
+ joyMIDI.filterMiddleButton
+ 0x80
+ 0x36
+
+
+
+
+
+ [Channel1]
+ joyMIDI.filterHighButton
+ 0x90
+ 0x37
+
+
+
+
+
+ [Channel1]
+ joyMIDI.filterHighButton
+ 0x80
+ 0x37
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue1Button
+ 0x90
+ 0x3C
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue1Button
+ 0x80
+ 0x3C
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue2Button
+ 0x90
+ 0x3D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue2Button
+ 0x80
+ 0x3D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue3Button
+ 0x90
+ 0x3E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue3Button
+ 0x80
+ 0x3E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue4Button
+ 0x90
+ 0x3F
+
+
+
+
+
+ [Channel1]
+ joyMIDI.hotcue4Button
+ 0x80
+ 0x3F
+
+
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x90
+ 0x44
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x80
+ 0x44
+
+
+
+
+
+ [Channel1]
+ joyMIDI.reloopToggleButton
+ 0x90
+ 0x45
+
+
+
+
+
+ [Channel1]
+ joyMIDI.reloopToggleButton
+ 0x80
+ 0x45
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loopInButton
+ 0x90
+ 0x46
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loopInButton
+ 0x80
+ 0x46
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loopOutButton
+ 0x90
+ 0x47
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loopOutButton
+ 0x80
+ 0x47
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_0p125_Button
+ 0x90
+ 0x4C
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_0p125_Button
+ 0x80
+ 0x4C
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_0p25_Button
+ 0x90
+ 0x4D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_0p25_Button
+ 0x80
+ 0x4D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_0p5_Button
+ 0x90
+ 0x4E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_0p5_Button
+ 0x80
+ 0x4E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_1_Button
+ 0x90
+ 0x4F
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loop_1_Button
+ 0x80
+ 0x4F
+
+
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x90
+ 0x54
+
+
+
+
+
+ [Channel1]
+ joyMIDI.shiftButton
+ 0x80
+ 0x54
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loadButton
+ 0x90
+ 0x55
+
+
+
+
+
+ [Channel1]
+ joyMIDI.loadButton
+ 0x80
+ 0x55
+
+
+
+
+
+ [Sampler1]
+ joyMIDI.sampler1Button
+ 0x90
+ 0x5C
+
+
+
+
+
+ [Sampler1]
+ joyMIDI.sampler1Button
+ 0x80
+ 0x5C
+
+
+
+
+
+ [Sampler2]
+ joyMIDI.sampler2Button
+ 0x90
+ 0x5D
+
+
+
+
+
+ [Sampler2]
+ joyMIDI.sampler2Button
+ 0x80
+ 0x5D
+
+
+
+
+
+ [Channel1]
+ joyMIDI.fx1Button
+ 0x90
+ 0x5E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.fx1Button
+ 0x80
+ 0x5E
+
+
+
+
+
+ [Channel1]
+ joyMIDI.fx2Button
+ 0x90
+ 0x5F
+
+
+
+
+
+ [Channel1]
+ joyMIDI.fx2Button
+ 0x80
+ 0x5F
+
+
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x90
+ 0x28
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x80
+ 0x28
+
+
+
+
+
+ [Channel2]
+ joyMIDI.syncButton
+ 0x90
+ 0x29
+
+
+
+
+
+ [Channel2]
+ joyMIDI.syncButton
+ 0x80
+ 0x29
+
+
+
+
+
+ [Channel2]
+ joyMIDI.cueButton
+ 0x90
+ 0x2A
+
+
+
+
+
+ [Channel2]
+ joyMIDI.cueButton
+ 0x80
+ 0x2A
+
+
+
+
+
+ [Channel2]
+ joyMIDI.playButton
+ 0x90
+ 0x2B
+
+
+
+
+
+ [Channel2]
+ joyMIDI.playButton
+ 0x80
+ 0x2B
+
+
+
+
+
+ [Channel2]
+ joyMIDI.scratchButton
+ 0x90
+ 0x30
+
+
+
+
+
+ [Channel2]
+ joyMIDI.scratchButton
+ 0x80
+ 0x30
+
+
+
+
+
+ [Channel2]
+ joyMIDI.keyButton
+ 0x90
+ 0x31
+
+
+
+
+
+ [Channel2]
+ joyMIDI.keyButton
+ 0x80
+ 0x31
+
+
+
+
+
+ [Channel2]
+ joyMIDI.beatgridButton
+ 0x90
+ 0x32
+
+
+
+
+
+ [Channel2]
+ joyMIDI.beatgridButton
+ 0x80
+ 0x32
+
+
+
+
+
+ [Channel2]
+ joyMIDI.volumeButton
+ 0x90
+ 0x33
+
+
+
+
+
+ [Channel2]
+ joyMIDI.volumeButton
+ 0x80
+ 0x33
+
+
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x90
+ 0x38
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x80
+ 0x38
+
+
+
+
+
+ [Channel2]
+ joyMIDI.filterLowButton
+ 0x90
+ 0x39
+
+
+
+
+
+ [Channel2]
+ joyMIDI.filterLowButton
+ 0x80
+ 0x39
+
+
+
+
+
+ [Channel2]
+ joyMIDI.filterMiddleButton
+ 0x90
+ 0x3A
+
+
+
+
+
+ [Channel2]
+ joyMIDI.filterMiddleButton
+ 0x80
+ 0x3A
+
+
+
+
+
+ [Channel2]
+ joyMIDI.filterHighButton
+ 0x90
+ 0x3B
+
+
+
+
+
+ [Channel2]
+ joyMIDI.filterHighButton
+ 0x80
+ 0x3B
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue1Button
+ 0x90
+ 0x40
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue1Button
+ 0x80
+ 0x40
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue2Button
+ 0x90
+ 0x41
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue2Button
+ 0x80
+ 0x41
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue3Button
+ 0x90
+ 0x42
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue3Button
+ 0x80
+ 0x42
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue4Button
+ 0x90
+ 0x43
+
+
+
+
+
+ [Channel2]
+ joyMIDI.hotcue4Button
+ 0x80
+ 0x43
+
+
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x90
+ 0x48
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x80
+ 0x48
+
+
+
+
+
+ [Channel2]
+ joyMIDI.reloopToggleButton
+ 0x90
+ 0x49
+
+
+
+
+
+ [Channel2]
+ joyMIDI.reloopToggleButton
+ 0x80
+ 0x49
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loopInButton
+ 0x90
+ 0x4A
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loopInButton
+ 0x80
+ 0x4A
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loopOutButton
+ 0x90
+ 0x4B
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loopOutButton
+ 0x80
+ 0x4B
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_0p125_Button
+ 0x90
+ 0x50
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_0p125_Button
+ 0x80
+ 0x50
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_0p25_Button
+ 0x90
+ 0x51
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_0p25_Button
+ 0x80
+ 0x51
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_0p5_Button
+ 0x90
+ 0x52
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_0p5_Button
+ 0x80
+ 0x52
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_1_Button
+ 0x90
+ 0x53
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loop_1_Button
+ 0x80
+ 0x53
+
+
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x90
+ 0x58
+
+
+
+
+
+ [Channel2]
+ joyMIDI.shiftButton
+ 0x80
+ 0x58
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loadButton
+ 0x90
+ 0x59
+
+
+
+
+
+ [Channel2]
+ joyMIDI.loadButton
+ 0x80
+ 0x59
+
+
+
+
+
+ [Sampler3]
+ joyMIDI.sampler3Button
+ 0x90
+ 0x60
+
+
+
+
+
+ [Sampler3]
+ joyMIDI.sampler3Button
+ 0x80
+ 0x60
+
+
+
+
+
+ [Sampler4]
+ joyMIDI.sampler4Button
+ 0x90
+ 0x61
+
+
+
+
+
+ [Sampler4]
+ joyMIDI.sampler4Button
+ 0x80
+ 0x61
+
+
+
+
+
+ [Channel2]
+ joyMIDI.fx1Button
+ 0x90
+ 0x62
+
+
+
+
+
+ [Channel2]
+ joyMIDI.fx1Button
+ 0x80
+ 0x62
+
+
+
+
+
+ [Channel2]
+ joyMIDI.fx2Button
+ 0x90
+ 0x63
+
+
+
+
+
+ [Channel2]
+ joyMIDI.fx2Button
+ 0x80
+ 0x63
+
+
+
+
+
+
+
+
diff --git a/res/controllers/Soundless_joyMIDI_scripts.js b/res/controllers/Soundless_joyMIDI_scripts.js
new file mode 100644
index 000000000000..79e1a07d859f
--- /dev/null
+++ b/res/controllers/Soundless_joyMIDI_scripts.js
@@ -0,0 +1,738 @@
+/*******************************************************************
+ * Soundless studio.
+ * Author: Ozzy Chiu (ozzy.chc@gmail.com)
+ * Version: 2019-09-04
+ *
+ * Variables on joyMIDI.userSetting can be modified by users to suit their preferences
+ *******************************************************************/
+var joyMIDI = {
+
+ // User Setting
+ "userSetting": {
+ // Enable the knob(button+jog) function
+ "knobEnable": true,
+ // Enable the beatloop with roll(1) function otherwise toggle(0)
+ "beatLoopRollEnable": true,
+ // Enable the samplers played from cue(1) otherwise from track start(0)
+ "samplerCueModeEnable": true,
+ },
+
+ // Channel1 variable
+ "[Channel1]": {
+ btnShift: 0,
+ btnSync: 0, syncTimerID: null, syncLongPress: 0,
+ btnScratch: 0, isScratchPermanent: 0, scratchTimerID: 0,
+ btnKey: 0,
+ btnBeatgrid: 0,
+ btnVolume: 0,
+ btnFilter: { "low": 0, "middle": 0, "high": 0 },
+ btnFx: { "1": 0, "2": 0 },
+
+ connBeatIndicator: {},
+ },
+
+ // Channel2 variable
+ "[Channel2]": {
+ btnShift: 0,
+ btnSync: 0, syncTimerID: null, syncLongPress: 0,
+ btnScratch: 0, isScratchPermanent: 0, scratchTimerID: 0,
+ btnKey: 0,
+ btnBeatgrid: 0,
+ btnVolume: 0,
+ btnFilter: { "low": 0, "middle": 0, "high": 0 },
+ btnFx: { "1": 0, "2": 0 },
+
+ connBeatIndicator: {},
+ },
+};
+
+// Constant
+var RELEASED = 0,
+ PRESSED = 1,
+ ADJUSTED = 2;
+
+//----------------------------
+// Constants for scratching
+//----------------------------
+
+/* Internal jog-wheel */
+var forwardFinetune = 0;
+var backwardFinetune = 0;
+
+var intervalPerRev = 64;
+var rpm = 33 + 1 / 3;
+var alpha = 1 / 16;
+var beta = (1 / 12) / 32;
+
+var scratchDisableTime = 100;
+var enableAccVal = true;
+
+/* external jog-wheel */
+// var forwardFinetune = 0;
+// var backwardFinetune = 1;
+
+// var intervalPerRev = 1000;
+// var rpm = 33 + 1 / 3;
+// var alpha = 1.0 / 8;
+// var beta = alpha / 32;
+
+// var scratchDisableTime = 150;
+// var enableAccVal = false;
+
+//----------------------------
+// Common variable
+//----------------------------
+var joystickDelay = false;
+var fsrDelay = false;
+
+
+
+
+//==== Init, Shutdown ========================================================
+joyMIDI.init = function(id, debug) {
+ joyMIDI['[Channel1]'].connBeatIndicator = engine.makeConnection('[Channel1]', 'beat_active', joyMIDI.onBeatIndicator);
+ joyMIDI['[Channel2]'].connBeatIndicator = engine.makeConnection('[Channel2]', 'beat_active', joyMIDI.onBeatIndicator);
+}
+
+joyMIDI.shutdown = function() {
+ joyMIDI['[Channel1]'].connBeatIndicator.disconnect();
+ joyMIDI['[Channel2]'].connBeatIndicator.disconnect();
+}
+
+//==== Callback ==============================================================
+joyMIDI.onBeatIndicator = function(value, group, control) {
+ var fader = engine.getParameter('[Master]', "crossfader");
+ if (fader < 0.5) {
+ // Left
+ if (group === '[Channel1]') {
+ midi.sendShortMsg(0x91, 0x00, (value) ? 0x7F : 0x00);
+ }
+ } else {
+ // Right
+ if (group === '[Channel2]') {
+ midi.sendShortMsg(0x92, 0x00, (value) ? 0x7F : 0x00);
+ }
+ }
+}
+
+//==== Button ================================================================
+joyMIDI.shiftButton = function(channel, control, value, status, group) {
+ joyMIDI[group].btnShift = (value > 0) ? PRESSED : RELEASED;
+}
+
+joyMIDI.syncButton = function(channel, control, value, status, group) {
+ if (!joyMIDI.userSetting.knobEnable) {
+ // knob disabled
+ if (value > 0) {
+ // press
+ if (!joyMIDI[group].btnShift) {
+ if (engine.getValue(group, "sync_enabled") > 0) {
+ engine.setValue(group, "sync_enabled", 0);
+ } else {
+ engine.setValue(group, "sync_enabled", 1);
+ if (joyMIDI[group].syncTimerID !== null) {
+ engine.stopTimer(joyMIDI[group].syncTimerID);
+ }
+ joyMIDI[group].syncLongPress = 0;
+ joyMIDI[group].syncTimerID =
+ engine.beginTimer(1000, "joyMIDI.syncTimerHandler('" + group + "')", true);
+ }
+ } else {
+ engine.setValue(group, "rate", 0);
+ }
+ } else {
+ // release
+ if (!joyMIDI[group].btnShift) {
+ if (joyMIDI[group].syncTimerID !== null) {
+ if (joyMIDI[group].syncLongPress === 0 && engine.getValue(group, "sync_enabled") === 1) {
+ engine.stopTimer(joyMIDI[group].syncTimerID);
+ engine.setValue(group, "sync_enabled", 0);
+ }
+ joyMIDI[group].syncTimerID = null;
+ }
+ }
+ }
+ } else {
+ // knob enabled
+ if (value > 0) {
+ // press
+ joyMIDI[group].btnSync = PRESSED;
+ } else {
+ // release
+ if (joyMIDI[group].btnSync != ADJUSTED) {
+ if (!joyMIDI[group].btnShift) {
+ engine.setValue(group, "beatsync", 1);
+ } else {
+ engine.setValue(group, "rate", 0);
+ }
+ }
+ joyMIDI[group].btnSync = RELEASED;
+ }
+ }
+}
+
+joyMIDI.syncTimerHandler = function(group) {
+ joyMIDI[group].syncLongPress = 1;
+}
+
+joyMIDI.cueButton = function(channel, control, value, status, group) {
+ var sft = joyMIDI[group].btnShift;
+ var cmd = sft ? "start" : "cue_default";
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.playButton = function(channel, control, value, status, group) {
+ var sft = joyMIDI[group].btnShift;
+ var cmd = sft ? "reverse" : "play";
+
+ if (value > 0) {
+ script.toggleControl(group, cmd);
+ }
+}
+
+joyMIDI.scratchButton = function(channel, control, value, status, group) {
+ if (value > 0) {
+ joyMIDI[group].isScratchPermanent = !joyMIDI[group].isScratchPermanent;
+ }
+}
+
+joyMIDI.keyButton = function(channel, control, value, status, group) {
+ if (!joyMIDI.userSetting.knobEnable) {
+ // knob disabled
+ if (value > 0) {
+ // press
+ if (!joyMIDI[group].btnShift) {
+ engine.setValue(group, "sync_key", 1);
+ } else {
+ engine.setValue(group, "reset_key", 1);
+ }
+ } else {
+ // release
+ }
+ } else {
+ // knob enabled
+ if (value > 0) {
+ // press
+ joyMIDI[group].btnKey = PRESSED;
+ } else {
+ // release
+ if (joyMIDI[group].btnKey != ADJUSTED) {
+ if (joyMIDI[group].btnShift === RELEASED) {
+ engine.setValue(group, "sync_key", 1);
+ } else {
+ engine.setValue(group, "reset_key", 1);
+ }
+ }
+ joyMIDI[group].btnKey = RELEASED;
+ }
+ }
+}
+
+joyMIDI.beatgridButton = function(channel, control, value, status, group) {
+ if (!joyMIDI.userSetting.knobEnable) {
+ // knob disabled
+ if (value > 0) {
+ // press
+ if (!joyMIDI[group].btnShift) {
+ engine.setValue(group, "beats_translate_curpos", 1);
+ engine.setValue(group, "beats_translate_curpos", 0);
+ } else {
+ engine.setValue(group, "beats_translate_match_alignment", 1);
+ engine.setValue(group, "beats_translate_match_alignment", 0);
+ }
+ } else {
+ // release
+ }
+ } else {
+ // knob enabled
+ if (value > 0) {
+ // press
+ joyMIDI[group].btnBeatgrid = PRESSED;
+ } else {
+ // release
+ if (joyMIDI[group].btnBeatgrid != ADJUSTED) {
+ if (joyMIDI[group].btnShift === RELEASED) {
+ engine.setValue(group, "beats_translate_curpos", 1);
+ engine.setValue(group, "beats_translate_curpos", 0);
+ } else {
+ engine.setValue(group, "beats_translate_match_alignment", 1);
+ engine.setValue(group, "beats_translate_match_alignment", 0);
+ }
+ }
+ joyMIDI[group].btnBeatgrid = RELEASED;
+ }
+ }
+}
+
+joyMIDI.volumeButton = function(channel, control, value, status, group) {
+ if (!joyMIDI.userSetting.knobEnable) {
+ // knob disabled
+ if (value > 0) {
+ // press
+ if (!joyMIDI[group].btnShift) {
+ engine.setValue(group, "volume", 1.0);
+ }
+ }
+ } else {
+ // knob enabled
+ if (value > 0) {
+ // press
+ joyMIDI[group].btnVolume = PRESSED;
+ } else {
+ // release
+ if (joyMIDI[group].btnVolume != ADJUSTED) {
+ if (joyMIDI[group].btnShift === RELEASED) {
+ engine.setValue(group, "volume", 1.0);
+ }
+ }
+ joyMIDI[group].btnVolume = RELEASED;
+ }
+ }
+}
+
+joyMIDI.filterLowButton = function(channel, control, value, status, group) {
+ joyMIDI.filterButton(channel, control, value, status, group,
+ "[EqualizerRack1_" + group + "_Effect1]", "button_parameter1", "low");
+}
+
+joyMIDI.filterMiddleButton = function(channel, control, value, status, group) {
+ joyMIDI.filterButton(channel, control, value, status, group,
+ "[EqualizerRack1_" + group + "_Effect1]", "button_parameter2", "middle");
+}
+
+joyMIDI.filterHighButton = function(channel, control, value, status, group) {
+ joyMIDI.filterButton(channel, control, value, status, group,
+ "[EqualizerRack1_" + group + "_Effect1]", "button_parameter3", "high");
+}
+
+joyMIDI.filterButton = function(channel, control, value, status, group, group2, param, select) {
+ if (!joyMIDI.userSetting.knobEnable) {
+ // knob disabled
+ if (value > 0) {
+ // press
+ if (!joyMIDI[group].btnShift) {
+ script.toggleControl(group2, param);
+ }
+ }
+ } else {
+ // knob enabled
+ if (value > 0) {
+ // press
+ joyMIDI[group].btnFilter[select] = PRESSED;
+ } else {
+ // release
+ if (joyMIDI[group].btnFilter[select] != ADJUSTED) {
+ if (joyMIDI[group].btnShift === RELEASED) {
+ script.toggleControl(group2, param);
+ }
+ }
+ joyMIDI[group].btnFilter[select] = RELEASED;
+ }
+ }
+}
+
+joyMIDI.hotcue1Button = function(channel, control, value, status, group) {
+ joyMIDI.hotcueButton(channel, control, value, status, group,
+ [ "hotcue_1_activate", "hotcue_1_clear" ]);
+}
+
+joyMIDI.hotcue2Button = function(channel, control, value, status, group) {
+ joyMIDI.hotcueButton(channel, control, value, status, group,
+ [ "hotcue_2_activate", "hotcue_2_clear" ]);
+}
+
+joyMIDI.hotcue3Button = function(channel, control, value, status, group) {
+ joyMIDI.hotcueButton(channel, control, value, status, group,
+ [ "hotcue_3_activate", "hotcue_3_clear" ]);
+}
+
+joyMIDI.hotcue4Button = function(channel, control, value, status, group) {
+ joyMIDI.hotcueButton(channel, control, value, status, group,
+ [ "hotcue_4_activate", "hotcue_4_clear" ]);
+}
+
+joyMIDI.hotcueButton = function(channel, control, value, status, group, commands) {
+ var sft = joyMIDI[group].btnShift;
+ var cmd = commands[sft];
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.reloopToggleButton = function(channel, control, value, status, group) {
+ var usr = joyMIDI.userSetting.beatLoopRollEnable;
+ var sft = joyMIDI[group].btnShift;
+ var cmd = sft ? (usr?"beatlooproll_activate":"beatloop_activate") : "reloop_toggle";
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.loopInButton = function(channel, control, value, status, group) {
+ var sft = joyMIDI[group].btnShift;
+ var cmd = sft ? "loop_halve" : "loop_in";
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.loopOutButton = function(channel, control, value, status, group) {
+ var sft = joyMIDI[group].btnShift;
+ var cmd = sft ? "loop_double" : "loop_out";
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.loop_0p125_Button = function(channel, control, value, status, group) {
+ joyMIDI.loop_x_Button(channel, control, value, status, group,
+ [ "beatloop_0.125_toggle", "beatloop_2_toggle",
+ "beatlooproll_0.125_activate", "beatlooproll_2_activate" ]);
+}
+
+joyMIDI.loop_0p25_Button = function(channel, control, value, status, group) {
+ joyMIDI.loop_x_Button(channel, control, value, status, group,
+ [ "beatloop_0.25_toggle", "beatloop_4_toggle",
+ "beatlooproll_0.25_activate", "beatlooproll_4_activate" ]);
+}
+
+joyMIDI.loop_0p5_Button = function(channel, control, value, status, group) {
+ joyMIDI.loop_x_Button(channel, control, value, status, group,
+ [ "beatloop_0.5_toggle", "beatloop_8_toggle",
+ "beatlooproll_0.5_activate", "beatlooproll_8_activate" ]);
+}
+
+joyMIDI.loop_1_Button = function(channel, control, value, status, group) {
+ joyMIDI.loop_x_Button(channel, control, value, status, group,
+ [ "beatloop_1_toggle", "beatloop_16_toggle",
+ "beatlooproll_1_activate", "beatlooproll_16_activate" ]);
+}
+
+joyMIDI.loop_x_Button = function(channel, control, value, status, group, commands) {
+ var usr = joyMIDI.userSetting.beatLoopRollEnable;
+ var sft = joyMIDI[group].btnShift;
+ var cmd = commands[ sft + (usr?2:0) ];
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.loadButton = function(channel, control, value, status, group) {
+ var sft = joyMIDI[group].btnShift;
+ var cmd = sft ? "eject" : "LoadSelectedTrack";
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.sampler1Button = function(channel, control, value, status, group) {
+ joyMIDI.samplerButton(channel, control, value, status, group, 1);
+}
+
+joyMIDI.sampler2Button = function(channel, control, value, status, group) {
+ joyMIDI.samplerButton(channel, control, value, status, group, 2);
+}
+
+joyMIDI.sampler3Button = function(channel, control, value, status, group) {
+ joyMIDI.samplerButton(channel, control, value, status, group, 3);
+}
+
+joyMIDI.sampler4Button = function(channel, control, value, status, group) {
+ joyMIDI.samplerButton(channel, control, value, status, group, 4);
+}
+
+joyMIDI.samplerButton = function(channel, control, value, status, group, number) {
+ var commands = ["start_play", "start_stop", "cue_gotoandplay", "cue_default"];
+ var usr = joyMIDI.userSetting.samplerCueModeEnable;
+ var sft = joyMIDI["[Channel"+ ((number > 2) ? 2 : 1) +"]"].btnShift;
+ var cmd = commands[sft + (usr?2:0)];
+ var val = (value > 0) ? 1 : 0;
+
+ engine.setValue(group, cmd, val);
+}
+
+joyMIDI.fx1Button = function(channel, control, value, status, group) {
+ joyMIDI.fxButton(channel, control, value, status, group,
+ "[EffectRack1_EffectUnit1]", "group_"+group+"_enable", 1);
+}
+
+joyMIDI.fx2Button = function(channel, control, value, status, group) {
+ joyMIDI.fxButton(channel, control, value, status, group,
+ "[EffectRack1_EffectUnit2]", "group_"+group+"_enable", 2);
+}
+
+joyMIDI.fxButton = function(channel, control, value, status, group, group2, param, select) {
+ if (!joyMIDI.userSetting.knobEnable) {
+ // knob disabled
+ if (value > 0) {
+ // press
+ if (!joyMIDI[group].btnShift) {
+ script.toggleControl(group2, param);
+ }
+ }
+ } else {
+ // knob enabled
+ if (value > 0) {
+ // press
+ joyMIDI[group].btnFx[select] = PRESSED;
+ } else {
+ // release
+ if (joyMIDI[group].btnFx[select] != ADJUSTED) {
+ if (joyMIDI[group].btnShift === RELEASED) {
+ script.toggleControl(group2, param);
+ }
+ }
+ joyMIDI[group].btnFx[select] = RELEASED;
+ }
+ }
+}
+
+//==== Wheel ============================================================
+joyMIDI.wheel = function(channel, control, value, status, group) {
+ if (joyMIDI[group].btnSync >= PRESSED) {
+ joyMIDI.wheelSync(channel, control, value, status, group);
+ joyMIDI[group].btnSync = ADJUSTED;
+ } else if (joyMIDI[group].btnKey >= PRESSED) {
+ joyMIDI.wheelKey(channel, control, value, status, group);
+ joyMIDI[group].btnKey = ADJUSTED;
+ } else if (joyMIDI[group].btnBeatgrid >= PRESSED) {
+ joyMIDI.wheelBeatgrid(channel, control, value, status, group);
+ joyMIDI[group].btnBeatgrid = ADJUSTED;
+ } else if (joyMIDI[group].btnVolume >= PRESSED) {
+ joyMIDI.wheelVolume(channel, control, value, status, group);
+ joyMIDI[group].btnVolume = ADJUSTED;
+
+ } else if (joyMIDI[group].btnFilter["low"] >= PRESSED) {
+ joyMIDI.wheelFilterLow(channel, control, value, status, group);
+ joyMIDI[group].btnFilter["low"] = ADJUSTED;
+ } else if (joyMIDI[group].btnFilter["middle"] >= PRESSED) {
+ joyMIDI.wheelFilterMiddle(channel, control, value, status, group);
+ joyMIDI[group].btnFilter["middle"] = ADJUSTED;
+ } else if (joyMIDI[group].btnFilter["high"] >= PRESSED) {
+ joyMIDI.wheelFilterHigh(channel, control, value, status, group);
+ joyMIDI[group].btnFilter["high"] = ADJUSTED;
+
+ } else if (joyMIDI[group].btnFx["1"] >= PRESSED) {
+ joyMIDI.wheelFx1(channel, control, value, status, group);
+ joyMIDI[group].btnFx["1"] = ADJUSTED;
+ } else if (joyMIDI[group].btnFx["2"] >= PRESSED) {
+ joyMIDI.wheelFx2(channel, control, value, status, group);
+ joyMIDI[group].btnFx["2"] = ADJUSTED;
+ } else if (joyMIDI[group].btnFx["3"] >= PRESSED) {
+ joyMIDI.wheelFx3(channel, control, value, status, group);
+ joyMIDI[group].btnFx["3"] = ADJUSTED;
+
+ } else if (joyMIDI[group].isScratchPermanent) {
+ joyMIDI.wheelScratch(channel, control, value, status, group);
+ } else {
+ joyMIDI.wheelPitchBend(channel, control, value, status, group);
+ }
+}
+
+joyMIDI.wheelPitchBend = function(channel, control, value, status, group) {
+ var newValue = joyMIDI.helperAccel(value - 64);
+
+ engine.setValue(group, 'jog', newValue);
+ print(group + "jog=" + newValue);
+}
+
+joyMIDI.wheelScratch = function(channel, control, value, status, group) {
+ var deck = script.deckFromGroup(group);
+ var newValue = joyMIDI.helperAccel(value - 64);
+
+ if (newValue > 0) newValue = newValue + forwardFinetune;
+ else newValue = newValue - backwardFinetune;
+
+ if (joyMIDI[group].scratchTimerID) {
+ engine.stopTimer(joyMIDI[group].scratchTimerID);
+ }
+
+ // if (!engine.isScratching(deck)) {
+ engine.scratchEnable(deck, intervalPerRev, rpm, alpha, beta);
+ // }
+
+ engine.scratchTick(deck, newValue);
+
+ joyMIDI[group].scratchTimerID =
+ engine.beginTimer(scratchDisableTime, "joyMIDI.ScratchTimerHandler('" +deck+ "','" +group+ "')", true);
+}
+
+joyMIDI.ScratchTimerHandler = function(deck, group){
+ engine.scratchDisable(deck);
+ joyMIDI[group].scratchTimerID = 0;
+}
+
+joyMIDI.wheelSync = function(channel, control, value, status, group) {
+ var delta = joyMIDI.helperAccel(value - 64) * 0.01;
+ var oldValue = engine.getValue(group, "rate");
+ var newValue = oldValue + delta;
+
+ newValue = joyMIDI.helperLimit(newValue, 1.0, -1.0);
+ engine.setValue(group, "rate", newValue);
+ print(group + "rate=" + newValue);
+}
+
+joyMIDI.wheelKey = function(channel, control, value, status, group) {
+ var delta = /*joyMIDI.helperAccel*/(value - 64) * 0.2;
+ var oldValue = engine.getValue(group, "pitch");
+ var newValue = oldValue + delta;
+
+ newValue = joyMIDI.helperLimit(newValue, 6.0, -6.0);
+ engine.setValue(group, "pitch", newValue);
+ print(group + "pitch=" + newValue);
+}
+
+joyMIDI.wheelBeatgrid = function(channel, control, value, status, group) {
+ var delta = joyMIDI.helperAccel(value - 64);
+ var count = Math.abs(delta);
+
+ for (var i=0; i 0) {
+ engine.setValue(group, "beats_translate_earlier", 1);
+ print(group + "beats_translate_earlier");
+ } else {
+ engine.setValue(group, "beats_translate_later", 1);
+ print(group + "beats_translate_later");
+ }
+ }
+}
+
+joyMIDI.wheelVolume = function(channel, control, value, status, group) {
+ var delta = joyMIDI.helperAccel(value - 64) * 0.01;
+ var oldValue = engine.getValue(group, "volume");
+ var newValue = oldValue + delta;
+
+ newValue = joyMIDI.helperLimit(newValue, 1.0, 0);
+ engine.setValue(group, "volume", newValue);
+ print(group + "volume=" + newValue);
+}
+
+joyMIDI.wheelFilterLow = function(channel, control, value, status, group) {
+ joyMIDI.wheelFilter(channel, control, value, status, group,
+ "[EqualizerRack1_" + group + "_Effect1]", "parameter1", "low");
+}
+
+joyMIDI.wheelFilterMiddle = function(channel, control, value, status, group) {
+ joyMIDI.wheelFilter(channel, control, value, status, group,
+ "[EqualizerRack1_" + group + "_Effect1]", "parameter2", "middle");
+}
+
+joyMIDI.wheelFilterHigh = function(channel, control, value, status, group) {
+ joyMIDI.wheelFilter(channel, control, value, status, group,
+ "[EqualizerRack1_" + group + "_Effect1]", "parameter3", "high");
+}
+
+joyMIDI.wheelFilter = function(channel, control, value, status, group, group2, param, select) {
+ var delta = joyMIDI.helperAccel(value - 64) * 0.01;
+ var oldValue = engine.getParameter(group2, param);
+ var newValue = oldValue + delta;
+
+ newValue = joyMIDI.helperLimit(newValue, 1.0, 0);
+ engine.setParameter(group2, param, newValue);
+ print(group + "EQ_" + select + "=" + newValue);
+}
+
+joyMIDI.wheelFx1 = function(channel, control, value, status, group) {
+ if (group === "[Channel1]") {
+ joyMIDI.wheelFx(channel, control, value, status, group,
+ "[EffectRack1_EffectUnit1]", "mix", 1);
+ } else {
+ joyMIDI.wheelFx(channel, control, value, status, group,
+ "[EffectRack1_EffectUnit2]", "mix", 1);
+ }
+}
+
+joyMIDI.wheelFx2 = function(channel, control, value, status, group) {
+ if (group === "[Channel1]") {
+ joyMIDI.wheelFx(channel, control, value, status, group,
+ "[EffectRack1_EffectUnit1]", "super1", 2);
+ } else {
+ joyMIDI.wheelFx(channel, control, value, status, group,
+ "[EffectRack1_EffectUnit2]", "super1", 2);
+ }
+}
+
+joyMIDI.wheelFx = function(channel, control, value, status, group, group2, param, select) {
+ var delta = joyMIDI.helperAccel(value - 64) * 0.01;
+ var oldValue = engine.getParameter(group2, param);
+ var newValue = oldValue + delta;
+
+ newValue = joyMIDI.helperLimit(newValue, 1.0, 0);
+ engine.setParameter(group2, param, newValue);
+}
+
+
+//==== Jojstick ============================================================
+joyMIDI.joystick = function(channel, control, value, status, group) {
+
+ if (joystickDelay == false) {
+ /* Delay a while */
+ joystickDelay = true;
+ engine.beginTimer(300, function() { joystickDelay = false; }, true);
+
+ var isShift = joyMIDI['[Channel1]'].btnShift == true || joyMIDI['[Channel2]'].btnShift == true;
+ if (!joyMIDI['[Channel1]'].btnShift) {
+ /* Joystick only */
+ if (control == 0x10) {
+ engine.setValue("[Library]", "MoveLeft", true);
+ } else if (control == 0x11) {
+ engine.setValue("[Library]", "MoveRight", true);
+ } else if (control == 0x12) {
+ engine.setValue("[Library]", "MoveDown", true);
+ } else if (control == 0x13) {
+ engine.setValue("[Library]", "MoveUp", true);
+ }
+ } else {
+ /* Joystick + shift */
+ if (control == 0x10) {
+ // Do nothing
+ } else if (control == 0x11) {
+ // Do nothing
+ } else if (control == 0x12) {
+ engine.setValue("[Library]", "ScrollDown", true);
+ } else if (control == 0x13) {
+ engine.setValue("[Library]", "ScrollUp", true);
+ }
+ }
+ }
+}
+
+
+//==== FSR ============================================================
+joyMIDI.fsr = function(channel, control, value, status, group) {
+
+ if (fsrDelay == false) {
+ /* Delay a while */
+ fsrDelay = true;
+ engine.beginTimer(300, function() { fsrDelay = false; }, true);
+
+ var isShift = joyMIDI['[Channel1]'].btnShift == true || joyMIDI['[Channel2]'].btnShift == true;
+ if (! isShift) {
+ /* FSR only */
+ engine.setValue("[Library]", "MoveFocusForward", true);
+ } else {
+ /* FSR + shift */
+ engine.setValue("[Library]", "MoveFocusBackward", true);
+ }
+ }
+}
+
+//==== Helper =============================================================
+joyMIDI.helperLimit = function(input, max, min) {
+ input = input > max ? max : input;
+ input = input < min ? min : input;
+ return input;
+}
+
+joyMIDI.helperAccel = function(input) {
+ var acc = 0;
+ if (enableAccVal == true) {
+ acc = Math.pow(Math.abs(input), 2);
+ acc = (input > 0) ? acc : -acc;
+ } else {
+ acc = input;
+ }
+ return acc;
+}