Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions res/controllers/Traktor Kontrol S4 MK3.hid.xml
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,28 @@
This color defines how the LED next to the tempo fader will be lit when the actual BPM is lower than the fader value.
</description>
</option>
<option
variable="tempoFaderRelativeMove"
type="enum"
label="Tempo fader relative move color">
<value label="Aqua">aqua</value>
<value label="Azalea">azalea</value>
<value label="Blue" default="true">blue</value>
<value label="Celeste">celeste</value>
<value label="Fuscia">fuscia</value>
<value label="Green">green</value>
<value label="Lime">lime</value>
<value label="Orange">orange</value>
<value label="Purple">purple</value>
<value label="Red">red</value>
<value label="Salmon">salmon</value>
<value label="Sky">sky</value>
<value label="White">white</value>
<value label="Yellow">yellow</value>
<description>
This color defines how the LED next to the tempo fader will be lit when the tempo fader is using the relative mode.
</description>
</option>
</group>

<group label="Tempo Fader">
Expand Down Expand Up @@ -252,6 +274,35 @@
Define the default layout used for the pads.
</description>
</option>
<option
variable="alternativeMoveMode"
type="boolean"
default="false"
label="Alternative assignments for move encoder">
<description>
Move encoder: Adjust beatjump size
:hwbtn:`SHIFT` + move encoder: Beatjump
Push + move encoder: adjust pitch up and down.
</description>
</option>
<option
variable="defaultRelativeTempoMode"
type="boolean"
default="false"
label="Tempo fader uses the relative movement mode by default">
<description>
No need for soft takeover. :hwbtn:`SHIFT` + tempo fader does not change tempo.
</description>
</option>
<option
variable="shiftTempoMode"
type="boolean"
default="false"
label="For Relative mode, Tempo does not adjust unless :hwbtn:`SHIFT` is held">
<description>
Helps protect against accidental bumps.
</description>
</option>
</group>

<group label="Mixer">
Expand Down Expand Up @@ -797,6 +848,19 @@
Define the sensitivity factor when the jogwheel is used to move the in and out point of a loop using the Loop Mode.
</description>
<row>
<option
variable="jogwheelAdjustFactor"
type="real"
precision="2"
min="0.1"
max="16.0"
step="0.1"
default="1.0"
label="Jogwheel sensitivity in adjustment">
<description>
Define the sensitivity of the jogwheel when adjusting (not touched).
</description>
</option>
<option
variable="loopEncoderMoveFactor"
type="integer"
Expand Down
98 changes: 73 additions & 25 deletions res/controllers/Traktor-Kontrol-S4-MK3.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ const LibrarySortableColumns = [
engine.getSetting("librarySortableColumns6Value"),
].map(c => parseInt(c)).filter(c => c); // Filter '0' column, equivalent to '---' value in the UI or disabled

const JogwheelAdjustFactor = engine.getSetting("jogwheelAdjustFactor") || 1.0;
const LoopWheelMoveFactor = engine.getSetting("loopWheelMoveFactor") || 50;
const LoopEncoderMoveFactor = engine.getSetting("loopEncoderMoveFactor") || 500;
const LoopEncoderShiftMoveFactor = engine.getSetting("loopEncoderShiftMoveFactor") || 2500;

const TempoFaderSoftTakeoverColorLow = LedColors[engine.getSetting("tempoFaderSoftTakeoverColorLow")] || LedColors.white;
const TempoFaderSoftTakeoverColorHigh = LedColors[engine.getSetting("tempoFaderSoftTakeoverColorHigh")] || LedColors.green;
const TempoFaderRelativeMove = LedColors[engine.getSetting("tempoFaderRelativeMove")] || LedColors.blue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The var name should contain 'color' IMO

Suggested change
const TempoFaderRelativeMove = LedColors[engine.getSetting("tempoFaderRelativeMove")] || LedColors.blue;
const TempoFaderRelativeModeColor = LedColors[engine.getSetting("tempoFaderRelativeModeColor")] || LedColors.blue;


// Tempo fader center snap range
// Transform user value (mm) into upper/lower values
Expand Down Expand Up @@ -198,6 +200,15 @@ const SoftwareMixerHeadphone = !!engine.getSetting("softwareMixerHeadphone");
// Define custom default layout used by the pads, instead of intro/outro and first 4 hotcues.
const DefaultPadLayout = engine.getSetting("defaultPadLayout");

// Use alternative movemode instead of default.
const AlternativeMoveMode = engine.getSetting("alternativeMoveMode") || false;

// Relative tempo slider mode -- the tempo slider does not care about its absolute position,
// only changes in position. Use Shift to adjust slider without changing tempo (for recentering, etc).
const DefaultRelativeTempoMode = engine.getSetting("defaultRelativeTempoMode") || false;
// Only adjust tempo if shift is held.
const ShiftTempoMode = engine.getSetting("shiftTempoMode") || false;

// 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
Expand Down Expand Up @@ -585,8 +596,10 @@ class Deck extends ComponentContainer {
this.color = colors[0];
}
this.settings = settings;
this.moveMode = moveModes.beat;
this.secondDeckModes = null;
this.selectedHotcue = null;
this.relativeTempoMode = DefaultRelativeTempoMode;

