diff options
author | Jan Holthuis <jholthuis@mixxx.org> | 2022-01-16 00:01:26 +0100 |
---|---|---|
committer | Jan Holthuis <jholthuis@mixxx.org> | 2022-01-16 00:01:26 +0100 |
commit | 418a7e362abac709e753589c2201504b98191f20 (patch) | |
tree | 7859016766ecab893018f960ea2dedc0088b3c38 | |
parent | 120255735322b6a2c45b578322718968fea5b60c (diff) | |
parent | 8d501b30e8dcc30b0818d82f27cab191919c7f77 (diff) |
Merge branch '2.3' of github.com:mixxxdj/mixxx
-rwxr-xr-x | res/controllers/Pioneer-DDJ-SB3-scripts.js | 1626 | ||||
-rwxr-xr-x | res/controllers/Pioneer-DDJ-SB3.midi.xml | 5061 | ||||
-rw-r--r-- | src/track/serato/markers.cpp | 4 | ||||
-rw-r--r-- | src/track/serato/markers2.cpp | 8 |
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 |