Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Denon MC7000 improvements #4021

Merged
merged 16 commits into from
Sep 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
180 changes: 113 additions & 67 deletions res/controllers/Denon-MC7000-scripts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Denon DJ MC7000 DJ controller script for Mixxx 2.3
* Denon DJ MC7000 DJ controller script for Mixxx 2.3.1
*
* Started in Dec. 2019 by OsZ
*
Expand Down Expand Up @@ -41,45 +41,52 @@ MC7000.experimental = false;

// Wanna have Needle Search active while playing a track ?
// In any case Needle Search is available holding "SHIFT" down.
// can be true or false (recommended: false)
// can be true or false
MC7000.needleSearchPlay = false;

// select if the previous sampler shall stop before a new sampler starts
// true: a running sampler will stop before the new sampler starts
// false: all triggered samplers will play simultaneously
MC7000.prevSamplerStop = true;

// Set Vinyl Mode on ("true") or off ("false") when MIXXX starts.
// This sets the Jog Wheel touch detection / Vinyl Mode
// and the Jog LEDs ("VINYL" on = spinny, "VINYL" off = track position).
MC7000.VinylModeOn = true;

// Possible pitchfader rate ranges given in percent.
// can be cycled through by the RANGE buttons.
// All default values are the same as selectable in Mixxx Preferences
MC7000.rateRanges = [
4/100, // default: 4/100
6/100, // default: 6/100
8/100, // default: 8/100
10/100, // default: 10/100
16/100, // default: 16/100
24/100, // default: 24/100
4/100,
6/100,
8/100,
10/100,
16/100,
24/100,
50/100,
90/100,
];

// Platter Ring LED mode
// Mode 0 = Single "off" LED chase (all others "on")
// Mode 1 = Single "on" LED chase (all others "off")
// use "SHIFT" + "DECK #" to toggle between both modes
MC7000.modeSingleLED = 1; // default: 1

// Set Vinyl Mode on ("true") or off ("false") when MIXXX starts.
// This sets the Jog Wheel touch detection / Vinyl Mode
// and the Jog LEDs ("VINYL" on = spinny, "VINYL" off = track position).
MC7000.VinylModeOn = true; // default: true
MC7000.modeSingleLED = 1;

// Scratch algorithm parameters
MC7000.scratchParams = {
recordSpeed: 33 + 1/3, // default: 33 + 1/3
alpha: (1.0/10), // default: (1.0/10)
beta: (1.0/10)/32 // default: (1.0/10)/32
recordSpeed: 33 + 1/3,
alpha: (1.0/10),
beta: (1.0/10)/32
};

// Sensitivity factor of the jog wheel (also depends on audio latency)
// 0.5 for half, 2 for double sensitivity - Recommendation:
// set to 0.5 with audio buffer set to 50ms
// set to 1 with audio buffer set to 25ms
// set to 3 with audio buffer set to 5ms

MC7000.jogSensitivity = 1; // default: 1.0 with audio buffer set to 23ms
MC7000.jogSensitivity = 1;

