Merge lp:~zestoi/mixxx/controller_scripts into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Zestoi
Status: Merged
Merged at revision: 3213
Proposed branch: lp:~zestoi/mixxx/controller_scripts
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 3109 lines (+3045/-2)
9 files modified
mixxx/res/controllers/DJ-Tech CDJ-101.midi.xml (+196/-0)
mixxx/res/controllers/DJ-Tech DJM-101.midi.xml (+204/-0)
mixxx/res/controllers/DJ-Tech-CDJ-101-scripts.js (+245/-0)
mixxx/res/controllers/DJ-Tech-DJM-101-scripts.js (+110/-0)
mixxx/res/controllers/Hercules DJ Console Mk2.cntrlr.xml (+13/-0)
mixxx/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js (+611/-0)
mixxx/res/controllers/Novation Launchpad.midi.xml (+736/-0)
mixxx/res/controllers/Novation-Launchpad-scripts.js (+819/-0)
mixxx/res/controllers/common-controller-scripts.js (+111/-2)
To merge this branch: bzr merge lp:~zestoi/mixxx/controller_scripts
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+107519@code.launchpad.net

Description of the change

new controller mappings:

* hercules dj console mk2 (hid)
* djtech cdj101
* djtech djm101
* novation launchpad (full led feedback and a "virtual mixer" page)

updates to common-controller-scripts.js:

* new effects functions: script.spinback(), script.brake(), script.spinbackDefault(), script.brakeDefault()

* fix to script.pitch() as it wasn't returning the value from script.midiPitch() only calling it

To post a comment you must log in.
lp:~zestoi/mixxx/controller_scripts updated
3210. By Owen Williams

Fix rate range setting not getting saved properly (float equality testing fail)

3211. By RJ Skerry-Ryan

Use track-ids instead of TrackPointers to keep the recent tracks window.

3212. By RJ Skerry-Ryan

Merging from lp:~zestoi/mixxx/spinback_brake

3213. By RJ Skerry-Ryan

