mixxx-mapping/Pioneer-DDJ-FLX4_neu-script.js

1056 lines
25 KiB
JavaScript

// Pioneer-DDJ-FLX4-script.js
// ****************************************************************************
// * Mixxx mapping script file for the Pioneer DDJ-FLX4.
// * Mostly adapted from the DDJ-400 mapping script
// * Authors: Warker, nschloe, dj3730, jusko, Robert904
// ****************************************************************************
//
// Implemented (as per manufacturer's manual):
// * Mixer Section (Faders, EQ, Filter, Gain, Cue)
// * Browsing and loading + Waveform zoom (shift)
// * Jogwheels, Scratching, Bending, Loop adjust
// * Cycle Temporange
// * Beat Sync
// * Hot Cue Mode
// * Beat Loop Mode
// * Beat Jump Mode
// * Sampler Mode
//
// Custom (Mixxx specific mappings):
// * BeatFX: Assigned Effect Unit 1
// v FX_SELECT Load next effect.
// SHIFT + v FX_SELECT Load previous effect.
// < LEFT Cycle effect focus leftward
// > RIGHT Cycle effect focus rightward
// ON/OFF toggles focused effect slot
// SHIFT + ON/OFF disables all three effect slots.
//
// * 32 beat jump forward & back (Shift + </> CUE/LOOP CALL arrows)
// * Toggle quantize (Shift + channel cue)
//
// Not implemented (after discussion and trial attempts):
// * Loop Section:
// * -4BEAT auto loop (hacky---prefer a clean way to set a 4 beat loop
// from a previous position on long press)
//
// * CUE/LOOP CALL - memory & delete (complex and not useful. Hot cues are sufficient)
//
// * Secondary pad modes (trial attempts complex and too experimental)
// * Keyboard mode
// * Pad FX1
// * Pad FX2
// * Keyshift mode
//
// Not implemented yet (but might be in the future):
// * Smart CFX
// * Smart fader
var PioneerDDJFLX4 = {};
PioneerDDJFLX4.lights = {
beatFx: {
status: 0x94,
data1: 0x47,
},
shiftBeatFx: {
status: 0x94,
data1: 0x43,
},
deck1: {
vuMeter: {
status: 0xb0,
data1: 0x02,
},
playPause: {
status: 0x90,
data1: 0x0b,
},
shiftPlayPause: {
status: 0x90,
data1: 0x47,
},
cue: {
status: 0x90,
data1: 0x0c,
},
shiftCue: {
status: 0x90,
data1: 0x48,
},
hotcueMode: {
status: 0x90,
data1: 0x1b,
},
keyboardMode: {
status: 0x90,
data1: 0x69,
},
padFX1Mode: {
status: 0x90,
data1: 0x1e,
},
padFX2Mode: {
status: 0x90,
data1: 0x6b,
},
beatJumpMode: {
status: 0x90,
data1: 0x20,
},
beatLoopMode: {
status: 0x90,
data1: 0x6d,
},
samplerMode: {
status: 0x90,
data1: 0x22,
},
keyShiftMode: {
status: 0x90,
data1: 0x6f,
},
},
deck2: {
vuMeter: {
status: 0xb0,
data1: 0x02,
},
playPause: {
status: 0x91,
data1: 0x0b,
},
shiftPlayPause: {
status: 0x91,
data1: 0x47,
},
cue: {
status: 0x91,
data1: 0x0c,
},
shiftCue: {
status: 0x91,
data1: 0x48,
},
hotcueMode: {
status: 0x91,
data1: 0x1b,
},
keyboardMode: {
status: 0x91,
data1: 0x69,
},
padFX1Mode: {
status: 0x91,
data1: 0x1e,
},
padFX2Mode: {
status: 0x91,
data1: 0x6b,
},
beatJumpMode: {
status: 0x91,
data1: 0x20,
},
beatLoopMode: {
status: 0x91,
data1: 0x6d,
},
samplerMode: {
status: 0x91,
data1: 0x22,
},
keyShiftMode: {
status: 0x91,
data1: 0x6f,
},
},
};
// Store timer IDs
PioneerDDJFLX4.timers = {};
// Keep alive timer
PioneerDDJFLX4.sendKeepAlive = function () {
midi.sendSysexMsg(
[0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x04, 0x05, 0x00, 0x50, 0x02, 0xf7],
12
); // This was reverse engineered with Wireshark
};
// Jog wheel constants
PioneerDDJFLX4.vinylMode = true;
PioneerDDJFLX4.alpha = 1.0 / 8;
PioneerDDJFLX4.beta = PioneerDDJFLX4.alpha / 32;
// Multiplier for fast seek through track using SHIFT+JOGWHEEL
PioneerDDJFLX4.fastSeekScale = 150;
PioneerDDJFLX4.bendScale = 0.4;
PioneerDDJFLX4.tempoRanges = [0.06, 0.1, 0.16, 0.25];
PioneerDDJFLX4.shiftButtonDown = [false, false];
// Jog wheel loop adjust
PioneerDDJFLX4.loopAdjustIn = [false, false];
PioneerDDJFLX4.loopAdjustOut = [false, false];
PioneerDDJFLX4.loopAdjustMultiply = 50;
// Beatjump pad (beatjump_size values)
PioneerDDJFLX4.beatjumpSizeForPad = {
0x20: -1, // PAD 1
0x21: 1, // PAD 2
0x22: -2, // PAD 3
0x23: 2, // PAD 4
0x24: -4, // PAD 5
0x25: 4, // PAD 6
0x26: -8, // PAD 7
0x27: 8, // PAD 8
};
PioneerDDJFLX4.quickJumpSize = 32;
// Used for tempo slider
PioneerDDJFLX4.highResMSB = {
"[Channel1]": {},
"[Channel2]": {},
};
PioneerDDJFLX4.trackLoadedLED = function (value, group, _control) {
midi.sendShortMsg(
0x9f,
group.match(script.channelRegEx)[1] - 1,
value > 0 ? 0x7f : 0x00
);
};
PioneerDDJFLX4.toggleLight = function (midiIn, active) {
midi.sendShortMsg(midiIn.status, midiIn.data1, active ? 0x7f : 0);
};
//
// Init
//
PioneerDDJFLX4.init = function () {
engine.setValue("[EffectRack1_EffectUnit1]", "show_focus", 1);
engine.makeConnection("[Channel1]", "vu_meter", PioneerDDJFLX4.vuMeterUpdate);
engine.makeConnection("[Channel2]", "vu_meter", PioneerDDJFLX4.vuMeterUpdate);
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.deck1.vuMeter, false);
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.deck2.vuMeter, false);
engine.softTakeover("[Channel1]", "rate", true);
engine.softTakeover("[Channel2]", "rate", true);
engine.softTakeover("[EffectRack1_EffectUnit1_Effect1]", "meta", true);
engine.softTakeover("[EffectRack1_EffectUnit1_Effect2]", "meta", true);
engine.softTakeover("[EffectRack1_EffectUnit1_Effect3]", "meta", true);
engine.softTakeover("[EffectRack1_EffectUnit1]", "mix", true);
const samplerCount = 16;
if (engine.getValue("[App]", "num_samplers") < samplerCount) {
engine.setValue("[App]", "num_samplers", samplerCount);
}
for (let i = 1; i <= samplerCount; ++i) {
engine.makeConnection(
"[Sampler" + i + "]",
"play",
PioneerDDJFLX4.samplerPlayOutputCallbackFunction
);
}
engine.makeConnection(
"[Channel1]",
"track_loaded",
PioneerDDJFLX4.trackLoadedLED
);
engine.makeConnection(
"[Channel2]",
"track_loaded",
PioneerDDJFLX4.trackLoadedLED
);
// play the "track loaded" animation on both decks at startup
midi.sendShortMsg(0x9f, 0x00, 0x7f);
midi.sendShortMsg(0x9f, 0x01, 0x7f);
PioneerDDJFLX4.setLoopButtonLights(0x90, 0x7f);
PioneerDDJFLX4.setLoopButtonLights(0x91, 0x7f);
engine.makeConnection(
"[Channel1]",
"loop_enabled",
PioneerDDJFLX4.loopToggle
);
engine.makeConnection(
"[Channel2]",
"loop_enabled",
PioneerDDJFLX4.loopToggle
);
for (i = 1; i <= 3; i++) {
engine.makeConnection(
"[EffectRack1_EffectUnit1_Effect" + i + "]",
"enabled",
PioneerDDJFLX4.toggleFxLight
);
}
engine.makeConnection(
"[EffectRack1_EffectUnit1]",
"focused_effect",
PioneerDDJFLX4.toggleFxLight
);
PioneerDDJFLX4.keepAliveTimer = engine.beginTimer(
200,
PioneerDDJFLX4.sendKeepAlive
);
// query the controller for current control positions on startup
PioneerDDJFLX4.sendKeepAlive(); // the query seems to double as a keep alive message
};
//
// Waveform zoom
//
PioneerDDJFLX4.waveformZoom = function (
midichan,
control,
value,
status,
group
) {
if (value === 0x7f) {
script.triggerControl(group, "waveform_zoom_up", 100);
} else {
script.triggerControl(group, "waveform_zoom_down", 100);
}
};
//
// Channel level lights
//
PioneerDDJFLX4.vuMeterUpdate = function (value, group) {
const newVal = value * 127;
switch (group) {
case "[Channel1]":
midi.sendShortMsg(0xb0, 0x02, newVal);
break;
case "[Channel2]":
midi.sendShortMsg(0xb1, 0x02, newVal);
break;
}
};
//
// Effects
//
PioneerDDJFLX4.toggleFxLight = function (_value, _group, _control) {
const enabled = engine.getValue(PioneerDDJFLX4.focusedFxGroup(), "enabled");
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.beatFx, enabled);
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.shiftBeatFx, enabled);
};
PioneerDDJFLX4.focusedFxGroup = function () {
const focusedFx = engine.getValue(
"[EffectRack1_EffectUnit1]",
"focused_effect"
);
return "[EffectRack1_EffectUnit1_Effect" + focusedFx + "]";
};
PioneerDDJFLX4.beatFxLevelDepthRotate = function (_channel, _control, value) {
if (PioneerDDJFLX4.shiftButtonDown[0] || PioneerDDJFLX4.shiftButtonDown[1]) {
engine.softTakeoverIgnoreNextValue("[EffectRack1_EffectUnit1]", "mix");
engine.setParameter(PioneerDDJFLX4.focusedFxGroup(), "meta", value / 0x7f);
} else {
engine.softTakeoverIgnoreNextValue(PioneerDDJFLX4.focusedFxGroup(), "meta");
engine.setParameter("[EffectRack1_EffectUnit1]", "mix", value / 0x7f);
}
};
PioneerDDJFLX4.changeFocusedEffectBy = function (numberOfSteps) {
let focusedEffect = engine.getValue(
"[EffectRack1_EffectUnit1]",
"focused_effect"
);
// Convert to zero-based index
focusedEffect -= 1;
// Standard Euclidean modulo by use of two plain modulos
const numberOfEffectsPerEffectUnit = 3;
focusedEffect =
(((focusedEffect + numberOfSteps) % numberOfEffectsPerEffectUnit) +
numberOfEffectsPerEffectUnit) %
numberOfEffectsPerEffectUnit;
// Convert back to one-based index
focusedEffect += 1;
engine.setValue("[EffectRack1_EffectUnit1]", "focused_effect", focusedEffect);
};
PioneerDDJFLX4.beatFxSelectPressed = function (_channel, _control, value) {
if (value === 0) {
return;
}
engine.setValue(PioneerDDJFLX4.focusedFxGroup(), "next_effect", value);
};
PioneerDDJFLX4.beatFxSelectShiftPressed = function (_channel, _control, value) {
if (value === 0) {
return;
}
engine.setValue(PioneerDDJFLX4.focusedFxGroup(), "prev_effect", value);
};
PioneerDDJFLX4.beatFxLeftPressed = function (_channel, _control, value) {
if (value === 0) {
return;
}
PioneerDDJFLX4.changeFocusedEffectBy(-1);
};
PioneerDDJFLX4.beatFxRightPressed = function (_channel, _control, value) {
if (value === 0) {
return;
}
PioneerDDJFLX4.changeFocusedEffectBy(1);
};
PioneerDDJFLX4.beatFxOnOffPressed = function (_channel, _control, value) {
if (value === 0) {
return;
}
const toggleEnabled = !engine.getValue(
PioneerDDJFLX4.focusedFxGroup(),
"enabled"
);
engine.setValue(PioneerDDJFLX4.focusedFxGroup(), "enabled", toggleEnabled);
};
PioneerDDJFLX4.beatFxOnOffShiftPressed = function (_channel, _control, value) {
if (value === 0) {
return;
}
engine.setParameter("[EffectRack1_EffectUnit1]", "mix", 0);
engine.softTakeoverIgnoreNextValue("[EffectRack1_EffectUnit1]", "mix");
for (let i = 1; i <= 3; i++) {
engine.setValue("[EffectRack1_EffectUnit1_Effect" + i + "]", "enabled", 0);
}
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.beatFx, false);
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.shiftBeatFx, false);
};
PioneerDDJFLX4.beatFxChannel1 = function (
_channel,
control,
value,
_status,
group
) {
let enableChannel = 0;
if (value === 0x7f) {
enableChannel = 1;
}
engine.setValue(group, "group_[Channel1]_enable", enableChannel);
};
PioneerDDJFLX4.beatFxChannel2 = function (
_channel,
control,
value,
_status,
group
) {
let enableChannel = 0;
if (value === 0x7f) {
enableChannel = 1;
}
engine.setValue(group, "group_[Channel2]_enable", enableChannel);
};
//
// Loop IN/OUT ADJUST
//
PioneerDDJFLX4.toggleLoopAdjustIn = function (
channel,
_control,
value,
_status,
group
) {
if (value === 0 || engine.getValue(group, "loop_enabled" === 0)) {
return;
}
PioneerDDJFLX4.loopAdjustIn[channel] = !PioneerDDJFLX4.loopAdjustIn[channel];
PioneerDDJFLX4.loopAdjustOut[channel] = false;
};
PioneerDDJFLX4.toggleLoopAdjustOut = function (
channel,
_control,
value,
_status,
group
) {
if (value === 0 || engine.getValue(group, "loop_enabled" === 0)) {
return;
}
PioneerDDJFLX4.loopAdjustOut[channel] =
!PioneerDDJFLX4.loopAdjustOut[channel];
PioneerDDJFLX4.loopAdjustIn[channel] = false;
};
// Two signals are sent here so that the light stays lit/unlit in its shift state too
PioneerDDJFLX4.setReloopLight = function (status, value) {
midi.sendShortMsg(status, 0x4d, value);
midi.sendShortMsg(status, 0x50, value);
};
PioneerDDJFLX4.setLoopButtonLights = function (status, value) {
[0x10, 0x11, 0x4e, 0x4c].forEach(function (control) {
midi.sendShortMsg(status, control, value);
});
};
PioneerDDJFLX4.startLoopLightsBlink = function (
channel,
control,
status,
group
) {
let blink = 0x7f;
PioneerDDJFLX4.stopLoopLightsBlink(group, control, status);
PioneerDDJFLX4.timers[group][control] = engine.beginTimer(500, () => {
blink = 0x7f - blink;
// When adjusting the loop out position, turn the loop in light off
if (PioneerDDJFLX4.loopAdjustOut[channel]) {
midi.sendShortMsg(status, 0x10, 0x00);
midi.sendShortMsg(status, 0x4c, 0x00);
} else {
midi.sendShortMsg(status, 0x10, blink);
midi.sendShortMsg(status, 0x4c, blink);
}
// When adjusting the loop in position, turn the loop out light off
if (PioneerDDJFLX4.loopAdjustIn[channel]) {
midi.sendShortMsg(status, 0x11, 0x00);
midi.sendShortMsg(status, 0x4e, 0x00);
} else {
midi.sendShortMsg(status, 0x11, blink);
midi.sendShortMsg(status, 0x4e, blink);
}
});
};
PioneerDDJFLX4.stopLoopLightsBlink = function (group, control, status) {
PioneerDDJFLX4.timers[group] = PioneerDDJFLX4.timers[group] || {};
if (PioneerDDJFLX4.timers[group][control] !== undefined) {
engine.stopTimer(PioneerDDJFLX4.timers[group][control]);
}
PioneerDDJFLX4.timers[group][control] = undefined;
PioneerDDJFLX4.setLoopButtonLights(status, 0x7f);
};
PioneerDDJFLX4.loopToggle = function (value, group, control) {
const status = group === "[Channel1]" ? 0x90 : 0x91,
channel = group === "[Channel1]" ? 0 : 1;
PioneerDDJFLX4.setReloopLight(status, value ? 0x7f : 0x00);
if (value) {
PioneerDDJFLX4.startLoopLightsBlink(channel, control, status, group);
} else {
PioneerDDJFLX4.stopLoopLightsBlink(group, control, status);
PioneerDDJFLX4.loopAdjustIn[channel] = false;
PioneerDDJFLX4.loopAdjustOut[channel] = false;
}
};
//
// CUE/LOOP CALL
//
PioneerDDJFLX4.cueLoopCallLeft = function (
_channel,
_control,
value,
_status,
group
) {
if (value) {
engine.setValue(group, "loop_scale", 0.5);
}
};
PioneerDDJFLX4.cueLoopCallRight = function (
_channel,
_control,
value,
_status,
group
) {
if (value) {
engine.setValue(group, "loop_scale", 2.0);
}
};
//
// BEAT SYNC
//
// Note that the controller sends different signals for a short press and a long
// press of the same button.
//
PioneerDDJFLX4.syncPressed = function (channel, control, value, status, group) {
if (engine.getValue(group, "sync_enabled") && value > 0) {
engine.setValue(group, "sync_enabled", 0);
} else {
engine.setValue(group, "beatsync_tempo", value);
}
};
PioneerDDJFLX4.syncLongPressed = function (
channel,
control,
value,
status,
group
) {
if (value) {
engine.setValue(group, "sync_enabled", 1);
}
};
PioneerDDJFLX4.cycleTempoRange = function (
_channel,
_control,
value,
_status,
group
) {
if (value === 0) {
return;
} // ignore release
const currRange = engine.getValue(group, "rateRange");
let idx = 0;
for (let i = 0; i < this.tempoRanges.length; i++) {
if (currRange === this.tempoRanges[i]) {
// idx get the index of the value in tempoRanges following the currently configured one
// or cycle back to 0 if the current is the last value of the list.
idx = (i + 1) % this.tempoRanges.length;
break;
}
}
engine.setValue(group, "rateRange", this.tempoRanges[idx]);
};
//
// Jog wheels
//
PioneerDDJFLX4.jogTurn = function (channel, _control, value, _status, group) {
const deckNum = channel + 1;
// wheel center at 64; <64 rew >64 fwd
let newVal = value - 64;
// loop_in / out adjust
const loopEnabled = engine.getValue(group, "loop_enabled");
if (loopEnabled > 0) {
if (PioneerDDJFLX4.loopAdjustIn[channel]) {
newVal =
newVal * PioneerDDJFLX4.loopAdjustMultiply +
engine.getValue(group, "loop_start_position");
engine.setValue(group, "loop_start_position", newVal);
return;
}
if (PioneerDDJFLX4.loopAdjustOut[channel]) {
newVal =
newVal * PioneerDDJFLX4.loopAdjustMultiply +
engine.getValue(group, "loop_end_position");
engine.setValue(group, "loop_end_position", newVal);
return;
}
}
if (engine.isScratching(deckNum)) {
engine.scratchTick(deckNum, newVal);
} else {
// fallback
engine.setValue(group, "jog", newVal * this.bendScale);
}
};
PioneerDDJFLX4.jogSearch = function (
_channel,
_control,
value,
_status,
group
) {
const newVal = (value - 64) * PioneerDDJFLX4.fastSeekScale;
engine.setValue(group, "jog", newVal);
};
PioneerDDJFLX4.jogTouch = function (channel, _control, value) {
const deckNum = channel + 1;
// skip while adjusting the loop points
if (
PioneerDDJFLX4.loopAdjustIn[channel] ||
PioneerDDJFLX4.loopAdjustOut[channel]
) {
return;
}
if (value !== 0 && this.vinylMode) {
engine.scratchEnable(deckNum, 720, 33 + 1 / 3, this.alpha, this.beta);
} else {
engine.scratchDisable(deckNum);
}
};
//
// Shift button
//
PioneerDDJFLX4.shiftPressed = function (
channel,
_control,
value,
_status,
_group
) {
PioneerDDJFLX4.shiftButtonDown[channel] = value === 0x7f;
};
//
// Tempo sliders
//
// The tempo option in Mixxx's deck preferences determine whether down/up
// increases/decreases the rate. Therefore it must be inverted here so that the
// UI and the control sliders always move in the same direction.
//
PioneerDDJFLX4.tempoSliderMSB = function (
channel,
control,
value,
status,
group
) {
PioneerDDJFLX4.highResMSB[group].tempoSlider = value;
};
PioneerDDJFLX4.tempoSliderLSB = function (
channel,
control,
value,
status,
group
) {
const fullValue = (PioneerDDJFLX4.highResMSB[group].tempoSlider << 7) + value;
engine.setValue(group, "rate", 1 - fullValue / 0x2000);
};
//
// Beat Jump mode
//
// Note that when we increase/decrease the sizes on the pad buttons, we use the
// value of the first pad (0x21) as an upper/lower limit beyond which we don't
// allow further increasing/decreasing of all the values.
//
PioneerDDJFLX4.beatjumpPadPressed = function (
_channel,
control,
value,
_status,
group
) {
if (value === 0) {
return;
}
engine.setValue(
group,
"beatjump_size",
Math.abs(PioneerDDJFLX4.beatjumpSizeForPad[control])
);
engine.setValue(
group,
"beatjump",
PioneerDDJFLX4.beatjumpSizeForPad[control]
);
};
PioneerDDJFLX4.increaseBeatjumpSizes = function (
_channel,
control,
value,
_status,
group
) {
if (value === 0 || PioneerDDJFLX4.beatjumpSizeForPad[0x21] * 16 > 16) {
return;
}
Object.keys(PioneerDDJFLX4.beatjumpSizeForPad).forEach(function (pad) {
PioneerDDJFLX4.beatjumpSizeForPad[pad] =
PioneerDDJFLX4.beatjumpSizeForPad[pad] * 16;
});
engine.setValue(
group,
"beatjump_size",
PioneerDDJFLX4.beatjumpSizeForPad[0x21]
);
};
PioneerDDJFLX4.decreaseBeatjumpSizes = function (
_channel,
control,
value,
_status,
group
) {
if (value === 0 || PioneerDDJFLX4.beatjumpSizeForPad[0x21] / 16 < 1 / 16) {
return;
}
Object.keys(PioneerDDJFLX4.beatjumpSizeForPad).forEach(function (pad) {
PioneerDDJFLX4.beatjumpSizeForPad[pad] =
PioneerDDJFLX4.beatjumpSizeForPad[pad] / 16;
});
engine.setValue(
group,
"beatjump_size",
PioneerDDJFLX4.beatjumpSizeForPad[0x21]
);
};
//
// Sampler mode
//
PioneerDDJFLX4.samplerPlayOutputCallbackFunction = function (
value,
group,
_control
) {
if (value === 1) {
const curPad = group.match(script.samplerRegEx)[1];
let deckIndex = 0;
let padIndex = 0;
if (curPad >= 1 && curPad <= 4) {
deckIndex = 0;
padIndex = curPad - 1;
} else if (curPad >= 5 && curPad <= 8) {
deckIndex = 2;
padIndex = curPad - 5;
} else if (curPad >= 9 && curPad <= 12) {
deckIndex = 0;
padIndex = curPad - 5;
} else if (curPad >= 13 && curPad <= 16) {
deckIndex = 2;
padIndex = curPad - 9;
}
PioneerDDJFLX4.startSamplerBlink(0x97 + deckIndex, 0x30 + padIndex, group);
}
};
PioneerDDJFLX4.padModeKeyPressed = function (
_channel,
_control,
value,
_status,
_group
) {
const deck =
_status === 0x90
? PioneerDDJFLX4.lights.deck1
: PioneerDDJFLX4.lights.deck2;
if (_control === 0x1b) {
PioneerDDJFLX4.toggleLight(deck.hotcueMode, true);
} else if (_control === 0x69) {
PioneerDDJFLX4.toggleLight(deck.keyboardMode, true);
} else if (_control === 0x1e) {
PioneerDDJFLX4.toggleLight(deck.padFX1Mode, true);
} else if (_control === 0x6b) {
PioneerDDJFLX4.toggleLight(deck.padFX2Mode, true);
} else if (_control === 0x20) {
PioneerDDJFLX4.toggleLight(deck.beatJumpMode, true);
} else if (_control === 0x6d) {
PioneerDDJFLX4.toggleLight(deck.beatLoopMode, true);
} else if (_control === 0x22) {
PioneerDDJFLX4.toggleLight(deck.samplerMode, true);
} else if (_control === 0x6f) {
PioneerDDJFLX4.toggleLight(deck.keyShiftMode, true);
}
};
PioneerDDJFLX4.samplerPadPressed = function (
_channel,
_control,
value,
_status,
group
) {
if (engine.getValue(group, "track_loaded")) {
engine.setValue(group, "cue_gotoandplay", value);
} else {
engine.setValue(group, "LoadSelectedTrack", value);
}
};
PioneerDDJFLX4.samplerPadShiftPressed = function (
_channel,
_control,
value,
_status,
group
) {
if (engine.getValue(group, "play")) {
engine.setValue(group, "cue_gotoandstop", value);
} else if (engine.getValue(group, "track_loaded")) {
engine.setValue(group, "eject", value);
}
};
PioneerDDJFLX4.startSamplerBlink = function (channel, control, group) {
let val = 0x7f;
PioneerDDJFLX4.stopSamplerBlink(channel, control);
PioneerDDJFLX4.timers[channel][control] = engine.beginTimer(250, () => {
val = 0x7f - val;
// blink the appropriate pad
midi.sendShortMsg(channel, control, val);
// also blink the pad while SHIFT is pressed
midi.sendShortMsg(channel + 1, control, val);
const isPlaying = engine.getValue(group, "play") === 1;
if (!isPlaying) {
// kill timer
PioneerDDJFLX4.stopSamplerBlink(channel, control);
// set the pad LED to ON
midi.sendShortMsg(channel, control, 0x7f);
// set the pad LED to ON while SHIFT is pressed
midi.sendShortMsg(channel + 1, control, 0x7f);
}
});
};
PioneerDDJFLX4.stopSamplerBlink = function (channel, control) {
PioneerDDJFLX4.timers[channel] = PioneerDDJFLX4.timers[channel] || {};
if (PioneerDDJFLX4.timers[channel][control] !== undefined) {
engine.stopTimer(PioneerDDJFLX4.timers[channel][control]);
PioneerDDJFLX4.timers[channel][control] = undefined;
}
};
PioneerDDJFLX4.toggleQuantize = function (
_channel,
_control,
value,
_status,
group
) {
if (value) {
script.toggleControl(group, "quantize");
}
};
PioneerDDJFLX4.quickJumpForward = function (
_channel,
_control,
value,
_status,
group
) {
if (value) {
engine.setValue(group, "beatjump", PioneerDDJFLX4.quickJumpSize);
}
};
PioneerDDJFLX4.quickJumpBack = function (
_channel,
_control,
value,
_status,
group
) {
if (value) {
engine.setValue(group, "beatjump", -PioneerDDJFLX4.quickJumpSize);
}
};
//
// Shutdown
//
PioneerDDJFLX4.shutdown = function () {
// reset vumeter
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.deck1.vuMeter, false);
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.deck2.vuMeter, false);
// housekeeping
// turn off all Sampler LEDs
for (var i = 0; i <= 7; ++i) {
midi.sendShortMsg(0x97, 0x30 + i, 0x00); // Deck 1 pads
midi.sendShortMsg(0x98, 0x30 + i, 0x00); // Deck 1 pads with SHIFT
midi.sendShortMsg(0x99, 0x30 + i, 0x00); // Deck 2 pads
midi.sendShortMsg(0x9a, 0x30 + i, 0x00); // Deck 2 pads with SHIFT
}
// turn off all Hotcue LEDs
for (i = 0; i <= 7; ++i) {
midi.sendShortMsg(0x97, 0x00 + i, 0x00); // Deck 1 pads
midi.sendShortMsg(0x98, 0x00 + i, 0x00); // Deck 1 pads with SHIFT
midi.sendShortMsg(0x99, 0x00 + i, 0x00); // Deck 2 pads
midi.sendShortMsg(0x9a, 0x00 + i, 0x00); // Deck 2 pads with SHIFT
}
// turn off loop in and out lights
PioneerDDJFLX4.setLoopButtonLights(0x90, 0x00);
PioneerDDJFLX4.setLoopButtonLights(0x91, 0x00);
// turn off reloop lights
PioneerDDJFLX4.setReloopLight(0x90, 0x00);
PioneerDDJFLX4.setReloopLight(0x91, 0x00);
// stop any flashing lights
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.beatFx, false);
PioneerDDJFLX4.toggleLight(PioneerDDJFLX4.lights.shiftBeatFx, false);
// stop the keepalive timer
engine.stopTimer(PioneerDDJFLX4.keepAliveTimer);
};