From 32a2e559bfaa7ec0654d13452c926b43777c2446 Mon Sep 17 00:00:00 2001 From: wamo Date: Tue, 28 Apr 2026 13:43:40 +0900 Subject: [PATCH 1/3] Added support to Traktor Kontrol S2 MK3 --- README.MD | 4 +- RekordJog_TraktorKontrolS2MK3.py | 101 +++++++++++++++++++++++ midi_mappings/Traktor Kontrol S2 MK3.csv | 56 +++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 RekordJog_TraktorKontrolS2MK3.py create mode 100644 midi_mappings/Traktor Kontrol S2 MK3.csv diff --git a/README.MD b/README.MD index a494fcb..b55b4d2 100644 --- a/README.MD +++ b/README.MD @@ -8,8 +8,10 @@ This program allows jog wheels and pitch control on third-party controllers to w - Hercules DJControl - Inpulse 200 - Mix -- Numark +- Numark - Mixtrack 3 +- Native Instruments + - Traktor Kontrol S2 MK3 To use it with other controllers, you'll need to find the MIDI calls and their pattern using MIDI-OX or a similar tool and modify the source code accordingly. Feel free to send a Pull Request! diff --git a/RekordJog_TraktorKontrolS2MK3.py b/RekordJog_TraktorKontrolS2MK3.py new file mode 100644 index 0000000..a2d5f2d --- /dev/null +++ b/RekordJog_TraktorKontrolS2MK3.py @@ -0,0 +1,101 @@ +import os +import mido +from functions.check_config import check_config +from functions.rekordjog_start_sequence import rekordjog_start_sequence + +# The JOG_MULTIPLIER is required for smooth jog operation. +JOG_MULTIPLIER = 1 + +# TODO: Fill this with actual values found via midi_investigator.py +# Pioneer expects 64 as neutral. +# Clockwise: 65 - 84 +# Counter-Clockwise: 46 - 63 +CONV_J_VAL = { + # Counter-Clockwise (CCW) + 46: 46, 47: 47, 48: 48, 49: 49, 50: 50, + 51: 51, 52: 52, 53: 53, 54: 54, 55: 55, + 56: 56, 57: 57, 58: 58, 59: 59, 60: 60, + 61: 61, 62: 62, 63: 63, + + # Clockwise (CW) + 65: 65, 66: 66, 67: 67, 68: 68, 69: 69, + 70: 70, 71: 71, 72: 72, 73: 73, 74: 74, + 75: 75, 76: 76, 77: 77, 78: 78, 79: 79, + 80: 80, 81: 81, 82: 82, 83: 83, 84: 84, +} + +JOG_CODES = { + (0xb1, 0x1e): 0, + (0xb3, 0x1e): 1 +} + +TOUCH_ON_CODES = { + (0x91, 0x14): 0, + (0x93, 0x14): 1 +} + +TOUCH_OFF_CODES = { + (0x91, 0x14): 0, + (0x93, 0x14): 1 +} + +def map_jog_value(midi_value): + return CONV_J_VAL.get(midi_value, 64) + +def jog(midi_out, msg): + try: + id = JOG_CODES[tuple(msg.bytes()[:2])] + v = map_jog_value(msg.bytes()[2]) + + ms = mido.Message.from_bytes([176 + id, 0x22, v]) + #print(f"[DEBUG] Jog: Sending {ms}") + + for i in range(JOG_MULTIPLIER): + midi_out.send(ms) + except KeyError: + pass + +def main(): + midi_inp, midi_out = check_config() + try: + midi_inp_conf, midi_out_conf = check_config() + midi_inp = mido.open_input(midi_inp_conf) + if os.name == 'nt': + midi_out = mido.open_output(midi_out_conf) + else: + midi_out = mido.open_output("Pioneer DDJ-SX", True) + + rekordjog_start_sequence() + print("\nTraktor Kontrol S2 MK3 handler started.") + print("Make sure you have filled out the MIDI values in RekordJog_TraktorKontrolS2MK3.py!") + + while True: + ims = midi_inp.receive() + ims_2b = tuple(ims.bytes()[:2]) + + if ims_2b in JOG_CODES: + jog(midi_out, ims) + + elif ims_2b in TOUCH_ON_CODES: + deck_id = TOUCH_ON_CODES[ims_2b] + + if hasattr(ims, 'type') and ims.type == 'note_on' and ims.velocity == 0: + release = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x00]) + #print(f"[DEBUG] Touch Release (vel 0): Sending {release}") + midi_out.send(release) + else: + touch = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x7F]) + #print(f"[DEBUG] Touch On: Sending {touch}") + midi_out.send(touch) + + elif ims_2b in TOUCH_OFF_CODES: + deck_id = TOUCH_OFF_CODES[ims_2b] + release = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x00]) + #print(f"[DEBUG] Touch Off: Sending {release}") + midi_out.send(release) + + except KeyboardInterrupt: + print("\nClosing RekordJog, bye.") + +if __name__ == "__main__": + main() diff --git a/midi_mappings/Traktor Kontrol S2 MK3.csv b/midi_mappings/Traktor Kontrol S2 MK3.csv new file mode 100644 index 0000000..aad0aaf --- /dev/null +++ b/midi_mappings/Traktor Kontrol S2 MK3.csv @@ -0,0 +1,56 @@ +@file,1,Traktor Kontrol S2 MK3 +Browse,,Rotary,B119,,,,,,,,,,, +Browse,,Rotary,B319,,,,,,,,,,, +ChannelFader,,KnobSlider,,B501,B601,,,,,,,,, +CrossFader,,KnobSlider,B701,,,,,,,,,,Fast;, +Cue,,Button,,9102,9302,,,,9102,9302,,,Fast;Blink=500;Priority=50;, +EQHigh,,KnobSlider,,B505,B605,,,,,,,,Fast;, +EQLow,,KnobSlider,,B503,B603,,,,,,,,Fast;, +EQMid,,KnobSlider,,B504,B604,,,,,,,,Fast;, +Gain,,KnobSlider,,B506,B606,,,,,,,,Fast;, +GridAdjust,,Button,,910D,930D,,,,910D,930D,,,, +HeadphoneCue,,Button,,9508,9608,,,,9508,9608,,,, +HeadphonesMix,,KnobSlider,B707,,,,,,,,,,, +HeadphonesVolume,,KnobSlider,B706,,,,,,,,,,, +HotCue,,Button,,910B,,,,,910B,,,,Fast;, +KeySync,,Button,,910F,930F,,,,910F,930F,,,, +Load,,Button,,9118,9318,,,,,,,,, +MFX1,,Rotary,B502,,,,,,,,,,, +MFX2,,Rotary,B602,,,,,,,,,,, +MasterLevel,,KnobSlider,B709,,,,,,,,,,, +NoFunction,,Button,,920B,,,,,920B,,,,Fast;, +NoFunction,,Button,,9227,,,,,9227,,,,Fast;, +NoFunction,,Button,,9314,,,,,9314,,,,Fast;, +NoFunction,,Button,,B31E,,,,,B31E,,,,Fast;, +NoFunction,,DdjSxPad,,9114,,,,,9114,,,,Fast;, +NoFunction,,DdjSxPad,,9203,,,,,9203,,,,Fast;, +NoFunction,,DdjSxPad,,B21E,,,,,B21E,,,,Fast;, +NoFunction,,Rotary,9129,,,,,,,,,,, +NoFunction,,Rotary,9329,,,,,,,,,,, +NoFunction,,Button,,,,,,,9118,9318,,,, +NoFunction,,Button,,,,,,,9129,9329,,,, +PAD1_HotCue,,DdjSxPad,,9103,,,,,9103,,,,Fast;, +PAD1_HotCue+Shift,,DdjSxPad,,9127,,,,,9127,,,,Fast;, +PAD1_PadFx1,,DdjSxPad,,B11E,,,,,B11E,,,,Fast;, +PAD2_HotCue,,DdjSxPad,,9104,,,,,9104,,,,Fast;, +PAD2_HotCue+Shift,,DdjSxPad,,9204,,,,,9204,,,,Fast;, +PAD3_HotCue,,DdjSxPad,,9105,,,,,9105,,,,Fast;, +PAD3_HotCue+Shift,,DdjSxPad,,9205,,,,,9205,,,,Fast;, +PAD4_HotCue,,DdjSxPad,,9106,,,,,9106,,,,Fast;, +PAD4_HotCue+Shift,,DdjSxPad,,9206,,,,,9206,,,,Fast;, +PAD5_HotCue,,DdjSxPad,,9107,,,,,9107,,,,Fast;, +PAD5_HotCue+Shift,,DdjSxPad,,9207,,,,,9207,,,,Fast;, +PAD6_HotCue,,DdjSxPad,,9108,,,,,9108,,,,Fast;, +PAD6_HotCue+Shift,,DdjSxPad,,9208,,,,,9208,,,,Fast;, +PAD7_HotCue,,DdjSxPad,,9109,,,,,9109,,,,Fast;, +PAD7_HotCue+Shift,,DdjSxPad,,9209,,,,,9209,,,,Fast;, +PAD8_HotCue,,DdjSxPad,,910A,,,,,910A,,,,Fast;, +PAD8_HotCue+Shift,,DdjSxPad,,920A,,,,,920A,,,,Fast;, +PadFx1,,Button,,910C,,,,,910C,,,,Fast;, +PlayPause,,Button,,9101,9301,,,,9101,9301,,,Fast;Blink=1000;Priority=50;, +Quantize,,Button,,970A,9328,,,,970A,9328,,,, +Reverse,,Button,,9115,9315,,,,9115,9315,,,Blink=500;, +SamplerGain,,KnobSlider,B708,,,,,,,,,,, +Slip,,Button,,9116,9316,,,,9116,9316,,,, +Sync,,Button,,9110,9310,,,,9110,9310,,,Blink=600;, +TempoSlider,,KnobSlider,,B12A,B32A,,,,,,,,Fast;, From 29a69cd97b0390bc3b57c2cbce64a69aa1dc9749 Mon Sep 17 00:00:00 2001 From: wamo Date: Tue, 28 Apr 2026 19:15:08 +0900 Subject: [PATCH 2/3] remove debug lines --- RekordJog_TraktorKontrolS2MK3.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/RekordJog_TraktorKontrolS2MK3.py b/RekordJog_TraktorKontrolS2MK3.py index a2d5f2d..a20808c 100644 --- a/RekordJog_TraktorKontrolS2MK3.py +++ b/RekordJog_TraktorKontrolS2MK3.py @@ -3,13 +3,8 @@ from functions.check_config import check_config from functions.rekordjog_start_sequence import rekordjog_start_sequence -# The JOG_MULTIPLIER is required for smooth jog operation. JOG_MULTIPLIER = 1 -# TODO: Fill this with actual values found via midi_investigator.py -# Pioneer expects 64 as neutral. -# Clockwise: 65 - 84 -# Counter-Clockwise: 46 - 63 CONV_J_VAL = { # Counter-Clockwise (CCW) 46: 46, 47: 47, 48: 48, 49: 49, 50: 50, @@ -48,7 +43,6 @@ def jog(midi_out, msg): v = map_jog_value(msg.bytes()[2]) ms = mido.Message.from_bytes([176 + id, 0x22, v]) - #print(f"[DEBUG] Jog: Sending {ms}") for i in range(JOG_MULTIPLIER): midi_out.send(ms) @@ -67,7 +61,6 @@ def main(): rekordjog_start_sequence() print("\nTraktor Kontrol S2 MK3 handler started.") - print("Make sure you have filled out the MIDI values in RekordJog_TraktorKontrolS2MK3.py!") while True: ims = midi_inp.receive() @@ -81,17 +74,14 @@ def main(): if hasattr(ims, 'type') and ims.type == 'note_on' and ims.velocity == 0: release = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x00]) - #print(f"[DEBUG] Touch Release (vel 0): Sending {release}") midi_out.send(release) else: touch = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x7F]) - #print(f"[DEBUG] Touch On: Sending {touch}") midi_out.send(touch) elif ims_2b in TOUCH_OFF_CODES: deck_id = TOUCH_OFF_CODES[ims_2b] release = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x00]) - #print(f"[DEBUG] Touch Off: Sending {release}") midi_out.send(release) except KeyboardInterrupt: From c8eb4fa5a12d63ceb0befc8fcef74e41433006a3 Mon Sep 17 00:00:00 2001 From: wamo Date: Tue, 28 Apr 2026 13:43:40 +0900 Subject: [PATCH 3/3] Added support to Traktor Kontrol S2 MK3 --- RekordJog_TraktorKontrolS2MK3.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/RekordJog_TraktorKontrolS2MK3.py b/RekordJog_TraktorKontrolS2MK3.py index a20808c..993b32e 100644 --- a/RekordJog_TraktorKontrolS2MK3.py +++ b/RekordJog_TraktorKontrolS2MK3.py @@ -34,9 +34,11 @@ (0x93, 0x14): 1 } + def map_jog_value(midi_value): return CONV_J_VAL.get(midi_value, 64) + def jog(midi_out, msg): try: id = JOG_CODES[tuple(msg.bytes()[:2])] @@ -49,6 +51,7 @@ def jog(midi_out, msg): except KeyError: pass + def main(): midi_inp, midi_out = check_config() try: @@ -73,10 +76,12 @@ def main(): deck_id = TOUCH_ON_CODES[ims_2b] if hasattr(ims, 'type') and ims.type == 'note_on' and ims.velocity == 0: - release = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x00]) + release = mido.Message.from_bytes( + [0x90 + deck_id, 0x36, 0x00]) midi_out.send(release) else: - touch = mido.Message.from_bytes([0x90 + deck_id, 0x36, 0x7F]) + touch = mido.Message.from_bytes( + [0x90 + deck_id, 0x36, 0x7F]) midi_out.send(touch) elif ims_2b in TOUCH_OFF_CODES: @@ -87,5 +92,6 @@ def main(): except KeyboardInterrupt: print("\nClosing RekordJog, bye.") + if __name__ == "__main__": main()