-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12366 from djantti/2.4
Native Instruments Traktor Kontrol Z1: Add new HID mapping
- Loading branch information
Showing
2 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version='1.0' encoding='utf-8'?> | ||
<MixxxControllerPreset mixxxVersion="2.4.0" schemaVersion="1"> | ||
<info> | ||
<name>Native Instruments Traktor Kontrol Z1</name> | ||
<author>djantti</author> | ||
<description>HID mapping for Native Instruments Traktor Kontrol Z1</description> | ||
<forums>https://mixxx.discourse.group/t/new-mapping-for-native-instruments-traktor-kontrol-z1/28436</forums> | ||
<manual>native_instruments_traktor_kontrol_z1</manual> | ||
<devices> | ||
<product protocol="hid" vendor_id="0x17cc" product_id="0x1210" /> | ||
</devices> | ||
</info> | ||
<controller id="Traktor"> | ||
<scriptfiles> | ||
<file filename="common-hid-packet-parser.js" functionprefix="" /> | ||
<file filename="Traktor-Kontrol-Z1-scripts.js" functionprefix="TraktorZ1" /> | ||
</scriptfiles> | ||
</controller> | ||
</MixxxControllerPreset> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,252 @@ | ||
// | ||
// Native Instruments Traktor Kontrol Z1 HID controller script for Mixxx 2.4 | ||
// ------------------------------------------------------------------------- | ||
// Based on: Kontrol S2 MK3 script by mi01 and Kontrol Z1 script by xeruf | ||
// Author: djantti | ||
// | ||
|
||
class TraktorZ1Class { | ||
constructor() { | ||
this.controller = new HIDController(); | ||
|
||
// Modifier state | ||
this.modePressed = false; | ||
|
||
// VuMeter | ||
this.vuLeftConnection = {}; | ||
this.vuRightConnection = {}; | ||
this.vuMeterThresholds = {"vu-30": (1 / 7), "vu-15": (2 / 7), "vu-6": (3 / 7), "vu-3": (4 / 7), "vu0": (5 / 7), "vu3": (6 / 7), "vu6": (7 / 7)}; | ||
} | ||
|
||
init(_id) { | ||
this.id = _id; | ||
this.registerInputPackets(); | ||
this.registerOutputPackets(); | ||
console.log(this.id + " initialized"); | ||
} | ||
|
||
registerInputPackets() { | ||
const InputReport0x01 = new HIDPacket("InputReport0x01", 0x01, this.inputReportCallback.bind(this)); | ||
|
||
// Mode button | ||
this.registerInputButton(InputReport0x01, "[ControlX]", "!mode", 0x1D, 0x02, this.modeHandler.bind(this)); | ||
|
||
// Headphone buttons | ||
this.registerInputButton(InputReport0x01, "[Channel1]", "!pfl", 0x1D, 0x10, this.headphoneHandler.bind(this)); | ||
this.registerInputButton(InputReport0x01, "[Channel2]", "!pfl", 0x1D, 0x01, this.headphoneHandler.bind(this)); | ||
|
||
// FX buttons | ||
this.registerInputButton(InputReport0x01, "[Channel1]", "!fx", 0x1D, 0x04, this.fxHandler.bind(this)); | ||
this.registerInputButton(InputReport0x01, "[Channel2]", "!fx", 0x1D, 0x08, this.fxHandler.bind(this)); | ||
|
||
// EQ knobs | ||
this.registerInputScaler(InputReport0x01, "[EqualizerRack1_[Channel1]_Effect1]", "parameter3", 0x03, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[EqualizerRack1_[Channel1]_Effect1]", "parameter2", 0x05, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[EqualizerRack1_[Channel1]_Effect1]", "parameter1", 0x07, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[EqualizerRack1_[Channel2]_Effect1]", "parameter3", 0x0D, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[EqualizerRack1_[Channel2]_Effect1]", "parameter2", 0x0F, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[EqualizerRack1_[Channel2]_Effect1]", "parameter1", 0x11, 0xFFFF, this.parameterHandler.bind(this)); | ||
|
||
// FX knobs | ||
this.registerInputScaler(InputReport0x01, "[QuickEffectRack1_[Channel1]]", "super1", 0x09, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[QuickEffectRack1_[Channel2]]", "super1", 0x13, 0xFFFF, this.parameterHandler.bind(this)); | ||
|
||
// Gain knobs | ||
this.registerInputScaler(InputReport0x01, "[Channel1]", "pregain", 0x01, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[Channel2]", "pregain", 0x0B, 0xFFFF, this.parameterHandler.bind(this)); | ||
|
||
// Headphone mix | ||
this.registerInputScaler(InputReport0x01, "[Master]", "headMix", 0x15, 0xFFFF, this.parameterHandler.bind(this)); | ||
|
||
// Volume faders | ||
this.registerInputScaler(InputReport0x01, "[Channel1]", "volume", 0x17, 0xFFFF, this.parameterHandler.bind(this)); | ||
this.registerInputScaler(InputReport0x01, "[Channel2]", "volume", 0x19, 0xFFFF, this.parameterHandler.bind(this)); | ||
|
||
// Crossfader | ||
this.registerInputScaler(InputReport0x01, "[Master]", "crossfader", 0x1B, 0xFFFF, this.parameterHandler.bind(this)); | ||
|
||
this.controller.registerInputPacket(InputReport0x01); | ||
|
||
// Soft takeover for all knobs and faders | ||
engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter3", true); | ||
engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter2", true); | ||
engine.softTakeover("[EqualizerRack1_[Channel1]_Effect1]", "parameter1", true); | ||
|
||
engine.softTakeover("[EqualizerRack1_[Channel2]_Effect1]", "parameter3", true); | ||
engine.softTakeover("[EqualizerRack1_[Channel2]_Effect1]", "parameter2", true); | ||
engine.softTakeover("[EqualizerRack1_[Channel2]_Effect1]", "parameter1", true); | ||
|
||
engine.softTakeover("[QuickEffectRack1_[Channel1]]", "super1", true); | ||
engine.softTakeover("[QuickEffectRack1_[Channel2]]", "super1", true); | ||
|
||
engine.softTakeover("[Channel1]", "pregain", true); | ||
engine.softTakeover("[Channel2]", "pregain", true); | ||
|
||
engine.softTakeover("[Master]", "headMix", true); | ||
|
||
engine.softTakeover("[Channel1]", "volume", true); | ||
engine.softTakeover("[Channel2]", "volume", true); | ||
|
||
engine.softTakeover("[Master]", "crossfader", true); | ||
} | ||
|
||
registerInputButton(inputReport, group, name, offset, bitmask, callback) { | ||
inputReport.addControl(group, name, offset, "B", bitmask); | ||
inputReport.setCallback(group, name, callback); | ||
} | ||
|
||
registerInputScaler(inputReport, group, name, offset, bitmask, callback) { | ||
inputReport.addControl(group, name, offset, "H", bitmask); | ||
inputReport.setCallback(group, name, callback); | ||
} | ||
|
||
registerOutputPackets() { | ||
const OutputReport0x80 = new HIDPacket("OutputReport0x80", 0x80); | ||
|
||
OutputReport0x80.addOutput("[ControlX]", "mode", 0x13, "B"); | ||
|
||
OutputReport0x80.addOutput("[Channel1]", "pfl", 0x0F, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "pfl", 0x10, "B"); | ||
|
||
OutputReport0x80.addOutput("[Channel1]", "play_indicator", 0x11, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "play_indicator", 0x14, "B"); | ||
|
||
OutputReport0x80.addOutput("[QuickEffectRack1_[Channel1]]", "enabled", 0x12, "B"); | ||
OutputReport0x80.addOutput("[QuickEffectRack1_[Channel2]]", "enabled", 0x15, "B"); | ||
|
||
OutputReport0x80.addOutput("[Channel1]", "vu-30", 0x01, "B"); | ||
OutputReport0x80.addOutput("[Channel1]", "vu-15", 0x02, "B"); | ||
OutputReport0x80.addOutput("[Channel1]", "vu-6", 0x03, "B"); | ||
OutputReport0x80.addOutput("[Channel1]", "vu-3", 0x04, "B"); | ||
OutputReport0x80.addOutput("[Channel1]", "vu0", 0x05, "B"); | ||
OutputReport0x80.addOutput("[Channel1]", "vu3", 0x06, "B"); | ||
OutputReport0x80.addOutput("[Channel1]", "vu6", 0x07, "B"); | ||
|
||
OutputReport0x80.addOutput("[Channel2]", "vu-30", 0x08, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "vu-15", 0x09, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "vu-6", 0x0A, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "vu-3", 0x0B, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "vu0", 0x0C, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "vu3", 0x0D, "B"); | ||
OutputReport0x80.addOutput("[Channel2]", "vu6", 0x0E, "B"); | ||
|
||
this.controller.registerOutputPacket(OutputReport0x80); | ||
|
||
engine.makeConnection("[QuickEffectRack1_[Channel1]]", "enabled", this.outputHandler.bind(this)); | ||
engine.makeConnection("[QuickEffectRack1_[Channel2]]", "enabled", this.outputHandler.bind(this)); | ||
|
||
engine.makeConnection("[Channel1]", "pfl", this.outputHandler.bind(this)); | ||
engine.makeConnection("[Channel2]", "pfl", this.outputHandler.bind(this)); | ||
|
||
this.vuLeftConnection = engine.makeUnbufferedConnection("[Channel1]", "vu_meter", this.vuMeterHandler.bind(this)); | ||
this.vuRightConnection = engine.makeUnbufferedConnection("[Channel2]", "vu_meter", this.vuMeterHandler.bind(this)); | ||
|
||
this.lightDeck(false); | ||
} | ||
|
||
modeHandler(field) { | ||
this.modePressed = field.value; | ||
this.outputHandler(field.value, field.group, "mode"); | ||
} | ||
|
||
headphoneHandler(field) { | ||
if (field.value === 0) { | ||
return; | ||
} | ||
// Go to cue and stop when modifier is active | ||
if (this.modePressed) { | ||
engine.setValue(field.group, "cue_gotoandstop", field.value); | ||
} else { | ||
script.toggleControl(field.group, "pfl"); | ||
} | ||
} | ||
|
||
fxHandler(field) { | ||
if (field.value === 0) { | ||
// Always clear play indicator on button release | ||
this.controller.setOutput(field.group, "play_indicator", 0x00, true); | ||
return; | ||
} | ||
// Control playback when modifier is active | ||
if (this.modePressed) { | ||
// Match play indicator (red led) brightness to fx indicator (blue led) | ||
const ledBrightness = engine.getValue("[QuickEffectRack1_" + field.group + "]", "enabled") ? 0x7F : 0x0A; | ||
this.controller.setOutput(field.group, "play_indicator", ledBrightness, true); | ||
script.toggleControl(field.group, "play"); | ||
} else { | ||
script.toggleControl("[QuickEffectRack1_" + field.group + "]", "enabled"); | ||
} | ||
} | ||
|
||
vuMeterHandler(value, group, _key) { | ||
const vuKeys = Object.keys(this.vuMeterThresholds); | ||
for (let i = 0; i < vuKeys.length; ++i) { | ||
// Avoid spamming HID by only sending last LED update | ||
const last = i === (vuKeys.length - 1); | ||
if (this.vuMeterThresholds[vuKeys[i]] > value) { | ||
this.controller.setOutput(group, vuKeys[i], 0x00, last); | ||
} else { | ||
this.controller.setOutput(group, vuKeys[i], 0x7E, last); | ||
} | ||
} | ||
} | ||
|
||
parameterHandler(field) { | ||
engine.setParameter(field.group, field.name, field.value / 4095); | ||
} | ||
|
||
outputHandler(value, group, key) { | ||
let ledValue; | ||
if (value === 0 || value === false) { | ||
// Off value (dimmed) | ||
ledValue = 0x0A; | ||
} else if (value === 1 || value === true) { | ||
// On value | ||
ledValue = 0x7F; | ||
} | ||
this.controller.setOutput(group, key, ledValue, true); | ||
} | ||
|
||
lightDeck(switchOff) { | ||
let softLight = 0x0A; | ||
let fullLight = 0x7F; | ||
let ledBrightness; | ||
|
||
if (switchOff) { | ||
softLight = 0x00; | ||
fullLight = 0x00; | ||
} | ||
|
||
this.controller.setOutput("[ControlX]", "mode", softLight, true); | ||
|
||
ledBrightness = engine.getValue("[QuickEffectRack1_[Channel1]]", "enabled") ? fullLight : softLight; | ||
this.controller.setOutput("[QuickEffectRack1_[Channel1]]", "enabled", ledBrightness, true); | ||
ledBrightness = engine.getValue("[QuickEffectRack1_[Channel2]]", "enabled") ? fullLight : softLight; | ||
this.controller.setOutput("[QuickEffectRack1_[Channel2]]", "enabled", ledBrightness, true); | ||
|
||
ledBrightness = engine.getValue("[Channel1]", "pfl") ? fullLight : softLight; | ||
this.controller.setOutput("[Channel1]", "pfl", ledBrightness, true); | ||
ledBrightness = engine.getValue("[Channel2]", "pfl") ? fullLight : softLight; | ||
this.controller.setOutput("[Channel2]", "pfl", ledBrightness, true); | ||
} | ||
|
||
inputReportCallback(packet, data) { | ||
for (const name in data) { | ||
if (Object.hasOwnProperty.call(data, name)) { | ||
this.controller.processButton(data[name]); | ||
} | ||
} | ||
} | ||
|
||
shutdown() { | ||
// Deactivate all LEDs | ||
this.lightDeck(true); | ||
console.log(this.id + " shut down"); | ||
} | ||
|
||
incomingData(data, length) { | ||
this.controller.parsePacket(data, length); | ||
} | ||
} | ||
|
||
var TraktorZ1 = new TraktorZ1Class; // eslint-disable-line no-var, no-unused-vars |