diff --git a/res/controllers/Hercules-DJControl-Inpulse-300-script.js b/res/controllers/Hercules-DJControl-Inpulse-300-script.js index e2759d619e2..380fcdf5de3 100644 --- a/res/controllers/Hercules-DJControl-Inpulse-300-script.js +++ b/res/controllers/Hercules-DJControl-Inpulse-300-script.js @@ -2,11 +2,20 @@ // // *************************************************************************** // * Mixxx mapping script file for the Hercules DJControl Inpulse 300. -// * Author: DJ Phatso, contributions by Kerrick Staley -// * Version 1.2 (March 2020) +// * Author: DJ Phatso, contributions by Kerrick Staley and BoredGuy1 +// * Version 1.3 (May 2024) // * Forum: https://www.mixxx.org/forums/viewtopic.php?f=7&t=12599 // * Wiki: https://mixxx.org/wiki/doku.php/hercules_djcontrol_inpulse_300 // +// Changes to v1.3 +// - Added ability to stop samplers (shift + button) +// - Added toneplay +// - Added shift + toneplay controls +// - Added slicer/slicer loop +// - Replaced the song end warning with an actual beatmatch guide +// - Changed the way scratching works (wheels have inertia, allowing backspins and other tricks) +// - Updated VU meter syntax (replaced vu_meter with VuMeter, connectControl with makeConnection, etc) +// // Changes to v1.2 // - Code cleanup. // @@ -20,20 +29,34 @@ // // TO DO: Functions that could be implemented to the script: // +// * HOTCUES: Make loop hotcues more intuitive. Currently, the pads are always lit when loop cues are set, +// regardless of whether or not the loop is enabled (maybe make the pad blink when set but inactive?) +// // * ROLL: Keep SLIP active (if already enabled) when exiting from rolls // -// * SLICER/SLICER LOOP +// * FX: See how to preselect effects for a rack // -// * TONEPLAY +// * SLICER: Currently resource intensive because it's connected to beat_distance (which is always updated). +// Is there a better way? +// +// * BEATMATCH: Also resource intensive because it's connected to beat_distance. We could optimize a bit by +// disconnecting functions when the beatmatch guide is disabled (currently the functions are +// always connected, but the LEDs are turned off by hardware controls) // -// * FX: -// - See how to preselect effects for a rack // **************************************************************************** var DJCi300 = {}; /////////////////////////////////////////////////////////////// // USER OPTIONS // /////////////////////////////////////////////////////////////// +// Beatmatch LED guide tolerances +DJCi300.beatmatchTempoTolerance = .1; // Measured in BPM (e.g. LEDS turn off if decks are <0.1 BPM apart) +DJCi300.beatmatchAlignTolerance = .02; // Measured in beats (e.g. LEDS turn off if decks are <0.02 beats apart) + +// Determines how fast the wheel must be moving to be considered "slipping" +// Higher numbers result in longer backspins +DJCi300.slipThreshold = .1; // Must be between 0 and 1, non-inclusive + // How fast scratching is. DJCi300.scratchScale = 1.0; @@ -44,80 +67,197 @@ DJCi300.scratchShiftMultiplier = 4; DJCi300.bendScale = 1.0; // Other scratch related options -DJCi300.kScratchActionNone = 0; +DJCi300.kScratchActionBend = 0; DJCi300.kScratchActionScratch = 1; DJCi300.kScratchActionSeek = 2; -DJCi300.kScratchActionBend = 3; -DJCi300.vuMeterUpdateMaster = function(value, _group, _control) { - value = (value * 122) + 5; +// Pad modes +DJCi300.padModeNone = 0; +// These correspond directly to the MIDI control values +DJCi300.padModeHotcue = 15; +DJCi300.padModeRoll = 16; +DJCi300.padModeSlicer = 17; +DJCi300.padModeSampler = 18; +DJCi300.padModeToneplay = 19; +DJCi300.padModeFX = 20; +DJCi300.padModeSlicerloop = 21; +DJCi300.padModeBeatjump = 22; + +DJCi300.vuMeterUpdateMain = function(value, _group, _control) { + value = (value * 125); midi.sendShortMsg(0xB0, 0x40, value); midi.sendShortMsg(0xB0, 0x41, value); }; DJCi300.vuMeterUpdateDeck = function(value, group, _control, _status) { - value = (value * 122) + 5; - var status = (group === "[Channel1]") ? 0xB1 : 0xB2; + value = (value * 125); + const status = (group === "[Channel1]") ? 0xB1 : 0xB2; midi.sendShortMsg(status, 0x40, value); }; DJCi300.init = function() { - if (engine.getValue("[App]", "num_samplers") < 16) { - engine.setValue("[App]", "num_samplers", 16); - } - // Scratch button state - DJCi300.scratchButtonState = true; + DJCi300.scratchButtonState = { + "[Channel1]": true, + "[Channel2]": true + }; // Scratch Action DJCi300.scratchAction = { - 1: DJCi300.kScratchActionNone, - 2: DJCi300.kScratchActionNone + "[Channel1]": DJCi300.kScratchActionBend, + "[Channel2]": DJCi300.kScratchActionBend + }; + // Platter state (whether the jog wheel is pressed or not) + DJCi300.wheelTouchState = { + "[Channel1]": false, + "[Channel2]": false + }; + + // Pad mode variables + // Initialize to padModeNone + DJCi300.padMode = { + "[Channel1]": DJCi300.padModeNone, + "[Channel2]": DJCi300.padModeNone }; // Turn On Vinyl buttons LED(one for each deck). midi.sendShortMsg(0x91, 0x03, 0x7F); midi.sendShortMsg(0x92, 0x03, 0x7F); - //Turn On Browser button LED + // Turn On Browser button LED midi.sendShortMsg(0x90, 0x04, 0x05); - //Softtakeover for Pitch fader - engine.softTakeover("[Channel1]", "rate", true); - engine.softTakeover("[Channel2]", "rate", true); - engine.softTakeoverIgnoreNextValue("[Channel1]", "rate"); - engine.softTakeoverIgnoreNextValue("[Channel2]", "rate"); - - // Connect the VUMeters - engine.connectControl("[Channel1]", "vu_meter", "DJCi300.vuMeterUpdateDeck"); - engine.getValue("[Channel1]", "vu_meter", "DJCi300.vuMeterUpdateDeck"); - engine.connectControl("[Channel2]", "vu_meter", "DJCi300.vuMeterUpdateDeck"); - engine.getValue("[Channel2]", "vu_meter", "DJCi300.vuMeterUpdateDeck"); - engine.connectControl("[Main]", "vu_meter_left", "DJCi300.vuMeterUpdateMaster"); - engine.connectControl("[Main]", "vu_meter_right", "DJCi300.vuMeterUpdateMaster"); - engine.getValue("[Main]", "vu_meter_left", "DJCi300.vuMeterUpdateMaster"); - engine.getValue("[Main]", "vu_meter_right", "DJCi300.vuMeterUpdateMaster"); + // Turn On Toneplay LED + midi.sendShortMsg(0x96, 0x40, 0x7F); + midi.sendShortMsg(0x96, 0x48, 0x7F); + midi.sendShortMsg(0x97, 0x40, 0x7F); + midi.sendShortMsg(0x97, 0x48, 0x7F); + + // Connect main VUMeters + engine.makeConnection("[Main]", "vu_meter_left", DJCi300.vuMeterUpdateMain); + engine.makeConnection("[Main]", "vu_meter_right", DJCi300.vuMeterUpdateMain); + + for (const group of ["[Channel1]", "[Channel2]"]) { + // Connect left and right VUMeters + engine.makeConnection(group, "vu_meter", DJCi300.vuMeterUpdateDeck); + + //Softtakeover for Pitch fader + engine.softTakeover(group, "rate", true); + engine.softTakeoverIgnoreNextValue(group, "rate"); + + // Connect jogwheel functions + engine.makeConnection(group, "scratch2", DJCi300.updateScratchAction); + + // Connect the toneplay LED updates + engine.makeConnection(group, "pitch", DJCi300.updateToneplayLED); + + // Connect beatmatch LED functions + engine.makeConnection(group, "bpm", DJCi300.updateBeatmatchTempoLED); + // We also want to update all beatmatch LEDs when a song is played/paused + engine.makeConnection(group, "play", DJCi300.updateBeatmatchAlignLED); + engine.makeConnection(group, "play", DJCi300.updateBeatmatchTempoLED); + } + // Only connect one channel to updateBeatmatchAlignLED because beatmatch LEDs are only enabled when both decks are playing + engine.makeConnection("[Channel1]", "beat_distance", DJCi300.updateBeatmatchAlignLED); // 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, 0x7F); + + DJCi300.deck = []; + for (let i = 0; i < 2; i++) { + DJCi300.deck[i] = new DJCi300.Deck(i + 1); + DJCi300.deck[i].setCurrentDeck(`[Channel${ i + 1 }]`); + // For some reason, the slicer callback functions start out connected + // This is a dirty hack to ensure they start disconnected + DJCi300.deck[i].slicerPad.forEachComponent(function(component) { + component.disconnect(); + }); + } +}; + +// Update beatmatch tempo LEDs +DJCi300.updateBeatmatchTempoLED = function(_value, _group, _control) { + const deck1tempo = engine.getValue("[Channel1]", "bpm"); + const deck2tempo = engine.getValue("[Channel2]", "bpm"); + + // If successfully synced, or if one of the songs are paused, turn all lights off + if ((Math.abs(deck1tempo - deck2tempo) < DJCi300.beatmatchTempoTolerance) || + (engine.getValue("[Channel1]", "play") === 0) || + (engine.getValue("[Channel2]", "play") === 0)) { + midi.sendShortMsg(0x91, 0x1E, 0x00); + midi.sendShortMsg(0x91, 0x1F, 0x00); + midi.sendShortMsg(0x92, 0x1E, 0x00); + midi.sendShortMsg(0x92, 0x1F, 0x00); + // If deck 1 is faster, lights tell user to slow down 1 and speed up 2 + } else if (deck1tempo > deck2tempo) { + midi.sendShortMsg(0x91, 0x1E, 0x7F); + midi.sendShortMsg(0x91, 0x1F, 0x00); + midi.sendShortMsg(0x92, 0x1E, 0x00); + midi.sendShortMsg(0x92, 0x1F, 0x7F); + // If deck 2 is faster, lights tell user to slow down 2 and speed up 1 + } else if (deck1tempo < deck2tempo) { + midi.sendShortMsg(0x91, 0x1E, 0x00); + midi.sendShortMsg(0x91, 0x1F, 0x7F); + midi.sendShortMsg(0x92, 0x1E, 0x7F); + midi.sendShortMsg(0x92, 0x1F, 0x00); + } +}; + +// Update beatmatch align LEDs +DJCi300.updateBeatmatchAlignLED = function(value, _group, _control) { + let deck1Align = value; + let deck2Align = engine.getValue("[Channel2]", "beat_distance"); + + // Because beat_distance resets to 0 every new beat, it's possible for the two decks to have + // very different beat values and still be almost aligned. So we must adjust for this + if (Math.abs(deck1Align - deck2Align) > .5) { + // Add 1 to the smaller number to compensate for roll over + if (deck1Align < deck2Align) { + deck1Align += 1; + } else { + deck2Align += 1; + } + } + + // If successfully synced, or if one of the songs are paused, turn all lights off + if ((Math.abs(deck1Align - deck2Align) < DJCi300.beatmatchAlignTolerance) || + (engine.getValue("[Channel1]", "play") === 0) || + (engine.getValue("[Channel2]", "play") === 0)) { + midi.sendShortMsg(0x91, 0x1C, 0x00); + midi.sendShortMsg(0x91, 0x1D, 0x00); + midi.sendShortMsg(0x92, 0x1C, 0x00); + midi.sendShortMsg(0x92, 0x1D, 0x00); + // If deck 1 is ahead, lights tell user to push 1 back and push 2 ahead + } else if (deck1Align > deck2Align) { + midi.sendShortMsg(0x91, 0x1C, 0x00); + midi.sendShortMsg(0x91, 0x1D, 0x7F); + midi.sendShortMsg(0x92, 0x1C, 0x7F); + midi.sendShortMsg(0x92, 0x1D, 0x00); + // If deck 2 is ahead, lights tell user to push 2 back and push 1 ahead + } else if (deck1Align < deck2Align) { + midi.sendShortMsg(0x91, 0x1C, 0x7F); + midi.sendShortMsg(0x91, 0x1D, 0x00); + midi.sendShortMsg(0x92, 0x1C, 0x00); + midi.sendShortMsg(0x92, 0x1D, 0x7F); + } }; // The Vinyl button, used to enable or disable scratching on the jog wheels (One per deck). -DJCi300.vinylButton = function(_channel, _control, value, status, _group) { +DJCi300.vinylButton = function(_channel, control, value, status, group) { if (value) { - if (DJCi300.scratchButtonState) { - DJCi300.scratchButtonState = false; - midi.sendShortMsg(status, 0x03, 0x00); + if (DJCi300.scratchButtonState[group]) { + DJCi300.scratchButtonState[group] = false; + midi.sendShortMsg(status, control, 0x00); } else { - DJCi300.scratchButtonState = true; - midi.sendShortMsg(status, 0x03, 0x7F); + DJCi300.scratchButtonState[group] = true; + midi.sendShortMsg(status, control, 0x7F); } } }; DJCi300._scratchEnable = function(deck) { - var alpha = 1.0/8; - var beta = alpha/32; + const alpha = 1.0/8; + const beta = alpha/32; engine.scratchEnable(deck, 248, 33 + 1/3, alpha, beta); }; @@ -128,55 +268,61 @@ DJCi300._convertWheelRotation = function(value) { return value < 0x40 ? 1 : -1; }; +// This is called immediately after the wheel is released and we want to switch between scratching and jogging gracefully. +// It is also connected to callbacks and called regularly to see if the wheel has slowed down enough. Once it does, then we switch from scratching to jogging +DJCi300.updateScratchAction = function(value, group, _control) { + const deck = script.deckFromGroup(group); + + // Stop scratching only if the jogwheel is slow enough and the wheels are not being touched + if (((Math.abs(value) < DJCi300.slipThreshold)) && (engine.isScratching(deck)) + && !DJCi300.wheelTouchState[group]) { + engine.scratchDisable(deck); + DJCi300.scratchAction[group] = DJCi300.kScratchActionBend; + } +}; + // The touch action on the jog wheel's top surface -DJCi300.wheelTouch = function(channel, control, value, _status, _group) { - var deck = channel; +DJCi300.wheelTouch = function(_channel, _control, value, _status, group) { + const deck = script.deckFromGroup(group); if (value > 0) { - // Touching the wheel. - if (engine.getValue("[Channel" + deck + "]", "play") !== 1 || DJCi300.scratchButtonState) { + // Enable scratching in vinyl mode OR if the deck is not playing + if ((engine.getValue(group, "play") !== 1) || (DJCi300.scratchButtonState[group])) { DJCi300._scratchEnable(deck); - DJCi300.scratchAction[deck] = DJCi300.kScratchActionScratch; + DJCi300.wheelTouchState[group] = true; + DJCi300.scratchAction[group] = DJCi300.kScratchActionScratch; } else { - DJCi300.scratchAction[deck] = DJCi300.kScratchActionBend; + DJCi300.scratchAction[group] = DJCi300.kScratchActionBend; } } else { // Released the wheel. - engine.scratchDisable(deck); - DJCi300.scratchAction[deck] = DJCi300.kScratchActionNone; + DJCi300.wheelTouchState[group] = false; + const scratchValue = engine.getValue(group, "scratch2"); + DJCi300.updateScratchAction(scratchValue, group); } }; // The touch action on the jog wheel's top surface while holding shift -DJCi300.wheelTouchShift = function(channel, control, value, _status, _group) { - var deck = channel - 3; +DJCi300.wheelTouchShift = function(_channel, _control, value, _status, group) { + const deck = script.deckFromGroup(group); // We always enable scratching regardless of button state. if (value > 0) { DJCi300._scratchEnable(deck); - DJCi300.scratchAction[deck] = DJCi300.kScratchActionSeek; + DJCi300.wheelTouchState[group] = true; + DJCi300.scratchAction[group] = DJCi300.kScratchActionSeek; + // Released the wheel. } else { - // Released the wheel. - engine.scratchDisable(deck); - DJCi300.scratchAction[deck] = DJCi300.kScratchActionNone; + DJCi300.wheelTouchState[group] = false; + const scratchValue = engine.getValue(group, "scratch2"); + DJCi300.updateScratchAction(scratchValue, group); } }; -// Scratching on the jog wheel (rotating it while pressing the top surface) -DJCi300.scratchWheel = function(channel, control, value, status, _group) { - var deck; - switch (status) { - case 0xB1: - case 0xB4: - deck = 1; - break; - case 0xB2: - case 0xB5: - deck = 2; - break; - default: - return; - } +// Using the jog wheel (spinning the jog wheel, regardless of whether surface or shift is held) +DJCi300.jogWheel = function(_channel, _control, value, _status, group) { + const deck = script.deckFromGroup(group); + var interval = DJCi300._convertWheelRotation(value); - var scratchAction = DJCi300.scratchAction[deck]; + const scratchAction = DJCi300.scratchAction[group]; if (scratchAction === DJCi300.kScratchActionScratch) { engine.scratchTick(deck, interval * DJCi300.scratchScale); } else if (scratchAction === DJCi300.kScratchActionSeek) { @@ -185,16 +331,322 @@ DJCi300.scratchWheel = function(channel, control, value, status, _group) { DJCi300.scratchShiftMultiplier); } else { engine.setValue( - "[Channel" + deck + "]", "jog", interval * DJCi300.bendScale); + group, "jog", interval * DJCi300.bendScale); } }; -// Bending on the jog wheel (rotating using the edge) -DJCi300.bendWheel = function(channel, control, value, _status, _group) { - var interval = DJCi300._convertWheelRotation(value); - engine.setValue( - "[Channel" + channel + "]", "jog", interval * DJCi300.bendScale); +// Helper function that calculates samples per beat +DJCi300._samplesPerBeat = function(group) { + const sampleRate = engine.getValue(group, "track_samplerate"); + const bpm = engine.getValue(group, "local_bpm"); + // The sample rate includes both channels (i.e. it is double the framerate) + // Hence, we multiply by 2*60 (120) instead of 60 to get the correct sample rate + const secondsPerBeat = 120/bpm; + const samplesPerBeat = secondsPerBeat * sampleRate; + return samplesPerBeat; +}; + +// Helper function that returns a deck object from a group +DJCi300._deckObjectFromGroup = function(group) { + return DJCi300.deck[script.deckFromGroup(group) - 1]; +}; + +// Mode buttons +DJCi300.changeMode = function(_channel, control, value, _status, group) { + const oldPadMode = DJCi300.padMode[group]; + DJCi300.padMode[group] = control; + const deckObject = DJCi300._deckObjectFromGroup(group); + + if (value) { + // Connect slicer when entering slicer or slicerloop mode + if ((DJCi300.padMode[group] === DJCi300.padModeSlicer) || + (DJCi300.padMode[group] === DJCi300.padModeSlicerloop)) { + + // If slicer connections are not present, connect them. Otherwise, disconnect them + if (deckObject.slicerPad.beatConnection === undefined || + deckObject.slicerPad.beatConnection.isConnected === false) { + + deckObject.slicerPad.forEachComponent(function(component) { + component.connect(engine.getValue(group, "beat_closest")); + }); + } else { + deckObject.slicerPad.forEachComponent(function(component) { + component.disconnect(); + }); + }; + + // When switching from slicer/slicer loop mode into the other modes, disconnect slicer functions + } else if ((oldPadMode === DJCi300.padModeSlicer) || + (oldPadMode === DJCi300.padModeSlicerloop)) { + + deckObject.slicerPad.forEachComponent(function(component) { + component.disconnect(); + }); + } + } +}; + +// Toneplay +DJCi300.toneplay = function(_channel, control, value, _status, group) { + let button = control - 0x40; + + if (value) { + // Pad buttons (buttons 1-8) will jump to a hotcue and change pitch + // Shift + pad buttons (buttons 9-16) will only change pitch without jumping + if (button < 8) { + // Jump to the most recently used hotcue + const recentHotcue = engine.getValue(group, "hotcue_focus"); + if ((recentHotcue > 0) && (engine.getValue(group, + `hotcue_${ recentHotcue }_status`) > 0)) { + engine.setValue(group, `hotcue_${ recentHotcue }_goto`, 1); + } else { + // If that hotcue doesn't exist or was deleted, jump to cue + engine.setValue(group, + "cue_goto", 1); + } + } + + // Subtract 8 from buttons if they're shifted + button = (button < 8) ? button : button - 8; + // Adjust pitch + if (button < 4) { + // Buttons 1-4 are +0 to +3 semitones + engine.setValue(group, "pitch", button); + // Buttons 5-8 are -4 to -1 semitones + } else { + engine.setValue(group, "pitch", button - 8); + } + } +}; + +// Update toneplay LEDs (LEDS will change depending on pitch, even if not caused by toneplay) +DJCi300.updateToneplayLED = function(value, group, _control) { + const status = (group === "[Channel1]") ? 0x96 : 0x97; + let control = 0x40; + + // Cut off the value at -4 and 3 semitones, then round + value = Math.min(value, 3); + value = Math.max(value, -4); + value = Math.round(value); + + // Buttons 1-4 (ctrl 0x40-0x43) are +0 to +3 semitones + // Buttons 5-8 (ctrl 0x44-0x47) are -4 to -1 semitones + if (value >= 0) { + control = control + value; + } else { + control = control + 8 + value; + } + + // Do the following for normal LEDs and the shifted LEDs + // Turn off all LEDs + for (let i = 0; i < 8; i++) { + midi.sendShortMsg(status, 0x40 + i, 0x00); + midi.sendShortMsg(status, 0x40 + i + 8, 0x00); + } + // Turn on current LED + midi.sendShortMsg(status, control, 0x7F); + midi.sendShortMsg(status, control + 8, 0x7F); +}; + +// Loop in button +DJCi300.loopInButton = function(_channel, _control, value, _status, group) { + const deckObject = DJCi300._deckObjectFromGroup(group); + + if (value) { + // Override the active slicer if it exists + deckObject.slicerPad.forEachComponent(function(component) { + component.disconnect(); + }); + + // Create a 4 beat loop + engine.setValue(group, "beatloop_4_activate", 1); + } +}; + +// Loop out button +DJCi300.loopOutButton = function(_channel, _control, value, _status, group) { + const deckObject = DJCi300._deckObjectFromGroup(group); + + if (value) { + // Override the active slicer if it exists + deckObject.slicerPad.forEachComponent(function(component) { + component.disconnect(); + }); + + // Disable the current loop if it exists + if (engine.getValue(group, "loop_enabled") === 1) { engine.setValue(group, "reloop_toggle", 1); } + } +}; + +DJCi300.Deck = function(deckNumber) { + components.Deck.call(this, deckNumber); + + // Slicer/slicer loop pad buttons + this.slicerPad = new components.ComponentContainer(); + // It's easier to keep track of which buttons are pressed as an array instead of having a property for each button + this.slicerPad.pressed = [false, false, false, false, false, false, false, false]; + // For slicer/slicer loop pads + for (const midiOffset of [0x20, 0x60]) { + for (let i = 0; i < 8; i++) { + this.slicerPad[i] = new components.Button({ + midi: [0x95 + deckNumber, midiOffset + i], + connect: function(startPos) { + const group = this.currentDeck; + const samplesBetweenSlices = DJCi300._samplesPerBeat(group) * engine.getValue(group, "beatloop_size") / 8; + // Calculate the start and end points (in samples) for each slice + this.slicerPad[i].startSample = (i === 0) ? startPos : this.slicerPad[i-1].endSample; + this.slicerPad[i].endSample = this.slicerPad[i].startSample + samplesBetweenSlices; + // Everything in the if-statement only needs to be done once (and not 8 times) + // when connected, which is why it is only executed when i === 7 + if (i === 7) { + // Connect callback functions if they are not connected already + if (this.slicerPad.beatConnection === undefined || this.slicerPad.beatConnection.isConnected === false) { + this.slicerPad.beatConnection = engine.makeConnection(group, "beat_distance", this.slicerCountBeat); + // This connection will reinitialize Slicer when the beatloop size spinbox changes + this.slicerPad.sizeConnection = engine.makeConnection(group, "beatloop_size", function() { + const nextStartPos = this.slicerPad[0].startSample; + this.slicerPad.forEachComponent(function(component) { + component.disconnect(); + component.connect(nextStartPos); + }); + }.bind(this)); + // This connection will remove Slicer when a new track is loaded + this.slicerPad.loadConnection = engine.makeConnection(group, "LoadSelectedTrack", function() { + this.slicerPad.forEachComponent(function(component) { + component.disconnect(); + }); + }.bind(this)); + } + // Set loop position indicators to the start and end of the Slicer section as visual feedback + engine.setValue(group, "loop_start_position", this.slicerPad[0].startSample); + engine.setValue(group, "loop_end_position", this.slicerPad[7].endSample); + if (DJCi300.padMode[group] === DJCi300.padModeSlicer) { + if (engine.getValue(group, "loop_enabled") === 1) { engine.setValue(group, "reloop_toggle", 1); } + } else if (DJCi300.padMode[group] === DJCi300.padModeSlicerloop) { + if (engine.getValue(group, "loop_enabled") === 0) { engine.setValue(group, "reloop_toggle", 1); } + } + }; + }.bind(this), + disconnect: function() { + // Set start and end points of each slice to placeholder value + this.slicerPad[i].startSample = -1; + this.slicerPad[i].endSample = -1; + // Much like before, everything in the if-statement only needs to be done once (not 8 times) + if (i === 0) { + const group = this.currentDeck; + // Disconnect slicer if it is connected + if (this.slicerPad.beatConnection !== undefined && this.slicerPad.beatConnection.isConnected === true) { + this.slicerPad.beatConnection.disconnect(); + this.slicerPad.sizeConnection.disconnect(); + this.slicerPad.loadConnection.disconnect(); + this.slicerPad.beat = -1; + this.slicerUpdateLED(group); + } + // Make loop position indicators disappear as visual feedback + engine.setValue(group, "loop_start_position", -1); + engine.setValue(group, "loop_end_position", -1); + } + }.bind(this), + input: function(_channel, control, value, _status, group) { + const button = control % 0x20; + + // Update array. 1 for on, 0 for off + if (value) { + this.slicerPad.pressed[button] = true; + } else { + this.slicerPad.pressed[button] = false; + } + + const startPad = this.slicerPad.pressed.indexOf(true); + const endPad = this.slicerPad.pressed.lastIndexOf(true); + + // If the slicer points are uninitialized, then do nothing. Otherwise: + if (this.slicerPad[0].startSample !== -1) { + // If at least one button is pressed, create a loop between those points + if (startPad !== -1) { + engine.setValue(group, "loop_start_position", this.slicerPad[startPad].startSample); + engine.setValue(group, "loop_end_position", this.slicerPad[endPad].endSample); + engine.setValue(group, "loop_in_goto", 1); + // Enable a loop if it doesn't already exist + if (engine.getValue(group, "loop_enabled") === 0) { + engine.setValue(group, "reloop_toggle", 1); + } + // If no buttons are pressed, reset the loop + } else { + engine.setValue(group, "loop_start_position", this.slicerPad[0].startSample); + engine.setValue(group, "loop_end_position", this.slicerPad[7].endSample); + + // Disable the loop (if we're not in slicer loop mode) + if (DJCi300.padMode[group] === DJCi300.padModeSlicer) { + if (engine.getValue(group, "loop_enabled") === 1) { engine.setValue(group, "reloop_toggle", 1); } + } else if (DJCi300.padMode[group] === DJCi300.padModeSlicerloop) { + if (engine.getValue(group, "loop_enabled") === 0) { engine.setValue(group, "reloop_toggle", 1); } + } + } + this.slicerUpdateLED(group); + } + }.bind(this), + }); + } + } + // This function will count beats and move the Slicer section forward when needed + // It also lights up LEDs corresponding to the beat + this.slicerCountBeat = function(_value, group, _control) { + // Calculate current position in samples + const currentPos = engine.getValue(group, "track_samples") * engine.getValue(group, "playposition"); + // Calculate beat + let beat = 0; + for (let i = 0; i < 8; i++) { + beat = (currentPos >= this.slicerPad[i].endSample) ? (beat + 1) : beat; + } + + // If the beat count has changed, update the object property's value + if (this.slicerPad.beat !== beat) { + this.slicerPad.beat = beat; + // Only send an LED update if no pads are currently held down (pressed pad LEDs are handled above) + if (!this.slicerPad.pressed.includes(true)) { this.slicerUpdateLED(group); } + }; + + // If in slicer mode (not slicer loop mode), check to see if the slicer section needs to be moved + if (DJCi300.padMode[group] === DJCi300.padModeSlicer) { + + // If slicerBeat is 8, move the slicer section forward + if (beat > 7) { + const nextStartPos = this.slicerPad[7].endSample; + this.slicerPad.forEachComponent(function(component) { + component.disconnect(); + component.connect(nextStartPos); + }); + } + } + }.bind(this); + this.slicerUpdateLED = function(group) { + const offset = (DJCi300.padMode[group] === DJCi300.padModeSlicer) ? 0x20 : 0x60; + const status = (group === "[Channel1]") ? 0x96 : 0x97; + + const startPad = this.slicerPad.pressed.indexOf(true); + const endPad = this.slicerPad.pressed.lastIndexOf(true); + + // Turn off all LEDs + for (let i = 0; i < 8; i++) { + midi.sendShortMsg(status, offset + i, 0x00); + } + // If the slicer points are uninitialized, then do nothing. Otherwise: + if (this.slicerPad[0].startSample !== -1) { + // If at least 1 button is held down, light that up + // Or in the case of 2+ buttons, light up everything between the outer 2 buttons + if (startPad !== -1) { + for (let i = startPad; i <= endPad; i++) { + midi.sendShortMsg(status, offset + i, 0x7F); + } + // Otherwise, light up the LED corresponding to the beat + } else { + midi.sendShortMsg(status, offset + Math.min(this.slicerPad.beat, 7), 0x7F); + } + } + }; }; +DJCi300.Deck.prototype = new components.Deck(); DJCi300.shutdown = function() { midi.sendShortMsg(0xB0, 0x7F, 0x00); diff --git a/res/controllers/Hercules_DJControl_Inpulse_300.midi.xml b/res/controllers/Hercules_DJControl_Inpulse_300.midi.xml index 464a320c8e6..c1c0a58cdd6 100644 --- a/res/controllers/Hercules_DJControl_Inpulse_300.midi.xml +++ b/res/controllers/Hercules_DJControl_Inpulse_300.midi.xml @@ -1,11 +1,11 @@ - + Hercules DJControl Inpulse 300 - DJ Phatso for Hercules Technical Support - MIDI Preset for Hercules DJControl Inpulse 300 - https://www.mixxx.org/wiki/doku.php/hercules_djcontrol_inpulse_300 - https://www.mixxx.org/forums/viewtopic.php?f=7&t=12599 + DJ Phatso for Hercules Technical Support + MIDI Preset for Hercules DJControl Inpulse 300 + https://www.mixxx.org/wiki/doku.php/hercules_djcontrol_inpulse_300 + https://www.mixxx.org/forums/viewtopic.php?f=7&t=12599 @@ -156,22 +156,22 @@ [Channel1] - beatloop_4_activate + DJCi300.loopInButton Loop In button 0x91 0x09 - + [Channel1] - reloop_toggle + DJCi300.loopOutButton Loop Out button 0x91 0x0A - + @@ -187,6 +187,88 @@ + + + [Channel1] + DJCi300.changeMode + Hot cue mode + 0x91 + 0x0F + + + + + + [Channel1] + DJCi300.changeMode + Roll mode + 0x91 + 0x10 + + + + + + [Channel1] + DJCi300.changeMode + Slicer mode + 0x91 + 0x11 + + + + + + [Channel1] + DJCi300.changeMode + Sampler mode + 0x91 + 0x12 + + + + + + [Channel1] + DJCi300.changeMode + Toneplay mode + 0x91 + 0x13 + + + + + + [Channel1] + DJCi300.changeMode + FX mode + 0x91 + 0x14 + + + + + + [Channel1] + DJCi300.changeMode + Slicer loop mode + 0x91 + 0x15 + + + + + + [Channel1] + DJCi300.changeMode + Beatjump mode + 0x91 + 0x16 + + + + + @@ -276,6 +358,7 @@ + [Channel2] @@ -287,6 +370,7 @@ + [Channel2] @@ -298,29 +382,30 @@ + [Channel2] - beatloop_4_activate + DJCi300.loopInButton Loop In button 0x92 0x09 - + [Channel2] - reloop_toggle + DJCi300.loopOutButton Loop Out button 0x92 0x0A - + - + [EffectRack1_EffectUnit2_Effect3] enabled @@ -332,6 +417,88 @@ + + + [Channel2] + DJCi300.changeMode + Hot cue mode + 0x92 + 0x0F + + + + + + [Channel2] + DJCi300.changeMode + Roll mode + 0x92 + 0x10 + + + + + + [Channel2] + DJCi300.changeMode + Slicer mode + 0x92 + 0x11 + + + + + + [Channel2] + DJCi300.changeMode + Sampler mode + 0x92 + 0x12 + + + + + + [Channel2] + DJCi300.changeMode + Toneplay mode + 0x92 + 0x13 + + + + + + [Channel2] + DJCi300.changeMode + FX mode + 0x92 + 0x14 + + + + + + [Channel2] + DJCi300.changeMode + Slicer loop mode + 0x92 + 0x15 + + + + + + [Channel2] + DJCi300.changeMode + Beatjump mode + 0x92 + 0x16 + + + + + @@ -485,7 +652,7 @@ [Channel2] loop_double - SHIFT + Loop Ou: Loop Double + SHIFT + Loop Out: Loop Double 0x95 0x0A @@ -992,589 +1159,1387 @@ - - - - - - - + - [Channel1] - beatjump_1_backward - PAD 1 + [Sampler1] + cue_gotoandstop + SHIFT + PAD 1 0x96 - 0x70 + 0x38 - [Channel1] - beatjump_1_forward - PAD 2 + [Sampler2] + cue_gotoandstop + SHIFT + PAD 2 0x96 - 0x71 + 0x39 - [Channel1] - beatjump_2_backward - PAD 3 + [Sampler3] + cue_gotoandstop + SHIFT + PAD 3 0x96 - 0x72 + 0x3A - [Channel1] - beatjump_2_forward - PAD 4 + [Sampler4] + cue_gotoandstop + SHIFT + PAD 4 0x96 - 0x73 + 0x3B - [Channel1] - beatjump_4_backward - PAD 5 + [Sampler5] + cue_gotoandstop + SHIFT + PAD 5 0x96 - 0x74 + 0x3C - [Channel1] - beatjump_4_forward - PAD 6 + [Sampler6] + cue_gotoandstop + SHIFT + PAD 6 0x96 - 0x75 + 0x3D - [Channel1] - beatjump_8_backward - PAD 7 + [Sampler7] + cue_gotoandstop + SHIFT + PAD 7 0x96 - 0x76 + 0x3E - [Channel1] - beatjump_8_forward - PAD 8 + [Sampler8] + cue_gotoandstop + SHIFT + PAD 8 0x96 - 0x77 + 0x3F - - - - - + - [Channel2] - hotcue_1_activate + [Channel1] + DJCi300.deck[0].slicerPad[0].input PAD 1 - 0x97 - 0x00 + 0x96 + 0x20 - + - [Channel2] - hotcue_2_activate + [Channel1] + DJCi300.deck[0].slicerPad[1].input PAD 2 - 0x97 - 0x01 + 0x96 + 0x21 - + - [Channel2] - hotcue_3_activate + [Channel1] + DJCi300.deck[0].slicerPad[2].input PAD 3 - 0x97 - 0x02 + 0x96 + 0x22 - + - [Channel2] - hotcue_4_activate + [Channel1] + DJCi300.deck[0].slicerPad[3].input PAD 4 - 0x97 - 0x03 + 0x96 + 0x23 - + - [Channel2] - hotcue_5_activate + [Channel1] + DJCi300.deck[0].slicerPad[4].input PAD 5 - 0x97 - 0x04 + 0x96 + 0x24 - + - [Channel2] - hotcue_6_activate + [Channel1] + DJCi300.deck[0].slicerPad[5].input PAD 6 - 0x97 - 0x05 + 0x96 + 0x25 - + - [Channel2] - hotcue_7_activate + [Channel1] + DJCi300.deck[0].slicerPad[6].input PAD 7 - 0x97 - 0x06 + 0x96 + 0x26 - + - [Channel2] - hotcue_8_activate + [Channel1] + DJCi300.deck[0].slicerPad[7].input PAD 8 - 0x97 - 0x07 + 0x96 + 0x27 - + - + - [Channel2] - hotcue_1_clear + [Channel1] + DJCi300.toneplay PAD 1 - 0x97 - 0x08 + 0x96 + 0x40 + + + + + + [Channel1] + DJCi300.toneplay + PAD 2 + 0x96 + 0x41 + + + + + + [Channel1] + DJCi300.toneplay + PAD 3 + 0x96 + 0x42 + + + + + + [Channel1] + DJCi300.toneplay + PAD 4 + 0x96 + 0x43 + + + + + + [Channel1] + DJCi300.toneplay + PAD 5 + 0x96 + 0x44 + + + + + + [Channel1] + DJCi300.toneplay + PAD 6 + 0x96 + 0x45 + + + + + + [Channel1] + DJCi300.toneplay + PAD 7 + 0x96 + 0x46 + + + + + + [Channel1] + DJCi300.toneplay + PAD 8 + 0x96 + 0x47 + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 1 + 0x96 + 0x48 + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 2 + 0x96 + 0x49 + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 3 + 0x96 + 0x4A + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 4 + 0x96 + 0x4B + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 5 + 0x96 + 0x4C + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 6 + 0x96 + 0x4D + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 7 + 0x96 + 0x4E + + + + + + [Channel1] + DJCi300.toneplay + SHIFT + PAD 8 + 0x96 + 0x4F + + + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[0].input + PAD 1 + 0x96 + 0x60 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[1].input + PAD 2 + 0x96 + 0x61 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[2].input + PAD 3 + 0x96 + 0x62 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[3].input + PAD 4 + 0x96 + 0x63 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[4].input + PAD 5 + 0x96 + 0x64 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[5].input + PAD 6 + 0x96 + 0x65 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[6].input + PAD 7 + 0x96 + 0x66 + + + + + + [Channel1] + DJCi300.deck[0].slicerPad[7].input + PAD 8 + 0x96 + 0x67 + + + + + + + [Channel1] + beatjump_1_backward + PAD 1 + 0x96 + 0x70 + + + + + + [Channel1] + beatjump_1_forward + PAD 2 + 0x96 + 0x71 + + + + + + [Channel1] + beatjump_2_backward + PAD 3 + 0x96 + 0x72 + + + + + + [Channel1] + beatjump_2_forward + PAD 4 + 0x96 + 0x73 + + + + + + [Channel1] + beatjump_4_backward + PAD 5 + 0x96 + 0x74 + + + + + + [Channel1] + beatjump_4_forward + PAD 6 + 0x96 + 0x75 + + + + + + [Channel1] + beatjump_8_backward + PAD 7 + 0x96 + 0x76 + + + + + + [Channel1] + beatjump_8_forward + PAD 8 + 0x96 + 0x77 + + + + + + + + + + + [Channel2] + hotcue_1_activate + PAD 1 + 0x97 + 0x00 + + + + + + [Channel2] + hotcue_2_activate + PAD 2 + 0x97 + 0x01 + + + + + + [Channel2] + hotcue_3_activate + PAD 3 + 0x97 + 0x02 + + + + + + [Channel2] + hotcue_4_activate + PAD 4 + 0x97 + 0x03 + + + + + + [Channel2] + hotcue_5_activate + PAD 5 + 0x97 + 0x04 + + + + + + [Channel2] + hotcue_6_activate + PAD 6 + 0x97 + 0x05 + + + + + + [Channel2] + hotcue_7_activate + PAD 7 + 0x97 + 0x06 + + + + + + [Channel2] + hotcue_8_activate + PAD 8 + 0x97 + 0x07 + + + + + + + [Channel2] + hotcue_1_clear + PAD 1 + 0x97 + 0x08 + + + + + + [Channel2] + hotcue_2_clear + PAD 2 + 0x97 + 0x09 + + + + + + [Channel2] + hotcue_3_clear + PAD 3 + 0x97 + 0x0A + + + + + + [Channel2] + hotcue_4_clear + PAD 4 + 0x97 + 0x0B + + + + + + [Channel2] + hotcue_5_clear + PAD 5 + 0x97 + 0x0C + + + + + + [Channel2] + hotcue_6_clear + PAD 6 + 0x97 + 0x0D + + + + + + [Channel2] + hotcue_7_clear + PAD 7 + 0x97 + 0x0E + + + + + + [Channel2] + hotcue_8_clear + PAD 8 + 0x97 + 0x0F + + + + + + + [EffectRack1_EffectUnit2_Effect1] + enabled + FX Unit 2 - Slot 1 On/Off + 0x97 + 0x50 + + + + + + [EffectRack1_EffectUnit2_Effect2] + enabled + FX Unit 2 - Slot 2 On/Off + 0x97 + 0x51 + + + + + + [EffectRack1_EffectUnit2_Effect3] + enabled + FX Unit 2 - Slot 3 On/Off + 0x97 + 0x52 + + + + + + [EffectRack1_EffectUnit1] + group_[Channel2]_enable + FX Unit 1 On/Off - Deck B + 0x97 + 0x53 + + + + + + [EffectRack1_EffectUnit2_Effect1] + next_effect + FX Unit 2 - Slot 1 next effect + 0x97 + 0x54 + + + + + + [EffectRack1_EffectUnit2_Effect2] + next_effect + FX Unit 2 - Slot 2 next effect + 0x97 + 0x55 + + + + + + [EffectRack1_EffectUnit2_Effect3] + next_effect + FX Unit 2 - Slot 3 next effect + 0x97 + 0x56 + + + + + + [EffectRack1_EffectUnit2] + group_[Channel2]_enable + FX Unit 2 On/Off - Deck B + 0x97 + 0x57 + + + + + + + [EffectRack1_EffectUnit4_Effect1] + enabled + FX Unit 4 - Slot 1 On/Off + 0x97 + 0x58 + + + + + + [EffectRack1_EffectUnit4_Effect2] + enabled + FX Unit 4 - Slot 2 On/Off + 0x97 + 0x59 + + + + + + [EffectRack1_EffectUnit4_Effect3] + enabled + FX Unit 4 - Slot 3 On/Off + 0x97 + 0x5A + + + + + + [EffectRack1_EffectUnit3] + group_[Channel2]_enable + FX Unit 3 On/Off - Deck B + 0x97 + 0x5B + + + + + + [EffectRack1_EffectUnit4_Effect1] + next_effect + FX Unit 4 - Slot 1 next effect + 0x97 + 0x5C + + + + + + [EffectRack1_EffectUnit4_Effect2] + next_effect + FX Unit 4 - Slot 2 next effect + 0x97 + 0x5D + + + + + + [EffectRack1_EffectUnit4_Effect3] + next_effect + FX Unit 4 - Slot 3 next effect + 0x97 + 0x5E + + + + + + [EffectRack1_EffectUnit4] + group_[Channel2]_enable + FX Unit 4 On/Off - Deck B + 0x97 + 0x5F + + + + + + + [Channel2] + beatlooproll_0.125_activate + Loop 1/8 Beat (Pad 1) + 0x97 + 0x10 + + + + + + [Channel2] + beatlooproll_0.25_activate + Loop 1/4 Beat (Pad 2) + 0x97 + 0x11 + + + + + + [Channel2] + beatlooproll_0.5_activate + Loop 1/2 Beat (Pad 3) + 0x97 + 0x12 [Channel2] - hotcue_2_clear - PAD 2 + beatlooproll_1_activate + Loop 1 Beat (Pad 4) 0x97 - 0x09 + 0x13 [Channel2] - hotcue_3_clear - PAD 3 + beatlooproll_2_activate + Loop 2 Beat (Pad 5) 0x97 - 0x0A + 0x14 [Channel2] - hotcue_4_clear - PAD 4 + beatlooproll_4_activate + Loop 4 Beat (Pad 6) 0x97 - 0x0B + 0x15 [Channel2] - hotcue_5_clear - PAD 5 + beatlooproll_8_activate + Loop 8 Beat (Pad 7) 0x97 - 0x0C + 0x16 [Channel2] - hotcue_6_clear - PAD 6 + beatlooproll_16_activate + Loop 16 Beat (Pad 8) 0x97 - 0x0D + 0x17 + - [Channel2] - hotcue_7_clear - PAD 7 + [Sampler9] + cue_gotoandplay + PAD 1 0x97 - 0x0E + 0x30 - [Channel2] - hotcue_8_clear - PAD 8 + [Sampler10] + cue_gotoandplay + PAD 2 0x97 - 0x0F + 0x31 - - [EffectRack1_EffectUnit2_Effect1] - enabled - FX Unit 2 - Slot 1 On/Off + [Sampler11] + cue_gotoandplay + PAD 3 0x97 - 0x50 + 0x32 - [EffectRack1_EffectUnit2_Effect2] - enabled - FX Unit 2 - Slot 2 On/Off + [Sampler12] + cue_gotoandplay + PAD 1 0x97 - 0x51 + 0x33 - [EffectRack1_EffectUnit2_Effect3] - enabled - FX Unit 2 - Slot 3 On/Off + [Sampler13] + cue_gotoandplay + PAD 5 0x97 - 0x52 + 0x34 - [EffectRack1_EffectUnit1] - group_[Channel2]_enable - FX Unit 1 On/Off - Deck B + [Sampler14] + cue_gotoandplay + PAD 6 0x97 - 0x53 + 0x35 - [EffectRack1_EffectUnit2_Effect1] - next_effect - FX Unit 2 - Slot 1 next effect + [Sampler15] + cue_gotoandplay + PAD 7 0x97 - 0x54 + 0x36 - [EffectRack1_EffectUnit2_Effect2] - next_effect - FX Unit 2 - Slot 2 next effect + [Sampler16] + cue_gotoandplay + PAD 8 0x97 - 0x55 + 0x37 + - [EffectRack1_EffectUnit2_Effect3] - next_effect - FX Unit 2 - Slot 3 next effect + [Sampler9] + cue_gotoandstop + SHIFT + PAD 1 0x97 - 0x56 + 0x38 - [EffectRack1_EffectUnit2] - group_[Channel2]_enable - FX Unit 2 On/Off - Deck B + [Sampler10] + cue_gotoandstop + SHIFT + PAD 2 0x97 - 0x57 + 0x39 - - [EffectRack1_EffectUnit4_Effect1] - enabled - FX Unit 4 - Slot 1 On/Off + [Sampler11] + cue_gotoandstop + SHIFT + PAD 3 0x97 - 0x58 + 0x3A - [EffectRack1_EffectUnit4_Effect2] - enabled - FX Unit 4 - Slot 2 On/Off + [Sampler12] + cue_gotoandstop + SHIFT + PAD 4 0x97 - 0x59 + 0x3B - [EffectRack1_EffectUnit4_Effect3] - enabled - FX Unit 4 - Slot 3 On/Off + [Sampler13] + cue_gotoandstop + SHIFT + PAD 5 0x97 - 0x5A + 0x3C - [EffectRack1_EffectUnit3] - group_[Channel2]_enable - FX Unit 3 On/Off - Deck B + [Sampler14] + cue_gotoandstop + SHIFT + PAD 6 0x97 - 0x5B + 0x3D - [EffectRack1_EffectUnit4_Effect1] - next_effect - FX Unit 4 - Slot 1 next effect + [Sampler15] + cue_gotoandstop + SHIFT + PAD 7 0x97 - 0x5C + 0x3E - [EffectRack1_EffectUnit4_Effect2] - next_effect - FX Unit 4 - Slot 2 next effect + [Sampler16] + cue_gotoandstop + SHIFT + PAD 8 0x97 - 0x5D + 0x3F + - [EffectRack1_EffectUnit4_Effect3] - next_effect - FX Unit 4 - Slot 3 next effect + [Channel2] + DJCi300.deck[1].slicerPad[0].input + PAD 1 0x97 - 0x5E + 0x20 - + - [EffectRack1_EffectUnit4] - group_[Channel2]_enable - FX Unit 4 On/Off - Deck B + [Channel2] + DJCi300.deck[1].slicerPad[1].input + PAD 2 0x97 - 0x5F + 0x21 - + - [Channel2] - beatlooproll_0.125_activate - Loop 1/8 Beat (Pad 1) + DJCi300.deck[1].slicerPad[2].input + PAD 3 0x97 - 0x10 + 0x22 - + [Channel2] - beatlooproll_0.25_activate - Loop 1/4 Beat (Pad 2) + DJCi300.deck[1].slicerPad[3].input + PAD 4 + 0x97 + 0x23 + + + + + + [Channel2] + DJCi300.deck[1].slicerPad[4].input + PAD 5 + 0x97 + 0x24 + + + + + + [Channel2] + DJCi300.deck[1].slicerPad[5].input + PAD 6 + 0x97 + 0x25 + + + + + + [Channel2] + DJCi300.deck[1].slicerPad[6].input + PAD 7 + 0x97 + 0x26 + + + + + + [Channel2] + DJCi300.deck[1].slicerPad[7].input + PAD 8 + 0x97 + 0x27 + + + + + + + [Channel2] + DJCi300.toneplay + PAD 1 + 0x97 + 0x40 + + + + + + [Channel2] + DJCi300.toneplay + PAD 2 + 0x97 + 0x41 + + + + + + [Channel2] + DJCi300.toneplay + PAD 3 + 0x97 + 0x42 + + + + + + [Channel2] + DJCi300.toneplay + PAD 4 + 0x97 + 0x43 + + + + + + [Channel2] + DJCi300.toneplay + PAD 5 + 0x97 + 0x44 + + + + + + [Channel2] + DJCi300.toneplay + PAD 6 + 0x97 + 0x45 + + + + + + [Channel2] + DJCi300.toneplay + PAD 7 + 0x97 + 0x46 + + + + + + [Channel2] + DJCi300.toneplay + PAD 8 + 0x97 + 0x47 + + + + + + [Channel2] + DJCi300.toneplay + SHIFT + PAD 1 + 0x97 + 0x48 + + + + + + [Channel2] + DJCi300.toneplay + SHIFT + PAD 2 0x97 - 0x11 + 0x49 - + [Channel2] - beatlooproll_0.5_activate - Loop 1/2 Beat (Pad 3) + DJCi300.toneplay + SHIFT + PAD 3 0x97 - 0x12 + 0x4A - + [Channel2] - beatlooproll_1_activate - Loop 1 Beat (Pad 4) + DJCi300.toneplay + SHIFT + PAD 4 0x97 - 0x13 + 0x4B - + [Channel2] - beatlooproll_2_activate - Loop 2 Beat (Pad 5) + DJCi300.toneplay + SHIFT + PAD 5 0x97 - 0x14 + 0x4C - + [Channel2] - beatlooproll_4_activate - Loop 4 Beat (Pad 6) + DJCi300.toneplay + SHIFT + PAD 6 0x97 - 0x15 + 0x4D - + [Channel2] - beatlooproll_8_activate - Loop 8 Beat (Pad 7) + DJCi300.toneplay + SHIFT + PAD 7 0x97 - 0x16 + 0x4E - + [Channel2] - beatlooproll_16_activate - Loop 16 Beat (Pad 8) + DJCi300.toneplay + SHIFT + PAD 8 0x97 - 0x17 + 0x4F - + - + + - [Sampler9] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[0].input PAD 1 0x97 - 0x30 + 0x60 - + - [Sampler10] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[1].input PAD 2 0x97 - 0x31 + 0x61 - + - [Sampler11] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[2].input PAD 3 0x97 - 0x32 + 0x62 - + - [Sampler12] - cue_gotoandplay - PAD 1 + [Channel2] + DJCi300.deck[1].slicerPad[3].input + PAD 4 0x97 - 0x33 + 0x63 - + - [Sampler13] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[4].input PAD 5 0x97 - 0x34 + 0x64 - + - [Sampler14] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[5].input PAD 6 0x97 - 0x35 + 0x65 - + - [Sampler15] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[6].input PAD 7 0x97 - 0x36 + 0x66 - + - [Sampler16] - cue_gotoandplay + [Channel2] + DJCi300.deck[1].slicerPad[7].input PAD 8 0x97 - 0x37 + 0x67 - + - - - - - - [Channel2] @@ -1784,7 +2749,7 @@ [Channel1] - DJCi300.scratchWheel + DJCi300.jogWheel Scratch Deck A (Jog-Wheel) 0xB1 0x0A @@ -1794,7 +2759,7 @@ [Channel1] - DJCi300.bendWheel + DJCi300.jogWheel Pitch Bend Deck A (Jog-Wheel) 0xB1 0x09 @@ -1802,16 +2767,6 @@ - - [Channel1] - DJCi300.scratchPad - Pitch Bend Deck A (FX PAD 7 / 8 ) - 0xB1 - 0x0C - - - - [EffectRack1_EffectUnit1_Effect3] @@ -1836,7 +2791,6 @@ - [Channel2] @@ -1923,8 +2877,8 @@ [Channel2] - DJCi300.scratchWheel - Pitch Bend Deck B (Jog-Wheel) + DJCi300.jogWheel + Scratch Deck B (Jog-Wheel) 0xB2 0x0A @@ -1933,7 +2887,7 @@ [Channel2] - DJCi300.bendWheel + DJCi300.jogWheel Pitch Bend Deck B (Jog-Wheel) 0xB2 0x09 @@ -1941,16 +2895,6 @@ - - [Channel2] - DJCi300.scratchPad - Pitch Bend Deck B (FX PAD 7 / 8 ) - 0xB2 - 0x0C - - - - @@ -1978,7 +2922,17 @@ [Channel1] - DJCi300.scratchWheel + DJCi300.jogWheel + SHIFT + Bend Deck A (Jog-Wheel) + 0xB4 + 0x09 + + + + + + [Channel1] + DJCi300.jogWheel SHIFT + Scratch Deck A (Jog-Wheel) 0xB4 0x0A @@ -1991,7 +2945,17 @@ [Channel2] - DJCi300.scratchWheel + DJCi300.jogWheel + SHIFT + Bend Deck B (Jog-Wheel) + 0xB5 + 0x09 + + + + + + [Channel2] + DJCi300.jogWheel SHIFT + Scratch Deck B (Jog-Wheel) 0xB5 0x0A @@ -2251,340 +3215,327 @@ 0x7f 0x0 - - - [Channel1] - end_of_track - Auto DJ On - 0.5 - 1 - 0x91 - 0x1C - 0x7f - 0x0 - - - [Channel1] - end_of_track - Auto DJ On - 0.5 - 1 - 0x91 - 0x1D - 0x7f - 0x0 - - - [Channel2] - end_of_track - Auto DJ On - 0.5 - 1 - 0x92 - 0x1C - 0x7f - 0x0 - - - [Channel2] - end_of_track - Auto DJ On - 0.5 - 1 - 0x92 - 0x1D - 0x7f - 0x0 - [Channel1] - hotcue_1_enabled + hotcue_1_status Hotcue 1 (Pad 1) 0x96 0x00 0x7E 0.5 + 2 [Channel1] - hotcue_2_enabled + hotcue_2_status Hotcue 2 (Pad 2) 0x96 0x01 0x7E 0.5 + 2 [Channel1] - hotcue_3_enabled + hotcue_3_status Hotcue 3 (Pad 3) 0x96 0x02 0x7E 0.5 + 2 [Channel1] - hotcue_4_enabled + hotcue_4_status Hotcue 4 (Pad 4) 0x96 0x03 0x7E 0.5 + 2 [Channel1] - hotcue_5_enabled + hotcue_5_status Hotcue 5 (Pad 5) 0x96 0x04 0x7E 0.5 + 2 [Channel1] - hotcue_6_enabled + hotcue_6_status Hotcue 6 (Pad 6) 0x96 0x05 0x7E 0.5 + 2 [Channel1] - hotcue_7_enabled + hotcue_7_status Hotcue 7 (Pad 7) 0x96 0x06 0x7E 0.5 + 2 [Channel1] - hotcue_8_enabled + hotcue_8_status Hotcue 8 (Pad 8) 0x96 0x07 0x7E 0.5 + 2 [Channel2] - hotcue_1_enabled + hotcue_1_status Hotcue 1 (Pad 1) 0x97 0x00 0x7E 0.5 + 2 [Channel2] - hotcue_2_enabled + hotcue_2_status Hotcue 2 (Pad 2) 0x97 0x01 0x7E 0.5 + 2 [Channel2] - hotcue_3_enabled + hotcue_3_status Hotcue 3 (Pad 3) 0x97 0x02 0x7E 0.5 + 2 [Channel2] - hotcue_4_enabled + hotcue_4_status Hotcue 4 (Pad 4) 0x97 0x03 0x7E 0.5 + 2 [Channel2] - hotcue_5_enabled + hotcue_5_status Hotcue 5 (Pad 5) 0x97 0x04 0x7E 0.5 + 2 [Channel2] - hotcue_6_enabled + hotcue_6_status Hotcue 6 (Pad 6) 0x97 0x05 0x7E 0.5 + 2 [Channel2] - hotcue_7_enabled + hotcue_7_status Hotcue 7 (Pad 7) 0x97 0x06 0x7E 0.5 + 2 [Channel2] - hotcue_8_enabled + hotcue_8_status Hotcue 8 (Pad 8) 0x97 0x07 0x7E 0.5 + 2 [Channel1] - hotcue_1_enabled + hotcue_1_status Hotcue 1 (Pad 1) 0x96 0x08 0x7E 0.5 + 2 [Channel1] - hotcue_2_enabled + hotcue_2_status Hotcue 2 (Pad 2) 0x96 0x09 0x7E 0.5 + 2 [Channel1] - hotcue_3_enabled + hotcue_3_status Hotcue 3 (Pad 3) 0x96 0x0A 0x7E 0.5 + 2 [Channel1] - hotcue_4_enabled + hotcue_4_status Hotcue 4 (Pad 4) 0x96 0x0B 0x7E 0.5 + 2 [Channel1] - hotcue_5_enabled + hotcue_5_status Hotcue 5 (Pad 5) 0x96 0x0C 0x7E 0.5 + 2 [Channel1] - hotcue_6_enabled + hotcue_6_status Hotcue 6 (Pad 6) 0x96 0x0D 0x7E 0.5 + 2 [Channel1] - hotcue_7_enabled + hotcue_7_status Hotcue 7 (Pad 7) 0x96 0x0E 0x7E 0.5 + 2 [Channel1] - hotcue_8_enabled + hotcue_8_status Hotcue 8 (Pad 8) 0x96 0x0F 0x7E 0.5 + 2 [Channel2] - hotcue_1_enabled + hotcue_1_status Hotcue 1 (Pad 1) 0x97 0x08 0x7E 0.5 + 2 [Channel2] - hotcue_2_enabled + hotcue_2_status Hotcue 2 (Pad 2) 0x97 0x09 0x7E 0.5 + 2 [Channel2] - hotcue_3_enabled + hotcue_3_status Hotcue 3 (Pad 3) 0x97 0x0A 0x7E 0.5 + 2 [Channel2] - hotcue_4_enabled + hotcue_4_status Hotcue 4 (Pad 4) 0x97 0x0B 0x7E 0.5 + 2 [Channel2] - hotcue_5_enabled + hotcue_5_status Hotcue 5 (Pad 5) 0x97 0x0C 0x7E 0.5 + 2 [Channel2] - hotcue_6_enabled + hotcue_6_status Hotcue 6 (Pad 6) 0x97 0x0D 0x7E 0.5 + 2 [Channel2] - hotcue_7_enabled + hotcue_7_status Hotcue 7 (Pad 7) 0x97 0x0E 0x7E 0.5 + 2 [Channel2] - hotcue_8_enabled + hotcue_8_status Hotcue 8 (Pad 8) 0x97 0x0F 0x7E 0.5 + 2 @@ -2803,7 +3754,7 @@ 0x7f - [EffectRack1_EffectUnit1] + [EffectRack1_EffectUnit2] group_[Channel1]_enable FX2 is active on Deck A 0.5 @@ -3197,6 +4148,150 @@ 0x37 0x7F 0.5 + + + [Sampler1] + play_indicator + (SHIFT + Pad 1 DECK A) + 0x96 + 0x38 + 0x7F + 0.5 + + + [Sampler9] + play_indicator + (SHIFT + Pad 1 DECK B) + 0x97 + 0x38 + 0x7F + 0.5 + + + [Sampler2] + play_indicator + (SHIFT + Pad 2 DECK A) + 0x96 + 0x39 + 0x7F + 0.5 + + + [Sampler10] + play_indicator + (SHIFT + Pad 2 DECK B) + 0x97 + 0x39 + 0x7F + 0.5 + + + [Sampler3] + play_indicator + (SHIFT + Pad 3 DECK A) + 0x96 + 0x3A + 0x7F + 0.5 + + + [Sampler11] + play_indicator + (SHIFT + Pad 3 DECK B) + 0x97 + 0x3A + 0x7F + 0.5 + + + [Sampler4] + play_indicator + (SHIFT + Pad 4 DECK A) + 0x96 + 0x3B + 0x7F + 0.5 + + + [Sampler12] + play_indicator + (SHIFT + Pad 4 DECK B) + 0x97 + 0x3B + 0x7F + 0.5 + + + [Sampler5] + play_indicator + (SHIFT + Pad 5 DECK A) + 0x96 + 0x3C + 0x7F + 0.5 + + + [Sampler13] + play_indicator + (SHIFT + Pad 5 DECK B) + 0x97 + 0x3C + 0x7F + 0.5 + + + [Sampler6] + play_indicator + (SHIFT + Pad 6 DECK A) + 0x96 + 0x3D + 0x7F + 0.5 + + + [Sampler14] + play_indicator + (SHIFT + Pad 6 DECK B) + 0x97 + 0x3D + 0x7F + 0.5 + + + [Sampler7] + play_indicator + (SHIFT + Pad 7 DECK A) + 0x96 + 0x3E + 0x7F + 0.5 + + + [Sampler15] + play_indicator + (SHIFT + Pad 7 DECK B) + 0x97 + 0x3E + 0x7F + 0.5 + + + [Sampler8] + play_indicator + (SHIFT + Pad 8 DECK A) + 0x96 + 0x3F + 0x7F + 0.5 + + + [Sampler16] + play_indicator + (SHIFT + Pad 4 DECK B) + 0x97 + 0x3F + 0x7F + 0.5