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