// 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.8; PioneerDDJFLX4.tempoRanges = [0.06, 0.10, 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); };