diff options
author | RJ Ryan <rryan@mixxx.org> | 2013-06-19 00:46:54 -0400 |
---|---|---|
committer | RJ Ryan <rryan@mixxx.org> | 2013-06-19 00:46:54 -0400 |
commit | 25d57b59dff3947212909634864dd82365c9606b (patch) | |
tree | f0d0f8a048f8ed2f45c5ff015fc96891662f72f1 /res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js | |
parent | e50f9f168fa08f2be6d0f13e8a225bbcc9de09bb (diff) |
Moving mixxx/* to the root. A new era begins!
Diffstat (limited to 'res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js')
-rw-r--r-- | res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js b/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js new file mode 100644 index 0000000000..0639975292 --- /dev/null +++ b/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js @@ -0,0 +1,614 @@ +/****************************************************************/ +/* Hercules DJ Console Mk2 HID controller script */ +/* For Mixxx version 1.11 */ +/* Author: zestoi */ +/****************************************************************/ + +HerculesMk2Hid = new Controller(); +HerculesMk2Hid.controls = []; +HerculesMk2Hid.leds = []; +HerculesMk2Hid.cache_in = []; +HerculesMk2Hid.cache_out = []; +HerculesMk2Hid.callbacks = []; +HerculesMk2Hid.feedbacks = []; +HerculesMk2Hid.layer = [ "fx", "fx" ]; // fx, hotcue, loop, kill +HerculesMk2Hid.tempo_scaling = 400; +HerculesMk2Hid.beatjump_size = 8; +HerculesMk2Hid.kills = [ { filterHighKill: 0, filterMidKill: 0, filterLowKill: 0 }, { filterHighKill: 0, filterMidKill: 0, filterLowKill: 0 } ]; +HerculesMk2Hid.loop_lengths = [ 0.25, 0.5, 1 ]; // edit to loop size needed +HerculesMk2Hid.kill_order = [ "filterHighKill", "filterMidKill", "filterLowKill" ]; // edit if needed +HerculesMk2Hid.scratch_enabled = { "[Channel1]": false, "[Channel2]": false }; +HerculesMk2Hid.jog_skip = { "[Channel1]": true, "[Channel2]": true }; +HerculesMk2Hid.shift = false; // either autobeat button + +// +// the actual mapping is defined in this function +// + +HerculesMk2Hid.init = function() { + + var c = HerculesMk2Hid; + + // + // define the hid packet + // + + c.define_hid_format(); + + // + // create the actual mapping + // deck controls (will work for any decks as the group is passed in) + // + + c.capture("crossfader", "all", function(g, e, v) { engine.setValue(g, e, (v - 128) / 128); }); + + c.capture("play", "press", function(g, e, v) { engine.setValue(g, e, !engine.getValue(g, e)); }); + c.capture("cue_default", "all", function(g, e, v) { engine.setValue(g, e, v); }); + c.capture("beatsync", "all", function(g, e, v) { HerculesMk2Hid.shift = v > 0; engine.setValue(g, e, v); }); + + c.capture("volume", "all", function(g, e, v) { engine.setValue(g, e, v / 256); }); + c.capture("filterHigh", "all", function(g, e, v) { engine.setValue(g, e, v / 128); }); + c.capture("filterMid", "all", function(g, e, v) { engine.setValue(g, e, v / 128); }); + c.capture("filterLow", "all", function(g, e, v) { engine.setValue(g, e, v / 128); }); + + c.capture("jog", "all", function(g, e, v, ctrl) { + // skip initial jog values + if (HerculesMk2Hid.jog_skip[g]) { + HerculesMk2Hid.jog_skip[g] = false; + return; + } + + // scratch mode + if (HerculesMk2Hid.scratch_enabled[g]) { + engine.scratchTick(parseInt(g.substring(8,9)), ctrl.relative); + } + + // fine jog mode when playing + else if (engine.getValue(g, "play")) { + engine.setValue(g, e, ctrl.relative/2); + } + + // track browsing when shift held (sync) and not playing + else if (HerculesMk2Hid.shift) { + engine.setValue("[Playlist]", "SelectTrackKnob", ctrl.relative); + } + + // normal jog mode when not playing + else { + engine.setValue(g, e, ctrl.relative); + } + }); + + // + // double up pitch bend buttons as beatjumps when the track is stopped + // + + c.capture("pitchbend_down", "all", function(g, e, v) { + if (engine.getValue(g, "play") == 0) { + engine.setValue(g, "back", v > 0 ? 1 : 0); + } + else if (v > 0) { + engine.setValue(g, "jog", -3); + } + }); + + c.capture("pitchbend_up", "all", function(g, e, v) { + if (engine.getValue(g, "play") == 0) { + engine.setValue(g, "fwd", v > 0 ? 1 : 0); + } + else if (v > 0) { + engine.setValue(g, "jog", 3); + } + }); + + // + // track browsing - don't use the joystick or track load buttons as they function as a normal mouse too + // (map joystick_y/joystick_x/load if you want to use the joystick) + // + + c.capture("track_previous_a", "all", c.scroll_tracks); + c.capture("track_next_a", "all", c.scroll_tracks); + c.capture("track_previous_b", "press", function(g, e, v) { engine.setValue("[Channel1]", "LoadSelectedTrack", 1); }); + c.capture("track_next_b", "press", function(g, e, v) { engine.setValue("[Channel2]", "LoadSelectedTrack", 1); }); + + // + // uncomment this code and the function HerculesMk2Hid.scroll_tracks_joystick() below if you really want the joystick + // to be used for track browsing and the left/right joystick buttons for track loading + // + + /* + c.capture("load", "press", function(g, e, v) { engine.setValue(g, "LoadSelectedTrack", 1); }); + + c.capture("joystick_y", "all", function(g, e, v) { + if (v == 128) { + HerculesMk2Hid.direction = 0; + if (HerculesMk2Hid.track_timer) { + engine.stopTimer(HerculesMk2Hid.track_timer); + HerculesMk2Hid.track_timer = null; + } + } + else { + if (v > 128) { + HerculesMk2Hid.direction = 1; + v -= 128; + } + else { + HerculesMk2Hid.direction = -1; + v = 128 - v; + } + + if (v < 30) v = 30; + if (HerculesMk2Hid.track_timer) engine.stopTimer(HerculesMk2Hid.track_timer); + HerculesMk2Hid.track_timer = engine.beginTimer(parseInt(5120 / v), 'HerculesMk2Hid.scroll_tracks_joystick'); + } + }); + */ + + // + // tempo encoder + // + + c.capture("rate", "all", function(g, e, v, ctrl) { + var rate = engine.getValue(g, "rate") + ctrl.relative / c.tempo_scaling; + if (rate > 1) rate = 1; else if (rate < -1) rate = -1; + engine.setValue(g, e, rate); + }); + + // + // enable/disable scratching with the beatlock buttons (as jogs are non touch sensitive) + // + + c.capture("beatlock", "press", function(g, e, v) { + + HerculesMk2Hid.scratch_enabled[g] = !HerculesMk2Hid.scratch_enabled[g]; + + if (HerculesMk2Hid.scratch_enabled[g]) { + engine.scratchEnable(parseInt(g.substring(8,9)), 64, 45, 0.125, 0.125/32); + } + else { + engine.scratchDisable(parseInt(g.substring(8,9))); + } + + c.send(g, e, HerculesMk2Hid.scratch_enabled[g] ? 1 : 0); + }); + + // + // toggle between fx, hotcue and loop modes and update leds + // + + c.capture("layer_select", "press", function(g, e, v) { + var deck = parseInt(g.substring(8,9)); + c.send(g, "fx", 0); + c.send(g, "hotcue", 0); + c.send(g, "loop", 0); + switch (c.layer[deck-1]) { + case "fx": c.layer[deck-1] = "hotcue"; break; + case "hotcue": c.layer[deck-1] = "loop"; break; + case "loop": + c.layer[deck-1] = "kill"; + c.send(g, "fx", !c.kills[deck-1]['filterHighKill']); + c.send(g, "hotcue", !c.kills[deck-1]['filterMidKill']); + c.send(g, "loop", !c.kills[deck-1]['filterLowKill']); + break; + case "kill": + c.layer[deck-1] = "fx"; + } + if (c.layer[deck-1] != "kill") { + c.send(g, c.layer[deck-1], 1); + } + }); + + c.capture("layer_btn1", "all", function(g, e, v) { c.layer_btn(g, e, v); }); + c.capture("layer_btn2", "all", function(g, e, v) { c.layer_btn(g, e, v); }); + c.capture("layer_btn3", "all", function(g, e, v) { c.layer_btn(g, e, v); }); + + // + // headphone cue + // + + c.capture("monitor_a", "all", function(g, e, v) { engine.setValue("[Channel1]", "pfl", v); }); + c.capture("monitor_b", "all", function(g, e, v) { engine.setValue("[Channel2]", "pfl", v); }); + c.capture("monitor_both", "all", function(g, e, v) { + engine.setValue("[Channel1]", "pfl", v); + engine.setValue("[Channel2]", "pfl", v); + }); + + // + // led feedback + // + + c.feedback("[Channel1]", "play", function(g, e, v) { c.send(g, e, v); }); + c.feedback("[Channel2]", "play", function(g, e, v) { c.send(g, e, v); }); + c.feedback("[Channel1]", "cue_default", function(g, e, v) { c.send(g, e, v); }); + c.feedback("[Channel2]", "cue_default", function(g, e, v) { c.send(g, e, v); }); + c.feedback("[Channel1]", "beatsync", function(g, e, v) { c.send(g, e, v); }); + c.feedback("[Channel2]", "beatsync", function(g, e, v) { c.send(g, e, v); }); + + // + // kill status + // + + c.feedback("[Channel1]", "filterHighKill", c.kill_status); + c.feedback("[Channel1]", "filterMidKill", c.kill_status); + c.feedback("[Channel1]", "filterLowKill", c.kill_status); + c.feedback("[Channel2]", "filterHighKill", c.kill_status); + c.feedback("[Channel2]", "filterMidKill", c.kill_status); + c.feedback("[Channel2]", "filterLowKill", c.kill_status); + + // + // clear/setup any initial leds + // + + for (id in c.leds) { + c.send(c.leds[id].group, c.leds[id].name, 0); + } + + c.send("[Channel1]", "fx", 1); + c.send("[Channel2]", "fx", 1); +} + +// +// map the 6 buttons that control either effects, hotcues, loops or kills +// + +HerculesMk2Hid.layer_btn = function(g, e, v) { + var deck = parseInt(g.substring(8,9)); + var btn = parseInt(e.substring(9,10)); + switch (HerculesMk2Hid.layer[deck-1]) { + case "fx": + switch (btn) { + case 1: + engine.setValue("[Flanger]", "lfoDepth", 1); + engine.setValue("[Flanger]", "lfoPeriod", 500000); + engine.setValue("[Flanger]", "lfoDelay", 666); + engine.setValue(g, "flanger", v > 0); + break; + case 2: + engine.spinback(parseInt(g.substring(8,9)), v > 0); + break; + case 3: + engine.brake(parseInt(g.substring(8,9)), v > 0); + } + break; + case "hotcue": + engine.setValue(g, "hotcue_" + btn + "_activate", v > 0 ? 1 : 0); + break; + case "loop": + var len = HerculesMk2Hid.loop_lengths[btn-1]; + engine.setValue(g, "beatloop_" + len + "_toggle", 1); + break; + case 'kill': + if (v > 0) { + engine.setValue(g, HerculesMk2Hid.kill_order[btn-1], !engine.getValue(g, HerculesMk2Hid.kill_order[btn-1])); + } + } +} + +// +// beatjump - will get out of sync if called while deck is playing +// + +HerculesMk2Hid.beatjump = function(group, jump) { + jump = jump * 120 / engine.getValue(group, "bpm") / engine.getValue(group, "track_samples") * engine.getValue(group, "track_samplerate"); + engine.setValue(group, "playposition", engine.getValue(group, "playposition") + jump); +} + +// +// playlist scroll nex/previous with auto-repeat when held +// + +HerculesMk2Hid.scroll_tracks = function(g, e, v) { + if (v > 0) { + engine.setValue("[Playlist]", e == "track_next_a" ? "SelectNextTrack" : "SelectPrevTrack", 1); + if (!HerculesMk2Hid.scroll_timer) { + HerculesMk2Hid.scroll_timer = engine.beginTimer(150, 'HerculesMk2Hid.scroll_tracks("[Playlist]","' + e + '",' + v + ')'); + } + } + else { + if (HerculesMk2Hid.scroll_timer) { + engine.stopTimer(HerculesMk2Hid.scroll_timer); + HerculesMk2Hid.scroll_timer = null; + } + } +} + +// +// eq kill status +// + +HerculesMk2Hid.kill_status = function(g, e, v) { + var deck = parseInt(g.substring(8,9)); + HerculesMk2Hid.kills[deck-1][e] = v; + + // + // update leds with kill status if we're on that layer + // + + if (HerculesMk2Hid.layer[deck-1] == "kill") { + switch (e) { + case 'filterHighKill': HerculesMk2Hid.send(g, "fx", !v); break; + case 'filterMidKill': HerculesMk2Hid.send(g, "hotcue", !v); break; + case 'filterLowKill': HerculesMk2Hid.send(g, "loop", !v); + } + } +} + +/* +HerculesMk2Hid.scroll_tracks_joystick = function() { + engine.setValue("[Playlist]", HerculesMk2Hid.direction > 0 ? "SelectNextTrack" : "SelectPrevTrack", 1); +} +*/ + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// define the hid packet to event mapping, could be defined via xml so can be used in multiple mappings +// naming the controls as much as possible inline with the mixxx engine names makes most mappings trivial +// + +HerculesMk2Hid.define_hid_format = function() { + + var c = HerculesMk2Hid; + var pid = 0x1; + + // deck 1 + + c.add_control(pid, "play", "[Channel1]", "button", 1, 0x80) + c.add_control(pid, "cue_default", "[Channel1]", "button", 2, 0x01) + c.add_control(pid, "track_previous_a", "[Channel1]", "button", 2, 0x04) + c.add_control(pid, "track_next_a", "[Channel1]", "button", 2, 0x08) + c.add_control(pid, "beatsync", "[Channel1]", "button", 2, 0x02) + c.add_control(pid, "pitchbend_down", "[Channel1]", "button", 3, 0x08) + c.add_control(pid, "pitchbend_up", "[Channel1]", "button", 3, 0x04) + c.add_control(pid, "load", "[Channel1]", "button", 4, 0x04) + c.add_control(pid, "beatlock", "[Channel1]", "button", 3, 0x20) + c.add_control(pid, "source", "[Channel1]", "button", 4, 0x10) + c.add_control(pid, "filterLow", "[Channel1]", "fader", 9, 0xff) + c.add_control(pid, "filterMid", "[Channel1]", "fader", 10, 0xff) + c.add_control(pid, "filterHigh", "[Channel1]", "fader", 11, 0xff) + c.add_control(pid, "volume", "[Channel1]", "fader", 13, 0xff) + c.add_control(pid, "rate", "[Channel1]", "encoder", 15, 0xff) + c.add_control(pid, "jog", "[Channel1]", "encoder", 17, 0xff) + c.add_control(pid, "layer_select", "[Channel1]", "button", 1, 0x40) + c.add_control(pid, "layer_btn1", "[Channel1]", "button", 2, 0x40) + c.add_control(pid, "layer_btn2", "[Channel1]", "button", 2, 0x20) + c.add_control(pid, "layer_btn3", "[Channel1]", "button", 2, 0x10) + + // deck 2 + + c.add_control(pid, "play", "[Channel2]", "button", 1, 0x02) + c.add_control(pid, "cue_default", "[Channel2]", "button", 1, 0x04) + c.add_control(pid, "track_previous_b", "[Channel2]", "button", 1, 0x10) + c.add_control(pid, "track_next_b", "[Channel2]", "button", 1, 0x20) + c.add_control(pid, "beatsync", "[Channel2]", "button", 1, 0x08) + c.add_control(pid, "pitchbend_down", "[Channel2]", "button", 3, 0x80) + c.add_control(pid, "pitchbend_up", "[Channel2]", "button", 3, 0x40) + c.add_control(pid, "load", "[Channel2]", "button", 4, 0x08) + c.add_control(pid, "beatlock", "[Channel2]", "button", 4, 0x02) + c.add_control(pid, "source", "[Channel2]", "button", 4, 0x20) + c.add_control(pid, "filterLow", "[Channel2]", "fader", 6, 0xff) + c.add_control(pid, "filterMid", "[Channel2]", "fader", 7, 0xff) + c.add_control(pid, "filterHigh", "[Channel2]", "fader", 8, 0xff) + c.add_control(pid, "volume", "[Channel2]", "fader", 14, 0xff) + c.add_control(pid, "rate", "[Channel2]", "encoder", 16, 0xff) + c.add_control(pid, "jog", "[Channel2]", "encoder", 18, 0xff) + c.add_control(pid, "layer_select", "[Channel2]", "button", 1, 0x01) + c.add_control(pid, "layer_btn1", "[Channel2]", "button", 2, 0x80) + c.add_control(pid, "layer_btn2", "[Channel2]", "button", 3, 0x01) + c.add_control(pid, "layer_btn3", "[Channel2]", "button", 3, 0x02) + + // master + + c.add_control(pid, "crossfader", "[Master]", "fader", 12, 0xff) + c.add_control(pid, "joystick_x", "[Master]", "fader", 19, 0xff) + c.add_control(pid, "joystick_y", "[Master]", "fader", 20, 0xff) + + // headphone cue + + c.add_control(pid, "monitor_a", "[Master]", "button", 5, 0x1); + c.add_control(pid, "monitor_b", "[Master]", "button", 5, 0x2); + //c.add_control(pid, "monitor_both", "[Master]", "button", 5, 0x4); + c.add_control(pid, "monitor_both", "[Master]", "button", 5, 0x8); + + // define led feedback + + pid = 0; + c.cache_out[pid] = [ pid, 0x0, 0x0, 0x0 ]; + + c.add_control(pid, "play", "[Channel1]", "led", 2, 0x01); // blinking: 3, 0x2 + c.add_control(pid, "cue_default", "[Channel1]", "led", 2, 0x08); + c.add_control(pid, "beatsync", "[Channel1]", "led", 2, 0x10); + c.add_control(pid, "beatlock", "[Channel1]", "led", 1, 0x01); + + c.add_control(pid, "play", "[Channel2]", "led", 1, 0x40); // blinking: 3, 0x1 + c.add_control(pid, "cue_default", "[Channel2]", "led", 1, 0x20); + c.add_control(pid, "beatsync", "[Channel2]", "led", 2, 0x20); + c.add_control(pid, "beatlock", "[Channel2]", "led", 1, 0x02); + + c.add_control(pid, "fx", "[Channel1]", "led", 1, 0x04); + c.add_control(pid, "fx", "[Channel2]", "led", 1, 0x08); + c.add_control(pid, "hotcue", "[Channel2]", "led", 1, 0x10); + c.add_control(pid, "hotcue", "[Channel1]", "led", 1, 0x80); + c.add_control(pid, "loop", "[Channel1]", "led", 2, 0x40); + c.add_control(pid, "loop", "[Channel2]", "led", 2, 0x80); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// non-specific controller framework to allow hid packets to be defined and processed via +// callback functions - could/should be in a shared file +// + +HerculesMk2Hid.add_control = function(packetid, name, group, type, offset, mask) { + if (type == "led") { + HerculesMk2Hid.leds[group + name] = new HerculesMk2Hid.control(packetid, name, group, type, offset, mask); + } + else { + if (HerculesMk2Hid.controls[offset] == undefined) { + HerculesMk2Hid.controls[offset] = []; + } + HerculesMk2Hid.controls[offset].push(new HerculesMk2Hid.control(packetid, name, group, type, offset, mask)); + } +} + +// +// bind a function to a modified controller value +// + +HerculesMk2Hid.capture = function(name, values, func) { + if (HerculesMk2Hid.callbacks[name] == undefined) { + HerculesMk2Hid.callbacks[name] = [ [ values, func ] ]; + } + else { + HerculesMk2Hid.callbacks[name].push([ values, func ]); + } +} + +// +// bind a function to feedback from mixxx, callbacks accept args in same order as from capture() +// + +HerculesMk2Hid.feedback = function(g, e, f) { + engine.connectControl(g, e, "HerculesMk2Hid.feedbackData"); + if (HerculesMk2Hid.feedbacks[g + e] == undefined) { + HerculesMk2Hid.feedbacks[g + e] = []; + } + HerculesMk2Hid.feedbacks[g + e].push(f); +} + +// +// controller feedback: send data to the controller by name and automatically send out the full hid packet needed +// + +HerculesMk2Hid.send = function(g, e, v) { + if ((ctrl = this.leds[g + e]) != undefined) { + + // + // for the byte in the hid packet that this led control affects, mask out it's old value + // and then add in it's new one + // + + this.cache_out[ctrl.packetid][ctrl.offset] = this.cache_out[ctrl.packetid][ctrl.offset] & ctrl.maskinv | (v << ctrl.bitshift); + + // + // send complete hid packet and update our cache + // + + controller.send(this.cache_out[ctrl.packetid], this.cache_out[ctrl.packetid].length, 0); + this.cache_out[ctrl.packetid] = this.cache_out[ctrl.packetid]; + } +} + +// +// process incoming data from mixxx and call any callbacks +// + +HerculesMk2Hid.feedbackData = function(v, g, e) { + if (HerculesMk2Hid.feedbacks[g + e] != undefined) { + for (func in HerculesMk2Hid.feedbacks[g + e]) { + if (typeof(HerculesMk2Hid.feedbacks[g + e][func]) == "function") { + HerculesMk2Hid.feedbacks[g + e][func](g, e, v); + } + } + } +} + +// +// a single hid control, store last known value and offset/mask to work out the new value from incoming data +// + +HerculesMk2Hid.control = function(packetid, name, group, type, offset, mask) { + this.packetid = packetid; + this.name = name; + this.group = group; + this.type = type; + this.value = 0; + this.relative = 0; + this.offset = offset; + this.mask = mask; + this.maskinv = ~mask; + this.bitshift = 0; + this.maxval = 255; // needed for encoder, could guess from the mask + this.changed = function(value) { + value = (value & this.mask) >> this.bitshift; + if (this.value == value) { + return false; + } + else { + // map to a relative value if it's an encoder, usually +1 or -1 + if (this.type == 'encoder') { + this.relative = value - this.value; + if (this.relative > 100) { + this.relative -= this.maxval; + } + else if (this.relative < -100) { + this.relative += this.maxval; + } + } + this.value = value; + return true; + } + }; + while (mask != 0 && (mask & 0x1) == 0) { + mask = mask >> 1; + this.bitshift++; + } +} + +// +// process incoming data and call any callbacks if their bound controls have changed +// + +HerculesMk2Hid.incomingData = function (data, length) { + + var c = HerculesMk2Hid; + var packetid = data[0]; + + // + // iterate thru each byte and only check controls for that byte if the byte has changed + // + + for (i=1; i<length; i++) { + if ((c.cache_in[packetid] == undefined || data[i] != c.cache_in[packetid][i]) && c.controls[i] != undefined) { + + // + // a byte has changed, check any controls defined in that byte, more efficient + // than checking old+new values for all controls + // + + for (key in c.controls[i]) { + var control = c.controls[i][key]; + if (typeof(control) == 'object' && control.packetid == data[0] && control.changed(data[i])) { + + // + // we found a hid control that has changed value within that byte, check for callbacks + // + + var callbacks = c.callbacks[control.name]; + if (callbacks != undefined) { + for (var i=0; i<callbacks.length; i++) { + if (typeof(callbacks[i][1]) == 'function') { + + // + // check we need to call for this value change: all, press, release + // + + if (callbacks[i][0] == "all" || + (callbacks[i][0] == "press" && control.value > 0) || + (callbacks[i][0] == "release" && control.value == 0)) { + + // + // call a callback function for this control + // + + callbacks[i][1](control.group, control.name, control.value, control); + } + } + } + } + } + } + } + } + + // store the new raw data + c.cache_in[data[0]] = data; +} |