updateRuntimeData({
selectedHotcue: {
Expand Down Expand Up @@ -1978,6 +1991,7 @@ class S4Mk3Deck extends Deck {
this.mixer = mixer;

this.syncMasterButton = new Button({
deck: this,
key: "sync_leader",
defaultRange: 0.08,
shift: UseKeylockOnMaster ? function() {
Expand All @@ -1990,13 +2004,18 @@ class S4Mk3Deck extends Deck {
script.toggleControl(this.group, this.inKey);
},
onLongPress: function() {
const currentRange = engine.getValue(this.group, "rateRange");
if (currentRange < 1.0) {
engine.setValue(this.group, "rateRange", 1.0);
this.indicator(true);
if (this.shifted) {
this.deck.relativeTempoMode = !this.deck.relativeTempoMode;
this.deck.tempoFader.resync();
} else {
engine.setValue(this.group, "rateRange", this.defaultRange);
this.indicator(false);
const currentRange = engine.getValue(this.group, "rateRange");
if (currentRange < 1.0) {
engine.setValue(this.group, "rateRange", 1.0);
this.indicator(true);
} else {
engine.setValue(this.group, "rateRange", this.defaultRange);
this.indicator(false);
}
}
},
});
Expand Down Expand Up @@ -2028,10 +2047,21 @@ class S4Mk3Deck extends Deck {
inKey: "rate",
outKey: "rate",
appliedValue: null,
lastHardwareValue: 0,
lastRelativeValue: null,
tempoCenterUpper: this.settings.tempoCenterUpper,
tempoCenterLower: this.settings.tempoCenterLower,
resync: function() {
this.appliedValue = null;
if (!this.deck.relativeTempoMode) {
this.input(this.lastHardwareValue);
} else {
this.send(TempoFaderRelativeMove + Button.prototype.brightnessOn);
}
},
input: function(value) {
const receivingFirstValue = this.appliedValue === null;
this.lastHardwareValue = value;

if (value < this.tempoCenterLower) {
// scale input for lower range
Expand All @@ -2043,18 +2073,35 @@ class S4Mk3Deck extends Deck {
// reset rate in center region
this.appliedValue = 0;
}

if (this.deck.relativeTempoMode) {
const lastVal = this.lastRelativeValue;
this.lastRelativeValue = this.appliedValue;
// We do want to reset the slider to the physical position when receiving the
// first value.
if (!receivingFirstValue) {
if (this.shifted ^ ShiftTempoMode) {
return;
}

let relVal = engine.getValue(this.group, "rate");
relVal += this.appliedValue - lastVal;
this.appliedValue = Math.max(Math.min(relVal, 10.0), -10.0);
}
}
engine.setValue(this.group, this.inKey, this.appliedValue);

if (receivingFirstValue) {
engine.softTakeover(this.group, this.inKey, true);
// Forec-update LED.
// Force-update LED.
// Output connection is made and updated before input() can set this.appliedValue
// (doesn't happen until getInputReport())
this.outTrigger();
}
},
output: function(value) {
if (this.appliedValue === null) {
if (this.deck.relativeTempoMode || this.appliedValue === null) {
this.send(TempoFaderRelativeMove + Button.prototype.brightnessOn);
return;
}

Expand Down Expand Up @@ -2378,28 +2425,29 @@ class S4Mk3Deck extends Deck {
engine.setValue(this.deck.group, `hotcue_${this.deck.selectedHotcue}_color`, Object.keys(LedColorMap)[currentColorIdx]);
break;
}
case moveModes.beat:
default:
if (!this.shifted) {
if (!this.deck.leftEncoderPress.pressed) {
if (right) {
script.triggerControl(this.group, "beatjump_forward");
} else {
script.triggerControl(this.group, "beatjump_backward");
}
if (AlternativeMoveMode) {
if (this.shifted) {
script.triggerControl(this.group, right ? "beatjump_forward" : "beatjump_backward");
} else {
let beatjumpSize = engine.getValue(this.group, "beatjump_size");
if (right) {
beatjumpSize *= 2;
if (this.deck.leftEncoderPress.pressed) {
script.triggerControl(this.group, right ? "pitch_up_small" : "pitch_down_small");
} else {
beatjumpSize /= 2;
const beatjumpSize = engine.getValue(this.group, "beatjump_size");
engine.setValue(this.group, "beatjump_size", beatjumpSize * (right ? 2 : 1/2));
}
engine.setValue(this.group, "beatjump_size", beatjumpSize);
}
break;
}
if (this.shifted) {
script.triggerControl(this.group, right ? "pitch_up_small" : "pitch_down_small");
} else {
if (right) {
script.triggerControl(this.group, "pitch_up_small");
if (this.deck.leftEncoderPress.pressed) {
const beatjumpSize = engine.getValue(this.group, "beatjump_size");
engine.setValue(this.group, "beatjump_size", beatjumpSize * (right ? 2 : 1/2));
} else {
script.triggerControl(this.group, "pitch_down_small");
script.triggerControl(this.group, right ? "beatjump_forward" : "beatjump_backward");
}
}
break;
Expand Down Expand Up @@ -3102,11 +3150,11 @@ class S4Mk3Deck extends Deck {
if (this.deck.wheelTouch.touched || engine.getValue(this.group, "scratch2") !== 0) {
engine.setValue(this.group, "scratch2", this.speed);
} else {
engine.setValue(this.group, "jog", this.speed);
engine.setValue(this.group, "jog", this.speed * JogwheelAdjustFactor);
}
break;
default:
engine.setValue(this.group, "jog", this.speed);
engine.setValue(this.group, "jog", this.speed * JogwheelAdjustFactor);
}
},
});
Expand Down
Loading