Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pioneer DDJ-FLX4: New mapping base on DDJ-400 #11245

Merged
merged 14 commits into from
Sep 4, 2023
Merged
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
806 changes: 806 additions & 0 deletions res/controllers/Pioneer-DDJ-FLX4-script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,806 @@
// 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 = {};

// 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.8;

PioneerDDJFLX4.tempoRanges = [0.06, 0.10, 0.16, 0.25];
JoergAtGithub marked this conversation as resolved.
Show resolved Hide resolved

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]", "VuMeter", PioneerDDJFLX4.vuMeterUpdate);
engine.makeConnection("[Channel2]", "VuMeter", 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);

for (var i = 1; i <= 16; ++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);

// query the controller for current control positions on startup
midi.sendSysexMsg([0xF0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x02, 0x06, 0x00, 0x03, 0x01, 0xf7], 12);
};

//
// 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, function() {
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", 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;
Copy link
Member

Choose a reason for hiding this comment

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

Please add a comment, which explains the calculation of idx

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, function() {
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);
};
3,238 changes: 3,238 additions & 0 deletions res/controllers/Pioneer-DDJ-FLX4.midi.xml

Large diffs are not rendered by default.