summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Holthuis <jholthuis@mixxx.org>2022-01-16 00:01:26 +0100
committerJan Holthuis <jholthuis@mixxx.org>2022-01-16 00:01:26 +0100
commit418a7e362abac709e753589c2201504b98191f20 (patch)
tree7859016766ecab893018f960ea2dedc0088b3c38
parent120255735322b6a2c45b578322718968fea5b60c (diff)
parent8d501b30e8dcc30b0818d82f27cab191919c7f77 (diff)
Merge branch '2.3' of github.com:mixxxdj/mixxx
-rwxr-xr-xres/controllers/Pioneer-DDJ-SB3-scripts.js1626
-rwxr-xr-xres/controllers/Pioneer-DDJ-SB3.midi.xml5061
-rw-r--r--src/track/serato/markers.cpp4
-rw-r--r--src/track/serato/markers2.cpp8
4 files changed, 6694 insertions, 5 deletions
diff --git a/res/controllers/Pioneer-DDJ-SB3-scripts.js b/res/controllers/Pioneer-DDJ-SB3-scripts.js
new file mode 100755
index 0000000000..e5740e7d48
--- /dev/null
+++ b/res/controllers/Pioneer-DDJ-SB3-scripts.js
@@ -0,0 +1,1626 @@
+var PioneerDDJSB3 = {};
+
+///////////////////////////////////////////////////////////////
+// USER OPTIONS //
+///////////////////////////////////////////////////////////////
+
+// If true, vinyl mode will be enabled when Mixxx starts.
+PioneerDDJSB3.vinylModeOnStartup = false;
+
+// If true, pressing shift + cue will play the track in reverse and enable slip mode,
+// which can be used like a censor effect. If false, pressing shift + cue jumps to
+// the beginning of the track and stops playback.
+PioneerDDJSB3.reverseRollOnShiftCue = false;
+
+// Sets the jogwheels sensitivity. 1 is default, 2 is twice as sensitive, 0.5 is half as sensitive.
+PioneerDDJSB3.jogwheelSensitivity = 1.0;
+
+// Sets how much more sensitive the jogwheels get when holding shift.
+// Set to 1 to disable jogwheel sensitivity increase when holding shift.
+PioneerDDJSB3.jogwheelShiftMultiplier = 100;
+
+// If true Level-Meter shows VU-Master left & right. If false shows level of active deck.
+PioneerDDJSB3.showVumeterMaster = false;
+
+// If true VU-Level twinkle if AutoDJ is ON.
+PioneerDDJSB3.twinkleVumeterAutodjOn = true;
+
+// If true, releasing browser knob jumps forward to jumpPreviewPosition.
+PioneerDDJSB3.jumpPreviewEnabled = true;
+// Position in the track to jump to. 0 is the beginning of the track and 1 is the end.
+PioneerDDJSB3.jumpPreviewPosition = 0.5;
+
+PioneerDDJSB3.looprollIntervals = [1 / 16, 1 / 8, 1 / 4, 1 / 2, 1, 2, 4, 8];
+
+/*
+ Pioneer DDJ-SB3 mapping for Mixxx
+ Copyright (c) 2017 Be (be.0@gmx.com), licensed under GPL version 2 or later
+ Copyright (c) 2014-2015 various contributors, licensed under MIT license
+
+ Contributors and change log:
+ - Be (be.0@gmx.com): update effects and autoloop mode for Mixxx 2.1, fix level meter scaling,
+ remove LED flickering when pressing shift, start porting to Components
+ - Michael Stahl (DG3NEC): original DDJ-SB2 mapping for Mixxx 2.0
+ - Joan Ardiaca Jové (joan.ardiaca@gmail.com): Pioneer DDJ-SB mapping for Mixxx 2.0
+ - wingcom (wwingcomm@gmail.com): start of Pioneer DDJ-SB mapping
+ https://github.com/wingcom/Mixxx-Pioneer-DDJ-SB
+ - Hilton Rudham: Pioneer DDJ-SR mapping
+ https://github.com/hrudham/Mixxx-Pioneer-DDJ-SR
+
+*/
+
+PioneerDDJSB3.PadMode = {
+ HotCue: 0x1B,
+ FxFadeMix: 0x1E,
+ PadScratch: 0x20,
+ Sampler: 0x22,
+ BeatJump: 0x69,
+ Roll: 0x6B,
+ Slicer: 0x6D,
+ Trans: 0x6E
+};
+
+///////////////////////////////////////////////////////////////
+// INIT, SHUTDOWN & GLOBAL HELPER //
+///////////////////////////////////////////////////////////////
+PioneerDDJSB3.trackLoaded = function(value, group) {
+ var deckIndex = PioneerDDJSB3.channelGroups[group];
+
+ if (value) {
+ midi.sendShortMsg(0x96, 0x46 + deckIndex, 0x7F);
+ } else {
+ midi.sendShortMsg(0x96, 0x46 + deckIndex, 0x0);
+ }
+};
+
+// When unloading a deck, it should pass 0 for BPM
+//
+// Controller should know track BPM to match the TRANS pads velocity
+// Also, changing deck without sending BPM does not work
+PioneerDDJSB3.updateBPM = function(bpm, group) {
+ // Prevent sending BPM for unselected Deck.
+ // We send it when changing deck also, to keep in sync
+ if (group === "[Channel1]" && PioneerDDJSB3.deck3Enabled) {
+ return;
+ }
+ if (group === "[Channel2]" && PioneerDDJSB3.deck4Enabled) {
+ return;
+ }
+ if (group === "[Channel3]" && !PioneerDDJSB3.deck3Enabled) {
+ return;
+ }
+ if (group === "[Channel4]" && !PioneerDDJSB3.deck4Enabled) {
+ return;
+ }
+
+ var bpmValue = Math.round(bpm * 100);
+ var bpmBits = bpmValue.toString(2);
+
+ var bpmBitsPadded = [];
+
+ var offset = 16 - bpmBits.length;
+
+ var i;
+
+ for (i = 0; i < 16; i++) {
+ if (i < offset) {
+ bpmBitsPadded[i] = "0";
+ } else {
+ bpmBitsPadded[i] = bpmBits[i - offset];
+ }
+ }
+
+ var bytes = [];
+
+ for (i = 0; i < 4; i++) {
+ var mbyte = 0;
+
+ for (var j = 0; j < 4; j++) {
+ var bitIndex = (i * 4) + j;
+ var bit = parseInt(bpmBitsPadded[bitIndex]);
+ mbyte = mbyte | (bit << (3 - j));
+ }
+
+ bytes[i] = mbyte;
+ }
+
+ var deckIndex = PioneerDDJSB3.channelGroups[group];
+ var deckByte = 0x11 + deckIndex;
+
+ var sysexMessage = [0xF0, 0x00, 0x20, 0x7F, deckByte, 0x00, 0x00, bytes[0], bytes[1], bytes[2], bytes[3], 0xF7];
+ midi.sendSysexMsg(sysexMessage, sysexMessage.length);
+};
+
+PioneerDDJSB3.longButtonPress = false;
+
+PioneerDDJSB3.flasher = {};
+
+PioneerDDJSB3.flasher.functions = [];
+
+PioneerDDJSB3.flasher.init = function() {
+ var flag = true;
+
+ PioneerDDJSB3.flasher.timer = engine.beginTimer(500, function() {
+ flag = !flag;
+
+ for (var i = 0; i < PioneerDDJSB3.flasher.functions.length; i++) {
+ PioneerDDJSB3.flasher.functions[i](flag);
+ }
+ });
+};
+
+PioneerDDJSB3.flasher.shutdown = function() {
+ engine.stopTimer(PioneerDDJSB3.flasher.timer);
+};
+
+PioneerDDJSB3.flasher.addFunction = function(fn) {
+ PioneerDDJSB3.flasher.functions.push(fn);
+};
+
+PioneerDDJSB3.flasher.removeFunction = function(fn) {
+ PioneerDDJSB3.flasher.functions = _.filter(PioneerDDJSB3.flasher.functions, function(f) {
+ return fn !== f;
+ });
+};
+
+PioneerDDJSB3.midiOutputBeatLedsStart = 0x60;
+PioneerDDJSB3.midiOutputBeatLedsCount = 8;
+
+PioneerDDJSB3.scratchSettings = {
+ "alpha": 1.0 / 8,
+ "beta": 1.0 / 8 / 32,
+ "jogResolution": 720,
+ "vinylSpeed": 33 + 1 / 3
+};
+
+PioneerDDJSB3.channelGroups = {
+ "[Channel1]": 0x00,
+ "[Channel2]": 0x01,
+ "[Channel3]": 0x02,
+ "[Channel4]": 0x03
+};
+
+PioneerDDJSB3.samplerGroups = {
+ "[Sampler1]": {channels: ["[Channel1]", "[Channel3]"], ledNumber: 0x00},
+ "[Sampler2]": {channels: ["[Channel1]", "[Channel3]"], ledNumber: 0x01},
+ "[Sampler3]": {channels: ["[Channel1]", "[Channel3]"], ledNumber: 0x02},
+ "[Sampler4]": {channels: ["[Channel1]", "[Channel3]"], ledNumber: 0x03},
+ "[Sampler5]": {channels: ["[Channel2]", "[Channel4]"], ledNumber: 0x00},
+ "[Sampler6]": {channels: ["[Channel2]", "[Channel4]"], ledNumber: 0x01},
+ "[Sampler7]": {channels: ["[Channel2]", "[Channel4]"], ledNumber: 0x02},
+ "[Sampler8]": {channels: ["[Channel2]", "[Channel4]"], ledNumber: 0x03},
+};
+
+PioneerDDJSB3.ledGroups = {
+ "hotCue": 0x00,
+ "fxFade": 0x10,
+ "padScratch": 0x20,
+ "sampler": 0x30,
+ "roll": 0x50,
+};
+
+PioneerDDJSB3.nonPadLeds = {
+ "headphoneCue": 0x54,
+ "shiftHeadphoneCue": 0x68,
+ "cue": 0x0C,
+ "shiftCue": 0x48,
+ "keyLock": 0x1A,
+ "shiftKeyLock": 0x60,
+ "play": 0x0B,
+ "shiftPlay": 0x47,
+ "vinyl": 0x17,
+ "shiftVinyl": 0x40,
+ "sync": 0x58,
+ "shiftSync": 0x5C,
+ "autoLoop": 0x14,
+ "shiftAutoLoop": 0x50,
+};
+
+PioneerDDJSB3.channelsToPadNumber = {
+ "[Channel1]": 1,
+ "[Channel2]": 2,
+ "[Channel3]": 3,
+ "[Channel4]": 4
+};
+
+PioneerDDJSB3.channelsToEffectUnitNumber = {
+ "[Channel1]": 1,
+ "[Channel2]": 2,
+ "[Channel3]": 1,
+ "[Channel4]": 2
+};
+
+PioneerDDJSB3.init = function() {
+ var initSysBytes = [0xF0, 0x00, 0x20, 0x7F, 0x03, 0x01, 0xF7];
+ midi.sendSysexMsg(initSysBytes, initSysBytes.length);
+
+ PioneerDDJSB3.shiftPressed = false;
+
+ PioneerDDJSB3.chFaderStart = [
+ null,
+ null
+ ];
+
+ PioneerDDJSB3.scratchMode = [false, false, false, false];
+
+ PioneerDDJSB3.valueVuMeter = {
+ "[Channel1]_current": 0,
+ "[Channel2]_current": 0,
+ "[Channel3]_current": 0,
+ "[Channel4]_current": 0,
+ "[Channel1]_enabled": 1,
+ "[Channel2]_enabled": 1,
+ "[Channel3]_enabled": 1,
+ "[Channel4]_enabled": 1,
+ };
+
+ PioneerDDJSB3.deck = [];
+ PioneerDDJSB3.deck[1] = new PioneerDDJSB3.Deck(1);
+ PioneerDDJSB3.deck[2] = new PioneerDDJSB3.Deck(2);
+ PioneerDDJSB3.deck[3] = new PioneerDDJSB3.Deck(3);
+ PioneerDDJSB3.deck[4] = new PioneerDDJSB3.Deck(4);
+
+ PioneerDDJSB3.effectUnit = [];
+ PioneerDDJSB3.effectUnit[1] = new PioneerDDJSB3.EffectUnit(1);
+ PioneerDDJSB3.effectUnit[2] = new PioneerDDJSB3.EffectUnit(2);
+
+ PioneerDDJSB3.padForDeck = [];
+ PioneerDDJSB3.padForDeck[1] = new PioneerDDJSB3.Pad(1);
+ PioneerDDJSB3.padForDeck[2] = new PioneerDDJSB3.Pad(2);
+ PioneerDDJSB3.padForDeck[3] = new PioneerDDJSB3.Pad(3);
+ PioneerDDJSB3.padForDeck[4] = new PioneerDDJSB3.Pad(4);
+
+ PioneerDDJSB3.bindNonDeckControlConnections(false);
+ PioneerDDJSB3.initDeck("[Channel1]");
+ PioneerDDJSB3.initDeck("[Channel2]");
+ PioneerDDJSB3.initDeck("[Channel3]");
+ PioneerDDJSB3.initDeck("[Channel4]");
+
+ if (PioneerDDJSB3.twinkleVumeterAutodjOn) {
+ PioneerDDJSB3.vuMeterTimer = engine.beginTimer(100, PioneerDDJSB3.vuMeterTwinkle());
+ }
+
+ // request the positions of the knobs and faders from the controller
+ midi.sendShortMsg(0x9B, 0x09, 0x7f);
+
+ PioneerDDJSB3.flasher.init();
+ PioneerDDJSB3.initFlashingPadLedControl();
+};
+
+PioneerDDJSB3.shiftListeners = [];
+
+PioneerDDJSB3.Deck = function(deckNumber) {
+ var theDeck = this;
+ this.group = "[Channel" + deckNumber + "]";
+
+ this.shiftButton = function(channel, control, value, status, group) {
+ var i;
+ if (value > 0) {
+ theDeck.shift();
+ PioneerDDJSB3.shiftPressed = true;
+ PioneerDDJSB3.chFaderStart[deckNumber] = null;
+ for (i = 0; i < PioneerDDJSB3.shiftListeners.length; i++) {
+ PioneerDDJSB3.shiftListeners[i](group, true);
+ }
+ } else {
+ theDeck.unshift();
+ PioneerDDJSB3.shiftPressed = false;
+ PioneerDDJSB3.headphoneLevel.unshift();
+ for (i = 0; i < PioneerDDJSB3.shiftListeners.length; i++) {
+ PioneerDDJSB3.shiftListeners[i](group, false);
+ }
+ }
+ };
+ this.playButton = new components.PlayButton({
+ midi: [0x90 + deckNumber - 1, 0x0B],
+ shiftOffset: 60,
+ shiftControl: true,
+ sendShifted: true
+ });
+
+ this.cueButton = new components.CueButton({
+ midi: [0x90 + deckNumber - 1, 0x0C],
+ shiftOffset: 60,
+ shiftControl: true,
+ sendShifted: true,
+ reverseRollOnShift: PioneerDDJSB3.reverseRollOnShiftCue,
+ });
+
+ this.syncButton = new components.SyncButton({
+ midi: [0x90 + deckNumber - 1, 0x58],
+ shiftOffset: 4,
+ shiftControl: true,
+ sendShifted: true,
+ });
+
+ var effectUnitNumber = deckNumber;
+ if (deckNumber > 2) {
+ effectUnitNumber -= 2;
+ }
+
+ // The Mixxx UI call this Gain, but on the controller the knob is labeled TRIM
+ this.gainKnob = new components.Pot({
+ unshift: function() {
+ this.group = "[Channel" + deckNumber + "]";
+ this.inKey = "pregain";
+ this.disconnect();
+ this.connect();
+ }
+ });
+
+ this.eqKnob = [];
+ for (var k = 1; k <= 3; k++) {
+ this.eqKnob[k] = new components.Pot({
+ number: k,
+ unshift: function() {
+ this.group = "[EqualizerRack1_[Channel" + deckNumber + "]_Effect1]";
+ this.inKey = "parameter" + this.number;
+ this.disconnect();
+ this.connect();
+ }
+ });
+ }
+
+ this.quickEffectKnob = new components.Pot({
+ unshift: function() {
+ this.group = "[QuickEffectRack1_[Channel" + deckNumber + "]]";
+ this.inKey = "super1";
+ this.disconnect();
+ this.connect();
+ },
+ shift: function() {
+ var focusedEffect = engine.getValue("[EffectRack1_EffectUnit" + effectUnitNumber + "]", "focused_effect");
+ this.group = "[EffectRack1_EffectUnit" + effectUnitNumber + "_Effect" + focusedEffect + "]";
+ this.inKey = "parameter5";
+ this.disconnect();
+ this.connect();
+ },
+ });
+
+ this.tempoFader = new components.Pot({
+ inKey: "rate",
+ invert: true,
+ });
+
+ this.forEachComponent(function(c) {
+ if (c.group === undefined) {
+ c.group = theDeck.group;
+ c.connect();
+ c.trigger();
+ }
+ });
+
+ engine.setValue("[Channel" + deckNumber + "]", "rate_dir", -1);
+
+ this.loadConnection = engine.makeConnection("[Channel" + deckNumber + "]", "track_loaded", PioneerDDJSB3.trackLoaded);
+ this.bpmConnection = engine.makeConnection("[Channel" + deckNumber + "]", "bpm", PioneerDDJSB3.updateBPM);
+ this.bpmConnection.trigger();
+};
+PioneerDDJSB3.Deck.prototype = components.ComponentContainer.prototype;
+
+PioneerDDJSB3.Pad = function(padNumber) {
+ var _this = this;
+
+ this.padNumber = padNumber;
+
+ this.slicerButtons = [];
+
+ for (var i = 1; i <= PioneerDDJSB3.midiOutputBeatLedsCount; i++) {
+ (function(beat) {
+ _this.slicerButtons[beat] = function(channel, control, value, status) {
+ if (_this.slicer) {
+ _this.slicer.buttons[beat](channel, control, value, status);
+ }
+ };
+ })(i);
+ }
+
+ // Change BeatJump leds when shifted
+ PioneerDDJSB3.shiftListeners.push(function(group, isShifted) {
+ if (PioneerDDJSB3.channelsToPadNumber[group] === padNumber) {
+ if (isShifted) {
+ for (var i = 0; i < 8; i++) {
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x40 + i, 0x7F);
+ }
+ } else {
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x40, 0x0);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x41, 0x0);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x42, 0x7F);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x43, 0x7F);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x44, 0x0);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x45, 0x0);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x46, 0x0);
+ midi.sendShortMsg(0x97 + padNumber - 1, 0x47, 0x0);
+ }
+ }
+ });
+};
+
+PioneerDDJSB3.Pad.prototype.setModeActive = function(activeMode) {
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.HotCue, activeMode === PioneerDDJSB3.PadMode.HotCue ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.FxFadeMix, activeMode === PioneerDDJSB3.PadMode.FxFadeMix ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.PadScratch, activeMode === PioneerDDJSB3.PadMode.PadScratch ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.Sampler, activeMode === PioneerDDJSB3.PadMode.Sampler ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.BeatJump, activeMode === PioneerDDJSB3.PadMode.BeatJump ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.Roll, activeMode === PioneerDDJSB3.PadMode.Roll ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.Slicer, activeMode === PioneerDDJSB3.PadMode.Slicer ? 0x7F : 0x0);
+ midi.sendShortMsg(0x90 + this.padNumber - 1, PioneerDDJSB3.PadMode.Trans, activeMode === PioneerDDJSB3.PadMode.Trans ? 0x7F : 0x0);
+};
+
+PioneerDDJSB3.Pad.prototype.clearSlicer = function() {
+ if (this.slicer) {
+ this.slicer.shutdown();
+ this.slicer = null;
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.hotcueMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.HotCue);
+ this.clearSlicer();
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.beatJumpMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.BeatJump);
+ this.clearSlicer();
+
+ // Let jump pad led on
+ midi.sendShortMsg(0x97 + this.padNumber - 1, 0x42, 0x7F);
+ midi.sendShortMsg(0x97 + this.padNumber - 1, 0x43, 0x7F);
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.fxFadeMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.FxFadeMix);
+ this.clearSlicer();
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.rollMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.Roll);
+ this.clearSlicer();
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.padScratchMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.PadScratch);
+ this.clearSlicer();
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.slicerMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.Slicer);
+
+ if (!this.slicer) {
+ var group = "[Channel" + this.padNumber + "]";
+ var midiOutputOp = 0x97 + this.padNumber - 1;
+
+ this.slicer = new PioneerDDJSB3.Slicer(group, midiOutputOp);
+ }
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.samplerMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.Sampler);
+ this.clearSlicer();
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.transMode = function(channel, control, value) {
+ if (value) {
+ this.setModeActive(PioneerDDJSB3.PadMode.Trans);
+ this.clearSlicer();
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.beatJumpMultiply = function(channel, control, value, status, group) {
+ if (value) {
+ var size = engine.getValue(group, "beatjump_size");
+ engine.setValue(group, "beatjump_size", size * 2.0);
+ }
+};
+
+PioneerDDJSB3.Pad.prototype.beatJumpDivide = function(channel, control, value, status, group) {
+ if (value) {
+ var size = engine.getValue(group, "beatjump_size");
+ engine.setValue(group, "beatjump_size", size / 2.0);
+ }
+};
+
+PioneerDDJSB3.shutdown = function() {
+ // turn off button LEDs
+ var skip = [0x72, 0x1B, 0x69, 0x1E, 0x6B, 0x20, 0x6D, 0x22, 0x6F, 0x70, 0x75];
+ for (var channel = 0; channel <= 10; channel++) {
+ for (var control = 0; control <= 127; control++) {
+ // skip deck toggle buttons and pad mode buttons
+ if (skip.indexOf(control) > -1) {
+ continue;
+ }
+ midi.sendShortMsg(0x90 + channel, control, 0);
+ }
+ }
+
+
+ // switch to decks 1 and 2 to turn off deck indication lights
+ midi.sendShortMsg(0x90, 0x72, 0x7f);
+ midi.sendShortMsg(0x91, 0x72, 0x7f);
+
+ // turn off level meters
+ for (channel = 0; channel <= 3; channel++) {
+ midi.sendShortMsg(0xB0 + channel, 2, 0);
+ }
+
+ PioneerDDJSB3.flasher.shutdown();
+};
+
+PioneerDDJSB3.longButtonPressWait = function() {
+ engine.stopTimer(PioneerDDJSB3.longButtonPressTimer);
+ PioneerDDJSB3.longButtonPress = true;
+};
+
+///////////////////////////////////////////////////////////////
+// VU - Meter //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.blinkAutodjState = 0; // new for DDJ-SB2
+
+PioneerDDJSB3.vuMeterTwinkle = function() {
+ if (engine.getValue("[AutoDJ]", "enabled")) {
+ PioneerDDJSB3.blinkAutodjState = PioneerDDJSB3.blinkAutodjState + 1;
+ if (PioneerDDJSB3.blinkAutodjState > 3) {
+ PioneerDDJSB3.blinkAutodjState = 0;
+ }
+ if (PioneerDDJSB3.blinkAutodjState === 0) {
+ PioneerDDJSB3.valueVuMeter["[Channel1]_enabled"] = 0;
+ PioneerDDJSB3.valueVuMeter["[Channel3]_enabled"] = 0;
+ PioneerDDJSB3.valueVuMeter["[Channel2]_enabled"] = 0;
+ PioneerDDJSB3.valueVuMeter["[Channel4]_enabled"] = 0;
+ }
+ if (PioneerDDJSB3.blinkAutodjState === 1) {
+ PioneerDDJSB3.valueVuMeter["[Channel1]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel3]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel2]_enabled"] = 0;
+ PioneerDDJSB3.valueVuMeter["[Channel4]_enabled"] = 0;
+ }
+ if (PioneerDDJSB3.blinkAutodjState === 2) {
+ PioneerDDJSB3.valueVuMeter["[Channel1]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel3]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel2]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel4]_enabled"] = 1;
+ }
+ if (PioneerDDJSB3.blinkAutodjState === 3) {
+ PioneerDDJSB3.valueVuMeter["[Channel1]_enabled"] = 0;
+ PioneerDDJSB3.valueVuMeter["[Channel3]_enabled"] = 0;
+ PioneerDDJSB3.valueVuMeter["[Channel2]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel4]_enabled"] = 1;
+ }
+ } else {
+ PioneerDDJSB3.valueVuMeter["[Channel1]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel3]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel2]_enabled"] = 1;
+ PioneerDDJSB3.valueVuMeter["[Channel4]_enabled"] = 1;
+ }
+};
+
+
+///////////////////////////////////////////////////////////////
+// AutoDJ //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.autodjSkipNext = function(channel, control, value) {
+ if (value === 0) {
+ return;
+ }
+ if (engine.getValue("[AutoDJ]", "enabled")) {
+ engine.setValue("[AutoDJ]", "skip_next", true);
+ }
+};
+
+PioneerDDJSB3.autodjToggle = function(channel, control, value) {
+ if (value === 0) {
+ return;
+ }
+ if (engine.getValue("[AutoDJ]", "enabled")) {
+ engine.setValue("[AutoDJ]", "enabled", false);
+ } else {
+ engine.setValue("[AutoDJ]", "enabled", true);
+ }
+};
+
+
+///////////////////////////////////////////////////////////////
+// CONTROL BINDING //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.bindSamplerControlConnections = function(samplerGroup) {
+ engine.makeConnection(samplerGroup, "duration", PioneerDDJSB3.samplerLedsDuration);
+ engine.makeConnection(samplerGroup, "play", PioneerDDJSB3.samplerLedsPlay);
+};
+
+PioneerDDJSB3.bindDeckControlConnections = function(channelGroup, isUnbinding) {
+ var i,
+ index,
+ controlsToFunctions = {
+ "pfl": PioneerDDJSB3.headphoneCueLed,
+ "keylock": PioneerDDJSB3.keyLockLed,
+ "loop_enabled": PioneerDDJSB3.autoLoopLed,
+ };
+
+ controlsToFunctions.slipEnabled = PioneerDDJSB3.slipLed;
+
+ for (i = 1; i <= 8; i++) {
+ controlsToFunctions["hotcue_" + i + "_enabled"] = PioneerDDJSB3.hotCueLeds;
+ }
+
+ for (index in PioneerDDJSB3.looprollIntervals) {
+ controlsToFunctions["beatlooproll_" + PioneerDDJSB3.looprollIntervals[index] + "_activate"] = PioneerDDJSB3.beatlooprollLeds;
+ }
+
+ script.bindConnections(channelGroup, controlsToFunctions, isUnbinding);
+};
+
+PioneerDDJSB3.bindNonDeckControlConnections = function(isUnbinding) {
+ var samplerIndex;
+
+ for (samplerIndex = 1; samplerIndex <= 8; samplerIndex++) {
+ PioneerDDJSB3.bindSamplerControlConnections("[Sampler" + samplerIndex + "]", isUnbinding);
+ }
+
+ if (PioneerDDJSB3.showVumeterMaster) {
+ engine.connectControl("[Master]", "VuMeterL", PioneerDDJSB3.VuMeterLeds, isUnbinding);
+ engine.connectControl("[Master]", "VuMeterR", PioneerDDJSB3.VuMeterLeds, isUnbinding);
+ } else {
+ engine.connectControl("[Channel1]", "VuMeter", PioneerDDJSB3.VuMeterLeds, isUnbinding);
+ engine.connectControl("[Channel2]", "VuMeter", PioneerDDJSB3.VuMeterLeds, isUnbinding);
+ engine.connectControl("[Channel3]", "VuMeter", PioneerDDJSB3.VuMeterLeds, isUnbinding);
+ engine.connectControl("[Channel4]", "VuMeter", PioneerDDJSB3.VuMeterLeds, isUnbinding);
+ }
+};
+
+///////////////////////////////////////////////////////////////
+// DECK SWITCHING //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.deckSwitchTable = {
+ "[Channel1]": "[Channel1]",
+ "[Channel2]": "[Channel2]",
+ "[Channel3]": "[Channel3]",
+ "[Channel4]": "[Channel4]"
+
+};
+
+PioneerDDJSB3.deckShiftSwitchTable = {
+ "[Channel1]": "[Channel3]",
+ "[Channel2]": "[Channel4]",
+ "[Channel3]": "[Channel1]",
+ "[Channel4]": "[Channel2]"
+};
+
+PioneerDDJSB3.initDeck = function(group) {
+ PioneerDDJSB3.bindDeckControlConnections(group, false);
+ PioneerDDJSB3.nonPadLedControl(group, PioneerDDJSB3.nonPadLeds.shiftKeyLock, PioneerDDJSB3.channelGroups[group] > 1);
+ PioneerDDJSB3.toggleScratch(null, null, PioneerDDJSB3.vinylModeOnStartup, null, group);
+};
+
+
+///////////////////////////////////////////////////////////////
+// HIGH RESOLUTION MIDI INPUT HANDLERS //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.highResMSB = {
+ "[Channel1]": {},
+ "[Channel2]": {},
+ "[Channel3]": {},
+ "[Channel4]": {}
+};
+
+PioneerDDJSB3.deckFaderMSB = function(channel, control, value, status, group) {
+ PioneerDDJSB3.highResMSB[group].deckFader = value;
+};
+
+PioneerDDJSB3.deckFaderLSB = function(channel, control, value, status, group) {
+ var fullValue = (PioneerDDJSB3.highResMSB[group].deckFader << 7) + value;
+
+ if (PioneerDDJSB3.shiftPressed &&
+ engine.getValue(group, "volume") === 0 &&
+ fullValue !== 0 &&
+ engine.getValue(group, "play") === 0
+ ) {
+ PioneerDDJSB3.chFaderStart[channel] = engine.getValue(group, "playposition");
+ engine.setValue(group, "play", 1);
+ } else if (
+ PioneerDDJSB3.shiftPressed &&
+ engine.getValue(group, "volume") !== 0 &&
+ fullValue === 0 &&
+ engine.getValue(group, "play") === 1 &&
+ PioneerDDJSB3.chFaderStart[channel] !== null
+ ) {
+ engine.setValue(group, "play", 0);
+ engine.setValue(group, "playposition", PioneerDDJSB3.chFaderStart[channel]);
+ PioneerDDJSB3.chFaderStart[channel] = null;
+ }
+ engine.setValue(group, "volume", fullValue / 0x3FFF);
+};
+
+///////////////////////////////////////////////////////////////
+// SINGLE MESSAGE MIDI INPUT HANDLERS //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.beatloopRollButtons = function(channel, control, value, status, group) {
+ var index = control - 0x50;
+ engine.setValue(
+ PioneerDDJSB3.deckSwitchTable[group],
+ "beatlooproll_" + PioneerDDJSB3.looprollIntervals[index] + "_activate",
+ value
+ );
+};
+
+PioneerDDJSB3.vinylButton = function(channel, control, value, status, group) {
+ PioneerDDJSB3.toggleScratch(channel, control, value, status, group);
+};
+
+PioneerDDJSB3.slipButton = function(channel, control, value, status, group) {
+ if (value) {
+ script.toggleControl(group, "slipEnabled");
+ }
+};
+
+PioneerDDJSB3.keyLockButton = function(channel, control, value, status, group) {
+ if (value) {
+ script.toggleControl(group, "keylock");
+ }
+};
+
+PioneerDDJSB3.shiftKeyLockButton = function(channel, control, value, status, group) {
+ if (value) {
+ var currentTempoRange = engine.getValue(group, "rateRange");
+ var deckIndex = status - 0x90 + 1;
+
+ PioneerDDJSB3.deck[deckIndex].tempoFader.skipSoftTakeover();
+
+ if (currentTempoRange < 0.081) {
+ engine.setValue(group, "rateRange", 0.16);
+ } else if (currentTempoRange < 0.161) {
+ engine.setValue(group, "rateRange", 0.50);
+ } else if (currentTempoRange < 0.501) {
+ engine.setValue(group, "rateRange", 1.0);
+ } else {
+ engine.setValue(group, "rateRange", 0.08);
+ }
+ }
+};
+
+PioneerDDJSB3.deck1Button = function(channel, control, value, status, group) {
+ if (value) {
+ PioneerDDJSB3.deck3Enabled = false;
+ var bpm = engine.getValue(group, "bpm");
+ PioneerDDJSB3.updateBPM(bpm, group);
+ midi.sendShortMsg(0xB0, 0x02, 0x0);
+ }
+};
+
+PioneerDDJSB3.deck2Button = function(channel, control, value, status, group) {
+ if (value) {
+ PioneerDDJSB3.deck4Enabled = false;
+ var bpm = engine.getValue(group, "bpm");
+ PioneerDDJSB3.updateBPM(bpm, group);
+ midi.sendShortMsg(0xB1, 0x02, 0x0);
+ }
+};
+
+PioneerDDJSB3.deck3Button = function(channel, control, value, status, group) {
+ if (value) {
+ PioneerDDJSB3.deck3Enabled = true;
+ midi.sendShortMsg(0xB2, 0x02, 0x0);
+ var bpm = engine.getValue(group, "bpm");
+ PioneerDDJSB3.updateBPM(bpm, group);
+ }
+};
+
+PioneerDDJSB3.deck4Button = function(channel, control, value, status, group) {
+ if (value) {
+ PioneerDDJSB3.deck4Enabled = true;
+ var bpm = engine.getValue(group, "bpm");
+ PioneerDDJSB3.updateBPM(bpm, group);
+ midi.sendShortMsg(0xB3, 0x02, 0x0);
+ }
+};
+
+PioneerDDJSB3.autoLoopButton = function(channel, control, value, status, group) {
+ if (value) {
+ if (engine.getValue(group, "loop_enabled")) {
+ engine.setValue(group, "reloop_toggle", true);
+ } else {
+ engine.setValue(group, "beatloop_activate", true);
+ }
+ }
+};
+
+PioneerDDJSB3.reloopButton = function(channel, control, value, status, group) {
+ if (value) {
+ engine.setValue(group, "reloop_toggle", true);
+ }
+};
+
+PioneerDDJSB3.loadButton = function(channel, control, value, status, group) {
+ if (value) {
+ engine.setValue(group, "LoadSelectedTrack", 1);
+ }
+};
+
+///////////////////////////////////////////////////////////////
+// HEADPHONE //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.headphoneCueMaster = false;
+
+PioneerDDJSB3.masterCueButton = function(channel, control, value) {
+ if (value) {
+ PioneerDDJSB3.headphoneCueMaster = !PioneerDDJSB3.headphoneCueMaster;
+ PioneerDDJSB3.headphoneMasterUpdate();
+ }
+};
+
+PioneerDDJSB3.headphoneCueButton = function(channel, control, value, status, group) {
+ if (value) {
+ script.toggleControl(group, "pfl");
+ PioneerDDJSB3.headphoneMasterUpdate();
+ }
+};
+
+PioneerDDJSB3.headphoneShiftCueButton = function(channel, control, value, status, group) {
+ if (value) {
+ script.toggleControl(PioneerDDJSB3.deckShiftSwitchTable[group], "pfl");
+ PioneerDDJSB3.headphoneMasterUpdate();
+ }
+};
+
+PioneerDDJSB3.headphoneMasterUpdate = function() {
+ var anyDeckCue = false;
+ var masterCue = PioneerDDJSB3.headphoneCueMaster;
+
+ for (var i = 1; i <= 4; i++) {
+ if (engine.getValue("[Channel" + i + "]", "pfl")) {
+ anyDeckCue = true;
+ }
+ }
+
+ if (masterCue) {
+ if (anyDeckCue) {
+ // 50% master 50% cue
+ engine.setValue("[Master]", "headMix", 0);
+ } else {
+ // 100% master
+ // Check if 1 is all master or all cue
+ engine.setValue("[Master]", "headMix", 1);
+ }
+ } else {
+ // 0% master
+ // Check if 1 is all master or all cue
+ engine.setValue("[Master]", "headMix", -1);
+ }
+};
+
+///////////////////////////////////////////////////////////////
+// LED HELPERS //
+///////////////////////////////////////////////////////////////
+
+PioneerDDJSB3.deckConverter = function(group) {
+ var index;
+
+ if (typeof group === "string") {
+ for (index in PioneerDDJSB3.deckSwitchTable) {
+ if (group === PioneerDDJSB3.deckSwitchTable[index]) {
+ return PioneerDDJSB3.channelGroups[group];
+ }
+ }
+ return null;
+ }
+ return group;
+};
+
+PioneerDDJSB3.padLedControl = function(deck, groupNumber, shiftGroup, ledNumber, shift, active) {
+ var midiChannelOffset = PioneerDDJSB3.deckConverter(deck);
+
+ if (midiChannelOffset === null || midiChannelOffset === undefined) {
+ return;
+ }
+
+ var padLedsBaseChannel = 0x97;
+ var padLedControl = (shiftGroup ? 0x40 : 0x00) + (shift ? 0x08 : 0x00) + groupNumber + ledNumber;
+
+ midi.sendShortMsg(
+ padLedsBaseChannel + midiChannelOffset,
+ padLedControl,
+ active ? 0x7F : 0x00
+ );
+};
+
+PioneerDDJSB3.flashingPadLedControl = [];
+
+PioneerDDJSB3.initFlashingPadLedControl = function() {
+ PioneerDDJSB3.flasher.addFunction(function(flag) {
+ for (var i = 0; i < PioneerDDJSB3.flashingPadLedControl.length; i++) {
+ PioneerDDJSB3.padLedControl(
+ PioneerDDJSB3.flashingPadLedControl[i].deck,
+ PioneerDDJSB3.flashingPadLedControl[i].groupNumber,
+ PioneerDDJSB3.flashingPadLedControl[i].shiftGroup,
+ PioneerDDJSB3.flashingPadLedControl[i].ledNumber,
+ PioneerDDJSB3.flashingPadLedControl[i].shift,
+ flag
+ );
+ }
+ });
+};
+
+PioneerDDJSB3.startFlashingPadLedControl = function(deck, groupNumber, shiftGroup, ledNumber, shift) {
+ PioneerDDJSB3.flashingPadLedControl.push({
+ deck: deck,
+ groupNumber: groupNumber,
+ shiftGroup: shiftGroup,
+ ledNumber: ledNumber,
+ shift: shift
+ });
+};
+
+PioneerDDJSB3.stopFlashingPadLedControl = function(deck, groupNumber, shiftGroup, ledNumber, shift) {
+ var target = {
+ deck: deck,
+ groupNumber: groupNumber,
+ shiftGroup: shiftGroup,
+ ledNumber: ledNumber,
+ shift: shift
+ };
+
+ PioneerDDJSB3.flashingPadLedControl = _.filter(PioneerDDJSB3.flashingPadLedControl, function(obj) { return _.isEqual(obj, target); });
+};
+
+PioneerDDJSB3.nonPadLedControl = function(deck, ledNumber, active) {
+ var midiChannelOffset = PioneerDDJSB3.deckConverter(deck);
+
+ if (midiChannelOffset === n