summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Holthuis <jan.holthuis@ruhr-uni-bochum.de>2020-10-25 16:32:39 +0100
committerJan Holthuis <jan.holthuis@ruhr-uni-bochum.de>2020-10-25 16:32:39 +0100
commita49ef339eeac3bdc79b187695acda7381b66a860 (patch)
treead9a26d5066fe250b3981699b6d73f7e2f62a6ea
parenta4d5c8ff136d22666833160081aa541a8ff63358 (diff)
parentf804f84754599b33f22fed84a8b4355e09c3be25 (diff)
Merge branch '2.2' of github.com:mixxxdj/mixxx into 2.3
-rw-r--r--res/controllers/Hercules_DJControl_Jogvision-scripts.js897
-rw-r--r--res/controllers/Hercules_DJControl_Jogvision.midi.xml100
-rw-r--r--src/mixer/samplerbank.cpp53
3 files changed, 581 insertions, 469 deletions
diff --git a/res/controllers/Hercules_DJControl_Jogvision-scripts.js b/res/controllers/Hercules_DJControl_Jogvision-scripts.js
index 76c9300601..b91aa32561 100644
--- a/res/controllers/Hercules_DJControl_Jogvision-scripts.js
+++ b/res/controllers/Hercules_DJControl_Jogvision-scripts.js
@@ -1,422 +1,531 @@
-// ****************************************************************************
// * Mixxx mapping script file for the Hercules DJControl Jogvision.
-// * Author: DJ Phatso, contributions by Kerrick Staley and David TV
-// * Version 1.16 (Jun 2020)
-// * Version 1.15 (Jun 2020)
-// * Version 1.14 (Jun 2020)
-// * Version 1.13 (May 2020)
-// * Version 1.11 (May 2020)
-// * Version 1.10 (May 2020)
-// * Version 1.9 (May 2020)
-// * Version 1.8 (May 2020)
-// * Version 1.6 (January 2020)
-// * Version 1.5 (January 2020)
-// * Version 1.4 (December 2019)
-// * Version 1.3 (November 2019)
-// * Version 1.2 (November 2019)
-// * Version 1.1 (March 2019)
+// * Author: DJ Phatso (contributions by Kerrick Staley, and fully rewritten, completed and enhanced by David TV)
// * Forum: https://www.mixxx.org/forums/viewtopic.php?f=7&t=12580
// * Wiki: https://www.mixxx.org/wiki/doku.php/hercules_dj_control_jogvision
-//
-// Changes to v1.16
-// - Added a "beatDetection" variable to use old school one (way better beat match, but no song position relative)
-//
-// Changes to v1.15
-// - Reset shift-"Loop ON", to do what Hercules wanted it to be: select FX2 for given channel. Now,
-// the loop creation is done with "MODE"+"Loop ON" combo (here, MODE key works like "SHIFT" key ;)
-//
-// Changes to v1.14
-// - Better beat_active matching
-//
-// Changes to v1.13
-// - Added "shift"+AIRFX to do a high pass (apart from the default low pass)
-// - Added shift+<multi FX> to set beatgrid at current play position
-//
-// Changes to v1.12
-// - Added "alternate" beat leds mode
-// - Changed "follow" beats led algorithm with a better/more elegant version
-//
-// Changes to v1.11
-// - Changed beats led algorithm to match song beats (forward and backward) and fix leads positions,
-// what allows backward led activation!! (didn't have that working previously)
-//
-// Changes to v1.10
-// - Changed jogwheel outer led movement based on playposition (much better!)
-// - Added a user variable to set or not some effects at ini
-// - Minor code convention corrections
-//
-// Changes to v1.9
-// - Added jogwheel outer led movement when scratch and back/forward spin
-//
-// Changes to v1.8
-// - Added normal, reverse, blink and follow modes for beat active led (see variable: beatActiveMode)
-//
-// Changes to v1.7
-// - Added back-spin (and forward-spin) effect on "vinyl" mode when jog wheel pushed from top
-//
-// Changes to v1.6
-// - Minor code-style changes (use camelCase, use "double quotes" and other if/else styles)
-//
-// Changes to v1.5
-// - Fixed regression not updating the VuMeters
-// - Fixed regression not updating the CUE/MIX buttons
-//
-// Changes to v1.4
-// - Code cleanup and standarization
-// - Added "orage" track led color (ala Serato) when play position is beyond the
-// part of the track, but before the "end of track" Mixxx signal
-//
-// Changes to v1.3
-// - Enabled the creation of beatloop (beatloopActivate) by using SHIFT+LoopON
-// - Changed "LOOP SIZE" to adjust the beatjumpSize (you still can change size
-// with surrounding buttons: reloop_toggle key)
-// - Added SHIFT+"LOOP SIZE" to move loop left or right by N beats (beatjump)
-//
-// Changes to v1.2
-// - Enabled Jogwheel Outer LED rotation
-// - Enabled Beat LEDs
-//
-// Changes to v1.1
-// - Controller knob/slider values are queried on startup, so MIXXX is synced.
-// - Fixed vinyl button behavior the first time it"s pressed.
-//
-// v1.0 : Original release
-//
-// ****************************************************************************
-
-// ****************************************************************************
-// User available variables (you may modify them)
-var beatActiveMode = "normal"; // normal (default), reverse, blink, follow, alternate
-var beatDetection = "normal"; // normal (default), follow (song position accurate, beat much less accurate)
-var initUpdateEffects = 1; // 1 (default) - set some effects values at startup; 0 - do not touch effects at startup
-
-// ****************************************************************************
-// Other internal constants (you may NOT modify anything from here)
-var on = 0x7F;
-var off = 0x00;
-var alpha = 1.0 / 8;
-var beta = alpha / 16;
-var speed = 33 + 1/3;
-var ledSpeed = (speed / 60) * 127;
-var masterLeds = 0x90;
-var ledRotationTimer = 0;
-var beatMax;
-if (beatActiveMode.match(/^(?:normal|reverse)$/g)) {
- beatMax = 4;
-} else if (beatActiveMode.match(/^(?:blink|alternate)$/g)) {
- beatMax = 2;
-} else {
- beatMax = 8;
-}
-
-var DJCJV = {};
-DJCJV.vinylModeActive = true;
-DJCJV.Channel = [];
-DJCJV.Channel["[Channel1]"] = {"central": 0x90, "deck": 0xB0, "beatPosition": 1, "rotation": 0x00, "n": 1, "onBeat": 0, "beatsPassed": 0, "shiftPressed": 0, "modePressed": 0};
-DJCJV.Channel["[Channel2]"] = {"central": 0x91, "deck": 0xB1, "beatPosition": 1, "rotation": 0x00, "n": 2, "onBeat": 0, "beatsPassed": 0, "shiftPressed": 0, "modePressed": 0};
-
-// ****************************************************************************
-// General functions
-
-// Function to rotate jogs' outer led (borrowed from the 'Pioneer-DDJ-SX-scripts.js' mapping)
-DJCJV.updateJogLeds = function(value, group, control) {
- var elapsedTime = value * engine.getValue(group, "duration");
- var wheelPos = parseInt(((value >= 0) ? 0 : 127) + 1 + ((ledSpeed * elapsedTime) % 127));
-
- // Only send midi message when the position is actually updated.
- if (DJCJV.Channel[group].rotation !== wheelPos) {
- midi.sendShortMsg(DJCJV.Channel[group].deck, 0x60, wheelPos); // Update the outer (spin) jog leds
- DJCJV.wheelInnerUpdate(value, group, control); // Also update the inner jog leds with updated song position
+// * Version: 1.25 (see changelog at https://github.com/mixxxdj/mixxx/pull/2370)
+
+// EXTRAS ADDED TO ORIGINAL MAPPING
+// - MODE+Loop ON : set a loop_in mark (with currently defined loop_size), activate it, and enable slip mode
+// - MODE+Loop X 1/2 / X 2 : do a 'beatjump_size' beats beatjump backward/forward
+// - MODE+Loop Size Knob : decrease/increase pitch (only key, not tempo!)
+// - MODE+JogWheel plate (playing) : scratch with 'Slip' ON (deactivate 'Slip' when plate is released)
+// - MODE+JogWheel plate (stopped) : move song position backward/forward faster by 'quickMoveFactor' factor
+// - MODE+Browser Knob Turn : move library selected position in groups of 'quickBrowseFactor' elements forward/backward
+// - MODE+LOAD A|B : toggle 'quantize' for deck where MODE key is pressed
+// - SHIFT+LOAD A|B : eject track from deck where SHIFT key is pressed
+// - SHIFT+Browser Knob Press : activate (double-click) currently selected item in browser
+// - SHIFT+Loop Size Knob : move existing loop forward/backward
+// - SHIFT+JogWheelTouch : do a 'backspin' with 'spinBackBrakeFactor' and 'spinBackInitialSpeed' factors
+// - SHIFT+MultiFX : set beatgrid to current position
+// - SHIFT+Aircontrol Filter : do the reverse than standard, that is, high-pass filter
+
+// ----------------------------------------------------------------------------
+// CONFIGURABLE MAPPING PARAMETERS
+var CFG = {
+ // USER Variables (you may modify them)
+ "user": {
+ "beatDetection": "normal", // normal|follow (follow=song position is more accurate with beat position, but beat detection is much less accurate)
+ "initUpdateEffects": 0.8, // [0..1] (if value > 0, set some effects values at startup with given float value)
+ "beatHelper": 1, // 0|1 (0=disabled; 1=use outer jog leg to indicate where to slide the pitch controller; see also 'CFG.fine.beatHelpSensitivity' variable)
+ "beatActiveMode": "follow" // normal|reverse|blink|follow|fill|bounce|alternate|off|on (see table below)
+ },
+ // FINE TUNING Variables (you may modify them, but not too far from the default values...)
+ "fine": {
+ "beatHelpSensitivity": 0.5, // [0..1) Max distance (float) in BPMs to be considered as a match (in use if CFG.user.beatHelper=1): the lower, the more sensitive. Values equal or bigger than 1 are reset to 0.9 and a warning is printed
+ "quickMoveFactor": 0.002, // [0..1] the smaller (float), the slower MODE+JogWheel will move 'playposition' (when such channel is NOT playing)
+ "quickBrowseFactor": 10, // [0..inf] the bigger, the faster MODE+Browser jog will move the cursor position in the library up/down
+ "spinBackBrakeFactor": 100, // (0..5000] the bigger, the softer the brake will be applied (0 = immediate stop; >=5000 = it will take almost forever to stop)
+ "spinBackInitialSpeed": 6, // (0..200] the bigger, the stronger will be the "back" impulse (1 = no spinbak, but stop and start sloooowly)
+ "mixGainFactor": 0.1 // (0..1) (float) the bigger, the faster the Pregain or Mix level will be updated
}
- DJCJV.Channel[group].rotation = wheelPos;
-
- DJCJV.Channel[group].beatsPassed = Math.round((value * engine.getValue(group, "duration")) * (engine.getValue(group, "bpm") / 60));
- DJCJV.Channel[group].beatPosition = Math.floor((DJCJV.Channel[group].beatsPassed % beatMax)) + 1;
+};
- if (beatDetection === "follow") {
- // If on beat_active, update the beat leds
- if (engine.getValue(group, "beat_active") || ((engine.getValue(group, "beat_closest") < engine.getValue(group, "beat_next"))) && (!DJCJV.Channel[group].onBeat)) {
- DJCJV.beatActive(0, group);
- DJCJV.Channel[group].onBeat = true;
- } else if (engine.getValue(group, "beat_closest") >= engine.getValue(group, "beat_next")) {
- DJCJV.Channel[group].onBeat = false;
+// BEATS leds lightning pattern depending on 'beatActiveMode', based on a 4x4 compass:
+//
+// beat number | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
+// --------------------------------------------------------------------------------------------------------------------
+// led (#=ON; ·=OFF) | 1 2 3 4 | 1 2 3 4 | 1 2 3 4 | 1 2 3 4 | 1 2 3 4 | 1 2 3 4 | 1 2 3 4 | 1 2 3 4 |
+// --------------------------------------------------------------------------------------------------------------------
+// | normal | # · · · | · # · · | · · # · | · · · # | # · · · | · # · · | · · # · | · · · # |
+// |-----------------------------------------------------------------------------------------------------------
+// | reverse | · # # # | # · # # | # # · # | # # # · | · # # # | # · # # | # # · # | # # # · |
+// |-----------------------------------------------------------------------------------------------------------
+// | blink | # # # # | · · · · | # # # # | · · · · | # # # # | · · · · | # # # # | · · · · |
+// beat |-----------------------------------------------------------------------------------------------------------
+// Active | follow | # · · · | # # · · | # # # · | # # # # | · # # # | · · # # | · · · # | · · · · |
+// Mode |-----------------------------------------------------------------------------------------------------------
+// | fill | # · · · | # # · · | # # # · | # # # # | # · · · | # # · · | # # # · | # # # # |
+// |-----------------------------------------------------------------------------------------------------------
+// | bounce | # · · · | · # · · | · · # · | · · · # | · · # · | · # · · | # · · · | · · · · |
+// |-----------------------------------------------------------------------------------------------------------
+// | alternate | # # · · | · · # # | # # · · | · · # # | # # · · | · · # # | # # · · | · · # # |
+// |-----------------------------------------------------------------------------------------------------------
+// | off | · · · · | · · · · | · · · · | · · · · | · · · · | · · · · | · · · · | · · · · |
+// |-----------------------------------------------------------------------------------------------------------
+// | on | # # # # | # # # # | # # # # | # # # # | # # # # | # # # # | # # # # | # # # # |
+
+// ----------------------------------------------------------------------------
+// GENERAL FUNCTIONS (nothing to be touched from here on...)
+var DJCJV = {
+ // Some controller-related constants
+ "led": {
+ "beat1": 0x3A,
+ "beat2": 0x3B,
+ "beat3": 0x3C,
+ "beat4": 0x3D,
+ "track": 0x45,
+ "vinylMode": 0x46,
+ "headCue": 0x4C,
+ "headMix": 0x4D,
+ "outerJog": 0x60,
+ "innerJog": 0x61,
+ "vuMeter": 0x44,
+ "master": 0x90
+ },
+ "colour": {
+ "orange": 0x02,
+ "green": 0x07
+ },
+ "other": {
+ "alpha": 0.125, //
+ "beta": 0.0078125, //
+ "speed": 33.3333333, //
+ "ledSpeed": 70.55555485, // these are re-calculated at DJCJV.init
+ "beatMax": 4, //
+ "vinylModeActive": true, //
+ "beatHelpTimer": 0, //
+ "left": 127,
+ "pressed": 127,
+ "on": 0x7F,
+ "off": 0x00
+ },
+ "Channel": {
+ "[Channel1]": {
+ "central": 0x90,
+ "deck": 0xB0,
+ "beatPosition": 1,
+ "rotation": 0x00,
+ "n": 1,
+ "onBeat": 0,
+ "beatsPassed": 0,
+ "shiftPressed": 0,
+ "modePressed": 0
+ },
+ "[Channel2]": {
+ "central": 0x91,
+ "deck": 0xB1,
+ "beatPosition": 1,
+ "rotation": 0x00,
+ "n": 2, "onBeat": 0,
+ "beatsPassed": 0,
+ "shiftPressed": 0,
+ "modePressed": 0
+ }
+ },
+ // Initialization
+ "init": function(id) {
+ print(id+": initializing...");
+
+ // Prepare some musical constants
+ if (CFG.fine.beatHelpSensitivity >= 1) {
+ print(id+": WARNING: variable 'CFG.fine.beatHelpSensitivity' is set to a value equal or bigger than 1 ("+CFG.fine.beatHelpSensitivity+"). Setting it exactly to 0.9");
+ CFG.fine.beatHelpSensitivity = 0.9;
}
- }
-};
-// Initialization
-DJCJV.init = function(id) {
+ // Prepare some musical constants
+ DJCJV.other.alpha = 1.0 / 8;
+ DJCJV.other.beta = DJCJV.other.alpha / 16;
+ DJCJV.other.speed = 33 + 1/3;
+ DJCJV.other.ledSpeed = (DJCJV.other.speed / 60) * 127;
+
+ // Calculate the number of beats to be taken into account for the beat_leds, depending on the CFG.user.beatActiveMode
+ if (CFG.user.beatActiveMode.match(/^(?:normal|reverse|fill)$/g)) {
+ DJCJV.other.beatMax = 4;
+ } else if (CFG.user.beatActiveMode.match(/^(?:blink|alternate)$/g)) {
+ DJCJV.other.beatMax = 2;
+ } else {
+ DJCJV.other.beatMax = 8; // follow|bounce
+ }
- print("Hercules DJControl Jogvision id: \""+id+"\" initializing...");
- print("Using beatActiveMode="+beatActiveMode+" (beatMax="+beatMax+")");
+ print(id+": Using CFG.user.beatActiveMode="+CFG.user.beatActiveMode+" (beatMax="+DJCJV.other.beatMax+")");
+ print(id+": Using CFG.user.beatDetection="+CFG.user.beatDetection);
+ print(id+":"+((CFG.user.initUpdateEffects === 0) ? " NOT " : " ") + "Initializing some effects at startup");
+ print(id+":"+((CFG.user.beatHelper !== 1) ? " NOT " : " ") + "Using beat helper");
- // Set all LED states to off
- midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, 0x7F, off);
- midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, 0x7F, off);
+ // Set all LED states to off
+ midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, 0x7F, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, 0x7F, DJCJV.other.off);
- // Set Vinyl button LED On.
- midi.sendShortMsg(masterLeds, 0x45, on);
- DJCJV.vinylModeActive = true;
- midi.sendShortMsg(masterLeds, 0x46, on);
+ // Set Vinyl button LED On.
+ DJCJV.other.vinylModeActive = true;
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.vinylMode, DJCJV.other.on);
- // Set Headphone CUE/MIX LED state
- if (engine.getValue("[Master]", "headMix") > 0.5) {
- midi.sendShortMsg(masterLeds, 0x4C, on); // headset "Mix" button LED
- midi.sendShortMsg(masterLeds, 0x4D, off);
- } else {
- midi.sendShortMsg(masterLeds, 0x4C, off);
- midi.sendShortMsg(masterLeds, 0x4D, on); // headset "Cue" button LED
- }
+ // Set Headphone CUE/MIX LED state
+ if (engine.getValue("[Master]", "headMix") > 0.5) {
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headCue, DJCJV.other.on); // headset "Mix" button LED
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headMix, DJCJV.other.off);
+ } else {
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headCue, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headMix, DJCJV.other.on); // headset "Cue" button LED
+ }
- // Enable Soft takeover
- engine.softTakeover("[Master]", "crossfader", true);
- engine.softTakeover("[QuickEffectRack1_[Channel1]]", "super1", true);
- engine.softTakeover("[QuickEffectRack1_[Channel2]]", "super1", true);
+ // Enable Soft takeover
+ engine.softTakeover("[Master]", "crossfader", true);
+ engine.softTakeover("[QuickEffectRack1_[Channel1]]", "super1", true);
+ engine.softTakeover("[QuickEffectRack1_[Channel2]]", "super1", true);
- if (initUpdateEffects === 1) {
- // Set effects Levels - Dry/Wet - Filters
// Done to work around the limited amount of controls in the Jogvision controller
- engine.setParameter("[EffectRack1_EffectUnit1_Effect1]", "meta", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit1_Effect2]", "meta", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit1_Effect3]", "meta", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit2_Effect1]", "meta", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit2_Effect2]", "meta", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit2_Effect3]", "meta", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit1]", "mix", 0.6);
- engine.setParameter("[EffectRack1_EffectUnit2]", "mix", 0.6);
- engine.setParameter("[QuickEffectRack1_[Channel1]]", "super1", 0.5);
- engine.setParameter("[QuickEffectRack1_[Channel2]]", "super1", 0.5);
- }
-
- // Connect the VUMeters
- engine.connectControl("[Channel1]", "VuMeter", "DJCJV.vuMeterUpdate");
- engine.connectControl("[Channel2]", "VuMeter", "DJCJV.vuMeterUpdate");
-
- // Set inner jog leds to 0
- midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, 0x61, 0);
- midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, 0x61, 0);
- // Set outer jog leds to 0
- midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, 0x60, 1);
- midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, 0x60, 1);
-
- // Enable jogs' outer leds rotation and Inner LEDs song position update
- engine.connectControl("[Channel1]", "playposition", "DJCJV.updateJogLeds");
- engine.connectControl("[Channel2]", "playposition", "DJCJV.updateJogLeds");
- if (beatDetection === "normal") {
- // Connect the beat_active with beat leds
- engine.connectControl("[Channel1]", "beat_active", "DJCJV.beatActive");
- engine.connectControl("[Channel2]", "beat_active", "DJCJV.beatActive");
- engine.connectControl("[Channel1]", "stop", "DJCJV.beatInactive");
- engine.connectControl("[Channel2]", "stop", "DJCJV.beatInactive");
- }
-
- // Ask the controller to send all current knob/slider values over MIDI, which will update the corresponding GUI controls in MIXXX.
- midi.sendShortMsg(0xB0, 0x7F, on);
-
- print("Hercules DJControl Jogvision id: \""+id+"\" initialized");
-};
-
-// Finalization
-DJCJV.shutdown = function() {
- if (ledRotationTimer) {
- engine.stopTimer(ledRotationTimer);
- ledRotationTimer = 0;
- }
- // Set all LED states to off
- midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, 0x7F, off);
- midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, 0x7F, off);
-};
-
-// Beat led ACTIVATE (move)
-DJCJV.beatActive = function(value, group, _control) {
- if (value === 1) {
- return;
- }
- var led = DJCJV.Channel[group].central;
- var pos = DJCJV.Channel[group].beatPosition;
-
- // Normal
- if (beatActiveMode === "normal") {
- midi.sendShortMsg(led, 0x3A, pos === 1 ? on : off);
- midi.sendShortMsg(led, 0x3B, pos === 2 ? on : off);
- midi.sendShortMsg(led, 0x3C, pos === 3 ? on : off);
- midi.sendShortMsg(led, 0x3D, pos === 4 ? on : off);
- // Reverse
- } else if (beatActiveMode === "reverse") {
- midi.sendShortMsg(led, 0x3A, pos !== 1 ? on : off);
- midi.sendShortMsg(led, 0x3B, pos !== 2 ? on : off);
- midi.sendShortMsg(led, 0x3C, pos !== 3 ? on : off);
- midi.sendShortMsg(led, 0x3D, pos !== 4 ? on : off);
- // Reverse
- } else if (beatActiveMode === "blink") {
- midi.sendShortMsg(led, 0x3A, pos === 1 ? on : off);
- midi.sendShortMsg(led, 0x3B, pos === 1 ? on : off);
- midi.sendShortMsg(led, 0x3C, pos === 1 ? on : off);
- midi.sendShortMsg(led, 0x3D, pos === 1 ? on : off);
- // Follow
- } else if (beatActiveMode === "follow") {
- midi.sendShortMsg(led, 0x3A, ((pos >= 1) && (pos <= 4)) ? on : off);
- midi.sendShortMsg(led, 0x3B, ((pos >= 2) && (pos <= 5)) ? on : off);
- midi.sendShortMsg(led, 0x3C, ((pos >= 3) && (pos <= 6)) ? on : off);
- midi.sendShortMsg(led, 0x3D, ((pos >= 4) && (pos <= 7)) ? on : off);
- // Alternate
- } else if (beatActiveMode === "alternate") {
- midi.sendShortMsg(led, 0x3A, pos === 1 ? on : off);
- midi.sendShortMsg(led, 0x3B, pos === 1 ? on : off);
- midi.sendShortMsg(led, 0x3C, pos === 2 ? on : off);
- midi.sendShortMsg(led, 0x3D, pos === 2 ? on : off);
- }
-};
-// Beat led DEACTIVATE (off all)
-DJCJV.beatInactive = function(value, group, _control) {
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x3A, off);
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x3B, off);
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x3C, off);
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x3D, off);
-
- DJCJV.Channel[group].beatPosition = 1;
-};
-
-// Jogwheels inner LED display - Play position
-DJCJV.wheelInnerUpdate = function(value, group, _control) {
- var playPos = value * 127;
- midi.sendShortMsg(DJCJV.Channel[group].deck, 0x61, playPos);
-
- // Also update the "track" led information
- if (engine.getValue(group, "end_of_track")) {
- // Let Mixxx"s engine turn flashing red automatically
- return;
- } else if (playPos > 64) {
- // Turn "track" led to orange if position is beyond the half
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x45, 0x02);
- } else {
- // Turn "track" led to green if position is in the first half
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x45, 0x07);
- }
-};
+ if (CFG.user.initUpdateEffects !== 0) {
+ engine.setParameter("[EffectRack1_EffectUnit1_Effect1]", "meta", CFG.user.initUpdateEffects); // Deck A, Effect 1 initial value
+ engine.setParameter("[EffectRack1_EffectUnit1_Effect2]", "meta", CFG.user.initUpdateEffects); // Deck A, Effect 2 initial value
+ engine.setParameter("[EffectRack1_EffectUnit1_Effect3]", "meta", CFG.user.initUpdateEffects); // Deck A, Effect 3 initial value
+ engine.setParameter("[EffectRack1_EffectUnit2_Effect1]", "meta", CFG.user.initUpdateEffects); // Deck B, Effect 1 initial value
+ engine.setParameter("[EffectRack1_EffectUnit2_Effect2]", "meta", CFG.user.initUpdateEffects); // Deck B, Effect 2 initial value
+ engine.setParameter("[EffectRack1_EffectUnit2_Effect3]", "meta", CFG.user.initUpdateEffects); // Deck B, Effect 3 initial value
+ engine.setParameter("[EffectRack1_EffectUnit1]", "mix", CFG.user.initUpdateEffects / 2); // Deck A, Effect Master mixer value
+ engine.setParameter("[EffectRack1_EffectUnit2]", "mix", CFG.user.initUpdateEffects / 2); // Deck A, Effect Master mixer value
+ engine.setParameter("[QuickEffectRack1_[Channel1]]", "super1", 0.5); // High/low filter Deck A (mapped to AIR FX)
+ engine.setParameter("[QuickEffectRack1_[Channel2]]", "super1", 0.5); // High/low filter Deck B (mapped to AIR FX)
+ }
-// Vu Meter
-DJCJV.vuMeterUpdate = function(value, group, _control) {
- midi.sendShortMsg(DJCJV.Channel[group].central, 0x44, value * 6);
-};
+ // Connect the VUMeters
+ engine.connectControl("[Channel1]", "VuMeter", "DJCJV.vuMeterUpdate");
+ engine.connectControl("[Channel2]", "VuMeter", "DJCJV.vuMeterUpdate");
+
+ // Set inner & outer jog leds to 0
+ DJCJV.updateJogLeds(0, "[Channel1]");
+ DJCJV.updateJogLeds(0, "[Channel2]");
+
+ // Enable jogs' outer leds rotation and Inner LEDs song position update
+ engine.connectControl("[Channel1]", "playposition", "DJCJV.updateJogLeds");
+ engine.connectControl("[Channel2]", "playposition", "DJCJV.updateJogLeds");
+ if (CFG.user.beatDetection === "normal") {
+ // Connect the beat_active with beat leds
+ engine.connectControl("[Channel1]", "beat_active", "DJCJV.beatActive");
+ engine.connectControl("[Channel2]", "beat_active", "DJCJV.beatActive");
+ engine.connectControl("[Channel1]", "stop", "DJCJV.beatInactive");
+ engine.connectControl("[Channel2]", "stop", "DJCJV.beatInactive");
+ }
-// Headphone CUE/MIX buttons status
-DJCJV.headCue = function(midino, control, value, status, group) {
- if (engine.getValue(group, "headMix") === 0) {
- engine.setValue(group, "headMix", -1.0);
- midi.sendShortMsg(masterLeds, 0x4D, 0x7f);
- midi.sendShortMsg(masterLeds, 0x4C, 0x00);
- }
-};
-DJCJV.headMix = function(midino, control, value, status, group) {
- if (engine.getValue(group, "headMix") !== 1) {
- engine.setValue(group, "headMix", 0);
- midi.sendShortMsg(masterLeds, 0x4D, 0x00);
- midi.sendShortMsg(masterLeds, 0x4C, 0x7f);
- }
-};
+ if (CFG.user.beatHelper === 1) {
+ DJCJV.other.beatHelpTimer = engine.beginTimer(100, "DJCJV.beatHelp");
+ }
-// Filter (AIR FX)
-DJCJV.Filter = function(channel, control, value, status, group) {
- var deck = group.substr(18, 10);
- var delta = DJCJV.Channel[deck].shiftPressed ? (value / 255) : (-1 * (value / 255));
- engine.setValue(group, "super1", 0.5 + delta);
-};
+ // Ask the controller to send all current knob/slider values over MIDI, which will update the corresponding GUI controls in MIXXX.
+ midi.sendShortMsg(0xB0, 0x7F, DJCJV.other.on);
+
+ print(id+": initialized");
+ },
+ // Finalization
+ "shutdown": function() {
+ print("Finishing...");
+ if (DJCJV.other.beatHelpTimer !== 0) {
+ engine.stopTimer(DJCJV.other.beatHelpTimer);
+ DJCJV.other.beatHelpTimer = 0;
+ }
-// Sniff decks' SHIFT presses and store them
-DJCJV.shiftKey = function(channel, control, value, status, group) {
- DJCJV.Channel[group].shiftPressed = (value === 127);
- return value;
-};
+ engine.setValue("[Channel1]", "eject", 1);
+ engine.setValue("[Channel2]", "eject", 1);
+ // /(Try to) Set all LED states to off
+ midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, 0x7F, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, 0x7F, DJCJV.other.off);
+ },
+ // Beat helper
+ "beatHelp": function() {
+ var diff = engine.getValue("[Channel1]", "bpm") - engine.getValue("[Channel2]", "bpm");
+ var move = 0;
+
+ if (diff < (-1 * CFG.fine.beatHelpSensitivity)) {
+ // Move ch1 pitch up or ch2 pitch down
+ move = 4*diff + 127;
+ } else if (diff > CFG.fine.beatHelpSensitivity) {
+ // Move ch1 pitch down or ch2 pitch up
+ move = 4*diff;
+ } else {
+ // Beats match
+ move = 0;
+ }
-// Sniff decks' MODE presses and store them
-DJCJV.modeKey = function(channel, control, value, status, group) {
- DJCJV.Channel[group].modePressed = (value === 127);
- return value;
-};
+ if ((engine.getValue("[Channel1]", "play") !== 1) && (!engine.isScratching(DJCJV.Channel["[Channel1]"].n))) {
+ midi.sendShortMsg(DJCJV.Channel["[Channel1]"].deck, DJCJV.led.outerJog, move);
+ }
+ if ((engine.getValue("[Channel2]", "play") !== 1) && (!engine.isScratching(DJCJV.Channel["[Channel2]"].n))) {
+ midi.sendShortMsg(DJCJV.Channel["[Channel2]"].deck, DJCJV.led.outerJog, move);
+ }
+ },
+ // Beat led ACTIVATE
+ "beatActive": function(value, group, _control) {
+ if ((value === 1) || (CFG.user.beatActiveMode === "off")) {
+ return;
+ }
+ var central = DJCJV.Channel[group].central;
+ var pos = DJCJV.Channel[group].beatPosition;
+
+ if (CFG.user.beatActiveMode === "normal") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, pos === 2 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, pos === 3 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, pos === 4 ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "reverse") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, pos !== 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, pos !== 2 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, pos !== 3 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, pos !== 4 ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "blink") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "follow") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, ((pos >= 1) && (pos <= 4)) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, ((pos >= 2) && (pos <= 5)) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, ((pos >= 3) && (pos <= 6)) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, ((pos >= 4) && (pos <= 7)) ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "bounce") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, ((pos >= 1) && (pos <= 7)) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, ((pos >= 2) && (pos <= 6)) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, ((pos >= 3) && (pos <= 5)) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, ((pos >= 4) && (pos <= 4)) ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "fill") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, (pos >= 1) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, (pos >= 2) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, (pos >= 3) ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, (pos >= 4) ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "alternate") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat2, pos === 1 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat3, pos === 2 ? DJCJV.other.on : DJCJV.other.off);
+ midi.sendShortMsg(central, DJCJV.led.beat4, pos === 2 ? DJCJV.other.on : DJCJV.other.off);
+ } else if (CFG.user.beatActiveMode === "on") {
+ midi.sendShortMsg(central, DJCJV.led.beat1, DJCJV.other.on);
+ midi.sendShortMsg(central, DJCJV.led.beat2, DJCJV.other.on);
+ midi.sendShortMsg(central, DJCJV.led.beat3, DJCJV.other.on);
+ midi.sendShortMsg(central, DJCJV.led.beat4, DJCJV.other.on);
+ }
-// Loop section
-// SHIFT + Loop ON creates a loop at given point
-DJCJV.beatloopActivate = function(channel, control, value, status, group) {
- if (!DJCJV.Channel[group].modePressed) {
+ if (CFG.user.beatDetection !== "follow") {
+ if (pos >= DJCJV.other.beatMax) {
+ DJCJV.Channel[group].beatPosition = 1;
+ } else {
+ DJCJV.Channel[group].beatPosition = pos + 1;
+ }
+ }
+ },
+ // Beat led DEACTIVATE
+ "beatInactive": function(value, group, _control) {
+ midi.sendShortMsg(DJCJV.Channel[group].central, DJCJV.led.beat1, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.Channel[group].central, DJCJV.led.beat2, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.Channel[group].central, DJCJV.led.beat3, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.Channel[group].central, DJCJV.led.beat4, DJCJV.other.off);
+
+ DJCJV.Channel[group].beatPosition = 1;
+ },
+ // Jogwheels inner LED display - Play position
+ "wheelInnerUpdate": function(value, group, _control) {
+ var playPos = value * 127;
+ midi.sendShortMsg(DJCJV.Channel[group].deck, DJCJV.led.innerJog, playPos);
+
+ // Also update the "track" led
+ if (engine.getValue(group, "end_of_track")) {
+ return; // Let Mixxx manage the end_of_track (flashing red) status
+ }
+ // Depending on playPos value, turn "track" led to -----------------------------> DJCJV.colour.orange : DJCJV.colour.green
+ midi.sendShortMsg(DJCJV.Channel[group].central, DJCJV.led.track, (playPos > 64) ? DJCJV.colour.orange : DJCJV.colour.green);
+ },
+ // Function to rotate jogs' outer led (borrowed from the 'Pioneer-DDJ-SX-scripts.js' mapping)
+ "updateJogLeds": function(value, group, control) {
+ var elapsedTime = value * engine.getValue(group, "duration");
+ var wheelPos = ((value >= 0) ? 0 : 127) + 1 + ((DJCJV.other.ledSpeed * elapsedTime) % 127);
+
+ // Only send midi message when the position is actually updated.
+ if (DJCJV.Channel[group].rotation !== wheelPos) {
+ midi.sendShortMsg(DJCJV.Channel[group].deck, DJCJV.led.outerJog, wheelPos); // Update the outer (spin) jog leds
+ DJCJV.wheelInnerUpdate(value, group, control); // Also update the inner jog leds with updated song position
+ }
+ DJCJV.Channel[group].rotation = wheelPos;
+
+ if (CFG.user.beatDetection === "follow") {
+ DJCJV.Channel[group].beatsPassed = Math.round((value * engine.getValue(group, "duration")) * (engine.getValue(group, "bpm") / 60));
+ DJCJV.Channel[group].beatPosition = Math.floor((DJCJV.Channel[group].beatsPassed % DJCJV.other.beatMax)) + 1;
+
+ // If on beat_active, update the beat leds
+ if (engine.getValue(group, "beat_active") || ((engine.getValue(group, "beat_closest") < engine.getValue(group, "beat_next"))) && (!DJCJV.Channel[group].onBeat)) {
+ DJCJV.beatActive(0, group);
+ DJCJV.Channel[group].onBeat = true;
+ } else if (engine.getValue(group, "beat_closest") >= engine.getValue(group, "beat_next")) {
+ DJCJV.Channel[group].onBeat = false;
+ }
+ }
+ },
+ // Vu Meter (has to be scaled x6)
+ "vuMeterUpdate": function(value, group, _control) {
+ midi.sendShortMsg(DJCJV.Channel[group].central, DJCJV.led.vuMeter, value * 6);
+ },
+ // Headphone CUE button
+ "headCue": function(midino, control, value, status, group) {
+ if (engine.getValue(group, "headMix") === 0) {
+ engine.setValue(group, "headMix", -1.0);
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headCue, DJCJV.other.off);
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headMix, DJCJV.other.on);
+ }
+ },
+ // Headphone MIX button
+ "headMix": function(midino, control, value, status, group) {
+ if (engine.getValue(group, "headMix") !== 1) {
+ engine.setValue(group, "headMix", 0);
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headCue, DJCJV.other.on);
+ midi.sendShortMsg(DJCJV.led.master, DJCJV.led.headMix, DJCJV.other.off);
+ }
+ },
+ // Filter (AIR FX)
+ "Filter": function(channel, control, value, status, group) {
+ var deck = group.substr(18, 10);
+ // Filter ---------------------------------------> High-pass : Low-pass
+ var delta = DJCJV.Channel[deck].shiftPressed ? (value / 255) : (-1 * (value / 255));
+ engine.setValue(group, "super1", 0.5 + delta);
+ },
+ // Sniff decks' SHIFT presses and store them
+ "shiftKey": function(channel, control, value, status, group) {
+ DJCJV.Channel[group].shiftPressed = (value === DJCJV.other.pressed);
return value;
- }
- engine.setValue(group, "beatloop_activate", (value !== 0));
-};
-
-// SHIFT+FX(Loop) Knob MOVES the existing loop one beat forward/backward
-DJCJV.beatjumpMove = function(channel, control, value, status, group) {
- if (value > 64) {
- engine.setValue(group, "beatjump_backward", 1);
- engine.setValue(group, "beatjump_backward", 0);
- } else {
- engine.setValue(group, "beatjump_forward", 1);
- engine.setValue(group, "beatjump_forward", 0);
- }
-};
-// FX(Loop) Knob changes the AMOUNT of beats to move the loop when requested
-DJCJV.beatjumpSize = function(channel, control, value, status, group) {
- var currentValue = engine.getValue(group, "beatjump_size");
- if (value > 64) {
- engine.setValue(group, "beatjump_size", currentValue /= 2);
- } else {
- engine.setValue(group, "beatjump_size", currentValue *= 2);
- }
-};
-
-// The Vinyl button, used to enable or disable scratching on the jog wheels.
-DJCJV.vinylButton = function(channel, control, value, _status, _group) {
- if (!value) {