/*/////////////////////////////////
// USER VARIABLES END //
Expand All @@ -99,10 +106,6 @@ MC7000.needleSearchTouched = [true, true, true, true];
// initial value for VINYL mode per Deck (see above for user input)
MC7000.isVinylMode = [MC7000.VinylModeOn, MC7000.VinylModeOn, MC7000.VinylModeOn, MC7000.VinylModeOn];

// used to keep track of which the rateRange of each slider.
// value used as an index to MC7000.rateRanges
MC7000.currentRateRangeIndex = [0, 0, 0, 0];

// initialize the "factor" function for Spinback
MC7000.factor = [];

Expand Down Expand Up @@ -168,10 +171,9 @@ MC7000.init = function() {

var i;

// Softtakeover for Pitch Faders
for (i = 1; i <= 4; i++) {
engine.softTakeover("[Channel" + i + "]", "rate", true);
}
// obtain all knob and slider positions
var ControllerStatusSysex = [0xF0, 0x00, 0x20, 0x7F, 0x03, 0x01, 0xF7];
midi.sendSysexMsg(ControllerStatusSysex, ControllerStatusSysex.length);

// VU meters
engine.makeConnection("[Channel1]", "VuMeter", MC7000.VuMeter);
Expand Down Expand Up @@ -211,14 +213,13 @@ MC7000.init = function() {
engine.makeConnection("[Sampler"+i+"]", "play", MC7000.SamplerLED);
}

// send Controller Status SysEx message delayed to avoid conflicts with Softtakeover
engine.beginTimer(2000, MC7000.delayedSysEx, true);
};

// SysEx message to receive all knob and fader positions
MC7000.delayedSysEx = function() {
var ControllerStatusSysex = [0xF0, 0x00, 0x20, 0x7F, 0x03, 0x01, 0xF7];
midi.sendSysexMsg(ControllerStatusSysex, ControllerStatusSysex.length);
// send Softtakeover delayed to avoid conflicts with ControllerStatusSysex
engine.beginTimer(2000, function() {
toszlanyi marked this conversation as resolved.
Show resolved Hide resolved
// Softtakeover for Pitch Faders only
for (i = 1; i <= 4; i++) {
engine.softTakeover("[Channel" + i + "]", "rate", true);
}
}, true);
};

// Sampler Volume Control
Expand Down Expand Up @@ -496,12 +497,17 @@ MC7000.PadButtons = function(channel, control, value, status, group) {
// activate and clear Hot Cues
if (MC7000.PADModeCue[deckNumber] && engine.getValue(group, "track_loaded") === 1) {
for (i = 1; i <= 8; i++) {
if (control === 0x14 + i - 1 && value >= 0x01) {
if (control === 0x14 + i - 1 && value === 0x7F) {
engine.setValue(group, "hotcue_" + i + "_activate", true);
} else {
} else if (control === 0x14 + i - 1 && value === 0x00) {
engine.setValue(group, "hotcue_" + i + "_activate", false);
}
if (control === 0x1C + i - 1 && value >= 0x01) {
if (engine.getValue(group, "slip_enabled")) {
engine.setValue(group, "slip_enabled", false);
engine.beginTimer(50, function() {
engine.setValue(group, "slip_enabled", true);
}, true);
}
} else if (control === 0x1C + i - 1 && value === 0x7F) {
engine.setValue(group, "hotcue_" + i + "_clear", true);
midi.sendShortMsg(0x94 + deckOffset, 0x1C + i - 1, MC7000.padColor.hotcueoff);
}
Expand Down Expand Up @@ -562,6 +568,13 @@ MC7000.PadButtons = function(channel, control, value, status, group) {
if (engine.getValue("[Sampler" + i + "]", "track_loaded") === 0) {
engine.setValue("[Sampler" + i + "]", "LoadSelectedTrack", 1);
} else if (engine.getValue("[Sampler" + i + "]", "track_loaded") === 1) {
if (MC7000.prevSamplerStop) {
// stop playing all other samplers ...
for (j = 1; j <=8; j++) {
engine.setValue("[Sampler" + j + "]", "cue_gotoandstop", 1);
}
}
// ... before the actual sampler to play gets started
engine.setValue("[Sampler" + i + "]", "cue_gotoandplay", 1);
}
} else if (control === 0x1C + i - 1 && value >= 0x01) {
Expand Down Expand Up @@ -640,14 +653,23 @@ MC7000.loadButton = function(channel, control, value, status, group) {
MC7000.wheelTouch = function(channel, control, value, status, group) {
var deckNumber = script.deckFromGroup(group);
var deckOffset = deckNumber - 1;
if (MC7000.isVinylMode[deckOffset]) {
var libraryMaximized = engine.getValue("[Master]", "maximize_library") > 0;
if (MC7000.isVinylMode[deckOffset] && !libraryMaximized) {
if (value === 0x7F) {
engine.scratchEnable(deckNumber, MC7000.jogWheelTicksPerRevolution,
MC7000.scratchParams.recordSpeed,
MC7000.scratchParams.alpha,
MC7000.scratchParams.beta);
} else {
engine.scratchDisable(deckNumber);
if (engine.getValue(group, "slip_enabled")) {
engine.scratchDisable(deckNumber, false); // stops scratching immediately
engine.setValue(group, "slip_enabled", false);
engine.beginTimer(50, function() {
engine.setValue(group, "slip_enabled", true);
}, true);
} else {
engine.scratchDisable(deckNumber); // continues scratching e.g. for backspin
}
}
}
};
Expand All @@ -659,22 +681,25 @@ MC7000.wheelTurn = function(channel, control, value, status, group) {

// A: For a control that centers on 0:
var numTicks = (value < 0x64) ? value : (value - 128);
var adjustedSpeed = numTicks * MC7000.jogSensitivity * 25;
var adjustedSpeed = numTicks * MC7000.jogSensitivity / 10;
var deckNumber = script.deckFromGroup(group);
var deckOffset = deckNumber - 1;
if (engine.isScratching(deckNumber)) {
var libraryMaximized = engine.getValue("[Master]", "maximize_library");
if (libraryMaximized === 1 && numTicks > 0) {
engine.setValue("[Library]", "MoveDown", 1);
} else if (libraryMaximized === 1 && numTicks < 0) {
engine.setValue("[Library]", "MoveUp", 1);
} else if (engine.isScratching(deckNumber)) {
// Scratch!
engine.scratchTick(deckNumber, numTicks * MC7000.jogSensitivity);
} else {
if (MC7000.shift[deckOffset]) {
// While Shift Button pressed -> Search through track
var jogSearch = 300 * adjustedSpeed / MC7000.jogWheelTicksPerRevolution;
var jogSearch = 100 * adjustedSpeed; // moves 100 times faster than normal jog
engine.setValue(group, "jog", jogSearch);
} else {
// While Shift Button released -> Pitch Bend
var jogDelta = adjustedSpeed / MC7000.jogWheelTicksPerRevolution;
var jogAbsolute = jogDelta + engine.getValue(group, "jog");
engine.setValue(group, "jog", jogAbsolute);
engine.setValue(group, "jog", adjustedSpeed);
}
}
};
Expand Down Expand Up @@ -729,26 +754,35 @@ MC7000.nextRateRange = function(midichan, control, value, status, group) {
if (value === 0) {
return; // don't respond to note off messages
}
var deckOffset = script.deckFromGroup(group) - 1;
// increment currentRateRangeIndex and check for overflow
if (++MC7000.currentRateRangeIndex[deckOffset] ===
MC7000.rateRanges.length) {
MC7000.currentRateRangeIndex[deckOffset] = 0;
var currRateRange = engine.getValue(group, "rateRange");
engine.setValue(group, "rateRange", MC7000.getNextRateRange(currRateRange));
};

MC7000.getNextRateRange = function(currRateRange) {
for (var i = 0; i < MC7000.rateRanges.length; i++) {
if (MC7000.rateRanges[i] > currRateRange) {
return MC7000.rateRanges[i];
}
}
engine.setValue(group, "rateRange", MC7000.rateRanges[MC7000.currentRateRangeIndex[deckOffset]]);
return MC7000.rateRanges[0];
};

// Previous Rate range toggle
MC7000.prevRateRange = function(midichan, control, value, status, group) {
if (value === 0) {
return; // don't respond to note off messages
}
var deckOffset = script.deckFromGroup(group) - 1;
// decrement currentRateRangeIndex and check for underflow
if (--MC7000.currentRateRangeIndex[deckOffset] < 0) {
MC7000.currentRateRangeIndex[deckOffset] = MC7000.rateRanges.length - 1;
var currRateRange = engine.getValue(group, "rateRange");
engine.setValue(group, "rateRange", MC7000.getPrevRateRange(currRateRange));
};

MC7000.getPrevRateRange = function(currRateRange) {
for (var i = MC7000.rateRanges.length; i >= 0; i--) {
if (MC7000.rateRanges[i] < currRateRange) {
return MC7000.rateRanges[i];
}
}
engine.setValue(group, "rateRange", MC7000.rateRanges[MC7000.currentRateRangeIndex[deckOffset]]);
return MC7000.rateRanges[MC7000.rateRanges.length - 1];
};

// Key & Waveform zoom Select
Expand Down Expand Up @@ -811,10 +845,19 @@ MC7000.reverse = function(channel, control, value, status, group) {
var deckNumber = script.deckFromGroup(group);
if (value > 0) {
// while the button is pressed spin back
engine.brake(deckNumber, true, MC7000.factor[deckNumber], - 15); // start at a rate of -15 and decrease by "factor"
// start at a rate of -10 and decrease by "MC7000.factor"
engine.brake(deckNumber, true, MC7000.factor[deckNumber], -10);
} else {
// when releasing the button the track starts softly again
engine.softStart(deckNumber, true, MC7000.factor[deckNumber]);
if (engine.getValue(group, "slip_enabled")) {
engine.brake(deckNumber, false); // disable brake effect
engine.setValue(group, "play", 1);
engine.setValue(group, "slip_enabled", false);
engine.beginTimer(50, function() {
engine.setValue(group, "slip_enabled", true);
}, true);
} else {
engine.softStart(deckNumber, true, MC7000.factor[deckNumber]);
}
}
};

Expand All @@ -827,6 +870,9 @@ MC7000.censor = function(channel, control, value, status, group) {
} else {
engine.setValue(group, "reverseroll", 0);
}
engine.beginTimer(50, function() {
engine.setValue(group, "slip_enabled", true);
}, true);
} else {
// reverse play while button pressed
if (value > 0) {
Expand Down Expand Up @@ -928,14 +974,14 @@ MC7000.TrackPositionLEDs = function(value, group) {
}
// now chose which PAD LED to turn on (+8 means shifted PAD LEDs)
if (beatCountLED === 0) {
midi.sendShortMsg(0x94 + deckOffset, 0x14, MC7000.padColor.hotcueon);
midi.sendShortMsg(0x94 + deckOffset, 0x14 + 8, MC7000.padColor.hotcueon);
midi.sendShortMsg(0x94 + deckOffset, 0x14, MC7000.padColor.slicerJumpFwd);
midi.sendShortMsg(0x94 + deckOffset, 0x14 + 8, MC7000.padColor.slicerJumpFwd);
} else if (beatCountLED === 7) {
midi.sendShortMsg(0x94 + deckOffset, 0x1B, MC7000.padColor.hotcueon);
midi.sendShortMsg(0x94 + deckOffset, 0x1B + 8, MC7000.padColor.hotcueon);
midi.sendShortMsg(0x94 + deckOffset, 0x1B, MC7000.padColor.slicerJumpFwd);
midi.sendShortMsg(0x94 + deckOffset, 0x1B + 8, MC7000.padColor.slicerJumpFwd);
} else if (beatCountLED > 0 && beatCountLED < 7) {
midi.sendShortMsg(0x94 + deckOffset, 0x14 + beatCountLED, MC7000.padColor.hotcueon);
midi.sendShortMsg(0x94 + deckOffset, 0x14 + 8 + beatCountLED, MC7000.padColor.hotcueon);
midi.sendShortMsg(0x94 + deckOffset, 0x14 + beatCountLED, MC7000.padColor.slicerJumpFwd);
midi.sendShortMsg(0x94 + deckOffset, 0x14 + 8 + beatCountLED, MC7000.padColor.slicerJumpFwd);
}
}
MC7000.prevPadLED[deckOffset] = beatCountLED;
Expand Down
6 changes: 3 additions & 3 deletions res/controllers/Denon-MC7000.midi.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<MixxxControllerPreset mixxxVersion="2.3" schemaVersion="1">
<MixxxControllerPreset mixxxVersion="2.3.1" schemaVersion="1">
<info>
<name>Denon MC7000</name>
<author>OsZ</author>
<description>Denon MC7000 mapping. Check your Linux Kernel version to get the Audio Interface working - see WIKI</description>
<description>A professional 4-channel DJ controller featuring 2 separate audio interfaces. Please refer to the Mixxx user manual to get the audio interfaces working on Linux.</description>
<forums>https://mixxx.discourse.group/t/denon-mc7000-mapping/18235</forums>
<wiki>https://github.com/mixxxdj/mixxx/wiki/Denon-MC7000</wiki>
<manual>denon_mc7000</manual>
Expand Down Expand Up @@ -939,7 +939,7 @@
<Script-Binding/>
</options>
</control>
<control>
<control>
<group>[Channel1]</group>
<key>MC7000.wheelTouch</key>
<description>MIDI Learned from 759 messages.</description>
Expand Down