diff options
author | Antoine C <mixxx@acolombier.dev> | 2023-02-18 13:18:07 +0000 |
---|---|---|
committer | Antoine C <mixxx@acolombier.dev> | 2023-06-04 17:25:04 +0100 |
commit | 29cd34f664ce05dcfb674ebd01ee0af4b00deac8 (patch) | |
tree | 96007107c692d24baa58b219df0b9fae3713d418 | |
parent | 7170863e66c798a9786dc3d4166c7d0eac3cac97 (diff) |
Kontrol S4 Mk3: adding settings for various behavior and small refactor
-rw-r--r-- | res/controllers/Traktor-Kontrol-S4-MK3.js | 220 |
1 files changed, 132 insertions, 88 deletions
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3.js b/res/controllers/Traktor-Kontrol-S4-MK3.js index b7601607fa..0494a2cc18 100644 --- a/res/controllers/Traktor-Kontrol-S4-MK3.js +++ b/res/controllers/Traktor-Kontrol-S4-MK3.js @@ -82,6 +82,18 @@ const loopEncoderShiftMoveFactor = 2500; const tempoFaderSoftTakeoverColorLow = LEDColors.white; const tempoFaderSoftTakeoverColorHigh = LEDColors.green; +// Define whether or not to keep LED that have only one color (reverse, flux, play) dimmed if they are inactive. +// 'true' will keep them dimmed, 'false' will turn them off. Default: true +const keepLEDWithOneColorDimedWhenInactive = true; + +// Define whether the keylock is mapped when doing "shift+master" (on press) or "shift+sync" (on release since long push copies the key)". +// 'true' will use "sync+master", 'false' will use "shift+sync". Default: false +const useKeylockOnMaster = false; + +// Define whether the grid button would blink when the playback is going over a detcted beat. Can help to adjust beat grid. +// Default: true +const gridButtonBlinkOverBeat = true; + // The LEDs only support 16 base colors. Adding 1 in addition to // the normal 2 for Button.prototype.brightnessOn changes the color // slightly, so use that get 25 different colors to include the Filter @@ -229,11 +241,16 @@ class HIDOutputPacket { class Component { constructor(options) { - Object.assign(this, options); + if (options) { + Object.keys(options).forEach(function(key) { + if (options[key] === undefined) { delete options[key]; } + }); + Object.assign(this, options); + } this.outConnections = []; - if (options !== undefined && typeof options.key === "string") { - this.inKey = options.key; - this.outKey = options.key; + if (typeof this.key === "string") { + this.inKey = this.key; + this.outKey = this.key; } if (this.unshift !== undefined && typeof this.unshift === "function") { this.unshift(); @@ -273,7 +290,13 @@ class Component { } outConnect() { if (this.outKey !== undefined && this.group !== undefined) { - this.outConnections[0] = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); + const connection = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); + // This is useful for case where effect would have been fully disabled in Mixxx. This appears to be the case during unit tests. + if (connection) { + this.outConnections[0] = connection; + } else { + console.warn("Unable to connect '"+this.group+"."+this.outKey+"' to the controller output. The control appears to be unaivailable."); + } } } outDisconnect() { @@ -422,6 +445,16 @@ class Button extends Component { this.inBitLength = 1; } } + setKey(key) { + this.inKey = key; + if (key === this.outKey) { + return; + } + this.outDisconnect(); + this.outKey = key; + this.outConnect(); + this.outTrigger(); + } output(value) { if (this.indicatorTimer !== 0) { return; @@ -610,11 +643,21 @@ class HotcueButton extends PushButton { } outConnect() { if (undefined !== this.group) { - this.outConnections[0] = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); - this.outConnections[1] = engine.makeConnection(this.group, this.colorKey, (colorCode) => { + const connection0 = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); + if (connection0) { + this.outConnections[0] = connection0; + } else { + console.warn("Unable to connect '"+this.group+"."+this.outKey+"' to the controller output. The control appears to be unaivailable."); + } + const connection1 = engine.makeConnection(this.group, this.colorKey, (colorCode) => { this.color = this.colorMap.getValueForNearestColor(colorCode); this.output(engine.getValue(this.group, this.outKey)); }); + if (connection1) { + this.outConnections[1] = connection1; + } else { + console.warn("Unable to connect '"+this.group+"."+this.colorKey+"' to the controller output. The control appears to be unaivailable."); + } } } } @@ -660,10 +703,15 @@ class KeyboardButton extends PushButton { } outConnect() { if (undefined !== this.group) { - this.outConnections[0] = engine.makeConnection(this.group, "key", (key) => { + const connection = engine.makeConnection(this.group, "key", (key) => { const offset = this.deck.keyboardOffset - (this.shifted ? 8 : 0); this.output(key === this.number + offset); }); + if (connection) { + this.outConnections[0] = connection; + } else { + console.warn("Unable to connect '"+this.group+".key' to the controller output. The control appears to be unaivailable."); + } } } } @@ -715,8 +763,18 @@ class SamplerButton extends Button { } outConnect() { if (undefined !== this.group) { - this.outConnections[0] = engine.makeConnection(this.group, "play", this.output.bind(this)); - this.outConnections[1] = engine.makeConnection(this.group, "track_loaded", this.output.bind(this)); + const connection0 = engine.makeConnection(this.group, "play", this.output.bind(this)); + if (connection0) { + this.outConnections[0] = connection0; + } else { + console.warn("Unable to connect '"+this.group+".play' to the controller output. The control appears to be unaivailable."); + } + const connection1 = engine.makeConnection(this.group, "track_loaded", this.output.bind(this)); + if (connection1) { + this.outConnections[1] = connection1; + } else { + console.warn("Unable to connect '"+this.group+".track_loaded' to the controller output. The control appears to be unaivailable."); + } } } } @@ -1061,8 +1119,18 @@ class QuickEffectButton extends Button { } outConnect() { if (this.group !== undefined) { - this.outConnections[0] = engine.makeConnection(this.group, "loaded_chain_preset", this.presetLoaded.bind(this)); - this.outConnections[1] = engine.makeConnection(this.group, "enabled", this.output.bind(this)); + const connection0 = engine.makeConnection(this.group, "loaded_chain_preset", this.presetLoaded.bind(this)); + if (connection0) { + this.outConnections[0] = connection0; + } else { + console.warn("Unable to connect '"+this.group+".loaded_chain_preset' to the controller output. The control appears to be unaivailable."); + } + const connection1 = engine.makeConnection(this.group, "enabled", this.output.bind(this)); + if (connection1) { + this.outConnections[1] = connection1; + } else { + console.warn("Unable to connect '"+this.group+".enabled' to the controller output. The control appears to be unaivailable."); + } } } } @@ -1080,6 +1148,13 @@ Encoder.prototype.inBitLength = 4; // valid range 0 - 3, but 3 makes some colors appear whitish Button.prototype.brightnessOff = 0; Button.prototype.brightnessOn = 2; +Button.prototype.uncoloredOutput = function(value) { + if (this.indicatorTimer !== 0) { + return; + } + const color = (value > 0) ? (this.color || LEDColors.white) + this.brightnessOn : LEDColors.off; + this.send(color); +}; Button.prototype.colorMap = new ColorMapper({ 0xCC0000: LEDColors.red, 0xCC5E00: LEDColors.carrot, @@ -1150,17 +1225,6 @@ let wheelTimerDelta = 0; * Kontrol S4 Mk3 hardware specific mapping logic */ -// used for buttons whose LEDs only support a single color -// Don't use dim colors for these because they are hard to tell apart -// from bright colors. -const uncoloredButtonOutput = function(value) { - if (value) { - this.send((this.color || LEDColors.white) + this.brightnessOn); - } else { - this.send((this.color || LEDColors.white) + this.brightnessOff); - } -}; - class S4Mk3EffectUnit extends ComponentContainer { constructor(unitNumber, inPackets, outPacket, io) { super(); @@ -1177,7 +1241,6 @@ class S4Mk3EffectUnit extends ComponentContainer { this.mainButton = new PowerWindowButton({ unit: this, - output: uncoloredButtonOutput, inPacket: inPackets[1], inByte: io.mainButton.inByte, inBit: io.mainButton.inBit, @@ -1193,7 +1256,7 @@ class S4Mk3EffectUnit extends ComponentContainer { this.outDisconnect(); this.outKey = undefined; this.group = undefined; - uncoloredButtonOutput.call(this, false); + this.output(false); }, input: function(pressed) { if (!this.shifted) { @@ -1227,7 +1290,6 @@ class S4Mk3EffectUnit extends ComponentContainer { unit: this, key: "enabled", group: effectGroup, - output: uncoloredButtonOutput, inPacket: inPackets[1], inByte: io.buttons[index].inByte, inBit: io.buttons[index].inBit, @@ -1290,7 +1352,7 @@ class S4Mk3Deck extends Deck { super(decks, colors); this.playButton = new PlayButton({ - output: uncoloredButtonOutput + output: keepLEDWithOneColorDimedWhenInactive ? undefined : Button.prototype.uncoloredOutput }); this.cueButton = new CueButton({ @@ -1301,7 +1363,12 @@ class S4Mk3Deck extends Deck { this.syncMasterButton = new Button({ key: "sync_leader", defaultRange: 0.08, - output: uncoloredButtonOutput, + shift: useKeylockOnMaster ? function() { + this.setKey("keylock"); + } : undefined, + unshift: useKeylockOnMaster ? function() { + this.setKey("sync_leader"); + } : undefined, onShortRelease: function() { script.toggleControl(this.group, this.inKey); }, @@ -1318,7 +1385,6 @@ class S4Mk3Deck extends Deck { }); this.syncButton = new Button({ key: "sync_enabled", - output: uncoloredButtonOutput, onLongPress: function() { if (this.shifted) { engine.setValue(this.group, "sync_key", true); @@ -1333,20 +1399,12 @@ class S4Mk3Deck extends Deck { engine.softTakeover(this.group, "rate", true); } }, - shift: function() { - this.outDisconnect(); - this.inKey = "keylock"; - this.outKey = "keylock"; - this.outConnect(); - this.outTrigger(); - }, - unshift: function() { - this.outDisconnect(); - this.inKey = "sync_enabled"; - this.outKey = "sync_enabled"; - this.outConnect(); - this.outTrigger(); - }, + shift: !useKeylockOnMaster ? function() { + this.setKey("keylock"); + } : undefined, + unshift: !useKeylockOnMaster ? function() { + this.setKey("sync_enabled"); + } : undefined, }); this.tempoFader = new Pot({ inKey: "rate", @@ -1388,19 +1446,13 @@ class S4Mk3Deck extends Deck { previousWheelMode: null, loopModeConnection: null, unshift: function() { - this.outDisconnect(); - this.outKey = "reverseroll"; - this.outConnect(); - this.outTrigger(); + this.setKey("reverseroll"); }, shift: function() { - this.outDisconnect(); - this.outKey = "loop_enabled"; - this.outConnect(); - this.outTrigger(); + this.setKey("loop_enabled"); }, - output: uncoloredButtonOutput, + output: keepLEDWithOneColorDimedWhenInactive ? undefined : Button.prototype.uncoloredOutput, onShortRelease: function() { if (!this.shifted) { engine.setValue(this.group, this.key, false); @@ -1473,24 +1525,23 @@ class S4Mk3Deck extends Deck { previousWheelMode: null, loopModeConnection: null, unshift: function() { - this.outDisconnect(); - this.outKey = "slip_enabled"; - this.outConnect(); - this.outTrigger(); + this.setKey("slip_enabled"); }, shift: function() { - this.outDisconnect(); - this.outKey = "loop_enabled"; - this.outConnect(); - this.outTrigger(); + this.setKey("loop_enabled"); }, outConnect: function() { if (this.outKey !== undefined && this.group !== undefined) { - this.outConnections[0] = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); + const connection = engine.makeConnection(this.group, this.outKey, this.output.bind(this)); + if (connection) { + this.outConnections[0] = connection; + } else { + console.warn("Unable to connect '"+this.group+"."+this.outKey+"' to the controller output. The control appears to be unaivailable."); + } } }, - output: uncoloredButtonOutput, + output: keepLEDWithOneColorDimedWhenInactive ? undefined : Button.prototype.uncoloredOutput, onShortRelease: function() { if (!this.shifted) { engine.setValue(this.group, this.key, false); @@ -1559,7 +1610,7 @@ class S4Mk3Deck extends Deck { } }); this.gridButton = new Button({ - key: "beat_active", + key: gridButtonBlinkOverBeat ? "beat_active" : undefined, deck: this, previousMoveMode: null, onShortPress: function() { @@ -2185,30 +2236,27 @@ class S4Mk3Deck extends Deck { engine.setValue(this.group, "scratch2", ttAvgSpeed / baseRevolutionsPerSecond); break; case wheelModes.loopIn: - engine.setValue( - this.group, - "loop_start_position", - Math.min( - engine.getValue( - this.group, - "loop_start_position" - ) + (avgSpeed * loopWheelMoveFactor), - engine.getValue( - this.group, - "loop_end_position" - ) - loopWheelMoveFactor - ) - ); + { + const loopStartPosition = engine.getValue(this.group, "loop_start_position"); + const loopEndPosition = engine.getValue(this.group, "loop_end_position"); + const value = Math.min(loopStartPosition + (avgSpeed * loopWheelMoveFactor), loopEndPosition - loopWheelMoveFactor); + engine.setValue( + this.group, + "loop_start_position", + value + ); + } break; case wheelModes.loopOut: - engine.setValue( - this.group, - "loop_end_position", - engine.getValue( + { + const loopEndPosition = engine.getValue(this.group, "loop_end_position"); + const value = loopEndPosition + (avgSpeed * loopWheelMoveFactor); + engine.setValue( this.group, - "loop_end_position" - ) + (avgSpeed * loopWheelMoveFactor) - ); + "loop_end_position", + value + ); + } break; case wheelModes.vinyl: if (this.deck.wheelTouch.touched || engine.getValue(this.group, "scratch2") !== 0) { @@ -2343,26 +2391,22 @@ class S4Mk3MixerColumn extends ComponentContainer { this.pfl = new ToggleButton({ inKey: "pfl", outKey: "pfl", - output: uncoloredButtonOutput, }); this.effectUnit1Assign = new PowerWindowButton({ group: "[EffectRack1_EffectUnit1]", key: "group_" + this.group + "_enable", - output: uncoloredButtonOutput, }); this.effectUnit2Assign = new PowerWindowButton({ group: "[EffectRack1_EffectUnit2]", key: "group_" + this.group + "_enable", - output: uncoloredButtonOutput, }); // FIXME: Why is output not working for these? this.saveGain = new PushButton({ key: "update_replaygain_from_pregain", group: group, - output: uncoloredButtonOutput, }); this.crossfaderSwitch = new Component({ |