Merge lp:~neale/mixxx/usbbulk into lp:~mixxxdevelopers/mixxx/trunk
- usbbulk
- Merge into trunk
Proposed by
Neale Pickett
Status: | Merged |
---|---|
Merged at revision: | 3294 |
Proposed branch: | lp:~neale/mixxx/usbbulk |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Diff against target: |
1147 lines (+1017/-2) 14 files modified
mixxx/SConstruct (+1/-0) mixxx/build/features.py (+31/-0) mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp (+6/-1) mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp (+1/-1) mixxx/res/controllers/Hercules DJ Control MP3 e2.cntrlr.xml (+15/-0) mixxx/res/controllers/Hercules-mp3e2-compat.js (+178/-0) mixxx/res/controllers/Hercules-mp3e2.js (+275/-0) mixxx/res/controllers/common-bulk-midi.js (+5/-0) mixxx/src/controllers/bulk/bulkcontroller.cpp (+274/-0) mixxx/src/controllers/bulk/bulkcontroller.h (+105/-0) mixxx/src/controllers/bulk/bulkenumerator.cpp (+71/-0) mixxx/src/controllers/bulk/bulkenumerator.h (+24/-0) mixxx/src/controllers/bulk/bulksupported.h (+24/-0) mixxx/src/controllers/controllermanager.cpp (+7/-0) |
To merge this branch: | bzr merge lp:~neale/mixxx/usbbulk |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mixxx Release Managers | Pending | ||
Review via email: mp+121312@code.launchpad.net |
Commit message
Description of the change
USB bulk driver, makes Hercules controllers work without a driver. Still has shutdown issues, but it works fine until you close mixxx. Linux users like me are in dire need of this.
Should be tested with non-Herc controllers to make sure the shutdown problem doesn't affect other USB devices.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'mixxx/SConstruct' |
2 | --- mixxx/SConstruct 2012-04-25 15:40:15 +0000 |
3 | +++ mixxx/SConstruct 2012-08-25 14:32:20 +0000 |
4 | @@ -38,6 +38,7 @@ |
5 | features.MediaFoundation, |
6 | features.HSS1394, |
7 | features.HID, |
8 | + features.Bulk, |
9 | features.VinylControl, |
10 | features.Shoutcast, |
11 | features.Profiling, |
12 | |
13 | === modified file 'mixxx/build/features.py' |
14 | --- mixxx/build/features.py 2012-07-08 07:30:32 +0000 |
15 | +++ mixxx/build/features.py 2012-08-25 14:32:20 +0000 |
16 | @@ -105,6 +105,37 @@ |
17 | sources.append(os.path.join(self.HIDAPI_INTERNAL_PATH, 'mac/hid.c')) |
18 | return sources |
19 | |
20 | +class Bulk(Feature): |
21 | + def description(self): |
22 | + return "USB Bulk controller support" |
23 | + |
24 | + def enabled(self, build): |
25 | + build.flags['bulk'] = util.get_flags(build.env, 'bulk', 1) |
26 | + if int(build.flags['bulk']): |
27 | + return True |
28 | + return False |
29 | + |
30 | + def add_options(self, build, vars): |
31 | + vars.Add('bulk', 'Set to 1 to enable USB Bulk controller support.', 1) |
32 | + |
33 | + def configure(self, build, conf): |
34 | + if not self.enabled(build): |
35 | + return |
36 | + |
37 | + build.env.ParseConfig('pkg-config libusb-1.0 --silence-errors --cflags --libs') |
38 | + if (not conf.CheckLib(['libusb-1.0', 'usb-1.0']) or |
39 | + not conf.CheckHeader('libusb-1.0/libusb.h')): |
40 | + raise Exception('Did not find the libusb 1.0 development library or its header file, exiting!') |
41 | + |
42 | + build.env.Append(CPPDEFINES = '__BULK__') |
43 | + |
44 | + def sources(self, build): |
45 | + sources = ['controllers/bulk/bulkcontroller.cpp', |
46 | + 'controllers/bulk/bulkenumerator.cpp'] |
47 | + |
48 | + return sources |
49 | + |
50 | + |
51 | class Mad(Feature): |
52 | def description(self): |
53 | return "MAD MP3 Decoder" |
54 | |
55 | === modified file 'mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp' |
56 | --- mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp 2012-05-04 03:44:44 +0000 |
57 | +++ mixxx/plugins/soundsourcem4a/m4a/mp4-mixxx.cpp 2012-08-25 14:32:20 +0000 |
58 | @@ -145,7 +145,7 @@ |
59 | priv->overflow_buf_len = 0; |
60 | priv->overflow_buf = NULL; |
61 | |
62 | - priv->sample_buf_len = 4096; |
63 | + priv->sample_buf_len = 8192; |
64 | priv->sample_buf = new char[priv->sample_buf_len]; |
65 | priv->sample_buf_frame = -1; |
66 | |
67 | @@ -185,6 +185,8 @@ |
68 | priv->aac_data_len = MP4GetTrackMaxSampleSize(priv->mp4.handle, priv->mp4.track); |
69 | priv->aac_data = new unsigned char[priv->aac_data_len]; |
70 | |
71 | + qDebug() << "AAC Data Length:" << priv->aac_data_len; |
72 | + |
73 | priv->mp4.num_samples = MP4GetTrackNumberOfSamples(priv->mp4.handle, priv->mp4.track); |
74 | // MP4 frames are 1-indexed |
75 | priv->mp4.sample = 1; |
76 | @@ -335,6 +337,7 @@ |
77 | bytes = frame_info.samples * 2; |
78 | |
79 | if (bytes > count) { |
80 | + qDebug() << "TOO MUCH BABY! " << bytes << ":" << count; |
81 | /* decoded too much; keep overflow. */ |
82 | //memcpy(priv->overflow_buf_base, sample_buf + count, bytes - count); |
83 | //priv->overflow_buf = priv->overflow_buf_base; |
84 | @@ -358,6 +361,8 @@ |
85 | if (priv->overflow_buf_len > 0) { |
86 | int len = priv->overflow_buf_len; |
87 | |
88 | + qDebug() << "using overflow " << len << ":" << count; |
89 | + |
90 | if (len > count) |
91 | len = count; |
92 | |
93 | |
94 | === modified file 'mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp' |
95 | --- mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp 2011-03-31 16:07:22 +0000 |
96 | +++ mixxx/plugins/soundsourcem4a/soundsourcem4a.cpp 2012-08-25 14:32:20 +0000 |
97 | @@ -131,7 +131,7 @@ |
98 | |
99 | int total_bytes_to_decode = size * m_iChannels; |
100 | int total_bytes_decoded = 0; |
101 | - int num_bytes_req = 4096; |
102 | + int num_bytes_req = 8192; |
103 | char* buffer = (char*)destination; |
104 | SAMPLE * as_buffer = (SAMPLE*) destination; //pointer for mono->stereo filling. |
105 | do { |
106 | |
107 | === added file 'mixxx/res/controllers/Hercules DJ Control MP3 e2.cntrlr.xml' |
108 | --- mixxx/res/controllers/Hercules DJ Control MP3 e2.cntrlr.xml 1970-01-01 00:00:00 +0000 |
109 | +++ mixxx/res/controllers/Hercules DJ Control MP3 e2.cntrlr.xml 2012-08-25 14:32:20 +0000 |
110 | @@ -0,0 +1,15 @@ |
111 | +<?xml version='1.0' encoding='utf-8'?> |
112 | +<MixxxControllerPreset mixxxVersion="1.11+" schemaVersion="1"> |
113 | + <info> |
114 | + <name>Hercules DJ Control MP3 e2</name> |
115 | + <author>Neale Pickett</author> |
116 | + <description>Hercules DJ Control MP3 e2</description> |
117 | + </info> |
118 | + <controller id="DJ"> |
119 | + <scriptfiles> |
120 | + <file functionprefix="" filename="common-bulk-midi.js"/> |
121 | + <file functionprefix="" filename="Hercules DJ Control MP3 e2-scripts.js"/> |
122 | + <file functionprefix="MP3e2" filename="Hercules-mp3e2-compat.js"/> |
123 | + </scriptfiles> |
124 | + </controller> |
125 | +</MixxxControllerPreset> |
126 | |
127 | === added file 'mixxx/res/controllers/Hercules-mp3e2-compat.js' |
128 | --- mixxx/res/controllers/Hercules-mp3e2-compat.js 1970-01-01 00:00:00 +0000 |
129 | +++ mixxx/res/controllers/Hercules-mp3e2-compat.js 2012-08-25 14:32:20 +0000 |
130 | @@ -0,0 +1,178 @@ |
131 | +MP3e2 = Object() |
132 | + |
133 | +blink = new Object(); |
134 | + |
135 | +function light(id, enable) { |
136 | + if (enable == blink) { |
137 | + bval = 0x7f; |
138 | + val = 0; |
139 | + } else { |
140 | + bval = 0; |
141 | + val = enable?0x7f:0; |
142 | + } |
143 | + midi.sendShortMsg(0x90, id, val); |
144 | + midi.sendShortMsg(0x90, id + 0x30, bval); |
145 | +} |
146 | + |
147 | +function simpleButton(id) { |
148 | + return function(val, group) { |
149 | + var btn = (group == "[Channel1]")?(id):(id+20); |
150 | + |
151 | + light(btn, !!val); |
152 | + } |
153 | +} |
154 | + |
155 | +LEDs = [ |
156 | + ["loop_start_position", simpleButton(1)], |
157 | + ["loop_end_position", simpleButton(2)], |
158 | + ["loop_enabled", simpleButton(51)], |
159 | + ["loop_enabled", simpleButton(52)], |
160 | + ["hotcue_1_enabled", simpleButton(5)], |
161 | + ["hotcue_2_enabled", simpleButton(6)], |
162 | + ["hotcue_3_enabled", simpleButton(7)], |
163 | + ["hotcue_4_enabled", simpleButton(8)], |
164 | + ["flanger", simpleButton(67)], |
165 | + ["play", simpleButton(15)], |
166 | + ["pfl", simpleButton(16)], |
167 | + ["beat_active", simpleButton(18)], |
168 | +]; |
169 | + |
170 | +MP3e2.init = function(id) { |
171 | + HerculesMP3e2.init(id); |
172 | + |
173 | + // Set up LEDs |
174 | + for (var i = 0; i < 2; i += 1) { |
175 | + group = "[Channel" + (i+1) + "]"; |
176 | + |
177 | + for (var j in LEDs) { |
178 | + var control = LEDs[j][0]; |
179 | + var func = LEDs[j][1]; |
180 | + |
181 | + engine.connectControl(group, control, func); |
182 | + engine.trigger(group, control); |
183 | + } |
184 | + } |
185 | +} |
186 | + |
187 | +var c1 = '[Channel1]' |
188 | +var c2 = '[Channel2]' |
189 | + |
190 | +MP3e2.incomingData = function(data, length) { |
191 | + for (var i = 0; i < length; i += 3) { |
192 | + var status = data[i]; |
193 | + var midino = data[i+1]; |
194 | + var value = data[i+2]; |
195 | + var group; |
196 | + var f = null; |
197 | + |
198 | + if (status == 0xb0) { |
199 | + if ((midino > 0x38) || |
200 | + ((midino < 0x34) && (midino & 1))) { |
201 | + group = c2; |
202 | + } else { |
203 | + group = c1; |
204 | + } |
205 | + } else if (status == 0x90) { |
206 | + if (midino <= 20) { |
207 | + group = c1; |
208 | + } else if (midino < 40) { |
209 | + group = c2; |
210 | + } |
211 | + } |
212 | + |
213 | + switch ((status<<8) | midino) { |
214 | + case 0x9001: case 0x9015: |
215 | + case 0x9002: case 0x9016: |
216 | + case 0x9003: case 0x9017: |
217 | + case 0x9004: case 0x9018: |
218 | + case 0x9005: case 0x9019: |
219 | + case 0x9006: case 0x901a: |
220 | + case 0x9007: case 0x901b: |
221 | + case 0x9008: case 0x901c: |
222 | + f = HerculesMP3e2.keyButton; |
223 | + break; |
224 | + case 0x900a: case 0x901e: |
225 | + case 0x900b: case 0x901f: |
226 | + f = HerculesMP3e2.pitchbend; |
227 | + break; |
228 | + case 0x900c: case 0x9020: |
229 | + f = "back"; |
230 | + break; |
231 | + case 0x900d: case 0x9021: |
232 | + f = "fwd"; |
233 | + break; |
234 | + case 0x900e: case 0x9022: |
235 | + f = HerculesMP3e2.cue; |
236 | + break; |
237 | + case 0x900f: case 0x9023: |
238 | + if (value == 0) return; |
239 | + f = "play"; |
240 | + value = ! engine.getValue(group, f); |
241 | + break; |
242 | + case 0x9010: case 0x9024: |
243 | + if (value == 0) return; |
244 | + f = "pfl"; |
245 | + value = ! engine.getValue(group, f); |
246 | + break; |
247 | + case 0x9011: case 0x9025: |
248 | + f = HerculesMP3e2.loadTrack; |
249 | + break; |
250 | + case 0x9012: case 0x9026: |
251 | + f = HerculesMP3e2.sync; |
252 | + break; |
253 | + case 0x9013: case 0x9027: |
254 | + f = HerculesMP3e2.masterTempo; |
255 | + break; |
256 | + |
257 | + |
258 | + case 0x9029: |
259 | + group = '[Playlist]'; |
260 | + f = 'SelectPrevTrack'; |
261 | + break; |
262 | + case 0x902a: |
263 | + group = '[Playlist]'; |
264 | + f = 'SelectNextTrack'; |
265 | + break; |
266 | + case 0x902b: |
267 | + case 0x902c: |
268 | + group = '[Playlist]'; |
269 | + f = HerculesMP3e2.scroll; |
270 | + break; |
271 | + case 0x902d: |
272 | + f = HerculesMP3e2.scratch; |
273 | + break; |
274 | + case 0x902e: |
275 | + f = HerculesMP3e2.automix; |
276 | + break; |
277 | + |
278 | + case 0xb030: case 0xb031: |
279 | + f = HerculesMP3e2.jogWheel; |
280 | + break; |
281 | + case 0xb032: case 0xb033: |
282 | + f = HerculesMP3e2.pitch; |
283 | + break; |
284 | + case 0xb034: case 0xb039: |
285 | + engine.setValue(group, "volume", script.absoluteLin(value, 0, 1)); |
286 | + break; |
287 | + case 0xb035: case 0xb03a: |
288 | + engine.setValue(group, "filterHigh", script.absoluteNonLin(value, 0, 1, 4)); |
289 | + break; |
290 | + case 0xb036: case 0xb03b: |
291 | + engine.setValue(group, "filterMid", script.absoluteNonLin(value, 0, 1, 4)); |
292 | + break; |
293 | + case 0xb037: case 0xb03c: |
294 | + engine.setValue(group, "filterLow", script.absoluteNonLin(value, 0, 1, 4)); |
295 | + break; |
296 | + case 0xb038: |
297 | + engine.setValue('[Master]', 'crossfader', script.absoluteLin(value, -1, 1)); |
298 | + break; |
299 | + } |
300 | + |
301 | + if (typeof(f) == 'string') { |
302 | + engine.setValue(group, f, (value>0)?1:0); |
303 | + } else if (f) { |
304 | + f(0, midino, value, status, group); |
305 | + } |
306 | + } |
307 | +} |
308 | + |
309 | |
310 | === added file 'mixxx/res/controllers/Hercules-mp3e2.js' |
311 | --- mixxx/res/controllers/Hercules-mp3e2.js 1970-01-01 00:00:00 +0000 |
312 | +++ mixxx/res/controllers/Hercules-mp3e2.js 2012-08-25 14:32:20 +0000 |
313 | @@ -0,0 +1,275 @@ |
314 | +secondsBlink = 30; |
315 | + |
316 | +MP3e2 = new Object(); |
317 | + |
318 | +function sendMsg(status, a, b) { |
319 | + midi.sendShortMsg(status, a, b); |
320 | + //controller.send([status, a, b], 3); |
321 | +} |
322 | + |
323 | +blink = new Object(); |
324 | + |
325 | +function light(id, enable) { |
326 | + if (enable == blink) { |
327 | + bval = 0x7f; |
328 | + val = 0; |
329 | + } else { |
330 | + bval = 0; |
331 | + val = enable?0x7f:0; |
332 | + } |
333 | + sendMsg(0x90, id, val); |
334 | + sendMsg(0x90, id + 0x30, bval); |
335 | +} |
336 | + |
337 | +function simpleButton(id) { |
338 | + return function(val, group) { |
339 | + var btn = (group == "[Channel1]")?(id):(id+20); |
340 | + |
341 | + light(btn, !!val); |
342 | + } |
343 | +} |
344 | + |
345 | +function toggleButton(id, val, group) { |
346 | + var btn = (group == "[Channel1]")?(id):(id + 20); |
347 | + |
348 | + light(btn, !!val); |
349 | +} |
350 | + |
351 | +function btn_loop_enabled(val, group) { |
352 | + toggleButton(3, val, group); |
353 | + toggleButton(4, val, group); |
354 | +} |
355 | + |
356 | +function btn_playposition(pos, group) { |
357 | + var secondsToEnd = engine.getValue(group, "duration") * (1 - pos); |
358 | + var btn = (group == "[Channel1]")?14:34; |
359 | + |
360 | + if (secondsToEnd < 1) { |
361 | + light(btn, false); |
362 | + } else if (secondsToEnd < secondsBlink) { |
363 | + light(btn, blink); |
364 | + } else { |
365 | + light(btn, true); |
366 | + } |
367 | +} |
368 | + |
369 | +binds = [ |
370 | + ["loop_start_position", simpleButton(1)], |
371 | + ["loop_end_position", simpleButton(2)], |
372 | + ["hotcue_1_enabled", simpleButton(5)], |
373 | + ["hotcue_2_enabled", simpleButton(6)], |
374 | + ["hotcue_3_enabled", simpleButton(7)], |
375 | + ["hotcue_4_enabled", simpleButton(8)], |
376 | + ["play", simpleButton(15)], |
377 | + ["pfl", simpleButton(16)], |
378 | + ["playposition", btn_playposition], |
379 | + ["loop_enabled", btn_loop_enabled], |
380 | +]; |
381 | + |
382 | +meta = false; |
383 | +ctl_id = ""; |
384 | + |
385 | +MP3e2.init = function(id) { |
386 | + print("MP3e2 controller initialized: " + id); |
387 | + |
388 | + ctl_id = id; |
389 | + |
390 | + for (var i = 0; i < 2; i += 1) { |
391 | + group = "[Channel" + (i+1) + "]"; |
392 | + |
393 | + |
394 | + for (var j in binds) { |
395 | + var control = binds[j][0]; |
396 | + var func = binds[j][1]; |
397 | + |
398 | + engine.connectControl(group, control, func); |
399 | + engine.trigger(group, control); |
400 | + } |
401 | + } |
402 | + |
403 | + // Turn everything off |
404 | + for (i = 1; i < 46; i += 1) { |
405 | + light(i, false); |
406 | + } |
407 | + |
408 | + // Request all faders report position |
409 | + sendMsg(0xb0, 0x7f, 0x7f); |
410 | + |
411 | + // Turn on some lights |
412 | + light(46, true); // Automix |
413 | + //light(14, true); // Cue A |
414 | + //light(34, true); // Cue B |
415 | +} |
416 | + |
417 | +MP3e2.shutdown = function() { |
418 | + controller.close(); |
419 | + print("MP3e2 controller shutdown: " + ctl_id); |
420 | +} |
421 | + |
422 | +meta_buttons = { |
423 | + 1: "loop_start_position", |
424 | + 2: "loop_end_position", |
425 | + 5: "hotcue_1_clear", |
426 | + 6: "hotcue_2_clear", |
427 | + 7: "hotcue_3_clear", |
428 | + 8: "hotcue_4_clear", |
429 | +}; |
430 | + |
431 | +buttons = { |
432 | + 1: "loop_in", |
433 | + 2: "loop_out", |
434 | + 3: "reloop_exit", |
435 | + 4: "reloop_exit", |
436 | + 5: "hotcue_1_activate", |
437 | + 6: "hotcue_2_activate", |
438 | + 7: "hotcue_3_activate", |
439 | + 8: "hotcue_4_activate", |
440 | + 10: "rate_temp_down", |
441 | + 11: "rate_temp_up", |
442 | + 12: "back", |
443 | + 13: "fwd", |
444 | + 14: "cue_default", |
445 | + 15: "play", |
446 | + 16: "pfl", |
447 | + 17: "LoadSelectedTrack", |
448 | + 18: "beatsync", |
449 | + |
450 | + 41: "SelectPrevTrack", |
451 | + 42: "SelectNextTrack", |
452 | +}; |
453 | + |
454 | +function handleButton(id, pressed) { |
455 | + var val = pressed ? 1 : 0; |
456 | + var did = id; |
457 | + var group, ctl; |
458 | + |
459 | + if (id > 40) { |
460 | + group = '[Playlist]'; |
461 | + } else if (id > 20) { |
462 | + group = '[Channel2]'; |
463 | + did -= 20; |
464 | + } else { |
465 | + group = '[Channel1]'; |
466 | + } |
467 | + |
468 | + if (meta) { |
469 | + ctl = meta_buttons[did]; |
470 | + } |
471 | + if (! ctl) { |
472 | + ctl = buttons[did]; |
473 | + } |
474 | + |
475 | + print(id + ": " + ctl); |
476 | + if (ctl) { |
477 | + switch (did) { |
478 | + case 1: |
479 | + case 2: |
480 | + case 5: |
481 | + case 6: |
482 | + case 7: |
483 | + case 8: |
484 | + if (meta) { |
485 | + val = -pressed; |
486 | + } |
487 | + break; |
488 | + case 15: |
489 | + case 16: |
490 | + // Ignore button release, toggle value |
491 | + if (! pressed) { |
492 | + return; |
493 | + } |
494 | + val = ! engine.getValue(group, ctl); |
495 | + break; |
496 | + } |
497 | + |
498 | + engine.setValue(group, ctl, val); |
499 | + return; |
500 | + } |
501 | + |
502 | + switch (id) { |
503 | + case 46: |
504 | + meta = !!pressed; |
505 | + light(30, meta); |
506 | + light(31, meta); |
507 | + light(10, meta); |
508 | + light(11, meta); |
509 | + light(19, meta); |
510 | + light(39, meta); |
511 | + break; |
512 | + } |
513 | +} |
514 | + |
515 | + |
516 | +faders = { |
517 | + 0x35: "filterHigh", |
518 | + 0x36: "filterMid", |
519 | + 0x37: "filterLow" |
520 | +}; |
521 | + |
522 | +function handleFader(id, val) { |
523 | + if (id < 0x34) { |
524 | + group = '[Channel' + ((id & 1) + 1) + ']'; |
525 | + id = id & 0xfe; |
526 | + } else if (id < 0x39) { |
527 | + group = '[Channel1]'; |
528 | + } else { |
529 | + group = '[Channel2]'; |
530 | + id -= 5; |
531 | + } |
532 | + |
533 | + ctl = faders[id]; |
534 | + |
535 | + switch (id) { |
536 | + case 0x30: // Jog wheel |
537 | + if (val == 127) { |
538 | + dir = -1; |
539 | + } else { |
540 | + dir = 1; |
541 | + } |
542 | + engine.setValue(group, "jog", dir); |
543 | + break; |
544 | + case 0x32: // Pitch adjust |
545 | + if (val == 127) { |
546 | + ctl = "rate_perm_down"; |
547 | + } else { |
548 | + ctl = "rate_perm_up"; |
549 | + } |
550 | + engine.setValue(group, ctl, 1); |
551 | + engine.setValue(group, ctl, 0); |
552 | + break; |
553 | + case 0x34: |
554 | + fval = script.absoluteLin(val, 0, 1); |
555 | + engine.setValue(group, "volume", fval); |
556 | + return; |
557 | + case 0x35: |
558 | + case 0x36: |
559 | + case 0x37: |
560 | + fval = script.absoluteNonLin(val, 0, 1, 4); |
561 | + engine.setValue(group, ctl, fval); |
562 | + break; |
563 | + case 0x38: // Crossfader |
564 | + fval = script.absoluteLin(val, -1, 1); |
565 | + engine.setValue('[Master]', 'crossfader', fval); |
566 | + break; |
567 | + } |
568 | +} |
569 | + |
570 | +MP3e2.incomingData = function(data, length) { |
571 | + for (i = 0; i < length; i += 3) { |
572 | + switch (data[i]) { |
573 | + case 0x90: |
574 | + handleButton(data[i+1], data[i+2] == 0x7f); |
575 | + break; |
576 | + case 0xb0: |
577 | + handleFader(data[i+1], data[i+2]); |
578 | + break; |
579 | + default: |
580 | + print("Unhandled packet: " + |
581 | + " 0x" + data[i].toString(16) + |
582 | + " 0x" + data[i+1].toString(16) + |
583 | + " 0x" + data[i+2].teString(16)) |
584 | + break; |
585 | + } |
586 | + } |
587 | +} |
588 | + |
589 | |
590 | === added file 'mixxx/res/controllers/common-bulk-midi.js' |
591 | --- mixxx/res/controllers/common-bulk-midi.js 1970-01-01 00:00:00 +0000 |
592 | +++ mixxx/res/controllers/common-bulk-midi.js 2012-08-25 14:32:20 +0000 |
593 | @@ -0,0 +1,5 @@ |
594 | +midi = new Object(); |
595 | + |
596 | +midi.sendShortMsg = function(status, a, b) { |
597 | + controller.send([status, a, b], 3); |
598 | +} |
599 | |
600 | === added directory 'mixxx/src/controllers/bulk' |
601 | === added file 'mixxx/src/controllers/bulk/bulkcontroller.cpp' |
602 | --- mixxx/src/controllers/bulk/bulkcontroller.cpp 1970-01-01 00:00:00 +0000 |
603 | +++ mixxx/src/controllers/bulk/bulkcontroller.cpp 2012-08-25 14:32:20 +0000 |
604 | @@ -0,0 +1,274 @@ |
605 | +/** |
606 | + * @file bulkcontroller.cpp |
607 | + * @author Neale Pickett neale@woozle.org |
608 | + * @date Thu Jun 28 2012 |
609 | + * @brief USB Bulk controller backend |
610 | + * |
611 | + */ |
612 | + |
613 | +#include "controllers/bulk/bulkcontroller.h" |
614 | +#include "controllers/bulk/bulksupported.h" |
615 | + |
616 | +#include <time.h> |
617 | +#include <stdio.h> |
618 | + |
619 | + |
620 | +/* This is for my debugging, because I like typing |
621 | + DUMP(); |
622 | + more than typing |
623 | + qDebug() << "foo"; |
624 | + */ |
625 | +#ifdef NODUMP |
626 | +# define DUMPf(fmt, args...) |
627 | +#else |
628 | +# define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) |
629 | +#endif |
630 | +#define DUMP() DUMPf("") |
631 | +#define DUMP_d(v) DUMPf("%s = %d", #v, v) |
632 | +#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v) |
633 | +#define DUMP_s(v) DUMPf("%s = %s", #v, v) |
634 | +#define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v) |
635 | +#define DUMP_p(v) DUMPf("%s = %p", #v, v) |
636 | + |
637 | +BulkReader::BulkReader(libusb_device_handle *handle, unsigned char in_epaddr) |
638 | + : QThread() { |
639 | + m_phandle = handle; |
640 | + m_in_epaddr = in_epaddr; |
641 | +} |
642 | + |
643 | +BulkReader::~BulkReader() { |
644 | + m_phandle = NULL; |
645 | +} |
646 | + |
647 | +void BulkReader::stop() { |
648 | + m_stop = 1; |
649 | +} |
650 | + |
651 | +void BulkReader::run() { |
652 | + m_stop = 0; |
653 | + unsigned char data[255]; |
654 | + |
655 | + while (m_stop == 0) { |
656 | + // Blocked polling: The only problem with this is that we can't close |
657 | + // the device until the block is released, which means the controller |
658 | + // has to send more data |
659 | + //result = hid_read_timeout(m_pHidDevice, data, 255, -1); |
660 | + |
661 | + // This relieves that at the cost of higher CPU usage since we only |
662 | + // block for a short while (500ms) |
663 | + int transferred; |
664 | + int result; |
665 | + |
666 | + result = libusb_bulk_transfer(m_phandle, |
667 | + m_in_epaddr, |
668 | + data, sizeof data, |
669 | + &transferred, 500); |
670 | + if (result >= 0) { |
671 | + //qDebug() << "Read" << result << "bytes, pointer:" << data; |
672 | + QByteArray outData((char *)data, transferred); |
673 | + emit(incomingData(outData)); |
674 | + } |
675 | + |
676 | + } |
677 | + qDebug() << "Stopped Reader"; |
678 | +} |
679 | + |
680 | +static QString |
681 | +get_string(libusb_device_handle *handle, u_int8_t id) |
682 | +{ |
683 | + unsigned char buf[128]; |
684 | + QString s; |
685 | + |
686 | + buf[0] = 0; |
687 | + if (id) { |
688 | + (void)libusb_get_string_descriptor_ascii(handle, id, buf, sizeof buf); |
689 | + } |
690 | + |
691 | + return QString::fromAscii((char *)buf); |
692 | +} |
693 | + |
694 | + |
695 | +BulkController::BulkController(libusb_device_handle *handle, |
696 | + struct libusb_device_descriptor *desc) |
697 | +{ |
698 | + vendor_id = desc->idVendor; |
699 | + product_id = desc->idProduct; |
700 | + |
701 | + manufacturer = get_string(handle, desc->iManufacturer); |
702 | + product = get_string(handle, desc->iProduct); |
703 | + m_sUID = get_string(handle, desc->iSerialNumber); |
704 | + |
705 | + setDeviceCategory(tr("USB Controller")); |
706 | + |
707 | + setDeviceName(QString("%1 %2").arg(product).arg(m_sUID)); |
708 | + |
709 | + setInputDevice(true); |
710 | + setOutputDevice(true); |
711 | + m_pReader = NULL; |
712 | +} |
713 | + |
714 | +BulkController::~BulkController() { |
715 | + close(); |
716 | +} |
717 | + |
718 | +void BulkController::visit(const MidiControllerPreset* preset) { |
719 | + Q_UNUSED(preset); |
720 | + // TODO(XXX): throw a hissy fit. |
721 | + qDebug() << "ERROR: Attempting to load a MidiControllerPreset to an HidController!"; |
722 | +} |
723 | + |
724 | +void BulkController::visit(const HidControllerPreset* preset) { |
725 | + m_preset = *preset; |
726 | + // Emit presetLoaded with a clone of the preset. |
727 | + emit(presetLoaded(getPreset())); |
728 | +} |
729 | + |
730 | +bool BulkController::savePreset(const QString fileName) const { |
731 | + HidControllerPresetFileHandler handler; |
732 | + return handler.save(m_preset, getName(), fileName); |
733 | +} |
734 | + |
735 | +bool BulkController::matchPreset(const PresetInfo& preset) { |
736 | + const QList< QHash<QString,QString> > products = preset.getProducts(); |
737 | + QHash <QString, QString> product; |
738 | + foreach (product, products) { |
739 | + if (matchProductInfo(product)) |
740 | + return true; |
741 | + } |
742 | + return false; |
743 | +} |
744 | + |
745 | +bool BulkController::matchProductInfo(QHash <QString,QString > info) { |
746 | + int value; |
747 | + bool ok; |
748 | + // Product and vendor match is always required |
749 | + value = info["vendor_id"].toInt(&ok,16); |
750 | + if (!ok || vendor_id!=value) return false; |
751 | + value = info["product_id"].toInt(&ok,16); |
752 | + if (!ok || product_id!=value) return false; |
753 | + |
754 | + // Match found |
755 | + return true; |
756 | +} |
757 | + |
758 | +int BulkController::open() { |
759 | + int i; |
760 | + |
761 | + if (isOpen()) { |
762 | + qDebug() << "USB Bulk device" << getName() << "already open"; |
763 | + return -1; |
764 | + } |
765 | + |
766 | + /* Look up endpoint addresses in supported database */ |
767 | + for (i = 0; bulk_supported[i].vendor_id; i += 1) { |
768 | + if ((bulk_supported[i].vendor_id == vendor_id) && |
769 | + (bulk_supported[i].product_id == product_id)) { |
770 | + in_epaddr = bulk_supported[i].in_epaddr; |
771 | + out_epaddr = bulk_supported[i].out_epaddr; |
772 | + break; |
773 | + } |
774 | + } |
775 | + |
776 | + if (bulk_supported[i].vendor_id == 0) { |
777 | + qWarning() << "USB Bulk device" << getName() << "unsupported"; |
778 | + return -1; |
779 | + } |
780 | + m_phandle = NULL; |
781 | + |
782 | + // XXX: we should enumerate devices and match vendor, product, and serial |
783 | + if (m_phandle == NULL) { |
784 | + m_phandle = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id); |
785 | + } |
786 | + |
787 | + if (m_phandle == NULL) { |
788 | + qWarning() << "Unable to open USB Bulk device" << getName(); |
789 | + return -1; |
790 | + } |
791 | + |
792 | + setOpen(true); |
793 | + startEngine(); |
794 | + |
795 | + if (m_pReader != NULL) { |
796 | + qWarning() << "BulkReader already present for" << getName(); |
797 | + } else { |
798 | + m_pReader = new BulkReader(m_phandle, in_epaddr); |
799 | + m_pReader->setObjectName(QString("BulkReader %1").arg(getName())); |
800 | + |
801 | + connect(m_pReader, SIGNAL(incomingData(QByteArray)), |
802 | + this, SLOT(receive(QByteArray))); |
803 | + |
804 | + // Controller input needs to be prioritized since it can affect the |
805 | + // audio directly, like when scratching |
806 | + m_pReader->start(QThread::HighPriority); |
807 | + } |
808 | + |
809 | + return 0; |
810 | +} |
811 | + |
812 | +int BulkController::close() { |
813 | + if (!isOpen()) { |
814 | + qDebug() << " device" << getName() << "already closed"; |
815 | + return -1; |
816 | + } |
817 | + |
818 | + qDebug() << "Shutting down USB Bulk device" << getName(); |
819 | + |
820 | + // Stop the reading thread |
821 | + if (m_pReader == NULL) { |
822 | + qWarning() << "BulkReader not present for" << getName() |
823 | + << "yet the device is open!"; |
824 | + } else { |
825 | + disconnect(m_pReader, SIGNAL(incomingData(QByteArray)), |
826 | + this, SLOT(receive(QByteArray))); |
827 | + m_pReader->stop(); |
828 | + if (debugging()) qDebug() << " Waiting on reader to finish"; |
829 | + m_pReader->wait(); |
830 | + delete m_pReader; |
831 | + m_pReader = NULL; |
832 | + } |
833 | + |
834 | + // Stop controller engine here to ensure it's done before the device is closed |
835 | + // incase it has any final parting messages |
836 | + stopEngine(); |
837 | + |
838 | + // Close device |
839 | + if (debugging()) { |
840 | + qDebug() << " Closing device"; |
841 | + } |
842 | + libusb_close(m_phandle); |
843 | + setOpen(false); |
844 | + return 0; |
845 | +} |
846 | + |
847 | +void BulkController::send(QList<int> data, unsigned int length) { |
848 | + Q_UNUSED(length); |
849 | + QByteArray temp; |
850 | + |
851 | + foreach (int datum, data) { |
852 | + temp.append(datum); |
853 | + } |
854 | + send(temp); |
855 | +} |
856 | + |
857 | +void BulkController::send(QByteArray data) { |
858 | + int ret; |
859 | + int transferred; |
860 | + |
861 | + // XXX: don't get drunk again. |
862 | + ret = libusb_bulk_transfer(m_phandle, out_epaddr, |
863 | + (unsigned char *)data.constData(), data.size(), |
864 | + &transferred, 0); |
865 | + if (ret < 0) { |
866 | + if (debugging()) { |
867 | + qWarning() << "Unable to send data to" << getName() |
868 | + << "serial #" << m_sUID << ":" |
869 | + << QString::fromAscii(libusb_error_name(ret)); |
870 | + } else { |
871 | + qWarning() << "Unable to send data to" << getName() << ":" |
872 | + << QString::fromAscii(libusb_error_name(ret)); |
873 | + } |
874 | + } else if (debugging()) { |
875 | + qDebug() << ret << "bytes sent to" << getName() |
876 | + << "serial #" << m_sUID; |
877 | + } |
878 | +} |
879 | |
880 | === added file 'mixxx/src/controllers/bulk/bulkcontroller.h' |
881 | --- mixxx/src/controllers/bulk/bulkcontroller.h 1970-01-01 00:00:00 +0000 |
882 | +++ mixxx/src/controllers/bulk/bulkcontroller.h 2012-08-25 14:32:20 +0000 |
883 | @@ -0,0 +1,105 @@ |
884 | +/** |
885 | + * @file bulkcontroller.h |
886 | + * @author Neale Picket neale@woozle.org |
887 | + * @date Thu Jun 28 2012 |
888 | + * @brief USB Bulk controller backend |
889 | + */ |
890 | + |
891 | +#ifndef BULKCONTROLLER_H |
892 | +#define BULKCONTROLLER_H |
893 | + |
894 | +#include <libusb.h> |
895 | + |
896 | +#include <QAtomicInt> |
897 | + |
898 | +#include "controllers/controller.h" |
899 | +#include "controllers/hid/hidcontrollerpreset.h" |
900 | +#include "controllers/hid/hidcontrollerpresetfilehandler.h" |
901 | + |
902 | +class BulkReader : public QThread { |
903 | + Q_OBJECT |
904 | + public: |
905 | + BulkReader(libusb_device_handle *handle, unsigned char in_epaddr); |
906 | + virtual ~BulkReader(); |
907 | + |
908 | + void stop(); |
909 | + |
910 | + signals: |
911 | + void incomingData(QByteArray data); |
912 | + |
913 | + protected: |
914 | + void run(); |
915 | + |
916 | + private: |
917 | + libusb_device_handle *m_phandle; |
918 | + QAtomicInt m_stop; |
919 | + unsigned char m_in_epaddr; |
920 | +}; |
921 | + |
922 | +class BulkController : public Controller { |
923 | + Q_OBJECT |
924 | + public: |
925 | + BulkController(libusb_device_handle *handle, struct libusb_device_descriptor *desc); |
926 | + virtual ~BulkController(); |
927 | + |
928 | + virtual ControllerPresetPointer getPreset() const { |
929 | + HidControllerPreset* pClone = new HidControllerPreset(); |
930 | + *pClone = m_preset; |
931 | + return ControllerPresetPointer(pClone); |
932 | + } |
933 | + |
934 | + virtual bool savePreset(const QString fileName) const; |
935 | + |
936 | + virtual ControllerPresetFileHandler* getFileHandler() const { |
937 | + return new HidControllerPresetFileHandler(); |
938 | + } |
939 | + |
940 | + virtual void visit(const MidiControllerPreset* preset); |
941 | + virtual void visit(const HidControllerPreset* preset); |
942 | + |
943 | + virtual bool isMappable() const { |
944 | + return m_preset.isMappable(); |
945 | + } |
946 | + |
947 | + virtual bool matchPreset(const PresetInfo& preset); |
948 | + virtual bool matchProductInfo(QHash <QString,QString >); |
949 | + |
950 | + protected: |
951 | + Q_INVOKABLE void send(QList<int> data, unsigned int length); |
952 | + |
953 | + private slots: |
954 | + int open(); |
955 | + int close(); |
956 | + |
957 | + private: |
958 | + // For devices which only support a single report, reportID must be set to |
959 | + // 0x0. |
960 | + virtual void send(QByteArray data); |
961 | + |
962 | + virtual bool isPolling() const { |
963 | + return false; |
964 | + } |
965 | + |
966 | + // Returns a pointer to the currently loaded controller preset. For internal |
967 | + // use only. |
968 | + virtual ControllerPreset* preset() { |
969 | + return &m_preset; |
970 | + } |
971 | + |
972 | + libusb_device_handle *m_phandle; |
973 | + |
974 | + // Local copies of things we need from desc |
975 | + |
976 | + unsigned short vendor_id; |
977 | + unsigned short product_id; |
978 | + unsigned char in_epaddr; |
979 | + unsigned char out_epaddr; |
980 | + QString manufacturer; |
981 | + QString product; |
982 | + |
983 | + QString m_sUID; |
984 | + BulkReader* m_pReader; |
985 | + HidControllerPreset m_preset; |
986 | +}; |
987 | + |
988 | +#endif |
989 | |
990 | === added file 'mixxx/src/controllers/bulk/bulkenumerator.cpp' |
991 | --- mixxx/src/controllers/bulk/bulkenumerator.cpp 1970-01-01 00:00:00 +0000 |
992 | +++ mixxx/src/controllers/bulk/bulkenumerator.cpp 2012-08-25 14:32:20 +0000 |
993 | @@ -0,0 +1,71 @@ |
994 | +/** |
995 | + * @file bulkenumerator.cpp |
996 | + * @author Neale Picket neale@woozle.org |
997 | + * @date Thu Jun 28 2012 |
998 | + * @brief USB Bulk controller backend |
999 | + */ |
1000 | + |
1001 | +#include <libusb.h> |
1002 | + |
1003 | +#include "controllers/bulk/bulkcontroller.h" |
1004 | +#include "controllers/bulk/bulkenumerator.h" |
1005 | +#include "controllers/bulk/bulksupported.h" |
1006 | + |
1007 | +BulkEnumerator::BulkEnumerator() : ControllerEnumerator() { |
1008 | + libusb_init(NULL); |
1009 | +} |
1010 | + |
1011 | +BulkEnumerator::~BulkEnumerator() { |
1012 | + qDebug() << "Deleting USB Bulk devices..."; |
1013 | + while (m_devices.size() > 0) { |
1014 | + delete m_devices.takeLast(); |
1015 | + } |
1016 | + libusb_exit(NULL); |
1017 | +} |
1018 | + |
1019 | +static int |
1020 | +is_interesting(struct libusb_device_descriptor *desc) |
1021 | +{ |
1022 | + int i; |
1023 | + |
1024 | + for (i = 0; bulk_supported[i].vendor_id; i += 1) { |
1025 | + if ((bulk_supported[i].vendor_id == desc->idVendor) && |
1026 | + (bulk_supported[i].product_id == desc->idProduct)) { |
1027 | + return 1; |
1028 | + } |
1029 | + } |
1030 | + |
1031 | + return 0; |
1032 | +} |
1033 | + |
1034 | +QList<Controller*> BulkEnumerator::queryDevices() { |
1035 | + qDebug() << "Scanning USB Bulk devices:"; |
1036 | + |
1037 | + libusb_device **list; |
1038 | + ssize_t cnt = libusb_get_device_list(NULL, &list); |
1039 | + ssize_t i = 0; |
1040 | + int err = 0; |
1041 | + |
1042 | + for (i = 0; i < cnt; i++) { |
1043 | + libusb_device *device = list[i]; |
1044 | + struct libusb_device_descriptor desc; |
1045 | + |
1046 | + libusb_get_device_descriptor(device, &desc); |
1047 | + if (is_interesting(&desc)) { |
1048 | + struct libusb_device_handle *handle; |
1049 | + |
1050 | + err = libusb_open(device, &handle); |
1051 | + if (err) { |
1052 | + qDebug() << "Error opening a device"; |
1053 | + continue; |
1054 | + } |
1055 | + |
1056 | + BulkController* currentDevice = new BulkController(handle, &desc); |
1057 | + m_devices.push_back(currentDevice); |
1058 | + } |
1059 | + } |
1060 | + |
1061 | + libusb_free_device_list(list, 1); |
1062 | + |
1063 | + return m_devices; |
1064 | +} |
1065 | |
1066 | === added file 'mixxx/src/controllers/bulk/bulkenumerator.h' |
1067 | --- mixxx/src/controllers/bulk/bulkenumerator.h 1970-01-01 00:00:00 +0000 |
1068 | +++ mixxx/src/controllers/bulk/bulkenumerator.h 2012-08-25 14:32:20 +0000 |
1069 | @@ -0,0 +1,24 @@ |
1070 | +/** |
1071 | +* @file bulkenumerator.h |
1072 | +* @author Neale Pickett neale@woozle.org |
1073 | +* @date Thu Jun 28 2012 |
1074 | +* @brief Locate supported USB bulk controllers |
1075 | +*/ |
1076 | + |
1077 | +#ifndef BULKENUMERATOR_H |
1078 | +#define BULKENUMERATOR_H |
1079 | + |
1080 | +#include "controllers/controllerenumerator.h" |
1081 | + |
1082 | +class BulkEnumerator : public ControllerEnumerator { |
1083 | + public: |
1084 | + BulkEnumerator(); |
1085 | + virtual ~BulkEnumerator(); |
1086 | + |
1087 | + QList<Controller*> queryDevices(); |
1088 | + |
1089 | + private: |
1090 | + QList<Controller*> m_devices; |
1091 | +}; |
1092 | + |
1093 | +#endif |
1094 | |
1095 | === added file 'mixxx/src/controllers/bulk/bulksupported.h' |
1096 | --- mixxx/src/controllers/bulk/bulksupported.h 1970-01-01 00:00:00 +0000 |
1097 | +++ mixxx/src/controllers/bulk/bulksupported.h 2012-08-25 14:32:20 +0000 |
1098 | @@ -0,0 +1,24 @@ |
1099 | +/** |
1100 | +* @file bulksupported.h |
1101 | +* @author Neale Picket neale@woozle.org |
1102 | +* @date Thu Jun 28 2012 |
1103 | +* @brief A list of supported USB bulk devices |
1104 | +*/ |
1105 | + |
1106 | +#ifndef BULKSUPPORTED_H |
1107 | +#define BULKSUPPORTED_H |
1108 | + |
1109 | +typedef struct bulk_supported { |
1110 | + unsigned short vendor_id; |
1111 | + unsigned short product_id; |
1112 | + unsigned char in_epaddr; |
1113 | + unsigned char out_epaddr; |
1114 | +} bulk_supported_t; |
1115 | + |
1116 | +static bulk_supported_t bulk_supported[] = { |
1117 | + {0x06f8, 0xb105, 0x82, 0x03}, // Hercules MP3e2 |
1118 | + {0x06f8, 0xb100, 0x86, 0x06}, // Hercules Mk2 |
1119 | + {0, 0, 0, 0} |
1120 | +}; |
1121 | + |
1122 | +#endif |
1123 | |
1124 | === modified file 'mixxx/src/controllers/controllermanager.cpp' |
1125 | --- mixxx/src/controllers/controllermanager.cpp 2012-07-08 23:27:47 +0000 |
1126 | +++ mixxx/src/controllers/controllermanager.cpp 2012-08-25 14:32:20 +0000 |
1127 | @@ -21,6 +21,10 @@ |
1128 | #include "controllers/hid/hidenumerator.h" |
1129 | #endif |
1130 | |
1131 | +#ifdef __BULK__ |
1132 | +# include "controllers/bulk/bulkenumerator.h" |
1133 | +#endif |
1134 | + |
1135 | // http://developer.qt.nokia.com/wiki/Threads_Events_QObjects |
1136 | |
1137 | // Poll every 1ms (where possible) for good controller response |
1138 | @@ -77,6 +81,9 @@ |
1139 | #ifdef __HSS1394__ |
1140 | m_enumerators.append(new Hss1394Enumerator()); |
1141 | #endif |
1142 | +#ifdef __BULK__ |
1143 | + m_enumerators.append(new BulkEnumerator()); |
1144 | +#endif |
1145 | #ifdef __HID__ |
1146 | m_enumerators.append(new HidEnumerator()); |
1147 | #endif |