Hi, I adapted Hercules DJ Console Mk2.midi.xml and Hercules-DJ-Console-Mk2-scripts.js to work for the "Hercules DJ Control mp3" console. I have no programming skills but I tested them on my system ( MacBook Unibody 2 GHz Core 2, Mac OSX Snow Leopard, Mixxx-1.8.0-beta1 ) and everything seems 100% working, moreover it solves an annoying bug ( https://bugs.launchpad.net/mixxx/+bug/453465 ) I had too. Please note that I did some small changes in the scripts to fit my taste ( but they can be easily reverted ):
* I used a multiplicative constant "HerculesMp3.jogCorrection" to let jogs be more sensitive. * Since the dj control mp3 console has no settings for headset beside pfl on/off, I used a very simple function "HerculesMp3.pfl" to control pfl. * The console permits to select three modes, namely "Fx", "Cue" and "Loop". "Cue" and "Loop" modes were unused in the original files. Now when in "Cue" mode, Track buttons act as fast forward and fast rewind. ( Obviously "Cue" mode is not related with cue buttons). Files are attached to this email. I hope they will be useful.
function HerculesMp3 () {}
HerculesMp3.ledOn = 0x7F;
HerculesMp3.ledOff = 0x00;
HerculesMp3.debug = false;
HerculesMp3.scratchMode = false;
HerculesMp3.decayLast = new Date().getTime();
HerculesMp3.decayInterval = 300;
HerculesMp3.decayRate = 1.5;
//More sensitive jogs.
HerculesMp3.jogCorrection = 4;
HerculesMp3.buttons123Modes = ["kill", "fx", "cue", "loop"];
HerculesMp3.buttons123used = {"[Channel1]": false, "[Channel1]": false};
// TODO HerculesMp3 controls should be divided into groups, then signals
// should directed to each group without thinking about specific controls
// to allow for easy rebinding.
HerculesMp3.controls = {
"inputs": {
0x09: { "channel": 1, "name": "cue", "type": "button" },
0x03: { "channel": 2, "name": "cue", "type": "button" },
0x08: { "channel": 1, "name": "play", "type": "button" },
0x02: { "channel": 2, "name": "play", "type": "button" },
0x07: { "channel": 1, "name": "fx select", "type": "button",
"mode": 0 },
0x01: { "channel": 2, "name": "fx select", "type": "button",
"mode": 0 },
0x0F: { "channel": 1, "name": "fx 1", "type": "button", "used": false },
0x10: { "channel": 2, "name": "fx 1", "type": "button", "used": false },
0x0E: { "channel": 1, "name": "fx 2", "type": "button", "used": false },
0x11: { "channel": 2, "name": "fx 2", "type": "button", "used": false },
0x0D: { "channel": 1, "name": "fx 3", "type": "button", "used": false },
0x12: { "channel": 2, "name": "fx 3", "type": "button", "used": false },
0x1B: { "channel": 1, "name": "mouse", "type": "button" },
0x1C: { "channel": 2, "name": "mouse", "type": "button" },
0x34: { "channel": 1, "name": "pitch", "type": "pot" },
0x35: { "channel": 2, "name": "pitch", "type": "pot" },
0x36: { "channel": 1, "name": "wheel", "type": "pot" },
0x37: { "channel": 2, "name": "wheel", "type": "pot" },
0x0B: { "channel": 1, "name": "prevtrack", "type": "button" },
0x0C: { "channel": 1, "name": "nexttrack", "type": "button" },
0x05: { "channel": 2, "name": "prevtrack", "type": "button" },
0x06: { "channel": 2, "name": "nexttrack", "type": "button" }
},
"outputs": {
0x0F: { "channel": 1, "name": "fx mode", "type": "led" },
0x10: { "channel": 2, "name": "fx mode", "type": "led" },
0x0E: { "channel": 1, "name": "cue mode", "type": "led" },
0x11: { "channel": 2, "name": "cue mode", "type": "led" },
0x0D: { "channel": 1, "name": "loop mode", "type": "led" },
0x12: { "channel": 2, "name": "loop mode", "type": "led" },
0x16: { "channel": 1, "name": "master tempo", "type": "led" },
0x1A: { "channel": 2, "name": "master tempo", "type": "led" },
0x0A: { "channel": 1, "name": "auto beat", "type": "led" },
0x04: { "channel": 2, "name": "auto beat", "type": "led" },
0x09: { "channel": 1, "name": "cue", "type": "led" },
0x03: { "channel": 2, "name": "cue", "type": "led" },
0x00: { "channel": 1, "name": "play blink", "type": "led" },
0x05: { "channel": 2, "name": "play blink", "type": "led" },
0x08: { "channel": 1, "name": "play", "type": "led" },
0x02: { "channel": 2, "name": "play", "type": "led" },
0x15: { "channel": 1, "name": "pfl", "type": "led" },
0x19: { "channel": 2, "name": "pfl", "type": "led" }
}
};
HerculesMp3.leds = {
};
HerculesMp3.init = function (id) { // called when the device is opened & set up
HerculesMp3.initializeControls();
engine.connectControl("[Channel1]","playposition","HerculesMp3.wheelDecay");
engine.connectControl("[Channel2]","playposition","HerculesMp3.wheelDecay");
print ("HerculesMp3 id: \""+id+"\" initialized.");
};
HerculesMp3.initializeControls = function () {
for (control in HerculesMp3.controls.outputs) {
if (HerculesMp3.controls.outputs[control].type == 'led') {
key = "[Channel" + HerculesMp3.controls.outputs[control].channel + "] " + HerculesMp3.controls.outputs[control].name;
HerculesMp3.leds[key] = control;
}
}
HerculesMp3.setLeds("on");
HerculesMp3.setLeds("off");
// Set controls in Mixxx to reflect settings on the device
midi.sendShortMsg(0xB0,0x7F,0x7F);
};
HerculesMp3.shutdown = function (id) {
HerculesMp3.setLeds("off");
};
HerculesMp3.getGroup = function (control){
// Get the "group" that used to be provided in group, this is not reusable
// across devices and also breaks remapping of these functions to other
// buttons.
return "[Channel" + HerculesMp3.controls.inputs[control].channel + "]";
};
HerculesMp3.getControl = function (io, channel, name) {
// Accept channel in form 'N' or '[ChannelN]'
channel = channel.replace(/\[Channel(\d)\]/, "$1");
for (control in HerculesMp3.controls.inputs) {
if (HerculesMp3.controls.inputs[control].channel == channel &&
HerculesMp3.controls.inputs[control].name == name
) return HerculesMp3.controls.inputs[control];
}
print ("HerculesMp3.getControl: Control not found: io=" + io + ": channel=" + channel + ": name=" + name);
};
HerculesMp3.setLeds = function (onOff) {
for (LED in HerculesMp3.leds) {
HerculesMp3.setLed(LED,onOff);
// Seems that if midi messages are sent too quickly, leds don't behave
// as expected. A pause rectifies this.
HerculesMp3.pauseScript(10);
}
};
HerculesMp3.setLed = function (led, onOff) {
value = onOff=="on" ? HerculesMp3.ledOn : HerculesMp3.ledOff;
if (HerculesMp3.debug) print ("HerculesMp3.setLed: Setting " + led + " led " + onOff);
if (HerculesMp3.debug) print ("HerculesMp3.setLed: midi.sendShortMsg(0xB0," + HerculesMp3.leds[led].toString(16) + "," + value + ")");
midi.sendShortMsg(0xB0,HerculesMp3.leds[led],value);
HerculesMp3.controls.outputs[HerculesMp3.leds[led]].isOn = onOff=="on" ? true : false;
};
HerculesMp3.pauseScript = function(ms) {
startDate = new Date();
currentDate = null;
while(currentDate-startDate < ms) currentDate = new Date();
};
HerculesMp3.pfl = function (group, control, value, status) {
if(value){
if (HerculesMp3.debug) print("HerculesMp3.pfl: " + control.toString(16) + " " + value.toString(16));
switch (control){
case 0x15:
if (HerculesMp3.debug) print("HerculesMp3.: Deck A");
engine.setValue("[Channel1]","pfl", !engine.getValue("[Channel1]", "pfl"));
break;
case 0x19:
if (HerculesMp3.debug) print("HerculesMp3.: Deck B");
engine.setValue("[Channel2]","pfl", !engine.getValue("[Channel2]", "pfl"));
}
}
};
HerculesMp3.cue = function (group, control, value, status) {
group = HerculesMp3.getGroup(control);
if ((engine.getValue(group, "duration") == 0) && (value)) {
print("No song on " + group);
return;
}
if (value) { // Down
engine.setValue(group,"cue_default",1);
HerculesMp3.setLed(group + " cue", "on");
} else { // Release
engine.setValue(group,"cue_default",0);
}
};
HerculesMp3.play = function (group, control, value, status) {
if (value) { // Only do stuff when play is pushed, not released.
group = HerculesMp3.getGroup(control);
if (engine.getValue(group, "duration") == 0) {
print("No song on " + group);
return;
}
engine.setValue(group,"play", !engine.getValue(group,"play"));
HerculesMp3.setLed(group + " cue", "off");
}
};
HerculesMp3.loadSelectedTrack = function (group, control, value, status) {
if (value) { // Only do stuff when pushed, not released.
group = HerculesMp3.getGroup(control);
engine.setValue(group, "LoadSelectedTrack", 1);
HerculesMp3.setLed(group + " cue", "on");
}
};
HerculesMp3.buttons123 = function (group, control, value, status) {
group = HerculesMp3.getGroup(control);
if (value) { // Button pressed.
HerculesMp3.controls.inputs[control].isDown = true;
} else { //Button released.
HerculesMp3.controls.inputs[control].isDown = false;
}
mode = HerculesMp3.getControl("inputs", group, "fx select").mode;
mode = HerculesMp3.buttons123Modes[mode];
switch (mode) {
case "kill": // Kill mode
if (value) { // Button pressed.
switch (HerculesMp3.controls.inputs[control].name) {
case "fx 1":
engine.setValue(group, "filterLowKill", !engine.getValue(group, "filterLowKill"));
break;
case "fx 2":
engine.setValue(group, "filterMidKill", !engine.getValue(group, "filterMidKill"));
break;
case "fx 3":
engine.setValue(group, "filterHighKill", !engine.getValue(group, "filterHighKill"));
break;
}
}
break; // End kill mode
case "fx": // Fx mode
// Were only turning off the flanger if any of the 123 buttons are
// released, not pushed. This is so we can have any of the 123 buttons
// held down, then use the pitch pot to modify the effect settings
if (!value) { // Button released.
if (HerculesMp3.controls.inputs[control].used) {
// Button was used to turn the pitch control into a modifier
// for the effect settings, so don't go on and turn the flanger
// on/off
HerculesMp3.controls.inputs[control].used = false;
return;
}
switch (HerculesMp3.controls.inputs[control].name) {
case "fx 1":
case "fx 2":
case "fx 3":
engine.setValue(group, "flanger", !engine.getValue(group, "flanger"));
break;
}
}
break; // End fx mode
case "cue":
if (HerculesMp3.debug) print("Switching to fwd/rev mode");
break;
case "loop":
print("HerculesMp3.buttons123: " + mode + " mode not supported yet");
break;
default:
print("HerculesMp3.buttons123: " + mode + " mode unsupported");
}
};
HerculesMp3.buttons123mode = function (group, control, value, status) {
group = HerculesMp3.getGroup(control);
if (value) { // Only do stuff when pushed, not released.
currentMode = HerculesMp3.controls.inputs[control].mode;
nextMode = currentMode < HerculesMp3.buttons123Modes.length-1 ? currentMode+1 : 0;
currentLed = group + " " + HerculesMp3.buttons123Modes[currentMode] + " mode";
nextLed = group + " " + HerculesMp3.buttons123Modes[nextMode] + " mode";
sNextMode = HerculesMp3.buttons123Modes[nextMode];
switch (sNextMode) {
case "kill":
case "fx":
print("HerculesMp3.buttons123mode: Switching to " + sNextMode + " mode");
break;
case "cue":
if (HerculesMp3.debug) print("Switching to fwd/rev mode");
break;
case "loop":
print("HerculesMp3.buttons123mode: " + sNextMode + " mode not supported yet");
break;
default:
print("HerculesMp3.buttons123mode: " + sNextMode + " mode unsupported");
}
// Only turn on/off leds for non-zero modes as 0 is kill mode which
// has no corresponding LED. i.e. all LEDs off for kill mode.
if (currentMode) HerculesMp3.setLed(currentLed, "off");
// Seems that if midi messages are sent too quickly, leds don't behave
// as expected. A pause rectifies this.
HerculesMp3.pauseScript(10);
if (nextMode) HerculesMp3.setLed(nextLed, "on");
HerculesMp3.controls.inputs[control].mode = nextMode;
}
};
// Adding some lines to control fast rewind and forward from the console.
// When "cue" mode is selected from "fx select", track buttons act as
// fast rewind and fast forward buttons.
HerculesMp3.NextTrack = function (group, control, value, status) {
group = HerculesMp3.getGroup(control);
currentMode = HerculesMp3.buttons123Modes[HerculesMp3.getControl("inputs", group, "fx select").mode];
//it is unclear for me what NextTrack/PrevTrack do
Action = "NextTrack";
if (currentMode == "cue"){
Action = "fwd";
}
if (value)
{
engine.setValue(group,Action,1);
}
else
{
engine.setValue(group,Action,0);
}
};
HerculesMp3.PrevTrack = function (group, control, value, status) {
group = HerculesMp3.getGroup(control);
currentMode = HerculesMp3.buttons123Modes[HerculesMp3.getControl("inputs", group, "fx select").mode];
//it is unclear for me what NextTrack/PrevTrack do
Action = "PrevTrack";
if (currentMode == "cue"){//We want PrevTrack buttons to act as back when in cue mode
Action = "back";
}
if (value)
{
engine.setValue(group,Action,1);
}
else
{
engine.setValue(group,Action,0);
}
};
HerculesMp3.pitch = function (group, control, value, status) {
// 7F > 40: CCW Slow > Fast - 127 > 64
// 01 > 3F: CW Slow > Fast - 0 > 63
group = HerculesMp3.getGroup(control);
pitchControl = HerculesMp3.getControl("inputs", group, "pitch");
done = false;
currentMode = HerculesMp3.getControl("inputs", group, "fx select").mode;
currentMode = HerculesMp3.buttons123Modes[currentMode];
// If in fx mode and one or more of buttons 123 are pressed, use pitch
// pot to adjust the relevant flanger parameters instead of changing
// the rate (as is normal operation for the pitch pot)
if (currentMode == "fx") {
potStep = 25; // How many clicks round the pot from min to max values
if (HerculesMp3.getControl("inputs", group, "fx 1").isDown) {
min = 0; max = 1;
increment = (max-min)/potStep;
increment = (value <= 0x3F) ? increment : increment * -1;
currentValue = engine.getValue("[Flanger]", "lfoDepth");
newValue = currentValue + increment;
newValue = newValue > max ? max : newValue < min ? min : newValue;
if (HerculesMp3.debug) print ("HerculesMp3.pitch: value= " + newValue);
if (newValue != currentValue)
engine.setValue("[Flanger]", "lfoDepth", newValue);
HerculesMp3.getControl("inputs", group, "fx 1").used = true;
done = true;
}
if (HerculesMp3.getControl("inputs", group, "fx 2").isDown) {
min = 50; max = 10000;
increment = (max-min)/potStep;
increment = (value <= 0x3F) ? increment : increment * -1;
currentValue = engine.getValue("[Flanger]", "lfoDelay");
newValue = currentValue + increment;
newValue = newValue > max ? max : newValue < min ? min : newValue;
if (HerculesMp3.debug) print ("HerculesMp3.pitch: value= " + newValue);
if (newValue != currentValue)
engine.setValue("[Flanger]", "lfoDelay", newValue);
HerculesMp3.getControl("inputs", group, "fx 2").used = true;
done = true;
}
if (HerculesMp3.getControl("inputs", group, "fx 3").isDown) {
min = 50000; max = 2000000
increment = (max-min)/potStep;
increment = (value <= 0x3F) ? increment : increment * -1;
currentValue = engine.getValue("[Flanger]", "lfoPeriod");
newValue = currentValue + increment;
newValue = newValue > max ? max : newValue < min ? min : newValue;
if (HerculesMp3.debug) print ("HerculesMp3.pitch: value= " + newValue);
if (newValue != currentValue)
engine.setValue("[Flanger]", "lfoPeriod", newValue);
HerculesMp3.getControl("inputs", group, "fx 3").used = true;
done = true;
}
}
if (done) return;
increment = 0.016;
increment = (value <= 0x3F) ? increment : increment * -1;
if (HerculesMp3.debug) print ("HerculesMp3.pitch: value=" + value);
engine.setValue(group, "rate", engine.getValue(group, "rate") + increment);
};
HerculesMp3.jog_wheel = function (group, control, value, status) {
// 7F > 40: CCW Slow > Fast - 127 > 64
// 01 > 3F: CW Slow > Fast - 0 > 63
group = HerculesMp3.getGroup(control);
if (HerculesMp3.controls.outputs[HerculesMp3.leds[group + " cue"]].isOn == true) {
HerculesMp3.setLed(group + " cue", "off");
}
jogValue = value >=0x40 ? value - 0x80 : value; // -64 to +63, - = CCW, + = CW
if (HerculesMp3.scratchMode) { // do some scratching
if (HerculesMp3.debug) print("Do scratching value:" + value + " jogValue: " + jogValue );
engine.setValue(group,"scratch", (engine.getValue(group,"scratch") + (jogValue/64)).toFixed(2));
} else { // do pitch adjustment
newValue = jogValue * HerculesMp3.jogCorrection;
if (HerculesMp3.debug) print("do pitching adjust " + jogValue + " new Value: " + newValue);
engine.setValue(group,"jog", newValue);
}
};
HerculesMp3.wheelDecay = function (value) {
currentDate = new Date().getTime();
if (currentDate > HerculesMp3.decayLast + HerculesMp3.decayInterval) {
HerculesMp3.decayLast = currentDate;
if (HerculesMp3.debug) print(" new playposition: " + value + " decayLast: "+ HerculesMp3.decayLast);
if (HerculesMp3.scratchMode) { // do some scratching
if (HerculesMp3.debug) print("Scratch deck1: " + engine.getValue("[Channel1]","scratch") + " deck2: "+ engine.getValue("[Channel2]","scratch"));
jog1DecayRate = HerculesMp3.decayRate * (engine.getValue("[Channel1]","play") ? 1 : 5);
jog1 = engine.getValue("[Channel1]","scratch");
if (jog1 != 0) {
if (Math.abs(jog1) > jog1DecayRate) {
engine.setValue("[Channel1]","scratch", (jog1 / jog1DecayRate).toFixed(2));
} else {
engine.setValue("[Channel1]","scratch", 0);
}
}
jog2DecayRate = HerculesMp3.decayRate * (engine.getValue("[Channel2]","play") ? 1 : 5);
jog2 = engine.getValue("[Channel2]","scratch");
if (jog2 != 0) {
if (Math.abs(jog2) > jog2DecayRate) {
engine.setValue("[Channel2]","scratch", (jog2 / jog2DecayRate).toFixed(2));
} else {
engine.setValue("[Channel2]","scratch", 0);
}
}
}
}
};
Hercules DJ Console Mp3.midi.xml
Description: XML document
------------------------------------------------------------------------------ Download Intel® Parallel Studio Eval Try the new software tools for yourself. Speed compiling, find bugs proactively, and fine-tune applications for parallel performance. See why Intel Parallel Studio got high marks during beta. http://p.sf.net/sfu/intel-sw-dev
_______________________________________________ Mixxx-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mixxx-devel