Merging from lp:~zestoi/mixxx/controller_scripts

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'mixxx/res/controllers/DJ-Tech CDJ-101.midi.xml'
2--- mixxx/res/controllers/DJ-Tech CDJ-101.midi.xml 1970-01-01 00:00:00 +0000
3+++ mixxx/res/controllers/DJ-Tech CDJ-101.midi.xml 2012-05-26 14:59:19 +0000
4@@ -0,0 +1,196 @@
5+<?xml version='1.0' encoding='utf-8'?>
6+<MixxxControllerPreset schemaVersion="1">
7+ <info>
8+ <name>DJTech CDJ 101</name>
9+ <author>zestoi</author>
10+ <description>Select midi channel 1 or 2 on the CDJ101 to select which deck to control in Mixxx. Hold down the push encoder for additional controls.</description>
11+ </info>
12+ <controller id="DJ-Tech CDJ-101">
13+ <scriptfiles>
14+ <file filename="DJ-Tech-CDJ-101-scripts.js" functionprefix="DJTechCDJ101"/>
15+ </scriptfiles>
16+
17+ <controls>
18+
19+ <!-- midi channel 1 for deck 1 -->
20+
21+ <!-- play/cue -->
22+
23+ <control>
24+ <group>[Channel1]</group>
25+ <key>DJTechCDJ101.play</key>
26+ <status>0x90</status>
27+ <midino>0x2a</midino>
28+ <options>
29+ <Script-Binding/>
30+ </options>
31+ </control>
32+
33+ <control>
34+ <group>[Channel1]</group>
35+ <key>DJTechCDJ101.cue</key>
36+ <status>0x90</status>
37+ <midino>0x2b</midino>
38+ <options>
39+ <Script-Binding/>
40+ </options>
41+ </control>
42+
43+ <!-- jog -->
44+
45+ <control>
46+ <group>[Channel1]</group>
47+ <status>0x90</status>
48+ <midino>0x20</midino>
49+ <key>DJTechCDJ101.jogtouch</key>
50+ <options>
51+ <Script-Binding/>
52+ </options>
53+ </control>
54+
55+ <control>
56+ <group>[Channel1]</group>
57+ <status>0xb0</status>
58+ <midino>0x35</midino>
59+ <key>DJTechCDJ101.jogouter</key>
60+ <options>
61+ <Script-Binding/>
62+ </options>
63+ </control>
64+
65+ <control>
66+ <group>[Channel1]</group>
67+ <status>0xb0</status>
68+ <midino>0x36</midino>
69+ <key>DJTechCDJ101.jogtop</key>
70+ <options>
71+ <Script-Binding/>
72+ </options>
73+ </control>
74+
75+ <!-- pitch -->
76+
77+ <control>
78+ <status>0xe0</status>
79+ <group>[Channel1]</group>
80+ <key>DJTechCDJ101.pitch</key>
81+ <options>
82+ <Script-Binding/>
83+ </options>
84+ </control>
85+
86+ <!-- track browse/load -->
87+
88+ <control>
89+ <group>[Channel1]</group>
90+ <key>DJTechCDJ101.push</key>
91+ <status>0x90</status>
92+ <midino>0x1F</midino>
93+ <options>
94+ <Script-Binding/>
95+ </options>
96+ </control>
97+
98+ <control>
99+ <group>[Channel1]</group>
100+ <key>DJTechCDJ101.browse</key>
101+ <status>0xB0</status>
102+ <midino>0x38</midino>
103+ <options>
104+ <Script-Binding/>
105+ </options>
106+ </control>
107+
108+ <!-- midi channel 2 for deck 2 -->
109+
110+ <!-- play/cue -->
111+
112+ <control>
113+ <group>[Channel2]</group>
114+ <key>DJTechCDJ101.play</key>
115+ <status>0x91</status>
116+ <midino>0x2a</midino>
117+ <options>
118+ <Script-Binding/>
119+ </options>
120+ </control>
121+
122+ <control>
123+ <group>[Channel2]</group>
124+ <key>DJTechCDJ101.cue</key>
125+ <status>0x91</status>
126+ <midino>0x2b</midino>
127+ <options>
128+ <Script-Binding/>
129+ </options>
130+ </control>
131+
132+ <!-- jog -->
133+
134+ <control>
135+ <group>[Channel2]</group>
136+ <status>0x91</status>
137+ <midino>0x20</midino>
138+ <key>DJTechCDJ101.jogtouch</key>
139+ <options>
140+ <Script-Binding/>
141+ </options>
142+ </control>
143+
144+ <control>
145+ <group>[Channel2]</group>
146+ <status>0xb1</status>
147+ <midino>0x35</midino>
148+ <key>DJTechCDJ101.jogouter</key>
149+ <options>
150+ <Script-Binding/>
151+ </options>
152+ </control>
153+
154+ <control>
155+ <group>[Channel2]</group>
156+ <status>0xb1</status>
157+ <midino>0x36</midino>
158+ <key>DJTechCDJ101.jogtop</key>
159+ <options>
160+ <Script-Binding/>
161+ </options>
162+ </control>
163+
164+ <!-- pitch -->
165+
166+ <control>
167+ <status>0xe1</status>
168+ <group>[Channel2]</group>
169+ <key>DJTechCDJ101.pitch</key>
170+ <options>
171+ <Script-Binding/>
172+ </options>
173+ </control>
174+
175+ <!-- track browse/load -->
176+
177+ <control>
178+ <group>[Channel2]</group>
179+ <key>DJTechCDJ101.push</key>
180+ <status>0x91</status>
181+ <midino>0x1F</midino>
182+ <options>
183+ <Script-Binding/>
184+ </options>
185+ </control>
186+
187+ <control>
188+ <group>[Channel1]</group>
189+ <key>DJTechCDJ101.browse</key>
190+ <status>0xB1</status>
191+ <midino>0x38</midino>
192+ <options>
193+ <Script-Binding/>
194+ </options>
195+ </control>
196+
197+ </controls>
198+
199+ </controller>
200+</MixxxControllerPreset>
201
202=== added file 'mixxx/res/controllers/DJ-Tech DJM-101.midi.xml'
203--- mixxx/res/controllers/DJ-Tech DJM-101.midi.xml 1970-01-01 00:00:00 +0000
204+++ mixxx/res/controllers/DJ-Tech DJM-101.midi.xml 2012-05-26 14:59:19 +0000
205@@ -0,0 +1,204 @@
206+<?xml version='1.0' encoding='utf-8'?>
207+<MixxxControllerPreset schemaVersion="1">
208+ <info>
209+ <name>DJTech DJM 101</name>
210+ <author>zestoi</author>
211+ <description>Mapping with full led feedback for the DJTech DJM 101</description>
212+ </info>
213+ <controller id="DJM-101">
214+ <scriptfiles>
215+ <file filename="DJ-Tech-DJM-101-scripts.js" functionprefix="DJTechDJM101"/>
216+ </scriptfiles>
217+ <controls>
218+
219+ <!-- global controls -->
220+
221+ <control>
222+ <group>[Master]</group>
223+ <key>headVolume</key>
224+ <status>0xB0</status>
225+ <midino>0x45</midino>
226+ <options>
227+ <normal/>
228+ </options>
229+ </control>
230+ <control>
231+ <group>[Master]</group>
232+ <key>headMix</key>
233+ <status>0xB0</status>
234+ <midino>0x44</midino>
235+ <options>
236+ <normal/>
237+ </options>
238+ </control>
239+
240+ <!-- master/pfl vumeter select -->
241+
242+ <control>
243+ <group>[Master]</group>
244+ <status>0x90</status>
245+ <midino>0x24</midino>
246+ <key>DJTechDJM101.vumeter_select_master</key>
247+ <options>
248+ <Script-Binding/>
249+ </options>
250+ </control>
251+
252+ <control>
253+ <group>[Master]</group>
254+ <status>0x90</status>
255+ <midino>0x23</midino>
256+ <key>DJTechDJM101.vumeter_select_pfl</key>
257+ <options>
258+ <Script-Binding/>
259+ </options>
260+ </control>
261+
262+ <control>
263+ <group>[Master]</group>
264+ <key>volume</key>
265+ <status>0xB0</status>
266+ <midino>0x43</midino>
267+ <options>
268+ <normal/>
269+ </options>
270+ </control>
271+
272+ <control>
273+ <group>[Master]</group>
274+ <key>crossfader</key>
275+ <status>0xB0</status>
276+ <midino>0x40</midino>
277+ <options>
278+ <normal/>
279+ </options>
280+ </control>
281+
282+ <!-- channel 1 -->
283+
284+ <control>
285+ <group>[Channel1]</group>
286+ <key>pregain</key>
287+ <status>0xB0</status>
288+ <midino>0x48</midino>
289+ <options>
290+ <normal/>
291+ </options>
292+ </control>
293+
294+ <control>
295+ <group>[Channel1]</group>
296+ <key>volume</key>
297+ <status>0xB0</status>
298+ <midino>0x41</midino>
299+ <options>
300+ <normal/>
301+ </options>
302+ </control>
303+
304+ <control>
305+ <group>[Channel1]</group>
306+ <key>pfl</key>
307+ <status>0x90</status>
308+ <midino>0x21</midino>
309+ <options>
310+ <normal/>
311+ </options>
312+ </control>
313+
314+ <!-- channel 1 eq -->
315+
316+ <control>
317+ <group>[Channel1]</group>
318+ <key>filterHigh</key>
319+ <status>0xB0</status>
320+ <midino>0x4A</midino>
321+ <options>
322+ <normal/>
323+ </options>
324+ </control>
325+ <control>
326+ <group>[Channel1]</group>
327+ <key>filterMid</key>
328+ <status>0xB0</status>
329+ <midino>0x4E</midino>
330+ <options>
331+ <normal/>
332+ </options>
333+ </control>
334+ <control>
335+ <group>[Channel1]</group>
336+ <key>filterLow</key>
337+ <status>0xB0</status>
338+ <midino>0x4C</midino>
339+ <options>
340+ <normal/>
341+ </options>
342+ </control>
343+
344+ <!-- channel 2 -->
345+
346+ <control>
347+ <group>[Channel2]</group>
348+ <key>pregain</key>
349+ <status>0xB0</status>
350+ <midino>0x4F</midino>
351+ <options>
352+ <normal/>
353+ </options>
354+ </control>
355+
356+ <control>
357+ <group>[Channel2]</group>
358+ <key>volume</key>
359+ <status>0xB0</status>
360+ <midino>0x42</midino>
361+ <options>
362+ <normal/>
363+ </options>
364+ </control>
365+
366+ <control>
367+ <group>[Channel2]</group>
368+ <key>pfl</key>
369+ <status>0x90</status>
370+ <midino>0x22</midino>
371+ <options>
372+ <normal/>
373+ </options>
374+ </control>
375+
376+ <!-- channel 2 eq -->
377+
378+ <control>
379+ <group>[Channel2]</group>
380+ <key>filterHigh</key>
381+ <status>0xB0</status>
382+ <midino>0x4D</midino>
383+ <options>
384+ <normal/>
385+ </options>
386+ </control>
387+ <control>
388+ <group>[Channel2]</group>
389+ <key>filterMid</key>
390+ <status>0xB0</status>
391+ <midino>0x4B</midino>
392+ <options>
393+ <normal/>
394+ </options>
395+ </control>
396+ <control>
397+ <group>[Channel2]</group>
398+ <key>filterLow</key>
399+ <status>0xB0</status>
400+ <midino>0x49</midino>
401+ <options>
402+ <normal/>
403+ </options>
404+ </control>
405+
406+ </controls>
407+ <outputs/>
408+ </controller>
409+</MixxxControllerPreset>
410
411=== added file 'mixxx/res/controllers/DJ-Tech-CDJ-101-scripts.js'
412--- mixxx/res/controllers/DJ-Tech-CDJ-101-scripts.js 1970-01-01 00:00:00 +0000
413+++ mixxx/res/controllers/DJ-Tech-CDJ-101-scripts.js 2012-05-26 14:59:19 +0000
414@@ -0,0 +1,245 @@
415+/****************************************************************/
416+/* DJ-Tech CDJ101 controller script */
417+/* For Mixxx version 1.11 */
418+/* Author: zestoi */
419+/****************************************************************/
420+//
421+// pitch slider works as you'd expect
422+// holding down the push button works as a "shift" to activate secondary functions
423+// needs the cdj101 to be on midi channel 1 for deck1 and 2 for deck2
424+//
425+// track playing:
426+//
427+// * surface of jog wheel: scratch
428+// * edge of jog wheel: tempo bend
429+// * shift + edge of jog wheel: fine tempo bend
430+//
431+// track not playing:
432+//
433+// * click push button: load a track
434+// * surface of jog wheel: scratch thru track
435+// * shift + surface of jog wheel: scan through track quickly
436+// * edge of jog wheel: jog though track slowly
437+// * shift + cue: move beatgrid
438+//
439+// track playing or not:
440+//
441+// * rotate push button: navigate tracks
442+// * shift + rotate push button: switch between playlists
443+// * cue button: default cue behaviour
444+// * play: toggle play
445+// * shift + play: sync
446+//
447+
448+DJTechCDJ101 = {};
449+DJTechCDJ101.pushmaxtime = 200;
450+DJTechCDJ101.pushed = false;
451+DJTechCDJ101.pushedon = 0;
452+DJTechCDJ101.outer2inner = false;
453+DJTechCDJ101.scratch_timeout = 100;
454+
455+DJTechCDJ101.init = function(id) {
456+ engine.connectControl("[Channel1]", "play", "DJTechCDJ101.play_feedback_deck1");
457+ engine.connectControl("[Channel2]", "play", "DJTechCDJ101.play_feedback_deck2");
458+ engine.connectControl("[Channel1]", "cue_default", "DJTechCDJ101.cue_feedback_deck1");
459+ engine.connectControl("[Channel2]", "cue_default", "DJTechCDJ101.cue_feedback_deck2");
460+}
461+
462+DJTechCDJ101.shutdown = function() {}
463+
464+//
465+// utility function
466+//
467+
468+DJTechCDJ101.mtime = function()
469+{
470+ var t = new Date();
471+ return t.getTime();
472+}
473+
474+//
475+// cue+play led feedback
476+//
477+
478+DJTechCDJ101.play_feedback_deck1 = function(value)
479+{
480+ midi.sendShortMsg(0x90, 0x2a, value > 0 ? 0x7f : 0);
481+}
482+
483+DJTechCDJ101.play_feedback_deck2 = function(value)
484+{
485+ midi.sendShortMsg(0x91, 0x2a, value > 0 ? 0x7f : 0);
486+}
487+
488+DJTechCDJ101.cue_feedback_deck1 = function(value)
489+{
490+ midi.sendShortMsg(0x90, 0x2b, value > 0 ? 0x7f : 0);
491+}
492+
493+DJTechCDJ101.cue_feedback_deck2 = function(value)
494+{
495+ midi.sendShortMsg(0x91, 0x2b, value > 0 ? 0x7f : 0);
496+}
497+
498+//
499+// translate 14bit pitchbend message into pitch control
500+//
501+
502+DJTechCDJ101.pitch = function(channel, lsb, msb, status, group)
503+{
504+ engine.setValue(group, "rate", script.pitch(lsb, msb, status)); // not working for some reason
505+ //engine.setValue(group, "rate", (8192 - (msb << 7 | lsb)) / 8192);
506+}
507+
508+DJTechCDJ101.beatjump = function(group, jump)
509+{
510+ jump = jump * 120 / engine.getValue(group, "bpm") / engine.getValue(group, "track_samples") * engine.getValue(group, "track_samplerate");
511+ engine.setValue(group, "playposition", engine.getValue(group, "playposition") + jump);
512+}
513+
514+//
515+// hold down encoder and turn to change playlists
516+// turn while not pushed in to scroll through tracks
517+// click to load track
518+//
519+
520+DJTechCDJ101.browse = function(channel, control, value, status, group)
521+{
522+ if (DJTechCDJ101.pushed) {
523+ engine.setValue("[Playlist]", value == 0x41 ? "SelectNextPlaylist" : "SelectPrevPlaylist", 1);
524+ }
525+ else {
526+ engine.setValue("[Playlist]", value == 0x41 ? "SelectNextTrack" : "SelectPrevTrack", 1);
527+ }
528+}
529+
530+DJTechCDJ101.push = function(channel, control, value, status, group)
531+{
532+ if (value > 0) {
533+ DJTechCDJ101.pushed = true;
534+ DJTechCDJ101.pushedon = DJTechCDJ101.mtime();
535+ }
536+ else {
537+
538+ //
539+ // load selected track if released quickly enough
540+ //
541+
542+ if (DJTechCDJ101.mtime() - DJTechCDJ101.pushedon < DJTechCDJ101.pushmaxtime) {
543+ engine.setValue(group, "LoadSelectedTrack", 1);
544+ }
545+
546+ DJTechCDJ101.pushed = false;
547+ DJTechCDJ101.pushedon = 0;
548+ }
549+}
550+
551+//
552+// when deck is not playing and the push button is held, pressing cue will move the beatgrid
553+//
554+
555+DJTechCDJ101.cue = function(channel, control, value, status, group)
556+{
557+ if (DJTechCDJ101.pushed && !engine.getValue(group, "play")) {
558+ engine.setValue(group, "beats_translate_curpos", value > 0 ? 1 : 0);
559+ }
560+ else {
561+ engine.setValue(group, "cue_default", value > 0 ? 1 : 0);
562+ }
563+}
564+
565+//
566+// when the push button is held, pressing play will sync
567+//
568+
569+DJTechCDJ101.play = function(channel, control, value, status, group)
570+{
571+ if (DJTechCDJ101.pushed) {
572+ engine.setValue(group, "beatsync", value > 0 ? 1 : 0);
573+ }
574+ else if (value > 0) {
575+ //
576+ // we want play to toggle
577+ //
578+
579+ engine.setValue(group, "play", !engine.getValue(group, "play"));
580+ }
581+}
582+
583+//
584+// when deck is playing either enable or disable scratch mode, no action otherwise
585+//
586+
587+DJTechCDJ101.jogtouch = function(channel, control, value, status, group)
588+{
589+ var deck = parseInt(group.substring(8,9));
590+
591+ if (value > 0) {
592+ engine.scratchEnable(deck, 143, 45, 0.125, 0.125/32);
593+ }
594+ else {
595+ //
596+ // don't disable right away in case of spin backs if playing and also morph
597+ // outer jog movements into top jog movements during this time
598+ //
599+
600+ if (!engine.getValue(group, "play")) {
601+ DJTechCDJ101.finishScratch(deck, false);
602+ }
603+ else {
604+ DJTechCDJ101.outer2inner = true;
605+ DJTechCDJ101.scratch_timer = engine.beginTimer(DJTechCDJ101.scratch_timeout, 'DJTechCDJ101.finishScratch(' + deck + ', true)');
606+ }
607+ }
608+}
609+
610+DJTechCDJ101.finishScratch = function(deck, stop_timer)
611+{
612+ if (stop_timer) {
613+ engine.stopTimer(DJTechCDJ101.scratch_timer);
614+ }
615+ engine.scratchDisable(deck);
616+ DJTechCDJ101.outer2inner = false;
617+}
618+
619+//
620+// use outer part of jog to pitchbend when in play mode and fine track seek when not
621+//
622+
623+DJTechCDJ101.jogouter = function(channel, control, value, status, group)
624+{
625+ //
626+ // when touch surface has been release for a short time convert
627+ // outer jog movements into surface jog ones for spinbacks etc
628+ //
629+
630+ if (DJTechCDJ101.outer2inner) {
631+ return DJTechCDJ101.jogtop(channel, control, value, status, group);
632+ }
633+
634+ //
635+ // scale down based on whether we are playing and the shift is held down
636+ //
637+
638+ value = (value - 0x40) / 2;
639+ if (DJTechCDJ101.pushed) value /= 2.5;
640+ if (!engine.getValue(group, "play")) value /= 2.5;
641+ engine.setValue(group, "jog", value);
642+}
643+
644+//
645+// track playing: top of jog scratches
646+// track not playing: seek thru track (using same scratch ticks and gives more postive response) or seek faster through track when push button held down
647+//
648+
649+DJTechCDJ101.jogtop = function(channel, control, value, status, group)
650+{
651+ value -= 0x40;
652+ if (!engine.getValue(group, "play") && DJTechCDJ101.pushed) {
653+ DJTechCDJ101.beatjump(group, value);
654+ }
655+ else {
656+ engine.scratchTick(parseInt(group.substring(8,9)), value);
657+ }
658+}
659+
660
661=== added file 'mixxx/res/controllers/DJ-Tech-DJM-101-scripts.js'
662--- mixxx/res/controllers/DJ-Tech-DJM-101-scripts.js 1970-01-01 00:00:00 +0000
663+++ mixxx/res/controllers/DJ-Tech-DJM-101-scripts.js 2012-05-26 14:59:19 +0000
664@@ -0,0 +1,110 @@
665+/****************************************************************/
666+/* DJ-Tech DJM101 controller script */
667+/* For Mixxx version 1.11 */
668+/* Author: zestoi */
669+/****************************************************************/
670+
671+DJTechDJM101 = {};
672+DJTechDJM101.vumeter = {};
673+DJTechDJM101.vumeter[1] = 0;
674+DJTechDJM101.vumeter[2] = 0;
675+
676+DJTechDJM101.init = function(id) {
677+ DJTechDJM101.vumeter_select(true);
678+ engine.connectControl("[Master]", "VuMeterL", "DJTechDJM101.VuMeterMasterL");
679+ engine.connectControl("[Master]", "VuMeterR", "DJTechDJM101.VuMeterMasterR");
680+ engine.connectControl("[Channel1]", "VuMeter", "DJTechDJM101.VuMeterDeck1");
681+ engine.connectControl("[Channel2]", "VuMeter", "DJTechDJM101.VuMeterDeck2");
682+ engine.connectControl("[Channel1]", "pfl", "DJTechDJM101.pfl");
683+ engine.connectControl("[Channel2]", "pfl", "DJTechDJM101.pfl");
684+}
685+
686+DJTechDJM101.shutdown = function() {}
687+
688+//
689+// change output to vumeter and reset
690+//
691+
692+DJTechDJM101.vumeter_select = function(master)
693+{
694+ DJTechDJM101.vumeter_master_mode = master;
695+ DJTechDJM101.update_vumeter(1, 0);
696+ DJTechDJM101.update_vumeter(2, 0);
697+}
698+
699+//
700+// select what data is sent to the vumeter
701+//
702+
703+DJTechDJM101.vumeter_select_master = function(channel, control, value, status, group)
704+{
705+ if (value > 0) {
706+ DJTechDJM101.vumeter_select(true);
707+ }
708+}
709+
710+DJTechDJM101.vumeter_select_pfl = function(channel, control, value, status, group)
711+{
712+ if (value > 0) {
713+ DJTechDJM101.vumeter_select(false);
714+ }
715+}
716+
717+//
718+// update a vumeter channel
719+//
720+
721+DJTechDJM101.update_vumeter = function(channel, value)
722+{
723+ var newval = parseInt(value * 0xf7);
724+ if (DJTechDJM101.vumeter[channel] != newval) {
725+ DJTechDJM101.vumeter[channel] = newval;
726+
727+ //
728+ // a bit nasty - four different cc's depending on master/pfl mode and which channel
729+ //
730+
731+ midi.sendShortMsg(0xb0, DJTechDJM101.vumeter_master_mode ? 0x4f + channel : 0x51 + channel, newval);
732+ }
733+}
734+
735+//
736+// only feed the correct levels to each channel of the vumeter
737+//
738+
739+DJTechDJM101.VuMeterMasterL = function(value)
740+{
741+ if (DJTechDJM101.vumeter_master_mode == false) return;
742+ DJTechDJM101.update_vumeter(1, value);
743+}
744+
745+DJTechDJM101.VuMeterMasterR = function(value)
746+{
747+ if (DJTechDJM101.vumeter_master_mode == false) return;
748+ DJTechDJM101.update_vumeter(2, value);
749+}
750+
751+DJTechDJM101.VuMeterDeck1 = function(value)
752+{
753+ if (DJTechDJM101.vumeter_master_mode == true) return;
754+ DJTechDJM101.update_vumeter(1, value);
755+}
756+
757+DJTechDJM101.VuMeterDeck2 = function(value)
758+{
759+ if (DJTechDJM101.vumeter_master_mode == true) return;
760+ DJTechDJM101.update_vumeter(2, value);
761+}
762+
763+//
764+// led feedback for headphone cue buttons
765+//
766+
767+DJTechDJM101.pfl = function(value, group)
768+{
769+ // has to be a noteon msg in both case and seems more reliable issuing 0x7f for 'on' amd 0x0 for 'off'
770+ midi.sendShortMsg(0x90, group == "[Channel1]" ? 0x21 : 0x22, value > 0 ? 0x7f : 0);
771+}
772+
773+
774+
775
776=== added file 'mixxx/res/controllers/Hercules DJ Console Mk2.cntrlr.xml'
777--- mixxx/res/controllers/Hercules DJ Console Mk2.cntrlr.xml 1970-01-01 00:00:00 +0000
778+++ mixxx/res/controllers/Hercules DJ Console Mk2.cntrlr.xml 2012-05-26 14:59:19 +0000
779@@ -0,0 +1,13 @@
780+<?xml version="1.0" encoding="utf-8"?>
781+<MixxxControllerPreset schemaVersion="1">
782+ <info>
783+ <name>Hercules DJ Console MK2 HID</name>
784+ <author>zestoi</author>
785+ <description>Native high-resolution HID script for the Hercules DJ Console MK2 controller.</description>
786+ </info>
787+ <controller id="Hercules DJ Console MK2 HID">
788+ <scriptfiles>
789+ <file filename="Hercules-DJ-Console-Mk2-hid-scripts.js" functionprefix="HerculesMk2Hid"/>
790+ </scriptfiles>
791+ </controller>
792+</MixxxControllerPreset>
793
794=== added file 'mixxx/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js'
795--- mixxx/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js 1970-01-01 00:00:00 +0000
796+++ mixxx/res/controllers/Hercules-DJ-Console-Mk2-hid-scripts.js 2012-05-26 14:59:19 +0000
797@@ -0,0 +1,611 @@
798+/****************************************************************/
799+/* Hercules DJ Console Mk2 HID controller script */
800+/* For Mixxx version 1.11 */
801+/* Author: zestoi */
802+/****************************************************************/
803+
804+HerculesMk2Hid = new Controller();
805+HerculesMk2Hid.controls = [];
806+HerculesMk2Hid.leds = [];
807+HerculesMk2Hid.cache_in = [];
808+HerculesMk2Hid.cache_out = [];
809+HerculesMk2Hid.callbacks = [];
810+HerculesMk2Hid.feedbacks = [];
811+HerculesMk2Hid.layer = [ "fx", "fx" ]; // fx, hotcue, loop, kill
812+HerculesMk2Hid.tempo_scaling = 400;
813+HerculesMk2Hid.beatjump_size = 8;
814+HerculesMk2Hid.kills = [ { filterHighKill: 0, filterMidKill: 0, filterLowKill: 0 }, { filterHighKill: 0, filterMidKill: 0, filterLowKill: 0 } ];
815+HerculesMk2Hid.loop_lengths = [ 0.25, 0.5, 1 ]; // edit to loop size needed
816+HerculesMk2Hid.kill_order = [ "filterHighKill", "filterMidKill", "filterLowKill" ]; // edit if needed
817+HerculesMk2Hid.scratch_enabled = { "[Channel1]": false, "[Channel2]": false };
818+HerculesMk2Hid.jog_skip = { "[Channel1]": true, "[Channel2]": true };
819+HerculesMk2Hid.shift = false; // either autobeat button
820+
821+//
822+// the actual mapping is defined in this function
823+//
824+
825+HerculesMk2Hid.init = function() {
826+
827+ var c = HerculesMk2Hid;
828+
829+ //
830+ // define the hid packet
831+ //
832+
833+ c.define_hid_format();
834+
835+ //
836+ // create the actual mapping
837+ // deck controls (will work for any decks as the group is passed in)
838+ //
839+
840+ c.capture("crossfader", "all", function(g, e, v) { engine.setValue(g, e, (v - 128) / 128); });
841+
842+ c.capture("play", "press", function(g, e, v) { engine.setValue(g, e, !engine.getValue(g, e)); });
843+ c.capture("cue_default", "all", function(g, e, v) { engine.setValue(g, e, v); });
844+ c.capture("beatsync", "all", function(g, e, v) { HerculesMk2Hid.shift = v > 0; engine.setValue(g, e, v); });
845+
846+ c.capture("volume", "all", function(g, e, v) { engine.setValue(g, e, v / 256); });
847+ c.capture("filterHigh", "all", function(g, e, v) { engine.setValue(g, e, v / 128); });
848+ c.capture("filterMid", "all", function(g, e, v) { engine.setValue(g, e, v / 128); });
849+ c.capture("filterLow", "all", function(g, e, v) { engine.setValue(g, e, v / 128); });
850+
851+ c.capture("jog", "all", function(g, e, v, ctrl) {
852+ // skip initial jog values
853+ if (HerculesMk2Hid.jog_skip[g]) {
854+ HerculesMk2Hid.jog_skip[g] = false;
855+ return;
856+ }
857+
858+ // scratch mode
859+ if (HerculesMk2Hid.scratch_enabled[g]) {
860+ engine.scratchTick(parseInt(g.substring(8,9)), ctrl.relative);
861+ }
862+
863+ // fine jog mode when playing
864+ else if (engine.getValue(g, "play")) {
865+ engine.setValue(g, e, ctrl.relative/2);
866+ }
867+
868+ // track browsing when shift held (sync) and not playing
869+ else if (HerculesMk2Hid.shift) {
870+ engine.setValue("[Playlist]", "SelectTrackKnob", ctrl.relative);
871+ }
872+
873+ // normal jog mode when not playing
874+ else {
875+ engine.setValue(g, e, ctrl.relative);
876+ }
877+ });
878+
879+ //
880+ // double up pitch bend buttons as beatjumps when the track is stopped
881+ //
882+
883+ c.capture("pitchbend_down", "all", function(g, e, v) {
884+ if (engine.getValue(g, "play") == 0) {
885+ engine.setValue(g, "back", v > 0 ? 1 : 0);
886+ }
887+ else if (v > 0) {
888+ engine.setValue(g, "jog", -3);
889+ }
890+ });
891+
892+ c.capture("pitchbend_up", "all", function(g, e, v) {
893+ if (engine.getValue(g, "play") == 0) {
894+ engine.setValue(g, "fwd", v > 0 ? 1 : 0);
895+ }
896+ else if (v > 0) {
897+ engine.setValue(g, "jog", 3);
898+ }
899+ });
900+
901+ //
902+ // track browsing - don't use the joystick or track load buttons as they function as a normal mouse too
903+ // (map joystick_y/joystick_x/load if you want to use the joystick)
904+ //
905+
906+ c.capture("track_previous_a", "all", c.scroll_tracks);
907+ c.capture("track_next_a", "all", c.scroll_tracks);
908+ c.capture("track_previous_b", "press", function(g, e, v) { engine.setValue("[Channel1]", "LoadSelectedTrack", 1); });
909+ c.capture("track_next_b", "press", function(g, e, v) { engine.setValue("[Channel2]", "LoadSelectedTrack", 1); });
910+
911+ //
912+ // uncomment this code and the function HerculesMk2Hid.scroll_tracks_joystick() below if you really want the joystick
913+ // to be used for track browsing and the left/right joystick buttons for track loading
914+ //
915+
916+ /*
917+ c.capture("load", "press", function(g, e, v) { engine.setValue(g, "LoadSelectedTrack", 1); });
918+
919+ c.capture("joystick_y", "all", function(g, e, v) {
920+ if (v == 128) {
921+ HerculesMk2Hid.direction = 0;
922+ if (HerculesMk2Hid.track_timer) {
923+ engine.stopTimer(HerculesMk2Hid.track_timer);
924+ HerculesMk2Hid.track_timer = null;
925+ }
926+ }
927+ else {
928+ if (v > 128) {
929+ HerculesMk2Hid.direction = 1;
930+ v -= 128;
931+ }
932+ else {
933+ HerculesMk2Hid.direction = -1;
934+ v = 128 - v;
935+ }
936+
937+ if (v < 30) v = 30;
938+ if (HerculesMk2Hid.track_timer) engine.stopTimer(HerculesMk2Hid.track_timer);
939+ HerculesMk2Hid.track_timer = engine.beginTimer(parseInt(5120 / v), 'HerculesMk2Hid.scroll_tracks_joystick');
940+ }
941+ });
942+ */
943+
944+ //
945+ // tempo encoder
946+ //
947+
948+ c.capture("rate", "all", function(g, e, v, ctrl) {
949+ var rate = engine.getValue(g, "rate") + ctrl.relative / c.tempo_scaling;
950+ if (rate > 1) rate = 1; else if (rate < -1) rate = -1;
951+ engine.setValue(g, e, rate);
952+ });
953+
954+ //
955+ // enable/disable scratching with the beatlock buttons (as jogs are non touch sensitive)
956+ //
957+
958+ c.capture("beatlock", "press", function(g, e, v) {
959+
960+ HerculesMk2Hid.scratch_enabled[g] = !HerculesMk2Hid.scratch_enabled[g];
961+
962+ if (HerculesMk2Hid.scratch_enabled[g]) {
963+ engine.scratchEnable(parseInt(g.substring(8,9)), 64, 45, 0.125, 0.125/32);
964+ }
965+ else {
966+ engine.scratchDisable(parseInt(g.substring(8,9)));
967+ }
968+
969+ c.send(g, e, HerculesMk2Hid.scratch_enabled[g] ? 1 : 0);
970+ });
971+
972+ //
973+ // toggle between fx, hotcue and loop modes and update leds
974+ //
975+
976+ c.capture("layer_select", "press", function(g, e, v) {
977+ var deck = parseInt(g.substring(8,9));
978+ c.send(g, "fx", 0);
979+ c.send(g, "hotcue", 0);
980+ c.send(g, "loop", 0);
981+ switch (c.layer[deck-1]) {
982+ case "fx": c.layer[deck-1] = "hotcue"; break;
983+ case "hotcue": c.layer[deck-1] = "loop"; break;
984+ case "loop":
985+ c.layer[deck-1] = "kill";
986+ c.send(g, "fx", !c.kills[deck-1]['filterHighKill']);
987+ c.send(g, "hotcue", !c.kills[deck-1]['filterMidKill']);
988+ c.send(g, "loop", !c.kills[deck-1]['filterLowKill']);
989+ break;
990+ case "kill":
991+ c.layer[deck-1] = "fx";
992+ }
993+ if (c.layer[deck-1] != "kill") {
994+ c.send(g, c.layer[deck-1], 1);
995+ }
996+ });
997+
998+ c.capture("layer_btn1", "all", function(g, e, v) { c.layer_btn(g, e, v); });
999+ c.capture("layer_btn2", "all", function(g, e, v) { c.layer_btn(g, e, v); });
1000+ c.capture("layer_btn3", "all", function(g, e, v) { c.layer_btn(g, e, v); });
1001+
1002+ //
1003+ // headphone cue
1004+ //
1005+
1006+ c.capture("monitor_a", "all", function(g, e, v) { engine.setValue("[Channel1]", "pfl", v); });
1007+ c.capture("monitor_b", "all", function(g, e, v) { engine.setValue("[Channel2]", "pfl", v); });
1008+ c.capture("monitor_both", "all", function(g, e, v) {
1009+ engine.setValue("[Channel1]", "pfl", v);
1010+ engine.setValue("[Channel2]", "pfl", v);
1011+ });
1012+
1013+ //
1014+ // led feedback
1015+ //
1016+
1017+ c.feedback("[Channel1]", "play", function(g, e, v) { c.send(g, e, v); });
1018+ c.feedback("[Channel2]", "play", function(g, e, v) { c.send(g, e, v); });
1019+ c.feedback("[Channel1]", "cue_default", function(g, e, v) { c.send(g, e, v); });
1020+ c.feedback("[Channel2]", "cue_default", function(g, e, v) { c.send(g, e, v); });
1021+ c.feedback("[Channel1]", "beatsync", function(g, e, v) { c.send(g, e, v); });
1022+ c.feedback("[Channel2]", "beatsync", function(g, e, v) { c.send(g, e, v); });
1023+
1024+ //
1025+ // kill status
1026+ //
1027+
1028+ c.feedback("[Channel1]", "filterHighKill", c.kill_status);
1029+ c.feedback("[Channel1]", "filterMidKill", c.kill_status);
1030+ c.feedback("[Channel1]", "filterLowKill", c.kill_status);
1031+
1032+ //
1033+ // clear/setup any initial leds
1034+ //
1035+
1036+ for (id in c.leds) {
1037+ c.send(c.leds[id].group, c.leds[id].name, 0);
1038+ }
1039+
1040+ c.send("[Channel1]", "fx", 1);
1041+ c.send("[Channel2]", "fx", 1);
1042+}
1043+
1044+//
1045+// map the 6 buttons that control either effects, hotcues, loops or kills
1046+//
1047+
1048+HerculesMk2Hid.layer_btn = function(g, e, v) {
1049+ var deck = parseInt(g.substring(8,9));
1050+ var btn = parseInt(e.substring(9,10));
1051+ switch (HerculesMk2Hid.layer[deck-1]) {
1052+ case "fx":
1053+ switch (btn) {
1054+ case 1:
1055+ engine.setValue("[Flanger]", "lfoDepth", 1);
1056+ engine.setValue("[Flanger]", "lfoPeriod", 500000);
1057+ engine.setValue("[Flanger]", "lfoDelay", 666);
1058+ engine.setValue(g, "flanger", v > 0);
1059+ break;
1060+ case 2:
1061+ script.spinback(g, v > 0);
1062+ break;
1063+ case 3:
1064+ script.brake(g, v > 0);
1065+ }
1066+ break;
1067+ case "hotcue":
1068+ engine.setValue(g, "hotcue_" + btn + "_activate", v > 0 ? 1 : 0);
1069+ break;
1070+ case "loop":
1071+ var len = HerculesMk2Hid.loop_lengths[btn-1];
1072+ engine.setValue(g, "beatloop_" + len + "_toggle", 1);
1073+ break;
1074+ case 'kill':
1075+ if (v > 0) {
1076+ engine.setValue(g, HerculesMk2Hid.kill_order[btn-1], !engine.getValue(g, HerculesMk2Hid.kill_order[btn-1]));
1077+ }
1078+ }
1079+}
1080+
1081+//
1082+// beatjump - will get out of sync if called while deck is playing
1083+//
1084+
1085+HerculesMk2Hid.beatjump = function(group, jump) {
1086+ jump = jump * 120 / engine.getValue(group, "bpm") / engine.getValue(group, "track_samples") * engine.getValue(group, "track_samplerate");
1087+ engine.setValue(group, "playposition", engine.getValue(group, "playposition") + jump);
1088+}
1089+
1090+//
1091+// playlist scroll nex/previous with auto-repeat when held
1092+//
1093+
1094+HerculesMk2Hid.scroll_tracks = function(g, e, v) {
1095+ if (v > 0) {
1096+ engine.setValue("[Playlist]", e == "track_next_a" ? "SelectNextTrack" : "SelectPrevTrack", 1);
1097+ if (!HerculesMk2Hid.scroll_timer) {
1098+ HerculesMk2Hid.scroll_timer = engine.beginTimer(150, 'HerculesMk2Hid.scroll_tracks("[Playlist]","' + e + '",' + v + ')');
1099+ }
1100+ }
1101+ else {
1102+ if (HerculesMk2Hid.scroll_timer) {
1103+ engine.stopTimer(HerculesMk2Hid.scroll_timer);
1104+ HerculesMk2Hid.scroll_timer = null;
1105+ }
1106+ }
1107+}
1108+
1109+//
1110+// eq kill status
1111+//
1112+
1113+HerculesMk2Hid.kill_status = function(g, e, v) {
1114+ var deck = parseInt(g.substring(8,9));
1115+ HerculesMk2Hid.kills[deck-1][e] = v;
1116+
1117+ //
1118+ // update leds with kill status if we're on that layer
1119+ //
1120+
1121+ if (HerculesMk2Hid.layer[deck-1] == "kill") {
1122+ switch (e) {
1123+ case 'filterHighKill': HerculesMk2Hid.send(g, "fx", !v); break;
1124+ case 'filterMidKill': HerculesMk2Hid.send(g, "hotcue", !v); break;
1125+ case 'filterLowKill': HerculesMk2Hid.send(g, "loop", !v);
1126+ }
1127+ }
1128+}
1129+
1130+/*
1131+HerculesMk2Hid.scroll_tracks_joystick = function() {
1132+ engine.setValue("[Playlist]", HerculesMk2Hid.direction > 0 ? "SelectNextTrack" : "SelectPrevTrack", 1);
1133+}
1134+*/
1135+
1136+
1137+////////////////////////////////////////////////////////////////////////////////////////////////////
1138+// define the hid packet to event mapping, could be defined via xml so can be used in multiple mappings
1139+// naming the controls as much as possible inline with the mixxx engine names makes most mappings trivial
1140+//
1141+
1142+HerculesMk2Hid.define_hid_format = function() {
1143+
1144+ var c = HerculesMk2Hid;
1145+ var pid = 0x1;
1146+
1147+ // deck 1
1148+
1149+ c.add_control(pid, "play", "[Channel1]", "button", 1, 0x80)
1150+ c.add_control(pid, "cue_default", "[Channel1]", "button", 2, 0x01)
1151+ c.add_control(pid, "track_previous_a", "[Channel1]", "button", 2, 0x04)
1152+ c.add_control(pid, "track_next_a", "[Channel1]", "button", 2, 0x08)
1153+ c.add_control(pid, "beatsync", "[Channel1]", "button", 2, 0x02)
1154+ c.add_control(pid, "pitchbend_down", "[Channel1]", "button", 3, 0x08)
1155+ c.add_control(pid, "pitchbend_up", "[Channel1]", "button", 3, 0x04)
1156+ c.add_control(pid, "load", "[Channel1]", "button", 4, 0x04)
1157+ c.add_control(pid, "beatlock", "[Channel1]", "button", 3, 0x20)
1158+ c.add_control(pid, "source", "[Channel1]", "button", 4, 0x10)
1159+ c.add_control(pid, "filterLow", "[Channel1]", "fader", 9, 0xff)
1160+ c.add_control(pid, "filterMid", "[Channel1]", "fader", 10, 0xff)
1161+ c.add_control(pid, "filterHigh", "[Channel1]", "fader", 11, 0xff)
1162+ c.add_control(pid, "volume", "[Channel1]", "fader", 13, 0xff)
1163+ c.add_control(pid, "rate", "[Channel1]", "encoder", 15, 0xff)
1164+ c.add_control(pid, "jog", "[Channel1]", "encoder", 17, 0xff)
1165+ c.add_control(pid, "layer_select", "[Channel1]", "button", 1, 0x40)
1166+ c.add_control(pid, "layer_btn1", "[Channel1]", "button", 2, 0x40)
1167+ c.add_control(pid, "layer_btn2", "[Channel1]", "button", 2, 0x20)
1168+ c.add_control(pid, "layer_btn3", "[Channel1]", "button", 2, 0x10)
1169+
1170+ // deck 2
1171+
1172+ c.add_control(pid, "play", "[Channel2]", "button", 1, 0x02)
1173+ c.add_control(pid, "cue_default", "[Channel2]", "button", 1, 0x04)
1174+ c.add_control(pid, "track_previous_b", "[Channel2]", "button", 1, 0x10)
1175+ c.add_control(pid, "track_next_b", "[Channel2]", "button", 1, 0x20)
1176+ c.add_control(pid, "beatsync", "[Channel2]", "button", 1, 0x08)
1177+ c.add_control(pid, "pitchbend_down", "[Channel2]", "button", 3, 0x80)
1178+ c.add_control(pid, "pitchbend_up", "[Channel2]", "button", 3, 0x40)
1179+ c.add_control(pid, "load", "[Channel2]", "button", 4, 0x08)
1180+ c.add_control(pid, "beatlock", "[Channel2]", "button", 4, 0x02)
1181+ c.add_control(pid, "source", "[Channel2]", "button", 4, 0x20)
1182+ c.add_control(pid, "filterLow", "[Channel2]", "fader", 6, 0xff)
1183+ c.add_control(pid, "filterMid", "[Channel2]", "fader", 7, 0xff)
1184+ c.add_control(pid, "filterHigh", "[Channel2]", "fader", 8, 0xff)
1185+ c.add_control(pid, "volume", "[Channel2]", "fader", 14, 0xff)
1186+ c.add_control(pid, "rate", "[Channel2]", "encoder", 16, 0xff)
1187+ c.add_control(pid, "jog", "[Channel2]", "encoder", 18, 0xff)
1188+ c.add_control(pid, "layer_select", "[Channel2]", "button", 1, 0x01)
1189+ c.add_control(pid, "layer_btn1", "[Channel2]", "button", 2, 0x80)
1190+ c.add_control(pid, "layer_btn2", "[Channel2]", "button", 3, 0x01)
1191+ c.add_control(pid, "layer_btn3", "[Channel2]", "button", 3, 0x02)
1192+
1193+ // master
1194+
1195+ c.add_control(pid, "crossfader", "[Master]", "fader", 12, 0xff)
1196+ c.add_control(pid, "joystick_x", "[Master]", "fader", 19, 0xff)
1197+ c.add_control(pid, "joystick_y", "[Master]", "fader", 20, 0xff)
1198+
1199+ // headphone cue
1200+
1201+ c.add_control(pid, "monitor_a", "[Master]", "button", 5, 0x1);
1202+ c.add_control(pid, "monitor_b", "[Master]", "button", 5, 0x2);
1203+ //c.add_control(pid, "monitor_both", "[Master]", "button", 5, 0x4);
1204+ c.add_control(pid, "monitor_both", "[Master]", "button", 5, 0x8);
1205+
1206+ // define led feedback
1207+
1208+ pid = 0;
1209+ c.cache_out[pid] = [ pid, 0x0, 0x0, 0x0 ];
1210+
1211+ c.add_control(pid, "play", "[Channel1]", "led", 2, 0x01); // blinking: 3, 0x2
1212+ c.add_control(pid, "cue_default", "[Channel1]", "led", 2, 0x08);
1213+ c.add_control(pid, "beatsync", "[Channel1]", "led", 2, 0x10);
1214+ c.add_control(pid, "beatlock", "[Channel1]", "led", 1, 0x01);
1215+
1216+ c.add_control(pid, "play", "[Channel2]", "led", 1, 0x40); // blinking: 3, 0x1
1217+ c.add_control(pid, "cue_default", "[Channel2]", "led", 1, 0x20);
1218+ c.add_control(pid, "beatsync", "[Channel2]", "led", 2, 0x20);
1219+ c.add_control(pid, "beatlock", "[Channel2]", "led", 1, 0x02);
1220+
1221+ c.add_control(pid, "fx", "[Channel1]", "led", 1, 0x04);
1222+ c.add_control(pid, "fx", "[Channel2]", "led", 1, 0x08);
1223+ c.add_control(pid, "hotcue", "[Channel2]", "led", 1, 0x10);
1224+ c.add_control(pid, "hotcue", "[Channel1]", "led", 1, 0x80);
1225+ c.add_control(pid, "loop", "[Channel1]", "led", 2, 0x40);
1226+ c.add_control(pid, "loop", "[Channel2]", "led", 2, 0x80);
1227+}
1228+
1229+////////////////////////////////////////////////////////////////////////////////////////////////////
1230+// non-specific controller framework to allow hid packets to be defined and processed via
1231+// callback functions - could/should be in a shared file
1232+//
1233+
1234+HerculesMk2Hid.add_control = function(packetid, name, group, type, offset, mask) {
1235+ if (type == "led") {
1236+ HerculesMk2Hid.leds[group + name] = new HerculesMk2Hid.control(packetid, name, group, type, offset, mask);
1237+ }
1238+ else {
1239+ if (HerculesMk2Hid.controls[offset] == undefined) {
1240+ HerculesMk2Hid.controls[offset] = [];
1241+ }
1242+ HerculesMk2Hid.controls[offset].push(new HerculesMk2Hid.control(packetid, name, group, type, offset, mask));
1243+ }
1244+}
1245+
1246+//
1247+// bind a function to a modified controller value
1248+//
1249+
1250+HerculesMk2Hid.capture = function(name, values, func) {
1251+ if (HerculesMk2Hid.callbacks[name] == undefined) {
1252+ HerculesMk2Hid.callbacks[name] = [ [ values, func ] ];
1253+ }
1254+ else {
1255+ HerculesMk2Hid.callbacks[name].push([ values, func ]);
1256+ }
1257+}
1258+
1259+//
1260+// bind a function to feedback from mixxx, callbacks accept args in same order as from capture()
1261+//
1262+
1263+HerculesMk2Hid.feedback = function(g, e, f) {
1264+ engine.connectControl(g, e, "HerculesMk2Hid.feedbackData");
1265+ if (HerculesMk2Hid.feedbacks[g + e] == undefined) {
1266+ HerculesMk2Hid.feedbacks[g + e] = [];
1267+ }
1268+ HerculesMk2Hid.feedbacks[g + e].push(f);
1269+}
1270+
1271+//
1272+// controller feedback: send data to the controller by name and automatically send out the full hid packet needed
1273+//
1274+
1275+HerculesMk2Hid.send = function(g, e, v) {
1276+ if ((ctrl = this.leds[g + e]) != undefined) {
1277+
1278+ //
1279+ // for the byte in the hid packet that this led control affects, mask out it's old value
1280+ // and then add in it's new one
1281+ //
1282+
1283+ this.cache_out[ctrl.packetid][ctrl.offset] = this.cache_out[ctrl.packetid][ctrl.offset] & ctrl.maskinv | (v << ctrl.bitshift);
1284+
1285+ //
1286+ // send complete hid packet and update our cache
1287+ //
1288+
1289+ controller.send(this.cache_out[ctrl.packetid], this.cache_out[ctrl.packetid].length, 0);
1290+ this.cache_out[ctrl.packetid] = this.cache_out[ctrl.packetid];
1291+ }
1292+}
1293+
1294+//
1295+// process incoming data from mixxx and call any callbacks
1296+//
1297+
1298+HerculesMk2Hid.feedbackData = function(v, g, e) {
1299+ if (HerculesMk2Hid.feedbacks[g + e] != undefined) {
1300+ for (func in HerculesMk2Hid.feedbacks[g + e]) {
1301+ if (typeof(HerculesMk2Hid.feedbacks[g + e][func]) == "function") {
1302+ HerculesMk2Hid.feedbacks[g + e][func](g, e, v);
1303+ }
1304+ }
1305+ }
1306+}
1307+
1308+//
1309+// a single hid control, store last known value and offset/mask to work out the new value from incoming data
1310+//
1311+
1312+HerculesMk2Hid.control = function(packetid, name, group, type, offset, mask) {
1313+ this.packetid = packetid;
1314+ this.name = name;
1315+ this.group = group;
1316+ this.type = type;
1317+ this.value = 0;
1318+ this.relative = 0;
1319+ this.offset = offset;
1320+ this.mask = mask;
1321+ this.maskinv = ~mask;
1322+ this.bitshift = 0;
1323+ this.maxval = 255; // needed for encoder, could guess from the mask
1324+ this.changed = function(value) {
1325+ value = (value & this.mask) >> this.bitshift;
1326+ if (this.value == value) {
1327+ return false;
1328+ }
1329+ else {
1330+ // map to a relative value if it's an encoder, usually +1 or -1
1331+ if (this.type == 'encoder') {
1332+ this.relative = value - this.value;
1333+ if (this.relative > 100) {
1334+ this.relative -= this.maxval;
1335+ }
1336+ else if (this.relative < -100) {
1337+ this.relative += this.maxval;
1338+ }
1339+ }
1340+ this.value = value;
1341+ return true;
1342+ }
1343+ };
1344+ while (mask != 0 && (mask & 0x1) == 0) {
1345+ mask = mask >> 1;
1346+ this.bitshift++;
1347+ }
1348+}
1349+
1350+//
1351+// process incoming data and call any callbacks if their bound controls have changed
1352+//
1353+
1354+HerculesMk2Hid.incomingData = function (data, length) {
1355+
1356+ var c = HerculesMk2Hid;
1357+ var packetid = data[0];
1358+
1359+ //
1360+ // iterate thru each byte and only check controls for that byte if the byte has changed
1361+ //
1362+
1363+ for (i=1; i<length; i++) {
1364+ if ((c.cache_in[packetid] == undefined || data[i] != c.cache_in[packetid][i]) && c.controls[i] != undefined) {
1365+
1366+ //
1367+ // a byte has changed, check any controls defined in that byte, more efficient
1368+ // than checking old+new values for all controls
1369+ //
1370+
1371+ for (key in c.controls[i]) {
1372+ var control = c.controls[i][key];
1373+ if (typeof(control) == 'object' && control.packetid == data[0] && control.changed(data[i])) {
1374+
1375+ //
1376+ // we found a hid control that has changed value within that byte, check for callbacks
1377+ //
1378+
1379+ var callbacks = c.callbacks[control.name];
1380+ if (callbacks != undefined) {
1381+ for (var i=0; i<callbacks.length; i++) {
1382+ if (typeof(callbacks[i][1]) == 'function') {
1383+
1384+ //
1385+ // check we need to call for this value change: all, press, release
1386+ //
1387+
1388+ if (callbacks[i][0] == "all" ||
1389+ (callbacks[i][0] == "press" && control.value > 0) ||
1390+ (callbacks[i][0] == "release" && control.value == 0)) {
1391+
1392+ //
1393+ // call a callback function for this control
1394+ //
1395+
1396+ callbacks[i][1](control.group, control.name, control.value, control);
1397+ }
1398+ }
1399+ }
1400+ }
1401+ }
1402+ }
1403+ }
1404+ }
1405+
1406+ // store the new raw data
1407+ c.cache_in[data[0]] = data;
1408+}
1409
1410=== added file 'mixxx/res/controllers/Novation Launchpad.midi.xml'
1411--- mixxx/res/controllers/Novation Launchpad.midi.xml 1970-01-01 00:00:00 +0000
1412+++ mixxx/res/controllers/Novation Launchpad.midi.xml 2012-05-26 14:59:19 +0000
1413@@ -0,0 +1,736 @@
1414+<?xml version='1.0' encoding='utf-8'?>
1415+<MixxxControllerPreset schemaVersion="1">
1416+ <controller id="Novation Launchpad">
1417+ <info>
1418+ <name>Novation Launchpad</name>
1419+ <author>zestoi</author>
1420+ <description>Multi page mapping for the Novation Launchpad</description>
1421+ </info>
1422+ <scriptfiles>
1423+ <file filename="Novation-Launchpad-scripts.js" functionprefix="NovationLaunchpad"/>
1424+ </scriptfiles>
1425+ <controls>
1426+ <control>
1427+ <group>[Master]</group>
1428+ <key>NovationLaunchpad.incomingData</key>
1429+ <status>0xb0</status>
1430+ <midino>0x68</midino>
1431+ <options>
1432+ <Script-Binding/>
1433+ </options>
1434+ </control>
1435+ <control>
1436+ <group>[Master]</group>
1437+ <key>NovationLaunchpad.incomingData</key>
1438+ <status>0xb0</status>
1439+ <midino>0x69</midino>
1440+ <options>
1441+ <Script-Binding/>
1442+ </options>
1443+ </control>
1444+ <control>
1445+ <group>[Master]</group>
1446+ <key>NovationLaunchpad.incomingData</key>
1447+ <status>0xb0</status>
1448+ <midino>0x6A</midino>
1449+ <options>
1450+ <Script-Binding/>
1451+ </options>
1452+ </control>
1453+ <control>
1454+ <group>[Master]</group>
1455+ <key>NovationLaunchpad.incomingData</key>
1456+ <status>0xb0</status>
1457+ <midino>0x6B</midino>
1458+ <options>
1459+ <Script-Binding/>
1460+ </options>
1461+ </control>
1462+ <control>
1463+ <group>[Master]</group>
1464+ <key>NovationLaunchpad.incomingData</key>
1465+ <status>0xb0</status>
1466+ <midino>0x6C</midino>
1467+ <options>
1468+ <Script-Binding/>
1469+ </options>
1470+ </control>
1471+ <control>
1472+ <group>[Master]</group>
1473+ <key>NovationLaunchpad.incomingData</key>
1474+ <status>0xb0</status>
1475+ <midino>0x6D</midino>
1476+ <options>
1477+ <Script-Binding/>
1478+ </options>
1479+ </control>
1480+ <control>
1481+ <group>[Master]</group>
1482+ <key>NovationLaunchpad.incomingData</key>
1483+ <status>0xb0</status>
1484+ <midino>0x6E</midino>
1485+ <options>
1486+ <Script-Binding/>
1487+ </options>
1488+ </control>
1489+ <control>
1490+ <group>[Master]</group>
1491+ <key>NovationLaunchpad.incomingData</key>
1492+ <status>0xb0</status>
1493+ <midino>0x6F</midino>
1494+ <options>
1495+ <Script-Binding/>
1496+ </options>
1497+ </control>
1498+ <control>
1499+ <group>[Master]</group>
1500+ <key>NovationLaunchpad.incomingData</key>
1501+ <status>0x90</status>
1502+ <midino>0x8</midino>
1503+ <options>
1504+ <Script-Binding/>
1505+ </options>
1506+ </control>
1507+ <control>
1508+ <group>[Master]</group>
1509+ <key>NovationLaunchpad.incomingData</key>
1510+ <status>0x90</status>
1511+ <midino>0x18</midino>
1512+ <options>
1513+ <Script-Binding/>
1514+ </options>
1515+ </control>
1516+ <control>
1517+ <group>[Master]</group>
1518+ <key>NovationLaunchpad.incomingData</key>
1519+ <status>0x90</status>
1520+ <midino>0x28</midino>
1521+ <options>
1522+ <Script-Binding/>
1523+ </options>
1524+ </control>
1525+ <control>
1526+ <group>[Master]</group>
1527+ <key>NovationLaunchpad.incomingData</key>
1528+ <status>0x90</status>
1529+ <midino>0x38</midino>
1530+ <options>
1531+ <Script-Binding/>
1532+ </options>
1533+ </control>
1534+ <control>
1535+ <group>[Master]</group>
1536+ <key>NovationLaunchpad.incomingData</key>
1537+ <status>0x90</status>
1538+ <midino>0x48</midino>
1539+ <options>
1540+ <Script-Binding/>
1541+ </options>
1542+ </control>
1543+ <control>
1544+ <group>[Master]</group>
1545+ <key>NovationLaunchpad.incomingData</key>
1546+ <status>0x90</status>
1547+ <midino>0x58</midino>
1548+ <options>
1549+ <Script-Binding/>
1550+ </options>
1551+ </control>
1552+ <control>
1553+ <group>[Master]</group>
1554+ <key>NovationLaunchpad.incomingData</key>
1555+ <status>0x90</status>
1556+ <midino>0x68</midino>
1557+ <options>
1558+ <Script-Binding/>
1559+ </options>
1560+ </control>
1561+ <control>
1562+ <group>[Master]</group>
1563+ <key>NovationLaunchpad.incomingData</key>
1564+ <status>0x90</status>
1565+ <midino>0x78</midino>
1566+ <options>
1567+ <Script-Binding/>
1568+ </options>
1569+ </control>
1570+ <control>
1571+ <group>[Master]</group>
1572+ <key>NovationLaunchpad.incomingData</key>
1573+ <status>0x90</status>
1574+ <midino>0x0</midino>
1575+ <options>
1576+ <Script-Binding/>
1577+ </options>
1578+ </control>
1579+ <control>
1580+ <group>[Master]</group>
1581+ <key>NovationLaunchpad.incomingData</key>
1582+ <status>0x90</status>
1583+ <midino>0x1</midino>
1584+ <options>
1585+ <Script-Binding/>
1586+ </options>
1587+ </control>
1588+ <control>
1589+ <group>[Master]</group>
1590+ <key>NovationLaunchpad.incomingData</key>
1591+ <status>0x90</status>
1592+ <midino>0x2</midino>
1593+ <options>
1594+ <Script-Binding/>
1595+ </options>
1596+ </control>
1597+ <control>
1598+ <group>[Master]</group>
1599+ <key>NovationLaunchpad.incomingData</key>
1600+ <status>0x90</status>
1601+ <midino>0x3</midino>
1602+ <options>
1603+ <Script-Binding/>
1604+ </options>
1605+ </control>
1606+ <control>
1607+ <group>[Master]</group>
1608+ <key>NovationLaunchpad.incomingData</key>
1609+ <status>0x90</status>
1610+ <midino>0x4</midino>
1611+ <options>
1612+ <Script-Binding/>
1613+ </options>
1614+ </control>
1615+ <control>
1616+ <group>[Master]</group>
1617+ <key>NovationLaunchpad.incomingData</key>
1618+ <status>0x90</status>
1619+ <midino>0x5</midino>
1620+ <options>
1621+ <Script-Binding/>
1622+ </options>
1623+ </control>
1624+ <control>
1625+ <group>[Master]</group>
1626+ <key>NovationLaunchpad.incomingData</key>
1627+ <status>0x90</status>
1628+ <midino>0x6</midino>
1629+ <options>
1630+ <Script-Binding/>
1631+ </options>
1632+ </control>
1633+ <control>
1634+ <group>[Master]</group>
1635+ <key>NovationLaunchpad.incomingData</key>
1636+ <status>0x90</status>
1637+ <midino>0x7</midino>
1638+ <options>
1639+ <Script-Binding/>
1640+ </options>
1641+ </control>
1642+ <control>
1643+ <group>[Master]</group>
1644+ <key>NovationLaunchpad.incomingData</key>
1645+ <status>0x90</status>
1646+ <midino>0x10</midino>
1647+ <options>
1648+ <Script-Binding/>
1649+ </options>
1650+ </control>
1651+ <control>
1652+ <group>[Master]</group>
1653+ <key>NovationLaunchpad.incomingData</key>
1654+ <status>0x90</status>
1655+ <midino>0x11</midino>
1656+ <options>
1657+ <Script-Binding/>
1658+ </options>
1659+ </control>
1660+ <control>
1661+ <group>[Master]</group>
1662+ <key>NovationLaunchpad.incomingData</key>
1663+ <status>0x90</status>
1664+ <midino>0x12</midino>
1665+ <options>
1666+ <Script-Binding/>
1667+ </options>
1668+ </control>
1669+ <control>
1670+ <group>[Master]</group>
1671+ <key>NovationLaunchpad.incomingData</key>
1672+ <status>0x90</status>
1673+ <midino>0x13</midino>
1674+ <options>
1675+ <Script-Binding/>
1676+ </options>
1677+ </control>
1678+ <control>
1679+ <group>[Master]</group>
1680+ <key>NovationLaunchpad.incomingData</key>
1681+ <status>0x90</status>
1682+ <midino>0x14</midino>
1683+ <options>
1684+ <Script-Binding/>
1685+ </options>
1686+ </control>
1687+ <control>
1688+ <group>[Master]</group>
1689+ <key>NovationLaunchpad.incomingData</key>
1690+ <status>0x90</status>
1691+ <midino>0x15</midino>
1692+ <options>
1693+ <Script-Binding/>
1694+ </options>
1695+ </control>
1696+ <control>
1697+ <group>[Master]</group>
1698+ <key>NovationLaunchpad.incomingData</key>
1699+ <status>0x90</status>
1700+ <midino>0x16</midino>
1701+ <options>
1702+ <Script-Binding/>
1703+ </options>
1704+ </control>
1705+ <control>
1706+ <group>[Master]</group>
1707+ <key>NovationLaunchpad.incomingData</key>
1708+ <status>0x90</status>
1709+ <midino>0x17</midino>
1710+ <options>
1711+ <Script-Binding/>
1712+ </options>
1713+ </control>
1714+ <control>
1715+ <group>[Master]</group>
1716+ <key>NovationLaunchpad.incomingData</key>
1717+ <status>0x90</status>
1718+ <midino>0x20</midino>
1719+ <options>
1720+ <Script-Binding/>
1721+ </options>
1722+ </control>
1723+ <control>
1724+ <group>[Master]</group>
1725+ <key>NovationLaunchpad.incomingData</key>
1726+ <status>0x90</status>
1727+ <midino>0x21</midino>
1728+ <options>
1729+ <Script-Binding/>
1730+ </options>
1731+ </control>
1732+ <control>
1733+ <group>[Master]</group>
1734+ <key>NovationLaunchpad.incomingData</key>
1735+ <status>0x90</status>
1736+ <midino>0x22</midino>
1737+ <options>
1738+ <Script-Binding/>
1739+ </options>
1740+ </control>
1741+ <control>
1742+ <group>[Master]</group>
1743+ <key>NovationLaunchpad.incomingData</key>
1744+ <status>0x90</status>
1745+ <midino>0x23</midino>
1746+ <options>
1747+ <Script-Binding/>
1748+ </options>
1749+ </control>
1750+ <control>
1751+ <group>[Master]</group>
1752+ <key>NovationLaunchpad.incomingData</key>
1753+ <status>0x90</status>
1754+ <midino>0x24</midino>
1755+ <options>
1756+ <Script-Binding/>
1757+ </options>
1758+ </control>
1759+ <control>
1760+ <group>[Master]</group>
1761+ <key>NovationLaunchpad.incomingData</key>
1762+ <status>0x90</status>
1763+ <midino>0x25</midino>
1764+ <options>
1765+ <Script-Binding/>
1766+ </options>
1767+ </control>
1768+ <control>
1769+ <group>[Master]</group>
1770+ <key>NovationLaunchpad.incomingData</key>
1771+ <status>0x90</status>
1772+ <midino>0x26</midino>
1773+ <options>
1774+ <Script-Binding/>
1775+ </options>
1776+ </control>
1777+ <control>
1778+ <group>[Master]</group>
1779+ <key>NovationLaunchpad.incomingData</key>
1780+ <status>0x90</status>
1781+ <midino>0x27</midino>
1782+ <options>
1783+ <Script-Binding/>
1784+ </options>
1785+ </control>
1786+ <control>
1787+ <group>[Master]</group>
1788+ <key>NovationLaunchpad.incomingData</key>
1789+ <status>0x90</status>
1790+ <midino>0x30</midino>
1791+ <options>
1792+ <Script-Binding/>
1793+ </options>
1794+ </control>
1795+ <control>
1796+ <group>[Master]</group>
1797+ <key>NovationLaunchpad.incomingData</key>
1798+ <status>0x90</status>
1799+ <midino>0x31</midino>
1800+ <options>
1801+ <Script-Binding/>
1802+ </options>
1803+ </control>
1804+ <control>
1805+ <group>[Master]</group>
1806+ <key>NovationLaunchpad.incomingData</key>
1807+ <status>0x90</status>
1808+ <midino>0x32</midino>
1809+ <options>
1810+ <Script-Binding/>
1811+ </options>
1812+ </control>
1813+ <control>
1814+ <group>[Master]</group>
1815+ <key>NovationLaunchpad.incomingData</key>
1816+ <status>0x90</status>
1817+ <midino>0x33</midino>
1818+ <options>
1819+ <Script-Binding/>
1820+ </options>
1821+ </control>
1822+ <control>
1823+ <group>[Master]</group>
1824+ <key>NovationLaunchpad.incomingData</key>
1825+ <status>0x90</status>
1826+ <midino>0x34</midino>
1827+ <options>
1828+ <Script-Binding/>
1829+ </options>
1830+ </control>
1831+ <control>
1832+ <group>[Master]</group>
1833+ <key>NovationLaunchpad.incomingData</key>
1834+ <status>0x90</status>
1835+ <midino>0x35</midino>
1836+ <options>
1837+ <Script-Binding/>
1838+ </options>
1839+ </control>
1840+ <control>
1841+ <group>[Master]</group>
1842+ <key>NovationLaunchpad.incomingData</key>
1843+ <status>0x90</status>
1844+ <midino>0x36</midino>
1845+ <options>
1846+ <Script-Binding/>
1847+ </options>
1848+ </control>
1849+ <control>
1850+ <group>[Master]</group>
1851+ <key>NovationLaunchpad.incomingData</key>
1852+ <status>0x90</status>
1853+ <midino>0x37</midino>
1854+ <options>
1855+ <Script-Binding/>
1856+ </options>
1857+ </control>
1858+ <control>
1859+ <group>[Master]</group>
1860+ <key>NovationLaunchpad.incomingData</key>
1861+ <status>0x90</status>
1862+ <midino>0x40</midino>
1863+ <options>
1864+ <Script-Binding/>
1865+ </options>
1866+ </control>
1867+ <control>
1868+ <group>[Master]</group>
1869+ <key>NovationLaunchpad.incomingData</key>
1870+ <status>0x90</status>
1871+ <midino>0x41</midino>
1872+ <options>
1873+ <Script-Binding/>
1874+ </options>
1875+ </control>
1876+ <control>
1877+ <group>[Master]</group>
1878+ <key>NovationLaunchpad.incomingData</key>
1879+ <status>0x90</status>
1880+ <midino>0x42</midino>
1881+ <options>
1882+ <Script-Binding/>
1883+ </options>
1884+ </control>
1885+ <control>
1886+ <group>[Master]</group>
1887+ <key>NovationLaunchpad.incomingData</key>
1888+ <status>0x90</status>
1889+ <midino>0x43</midino>
1890+ <options>
1891+ <Script-Binding/>
1892+ </options>
1893+ </control>
1894+ <control>
1895+ <group>[Master]</group>
1896+ <key>NovationLaunchpad.incomingData</key>
1897+ <status>0x90</status>
1898+ <midino>0x44</midino>
1899+ <options>
1900+ <Script-Binding/>
1901+ </options>
1902+ </control>
1903+ <control>
1904+ <group>[Master]</group>
1905+ <key>NovationLaunchpad.incomingData</key>
1906+ <status>0x90</status>
1907+ <midino>0x45</midino>
1908+ <options>
1909+ <Script-Binding/>
1910+ </options>
1911+ </control>
1912+ <control>
1913+ <group>[Master]</group>
1914+ <key>NovationLaunchpad.incomingData</key>
1915+ <status>0x90</status>
1916+ <midino>0x46</midino>
1917+ <options>
1918+ <Script-Binding/>
1919+ </options>
1920+ </control>
1921+ <control>
1922+ <group>[Master]</group>
1923+ <key>NovationLaunchpad.incomingData</key>
1924+ <status>0x90</status>
1925+ <midino>0x47</midino>
1926+ <options>
1927+ <Script-Binding/>
1928+ </options>
1929+ </control>
1930+ <control>
1931+ <group>[Master]</group>
1932+ <key>NovationLaunchpad.incomingData</key>
1933+ <status>0x90</status>
1934+ <midino>0x50</midino>
1935+ <options>
1936+ <Script-Binding/>
1937+ </options>
1938+ </control>
1939+ <control>
1940+ <group>[Master]</group>
1941+ <key>NovationLaunchpad.incomingData</key>
1942+ <status>0x90</status>
1943+ <midino>0x51</midino>
1944+ <options>
1945+ <Script-Binding/>
1946+ </options>
1947+ </control>
1948+ <control>
1949+ <group>[Master]</group>
1950+ <key>NovationLaunchpad.incomingData</key>
1951+ <status>0x90</status>
1952+ <midino>0x52</midino>
1953+ <options>
1954+ <Script-Binding/>
1955+ </options>
1956+ </control>
1957+ <control>
1958+ <group>[Master]</group>
1959+ <key>NovationLaunchpad.incomingData</key>
1960+ <status>0x90</status>
1961+ <midino>0x53</midino>
1962+ <options>
1963+ <Script-Binding/>
1964+ </options>
1965+ </control>
1966+ <control>
1967+ <group>[Master]</group>
1968+ <key>NovationLaunchpad.incomingData</key>
1969+ <status>0x90</status>
1970+ <midino>0x54</midino>
1971+ <options>
1972+ <Script-Binding/>
1973+ </options>
1974+ </control>
1975+ <control>
1976+ <group>[Master]</group>
1977+ <key>NovationLaunchpad.incomingData</key>
1978+ <status>0x90</status>
1979+ <midino>0x55</midino>
1980+ <options>
1981+ <Script-Binding/>
1982+ </options>
1983+ </control>
1984+ <control>
1985+ <group>[Master]</group>
1986+ <key>NovationLaunchpad.incomingData</key>
1987+ <status>0x90</status>
1988+ <midino>0x56</midino>
1989+ <options>
1990+ <Script-Binding/>
1991+ </options>
1992+ </control>
1993+ <control>
1994+ <group>[Master]</group>
1995+ <key>NovationLaunchpad.incomingData</key>
1996+ <status>0x90</status>
1997+ <midino>0x57</midino>
1998+ <options>
1999+ <Script-Binding/>
2000+ </options>
2001+ </control>
2002+ <control>
2003+ <group>[Master]</group>
2004+ <key>NovationLaunchpad.incomingData</key>
2005+ <status>0x90</status>
2006+ <midino>0x60</midino>
2007+ <options>
2008+ <Script-Binding/>
2009+ </options>
2010+ </control>
2011+ <control>
2012+ <group>[Master]</group>
2013+ <key>NovationLaunchpad.incomingData</key>
2014+ <status>0x90</status>
2015+ <midino>0x61</midino>
2016+ <options>
2017+ <Script-Binding/>
2018+ </options>
2019+ </control>
2020+ <control>
2021+ <group>[Master]</group>
2022+ <key>NovationLaunchpad.incomingData</key>
2023+ <status>0x90</status>
2024+ <midino>0x62</midino>
2025+ <options>
2026+ <Script-Binding/>
2027+ </options>
2028+ </control>
2029+ <control>
2030+ <group>[Master]</group>
2031+ <key>NovationLaunchpad.incomingData</key>
2032+ <status>0x90</status>
2033+ <midino>0x63</midino>
2034+ <options>
2035+ <Script-Binding/>
2036+ </options>
2037+ </control>
2038+ <control>
2039+ <group>[Master]</group>
2040+ <key>NovationLaunchpad.incomingData</key>
2041+ <status>0x90</status>
2042+ <midino>0x64</midino>
2043+ <options>
2044+ <Script-Binding/>
2045+ </options>
2046+ </control>
2047+ <control>
2048+ <group>[Master]</group>
2049+ <key>NovationLaunchpad.incomingData</key>
2050+ <status>0x90</status>
2051+ <midino>0x65</midino>
2052+ <options>
2053+ <Script-Binding/>
2054+ </options>
2055+ </control>
2056+ <control>
2057+ <group>[Master]</group>
2058+ <key>NovationLaunchpad.incomingData</key>
2059+ <status>0x90</status>
2060+ <midino>0x66</midino>
2061+ <options>
2062+ <Script-Binding/>
2063+ </options>
2064+ </control>
2065+ <control>
2066+ <group>[Master]</group>
2067+ <key>NovationLaunchpad.incomingData</key>
2068+ <status>0x90</status>
2069+ <midino>0x67</midino>
2070+ <options>
2071+ <Script-Binding/>
2072+ </options>
2073+ </control>
2074+ <control>
2075+ <group>[Master]</group>
2076+ <key>NovationLaunchpad.incomingData</key>
2077+ <status>0x90</status>
2078+ <midino>0x70</midino>
2079+ <options>
2080+ <Script-Binding/>
2081+ </options>
2082+ </control>
2083+ <control>
2084+ <group>[Master]</group>
2085+ <key>NovationLaunchpad.incomingData</key>
2086+ <status>0x90</status>
2087+ <midino>0x71</midino>
2088+ <options>
2089+ <Script-Binding/>
2090+ </options>
2091+ </control>
2092+ <control>
2093+ <group>[Master]</group>
2094+ <key>NovationLaunchpad.incomingData</key>
2095+ <status>0x90</status>
2096+ <midino>0x72</midino>
2097+ <options>
2098+ <Script-Binding/>
2099+ </options>
2100+ </control>
2101+ <control>
2102+ <group>[Master]</group>
2103+ <key>NovationLaunchpad.incomingData</key>
2104+ <status>0x90</status>
2105+ <midino>0x73</midino>
2106+ <options>
2107+ <Script-Binding/>
2108+ </options>
2109+ </control>
2110+ <control>
2111+ <group>[Master]</group>
2112+ <key>NovationLaunchpad.incomingData</key>
2113+ <status>0x90</status>
2114+ <midino>0x74</midino>
2115+ <options>
2116+ <Script-Binding/>
2117+ </options>
2118+ </control>
2119+ <control>
2120+ <group>[Master]</group>
2121+ <key>NovationLaunchpad.incomingData</key>
2122+ <status>0x90</status>
2123+ <midino>0x75</midino>
2124+ <options>
2125+ <Script-Binding/>
2126+ </options>
2127+ </control>
2128+ <control>
2129+ <group>[Master]</group>
2130+ <key>NovationLaunchpad.incomingData</key>
2131+ <status>0x90</status>
2132+ <midino>0x76</midino>
2133+ <options>
2134+ <Script-Binding/>
2135+ </options>
2136+ </control>
2137+ <control>
2138+ <group>[Master]</group>
2139+ <key>NovationLaunchpad.incomingData</key>
2140+ <status>0x90</status>
2141+ <midino>0x77</midino>
2142+ <options>
2143+ <Script-Binding/>
2144+ </options>
2145+ </control>
2146+ </controls>
2147+ <outputs/>
2148+ </controller>
2149+</MixxxControllerPreset>
2150
2151=== added file 'mixxx/res/controllers/Novation-Launchpad-scripts.js'
2152--- mixxx/res/controllers/Novation-Launchpad-scripts.js 1970-01-01 00:00:00 +0000
2153+++ mixxx/res/controllers/Novation-Launchpad-scripts.js 2012-05-26 14:59:19 +0000
2154@@ -0,0 +1,819 @@
2155+/****************************************************************/
2156+/* Novation Launchpad Mapping */
2157+/* For Mixxx version 1.11 */
2158+/* Author: zestoi */
2159+/****************************************************************/
2160+
2161+NovationLaunchpad = {
2162+
2163+ init: function() {
2164+
2165+ //
2166+ // setup variables and methods
2167+ //
2168+
2169+ this.page = 1;
2170+ this.shift = 0;
2171+ this.shift2 = 0;
2172+ this.callbacks = {};
2173+ this.feedbacks = {};
2174+ this.cache = [{}, {}, {}, {}];
2175+ this.feedback_cache = {};
2176+ this.toggle_cache = [{}, {}, {}, {}];
2177+ this.name2control = {};
2178+ this.control2name = {};
2179+ this.vumeters = [];
2180+
2181+ var self = NovationLaunchpad;
2182+ this.colors = self.colors();
2183+ this.capture = self.capture;
2184+ this.feedback = self.feedback;
2185+ this.send = self.send;
2186+ this.button = self.button;
2187+ this.toggle = self.toggle;
2188+ this.hotcue = self.hotcue;
2189+ this.flanger = self.flanger;
2190+ this.jog = self.jog;
2191+ this.get = self.get;
2192+ this.loop = self.loop;
2193+ this.gator = self.gator;
2194+ this.set_page = self.set_page;
2195+ this.vfader = self.vfader;
2196+ this.vumeter = self.vumeter;
2197+ this.vumeter_toggle = self.vumeter_toggle;
2198+ this.playlist = self.playlist;
2199+
2200+ //
2201+ // map the midi config into something more useful
2202+ //
2203+
2204+ var buttons = self.buttons();
2205+ for (name in buttons) {
2206+ var type = buttons[name][0];
2207+ var chan = buttons[name][1];
2208+ var value = buttons[name][2];
2209+ var status = (type == 'cc') ? 0xb0 + chan - 1 : 0x90 + chan - 1;
2210+ this.name2control[name]= [ status, value ];
2211+ this.control2name["" + status + value] = name; // stringify it
2212+ }
2213+
2214+ //
2215+ // reset device, enable flashing colors
2216+ //
2217+
2218+ midi.sendShortMsg(0xb0, 0x0, 0x0);
2219+ midi.sendShortMsg(0xb0, 0x0, 0x28);
2220+
2221+ /////////////////////////////////////////////////////////////////////////
2222+ // button layout mapping starts here
2223+ /////////////////////////////////////////////////////////////////////////
2224+
2225+ // shift buttons
2226+
2227+ this.button("arm", "all", 1, 'hi_yellow', 'lo_yellow', '', '', function(g, n, v) { this.shift = v > 0 ? 1 : 0; });
2228+ this.button("solo", "all", 1, 'hi_yellow', 'lo_yellow', '', '', function(g, n, v) { this.shift2 = v > 0 ? 1 : 0; });
2229+ this.toggle("mixer", "all", 0, 'hi_red', 'lo_red', '', '', function(g, n, v) { this.set_page(v > 0 ? 2 : 1); });
2230+ this.toggle("vol", "all", 2, 'hi_red', 'lo_red', '', '', function(g, n, v) { this.vumeter_toggle(v); });
2231+
2232+ //// MAIN PAGE ////
2233+
2234+ // track navigation
2235+
2236+ this.playlist("up", 0, "SelectPrevTrack");
2237+ this.playlist("down", 0, "SelectNextTrack");
2238+ this.button("left", "press", 0, 'hi_yellow', 'lo_yellow', "[Playlist]", "SelectPrevPlaylist");
2239+ this.button("right", "press", 0, 'hi_yellow', 'lo_yellow', "[Playlist]", "SelectNextPlaylist");
2240+
2241+ // deck mappings
2242+
2243+ for (deck=1; deck<=2; deck++) {
2244+ var offset = deck == 1 ? 0 : 4;
2245+ var group = "[Channel" + deck + "]";
2246+
2247+ // tracks
2248+
2249+ this.toggle("0," + (offset + 0), "all", 1, 'hi_red', 'lo_red', group, "quantize");
2250+ this.toggle("0," + (offset + 1), "all", 1, 'hi_red', 'lo_red', group, "keylock");
2251+ this.toggle("0," + (offset + 2), "all", 1, 'hi_red', 'lo_red', group, "pfl");
2252+ this.button("0," + (offset + 3), "all", 1, 'hi_yellow', 'lo_amber', group, "LoadSelectedTrack");
2253+
2254+ // flanger
2255+
2256+ this.flanger("1," + (offset + 0), 1, group, 0.5, 1500000, 333);
2257+ this.flanger("1," + (offset + 1), 1, group, 1, 500000, 666);
2258+
2259+ // spinback effect
2260+
2261+ this.button("1," + (offset + 2), "all", 1, 'hi_red', 'lo_red', group, "", function(g, n, v) {
2262+ script.spinback(g, v > 0);
2263+ });
2264+
2265+ // brake effect
2266+
2267+ this.button("1," + (offset + 3), "all", 1, 'hi_red', 'lo_red', group, "", function(g, n, v) {
2268+ script.brake(g, v > 0);
2269+ });
2270+
2271+ // instant loops
2272+
2273+ this.loop("2," + (offset + 0), 1, group, 1);
2274+ this.loop("2," + (offset + 1), 1, group, 0.5);
2275+ this.loop("2," + (offset + 2), 1, group, 0.25);
2276+ this.loop("2," + (offset + 3), 1, group, 0.125);
2277+
2278+ // loop in or loop half when active
2279+
2280+ this.button("3," + (offset + 0), "press", 1, 'hi_green', 'lo_green', group, "", function(g, n, v) {
2281+ if (engine.getValue(g, "loop_enabled")) {
2282+ engine.setValue(g, "loop_halve", 1);
2283+ }
2284+ else {
2285+ engine.setValue(g, "loop_in", 1);
2286+ }
2287+ });
2288+
2289+ // loop out or loop double when active
2290+
2291+ this.button("3," + (offset + 1), "press", 1, 'hi_green', 'lo_green', group, "", function(g, n, v) {
2292+ if (engine.getValue(g, "loop_enabled")) {
2293+ engine.setValue(g, "loop_double", 1);
2294+ }
2295+ else {
2296+ engine.setValue(g, "loop_out", 1);
2297+ }
2298+ });
2299+
2300+ // reloop or exit loop
2301+
2302+ this.button("3," + (offset + 2), "all", 1, 'hi_green', 'lo_green', group, "reloop_exit");
2303+
2304+ // gator effect
2305+
2306+ this.gator("3," + (offset + 3), 1, group, 8, 0.7);
2307+
2308+ // led feedback for loop in/out buttons to show loop status
2309+
2310+ this.feedback(group, "loop_enabled", function(self, g, e, value) {
2311+ var offset = g == "[Channel1]" ? 0 : 4; // ????
2312+ self.send("3," + (offset + 0), self.colors[value > 0 ? 'hi_green' : 'lo_green'], page);
2313+ self.send("3," + (offset + 1), self.colors[value > 0 ? 'hi_green' : 'lo_green'], page);
2314+ });
2315+
2316+ // hotcues or needle drop with shift2 pressed
2317+
2318+ this.hotcue("4," + (offset + 0), 1, group, 1);
2319+ this.hotcue("4," + (offset + 1), 1, group, 2);
2320+ this.hotcue("4," + (offset + 2), 1, group, 3);
2321+ this.hotcue("4," + (offset + 3), 1, group, 4);
2322+ this.hotcue("5," + (offset + 0), 1, group, 5);
2323+ this.hotcue("5," + (offset + 1), 1, group, 6);
2324+ this.hotcue("5," + (offset + 2), 1, group, 7);
2325+ this.hotcue("5," + (offset + 3), 1, group, 8);
2326+
2327+ // transport
2328+
2329+ this.button("6," + (offset + 0), "all", 1, 'hi_yellow', 'lo_red', group, "cue_default");
2330+
2331+ this.button("6," + (offset + 1), "press", 1, 'hi_yellow', 'lo_yellow', group, "rate", function(g, n, v) {
2332+ engine.setValue(g, n, 0);
2333+ });
2334+
2335+ this.button("6," + (offset + 2), "press", 1, 'hi_yellow', 'lo_yellow', group, "rate_perm_down_small");
2336+ this.button("6," + (offset + 3), "press", 1, 'hi_yellow', 'lo_yellow', group, "rate_perm_up_small");
2337+
2338+ // play button
2339+ this.toggle("7," + (offset + 0), "press", 1, 'hi_yellow', 'lo_red', group, "play");
2340+
2341+ // flash play button when near end of track
2342+ this.feedback(group, "playposition", function(self, g, e, value) {
2343+ if (value > 0.9 && engine.getValue(g, "play") > 0) {
2344+ self.send(g == "[Channel1]" ? "7,0" : "7,4", self.colors['flash_hi_red'], 1);
2345+ }
2346+ });
2347+
2348+ // sync or move beatgrid when shift is pressed
2349+
2350+ this.button("7," + (offset + 1), "all", 1, 'hi_yellow', 'lo_amber', group, "beatsync", function(g, n, v) {
2351+ if (this.shift > 0)
2352+ engine.setValue(g, "beats_translate_curpos", v > 0 ? 1 : 0);
2353+ else
2354+ engine.setValue(g, n, v > 0 ? 1 : 0);
2355+ });
2356+
2357+ // fwd/rev when not playing unless shift and then fine jog movements for beat gridding, jog when playing, jog more when shift+playing
2358+
2359+ this.jog("7," + (offset + 2), 1, group, "back");
2360+ this.jog("7," + (offset + 3), 1, group, "fwd");
2361+ }
2362+
2363+ //// MIXER PAGE ////
2364+
2365+ this.toggle("0,0", "all", 2, 'flash_hi_red', 'lo_red', "[Channel1]", "filterHighKill");
2366+ this.toggle("0,1", "all", 2, 'flash_hi_red', 'lo_red', "[Channel1]", "filterMidKill");
2367+ this.toggle("0,2", "all", 2, 'flash_hi_red', 'lo_red', "[Channel1]", "filterLowKill");
2368+ this.toggle("0,5", "all", 2, 'flash_hi_red', 'lo_red', "[Channel2]", "filterHighKill");
2369+ this.toggle("0,6", "all", 2, 'flash_hi_red', 'lo_red', "[Channel2]", "filterMidKill");
2370+ this.toggle("0,7", "all", 2, 'flash_hi_red', 'lo_red', "[Channel2]", "filterLowKill");
2371+
2372+ this.vfader(7, 0, 2, 7, 'hi_orange', 'lo_green', "[Channel1]", "filterLow");
2373+ this.vfader(7, 1, 2, 7, 'hi_orange', 'lo_green', "[Channel1]", "filterMid");
2374+ this.vfader(7, 2, 2, 7, 'hi_orange', 'lo_green', "[Channel1]", "filterHigh");
2375+ this.vfader(7, 5, 2, 7, 'hi_orange', 'lo_green', "[Channel2]", "filterLow");
2376+ this.vfader(7, 6, 2, 7, 'hi_orange', 'lo_green', "[Channel2]", "filterMid");
2377+ this.vfader(7, 7, 2, 7, 'hi_orange', 'lo_green', "[Channel2]", "filterHigh");
2378+
2379+ this.vfader(7, 3, 2, 8, 'hi_yellow', 'lo_red', "[Channel1]", "volume");
2380+ this.vfader(7, 4, 2, 8, 'hi_yellow', 'lo_red', "[Channel2]", "volume");
2381+ this.vumeter(7, 3, 2, 8, 'hi_yellow', 'lo_red', "[Channel1]", "VuMeter");
2382+ this.vumeter(7, 4, 2, 8, 'hi_yellow', 'lo_red', "[Channel2]", "VuMeter");
2383+
2384+ /////////////////////////////////////////////////////////////////////////
2385+ // button layout mapping ends here
2386+ /////////////////////////////////////////////////////////////////////////
2387+ },
2388+
2389+ //
2390+ // convert incoming midi to a 'name' and call callbacks (if any)
2391+ //
2392+
2393+ incomingData: function(channel, control, value, status, group) {
2394+ if ((name = this.control2name["" + status + control]) != undefined) {
2395+ if (this.callbacks[name] != undefined) {
2396+ var callbacks = this.callbacks[name];
2397+ for (var i=0; i<callbacks.length; i++) {
2398+ if ((callbacks[i][1] == 0 || callbacks[i][1] == this.page) && typeof(callbacks[i][2]) == 'function') {
2399+
2400+ //
2401+ // check we need to call for this value change: all, press, release
2402+ //
2403+
2404+ if (callbacks[i][0] == "all" ||
2405+ (callbacks[i][0] == "press" && value > 0) ||
2406+ (callbacks[i][0] == "release" && value == 0)) {
2407+
2408+ //
2409+ // call a callback function for this control
2410+ //
2411+
2412+ callbacks[i][2](this, group, name, value);
2413+ }
2414+ }
2415+ }
2416+ }
2417+ }
2418+ },
2419+
2420+ //
2421+ // gator effect using high eq kill
2422+ //
2423+
2424+ gator: function(name, page, group, rate, depth) {
2425+ this.button(name, "all", page, 'hi_red', 'lo_red', group, "", function(g, n, v) {
2426+ var self = NovationLaunchpad;
2427+ if (typeof(self.gator_timer) != undefined && self.gator_timer != null) {
2428+ engine.stopTimer(self.gator_timer);
2429+ self.gator_timer = null;
2430+ }
2431+
2432+ if (v > 0) {
2433+ if ((bpm = engine.getValue(g, 'bpm')) > 0) {
2434+ var interval = parseInt(1000 / bpm * 60 / rate);
2435+ self.gator_direction = false;
2436+ self.gator_depth = depth;
2437+ self.gator_timer = engine.beginTimer(interval, 'NovationLaunchpad.process_gator("' + g + '")');
2438+ }
2439+ }
2440+ else {
2441+ engine.setValue(group, 'filterHighKill', 0);
2442+ }
2443+ });
2444+ },
2445+
2446+ //
2447+ // gator
2448+ //
2449+
2450+ process_gator: function(group) {
2451+ var self = NovationLaunchpad;
2452+ self.gator_direction = !self.gator_direction;
2453+ engine.setValue(group, 'filterHighKill', self.gator_direction ? 1 : 0);
2454+ },
2455+
2456+ //
2457+ // flanger button
2458+ //
2459+
2460+ flanger: function(name, page, group, depth, period, delay) {
2461+ this.button(name, "all", page, 'hi_amber', 'lo_amber', group, "flanger", function(g, name, v) {
2462+ if (v > 0) {
2463+ engine.setValue("[Flanger]", "lfoDepth", depth);
2464+ engine.setValue("[Flanger]", "lfoPeriod", period);
2465+ engine.setValue("[Flanger]", "lfoDelay", delay);
2466+ }
2467+ engine.setValue(group, "flanger", v > 0 ? 1 : 0);
2468+ });
2469+ },
2470+
2471+ loop: function(name, page, group, size) {
2472+ this.button(name, "all", page, 'hi_yellow', 'lo_yellow', group, "", function(g, name, v) {
2473+ if (v > 0) {
2474+ engine.setValue(g, "beatloop_" + size + "_activate", 1);
2475+ }
2476+ else {
2477+ if (engine.getValue(g, "beatloop_" + size + "_enabled")) {
2478+ engine.setValue(g, "beatloop_" + size + "_toggle", 1);
2479+ }
2480+ }
2481+ });
2482+ },
2483+
2484+ //
2485+ // track scrolling
2486+ //
2487+
2488+ playlist: function(name, page, action) {
2489+ this.button(name, "all", page, 'hi_yellow', 'lo_yellow', "[Playlist]", action, function(g, n, v) {
2490+ var self = NovationLaunchpad;
2491+ if (typeof(self.playlist_timer) != undefined && self.playlist_timer != null) {
2492+ engine.stopTimer(self.playlist_timer);
2493+ self.playlist_timer = null;
2494+ }
2495+
2496+ if (v > 0) {
2497+ engine.setValue("[Playlist]", action, 1);
2498+ self.playlist_timer = engine.beginTimer(this.shift > 0 ? 30 : 150, 'NovationLaunchpad.process_playlist("' + action + '")');
2499+ }
2500+ });
2501+ },
2502+
2503+ process_playlist: function(name) {
2504+ engine.setValue("[Playlist]", name, 1);
2505+ },
2506+
2507+ //
2508+ // map a callback to an event from mixxx
2509+ //
2510+
2511+ feedback: function(g, e, f) {
2512+ if (g != "" && e != "") {
2513+ engine.connectControl(g, e, "NovationLaunchpad.feedbackData");
2514+ if (this.feedbacks[g + e] == undefined) {
2515+ this.feedbacks[g + e] = [];
2516+ }
2517+ this.feedbacks[g + e].push(f);
2518+ }
2519+ },
2520+
2521+ //
2522+ // call callbacks from mixxx events
2523+ //
2524+
2525+ feedbackData: function(v, g, e) {
2526+ this.feedback_cache[g + e] = v;
2527+ if (this.feedbacks[g + e] != undefined) {
2528+ for (func in this.feedbacks[g + e]) {
2529+ if (typeof(this.feedbacks[g + e][func]) == "function") {
2530+ this.feedbacks[g + e][func](this, g, e, v);
2531+ }
2532+ }
2533+ }
2534+ },
2535+
2536+ //
2537+ // map a callback to a launchpad button name
2538+ //
2539+
2540+ capture: function(name, values, page, func) {
2541+ if (this.callbacks[name] == undefined) {
2542+ this.callbacks[name] = [ [ values, page, func ] ];
2543+ }
2544+ else {
2545+ this.callbacks[name].push([ values, page, func ]);
2546+ }
2547+ },
2548+
2549+ //
2550+ // send back to the launchpad for leds by name
2551+ //
2552+
2553+ send: function(name, value, page) {
2554+ if (page == 0 || this.page == page) {
2555+ if ((control = this.name2control[name]) != undefined) {
2556+ if (this.cache[page][name] == value) return;
2557+ midi.sendShortMsg(control[0], control[1], value);
2558+ }
2559+ }
2560+ this.cache[page][name] = value;
2561+ },
2562+
2563+ //
2564+ // hold button
2565+ //
2566+
2567+ button: function(name, values, page, on_color, off_color, group, event, callback) {
2568+
2569+ // launchpad => mixxx
2570+
2571+ this.capture(name, "all", page, function(self, g, name, value) {
2572+ if (callback == undefined) {
2573+ engine.setValue(group, event, value);
2574+ }
2575+ else if (typeof(callback) == "function") {
2576+ if (values == "all" || (values == "press" && value > 0) || (values == "release" && value == 0)) {
2577+ callback(group, event, value);
2578+ }
2579+ }
2580+
2581+ if (values == "all" || (values == "press" && value > 0) || (values == "release" && value == 0)) {
2582+ self.send(name, self.colors[value > 0 ? on_color : off_color], page);
2583+ }
2584+ });
2585+
2586+ // mixxx => launchpad
2587+
2588+ this.feedback(group, event, function(self, g, e, value) {
2589+ self.send(name, self.colors[value > 0 ? on_color : off_color], page);
2590+ });
2591+
2592+ // init led
2593+
2594+ this.send(name, this.colors[off_color], page);
2595+ },
2596+
2597+ //
2598+ // toggle
2599+ //
2600+
2601+ toggle: function(name, values, page, on_color, off_color, group, event, callback) {
2602+ this.capture(name, "press", page, function(self, g, name, value) {
2603+ if (typeof(self.toggle_cache[page][name]) == "undefined") {
2604+ self.toggle_cache[page][name] = 0;
2605+ }
2606+ self.toggle_cache[page][name] = self.toggle_cache[page][name] == 0 ? 1 : 0;
2607+
2608+ if (callback == undefined) {
2609+ engine.setValue(group, event, self.toggle_cache[page][name]);
2610+ }
2611+ else if (typeof(callback) == "function") {
2612+ callback(group, event, self.toggle_cache[page][name]);
2613+ }
2614+
2615+ self.send(name, self.colors[self.toggle_cache[page][name] > 0 ? on_color : off_color], page);
2616+ });
2617+
2618+ // mixxx => launchpad
2619+
2620+ this.feedback(group, event, function(self, g, e, value) {
2621+ self.send(name, self.colors[value > 0 ? on_color : off_color], page);
2622+ self.toggle_cache[page][name] = value > 0 ? 1 : 0;
2623+ });
2624+
2625+ // init led
2626+
2627+ this.send(name, this.colors[off_color], page);
2628+ },
2629+
2630+ //
2631+ // hotcues
2632+ //
2633+
2634+ hotcue: function(name, page, group, num) {
2635+ this.capture(name, "press", page, function(self, g, name, value) {
2636+ if (self.shift2) {
2637+ engine.setValue(group, "playposition", (num-1)/8);
2638+ }
2639+ else if (self.shift) {
2640+ engine.setValue(group, "hotcue_" + num + "_clear", 1);
2641+ }
2642+ else {
2643+ engine.setValue(group, "hotcue_" + num + "_activate", 1);
2644+ }
2645+ });
2646+
2647+ this.feedback(group, "hotcue_" + num + "_enabled", function(self, g, e, value) {
2648+ self.send(name, self.colors[value > 0 ? 'hi_red' : 'black'], page);
2649+ });
2650+ },
2651+
2652+ //
2653+ // jog
2654+ //
2655+
2656+ jog: function(name, page, group, dir) {
2657+ this.button(name, "all", page, 'hi_yellow', 'lo_amber', group, "", function(g, n, v) {
2658+
2659+ if (dir == "fwd") {
2660+ mult = 1;
2661+ rate = "rate_temp_up";
2662+ }
2663+ else {
2664+ mult = -1;
2665+ rate = "rate_temp_down";
2666+ }
2667+
2668+ if (engine.getValue(g, "play") > 0) {
2669+ if (this.shift > 0) {
2670+ engine.setValue(g, rate, v > 0 ? 1 : 0);
2671+ }
2672+ else {
2673+ engine.setValue(g, rate + "_small", v > 0 ? 1 : 0);
2674+ }
2675+ }
2676+ else if (this.shift > 0) {
2677+ if (v > 0) {
2678+ engine.setValue(g, 'jog', 0.2 * mult);
2679+ }
2680+ }
2681+ else engine.setValue(g, dir, v);
2682+ });
2683+ },
2684+
2685+ //
2686+ // turn a column of pads into a virtual fader
2687+ //
2688+
2689+ vfader: function(y, x, page, nbtns, on_color, off_color, group, action) {
2690+ var incr = 1 / nbtns;
2691+
2692+ // launchpad => mixxx
2693+
2694+ for (var btn=0; btn<nbtns; btn++) {
2695+ this.capture((y-btn)+","+x, "press", page, function(self, g, name, value) {
2696+ var cap = name.match(/^(\d+),\d+/);
2697+ var num = y - cap[1] + 1;
2698+ engine.setValue(group, action, incr * num);
2699+ });
2700+ this.send((y-btn)+","+x, this.colors[on_color], page);
2701+ }
2702+
2703+ // mixxx => launchpad
2704+
2705+ this.feedback(group, action, function(self, g, e, value) {
2706+ for (btn=0; btn<nbtns; btn++) {
2707+ if (value > btn*incr) {
2708+ self.send((y-btn)+","+x, self.colors[on_color], page);
2709+ }
2710+ else {
2711+ self.send((y-btn)+","+x, self.colors[off_color], page);
2712+ }
2713+ }
2714+ });
2715+ },
2716+
2717+ //
2718+ // turn a column of pads into a vumeter
2719+ //
2720+
2721+ vumeter: function(y, x, page, nbtns, on_color, off_color, group, action) {
2722+ var incr = 1 / nbtns;
2723+ this.vumeters.push([ y, x, page, nbtns, on_color, off_color, group, action ]);
2724+ this.feedback(group, action, function(self, g, e, value) {
2725+ if (self.vumeter_shift > 0) {
2726+ for (btn=0; btn<nbtns; btn++) {
2727+ if (value > btn*incr) {
2728+ self.send((y-btn)+","+x, self.colors[on_color], page);
2729+ }
2730+ else {
2731+ self.send((y-btn)+","+x, self.colors[off_color], page);
2732+ }
2733+ }
2734+ }
2735+ });
2736+ },
2737+
2738+ vumeter_toggle: function(v) {
2739+ this.vumeter_shift = v > 0 ? 1 : 0;
2740+
2741+ //
2742+ // clear fader leds when enabling vumeter and set back the leds for the volume if disabling
2743+ //
2744+
2745+ for (i in this.vumeters) {
2746+
2747+ var value = this.vumeter_shift > 0 ? 0 : this.feedback_cache[ this.vumeters[i][6] + 'volume' ];
2748+
2749+ if (value != undefined) {
2750+ var y = this.vumeters[i][0];
2751+ var x = this.vumeters[i][1];
2752+ var page = this.vumeters[i][2];
2753+ var nbtns = this.vumeters[i][3];
2754+ var on_color = this.vumeters[i][4];
2755+ var off_color = this.vumeters[i][5];
2756+ var incr = 1 / nbtns;
2757+
2758+ for (btn=0; btn<nbtns; btn++) {
2759+ if (value > btn*incr) {
2760+ this.send((y-btn)+","+x, this.colors[on_color], page);
2761+ }
2762+ else {
2763+ this.send((y-btn)+","+x, this.colors[off_color], page);
2764+ }
2765+ }
2766+ }
2767+ }
2768+ },
2769+
2770+ //
2771+ // get the last value sent to a launchpad led
2772+ //
2773+
2774+ get: function(name, page) {
2775+ if (typeof(this.cache[page][name]) == undefined) {
2776+ return 0;
2777+ }
2778+ else {
2779+ return this.cache[page][name];
2780+ }
2781+ },
2782+
2783+ //
2784+ // set page
2785+ //
2786+
2787+ set_page: function(page) {
2788+ if (page == this.page) return;
2789+
2790+ var updates = {};
2791+ var flashing = [];
2792+
2793+ for (i in this.cache[page]) {
2794+ if (this.cache[this.page][i] == undefined || this.cache[this.page][i] != this.cache[page][i]) {
2795+ updates[i] = this.cache[page][i];
2796+ }
2797+ }
2798+
2799+ for (i in this.cache[this.page]) {
2800+ if (this.cache[page][i] == undefined) {
2801+ updates[i] = 0x4; // black with copy bit set
2802+ }
2803+ else if (this.cache[this.page][i] != this.cache[page][i] && updates[i] == undefined) {
2804+ updates[i] = this.cache[page][i];
2805+ }
2806+ }
2807+
2808+ // select buffer 1
2809+ midi.sendShortMsg(0xb0, 0x0, 0x31);
2810+
2811+ for (i in updates) {
2812+ if ((control = this.name2control[i]) != undefined) {
2813+
2814+ // 0 makes no sense - need 0x4 for black/off
2815+ if (updates[i] == 0) {
2816+ updates[i] = 0x4;
2817+ }
2818+
2819+ // send out non-flashing colors with copy bit removed
2820+ if (updates[i] & 0x4) {
2821+ midi.sendShortMsg(control[0], control[1], updates[i] & 0xfb);
2822+ }
2823+ else {
2824+ // send out off for this buffer for a flashing color
2825+ midi.sendShortMsg(control[0], control[1], 0x4);
2826+ flashing.push([ control[0], control[1], updates[i] ]);
2827+ }
2828+ }
2829+ }
2830+
2831+ // select buffer 0
2832+ midi.sendShortMsg(0xb0, 0x0, 0x34);
2833+
2834+ // send out any flashing updates
2835+ for (i in flashing) {
2836+ midi.sendShortMsg(flashing[i][0], flashing[i][1], flashing[i][2]);
2837+ }
2838+
2839+ // re-enable internal buffer cycling for flashing colors
2840+ midi.sendShortMsg(0xb0, 0x0, 0x28);
2841+
2842+ this.page = page;
2843+ },
2844+
2845+ //
2846+ // define colors
2847+ //
2848+
2849+ colors: function() {
2850+ return {
2851+ black: 4,
2852+
2853+ lo_red: 1 + 4,
2854+ mi_red: 2 + 4,
2855+ hi_red: 3 + 4,
2856+ lo_green: 16 + 4,
2857+ mi_green: 32 + 4,
2858+ hi_green: 48 + 4,
2859+ lo_amber: 17 + 4,
2860+ mi_amber: 34 + 4,
2861+ hi_amber: 51 + 4,
2862+ hi_orange: 35 + 4,
2863+ lo_orange: 18 + 4,
2864+ hi_yellow: 50 + 4,
2865+ lo_yellow: 33 + 4,
2866+
2867+ flash_lo_red: 1,
2868+ flash_mi_red: 2,
2869+ flash_hi_red: 3,
2870+ flash_lo_green: 16,
2871+ flash_mi_green: 32,
2872+ flash_hi_green: 48,
2873+ flash_lo_amber: 17,
2874+ flash_mi_amber: 34,
2875+ flash_hi_amber: 51,
2876+ flash_hi_orange: 35,
2877+ flash_lo_orange: 18,
2878+ flash_hi_yellow: 50,
2879+ flash_lo_yellow: 33
2880+ }
2881+ },
2882+
2883+ //
2884+ // define midi for all the buttons (as we can't define names in the xml or access that data here)
2885+ // to create a 90 degree rotated mapping just redefine this list so "0,0" is still top left etc
2886+ //
2887+
2888+ buttons: function() {
2889+ return {
2890+ 'up': [ 'cc', 1, 0x68 ],
2891+ 'down': [ 'cc', 1, 0x69 ],
2892+ 'left': [ 'cc', 1, 0x6A ],
2893+ 'right': [ 'cc', 1, 0x6B ],
2894+ 'session': [ 'cc', 1, 0x6C ],
2895+ 'user1': [ 'cc', 1, 0x6D ],
2896+ 'user2': [ 'cc', 1, 0x6E ],
2897+ 'mixer': [ 'cc', 1, 0x6F ],
2898+ 'vol': [ 'note', 1, 0x8 ],
2899+ 'pan': [ 'note', 1, 0x18 ],
2900+ 'snda': [ 'note', 1, 0x28 ],
2901+ 'sndb': [ 'note', 1, 0x38 ],
2902+ 'stop': [ 'note', 1, 0x48 ],
2903+ 'trkon': [ 'note', 1, 0x58 ],
2904+ 'solo': [ 'note', 1, 0x68 ],
2905+ 'arm': [ 'note', 1, 0x78 ],
2906+ '0,0': [ 'note', 1, 0x00 ],
2907+ '0,1': [ 'note', 1, 0x01 ],
2908+ '0,2': [ 'note', 1, 0x02 ],
2909+ '0,3': [ 'note', 1, 0x03 ],
2910+ '0,4': [ 'note', 1, 0x04 ],
2911+ '0,5': [ 'note', 1, 0x05 ],
2912+ '0,6': [ 'note', 1, 0x06 ],
2913+ '0,7': [ 'note', 1, 0x07 ],
2914+ '1,0': [ 'note', 1, 0x10 ],
2915+ '1,1': [ 'note', 1, 0x11 ],
2916+ '1,2': [ 'note', 1, 0x12 ],
2917+ '1,3': [ 'note', 1, 0x13 ],
2918+ '1,4': [ 'note', 1, 0x14 ],
2919+ '1,5': [ 'note', 1, 0x15 ],
2920+ '1,6': [ 'note', 1, 0x16 ],
2921+ '1,7': [ 'note', 1, 0x17 ],
2922+ '2,0': [ 'note', 1, 0x20 ],
2923+ '2,1': [ 'note', 1, 0x21 ],
2924+ '2,2': [ 'note', 1, 0x22 ],
2925+ '2,3': [ 'note', 1, 0x23 ],
2926+ '2,4': [ 'note', 1, 0x24 ],
2927+ '2,5': [ 'note', 1, 0x25 ],
2928+ '2,6': [ 'note', 1, 0x26 ],
2929+ '2,7': [ 'note', 1, 0x27 ],
2930+ '3,0': [ 'note', 1, 0x30 ],
2931+ '3,1': [ 'note', 1, 0x31 ],
2932+ '3,2': [ 'note', 1, 0x32 ],
2933+ '3,3': [ 'note', 1, 0x33 ],
2934+ '3,4': [ 'note', 1, 0x34 ],
2935+ '3,5': [ 'note', 1, 0x35 ],
2936+ '3,6': [ 'note', 1, 0x36 ],
2937+ '3,7': [ 'note', 1, 0x37 ],
2938+ '4,0': [ 'note', 1, 0x40 ],
2939+ '4,1': [ 'note', 1, 0x41 ],
2940+ '4,2': [ 'note', 1, 0x42 ],
2941+ '4,3': [ 'note', 1, 0x43 ],
2942+ '4,4': [ 'note', 1, 0x44 ],
2943+ '4,5': [ 'note', 1, 0x45 ],
2944+ '4,6': [ 'note', 1, 0x46 ],
2945+ '4,7': [ 'note', 1, 0x47 ],
2946+ '5,0': [ 'note', 1, 0x50 ],
2947+ '5,1': [ 'note', 1, 0x51 ],
2948+ '5,2': [ 'note', 1, 0x52 ],
2949+ '5,3': [ 'note', 1, 0x53 ],
2950+ '5,4': [ 'note', 1, 0x54 ],
2951+ '5,5': [ 'note', 1, 0x55 ],
2952+ '5,6': [ 'note', 1, 0x56 ],
2953+ '5,7': [ 'note', 1, 0x57 ],
2954+ '6,0': [ 'note', 1, 0x60 ],
2955+ '6,1': [ 'note', 1, 0x61 ],
2956+ '6,2': [ 'note', 1, 0x62 ],
2957+ '6,3': [ 'note', 1, 0x63 ],
2958+ '6,4': [ 'note', 1, 0x64 ],
2959+ '6,5': [ 'note', 1, 0x65 ],
2960+ '6,6': [ 'note', 1, 0x66 ],
2961+ '6,7': [ 'note', 1, 0x67 ],
2962+ '7,0': [ 'note', 1, 0x70 ],
2963+ '7,1': [ 'note', 1, 0x71 ],
2964+ '7,2': [ 'note', 1, 0x72 ],
2965+ '7,3': [ 'note', 1, 0x73 ],
2966+ '7,4': [ 'note', 1, 0x74 ],
2967+ '7,5': [ 'note', 1, 0x75 ],
2968+ '7,6': [ 'note', 1, 0x76 ],
2969+ '7,7': [ 'note', 1, 0x77 ]
2970+ };
2971+ }
2972+};
2973+
2974
2975=== modified file 'mixxx/res/controllers/common-controller-scripts.js'
2976--- mixxx/res/controllers/common-controller-scripts.js 2012-04-03 11:49:10 +0000
2977+++ mixxx/res/controllers/common-controller-scripts.js 2012-05-26 14:59:19 +0000
2978@@ -82,7 +82,7 @@
2979
2980
2981 script.pitch = function (LSB, MSB, status) {
2982- script.midiPitch(LSB, MSB, status);
2983+ return script.midiPitch(LSB, MSB, status);
2984 }
2985
2986 /* -------- ------------------------------------------------------
2987@@ -106,6 +106,115 @@
2988 return rate;
2989 }
2990
2991+/* -------- ------------------------------------------------------
2992+ script.spinbackDefault
2993+ Purpose: wrapper around spinback() that can be directly mapped
2994+ from xml for a spinback effect
2995+ Input: channel, control, value, status, group
2996+ Output: none
2997+ -------- ------------------------------------------------------ */
2998+script.spinbackDefault = function(channel, control, value, status, group) {
2999+ // disable on note-off or zero value note/cc
3000+ script.spinback(group, ((status & 0xF0) != 0x80 && value > 0));
3001+}
3002+
3003+/* -------- ------------------------------------------------------
3004+ script.brakeDefault
3005+ Purpose: wrapper around brake() that can be directly mapped
3006+ from xml for a brake effect
3007+ Input: channel, control, value, status, group
3008+ Output: none
3009+ -------- ------------------------------------------------------ */
3010+script.brakeDefault = function(channel, control, value, status, group) {
3011+ // disable on note-off or zero value note/cc
3012+ script.brake(group, ((status & 0xF0) != 0x80 && value > 0));
3013+}
3014+
3015+/* -------- ------------------------------------------------------
3016+ script.spinback
3017+ Purpose: Activate or disable a spinback effect on the chosen deck
3018+ Input: group, enable/disable, [delay], [factor], [inital rate]
3019+ Output: None
3020+ -------- ------------------------------------------------------ */
3021+script.spinback = function(group, activate, factor, rate, delay) {
3022+ if (factor == undefined) factor = 0.8;
3023+ if (rate == undefined) rate = -10;
3024+ if (delay == undefined) delay = 5;
3025+ script.deckSpinbackBrake(group, activate, factor, rate, delay);
3026+}
3027+
3028+/* -------- ------------------------------------------------------
3029+ script.brake
3030+ Purpose: Activate or disable a brake effect on the chosen deck
3031+ Input: group, enable/disable, [delay], [factor], [inital rate]
3032+ Output: None
3033+ -------- ------------------------------------------------------ */
3034+script.brake = function(group, activate, factor, rate, delay) {
3035+ if (factor == undefined) factor = 0.95;
3036+ if (rate == undefined) rate = 1;
3037+ if (delay == undefined) delay = 0;
3038+ script.deckSpinbackBrake(group, activate, factor, rate, delay);
3039+}
3040+
3041+script.deckSpinbackBrakeData = {};
3042+
3043+script.deckSpinbackBrake = function(group, activate, factor, rate, delay) {
3044+
3045+ if (activate != undefined) {
3046+
3047+ // store the current settings
3048+
3049+ if (script.deckSpinbackBrakeData[group] == undefined) {
3050+ script.deckSpinbackBrakeData[group] = { timer: null, delay: delay, factor: factor, rate: rate };
3051+ }
3052+ else {
3053+ script.deckSpinbackBrakeData[group].delay = delay;
3054+ script.deckSpinbackBrakeData[group].factor = factor;
3055+ script.deckSpinbackBrakeData[group].rate = rate;
3056+ }
3057+
3058+ // kill timer when both enabling or disabling
3059+
3060+ if (script.deckSpinbackBrakeData[group].timer != null) {
3061+ engine.stopTimer(script.deckSpinbackBrakeData[group].timer);
3062+ script.deckSpinbackBrakeData[group].timer = null;
3063+ }
3064+
3065+ // enable/disable scratch2 mode
3066+
3067+ engine.setValue(group, 'scratch2_enable', activate ? 1 : 0);
3068+
3069+ if (activate) {
3070+ // save keylock status and disable it
3071+ if ((script.deckSpinbackBrakeData[group].keylock = engine.getValue(group, "keylock")) > 0) {
3072+ engine.setValue(group, "keylock", 0);
3073+ }
3074+
3075+ // setup timer and send first scratch2 'tick' if activating
3076+ script.deckSpinbackBrakeData[group].timer = engine.beginTimer(50, 'script.deckSpinbackBrake("' + group + '")');
3077+ engine.setValue(group, 'scratch2', script.deckSpinbackBrakeData[group].rate);
3078+ }
3079+
3080+ // re-enable keylock if needed
3081+
3082+ else if (script.deckSpinbackBrakeData[group].keylock) {
3083+ engine.setValue(group, "keylock", 1);
3084+ }
3085+ }
3086+ else {
3087+ // being called from a timer
3088+
3089+ engine.setValue(group, 'scratch2', script.deckSpinbackBrakeData[group].rate);
3090+
3091+ if (script.deckSpinbackBrakeData[group].delay > 0) {
3092+ script.deckSpinbackBrakeData[group].delay--;
3093+ }
3094+ else {
3095+ script.deckSpinbackBrakeData[group].rate *= script.deckSpinbackBrakeData[group].factor;
3096+ }
3097+ }
3098+}
3099+
3100 // bpm - Used for tapping the desired BPM for a deck
3101 function bpm() {}
3102
3103@@ -244,4 +353,4 @@
3104 }
3105 }
3106
3107-// ----------------- END Object definitions ----------------------
3108\ No newline at end of file
3109+// ----------------- END Object definitions ----------------------

Subscribers

People subscribed via source and target branches