Skip to content

Commit 270372e

Browse files
committed
Traktor S4MK3: Add alternative modes for a few controls.
Relative Tempo Fader mode: Tempo fader only responds to changes, not absolute position. Use shift to change position without adjusting rate. Relative Tempo Fader, Shifted mode: Only respond to changes when shift is held. Prevents accidental bumps when DJing in poor tactile environments (wearing fursuit paws) Jog Wheel Adjustment factor: Adjust sensitivity of jog wheel when making sync adjustments. Alternative Mode mode: for the move encoder, change assignments: unshifted to change beatjump size, shift to do a beatjump, push and change to adjust pitch. Signed-off-by: Owen Williams <owilliams@mixxx.org>
1 parent d2c4f03 commit 270372e

2 files changed

Lines changed: 111 additions & 25 deletions

File tree

res/controllers/Traktor Kontrol S4 MK3.hid.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,35 @@
252252
Define the default layout used for the pads.
253253
</description>
254254
</option>
255+
<option
256+
variable="alternativeMoveMode"
257+
type="boolean"
258+
default="false"
259+
label="Alternative assignments for move button">
260+
<description>
261+
Move encoder: Adjust beatjump size
262+
:hwbtn:`SHIFT` + move encoder: Beatjump
263+
Push + move encoder: adjust pitch up and down.
264+
</description>
265+
</option>
266+
<option
267+
variable="relativeTempoMode"
268+
type="boolean"
269+
default="false"
270+
label="Tempo fader relative movement mode">
271+
<description>
272+
No need for soft takeover. :hwbtn:`SHIFT` + tempo fader does not change tempo.
273+
</description>
274+
</option>
275+
<option
276+
variable="shiftTempoMode"
277+
type="boolean"
278+
default="false"
279+
label="For Relative mode, Tempo does not adjust unless :hwbtn:`SHIFT` is held">
280+
<description>
281+
Helps protect against accidental bumps.
282+
</description>
283+
</option>
255284
</group>
256285

257286
<group label="Mixer">
@@ -797,6 +826,19 @@
797826
Define the sensitivity factor when the jogwheel is used to move the in and out point of a loop using the Loop Mode.
798827
</description>
799828
<row>
829+
<option
830+
variable="jogwheelAdjustFactor"
831+
type="real"
832+
precision="2"
833+
min="0.1"
834+
max="16.0"
835+
step="0.1"
836+
default="1.0"
837+
label="Jogwheel sensitivity in adjustment">
838+
<description>
839+
Define the sensitivity of the jogwheel when adjusting (not touched).
840+
</description>
841+
</option>
800842
<option
801843
variable="loopEncoderMoveFactor"
802844
type="integer"

res/controllers/Traktor-Kontrol-S4-MK3.js

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ const LibrarySortableColumns = [
8888
engine.getSetting("librarySortableColumns6Value"),
8989
].map(c => parseInt(c)).filter(c => c); // Filter '0' column, equivalent to '---' value in the UI or disabled
9090

91+
const JogwheelAdjustFactor = engine.getSetting("jogwheelAdjustFactor") || 1.0;
9192
const LoopWheelMoveFactor = engine.getSetting("loopWheelMoveFactor") || 50;
9293
const LoopEncoderMoveFactor = engine.getSetting("loopEncoderMoveFactor") || 500;
9394
const LoopEncoderShiftMoveFactor = engine.getSetting("loopEncoderShiftMoveFactor") || 2500;
@@ -198,6 +199,15 @@ const SoftwareMixerHeadphone = !!engine.getSetting("softwareMixerHeadphone");
198199
// Define custom default layout used by the pads, instead of intro/outro and first 4 hotcues.
199200
const DefaultPadLayout = engine.getSetting("defaultPadLayout");
200201

