diff --git a/build/nsis/Mixxx.nsi b/build/nsis/Mixxx.nsi index a38c6b4a9174..8f7c5f2c398b 100644 --- a/build/nsis/Mixxx.nsi +++ b/build/nsis/Mixxx.nsi @@ -473,6 +473,8 @@ Section "Uninstall" Delete "$INSTDIR\controllers\Numark DJ2Go.midi.xml" Delete "$INSTDIR\controllers\Numark Mixtrack Pro.midi.xml" Delete "$INSTDIR\controllers\Numark MIXTRACK.midi.xml" + Delete "$INSTDIR\controllers\Numark-Mixtrack-3.midi.xml" + Delete "$INSTDIR\controllers\Numark-Mixtrack-3-scripts.js" Delete "$INSTDIR\controllers\Numark N4.midi.xml" Delete "$INSTDIR\controllers\Numark NS7.midi.xml" Delete "$INSTDIR\controllers\Numark Omni Control.midi.xml" @@ -546,6 +548,8 @@ Section "Uninstall" Delete "$INSTDIR\controllers\Wireless-DJ-scripts.js" Delete "$INSTDIR\controllers\Xone K2.midi.xml" Delete "$INSTDIR\controllers\Xone-K2-scripts.js" + + ;Delete $INSTDIR\controllers\*.* ; Avoid this since it will delete customized files too RMDir "$INSTDIR\controllers" diff --git a/res/controllers/Numark-Mixtrack-3-scripts.js b/res/controllers/Numark-Mixtrack-3-scripts.js new file mode 100644 index 000000000000..c38a9c8924c9 --- /dev/null +++ b/res/controllers/Numark-Mixtrack-3-scripts.js @@ -0,0 +1,2352 @@ +/*********************************************************************** + * ============== Configuration Options ================= + * valid values are "true" or "false" unless specified + **********************************************************************/ +// TrackEndWarning: "true": when you reach the end of the track, +// the jog wheel Button will flash. "false": No flash of Jog Wheel Button +var TrackEndWarning = true; + +//iCutEnabled: iCut mode will automatically cut your track with the cross fader +// when SHIFT enabled and scratching with the jog wheel +var iCutEnabled = true; + +// fastSeekEnabled: enable fast seek with Jog Wheel with Wheel Off and Shift ON +// Shift can be locked or not +var fastSeekEnabled = true; + +//activate PFL of deck on track load +var smartPFL = true; + +// use beatlooproll instead of beatloop +var beatlooprollActivate = false; + +// PAD Loop button behavior: "true": Loop stops when finger release. +//"false" will force start loop on press and stop on 2nd press +var PADLoopButtonHold = false; + +// PAD Sample button behavior:"true": Sampler stops when finger release. +//"false" will force start Sampler on press and stop on 2nd press +var PADSampleButtonHold = false; + +// LED Flash on Beat Active : "true": TAP LED will flash to the beat +// if Shift Lock is enable TAP LED will remain ON +var OnBeatActiveFlash = true; + +// If Dark Metal Skin is used, set this to true: +// this is required to expand library view with DM skin. +var DarkMetalSkin = false; + +// Use Beat knob to adjust Sampler Volume. +// If "true": Deck 1 adjusts Samplers 1-4 ; Deck 2 adjusts Samplers 5-8 +// Shift + Beat knob moves beat grid +// If false; beat knob will adjust beatgrid, shift + knob will adjust grid size +var BeatKnobAsSamplerVolume = true; + +//Disable Play on Sync button Double Press +var noPlayOnSyncDoublePress = false; + +/************************** + * scriptpause + * --------------- + * period (in ms) while the script will be paused when sending messages + * to the controller in order to avoid too much data flow at once in the same time. + * - default value : 5 ms + * - To disable : 0; + **************************/ +var scriptpause = 0; + +/************************** + * Constants for scratching : + **************************/ +var intervalsPerRev = 1200, + rpm = 33 + 1 / 3, //Like a real vinyl !!! :) + alpha = 1.0 / 8, //Adjust to suit. + beta = alpha / 32; //Adjust to suit. + +/************************ GPL v2 licence ***************************** + * Numark Mixtrack Pro 3 controller script + * Author: Stéphane Morin, largely based on script from Chloé AVRILLON (DJ Chloé) + * + * Key features + * ------------ + * - ICUT effect for scratching + * - Fader Start + * - press/double press/long press handling + * - Smart PFL + ********************************************************************** + * User References + * --------------- + * Wiki/manual : http://mixxx.org/wiki/doku.php/numark_mixtrack_pro_3 + * support forum : http://mixxx.org/forums/viewtopic.php?f=7&p=27984#p27984 + * e-mail : steph@smorin.com + * + * Thanks + * ---------------- + * Thanks to Chloé AVRILLON (DJ Chloé) and authors of other scripts and particularly + * to authors of Numark Dj2Go, KANE QuNeo, Vestax-VCI-400 + * + * Revision history + * ---------------- + * 2016-01-12 (0.9) - Chloé AVRILLON + * - Initial revision for Mixxx 2.0+ + * - GPL v2 licence, rework of this header, JSHint.com quality check, + * a few comments, minor changes, typos + * - Make some code reusable (lights : LED object; Special Buttons, iCUT) + * 2016-01-14 (1.0 beta 1) - Chloé AVRILLON + * - Fixed Pitch Bend Button + * - Fixed Syntax error at line 1625, column 1 + * - Fixed line 907 + * - Samplers management integration + * 2016-01-14 (1.0 beta 2) - Chloé AVRILLON + * - Debugging session working with Emulator Pro as a midi controller + * (moved midi.sendmessage calls to a util function + * easy to comment/uncomment + * to avoid midi messages looping over in Mixx) + * 2016-01-15 (1.0 beta 3) - Chloé AVRILLON + * - Fixed bug line 1963 + * - Pitch bend buttons and strip were not bind + * to the javascript () + *2016-02-17 (1.0 beta 4) - Stéphane Morin + * - rewrite of wheel functions (wheel move and wheel touch) + * - ensured Padmode LEDs are lit + * - added global variables for preferences + * - added print statement to aid troubleshooting (these could be removed later) + * - Broke sync LEDs... they don't light up anymore.. can someone fix this? + *2016-02-18 (1.0 beta 5) - Stéphane Morin + * - Sync LEDs fixed + *2016-02-23 (1.0 beta 6) - Stéphane Morin + * - AutoLoop fixed, including LEDs management + * - Samplers fixed, including LEDs management + * - Added Smart PFL + *2016-02-25 (1.0 beta 7) - Stéphane Morin + * - Faderstart corrected + * - Implement PADLoopButtonPressed + *2016-02-25 (1.0 beta 8) - Stéphane Morin + * - replaced a script function (script.deckFromGroup) by + * (NumarkMixtrack3.deckFromGroup) + * a portion of script.deckFromGroup is commented in the common file + * and not usable for this script sampler implementation + *2016-03-07 (1.0 ) - Stéphane Morin - https://github.com/mixxxdj/mixxx/pull/905 + * - Code Clean up + * - Add Maximize Library function to TAP button + * - Added function: Sampler + Shift Key: play sample with NO Sync + * - Fixed Super Effect button + * - Fixed Sampler Shift - Sync now removed if present + *2016-03-07 (1.1 ) - Stéphane Morin - https://github.com/mixxxdj/mixxx/pull/905 + * - Corrected Pitch Bend rate of wheel for smoother operation + * - Add option (noPlayOnSyncDoublePress) to disable Play on Double press of Sync button + *2016-04-08 (1.2 ) - Stéphane Morin - https://github.com/mixxxdj/mixxx/pull/905 + * - Renamed user options: PADLoopButtonPressed to PADLoopButtonHold + * - Renamed user options: PADSampleButtonPressed to PADSampleButtonHold + * - TapExpandLibrary moved from Tap button to Browse Button push + * - Linked printComments to debug value of init function: The debugging parameter is set to 'true' + * if the user specified the --mididebug parameter on the command line + * - Cleaned NumarkMixtrack3.shutdown function + *2016-04-08 (1.3 ) - Stéphane Morin - https://github.com/mixxxdj/mixxx/pull/905 + * - changed skin option to use boolean for + * DarkMetalSkin. It requires different code to expand library view. + * - removed trailing empty lines at end of script + * - line 1749, change .75 to 0.75 in var gammaOutputRange + * - added spacing in numerous place for easier reading + * + * To do - (maybe..) + * ---------------- + * - Add script to control "volume","filterHigh","filterMid","filterLow" and"crossfader" + * In order for engine.softTakeover to work for these controls + * - Add configuration option: enable Samplers Sync by default (True/False) default: + * false + * - Add brake effect when pausing track with Play button + * - Add 4 deck support for script + * - Allow Beat knobs to control FX parameter; this button is Binary. + * knob has 20 notches - it sends only 1 or 127 as output. + * Note to developer: I considered hard using the beat knob to adjust parameterK + * (Deck 1: 1st parameter of effect 1; Deck 2: 2nd parameter of effect 1) for + * units 1 to 3, by selecting the target effect unit using LONG_PRESS + FX buttons + * but since all but one of the 8 effects have more than 2 parameters, + * I could not convince myself of the added value, since you still + * need to adjust effect on the GUI. + * + *********************************************************************** + * GPL v2 licence + * -------------- + * Numark Mixtrack Pro 3 controller script 1.0 beta 7 for Mixxx 2.0+ + * Copyright (C) 2016 Stéphane Morin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +***********************************************************************/ +//////////////////////////////////////////////////////////////////////// +// JSHint configuration // +//////////////////////////////////////////////////////////////////////// +/* global engine */ +/* global script */ +/* global print */ +/* global midi */ +//////////////////////////////////////////////////////////////////////// + +// Line below to remove warnings from JSHINT page regarding dot notation +/*jshint sub:true*/ + +function NumarkMixtrack3() {} + +NumarkMixtrack3(); // Very important ! Initializes some reusable objects. + +// Array of Objects can be created +NumarkMixtrack3.group = "[Master]"; +NumarkMixtrack3.decknum = 0; +NumarkMixtrack3.decks = []; + +// Global constants/variables +var ON = 0x7F, OFF = 0x00, DOWN = 0x7F; +var QUICK_PRESS = 1, DOUBLE_PRESS = 2, LONG_PRESS = 3; + +//LEDs constants +var ledCategories = { + "master": 0, + "channel1": 1, + "channel2": 2, + "meters": 32 + }; + +var leds = { + // Master: all are first byte 0x90 ( = 0x90+ledcatecories.master ) + "headphones1": 0x0e, + "headphones2": 0x0f, + "all": 0x75, + // Deck 1: first byte 0x91 ( = 0x90+ledcatecories.channel1 = 0x90+1 ) + // Deck 2: first byte 0x92 ( = 0x90+ledcatecories.channel2 = 0x90+2 ) + "jogWheelsInScratchMode": 0x06, + "loopin": 0x13, + "loopout": 0x14, + "reloop_exit": 0x15, + "loop_halve": 0x16, + "hotCue1": 0x1b, + "hotCue2": 0x1c, + "hotCue3": 0x1d, + "hotCue4": 0x1e, + "Cue": 0x03, + "sync": 0x02, + "play": 0x01, + "fx1": 0x07, + "fx2": 0x08, + "fx3": 0x09, + "tap": 0x0a, + "PADloop1": 0x17, + "PADloop2": 0x18, + "PADloop3": 0x19, + "PADloop4": 0x1A, + "PADsampler1": 0x20, + "PADsampler2": 0x21, + "PADsampler3": 0x22, + "PADsampler4": 0x23, + "PADsampler5": 0x20, + "PADsampler6": 0x21, + "PADsampler7": 0x22, + "PADsampler8": 0x23, + + // Meters: first byte 0xb0 ( = 0x90+ledcatecories.meters ) + "meter1": 0x02, + "meter2": 0x03 +}; + +var PADcolors = { + "black" : 0, + "blue" : 32, + "yellow": 96, + "purple": 127 +}; + +// Utilities +// ===================================================================== + +function printInfo(string) { + if (NumarkMixtrack3.debug) print(string); +} + +function pauseScript(ms) { + if (ms>0) { + var startDate = new Date(); + var currentDate = null; + while (currentDate - startDate < ms) { + currentDate = new Date(); + } + } +} + +Math.sign = Math.sign || function(x) { + x = +x; // convert the parameter into a number + if (x === 0 || isNaN(x)) { + return x; + } + return x > 0 ? 1 : -1; +}; + +function toggleValue(group,key) { + engine.setValue(group,key,!engine.getValue(group,key)); +} + +function TrackIsLoaded(group) { + return (engine.getValue(group, "track_samples") > 0) ? true : false; +} + +function RealDuration(group) { + var ts = engine.getValue(group, "track_samples"); + if (ts <= 0) { + return 0; + } else { + // this is an integer : + var d1 = engine.getValue(group, "duration"); + //this is a real value : + var d2 = engine.getValue(group, "track_samples") / engine.getValue(group, "track_samplerate"); + + if (d1===d2) { + //it is mono + return d2; + } else { + if ( (d1 > Math.floor(d2)) && (d1 < Math.ceil(d2)) ) { + //It is mono + return d2; + } else { + //It is stereo + return d2/2; + } + } + } +} + +function sendShortMsg(control,midino,value) { + midi.sendShortMsg(control, midino, value); +} + +// ===================================================================== +// Reusable Objects (special buttons handling, LEDs, iCUT and Jog wheels) +// ===================================================================== + +// LED class object +var LED = function(control, midino) { + this.control = control; + this.midino = midino; + this.lit = 0; + this.flashTimer = 0; + this.flashTimer2 = 0; + this.flashOnceTimer = 0; + this.flashDuration = 0; + this.flashOnceDuration = 0; + this.num_ms_on = 0; + this.valueon = 0; + this.num_ms_off = 0; + this.flashCount = 0; + this.relight = 0; + this.valueoff = 0; +}; + +// public : light on/off +LED.prototype.onOff = function(value) { + // stop pending flashing effects now + if (this.flashTimer !== 0) { + engine.stopTimer(this.flashTimer); + this.flashTimer = 0; + this.flashDuration = 0; + } + + if (this.flashTimer2 !== 0) { + engine.stopTimer(this.flashTimer2); + this.flashTimer2 = 0; + this.flashDuration = 0; + } + + if (this.flashOnceTimer !== 0) { + engine.stopTimer(this.flashOnceTimer); + this.flashOnceTimer = 0; + this.flashOnceDuration = 0; + } + + sendShortMsg(this.control, this.midino, value); + pauseScript(scriptpause); + this.lit = value; +}; + +// public : make a light flashing +// ------------------------------ +// num_ms_on : number of ms the light should stay enlighted when blinking +// value : value to send to the controller to lit it up, +// generally 0x00 means OFF, 0x7F means ON, but the light +// can receive some other values if it can have various colors +// num_ms_off : number of ms the light should be switched off when blinking +// flashcount : number of time the light should blink (3 times ? 10 times ? only once (1) ? +// if set to 0 or not set, flashes for ever, can be stopped with flashOff() +// relight : once the light has finished to blink, should we restore it in its original state (true) or must it be switched off (false). +// if not set, it considers it as a switch off (default=false) +// valueoff : like "value". That permits for instance with two colors (once red(on), once blue(off), once red(on), etc...) + +LED.prototype.flashOn = function(num_ms_on, value, num_ms_off, flashCount, relight, valueoff) { + var myself = this; + + // stop pending timers + this.flashOff(); + + // init + this.flashDuration = num_ms_on; + this.num_ms_on = num_ms_on; + this.valueon = value; + this.num_ms_off = num_ms_off; + this.flashCount = flashCount; + this.relight = relight; + this.valueoff = valueoff; + + // 1st flash + // This is because the permanent timer below takes + // num_ms_on milisecs before first flash. + this.flashOnceOn(num_ms_on, value); + + if (flashCount !== 1) { + // flashcount =0 means permanent flash, + // flashcount>0 , means temporary flash, first flash already done, + // so we don't need this part if flashcount=1 + // permanent timer + + this.flashTimer = engine.beginTimer( num_ms_on + num_ms_off, function() { myself.flashOnceOn(false); } ); + } + if (flashCount > 1) { + // flashcount>0 , means temporary flash, first flash already done, + // so we don't need this part if flashcount=1 + // temporary timer. The end of this timer stops the permanent flashing + + this.flashTimer2 = engine.beginTimer(flashCount * (num_ms_on + num_ms_off) - num_ms_off, function() { myself.Stopflash(relight); }, true); + } +}; + +// public +LED.prototype.getFlashDuration = function() { + return this.flashDuration; +}; + +LED.prototype.checkOn = function() { + return this.lit; +}; + +// private : relight=true : restore light state before it was flashing +// this is a call back function (called in flashon() ) +LED.prototype.flashOff = function(relight) { + // stop permanent timer if any + if (this.flashTimer !== 0) { + engine.stopTimer(this.flashTimer); + // reset flash variables to 0 + this.flashTimer = 0; + } + if (this.flashTimer2 !== 0) { + engine.stopTimer(this.flashTimer2); + // reset flash variables to 0 + this.flashTimer2 = 0; + } + this.flashDuration = 0; + if (relight) { + this.onOff(this.lit); + } else { + this.onOff(OFF); + } +}; + +// private : relight=true : restore light state before it was flashing +// this is a call back function (called in flashon() ) +LED.prototype.Stopflash = function(relight) { + // stop permanent timer + if (this.flashTimer !== 0) { + engine.stopTimer(this.flashTimer); + } + // reset flash variables to 0 + this.flashTimer = 0; + this.flashTimer2 = 0; + this.flashDuration = 0; + this.flashOff(relight); +}; + +// private : call back function (called in flashon() ) +LED.prototype.flashOnceOn = function(relight) { + var myself = this; + sendShortMsg(this.control, this.midino, this.valueon); + pauseScript(scriptpause); + this.flashOnceDuration = this.num_ms_on; + this.flashOnceTimer = engine.beginTimer(this.num_ms_on - scriptpause, function() { myself.flashOnceOff(relight); }, true); +}; + +// private :call back function (called in flashOnceOn() ) +LED.prototype.flashOnceOff = function(relight) { + this.flashOnceTimer = 0; + this.flashOnceDuration = 0; + + if (relight) { + sendShortMsg(this.control, this.midino, this.lit); + pauseScript(scriptpause); + } else { + sendShortMsg(this.control, this.midino, this.valueoff); + pauseScript(scriptpause); + this.lit = OFF; + } +}; + +// ********* special buttons handlers (SHIFT ,LOAD, PFL and SYNC buttons) +// ======================= SingleDoubleBtn +// Callback : Callback function you have to provide (see end of +// the code), that will return the original event +// parameters (channel, control, value, status, group) +// and the kind of press event affecting your button +// (eventkind). +// This callback will be triggered as soon as you +// press the button a second time (Value will be +// equal to DOWN), or the Long press is asserted +// (value = DOWN because you are still holding down +// the button or value=UP because you have realeased +// the button only once before it becomes a long press). +// DoublePressTimeOut : delay in ms above wich a second press on the +// button will not be considered as a potential double +// but as a new press cycle event (default = 400ms). +var SingleDoubleBtn = function(Callback, DoublePressTimeOut) { + this.channel = 0; + this.control = 0; + this.value = 0; + this.status = 0; + this.group = ""; + this.Callback = Callback; + if (DoublePressTimeOut) { + this.DoublePressTimeOut = DoublePressTimeOut; + } else { + //Sets a default value of 400 ms + this.DoublePressTimeOut = 400; + } + this.ButtonCount = 0; + this.ButtonTimer = 0; +}; + +// Button pressed +SingleDoubleBtn.prototype.ButtonDown = function(channel, control, value, status, group) { + var myself = this; + this.channel = channel; + this.control = control; + this.value = value; + this.status = status; + this.group = group; + if (this.ButtonTimer === 0) { // first press + + this.ButtonTimer = + engine.beginTimer(this.DoublePressTimeOut, + function() { myself.ButtonDecide(); }, true); + this.ButtonCount = 1; + } else { // 2nd press (before timer's out) + engine.stopTimer(this.ButtonTimer); + this.ButtonTimer = 0; + this.ButtonCount = 2; + this.ButtonDecide(); + } +}; + +// Take action +SingleDoubleBtn.prototype.ButtonDecide = function() { + this.ButtonTimer = 0; + this.Callback(this.channel, this.control, this.value, this.status, this.group, this.ButtonCount); + this.ButtonCount = 0; +}; + +// ======================= LongShortBtn +// Callback : Callback function you have to provide (see end of the code), that will return +// the original event parameters (channel, control, value, status, group) +// and the kind of press event affecting your button (eventkind) +// This callback will be called once you release the button +// (Value will be equal to UP). You must provide this parameter. +// LongPressThreshold : delay in ms above which a firts press on the +// button will be considered as a Long press (default = 500ms). +// This parameter is optional. +// CallBackOKLongPress : This callback will give you the same values than the first one +// but it will be triggered as soon as the Long press is taken +// into account ( at this moment, value = DOWN because you are still +// holding down the button). This permits for instance to lit up a light indicating +// the user that he/she can release the button. This callback occurs before the first one. +// This parameter is optional. +// Like that, you can decide to put the code for the long press in either callback function +var LongShortBtn = function(Callback, LongPressThreshold, CallBackOKLongPress) { + this.Callback = Callback; + this.channel = 0; + this.control = 0; + this.value = 0; + this.status = 0; + this.group = ""; + this.CallBackOKLongPress = CallBackOKLongPress; + if (LongPressThreshold) { + this.LongPressThreshold = LongPressThreshold; + } else { + //Sets a default value of 500 ms + this.LongPressThreshold = 500; + } + + this.ButtonLongPress = false; + this.ButtonLongPressTimer = 0; +}; + +// Timer's call back for long press +LongShortBtn.prototype.ButtonAssertLongPress = function() { + this.ButtonLongPress = true; + //the timer was stopped, we set it to zero + this.ButtonLongPressTimer = 0; + // let's take action of the long press + // Make sure the callback is a function​ and exist + if (typeof callback === "function") { + // Call it, since we have confirmed it is callable​ + this.CallBackOKLongPress(this.channel, this.control, this.value, this.status, this.group, LONG_PRESS); + } +}; + +LongShortBtn.prototype.ButtonDown = function(channel, control, value, status, group) { + var myself = this; + this.channel = channel; + this.control = control; + this.value = value; + this.status = status; + this.group = group; + this.ButtonLongPress = false; + this.ButtonLongPressTimer = engine.beginTimer(this.LongPressThreshold, function() { myself.ButtonAssertLongPress(); }, true); +}; + +LongShortBtn.prototype.ButtonUp = function() { + if (this.ButtonLongPressTimer !== 0) { + engine.stopTimer(this.ButtonLongPressTimer); + this.ButtonLongPressTimer = 0; + } + if (this.ButtonLongPress) { + this.Callback(this.channel, this.control, this.value, this.status, this.group, LONG_PRESS); + } else { + this.Callback(this.channel, this.control, this.value, this.status, this.group, QUICK_PRESS); + } +}; + +// ======================= LongShortDoubleBtn +// Callback : Callback function you have to provide (see end of +// the code), that will return the original event +// parameters (channel, control, value, status, group) +// and the kind of press event affecting your button +// (eventkind). +// This callback will be triggered as soon as you +// press the button a second time (Value will be +// equal to DOWN), or the Long press is asserted +// (value = DOWN because you are still holding down +// the button or value=UP because you have realeased +// the button only once before it becomes a long press). +// LongPressThreshold : delay in ms above which a firts press on the +// button will be considered as a Long press (default = 500ms). +// DoublePressTimeOut : delay in ms above wich a second press on the +// button will not be considered as a potential double +// but as a new press cycle event (default = 400ms). + +var LongShortDoubleBtn = function(Callback, LongPressThreshold, DoublePressTimeOut) { + this.Callback = Callback; + this.channel = 0; + this.control = 0; + this.value = 0; + this.status = 0; + this.group = ""; + if (LongPressThreshold) { + this.LongPressThreshold = LongPressThreshold; + } else { + // Sets a default value of 500 ms + this.LongPressThreshold = 500; + } + if (DoublePressTimeOut) { + this.DoublePressTimeOut = DoublePressTimeOut; + } else { + // Sets a default value of 400 ms + this.DoublePressTimeOut = 400; + } + this.ButtonTimer = 0; + this.ButtonLongPress = false; + this.ButtonLongPressTimer = 0; + this.ButtonCount = 0; +}; + +// Timer's call back for long press +LongShortDoubleBtn.prototype.ButtonAssertLongPress = function() { + this.ButtonLongPress = true; + // the timer was stopped, we set it to zero + this.ButtonLongPressTimer = 0; + // let's take action of the long press + this.ButtonDecide(); +}; + +// Timer's callback for single press/double press +LongShortDoubleBtn.prototype.ButtonAssert1Press = function() { + // Short Timer ran out before it was manually stopped by release + // of the button (ButtonUp): + // for sure it is a single click (short or long), we will know + // when button will be released or when longtimer will stop by itself + + // the timer was stopped, we set it to zero + this.ButtonTimer = 0; + this.ButtonCount = 1; + if (this.ButtonLongPressTimer === 0) { + // long press timer was stopped (short press) + //take action + this.ButtonDecide(); + } +}; + +// Button pressed (function called by mapper's code) +LongShortDoubleBtn.prototype.ButtonDown = function(channel, control, value, status, group) { + var myself = this; + this.channel = channel; + this.control = control; + this.value = value; + this.status = status; + this.group = group; + + if (this.ButtonCount === 0) { //first press (inits) + // 1st press + this.ButtonCount = 1; + // and short press + this.ButtonLongPress = false; + this.ButtonLongPressTimer = + engine.beginTimer(this.LongPressThreshold, + function() { myself.ButtonAssertLongPress(); }, + true); + this.ButtonTimer = + engine.beginTimer(this.DoublePressTimeOut, + function() { myself.ButtonAssert1Press(); }, + true); + } else if (this.ButtonCount === 1) { // 2nd press (before short timer's out) + // stop timers... + if (this.ButtonLongPressTimer !== 0) { + engine.stopTimer(this.ButtonLongPressTimer); + this.ButtonLongPressTimer = 0; + } + // we stopped the timer, we have to set it to zero. + // You must have this reflex : "stopTimer(timer)/timer=0" in mind + // so that you can test later on if it is active or not. Other else + // it's value stays with the one given by engine.beginTimer + + // "stopTimer(timer)/timer=0" + if (this.ButtonTimer !== 0) { + engine.stopTimer(this.ButtonTimer); + this.ButtonTimer = 0 ; + } + + // 2nd press + this.ButtonCount = 2; + + // ...and take action immediatly + this.ButtonDecide(); + } // else : + // 2nd press after short timer's out, this cannot happen, + // do nothing +}; + +// Button released (function called by mapper's code) +LongShortDoubleBtn.prototype.ButtonUp = function() { + // button released + if (this.ButtonLongPress === false) { + // long press was not asserted by timer (ButtonAssertLongPress) + // Button is released before timer's out + + // If first Buttun up, long timer is still running + // stop long timer if it is still running, keep short timer, + // longpress will never happen + if (this.ButtonLongPressTimer !== 0) { + engine.stopTimer(this.ButtonLongPressTimer); + this.ButtonLongPressTimer = 0; + } + } // else : + // longpressed is confirmed, we already took action in ButtonAssertLongPress +}; + +// Take actions and call callback +LongShortDoubleBtn.prototype.ButtonDecide = function() { + if (this.ButtonLongPressTimer !== 0) { + engine.stopTimer(this.ButtonLongPressTimer); + } + this.ButtonLongPressTimer = 0; + this.ButtonTimer = 0; + + if (this.ButtonLongPress) { + this.Callback(this.channel, this.control, this.value, this.status, this.group, LONG_PRESS); + } else { + if (this.ButtonCount === 2) { + this.Callback(this.channel, this.control, this.value, this.status, this.group, DOUBLE_PRESS); + } else { // We pressed sync only once + this.Callback(this.channel, this.control, this.value, this.status, this.group, QUICK_PRESS); + } + } + // re-init + this.ButtonCount = 0; + this.ButtonLongPress = false; +}; + +// ************************************************* +// iCut mode management +// **** +// this mode simulates a scratch routine. When the jog wheel is turned back +// the crossfader closes, when the jog wheel is turned forward the crossfader +// will open. + +var AutoCut = function (deckNum) { + this.deckNum = deckNum; + this.timer = 0; + this.delay = 20; + this.fadersave = 0; + this.enabled = false; +}; + +AutoCut.prototype.On = function() { + if (!this.enabled) { + this.enabled = true; + engine.softTakeover("[Master]", "crossfader", false); + } +}; + +AutoCut.prototype.FaderCut = function(jogValue) { + if (this.enabled) { + var direction = Math.sign(jogValue); //Get Jog wheel direction + // Backward=-1 (close), forward =0 (open) + if (direction > 0) { + direction = 0; + } + // Left Deck ? direction = 0 (open : crossfader to zéro) or 1 (close : crossfader to the right) + // Right Deck ? direction = 0 (open : crossfader to zéro) or -1 (close : crossfader to the left) + if (this.deckNum === 1) { + direction = -direction; + } // else direction is of the good sign + engine.setValue('[Master]', 'crossfader', direction); + } +}; + +AutoCut.prototype.Off = function() { + if (this.enabled) { + this.enabled = false; + engine.setValue('[Master]', 'crossfader', 0); + engine.softTakeover("[Master]", "crossfader", true); + } +}; + +// ***************************************************************** +// Jog wheel management (scratching, bending, ...) +// ****** +// Thank you to the authors of the Vestax VCI 400 mapping script +// Your controller is a "Model A" controller for scratching, +// if it centers on 0. +// See http://www.mixxx.org/wiki/doku.php/midi_scripting#scratching + +var Jogger = function (group, deckNum, model) { + this.deckNum = deckNum; + this.group = group; + this.wheelTouchInertiaTimer = 0; + this.iCUT = new AutoCut(deckNum); + this.model = model; +}; + +NumarkMixtrack3.SamplerBank = function() { + this.bankactive = 1; + this.loaded = []; + this.loaded.length = 17; +}; + +//Sample action +NumarkMixtrack3.SamplerBank.prototype.play = function(samplerindex,value) { + + var deck = NumarkMixtrack3.decks["D" + samplerindex]; + var isplaying = engine.getValue("[Sampler" + samplerindex + "]", "play"); + + if (value) { + if (!isplaying) { + if (deck.shiftKey) { + //Shift is on, play sampler with no Sync + engine.setValue("[Sampler" + samplerindex + "]", "beatsync", 0); + engine.setValue("[Sampler" + samplerindex + "]", "cue_gotoandplay", 1); + deck.LEDs["PADsampler" + samplerindex].flashOn(300, PADcolors.purple, 300); + } else { + //play sampler with Sync + engine.setValue("[Sampler" + samplerindex + "]", "cue_gotoandplay", 1); + engine.setValue("[Sampler" + samplerindex + "]", "beatsync", 1); + deck.LEDs["PADsampler" + samplerindex].flashOn(300, PADcolors.purple, 300); + } + } else { + engine.setValue("[Sampler" + samplerindex + "]", "stop", 1); + NumarkMixtrack3.decks["D"+ samplerindex].LEDs["PADsampler"+ samplerindex].onOff(ON); + } + } else { + engine.setValue("[Sampler" + samplerindex + "]", "stop", 1); + NumarkMixtrack3.decks["D"+ samplerindex].LEDs["PADsampler"+ samplerindex].onOff(ON); + } +}; + +// ****************************************************************** +// Samplers - create object +// ********* + +NumarkMixtrack3.samplers = new NumarkMixtrack3.SamplerBank(); + +// ****************************************************************** +// Decks +// ********* +NumarkMixtrack3.deck = function(deckNum) { + this.deckNum = deckNum; + this.group = "[Channel" + deckNum + "]"; + this.loaded = false; + this.LoadInitiated = false; + this.jogWheelsInScratchMode = false; + this.PADMode = 0; //0=Manual Loop;1=Auto Loop; 2=Sampler ??? // Needed? + this.shiftKey = false; + this.shiftLock = false; + this.touch = false; + this.faderstart = false; + this.PitchFaderHigh = 0; + this.lastfadervalue = 0; + this.scratchTimer = 0; + this.seekingfast = false; + this.iCutStatus = false; + this.LEDs=[]; + // NMTP3 is a "Model A" controller for scratching, it centers on 0. + // See http://www.mixxx.org/wiki/doku.php/midi_scripting#scratching + // and see "Jogger" object constructor + this.Jog = new Jogger(this.group, this.deckNum, "A"); +}; + +NumarkMixtrack3.deck.prototype.TrackIsLoaded = function() { + return TrackIsLoaded(this.group); +}; + +NumarkMixtrack3.deck.prototype.StripEffect = function(value, decknum) { + // var decknum = NumarkMixtrack3.deckFromGroup(group); + // var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (decknum === 1) { + engine.setValue("[EffectRack1_EffectUnit1]", "super1", value/127); + engine.setValue("[EffectRack1_EffectUnit2]", "super1", value/127); + engine.setValue("[EffectRack1_EffectUnit3]", "super1", value/127); + } else { + engine.setValue("[EffectRack1_EffectUnit1]", "mix", value/127); + engine.setValue("[EffectRack1_EffectUnit2]", "mix", value/127); + engine.setValue("[EffectRack1_EffectUnit3]", "mix", value/127); + } +}; + +// ===================================================================== +// Initialization of the mapping +// ===================================================================== +// Create decks + +// not completely clean... D1 and D2 are for the actual decks, D1 to D8 are for samplers. +// this will need to be fixed if someone is to enable the ability to map Deck 3 and 4 +NumarkMixtrack3.decks.D1 = new NumarkMixtrack3.deck("1"); +NumarkMixtrack3.decks.D2 = new NumarkMixtrack3.deck("2"); +NumarkMixtrack3.decks.D3 = new NumarkMixtrack3.deck("3"); +NumarkMixtrack3.decks.D4 = new NumarkMixtrack3.deck("4"); +NumarkMixtrack3.decks.D5 = new NumarkMixtrack3.deck("5"); +NumarkMixtrack3.decks.D6 = new NumarkMixtrack3.deck("6"); +NumarkMixtrack3.decks.D7 = new NumarkMixtrack3.deck("7"); +NumarkMixtrack3.decks.D8 = new NumarkMixtrack3.deck("8"); + + +NumarkMixtrack3.initLEDsObjects = function() { + var i; + // Lets create some LEDs + NumarkMixtrack3.AllLeds = + new LED(0x90+ledCategories.master, leds.all); + NumarkMixtrack3.decks["D1"].LEDs.PADsampler1 = + new LED(0x91,leds.PADsampler1); + NumarkMixtrack3.decks["D2"].LEDs.PADsampler2 = + new LED(0x91,leds.PADsampler2); + NumarkMixtrack3.decks["D3"].LEDs.PADsampler3 = + new LED(0x91,leds.PADsampler3); + NumarkMixtrack3.decks["D4"].LEDs.PADsampler4 = + new LED(0x91,leds.PADsampler4); + NumarkMixtrack3.decks["D5"].LEDs.PADsampler5 = + new LED(0x92,leds.PADsampler5); + NumarkMixtrack3.decks["D6"].LEDs.PADsampler6 = + new LED(0x92,leds.PADsampler6); + NumarkMixtrack3.decks["D7"].LEDs.PADsampler7 = + new LED(0x92,leds.PADsampler7); + NumarkMixtrack3.decks["D8"].LEDs.PADsampler8= + new LED(0x92,leds.PADsampler8); + + + for (i=1;i<=2;i++) { + NumarkMixtrack3.decks["D"+i].LEDs.headphones = + new LED(0x90+ledCategories.master,leds.headphones1-1+i); + NumarkMixtrack3.decks["D"+i].LEDs.jogWheelsInScratchMode = + new LED(0x90+i,leds.jogWheelsInScratchMode); + NumarkMixtrack3.decks["D"+i].LEDs.loopin = + new LED(0x90+i,leds.loopin); + NumarkMixtrack3.decks["D"+i].LEDs.loopout = + new LED(0x90+i,leds.loopout); + NumarkMixtrack3.decks["D"+i].LEDs.reloop_exit = + new LED(0x90+i,leds.reloop_exit); + NumarkMixtrack3.decks["D"+i].LEDs.loop_halve = + new LED(0x90+i,leds.loop_halve); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue1 = + new LED(0x90+i,leds.hotCue1); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue2 = + new LED(0x90+i,leds.hotCue2); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue3 = + new LED(0x90+i,leds.hotCue3); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue4 = + new LED(0x90+i,leds.hotCue4); + NumarkMixtrack3.decks["D"+i].LEDs.Cue = + new LED(0x90+i,leds.Cue); + NumarkMixtrack3.decks["D"+i].LEDs.sync = + new LED(0x90+i,leds.sync); + NumarkMixtrack3.decks["D"+i].LEDs.play = + new LED(0x90+i,leds.play); + NumarkMixtrack3.decks["D"+i].LEDs.fx1 = + new LED(0x90+i,leds.fx1); + NumarkMixtrack3.decks["D"+i].LEDs.fx2 = + new LED(0x90+i,leds.fx2); + NumarkMixtrack3.decks["D"+i].LEDs.fx3 = + new LED(0x90+i,leds.fx3); + NumarkMixtrack3.decks["D"+i].LEDs.tap = + new LED(0x90+i,leds.tap); + NumarkMixtrack3.decks["D"+i].LEDs.PADloop1 = + new LED(0x90+i,leds.PADloop1); + NumarkMixtrack3.decks["D"+i].LEDs.PADloop2 = + new LED(0x90+i,leds.PADloop2); + NumarkMixtrack3.decks["D"+i].LEDs.PADloop3 = + new LED(0x90+i,leds.PADloop3); + NumarkMixtrack3.decks["D"+i].LEDs.PADloop4 = + new LED(0x90+i,leds.PADloop4); + + NumarkMixtrack3.decks["D"+i].LEDs.meter = + new LED(0x90+ledCategories.meters,leds.meter1-1+i); + } +}; + +NumarkMixtrack3.initButtonsObjects = function() { + var i; + for (i=1;i<=2;i++) { + NumarkMixtrack3.decks["D"+i].LoadButtonControl = + new LongShortBtn(NumarkMixtrack3.OnLoadButton); + NumarkMixtrack3.decks["D"+i].SyncButtonControl = + new LongShortDoubleBtn(NumarkMixtrack3.OnSyncButton); + NumarkMixtrack3.decks["D"+i].ShiftButtonControl = + new SingleDoubleBtn(NumarkMixtrack3.OnShiftButton); + NumarkMixtrack3.decks["D"+i].ShiftedPFLButtonControl = + new SingleDoubleBtn(NumarkMixtrack3.OnShiftedPFLButton); + } +}; + +// Called when the MIDI device is opened & set up +NumarkMixtrack3.init = function(id,debug) { + print("********* Initialisation process engaged *****************"); + print(" Mapping initialization"); + print("============================"); + + var i,j,k; + NumarkMixtrack3.id = id; // Store the ID of this device for later use + NumarkMixtrack3.debug = debug; + //print comments on prompt screen in order to facilitate debugging + print (" Debug and printComments setting: " + NumarkMixtrack3.debug); + + NumarkMixtrack3.libraryMode = false; + + print(" Init LEDs"); + NumarkMixtrack3.initLEDsObjects(); + print(" Init Buttons"); + NumarkMixtrack3.initButtonsObjects(); + + // Turn ON all the lights: the only way PADMode Leds light up + NumarkMixtrack3.AllLeds.onOff(ON); + // Initialise some others (PAD LEDs) + for (i=1;i<=2;i++) { + for (j=1;j<=4;j++) { + NumarkMixtrack3.decks["D"+i].LEDs["PADloop"+j].onOff(PADcolors.black); + } + for (k=1;k<=8;k++) { + NumarkMixtrack3.decks["D"+k].LEDs["PADsampler"+k].onOff(PADcolors.black); + } + NumarkMixtrack3.decks["D"+i].LEDs.jogWheelsInScratchMode.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.headphones.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.loopin.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.loopout.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.reloop_exit.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.loop_halve.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue1.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue2.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue3.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.hotCue4.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.Cue.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.sync.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.play.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.fx1.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.fx2.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.fx3.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.tap.onOff(OFF); + NumarkMixtrack3.decks["D"+i].LEDs.meter.onOff(OFF); + + print(" set LEDs state "+"D"+i); + } + + print(" Init Soft Takeovers"); + // Enable soft-takeover for Pitch slider + engine.softTakeover("[Channel1]", "rate", true); + engine.softTakeover("[Channel2]", "rate", true); + + // Set soft-takeover for all Sampler volumes + for (i = engine.getValue("[Master]", "num_samplers"); i >= 1; i--) { + engine.softTakeover("[Sampler" + i + "]", "pregain", true); + } + // Set soft-takeover for all applicable Deck controls + // the following controls are mapped in the XML file, not by script + // therefore this is not required + + /* + for (i = engine.getValue("[Master]", "num_decks"); i >= 1; i--) { + engine.softTakeover("[Channel" + i + "]", "volume", true); + engine.softTakeover("[Channel" + i + "]", "filterHigh", true); + engine.softTakeover("[Channel" + i + "]", "filterMid", true); + engine.softTakeover("[Channel" + i + "]", "filterLow", true); + } + + engine.softTakeover("[Master]", "crossfader", true); + */ + for (i = 1; i <= 4; i++) { + engine.softTakeover("[EffectRack1_EffectUnit" + i + "]", "super1", true); + } + + for (i = 1; i <= 4; i++) { + engine.softTakeover("[EffectRack1_EffectUnit" + i + "]", "mix", true); + } + + print(" Init Connect controls"); + // Add event listeners + engine.connectControl("[Channel1]", "hotcue_1_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 1); + }); + engine.connectControl("[Channel2]", "hotcue_1_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 1); + }); + engine.connectControl("[Channel1]", "hotcue_2_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 2); + }); + engine.connectControl("[Channel2]", "hotcue_2_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 2); + }); + engine.connectControl("[Channel1]", "hotcue_3_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 3); + }); + engine.connectControl("[Channel2]", "hotcue_3_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 3); + }); + engine.connectControl("[Channel1]", "hotcue_4_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 4); + }); + engine.connectControl("[Channel2]", "hotcue_4_enabled", function(value, group, control) { + NumarkMixtrack3.OnHotcueChange(value, group, control, 4); + }); + + // The track_samples control is being used to tell if a track has successfully loaded + engine.connectControl("[Channel1]", "track_samples", "NumarkMixtrack3.OnTrackLoaded"); + engine.connectControl("[Channel2]", "track_samples", "NumarkMixtrack3.OnTrackLoaded"); + + // VU Meters + engine.connectControl("[Channel1]", "VuMeter", "NumarkMixtrack3.OnVuMeterChange"); + engine.connectControl("[Channel2]", "VuMeter", "NumarkMixtrack3.OnVuMeterChange"); + + //other lights + engine.connectControl("[Channel1]", "playposition", "NumarkMixtrack3.OnPlaypositionChange"); + engine.connectControl("[Channel2]", "playposition", "NumarkMixtrack3.OnPlaypositionChange"); + engine.connectControl("[Channel1]", "volume", "NumarkMixtrack3.OnVolumeChange"); + engine.connectControl("[Channel2]", "volume", "NumarkMixtrack3.OnVolumeChange"); + engine.connectControl("[Channel1]", "pfl", "NumarkMixtrack3.OnPFLStatusChange"); + engine.connectControl("[Channel2]", "pfl", "NumarkMixtrack3.OnPFLStatusChange"); + engine.connectControl("[Channel1]", "play_indicator", "NumarkMixtrack3.OnPlayIndicatorChange"); + engine.connectControl("[Channel2]", "play_indicator", "NumarkMixtrack3.OnPlayIndicatorChange"); + engine.connectControl("[Channel1]", "beat_active", "NumarkMixtrack3.OnBeatActive"); + engine.connectControl("[Channel2]", "beat_active", "NumarkMixtrack3.OnBeatActive"); + engine.connectControl("[Channel1]", "cue_indicator", "NumarkMixtrack3.OnCuePointChange"); + engine.connectControl("[Channel2]", "cue_indicator", "NumarkMixtrack3.OnCuePointChange"); + engine.connectControl("[Channel1]", "loop_start_position", "NumarkMixtrack3.OnLoopInOutChange"); + engine.connectControl("[Channel2]", "loop_start_position", "NumarkMixtrack3.OnLoopInOutChange"); + engine.connectControl("[Channel1]", "loop_end_position", "NumarkMixtrack3.OnLoopInOutChange"); + engine.connectControl("[Channel2]", "loop_end_position", "NumarkMixtrack3.OnLoopInOutChange"); + engine.connectControl("[Channel1]", "loop_enabled", "NumarkMixtrack3.OnLoopInOutChange"); + engine.connectControl("[Channel2]", "loop_enabled", "NumarkMixtrack3.OnLoopInOutChange"); + engine.connectControl("[Channel1]", "sync_enabled", "NumarkMixtrack3.OnSyncButtonChange"); + engine.connectControl("[Channel2]", "sync_enabled", "NumarkMixtrack3.OnSyncButtonChange"); + + var loopsize = [0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; + var l; + for (l=0;l Open/Close selected side bar item + engine.setValue(group, "ToggleSelectedSidebarItem", true); + } else { + // Browse push : maximize/minimize library view + if (value === ON) { + + NumarkMixtrack3.libraryMode = !NumarkMixtrack3.libraryMode; + if (maxview) { + engine.setValue(LibraryGroup,LibraryCommand,expand); + + } else { + engine.setValue(LibraryGroup,LibraryCommand,contract); + + } + } + } +}; + +NumarkMixtrack3.PadModeButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var loopsize = [0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; + var i; + +NumarkMixtrack3.PadModeButton = !NumarkMixtrack3.PadModeButton; + + if (value === DOWN) { + //ensure all LEDs are ON (default) + if (decknum === 1) { + NumarkMixtrack3.decks["D1"].LEDs["PADsampler1"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D2"].LEDs["PADsampler2"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D3"].LEDs["PADsampler3"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D4"].LEDs["PADsampler4"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D1"].LEDs["PADloop1"].onOff(PADcolors.yellow); + NumarkMixtrack3.decks["D1"].LEDs["PADloop2"].onOff(PADcolors.yellow); + NumarkMixtrack3.decks["D1"].LEDs["PADloop3"].onOff(PADcolors.yellow); + NumarkMixtrack3.decks["D1"].LEDs["PADloop4"].onOff(PADcolors.yellow); + } + + if (decknum === 2) { + NumarkMixtrack3.decks["D5"].LEDs["PADsampler5"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D6"].LEDs["PADsampler6"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D7"].LEDs["PADsampler7"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D8"].LEDs["PADsampler8"].onOff(PADcolors.purple); + NumarkMixtrack3.decks["D2"].LEDs["PADloop1"].onOff(PADcolors.yellow); + NumarkMixtrack3.decks["D2"].LEDs["PADloop2"].onOff(PADcolors.yellow); + NumarkMixtrack3.decks["D2"].LEDs["PADloop3"].onOff(PADcolors.yellow); + NumarkMixtrack3.decks["D2"].LEDs["PADloop4"].onOff(PADcolors.yellow); + } + } + + // Now check which one should be blinking + // Need to check if loop is enabled; if yes, stop it , else start it + //Autoloop + if (value === DOWN) { + for (i=0;i4) { + index = index - 4; + } + + if (engine.getValue(group, "beatloop_" + loopsize[i] + "_enabled")) { + deck.LEDs["PADloop" + index].flashOn(300, PADcolors.yellow, 300); + } + } + + //Sampler + for (i = 1; i <= 8; i++) { + engine.trigger("[Sampler" + i + "]", "play"); + } + } + +}; + +NumarkMixtrack3.BrowseKnob = function(channel, control, value, status, group) { + var i; + var shifted = NumarkMixtrack3.decks.D1.shiftKey || NumarkMixtrack3.decks.D1.shiftKey; + // value = 1 / 2 / 3 ... for positive //value = 1 / 2 / 3 + var nval = (value>0x40 ? value-0x80 : value); + + if (shifted) { + // SHIFT+Turn BROWSE Knob : directory mode --> select Play List/Side bar item + if (nval > 0) { + for (i = 0; i < nval; i++) { + engine.setValue(group, "SelectNextPlaylist", 1); + } + } else { + for (i = 0; i < -nval; i++) { + engine.setValue(group, "SelectPrevPlaylist", 1); + } + } + } else { + // Turn BROWSE Knob : track list mode --> select track + engine.setValue(group, "SelectTrackKnob", nval); + } +}; + +/****************** Load button : + * - Load a track : Press these buttons to load the selected track from + * (short press) the Browser to left or right deck. The LED of the + * button will be on if the deck is loaded. + * - Eject : Hold the same button for more than half of a second + * (long press) to unload the same deck. + ***********************************************************************/ +NumarkMixtrack3.LoadButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + //deck.faderstart = false; + var groupOff; + var deckOff; + if (decknum === 1) { + groupOff = "[Channel2]"; + deckOff = NumarkMixtrack3.decks["D2"]; + }else{ + groupOff = "[Channel1]"; + deckOff = NumarkMixtrack3.decks["D1"]; + } + + if (value === DOWN) { + deck.LEDs["headphones"].onOff(ON); + deck.faderstart = false; + + if (smartPFL) { + print("smartPFL" +smartPFL); + engine.setValue(group, 'pfl', true); + engine.setValue(groupOff, 'pfl', false); + deck.LEDs["headphones"].onOff(ON); + deckOff.LEDs["headphones"].onOff(ON); + } + + + if (deck.shiftKey) { + // SHIFT + Load = fader start activated + + deck.faderstart = true; + deck.LEDs["headphones"].flashOn(250, ON, 250); + + if (!deck.TrackIsLoaded) { + printInfo("track not loaded, load track"); + engine.setValue(group, 'LoadSelectedTrack', true); + } + + } + + deck.LoadButtonControl.ButtonDown(channel, control, value, status, group); + } else { + + deck.LoadButtonControl.ButtonUp(); + } +}; + +// Callback for the Load Button +NumarkMixtrack3.OnLoadButton = function(channel, control, value, status, group, eventkind) { + // var decknum = NumarkMixtrack3.deckFromGroup(group); + // var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (eventkind === LONG_PRESS) { + engine.setValue(group, 'eject', true); + } else { + engine.setValue(group, 'LoadSelectedTrack', true); + } +}; + +/****************** Sync button : + * - Short Press : Press once to synchronize the tempo (BPM) and phase + * to that of to that of the other track. + * - Double Press : press twice QUICKLY to play the track immediatly, + * synchronized to the tempo (BPM) and to the phase of + * the other track, if the track was paused. + * - Long Press (Sync Locck) : + * Hold for at least half of a second to enable sync lock + * for this deck. Decks with sync locked will all play at + * the same tempo, and decks that also have quantize + * enabled will always have their beats lined up. + * If the Sync Loack was previously activated, it just desactivate it, + * regardless of the Short press/Double Press + * + * - SHIFT + Press : Toggle Key Lock + ***********************************************************************/ +NumarkMixtrack3.SyncButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (!deck.shiftKey) { + if (value === DOWN) { + + deck.SyncButtonControl.ButtonDown(channel, control, value, status, group); + } else { + + deck.SyncButtonControl.ButtonUp(); + } + } else { + if (value === DOWN) { + toggleValue(group,"keylock"); + } + } +}; + +// Callback for the SYNC Button +NumarkMixtrack3.OnSyncButton = function(channel, control, value, status, group, eventkind) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + + if (eventkind === LONG_PRESS) { + deck.LEDs.sync.onOff(ON); + engine.setValue(group, 'sync_enabled', true); + } else { + if (engine.getValue(group, 'sync_enabled')) { + // If sync lock is enabled, simply disable sync lock + engine.setValue(group, 'sync_enabled', false); + deck.LEDs.sync.onOff(OFF); + } else { + if (eventkind === DOUBLE_PRESS && !noPlayOnSyncDoublePress) { + // Double press : Sync and play (if the track was paused + // the playback starts, synchronized to the other track + engine.setValue(group, 'play', true); + engine.setValue(group, 'beatsync', true); + deck.LEDs.sync.flashOn(100, ON, 100, 3); + + } else { // We pressed sync only once, we sync the track + // with the other track (eventkind === QUICK_PRESS + engine.setValue(group, 'beatsync', true); + deck.LEDs.sync.flashOn(100, ON, 100, 3); + } + } + } +}; + + +NumarkMixtrack3.OnSyncButtonChange = function(value, group, key) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var valIn = engine.getValue(group, 'sync_enabled'); + + if (valIn) { + deck.LEDs.sync.onOff(ON); + }else{ + deck.LEDs.sync.onOff(OFF); + } +}; + +/****************** Cue button : + * - press : Well, it is the Cue Button :) + * - SHIFT + press : Go to start of the track + ***********************************************************************/ +NumarkMixtrack3.CueButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (!deck.shiftKey) { + // Don't set Cue accidentaly at the end of the song + if (engine.getValue(group, "playposition") <= 0.97) { + engine.setValue(group, "cue_default", value ? 1 : 0); + } else { + engine.setValue(group, "cue_preview", value ? 1 : 0); + } + } else { + engine.setValue(group, "start", true); + } +}; + +// Pitch faders send 2*7bits +NumarkMixtrack3.PitchFaderHighValue = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + deck.PitchFaderHigh = value; +}; + +NumarkMixtrack3.PitchFaderLowValue = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var calcvalue = (8192-((deck.PitchFaderHigh*128)+value))/8192; + engine.setValue(group, "rate", calcvalue); +}; + + +NumarkMixtrack3.toggleJogMode = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (value === DOWN) { + // Toggle setting and light + deck.jogWheelsInScratchMode = !deck.jogWheelsInScratchMode; + deck.LEDs.jogWheelsInScratchMode.onOff(deck.jogWheelsInScratchMode ? ON : OFF); + } +}; + + +NumarkMixtrack3.WheelTouch = function(channel, control, value, status, group) { + + /* + This function sets the variable to assign the wheel move action + - Pitch bend / jog = default + - fast seek - deck.seekingfast = true + - iCut = deck.iCutStatus = true + - Scratching = deck.touch = true + */ + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + deck.touch = false; + deck.iCutStatus = false; + deck.seekingfast = false; + + printInfo("Start WheelMove decknum=" + decknum +"; deck="+deck+"; group="+group+"; WheelTouch = DOWN (on) "); + + if (value === DOWN) { + printInfo(" WheelTouch = DOWN (on) "); + + if (deck.jogWheelsInScratchMode) { + + engine.scratchEnable(decknum, intervalsPerRev, rpm, alpha, beta); + + // Wheel is On - test for Shift Key"); + + if (deck.shiftKey && iCutEnabled) { + printInfo(" Wheel ON - Shift true - ICUT = true"); + deck.iCutStatus = true; + deck.Jog.iCUT.On(); + + } else { + printInfo(" Wheel ON - Shift = false - Scratching - deck.touch = true"); + deck.iCutStatus = false; + deck.touch = true; + deck.Jog.iCUT.Off(); + } + + } else { + + if (fastSeekEnabled && deck.shiftKey) { + printInfo(" Wheel Off - Shift true - Fast Seek"); + deck.seekingfast = true; + } + } + } else { + + printInfo(" WheelTouch = UP (off) "); + engine.scratchDisable(decknum, true); + deck.seekingfast = false; + deck.Jog.iCUT.Off(); + + } +}; + + + NumarkMixtrack3.WheelMove = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + // Set jog value + var adjustedJog = parseFloat(value); + var posNeg = 1; + + if (adjustedJog > 63) { // Counter-clockwise + posNeg = -1; + adjustedJog = value - 128; + } + +/* This function performs that actions defined by wheel touch + - Pitch bend / jog = default + - fast seek - deck.seekingfast = true + - iCut = deck.iCutStatus = true + - Scratching = deck.touch = true */ + + + if (deck.iCutStatus) { + printInfo(" WheelMove - ICUT = true - adjustedJog =" +adjustedJog +" value="+value); + deck.Jog.iCUT.On(); + deck.Jog.iCUT.FaderCut(adjustedJog); + } + + // the 2 conditions below may not be required as the simply default to + if (deck.touch) { + printInfo(" WheelMove - Scratching = true - adjustedJog =" +adjustedJog+" value="+value); + // scratch is enabled in wheel touch, we just record ticks + } + + if (!deck.seekingfast && !deck.iCutStatus && !deck.touch) { + printInfo(" WheelMove - Jog / Pitch bend"); + } + + if (deck.seekingfast) { + printInfo(" WheelMove - seekingfast = true - adjustedJog =" +adjustedJog+" value="+value); + engine.setValue(deck.Jog.group, "beatjump", adjustedJog * 2); + } + + engine.scratchTick(decknum, adjustedJog); + + printInfo( "engine.scratchTick("+ decknum +"," +adjustedJog+") value="+value); + + //Pitch bend when playing - side or platter have same effect + if (engine.getValue(deck.Jog.group, "play")) { + var gammaInputRange = 13; // Max jog speed + var maxOutFraction = 0.8; // Where on the curve it should peak; 0.5 is half-way + var sensitivity = 0.5; // Adjustment gamma + var gammaOutputRange = 0.75; // Max rate change + + adjustedJog = posNeg * gammaOutputRange * Math.pow(Math.abs( + adjustedJog) / (gammaInputRange * maxOutFraction), + sensitivity); + engine.setValue(deck.Jog.group, "jog", adjustedJog); + } +}; + +NumarkMixtrack3.HotCueButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var hotCue = control - leds.hotCue1 + 1; + + // onHotCueChange called automatically + if (deck.shiftKey) { + if (value === DOWN) { + engine.setValue(group, "hotcue_" + hotCue + "_clear", true); + deck.LEDs["hotCue" + hotCue].onOff(OFF); + } + } else { + if (value === DOWN) { + engine.setValue(group, "hotcue_" + hotCue + "_activate", 1); + } else { + engine.setValue(group, "hotcue_" + hotCue + "_activate", 0); + } + } +}; + +// Returns the deck number of a "ChannelN" or "SamplerN" group +// copied from the common script because for some reason the section for extracting the sampler number +// is commented out in the common script +NumarkMixtrack3.deckFromGroup = function (group) { + var deck = 0; + if (group.substring(2,8)=="hannel") { + // Extract deck number from the group text + deck = group.substring(8,group.length-1); + } + + else if (group.substring(2,8)=="ampler") { + // Extract sampler number from the group text + deck = group.substring(8,group.length-1); + } + + return parseInt(deck); +}; + +NumarkMixtrack3.SamplerButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var padindex = decknum; + + if (value=== DOWN) { + NumarkMixtrack3.samplers.play(padindex,true); + } + + if (value === OFF && PADSampleButtonHold) { + NumarkMixtrack3.samplers.play(padindex,false); + } +}; + +NumarkMixtrack3.PADLoopButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var padindex = control - leds.PADloop1 + 1; + var loopsize = 1; + var trueFalse; + + if (deck.shiftKey) { + loopsize = Math.pow(2, padindex); + } else { + loopsize = Math.pow(2, padindex - 4); + } + + var loopCommand1; //verify if loop is active + var loopCommand2; //enable loop + var loopCommand3; //stop loop + + if (beatlooprollActivate) { + loopCommand1 = "beatlooproll_" + loopsize + "_activate"; + loopCommand2 = "beatlooproll_" + loopsize + "_activate"; + loopCommand3 = "beatlooproll_" + loopsize + "_activate"; + trueFalse = false; + } else { + loopCommand1 = "beatloop_" + loopsize + "_enabled"; + loopCommand2 = "beatloop_" + loopsize + "_toggle"; + loopCommand3 = "reloop_exit"; + trueFalse = true; + } + + if (value === DOWN) { + // make sure all LED are ON + deck.LEDs["PADloop1"].onOff(PADcolors.yellow); + deck.LEDs["PADloop2"].onOff(PADcolors.yellow); + deck.LEDs["PADloop3"].onOff(PADcolors.yellow); + deck.LEDs["PADloop4"].onOff(PADcolors.yellow); + + if (engine.getValue(group,loopCommand1)) { + // Loop is active, turn it off + print("LED ON - loop OFF "+ padindex); + engine.setValue(group,loopCommand3, trueFalse); + deck.LEDs["PADloop" + padindex].onOff(PADcolors.yellow); + + } else { + // Loop is not active, turn it on + print("Flash LED - loop "+ padindex); + deck.LEDs["PADloop" + padindex].flashOn(250, PADcolors.yellow, 250); + print("loopCommand2 "+ loopCommand2); + engine.setValue(group,loopCommand2,true); + + } + } + + if (value === OFF && PADLoopButtonHold) { + engine.setValue(group,loopCommand2,false); + deck.LEDs["PADloop" + padindex].onOff(PADcolors.yellow); + } +}; + + +NumarkMixtrack3.StripTouchEffect = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + if (deck.shiftKey) { + engine.setValue(group, "playposition", value / 127); + } else { + deck.StripEffect(value, decknum); + } +}; + +NumarkMixtrack3.StripTouchSearch = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + if (deck.shiftKey) { + engine.setValue(group, "playposition", value / 127); + } else { + deck.StripEffect(value, decknum); + } +}; + +NumarkMixtrack3.FXButton = function(channel, control, value, status, group) { + if (!value) return; + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var ButtonNum = control - leds.fx1+1; + if (value === DOWN) { + if (deck.shiftKey) { + // Select Effect + if (decknum===1) { + engine.setValue("[EffectRack1_EffectUnit" + ButtonNum + "]", "prev_chain", true); + } else { + engine.setValue("[EffectRack1_EffectUnit" + ButtonNum + "]", "next_chain", true); + } + } else { + // Toggle effect + var new_value = !engine.getValue("[EffectRack1_EffectUnit" + ButtonNum + "]","group_" + group + "_enable"); + engine.setValue("[EffectRack1_EffectUnit" + ButtonNum + "]","group_" + group + "_enable",new_value); + deck.LEDs["fx" + ButtonNum].onOff(new_value?ON:OFF); + } + } +}; + +/****************** Shift Button : + * - Press : toggle PFL + * - SHIFT + press : toggle slip mode + * - SHIFT + double press : toggle quantize mode + * *********************************************************************/ +NumarkMixtrack3.PFLButton = function(channel, control, value, status, group) { + if (!value) return; + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + if (value === DOWN) { + if (deck.shiftKey) { + deck.ShiftedPFLButtonControl.ButtonDown(channel, control, value, status, group); + } else { + toggleValue(group,"pfl"); + } + } +}; + +// Callback for the PFL Button +NumarkMixtrack3.OnShiftedPFLButton = function(channel, control, value, status, group, eventkind) { + if (eventkind === DOUBLE_PRESS) { + // Double press : toggle slip mode + toggleValue(group,"slip_enabled"); + } else { + // Single press : toggle quantize mode + toggleValue(group,"quantize"); + } +}; + +NumarkMixtrack3.LoopHalveButton = function(channel, control, value, status, group) { + if (value===DOWN) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (deck.shiftKey) { + engine.setValue(group, "loop_double", true); + } else { + engine.setValue(group, "loop_halve", true); + } + } +}; + +NumarkMixtrack3.PitchBendMinusButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + if (value===DOWN) { + if (deck.shiftKey) { + engine.setValue(group, "beatjump_1_backward", true); + } else { + engine.setValue(group, "rate_temp_down", true); + } + } else if (!deck.shiftKey) { + engine.setValue(group, "rate_temp_down", false); + } +}; + +NumarkMixtrack3.PitchBendPlusButton = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (value===DOWN) { + + if (deck.shiftKey) { + engine.setValue(group, "beatjump_1_forward", true); + } else { + engine.setValue(group, "rate_temp_up", true); + } + } + else if (!deck.shiftKey) { + engine.setValue(group, "rate_temp_up", false); + } +}; + +NumarkMixtrack3.BeatKnob = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var gainIncrement; + var gainValue = []; + var i,j; + var knobValue; + + if (!BeatKnobAsSamplerVolume) { + // Default configuration option, use knob for Beatgrid only + if (value-64 > 0) { + knobValue = value-128; + } else { + knobValue = value; + } + + if (deck.shiftKey) { + if (knobValue<0) { + engine.setValue(group, "beats_adjust_slower", true); + } else { + engine.setValue(group, "beats_adjust_faster", true); + } + } else { + if (knobValue<0) { + engine.setValue(group, "beats_translate_earlier", true); + } else { + engine.setValue(group, "beats_translate_later", true); + } + } + + } else { + // Option: use BeatKnob to adjust Sampler volumes + if (deck.shiftKey) { + if (value-64 > 0) { + knobValue = value-128; + } else { + knobValue = value; + } + + if (knobValue<0) { + engine.setValue(group, "beats_translate_earlier", true); + } else { + engine.setValue(group, "beats_translate_later", true); + } + } else { + // Shift key is not used, we adjust Sampler volumes + // Define new value of sampler pregain + // Off = 1, centered = 1, max = 4 + if (decknum === 1) { + for (i = 1; i <= 4; i++) { + gainValue[i-1] = engine.getValue("[Sampler" + [i] + "]", "pregain"); + + if (gainValue[i-1] <= 1) { + // increment value between 0 and 1 + gainIncrement = 1/20; // 20 increments in one full knob turn + }else{ + // increment value between 1 and 4 + gainIncrement = 3/20; // 20 increments in one full knob turn + } + + // beat knobs sends 1 or 127 as value. If value = 127, turn is counterclockwise, we reduce gain + if (value === 127) { + gainIncrement = - gainIncrement; + } + + gainValue[i-1] = gainValue[i-1] + gainIncrement; + + if ((gainValue[i-1] + gainIncrement) <0) { + gainValue[i-1] = 0; + } + + if ((gainValue[i-1] + gainIncrement) >4) { + gainValue[i-1] = 4; + } + + } + } else { + for (j = 5; j <= 8; j++) { + gainValue[j-1] = engine.getValue("[Sampler" + [j] + "]", "pregain"); + + if (gainValue[j-1] <= 1) { + // increment value between 0 and 1 + gainIncrement = 1/20; // 20 increments in one full knob turn + }else{ + // increment value between 1 and 4 + gainIncrement = 3/20; // 20 increments in one full knob turn + } + + // beat knobs sends 1 or 127 as value. If value = 127, turn is counterclockwise, we reduce gain + if (value === 127) { + gainIncrement = - gainIncrement; + } + + gainValue[j-1] = gainValue[j-1] + gainIncrement; + + if ((gainValue[j-1] + gainIncrement) <0) { + gainValue[j-1] = 0; + } + + if ((gainValue[j-1] + gainIncrement) >4) { + gainValue[j-1] = 4; + } + } + } + + // we adjust pregain with adjusted value + if (decknum === 1) { + for (i = 1; i <= 4; i++) { + if (gainValue[i-1] >=0 && gainValue[i-1] <= 4) { + engine.setValue("[Sampler" + i + "]", "pregain", gainValue[i-1]); + } + } + } else { + for (i = 5; i <= 8; i++) { + if (gainValue[i-1] >= 0 && gainValue[i-1] <= 4) { + engine.setValue("[Sampler" + i + "]", "pregain", gainValue[i-1]); + } + } + } + } + } +}; + +NumarkMixtrack3.bpmTap = function(channel, control, value, status, group) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (value===DOWN) { + engine.setValue(group, "bpm_tap", true); + } else { + engine.setValue(group, "bpm_tap", false); + } +}; + +// ************************ Connected controls +NumarkMixtrack3.OnVuMeterChange = function(value, group, control) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + deck.LEDs.meter.onOff(120*value); +}; + +NumarkMixtrack3.OnPlaypositionChange = function(value, group, control) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (deck.loaded) { + var timeremaining = RealDuration(group) * (1 - value); + var trackwarning = 0; + if (timeremaining <= 30) { + trackwarning = 1; + } + if (timeremaining <= 10) { + trackwarning = 2; + } + if (timeremaining <= 0) { + trackwarning = 3; + } + if (!TrackEndWarning) { + trackwarning = 0; + } + + switch (trackwarning) { + case 0: + deck.LEDs.jogWheelsInScratchMode.onOff(deck.jogWheelsInScratchMode ? ON : OFF); + break; + + case 1: // if less than 30 seconds before end of track : flashing slowly + if (deck.LEDs.jogWheelsInScratchMode.getFlashDuration() !== 1000) { + deck.LEDs.jogWheelsInScratchMode.flashOn(1000, ON, 1000); + } + break; + + case 2: // if less than 10 seconds before end of track : flashing fast + if (deck.LEDs.jogWheelsInScratchMode.getFlashDuration() !== 300) { + deck.LEDs.jogWheelsInScratchMode.flashOn(300, ON, 300); + } + break; + + case 3: // end of strack : full ring lit + deck.LEDs.jogWheelsInScratchMode.onOff(deck.jogWheelsInScratchMode ? ON : OFF); + break; + default: + break; + } + } else { + deck.LEDs.jogWheelsInScratchMode.onOff(deck.jogWheelsInScratchMode ? ON : OFF); + } +}; + +NumarkMixtrack3.OnHotcueChange = function(value, group, control, padindex) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + deck.LEDs["hotCue" + padindex].onOff((value) ? ON : OFF); +}; + +NumarkMixtrack3.OnTrackLoaded = function(value, group, control) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (value!==0) { + if ( !deck.faderstart ) { + // Light up the PFL light indicating that a track is loaded + deck.LEDs["headphones"].onOff(ON); + } else { + // Flash up the PFL light button indicating that a track is loaded with fader start + deck.LEDs["headphones"].flashOn(300,ON,300); + + } + } else { + // Switch off the PFL light indicating that a track is ejected + deck.LEDs["headphones"].onOff(OFF); + } + + var oldloaded = deck.loaded; + deck.loaded = (value !== 0); + if (oldloaded !== deck.loaded) { // if this value changed we update the jog light + engine.trigger(group, "playposition"); + } +}; + +NumarkMixtrack3.OnVolumeChange = function(value, group, control) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var delta = value - deck.lastfadervalue; + + if (deck.faderstart) { + if (value<=0.01) { + engine.setValue(group, "play", 0); + } else { + if (delta>0) { + engine.setValue(group, "play", 1); + } + } + } + deck.lastfadervalue = value; +}; + +NumarkMixtrack3.OnPFLStatusChange = function(value, group, control) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + deck.LEDs.headphones.onOff((value) ? ON : OFF); +}; + +NumarkMixtrack3.OnPlayIndicatorChange = function(value, group, control) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + deck.LEDs.play.onOff((value) ? ON : OFF); +}; + +NumarkMixtrack3.OnBeatActive = function(value, group, control) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + if (!deck.shiftKey && OnBeatActiveFlash) { + deck.LEDs.tap.onOff((value) ? ON : OFF); + } +}; + +NumarkMixtrack3.OnCuePointChange = function(value, group, control) { + var decknum = parseInt(group.substring(8,9)); + var deck = NumarkMixtrack3.decks["D" + decknum]; + deck.LEDs.Cue.onOff((value) ? ON : OFF); +}; + +NumarkMixtrack3.OnLoopInOutChange = function(value, group, key) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var valIn = engine.getValue(group, "loop_start_position"); + var valOut = engine.getValue(group, "loop_end_position"); + var valEnabled = engine.getValue(group, "loop_enabled"); + + if (valIn==-1) { + if (deck.LEDs.loopin.getFlashDuration()!==300) { + deck.LEDs.loopin.flashOn(300,PADcolors.blue,300); + } + deck.LEDs.loopout.onOff(OFF); + deck.LEDs.reloop_exit.onOff(OFF); + deck.LEDs.loop_halve.onOff(OFF); + } else if (valOut==-1) { + deck.LEDs.loopin.onOff(PADcolors.blue); + if (deck.LEDs.loopout.getFlashDuration()!==300) { + deck.LEDs.loopout.flashOn(300,PADcolors.blue,300); + } + deck.LEDs.reloop_exit.onOff(OFF); + deck.LEDs.loop_halve.onOff(OFF); + } else if (!valEnabled) { + deck.LEDs.loopin.onOff(PADcolors.blue); + deck.LEDs.loopout.onOff(PADcolors.blue); + if (deck.LEDs.reloop_exit.getFlashDuration()!==300) { + deck.LEDs.reloop_exit.flashOn(300,PADcolors.blue,300); + } + deck.LEDs.loop_halve.onOff(PADcolors.blue); + } else { + deck.LEDs.loopin.onOff(PADcolors.blue); + deck.LEDs.loopout.onOff(PADcolors.blue); + deck.LEDs.reloop_exit.onOff(PADcolors.blue); + deck.LEDs.loop_halve.onOff(PADcolors.blue); + } +}; + +NumarkMixtrack3.OnEffectLoaded = function(value, group, control, index) { + var i; + var parameterlinkedcount = 0; + var linktype = 0; + if (value) { + var numparams = engine.getValue("[EffectRack1_EffectUnit" + index + "_Effect1]", "num_parameters"); + if (numparams > 0) { + for (i = 1; i <= numparams; i++) { + linktype = engine.getValue("[EffectRack1_EffectUnit" + index + "_Effect1]", "parameter" + i + "_link_type"); + if (linktype !== 0) { + parameterlinkedcount += 1; + } + } + } + if (parameterlinkedcount === 0) { + for (i = 1; i <= numparams; i++) { + engine.setValue("[EffectRack1_EffectUnit" + index + "_Effect1]", "parameter" + i + "_link_type", 1); + } + } + } +}; + +NumarkMixtrack3.OnSamplePlayStop = function(value, group, control) { + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + + if (value === 1) { + deck.LEDs["PADsampler" + decknum].flashOn(300, PADcolors.purple, 300); + } else { + deck.LEDs["PADsampler" + decknum].onOff(ON); + } +}; + +NumarkMixtrack3.OnPADLoopButtonChange = function(value, group, control) { + var loopsize = [0.125, 0.25, 0.5, 1, 2, 4, 8, 16]; + var decknum = NumarkMixtrack3.deckFromGroup(group); + var deck = NumarkMixtrack3.decks["D" + decknum]; + var l; + var index; + + if (value === 1) { + for (l=0;l4) { + index = index - 4; + } + + deck.LEDs["PADloop" + index].flashOn(300, PADcolors.yellow, 300); + + } + } + } else { + + for (l=0;l4) { + index = index - 4; + } + + deck.LEDs["PADloop"+ index].onOff(PADcolors.yellow); + } + } + } + +}; \ No newline at end of file diff --git a/res/controllers/Numark-Mixtrack-3.midi.xml b/res/controllers/Numark-Mixtrack-3.midi.xml new file mode 100644 index 000000000000..5bd526bcef39 --- /dev/null +++ b/res/controllers/Numark-Mixtrack-3.midi.xml @@ -0,0 +1,919 @@ + + + + + Numark Mixtrack (Pro) 3 + Stéphane Morin + The Numark Mixtrack 3 and Numark Mixtrack Pro 3 are the same controller except that the Pro version has an integrated sound card. + http://mixxx.org/forums/viewtopic.php?f=7&t=7286 + http://www.mixxx.org/wiki/doku.php/numark_mixtrack_pro_3 + + + + + + + + [Master] + crossfader + Crossfader + 0xB0 + 0x07 + + + + + + [Channel1] + volume + Channel 1 line fader + 0xB1 + 0x05 + + + + + + [Channel2] + volume + Channel 2 line fader + 0xB2 + 0x05 + + + + + + [Master] + gain + Master gain knob + 0xB0 + 0x0A + + + + + + [Master] + headMix + Cue mix knob + 0xB0 + 0x09 + + + + + + [Master] + headGain + Cue gain knob + 0xB0 + 0x08 + + + + + + [Playlist] + NumarkMixtrack3.BrowseButton + Browse button + 0x90 + 0x12 + + + + + + [Playlist] + NumarkMixtrack3.BrowseKnob + Browse knob + 0xB0 + 0x17 + + + + + + [Channel1] + NumarkMixtrack3.LoadButton + Channel 1 load button + 0x90 + 0x10 + + + + + + [Channel2] + NumarkMixtrack3.LoadButton + Channel 2 load button + 0x90 + 0x11 + + + + + + [Channel1] + NumarkMixtrack3.PadModeButton + Channel 1 Pad Mode button + 0x91 + 0x0D + + + + + + [Channel2] + NumarkMixtrack3.PadModeButton + Channel 2 Pad Mode button + 0x92 + 0x0D + + + + + + [Channel1] + NumarkMixtrack3.bpmTap + bpm_tap + 0x91 + 0x0A + + + + + + [Channel2] + NumarkMixtrack3.bpmTap + bpm_tap + 0x92 + 0x0A + + + + + + [Channel1] + NumarkMixtrack3.FXButton + Channel 1 FX 1 button + 0x91 + 0x07 + + + + + + [Channel2] + NumarkMixtrack3.FXButton + Channel 2 FX 1 button + 0x92 + 0x07 + + + + + + [Channel1] + NumarkMixtrack3.FXButton + Channel 1 FX 2 button + 0x91 + 0x08 + + + + + + [Channel2] + NumarkMixtrack3.FXButton + Channel 2 FX 2 button + 0x92 + 0x08 + + + + + + [Channel1] + NumarkMixtrack3.FXButton + Channel 1 FX 3 button + 0x91 + 0x09 + + + + + + [Channel2] + NumarkMixtrack3.FXButton + Channel 2 FX 3 button + 0x92 + 0x09 + + + + + + [Channel1] + NumarkMixtrack3.PFLButton + Channel 1 PFL button + 0x90 + 0x0E + + + + + + [Channel2] + NumarkMixtrack3.PFLButton + Channel 2 PFL button + 0x90 + 0x0F + + + + + + [Channel1] + NumarkMixtrack3.PlayButton + Channel 1 play/pause button + 0x91 + 0x01 + + + + + + [Channel2] + NumarkMixtrack3.PlayButton + Channel 2 play/pause button + 0x92 + 0x01 + + + + + + [Channel1] + NumarkMixtrack3.CueButton + Channel 1 cue button + 0x91 + 0x03 + + + + + + [Channel2] + NumarkMixtrack3.CueButton + Channel 2 cue button + 0x92 + 0x03 + + + + + + [Channel1] + NumarkMixtrack3.SyncButton + Channel 1 sync button + 0x91 + 0x02 + + + + + + [Channel2] + NumarkMixtrack3.SyncButton + Channel 2 sync button + 0x92 + 0x02 + + + + + + [Channel1] + NumarkMixtrack3.ShiftButton + Channel 1 shift button + 0x91 + 0x0B + + + + + + [Channel2] + NumarkMixtrack3.ShiftButton + Channel 2 shift button + 0x92 + 0x0B + + + + + + [Channel1] + filterLow + Channel 1 low EQ knob + 0xB1 + 0x03 + + + + + + [Channel2] + filterLow + Channel 2 low EQ knob + 0xB2 + 0x03 + + + + + + [Channel1] + filterMid + Channel 1 mid EQ knob + 0xB1 + 0x02 + + + + + + [Channel2] + filterMid + Channel 2 mid EQ knob + 0xB2 + 0x02 + + + + + + [Channel1] + filterHigh + Channel 1 high EQ knob + 0xB1 + 0x01 + + + + + + [Channel2] + filterHigh + Channel 2 high EQ knob + 0xB2 + 0x01 + + + + + + [QuickEffectRack1_[Channel1]] + super1 + Channel 1 filter knob + 0xB1 + 0x04 + + + + + + [QuickEffectRack1_[Channel2]] + super1 + Channel 2 filter knob + 0xB2 + 0x04 + + + + + + [Channel1] + loop_in + Channel 1 loop in button + 0x91 + 0x13 + + + + + + [Channel2] + loop_in + Channel 1 loop in button + 0x92 + 0x13 + + + + + + [Channel1] + loop_out + Channel 1 loop out button + 0x91 + 0x14 + + + + + + [Channel2] + loop_out + Channel 2 loop out button + 0x92 + 0x14 + + + + + + [Channel1] + reloop_exit + Channel 1 loop on/off button + 0x91 + 0x15 + + + + + + [Channel2] + reloop_exit + Channel 2 loop on/off button + 0x92 + 0x15 + + + + + + [Channel1] + NumarkMixtrack3.LoopHalveButton + Channel 1 halve loop button + 0x91 + 0x16 + + + + + + [Channel2] + NumarkMixtrack3.LoopHalveButton + Channel 2 halve loop button + 0x92 + 0x16 + + + + + + [Channel1] + NumarkMixtrack3.PADLoopButton + Channel 1 PAD loop button 1 + 0x91 + 0x17 + + + + + + [Channel2] + NumarkMixtrack3.PADLoopButton + Channel 2 PAD loop button 1 + 0x92 + 0x17 + + + + + + [Channel1] + NumarkMixtrack3.PADLoopButton + Channel 1 PAD loop button 2 + 0x91 + 0x18 + + + + + + [Channel2] + NumarkMixtrack3.PADLoopButton + Channel 2 PAD loop button 2 + 0x92 + 0x18 + + + + + + [Channel1] + NumarkMixtrack3.PADLoopButton + Channel 1 PAD loop button 3 + 0x91 + 0x19 + + + + + + [Channel2] + NumarkMixtrack3.PADLoopButton + Channel 2 PAD loop button 3 + 0x92 + 0x19 + + + + + + [Channel1] + NumarkMixtrack3.PADLoopButton + Channel 1 PAD loop button 4 + 0x91 + 0x1A + + + + + + [Channel2] + NumarkMixtrack3.PADLoopButton + Channel 2 PAD loop button 4 + 0x92 + 0x1A + + + + + + [Channel1] + NumarkMixtrack3.HotCueButton + Channel 1 hotcue 1 button + 0x91 + 0x1B + + + + + + [Channel2] + NumarkMixtrack3.HotCueButton + Channel 2 hotcue 1 button + 0x92 + 0x1B + + + + + + [Channel1] + NumarkMixtrack3.HotCueButton + Channel 1 hotcue 2 button + 0x91 + 0x1C + + + + + + [Channel2] + NumarkMixtrack3.HotCueButton + Channel 2 hotcue 2 button + 0x92 + 0x1C + + + + + + [Channel1] + NumarkMixtrack3.HotCueButton + Channel 1 hotcue 3 button + 0x91 + 0x1D + + + + + + [Channel2] + NumarkMixtrack3.HotCueButton + Channel 2 hotcue 3 button + 0x92 + 0x1D + + + + + + [Channel1] + NumarkMixtrack3.HotCueButton + Channel 1 hotcue 4 button + 0x91 + 0x1E + + + + + + [Channel2] + NumarkMixtrack3.HotCueButton + Channel 2 hotcue 4 button + 0x92 + 0x1E + + + + + + [Channel1] + NumarkMixtrack3.WheelMove + Channel 1 jogwheel + 0xB1 + 0x11 + + + + + + [Channel2] + NumarkMixtrack3.WheelMove + Channel 2 jogwheel + 0xB2 + 0x11 + + + + + + [Channel1] + NumarkMixtrack3.WheelTouch + Channel 1 jogwheel capacitive sensor + 0x91 + 0x0C + + + + + + [Channel2] + NumarkMixtrack3.WheelTouch + Channel 2 jogwheel capacitive sensor + 0x92 + 0x0C + + + + + + [Channel1] + NumarkMixtrack3.toggleJogMode + Channel 1 wheel button + 0x91 + 0x06 + + + + + + [Channel2] + NumarkMixtrack3.toggleJogMode + Channel 2 wheel button + 0x92 + 0x06 + + + + + + [Channel1] + NumarkMixtrack3.PitchFaderHighValue + Channel 1 pitch fader main bits + 0xB1 + 0x18 + + + + + + [Channel2] + NumarkMixtrack3.PitchFaderHighValue + Channel 2 pitch fader main bits + 0xB2 + 0x18 + + + + + + [Channel1] + NumarkMixtrack3.PitchFaderLowValue + Channel 1 pitch fader precision bits + 0xB1 + 0x38 + + + + + + [Channel2] + NumarkMixtrack3.PitchFaderLowValue + Channel 2 pitch fader precision bits + 0xB2 + 0x38 + + + + + + [Channel1] + NumarkMixtrack3.PitchBendPlusButton + Channel 1 pitch bend up button + 0x91 + 0x05 + + + + + + [Channel2] + NumarkMixtrack3.PitchBendPlusButton + Channel 2 pitch bend up button + 0x92 + 0x05 + + + + + + [Channel1] + NumarkMixtrack3.PitchBendMinusButton + Channel 1 pitch bend down button + 0x91 + 0x04 + + + + + + [Channel2] + NumarkMixtrack3.PitchBendMinusButton + Channel 2 pitch bend down button + 0x92 + 0x04 + + + + + + [Sampler1] + NumarkMixtrack3.SamplerButton + Channel 1 sampler 1 button + 0x91 + 0x20 + + + + + + [Sampler1] + NumarkMixtrack3.SamplerButton + Channel 2 sampler 1 button + 0x92 + 0x20 + + + + + + [Sampler2] + NumarkMixtrack3.SamplerButton + Channel 1 sampler 2 button + 0x91 + 0x21 + + + + + + [Sampler2] + NumarkMixtrack3.SamplerButton + Channel 2 sampler 2 button + 0x92 + 0x21 + + + + + + [Sampler3] + NumarkMixtrack3.SamplerButton + Channel 1 sampler 3 button + 0x91 + 0x22 + + + + + + [Sampler3] + NumarkMixtrack3.SamplerButton + Channel 2 sampler 3 button + 0x92 + 0x22 + + + + + + [Sampler4] + NumarkMixtrack3.SamplerButton + Channel 1 sampler 4 button + 0x91 + 0x23 + + + + + + [Sampler4] + NumarkMixtrack3.SamplerButton + Channel 2 sampler 4 button + 0x92 + 0x23 + + + + + + [Channel1] + NumarkMixtrack3.StripTouchEffect + Channel 1 strip + 0xB1 + 0x1A + + + + + + [Channel2] + NumarkMixtrack3.StripTouchEffect + Channel 2 strip + 0xB2 + 0x1A + + + + + + [Channel1] + NumarkMixtrack3.StripTouchSearch + Channel 1 SHIFT+strip + 0xB1 + 0x1B + + + + + + [Channel2] + NumarkMixtrack3.StripTouchSearch + Channel 2 SHIFT+strip + 0xB2 + 0x1B + + + + + + [Channel1] + NumarkMixtrack3.BeatKnob + Channel 1 FX 3 Knob + 0xB1 + 0x13 + + + + + + [Channel2] + NumarkMixtrack3.BeatKnob + Channel 2 Beat Knob + 0xB2 + 0x13 + + + + + + + +