From 65e2f06e099cd74aabd9c888e9d67b7f6af96f74 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 2 Oct 2019 02:51:16 +0200 Subject: [PATCH 01/26] Traktor Kontrol S2MK3: Initial mapping script --- .../Traktor Kontrol S2 MK3.hid.xml | 19 + .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 759 ++++++++++++++++++ 2 files changed, 778 insertions(+) create mode 100644 res/controllers/Traktor Kontrol S2 MK3.hid.xml create mode 100644 res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js diff --git a/res/controllers/Traktor Kontrol S2 MK3.hid.xml b/res/controllers/Traktor Kontrol S2 MK3.hid.xml new file mode 100644 index 00000000000..b9985996d97 --- /dev/null +++ b/res/controllers/Traktor Kontrol S2 MK3.hid.xml @@ -0,0 +1,19 @@ + + + + Traktor Kontrol S2 MK3 + Michael Schmidt + HID Mapping for Traktor Kontrol S2 MK3 + http://mixxx.org/wiki/doku.php/traktor_s2_mk3 + + + + + + + + + + + + diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js new file mode 100644 index 00000000000..cadeb9f0b8d --- /dev/null +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -0,0 +1,759 @@ +/**************************************************** + Traktor Kontrol S2 MK3 HID controller script v1.00 + + USB HID descriptors (only usage 0x1 and 0x80 is used) + $> usbhid-dump -d17cc:1710 | grep -v : | xxd -r -p | hidrd-convert -o spec + + Usage Page (FF01h), ; FF01h, vendor-defined + Usage (00h), + Collection (Application), + Usage (01h), + Collection (Logical), + Report ID (1), + Usage (02h), + Logical Minimum (0), + Logical Maximum (1), + Report Size (1), + Report Count (64), + Input (Variable), + Usage (03h), + Logical Minimum (0), + Logical Maximum (15), + Report Size (4), + Report Count (6), + Input (Variable), + Usage (31h), + Logical Minimum (0), + Logical Maximum (65535), + Report Size (16), + Report Count (4), + Input (Variable), + End Collection, + Usage (01h), + Collection (Logical), + Report ID (2), + Usage (05h), + Logical Minimum (0), + Logical Maximum (4095), + Report Size (16), + Report Count (5), + Input (Variable), + Usage (04h), + Logical Minimum (0), + Logical Maximum (4095), + Report Size (16), + Report Count (14), + Input (Variable), + End Collection, + Usage (80h), + Collection (Logical), + Report ID (128), + Usage (81h), + Logical Minimum (0), + Logical Maximum (127), + Report Size (8), + Report Count (61), + Output (Variable), + End Collection, +End Collection + +/****************************************************/ + +var TraktorS2MK3 = {}; + +TraktorS2MK3 = new function () { + this.controller = new HIDController(); + this.shiftPressed = { "[Channel1]": false, "[Channel2]": false }; + this.browseState = { "[Channel1]": 0, "[Channel2]": 0 }; + this.loopSizeState = { "[Channel1]": 0, "[Channel2]": 0 }; + this.beatjumpState = { "[Channel1]": 0, "[Channel2]": 0 }; + this.fxButtonState = { 1: false, 2: false, 3: false, 4: false }; + this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode + this.quantizeState = 0; // 0 = Off, 1 = On + + // Jog wheels + this.last_tick_val = [0, 0]; + this.last_tick_time = [0.0, 0.0]; +} + +TraktorS2MK3.init = function (id) { + TraktorS2MK3.registerInputPackets(); + TraktorS2MK3.registerOutputPackets(); + HIDDebug("TraktorS2MK3: Init done!"); + + // Output package set only the LEDs in the controller + // Each byte representing the state of one LED + var data_string = "00 00 00 \n" + // Rev Left, FLX Left, Preparation Left + "00 00 00 00 \n" + // Browse View Left, Grid Left, Shift Left, Hotcues Left + "00 00 00 00 \n" + // Samples Left, Sync Left, Keylock Left, Cue Left + "00 00 00 00 \n" + // Play Left, Hotcue1 Left, Hotcue2 Left, Hotcue3 Left + "00 00 00 00 \n" + // Hotcue4 Left, Hotcue5 Left, Hotcue6 Left, Hotcue7 Left + "00 00 00 00 \n" + // Hotcue8 Left, Sample Mixer LED, FX Select1, FX Select2 + "00 00 00 00 \n" + // FX Select3, FX Select4, Phones Left, Phones Right + "00 00 00 00 \n" + // Level Channel A -18, -12, -6, 0 + "00 00 00 00 \n" + // Level Channel A 6, Level Channel B -18, -12 + "00 00 00 00 \n" + // Level Channel B -6, 0, 6, Clip + "00 00 00 00 \n" + // Rev Right, FLX Right, Preparation Right, Browse View Right + "00 00 00 00 \n" + // Grid Right, Shift Right, Hotcues Right, Samples Right + "00 00 00 00 \n" + // Sync Right, Keylock Right, Cue Right, Play Right + "00 00 00 00 \n" + // Hotcue1 Right, Hotcue2 Right, Hotcue3 Right, Hotcue4 Right + "00 00 00 00 \n" + // Hotcue5 Right, Hotcue6 Right, Hotcue7 Right, Hotcue8 Right + "00 00"; // GNT, MIC + // this.rawOutput(data_string); +} + +TraktorS2MK3.registerInputPackets = function () { + var messageShort = new HIDPacket("shortmessage", 0x01, this.messageCallback); + var messageLong = new HIDPacket("longmessage", 0x02, this.messageCallback); + + /* + Offset 0x01, Bitmasks: + - 0x01 Rev Left - 0x02 FLX Left - 0x04 Prepation Left - 0x08 Browse View Left + - 0x10 Grid Left - 0x20 Shift Left - 0x40 Hotcues Left - 0x80 Samples Left + Offset 0x02, Bitmasks: + - 0x01 Sync Left - 0x02 Keylock Left - 0x04 Cue Left - 0x08 Play Left + - 0x10 Hotcue1 Left - 0x20 Hotcue2 Left - 0x40 Hotcue3 Left - 0x80 Hotcue4 Left + Offset 0x03, Bitmasks: + - 0x01 Hotcue5 Left - 0x02 Hotcue6 Left - 0x04 Hotcue7 Left - 0x08 Hotcue8 Left + - 0x10 FX Select1 - 0x20 FX Select2 - 0x40 FX Select3 - 0x80 FX Select4 + Offset 0x04, Bitmasks: + - 0x01 Phones Left - 0x02 Phones Right - 0x04 Rev Right - 0x08 FLX Right + - 0x10 Prepation Right - 0x20 Browse View Right - 0x40 Grid Right - 0x80 Shift Right + Offset 0x05, Bitmasks: + - 0x01 Hotcues Right - 0x02 Samples Right - 0x04 Sync Right - 0x08 Keylock Right + - 0x10 Cue Right - 0x20 Play Right - 0x40 Hotcue1 Right - 0x80 Hotcue2 Right + Offset 0x06, Bitmasks: + - 0x01 Hotcue3 Right - 0x02 Hotcue4 Right - 0x04 Hotcue5 Right - 0x08 Hotcue6 Right + - 0x10 Hotcue7 Right - 0x20 Hotcue8 Right - 0x40 GNT - 0x80 MIC + Offset 0x07, Bitmasks: + - 0x01 Browse Left Click - 0x02 Move Left Click - 0x04 Loop Left Click - 0x08 Browse Right Click + - 0x10 Move Right Click - 0x20 Loop Right Click - 0x40 ??? - 0x80 ??? + Offset 0x08, Bitmasks: + - 0x01 Browse Left Touch - 0x02 Move Left Touch - 0x04 Loop Left Touch - 0x08 Browse Right Touch + - 0x10 Move Right Touch - 0x20 Loop Right Touch - 0x40 Jog Wheel Left Touch - 0x80 Jog Wheel Right Touch + Offset 0x09, Bitmasks: + - Lowest nibble: Browse Left + - Highest nibble: Move Left + Offset 0x0A, Bitmasks: + - Lowest nibble: Loop Left + - Highest nibble: Browse Right + Offset 0x0B, Bitmasks: + - Lowest nibble: Move Right + - Highest nibble: Loop Right + Offset 0x0C - 0x0F: Jog Wheel Left + Offset 0x10 - 0x13: Jog Wheel Right + */ + + this.registerInputButton(messageShort, "[Channel1]", "!play", 0x02, 0x08, this.playHandler); + this.registerInputButton(messageShort, "[Channel2]", "!play", 0x05, 0x20, this.playHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!cue_default", 0x02, 0x04, this.cueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!cue_default", 0x05, 0x10, this.cueHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!shift", 0x01, 0x20, this.shiftHandler); + this.registerInputButton(messageShort, "[Channel2]", "!shift", 0x04, 0x80, this.shiftHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!sync", 0x02, 0x01, this.syncHandler); + this.registerInputButton(messageShort, "[Channel2]", "!sync", 0x05, 0x04, this.syncHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!keylock", 0x02, 0x02, this.keylockHandler); + this.registerInputButton(messageShort, "[Channel2]", "!keylock", 0x05, 0x08, this.keylockHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!hotcues", 0x01, 0x40, this.padModeHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcues", 0x05, 0x01, this.padModeHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!samples", 0x01, 0x80, this.padModeHandler); + this.registerInputButton(messageShort, "[Channel2]", "!samples", 0x05, 0x02, this.padModeHandler); + + // Hotcues + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_1", 0x02, 0x10, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_2", 0x02, 0x20, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_3", 0x02, 0x40, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_4", 0x02, 0x80, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_5", 0x03, 0x01, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_6", 0x03, 0x02, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_7", 0x03, 0x04, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_8", 0x03, 0x08, this.hotcueHandler); + + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_1", 0x05, 0x40, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_2", 0x05, 0x80, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_3", 0x06, 0x01, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_4", 0x06, 0x02, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_5", 0x06, 0x04, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_6", 0x06, 0x08, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_7", 0x06, 0x10, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_8", 0x06, 0x20, this.hotcueHandler); + + // Headphone buttons + this.registerInputButton(messageShort, "[Channel1]", "!pfl", 0x04, 0x01, this.headphoneHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pfl", 0x04, 0x02, this.headphoneHandler); + + // Track browsing + this.registerInputButton(messageShort, "[Channel1]", "!SelectTrack", 0x09, 0x0F, this.selectTrackHandler); + this.registerInputButton(messageShort, "[Channel2]", "!SelectTrack", 0x0A, 0xF0, this.selectTrackHandler); + this.registerInputButton(messageShort, "[Channel1]", "!LoadSelectedTrack", 0x07, 0x01, this.loadTrackHandler); + this.registerInputButton(messageShort, "[Channel2]", "!LoadSelectedTrack", 0x07, 0x08, this.loadTrackHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!MaximizeLibrary", 0x01, 0x08, this.maximizeLibraryHandler); + this.registerInputButton(messageShort, "[Channel2]", "!MaximizeLibrary", 0x04, 0x20, this.maximizeLibraryHandler); + + // Loop control + this.registerInputButton(messageShort, "[Channel1]", "!SelectLoop", 0x0A, 0x0F, this.selectLoopHandler); + this.registerInputButton(messageShort, "[Channel2]", "!SelectLoop", 0x0B, 0xF0, this.selectLoopHandler); + this.registerInputButton(messageShort, "[Channel1]", "!LoadSelectedTrack", 0x07, 0x04, this.activateLoopHandler); + this.registerInputButton(messageShort, "[Channel2]", "!LoadSelectedTrack", 0x07, 0x20, this.activateLoopHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!beatjump", 0x09, 0xF0, this.beatjumpHandler); + this.registerInputButton(messageShort, "[Channel2]", "!beatjump", 0x0B, 0x0F, this.beatjumpHandler); + + // There is only one button on the controller, we use to toggle quantization for all channels + this.registerInputButton(messageShort, "[ChannelX]", "!quantize", 0x06, 0x40, this.quantizeHandler); + + // Jog wheels + this.registerInputButton(messageShort, "[Channel1]", "!jog_touch", 0x08, 0x40, this.jogTouchHandler); + this.registerInputButton(messageShort, "[Channel2]", "!jog_touch", 0x08, 0x80, this.jogTouchHandler); + this.registerInputJog(messageShort, "[Channel1]", "!jog", 0x0C, 0xFFFFFFFF, this.jogHandler); + this.registerInputJog(messageShort, "[Channel2]", "!jog", 0x10, 0xFFFFFFFF, this.jogHandler); + + // FX Buttons + this.registerInputButton(messageShort, "[ChannelX]", "!fx1", 0x03, 0x10, this.fxHandler); + this.registerInputButton(messageShort, "[ChannelX]", "!fx2", 0x03, 0x20, this.fxHandler); + this.registerInputButton(messageShort, "[ChannelX]", "!fx3", 0x03, 0x40, this.fxHandler); + this.registerInputButton(messageShort, "[ChannelX]", "!fx4", 0x03, 0x80, this.fxHandler); + + /////////////////////////////////// + // TODO: Sampler button and control + /////////////////////////////////// + + this.controller.registerInputPacket(messageShort); + + this.registerInputScaler(messageLong, "[Channel1]", "rate", 0x01, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Channel2]", "rate", 0x09, 0xFFFF, this.parameterHandler); + + this.registerInputScaler(messageLong, "[Channel1]", "volume", 0x03, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Channel2]", "volume", 0x07, 0xFFFF, this.parameterHandler); + + this.registerInputScaler(messageLong, "[Channel1]", "pregain", 0x0B, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Channel2]", "pregain", 0x1D, 0xFFFF, this.parameterHandler); + + this.registerInputScaler(messageLong, "[EqualizerRack1_[Channel1]_Effect1]", "parameter3", 0x0D, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[EqualizerRack1_[Channel1]_Effect1]", "parameter2", 0x0F, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[EqualizerRack1_[Channel1]_Effect1]", "parameter1", 0x11, 0xFFFF, this.parameterHandler); + + this.registerInputScaler(messageLong, "[EqualizerRack1_[Channel2]_Effect1]", "parameter3", 0x1F, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[EqualizerRack1_[Channel2]_Effect1]", "parameter2", 0x21, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[EqualizerRack1_[Channel2]_Effect1]", "parameter1", 0x23, 0xFFFF, this.parameterHandler); + + this.registerInputScaler(messageLong, "[QuickEffectRack1_[Channel1]]", "super1", 0x13, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[QuickEffectRack1_[Channel2]]", "super1", 0x25, 0xFFFF, this.parameterHandler); + + this.registerInputScaler(messageLong, "[Master]", "crossfader", 0x05, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Master]", "gain", 0x15, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Master]", "headMix", 0x19, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Master]", "headGain", 0x1B, 0xFFFF, this.parameterHandler); + + this.controller.registerInputPacket(messageLong); + + // Soft takeover for all knobs + engine.softTakeover("[Channel1]", "rate", true); + engine.softTakeover("[Channel2]", "rate", true); + + engine.softTakeover("[Channel1]", "volume", true); + engine.softTakeover("[Channel2]", "volume", true); + + engine.softTakeover("[Channel1]", "pregain", true); + engine.softTakeover("[Channel2]", "pregain", true); + + engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter3", true); + engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter2", true); + engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter1", true); + + engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter3", true); + engine.softTakeover("[EqualizerRack1_[Channel2]_Effect1]", "parameter2", true); + engine.softTakeover("[EqualizerRack1_[Channel3]_Effect1]", "parameter1", true); + + engine.softTakeover("[QuickEffectRack1_[Channel1]]", "super1", true); + engine.softTakeover("[QuickEffectRack1_[Channel2]]", "super1", true); + + engine.softTakeover("[Master]", "crossfader", true); + engine.softTakeover("[Master]", "gain", true); + engine.softTakeover("[Master]", "headMix", true); + engine.softTakeover("[Master]", "headGain", true); + + // Dirty hack to set initial values in the packet parser + var data = TraktorS2MK3.toBytes("01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); + TraktorS2MK3.incomingData(data); +} + +TraktorS2MK3.registerInputJog = function (message, group, name, offset, bitmask, callback) { + message.addControl(group, name, offset, "I", bitmask); + message.setCallback(group, name, callback); +} + +TraktorS2MK3.registerInputScaler = function (message, group, name, offset, bitmask, callback) { + message.addControl(group, name, offset, "H", bitmask); + message.setCallback(group, name, callback); +} + +TraktorS2MK3.registerInputButton = function (message, group, name, offset, bitmask, callback) { + message.addControl(group, name, offset, "B", bitmask); + message.setCallback(group, name, callback); +} + +TraktorS2MK3.playHandler = function (field) { + if (field.value === 0) { + return; + } + + var playing = engine.getValue(field.group, "play"); + engine.setValue(field.group, "play", !playing); +} + +TraktorS2MK3.shiftHandler = function (field) { + TraktorS2MK3.shiftPressed[field.group] = field.value; + var playing = engine.setValue("[Controls]", "touch_shift", field.value); + TraktorS2MK3.outputHandler(field.value, field.group, "shift"); +} + +TraktorS2MK3.keylockHandler = function (field) { + if (field.value === 0) { + return; + } + + var keylock = engine.getValue(field.group, "keylock"); + engine.setValue(field.group, "keylock", !keylock); +} + +TraktorS2MK3.padModeHandler = function (field) { + if (field.value === 0) { + return; + } + + // If we are in hotcues mode and samples mode is activated + if (TraktorS2MK3.padModeState[field.group] === 0 && field.name === "!samples") { + HIDDebug("Samples"); + TraktorS2MK3.padModeState[field.group] = 1; + TraktorS2MK3.outputHandler(!field.value, field.group, "hotcues"); + TraktorS2MK3.outputHandler(field.value, field.group, "samples"); + // TODO: Change number buttons + } + // If we are in samples mode and hotcues mode is activated + else if (field.name === "!hotcues") { + TraktorS2MK3.padModeState[field.group] = 0; + TraktorS2MK3.outputHandler(field.value, field.group, "hotcues"); + TraktorS2MK3.outputHandler(!field.value, field.group, "samples"); + // TODO: Change number buttons + } +} + +TraktorS2MK3.syncHandler = function (field) { + if (field.value === 0) { + return; + } + + var sync = engine.getValue(field.group, "sync_enabled"); + engine.setValue(field.group, "sync_enabled", !sync); +} + +TraktorS2MK3.cueHandler = function (field) { + if (field.value === 0) { + return; + } + + engine.setValue(field.group, "cue_default", field.value); +} + +TraktorS2MK3.hotcueHandler = function (field) { + if (field.value === 0) { + return; + } + + var hotcueNumber = parseInt(field.id[field.id.length - 1]); + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "hotcue_" + hotcueNumber + "_clear", field.value); + } else { + engine.setValue(field.group, "hotcue_" + hotcueNumber + "_activate", field.value); + } +} + +TraktorS2MK3.headphoneHandler = function (field) { + if (field.value === 0) { + return; + } + + var pfl = engine.getValue(field.group, "pfl"); + engine.setValue(field.group, "pfl", !pfl); +} + +TraktorS2MK3.selectTrackHandler = function (field) { + if ((field.value + 1) % 16 == TraktorS2MK3.browseState[field.group]) { + engine.setValue("[Library]", "MoveUp", 1); + } + else { + engine.setValue("[Library]", "MoveDown", 1); + } + TraktorS2MK3.browseState[field.group] = field.value; +} + +TraktorS2MK3.loadTrackHandler = function (field) { + if (field.value === 0) { + return; + } + + engine.setValue(field.group, "LoadSelectedTrack", field.value); +} + +TraktorS2MK3.maximizeLibraryHandler = function (field) { + if (field.value === 0) { + return; + } + + var maximize = engine.getValue("[Master]", "maximize_library"); + engine.setValue("[Master]", "maximize_library", !maximize); +} + +TraktorS2MK3.selectLoopHandler = function (field) { + if ((field.value + 1) % 16 == TraktorS2MK3.loopSizeState[field.group]) { + engine.setValue(field.group, "loop_halve", 1); + + } + else { + engine.setValue(field.group, "loop_double", 1); + } + TraktorS2MK3.loopSizeState[field.group] = field.value; +} + +TraktorS2MK3.activateLoopHandler = function (field) { + if (field.value === 0) { + return; + } + + engine.setValue(field.group, "beatloop_activate", field.value); +} + +TraktorS2MK3.beatjumpHandler = function (field) { + if ((field.value + 1) % 16 == TraktorS2MK3.beatjumpState[field.group]) { + engine.setValue(field.group, "beatjump_backward", 1); + + } + else { + engine.setValue(field.group, "beatjump_forward", 1); + } + TraktorS2MK3.beatjumpState[field.group] = field.value; +} + +TraktorS2MK3.quantizeHandler = function (field) { + if (field.value === 0) { + return; + } + + this.quantizeState = !this.quantizeState; + engine.setValue("[Channel1]", "quantize", this.quantizeState); + engine.setValue("[Channel2]", "quantize", this.quantizeState); + TraktorS2MK3.outputHandler(this.quantizeState, field.group, "quantize"); +} + +TraktorS2MK3.parameterHandler = function (field) { + engine.setParameter(field.group, field.name, field.value / 4095); +} + +TraktorS2MK3.jogTouchHandler = function (field) { + var deckNumber = TraktorS2MK3.controller.resolveDeck(group); + if (field.value > 0) { + engine.scratchEnable(deckNumber, 1024, 33.3333, 0.125, 0.125 / 8, true); + } + else { + engine.scratchDisable(deckNumber); + } +} + +TraktorS2MK3.jogHandler = function (field) { + var deckNumber = TraktorS2MK3.controller.resolveDeck(group); + + // Jog wheel control is based on the S4MK2 mapping, might need some more review + if (engine.isScratching(deckNumber)) { + var deltas = TraktorS2MK3.wheelDeltas(field.group, field.value); + var tick_delta = deltas[0]; + var time_delta = deltas[1]; + + var velocity = (tick_delta / time_delta) / 3; + engine.setValue(field.group, "jog", velocity); + if (engine.getValue(field.group, "scratch2_enable")) { + engine.scratchTick(deckNumber, tick_delta); + } + } +} + +TraktorS2MK3.wheelDeltas = function (group, value) { + // When the wheel is touched, four bytes change, but only the first behaves predictably. + // It looks like the wheel is 1024 ticks per revolution. + var tickval = value & 0xFF; + var timeval = value >>> 16; + var prev_tick = 0; + var prev_time = 0; + + if (group[8] === "1" || group[8] === "3") { + prev_tick = this.last_tick_val[0]; + prev_time = this.last_tick_time[0]; + this.last_tick_val[0] = tickval; + this.last_tick_time[0] = timeval; + } else { + prev_tick = this.last_tick_val[1]; + prev_time = this.last_tick_time[1]; + this.last_tick_val[1] = tickval; + this.last_tick_time[1] = timeval; + } + + if (prev_time > timeval) { + // We looped around. Adjust current time so that subtraction works. + timeval += 0x10000; + } + var time_delta = timeval - prev_time; + if (time_delta === 0) { + // Spinning too fast to detect speed! By not dividing we are guessing it took 1ms. + time_delta = 1; + } + + var tick_delta = 0; + if (prev_tick >= 200 && tickval <= 100) { + tick_delta = tickval + 256 - prev_tick; + } else if (prev_tick <= 100 && tickval >= 200) { + tick_delta = tickval - prev_tick - 256; + } else { + tick_delta = tickval - prev_tick; + } + + return [tick_delta, time_delta]; +} + +TraktorS2MK3.fxHandler = function (field) { + if (field.value === 0) { + return; + } + + var fxNumber = parseInt(field.id[field.id.length - 1]); + var group = "[EffectRack1_EffectUnit" + fxNumber + "]"; + + // Toggle effect unit + TraktorS2MK3.fxButtonState[fxNumber] = !TraktorS2MK3.fxButtonState[fxNumber]; + + engine.setValue(group, "group_[Channel1]_enable", TraktorS2MK3.fxButtonState[fxNumber]); + engine.setValue(group, "group_[Channel2]_enable", TraktorS2MK3.fxButtonState[fxNumber]); + TraktorS2MK3.outputHandler(TraktorS2MK3.fxButtonState[fxNumber], field.group, "fxButton" + fxNumber); +} + +TraktorS2MK3.registerOutputPackets = function () { + var output = new HIDPacket("output", 0x80); + + output.addOutput("[Channel1]", "play_indicator", 0x0C, "B"); + output.addOutput("[Channel2]", "play_indicator", 0x33, "B"); + + output.addOutput("[Channel1]", "cue_indicator", 0x0B, "B"); + output.addOutput("[Channel2]", "cue_indicator", 0x32, "B"); + + output.addOutput("[Channel1]", "shift", 0x06, "B"); + output.addOutput("[Channel2]", "shift", 0x2D, "B"); + + output.addOutput("[Channel1]", "hotcues", 0x07, "B"); + output.addOutput("[Channel2]", "hotcues", 0x2E, "B"); + + output.addOutput("[Channel1]", "samples", 0x08, "B"); + output.addOutput("[Channel2]", "samples", 0x2F, "B"); + + output.addOutput("[Channel1]", "sync_enabled", 0x09, "B"); + output.addOutput("[Channel2]", "sync_enabled", 0x30, "B"); + + output.addOutput("[Channel1]", "keylock", 0x0A, "B"); + output.addOutput("[Channel2]", "keylock", 0x31, "B"); + + output.addOutput("[Channel1]", "hotcue_1_enabled", 0x0D, "B"); + output.addOutput("[Channel1]", "hotcue_2_enabled", 0x0E, "B"); + output.addOutput("[Channel1]", "hotcue_3_enabled", 0x0F, "B"); + output.addOutput("[Channel1]", "hotcue_4_enabled", 0x10, "B"); + output.addOutput("[Channel1]", "hotcue_5_enabled", 0x11, "B"); + output.addOutput("[Channel1]", "hotcue_6_enabled", 0x12, "B"); + output.addOutput("[Channel1]", "hotcue_7_enabled", 0x13, "B"); + output.addOutput("[Channel1]", "hotcue_8_enabled", 0x14, "B"); + + output.addOutput("[Channel2]", "hotcue_1_enabled", 0x34, "B"); + output.addOutput("[Channel2]", "hotcue_2_enabled", 0x35, "B"); + output.addOutput("[Channel2]", "hotcue_3_enabled", 0x36, "B"); + output.addOutput("[Channel2]", "hotcue_4_enabled", 0x37, "B"); + output.addOutput("[Channel2]", "hotcue_5_enabled", 0x38, "B"); + output.addOutput("[Channel2]", "hotcue_6_enabled", 0x39, "B"); + output.addOutput("[Channel2]", "hotcue_7_enabled", 0x3A, "B"); + output.addOutput("[Channel2]", "hotcue_8_enabled", 0x3B, "B"); + + output.addOutput("[Channel1]", "pfl", 0x1A, "B"); + output.addOutput("[Channel2]", "pfl", 0x1B, "B"); + + output.addOutput("[ChannelX]", "fxButton1", 0x16, "B"); + output.addOutput("[ChannelX]", "fxButton2", 0x17, "B"); + output.addOutput("[ChannelX]", "fxButton3", 0x18, "B"); + output.addOutput("[ChannelX]", "fxButton4", 0x19, "B"); + + output.addOutput("[Channel1]", "MaximizeLibrary", 0x04, "B"); + output.addOutput("[Channel2]", "MaximizeLibrary", 0x2B, "B"); + + output.addOutput("[ChannelX]", "quantize", 0x3C, "B"); + + this.controller.registerOutputPacket(output); + + this.linkOutput("[Channel1]", "play_indicator", this.outputHandler); + this.linkOutput("[Channel2]", "play_indicator", this.outputHandler); + + this.linkOutput("[Channel1]", "cue_indicator", this.outputHandler); + this.linkOutput("[Channel2]", "cue_indicator", this.outputHandler); + + this.linkOutput("[Channel1]", "sync_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "sync_enabled", this.outputHandler); + + this.linkOutput("[Channel1]", "keylock", this.outputHandler); + this.linkOutput("[Channel2]", "keylock", this.outputHandler); + + this.linkOutput("[Channel1]", "hotcue_1_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_2_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_3_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_4_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_5_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_6_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_7_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_8_enabled", this.outputHandler); + + this.linkOutput("[Channel2]", "hotcue_1_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_2_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_3_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_4_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_5_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_6_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_7_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "hotcue_8_enabled", this.outputHandler); + + this.linkOutput("[Channel1]", "pfl", this.outputHandler); + this.linkOutput("[Channel2]", "pfl", this.outputHandler); + + TraktorS2MK3.lightDeck(); +} + +/* Helper function to link output in a short form */ +TraktorS2MK3.linkOutput = function (group, name, callback) { + TraktorS2MK3.controller.linkOutput(group, name, group, name, callback); +} + +TraktorS2MK3.outputHandler = function (value, group, key) { + var led_value = 0x7C; + if (value) { + led_value = 0x7E; + } + + TraktorS2MK3.controller.setOutput(group, key, led_value, true); +} + +TraktorS2MK3.lightDeck = function () { + + TraktorS2MK3.controller.setOutput("[Channel1]", "play_indicator", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "play_indicator", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "cue_indicator", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "cue_indicator", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "shift", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "shift", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "sync_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "sync_enabled", 0x7C, false); + + // Hotcues mode is default start value + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcues", 0x7E, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "samples", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcues", 0x7E, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "samples", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "keylock", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_1_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_2_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_3_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_4_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_5_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_6_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_7_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_8_enabled", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_1_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_2_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_3_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_4_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_5_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_6_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_7_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_8_enabled", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "pfl", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton1", 0x7C, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton2", 0x7C, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton3", 0x7C, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton4", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", 0x7C, false); + + // For the last output we should send the packet finally + TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", 0x7C, true); +} + +TraktorS2MK3.messageCallback = function (packet, data) { + for (name in data) { + field = data[name]; + HIDDebug("TraktorS2MK3: messageCallback - field: " + name); + TraktorS2MK3.controller.processButton(field); + } +} + +TraktorS2MK3.shutdown = function () { + // Deactivate all LEDs + var data_string = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00"; + this.rawOutput(data_string); + + HIDDebug("TraktorS2MK3: Shutdown done!"); +} + +TraktorS2MK3.incomingData = function (data, length) { + TraktorS2MK3.controller.parsePacket(data, length); +} + +/* Helper function to convert a string into raw bytes */ +TraktorS2MK3.toBytes = function (data_string) { + var data = Object(); + var ok = true; + var splitted = data_string.split(/\s+/); + data.length = splitted.length; + for (j = 0; j < splitted.length; j++) { + var byte_str = splitted[j]; + if (byte_str.length !== 2) { + ok = false; + HIDDebug("not two characters?? " + byte_str); + } + var b = parseInt(byte_str, 16); + if (b < 0 || b > 255) { + HIDDebug("number out of range: " + byte_str + " " + b); + return {}; + } + data[j] = b; + } + + return data; +} + +/* Helper function to send a binary string to the controller */ +TraktorS2MK3.rawOutput = function (data_string) { + HIDDebug("TraktorS2MK3: Send raw output to controller ..."); + var data = TraktorS2MK3.toBytes(data_string); + controller.send(data, data.length, 0x80); +} From 43988b41b3071f6e1ff5ee0edfb4cee64f43c1ba Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 7 Oct 2019 20:25:16 +0200 Subject: [PATCH 02/26] Traktor Kontrol S2MK3: VuMeter handling --- .../Traktor Kontrol S2 MK3.hid.xml | 2 +- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/res/controllers/Traktor Kontrol S2 MK3.hid.xml b/res/controllers/Traktor Kontrol S2 MK3.hid.xml index b9985996d97..813083e5e44 100644 --- a/res/controllers/Traktor Kontrol S2 MK3.hid.xml +++ b/res/controllers/Traktor Kontrol S2 MK3.hid.xml @@ -4,7 +4,7 @@ Traktor Kontrol S2 MK3 Michael Schmidt HID Mapping for Traktor Kontrol S2 MK3 - http://mixxx.org/wiki/doku.php/traktor_s2_mk3 + https://www.mixxx.org/wiki/doku.php/native_instruments_traktor_s2_mk3 diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index cadeb9f0b8d..43ff4b61ae4 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -74,6 +74,13 @@ TraktorS2MK3 = new function () { // Jog wheels this.last_tick_val = [0, 0]; this.last_tick_time = [0.0, 0.0]; + + // VuMeter + this.vuLeftConnection = {}; + this.vuRightConnection = {}; + this.clipLeftConnection = {}; + this.clipRightConnection = {}; + this.vuMeterValues = {"vu-18": (1/6), "vu-12": (1/6*2), "vu-6": (1/6*3), "vu0": (1/6*4), "vu6": (1/6*5)}; } TraktorS2MK3.init = function (id) { @@ -587,6 +594,20 @@ TraktorS2MK3.registerOutputPackets = function () { output.addOutput("[Channel1]", "pfl", 0x1A, "B"); output.addOutput("[Channel2]", "pfl", 0x1B, "B"); + output.addOutput("[Channel1]", "vu-18", 0x1C, "B"); + output.addOutput("[Channel1]", "vu-12", 0x1D, "B"); + output.addOutput("[Channel1]", "vu-6", 0x1E, "B"); + output.addOutput("[Channel1]", "vu0", 0x1F, "B"); + output.addOutput("[Channel1]", "vu6", 0x20, "B"); + output.addOutput("[Channel1]", "PeakIndicator", 0x21, "B"); + + output.addOutput("[Channel2]", "vu-18", 0x22, "B"); + output.addOutput("[Channel2]", "vu-12", 0x23, "B"); + output.addOutput("[Channel2]", "vu-6", 0x24, "B"); + output.addOutput("[Channel2]", "vu0", 0x25, "B"); + output.addOutput("[Channel2]", "vu6", 0x26, "B"); + output.addOutput("[Channel2]", "PeakIndicator", 0x27, "B"); + output.addOutput("[ChannelX]", "fxButton1", 0x16, "B"); output.addOutput("[ChannelX]", "fxButton2", 0x17, "B"); output.addOutput("[ChannelX]", "fxButton3", 0x18, "B"); @@ -632,6 +653,12 @@ TraktorS2MK3.registerOutputPackets = function () { this.linkOutput("[Channel1]", "pfl", this.outputHandler); this.linkOutput("[Channel2]", "pfl", this.outputHandler); + // VuMeter + this.vuLeftConnection = engine.makeConnection("[Channel1]", "VuMeter", this.vuMeterHandler); + this.vuRightConnection = engine.makeConnection("[Channel2]", "VuMeter", this.vuMeterHandler); + this.clipLeftConnection = engine.makeConnection("[Channel1]", "PeakIndicator", this.vuOutputHandler); + this.clipRightConnection = engine.makeConnection("[Channel2]", "PeakIndicator", this.vuOutputHandler); + TraktorS2MK3.lightDeck(); } @@ -640,6 +667,25 @@ TraktorS2MK3.linkOutput = function (group, name, callback) { TraktorS2MK3.controller.linkOutput(group, name, group, name, callback); } +TraktorS2MK3.vuMeterHandler = function (value, group, key) { + for (var vuKey in TraktorS2MK3.vuMeterValues) { + if(TraktorS2MK3.vuMeterValues[vuKey] > value) { + TraktorS2MK3.vuOutputHandler(false, group, vuKey); + } else { + TraktorS2MK3.vuOutputHandler(true, group, vuKey); + } + } +} + +TraktorS2MK3.vuOutputHandler = function (value, group, key) { + var led_value = 0x00; + if (value) { + led_value = 0x7E; + } + + TraktorS2MK3.controller.setOutput(group, key, led_value, true); +} + TraktorS2MK3.outputHandler = function (value, group, key) { var led_value = 0x7C; if (value) { @@ -714,6 +760,13 @@ TraktorS2MK3.messageCallback = function (packet, data) { } TraktorS2MK3.shutdown = function () { + + // VuMeter connections + this.vuLeftConnection.disconnect(); + this.vuRightConnection.disconnect(); + this.clipLeftConnection.disconnect(); + this.clipRightConnection.disconnect(); + // Deactivate all LEDs var data_string = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + From 6e9f760c6522f33a63c3d587ce064d3ea866c487 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 7 Oct 2019 23:09:06 +0200 Subject: [PATCH 03/26] Traktor Kontrol S2MK3: Microphone button --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 43ff4b61ae4..e13dd528e65 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -71,6 +71,9 @@ TraktorS2MK3 = new function () { this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode this.quantizeState = 0; // 0 = Off, 1 = On + this.microphonePressedTimer = 0; + this.microphonePressedState = 0; // 0 = Not pressed, 1 = Pressed + // Jog wheels this.last_tick_val = [0, 0]; this.last_tick_time = [0.0, 0.0]; @@ -80,7 +83,7 @@ TraktorS2MK3 = new function () { this.vuRightConnection = {}; this.clipLeftConnection = {}; this.clipRightConnection = {}; - this.vuMeterValues = {"vu-18": (1/6), "vu-12": (1/6*2), "vu-6": (1/6*3), "vu0": (1/6*4), "vu6": (1/6*5)}; + this.vuMeterValues = { "vu-18": (1 / 6), "vu-12": (1 / 6 * 2), "vu-6": (1 / 6 * 3), "vu0": (1 / 6 * 4), "vu6": (1 / 6 * 5) }; } TraktorS2MK3.init = function (id) { @@ -216,6 +219,9 @@ TraktorS2MK3.registerInputPackets = function () { // There is only one button on the controller, we use to toggle quantization for all channels this.registerInputButton(messageShort, "[ChannelX]", "!quantize", 0x06, 0x40, this.quantizeHandler); + // Microphone + this.registerInputButton(messageShort, "[Microphone]", "!talkover", 0x06, 0x80, this.microphoneHandler); + // Jog wheels this.registerInputButton(messageShort, "[Channel1]", "!jog_touch", 0x08, 0x40, this.jogTouchHandler); this.registerInputButton(messageShort, "[Channel2]", "!jog_touch", 0x08, 0x80, this.jogTouchHandler); @@ -460,6 +466,39 @@ TraktorS2MK3.quantizeHandler = function (field) { TraktorS2MK3.outputHandler(this.quantizeState, field.group, "quantize"); } +TraktorS2MK3.microphoneHandler = function (field) { + if (field.value) { + if (!TraktorS2MK3.microphonePressedState) { + // Start timer to measure how long button is pressed + TraktorS2MK3.microphonePressedTimer = engine.beginTimer(1000, "TraktorS2MK3.microphoneTimer()"); + } + + TraktorS2MK3.microphonePressedState = !TraktorS2MK3.microphonePressedState; + engine.setValue("[Microphone]", "talkover", TraktorS2MK3.microphonePressedState); + } + else { + // Button is released, check if timer is still running + if (TraktorS2MK3.microphonePressedTimer !== 0) { + // short klick -> permanent activation + engine.stopTimer(TraktorS2MK3.microphonePressedTimer); + TraktorS2MK3.microphonePressedTimer = 0; + } else { + TraktorS2MK3.microphonePressedState = false; + engine.setValue("[Microphone]", "talkover", TraktorS2MK3.microphonePressedState); + } + } + + TraktorS2MK3.outputHandler(TraktorS2MK3.microphonePressedState, field.group, "talkover"); +} + +TraktorS2MK3.microphoneTimer = function () { + // Reset microphone button timer if active + if (TraktorS2MK3.microphonePressedTimer !== 0) { + engine.stopTimer(TraktorS2MK3.microphonePressedTimer); + TraktorS2MK3.microphonePressedTimer = 0; + } +} + TraktorS2MK3.parameterHandler = function (field) { engine.setParameter(field.group, field.name, field.value / 4095); } @@ -617,6 +656,7 @@ TraktorS2MK3.registerOutputPackets = function () { output.addOutput("[Channel2]", "MaximizeLibrary", 0x2B, "B"); output.addOutput("[ChannelX]", "quantize", 0x3C, "B"); + output.addOutput("[Microphone]", "talkover", 0x3D, "B"); this.controller.registerOutputPacket(output); @@ -668,8 +708,9 @@ TraktorS2MK3.linkOutput = function (group, name, callback) { } TraktorS2MK3.vuMeterHandler = function (value, group, key) { + // TODO: Only send one packet for all LEDs for (var vuKey in TraktorS2MK3.vuMeterValues) { - if(TraktorS2MK3.vuMeterValues[vuKey] > value) { + if (TraktorS2MK3.vuMeterValues[vuKey] > value) { TraktorS2MK3.vuOutputHandler(false, group, vuKey); } else { TraktorS2MK3.vuOutputHandler(true, group, vuKey); From d74c9140beed641b66e730e14aec98a521c448d1 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 8 Oct 2019 03:14:05 +0200 Subject: [PATCH 04/26] Traktor Kontrol S2MK3: Samplers --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 227 +++++++++++++----- 1 file changed, 164 insertions(+), 63 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index e13dd528e65..7913bddcfea 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -71,7 +71,7 @@ TraktorS2MK3 = new function () { this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode this.quantizeState = 0; // 0 = Off, 1 = On - this.microphonePressedTimer = 0; + this.microphonePressedTimer = 0; // Timer to distinguish between short and long press this.microphonePressedState = 0; // 0 = Not pressed, 1 = Pressed // Jog wheels @@ -84,6 +84,16 @@ TraktorS2MK3 = new function () { this.clipLeftConnection = {}; this.clipRightConnection = {}; this.vuMeterValues = { "vu-18": (1 / 6), "vu-12": (1 / 6 * 2), "vu-6": (1 / 6 * 3), "vu0": (1 / 6 * 4), "vu6": (1 / 6 * 5) }; + + // Sampler callbacks + this.samplerCallbacks = []; + this.samplerHotcuesRelation = { + "[Channel1]": { + 1: 1, 2: 2, 3: 3, 4: 4, 5: 9, 6: 10, 7: 11, 8: 12 + }, "[Channel2]": { + 1: 5, 2: 6, 3: 7, 4: 8, 5: 13, 6: 14, 7: 15, 8: 16 + } + }; } TraktorS2MK3.init = function (id) { @@ -176,23 +186,23 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[Channel2]", "!samples", 0x05, 0x02, this.padModeHandler); // Hotcues - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_1", 0x02, 0x10, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_2", 0x02, 0x20, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_3", 0x02, 0x40, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_4", 0x02, 0x80, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_5", 0x03, 0x01, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_6", 0x03, 0x02, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_7", 0x03, 0x04, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_8", 0x03, 0x08, this.hotcueHandler); - - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_1", 0x05, 0x40, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_2", 0x05, 0x80, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_3", 0x06, 0x01, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_4", 0x06, 0x02, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_5", 0x06, 0x04, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_6", 0x06, 0x08, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_7", 0x06, 0x10, this.hotcueHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_8", 0x06, 0x20, this.hotcueHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_1", 0x02, 0x10, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_2", 0x02, 0x20, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_3", 0x02, 0x40, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_4", 0x02, 0x80, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_5", 0x03, 0x01, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_6", 0x03, 0x02, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_7", 0x03, 0x04, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!hotcue_8", 0x03, 0x08, this.numberButtonHandler); + + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_1", 0x05, 0x40, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_2", 0x05, 0x80, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_3", 0x06, 0x01, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_4", 0x06, 0x02, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_5", 0x06, 0x04, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_6", 0x06, 0x08, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_7", 0x06, 0x10, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!hotcue_8", 0x06, 0x20, this.numberButtonHandler); // Headphone buttons this.registerInputButton(messageShort, "[Channel1]", "!pfl", 0x04, 0x01, this.headphoneHandler); @@ -337,6 +347,23 @@ TraktorS2MK3.keylockHandler = function (field) { engine.setValue(field.group, "keylock", !keylock); } +TraktorS2MK3.syncHandler = function (field) { + if (field.value === 0) { + return; + } + + var sync = engine.getValue(field.group, "sync_enabled"); + engine.setValue(field.group, "sync_enabled", !sync); +} + +TraktorS2MK3.cueHandler = function (field) { + if (field.value === 0) { + return; + } + + engine.setValue(field.group, "cue_default", field.value); +} + TraktorS2MK3.padModeHandler = function (field) { if (field.value === 0) { return; @@ -344,48 +371,55 @@ TraktorS2MK3.padModeHandler = function (field) { // If we are in hotcues mode and samples mode is activated if (TraktorS2MK3.padModeState[field.group] === 0 && field.name === "!samples") { - HIDDebug("Samples"); + engine.setValue("[Samplers]", "show_samplers", 1); TraktorS2MK3.padModeState[field.group] = 1; TraktorS2MK3.outputHandler(!field.value, field.group, "hotcues"); TraktorS2MK3.outputHandler(field.value, field.group, "samples"); - // TODO: Change number buttons + + // Light LEDs for all slots with loaded samplers + for (var k in TraktorS2MK3.samplerHotcuesRelation[field.group]) { + var loaded = engine.getValue("[Sampler" + TraktorS2MK3.samplerHotcuesRelation[field.group][k] + "]", "track_loaded"); + TraktorS2MK3.outputHandler(loaded, field.group, "hotcue_" + k + "_enabled"); + } } // If we are in samples mode and hotcues mode is activated else if (field.name === "!hotcues") { TraktorS2MK3.padModeState[field.group] = 0; TraktorS2MK3.outputHandler(field.value, field.group, "hotcues"); TraktorS2MK3.outputHandler(!field.value, field.group, "samples"); - // TODO: Change number buttons + + // Light LEDs for all enabled hotcues + for(var i = 1; i <= 8; ++i) { + var active = engine.getValue(field.group, "hotcue_" + i + "_enabled"); + TraktorS2MK3.outputHandler(active, field.group, "hotcue_" + i + "_enabled"); + } } } -TraktorS2MK3.syncHandler = function (field) { - if (field.value === 0) { - return; - } +TraktorS2MK3.numberButtonHandler = function (field) { + HIDDebug(field.id + ": " + field.value); - var sync = engine.getValue(field.group, "sync_enabled"); - engine.setValue(field.group, "sync_enabled", !sync); -} - -TraktorS2MK3.cueHandler = function (field) { - if (field.value === 0) { - return; - } - - engine.setValue(field.group, "cue_default", field.value); -} - -TraktorS2MK3.hotcueHandler = function (field) { if (field.value === 0) { return; } var hotcueNumber = parseInt(field.id[field.id.length - 1]); - if (TraktorS2MK3.shiftPressed[field.group]) { - engine.setValue(field.group, "hotcue_" + hotcueNumber + "_clear", field.value); - } else { - engine.setValue(field.group, "hotcue_" + hotcueNumber + "_activate", field.value); + if (TraktorS2MK3.padModeState[field.group] === 0) { + // Hotcues mode + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "hotcue_" + hotcueNumber + "_clear", field.value); + } else { + engine.setValue(field.group, "hotcue_" + hotcueNumber + "_activate", field.value); + } + } + else { + // Samples mode + var sampler = TraktorS2MK3.samplerHotcuesRelation[field.group][hotcueNumber]; + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue("[Sampler" + sampler + "]", "cue_default", field.value); + } else { + engine.setValue("[Sampler" + sampler + "]", "play_stutter", field.value); + } } } @@ -487,8 +521,6 @@ TraktorS2MK3.microphoneHandler = function (field) { engine.setValue("[Microphone]", "talkover", TraktorS2MK3.microphonePressedState); } } - - TraktorS2MK3.outputHandler(TraktorS2MK3.microphonePressedState, field.group, "talkover"); } TraktorS2MK3.microphoneTimer = function () { @@ -672,33 +704,54 @@ TraktorS2MK3.registerOutputPackets = function () { this.linkOutput("[Channel1]", "keylock", this.outputHandler); this.linkOutput("[Channel2]", "keylock", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_1_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_2_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_3_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_4_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_5_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_6_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_7_enabled", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_8_enabled", this.outputHandler); - - this.linkOutput("[Channel2]", "hotcue_1_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_2_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_3_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_4_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_5_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_6_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_7_enabled", this.outputHandler); - this.linkOutput("[Channel2]", "hotcue_8_enabled", this.outputHandler); + this.linkOutput("[Channel1]", "hotcue_1_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_2_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_3_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_4_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_5_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_6_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_7_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel1]", "hotcue_8_enabled", this.hotcueOutputHandler); + + this.linkOutput("[Channel2]", "hotcue_1_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_2_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_3_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_4_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_5_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_6_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_7_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_8_enabled", this.hotcueOutputHandler); this.linkOutput("[Channel1]", "pfl", this.outputHandler); this.linkOutput("[Channel2]", "pfl", this.outputHandler); + this.linkOutput("[Microphone]", "talkover", this.outputHandler); + // VuMeter this.vuLeftConnection = engine.makeConnection("[Channel1]", "VuMeter", this.vuMeterHandler); this.vuRightConnection = engine.makeConnection("[Channel2]", "VuMeter", this.vuMeterHandler); this.clipLeftConnection = engine.makeConnection("[Channel1]", "PeakIndicator", this.vuOutputHandler); this.clipRightConnection = engine.makeConnection("[Channel2]", "PeakIndicator", this.vuOutputHandler); + // Sampler callbacks + this.samplerCallbacks.push(engine.makeConnection("[Sampler1]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler2]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler3]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler4]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler5]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler6]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler7]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler8]", "track_loaded", this.samplesOutputHandler)); + + this.samplerCallbacks.push(engine.makeConnection("[Sampler9]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler10]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler11]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler12]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler13]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler14]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler15]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler16]", "track_loaded", this.samplesOutputHandler)); + TraktorS2MK3.lightDeck(); } @@ -736,6 +789,47 @@ TraktorS2MK3.outputHandler = function (value, group, key) { TraktorS2MK3.controller.setOutput(group, key, led_value, true); } +TraktorS2MK3.hotcueOutputHandler = function (value, group, key) { + if (TraktorS2MK3.padModeState[group] === 0) { + TraktorS2MK3.outputHandler(value, group, key); + } +} + +TraktorS2MK3.samplesOutputHandler = function (value, group, key) { + // Sampler 1-4, 9-12 -> Channel1 + // Samples 5-8, 13-16 -> Channel2 + var sampler = TraktorS2MK3.resolveSampler(group); + if (sampler > 0 && sampler < 5) { + var deck = "[Channel1]"; + var num = sampler; + } + else if (sampler > 4 && sampler < 9) { + var deck = "[Channel2]"; + var num = sampler - 4; + } else if (sampler > 8 && sampler < 13) { + var deck = "[Channel1]"; + var num = sampler - 4; + } else if (sampler > 12 && sampler < 17) { + var deck = "[Channel2]"; + var num = sampler - 8; + } + + // If we are in samples modes light corresponding LED + if (TraktorS2MK3.padModeState[deck] === 1) { + TraktorS2MK3.outputHandler(value, deck, "hotcue_" + num + "_enabled"); + } +} + +TraktorS2MK3.resolveSampler = function (group) { + if (group == undefined) + return undefined; + var result = group.match(/\[Sampler[0-9]+\]/); + if (!result) + return undefined; + var str = group.replace(/\[Sampler/, ""); + return str.substring(0, str.length - 1); +} + TraktorS2MK3.lightDeck = function () { TraktorS2MK3.controller.setOutput("[Channel1]", "play_indicator", 0x7C, false); @@ -788,8 +882,10 @@ TraktorS2MK3.lightDeck = function () { TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", 0x7C, false); TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", 0x7C, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", 0x7C, false); + // For the last output we should send the packet finally - TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", 0x7C, true); + TraktorS2MK3.controller.setOutput("[Microphone]", "talkover", 0x7C, true); } TraktorS2MK3.messageCallback = function (packet, data) { @@ -802,12 +898,17 @@ TraktorS2MK3.messageCallback = function (packet, data) { TraktorS2MK3.shutdown = function () { - // VuMeter connections + // Disconnect VuMeter callbacks this.vuLeftConnection.disconnect(); this.vuRightConnection.disconnect(); this.clipLeftConnection.disconnect(); this.clipRightConnection.disconnect(); + // Disconnect Sampler callbacks + this.samplerCallbacks.forEach(function (item) { + item.disconnect(); + }); + // Deactivate all LEDs var data_string = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + From 07c7accb107b316cfbe7780ad99ed0c76dcec247 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 8 Oct 2019 04:16:08 +0200 Subject: [PATCH 05/26] Traktor Kontrol S2MK3: Remove HID specs from source file --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 132 ++---------------- 1 file changed, 9 insertions(+), 123 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 7913bddcfea..dbcae25ec57 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -1,63 +1,6 @@ /**************************************************** Traktor Kontrol S2 MK3 HID controller script v1.00 - - USB HID descriptors (only usage 0x1 and 0x80 is used) - $> usbhid-dump -d17cc:1710 | grep -v : | xxd -r -p | hidrd-convert -o spec - - Usage Page (FF01h), ; FF01h, vendor-defined - Usage (00h), - Collection (Application), - Usage (01h), - Collection (Logical), - Report ID (1), - Usage (02h), - Logical Minimum (0), - Logical Maximum (1), - Report Size (1), - Report Count (64), - Input (Variable), - Usage (03h), - Logical Minimum (0), - Logical Maximum (15), - Report Size (4), - Report Count (6), - Input (Variable), - Usage (31h), - Logical Minimum (0), - Logical Maximum (65535), - Report Size (16), - Report Count (4), - Input (Variable), - End Collection, - Usage (01h), - Collection (Logical), - Report ID (2), - Usage (05h), - Logical Minimum (0), - Logical Maximum (4095), - Report Size (16), - Report Count (5), - Input (Variable), - Usage (04h), - Logical Minimum (0), - Logical Maximum (4095), - Report Size (16), - Report Count (14), - Input (Variable), - End Collection, - Usage (80h), - Collection (Logical), - Report ID (128), - Usage (81h), - Logical Minimum (0), - Logical Maximum (127), - Report Size (8), - Report Count (61), - Output (Variable), - End Collection, -End Collection - -/****************************************************/ +****************************************************/ var TraktorS2MK3 = {}; @@ -83,7 +26,7 @@ TraktorS2MK3 = new function () { this.vuRightConnection = {}; this.clipLeftConnection = {}; this.clipRightConnection = {}; - this.vuMeterValues = { "vu-18": (1 / 6), "vu-12": (1 / 6 * 2), "vu-6": (1 / 6 * 3), "vu0": (1 / 6 * 4), "vu6": (1 / 6 * 5) }; + this.vuMeterThresholds = { "vu-18": (1 / 6), "vu-12": (1 / 6 * 2), "vu-6": (1 / 6 * 3), "vu0": (1 / 6 * 4), "vu6": (1 / 6 * 5) }; // Sampler callbacks this.samplerCallbacks = []; @@ -100,70 +43,12 @@ TraktorS2MK3.init = function (id) { TraktorS2MK3.registerInputPackets(); TraktorS2MK3.registerOutputPackets(); HIDDebug("TraktorS2MK3: Init done!"); - - // Output package set only the LEDs in the controller - // Each byte representing the state of one LED - var data_string = "00 00 00 \n" + // Rev Left, FLX Left, Preparation Left - "00 00 00 00 \n" + // Browse View Left, Grid Left, Shift Left, Hotcues Left - "00 00 00 00 \n" + // Samples Left, Sync Left, Keylock Left, Cue Left - "00 00 00 00 \n" + // Play Left, Hotcue1 Left, Hotcue2 Left, Hotcue3 Left - "00 00 00 00 \n" + // Hotcue4 Left, Hotcue5 Left, Hotcue6 Left, Hotcue7 Left - "00 00 00 00 \n" + // Hotcue8 Left, Sample Mixer LED, FX Select1, FX Select2 - "00 00 00 00 \n" + // FX Select3, FX Select4, Phones Left, Phones Right - "00 00 00 00 \n" + // Level Channel A -18, -12, -6, 0 - "00 00 00 00 \n" + // Level Channel A 6, Level Channel B -18, -12 - "00 00 00 00 \n" + // Level Channel B -6, 0, 6, Clip - "00 00 00 00 \n" + // Rev Right, FLX Right, Preparation Right, Browse View Right - "00 00 00 00 \n" + // Grid Right, Shift Right, Hotcues Right, Samples Right - "00 00 00 00 \n" + // Sync Right, Keylock Right, Cue Right, Play Right - "00 00 00 00 \n" + // Hotcue1 Right, Hotcue2 Right, Hotcue3 Right, Hotcue4 Right - "00 00 00 00 \n" + // Hotcue5 Right, Hotcue6 Right, Hotcue7 Right, Hotcue8 Right - "00 00"; // GNT, MIC - // this.rawOutput(data_string); } TraktorS2MK3.registerInputPackets = function () { var messageShort = new HIDPacket("shortmessage", 0x01, this.messageCallback); var messageLong = new HIDPacket("longmessage", 0x02, this.messageCallback); - /* - Offset 0x01, Bitmasks: - - 0x01 Rev Left - 0x02 FLX Left - 0x04 Prepation Left - 0x08 Browse View Left - - 0x10 Grid Left - 0x20 Shift Left - 0x40 Hotcues Left - 0x80 Samples Left - Offset 0x02, Bitmasks: - - 0x01 Sync Left - 0x02 Keylock Left - 0x04 Cue Left - 0x08 Play Left - - 0x10 Hotcue1 Left - 0x20 Hotcue2 Left - 0x40 Hotcue3 Left - 0x80 Hotcue4 Left - Offset 0x03, Bitmasks: - - 0x01 Hotcue5 Left - 0x02 Hotcue6 Left - 0x04 Hotcue7 Left - 0x08 Hotcue8 Left - - 0x10 FX Select1 - 0x20 FX Select2 - 0x40 FX Select3 - 0x80 FX Select4 - Offset 0x04, Bitmasks: - - 0x01 Phones Left - 0x02 Phones Right - 0x04 Rev Right - 0x08 FLX Right - - 0x10 Prepation Right - 0x20 Browse View Right - 0x40 Grid Right - 0x80 Shift Right - Offset 0x05, Bitmasks: - - 0x01 Hotcues Right - 0x02 Samples Right - 0x04 Sync Right - 0x08 Keylock Right - - 0x10 Cue Right - 0x20 Play Right - 0x40 Hotcue1 Right - 0x80 Hotcue2 Right - Offset 0x06, Bitmasks: - - 0x01 Hotcue3 Right - 0x02 Hotcue4 Right - 0x04 Hotcue5 Right - 0x08 Hotcue6 Right - - 0x10 Hotcue7 Right - 0x20 Hotcue8 Right - 0x40 GNT - 0x80 MIC - Offset 0x07, Bitmasks: - - 0x01 Browse Left Click - 0x02 Move Left Click - 0x04 Loop Left Click - 0x08 Browse Right Click - - 0x10 Move Right Click - 0x20 Loop Right Click - 0x40 ??? - 0x80 ??? - Offset 0x08, Bitmasks: - - 0x01 Browse Left Touch - 0x02 Move Left Touch - 0x04 Loop Left Touch - 0x08 Browse Right Touch - - 0x10 Move Right Touch - 0x20 Loop Right Touch - 0x40 Jog Wheel Left Touch - 0x80 Jog Wheel Right Touch - Offset 0x09, Bitmasks: - - Lowest nibble: Browse Left - - Highest nibble: Move Left - Offset 0x0A, Bitmasks: - - Lowest nibble: Loop Left - - Highest nibble: Browse Right - Offset 0x0B, Bitmasks: - - Lowest nibble: Move Right - - Highest nibble: Loop Right - Offset 0x0C - 0x0F: Jog Wheel Left - Offset 0x10 - 0x13: Jog Wheel Right - */ - this.registerInputButton(messageShort, "[Channel1]", "!play", 0x02, 0x08, this.playHandler); this.registerInputButton(messageShort, "[Channel2]", "!play", 0x05, 0x20, this.playHandler); @@ -328,6 +213,7 @@ TraktorS2MK3.playHandler = function (field) { return; } + // TODO: Shift function var playing = engine.getValue(field.group, "play"); engine.setValue(field.group, "play", !playing); } @@ -352,6 +238,7 @@ TraktorS2MK3.syncHandler = function (field) { return; } + // TODO: Shift function / timer var sync = engine.getValue(field.group, "sync_enabled"); engine.setValue(field.group, "sync_enabled", !sync); } @@ -361,6 +248,7 @@ TraktorS2MK3.cueHandler = function (field) { return; } + // TODO: Shift function engine.setValue(field.group, "cue_default", field.value); } @@ -387,9 +275,9 @@ TraktorS2MK3.padModeHandler = function (field) { TraktorS2MK3.padModeState[field.group] = 0; TraktorS2MK3.outputHandler(field.value, field.group, "hotcues"); TraktorS2MK3.outputHandler(!field.value, field.group, "samples"); - + // Light LEDs for all enabled hotcues - for(var i = 1; i <= 8; ++i) { + for (var i = 1; i <= 8; ++i) { var active = engine.getValue(field.group, "hotcue_" + i + "_enabled"); TraktorS2MK3.outputHandler(active, field.group, "hotcue_" + i + "_enabled"); } @@ -397,8 +285,6 @@ TraktorS2MK3.padModeHandler = function (field) { } TraktorS2MK3.numberButtonHandler = function (field) { - HIDDebug(field.id + ": " + field.value); - if (field.value === 0) { return; } @@ -762,8 +648,8 @@ TraktorS2MK3.linkOutput = function (group, name, callback) { TraktorS2MK3.vuMeterHandler = function (value, group, key) { // TODO: Only send one packet for all LEDs - for (var vuKey in TraktorS2MK3.vuMeterValues) { - if (TraktorS2MK3.vuMeterValues[vuKey] > value) { + for (var vuKey in TraktorS2MK3.vuMeterThresholds) { + if (TraktorS2MK3.vuMeterThresholds[vuKey] > value) { TraktorS2MK3.vuOutputHandler(false, group, vuKey); } else { TraktorS2MK3.vuOutputHandler(true, group, vuKey); From 1b7c7e66695dc296b8145fddbbb2137a180f7138 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 9 Oct 2019 02:01:33 +0200 Subject: [PATCH 06/26] Traktor Kontrol S2MK3: Added shift-functions for play, cue and sync --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index dbcae25ec57..87d747dc653 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -1,5 +1,5 @@ /**************************************************** - Traktor Kontrol S2 MK3 HID controller script v1.00 + Traktor Kontrol S2 MK3 HID controller script v1.01 ****************************************************/ var TraktorS2MK3 = {}; @@ -8,14 +8,17 @@ TraktorS2MK3 = new function () { this.controller = new HIDController(); this.shiftPressed = { "[Channel1]": false, "[Channel2]": false }; this.browseState = { "[Channel1]": 0, "[Channel2]": 0 }; - this.loopSizeState = { "[Channel1]": 0, "[Channel2]": 0 }; + this.loopSizeState = { "[Channel1]": 0, "[Channel1]": 0 }; this.beatjumpState = { "[Channel1]": 0, "[Channel2]": 0 }; this.fxButtonState = { 1: false, 2: false, 3: false, 4: false }; this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode - this.quantizeState = 0; // 0 = Off, 1 = On + this.quantizeState = false; // false = Off, true = On + // Microphone button this.microphonePressedTimer = 0; // Timer to distinguish between short and long press - this.microphonePressedState = 0; // 0 = Not pressed, 1 = Pressed + + // Sync buttons + this.syncPressedTimer = { "[Channel1]": 0, "[Channel2]": 0 }; // Timer to distinguish between short and long press // Jog wheels this.last_tick_val = [0, 0]; @@ -213,9 +216,24 @@ TraktorS2MK3.playHandler = function (field) { return; } - // TODO: Shift function - var playing = engine.getValue(field.group, "play"); - engine.setValue(field.group, "play", !playing); + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "cue_set", field.value); + } else { + var playing = engine.getValue(field.group, "play"); + engine.setValue(field.group, "play", !playing); + } +} + +TraktorS2MK3.cueHandler = function (field) { + if (field.value === 0) { + return; + } + + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "cue_gotoandstop", field.value); + } else { + engine.setValue(field.group, "cue_default", field.value); + } } TraktorS2MK3.shiftHandler = function (field) { @@ -234,22 +252,41 @@ TraktorS2MK3.keylockHandler = function (field) { } TraktorS2MK3.syncHandler = function (field) { - if (field.value === 0) { - return; + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "beatsync_phase", field.value); + // Light LED while pressed + TraktorS2MK3.outputHandler(field.value, field.group, "sync_enabled"); + } + else { + if (field.value) { + if (!TraktorS2MK3.syncPressedTimer[field.group]) { + // Start timer to measure how long button is pressed + TraktorS2MK3.syncPressedTimer[field.group] = engine.beginTimer(1000, "TraktorS2MK3.syncTimer(\"" + field.group + "\")"); + } + + var sync = engine.getValue(field.group, "sync_enabled"); + engine.setValue(field.group, "sync_enabled", !sync); + } + else { + // Button is released, check if timer is still running + if (TraktorS2MK3.syncPressedTimer[field.group] === 0) { + // long press -> sync lock + engine.stopTimer(TraktorS2MK3.syncPressedTimer[field.group]); + TraktorS2MK3.syncPressedTimer[field.group] = 0; + } else { + // short press -> disable sync + engine.setValue(field.group, "sync_enabled", 0); + } + } } - - // TODO: Shift function / timer - var sync = engine.getValue(field.group, "sync_enabled"); - engine.setValue(field.group, "sync_enabled", !sync); } -TraktorS2MK3.cueHandler = function (field) { - if (field.value === 0) { - return; +TraktorS2MK3.syncTimer = function (group) { + // Reset sync button timer if active + if (TraktorS2MK3.syncPressedTimer[group] !== 0) { + engine.stopTimer(TraktorS2MK3.syncPressedTimer[group]); + TraktorS2MK3.syncPressedTimer[group] = 0; } - - // TODO: Shift function - engine.setValue(field.group, "cue_default", field.value); } TraktorS2MK3.padModeHandler = function (field) { @@ -388,13 +425,13 @@ TraktorS2MK3.quantizeHandler = function (field) { TraktorS2MK3.microphoneHandler = function (field) { if (field.value) { - if (!TraktorS2MK3.microphonePressedState) { + if (TraktorS2MK3.microphonePressedTimer === 0) { // Start timer to measure how long button is pressed TraktorS2MK3.microphonePressedTimer = engine.beginTimer(1000, "TraktorS2MK3.microphoneTimer()"); } - TraktorS2MK3.microphonePressedState = !TraktorS2MK3.microphonePressedState; - engine.setValue("[Microphone]", "talkover", TraktorS2MK3.microphonePressedState); + var talkover = engine.getValue("[Microphone]", "talkover"); + engine.setValue("[Microphone]", "talkover", !talkover); } else { // Button is released, check if timer is still running @@ -403,8 +440,7 @@ TraktorS2MK3.microphoneHandler = function (field) { engine.stopTimer(TraktorS2MK3.microphonePressedTimer); TraktorS2MK3.microphonePressedTimer = 0; } else { - TraktorS2MK3.microphonePressedState = false; - engine.setValue("[Microphone]", "talkover", TraktorS2MK3.microphonePressedState); + engine.setValue("[Microphone]", "talkover", 0); } } } From b4e70dc5a76029a62832a4b015a7e0210c8bc17a Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 9 Oct 2019 03:42:20 +0200 Subject: [PATCH 07/26] Traktor Kontrol S2MK3: Avoid spamming bus with VuMeter --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 87d747dc653..ab8f6423c71 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -132,10 +132,6 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[ChannelX]", "!fx3", 0x03, 0x40, this.fxHandler); this.registerInputButton(messageShort, "[ChannelX]", "!fx4", 0x03, 0x80, this.fxHandler); - /////////////////////////////////// - // TODO: Sampler button and control - /////////////////////////////////// - this.controller.registerInputPacket(messageShort); this.registerInputScaler(messageLong, "[Channel1]", "rate", 0x01, 0xFFFF, this.parameterHandler); @@ -683,12 +679,14 @@ TraktorS2MK3.linkOutput = function (group, name, callback) { } TraktorS2MK3.vuMeterHandler = function (value, group, key) { - // TODO: Only send one packet for all LEDs - for (var vuKey in TraktorS2MK3.vuMeterThresholds) { - if (TraktorS2MK3.vuMeterThresholds[vuKey] > value) { - TraktorS2MK3.vuOutputHandler(false, group, vuKey); + var vuKeys = Object.keys(TraktorS2MK3.vuMeterThresholds); + for (var i = 0; i < vuKeys.length; ++i) { + // Avoid spamming HID by only sending last LED update + var last = (i === vuKeys.length - 1); + if (TraktorS2MK3.vuMeterThresholds[vuKeys[i]] > value) { + TraktorS2MK3.controller.setOutput(group, vuKeys[i], 0x00, last); } else { - TraktorS2MK3.vuOutputHandler(true, group, vuKey); + TraktorS2MK3.controller.setOutput(group, vuKeys[i], 0x7E, last); } } } @@ -712,6 +710,7 @@ TraktorS2MK3.outputHandler = function (value, group, key) { } TraktorS2MK3.hotcueOutputHandler = function (value, group, key) { + // Light button LED only when we are in hotcue mode if (TraktorS2MK3.padModeState[group] === 0) { TraktorS2MK3.outputHandler(value, group, key); } @@ -724,8 +723,7 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { if (sampler > 0 && sampler < 5) { var deck = "[Channel1]"; var num = sampler; - } - else if (sampler > 4 && sampler < 9) { + } else if (sampler > 4 && sampler < 9) { var deck = "[Channel2]"; var num = sampler - 4; } else if (sampler > 8 && sampler < 13) { From 8386ecf66ef810562fe955aef5a1d0e15e860680 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 9 Oct 2019 22:47:07 +0200 Subject: [PATCH 08/26] Traktor Kontrol S2MK3: Better sampler handling --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 224 ++++++++---------- 1 file changed, 104 insertions(+), 120 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index ab8f6423c71..d068c044396 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -21,8 +21,8 @@ TraktorS2MK3 = new function () { this.syncPressedTimer = { "[Channel1]": 0, "[Channel2]": 0 }; // Timer to distinguish between short and long press // Jog wheels - this.last_tick_val = [0, 0]; - this.last_tick_time = [0.0, 0.0]; + this.lastTickVal = [0, 0]; + this.lastTickTime = [0.0, 0.0]; // VuMeter this.vuLeftConnection = {}; @@ -252,8 +252,7 @@ TraktorS2MK3.syncHandler = function (field) { engine.setValue(field.group, "beatsync_phase", field.value); // Light LED while pressed TraktorS2MK3.outputHandler(field.value, field.group, "sync_enabled"); - } - else { + } else { if (field.value) { if (!TraktorS2MK3.syncPressedTimer[field.group]) { // Start timer to measure how long button is pressed @@ -262,8 +261,7 @@ TraktorS2MK3.syncHandler = function (field) { var sync = engine.getValue(field.group, "sync_enabled"); engine.setValue(field.group, "sync_enabled", !sync); - } - else { + } else { // Button is released, check if timer is still running if (TraktorS2MK3.syncPressedTimer[field.group] === 0) { // long press -> sync lock @@ -290,8 +288,8 @@ TraktorS2MK3.padModeHandler = function (field) { return; } - // If we are in hotcues mode and samples mode is activated if (TraktorS2MK3.padModeState[field.group] === 0 && field.name === "!samples") { + // If we are in hotcues mode and samples mode is activated engine.setValue("[Samplers]", "show_samplers", 1); TraktorS2MK3.padModeState[field.group] = 1; TraktorS2MK3.outputHandler(!field.value, field.group, "hotcues"); @@ -302,9 +300,8 @@ TraktorS2MK3.padModeHandler = function (field) { var loaded = engine.getValue("[Sampler" + TraktorS2MK3.samplerHotcuesRelation[field.group][k] + "]", "track_loaded"); TraktorS2MK3.outputHandler(loaded, field.group, "hotcue_" + k + "_enabled"); } - } - // If we are in samples mode and hotcues mode is activated - else if (field.name === "!hotcues") { + } else if (field.name === "!hotcues") { + // If we are in samples mode and hotcues mode is activated TraktorS2MK3.padModeState[field.group] = 0; TraktorS2MK3.outputHandler(field.value, field.group, "hotcues"); TraktorS2MK3.outputHandler(!field.value, field.group, "samples"); @@ -330,14 +327,25 @@ TraktorS2MK3.numberButtonHandler = function (field) { } else { engine.setValue(field.group, "hotcue_" + hotcueNumber + "_activate", field.value); } - } - else { + } else { // Samples mode var sampler = TraktorS2MK3.samplerHotcuesRelation[field.group][hotcueNumber]; if (TraktorS2MK3.shiftPressed[field.group]) { - engine.setValue("[Sampler" + sampler + "]", "cue_default", field.value); + var playing = engine.getValue("[Sampler" + sampler + "]", "play_indicator"); + if (playing) { + engine.setValue("[Sampler" + sampler + "]", "cue_default", field.value); + } else { + engine.setValue("[Sampler" + sampler + "]", "eject", field.value); + // Reset eject button + engine.setValue("[Sampler" + sampler + "]", "eject", !field.value); + } } else { - engine.setValue("[Sampler" + sampler + "]", "play_stutter", field.value); + var loaded = engine.getValue("[Sampler" + sampler + "]", "track_loaded"); + if (loaded) { + engine.setValue("[Sampler" + sampler + "]", "cue_gotoandplay", field.value); + } else { + engine.setValue("[Sampler" + sampler + "]", "LoadSelectedTrack", field.value); + } } } } @@ -354,10 +362,10 @@ TraktorS2MK3.headphoneHandler = function (field) { TraktorS2MK3.selectTrackHandler = function (field) { if ((field.value + 1) % 16 == TraktorS2MK3.browseState[field.group]) { engine.setValue("[Library]", "MoveUp", 1); - } - else { + } else { engine.setValue("[Library]", "MoveDown", 1); } + TraktorS2MK3.browseState[field.group] = field.value; } @@ -381,9 +389,7 @@ TraktorS2MK3.maximizeLibraryHandler = function (field) { TraktorS2MK3.selectLoopHandler = function (field) { if ((field.value + 1) % 16 == TraktorS2MK3.loopSizeState[field.group]) { engine.setValue(field.group, "loop_halve", 1); - - } - else { + } else { engine.setValue(field.group, "loop_double", 1); } TraktorS2MK3.loopSizeState[field.group] = field.value; @@ -400,9 +406,7 @@ TraktorS2MK3.activateLoopHandler = function (field) { TraktorS2MK3.beatjumpHandler = function (field) { if ((field.value + 1) % 16 == TraktorS2MK3.beatjumpState[field.group]) { engine.setValue(field.group, "beatjump_backward", 1); - - } - else { + } else { engine.setValue(field.group, "beatjump_forward", 1); } TraktorS2MK3.beatjumpState[field.group] = field.value; @@ -428,8 +432,7 @@ TraktorS2MK3.microphoneHandler = function (field) { var talkover = engine.getValue("[Microphone]", "talkover"); engine.setValue("[Microphone]", "talkover", !talkover); - } - else { + } else { // Button is released, check if timer is still running if (TraktorS2MK3.microphonePressedTimer !== 0) { // short klick -> permanent activation @@ -457,8 +460,7 @@ TraktorS2MK3.jogTouchHandler = function (field) { var deckNumber = TraktorS2MK3.controller.resolveDeck(group); if (field.value > 0) { engine.scratchEnable(deckNumber, 1024, 33.3333, 0.125, 0.125 / 8, true); - } - else { + } else { engine.scratchDisable(deckNumber); } } @@ -469,10 +471,10 @@ TraktorS2MK3.jogHandler = function (field) { // Jog wheel control is based on the S4MK2 mapping, might need some more review if (engine.isScratching(deckNumber)) { var deltas = TraktorS2MK3.wheelDeltas(field.group, field.value); - var tick_delta = deltas[0]; - var time_delta = deltas[1]; + var tickDelta = deltas[0]; + var timeDelta = deltas[1]; - var velocity = (tick_delta / time_delta) / 3; + var velocity = (tick_delta / timeDelta) / 3; engine.setValue(field.group, "jog", velocity); if (engine.getValue(field.group, "scratch2_enable")) { engine.scratchTick(deckNumber, tick_delta); @@ -485,41 +487,41 @@ TraktorS2MK3.wheelDeltas = function (group, value) { // It looks like the wheel is 1024 ticks per revolution. var tickval = value & 0xFF; var timeval = value >>> 16; - var prev_tick = 0; - var prev_time = 0; + var prevTick = 0; + var prevTime = 0; if (group[8] === "1" || group[8] === "3") { - prev_tick = this.last_tick_val[0]; - prev_time = this.last_tick_time[0]; - this.last_tick_val[0] = tickval; - this.last_tick_time[0] = timeval; + prevTick = this.lastTickVal[0]; + prevTime = this.lastTickTime[0]; + this.lastTickVal[0] = tickval; + this.lastTickTime[0] = timeval; } else { - prev_tick = this.last_tick_val[1]; - prev_time = this.last_tick_time[1]; - this.last_tick_val[1] = tickval; - this.last_tick_time[1] = timeval; + prevTick = this.lastTickVal[1]; + prevTime = this.lastTickTime[1]; + this.lastTickVal[1] = tickval; + this.lastTickTime[1] = timeval; } - if (prev_time > timeval) { + if (prevTime > timeval) { // We looped around. Adjust current time so that subtraction works. timeval += 0x10000; } - var time_delta = timeval - prev_time; - if (time_delta === 0) { + var timeDelta = timeval - prevTime; + if (timeDelta === 0) { // Spinning too fast to detect speed! By not dividing we are guessing it took 1ms. - time_delta = 1; + timeDelta = 1; } - var tick_delta = 0; - if (prev_tick >= 200 && tickval <= 100) { - tick_delta = tickval + 256 - prev_tick; - } else if (prev_tick <= 100 && tickval >= 200) { - tick_delta = tickval - prev_tick - 256; + var tickDelta = 0; + if (prevTick >= 200 && tickval <= 100) { + tickDelta = tickval + 256 - prevTick; + } else if (prevTick <= 100 && tickval >= 200) { + tickDelta = tickval - prevTick - 256; } else { - tick_delta = tickval - prev_tick; + tickDelta = tickval - prevTick; } - return [tick_delta, time_delta]; + return [tickDelta, timeDelta]; } TraktorS2MK3.fxHandler = function (field) { @@ -622,23 +624,10 @@ TraktorS2MK3.registerOutputPackets = function () { this.linkOutput("[Channel1]", "keylock", this.outputHandler); this.linkOutput("[Channel2]", "keylock", this.outputHandler); - this.linkOutput("[Channel1]", "hotcue_1_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_2_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_3_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_4_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_5_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_6_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_7_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel1]", "hotcue_8_enabled", this.hotcueOutputHandler); - - this.linkOutput("[Channel2]", "hotcue_1_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_2_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_3_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_4_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_5_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_6_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_7_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_8_enabled", this.hotcueOutputHandler); + for (var i = 1; i <= 8; ++i) { + this.linkOutput("[Channel1]", "hotcue_" + i + "_enabled", this.hotcueOutputHandler); + this.linkOutput("[Channel2]", "hotcue_" + i + "_enabled", this.hotcueOutputHandler); + } this.linkOutput("[Channel1]", "pfl", this.outputHandler); this.linkOutput("[Channel2]", "pfl", this.outputHandler); @@ -648,27 +637,14 @@ TraktorS2MK3.registerOutputPackets = function () { // VuMeter this.vuLeftConnection = engine.makeConnection("[Channel1]", "VuMeter", this.vuMeterHandler); this.vuRightConnection = engine.makeConnection("[Channel2]", "VuMeter", this.vuMeterHandler); - this.clipLeftConnection = engine.makeConnection("[Channel1]", "PeakIndicator", this.vuOutputHandler); - this.clipRightConnection = engine.makeConnection("[Channel2]", "PeakIndicator", this.vuOutputHandler); + this.clipLeftConnection = engine.makeConnection("[Channel1]", "PeakIndicator", this.peakOutputHandler); + this.clipRightConnection = engine.makeConnection("[Channel2]", "PeakIndicator", this.peakOutputHandler); // Sampler callbacks - this.samplerCallbacks.push(engine.makeConnection("[Sampler1]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler2]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler3]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler4]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler5]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler6]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler7]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler8]", "track_loaded", this.samplesOutputHandler)); - - this.samplerCallbacks.push(engine.makeConnection("[Sampler9]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler10]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler11]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler12]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler13]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler14]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler15]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler16]", "track_loaded", this.samplesOutputHandler)); + for (var i = 1; i <= 16; ++i) { + this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "track_loaded", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "play_indicator", this.samplesOutputHandler)); + } TraktorS2MK3.lightDeck(); } @@ -691,22 +667,30 @@ TraktorS2MK3.vuMeterHandler = function (value, group, key) { } } -TraktorS2MK3.vuOutputHandler = function (value, group, key) { - var led_value = 0x00; +TraktorS2MK3.peakOutputHandler = function (value, group, key) { + var ledValue = 0x00; if (value) { - led_value = 0x7E; + ledValue = 0x7E; } - TraktorS2MK3.controller.setOutput(group, key, led_value, true); + TraktorS2MK3.controller.setOutput(group, key, ledValue, true); } TraktorS2MK3.outputHandler = function (value, group, key) { - var led_value = 0x7C; - if (value) { - led_value = 0x7E; + HIDDebug("outputHandler.value: " + value); + var ledValue; + if (value === 0) { + // Off value + ledValue = 0x7C; + } else if (value === 1) { + // On value + ledValue = 0x7E; + } else { + // Custom value for multi-colored LEDs + ledValue = value; } - TraktorS2MK3.controller.setOutput(group, key, led_value, true); + TraktorS2MK3.controller.setOutput(group, key, ledValue, true); } TraktorS2MK3.hotcueOutputHandler = function (value, group, key) { @@ -720,7 +704,9 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { // Sampler 1-4, 9-12 -> Channel1 // Samples 5-8, 13-16 -> Channel2 var sampler = TraktorS2MK3.resolveSampler(group); - if (sampler > 0 && sampler < 5) { + if (sampler === undefined) { + return; + } else if (sampler > 0 && sampler < 5) { var deck = "[Channel1]"; var num = sampler; } else if (sampler > 4 && sampler < 9) { @@ -736,16 +722,28 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { // If we are in samples modes light corresponding LED if (TraktorS2MK3.padModeState[deck] === 1) { - TraktorS2MK3.outputHandler(value, deck, "hotcue_" + num + "_enabled"); + if (key === "play_indicator") { + if (value) { + // Green light on play + TraktorS2MK3.outputHandler(0x9E, deck, "hotcue_" + num + "_enabled"); + } else { + TraktorS2MK3.outputHandler(1, deck, "hotcue_" + num + "_enabled"); + } + } else if (key === "track_loaded") { + TraktorS2MK3.outputHandler(value, deck, "hotcue_" + num + "_enabled"); + } } } TraktorS2MK3.resolveSampler = function (group) { - if (group == undefined) + if (group == undefined) { return undefined; - var result = group.match(/\[Sampler[0-9]+\]/); - if (!result) + } + + if (!group.match(/\[Sampler[0-9]+\]/)) { return undefined; + } + var str = group.replace(/\[Sampler/, ""); return str.substring(0, str.length - 1); } @@ -773,23 +771,10 @@ TraktorS2MK3.lightDeck = function () { TraktorS2MK3.controller.setOutput("[Channel1]", "keylock", 0x7C, false); TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_1_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_2_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_3_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_4_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_5_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_6_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_7_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_8_enabled", 0x7C, false); - - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_1_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_2_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_3_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_4_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_5_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_6_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_7_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_8_enabled", 0x7C, false); + for (var i = 1; i <= 8; ++i) { + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_" + i + "_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_" + i + "_enabled", 0x7C, false); + } TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", 0x7C, false); TraktorS2MK3.controller.setOutput("[Channel2]", "pfl", 0x7C, false); @@ -846,18 +831,17 @@ TraktorS2MK3.incomingData = function (data, length) { /* Helper function to convert a string into raw bytes */ TraktorS2MK3.toBytes = function (data_string) { var data = Object(); - var ok = true; var splitted = data_string.split(/\s+/); data.length = splitted.length; for (j = 0; j < splitted.length; j++) { var byte_str = splitted[j]; if (byte_str.length !== 2) { - ok = false; - HIDDebug("not two characters?? " + byte_str); + HIDDebug("Not two characters: " + byte_str); + return {}; } var b = parseInt(byte_str, 16); if (b < 0 || b > 255) { - HIDDebug("number out of range: " + byte_str + " " + b); + HIDDebug("Number out of range: " + byte_str); return {}; } data[j] = b; From 0b0121d747aa42c727508a649adf2553f7610efe Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Thu, 10 Oct 2019 00:23:39 +0200 Subject: [PATCH 09/26] Traktor Kontrol S2MK3: Improved track selection & bugfixes --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index d068c044396..f792e8deb33 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -108,8 +108,8 @@ TraktorS2MK3.registerInputPackets = function () { // Loop control this.registerInputButton(messageShort, "[Channel1]", "!SelectLoop", 0x0A, 0x0F, this.selectLoopHandler); this.registerInputButton(messageShort, "[Channel2]", "!SelectLoop", 0x0B, 0xF0, this.selectLoopHandler); - this.registerInputButton(messageShort, "[Channel1]", "!LoadSelectedTrack", 0x07, 0x04, this.activateLoopHandler); - this.registerInputButton(messageShort, "[Channel2]", "!LoadSelectedTrack", 0x07, 0x20, this.activateLoopHandler); + this.registerInputButton(messageShort, "[Channel1]", "!ActivateLoop", 0x07, 0x04, this.activateLoopHandler); + this.registerInputButton(messageShort, "[Channel2]", "!ActivateLoop", 0x07, 0x20, this.activateLoopHandler); this.registerInputButton(messageShort, "[Channel1]", "!beatjump", 0x09, 0xF0, this.beatjumpHandler); this.registerInputButton(messageShort, "[Channel2]", "!beatjump", 0x0B, 0x0F, this.beatjumpHandler); @@ -360,21 +360,33 @@ TraktorS2MK3.headphoneHandler = function (field) { } TraktorS2MK3.selectTrackHandler = function (field) { + var delta = 1; if ((field.value + 1) % 16 == TraktorS2MK3.browseState[field.group]) { - engine.setValue("[Library]", "MoveUp", 1); + delta = -1; + } + + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue("[Library]", "MoveHorizontal", delta); } else { - engine.setValue("[Library]", "MoveDown", 1); + engine.setValue("[Library]", "MoveVertical", delta); } TraktorS2MK3.browseState[field.group] = field.value; } TraktorS2MK3.loadTrackHandler = function (field) { + HIDDebug("loadTrackHandler: " + field.group + " - " + field.value); if (field.value === 0) { return; } - engine.setValue(field.group, "LoadSelectedTrack", field.value); + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "eject", field.value); + // Reset eject button + engine.setValue(field.group, "eject", 0); + } else { + engine.setValue(field.group, "LoadSelectedTrack", field.value); + } } TraktorS2MK3.maximizeLibraryHandler = function (field) { @@ -417,10 +429,10 @@ TraktorS2MK3.quantizeHandler = function (field) { return; } - this.quantizeState = !this.quantizeState; - engine.setValue("[Channel1]", "quantize", this.quantizeState); - engine.setValue("[Channel2]", "quantize", this.quantizeState); - TraktorS2MK3.outputHandler(this.quantizeState, field.group, "quantize"); + var res = !(engine.getValue("[Channel1]", "quantize") && engine.getValue("[Channel2]", "quantize")); + engine.setValue("[Channel1]", "quantize", res); + engine.setValue("[Channel2]", "quantize", res); + TraktorS2MK3.outputHandler(res, field.group, "quantize"); } TraktorS2MK3.microphoneHandler = function (field) { @@ -474,10 +486,10 @@ TraktorS2MK3.jogHandler = function (field) { var tickDelta = deltas[0]; var timeDelta = deltas[1]; - var velocity = (tick_delta / timeDelta) / 3; + var velocity = (tickDelta / timeDelta) / 3; engine.setValue(field.group, "jog", velocity); if (engine.getValue(field.group, "scratch2_enable")) { - engine.scratchTick(deckNumber, tick_delta); + engine.scratchTick(deckNumber, tickDelta); } } } @@ -679,10 +691,10 @@ TraktorS2MK3.peakOutputHandler = function (value, group, key) { TraktorS2MK3.outputHandler = function (value, group, key) { HIDDebug("outputHandler.value: " + value); var ledValue; - if (value === 0) { + if (value === 0 || value === false) { // Off value ledValue = 0x7C; - } else if (value === 1) { + } else if (value === 1 || value === true) { // On value ledValue = 0x7E; } else { From 93b7a00ad40707f7ee64f924ae09eeb0c06e69b3 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Thu, 10 Oct 2019 02:11:04 +0200 Subject: [PATCH 10/26] Traktor Kontrol S2MK3: Reverse and slip mode --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 75 +++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index f792e8deb33..e4541e267c4 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -12,7 +12,6 @@ TraktorS2MK3 = new function () { this.beatjumpState = { "[Channel1]": 0, "[Channel2]": 0 }; this.fxButtonState = { 1: false, 2: false, 3: false, 4: false }; this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode - this.quantizeState = false; // false = Off, true = On // Microphone button this.microphonePressedTimer = 0; // Timer to distinguish between short and long press @@ -104,6 +103,8 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[Channel1]", "!MaximizeLibrary", 0x01, 0x08, this.maximizeLibraryHandler); this.registerInputButton(messageShort, "[Channel2]", "!MaximizeLibrary", 0x04, 0x20, this.maximizeLibraryHandler); + this.registerInputButton(messageShort, "[Channel1]", "!AddTrack", 0x01, 0x04, this.addTrackHandler); + this.registerInputButton(messageShort, "[Channel2]", "!AddTrack", 0x04, 0x10, this.addTrackHandler); // Loop control this.registerInputButton(messageShort, "[Channel1]", "!SelectLoop", 0x0A, 0x0F, this.selectLoopHandler); @@ -132,6 +133,16 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[ChannelX]", "!fx3", 0x03, 0x40, this.fxHandler); this.registerInputButton(messageShort, "[ChannelX]", "!fx4", 0x03, 0x80, this.fxHandler); + // Rev / FLX / GRID + this.registerInputButton(messageShort, "[Channel1]", "!reverse", 0x01, 0x01, this.reverseHandler); + this.registerInputButton(messageShort, "[Channel2]", "!reverse", 0x04, 0x04, this.reverseHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!flx", 0x01, 0x02, this.flxHandler); + this.registerInputButton(messageShort, "[Channel2]", "!flx", 0x04, 0x08, this.flxHandler); + + this.registerInputButton(messageShort, "[Channel1]", "!grid", 0x01, 0x10, this.gridHandler); + this.registerInputButton(messageShort, "[Channel2]", "!grid", 0x04, 0x40, this.gridHandler); + this.controller.registerInputPacket(messageShort); this.registerInputScaler(messageLong, "[Channel1]", "rate", 0x01, 0xFFFF, this.parameterHandler); @@ -361,7 +372,7 @@ TraktorS2MK3.headphoneHandler = function (field) { TraktorS2MK3.selectTrackHandler = function (field) { var delta = 1; - if ((field.value + 1) % 16 == TraktorS2MK3.browseState[field.group]) { + if ((field.value + 1) % 16 === TraktorS2MK3.browseState[field.group]) { delta = -1; } @@ -389,6 +400,10 @@ TraktorS2MK3.loadTrackHandler = function (field) { } } +TraktorS2MK3.addTrackHandler = function (field) { + // TODO: Not implemented yet +} + TraktorS2MK3.maximizeLibraryHandler = function (field) { if (field.value === 0) { return; @@ -399,7 +414,7 @@ TraktorS2MK3.maximizeLibraryHandler = function (field) { } TraktorS2MK3.selectLoopHandler = function (field) { - if ((field.value + 1) % 16 == TraktorS2MK3.loopSizeState[field.group]) { + if ((field.value + 1) % 16 === TraktorS2MK3.loopSizeState[field.group]) { engine.setValue(field.group, "loop_halve", 1); } else { engine.setValue(field.group, "loop_double", 1); @@ -416,10 +431,12 @@ TraktorS2MK3.activateLoopHandler = function (field) { } TraktorS2MK3.beatjumpHandler = function (field) { - if ((field.value + 1) % 16 == TraktorS2MK3.beatjumpState[field.group]) { + if ((field.value + 1) % 16 === TraktorS2MK3.beatjumpState[field.group]) { engine.setValue(field.group, "beatjump_backward", 1); + engine.setValue(field.group, "beatjump_backward", 0); } else { engine.setValue(field.group, "beatjump_forward", 1); + engine.setValue(field.group, "beatjump_forward", 0); } TraktorS2MK3.beatjumpState[field.group] = field.value; } @@ -552,6 +569,30 @@ TraktorS2MK3.fxHandler = function (field) { TraktorS2MK3.outputHandler(TraktorS2MK3.fxButtonState[fxNumber], field.group, "fxButton" + fxNumber); } +TraktorS2MK3.reverseHandler = function (field) { + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "reverseroll", field.value); + } else { + engine.setValue(field.group, "reverse", field.value); + } + + TraktorS2MK3.outputHandler(field.value, field.group, "reverse"); +} + +TraktorS2MK3.flxHandler = function (field) { + if (field.value === 0) { + return; + } + + var slip = engine.getValue(field.group, "slip_enabled"); + engine.setValue(field.group, "slip_enabled", !slip); + TraktorS2MK3.outputHandler(!slip, field.group, "flx"); +} + +TraktorS2MK3.gridHandler = function (field) { + // TODO: Not implemented yet +} + TraktorS2MK3.registerOutputPackets = function () { var output = new HIDPacket("output", 0x80); @@ -616,6 +657,18 @@ TraktorS2MK3.registerOutputPackets = function () { output.addOutput("[ChannelX]", "fxButton3", 0x18, "B"); output.addOutput("[ChannelX]", "fxButton4", 0x19, "B"); + output.addOutput("[Channel1]", "reverse", 0x01, "B"); + output.addOutput("[Channel2]", "reverse", 0x28, "B"); + + output.addOutput("[Channel1]", "flx", 0x02, "B"); + output.addOutput("[Channel2]", "flx", 0x29, "B"); + + output.addOutput("[Channel1]", "addTrack", 0x03, "B"); + output.addOutput("[Channel2]", "addTrack", 0x2A, "B"); + + output.addOutput("[Channel1]", "grid", 0x05, "B"); + output.addOutput("[Channel2]", "grid", 0x2C, "B"); + output.addOutput("[Channel1]", "MaximizeLibrary", 0x04, "B"); output.addOutput("[Channel2]", "MaximizeLibrary", 0x2B, "B"); @@ -748,7 +801,7 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { } TraktorS2MK3.resolveSampler = function (group) { - if (group == undefined) { + if (group === undefined) { return undefined; } @@ -796,6 +849,18 @@ TraktorS2MK3.lightDeck = function () { TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton3", 0x7C, false); TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton4", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "reverse", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "reverse", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "flx", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "flx", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", 0x7C, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "grid", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "grid", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", 0x7C, false); TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", 0x7C, false); From 688c341109f022f283ab158bd15b6d8876d10959 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Thu, 10 Oct 2019 02:37:16 +0200 Subject: [PATCH 11/26] Traktor Kontrol S2MK3: Better beatjump and loop handling --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index e4541e267c4..e2f76ad613a 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -427,17 +427,36 @@ TraktorS2MK3.activateLoopHandler = function (field) { return; } - engine.setValue(field.group, "beatloop_activate", field.value); + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "reloop_andstop", field.value); + } else { + engine.setValue(field.group, "reloop_toggle", field.value); + } } TraktorS2MK3.beatjumpHandler = function (field) { + var delta = 1; if ((field.value + 1) % 16 === TraktorS2MK3.beatjumpState[field.group]) { - engine.setValue(field.group, "beatjump_backward", 1); - engine.setValue(field.group, "beatjump_backward", 0); + delta = -1; + } + + if (TraktorS2MK3.shiftPressed[field.group]) { + var beatjump_size = engine.getValue(field.group, "beatjump_size"); + if (delta > 0) { + engine.setValue(field.group, "beatjump_size", beatjump_size * 2); + } else { + engine.setValue(field.group, "beatjump_size", beatjump_size / 2); + } } else { - engine.setValue(field.group, "beatjump_forward", 1); - engine.setValue(field.group, "beatjump_forward", 0); + if (delta < 0) { + engine.setValue(field.group, "beatjump_backward", 1); + engine.setValue(field.group, "beatjump_backward", 0); + } else { + engine.setValue(field.group, "beatjump_forward", 1); + engine.setValue(field.group, "beatjump_forward", 0); + } } + TraktorS2MK3.beatjumpState[field.group] = field.value; } @@ -854,7 +873,7 @@ TraktorS2MK3.lightDeck = function () { TraktorS2MK3.controller.setOutput("[Channel1]", "flx", 0x7C, false); TraktorS2MK3.controller.setOutput("[Channel2]", "flx", 0x7C, false); - + TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", 0x7C, false); TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", 0x7C, false); From 515268313d7f7fac29df77d9b2b709a6226dfd1f Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Thu, 17 Oct 2019 00:04:54 +0200 Subject: [PATCH 12/26] Traktor Kontrol S2MK3: GRID and preperation button --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index e2f76ad613a..47cf60d3e2a 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -1,6 +1,10 @@ -/**************************************************** - Traktor Kontrol S2 MK3 HID controller script v1.01 -****************************************************/ +/************************************************************************************/ +/* Traktor Kontrol S2 MK3 HID controller script v1.00 */ +/* Last modification: October 2019 */ +/* Author: Michael Schmidt */ +/* https://www.mixxx.org/wiki/doku.php/native_instruments_traktor_kontrol_s2_mk3 */ +/* */ +/************************************************************************************/ var TraktorS2MK3 = {}; @@ -204,6 +208,7 @@ TraktorS2MK3.registerInputPackets = function () { } TraktorS2MK3.registerInputJog = function (message, group, name, offset, bitmask, callback) { + // Jog wheels have 4 byte input message.addControl(group, name, offset, "I", bitmask); message.setCallback(group, name, callback); } @@ -386,7 +391,6 @@ TraktorS2MK3.selectTrackHandler = function (field) { } TraktorS2MK3.loadTrackHandler = function (field) { - HIDDebug("loadTrackHandler: " + field.group + " - " + field.value); if (field.value === 0) { return; } @@ -401,7 +405,17 @@ TraktorS2MK3.loadTrackHandler = function (field) { } TraktorS2MK3.addTrackHandler = function (field) { - // TODO: Not implemented yet + TraktorS2MK3.outputHandler(field.value, field.group, "addTrack"); + + if (field.value === 0) { + return; + } + + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue("[Library]", "AutoDjAddTop", field.value); + } else { + engine.setValue("[Library]", "AutoDjAddBottom", field.value); + } } TraktorS2MK3.maximizeLibraryHandler = function (field) { @@ -609,7 +623,13 @@ TraktorS2MK3.flxHandler = function (field) { } TraktorS2MK3.gridHandler = function (field) { - // TODO: Not implemented yet + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "beats_translate_match_alignment", field.value); + } else { + engine.setValue(field.group, "beats_translate_curpos", field.value); + } + + TraktorS2MK3.outputHandler(field.value, field.group, "grid"); } TraktorS2MK3.registerOutputPackets = function () { @@ -761,17 +781,14 @@ TraktorS2MK3.peakOutputHandler = function (value, group, key) { } TraktorS2MK3.outputHandler = function (value, group, key) { - HIDDebug("outputHandler.value: " + value); - var ledValue; + // Custom value for multi-colored LEDs + var ledValue = value; if (value === 0 || value === false) { // Off value ledValue = 0x7C; } else if (value === 1 || value === true) { // On value ledValue = 0x7E; - } else { - // Custom value for multi-colored LEDs - ledValue = value; } TraktorS2MK3.controller.setOutput(group, key, ledValue, true); @@ -892,7 +909,7 @@ TraktorS2MK3.lightDeck = function () { TraktorS2MK3.messageCallback = function (packet, data) { for (name in data) { field = data[name]; - HIDDebug("TraktorS2MK3: messageCallback - field: " + name); + HIDDebug("TraktorS2MK3.messageCallback() - field: " + name); TraktorS2MK3.controller.processButton(field); } } @@ -948,7 +965,6 @@ TraktorS2MK3.toBytes = function (data_string) { /* Helper function to send a binary string to the controller */ TraktorS2MK3.rawOutput = function (data_string) { - HIDDebug("TraktorS2MK3: Send raw output to controller ..."); var data = TraktorS2MK3.toBytes(data_string); controller.send(data, data.length, 0x80); } From 236b5ea8686e00be67a0d4bfbebd0f389650bcb7 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 22 Oct 2019 00:52:20 +0200 Subject: [PATCH 13/26] Traktor Kontrol S2MK3: JS code refactoring --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 174 +++++++++--------- 1 file changed, 92 insertions(+), 82 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 47cf60d3e2a..2f637a95bb3 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -1,18 +1,25 @@ -/************************************************************************************/ -/* Traktor Kontrol S2 MK3 HID controller script v1.00 */ -/* Last modification: October 2019 */ -/* Author: Michael Schmidt */ -/* https://www.mixxx.org/wiki/doku.php/native_instruments_traktor_kontrol_s2_mk3 */ -/* */ -/************************************************************************************/ - -var TraktorS2MK3 = {}; - -TraktorS2MK3 = new function () { +/////////////////////////////////////////////////////////////////////////////////// +// JSHint configuration // +/////////////////////////////////////////////////////////////////////////////////// +/* global engine */ +/* global HIDDebug */ +/* global HIDPacket */ +/* global HIDController */ +/* jshint -W016 */ +/////////////////////////////////////////////////////////////////////////////////// +/* */ +/* Traktor Kontrol S2 MK3 HID controller script v1.00 */ +/* Last modification: October 2019 */ +/* Author: Michael Schmidt */ +/* https://www.mixxx.org/wiki/doku.php/native_instruments_traktor_kontrol_s2_mk3 */ +/* */ +/////////////////////////////////////////////////////////////////////////////////// + +TraktorS2MK3 = function () { this.controller = new HIDController(); this.shiftPressed = { "[Channel1]": false, "[Channel2]": false }; this.browseState = { "[Channel1]": 0, "[Channel2]": 0 }; - this.loopSizeState = { "[Channel1]": 0, "[Channel1]": 0 }; + this.loopSizeState = { "[Channel1]": 0, "[Channel2]": 0 }; this.beatjumpState = { "[Channel1]": 0, "[Channel2]": 0 }; this.fxButtonState = { 1: false, 2: false, 3: false, 4: false }; this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode @@ -43,13 +50,15 @@ TraktorS2MK3 = new function () { 1: 5, 2: 6, 3: 7, 4: 8, 5: 13, 6: 14, 7: 15, 8: 16 } }; -} +}; + +var TraktorS2MK3 = new TraktorS2MK3(); TraktorS2MK3.init = function (id) { TraktorS2MK3.registerInputPackets(); TraktorS2MK3.registerOutputPackets(); HIDDebug("TraktorS2MK3: Init done!"); -} +}; TraktorS2MK3.registerInputPackets = function () { var messageShort = new HIDPacket("shortmessage", 0x01, this.messageCallback); @@ -205,23 +214,23 @@ TraktorS2MK3.registerInputPackets = function () { // Dirty hack to set initial values in the packet parser var data = TraktorS2MK3.toBytes("01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); TraktorS2MK3.incomingData(data); -} +}; TraktorS2MK3.registerInputJog = function (message, group, name, offset, bitmask, callback) { // Jog wheels have 4 byte input message.addControl(group, name, offset, "I", bitmask); message.setCallback(group, name, callback); -} +}; TraktorS2MK3.registerInputScaler = function (message, group, name, offset, bitmask, callback) { message.addControl(group, name, offset, "H", bitmask); message.setCallback(group, name, callback); -} +}; TraktorS2MK3.registerInputButton = function (message, group, name, offset, bitmask, callback) { message.addControl(group, name, offset, "B", bitmask); message.setCallback(group, name, callback); -} +}; TraktorS2MK3.playHandler = function (field) { if (field.value === 0) { @@ -234,7 +243,7 @@ TraktorS2MK3.playHandler = function (field) { var playing = engine.getValue(field.group, "play"); engine.setValue(field.group, "play", !playing); } -} +}; TraktorS2MK3.cueHandler = function (field) { if (field.value === 0) { @@ -246,13 +255,13 @@ TraktorS2MK3.cueHandler = function (field) { } else { engine.setValue(field.group, "cue_default", field.value); } -} +}; TraktorS2MK3.shiftHandler = function (field) { TraktorS2MK3.shiftPressed[field.group] = field.value; - var playing = engine.setValue("[Controls]", "touch_shift", field.value); + engine.setValue("[Controls]", "touch_shift", field.value); TraktorS2MK3.outputHandler(field.value, field.group, "shift"); -} +}; TraktorS2MK3.keylockHandler = function (field) { if (field.value === 0) { @@ -261,7 +270,7 @@ TraktorS2MK3.keylockHandler = function (field) { var keylock = engine.getValue(field.group, "keylock"); engine.setValue(field.group, "keylock", !keylock); -} +}; TraktorS2MK3.syncHandler = function (field) { if (TraktorS2MK3.shiftPressed[field.group]) { @@ -289,7 +298,7 @@ TraktorS2MK3.syncHandler = function (field) { } } } -} +}; TraktorS2MK3.syncTimer = function (group) { // Reset sync button timer if active @@ -297,7 +306,7 @@ TraktorS2MK3.syncTimer = function (group) { engine.stopTimer(TraktorS2MK3.syncPressedTimer[group]); TraktorS2MK3.syncPressedTimer[group] = 0; } -} +}; TraktorS2MK3.padModeHandler = function (field) { if (field.value === 0) { @@ -312,9 +321,11 @@ TraktorS2MK3.padModeHandler = function (field) { TraktorS2MK3.outputHandler(field.value, field.group, "samples"); // Light LEDs for all slots with loaded samplers - for (var k in TraktorS2MK3.samplerHotcuesRelation[field.group]) { - var loaded = engine.getValue("[Sampler" + TraktorS2MK3.samplerHotcuesRelation[field.group][k] + "]", "track_loaded"); - TraktorS2MK3.outputHandler(loaded, field.group, "hotcue_" + k + "_enabled"); + for (var key in TraktorS2MK3.samplerHotcuesRelation[field.group]) { + if (TraktorS2MK3.samplerHotcuesRelation[field.group].hasOwnProperty(key)) { + var loaded = engine.getValue("[Sampler" + TraktorS2MK3.samplerHotcuesRelation[field.group][key] + "]", "track_loaded"); + TraktorS2MK3.outputHandler(loaded, field.group, "hotcue_" + key + "_enabled"); + } } } else if (field.name === "!hotcues") { // If we are in samples mode and hotcues mode is activated @@ -328,7 +339,7 @@ TraktorS2MK3.padModeHandler = function (field) { TraktorS2MK3.outputHandler(active, field.group, "hotcue_" + i + "_enabled"); } } -} +}; TraktorS2MK3.numberButtonHandler = function (field) { if (field.value === 0) { @@ -364,7 +375,7 @@ TraktorS2MK3.numberButtonHandler = function (field) { } } } -} +}; TraktorS2MK3.headphoneHandler = function (field) { if (field.value === 0) { @@ -373,7 +384,7 @@ TraktorS2MK3.headphoneHandler = function (field) { var pfl = engine.getValue(field.group, "pfl"); engine.setValue(field.group, "pfl", !pfl); -} +}; TraktorS2MK3.selectTrackHandler = function (field) { var delta = 1; @@ -388,7 +399,7 @@ TraktorS2MK3.selectTrackHandler = function (field) { } TraktorS2MK3.browseState[field.group] = field.value; -} +}; TraktorS2MK3.loadTrackHandler = function (field) { if (field.value === 0) { @@ -402,7 +413,7 @@ TraktorS2MK3.loadTrackHandler = function (field) { } else { engine.setValue(field.group, "LoadSelectedTrack", field.value); } -} +}; TraktorS2MK3.addTrackHandler = function (field) { TraktorS2MK3.outputHandler(field.value, field.group, "addTrack"); @@ -416,7 +427,7 @@ TraktorS2MK3.addTrackHandler = function (field) { } else { engine.setValue("[Library]", "AutoDjAddBottom", field.value); } -} +}; TraktorS2MK3.maximizeLibraryHandler = function (field) { if (field.value === 0) { @@ -425,7 +436,7 @@ TraktorS2MK3.maximizeLibraryHandler = function (field) { var maximize = engine.getValue("[Master]", "maximize_library"); engine.setValue("[Master]", "maximize_library", !maximize); -} +}; TraktorS2MK3.selectLoopHandler = function (field) { if ((field.value + 1) % 16 === TraktorS2MK3.loopSizeState[field.group]) { @@ -434,7 +445,7 @@ TraktorS2MK3.selectLoopHandler = function (field) { engine.setValue(field.group, "loop_double", 1); } TraktorS2MK3.loopSizeState[field.group] = field.value; -} +}; TraktorS2MK3.activateLoopHandler = function (field) { if (field.value === 0) { @@ -446,7 +457,7 @@ TraktorS2MK3.activateLoopHandler = function (field) { } else { engine.setValue(field.group, "reloop_toggle", field.value); } -} +}; TraktorS2MK3.beatjumpHandler = function (field) { var delta = 1; @@ -472,7 +483,7 @@ TraktorS2MK3.beatjumpHandler = function (field) { } TraktorS2MK3.beatjumpState[field.group] = field.value; -} +}; TraktorS2MK3.quantizeHandler = function (field) { if (field.value === 0) { @@ -483,7 +494,7 @@ TraktorS2MK3.quantizeHandler = function (field) { engine.setValue("[Channel1]", "quantize", res); engine.setValue("[Channel2]", "quantize", res); TraktorS2MK3.outputHandler(res, field.group, "quantize"); -} +}; TraktorS2MK3.microphoneHandler = function (field) { if (field.value) { @@ -504,7 +515,7 @@ TraktorS2MK3.microphoneHandler = function (field) { engine.setValue("[Microphone]", "talkover", 0); } } -} +}; TraktorS2MK3.microphoneTimer = function () { // Reset microphone button timer if active @@ -512,23 +523,23 @@ TraktorS2MK3.microphoneTimer = function () { engine.stopTimer(TraktorS2MK3.microphonePressedTimer); TraktorS2MK3.microphonePressedTimer = 0; } -} +}; TraktorS2MK3.parameterHandler = function (field) { engine.setParameter(field.group, field.name, field.value / 4095); -} +}; TraktorS2MK3.jogTouchHandler = function (field) { - var deckNumber = TraktorS2MK3.controller.resolveDeck(group); + var deckNumber = TraktorS2MK3.controller.resolveDeck(field.group); if (field.value > 0) { engine.scratchEnable(deckNumber, 1024, 33.3333, 0.125, 0.125 / 8, true); } else { engine.scratchDisable(deckNumber); } -} +}; TraktorS2MK3.jogHandler = function (field) { - var deckNumber = TraktorS2MK3.controller.resolveDeck(group); + var deckNumber = TraktorS2MK3.controller.resolveDeck(field.group); // Jog wheel control is based on the S4MK2 mapping, might need some more review if (engine.isScratching(deckNumber)) { @@ -542,7 +553,7 @@ TraktorS2MK3.jogHandler = function (field) { engine.scratchTick(deckNumber, tickDelta); } } -} +}; TraktorS2MK3.wheelDeltas = function (group, value) { // When the wheel is touched, four bytes change, but only the first behaves predictably. @@ -584,7 +595,7 @@ TraktorS2MK3.wheelDeltas = function (group, value) { } return [tickDelta, timeDelta]; -} +}; TraktorS2MK3.fxHandler = function (field) { if (field.value === 0) { @@ -600,7 +611,7 @@ TraktorS2MK3.fxHandler = function (field) { engine.setValue(group, "group_[Channel1]_enable", TraktorS2MK3.fxButtonState[fxNumber]); engine.setValue(group, "group_[Channel2]_enable", TraktorS2MK3.fxButtonState[fxNumber]); TraktorS2MK3.outputHandler(TraktorS2MK3.fxButtonState[fxNumber], field.group, "fxButton" + fxNumber); -} +}; TraktorS2MK3.reverseHandler = function (field) { if (TraktorS2MK3.shiftPressed[field.group]) { @@ -610,7 +621,7 @@ TraktorS2MK3.reverseHandler = function (field) { } TraktorS2MK3.outputHandler(field.value, field.group, "reverse"); -} +}; TraktorS2MK3.flxHandler = function (field) { if (field.value === 0) { @@ -620,7 +631,7 @@ TraktorS2MK3.flxHandler = function (field) { var slip = engine.getValue(field.group, "slip_enabled"); engine.setValue(field.group, "slip_enabled", !slip); TraktorS2MK3.outputHandler(!slip, field.group, "flx"); -} +}; TraktorS2MK3.gridHandler = function (field) { if (TraktorS2MK3.shiftPressed[field.group]) { @@ -630,7 +641,7 @@ TraktorS2MK3.gridHandler = function (field) { } TraktorS2MK3.outputHandler(field.value, field.group, "grid"); -} +}; TraktorS2MK3.registerOutputPackets = function () { var output = new HIDPacket("output", 0x80); @@ -745,18 +756,18 @@ TraktorS2MK3.registerOutputPackets = function () { this.clipRightConnection = engine.makeConnection("[Channel2]", "PeakIndicator", this.peakOutputHandler); // Sampler callbacks - for (var i = 1; i <= 16; ++i) { + for (i = 1; i <= 16; ++i) { this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "track_loaded", this.samplesOutputHandler)); this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "play_indicator", this.samplesOutputHandler)); } TraktorS2MK3.lightDeck(); -} +}; /* Helper function to link output in a short form */ TraktorS2MK3.linkOutput = function (group, name, callback) { TraktorS2MK3.controller.linkOutput(group, name, group, name, callback); -} +}; TraktorS2MK3.vuMeterHandler = function (value, group, key) { var vuKeys = Object.keys(TraktorS2MK3.vuMeterThresholds); @@ -769,7 +780,7 @@ TraktorS2MK3.vuMeterHandler = function (value, group, key) { TraktorS2MK3.controller.setOutput(group, vuKeys[i], 0x7E, last); } } -} +}; TraktorS2MK3.peakOutputHandler = function (value, group, key) { var ledValue = 0x00; @@ -778,7 +789,7 @@ TraktorS2MK3.peakOutputHandler = function (value, group, key) { } TraktorS2MK3.controller.setOutput(group, key, ledValue, true); -} +}; TraktorS2MK3.outputHandler = function (value, group, key) { // Custom value for multi-colored LEDs @@ -792,33 +803,31 @@ TraktorS2MK3.outputHandler = function (value, group, key) { } TraktorS2MK3.controller.setOutput(group, key, ledValue, true); -} +}; TraktorS2MK3.hotcueOutputHandler = function (value, group, key) { // Light button LED only when we are in hotcue mode if (TraktorS2MK3.padModeState[group] === 0) { TraktorS2MK3.outputHandler(value, group, key); } -} +}; TraktorS2MK3.samplesOutputHandler = function (value, group, key) { // Sampler 1-4, 9-12 -> Channel1 // Samples 5-8, 13-16 -> Channel2 var sampler = TraktorS2MK3.resolveSampler(group); + var deck = "[Channel1]"; + var num = sampler; if (sampler === undefined) { return; - } else if (sampler > 0 && sampler < 5) { - var deck = "[Channel1]"; - var num = sampler; } else if (sampler > 4 && sampler < 9) { - var deck = "[Channel2]"; - var num = sampler - 4; + deck = "[Channel2]"; + num = sampler - 4; } else if (sampler > 8 && sampler < 13) { - var deck = "[Channel1]"; - var num = sampler - 4; + num = sampler - 4; } else if (sampler > 12 && sampler < 17) { - var deck = "[Channel2]"; - var num = sampler - 8; + deck = "[Channel2]"; + num = sampler - 8; } // If we are in samples modes light corresponding LED @@ -834,7 +843,7 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { TraktorS2MK3.outputHandler(value, deck, "hotcue_" + num + "_enabled"); } } -} +}; TraktorS2MK3.resolveSampler = function (group) { if (group === undefined) { @@ -847,7 +856,7 @@ TraktorS2MK3.resolveSampler = function (group) { var str = group.replace(/\[Sampler/, ""); return str.substring(0, str.length - 1); -} +}; TraktorS2MK3.lightDeck = function () { @@ -904,15 +913,16 @@ TraktorS2MK3.lightDeck = function () { // For the last output we should send the packet finally TraktorS2MK3.controller.setOutput("[Microphone]", "talkover", 0x7C, true); -} +}; TraktorS2MK3.messageCallback = function (packet, data) { - for (name in data) { - field = data[name]; - HIDDebug("TraktorS2MK3.messageCallback() - field: " + name); - TraktorS2MK3.controller.processButton(field); + for (var name in data) { + if (data.hasOwnProperty(name)) { + HIDDebug("TraktorS2MK3.messageCallback() - field: " + name); + TraktorS2MK3.controller.processButton(data[name]); + } } -} +}; TraktorS2MK3.shutdown = function () { @@ -935,18 +945,18 @@ TraktorS2MK3.shutdown = function () { this.rawOutput(data_string); HIDDebug("TraktorS2MK3: Shutdown done!"); -} +}; TraktorS2MK3.incomingData = function (data, length) { TraktorS2MK3.controller.parsePacket(data, length); -} +}; /* Helper function to convert a string into raw bytes */ TraktorS2MK3.toBytes = function (data_string) { - var data = Object(); + var data = {}; var splitted = data_string.split(/\s+/); data.length = splitted.length; - for (j = 0; j < splitted.length; j++) { + for (var j = 0; j < splitted.length; j++) { var byte_str = splitted[j]; if (byte_str.length !== 2) { HIDDebug("Not two characters: " + byte_str); @@ -961,10 +971,10 @@ TraktorS2MK3.toBytes = function (data_string) { } return data; -} +}; /* Helper function to send a binary string to the controller */ TraktorS2MK3.rawOutput = function (data_string) { var data = TraktorS2MK3.toBytes(data_string); - controller.send(data, data.length, 0x80); -} + TraktorS2MK3.controller.send(data, data.length, 0x80); +}; From b773bac2d93d6384bf79547d8593fb613daf2a7a Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Tue, 5 Nov 2019 01:48:21 +0100 Subject: [PATCH 14/26] Traktor Kontrol S2MK3: Fix review problems and remove raw data sending --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 100 ++++++++---------- 1 file changed, 47 insertions(+), 53 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 2f637a95bb3..68e05ef6576 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -15,7 +15,7 @@ /* */ /////////////////////////////////////////////////////////////////////////////////// -TraktorS2MK3 = function () { +var TraktorS2MK3 = new function () { this.controller = new HIDController(); this.shiftPressed = { "[Channel1]": false, "[Channel2]": false }; this.browseState = { "[Channel1]": 0, "[Channel2]": 0 }; @@ -52,8 +52,6 @@ TraktorS2MK3 = function () { }; }; -var TraktorS2MK3 = new TraktorS2MK3(); - TraktorS2MK3.init = function (id) { TraktorS2MK3.registerInputPackets(); TraktorS2MK3.registerOutputPackets(); @@ -761,7 +759,7 @@ TraktorS2MK3.registerOutputPackets = function () { this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "play_indicator", this.samplesOutputHandler)); } - TraktorS2MK3.lightDeck(); + TraktorS2MK3.lightDeck(false); }; /* Helper function to link output in a short form */ @@ -773,7 +771,7 @@ TraktorS2MK3.vuMeterHandler = function (value, group, key) { var vuKeys = Object.keys(TraktorS2MK3.vuMeterThresholds); for (var i = 0; i < vuKeys.length; ++i) { // Avoid spamming HID by only sending last LED update - var last = (i === vuKeys.length - 1); + var last = (i === (vuKeys.length - 1)); if (TraktorS2MK3.vuMeterThresholds[vuKeys[i]] > value) { TraktorS2MK3.controller.setOutput(group, vuKeys[i], 0x00, last); } else { @@ -858,74 +856,80 @@ TraktorS2MK3.resolveSampler = function (group) { return str.substring(0, str.length - 1); }; -TraktorS2MK3.lightDeck = function () { +TraktorS2MK3.lightDeck = function (switchOff) { + + var valueSoftLight = 0x7C; + var valueFullLight = 0x7E; + if(switchOff) { + valueSoftLight = 0x00; + valueFullLight = 0x00; + } - TraktorS2MK3.controller.setOutput("[Channel1]", "play_indicator", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "play_indicator", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "play_indicator", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "play_indicator", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "cue_indicator", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "cue_indicator", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "cue_indicator", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "cue_indicator", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "shift", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "shift", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "shift", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "shift", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "sync_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "sync_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "sync_enabled", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "sync_enabled", valueSoftLight, false); // Hotcues mode is default start value - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcues", 0x7E, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "samples", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcues", 0x7E, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "samples", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcues", valueFullLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcues", valueFullLight, false); + + TraktorS2MK3.controller.setOutput("[Channel1]", "samples", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "samples", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "keylock", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "keylock", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", valueSoftLight, false); for (var i = 1; i <= 8; ++i) { - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_" + i + "_enabled", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_" + i + "_enabled", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_" + i + "_enabled", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_" + i + "_enabled", valueSoftLight, false); } - TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "pfl", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "pfl", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton1", 0x7C, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton2", 0x7C, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton3", 0x7C, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton4", 0x7C, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton1", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton2", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton3", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton4", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "reverse", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "reverse", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "reverse", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "reverse", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "flx", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "flx", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "flx", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "flx", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "grid", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "grid", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "grid", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "grid", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", 0x7C, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", 0x7C, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", 0x7C, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", valueSoftLight, false); // For the last output we should send the packet finally - TraktorS2MK3.controller.setOutput("[Microphone]", "talkover", 0x7C, true); + TraktorS2MK3.controller.setOutput("[Microphone]", "talkover", valueSoftLight, true); }; TraktorS2MK3.messageCallback = function (packet, data) { for (var name in data) { if (data.hasOwnProperty(name)) { - HIDDebug("TraktorS2MK3.messageCallback() - field: " + name); TraktorS2MK3.controller.processButton(data[name]); } } }; TraktorS2MK3.shutdown = function () { - // Disconnect VuMeter callbacks this.vuLeftConnection.disconnect(); this.vuRightConnection.disconnect(); @@ -938,11 +942,7 @@ TraktorS2MK3.shutdown = function () { }); // Deactivate all LEDs - var data_string = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + - "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n" + - "00 00 00 00 00 00 00 00 00 00 00 00 00 00"; - this.rawOutput(data_string); + TraktorS2MK3.lightDeck(true); HIDDebug("TraktorS2MK3: Shutdown done!"); }; @@ -972,9 +972,3 @@ TraktorS2MK3.toBytes = function (data_string) { return data; }; - -/* Helper function to send a binary string to the controller */ -TraktorS2MK3.rawOutput = function (data_string) { - var data = TraktorS2MK3.toBytes(data_string); - TraktorS2MK3.controller.send(data, data.length, 0x80); -}; From c543eb2d39504512520f8e8dc2fd5795c195af4e Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 4 Dec 2019 00:58:52 +0100 Subject: [PATCH 15/26] Traktor Kontrol S2MK3: Forums link in XML --- res/controllers/Traktor Kontrol S2 MK3.hid.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/res/controllers/Traktor Kontrol S2 MK3.hid.xml b/res/controllers/Traktor Kontrol S2 MK3.hid.xml index 813083e5e44..556a7e217a1 100644 --- a/res/controllers/Traktor Kontrol S2 MK3.hid.xml +++ b/res/controllers/Traktor Kontrol S2 MK3.hid.xml @@ -5,6 +5,7 @@ Michael Schmidt HID Mapping for Traktor Kontrol S2 MK3 https://www.mixxx.org/wiki/doku.php/native_instruments_traktor_s2_mk3 + https://www.mixxx.org/forums/viewtopic.php?f=7&t=12999 From cfe54b3898ebc157d494a1eb8d5dac8242187b7f Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Wed, 4 Dec 2019 01:56:07 +0100 Subject: [PATCH 16/26] Traktor Kontrol S2MK3: Do not stop already expired SYNC timer --- res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 68e05ef6576..4dff41a6271 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -286,11 +286,8 @@ TraktorS2MK3.syncHandler = function (field) { engine.setValue(field.group, "sync_enabled", !sync); } else { // Button is released, check if timer is still running - if (TraktorS2MK3.syncPressedTimer[field.group] === 0) { - // long press -> sync lock - engine.stopTimer(TraktorS2MK3.syncPressedTimer[field.group]); - TraktorS2MK3.syncPressedTimer[field.group] = 0; - } else { + // If not expired disable sync, otherwise do nothing to lock sync + if (TraktorS2MK3.syncPressedTimer[field.group] !== 0) { // short press -> disable sync engine.setValue(field.group, "sync_enabled", 0); } From d8d6807ffd07237959ef6af4523085fc29783671 Mon Sep 17 00:00:00 2001 From: mi01 Date: Wed, 4 Dec 2019 02:25:20 +0100 Subject: [PATCH 17/26] Traktor Kontrol S2MK3: More precise intervals per rev Co-Authored-By: Jan Holthuis --- res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 4dff41a6271..24ba08ad07a 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -527,7 +527,7 @@ TraktorS2MK3.parameterHandler = function (field) { TraktorS2MK3.jogTouchHandler = function (field) { var deckNumber = TraktorS2MK3.controller.resolveDeck(field.group); if (field.value > 0) { - engine.scratchEnable(deckNumber, 1024, 33.3333, 0.125, 0.125 / 8, true); + engine.scratchEnable(deckNumber, 1024, 33 + 1/3, 0.125, 0.125 / 8, true); } else { engine.scratchDisable(deckNumber); } From 189ecce1d1964dd160dd4d5136e34ab84fe6a399 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 9 Dec 2019 00:56:56 +0100 Subject: [PATCH 18/26] Traktor Kontrol S2MK3: Use toggleControl() --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 24ba08ad07a..363cc8b35e3 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -238,8 +238,7 @@ TraktorS2MK3.playHandler = function (field) { if (TraktorS2MK3.shiftPressed[field.group]) { engine.setValue(field.group, "cue_set", field.value); } else { - var playing = engine.getValue(field.group, "play"); - engine.setValue(field.group, "play", !playing); + script.toggleControl(field.group, "play"); } }; @@ -266,8 +265,7 @@ TraktorS2MK3.keylockHandler = function (field) { return; } - var keylock = engine.getValue(field.group, "keylock"); - engine.setValue(field.group, "keylock", !keylock); + script.toggleControl(field.group, "keylock"); }; TraktorS2MK3.syncHandler = function (field) { @@ -282,8 +280,7 @@ TraktorS2MK3.syncHandler = function (field) { TraktorS2MK3.syncPressedTimer[field.group] = engine.beginTimer(1000, "TraktorS2MK3.syncTimer(\"" + field.group + "\")"); } - var sync = engine.getValue(field.group, "sync_enabled"); - engine.setValue(field.group, "sync_enabled", !sync); + script.toggleControl(field.group, "sync_enabled"); } else { // Button is released, check if timer is still running // If not expired disable sync, otherwise do nothing to lock sync @@ -377,8 +374,7 @@ TraktorS2MK3.headphoneHandler = function (field) { return; } - var pfl = engine.getValue(field.group, "pfl"); - engine.setValue(field.group, "pfl", !pfl); + script.toggleControl(field.group, "pfl"); }; TraktorS2MK3.selectTrackHandler = function (field) { @@ -429,8 +425,7 @@ TraktorS2MK3.maximizeLibraryHandler = function (field) { return; } - var maximize = engine.getValue("[Master]", "maximize_library"); - engine.setValue("[Master]", "maximize_library", !maximize); + script.toggleControl("[Master]", "maximize_library"); }; TraktorS2MK3.selectLoopHandler = function (field) { @@ -498,8 +493,7 @@ TraktorS2MK3.microphoneHandler = function (field) { TraktorS2MK3.microphonePressedTimer = engine.beginTimer(1000, "TraktorS2MK3.microphoneTimer()"); } - var talkover = engine.getValue("[Microphone]", "talkover"); - engine.setValue("[Microphone]", "talkover", !talkover); + script.toggleControl("[Microphone]", "talkover"); } else { // Button is released, check if timer is still running if (TraktorS2MK3.microphonePressedTimer !== 0) { From 2251216f6014ac76708b4e99572f7c5da7c03d4c Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Mon, 9 Dec 2019 20:17:12 +0100 Subject: [PATCH 19/26] Traktor Kontrol S2MK3: Sampler pregain knob --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 363cc8b35e3..565f95dcc87 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -178,6 +178,7 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputScaler(messageLong, "[Master]", "crossfader", 0x05, 0xFFFF, this.parameterHandler); this.registerInputScaler(messageLong, "[Master]", "gain", 0x15, 0xFFFF, this.parameterHandler); + this.registerInputScaler(messageLong, "[Sampler]", "pregain", 0x17, 0xFFFF, this.samplerPregainHandler); this.registerInputScaler(messageLong, "[Master]", "headMix", 0x19, 0xFFFF, this.parameterHandler); this.registerInputScaler(messageLong, "[Master]", "headGain", 0x1B, 0xFFFF, this.parameterHandler); @@ -209,6 +210,10 @@ TraktorS2MK3.registerInputPackets = function () { engine.softTakeover("[Master]", "headMix", true); engine.softTakeover("[Master]", "headGain", true); + for (var i = 1; i <= 16; ++i) { + engine.softTakeover("[Sampler" + i + "]", "pregain", true); + } + // Dirty hack to set initial values in the packet parser var data = TraktorS2MK3.toBytes("01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); TraktorS2MK3.incomingData(data); @@ -518,6 +523,14 @@ TraktorS2MK3.parameterHandler = function (field) { engine.setParameter(field.group, field.name, field.value / 4095); }; +TraktorS2MK3.samplerPregainHandler = function (field) { + // Map sampler gain knob of all sampler together. + // Dirty hack, but the best we can do for now. + for (var i = 1; i <= 16; ++i) { + engine.setParameter("[Sampler" + i + "]", field.name, field.value / 4095); + } +}; + TraktorS2MK3.jogTouchHandler = function (field) { var deckNumber = TraktorS2MK3.controller.resolveDeck(field.group); if (field.value > 0) { From 0aee2262394f10231734e88105c063b14f61b46b Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 19:24:50 +0100 Subject: [PATCH 20/26] Traktor Kontrol S2MK3: Implement review feedback --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 292 +++++++----------- 1 file changed, 120 insertions(+), 172 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 565f95dcc87..fec04aa30de 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -2,6 +2,7 @@ // JSHint configuration // /////////////////////////////////////////////////////////////////////////////////// /* global engine */ +/* global script */ /* global HIDDebug */ /* global HIDPacket */ /* global HIDController */ @@ -9,7 +10,7 @@ /////////////////////////////////////////////////////////////////////////////////// /* */ /* Traktor Kontrol S2 MK3 HID controller script v1.00 */ -/* Last modification: October 2019 */ +/* Last modification: December 2019 */ /* Author: Michael Schmidt */ /* https://www.mixxx.org/wiki/doku.php/native_instruments_traktor_kontrol_s2_mk3 */ /* */ @@ -18,12 +19,15 @@ var TraktorS2MK3 = new function () { this.controller = new HIDController(); this.shiftPressed = { "[Channel1]": false, "[Channel2]": false }; - this.browseState = { "[Channel1]": 0, "[Channel2]": 0 }; - this.loopSizeState = { "[Channel1]": 0, "[Channel2]": 0 }; - this.beatjumpState = { "[Channel1]": 0, "[Channel2]": 0 }; this.fxButtonState = { 1: false, 2: false, 3: false, 4: false }; this.padModeState = { "[Channel1]": 0, "[Channel2]": 0 }; // 0 = Hotcues Mode, 1 = Samples Mode + // Knob encoder states (hold values between 0x0 and 0xF) + // Rotate to the right is +1 and to the left is means -1 + this.browseKnobEncoderState = { "[Channel1]": 0, "[Channel2]": 0 }; + this.loopKnobEncoderState = { "[Channel1]": 0, "[Channel2]": 0 }; + this.moveKnobEncoderState = { "[Channel1]": 0, "[Channel2]": 0 }; + // Microphone button this.microphonePressedTimer = 0; // Timer to distinguish between short and long press @@ -39,7 +43,7 @@ var TraktorS2MK3 = new function () { this.vuRightConnection = {}; this.clipLeftConnection = {}; this.clipRightConnection = {}; - this.vuMeterThresholds = { "vu-18": (1 / 6), "vu-12": (1 / 6 * 2), "vu-6": (1 / 6 * 3), "vu0": (1 / 6 * 4), "vu6": (1 / 6 * 5) }; + this.vuMeterThresholds = { "vu-18": (1 / 6), "vu-12": (2 / 6), "vu-6": (3 / 6), "vu0": (4 / 6), "vu6": (5 / 6) }; // Sampler callbacks this.samplerCallbacks = []; @@ -83,24 +87,24 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[Channel1]", "!samples", 0x01, 0x80, this.padModeHandler); this.registerInputButton(messageShort, "[Channel2]", "!samples", 0x05, 0x02, this.padModeHandler); - // Hotcues - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_1", 0x02, 0x10, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_2", 0x02, 0x20, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_3", 0x02, 0x40, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_4", 0x02, 0x80, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_5", 0x03, 0x01, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_6", 0x03, 0x02, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_7", 0x03, 0x04, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel1]", "!hotcue_8", 0x03, 0x08, this.numberButtonHandler); - - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_1", 0x05, 0x40, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_2", 0x05, 0x80, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_3", 0x06, 0x01, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_4", 0x06, 0x02, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_5", 0x06, 0x04, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_6", 0x06, 0x08, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_7", 0x06, 0x10, this.numberButtonHandler); - this.registerInputButton(messageShort, "[Channel2]", "!hotcue_8", 0x06, 0x20, this.numberButtonHandler); + // Number pad buttons (Hotcues or Samplers depending on current mode) + this.registerInputButton(messageShort, "[Channel1]", "!pad_1", 0x02, 0x10, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_2", 0x02, 0x20, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_3", 0x02, 0x40, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_4", 0x02, 0x80, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_5", 0x03, 0x01, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_6", 0x03, 0x02, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_7", 0x03, 0x04, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel1]", "!pad_8", 0x03, 0x08, this.numberButtonHandler); + + this.registerInputButton(messageShort, "[Channel2]", "!pad_1", 0x05, 0x40, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_2", 0x05, 0x80, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_3", 0x06, 0x01, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_4", 0x06, 0x02, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_5", 0x06, 0x04, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_6", 0x06, 0x08, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_7", 0x06, 0x10, this.numberButtonHandler); + this.registerInputButton(messageShort, "[Channel2]", "!pad_8", 0x06, 0x20, this.numberButtonHandler); // Headphone buttons this.registerInputButton(messageShort, "[Channel1]", "!pfl", 0x04, 0x01, this.headphoneHandler); @@ -148,11 +152,11 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[Channel1]", "!reverse", 0x01, 0x01, this.reverseHandler); this.registerInputButton(messageShort, "[Channel2]", "!reverse", 0x04, 0x04, this.reverseHandler); - this.registerInputButton(messageShort, "[Channel1]", "!flx", 0x01, 0x02, this.flxHandler); - this.registerInputButton(messageShort, "[Channel2]", "!flx", 0x04, 0x08, this.flxHandler); + this.registerInputButton(messageShort, "[Channel1]", "!flx", 0x01, 0x02, this.fluxHandler); + this.registerInputButton(messageShort, "[Channel2]", "!flx", 0x04, 0x08, this.fluxHandler); - this.registerInputButton(messageShort, "[Channel1]", "!grid", 0x01, 0x10, this.gridHandler); - this.registerInputButton(messageShort, "[Channel2]", "!grid", 0x04, 0x40, this.gridHandler); + this.registerInputButton(messageShort, "[Channel1]", "!grid", 0x01, 0x10, this.beatgridHandler); + this.registerInputButton(messageShort, "[Channel2]", "!grid", 0x04, 0x40, this.beatgridHandler); this.controller.registerInputPacket(messageShort); @@ -215,7 +219,7 @@ TraktorS2MK3.registerInputPackets = function () { } // Dirty hack to set initial values in the packet parser - var data = TraktorS2MK3.toBytes("01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); + var data = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; TraktorS2MK3.incomingData(data); }; @@ -236,22 +240,14 @@ TraktorS2MK3.registerInputButton = function (message, group, name, offset, bitma }; TraktorS2MK3.playHandler = function (field) { - if (field.value === 0) { - return; - } - if (TraktorS2MK3.shiftPressed[field.group]) { - engine.setValue(field.group, "cue_set", field.value); - } else { + engine.setValue(field.group, "start_stop", field.value); + } else if (field.value === 1) { script.toggleControl(field.group, "play"); } }; TraktorS2MK3.cueHandler = function (field) { - if (field.value === 0) { - return; - } - if (TraktorS2MK3.shiftPressed[field.group]) { engine.setValue(field.group, "cue_gotoandstop", field.value); } else { @@ -279,32 +275,37 @@ TraktorS2MK3.syncHandler = function (field) { // Light LED while pressed TraktorS2MK3.outputHandler(field.value, field.group, "sync_enabled"); } else { - if (field.value) { - if (!TraktorS2MK3.syncPressedTimer[field.group]) { - // Start timer to measure how long button is pressed - TraktorS2MK3.syncPressedTimer[field.group] = engine.beginTimer(1000, "TraktorS2MK3.syncTimer(\"" + field.group + "\")"); - } + script.toggleControl(field.group, "beatsync"); - script.toggleControl(field.group, "sync_enabled"); + if (field.value) { + // Start timer to measure how long button is pressed + TraktorS2MK3.syncPressedTimer[field.group] = engine.beginTimer(300, function () { + // Reset sync button timer state if active + if (TraktorS2MK3.syncPressedTimer[field.group] !== 0) { + TraktorS2MK3.syncPressedTimer[field.group] = 0; + } + }, true); + + // Light corresponding LED when button is pressed + TraktorS2MK3.outputHandler(1, field.group, "sync_enabled"); } else { - // Button is released, check if timer is still running - // If not expired disable sync, otherwise do nothing to lock sync - if (TraktorS2MK3.syncPressedTimer[field.group] !== 0) { - // short press -> disable sync + if (engine.getValue(field.group, "sync_enabled")) { + // Deactivate sync lock engine.setValue(field.group, "sync_enabled", 0); + } else { + // Button is released, check if timer already expired + if (TraktorS2MK3.syncPressedTimer[field.group] === 0) { + // Timer expired -> long press -> enable sync lock + engine.setValue(field.group, "sync_enabled", 1); + } else { + // Timer still running -> just unlight LED + TraktorS2MK3.outputHandler(0, field.group, "sync_enabled"); + } } } } }; -TraktorS2MK3.syncTimer = function (group) { - // Reset sync button timer if active - if (TraktorS2MK3.syncPressedTimer[group] !== 0) { - engine.stopTimer(TraktorS2MK3.syncPressedTimer[group]); - TraktorS2MK3.syncPressedTimer[group] = 0; - } -}; - TraktorS2MK3.padModeHandler = function (field) { if (field.value === 0) { return; @@ -314,54 +315,48 @@ TraktorS2MK3.padModeHandler = function (field) { // If we are in hotcues mode and samples mode is activated engine.setValue("[Samplers]", "show_samplers", 1); TraktorS2MK3.padModeState[field.group] = 1; - TraktorS2MK3.outputHandler(!field.value, field.group, "hotcues"); - TraktorS2MK3.outputHandler(field.value, field.group, "samples"); + TraktorS2MK3.outputHandler(0, field.group, "hotcues"); + TraktorS2MK3.outputHandler(1, field.group, "samples"); // Light LEDs for all slots with loaded samplers for (var key in TraktorS2MK3.samplerHotcuesRelation[field.group]) { if (TraktorS2MK3.samplerHotcuesRelation[field.group].hasOwnProperty(key)) { var loaded = engine.getValue("[Sampler" + TraktorS2MK3.samplerHotcuesRelation[field.group][key] + "]", "track_loaded"); - TraktorS2MK3.outputHandler(loaded, field.group, "hotcue_" + key + "_enabled"); + TraktorS2MK3.outputHandler(loaded, field.group, "pad_" + key); } } } else if (field.name === "!hotcues") { // If we are in samples mode and hotcues mode is activated TraktorS2MK3.padModeState[field.group] = 0; - TraktorS2MK3.outputHandler(field.value, field.group, "hotcues"); - TraktorS2MK3.outputHandler(!field.value, field.group, "samples"); + TraktorS2MK3.outputHandler(1, field.group, "hotcues"); + TraktorS2MK3.outputHandler(0, field.group, "samples"); // Light LEDs for all enabled hotcues for (var i = 1; i <= 8; ++i) { var active = engine.getValue(field.group, "hotcue_" + i + "_enabled"); - TraktorS2MK3.outputHandler(active, field.group, "hotcue_" + i + "_enabled"); + TraktorS2MK3.outputHandler(active, field.group, "pad_" + i); } } }; TraktorS2MK3.numberButtonHandler = function (field) { - if (field.value === 0) { - return; - } - - var hotcueNumber = parseInt(field.id[field.id.length - 1]); + var padNumber = parseInt(field.id[field.id.length - 1]); if (TraktorS2MK3.padModeState[field.group] === 0) { // Hotcues mode if (TraktorS2MK3.shiftPressed[field.group]) { - engine.setValue(field.group, "hotcue_" + hotcueNumber + "_clear", field.value); + engine.setValue(field.group, "hotcue_" + padNumber + "_clear", field.value); } else { - engine.setValue(field.group, "hotcue_" + hotcueNumber + "_activate", field.value); + engine.setValue(field.group, "hotcue_" + padNumber + "_activate", field.value); } } else { // Samples mode - var sampler = TraktorS2MK3.samplerHotcuesRelation[field.group][hotcueNumber]; + var sampler = TraktorS2MK3.samplerHotcuesRelation[field.group][padNumber]; if (TraktorS2MK3.shiftPressed[field.group]) { - var playing = engine.getValue("[Sampler" + sampler + "]", "play_indicator"); + var playing = engine.getValue("[Sampler" + sampler + "]", "play"); if (playing) { engine.setValue("[Sampler" + sampler + "]", "cue_default", field.value); } else { engine.setValue("[Sampler" + sampler + "]", "eject", field.value); - // Reset eject button - engine.setValue("[Sampler" + sampler + "]", "eject", !field.value); } } else { var loaded = engine.getValue("[Sampler" + sampler + "]", "track_loaded"); @@ -384,7 +379,7 @@ TraktorS2MK3.headphoneHandler = function (field) { TraktorS2MK3.selectTrackHandler = function (field) { var delta = 1; - if ((field.value + 1) % 16 === TraktorS2MK3.browseState[field.group]) { + if ((field.value + 1) % 16 === TraktorS2MK3.browseKnobEncoderState[field.group]) { delta = -1; } @@ -394,18 +389,12 @@ TraktorS2MK3.selectTrackHandler = function (field) { engine.setValue("[Library]", "MoveVertical", delta); } - TraktorS2MK3.browseState[field.group] = field.value; + TraktorS2MK3.browseKnobEncoderState[field.group] = field.value; }; TraktorS2MK3.loadTrackHandler = function (field) { - if (field.value === 0) { - return; - } - if (TraktorS2MK3.shiftPressed[field.group]) { engine.setValue(field.group, "eject", field.value); - // Reset eject button - engine.setValue(field.group, "eject", 0); } else { engine.setValue(field.group, "LoadSelectedTrack", field.value); } @@ -414,10 +403,6 @@ TraktorS2MK3.loadTrackHandler = function (field) { TraktorS2MK3.addTrackHandler = function (field) { TraktorS2MK3.outputHandler(field.value, field.group, "addTrack"); - if (field.value === 0) { - return; - } - if (TraktorS2MK3.shiftPressed[field.group]) { engine.setValue("[Library]", "AutoDjAddTop", field.value); } else { @@ -434,12 +419,13 @@ TraktorS2MK3.maximizeLibraryHandler = function (field) { }; TraktorS2MK3.selectLoopHandler = function (field) { - if ((field.value + 1) % 16 === TraktorS2MK3.loopSizeState[field.group]) { - engine.setValue(field.group, "loop_halve", 1); + if ((field.value + 1) % 16 === TraktorS2MK3.loopKnobEncoderState[field.group]) { + script.triggerControl(field.group, "loop_halve"); } else { - engine.setValue(field.group, "loop_double", 1); + script.triggerControl(field.group, "loop_double"); } - TraktorS2MK3.loopSizeState[field.group] = field.value; + + TraktorS2MK3.loopKnobEncoderState[field.group] = field.value; }; TraktorS2MK3.activateLoopHandler = function (field) { @@ -456,7 +442,7 @@ TraktorS2MK3.activateLoopHandler = function (field) { TraktorS2MK3.beatjumpHandler = function (field) { var delta = 1; - if ((field.value + 1) % 16 === TraktorS2MK3.beatjumpState[field.group]) { + if ((field.value + 1) % 16 === TraktorS2MK3.moveKnobEncoderState[field.group]) { delta = -1; } @@ -469,15 +455,13 @@ TraktorS2MK3.beatjumpHandler = function (field) { } } else { if (delta < 0) { - engine.setValue(field.group, "beatjump_backward", 1); - engine.setValue(field.group, "beatjump_backward", 0); + script.triggerControl(field.group, "beatjump_backward"); } else { - engine.setValue(field.group, "beatjump_forward", 1); - engine.setValue(field.group, "beatjump_forward", 0); + script.triggerControl(field.group, "beatjump_forward"); } } - TraktorS2MK3.beatjumpState[field.group] = field.value; + TraktorS2MK3.moveKnobEncoderState[field.group] = field.value; }; TraktorS2MK3.quantizeHandler = function (field) { @@ -495,7 +479,12 @@ TraktorS2MK3.microphoneHandler = function (field) { if (field.value) { if (TraktorS2MK3.microphonePressedTimer === 0) { // Start timer to measure how long button is pressed - TraktorS2MK3.microphonePressedTimer = engine.beginTimer(1000, "TraktorS2MK3.microphoneTimer()"); + TraktorS2MK3.microphonePressedTimer = engine.beginTimer(300, function () { + // Reset microphone button timer status if active + if (TraktorS2MK3.microphonePressedTimer !== 0) { + TraktorS2MK3.microphonePressedTimer = 0; + } + }, true); } script.toggleControl("[Microphone]", "talkover"); @@ -503,7 +492,6 @@ TraktorS2MK3.microphoneHandler = function (field) { // Button is released, check if timer is still running if (TraktorS2MK3.microphonePressedTimer !== 0) { // short klick -> permanent activation - engine.stopTimer(TraktorS2MK3.microphonePressedTimer); TraktorS2MK3.microphonePressedTimer = 0; } else { engine.setValue("[Microphone]", "talkover", 0); @@ -511,14 +499,6 @@ TraktorS2MK3.microphoneHandler = function (field) { } }; -TraktorS2MK3.microphoneTimer = function () { - // Reset microphone button timer if active - if (TraktorS2MK3.microphonePressedTimer !== 0) { - engine.stopTimer(TraktorS2MK3.microphonePressedTimer); - TraktorS2MK3.microphonePressedTimer = 0; - } -}; - TraktorS2MK3.parameterHandler = function (field) { engine.setParameter(field.group, field.name, field.value / 4095); }; @@ -534,7 +514,7 @@ TraktorS2MK3.samplerPregainHandler = function (field) { TraktorS2MK3.jogTouchHandler = function (field) { var deckNumber = TraktorS2MK3.controller.resolveDeck(field.group); if (field.value > 0) { - engine.scratchEnable(deckNumber, 1024, 33 + 1/3, 0.125, 0.125 / 8, true); + engine.scratchEnable(deckNumber, 1024, 33 + 1 / 3, 0.125, 0.125 / 8, true); } else { engine.scratchDisable(deckNumber); } @@ -625,7 +605,7 @@ TraktorS2MK3.reverseHandler = function (field) { TraktorS2MK3.outputHandler(field.value, field.group, "reverse"); }; -TraktorS2MK3.flxHandler = function (field) { +TraktorS2MK3.fluxHandler = function (field) { if (field.value === 0) { return; } @@ -635,7 +615,7 @@ TraktorS2MK3.flxHandler = function (field) { TraktorS2MK3.outputHandler(!slip, field.group, "flx"); }; -TraktorS2MK3.gridHandler = function (field) { +TraktorS2MK3.beatgridHandler = function (field) { if (TraktorS2MK3.shiftPressed[field.group]) { engine.setValue(field.group, "beats_translate_match_alignment", field.value); } else { @@ -669,23 +649,23 @@ TraktorS2MK3.registerOutputPackets = function () { output.addOutput("[Channel1]", "keylock", 0x0A, "B"); output.addOutput("[Channel2]", "keylock", 0x31, "B"); - output.addOutput("[Channel1]", "hotcue_1_enabled", 0x0D, "B"); - output.addOutput("[Channel1]", "hotcue_2_enabled", 0x0E, "B"); - output.addOutput("[Channel1]", "hotcue_3_enabled", 0x0F, "B"); - output.addOutput("[Channel1]", "hotcue_4_enabled", 0x10, "B"); - output.addOutput("[Channel1]", "hotcue_5_enabled", 0x11, "B"); - output.addOutput("[Channel1]", "hotcue_6_enabled", 0x12, "B"); - output.addOutput("[Channel1]", "hotcue_7_enabled", 0x13, "B"); - output.addOutput("[Channel1]", "hotcue_8_enabled", 0x14, "B"); - - output.addOutput("[Channel2]", "hotcue_1_enabled", 0x34, "B"); - output.addOutput("[Channel2]", "hotcue_2_enabled", 0x35, "B"); - output.addOutput("[Channel2]", "hotcue_3_enabled", 0x36, "B"); - output.addOutput("[Channel2]", "hotcue_4_enabled", 0x37, "B"); - output.addOutput("[Channel2]", "hotcue_5_enabled", 0x38, "B"); - output.addOutput("[Channel2]", "hotcue_6_enabled", 0x39, "B"); - output.addOutput("[Channel2]", "hotcue_7_enabled", 0x3A, "B"); - output.addOutput("[Channel2]", "hotcue_8_enabled", 0x3B, "B"); + output.addOutput("[Channel1]", "pad_1", 0x0D, "B"); + output.addOutput("[Channel1]", "pad_2", 0x0E, "B"); + output.addOutput("[Channel1]", "pad_3", 0x0F, "B"); + output.addOutput("[Channel1]", "pad_4", 0x10, "B"); + output.addOutput("[Channel1]", "pad_5", 0x11, "B"); + output.addOutput("[Channel1]", "pad_6", 0x12, "B"); + output.addOutput("[Channel1]", "pad_7", 0x13, "B"); + output.addOutput("[Channel1]", "pad_8", 0x14, "B"); + + output.addOutput("[Channel2]", "pad_1", 0x34, "B"); + output.addOutput("[Channel2]", "pad_2", 0x35, "B"); + output.addOutput("[Channel2]", "pad_3", 0x36, "B"); + output.addOutput("[Channel2]", "pad_4", 0x37, "B"); + output.addOutput("[Channel2]", "pad_5", 0x38, "B"); + output.addOutput("[Channel2]", "pad_6", 0x39, "B"); + output.addOutput("[Channel2]", "pad_7", 0x3A, "B"); + output.addOutput("[Channel2]", "pad_8", 0x3B, "B"); output.addOutput("[Channel1]", "pfl", 0x1A, "B"); output.addOutput("[Channel2]", "pfl", 0x1B, "B"); @@ -742,8 +722,8 @@ TraktorS2MK3.registerOutputPackets = function () { this.linkOutput("[Channel2]", "keylock", this.outputHandler); for (var i = 1; i <= 8; ++i) { - this.linkOutput("[Channel1]", "hotcue_" + i + "_enabled", this.hotcueOutputHandler); - this.linkOutput("[Channel2]", "hotcue_" + i + "_enabled", this.hotcueOutputHandler); + TraktorS2MK3.controller.linkOutput("[Channel1]", "pad_" + i, "[Channel1]", "hotcue_" + i + "_enabled", this.hotcueOutputHandler); + TraktorS2MK3.controller.linkOutput("[Channel2]", "pad_" + i, "[Channel2]", "hotcue_" + i + "_enabled", this.hotcueOutputHandler); } this.linkOutput("[Channel1]", "pfl", this.outputHandler); @@ -760,7 +740,7 @@ TraktorS2MK3.registerOutputPackets = function () { // Sampler callbacks for (i = 1; i <= 16; ++i) { this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "track_loaded", this.samplesOutputHandler)); - this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "play_indicator", this.samplesOutputHandler)); + this.samplerCallbacks.push(engine.makeConnection("[Sampler" + i + "]", "play", this.samplesOutputHandler)); } TraktorS2MK3.lightDeck(false); @@ -834,15 +814,15 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { // If we are in samples modes light corresponding LED if (TraktorS2MK3.padModeState[deck] === 1) { - if (key === "play_indicator") { + if (key === "play" && engine.getValue(group, "track_loaded")) { if (value) { // Green light on play - TraktorS2MK3.outputHandler(0x9E, deck, "hotcue_" + num + "_enabled"); + TraktorS2MK3.outputHandler(0x9E, deck, "pad_" + num); } else { - TraktorS2MK3.outputHandler(1, deck, "hotcue_" + num + "_enabled"); + TraktorS2MK3.outputHandler(1, deck, "pad_" + num); } } else if (key === "track_loaded") { - TraktorS2MK3.outputHandler(value, deck, "hotcue_" + num + "_enabled"); + TraktorS2MK3.outputHandler(value, deck, "pad_" + num); } } }; @@ -852,19 +832,20 @@ TraktorS2MK3.resolveSampler = function (group) { return undefined; } - if (!group.match(/\[Sampler[0-9]+\]/)) { + var result = group.match(script.samplerRegEx); + + if (result === null) { return undefined; } - var str = group.replace(/\[Sampler/, ""); - return str.substring(0, str.length - 1); + // Return sample number + return result[1]; }; TraktorS2MK3.lightDeck = function (switchOff) { - var valueSoftLight = 0x7C; var valueFullLight = 0x7E; - if(switchOff) { + if (switchOff) { valueSoftLight = 0x00; valueFullLight = 0x00; } @@ -892,8 +873,8 @@ TraktorS2MK3.lightDeck = function (switchOff) { TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", valueSoftLight, false); for (var i = 1; i <= 8; ++i) { - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcue_" + i + "_enabled", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcue_" + i + "_enabled", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "pad_" + i, valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "pad_" + i, valueSoftLight, false); } TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", valueSoftLight, false); @@ -934,17 +915,6 @@ TraktorS2MK3.messageCallback = function (packet, data) { }; TraktorS2MK3.shutdown = function () { - // Disconnect VuMeter callbacks - this.vuLeftConnection.disconnect(); - this.vuRightConnection.disconnect(); - this.clipLeftConnection.disconnect(); - this.clipRightConnection.disconnect(); - - // Disconnect Sampler callbacks - this.samplerCallbacks.forEach(function (item) { - item.disconnect(); - }); - // Deactivate all LEDs TraktorS2MK3.lightDeck(true); @@ -954,25 +924,3 @@ TraktorS2MK3.shutdown = function () { TraktorS2MK3.incomingData = function (data, length) { TraktorS2MK3.controller.parsePacket(data, length); }; - -/* Helper function to convert a string into raw bytes */ -TraktorS2MK3.toBytes = function (data_string) { - var data = {}; - var splitted = data_string.split(/\s+/); - data.length = splitted.length; - for (var j = 0; j < splitted.length; j++) { - var byte_str = splitted[j]; - if (byte_str.length !== 2) { - HIDDebug("Not two characters: " + byte_str); - return {}; - } - var b = parseInt(byte_str, 16); - if (b < 0 || b > 255) { - HIDDebug("Number out of range: " + byte_str); - return {}; - } - data[j] = b; - } - - return data; -}; From e9a5eab3149b12c5335bb745e0b8c2d4c9a13ecc Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 20:42:52 +0100 Subject: [PATCH 21/26] Traktor Kontrol S2MK3: Comment on LED color --- res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index fec04aa30de..ed35b2cbace 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -424,7 +424,7 @@ TraktorS2MK3.selectLoopHandler = function (field) { } else { script.triggerControl(field.group, "loop_double"); } - + TraktorS2MK3.loopKnobEncoderState[field.group] = field.value; }; @@ -819,6 +819,7 @@ TraktorS2MK3.samplesOutputHandler = function (value, group, key) { // Green light on play TraktorS2MK3.outputHandler(0x9E, deck, "pad_" + num); } else { + // Reset LED to full white light TraktorS2MK3.outputHandler(1, deck, "pad_" + num); } } else if (key === "track_loaded") { From 2ab9ba0bfc3197dd83bf96580492f856b4043dcb Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 21:27:55 +0100 Subject: [PATCH 22/26] Traktor Kontrol S2MK3: Better SYNC button handling --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index ed35b2cbace..8d4fb87b296 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -275,32 +275,29 @@ TraktorS2MK3.syncHandler = function (field) { // Light LED while pressed TraktorS2MK3.outputHandler(field.value, field.group, "sync_enabled"); } else { - script.toggleControl(field.group, "beatsync"); - if (field.value) { - // Start timer to measure how long button is pressed - TraktorS2MK3.syncPressedTimer[field.group] = engine.beginTimer(300, function () { - // Reset sync button timer state if active - if (TraktorS2MK3.syncPressedTimer[field.group] !== 0) { - TraktorS2MK3.syncPressedTimer[field.group] = 0; - } - }, true); - - // Light corresponding LED when button is pressed - TraktorS2MK3.outputHandler(1, field.group, "sync_enabled"); - } else { - if (engine.getValue(field.group, "sync_enabled")) { + if (engine.getValue(field.group, 'sync_enabled') === 0) { + script.triggerControl(field.group, "beatsync"); + // Start timer to measure how long button is pressed + TraktorS2MK3.syncPressedTimer[field.group] = engine.beginTimer(300, function () { + engine.setValue(field.group, "sync_enabled", 1); + // Reset sync button timer state if active + if (TraktorS2MK3.syncPressedTimer[field.group] !== 0) { + TraktorS2MK3.syncPressedTimer[field.group] = 0; + } + }, true); + + // Light corresponding LED when button is pressed + TraktorS2MK3.outputHandler(1, field.group, "sync_enabled"); + } else { // Deactivate sync lock engine.setValue(field.group, "sync_enabled", 0); - } else { - // Button is released, check if timer already expired - if (TraktorS2MK3.syncPressedTimer[field.group] === 0) { - // Timer expired -> long press -> enable sync lock - engine.setValue(field.group, "sync_enabled", 1); - } else { - // Timer still running -> just unlight LED - TraktorS2MK3.outputHandler(0, field.group, "sync_enabled"); - } + } + } else { + if (TraktorS2MK3.syncPressedTimer[field.group] !== 0) { + // Timer still running -> stop it and unlight LED + engine.stopTimer(TraktorS2MK3.syncPressedTimer[field.group]); + TraktorS2MK3.outputHandler(0, field.group, "sync_enabled"); } } } From 6c96ff8f4cbd234c9bfc426183ccce413c60bb01 Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 22:13:53 +0100 Subject: [PATCH 23/26] Traktor Kontrol S2MK3: Better loop handling --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 8d4fb87b296..c037393947f 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -127,8 +127,11 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[Channel1]", "!ActivateLoop", 0x07, 0x04, this.activateLoopHandler); this.registerInputButton(messageShort, "[Channel2]", "!ActivateLoop", 0x07, 0x20, this.activateLoopHandler); - this.registerInputButton(messageShort, "[Channel1]", "!beatjump", 0x09, 0xF0, this.beatjumpHandler); - this.registerInputButton(messageShort, "[Channel2]", "!beatjump", 0x0B, 0x0F, this.beatjumpHandler); + // Beatjump + this.registerInputButton(messageShort, "[Channel1]", "!SelectBeatjump", 0x09, 0xF0, this.selectBeatjumpHandler); + this.registerInputButton(messageShort, "[Channel2]", "!SelectBeatjump", 0x0B, 0x0F, this.selectBeatjumpHandler); + this.registerInputButton(messageShort, "[Channel1]", "!ActivateBeatjump", 0x07, 0x02, this.activateBeatjumpHandler); + this.registerInputButton(messageShort, "[Channel2]", "!ActivateBeatjump", 0x07, 0x10, this.activateBeatjumpHandler); // There is only one button on the controller, we use to toggle quantization for all channels this.registerInputButton(messageShort, "[ChannelX]", "!quantize", 0x06, 0x40, this.quantizeHandler); @@ -291,6 +294,7 @@ TraktorS2MK3.syncHandler = function (field) { TraktorS2MK3.outputHandler(1, field.group, "sync_enabled"); } else { // Deactivate sync lock + // LED is turned off by the callback handler for sync_enabled engine.setValue(field.group, "sync_enabled", 0); } } else { @@ -431,13 +435,13 @@ TraktorS2MK3.activateLoopHandler = function (field) { } if (TraktorS2MK3.shiftPressed[field.group]) { - engine.setValue(field.group, "reloop_andstop", field.value); - } else { engine.setValue(field.group, "reloop_toggle", field.value); + } else { + engine.setValue(field.group, "beatloop_activate", field.value); } }; -TraktorS2MK3.beatjumpHandler = function (field) { +TraktorS2MK3.selectBeatjumpHandler = function (field) { var delta = 1; if ((field.value + 1) % 16 === TraktorS2MK3.moveKnobEncoderState[field.group]) { delta = -1; @@ -461,6 +465,18 @@ TraktorS2MK3.beatjumpHandler = function (field) { TraktorS2MK3.moveKnobEncoderState[field.group] = field.value; }; +TraktorS2MK3.activateBeatjumpHandler = function (field) { + if (field.value === 0) { + return; + } + + if (TraktorS2MK3.shiftPressed[field.group]) { + engine.setValue(field.group, "reloop_andstop", field.value); + } else { + engine.setValue(field.group, "beatlooproll_activate", field.value); + } +}; + TraktorS2MK3.quantizeHandler = function (field) { if (field.value === 0) { return; From f015034fbb82fea6e1672387db684fc353104f1f Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 23:00:11 +0100 Subject: [PATCH 24/26] Traktor Kontrol S2MK3: Set LEDs on startup according to current control state --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index c037393947f..30d0ab57bee 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -857,67 +857,82 @@ TraktorS2MK3.resolveSampler = function (group) { }; TraktorS2MK3.lightDeck = function (switchOff) { - var valueSoftLight = 0x7C; - var valueFullLight = 0x7E; + var softLight = 0x7C; + var fullLight = 0x7E; if (switchOff) { - valueSoftLight = 0x00; - valueFullLight = 0x00; + softLight = 0x00; + fullLight = 0x00; } - TraktorS2MK3.controller.setOutput("[Channel1]", "play_indicator", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "play_indicator", valueSoftLight, false); + var current = (!!engine.getValue("[Channel1]", "play_indicator") ? fullLight : softLight); + TraktorS2MK3.controller.setOutput("[Channel1]", "play_indicator", current, false); + current = (!!engine.getValue("[Channel2]", "play_indicator")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "play_indicator", current, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "cue_indicator", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "cue_indicator", valueSoftLight, false); + current = (!!engine.getValue("[Channel1]", "cue_indicator")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel1]", "cue_indicator", current, false); + current = (!!engine.getValue("[Channel2]", "cue_indicator")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "cue_indicator", current, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "shift", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "shift", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "shift", softLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "shift", softLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "sync_enabled", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "sync_enabled", valueSoftLight, false); + current = (!!engine.getValue("[Channel1]", "sync_enabled")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel1]", "sync_enabled", current, false); + current = (!!engine.getValue("[Channel1]", "sync_enabled")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "sync_enabled", current, false); // Hotcues mode is default start value - TraktorS2MK3.controller.setOutput("[Channel1]", "hotcues", valueFullLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "hotcues", valueFullLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "hotcues", fullLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "hotcues", fullLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "samples", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "samples", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "samples", softLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "samples", softLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "keylock", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", valueSoftLight, false); + current = (!!engine.getValue("[Channel1]", "keylock")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel1]", "keylock", current, false); + current = (!!engine.getValue("[Channel2]", "keylock")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "keylock", current, false); for (var i = 1; i <= 8; ++i) { - TraktorS2MK3.controller.setOutput("[Channel1]", "pad_" + i, valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "pad_" + i, valueSoftLight, false); + current = (!!engine.getValue("[Channel1]", "hotcue_" + i + "_enabled")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel1]", "pad_" + i, current, false); + current = (!!engine.getValue("[Channel2]", "hotcue_" + i + "_enabled")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "pad_" + i, current, false); } - TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "pfl", valueSoftLight, false); + current = (!!engine.getValue("[Channel1]", "pfl")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel1]", "pfl", current, false); + current = (!!engine.getValue("[Channel2]", "pfl")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "pfl", current, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton1", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton2", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton3", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton4", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton1", softLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton2", softLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton3", softLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "fxButton4", softLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "reverse", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "reverse", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "reverse", softLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "reverse", softLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "flx", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "flx", valueSoftLight, false); + current = (!!engine.getValue("[Channel1]", "slip_enabled")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel1]", "flx", current, false); + current = (!!engine.getValue("[Channel2]", "slip_enabled")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Channel2]", "flx", current, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", softLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", softLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "grid", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "grid", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "grid", softLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "grid", softLight, false); - TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", valueSoftLight, false); - TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "MaximizeLibrary", softLight, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "MaximizeLibrary", softLight, false); - TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", valueSoftLight, false); + TraktorS2MK3.controller.setOutput("[ChannelX]", "quantize", softLight, false); // For the last output we should send the packet finally - TraktorS2MK3.controller.setOutput("[Microphone]", "talkover", valueSoftLight, true); + current = (!!engine.getValue("[Microphone]", "talkover")) ? fullLight : softLight; + TraktorS2MK3.controller.setOutput("[Microphone]", "talkover", current, true); }; TraktorS2MK3.messageCallback = function (packet, data) { From ce4a4a443679887ab6a83abeac97dc179b8e4b3e Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 23:06:01 +0100 Subject: [PATCH 25/26] Traktor Kontrol S2MK3: Better slip mode handling --- .../Traktor-Kontrol-S2-MK3-hid-scripts.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 30d0ab57bee..11415dee089 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -151,12 +151,12 @@ TraktorS2MK3.registerInputPackets = function () { this.registerInputButton(messageShort, "[ChannelX]", "!fx3", 0x03, 0x40, this.fxHandler); this.registerInputButton(messageShort, "[ChannelX]", "!fx4", 0x03, 0x80, this.fxHandler); - // Rev / FLX / GRID + // Rev / FLUX / GRID this.registerInputButton(messageShort, "[Channel1]", "!reverse", 0x01, 0x01, this.reverseHandler); this.registerInputButton(messageShort, "[Channel2]", "!reverse", 0x04, 0x04, this.reverseHandler); - this.registerInputButton(messageShort, "[Channel1]", "!flx", 0x01, 0x02, this.fluxHandler); - this.registerInputButton(messageShort, "[Channel2]", "!flx", 0x04, 0x08, this.fluxHandler); + this.registerInputButton(messageShort, "[Channel1]", "!slip_enabled", 0x01, 0x02, this.fluxHandler); + this.registerInputButton(messageShort, "[Channel2]", "!slip_enabled", 0x04, 0x08, this.fluxHandler); this.registerInputButton(messageShort, "[Channel1]", "!grid", 0x01, 0x10, this.beatgridHandler); this.registerInputButton(messageShort, "[Channel2]", "!grid", 0x04, 0x40, this.beatgridHandler); @@ -623,9 +623,7 @@ TraktorS2MK3.fluxHandler = function (field) { return; } - var slip = engine.getValue(field.group, "slip_enabled"); - engine.setValue(field.group, "slip_enabled", !slip); - TraktorS2MK3.outputHandler(!slip, field.group, "flx"); + script.toggleControl(field.group, "slip_enabled"); }; TraktorS2MK3.beatgridHandler = function (field) { @@ -705,8 +703,8 @@ TraktorS2MK3.registerOutputPackets = function () { output.addOutput("[Channel1]", "reverse", 0x01, "B"); output.addOutput("[Channel2]", "reverse", 0x28, "B"); - output.addOutput("[Channel1]", "flx", 0x02, "B"); - output.addOutput("[Channel2]", "flx", 0x29, "B"); + output.addOutput("[Channel1]", "slip_enabled", 0x02, "B"); + output.addOutput("[Channel2]", "slip_enabled", 0x29, "B"); output.addOutput("[Channel1]", "addTrack", 0x03, "B"); output.addOutput("[Channel2]", "addTrack", 0x2A, "B"); @@ -742,6 +740,9 @@ TraktorS2MK3.registerOutputPackets = function () { this.linkOutput("[Channel1]", "pfl", this.outputHandler); this.linkOutput("[Channel2]", "pfl", this.outputHandler); + this.linkOutput("[Channel1]", "slip_enabled", this.outputHandler); + this.linkOutput("[Channel2]", "slip_enabled", this.outputHandler); + this.linkOutput("[Microphone]", "talkover", this.outputHandler); // VuMeter @@ -915,9 +916,9 @@ TraktorS2MK3.lightDeck = function (switchOff) { TraktorS2MK3.controller.setOutput("[Channel2]", "reverse", softLight, false); current = (!!engine.getValue("[Channel1]", "slip_enabled")) ? fullLight : softLight; - TraktorS2MK3.controller.setOutput("[Channel1]", "flx", current, false); + TraktorS2MK3.controller.setOutput("[Channel1]", "slip_enabled", current, false); current = (!!engine.getValue("[Channel2]", "slip_enabled")) ? fullLight : softLight; - TraktorS2MK3.controller.setOutput("[Channel2]", "flx", current, false); + TraktorS2MK3.controller.setOutput("[Channel2]", "slip_enabled", current, false); TraktorS2MK3.controller.setOutput("[Channel1]", "addTrack", softLight, false); TraktorS2MK3.controller.setOutput("[Channel2]", "addTrack", softLight, false); From 1698b2511e761542844168a3d275398ba70ae3dc Mon Sep 17 00:00:00 2001 From: Michael Schmidt Date: Fri, 13 Dec 2019 23:14:49 +0100 Subject: [PATCH 26/26] Traktor Kontrol S2MK3: Fix beatloop roll --- res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js index 11415dee089..74789054c6a 100644 --- a/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js +++ b/res/controllers/Traktor-Kontrol-S2-MK3-hid-scripts.js @@ -466,10 +466,6 @@ TraktorS2MK3.selectBeatjumpHandler = function (field) { }; TraktorS2MK3.activateBeatjumpHandler = function (field) { - if (field.value === 0) { - return; - } - if (TraktorS2MK3.shiftPressed[field.group]) { engine.setValue(field.group, "reloop_andstop", field.value); } else {