202+
// Use alternative movemode instead of default.
203+
const AlternativeMoveMode = engine.getSetting("alternativeMoveMode") || false;
204+
205+
// Relative tempo slider mode -- the tempo slider does not care about its absolute position,
206+
// only changes in position. Use Shift to adjust slider without changing tempo (for recentering, etc).
207+
const RelativeTempoMode = engine.getSetting("relativeTempoMode") || false;
208+
// Only adjust tempo if shift is held.
209+
const ShiftTempoMode = engine.getSetting("shiftTempoMode") || false;
210+
201211
// The LEDs only support 16 base colors. Adding 1 in addition to
202212
// the normal 2 for Button.prototype.brightnessOn changes the color
203213
// slightly, so use that get 25 different colors to include the Filter
@@ -585,8 +595,10 @@ class Deck extends ComponentContainer {
585595
this.color = colors[0];
586596
}
587597
this.settings = settings;
598+
this.moveMode = moveModes.beat;
588599
this.secondDeckModes = null;
589600
this.selectedHotcue = null;
601+
this.relativeTempoMode = RelativeTempoMode;
590602

591603
updateRuntimeData({
592604
selectedHotcue: {
@@ -1975,6 +1987,7 @@ class S4Mk3Deck extends Deck {
19751987
this.mixer = mixer;
19761988

19771989
this.syncMasterButton = new Button({
1990+
deck: this,
19781991
key: "sync_leader",
19791992
defaultRange: 0.08,
19801993
shift: UseKeylockOnMaster ? function() {
@@ -1987,13 +2000,18 @@ class S4Mk3Deck extends Deck {
19872000
script.toggleControl(this.group, this.inKey);
19882001
},
19892002
onLongPress: function() {
1990-
const currentRange = engine.getValue(this.group, "rateRange");
1991-
if (currentRange < 1.0) {
1992-
engine.setValue(this.group, "rateRange", 1.0);
1993-
this.indicator(true);
2003+
if (this.shifted) {
2004+
this.deck.relativeTempoMode = !this.deck.relativeTempoMode;
2005+
this.deck.tempoFader.resync();
19942006
} else {
1995-
engine.setValue(this.group, "rateRange", this.defaultRange);
1996-
this.indicator(false);
2007+
const currentRange = engine.getValue(this.group, "rateRange");
2008+
if (currentRange < 1.0) {
2009+
engine.setValue(this.group, "rateRange", 1.0);
2010+
this.indicator(true);
2011+
} else {
2012+
engine.setValue(this.group, "rateRange", this.defaultRange);
2013+
this.indicator(false);
2014+
}
19972015
}
19982016
},
19992017
});
@@ -2025,10 +2043,19 @@ class S4Mk3Deck extends Deck {
20252043
inKey: "rate",
20262044
outKey: "rate",
20272045
appliedValue: null,
2046+
lastHardwareValue: 0,
2047+
lastRelativeValue: null,
20282048
tempoCenterUpper: this.settings.tempoCenterUpper,
20292049
tempoCenterLower: this.settings.tempoCenterLower,
2050+
resync: function() {
2051+
this.appliedValue = null;
2052+
if (!this.deck.relativeTempoMode) {
2053+
this.input(this.lastHardwareValue);
2054+
}
2055+
},
20302056
input: function(value) {
20312057
const receivingFirstValue = this.appliedValue === null;
2058+
this.lastHardwareValue = value;
20322059

20332060
if (value < this.tempoCenterLower) {
20342061
// scale input for lower range
@@ -2040,18 +2067,34 @@ class S4Mk3Deck extends Deck {
20402067
// reset rate in center region
20412068
this.appliedValue = 0;
20422069
}
2070+
2071+
if (this.deck.relativeTempoMode) {
2072+
const lastVal = this.lastRelativeValue;
2073+
this.lastRelativeValue = this.appliedValue;
2074+
// We do want to reset the slider to the physical position when receiving the
2075+
// first value.
2076+
if (!receivingFirstValue) {
2077+
if (this.shifted ^ ShiftTempoMode) {
2078+
return;
2079+
}
2080+
2081+
let relVal = engine.getValue(this.group, "rate");
2082+
relVal += this.appliedValue - lastVal;
2083+
this.appliedValue = Math.max(Math.min(relVal, 10.0), -10.0);
2084+
}
2085+
}
20432086
engine.setValue(this.group, this.inKey, this.appliedValue);
20442087

20452088
if (receivingFirstValue) {
20462089
engine.softTakeover(this.group, this.inKey, true);
2047-
// Forec-update LED.
2090+
// Force-update LED.
20482091
// Output connection is made and updated before input() can set this.appliedValue
20492092
// (doesn't happen until getInputReport())
20502093
this.outTrigger();
20512094
}
20522095
},
20532096
output: function(value) {
2054-
if (this.appliedValue === null) {
2097+
if (this.deck.relativeTempoMode || this.appliedValue === null) {
20552098
return;
20562099
}
20572100

@@ -2375,28 +2418,29 @@ class S4Mk3Deck extends Deck {
23752418
engine.setValue(this.deck.group, `hotcue_${this.deck.selectedHotcue}_color`, Object.keys(LedColorMap)[currentColorIdx]);
23762419
break;
23772420
}
2421+
case moveModes.beat:
23782422
default:
2379-
if (!this.shifted) {
2380-
if (!this.deck.leftEncoderPress.pressed) {
2381-
if (right) {
2382-
script.triggerControl(this.group, "beatjump_forward");
2383-
} else {
2384-
script.triggerControl(this.group, "beatjump_backward");
2385-
}
2423+
if (AlternativeMoveMode) {
2424+
if (this.shifted) {
2425+
script.triggerControl(this.group, right ? "beatjump_forward" : "beatjump_backward");
23862426
} else {
2387-
let beatjumpSize = engine.getValue(this.group, "beatjump_size");
2388-
if (right) {
2389-
beatjumpSize *= 2;
2427+
if (this.deck.leftEncoderPress.pressed) {
2428+
script.triggerControl(this.group, right ? "pitch_up_small" : "pitch_down_small");
23902429
} else {
2391-
beatjumpSize /= 2;
2430+
const beatjumpSize = engine.getValue(this.group, "beatjump_size");
2431+
engine.setValue(this.group, "beatjump_size", beatjumpSize * (right ? 2 : 1/2));
23922432
}
2393-
engine.setValue(this.group, "beatjump_size", beatjumpSize);
23942433
}
2434+
break;
2435+
}
2436+
if (this.shifted) {
2437+
script.triggerControl(this.group, right ? "pitch_up_small" : "pitch_down_small");
23952438
} else {
2396-
if (right) {
2397-
script.triggerControl(this.group, "pitch_up_small");
2439+
if (this.deck.leftEncoderPress.pressed) {
2440+
const beatjumpSize = engine.getValue(this.group, "beatjump_size");
2441+
engine.setValue(this.group, "beatjump_size", beatjumpSize * (right ? 2 : 1/2));
23982442
} else {
2399-
script.triggerControl(this.group, "pitch_down_small");
2443+
script.triggerControl(this.group, right ? "beatjump_forward" : "beatjump_backward");
24002444
}
24012445
}
24022446
break;
@@ -3099,11 +3143,11 @@ class S4Mk3Deck extends Deck {
30993143
if (this.deck.wheelTouch.touched || engine.getValue(this.group, "scratch2") !== 0) {
31003144
engine.setValue(this.group, "scratch2", this.speed);
31013145
} else {
3102-
engine.setValue(this.group, "jog", this.speed);
3146+
engine.setValue(this.group, "jog", this.speed * JogwheelAdjustFactor);
31033147
}
31043148
break;
31053149
default:
3106-
engine.setValue(this.group, "jog", this.speed);
3150+
engine.setValue(this.group, "jog", this.speed * JogwheelAdjustFactor);
31073151
}
31083152
},
31093153
});

0 commit comments

Comments
 (0)