Merge lp:~mixxxdevelopers/mixxx/features_portmidi into lp:~mixxxdevelopers/mixxx/trunk
- features_portmidi
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mixxxdevelopers/mixxx/features_portmidi |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Prerequisite: | lp:mixxx/1.7 |
Diff against target: | 8460 lines |
To merge this branch: | bzr merge lp:~mixxxdevelopers/mixxx/features_portmidi |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mixxx Development Team | Pending | ||
Review via email: mp+14577@code.launchpad.net |
This proposal has been superseded by a proposal from 2009-11-11.
Commit message
Description of the change
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
- 2519. By Sean M. Pappalardo
-
Removed unneeded include
RJ Skerry-Ryan (rryan) wrote : | # |
Pegasus wrote:
> Pegasus has proposed merging lp:~mixxxdevelopers/mixxx/features_portmidi into lp:mixxx with lp:mixxx/1.7 as a prerequisite.
>
> Requested reviews:
> Mixxx Development Team (mixxxdevelopers)
>
>
> Mutliple MIDI device support has been working for a few weeks now with limited testing by myself and Phillip. The essential user-facing GUI changes are now complete, so this is ready to be considered for merging to trunk in time for v1.8.
>
I think you didn't quite finish merging this or something because there
are some merge conflict markers scattered across this diff.
Sean M. Pappalardo (pegasus-renegadetech) wrote : | # |
I was wondering about those. But they're not in the PM branch or 1.7. I guess they're a result of my specifying that merging the 1.7 branch to trunk is a prerequisite for merging this one?? (I guess that's not strictly true since this branch is based on 1.7 and the single merge would net both branches to trunk, effectively, no?)
Phillip Whelan (pwhelan) wrote : | # |
You should probably rebase off trunk since no new features are being
merged into 1.7.
RJ Skerry-Ryan (rryan) wrote : | # |
pwhelan wrote:
> You should probably rebase off trunk since no new features are being
> merged into 1.7.
>
Yea I agree -- you should rebase this branch to trunk so that the diff
we are looking at is exactly what will be added to trunk if the merge is
approved.
- 2520. By RJ Skerry-Ryan
-
Rebase merge of features_portmidi from the 1.7.x series to trunk.
- 2521. By Sean M. Pappalardo
-
Fixed a couple trunk merge issues
- 2522. By RJ Skerry-Ryan
-
Since the MidiMapping lock is now recursive, make buildDomElement() just relock it instead of asserting it is held.
- 2523. By Albert Santoni
-
* Select the autopaired output device in the MIDI bindings dialog's combobox, but grey it out.
* Removed some obsolete "correspondingOutputDevice" functions. We deprecated that in favour of the aggregating MidiDevice approach.
* Cleaned up some other obsolete stuff. - 2524. By Albert Santoni
-
Only look for porttime header because it's symbols live inside the portmidi shared lib
- 2525. By Albert Santoni
-
Force user to apply settings before going into MIDI learn wizard.
- 2526. By Sean M. Pappalardo
-
- Removed unused PrimaryMidiDevice class variable
- White space fix - 2527. By Albert Santoni
-
Fixed RJ's suggestions from merge review. One remaining race condition to solve...
- 2528. By Albert Santoni
-
* Add MIDI receive inhibit flag to MidiDevice. Should help us prevent a race condition...
* Removed old MIDI dialog - 2529. By Albert Santoni
-
Fixed more deadlocks
- 2530. By Sean M. Pappalardo
-
Removed PM CPPDEFINE since its not used and messes up the SNDFILE one somehow
- 2531. By Sean M. Pappalardo
-
Merge with trunk
- 2532. By Sean M. Pappalardo
-
- Added preliminary mapping for SCS.3m (in default automatic mode...no direct feedback right now.)
- Added prelminiary single-deck mode for the SCS.3d (to avoid easy deck changes) - 2533. By Sean M. Pappalardo
-
PortTime is needed on Windows and the Linux check works fine for me.
- 2534. By RJ Skerry-Ryan
-
Merging from trunk.
- 2535. By RJ Skerry-Ryan
-
Unhacking the SConscript
Unmerged revisions
Preview Diff
1 | === added file 'mixxx-win64lib/portmidi.h' |
2 | --- mixxx-win64lib/portmidi.h 1970-01-01 00:00:00 +0000 |
3 | +++ mixxx-win64lib/portmidi.h 2009-11-10 00:33:17 +0000 |
4 | @@ -0,0 +1,604 @@ |
5 | +#ifndef PORT_MIDI_H |
6 | +#define PORT_MIDI_H |
7 | +#ifdef __cplusplus |
8 | +extern "C" { |
9 | +#endif /* __cplusplus */ |
10 | + |
11 | +/* |
12 | + * PortMidi Portable Real-Time MIDI Library |
13 | + * PortMidi API Header File |
14 | + * Latest version available at: http://sourceforge.net/projects/portmedia |
15 | + * |
16 | + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk |
17 | + * Copyright (c) 2001-2006 Roger B. Dannenberg |
18 | + * |
19 | + * Permission is hereby granted, free of charge, to any person obtaining |
20 | + * a copy of this software and associated documentation files |
21 | + * (the "Software"), to deal in the Software without restriction, |
22 | + * including without limitation the rights to use, copy, modify, merge, |
23 | + * publish, distribute, sublicense, and/or sell copies of the Software, |
24 | + * and to permit persons to whom the Software is furnished to do so, |
25 | + * subject to the following conditions: |
26 | + * |
27 | + * The above copyright notice and this permission notice shall be |
28 | + * included in all copies or substantial portions of the Software. |
29 | + * |
30 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
31 | + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
32 | + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
33 | + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR |
34 | + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
35 | + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
36 | + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
37 | + */ |
38 | + |
39 | +/* |
40 | + * The text above constitutes the entire PortMidi license; however, |
41 | + * the PortMusic community also makes the following non-binding requests: |
42 | + * |
43 | + * Any person wishing to distribute modifications to the Software is |
44 | + * requested to send the modifications to the original developer so that |
45 | + * they can be incorporated into the canonical version. It is also |
46 | + * requested that these non-binding requests be included along with the |
47 | + * license above. |
48 | + */ |
49 | + |
50 | +/* CHANGELOG FOR PORTMIDI |
51 | + * (see ../CHANGELOG.txt) |
52 | + * |
53 | + * NOTES ON HOST ERROR REPORTING: |
54 | + * |
55 | + * PortMidi errors (of type PmError) are generic, system-independent errors. |
56 | + * When an error does not map to one of the more specific PmErrors, the |
57 | + * catch-all code pmHostError is returned. This means that PortMidi has |
58 | + * retained a more specific system-dependent error code. The caller can |
59 | + * get more information by calling Pm_HasHostError() to test if there is |
60 | + * a pending host error, and Pm_GetHostErrorText() to get a text string |
61 | + * describing the error. Host errors are reported on a per-device basis |
62 | + * because only after you open a device does PortMidi have a place to |
63 | + * record the host error code. I.e. only |
64 | + * those routines that receive a (PortMidiStream *) argument check and |
65 | + * report errors. One exception to this is that Pm_OpenInput() and |
66 | + * Pm_OpenOutput() can report errors even though when an error occurs, |
67 | + * there is no PortMidiStream* to hold the error. Fortunately, both |
68 | + * of these functions return any error immediately, so we do not really |
69 | + * need per-device error memory. Instead, any host error code is stored |
70 | + * in a global, pmHostError is returned, and the user can call |
71 | + * Pm_GetHostErrorText() to get the error message (and the invalid stream |
72 | + * parameter will be ignored.) The functions |
73 | + * pm_init and pm_term do not fail or raise |
74 | + * errors. The job of pm_init is to locate all available devices so that |
75 | + * the caller can get information via PmDeviceInfo(). If an error occurs, |
76 | + * the device is simply not listed as available. |
77 | + * |
78 | + * Host errors come in two flavors: |
79 | + * a) host error |
80 | + * b) host error during callback |
81 | + * These can occur w/midi input or output devices. (b) can only happen |
82 | + * asynchronously (during callback routines), whereas (a) only occurs while |
83 | + * synchronously running PortMidi and any resulting system dependent calls. |
84 | + * Both (a) and (b) are reported by the next read or write call. You can |
85 | + * also query for asynchronous errors (b) at any time by calling |
86 | + * Pm_HasHostError(). |
87 | + * |
88 | + * NOTES ON COMPILE-TIME SWITCHES |
89 | + * |
90 | + * DEBUG assumes stdio and a console. Use this if you want automatic, simple |
91 | + * error reporting, e.g. for prototyping. If you are using MFC or some |
92 | + * other graphical interface with no console, DEBUG probably should be |
93 | + * undefined. |
94 | + * PM_CHECK_ERRORS more-or-less takes over error checking for return values, |
95 | + * stopping your program and printing error messages when an error |
96 | + * occurs. This also uses stdio for console text I/O. |
97 | + */ |
98 | + |
99 | +#ifndef FALSE |
100 | + #define FALSE 0 |
101 | +#endif |
102 | +#ifndef TRUE |
103 | + #define TRUE 1 |
104 | +#endif |
105 | + |
106 | +/* default size of buffers for sysex transmission: */ |
107 | +#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 |
108 | + |
109 | +/** List of portmidi errors.*/ |
110 | +typedef enum { |
111 | + pmNoError = 0, |
112 | + pmNoData = 0, /**< A "no error" return that also indicates no data avail. */ |
113 | + pmGotData = 1, /**< A "no error" return that also indicates data available */ |
114 | + pmHostError = -10000, |
115 | + pmInvalidDeviceId, /** out of range or |
116 | + * output device when input is requested or |
117 | + * input device when output is requested or |
118 | + * device is already opened |
119 | + */ |
120 | + pmInsufficientMemory, |
121 | + pmBufferTooSmall, |
122 | + pmBufferOverflow, |
123 | + pmBadPtr, /* PortMidiStream parameter is NULL or |
124 | + * stream is not opened or |
125 | + * stream is output when input is required or |
126 | + * stream is input when output is required */ |
127 | + pmBadData, /** illegal midi data, e.g. missing EOX */ |
128 | + pmInternalError, |
129 | + pmBufferMaxSize /** buffer is already as large as it can be */ |
130 | + /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */ |
131 | +} PmError; |
132 | + |
133 | +/** |
134 | + Pm_Initialize() is the library initialisation function - call this before |
135 | + using the library. |
136 | +*/ |
137 | +PmError Pm_Initialize( void ); |
138 | + |
139 | +/** |
140 | + Pm_Terminate() is the library termination function - call this after |
141 | + using the library. |
142 | +*/ |
143 | +PmError Pm_Terminate( void ); |
144 | + |
145 | +/** A single PortMidiStream is a descriptor for an open MIDI device. |
146 | +*/ |
147 | +typedef void PortMidiStream; |
148 | +#define PmStream PortMidiStream |
149 | + |
150 | +/** |
151 | + Test whether stream has a pending host error. Normally, the client finds |
152 | + out about errors through returned error codes, but some errors can occur |
153 | + asynchronously where the client does not |
154 | + explicitly call a function, and therefore cannot receive an error code. |
155 | + The client can test for a pending error using Pm_HasHostError(). If true, |
156 | + the error can be accessed and cleared by calling Pm_GetErrorText(). |
157 | + Errors are also cleared by calling other functions that can return |
158 | + errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The |
159 | + client does not need to call Pm_HasHostError(). Any pending error will be |
160 | + reported the next time the client performs an explicit function call on |
161 | + the stream, e.g. an input or output operation. Until the error is cleared, |
162 | + no new error codes will be obtained, even for a different stream. |
163 | +*/ |
164 | +int Pm_HasHostError( PortMidiStream * stream ); |
165 | + |
166 | + |
167 | +/** Translate portmidi error number into human readable message. |
168 | + These strings are constants (set at compile time) so client has |
169 | + no need to allocate storage |
170 | +*/ |
171 | +const char *Pm_GetErrorText( PmError errnum ); |
172 | + |
173 | +/** Translate portmidi host error into human readable message. |
174 | + These strings are computed at run time, so client has to allocate storage. |
175 | + After this routine executes, the host error is cleared. |
176 | +*/ |
177 | +void Pm_GetHostErrorText(char * msg, unsigned int len); |
178 | + |
179 | +#define HDRLENGTH 50 |
180 | +#define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less |
181 | + than this number of characters */ |
182 | + |
183 | +/** |
184 | + Device enumeration mechanism. |
185 | + |
186 | + Device ids range from 0 to Pm_CountDevices()-1. |
187 | + |
188 | +*/ |
189 | +typedef int PmDeviceID; |
190 | +#define pmNoDevice -1 |
191 | +typedef struct { |
192 | + int structVersion; /**< this internal structure version */ |
193 | + const char *interf; /**< underlying MIDI API, e.g. MMSystem or DirectX */ |
194 | + const char *name; /**< device name, e.g. USB MidiSport 1x1 */ |
195 | + int input; /**< true iff input is available */ |
196 | + int output; /**< true iff output is available */ |
197 | + int opened; /**< used by generic PortMidi code to do error checking on arguments */ |
198 | + |
199 | +} PmDeviceInfo; |
200 | + |
201 | +/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */ |
202 | +int Pm_CountDevices( void ); |
203 | +/** |
204 | + Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() |
205 | + |
206 | + Return the default device ID or pmNoDevice if there are no devices. |
207 | + The result (but not pmNoDevice) can be passed to Pm_OpenMidi(). |
208 | + |
209 | + The default device can be specified using a small application |
210 | + named pmdefaults that is part of the PortMidi distribution. This |
211 | + program in turn uses the Java Preferences object created by |
212 | + java.util.prefs.Preferences.userRoot().node("/PortMidi"); the |
213 | + preference is set by calling |
214 | + prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName); |
215 | + or prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName); |
216 | + |
217 | + In the statements above, prefName is a string describing the |
218 | + MIDI device in the form "interf, name" where interf identifies |
219 | + the underlying software system or API used by PortMdi to access |
220 | + devices and name is the name of the device. These correspond to |
221 | + the interf and name fields of a PmDeviceInfo. (Currently supported |
222 | + interfaces are "MMSystem" for Win32, "ALSA" for Linux, and |
223 | + "CoreMIDI" for OS X, so in fact, there is no choice of interface.) |
224 | + In "interf, name", the strings are actually substrings of |
225 | + the full interface and name strings. For example, the preference |
226 | + "Core, Sport" will match a device with interface "CoreMIDI" |
227 | + and name "In USB MidiSport 1x1". It will also match "CoreMIDI" |
228 | + and "In USB MidiSport 2x2". The devices are enumerated in device |
229 | + ID order, so the lowest device ID that matches the pattern becomes |
230 | + the default device. Finally, if the comma-space (", ") separator |
231 | + between interface and name parts of the preference is not found, |
232 | + the entire preference string is interpreted as a name, and the |
233 | + interface part is the empty string, which matches anything. |
234 | + |
235 | + On the MAC, preferences are stored in |
236 | + /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist |
237 | + which is a binary file. In addition to the pmdefaults program, |
238 | + there are utilities that can read and edit this preference file. |
239 | + |
240 | + On the PC, |
241 | + |
242 | + On Linux, |
243 | + |
244 | +*/ |
245 | +PmDeviceID Pm_GetDefaultInputDeviceID( void ); |
246 | +/** see PmDeviceID Pm_GetDefaultInputDeviceID() */ |
247 | +PmDeviceID Pm_GetDefaultOutputDeviceID( void ); |
248 | + |
249 | +/** |
250 | + PmTimestamp is used to represent a millisecond clock with arbitrary |
251 | + start time. The type is used for all MIDI timestampes and clocks. |
252 | +*/ |
253 | +typedef long PmTimestamp; |
254 | +typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); |
255 | + |
256 | +/** TRUE if t1 before t2 */ |
257 | +#define PmBefore(t1,t2) ((t1-t2) < 0) |
258 | +/** |
259 | + \defgroup grp_device Input/Output Devices Handling |
260 | + @{ |
261 | +*/ |
262 | +/** |
263 | + Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure |
264 | + referring to the device specified by id. |
265 | + If id is out of range the function returns NULL. |
266 | + |
267 | + The returned structure is owned by the PortMidi implementation and must |
268 | + not be manipulated or freed. The pointer is guaranteed to be valid |
269 | + between calls to Pm_Initialize() and Pm_Terminate(). |
270 | +*/ |
271 | +const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); |
272 | + |
273 | +/** |
274 | + Pm_OpenInput() and Pm_OpenOutput() open devices. |
275 | + |
276 | + stream is the address of a PortMidiStream pointer which will receive |
277 | + a pointer to the newly opened stream. |
278 | + |
279 | + inputDevice is the id of the device used for input (see PmDeviceID above). |
280 | + |
281 | + inputDriverInfo is a pointer to an optional driver specific data structure |
282 | + containing additional information for device setup or handle processing. |
283 | + inputDriverInfo is never required for correct operation. If not used |
284 | + inputDriverInfo should be NULL. |
285 | + |
286 | + outputDevice is the id of the device used for output (see PmDeviceID above.) |
287 | + |
288 | + outputDriverInfo is a pointer to an optional driver specific data structure |
289 | + containing additional information for device setup or handle processing. |
290 | + outputDriverInfo is never required for correct operation. If not used |
291 | + outputDriverInfo should be NULL. |
292 | + |
293 | + For input, the buffersize specifies the number of input events to be |
294 | + buffered waiting to be read using Pm_Read(). For output, buffersize |
295 | + specifies the number of output events to be buffered waiting for output. |
296 | + (In some cases -- see below -- PortMidi does not buffer output at all |
297 | + and merely passes data to a lower-level API, in which case buffersize |
298 | + is ignored.) |
299 | + |
300 | + latency is the delay in milliseconds applied to timestamps to determine |
301 | + when the output should actually occur. (If latency is < 0, 0 is assumed.) |
302 | + If latency is zero, timestamps are ignored and all output is delivered |
303 | + immediately. If latency is greater than zero, output is delayed until the |
304 | + message timestamp plus the latency. (NOTE: the time is measured relative |
305 | + to the time source indicated by time_proc. Timestamps are absolute, |
306 | + not relative delays or offsets.) In some cases, PortMidi can obtain |
307 | + better timing than your application by passing timestamps along to the |
308 | + device driver or hardware. Latency may also help you to synchronize midi |
309 | + data to audio data by matching midi latency to the audio buffer latency. |
310 | + |
311 | + time_proc is a pointer to a procedure that returns time in milliseconds. It |
312 | + may be NULL, in which case a default millisecond timebase (PortTime) is |
313 | + used. If the application wants to use PortTime, it should start the timer |
314 | + (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the |
315 | + application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput, |
316 | + it may get a ptAlreadyStarted error from Pt_Start, and the application's |
317 | + preferred time resolution and callback function will be ignored. |
318 | + time_proc result values are appended to incoming MIDI data, and time_proc |
319 | + times are used to schedule outgoing MIDI data (when latency is non-zero). |
320 | + |
321 | + time_info is a pointer passed to time_proc. |
322 | + |
323 | + Example: If I provide a timestamp of 5000, latency is 1, and time_proc |
324 | + returns 4990, then the desired output time will be when time_proc returns |
325 | + timestamp+latency = 5001. This will be 5001-4990 = 11ms from now. |
326 | + |
327 | + return value: |
328 | + Upon success Pm_Open() returns PmNoError and places a pointer to a |
329 | + valid PortMidiStream in the stream argument. |
330 | + If a call to Pm_Open() fails a nonzero error code is returned (see |
331 | + PMError above) and the value of port is invalid. |
332 | + |
333 | + Any stream that is successfully opened should eventually be closed |
334 | + by calling Pm_Close(). |
335 | + |
336 | +*/ |
337 | +PmError Pm_OpenInput( PortMidiStream** stream, |
338 | + PmDeviceID inputDevice, |
339 | + void *inputDriverInfo, |
340 | + long bufferSize, |
341 | + PmTimeProcPtr time_proc, |
342 | + void *time_info ); |
343 | + |
344 | +PmError Pm_OpenOutput( PortMidiStream** stream, |
345 | + PmDeviceID outputDevice, |
346 | + void *outputDriverInfo, |
347 | + long bufferSize, |
348 | + PmTimeProcPtr time_proc, |
349 | + void *time_info, |
350 | + long latency ); |
351 | + /** @} */ |
352 | + |
353 | +/** |
354 | + \defgroup grp_events_filters Events and Filters Handling |
355 | + @{ |
356 | +*/ |
357 | + |
358 | +/* \function PmError Pm_SetFilter( PortMidiStream* stream, long filters ) |
359 | + Pm_SetFilter() sets filters on an open input stream to drop selected |
360 | + input types. By default, only active sensing messages are filtered. |
361 | + To prohibit, say, active sensing and sysex messages, call |
362 | + Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); |
363 | + |
364 | + Filtering is useful when midi routing or midi thru functionality is being |
365 | + provided by the user application. |
366 | + For example, you may want to exclude timing messages (clock, MTC, start/stop/continue), |
367 | + while allowing note-related messages to pass. |
368 | + Or you may be using a sequencer or drum-machine for MIDI clock information but want to |
369 | + exclude any notes it may play. |
370 | + */ |
371 | + |
372 | +/* Filter bit-mask definitions */ |
373 | +/** filter active sensing messages (0xFE): */ |
374 | +#define PM_FILT_ACTIVE (1 << 0x0E) |
375 | +/** filter system exclusive messages (0xF0): */ |
376 | +#define PM_FILT_SYSEX (1 << 0x00) |
377 | +/** filter MIDI clock message (0xF8) */ |
378 | +#define PM_FILT_CLOCK (1 << 0x08) |
379 | +/** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ |
380 | +#define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) |
381 | +/** filter tick messages (0xF9) */ |
382 | +#define PM_FILT_TICK (1 << 0x09) |
383 | +/** filter undefined FD messages */ |
384 | +#define PM_FILT_FD (1 << 0x0D) |
385 | +/** filter undefined real-time messages */ |
386 | +#define PM_FILT_UNDEFINED PM_FILT_FD |
387 | +/** filter reset messages (0xFF) */ |
388 | +#define PM_FILT_RESET (1 << 0x0F) |
389 | +/** filter all real-time messages */ |
390 | +#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ |
391 | + PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) |
392 | +/** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ |
393 | +#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) |
394 | +/** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ |
395 | +#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) |
396 | +/** per-note aftertouch (0xA0-0xAF) */ |
397 | +#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) |
398 | +/** filter both channel and poly aftertouch */ |
399 | +#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) |
400 | +/** Program changes (0xC0-0xCF) */ |
401 | +#define PM_FILT_PROGRAM (1 << 0x1C) |
402 | +/** Control Changes (CC's) (0xB0-0xBF)*/ |
403 | +#define PM_FILT_CONTROL (1 << 0x1B) |
404 | +/** Pitch Bender (0xE0-0xEF*/ |
405 | +#define PM_FILT_PITCHBEND (1 << 0x1E) |
406 | +/** MIDI Time Code (0xF1)*/ |
407 | +#define PM_FILT_MTC (1 << 0x01) |
408 | +/** Song Position (0xF2) */ |
409 | +#define PM_FILT_SONG_POSITION (1 << 0x02) |
410 | +/** Song Select (0xF3)*/ |
411 | +#define PM_FILT_SONG_SELECT (1 << 0x03) |
412 | +/** Tuning request (0xF6)*/ |
413 | +#define PM_FILT_TUNE (1 << 0x06) |
414 | +/** All System Common messages (mtc, song position, song select, tune request) */ |
415 | +#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) |
416 | + |
417 | + |
418 | +PmError Pm_SetFilter( PortMidiStream* stream, long filters ); |
419 | + |
420 | +#define Pm_Channel(channel) (1<<(channel)) |
421 | +/** |
422 | + Pm_SetChannelMask() filters incoming messages based on channel. |
423 | + The mask is a 16-bit bitfield corresponding to appropriate channels |
424 | + The Pm_Channel macro can assist in calling this function. |
425 | + i.e. to set receive only input on channel 1, call with |
426 | + Pm_SetChannelMask(Pm_Channel(1)); |
427 | + Multiple channels should be OR'd together, like |
428 | + Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) |
429 | + |
430 | + All channels are allowed by default |
431 | +*/ |
432 | +PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); |
433 | + |
434 | +/** |
435 | + Pm_Abort() terminates outgoing messages immediately |
436 | + The caller should immediately close the output port; |
437 | + this call may result in transmission of a partial midi message. |
438 | + There is no abort for Midi input because the user can simply |
439 | + ignore messages in the buffer and close an input device at |
440 | + any time. |
441 | + */ |
442 | +PmError Pm_Abort( PortMidiStream* stream ); |
443 | + |
444 | +/** |
445 | + Pm_Close() closes a midi stream, flushing any pending buffers. |
446 | + (PortMidi attempts to close open streams when the application |
447 | + exits -- this is particularly difficult under Windows.) |
448 | +*/ |
449 | +PmError Pm_Close( PortMidiStream* stream ); |
450 | + |
451 | +/** |
452 | + Pm_Message() encodes a short Midi message into a long word. If data1 |
453 | + and/or data2 are not present, use zero. |
454 | + |
455 | + Pm_MessageStatus(), Pm_MessageData1(), and |
456 | + Pm_MessageData2() extract fields from a long-encoded midi message. |
457 | +*/ |
458 | +#define Pm_Message(status, data1, data2) \ |
459 | + ((((data2) << 16) & 0xFF0000) | \ |
460 | + (((data1) << 8) & 0xFF00) | \ |
461 | + ((status) & 0xFF)) |
462 | +#define Pm_MessageStatus(msg) ((msg) & 0xFF) |
463 | +#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) |
464 | +#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) |
465 | + |
466 | +typedef long PmMessage; /**< see PmEvent */ |
467 | +/** |
468 | + All midi data comes in the form of PmEvent structures. A sysex |
469 | + message is encoded as a sequence of PmEvent structures, with each |
470 | + structure carrying 4 bytes of the message, i.e. only the first |
471 | + PmEvent carries the status byte. |
472 | + |
473 | + Note that MIDI allows nested messages: the so-called "real-time" MIDI |
474 | + messages can be inserted into the MIDI byte stream at any location, |
475 | + including within a sysex message. MIDI real-time messages are one-byte |
476 | + messages used mainly for timing (see the MIDI spec). PortMidi retains |
477 | + the order of non-real-time MIDI messages on both input and output, but |
478 | + it does not specify exactly how real-time messages are processed. This |
479 | + is particulary problematic for MIDI input, because the input parser |
480 | + must either prepare to buffer an unlimited number of sysex message |
481 | + bytes or to buffer an unlimited number of real-time messages that |
482 | + arrive embedded in a long sysex message. To simplify things, the input |
483 | + parser is allowed to pass real-time MIDI messages embedded within a |
484 | + sysex message, and it is up to the client to detect, process, and |
485 | + remove these messages as they arrive. |
486 | + |
487 | + When receiving sysex messages, the sysex message is terminated |
488 | + by either an EOX status byte (anywhere in the 4 byte messages) or |
489 | + by a non-real-time status byte in the low order byte of the message. |
490 | + If you get a non-real-time status byte but there was no EOX byte, it |
491 | + means the sysex message was somehow truncated. This is not |
492 | + considered an error; e.g., a missing EOX can result from the user |
493 | + disconnecting a MIDI cable during sysex transmission. |
494 | + |
495 | + A real-time message can occur within a sysex message. A real-time |
496 | + message will always occupy a full PmEvent with the status byte in |
497 | + the low-order byte of the PmEvent message field. (This implies that |
498 | + the byte-order of sysex bytes and real-time message bytes may not |
499 | + be preserved -- for example, if a real-time message arrives after |
500 | + 3 bytes of a sysex message, the real-time message will be delivered |
501 | + first. The first word of the sysex message will be delivered only |
502 | + after the 4th byte arrives, filling the 4-byte PmEvent message field. |
503 | + |
504 | + The timestamp field is observed when the output port is opened with |
505 | + a non-zero latency. A timestamp of zero means "use the current time", |
506 | + which in turn means to deliver the message with a delay of |
507 | + latency (the latency parameter used when opening the output port.) |
508 | + Do not expect PortMidi to sort data according to timestamps -- |
509 | + messages should be sent in the correct order, and timestamps MUST |
510 | + be non-decreasing. See also "Example" for Pm_OpenOutput() above. |
511 | + |
512 | + A sysex message will generally fill many PmEvent structures. On |
513 | + output to a PortMidiStream with non-zero latency, the first timestamp |
514 | + on sysex message data will determine the time to begin sending the |
515 | + message. PortMidi implementations may ignore timestamps for the |
516 | + remainder of the sysex message. |
517 | + |
518 | + On input, the timestamp ideally denotes the arrival time of the |
519 | + status byte of the message. The first timestamp on sysex message |
520 | + data will be valid. Subsequent timestamps may denote |
521 | + when message bytes were actually received, or they may be simply |
522 | + copies of the first timestamp. |
523 | + |
524 | + Timestamps for nested messages: If a real-time message arrives in |
525 | + the middle of some other message, it is enqueued immediately with |
526 | + the timestamp corresponding to its arrival time. The interrupted |
527 | + non-real-time message or 4-byte packet of sysex data will be enqueued |
528 | + later. The timestamp of interrupted data will be equal to that of |
529 | + the interrupting real-time message to insure that timestamps are |
530 | + non-decreasing. |
531 | + */ |
532 | +typedef struct { |
533 | + PmMessage message; |
534 | + PmTimestamp timestamp; |
535 | +} PmEvent; |
536 | + |
537 | +/** |
538 | + @} |
539 | +*/ |
540 | +/** \defgroup grp_io Reading and Writing Midi Messages |
541 | + @{ |
542 | +*/ |
543 | +/** |
544 | + Pm_Read() retrieves midi data into a buffer, and returns the number |
545 | + of events read. Result is a non-negative number unless an error occurs, |
546 | + in which case a PmError value will be returned. |
547 | + |
548 | + Buffer Overflow |
549 | + |
550 | + The problem: if an input overflow occurs, data will be lost, ultimately |
551 | + because there is no flow control all the way back to the data source. |
552 | + When data is lost, the receiver should be notified and some sort of |
553 | + graceful recovery should take place, e.g. you shouldn't resume receiving |
554 | + in the middle of a long sysex message. |
555 | + |
556 | + With a lock-free fifo, which is pretty much what we're stuck with to |
557 | + enable portability to the Mac, it's tricky for the producer and consumer |
558 | + to synchronously reset the buffer and resume normal operation. |
559 | + |
560 | + Solution: the buffer managed by PortMidi will be flushed when an overflow |
561 | + occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) |
562 | + and ordinary processing resumes as soon as a new message arrives. The |
563 | + remainder of a partial sysex message is not considered to be a "new |
564 | + message" and will be flushed as well. |
565 | + |
566 | +*/ |
567 | +int Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length ); |
568 | + |
569 | +/** |
570 | + Pm_Poll() tests whether input is available, |
571 | + returning TRUE, FALSE, or an error value. |
572 | +*/ |
573 | +PmError Pm_Poll( PortMidiStream *stream); |
574 | + |
575 | +/** |
576 | + Pm_Write() writes midi data from a buffer. This may contain: |
577 | + - short messages |
578 | + or |
579 | + - sysex messages that are converted into a sequence of PmEvent |
580 | + structures, e.g. sending data from a file or forwarding them |
581 | + from midi input. |
582 | + |
583 | + Use Pm_WriteSysEx() to write a sysex message stored as a contiguous |
584 | + array of bytes. |
585 | + |
586 | + Sysex data may contain embedded real-time messages. |
587 | +*/ |
588 | +PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length ); |
589 | + |
590 | +/** |
591 | + Pm_WriteShort() writes a timestamped non-system-exclusive midi message. |
592 | + Messages are delivered in order as received, and timestamps must be |
593 | + non-decreasing. (But timestamps are ignored if the stream was opened |
594 | + with latency = 0.) |
595 | +*/ |
596 | +PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg); |
597 | + |
598 | +/** |
599 | + Pm_WriteSysEx() writes a timestamped system-exclusive midi message. |
600 | +*/ |
601 | +PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg); |
602 | + |
603 | +/** @} */ |
604 | + |
605 | +#ifdef __cplusplus |
606 | +} |
607 | +#endif /* __cplusplus */ |
608 | +#endif /* PORT_MIDI_H */ |
609 | |
610 | === added file 'mixxx-win64lib/portmidi.lib' |
611 | Binary files mixxx-win64lib/portmidi.lib 1970-01-01 00:00:00 +0000 and mixxx-win64lib/portmidi.lib 2009-11-10 00:33:17 +0000 differ |
612 | === added file 'mixxx-win64lib/porttime.h' |
613 | --- mixxx-win64lib/porttime.h 1970-01-01 00:00:00 +0000 |
614 | +++ mixxx-win64lib/porttime.h 2009-11-10 00:33:17 +0000 |
615 | @@ -0,0 +1,72 @@ |
616 | +/* porttime.h -- portable interface to millisecond timer */ |
617 | + |
618 | +/* CHANGE LOG FOR PORTTIME |
619 | + 10-Jun-03 Mark Nelson & RBD |
620 | + boost priority of timer thread in ptlinux.c implementation |
621 | + */ |
622 | + |
623 | +/* Should there be a way to choose the source of time here? */ |
624 | + |
625 | +#ifdef __cplusplus |
626 | +extern "C" { |
627 | +#endif |
628 | + |
629 | + |
630 | +typedef enum { |
631 | + ptNoError = 0, /* success */ |
632 | + ptHostError = -10000, /* a system-specific error occurred */ |
633 | + ptAlreadyStarted, /* cannot start timer because it is already started */ |
634 | + ptAlreadyStopped, /* cannot stop timer because it is already stopped */ |
635 | + ptInsufficientMemory /* memory could not be allocated */ |
636 | +} PtError; |
637 | + |
638 | + |
639 | +typedef long PtTimestamp; |
640 | + |
641 | +typedef void (PtCallback)( PtTimestamp timestamp, void *userData ); |
642 | + |
643 | +/* |
644 | + Pt_Start() starts a real-time service. |
645 | + |
646 | + resolution is the timer resolution in ms. The time will advance every |
647 | + resolution ms. |
648 | + |
649 | + callback is a function pointer to be called every resolution ms. |
650 | + |
651 | + userData is passed to callback as a parameter. |
652 | + |
653 | + return value: |
654 | + Upon success, returns ptNoError. See PtError for other values. |
655 | +*/ |
656 | +PtError Pt_Start(int resolution, PtCallback *callback, void *userData); |
657 | + |
658 | +/* |
659 | + Pt_Stop() stops the timer. |
660 | + |
661 | + return value: |
662 | + Upon success, returns ptNoError. See PtError for other values. |
663 | +*/ |
664 | +PtError Pt_Stop(); |
665 | + |
666 | +/* |
667 | + Pt_Started() returns true iff the timer is running. |
668 | +*/ |
669 | +int Pt_Started(); |
670 | + |
671 | +/* |
672 | + Pt_Time() returns the current time in ms. |
673 | +*/ |
674 | +PtTimestamp Pt_Time(); |
675 | + |
676 | +/* |
677 | + Pt_Sleep() pauses, allowing other threads to run. |
678 | + |
679 | + duration is the length of the pause in ms. The true duration |
680 | + of the pause may be rounded to the nearest or next clock tick |
681 | + as determined by resolution in Pt_Start(). |
682 | +*/ |
683 | +void Pt_Sleep(long duration); |
684 | + |
685 | +#ifdef __cplusplus |
686 | +} |
687 | +#endif |
688 | |
689 | === added file 'mixxx-win64lib/porttime.lib' |
690 | Binary files mixxx-win64lib/porttime.lib 1970-01-01 00:00:00 +0000 and mixxx-win64lib/porttime.lib 2009-11-10 00:33:17 +0000 differ |
691 | === added file 'mixxx-winlib/portmidi.h' |
692 | --- mixxx-winlib/portmidi.h 1970-01-01 00:00:00 +0000 |
693 | +++ mixxx-winlib/portmidi.h 2009-11-10 00:33:17 +0000 |
694 | @@ -0,0 +1,604 @@ |
695 | +#ifndef PORT_MIDI_H |
696 | +#define PORT_MIDI_H |
697 | +#ifdef __cplusplus |
698 | +extern "C" { |
699 | +#endif /* __cplusplus */ |
700 | + |
701 | +/* |
702 | + * PortMidi Portable Real-Time MIDI Library |
703 | + * PortMidi API Header File |
704 | + * Latest version available at: http://sourceforge.net/projects/portmedia |
705 | + * |
706 | + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk |
707 | + * Copyright (c) 2001-2006 Roger B. Dannenberg |
708 | + * |
709 | + * Permission is hereby granted, free of charge, to any person obtaining |
710 | + * a copy of this software and associated documentation files |
711 | + * (the "Software"), to deal in the Software without restriction, |
712 | + * including without limitation the rights to use, copy, modify, merge, |
713 | + * publish, distribute, sublicense, and/or sell copies of the Software, |
714 | + * and to permit persons to whom the Software is furnished to do so, |
715 | + * subject to the following conditions: |
716 | + * |
717 | + * The above copyright notice and this permission notice shall be |
718 | + * included in all copies or substantial portions of the Software. |
719 | + * |
720 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
721 | + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
722 | + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
723 | + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR |
724 | + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
725 | + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
726 | + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
727 | + */ |
728 | + |
729 | +/* |
730 | + * The text above constitutes the entire PortMidi license; however, |
731 | + * the PortMusic community also makes the following non-binding requests: |
732 | + * |
733 | + * Any person wishing to distribute modifications to the Software is |
734 | + * requested to send the modifications to the original developer so that |
735 | + * they can be incorporated into the canonical version. It is also |
736 | + * requested that these non-binding requests be included along with the |
737 | + * license above. |
738 | + */ |
739 | + |
740 | +/* CHANGELOG FOR PORTMIDI |
741 | + * (see ../CHANGELOG.txt) |
742 | + * |
743 | + * NOTES ON HOST ERROR REPORTING: |
744 | + * |
745 | + * PortMidi errors (of type PmError) are generic, system-independent errors. |
746 | + * When an error does not map to one of the more specific PmErrors, the |
747 | + * catch-all code pmHostError is returned. This means that PortMidi has |
748 | + * retained a more specific system-dependent error code. The caller can |
749 | + * get more information by calling Pm_HasHostError() to test if there is |
750 | + * a pending host error, and Pm_GetHostErrorText() to get a text string |
751 | + * describing the error. Host errors are reported on a per-device basis |
752 | + * because only after you open a device does PortMidi have a place to |
753 | + * record the host error code. I.e. only |
754 | + * those routines that receive a (PortMidiStream *) argument check and |
755 | + * report errors. One exception to this is that Pm_OpenInput() and |
756 | + * Pm_OpenOutput() can report errors even though when an error occurs, |
757 | + * there is no PortMidiStream* to hold the error. Fortunately, both |
758 | + * of these functions return any error immediately, so we do not really |
759 | + * need per-device error memory. Instead, any host error code is stored |
760 | + * in a global, pmHostError is returned, and the user can call |
761 | + * Pm_GetHostErrorText() to get the error message (and the invalid stream |
762 | + * parameter will be ignored.) The functions |
763 | + * pm_init and pm_term do not fail or raise |
764 | + * errors. The job of pm_init is to locate all available devices so that |
765 | + * the caller can get information via PmDeviceInfo(). If an error occurs, |
766 | + * the device is simply not listed as available. |
767 | + * |
768 | + * Host errors come in two flavors: |
769 | + * a) host error |
770 | + * b) host error during callback |
771 | + * These can occur w/midi input or output devices. (b) can only happen |
772 | + * asynchronously (during callback routines), whereas (a) only occurs while |
773 | + * synchronously running PortMidi and any resulting system dependent calls. |
774 | + * Both (a) and (b) are reported by the next read or write call. You can |
775 | + * also query for asynchronous errors (b) at any time by calling |
776 | + * Pm_HasHostError(). |
777 | + * |
778 | + * NOTES ON COMPILE-TIME SWITCHES |
779 | + * |
780 | + * DEBUG assumes stdio and a console. Use this if you want automatic, simple |
781 | + * error reporting, e.g. for prototyping. If you are using MFC or some |
782 | + * other graphical interface with no console, DEBUG probably should be |
783 | + * undefined. |
784 | + * PM_CHECK_ERRORS more-or-less takes over error checking for return values, |
785 | + * stopping your program and printing error messages when an error |
786 | + * occurs. This also uses stdio for console text I/O. |
787 | + */ |
788 | + |
789 | +#ifndef FALSE |
790 | + #define FALSE 0 |
791 | +#endif |
792 | +#ifndef TRUE |
793 | + #define TRUE 1 |
794 | +#endif |
795 | + |
796 | +/* default size of buffers for sysex transmission: */ |
797 | +#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 |
798 | + |
799 | +/** List of portmidi errors.*/ |
800 | +typedef enum { |
801 | + pmNoError = 0, |
802 | + pmNoData = 0, /**< A "no error" return that also indicates no data avail. */ |
803 | + pmGotData = 1, /**< A "no error" return that also indicates data available */ |
804 | + pmHostError = -10000, |
805 | + pmInvalidDeviceId, /** out of range or |
806 | + * output device when input is requested or |
807 | + * input device when output is requested or |
808 | + * device is already opened |
809 | + */ |
810 | + pmInsufficientMemory, |
811 | + pmBufferTooSmall, |
812 | + pmBufferOverflow, |
813 | + pmBadPtr, /* PortMidiStream parameter is NULL or |
814 | + * stream is not opened or |
815 | + * stream is output when input is required or |
816 | + * stream is input when output is required */ |
817 | + pmBadData, /** illegal midi data, e.g. missing EOX */ |
818 | + pmInternalError, |
819 | + pmBufferMaxSize /** buffer is already as large as it can be */ |
820 | + /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */ |
821 | +} PmError; |
822 | + |
823 | +/** |
824 | + Pm_Initialize() is the library initialisation function - call this before |
825 | + using the library. |
826 | +*/ |
827 | +PmError Pm_Initialize( void ); |
828 | + |
829 | +/** |
830 | + Pm_Terminate() is the library termination function - call this after |
831 | + using the library. |
832 | +*/ |
833 | +PmError Pm_Terminate( void ); |
834 | + |
835 | +/** A single PortMidiStream is a descriptor for an open MIDI device. |
836 | +*/ |
837 | +typedef void PortMidiStream; |
838 | +#define PmStream PortMidiStream |
839 | + |
840 | +/** |
841 | + Test whether stream has a pending host error. Normally, the client finds |
842 | + out about errors through returned error codes, but some errors can occur |
843 | + asynchronously where the client does not |
844 | + explicitly call a function, and therefore cannot receive an error code. |
845 | + The client can test for a pending error using Pm_HasHostError(). If true, |
846 | + the error can be accessed and cleared by calling Pm_GetErrorText(). |
847 | + Errors are also cleared by calling other functions that can return |
848 | + errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The |
849 | + client does not need to call Pm_HasHostError(). Any pending error will be |
850 | + reported the next time the client performs an explicit function call on |
851 | + the stream, e.g. an input or output operation. Until the error is cleared, |
852 | + no new error codes will be obtained, even for a different stream. |
853 | +*/ |
854 | +int Pm_HasHostError( PortMidiStream * stream ); |
855 | + |
856 | + |
857 | +/** Translate portmidi error number into human readable message. |
858 | + These strings are constants (set at compile time) so client has |
859 | + no need to allocate storage |
860 | +*/ |
861 | +const char *Pm_GetErrorText( PmError errnum ); |
862 | + |
863 | +/** Translate portmidi host error into human readable message. |
864 | + These strings are computed at run time, so client has to allocate storage. |
865 | + After this routine executes, the host error is cleared. |
866 | +*/ |
867 | +void Pm_GetHostErrorText(char * msg, unsigned int len); |
868 | + |
869 | +#define HDRLENGTH 50 |
870 | +#define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less |
871 | + than this number of characters */ |
872 | + |
873 | +/** |
874 | + Device enumeration mechanism. |
875 | + |
876 | + Device ids range from 0 to Pm_CountDevices()-1. |
877 | + |
878 | +*/ |
879 | +typedef int PmDeviceID; |
880 | +#define pmNoDevice -1 |
881 | +typedef struct { |
882 | + int structVersion; /**< this internal structure version */ |
883 | + const char *interf; /**< underlying MIDI API, e.g. MMSystem or DirectX */ |
884 | + const char *name; /**< device name, e.g. USB MidiSport 1x1 */ |
885 | + int input; /**< true iff input is available */ |
886 | + int output; /**< true iff output is available */ |
887 | + int opened; /**< used by generic PortMidi code to do error checking on arguments */ |
888 | + |
889 | +} PmDeviceInfo; |
890 | + |
891 | +/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */ |
892 | +int Pm_CountDevices( void ); |
893 | +/** |
894 | + Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() |
895 | + |
896 | + Return the default device ID or pmNoDevice if there are no devices. |
897 | + The result (but not pmNoDevice) can be passed to Pm_OpenMidi(). |
898 | + |
899 | + The default device can be specified using a small application |
900 | + named pmdefaults that is part of the PortMidi distribution. This |
901 | + program in turn uses the Java Preferences object created by |
902 | + java.util.prefs.Preferences.userRoot().node("/PortMidi"); the |
903 | + preference is set by calling |
904 | + prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName); |
905 | + or prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName); |
906 | + |
907 | + In the statements above, prefName is a string describing the |
908 | + MIDI device in the form "interf, name" where interf identifies |
909 | + the underlying software system or API used by PortMdi to access |
910 | + devices and name is the name of the device. These correspond to |
911 | + the interf and name fields of a PmDeviceInfo. (Currently supported |
912 | + interfaces are "MMSystem" for Win32, "ALSA" for Linux, and |
913 | + "CoreMIDI" for OS X, so in fact, there is no choice of interface.) |
914 | + In "interf, name", the strings are actually substrings of |
915 | + the full interface and name strings. For example, the preference |
916 | + "Core, Sport" will match a device with interface "CoreMIDI" |
917 | + and name "In USB MidiSport 1x1". It will also match "CoreMIDI" |
918 | + and "In USB MidiSport 2x2". The devices are enumerated in device |
919 | + ID order, so the lowest device ID that matches the pattern becomes |
920 | + the default device. Finally, if the comma-space (", ") separator |
921 | + between interface and name parts of the preference is not found, |
922 | + the entire preference string is interpreted as a name, and the |
923 | + interface part is the empty string, which matches anything. |
924 | + |
925 | + On the MAC, preferences are stored in |
926 | + /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist |
927 | + which is a binary file. In addition to the pmdefaults program, |
928 | + there are utilities that can read and edit this preference file. |
929 | + |
930 | + On the PC, |
931 | + |
932 | + On Linux, |
933 | + |
934 | +*/ |
935 | +PmDeviceID Pm_GetDefaultInputDeviceID( void ); |
936 | +/** see PmDeviceID Pm_GetDefaultInputDeviceID() */ |
937 | +PmDeviceID Pm_GetDefaultOutputDeviceID( void ); |
938 | + |
939 | +/** |
940 | + PmTimestamp is used to represent a millisecond clock with arbitrary |
941 | + start time. The type is used for all MIDI timestampes and clocks. |
942 | +*/ |
943 | +typedef long PmTimestamp; |
944 | +typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); |
945 | + |
946 | +/** TRUE if t1 before t2 */ |
947 | +#define PmBefore(t1,t2) ((t1-t2) < 0) |
948 | +/** |
949 | + \defgroup grp_device Input/Output Devices Handling |
950 | + @{ |
951 | +*/ |
952 | +/** |
953 | + Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure |
954 | + referring to the device specified by id. |
955 | + If id is out of range the function returns NULL. |
956 | + |
957 | + The returned structure is owned by the PortMidi implementation and must |
958 | + not be manipulated or freed. The pointer is guaranteed to be valid |
959 | + between calls to Pm_Initialize() and Pm_Terminate(). |
960 | +*/ |
961 | +const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); |
962 | + |
963 | +/** |
964 | + Pm_OpenInput() and Pm_OpenOutput() open devices. |
965 | + |
966 | + stream is the address of a PortMidiStream pointer which will receive |
967 | + a pointer to the newly opened stream. |
968 | + |
969 | + inputDevice is the id of the device used for input (see PmDeviceID above). |
970 | + |
971 | + inputDriverInfo is a pointer to an optional driver specific data structure |
972 | + containing additional information for device setup or handle processing. |
973 | + inputDriverInfo is never required for correct operation. If not used |
974 | + inputDriverInfo should be NULL. |
975 | + |
976 | + outputDevice is the id of the device used for output (see PmDeviceID above.) |
977 | + |
978 | + outputDriverInfo is a pointer to an optional driver specific data structure |
979 | + containing additional information for device setup or handle processing. |
980 | + outputDriverInfo is never required for correct operation. If not used |
981 | + outputDriverInfo should be NULL. |
982 | + |
983 | + For input, the buffersize specifies the number of input events to be |
984 | + buffered waiting to be read using Pm_Read(). For output, buffersize |
985 | + specifies the number of output events to be buffered waiting for output. |
986 | + (In some cases -- see below -- PortMidi does not buffer output at all |
987 | + and merely passes data to a lower-level API, in which case buffersize |
988 | + is ignored.) |
989 | + |
990 | + latency is the delay in milliseconds applied to timestamps to determine |
991 | + when the output should actually occur. (If latency is < 0, 0 is assumed.) |
992 | + If latency is zero, timestamps are ignored and all output is delivered |
993 | + immediately. If latency is greater than zero, output is delayed until the |
994 | + message timestamp plus the latency. (NOTE: the time is measured relative |
995 | + to the time source indicated by time_proc. Timestamps are absolute, |
996 | + not relative delays or offsets.) In some cases, PortMidi can obtain |
997 | + better timing than your application by passing timestamps along to the |
998 | + device driver or hardware. Latency may also help you to synchronize midi |
999 | + data to audio data by matching midi latency to the audio buffer latency. |
1000 | + |
1001 | + time_proc is a pointer to a procedure that returns time in milliseconds. It |
1002 | + may be NULL, in which case a default millisecond timebase (PortTime) is |
1003 | + used. If the application wants to use PortTime, it should start the timer |
1004 | + (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the |
1005 | + application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput, |
1006 | + it may get a ptAlreadyStarted error from Pt_Start, and the application's |
1007 | + preferred time resolution and callback function will be ignored. |
1008 | + time_proc result values are appended to incoming MIDI data, and time_proc |
1009 | + times are used to schedule outgoing MIDI data (when latency is non-zero). |
1010 | + |
1011 | + time_info is a pointer passed to time_proc. |
1012 | + |
1013 | + Example: If I provide a timestamp of 5000, latency is 1, and time_proc |
1014 | + returns 4990, then the desired output time will be when time_proc returns |
1015 | + timestamp+latency = 5001. This will be 5001-4990 = 11ms from now. |
1016 | + |
1017 | + return value: |
1018 | + Upon success Pm_Open() returns PmNoError and places a pointer to a |
1019 | + valid PortMidiStream in the stream argument. |
1020 | + If a call to Pm_Open() fails a nonzero error code is returned (see |
1021 | + PMError above) and the value of port is invalid. |
1022 | + |
1023 | + Any stream that is successfully opened should eventually be closed |
1024 | + by calling Pm_Close(). |
1025 | + |
1026 | +*/ |
1027 | +PmError Pm_OpenInput( PortMidiStream** stream, |
1028 | + PmDeviceID inputDevice, |
1029 | + void *inputDriverInfo, |
1030 | + long bufferSize, |
1031 | + PmTimeProcPtr time_proc, |
1032 | + void *time_info ); |
1033 | + |
1034 | +PmError Pm_OpenOutput( PortMidiStream** stream, |
1035 | + PmDeviceID outputDevice, |
1036 | + void *outputDriverInfo, |
1037 | + long bufferSize, |
1038 | + PmTimeProcPtr time_proc, |
1039 | + void *time_info, |
1040 | + long latency ); |
1041 | + /** @} */ |
1042 | + |
1043 | +/** |
1044 | + \defgroup grp_events_filters Events and Filters Handling |
1045 | + @{ |
1046 | +*/ |
1047 | + |
1048 | +/* \function PmError Pm_SetFilter( PortMidiStream* stream, long filters ) |
1049 | + Pm_SetFilter() sets filters on an open input stream to drop selected |
1050 | + input types. By default, only active sensing messages are filtered. |
1051 | + To prohibit, say, active sensing and sysex messages, call |
1052 | + Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); |
1053 | + |
1054 | + Filtering is useful when midi routing or midi thru functionality is being |
1055 | + provided by the user application. |
1056 | + For example, you may want to exclude timing messages (clock, MTC, start/stop/continue), |
1057 | + while allowing note-related messages to pass. |
1058 | + Or you may be using a sequencer or drum-machine for MIDI clock information but want to |
1059 | + exclude any notes it may play. |
1060 | + */ |
1061 | + |
1062 | +/* Filter bit-mask definitions */ |
1063 | +/** filter active sensing messages (0xFE): */ |
1064 | +#define PM_FILT_ACTIVE (1 << 0x0E) |
1065 | +/** filter system exclusive messages (0xF0): */ |
1066 | +#define PM_FILT_SYSEX (1 << 0x00) |
1067 | +/** filter MIDI clock message (0xF8) */ |
1068 | +#define PM_FILT_CLOCK (1 << 0x08) |
1069 | +/** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ |
1070 | +#define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) |
1071 | +/** filter tick messages (0xF9) */ |
1072 | +#define PM_FILT_TICK (1 << 0x09) |
1073 | +/** filter undefined FD messages */ |
1074 | +#define PM_FILT_FD (1 << 0x0D) |
1075 | +/** filter undefined real-time messages */ |
1076 | +#define PM_FILT_UNDEFINED PM_FILT_FD |
1077 | +/** filter reset messages (0xFF) */ |
1078 | +#define PM_FILT_RESET (1 << 0x0F) |
1079 | +/** filter all real-time messages */ |
1080 | +#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ |
1081 | + PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) |
1082 | +/** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ |
1083 | +#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) |
1084 | +/** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ |
1085 | +#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) |
1086 | +/** per-note aftertouch (0xA0-0xAF) */ |
1087 | +#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) |
1088 | +/** filter both channel and poly aftertouch */ |
1089 | +#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) |
1090 | +/** Program changes (0xC0-0xCF) */ |
1091 | +#define PM_FILT_PROGRAM (1 << 0x1C) |
1092 | +/** Control Changes (CC's) (0xB0-0xBF)*/ |
1093 | +#define PM_FILT_CONTROL (1 << 0x1B) |
1094 | +/** Pitch Bender (0xE0-0xEF*/ |
1095 | +#define PM_FILT_PITCHBEND (1 << 0x1E) |
1096 | +/** MIDI Time Code (0xF1)*/ |
1097 | +#define PM_FILT_MTC (1 << 0x01) |
1098 | +/** Song Position (0xF2) */ |
1099 | +#define PM_FILT_SONG_POSITION (1 << 0x02) |
1100 | +/** Song Select (0xF3)*/ |
1101 | +#define PM_FILT_SONG_SELECT (1 << 0x03) |
1102 | +/** Tuning request (0xF6)*/ |
1103 | +#define PM_FILT_TUNE (1 << 0x06) |
1104 | +/** All System Common messages (mtc, song position, song select, tune request) */ |
1105 | +#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) |
1106 | + |
1107 | + |
1108 | +PmError Pm_SetFilter( PortMidiStream* stream, long filters ); |
1109 | + |
1110 | +#define Pm_Channel(channel) (1<<(channel)) |
1111 | +/** |
1112 | + Pm_SetChannelMask() filters incoming messages based on channel. |
1113 | + The mask is a 16-bit bitfield corresponding to appropriate channels |
1114 | + The Pm_Channel macro can assist in calling this function. |
1115 | + i.e. to set receive only input on channel 1, call with |
1116 | + Pm_SetChannelMask(Pm_Channel(1)); |
1117 | + Multiple channels should be OR'd together, like |
1118 | + Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) |
1119 | + |
1120 | + All channels are allowed by default |
1121 | +*/ |
1122 | +PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); |
1123 | + |
1124 | +/** |
1125 | + Pm_Abort() terminates outgoing messages immediately |
1126 | + The caller should immediately close the output port; |
1127 | + this call may result in transmission of a partial midi message. |
1128 | + There is no abort for Midi input because the user can simply |
1129 | + ignore messages in the buffer and close an input device at |
1130 | + any time. |
1131 | + */ |
1132 | +PmError Pm_Abort( PortMidiStream* stream ); |
1133 | + |
1134 | +/** |
1135 | + Pm_Close() closes a midi stream, flushing any pending buffers. |
1136 | + (PortMidi attempts to close open streams when the application |
1137 | + exits -- this is particularly difficult under Windows.) |
1138 | +*/ |
1139 | +PmError Pm_Close( PortMidiStream* stream ); |
1140 | + |
1141 | +/** |
1142 | + Pm_Message() encodes a short Midi message into a long word. If data1 |
1143 | + and/or data2 are not present, use zero. |
1144 | + |
1145 | + Pm_MessageStatus(), Pm_MessageData1(), and |
1146 | + Pm_MessageData2() extract fields from a long-encoded midi message. |
1147 | +*/ |
1148 | +#define Pm_Message(status, data1, data2) \ |
1149 | + ((((data2) << 16) & 0xFF0000) | \ |
1150 | + (((data1) << 8) & 0xFF00) | \ |
1151 | + ((status) & 0xFF)) |
1152 | +#define Pm_MessageStatus(msg) ((msg) & 0xFF) |
1153 | +#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) |
1154 | +#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) |
1155 | + |
1156 | +typedef long PmMessage; /**< see PmEvent */ |
1157 | +/** |
1158 | + All midi data comes in the form of PmEvent structures. A sysex |
1159 | + message is encoded as a sequence of PmEvent structures, with each |
1160 | + structure carrying 4 bytes of the message, i.e. only the first |
1161 | + PmEvent carries the status byte. |
1162 | + |
1163 | + Note that MIDI allows nested messages: the so-called "real-time" MIDI |
1164 | + messages can be inserted into the MIDI byte stream at any location, |
1165 | + including within a sysex message. MIDI real-time messages are one-byte |
1166 | + messages used mainly for timing (see the MIDI spec). PortMidi retains |
1167 | + the order of non-real-time MIDI messages on both input and output, but |
1168 | + it does not specify exactly how real-time messages are processed. This |
1169 | + is particulary problematic for MIDI input, because the input parser |
1170 | + must either prepare to buffer an unlimited number of sysex message |
1171 | + bytes or to buffer an unlimited number of real-time messages that |
1172 | + arrive embedded in a long sysex message. To simplify things, the input |
1173 | + parser is allowed to pass real-time MIDI messages embedded within a |
1174 | + sysex message, and it is up to the client to detect, process, and |
1175 | + remove these messages as they arrive. |
1176 | + |
1177 | + When receiving sysex messages, the sysex message is terminated |
1178 | + by either an EOX status byte (anywhere in the 4 byte messages) or |
1179 | + by a non-real-time status byte in the low order byte of the message. |
1180 | + If you get a non-real-time status byte but there was no EOX byte, it |
1181 | + means the sysex message was somehow truncated. This is not |
1182 | + considered an error; e.g., a missing EOX can result from the user |
1183 | + disconnecting a MIDI cable during sysex transmission. |
1184 | + |
1185 | + A real-time message can occur within a sysex message. A real-time |
1186 | + message will always occupy a full PmEvent with the status byte in |
1187 | + the low-order byte of the PmEvent message field. (This implies that |
1188 | + the byte-order of sysex bytes and real-time message bytes may not |
1189 | + be preserved -- for example, if a real-time message arrives after |
1190 | + 3 bytes of a sysex message, the real-time message will be delivered |
1191 | + first. The first word of the sysex message will be delivered only |
1192 | + after the 4th byte arrives, filling the 4-byte PmEvent message field. |
1193 | + |
1194 | + The timestamp field is observed when the output port is opened with |
1195 | + a non-zero latency. A timestamp of zero means "use the current time", |
1196 | + which in turn means to deliver the message with a delay of |
1197 | + latency (the latency parameter used when opening the output port.) |
1198 | + Do not expect PortMidi to sort data according to timestamps -- |
1199 | + messages should be sent in the correct order, and timestamps MUST |
1200 | + be non-decreasing. See also "Example" for Pm_OpenOutput() above. |
1201 | + |
1202 | + A sysex message will generally fill many PmEvent structures. On |
1203 | + output to a PortMidiStream with non-zero latency, the first timestamp |
1204 | + on sysex message data will determine the time to begin sending the |
1205 | + message. PortMidi implementations may ignore timestamps for the |
1206 | + remainder of the sysex message. |
1207 | + |
1208 | + On input, the timestamp ideally denotes the arrival time of the |
1209 | + status byte of the message. The first timestamp on sysex message |
1210 | + data will be valid. Subsequent timestamps may denote |
1211 | + when message bytes were actually received, or they may be simply |
1212 | + copies of the first timestamp. |
1213 | + |
1214 | + Timestamps for nested messages: If a real-time message arrives in |
1215 | + the middle of some other message, it is enqueued immediately with |
1216 | + the timestamp corresponding to its arrival time. The interrupted |
1217 | + non-real-time message or 4-byte packet of sysex data will be enqueued |
1218 | + later. The timestamp of interrupted data will be equal to that of |
1219 | + the interrupting real-time message to insure that timestamps are |
1220 | + non-decreasing. |
1221 | + */ |
1222 | +typedef struct { |
1223 | + PmMessage message; |
1224 | + PmTimestamp timestamp; |
1225 | +} PmEvent; |
1226 | + |
1227 | +/** |
1228 | + @} |
1229 | +*/ |
1230 | +/** \defgroup grp_io Reading and Writing Midi Messages |
1231 | + @{ |
1232 | +*/ |
1233 | +/** |
1234 | + Pm_Read() retrieves midi data into a buffer, and returns the number |
1235 | + of events read. Result is a non-negative number unless an error occurs, |
1236 | + in which case a PmError value will be returned. |
1237 | + |
1238 | + Buffer Overflow |
1239 | + |
1240 | + The problem: if an input overflow occurs, data will be lost, ultimately |
1241 | + because there is no flow control all the way back to the data source. |
1242 | + When data is lost, the receiver should be notified and some sort of |
1243 | + graceful recovery should take place, e.g. you shouldn't resume receiving |
1244 | + in the middle of a long sysex message. |
1245 | + |
1246 | + With a lock-free fifo, which is pretty much what we're stuck with to |
1247 | + enable portability to the Mac, it's tricky for the producer and consumer |
1248 | + to synchronously reset the buffer and resume normal operation. |
1249 | + |
1250 | + Solution: the buffer managed by PortMidi will be flushed when an overflow |
1251 | + occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) |
1252 | + and ordinary processing resumes as soon as a new message arrives. The |
1253 | + remainder of a partial sysex message is not considered to be a "new |
1254 | + message" and will be flushed as well. |
1255 | + |
1256 | +*/ |
1257 | +int Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length ); |
1258 | + |
1259 | +/** |
1260 | + Pm_Poll() tests whether input is available, |
1261 | + returning TRUE, FALSE, or an error value. |
1262 | +*/ |
1263 | +PmError Pm_Poll( PortMidiStream *stream); |
1264 | + |
1265 | +/** |
1266 | + Pm_Write() writes midi data from a buffer. This may contain: |
1267 | + - short messages |
1268 | + or |
1269 | + - sysex messages that are converted into a sequence of PmEvent |
1270 | + structures, e.g. sending data from a file or forwarding them |
1271 | + from midi input. |
1272 | + |
1273 | + Use Pm_WriteSysEx() to write a sysex message stored as a contiguous |
1274 | + array of bytes. |
1275 | + |
1276 | + Sysex data may contain embedded real-time messages. |
1277 | +*/ |
1278 | +PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length ); |
1279 | + |
1280 | +/** |
1281 | + Pm_WriteShort() writes a timestamped non-system-exclusive midi message. |
1282 | + Messages are delivered in order as received, and timestamps must be |
1283 | + non-decreasing. (But timestamps are ignored if the stream was opened |
1284 | + with latency = 0.) |
1285 | +*/ |
1286 | +PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg); |
1287 | + |
1288 | +/** |
1289 | + Pm_WriteSysEx() writes a timestamped system-exclusive midi message. |
1290 | +*/ |
1291 | +PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg); |
1292 | + |
1293 | +/** @} */ |
1294 | + |
1295 | +#ifdef __cplusplus |
1296 | +} |
1297 | +#endif /* __cplusplus */ |
1298 | +#endif /* PORT_MIDI_H */ |
1299 | |
1300 | === added file 'mixxx-winlib/portmidi.lib' |
1301 | Binary files mixxx-winlib/portmidi.lib 1970-01-01 00:00:00 +0000 and mixxx-winlib/portmidi.lib 2009-11-10 00:33:17 +0000 differ |
1302 | === added file 'mixxx-winlib/porttime.h' |
1303 | --- mixxx-winlib/porttime.h 1970-01-01 00:00:00 +0000 |
1304 | +++ mixxx-winlib/porttime.h 2009-11-10 00:33:17 +0000 |
1305 | @@ -0,0 +1,72 @@ |
1306 | +/* porttime.h -- portable interface to millisecond timer */ |
1307 | + |
1308 | +/* CHANGE LOG FOR PORTTIME |
1309 | + 10-Jun-03 Mark Nelson & RBD |
1310 | + boost priority of timer thread in ptlinux.c implementation |
1311 | + */ |
1312 | + |
1313 | +/* Should there be a way to choose the source of time here? */ |
1314 | + |
1315 | +#ifdef __cplusplus |
1316 | +extern "C" { |
1317 | +#endif |
1318 | + |
1319 | + |
1320 | +typedef enum { |
1321 | + ptNoError = 0, /* success */ |
1322 | + ptHostError = -10000, /* a system-specific error occurred */ |
1323 | + ptAlreadyStarted, /* cannot start timer because it is already started */ |
1324 | + ptAlreadyStopped, /* cannot stop timer because it is already stopped */ |
1325 | + ptInsufficientMemory /* memory could not be allocated */ |
1326 | +} PtError; |
1327 | + |
1328 | + |
1329 | +typedef long PtTimestamp; |
1330 | + |
1331 | +typedef void (PtCallback)( PtTimestamp timestamp, void *userData ); |
1332 | + |
1333 | +/* |
1334 | + Pt_Start() starts a real-time service. |
1335 | + |
1336 | + resolution is the timer resolution in ms. The time will advance every |
1337 | + resolution ms. |
1338 | + |
1339 | + callback is a function pointer to be called every resolution ms. |
1340 | + |
1341 | + userData is passed to callback as a parameter. |
1342 | + |
1343 | + return value: |
1344 | + Upon success, returns ptNoError. See PtError for other values. |
1345 | +*/ |
1346 | +PtError Pt_Start(int resolution, PtCallback *callback, void *userData); |
1347 | + |
1348 | +/* |
1349 | + Pt_Stop() stops the timer. |
1350 | + |
1351 | + return value: |
1352 | + Upon success, returns ptNoError. See PtError for other values. |
1353 | +*/ |
1354 | +PtError Pt_Stop(); |
1355 | + |
1356 | +/* |
1357 | + Pt_Started() returns true iff the timer is running. |
1358 | +*/ |
1359 | +int Pt_Started(); |
1360 | + |
1361 | +/* |
1362 | + Pt_Time() returns the current time in ms. |
1363 | +*/ |
1364 | +PtTimestamp Pt_Time(); |
1365 | + |
1366 | +/* |
1367 | + Pt_Sleep() pauses, allowing other threads to run. |
1368 | + |
1369 | + duration is the length of the pause in ms. The true duration |
1370 | + of the pause may be rounded to the nearest or next clock tick |
1371 | + as determined by resolution in Pt_Start(). |
1372 | +*/ |
1373 | +void Pt_Sleep(long duration); |
1374 | + |
1375 | +#ifdef __cplusplus |
1376 | +} |
1377 | +#endif |
1378 | |
1379 | === added file 'mixxx-winlib/porttime.lib' |
1380 | Binary files mixxx-winlib/porttime.lib 1970-01-01 00:00:00 +0000 and mixxx-winlib/porttime.lib 2009-11-10 00:33:17 +0000 differ |
1381 | === added file 'mixxx/Mixxx.nsi' |
1382 | --- mixxx/Mixxx.nsi 1970-01-01 00:00:00 +0000 |
1383 | +++ mixxx/Mixxx.nsi 2009-08-11 04:38:51 +0000 |
1384 | @@ -0,0 +1,255 @@ |
1385 | +; Mixxx.nsi |
1386 | +; |
1387 | +; Mixxx NSI install script. |
1388 | +; has uninstall support and (optionally) installs start menu shortcuts. |
1389 | +; |
1390 | +; By Tue Haste Andersen <haste@diku.dk>, June 2004. |
1391 | +; Heavily modified since by Albert Santoni, Garth Dahlstrom and Sean Pappalardo. |
1392 | +; |
1393 | +; Lots of bits lifted from http://www.improve.dk/downloads/InstallScript.txt |
1394 | +; |
1395 | +;Include Modern UI |
1396 | +!include "MUI.nsh" |
1397 | + |
1398 | +; Definitions |
1399 | +!define PRODUCT_NAME "Mixxx" |
1400 | +;!define PRODUCT_VERSION "" ; Specified by the SConscript |
1401 | +!define PRODUCT_PUBLISHER "The Mixxx Team" |
1402 | +!define PRODUCT_WEB_SITE "http://www.mixxx.org" |
1403 | +!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\Mixxx.exe" |
1404 | +!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" |
1405 | +!define PRODUCT_UNINST_ROOT_KEY "HKLM" |
1406 | + |
1407 | +; The name of the installer |
1408 | +Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" |
1409 | + |
1410 | +; Disable the Nullsoft Installer branding text at the bottom. |
1411 | +BrandingText " " |
1412 | + |
1413 | +; The file to write and default installation directory |
1414 | +!ifdef x64 |
1415 | + OutFile "${PRODUCT_NAME}-${PRODUCT_VERSION}-x64.exe" |
1416 | + InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}" |
1417 | +!else |
1418 | + OutFile "${PRODUCT_NAME}-${PRODUCT_VERSION}-x86.exe" |
1419 | + InstallDir "$PROGRAMFILES\${PRODUCT_NAME}" |
1420 | +!endif |
1421 | + |
1422 | +; Use best compression |
1423 | +SetCompressor /SOLID lzma |
1424 | + |
1425 | +; Registry key to check for directory (so if you install again, it will |
1426 | +; overwrite the old one automatically) |
1427 | +;InstallDirRegKey HKLM "Software\NSIS_Mixxx" "Install_Dir" |
1428 | +InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" "" |
1429 | + |
1430 | +;Interface Settings |
1431 | +!define MUI_ABORTWARNING |
1432 | + |
1433 | +!define MUI_HEADERIMAGE |
1434 | +;!define MUI_HEADERIMAGE_RIGHT |
1435 | +!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH |
1436 | +!define MUI_HEADERIMAGE_BITMAP "res\images\mixxx_install_logo.bmp" |
1437 | +!define MUI_ICON "res\images\icon.ico" |
1438 | + |
1439 | +; Pages |
1440 | +!insertmacro MUI_PAGE_LICENSE "LICENSE" |
1441 | +!insertmacro MUI_PAGE_COMPONENTS |
1442 | +!insertmacro MUI_PAGE_DIRECTORY |
1443 | +!insertmacro MUI_PAGE_INSTFILES |
1444 | + |
1445 | +!insertmacro MUI_UNPAGE_CONFIRM |
1446 | +!insertmacro MUI_UNPAGE_INSTFILES |
1447 | + |
1448 | +;Languages |
1449 | +!insertmacro MUI_LANGUAGE "English" |
1450 | + |
1451 | +;-------------------------------- |
1452 | +; Install functions |
1453 | + |
1454 | +Function .onInit ; Prevent multiple installer instances |
1455 | + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "runningMixxxInstallerMutex") i .r1 ?e' |
1456 | + Pop $R0 |
1457 | + |
1458 | + StrCmp $R0 0 +3 |
1459 | + MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." |
1460 | + Abort |
1461 | +FunctionEnd |
1462 | + |
1463 | +;-------------------------------- |
1464 | +; The stuff to install |
1465 | +Section "Mixxx (required)" SecMixxx |
1466 | + |
1467 | + SectionIn RO |
1468 | + |
1469 | + ; Set output path to the installation directory. |
1470 | + SetOutPath $INSTDIR |
1471 | + |
1472 | + ; Put binary files there |
1473 | + File "dist\mixxx.exe" |
1474 | + File "dist\*.dll" |
1475 | + |
1476 | + ; NOTE: you need to check the mixxx.exe.manifest file in the win??_build directory |
1477 | + ; and place the appropriate versions of the listed DLL files and their manifest files |
1478 | + ; into the mixxx-win[64]lib directory for packaging before making the installer |
1479 | + ; (Visual C++ 2005 is msvc?80.dll and Microsoft.VC80.CRT.manifest, Visual C++ 2008 is msvc?90.dll and Microsoft.VC90.CRT.manifest) |
1480 | + ; |
1481 | + ; See http://mixxx.org/wiki/doku.php/build_windows_installer for full details. |
1482 | + |
1483 | + !ifdef x64 ; x64 versions |
1484 | + File ..\mixxx-win64lib\msvcr*.dll |
1485 | + File ..\mixxx-win64lib\msvcp*.dll |
1486 | + File /nonfatal ..\mixxx-win64lib\msvcm*.dll |
1487 | + File ..\mixxx-win64lib\Microsoft.VC*.CRT.manifest |
1488 | + !else ; x86 versions |
1489 | + File ..\mixxx-winlib\msvcr*.dll |
1490 | + File ..\mixxx-winlib\msvcp*.dll |
1491 | + File /nonfatal ..\mixxx-winlib\msvcm*.dll |
1492 | + File ..\mixxx-winlib\Microsoft.VC*.CRT.manifest |
1493 | + !endif |
1494 | + |
1495 | + ; And documentation, licence etc. |
1496 | + File "Mixxx-Manual.pdf" |
1497 | + File "LICENSE" |
1498 | + File "README" |
1499 | + File "COPYING" |
1500 | + |
1501 | + SetOutPath $INSTDIR\midi |
1502 | + File /r /x ".svn" /x ".bzr" dist\midi\*.* |
1503 | + |
1504 | + ;Disabled for initial 1.6.0 release |
1505 | + ;SetOutPath $INSTDIR\promo |
1506 | + ;File "dist\promo\*" |
1507 | + |
1508 | + SetOutPath $INSTDIR\keyboard |
1509 | + File "dist\keyboard\Standard.kbd.cfg" |
1510 | + File "dist\keyboard\Old.kbd.cfg" |
1511 | + |
1512 | + SetOutPath "$INSTDIR\skins" |
1513 | + File /r /x ".svn" /x ".bzr" dist\skins\*.* |
1514 | + |
1515 | + ; Write the installation path into the registry |
1516 | + ;WriteRegStr HKLM SOFTWARE\NSIS_Mixxx "Install_Dir" "$INSTDIR" |
1517 | + WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\Mixxx.exe" |
1518 | + |
1519 | + ; Write the uninstall keys for Windows |
1520 | + WriteUninstaller "$INSTDIR\uninst.exe" |
1521 | + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" |
1522 | + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" |
1523 | + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\Mixxx.exe" |
1524 | + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" |
1525 | + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" |
1526 | + WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" |
1527 | + WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoModify" 1 |
1528 | + WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoRepair" 1 |
1529 | + |
1530 | +SectionEnd |
1531 | + |
1532 | +; Optional section (can be disabled by the user) |
1533 | +Section "Start Menu Shortcuts" SecStartMenu |
1534 | + |
1535 | + CreateDirectory "$SMPROGRAMS\Mixxx" |
1536 | + SetOutPath $INSTDIR |
1537 | + CreateShortCut "$SMPROGRAMS\Mixxx\Mixxx.lnk" "$INSTDIR\mixxx.exe" "" "$INSTDIR\mixxx.exe" 0 |
1538 | + CreateShortCut "$SMPROGRAMS\Mixxx\Manual.lnk" "$INSTDIR\Mixxx-Manual.pdf" "" "$INSTDIR\Mixxx-Manual.pdf" 0 |
1539 | + CreateShortCut "$SMPROGRAMS\Mixxx\Uninstall.lnk" "$INSTDIR\uninst.exe" "" "$INSTDIR\uninst.exe" 0 |
1540 | + |
1541 | +SectionEnd |
1542 | + |
1543 | +; Optional section (can be disabled by the user) |
1544 | +Section "Desktop Shortcut" SecDesktop |
1545 | + |
1546 | + SetOutPath $INSTDIR |
1547 | + CreateShortCut "$DESKTOP\Mixxx.lnk" "$INSTDIR\mixxx.exe" "" "$INSTDIR\mixxx.exe" 0 |
1548 | + |
1549 | +SectionEnd |
1550 | + |
1551 | +;-------------------------------- |
1552 | +;Descriptions |
1553 | + |
1554 | + ;Language strings |
1555 | + LangString DESC_SecMixxx ${LANG_ENGLISH} "Mixxx software." |
1556 | + LangString DESC_SecStartMenu ${LANG_ENGLISH} "Start menu shortcuts." |
1557 | + LangString DESC_SecDesktop ${LANG_ENGLISH} "Desktop shortcut." |
1558 | + |
1559 | + ;Assign language strings to sections |
1560 | + !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN |
1561 | + !insertmacro MUI_DESCRIPTION_TEXT ${SecMixxx} $(DESC_SecMixxx) |
1562 | + !insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} $(DESC_SecStartMenu) |
1563 | + !insertmacro MUI_DESCRIPTION_TEXT ${SecDesktop} $(DESC_SecDesktop) |
1564 | + !insertmacro MUI_FUNCTION_DESCRIPTION_END |
1565 | + |
1566 | + |
1567 | +;-------------------------------- |
1568 | + |
1569 | +; Uninstaller |
1570 | + |
1571 | +Function un.onUninstSuccess |
1572 | + HideWindow |
1573 | + MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." |
1574 | +FunctionEnd |
1575 | + |
1576 | +Function un.onInit |
1577 | +!insertmacro MUI_UNGETLANGUAGE |
1578 | + MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2 |
1579 | + Abort |
1580 | +FunctionEnd |
1581 | + |
1582 | +Section "Uninstall" |
1583 | + |
1584 | + ; Remove files and uninstaller |
1585 | + Delete $INSTDIR\mixxx.exe |
1586 | + Delete $INSTDIR\mixxx.log |
1587 | + Delete $INSTDIR\*.dll |
1588 | + Delete $INSTDIR\uninst.exe |
1589 | + Delete $INSTDIR\Mixxx-Manual.pdf |
1590 | + Delete $INSTDIR\LICENSE |
1591 | + Delete $INSTDIR\README |
1592 | + Delete $INSTDIR\COPYING |
1593 | + |
1594 | + ; Remove skins, keyboard, midi defs |
1595 | + Delete $INSTDIR\skins\outline\*.* |
1596 | + Delete $INSTDIR\skins\outlineClose\*.* |
1597 | + Delete $INSTDIR\skins\outlineNetbook\*.* |
1598 | + Delete $INSTDIR\skins\outlineSmall\*.* |
1599 | + Delete $INSTDIR\skins\outlineMini\*.* |
1600 | + Delete "$INSTDIR\skins\Collusion (1280)\*.*" |
1601 | + Delete "$INSTDIR\skins\Collusion (1280-WS)\*.*" |
1602 | + Delete $INSTDIR\skins\hercules\*.* |
1603 | + Delete $INSTDIR\skins\nCut\*.* |
1604 | + Delete $INSTDIR\skins\traditional\*.* |
1605 | + Delete $INSTDIR\skins\*.* |
1606 | + Delete $INSTDIR\keyboard\*.* |
1607 | + Delete $INSTDIR\midi\*.* |
1608 | + ;Delete $INSTDIR\promo\*.* |
1609 | + RMDir "$INSTDIR\skins\outline" |
1610 | + RMDir "$INSTDIR\skins\outlineNetbook" |
1611 | + RMDir "$INSTDIR\skins\outlineClose" |
1612 | + RMDir "$INSTDIR\skins\outlineSmall" |
1613 | + RMDir "$INSTDIR\skins\outlineMini" |
1614 | + RMDir "$INSTDIR\skins\Collusion (1280)" |
1615 | + RMDir "$INSTDIR\skins\Collusion (1280-WS)" |
1616 | + RMDir "$INSTDIR\skins\hercules" |
1617 | + RMDir "$INSTDIR\skins\nCut" |
1618 | + RMDir "$INSTDIR\skins\traditional" |
1619 | + RMDir "$INSTDIR\skins" |
1620 | + RMDir "$INSTDIR\midi" |
1621 | + RMDir "$INSTDIR\keyboard" |
1622 | + ;RMDir "$INSTDIR\promo" |
1623 | + |
1624 | + |
1625 | + ; Remove shortcuts, if any |
1626 | + Delete "$SMPROGRAMS\Mixxx\*.*" |
1627 | + Delete "$DESKTOP\Mixxx.lnk" |
1628 | + |
1629 | + ; Remove directories used |
1630 | + RMDir "$SMPROGRAMS\Mixxx" |
1631 | + RMDir "$INSTDIR" |
1632 | + |
1633 | + ; Remove registry keys |
1634 | + DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" |
1635 | + ;DeleteRegKey HKLM SOFTWARE\NSIS_Mixxx |
1636 | + DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}" |
1637 | + SetAutoClose true |
1638 | + |
1639 | +SectionEnd |
1640 | |
1641 | === modified file 'mixxx/src/SConscript' |
1642 | --- mixxx/src/SConscript 2009-11-10 00:33:12 +0000 |
1643 | +++ mixxx/src/SConscript 2009-11-10 00:33:16 +0000 |
1644 | @@ -1,4 +1,5 @@ |
1645 | #!/usr/bin/env python |
1646 | +# -*- coding: utf-8 -*- |
1647 | import os |
1648 | import sys |
1649 | import SCons |
1650 | @@ -216,7 +217,6 @@ |
1651 | vars.Add('script', 'Set to 1 to enable MixxxScript/QtScript Studio support.', 0) |
1652 | vars.Add('midiscript', 'Set to 1 to enable MIDI Scripting support.', 1) |
1653 | vars.Add('tonal', 'Set to 1 to enable tonal analysis', 0) |
1654 | -vars.Add('portmidi', 'Set to 1 to enable PortMidi unified MIDI backend', 0) |
1655 | vars.Add('m4a','Set to 1 to enable support for M4A audio (Apple non-drm''d music format)', 1) |
1656 | vars.Add('qdebug', 'Set to 1 to enable verbose console debug output.', 1) |
1657 | vars.Add('test', 'Set to 1 to build Mixxx test fixtures.', 0) |
1658 | @@ -326,20 +326,19 @@ |
1659 | analyserbpm.cpp |
1660 | analyserwaveform.cpp |
1661 | |
1662 | + midi/midimapping.cpp |
1663 | + midi/midiinputmappingtablemodel.cpp |
1664 | + midi/midioutputmappingtablemodel.cpp |
1665 | + midi/midichanneldelegate.cpp |
1666 | + midi/midistatusdelegate.cpp |
1667 | + midi/midinodelegate.cpp |
1668 | + midi/midioptiondelegate.cpp |
1669 | + midi/midimessage.cpp |
1670 | + midi/midiledhandler.cpp |
1671 | + |
1672 | main.cpp |
1673 | - midiobject.cpp |
1674 | - midimapping.cpp |
1675 | - midiobjectnull.cpp |
1676 | - mididevicehandler.cpp |
1677 | - midiinputmappingtablemodel.cpp |
1678 | - midioutputmappingtablemodel.cpp |
1679 | - midichanneldelegate.cpp |
1680 | - midistatusdelegate.cpp |
1681 | - midinodelegate.cpp |
1682 | - midioptiondelegate.cpp |
1683 | controlgroupdelegate.cpp |
1684 | controlvaluedelegate.cpp |
1685 | - midimessage.cpp |
1686 | mixxxcontrol.cpp |
1687 | mixxx.cpp |
1688 | mixxxview.cpp |
1689 | @@ -411,7 +410,6 @@ |
1690 | imgcolor.cpp |
1691 | |
1692 | trackinfoobject.cpp |
1693 | - midiledhandler.cpp |
1694 | sounddevice.cpp |
1695 | soundmanager.cpp |
1696 | sounddeviceportaudio.cpp |
1697 | @@ -433,17 +431,6 @@ |
1698 | #elif 'win' in platform: |
1699 | # sources += Split("""powermatewin.cpp mousewin.cpp """) |
1700 | |
1701 | -#Compile platform specific MIDI support |
1702 | -# Linux moved to the flag parsing area since it depends on the rawmidi flag |
1703 | -if 'win' in platform: |
1704 | - sources += Split("""midiobjectwin.cpp """) #Windows MIDI support |
1705 | - env.Append(CXXFLAGS = '-D__WINMIDI__') |
1706 | -elif platform == 'osx': |
1707 | - sources += Split("""midiobjectcoremidi.cpp """) #CoreMidi support for OS X |
1708 | - env.Append(CXXFLAGS = '-D__COREMIDI__') |
1709 | -elif platform == 'bsd': |
1710 | - print 'Warning: Mixxx has no support for BSD midi (yet).' #uuuuhhhh not a very good solution |
1711 | - |
1712 | #Set up the library path on Windows: |
1713 | if platform == 'win64': |
1714 | env.Append(CPPPATH='#/../mixxx-win64lib') #If you add more directories, separate them with a semicolon (;) |
1715 | @@ -456,6 +443,7 @@ |
1716 | if 'win' in platform: |
1717 | env.Append(CPPPATH='../../lib/ladspa') #If you add more directories, separate them with a semicolon (;) |
1718 | env.Append(LINKFLAGS = ['/nodefaultlib:libc.lib', '/nodefaultlib:libcd.lib', '/entry:mainCRTStartup']) |
1719 | + env.Append(LIBS='advapi32') # needed for PortMIDI |
1720 | #'/subsystem:windows', |
1721 | |
1722 | if platform == 'bsd': |
1723 | @@ -480,21 +468,6 @@ |
1724 | #if not env.GetOption('clean') and not SCons.Util.containsAny(os.sys.argv, ['-h', '--help']): |
1725 | conf = Configure(env, custom_tests = { 'CheckForPKGConfig' : CheckForPKGConfig, 'CheckForPKG' : CheckForPKG }) |
1726 | |
1727 | - |
1728 | -#PortMidi backend support |
1729 | -flags_portmidi = getFlags(env, 'portmidi', 0) |
1730 | -if int(flags_portmidi): |
1731 | - if not conf.CheckLib(['portmidi', 'libportmidi']): |
1732 | - print "Did not find portmidi or it\'s development headers, exiting!" |
1733 | - Exit(1) |
1734 | - #if not conf.CheckLib(['porttime', 'libporttime']): |
1735 | - # print "Did not find porttime or it\'s development headers, exiting!" |
1736 | - # Exit(1) |
1737 | - sources += Split("""midiobjectportmidi.cpp """); |
1738 | - env.Append(CPPDEFINES = '__PORTMIDI__') |
1739 | - |
1740 | - |
1741 | - |
1742 | #TODO: Add all of the other configure checks as custom_tests properly. |
1743 | |
1744 | # On Posix default SCons.LIBPREFIX = 'lib', on Windows default SCons.LIBPREFIX = '' |
1745 | @@ -566,6 +539,20 @@ |
1746 | #Check if FFMPEG was enabled |
1747 | CheckFFMPEG(conf, sources) |
1748 | |
1749 | +#Check for PortTime |
1750 | +if not conf.CheckLib(['porttime', 'libporttime']): |
1751 | + print "Did not find PortTime or it\'s development headers, exiting!" |
1752 | + Exit(1) |
1753 | + |
1754 | +#Check for PortMIDI |
1755 | +if not conf.CheckLib(['portmidi', 'libportmidi']): |
1756 | + print "Did not find PortMidi or it\'s development headers, exiting!" |
1757 | + Exit(1) |
1758 | + |
1759 | +sources += Split("""midi/mididevice.cpp """); |
1760 | +sources += Split("""midi/mididevicemanager.cpp """); |
1761 | +sources += Split("""midi/midideviceportmidi.cpp """); |
1762 | +env.Append(CPPDEFINES = '__PORTMIDI__') |
1763 | |
1764 | #Platform-specific checks for Linux... |
1765 | if platform == 'linux': |
1766 | @@ -611,7 +598,6 @@ |
1767 | debug=False, |
1768 | ) |
1769 | |
1770 | - |
1771 | #Check for libasound (libasound2?) (needed for ALSA seq MIDI support) |
1772 | if not conf.CheckLib('asound') and not conf.CheckForPKG('alsa', '1.0.10'): |
1773 | print "Did not find libasound (aka. libasound2), exiting!" |
1774 | @@ -1092,7 +1078,7 @@ |
1775 | print "MIDI Scripting... enabled" |
1776 | |
1777 | build_flags += 'midiscript ' |
1778 | - sources += Split("""script/midiscriptengine.cpp""") |
1779 | + sources += Split("""midi/midiscriptengine.cpp""") |
1780 | env.Append(CPPPATH = '$QTDIR/include/QtScript') |
1781 | env.Append(CPPDEFINES = '__MIDISCRIPT__') |
1782 | else: |
1783 | @@ -1215,18 +1201,6 @@ |
1784 | env.Append(CPPDEFINES = 'QT_NO_DEBUG_OUTPUT') |
1785 | print "Debugging message output... disabled" |
1786 | |
1787 | -#ALSA API selection |
1788 | -if platform == 'linux': |
1789 | - flags_rawmidi = getFlags(env, 'rawmidi', 0) |
1790 | - if int(flags_rawmidi): |
1791 | - build_flags += 'rawmidi ' |
1792 | - sources += Split("""midiobjectalsa.cpp """) #ALSA RawMIDI support for Linux |
1793 | - env.Append(CXXFLAGS = '-D__ALSAMIDI__') |
1794 | - print "ALSA API... RawMIDI" |
1795 | - else: |
1796 | - sources += Split("""midiobjectalsaseq.cpp """) #ALSA Sequencer MIDI support for Linux |
1797 | - env.Append(CXXFLAGS = '-D__ALSASEQMIDI__') |
1798 | - print "ALSA API... Sequencer" |
1799 | |
1800 | #Visual Studio 2005 hacks (MSVS Express Edition users shouldn't enable this) |
1801 | flags_msvshacks = getFlags(env, 'msvshacks', 0) |
1802 | |
1803 | === modified file 'mixxx/src/controlbeat.h' |
1804 | --- mixxx/src/controlbeat.h 2005-03-02 14:02:29 +0000 |
1805 | +++ mixxx/src/controlbeat.h 2009-11-10 00:33:16 +0000 |
1806 | @@ -20,6 +20,8 @@ |
1807 | |
1808 | #include "controlobject.h" |
1809 | #include "configobject.h" |
1810 | +#include "defs.h" |
1811 | +#include "midi/midimessage.h" |
1812 | #include <qdatetime.h> |
1813 | |
1814 | /** |
1815 | |
1816 | === modified file 'mixxx/src/controlgroupdelegate.cpp' |
1817 | --- mixxx/src/controlgroupdelegate.cpp 2009-03-19 17:17:16 +0000 |
1818 | +++ mixxx/src/controlgroupdelegate.cpp 2009-11-10 00:33:16 +0000 |
1819 | @@ -8,7 +8,7 @@ |
1820 | #include <QtCore> |
1821 | #include <QtGui> |
1822 | #include "configobject.h" |
1823 | -#include "midiinputmappingtablemodel.h" //Need this to know MIDIINPUTTABLEINDEX_CONTROLOBJECTVALUE |
1824 | +#include "midi/midiinputmappingtablemodel.h" //Need this to know MIDIINPUTTABLEINDEX_CONTROLOBJECTVALUE |
1825 | #include "controlvaluedelegate.h" |
1826 | #include "controlgroupdelegate.h" |
1827 | |
1828 | |
1829 | === modified file 'mixxx/src/controllogpotmeter.cpp' |
1830 | --- mixxx/src/controllogpotmeter.cpp 2008-03-27 05:04:43 +0000 |
1831 | +++ mixxx/src/controllogpotmeter.cpp 2009-11-10 00:33:17 +0000 |
1832 | @@ -15,6 +15,7 @@ |
1833 | * * |
1834 | ***************************************************************************/ |
1835 | |
1836 | +#include <math.h> |
1837 | #include "controllogpotmeter.h" |
1838 | |
1839 | /* -------- ------------------------------------------------------ |
1840 | |
1841 | === modified file 'mixxx/src/controlobject.h' |
1842 | --- mixxx/src/controlobject.h 2009-07-17 05:06:48 +0000 |
1843 | +++ mixxx/src/controlobject.h 2009-11-10 00:33:17 +0000 |
1844 | @@ -24,8 +24,8 @@ |
1845 | //#include <qaccel.h> |
1846 | #include <q3ptrqueue.h> |
1847 | #include <QMutex> |
1848 | +#include "midi/midimessage.h" |
1849 | #include "configobject.h" |
1850 | -#include "midiobject.h" |
1851 | #include "controlobjectthread.h" |
1852 | |
1853 | class QWidget; |
1854 | @@ -73,8 +73,8 @@ |
1855 | static ControlObject *getControl(ConfigKey key); |
1856 | /** Used to add a pointer to the corresponding ControlObjectThread of this ControlObject */ |
1857 | void addProxy(ControlObjectThread *pControlObjectThread); |
1858 | - // To get rid of a proxy when the corresponding object is being deleted for example |
1859 | - void removeProxy(ControlObjectThread *pControlObjectThread); |
1860 | + // To get rid of a proxy when the corresponding object is being deleted for example |
1861 | + void removeProxy(ControlObjectThread *pControlObjectThread); |
1862 | /** Update proxies, execep the one given a pointer to. Returns true if all updates |
1863 | * happend, otherwise false. */ |
1864 | bool updateProxies(ControlObjectThread *pProxyNoUpdate=0); |
1865 | |
1866 | === modified file 'mixxx/src/controlvaluedelegate.cpp' |
1867 | --- mixxx/src/controlvaluedelegate.cpp 2009-04-30 12:54:20 +0000 |
1868 | +++ mixxx/src/controlvaluedelegate.cpp 2009-11-10 00:33:17 +0000 |
1869 | @@ -10,7 +10,7 @@ |
1870 | #include "configobject.h" |
1871 | #include "controlgroupdelegate.h" //Need to get CONTROLGROUP_CHANNEL1_STRING, etc. |
1872 | #include "controlvaluedelegate.h" |
1873 | -#include "midiinputmappingtablemodel.h" //Need this to know MIDIINPUTTABLEINDEX_CONTROLOBJECTGROUP |
1874 | +#include "midi/midiinputmappingtablemodel.h" //Need this to know MIDIINPUTTABLEINDEX_CONTROLOBJECTGROUP |
1875 | |
1876 | //Static var declarations |
1877 | QStringList ControlValueDelegate::m_channelControlValues; |
1878 | |
1879 | === modified file 'mixxx/src/defs.h' |
1880 | --- mixxx/src/defs.h 2009-11-10 00:33:14 +0000 |
1881 | +++ mixxx/src/defs.h 2009-11-10 00:33:17 +0000 |
1882 | @@ -17,11 +17,8 @@ |
1883 | #ifndef DEFS_H |
1884 | #define DEFS_H |
1885 | |
1886 | -<<<<<<< TREE |
1887 | -#define VERSION "Trunk" |
1888 | -======= |
1889 | // Version number moved to defs_version.h to avoid lots of rebuilding |
1890 | ->>>>>>> MERGE-SOURCE |
1891 | + |
1892 | #define MIXXX_PROMO_DIR "promo" |
1893 | |
1894 | #include <math.h> |
1895 | |
1896 | === modified file 'mixxx/src/dlgmidilearning.cpp' |
1897 | --- mixxx/src/dlgmidilearning.cpp 2009-03-12 22:35:33 +0000 |
1898 | +++ mixxx/src/dlgmidilearning.cpp 2009-11-10 00:33:17 +0000 |
1899 | @@ -17,8 +17,8 @@ |
1900 | |
1901 | #include "dlgmidilearning.h" |
1902 | #include "mixxxcontrol.h" |
1903 | -#include "midimessage.h" |
1904 | -#include "midimapping.h" |
1905 | +#include "midi/midimessage.h" |
1906 | +#include "midi/midimapping.h" |
1907 | |
1908 | DlgMidiLearning::DlgMidiLearning(QWidget * parent, MidiMapping* mapping) : QDialog(parent), Ui::DlgMidiLearning() |
1909 | { |
1910 | |
1911 | === modified file 'mixxx/src/dlgmidilearning.h' |
1912 | --- mixxx/src/dlgmidilearning.h 2009-03-12 22:35:33 +0000 |
1913 | +++ mixxx/src/dlgmidilearning.h 2009-11-10 00:33:17 +0000 |
1914 | @@ -23,7 +23,7 @@ |
1915 | #include "ui_dlgmidilearning.h" |
1916 | #include "configobject.h" |
1917 | #include "mixxxcontrol.h" |
1918 | -#include "midimessage.h" |
1919 | +#include "midi/midimessage.h" |
1920 | |
1921 | class MidiMapping; |
1922 | |
1923 | |
1924 | === modified file 'mixxx/src/dlgpreferences.cpp' |
1925 | --- mixxx/src/dlgpreferences.cpp 2009-04-12 19:17:15 +0000 |
1926 | +++ mixxx/src/dlgpreferences.cpp 2009-11-10 00:33:17 +0000 |
1927 | @@ -36,7 +36,8 @@ |
1928 | #include "dlgprefrecord.h" |
1929 | #include "mixxx.h" |
1930 | #include "track.h" |
1931 | -#include "midiobject.h" |
1932 | +#include "midi/mididevicemanager.h" |
1933 | +#include "midi/mididevice.h" |
1934 | #include <QTabWidget> |
1935 | |
1936 | #include <QTabBar> |
1937 | @@ -46,10 +47,10 @@ |
1938 | |
1939 | DlgPreferences::DlgPreferences(MixxxApp * mixxx, MixxxView * view, |
1940 | SoundManager * soundman, Track *track, |
1941 | - MidiObject * midi, ConfigObject<ConfigValue> * _config) : QDialog(), Ui::DlgPreferencesDlg() |
1942 | + MidiDeviceManager * midi, ConfigObject<ConfigValue> * _config) : QDialog(), Ui::DlgPreferencesDlg() |
1943 | { |
1944 | m_pMixxx = mixxx; |
1945 | - m_pMidiObject = midi; |
1946 | + m_pMidiDeviceManager = midi; |
1947 | |
1948 | setupUi(this); |
1949 | #if QT_VERSION >= 0x040400 //setHeaderHidden is a qt4.4 addition so having it in the .ui file breaks the build on OpenBSD4.4 (FIXME: revisit this when OpenBSD4.5 comes out?) |
1950 | @@ -108,7 +109,7 @@ |
1951 | // Connections |
1952 | connect(this, SIGNAL(showDlg()), this, SLOT(slotShow())); |
1953 | connect(this, SIGNAL(closeDlg()), this, SLOT(slotHide())); |
1954 | - connect(m_pMidiObject, SIGNAL(devicesChanged()), this, SLOT(rescanMidi())); |
1955 | + connect(m_pMidiDeviceManager, SIGNAL(devicesChanged()), this, SLOT(rescanMidi())); |
1956 | |
1957 | connect(this, SIGNAL(showDlg()), wsound, SLOT(slotUpdate())); |
1958 | connect(this, SIGNAL(showDlg()), wplaylist, SLOT(slotUpdate())); |
1959 | @@ -158,7 +159,7 @@ |
1960 | } |
1961 | |
1962 | void DlgPreferences::createIcons() |
1963 | -{ |
1964 | +{ |
1965 | m_pSoundButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); |
1966 | m_pSoundButton->setIcon(0, QIcon(":/images/preferences/soundhardware.png")); |
1967 | m_pSoundButton->setText(0, tr("Sound Hardware")); |
1968 | @@ -191,7 +192,7 @@ |
1969 | m_pPlaylistButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
1970 | m_pPlaylistButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
1971 | |
1972 | - m_pControlsButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); |
1973 | + m_pControlsButton = new QTreeWidgetItem(contentsTreeWidget, QTreeWidgetItem::Type); |
1974 | m_pControlsButton->setIcon(0, QIcon(":/images/preferences/interface.png")); |
1975 | m_pControlsButton->setText(0, tr("Interface")); |
1976 | m_pControlsButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
1977 | @@ -248,66 +249,66 @@ |
1978 | if (!current) |
1979 | current = previous; |
1980 | |
1981 | - if (current == m_pSoundButton) |
1982 | - pagesWidget->setCurrentWidget(wsound); |
1983 | - else if (current == m_pPlaylistButton) |
1984 | - pagesWidget->setCurrentWidget(wplaylist); |
1985 | - else if (current == m_pControlsButton) |
1986 | - pagesWidget->setCurrentWidget(wcontrols); |
1987 | - else if (current == m_pEqButton) |
1988 | - pagesWidget->setCurrentWidget(weq); |
1989 | - else if (current == m_pCrossfaderButton) |
1990 | - pagesWidget->setCurrentWidget(wcrossfader); |
1991 | - else if (current == m_pRecordingButton) |
1992 | - pagesWidget->setCurrentWidget(wrecord); |
1993 | - else if (current == m_pBPMdetectButton) |
1994 | - pagesWidget->setCurrentWidget(wbpm); |
1995 | + if (current == m_pSoundButton) |
1996 | + pagesWidget->setCurrentWidget(wsound); |
1997 | + else if (current == m_pPlaylistButton) |
1998 | + pagesWidget->setCurrentWidget(wplaylist); |
1999 | + else if (current == m_pControlsButton) |
2000 | + pagesWidget->setCurrentWidget(wcontrols); |
2001 | + else if (current == m_pEqButton) |
2002 | + pagesWidget->setCurrentWidget(weq); |
2003 | + else if (current == m_pCrossfaderButton) |
2004 | + pagesWidget->setCurrentWidget(wcrossfader); |
2005 | + else if (current == m_pRecordingButton) |
2006 | + pagesWidget->setCurrentWidget(wrecord); |
2007 | + else if (current == m_pBPMdetectButton) |
2008 | + pagesWidget->setCurrentWidget(wbpm); |
2009 | #ifdef __VINYLCONTROL__ |
2010 | - else if (current == m_pVinylControlButton) |
2011 | - pagesWidget->setCurrentWidget(wvinylcontrol); |
2012 | + else if (current == m_pVinylControlButton) |
2013 | + pagesWidget->setCurrentWidget(wvinylcontrol); |
2014 | #endif |
2015 | #ifdef __SHOUTCAST__ |
2016 | - else if (current == m_pShoutcastButton) |
2017 | - pagesWidget->setCurrentWidget(wshoutcast); |
2018 | + else if (current == m_pShoutcastButton) |
2019 | + pagesWidget->setCurrentWidget(wshoutcast); |
2020 | #endif |
2021 | |
2022 | - //Handle selection of midi device items |
2023 | - else if (m_midiBindingsButtons.indexOf(current) >= 0) |
2024 | - { |
2025 | - int index = m_midiBindingsButtons.indexOf(current); |
2026 | - pagesWidget->setCurrentWidget(wmidiBindingsForDevice.value(index)); |
2027 | - //Manually fire this slot since it doesn't work right... |
2028 | - wmidiBindingsForDevice.value(index)->slotUpdate(); |
2029 | - } |
2030 | - |
2031 | - //If the root "MIDI Device" item is clicked, select the first MIDI device instead. |
2032 | - //If there is no first MIDI device, display a page that says so (just so we don't not change the page) |
2033 | - else if (current == m_pMIDITreeItem) |
2034 | - { |
2035 | - if (wmidiBindingsForDevice.count() > 0) |
2036 | - { |
2037 | - //Expand the MIDI subtree |
2038 | - contentsTreeWidget->setItemExpanded(m_pMIDITreeItem, true); |
2039 | - |
2040 | - /* |
2041 | - * FIXME: None of the following works right, for some reason. - Albert Feb 9/09 |
2042 | - */ |
2043 | - |
2044 | - //Select the first MIDI device |
2045 | - //contentsTreeWidget->setItemSelected(m_pMIDITreeItem, false); |
2046 | - /* |
2047 | - foreach(QTreeWidgetItem* item, contentsTreeWidget->selectedItems()) |
2048 | - { |
2049 | - contentsTreeWidget->setItemSelected(item, false); |
2050 | - }*/ |
2051 | - //contentsTreeWidget->setItemSelected(m_midiBindingsButtons.value(0), true); |
2052 | - |
2053 | - } |
2054 | - else |
2055 | - { |
2056 | - pagesWidget->setCurrentWidget(wNoMidi); |
2057 | - } |
2058 | - } |
2059 | + //Handle selection of midi device items |
2060 | + else if (m_midiBindingsButtons.indexOf(current) >= 0) |
2061 | + { |
2062 | + int index = m_midiBindingsButtons.indexOf(current); |
2063 | + pagesWidget->setCurrentWidget(wmidiBindingsForDevice.value(index)); |
2064 | + //Manually fire this slot since it doesn't work right... |
2065 | + wmidiBindingsForDevice.value(index)->slotUpdate(); |
2066 | + } |
2067 | + |
2068 | + //If the root "MIDI Device" item is clicked, select the first MIDI device instead. |
2069 | + //If there is no first MIDI device, display a page that says so (just so we don't not change the page) |
2070 | + else if (current == m_pMIDITreeItem) |
2071 | + { |
2072 | + if (wmidiBindingsForDevice.count() > 0) |
2073 | + { |
2074 | + //Expand the MIDI subtree |
2075 | + contentsTreeWidget->setItemExpanded(m_pMIDITreeItem, true); |
2076 | + |
2077 | + /* |
2078 | + * FIXME: None of the following works right, for some reason. - Albert Feb 9/09 |
2079 | + */ |
2080 | + |
2081 | + //Select the first MIDI device |
2082 | + //contentsTreeWidget->setItemSelected(m_pMIDITreeItem, false); |
2083 | + /* |
2084 | + foreach(QTreeWidgetItem* item, contentsTreeWidget->selectedItems()) |
2085 | + { |
2086 | + contentsTreeWidget->setItemSelected(item, false); |
2087 | + }*/ |
2088 | + //contentsTreeWidget->setItemSelected(m_midiBindingsButtons.value(0), true); |
2089 | + |
2090 | + } |
2091 | + else |
2092 | + { |
2093 | + pagesWidget->setCurrentWidget(wNoMidi); |
2094 | + } |
2095 | + } |
2096 | |
2097 | } |
2098 | |
2099 | @@ -315,7 +316,7 @@ |
2100 | { |
2101 | #ifdef __VINYLCONTROL__ |
2102 | pagesWidget->setCurrentWidget(wvinylcontrol); |
2103 | - contentsTreeWidget->setCurrentItem(m_pVinylControlButton); |
2104 | + contentsTreeWidget->setCurrentItem(m_pVinylControlButton); |
2105 | #endif |
2106 | } |
2107 | |
2108 | @@ -372,34 +373,50 @@ |
2109 | |
2110 | void DlgPreferences::setupMidiWidgets() |
2111 | { |
2112 | - |
2113 | - //TODO: For each MIDI device, create a MIDI dialog and put a little link to it in the treepane on the left |
2114 | - QList<QString>* deviceList = m_pMidiObject->getDeviceList(); |
2115 | - QListIterator<QString> it(*deviceList); |
2116 | - |
2117 | - while (it.hasNext()) |
2118 | - { |
2119 | - QString curDeviceName = QString(it.next()); //make a copy of it.next() so that the original list getting freed doesn't kill us |
2120 | - //qDebug() << "curDeviceName: " << curDeviceName; |
2121 | - DlgPrefMidiBindings* midiDlg = new DlgPrefMidiBindings(this, *m_pMidiObject, curDeviceName, config); |
2122 | - wmidiBindingsForDevice.append(midiDlg); |
2123 | - pagesWidget->addWidget(midiDlg); |
2124 | - connect(this, SIGNAL(showDlg()), midiDlg, SLOT(slotUpdate())); |
2125 | - connect(buttonBox, SIGNAL(accepted()), midiDlg, SLOT(slotApply())); |
2126 | - |
2127 | - QTreeWidgetItem * midiBindingsButton = new QTreeWidgetItem(QTreeWidgetItem::Type); |
2128 | - //qDebug() << curDeviceName << " QTreeWidgetItem point is " << midiBindingsButton; |
2129 | - midiBindingsButton->setIcon(0, QIcon(":/images/preferences/controllers.png")); |
2130 | - midiBindingsButton->setText(0, curDeviceName); |
2131 | - midiBindingsButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
2132 | - midiBindingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
2133 | - m_pMIDITreeItem->addChild(midiBindingsButton); |
2134 | - m_midiBindingsButtons.append(midiBindingsButton); |
2135 | - } |
2136 | + //For each MIDI device, create a MIDI dialog and put a little link to it in the treepane on the left |
2137 | + QList<MidiDevice*> deviceList = m_pMidiDeviceManager->getDeviceList(false, true); |
2138 | + QListIterator<MidiDevice*> it(deviceList); |
2139 | + |
2140 | + while (it.hasNext()) |
2141 | + { |
2142 | + MidiDevice* currentDevice = it.next(); |
2143 | + QString curDeviceName = currentDevice->getName(); |
2144 | + //qDebug() << "curDeviceName: " << curDeviceName; |
2145 | + DlgPrefMidiBindings* midiDlg = new DlgPrefMidiBindings(this, currentDevice, m_pMidiDeviceManager, config); |
2146 | + wmidiBindingsForDevice.append(midiDlg); |
2147 | + pagesWidget->addWidget(midiDlg); |
2148 | + connect(this, SIGNAL(showDlg()), midiDlg, SLOT(slotUpdate())); |
2149 | + connect(buttonBox, SIGNAL(accepted()), midiDlg, SLOT(slotApply())); |
2150 | + connect(midiDlg, SIGNAL(deviceStateChanged(DlgPrefMidiBindings*,bool)), this, SLOT(slotHighlightDevice(DlgPrefMidiBindings*,bool))); |
2151 | + |
2152 | + QTreeWidgetItem * midiBindingsButton = new QTreeWidgetItem(QTreeWidgetItem::Type); |
2153 | + //qDebug() << curDeviceName << " QTreeWidgetItem point is " << midiBindingsButton; |
2154 | + midiBindingsButton->setIcon(0, QIcon(":/images/preferences/controllers.png")); |
2155 | + midiBindingsButton->setText(0, curDeviceName); |
2156 | + midiBindingsButton->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); |
2157 | + midiBindingsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); |
2158 | + m_pMIDITreeItem->addChild(midiBindingsButton); |
2159 | + m_midiBindingsButtons.append(midiBindingsButton); |
2160 | + |
2161 | + // Set the font correctly |
2162 | + QFont temp = midiBindingsButton->font(0); |
2163 | + if (currentDevice->isOpen()) temp.setBold(true); |
2164 | + else temp.setBold(false); |
2165 | + midiBindingsButton->setFont(0,temp); |
2166 | + } |
2167 | } |
2168 | |
2169 | void DlgPreferences::slotApply() |
2170 | { |
2171 | + m_pMidiDeviceManager->saveMappings(); |
2172 | // m_pMixxx->grabKeyboard(); |
2173 | } |
2174 | |
2175 | +void DlgPreferences::slotHighlightDevice(DlgPrefMidiBindings* dialog, bool enabled) |
2176 | +{ |
2177 | + QTreeWidgetItem * midiBindingsButton = m_midiBindingsButtons.at(wmidiBindingsForDevice.indexOf(dialog)); |
2178 | + QFont temp = midiBindingsButton->font(0); |
2179 | + if (enabled) temp.setBold(true); |
2180 | + else temp.setBold(false); |
2181 | + midiBindingsButton->setFont(0,temp); |
2182 | +} |
2183 | \ No newline at end of file |
2184 | |
2185 | === modified file 'mixxx/src/dlgpreferences.h' |
2186 | --- mixxx/src/dlgpreferences.h 2009-03-02 02:48:16 +0000 |
2187 | +++ mixxx/src/dlgpreferences.h 2009-11-10 00:33:17 +0000 |
2188 | @@ -46,7 +46,7 @@ |
2189 | class DlgPrefVinyl; |
2190 | class DlgPrefShoutcast; |
2191 | class PowerMate; |
2192 | -class MidiObject; |
2193 | +class MidiDeviceManager; |
2194 | |
2195 | /** |
2196 | *@author Tue & Ken Haste Andersen |
2197 | @@ -57,7 +57,7 @@ |
2198 | Q_OBJECT |
2199 | public: |
2200 | DlgPreferences(MixxxApp *mixxx, MixxxView *view, SoundManager *soundman, |
2201 | - Track *track, MidiObject * midi, ConfigObject<ConfigValue> *config); |
2202 | + Track *track, MidiDeviceManager* midi, ConfigObject<ConfigValue> *config); |
2203 | ~DlgPreferences(); |
2204 | void createIcons(); |
2205 | public slots: |
2206 | @@ -67,6 +67,7 @@ |
2207 | void slotApply(); |
2208 | void changePage(QTreeWidgetItem *current, QTreeWidgetItem *previous); |
2209 | void showVinylControlPage(); |
2210 | + void slotHighlightDevice(DlgPrefMidiBindings* dialog, bool enabled); |
2211 | signals: |
2212 | void closeDlg(); |
2213 | void showDlg(); |
2214 | @@ -102,7 +103,7 @@ |
2215 | ConfigObject<ConfigValue> *config; |
2216 | MixxxApp *m_pMixxx; |
2217 | Track* m_pTrack; |
2218 | - MidiObject* m_pMidiObject; |
2219 | + MidiDeviceManager* m_pMidiDeviceManager; |
2220 | }; |
2221 | |
2222 | #endif |
2223 | |
2224 | === modified file 'mixxx/src/dlgprefmidibindings.cpp' |
2225 | --- mixxx/src/dlgprefmidibindings.cpp 2009-04-30 20:27:18 +0000 |
2226 | +++ mixxx/src/dlgprefmidibindings.cpp 2009-11-10 00:33:17 +0000 |
2227 | @@ -16,45 +16,44 @@ |
2228 | ***************************************************************************/ |
2229 | #include <QtGui> |
2230 | #include <QDebug> |
2231 | -#include "midiinputmappingtablemodel.h" |
2232 | -#include "midioutputmappingtablemodel.h" |
2233 | -#include "midichanneldelegate.h" |
2234 | -#include "midistatusdelegate.h" |
2235 | -#include "midinodelegate.h" |
2236 | -#include "midioptiondelegate.h" |
2237 | +#include "midi/midiinputmappingtablemodel.h" |
2238 | +#include "midi/midioutputmappingtablemodel.h" |
2239 | +#include "midi/midichanneldelegate.h" |
2240 | +#include "midi/midistatusdelegate.h" |
2241 | +#include "midi/midinodelegate.h" |
2242 | +#include "midi/midioptiondelegate.h" |
2243 | #include "controlgroupdelegate.h" |
2244 | #include "controlvaluedelegate.h" |
2245 | #include "dlgprefmidibindings.h" |
2246 | +#include "midi/mididevice.h" |
2247 | +#include "midi/mididevicemanager.h" |
2248 | #include "widget/wwidget.h" |
2249 | #include "configobject.h" |
2250 | -#include "midimapping.h" |
2251 | +#include "midi/midimapping.h" |
2252 | |
2253 | #ifdef __MIDISCRIPT__ |
2254 | -#include "script/midiscriptengine.h" |
2255 | +#include "midi/midiscriptengine.h" |
2256 | #endif |
2257 | |
2258 | -const QStringList options = (QStringList() << "Normal" << "Script-Binding" << "Invert" << "Rot64" << "Rot64Inv" |
2259 | - << "Rot64Fast" << "Diff" << "Button" << "Switch" << "HercJog" |
2260 | - << "Spread64" << "SelectKnob"); |
2261 | - |
2262 | -QStringList controKeyOptionChoices; |
2263 | - |
2264 | -const QStringList outputTypeChoices = (QStringList() << "light"); |
2265 | - |
2266 | -DlgPrefMidiBindings::DlgPrefMidiBindings(QWidget *parent, MidiObject &midi, QString deviceName, |
2267 | - ConfigObject<ConfigValue> *pConfig) : |
2268 | - QWidget(parent), Ui::DlgPrefMidiBindingsDlg(), m_rMidi(midi) { |
2269 | + |
2270 | +#define MIXXX_TEXT_NO_OUTPUT_DEVICE tr("None") |
2271 | + |
2272 | +DlgPrefMidiBindings::DlgPrefMidiBindings(QWidget *parent, MidiDevice* midiDevice, |
2273 | + MidiDeviceManager* midiDeviceManager, |
2274 | + ConfigObject<ConfigValue> *pConfig) : |
2275 | + QWidget(parent), Ui::DlgPrefMidiBindingsDlg() { |
2276 | setupUi(this); |
2277 | m_pConfig = pConfig; |
2278 | - m_deviceName = deviceName; |
2279 | + m_pMidiDevice = midiDevice; |
2280 | + m_pMidiDeviceManager = midiDeviceManager; |
2281 | |
2282 | m_pDlgMidiLearning = NULL; |
2283 | |
2284 | - labelDeviceName->setText(m_deviceName); |
2285 | + labelDeviceName->setText(m_pMidiDevice->getName()); |
2286 | |
2287 | //Tell the input mapping table widget which data model it should be viewing |
2288 | //(note that m_pInputMappingTableView is defined in the .ui file!) |
2289 | - m_pInputMappingTableView->setModel((QAbstractItemModel*)m_rMidi.getMidiMapping()->getMidiInputMappingTableModel()); |
2290 | + m_pInputMappingTableView->setModel((QAbstractItemModel*)m_pMidiDevice->getMidiMapping()->getMidiInputMappingTableModel()); |
2291 | |
2292 | m_pInputMappingTableView->setSelectionBehavior(QAbstractItemView::SelectRows); |
2293 | m_pInputMappingTableView->setSelectionMode(QAbstractItemView::ContiguousSelection); //The model won't like ExtendedSelection, probably. |
2294 | @@ -84,7 +83,7 @@ |
2295 | |
2296 | //Tell the output mapping table widget which data model it should be viewing |
2297 | //(note that m_pOutputMappingTableView is defined in the .ui file!) |
2298 | - m_pOutputMappingTableView->setModel((QAbstractItemModel*)m_rMidi.getMidiMapping()->getMidiOutputMappingTableModel()); |
2299 | + m_pOutputMappingTableView->setModel((QAbstractItemModel*)m_pMidiDevice->getMidiMapping()->getMidiOutputMappingTableModel()); |
2300 | m_pOutputMappingTableView->setSelectionBehavior(QAbstractItemView::SelectRows); |
2301 | m_pOutputMappingTableView->setSelectionMode(QAbstractItemView::ContiguousSelection); |
2302 | m_pOutputMappingTableView->verticalHeader()->hide(); |
2303 | @@ -111,14 +110,15 @@ |
2304 | connect(btnClearAllOutputBindings, SIGNAL(clicked()), this, SLOT(slotClearAllOutputBindings())); |
2305 | connect(btnRemoveOutputBinding, SIGNAL(clicked()), this, SLOT(slotRemoveOutputBinding())); |
2306 | connect(btnAddOutputBinding, SIGNAL(clicked()), this, SLOT(slotAddOutputBinding())); |
2307 | - |
2308 | - //Connect the activate button. One day this will be replaced with an "Enabled" checkbox. |
2309 | - connect(btnActivateDevice, SIGNAL(clicked()), this, SLOT(slotEnableDevice())); |
2310 | |
2311 | connect(comboBoxPreset, SIGNAL(activated(const QString&)), this, SLOT(slotLoadMidiMapping(const QString&))); |
2312 | |
2313 | //Load the list of presets into the presets combobox. |
2314 | enumeratePresets(); |
2315 | + |
2316 | + //Initialize the output device combobox |
2317 | + enumerateOutputDevices(); |
2318 | + |
2319 | } |
2320 | |
2321 | DlgPrefMidiBindings::~DlgPrefMidiBindings() { |
2322 | @@ -130,6 +130,31 @@ |
2323 | delete m_deleteMIDIInputRowAction; |
2324 | } |
2325 | |
2326 | +void DlgPrefMidiBindings::enumerateOutputDevices() |
2327 | +{ |
2328 | + comboBoxOutputDevice->clear(); |
2329 | + |
2330 | + comboBoxOutputDevice->addItem(MIXXX_TEXT_NO_OUTPUT_DEVICE); |
2331 | + |
2332 | + //For each MIDI output device, insert an item into the output device combobox. |
2333 | + QList<MidiDevice*> deviceList = m_pMidiDeviceManager->getDeviceList(true, false); |
2334 | + QListIterator<MidiDevice*> it(deviceList); |
2335 | + |
2336 | + while (it.hasNext()) |
2337 | + { |
2338 | + MidiDevice* currentDevice = it.next(); |
2339 | + QString curDeviceName = currentDevice->getName(); |
2340 | + //qDebug() << "curDeviceName: " << curDeviceName; |
2341 | + comboBoxOutputDevice->addItem(curDeviceName); |
2342 | + } |
2343 | + |
2344 | + //Assume autopairing was done and let's just show the output device combobox with the name |
2345 | + //of the input device selected for now... |
2346 | + QString currentOutputMidiDeviceName = m_pMidiDevice->getName(); |
2347 | + comboBoxOutputDevice->setCurrentIndex(comboBoxOutputDevice->findText(currentOutputMidiDeviceName)); |
2348 | + |
2349 | +} |
2350 | + |
2351 | void DlgPrefMidiBindings::enumeratePresets() |
2352 | { |
2353 | QList<QString> presetsList; |
2354 | @@ -157,12 +182,6 @@ |
2355 | comboBoxPreset->addItems(presetsList); |
2356 | } |
2357 | |
2358 | -/* loadPreset(QString) |
2359 | - * Asks MidiMapping to load a set of MIDI bindings from an XML file |
2360 | - */ |
2361 | -void DlgPrefMidiBindings::loadPreset(QString path) { |
2362 | - m_rMidi.getMidiMapping()->loadPreset(path); |
2363 | -} |
2364 | |
2365 | |
2366 | /* slotUpdate() |
2367 | @@ -171,34 +190,47 @@ |
2368 | void DlgPrefMidiBindings::slotUpdate() { |
2369 | |
2370 | //Check if the device that this dialog is for is already enabled... |
2371 | - if (m_rMidi.getOpenDevice() == m_deviceName) |
2372 | + if (m_pMidiDevice->isOpen()) |
2373 | { |
2374 | - btnActivateDevice->setEnabled(false); //Disable activate button |
2375 | + chkEnabledDevice->setCheckState(Qt::Checked); //Check the "Enabled" box |
2376 | toolBox->setEnabled(true); //Enable MIDI in/out toolbox. |
2377 | groupBoxPresets->setEnabled(true); //Enable presets group box. |
2378 | } |
2379 | else { |
2380 | - btnActivateDevice->setEnabled(true); //Enable activate button |
2381 | + chkEnabledDevice->setCheckState(Qt::Unchecked); //Uncheck the "Enabled" box |
2382 | toolBox->setEnabled(false); //Disable MIDI in/out toolbox. |
2383 | groupBoxPresets->setEnabled(false); //Disable presets group box. |
2384 | } |
2385 | + |
2386 | + //Connect the "Enabled" checkbox after the checkbox state is set |
2387 | + connect(chkEnabledDevice, SIGNAL(stateChanged(int)), this, SLOT(slotDeviceState(int))); |
2388 | } |
2389 | |
2390 | /* slotApply() |
2391 | * Called when the OK button is pressed. |
2392 | */ |
2393 | void DlgPrefMidiBindings::slotApply() { |
2394 | - /* User has pressed OK, so write the controls to the DOM, reload the MIDI |
2395 | - * bindings, and save the default XML file. */ |
2396 | - m_rMidi.getMidiMapping()->savePreset(); // use default bindings path |
2397 | - m_rMidi.getMidiMapping()->applyPreset(); |
2398 | - m_rMidi.disableMidiLearn(); |
2399 | + /* User has pressed OK, so enable or disable the device, write the controls to the DOM, and reload the MIDI |
2400 | + * bindings. */ |
2401 | + m_pMidiDevice->disableMidiLearn(); |
2402 | + if (chkEnabledDevice->isChecked()) { |
2403 | + enableDevice(); |
2404 | + m_pMidiDevice->getMidiMapping()->applyPreset(); |
2405 | + |
2406 | + //FIXME: We need some logic like this to make changing the output device work. |
2407 | + // See MidiDeviceManager::associateInputAndOutputDevices() for more info... |
2408 | + /* |
2409 | + if (comboBoxOutputDevice->currentText() != MIXXX_TEXT_NO_OUTPUT_DEVICE) |
2410 | + m_pMidiDeviceManager->associateInputAndOutputDevices(m_pMidiDevice, comboBoxOutputDevice->currentText()); |
2411 | + */ |
2412 | + } |
2413 | + else disableDevice(); |
2414 | } |
2415 | |
2416 | void DlgPrefMidiBindings::slotShowMidiLearnDialog() { |
2417 | //Note that DlgMidiLearning is set to delete itself on |
2418 | //close using the Qt::WA_DeleteOnClose attribute (so this "new" doesn't leak memory) |
2419 | - m_pDlgMidiLearning = new DlgMidiLearning(this, m_rMidi.getMidiMapping()); |
2420 | + m_pDlgMidiLearning = new DlgMidiLearning(this, m_pMidiDevice->getMidiMapping()); |
2421 | m_pDlgMidiLearning->show(); |
2422 | } |
2423 | |
2424 | @@ -211,7 +243,7 @@ |
2425 | return; |
2426 | |
2427 | //Ask for confirmation if the MIDI tables aren't empty... |
2428 | - MidiMapping* mapping = m_rMidi.getMidiMapping(); |
2429 | + MidiMapping* mapping = m_pMidiDevice->getMidiMapping(); |
2430 | if (mapping->numInputMidiMessages() > 0 || |
2431 | mapping->numOutputMixxxControls() > 0) |
2432 | { |
2433 | @@ -220,19 +252,16 @@ |
2434 | tr("Are you sure you'd like to load the " + name + " mapping?\n" |
2435 | "This will overwrite your existing MIDI mapping."), |
2436 | QMessageBox::Yes | QMessageBox::No); |
2437 | - |
2438 | + |
2439 | if (result == QMessageBox::No) { |
2440 | //Select the "..." item again in the combobox. |
2441 | comboBoxPreset->setCurrentIndex(0); |
2442 | - return; |
2443 | + return; |
2444 | } |
2445 | } |
2446 | |
2447 | QString filename = m_pConfig->getConfigPath().append("midi/") + name + MIDI_MAPPING_EXTENSION; |
2448 | - if (!filename.isNull()) { |
2449 | - loadPreset(filename); |
2450 | - m_rMidi.getMidiMapping()->applyPreset(); |
2451 | - } |
2452 | + if (!filename.isNull()) m_pMidiDevice->getMidiMapping()->loadPreset(filename, true); // It's applied on prefs close |
2453 | m_pInputMappingTableView->update(); |
2454 | |
2455 | //Select the "..." item again in the combobox. |
2456 | @@ -246,20 +275,37 @@ |
2457 | QString fileName = QFileDialog::getSaveFileName(this, |
2458 | "Export Mixxx MIDI Bindings", m_pConfig->getConfigPath().append("midi/"), |
2459 | "Preset Files (*.midi.xml)"); |
2460 | - if (!fileName.isNull()) m_rMidi.getMidiMapping()->savePreset(fileName); |
2461 | -} |
2462 | - |
2463 | -void DlgPrefMidiBindings::slotEnableDevice() |
2464 | -{ |
2465 | - //Just tell MidiObject to close the old device and open this device |
2466 | - m_rMidi.devClose(); |
2467 | - m_rMidi.devOpen(m_deviceName); |
2468 | - m_pConfig->set(ConfigKey("[Midi]","Device"), m_deviceName); |
2469 | - btnActivateDevice->setEnabled(false); |
2470 | - toolBox->setEnabled(true); //Enable MIDI in/out toolbox. |
2471 | - groupBoxPresets->setEnabled(true); //Enable presets group box. |
2472 | - |
2473 | - //TODO: Should probably check if devOpen() actually succeeded. |
2474 | + if (!fileName.isNull()) m_pMidiDevice->getMidiMapping()->savePreset(fileName); |
2475 | +} |
2476 | + |
2477 | +void DlgPrefMidiBindings::slotDeviceState(int state) { |
2478 | + if (state == Qt::Checked) { |
2479 | + toolBox->setEnabled(true); //Enable MIDI in/out toolbox. |
2480 | + groupBoxPresets->setEnabled(true); //Enable presets group box. |
2481 | + emit deviceStateChanged(this,true); // Set tree item text to bold |
2482 | + } |
2483 | + else { |
2484 | + toolBox->setEnabled(false); //Disable MIDI in/out toolbox. |
2485 | + groupBoxPresets->setEnabled(false); //Disable presets group box. |
2486 | + emit deviceStateChanged(this,false); // Set tree item text to not bold |
2487 | + } |
2488 | +} |
2489 | + |
2490 | +void DlgPrefMidiBindings::enableDevice() |
2491 | +{ |
2492 | + m_pMidiDevice->close(); |
2493 | + m_pMidiDevice->open(); |
2494 | + m_pConfig->set(ConfigKey("[Midi]", m_pMidiDevice->getName().replace(" ", "_")), 1); |
2495 | + |
2496 | + //TODO: Should probably check if open() actually succeeded. |
2497 | +} |
2498 | + |
2499 | +void DlgPrefMidiBindings::disableDevice() |
2500 | +{ |
2501 | + m_pMidiDevice->close(); |
2502 | + m_pConfig->set(ConfigKey("[Midi]", m_pMidiDevice->getName().replace(" ", "_")), 0); |
2503 | + |
2504 | + //TODO: Should probably check if close() actually succeeded. |
2505 | } |
2506 | |
2507 | void DlgPrefMidiBindings::slotAddInputBinding() |
2508 | @@ -296,40 +342,40 @@ |
2509 | MixxxControl mixxxControl(controlGroup, controlValue); |
2510 | MidiMessage message; |
2511 | |
2512 | - while (m_rMidi.getMidiMapping()->isMidiMessageMapped(message)) |
2513 | + while (m_pMidiDevice->getMidiMapping()->isMidiMessageMapped(message)) |
2514 | { |
2515 | message.setMidiNo(message.getMidiNo() + 1); |
2516 | if (message.getMidiNo() >= 127) //If the table is full, then overwrite something... |
2517 | break; |
2518 | } |
2519 | - m_rMidi.getMidiMapping()->setInputMidiMapping(message, mixxxControl); |
2520 | + m_pMidiDevice->getMidiMapping()->setInputMidiMapping(message, mixxxControl); |
2521 | } |
2522 | |
2523 | void DlgPrefMidiBindings::slotRemoveInputBinding() |
2524 | { |
2525 | - QModelIndexList selectedIndices = m_pInputMappingTableView->selectionModel()->selectedRows(); |
2526 | - if (selectedIndices.size() > 0) |
2527 | - { |
2528 | - MidiInputMappingTableModel* tableModel = dynamic_cast<MidiInputMappingTableModel*>(m_pInputMappingTableView->model()); |
2529 | - if (tableModel) { |
2530 | - |
2531 | - QModelIndex curIndex; |
2532 | - //The model indices are sorted so that we remove the rows from the table |
2533 | + QModelIndexList selectedIndices = m_pInputMappingTableView->selectionModel()->selectedRows(); |
2534 | + if (selectedIndices.size() > 0) |
2535 | + { |
2536 | + MidiInputMappingTableModel* tableModel = dynamic_cast<MidiInputMappingTableModel*>(m_pInputMappingTableView->model()); |
2537 | + if (tableModel) { |
2538 | + |
2539 | + QModelIndex curIndex; |
2540 | + //The model indices are sorted so that we remove the rows from the table |
2541 | //in ascending order. This is necessary because if row A is above row B in |
2542 | //the table, and you remove row A, the model index for row B will change. |
2543 | //Sorting the indices first means we don't have to worry about this. |
2544 | qSort(selectedIndices); |
2545 | |
2546 | //Going through the model indices in descending order (see above comment for explanation). |
2547 | - QListIterator<QModelIndex> it(selectedIndices); |
2548 | - it.toBack(); |
2549 | - while (it.hasPrevious()) |
2550 | - { |
2551 | - curIndex = it.previous(); |
2552 | - tableModel->removeRow(curIndex.row()); |
2553 | - } |
2554 | - } |
2555 | - } |
2556 | + QListIterator<QModelIndex> it(selectedIndices); |
2557 | + it.toBack(); |
2558 | + while (it.hasPrevious()) |
2559 | + { |
2560 | + curIndex = it.previous(); |
2561 | + tableModel->removeRow(curIndex.row()); |
2562 | + } |
2563 | + } |
2564 | + } |
2565 | } |
2566 | |
2567 | void DlgPrefMidiBindings::slotClearAllInputBindings() { |
2568 | @@ -349,35 +395,35 @@ |
2569 | void DlgPrefMidiBindings::slotAddOutputBinding() { |
2570 | qDebug() << "STUB: DlgPrefMidiBindings::slotAddOutputBinding()"; |
2571 | |
2572 | - m_rMidi.getMidiMapping()->setOutputMidiMapping(MixxxControl(), MidiMessage()); |
2573 | + m_pMidiDevice->getMidiMapping()->setOutputMidiMapping(MixxxControl(), MidiMessage()); |
2574 | } |
2575 | |
2576 | void DlgPrefMidiBindings::slotRemoveOutputBinding() |
2577 | { |
2578 | - QModelIndexList selectedIndices = m_pOutputMappingTableView->selectionModel()->selectedRows(); |
2579 | - if (selectedIndices.size() > 0) |
2580 | - { |
2581 | - MidiOutputMappingTableModel* tableModel = |
2582 | - dynamic_cast<MidiOutputMappingTableModel*>(m_pOutputMappingTableView->model()); |
2583 | - if (tableModel) { |
2584 | - QModelIndex curIndex; |
2585 | - //The model indices are sorted so that we remove the rows from the table |
2586 | + QModelIndexList selectedIndices = m_pOutputMappingTableView->selectionModel()->selectedRows(); |
2587 | + if (selectedIndices.size() > 0) |
2588 | + { |
2589 | + MidiOutputMappingTableModel* tableModel = |
2590 | + dynamic_cast<MidiOutputMappingTableModel*>(m_pOutputMappingTableView->model()); |
2591 | + if (tableModel) { |
2592 | + QModelIndex curIndex; |
2593 | + //The model indices are sorted so that we remove the rows from the table |
2594 | //in ascending order. This is necessary because if row A is above row B in |
2595 | //the table, and you remove row A, the model index for row B will change. |
2596 | //Sorting the indices first means we don't have to worry about this. |
2597 | //qSort(selectedIndices); |
2598 | |
2599 | //Going through the model indices in descending order (see above comment for explanation). |
2600 | - QListIterator<QModelIndex> it(selectedIndices); |
2601 | - it.toBack(); |
2602 | - while (it.hasPrevious()) |
2603 | - { |
2604 | - curIndex = it.previous(); |
2605 | - qDebug() << "Dlg: removing row" << curIndex.row(); |
2606 | - tableModel->removeRow(curIndex.row()); |
2607 | - } |
2608 | - } |
2609 | - } |
2610 | + QListIterator<QModelIndex> it(selectedIndices); |
2611 | + it.toBack(); |
2612 | + while (it.hasPrevious()) |
2613 | + { |
2614 | + curIndex = it.previous(); |
2615 | + qDebug() << "Dlg: removing row" << curIndex.row(); |
2616 | + tableModel->removeRow(curIndex.row()); |
2617 | + } |
2618 | + } |
2619 | + } |
2620 | } |
2621 | |
2622 | void DlgPrefMidiBindings::slotClearAllOutputBindings() { |
2623 | |
2624 | === modified file 'mixxx/src/dlgprefmidibindings.h' |
2625 | --- mixxx/src/dlgprefmidibindings.h 2009-04-11 20:16:51 +0000 |
2626 | +++ mixxx/src/dlgprefmidibindings.h 2009-11-10 00:33:17 +0000 |
2627 | @@ -21,7 +21,6 @@ |
2628 | #include "ui_dlgprefmidibindingsdlg.h" |
2629 | #include "dlgmidilearning.h" |
2630 | #include "configobject.h" |
2631 | -#include "midiobject.h" |
2632 | |
2633 | //Forward declarations |
2634 | class MidiChannelDelegate; |
2635 | @@ -30,11 +29,14 @@ |
2636 | class MidiOptionDelegate; |
2637 | class ControlGroupDelegate; |
2638 | class ControlValueDelegate; |
2639 | +class MidiDevice; |
2640 | +class MidiDeviceManager; |
2641 | |
2642 | class DlgPrefMidiBindings : public QWidget, public Ui::DlgPrefMidiBindingsDlg { |
2643 | Q_OBJECT |
2644 | public: |
2645 | - DlgPrefMidiBindings(QWidget *parent, MidiObject &midi, QString deviceName, |
2646 | + DlgPrefMidiBindings(QWidget *parent, MidiDevice* midiDevice, |
2647 | + MidiDeviceManager* midiDeviceManager, |
2648 | ConfigObject<ConfigValue> *pConfig); |
2649 | ~DlgPrefMidiBindings(); |
2650 | |
2651 | @@ -45,7 +47,7 @@ |
2652 | void slotShowMidiLearnDialog(); |
2653 | void slotLoadMidiMapping(const QString &name); |
2654 | void slotExportXML(); |
2655 | - void slotEnableDevice(); |
2656 | + void slotDeviceState(int state); |
2657 | |
2658 | //Input bindings |
2659 | void slotClearAllInputBindings(); |
2660 | @@ -56,15 +58,20 @@ |
2661 | void slotAddOutputBinding(); |
2662 | void slotClearAllOutputBindings(); |
2663 | void slotRemoveOutputBinding(); |
2664 | + |
2665 | +signals: |
2666 | + void deviceStateChanged(DlgPrefMidiBindings*, bool); |
2667 | |
2668 | private: |
2669 | void setRowBackground(int row, QColor color); |
2670 | - void loadPreset(QString path); |
2671 | void savePreset(QString path); |
2672 | void enumeratePresets(); |
2673 | + void enumerateOutputDevices(); |
2674 | |
2675 | + void enableDevice(); |
2676 | + void disableDevice(); |
2677 | + |
2678 | int currentGroupRow; |
2679 | - MidiObject &m_rMidi; |
2680 | MidiChannelDelegate* m_pMidiChannelDelegate; |
2681 | MidiStatusDelegate* m_pMidiStatusDelegate; |
2682 | MidiNoDelegate* m_pMidiNoDelegate; |
2683 | @@ -73,7 +80,8 @@ |
2684 | ControlValueDelegate* m_pControlValueDelegate; |
2685 | QAction* m_deleteMIDIInputRowAction; /** Used for setting up the shortcut for delete button */ |
2686 | ConfigObject<ConfigValue> *m_pConfig; |
2687 | - QString m_deviceName; |
2688 | + MidiDevice* m_pMidiDevice; |
2689 | + MidiDeviceManager* m_pMidiDeviceManager; |
2690 | DlgMidiLearning* m_pDlgMidiLearning; |
2691 | }; |
2692 | |
2693 | |
2694 | === modified file 'mixxx/src/dlgprefmidibindingsdlg.ui' |
2695 | --- mixxx/src/dlgprefmidibindingsdlg.ui 2009-04-11 21:29:07 +0000 |
2696 | +++ mixxx/src/dlgprefmidibindingsdlg.ui 2009-11-10 00:33:17 +0000 |
2697 | @@ -1,7 +1,8 @@ |
2698 | -<ui version="4.0" > |
2699 | +<?xml version="1.0" encoding="UTF-8"?> |
2700 | +<ui version="4.0"> |
2701 | <class>DlgPrefMidiBindingsDlg</class> |
2702 | - <widget class="QWidget" name="DlgPrefMidiBindingsDlg" > |
2703 | - <property name="geometry" > |
2704 | + <widget class="QWidget" name="DlgPrefMidiBindingsDlg"> |
2705 | + <property name="geometry"> |
2706 | <rect> |
2707 | <x>0</x> |
2708 | <y>0</y> |
2709 | @@ -9,30 +10,30 @@ |
2710 | <height>436</height> |
2711 | </rect> |
2712 | </property> |
2713 | - <property name="windowTitle" > |
2714 | + <property name="windowTitle"> |
2715 | <string>Dialog</string> |
2716 | </property> |
2717 | - <layout class="QGridLayout" name="gridLayout_4" > |
2718 | - <item row="0" column="0" > |
2719 | - <widget class="QLabel" name="labelDeviceName" > |
2720 | - <property name="font" > |
2721 | + <layout class="QGridLayout" name="gridLayout_4"> |
2722 | + <item row="0" column="0"> |
2723 | + <widget class="QLabel" name="labelDeviceName"> |
2724 | + <property name="font"> |
2725 | <font> |
2726 | <pointsize>14</pointsize> |
2727 | <weight>75</weight> |
2728 | <bold>true</bold> |
2729 | </font> |
2730 | </property> |
2731 | - <property name="text" > |
2732 | + <property name="text"> |
2733 | <string>Your Device Name</string> |
2734 | </property> |
2735 | </widget> |
2736 | </item> |
2737 | - <item rowspan="2" row="0" column="1" > |
2738 | - <spacer name="horizontalSpacer" > |
2739 | - <property name="orientation" > |
2740 | + <item row="0" column="2" rowspan="2"> |
2741 | + <spacer name="horizontalSpacer"> |
2742 | + <property name="orientation"> |
2743 | <enum>Qt::Horizontal</enum> |
2744 | </property> |
2745 | - <property name="sizeHint" stdset="0" > |
2746 | + <property name="sizeHint" stdset="0"> |
2747 | <size> |
2748 | <width>115</width> |
2749 | <height>20</height> |
2750 | @@ -40,136 +41,150 @@ |
2751 | </property> |
2752 | </spacer> |
2753 | </item> |
2754 | - <item rowspan="2" row="0" column="2" > |
2755 | - <widget class="QGroupBox" name="groupBoxPresets" > |
2756 | - <property name="title" > |
2757 | + <item row="0" column="3" rowspan="2"> |
2758 | + <widget class="QGroupBox" name="groupBoxPresets"> |
2759 | + <property name="title"> |
2760 | <string/> |
2761 | </property> |
2762 | - <property name="flat" > |
2763 | + <property name="flat"> |
2764 | <bool>true</bool> |
2765 | </property> |
2766 | - <property name="checkable" > |
2767 | + <property name="checkable"> |
2768 | <bool>false</bool> |
2769 | </property> |
2770 | - <layout class="QGridLayout" name="gridLayout" > |
2771 | - <item row="0" column="0" > |
2772 | - <widget class="QLabel" name="label" > |
2773 | - <property name="text" > |
2774 | + <layout class="QGridLayout" name="gridLayout"> |
2775 | + <item row="1" column="1"> |
2776 | + <widget class="QLabel" name="label"> |
2777 | + <property name="text"> |
2778 | <string>Load Preset:</string> |
2779 | </property> |
2780 | - <property name="alignment" > |
2781 | + <property name="alignment"> |
2782 | <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> |
2783 | </property> |
2784 | </widget> |
2785 | </item> |
2786 | - <item row="0" column="1" > |
2787 | - <widget class="QComboBox" name="comboBoxPreset" > |
2788 | - <property name="sizePolicy" > |
2789 | - <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > |
2790 | + <item row="1" column="2"> |
2791 | + <widget class="QComboBox" name="comboBoxPreset"> |
2792 | + <property name="sizePolicy"> |
2793 | + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
2794 | <horstretch>0</horstretch> |
2795 | <verstretch>0</verstretch> |
2796 | </sizepolicy> |
2797 | </property> |
2798 | </widget> |
2799 | </item> |
2800 | - <item row="0" column="2" > |
2801 | - <widget class="QPushButton" name="btnExportXML" > |
2802 | - <property name="styleSheet" > |
2803 | - <string notr="true" /> |
2804 | + <item row="1" column="3"> |
2805 | + <widget class="QPushButton" name="btnExportXML"> |
2806 | + <property name="styleSheet"> |
2807 | + <string notr="true"/> |
2808 | </property> |
2809 | - <property name="text" > |
2810 | + <property name="text"> |
2811 | <string>Export</string> |
2812 | </property> |
2813 | </widget> |
2814 | </item> |
2815 | + <item row="2" column="2"> |
2816 | + <widget class="QComboBox" name="comboBoxOutputDevice"> |
2817 | + <property name="enabled"> |
2818 | + <bool>false</bool> |
2819 | + </property> |
2820 | + </widget> |
2821 | + </item> |
2822 | + <item row="2" column="1"> |
2823 | + <widget class="QLabel" name="label_2"> |
2824 | + <property name="text"> |
2825 | + <string>Output:</string> |
2826 | + </property> |
2827 | + </widget> |
2828 | + </item> |
2829 | </layout> |
2830 | </widget> |
2831 | </item> |
2832 | - <item row="1" column="0" > |
2833 | - <widget class="QPushButton" name="btnActivateDevice" > |
2834 | - <property name="text" > |
2835 | - <string>Activate</string> |
2836 | + <item row="1" column="0"> |
2837 | + <widget class="QCheckBox" name="chkEnabledDevice"> |
2838 | + <property name="text"> |
2839 | + <string>Enabled</string> |
2840 | </property> |
2841 | </widget> |
2842 | </item> |
2843 | - <item row="2" column="0" colspan="3" > |
2844 | - <widget class="QToolBox" name="toolBox" > |
2845 | - <property name="frameShape" > |
2846 | + <item row="2" column="0" colspan="4"> |
2847 | + <widget class="QToolBox" name="toolBox"> |
2848 | + <property name="frameShape"> |
2849 | <enum>QFrame::NoFrame</enum> |
2850 | </property> |
2851 | - <property name="currentIndex" > |
2852 | + <property name="currentIndex"> |
2853 | <number>0</number> |
2854 | </property> |
2855 | - <widget class="QWidget" name="page" > |
2856 | - <property name="geometry" > |
2857 | + <widget class="QWidget" name="page"> |
2858 | + <property name="geometry"> |
2859 | <rect> |
2860 | <x>0</x> |
2861 | <y>0</y> |
2862 | <width>580</width> |
2863 | - <height>287</height> |
2864 | + <height>252</height> |
2865 | </rect> |
2866 | </property> |
2867 | - <attribute name="label" > |
2868 | + <attribute name="label"> |
2869 | <string>MIDI Input</string> |
2870 | </attribute> |
2871 | - <layout class="QGridLayout" name="gridLayout_2" > |
2872 | - <item row="1" column="0" colspan="2" > |
2873 | - <widget class="QTableView" name="m_pInputMappingTableView" /> |
2874 | + <layout class="QGridLayout" name="gridLayout_2"> |
2875 | + <item row="1" column="0" colspan="2"> |
2876 | + <widget class="QTableView" name="m_pInputMappingTableView"/> |
2877 | </item> |
2878 | - <item row="2" column="0" colspan="2" > |
2879 | - <widget class="QGroupBox" name="groupBoxInputManagement" > |
2880 | - <property name="minimumSize" > |
2881 | + <item row="2" column="0" colspan="2"> |
2882 | + <widget class="QGroupBox" name="groupBoxInputManagement"> |
2883 | + <property name="minimumSize"> |
2884 | <size> |
2885 | <width>300</width> |
2886 | <height>0</height> |
2887 | </size> |
2888 | </property> |
2889 | - <property name="title" > |
2890 | + <property name="title"> |
2891 | <string>Controls</string> |
2892 | </property> |
2893 | - <layout class="QHBoxLayout" name="horizontalLayout_2" > |
2894 | + <layout class="QHBoxLayout" name="horizontalLayout_2"> |
2895 | <item> |
2896 | - <widget class="QPushButton" name="btnAddInputBinding" > |
2897 | - <property name="styleSheet" > |
2898 | - <string notr="true" /> |
2899 | + <widget class="QPushButton" name="btnAddInputBinding"> |
2900 | + <property name="styleSheet"> |
2901 | + <string notr="true"/> |
2902 | </property> |
2903 | - <property name="text" > |
2904 | + <property name="text"> |
2905 | <string>Add</string> |
2906 | </property> |
2907 | </widget> |
2908 | </item> |
2909 | <item> |
2910 | - <widget class="QPushButton" name="btnRemoveInputBinding" > |
2911 | - <property name="styleSheet" > |
2912 | - <string notr="true" /> |
2913 | + <widget class="QPushButton" name="btnRemoveInputBinding"> |
2914 | + <property name="styleSheet"> |
2915 | + <string notr="true"/> |
2916 | </property> |
2917 | - <property name="text" > |
2918 | + <property name="text"> |
2919 | <string>Remove</string> |
2920 | </property> |
2921 | </widget> |
2922 | </item> |
2923 | <item> |
2924 | - <widget class="QPushButton" name="btnMidiLearnWizard" > |
2925 | - <property name="styleSheet" > |
2926 | - <string notr="true" /> |
2927 | + <widget class="QPushButton" name="btnMidiLearnWizard"> |
2928 | + <property name="styleSheet"> |
2929 | + <string notr="true"/> |
2930 | </property> |
2931 | - <property name="text" > |
2932 | + <property name="text"> |
2933 | <string>MIDI Learning Wizard</string> |
2934 | </property> |
2935 | - <property name="checkable" > |
2936 | + <property name="checkable"> |
2937 | <bool>false</bool> |
2938 | </property> |
2939 | - <property name="checked" > |
2940 | + <property name="checked"> |
2941 | <bool>false</bool> |
2942 | </property> |
2943 | </widget> |
2944 | </item> |
2945 | <item> |
2946 | - <spacer name="horizontalSpacer_3" > |
2947 | - <property name="orientation" > |
2948 | + <spacer name="horizontalSpacer_3"> |
2949 | + <property name="orientation"> |
2950 | <enum>Qt::Horizontal</enum> |
2951 | </property> |
2952 | - <property name="sizeHint" stdset="0" > |
2953 | + <property name="sizeHint" stdset="0"> |
2954 | <size> |
2955 | <width>219</width> |
2956 | <height>20</height> |
2957 | @@ -178,14 +193,14 @@ |
2958 | </spacer> |
2959 | </item> |
2960 | <item> |
2961 | - <widget class="QPushButton" name="btnClearAllInputBindings" > |
2962 | - <property name="autoFillBackground" > |
2963 | + <widget class="QPushButton" name="btnClearAllInputBindings"> |
2964 | + <property name="autoFillBackground"> |
2965 | <bool>false</bool> |
2966 | </property> |
2967 | - <property name="styleSheet" > |
2968 | - <string notr="true" /> |
2969 | + <property name="styleSheet"> |
2970 | + <string notr="true"/> |
2971 | </property> |
2972 | - <property name="text" > |
2973 | + <property name="text"> |
2974 | <string>Clear All</string> |
2975 | </property> |
2976 | </widget> |
2977 | @@ -195,55 +210,55 @@ |
2978 | </item> |
2979 | </layout> |
2980 | </widget> |
2981 | - <widget class="QWidget" name="page_2" > |
2982 | - <property name="geometry" > |
2983 | + <widget class="QWidget" name="page_2"> |
2984 | + <property name="geometry"> |
2985 | <rect> |
2986 | <x>0</x> |
2987 | <y>0</y> |
2988 | - <width>580</width> |
2989 | - <height>287</height> |
2990 | + <width>317</width> |
2991 | + <height>174</height> |
2992 | </rect> |
2993 | </property> |
2994 | - <attribute name="label" > |
2995 | + <attribute name="label"> |
2996 | <string>MIDI Output</string> |
2997 | </attribute> |
2998 | - <layout class="QGridLayout" name="gridLayout_3" > |
2999 | - <item row="0" column="0" > |
3000 | - <widget class="QTableView" name="m_pOutputMappingTableView" > |
3001 | - <property name="enabled" > |
3002 | + <layout class="QGridLayout" name="gridLayout_3"> |
3003 | + <item row="0" column="0"> |
3004 | + <widget class="QTableView" name="m_pOutputMappingTableView"> |
3005 | + <property name="enabled"> |
3006 | <bool>true</bool> |
3007 | </property> |
3008 | </widget> |
3009 | </item> |
3010 | - <item row="1" column="0" > |
3011 | - <widget class="QGroupBox" name="groupBoxOutputs" > |
3012 | - <property name="title" > |
3013 | + <item row="1" column="0"> |
3014 | + <widget class="QGroupBox" name="groupBoxOutputs"> |
3015 | + <property name="title"> |
3016 | <string>Outputs</string> |
3017 | </property> |
3018 | - <layout class="QHBoxLayout" name="horizontalLayout" > |
3019 | + <layout class="QHBoxLayout" name="horizontalLayout"> |
3020 | <item> |
3021 | - <widget class="QPushButton" name="btnAddOutputBinding" > |
3022 | - <property name="enabled" > |
3023 | + <widget class="QPushButton" name="btnAddOutputBinding"> |
3024 | + <property name="enabled"> |
3025 | <bool>false</bool> |
3026 | </property> |
3027 | - <property name="text" > |
3028 | + <property name="text"> |
3029 | <string>Add</string> |
3030 | </property> |
3031 | </widget> |
3032 | </item> |
3033 | <item> |
3034 | - <widget class="QPushButton" name="btnRemoveOutputBinding" > |
3035 | - <property name="text" > |
3036 | + <widget class="QPushButton" name="btnRemoveOutputBinding"> |
3037 | + <property name="text"> |
3038 | <string>Remove</string> |
3039 | </property> |
3040 | </widget> |
3041 | </item> |
3042 | <item> |
3043 | - <spacer name="horizontalSpacer_2" > |
3044 | - <property name="orientation" > |
3045 | + <spacer name="horizontalSpacer_2"> |
3046 | + <property name="orientation"> |
3047 | <enum>Qt::Horizontal</enum> |
3048 | </property> |
3049 | - <property name="sizeHint" stdset="0" > |
3050 | + <property name="sizeHint" stdset="0"> |
3051 | <size> |
3052 | <width>219</width> |
3053 | <height>20</height> |
3054 | @@ -252,8 +267,8 @@ |
3055 | </spacer> |
3056 | </item> |
3057 | <item> |
3058 | - <widget class="QPushButton" name="btnClearAllOutputBindings" > |
3059 | - <property name="text" > |
3060 | + <widget class="QPushButton" name="btnClearAllOutputBindings"> |
3061 | + <property name="text"> |
3062 | <string>Clear All</string> |
3063 | </property> |
3064 | </widget> |
3065 | |
3066 | === modified file 'mixxx/src/engine/enginesidechain.h' |
3067 | --- mixxx/src/engine/enginesidechain.h 2009-01-24 04:39:32 +0000 |
3068 | +++ mixxx/src/engine/enginesidechain.h 2009-11-10 00:33:17 +0000 |
3069 | @@ -17,6 +17,7 @@ |
3070 | #ifndef ENGINESIDECHAIN_H |
3071 | #define ENGINESIDECHAIN_H |
3072 | |
3073 | +#include <QtCore> |
3074 | #include "defs.h" |
3075 | #include "configobject.h" |
3076 | #include "controlobject.h" |
3077 | |
3078 | === modified file 'mixxx/src/input.cpp' |
3079 | --- mixxx/src/input.cpp 2009-02-10 23:42:19 +0000 |
3080 | +++ mixxx/src/input.cpp 2009-11-10 00:33:17 +0000 |
3081 | @@ -20,7 +20,6 @@ |
3082 | #include "controlobject.h" |
3083 | #include "controlobjectthreadmain.h" |
3084 | #include "controlobjectthread.h" |
3085 | -#include "midiobject.h" |
3086 | #include "mathstuff.h" |
3087 | |
3088 | // TODO: Investigate the use of ControlObjectThread vs. ControlObjectThreadMain |
3089 | |
3090 | === added directory 'mixxx/src/midi' |
3091 | === renamed file 'mixxx/src/midichanneldelegate.cpp' => 'mixxx/src/midi/midichanneldelegate.cpp' |
3092 | === renamed file 'mixxx/src/midichanneldelegate.h' => 'mixxx/src/midi/midichanneldelegate.h' |
3093 | === added file 'mixxx/src/midi/mididevice.cpp' |
3094 | --- mixxx/src/midi/mididevice.cpp 1970-01-01 00:00:00 +0000 |
3095 | +++ mixxx/src/midi/mididevice.cpp 2009-11-10 00:33:17 +0000 |
3096 | @@ -0,0 +1,201 @@ |
3097 | +/*************************************************************************** |
3098 | + mididevice.cpp |
3099 | + MIDI Device Class |
3100 | + ------------------- |
3101 | + begin : Thu Dec 18 2008 |
3102 | + copyright : (C) 2008 Albert Santoni |
3103 | + email : alberts@mixxx.org |
3104 | + |
3105 | +***************************************************************************/ |
3106 | + |
3107 | +/*************************************************************************** |
3108 | +* * |
3109 | +* This program is free software; you can redistribute it and/or modify * |
3110 | +* it under the terms of the GNU General Public License as published by * |
3111 | +* the Free Software Foundation; either version 2 of the License, or * |
3112 | +* (at your option) any later version. * |
3113 | +* * |
3114 | +***************************************************************************/ |
3115 | + |
3116 | +#include <qapplication.h> // For command line arguments |
3117 | +#include "mididevice.h" |
3118 | +#include "midimapping.h" |
3119 | +#include "midimessage.h" |
3120 | +#include "mixxxcontrol.h" |
3121 | +#include "controlobject.h" |
3122 | + |
3123 | +#ifdef __MIDISCRIPT__ |
3124 | +#include "midiscriptengine.h" |
3125 | +#endif |
3126 | + |
3127 | +static QString toHex(QString numberStr) { |
3128 | + return "0x" + QString("0" + QString::number(numberStr.toUShort(), 16).toUpper()).right(2); |
3129 | +} |
3130 | + |
3131 | +MidiDevice::MidiDevice(MidiMapping* mapping) : QObject() |
3132 | +{ |
3133 | + m_bIsOutputDevice = false; |
3134 | + m_bIsInputDevice = false; |
3135 | + m_pMidiMapping = mapping; |
3136 | + m_pCorrespondingOutputDevice = NULL; |
3137 | + m_bIsOpen = false; |
3138 | + m_bMidiLearn = false; |
3139 | + |
3140 | + if (m_pMidiMapping == NULL) { |
3141 | + m_pMidiMapping = new MidiMapping(this); |
3142 | + m_pMidiMapping->setName(m_strDeviceName); |
3143 | + } |
3144 | + |
3145 | + // Get --midiDebug command line option |
3146 | + QStringList commandLineArgs = QApplication::arguments(); |
3147 | + m_midiDebug = commandLineArgs.indexOf("--midiDebug"); |
3148 | + |
3149 | + connect(m_pMidiMapping, SIGNAL(midiLearningStarted()), this, SLOT(enableMidiLearn())); |
3150 | + connect(m_pMidiMapping, SIGNAL(midiLearningFinished()), this, SLOT(disableMidiLearn())); |
3151 | + |
3152 | +} |
3153 | + |
3154 | +MidiDevice::~MidiDevice() |
3155 | +{ |
3156 | + qDebug() << "MidiDevice: Deleting MidiMapping..."; |
3157 | + delete m_pMidiMapping; |
3158 | + |
3159 | +} |
3160 | + |
3161 | +void MidiDevice::startup() |
3162 | +{ |
3163 | +#ifdef __MIDISCRIPT__ |
3164 | + m_pMidiMapping->startupScriptEngine(); |
3165 | +#endif |
3166 | +} |
3167 | + |
3168 | +void MidiDevice::shutdown() |
3169 | +{ |
3170 | +#ifdef __MIDISCRIPT__ |
3171 | + m_pMidiMapping->shutdownScriptEngine(); |
3172 | +#endif |
3173 | +} |
3174 | + |
3175 | +void MidiDevice::setMidiMapping(MidiMapping* mapping) |
3176 | +{ |
3177 | + m_pMidiMapping = mapping; |
3178 | + |
3179 | + if (m_pCorrespondingOutputDevice) |
3180 | + { |
3181 | + m_pCorrespondingOutputDevice->setMidiMapping(m_pMidiMapping); |
3182 | + } |
3183 | +} |
3184 | + |
3185 | +void MidiDevice::sendShortMsg(unsigned char status, unsigned char byte1, unsigned char byte2) { |
3186 | + unsigned int word = (((unsigned int)byte2) << 16) | |
3187 | + (((unsigned int)byte1) << 8) | status; |
3188 | + sendShortMsg(word); |
3189 | +} |
3190 | + |
3191 | +void MidiDevice::sendShortMsg(unsigned int word) { |
3192 | + qDebug() << "MIDI short message sending not yet implemented for this API or platform"; |
3193 | +} |
3194 | + |
3195 | +void MidiDevice::sendSysexMsg(QList<int> data, unsigned int length) { |
3196 | + unsigned char * sysexMsg; |
3197 | + sysexMsg = new unsigned char [length]; |
3198 | + |
3199 | + for (unsigned int i=0; i<length; i++) { |
3200 | + sysexMsg[i] = data.at(i); |
3201 | +// qDebug() << "sysexMsg" << i << "=" << sysexMsg[i] << ", data=" << data.at(i); |
3202 | + } |
3203 | + |
3204 | + sendSysexMsg(sysexMsg,length); |
3205 | + delete[] sysexMsg; |
3206 | +} |
3207 | + |
3208 | +void MidiDevice::sendSysexMsg(unsigned char data[], unsigned int length) { |
3209 | + qDebug() << "MIDI system exclusive message sending not yet implemented for this API or platform"; |
3210 | +} |
3211 | + |
3212 | +bool MidiDevice::getMidiLearnStatus() { |
3213 | + return m_bMidiLearn; |
3214 | +} |
3215 | + |
3216 | +void MidiDevice::enableMidiLearn() { |
3217 | + m_bMidiLearn = true; |
3218 | + connect(this, SIGNAL(midiEvent(MidiMessage)), m_pMidiMapping, SLOT(finishMidiLearn(MidiMessage))); |
3219 | + |
3220 | +} |
3221 | + |
3222 | +void MidiDevice::disableMidiLearn() { |
3223 | + m_bMidiLearn = false; |
3224 | + disconnect(this, SIGNAL(midiEvent(MidiMessage)), m_pMidiMapping, SLOT(finishMidiLearn(MidiMessage))); |
3225 | +} |
3226 | + |
3227 | +void MidiDevice::receive(MidiStatusByte status, char channel, char control, char value) |
3228 | +{ |
3229 | + if (midiDebugging()) qDebug() << QString("MIDI ch %1: status: %2, ctrl: %3, val: %4") |
3230 | + .arg(QString::number(channel+1, 16).toUpper()) |
3231 | + .arg(QString::number(status & 255, 16).toUpper()) |
3232 | + .arg(QString::number(control, 16).toUpper()) |
3233 | + .arg(QString::number(value, 16).toUpper()); |
3234 | + |
3235 | + MidiMessage inputCommand(status, control, channel); |
3236 | + |
3237 | + if (m_bMidiLearn) { |
3238 | + emit(midiEvent(inputCommand)); |
3239 | + return; // Don't process midi messages further when MIDI learning |
3240 | + } |
3241 | + |
3242 | + // Only check for a mapping if the status byte is one we know how to handle |
3243 | + if (status == MIDI_STATUS_NOTE_ON |
3244 | + || status == MIDI_STATUS_NOTE_OFF |
3245 | + || status == MIDI_STATUS_PITCH_BEND |
3246 | + || status == MIDI_STATUS_CC) { |
3247 | + // If there was no control bound to that MIDI command, return; |
3248 | + if (!m_pMidiMapping->isMidiMessageMapped(inputCommand)) { |
3249 | + return; |
3250 | + } |
3251 | + } |
3252 | + |
3253 | + MixxxControl mixxxControl = m_pMidiMapping->getInputMixxxControl(inputCommand); |
3254 | + //qDebug() << "MidiDevice: " << mixxxControl.getControlObjectGroup() << mixxxControl.getControlObjectValue(); |
3255 | + |
3256 | + ConfigKey configKey(mixxxControl.getControlObjectGroup(), mixxxControl.getControlObjectValue()); |
3257 | + |
3258 | +#ifdef __MIDISCRIPT__ |
3259 | + // Custom MixxxScript (QtScript) handler |
3260 | + //FIXME: SEAN - The script engine will live inside the MidiMapping! Update this code accordingly. |
3261 | + |
3262 | + if (mixxxControl.getMidiOption() == MIDI_OPT_SCRIPT) { |
3263 | + // qDebug() << "MidiDevice: Calling script function" << configKey.item << "with" << (int)channel << (int)control << (int)value << (int)status; |
3264 | + |
3265 | + if (!m_pMidiMapping->getMidiScriptEngine()->execute(configKey.item, channel, control, value, status, mixxxControl.getControlObjectGroup())) { |
3266 | + qDebug() << "MidiDevice: Invalid script function" << configKey.item; |
3267 | + } |
3268 | + return; |
3269 | + } |
3270 | +#endif |
3271 | + |
3272 | + ControlObject * p = ControlObject::getControl(configKey); |
3273 | + |
3274 | + if (p) //Only pass values on to valid ControlObjects. |
3275 | + { |
3276 | + double newValue = m_pMidiMapping->ComputeValue(mixxxControl.getMidiOption(), p->GetMidiValue(), value); |
3277 | + |
3278 | + // ControlPushButton ControlObjects only accept NOTE_ON, so if the midi mapping is <button> we override the Midi 'status' appropriately. |
3279 | + switch (mixxxControl.getMidiOption()) { |
3280 | + case MIDI_OPT_BUTTON: |
3281 | + case MIDI_OPT_SWITCH: status = MIDI_STATUS_NOTE_ON; break; // Buttons and Switches are treated the same, except that their values are computed differently. |
3282 | + default: break; |
3283 | + } |
3284 | + |
3285 | + ControlObject::sync(); |
3286 | + |
3287 | + //Super dangerous cast here... Should be fine once MidiCategory is replaced with MidiStatusByte permanently. |
3288 | + p->queueFromMidi((MidiCategory)status, newValue); |
3289 | + } |
3290 | + |
3291 | + return; |
3292 | +} |
3293 | + |
3294 | +bool MidiDevice::midiDebugging() { |
3295 | + if (m_midiDebug != -1) return true; |
3296 | + else return false; |
3297 | +} |
3298 | |
3299 | === added file 'mixxx/src/midi/mididevice.h' |
3300 | --- mixxx/src/midi/mididevice.h 1970-01-01 00:00:00 +0000 |
3301 | +++ mixxx/src/midi/mididevice.h 2009-11-10 00:33:17 +0000 |
3302 | @@ -0,0 +1,82 @@ |
3303 | +/*************************************************************************** |
3304 | + mididevice.h |
3305 | + MIDI Device Class |
3306 | + ------------------- |
3307 | + begin : Thu Dec 18 2008 |
3308 | + copyright : (C) 2008 Albert Santoni |
3309 | + email : alberts@mixxx.org |
3310 | + |
3311 | +***************************************************************************/ |
3312 | + |
3313 | +/*************************************************************************** |
3314 | +* * |
3315 | +* This program is free software; you can redistribute it and/or modify * |
3316 | +* it under the terms of the GNU General Public License as published by * |
3317 | +* the Free Software Foundation; either version 2 of the License, or * |
3318 | +* (at your option) any later version. * |
3319 | +* * |
3320 | +***************************************************************************/ |
3321 | + |
3322 | +#ifndef MIDIDEVICE_H |
3323 | +#define MIDIDEVICE_H |
3324 | + |
3325 | +#include <QtCore> |
3326 | +#include "midimessage.h" |
3327 | + |
3328 | +//Forward declarations |
3329 | +class MidiMapping; |
3330 | +#ifdef __MIDISCRIPT__ |
3331 | +class MidiScriptEngine; |
3332 | +#endif |
3333 | + |
3334 | +class MidiDevice : public QObject |
3335 | +{ |
3336 | +Q_OBJECT |
3337 | + public: |
3338 | + MidiDevice(MidiMapping* mapping); |
3339 | + virtual ~MidiDevice(); |
3340 | + virtual int open() = 0; |
3341 | + virtual int close() = 0; |
3342 | + void startup(); |
3343 | + void shutdown(); |
3344 | + bool isOpen() { return m_bIsOpen; }; |
3345 | + bool isOutputDevice() { return m_bIsOutputDevice; }; |
3346 | + bool isInputDevice() { return m_bIsInputDevice; }; |
3347 | + QString getName() { return m_strDeviceName; }; |
3348 | + void setMidiMapping(MidiMapping* mapping); |
3349 | + MidiMapping* getMidiMapping() { return m_pMidiMapping; }; |
3350 | + Q_INVOKABLE void sendShortMsg(unsigned char status, unsigned char byte1, unsigned char byte2); |
3351 | + virtual void sendShortMsg(unsigned int word); |
3352 | + virtual void sendSysexMsg(unsigned char data[], unsigned int length); |
3353 | + Q_INVOKABLE void sendSysexMsg(QList<int> data, unsigned int length); |
3354 | + bool getMidiLearnStatus(); |
3355 | + void receive(MidiStatusByte status, char channel, char control, char value); |
3356 | + bool midiDebugging(); |
3357 | + |
3358 | + public slots: |
3359 | + void disableMidiLearn(); |
3360 | + void enableMidiLearn(); |
3361 | + |
3362 | + signals: |
3363 | + void midiEvent(MidiMessage message); |
3364 | + protected: |
3365 | + /** Verbose device name, in format "[index]. [device name]". Suitable for display in GUI. */ |
3366 | + QString m_strDeviceName; |
3367 | + /** Flag indicating if this device supports MIDI output */ |
3368 | + bool m_bIsOutputDevice; |
3369 | + /** Flag indicating if this device supports MIDI input */ |
3370 | + bool m_bIsInputDevice; |
3371 | + /** MIDI Mapping for this MIDI device, maps MIDI messages onto Mixxx controls */ |
3372 | + MidiMapping* m_pMidiMapping; |
3373 | + /** Indicates whether or not the MIDI device has been opened for input/output. */ |
3374 | + bool m_bIsOpen; |
3375 | + /** Indicates whether MIDI learning is currently enabled or not */ |
3376 | + bool m_bMidiLearn; |
3377 | + /** Pointer to the output device that corresponds to this physical input device. MIDI is a half-duplex |
3378 | + protocol that treats input and output ports on a single device as complete separate entities. This |
3379 | + helps us group those back together for sanity/usability. */ |
3380 | + MidiDevice* m_pCorrespondingOutputDevice; |
3381 | + int m_midiDebug; |
3382 | +}; |
3383 | + |
3384 | +#endif |
3385 | |
3386 | === added file 'mixxx/src/midi/mididevicedummy.h' |
3387 | --- mixxx/src/midi/mididevicedummy.h 1970-01-01 00:00:00 +0000 |
3388 | +++ mixxx/src/midi/mididevicedummy.h 2009-11-10 00:33:17 +0000 |
3389 | @@ -0,0 +1,41 @@ |
3390 | +/** |
3391 | + * @file mididevicedummy.h |
3392 | + * @author Albert Santoni alberts@mixxx.org |
3393 | + * @date Sun Aug 9 2009 |
3394 | + * @brief Dummy MIDI backend |
3395 | + * |
3396 | + */ |
3397 | + |
3398 | +/*************************************************************************** |
3399 | + * * |
3400 | + * This program is free software; you can redistribute it and/or modify * |
3401 | + * it under the terms of the GNU General Public License as published by * |
3402 | + * the Free Software Foundation; either version 2 of the License, or * |
3403 | + * (at your option) any later version. * |
3404 | + * * |
3405 | + ***************************************************************************/ |
3406 | + |
3407 | +#ifndef MIDIDEVICEDUMMY_H |
3408 | +#define MIDIDEVICEDUMMY_H |
3409 | + |
3410 | +#include <QtCore> |
3411 | +#include "mididevice.h" |
3412 | + |
3413 | +/** |
3414 | + *@author Albert Santoni |
3415 | + */ |
3416 | + |
3417 | +/** A dummy implementation of MidiDevice */ |
3418 | +class MidiDeviceDummy : public MidiDevice { |
3419 | +public: |
3420 | + MidiDeviceDummy(MidiMapping* mapping=NULL) : MidiDevice(mapping) { setObjectName("Dummy MIDI Device");}; |
3421 | + ~MidiDeviceDummy() {}; |
3422 | + int open() { return 0; }; |
3423 | + int close() { return 0; }; |
3424 | + void sendShortMsg(unsigned int word) {}; |
3425 | + void sendSysexMsg(unsigned char data[], unsigned int length) {}; |
3426 | +protected: |
3427 | + |
3428 | +}; |
3429 | + |
3430 | +#endif |
3431 | |
3432 | === added file 'mixxx/src/midi/mididevicemanager.cpp' |
3433 | --- mixxx/src/midi/mididevicemanager.cpp 1970-01-01 00:00:00 +0000 |
3434 | +++ mixxx/src/midi/mididevicemanager.cpp 2009-11-10 00:33:17 +0000 |
3435 | @@ -0,0 +1,299 @@ |
3436 | +/** |
3437 | + * @file mididevicemanager.cpp |
3438 | + * @author Albert Santoni alberts@mixxx.org & Sean Pappalardo pegasus@c64.org |
3439 | + * @date Thu Dec 18 2008 |
3440 | + * @brief Manages creation/enumeration/deletion of MidiDevices. |
3441 | + */ |
3442 | + |
3443 | +/*************************************************************************** |
3444 | +* * |
3445 | +* This program is free software; you can redistribute it and/or modify * |
3446 | +* it under the terms of the GNU General Public License as published by * |
3447 | +* the Free Software Foundation; either version 2 of the License, or * |
3448 | +* (at your option) any later version. * |
3449 | +* * |
3450 | +***************************************************************************/ |
3451 | + |
3452 | +#include <QtCore> |
3453 | +#include "mididevice.h" |
3454 | +//#include "midilearnlistener.h" |
3455 | +//#include "midilearningprocessor.h" |
3456 | +//#include "midicontrolprocessor.h" |
3457 | +#include "midideviceportmidi.h" |
3458 | +#include "dlgprefmidibindings.h" |
3459 | +#include "mididevicemanager.h" |
3460 | +#include "../mixxxcontrol.h" |
3461 | +#include "midimapping.h" |
3462 | + |
3463 | +#define DEVICE_CONFIG_PATH BINDINGS_PATH.append("MixxxMIDIDevices") |
3464 | + |
3465 | +MidiDeviceManager::MidiDeviceManager(ConfigObject<ConfigValue> * pConfig) : QObject() |
3466 | +{ |
3467 | + m_pConfig = pConfig; |
3468 | + m_pPrimaryMidiDevice = NULL; |
3469 | + m_pDeviceSettings = new ConfigObject<ConfigValue>(DEVICE_CONFIG_PATH); |
3470 | +} |
3471 | + |
3472 | +MidiDeviceManager::~MidiDeviceManager() |
3473 | +{ |
3474 | + closeDevices(); |
3475 | +} |
3476 | + |
3477 | +void MidiDeviceManager::saveMappings(bool onlyActive) { |
3478 | + // Write out MIDI mappings for currently connected devices |
3479 | + QList<MidiDevice*> deviceList = getDeviceList(false, true); |
3480 | + QListIterator<MidiDevice*> it(deviceList); |
3481 | + |
3482 | + QList<QString> filenames; |
3483 | + |
3484 | + while (it.hasNext()) |
3485 | + { |
3486 | + MidiDevice *cur= it.next(); |
3487 | + if (onlyActive && !cur->isOpen()) continue; |
3488 | + MidiMapping *mapping = cur->getMidiMapping(); |
3489 | + QString name = cur->getName(); |
3490 | + |
3491 | + QString ofilename = name.right(name.size()-name.indexOf(" ")-1).replace(" ", "_"); |
3492 | + |
3493 | + QString filename = ofilename; |
3494 | + |
3495 | + int i=1; |
3496 | + while (filenames.contains(filename)) { |
3497 | + i++; |
3498 | + filename = QString("%1--%2").arg(ofilename).arg(i); |
3499 | + } |
3500 | + |
3501 | + filenames.append(filename); |
3502 | + mapping->savePreset(BINDINGS_PATH.append(filename + MIDI_MAPPING_EXTENSION)); |
3503 | + } |
3504 | +} |
3505 | + |
3506 | +QList<MidiDevice*> MidiDeviceManager::getDeviceList(bool bOutputDevices, bool bInputDevices) |
3507 | +{ |
3508 | + qDebug() << "MidiDeviceManager::getDeviceList"; |
3509 | + bool bMatchedCriteria = false; //Whether or not the current device matched the filtering criteria |
3510 | + |
3511 | + if (m_devices.empty()) |
3512 | + this->queryDevices(); |
3513 | + |
3514 | + //Create a list of MIDI devices filtered to match the given input/output options. |
3515 | + QList<MidiDevice*> filteredDeviceList; |
3516 | + QListIterator<MidiDevice*> dev_it(m_devices); |
3517 | + while (dev_it.hasNext()) |
3518 | + { |
3519 | + bMatchedCriteria = false; //Reset this for the next device. |
3520 | + MidiDevice *device = dev_it.next(); |
3521 | + |
3522 | + if ((bOutputDevices == device->isOutputDevice()) || |
3523 | + (bInputDevices == device->isInputDevice())) { |
3524 | + bMatchedCriteria = true; |
3525 | + } |
3526 | + |
3527 | + if (bMatchedCriteria) |
3528 | + filteredDeviceList.push_back(device); |
3529 | + } |
3530 | + return filteredDeviceList; |
3531 | +} |
3532 | + |
3533 | +void MidiDeviceManager::closeDevices() |
3534 | +{ |
3535 | + QListIterator<MidiDevice*> dev_it(m_devices); |
3536 | + while (dev_it.hasNext()) |
3537 | + { |
3538 | + qDebug() << "Closing MIDI device" << dev_it.peekNext()->getName(); |
3539 | + dev_it.next()->close(); |
3540 | + } |
3541 | +} |
3542 | + |
3543 | +/** Enumerate the MIDI devices |
3544 | + * This method needs a bit of intelligence because PortMidi (and the underlying MIDI APIs) like to split |
3545 | + * output and input into separate devices. Eg. PortMidi would tell us the Hercules is two half-duplex devices. |
3546 | + * To help simplify a lot of code, we're going to aggregate these two streams into a single full-duplex device. |
3547 | + */ |
3548 | +void MidiDeviceManager::queryDevices() |
3549 | +{ |
3550 | + qDebug() << "Scanning MIDI devices:"; |
3551 | + int iNumDevices = Pm_CountDevices(); |
3552 | + |
3553 | + QListIterator<MidiDevice*> dev_it(m_devices); |
3554 | + while (dev_it.hasNext()) { |
3555 | + delete dev_it.next(); |
3556 | + } |
3557 | + |
3558 | + m_devices.clear(); |
3559 | + |
3560 | + const PmDeviceInfo *deviceInfo, *inputDeviceInfo, *outputDeviceInfo; |
3561 | + int inputDevIndex, outputDevIndex; |
3562 | + QMap<int,QString> unassignedOutputDevices; |
3563 | + |
3564 | + // Build a complete list of output devices for later pairing |
3565 | + for (int i = 0; i < iNumDevices; i++) |
3566 | + { |
3567 | + deviceInfo = Pm_GetDeviceInfo(i); |
3568 | + if (deviceInfo->output) { |
3569 | + qDebug() << " Found output device" << "#" << i << deviceInfo->name; |
3570 | + QString deviceName = deviceInfo->name; |
3571 | + // Ignore "To" text in the device names |
3572 | + if (deviceName.indexOf("to",0,Qt::CaseInsensitive)!=-1) deviceName = deviceName.right(deviceName.length()-2); |
3573 | + unassignedOutputDevices[i] = deviceName; |
3574 | + } |
3575 | + } |
3576 | + |
3577 | + // Search for input devices and pair them with output devices if applicable |
3578 | + for (int i = 0; i < iNumDevices; i++) |
3579 | + { |
3580 | + deviceInfo = Pm_GetDeviceInfo(i); |
3581 | + |
3582 | + //If we found an input device |
3583 | + if (deviceInfo->input) |
3584 | + { |
3585 | + qDebug() << " Found input device" << "#" << i << deviceInfo->name; |
3586 | + inputDeviceInfo = deviceInfo; |
3587 | + inputDevIndex = i; |
3588 | + |
3589 | + //Reset our output device variables before we look for one incase we find none. |
3590 | + outputDeviceInfo = NULL; |
3591 | + outputDevIndex = -1; |
3592 | + |
3593 | + //Search for a corresponding output device |
3594 | + QMapIterator<int, QString> j(unassignedOutputDevices); |
3595 | + while (j.hasNext()) { |
3596 | + j.next(); |
3597 | + |
3598 | + // Ignore "From" text in the device names |
3599 | + QString deviceName = inputDeviceInfo->name; |
3600 | + if (deviceName.indexOf("from",0,Qt::CaseInsensitive)!=-1) deviceName = deviceName.right(deviceName.length()-4); |
3601 | + |
3602 | + QByteArray outputName = QString(j.value()).toUtf8(); |
3603 | + if (strcmp(outputName, deviceName) == 0) { |
3604 | + outputDevIndex = j.key(); |
3605 | + outputDeviceInfo = Pm_GetDeviceInfo(outputDevIndex); |
3606 | + |
3607 | + unassignedOutputDevices.remove(outputDevIndex); |
3608 | + |
3609 | + qDebug() << " Linking to output device #" << outputDevIndex << outputName; |
3610 | + break; |
3611 | + } |
3612 | + } |
3613 | + |
3614 | + //So at this point in the code, we either have an input-only MIDI device (outputDeviceInfo == NULL) |
3615 | + //or we've found a matching output MIDI device (outputDeviceInfo != NULL). |
3616 | + |
3617 | + //.... so create our (aggregate) MIDI device! |
3618 | + MidiDevicePortMidi *currentDevice = new MidiDevicePortMidi(/*new MidiControlProcessor(NULL)*/ NULL, |
3619 | + inputDeviceInfo, |
3620 | + outputDeviceInfo, |
3621 | + inputDevIndex, |
3622 | + outputDevIndex); |
3623 | + m_devices.push_back((MidiDevice*)currentDevice); |
3624 | + |
3625 | + } |
3626 | + |
3627 | +// if (deviceInfo->input || deviceInfo->output) |
3628 | +// { |
3629 | +// MidiDevicePortMidi *currentDevice = new MidiDevicePortMidi(new MidiControlProcessor(NULL), deviceInfo, i); |
3630 | +// m_devices.push_back((MidiDevice*)currentDevice); |
3631 | +// } |
3632 | + } |
3633 | +} |
3634 | + |
3635 | +/** Open whatever MIDI devices are selected in the preferences. */ |
3636 | +int MidiDeviceManager::setupDevices() |
3637 | +{ |
3638 | + QList<MidiDevice*> deviceList = getDeviceList(false, true); |
3639 | + QListIterator<MidiDevice*> it(deviceList); |
3640 | + |
3641 | + qDebug() << "MidiDeviceManager: Setting up devices"; |
3642 | + |
3643 | + QList<QString> filenames; |
3644 | + |
3645 | + while (it.hasNext()) |
3646 | + { |
3647 | + MidiDevice *cur= it.next(); |
3648 | + MidiMapping *mapping = cur->getMidiMapping(); |
3649 | + QString name = cur->getName(); |
3650 | + mapping->setName(name); |
3651 | + |
3652 | + cur->close(); |
3653 | + |
3654 | + QString ofilename = name.right(name.size()-name.indexOf(" ")-1).replace(" ", "_"); |
3655 | + |
3656 | + QString filename = ofilename; |
3657 | + |
3658 | + int i=1; |
3659 | + while (filenames.contains(filename)) { |
3660 | + i++; |
3661 | + filename = QString("%1--%2").arg(ofilename).arg(i); |
3662 | + } |
3663 | + |
3664 | + filenames.append(filename); |
3665 | + mapping->loadPreset(BINDINGS_PATH.append(filename + MIDI_MAPPING_EXTENSION),true); |
3666 | + |
3667 | + if ( m_pConfig->getValueString(ConfigKey("[Midi]", name.replace(" ", "_"))) != "1" ) |
3668 | + continue; |
3669 | + |
3670 | + qDebug() << "Opening Device:" << name; |
3671 | + |
3672 | + cur->open(); |
3673 | + mapping->applyPreset(); |
3674 | + |
3675 | + } |
3676 | + |
3677 | + return 0; |
3678 | +} |
3679 | + |
3680 | + |
3681 | +/** This should be in MidiProcessor because it's bindings related */ |
3682 | +QStringList MidiDeviceManager::getConfigList(QString path) |
3683 | +{ |
3684 | + // Make sure list is empty |
3685 | + QStringList configs; |
3686 | + configs.clear(); |
3687 | + |
3688 | + // Get list of available midi configurations |
3689 | + QDir dir(path); |
3690 | + dir.setFilter(QDir::Files); |
3691 | + dir.setNameFilters(QStringList() << "*.midi.xml" << "*.MIDI.XML"); |
3692 | + |
3693 | + //const QFileInfoList *list = dir.entryInfoList(); |
3694 | + //if (dir.entryInfoList().empty()) |
3695 | + { |
3696 | + QListIterator<QFileInfo> it(dir.entryInfoList()); |
3697 | + QFileInfo fi; // pointer for traversing |
3698 | + while (it.hasNext()) |
3699 | + { |
3700 | + fi = it.next(); |
3701 | + configs.append(fi.fileName()); |
3702 | + } |
3703 | + } |
3704 | + |
3705 | + return configs; |
3706 | +} |
3707 | + |
3708 | + |
3709 | +void MidiDeviceManager::associateInputAndOutputDevices(MidiDevice* inputDevice, QString outputDeviceName) |
3710 | +{ |
3711 | + //TODO: This function needs to be updated to work with our "aggregate" input/ouput MidiDevice class |
3712 | + // or just simply removed all together. I just sent out a mixxx-devel email with more history |
3713 | + // on this, check the archive if you need more info. -- Albert Nov 9/09 (1.8 CRUNCH TIME!) |
3714 | + // |
3715 | + |
3716 | +/* |
3717 | + //Find the output MidiDevice object that corresponds to outputDeviceName. |
3718 | + QListIterator<MidiDevice*> dev_it(m_devices); |
3719 | + MidiDevice* outputDevice = NULL; |
3720 | + while (dev_it.hasNext()) { |
3721 | + outputDevice = dev_it.next(); |
3722 | + if (outputDevice->getName() == outputDeviceName) { |
3723 | + qDebug() << "associating input dev" << inputDevice->getName() << "with" << outputDeviceName; |
3724 | + break; |
3725 | + } |
3726 | + } |
3727 | + |
3728 | + if (outputDevice == NULL) //No output device matched outputDeviceName... |
3729 | + return; |
3730 | + |
3731 | + //Tell the input device that it's corresponding output device is... outputDevice. |
3732 | + inputDevice->setOutputDevice(outputDevice); |
3733 | + */ |
3734 | +} |
3735 | |
3736 | === added file 'mixxx/src/midi/mididevicemanager.h' |
3737 | --- mixxx/src/midi/mididevicemanager.h 1970-01-01 00:00:00 +0000 |
3738 | +++ mixxx/src/midi/mididevicemanager.h 2009-11-10 00:33:17 +0000 |
3739 | @@ -0,0 +1,53 @@ |
3740 | +/** |
3741 | + * @file mididevicemanager.h |
3742 | + * @author Albert Santoni alberts@mixxx.org |
3743 | + * @date Thu Dec 18 2008 |
3744 | + * @brief Manages creation/enumeration/deletion of MidiDevices. |
3745 | + */ |
3746 | + |
3747 | +/*************************************************************************** |
3748 | +* * |
3749 | +* This program is free software; you can redistribute it and/or modify * |
3750 | +* it under the terms of the GNU General Public License as published by * |
3751 | +* the Free Software Foundation; either version 2 of the License, or * |
3752 | +* (at your option) any later version. * |
3753 | +* * |
3754 | +***************************************************************************/ |
3755 | + |
3756 | +#ifndef MIDIDEVICEMANAGER_H |
3757 | +#define MIDIDEVICEMANAGER_H |
3758 | + |
3759 | +#include "configobject.h" |
3760 | +class MidiDevice; |
3761 | +class MidiLearnListener; |
3762 | +class MidiLearnProcessor; |
3763 | +class DlgPrefMidiBindings; |
3764 | + |
3765 | +/** Manages creation/enumeration/deletion of MidiDevices. */ |
3766 | +class MidiDeviceManager : public QObject |
3767 | +{ |
3768 | + Q_OBJECT |
3769 | + public: |
3770 | + MidiDeviceManager(ConfigObject<ConfigValue> * pConfig); |
3771 | + ~MidiDeviceManager(); |
3772 | + QList<MidiDevice*> getDeviceList(bool bOutputDevices=true, bool bInputDevice=true); |
3773 | + QStringList getConfigList(QString path); |
3774 | + void saveMappings(bool onlyActive=false); |
3775 | + void closeDevices(); |
3776 | + void queryDevices(); |
3777 | + int setupDevices(); |
3778 | + ConfigObject<ConfigValue>* getDeviceSettings() { return m_pDeviceSettings; }; |
3779 | + void associateInputAndOutputDevices(MidiDevice* inputDevice, QString outputDeviceName); |
3780 | + /* MidiDevice* getPrimaryMidiDevice() { return m_pPrimaryMidiDevice; }; //HACK while our code still sucks |
3781 | + void enableMidiLearn(DlgPrefMidiBindings* listener); |
3782 | + void disableMidiLearn(); */ //Moving MIDI learning into MIDI device, a la 1.7.0 |
3783 | + signals: |
3784 | + void devicesChanged(); |
3785 | + private: |
3786 | + QList<MidiDevice*> m_devices; |
3787 | + ConfigObject<ConfigValue> *m_pDeviceSettings; |
3788 | + ConfigObject<ConfigValue> *m_pConfig; |
3789 | + MidiDevice* m_pPrimaryMidiDevice; |
3790 | + }; |
3791 | + |
3792 | +#endif |
3793 | |
3794 | === added file 'mixxx/src/midi/midideviceportmidi.cpp' |
3795 | --- mixxx/src/midi/midideviceportmidi.cpp 1970-01-01 00:00:00 +0000 |
3796 | +++ mixxx/src/midi/midideviceportmidi.cpp 2009-11-10 00:33:17 +0000 |
3797 | @@ -0,0 +1,245 @@ |
3798 | +/** |
3799 | + * @file midideviceportmidi.cpp |
3800 | + * @author Albert Santoni alberts@mixxx.org |
3801 | + * @date Thu Dec 18 2008 |
3802 | + * @brief PortMidi-based MIDI backend |
3803 | + * |
3804 | + * MidiDevicePortMidi is a class representing a MIDI device, either |
3805 | + * physical or software. It uses the PortMidi API to send and receive |
3806 | + * MIDI events to/from the device. It's important to note that PortMidi |
3807 | + * treats input and output on a single physical device as two separate |
3808 | + * half-duplex devices. In this class, we wrap those together into a |
3809 | + * single device, which is why the MidiDevicePortMidi constructor takes |
3810 | + * both arguments pertaining to both input and output "devices". |
3811 | + * |
3812 | + * |
3813 | + */ |
3814 | + |
3815 | +/*************************************************************************** |
3816 | +* * |
3817 | +* This program is free software; you can redistribute it and/or modify * |
3818 | +* it under the terms of the GNU General Public License as published by * |
3819 | +* the Free Software Foundation; either version 2 of the License, or * |
3820 | +* (at your option) any later version. * |
3821 | +* * |
3822 | +***************************************************************************/ |
3823 | + |
3824 | +#include <QtCore> |
3825 | +#include "configobject.h" |
3826 | +#include "midimapping.h" |
3827 | +#include "midideviceportmidi.h" |
3828 | + |
3829 | +QMutex MidiDevicePortMidi::m_sPMLock; // PortMidi is not thread-safe |
3830 | + |
3831 | +MidiDevicePortMidi::MidiDevicePortMidi(MidiMapping* mapping, |
3832 | + const PmDeviceInfo* inputDeviceInfo, |
3833 | + const PmDeviceInfo* outputDeviceInfo, |
3834 | + int inputDeviceIndex, |
3835 | + int outputDeviceIndex) |
3836 | + : MidiDevice(mapping), QThread() |
3837 | +{ |
3838 | + m_pInputStream = NULL; |
3839 | + m_pOutputStream = NULL; |
3840 | + m_bStopRequested = false; |
3841 | + m_pInputDeviceInfo = inputDeviceInfo; |
3842 | + m_pOutputDeviceInfo = outputDeviceInfo; |
3843 | + m_iInputDeviceIndex = inputDeviceIndex; |
3844 | + m_iOutputDeviceIndex = outputDeviceIndex; |
3845 | + |
3846 | + //Note: We prepend the input stream's index to the device's name to prevent duplicate devices from causing mayhem. |
3847 | + m_strDeviceName = QString("%1. %2").arg(QString::number(m_iInputDeviceIndex)).arg(inputDeviceInfo->name); |
3848 | + |
3849 | + if (inputDeviceInfo) { |
3850 | + m_bIsInputDevice = m_pInputDeviceInfo->input; |
3851 | + } |
3852 | + if (outputDeviceInfo) { |
3853 | + m_bIsOutputDevice = m_pOutputDeviceInfo->output; |
3854 | + } |
3855 | +} |
3856 | + |
3857 | +MidiDevicePortMidi::~MidiDevicePortMidi() |
3858 | +{ |
3859 | + close(); |
3860 | +} |
3861 | + |
3862 | +int MidiDevicePortMidi::open() |
3863 | +{ |
3864 | + if (m_bIsOpen) { |
3865 | + qDebug() << "PortMIDI device" << m_strDeviceName << "already open"; |
3866 | + return -1; |
3867 | + } |
3868 | + |
3869 | + startup(); |
3870 | + |
3871 | + m_bStopRequested = false; |
3872 | + |
3873 | + if (m_strDeviceName == MIXXX_PORTMIDI_NO_DEVICE_STRING) |
3874 | + return -1; |
3875 | + |
3876 | + PmError err = Pm_Initialize(); |
3877 | + if( err != pmNoError ) |
3878 | + { |
3879 | + qDebug() << "PortMidi error:" << Pm_GetErrorText(err); |
3880 | + return -1; |
3881 | + } |
3882 | + |
3883 | + if (m_pInputDeviceInfo) |
3884 | + { |
3885 | + if (m_bIsInputDevice) |
3886 | + { |
3887 | + if (midiDebugging()) qDebug() << "MidiDevicePortMidi: Opening" << m_pInputDeviceInfo->name << "index" << m_iInputDeviceIndex << "for input"; |
3888 | + |
3889 | + err = Pm_OpenInput( &m_pInputStream, |
3890 | + m_iInputDeviceIndex, |
3891 | + NULL, //No drive hacks |
3892 | + MIXXX_PORTMIDI_BUFFER_LEN, |
3893 | + NULL, |
3894 | + NULL); |
3895 | + |
3896 | + if( err != pmNoError ) |
3897 | + { |
3898 | + qDebug() << "PortMidi error:" << Pm_GetErrorText(err); |
3899 | + return -2; |
3900 | + } |
3901 | + } |
3902 | + } |
3903 | + if (m_pOutputDeviceInfo) |
3904 | + { |
3905 | + if (m_bIsOutputDevice) |
3906 | + { |
3907 | + if (midiDebugging()) qDebug() << "MidiDevicePortMidi: Opening" << m_pOutputDeviceInfo->name << "index" << m_iOutputDeviceIndex << "for output"; |
3908 | + |
3909 | + err = Pm_OpenOutput( &m_pOutputStream, |
3910 | + m_iOutputDeviceIndex, |
3911 | + NULL, // No driver hacks |
3912 | + 0, // No buffering |
3913 | + NULL, // Use PortTime for timing |
3914 | + NULL, // No time info |
3915 | + 0); // No latency compensation. |
3916 | + |
3917 | + if( err != pmNoError ) |
3918 | + { |
3919 | + qDebug() << "PortMidi error:" << Pm_GetErrorText(err); |
3920 | + return -2; |
3921 | + } |
3922 | + } |
3923 | + } |
3924 | + |
3925 | + m_bIsOpen = true; |
3926 | + start(); |
3927 | + |
3928 | + return 0; |
3929 | + |
3930 | +} |
3931 | + |
3932 | +int MidiDevicePortMidi::close() |
3933 | +{ |
3934 | + if (!m_bIsOpen) { |
3935 | + qDebug() << "PortMIDI device" << m_strDeviceName << "already closed"; |
3936 | + return -1; |
3937 | + } |
3938 | + |
3939 | + shutdown(); |
3940 | + |
3941 | + m_bStopRequested = true; |
3942 | + m_mutex.lock(); |
3943 | + |
3944 | + if (m_pInputStream) |
3945 | + { |
3946 | + m_sPMLock.lock(); |
3947 | + PmError err = Pm_Close(m_pInputStream); |
3948 | + if( err != pmNoError ) |
3949 | + { |
3950 | + qDebug() << "PortMidi error:" << Pm_GetErrorText(err); |
3951 | + m_sPMLock.unlock(); |
3952 | + return -1; |
3953 | + } |
3954 | + m_sPMLock.unlock(); |
3955 | + } |
3956 | + |
3957 | + if (m_pOutputStream) |
3958 | + { |
3959 | + m_sPMLock.lock(); |
3960 | + PmError err = Pm_Close(m_pOutputStream); |
3961 | + if( err != pmNoError ) |
3962 | + { |
3963 | + qDebug() << "PortMidi error:" << Pm_GetErrorText(err); |
3964 | + m_sPMLock.unlock(); |
3965 | + return -1; |
3966 | + } |
3967 | + m_sPMLock.unlock(); |
3968 | + } |
3969 | + |
3970 | + m_bIsOpen = false; |
3971 | + |
3972 | + m_mutex.unlock(); |
3973 | + |
3974 | + return 0; |
3975 | +} |
3976 | + |
3977 | +void MidiDevicePortMidi::run() |
3978 | +{ |
3979 | + QThread::currentThread()->setObjectName(QString("PM %1").arg(m_strDeviceName)); |
3980 | + int numEvents = 0; |
3981 | + bool stopRunning = false; |
3982 | + |
3983 | + |
3984 | + do |
3985 | + { |
3986 | + m_mutex.lock(); |
3987 | + if (m_pInputStream) |
3988 | + { |
3989 | + m_sPMLock.lock(); |
3990 | + numEvents = Pm_Read(m_pInputStream, m_midiBuffer, MIXXX_PORTMIDI_BUFFER_LEN); |
3991 | + m_sPMLock.unlock(); |
3992 | + |
3993 | + for (int i = 0; i < numEvents; i++) |
3994 | + { |
3995 | + //if (Pm_MessageStatus(m_midiBuffer[i].message) == 0x90) //Note on, channel 1 |
3996 | + { |
3997 | + m_sPMLock.lock(); |
3998 | + unsigned char status = Pm_MessageStatus(m_midiBuffer[i].message); |
3999 | + unsigned char opcode = status & 0xF0; |
4000 | + unsigned char channel = status & 0x0F; |
4001 | + unsigned char note = Pm_MessageData1(m_midiBuffer[i].message); |
4002 | + unsigned char velocity = Pm_MessageData2(m_midiBuffer[i].message); |
4003 | + m_sPMLock.unlock(); |
4004 | + |
4005 | + MidiDevice::receive((MidiStatusByte)status, channel, note, velocity); |
4006 | + |
4007 | + } |
4008 | + } |
4009 | + } |
4010 | + |
4011 | + usleep(5000); //Sleep this thread for 5 milliseconds between checking for new MIDI events. |
4012 | + |
4013 | + stopRunning = m_bStopRequested; //Cache locally for thread-safety. |
4014 | + m_mutex.unlock(); //Have to unlock inside the loop to give the other thread a chance to lock. |
4015 | + |
4016 | + } while (!stopRunning); |
4017 | + |
4018 | + |
4019 | +} |
4020 | + |
4021 | +void MidiDevicePortMidi::sendShortMsg(unsigned int word) |
4022 | +{ |
4023 | + if (m_pOutputStream) |
4024 | + { |
4025 | + m_sPMLock.lock(); |
4026 | + PmError err = Pm_WriteShort(m_pOutputStream, 0, word); |
4027 | + if( err != pmNoError ) qDebug() << "PortMidi sendShortMsg error:" << Pm_GetErrorText(err); |
4028 | + m_sPMLock.unlock(); |
4029 | + } |
4030 | +} |
4031 | + |
4032 | +// The sysex data must already contain the start byte 0xf0 and the end byte 0xf7. |
4033 | +void MidiDevicePortMidi::sendSysexMsg(unsigned char data[], unsigned int length) |
4034 | +{ |
4035 | + if (m_pOutputStream) |
4036 | + { |
4037 | + m_sPMLock.lock(); |
4038 | + PmError err = Pm_WriteSysEx(m_pOutputStream, 0, data); |
4039 | + if( err != pmNoError ) qDebug() << "PortMidi sendSysexMsg error:" << Pm_GetErrorText(err); |
4040 | + m_sPMLock.unlock(); |
4041 | + } |
4042 | +} |
4043 | |
4044 | === added file 'mixxx/src/midi/midideviceportmidi.h' |
4045 | --- mixxx/src/midi/midideviceportmidi.h 1970-01-01 00:00:00 +0000 |
4046 | +++ mixxx/src/midi/midideviceportmidi.h 2009-11-10 00:33:17 +0000 |
4047 | @@ -0,0 +1,60 @@ |
4048 | +/** |
4049 | + * @file midideviceportmidi.h |
4050 | + * @author Albert Santoni alberts@mixxx.org |
4051 | + * @date Thu Dec 18 2008 |
4052 | + * @brief PortMidi-based MIDI backend |
4053 | + * |
4054 | + */ |
4055 | + |
4056 | +/*************************************************************************** |
4057 | + * * |
4058 | + * This program is free software; you can redistribute it and/or modify * |
4059 | + * it under the terms of the GNU General Public License as published by * |
4060 | + * the Free Software Foundation; either version 2 of the License, or * |
4061 | + * (at your option) any later version. * |
4062 | + * * |
4063 | + ***************************************************************************/ |
4064 | + |
4065 | +#ifndef MIDIDEVICEPORTMIDI_H |
4066 | +#define MIDIDEVICEPORTMIDI_H |
4067 | + |
4068 | +#include <portmidi.h> |
4069 | +#include <porttime.h> |
4070 | +#include <QtCore> |
4071 | +#include "mididevice.h" |
4072 | + |
4073 | +#define MIXXX_PORTMIDI_BUFFER_LEN 64 /**Number of MIDI messages to buffer*/ |
4074 | +#define MIXXX_PORTMIDI_NO_DEVICE_STRING "None" /**String to display for no MIDI devices present */ |
4075 | +/** |
4076 | + *@author Albert Santoni |
4077 | + */ |
4078 | + |
4079 | +/** A PortMidi-based implementation of MidiDevice */ |
4080 | +class MidiDevicePortMidi : public MidiDevice, public QThread { |
4081 | +public: |
4082 | + MidiDevicePortMidi(MidiMapping* mapping, |
4083 | + const PmDeviceInfo* inputDeviceInfo, |
4084 | + const PmDeviceInfo* outputDeviceInfo, |
4085 | + int inputDeviceIndex, |
4086 | + int outputDeviceIndex); |
4087 | + ~MidiDevicePortMidi(); |
4088 | + int open(); |
4089 | + int close(); |
4090 | + void sendShortMsg(unsigned int word); |
4091 | + void sendSysexMsg(unsigned char data[], unsigned int length); |
4092 | +protected: |
4093 | + void run(); |
4094 | + const PmDeviceInfo* m_pInputDeviceInfo; |
4095 | + const PmDeviceInfo* m_pOutputDeviceInfo; |
4096 | + int m_iInputDeviceIndex; |
4097 | + int m_iOutputDeviceIndex; |
4098 | + PortMidiStream *m_pInputStream; |
4099 | + PortMidiStream *m_pOutputStream; |
4100 | + PmEvent m_midiBuffer[MIXXX_PORTMIDI_BUFFER_LEN]; |
4101 | + static QList<QString> m_deviceList; |
4102 | + QMutex m_mutex; |
4103 | + static QMutex m_sPMLock; // PortMidi is not thread-safe, so we need to only allow one thread at a time |
4104 | + bool m_bStopRequested; |
4105 | +}; |
4106 | + |
4107 | +#endif |
4108 | |
4109 | === renamed file 'mixxx/src/midiinputmapping.h' => 'mixxx/src/midi/midiinputmapping.h' |
4110 | === renamed file 'mixxx/src/midiinputmappingtablemodel.cpp' => 'mixxx/src/midi/midiinputmappingtablemodel.cpp' |
4111 | === renamed file 'mixxx/src/midiinputmappingtablemodel.h' => 'mixxx/src/midi/midiinputmappingtablemodel.h' |
4112 | === renamed file 'mixxx/src/midiledhandler.cpp' => 'mixxx/src/midi/midiledhandler.cpp' |
4113 | --- mixxx/src/midiledhandler.cpp 2009-08-11 04:38:51 +0000 |
4114 | +++ mixxx/src/midi/midiledhandler.cpp 2009-11-10 00:33:17 +0000 |
4115 | @@ -6,9 +6,9 @@ |
4116 | |
4117 | Q3PtrList<MidiLedHandler> MidiLedHandler::allhandlers = Q3PtrList<MidiLedHandler>(); |
4118 | |
4119 | -MidiLedHandler::MidiLedHandler(QString group, QString key, MidiObject & midi, double min, |
4120 | - double max, unsigned char status, unsigned char midino, QString device, unsigned char on, unsigned char off) |
4121 | - : m_min(min), m_max(max), m_midi(midi), m_status(status), m_midino(midino), m_device(device), m_on(on), m_off(off) { |
4122 | +MidiLedHandler::MidiLedHandler(QString group, QString key, MidiDevice & midi, double min, |
4123 | + double max, unsigned char status, unsigned char midino, unsigned char on, unsigned char off) |
4124 | + : m_min(min), m_max(max), m_midi(midi), m_status(status), m_midino(midino), m_on(on), m_off(off) { |
4125 | |
4126 | //OMGWTFBBQ: Massive hack to temporarily fix LP #254564 for the 1.6.0 release. |
4127 | // Something's funky with our <lights> blocks handling? -- Albert 08/05/2008 |
4128 | @@ -49,7 +49,7 @@ |
4129 | m_reentracyBlock.unlock(); |
4130 | } |
4131 | |
4132 | -void MidiLedHandler::createHandlers(QDomNode node, MidiObject & midi, QString device) { |
4133 | +void MidiLedHandler::createHandlers(QDomNode node, MidiDevice & midi) { |
4134 | if (!node.isNull() && node.isElement()) { |
4135 | QDomNode light = node; |
4136 | while (!light.isNull()) { |
4137 | @@ -78,8 +78,8 @@ |
4138 | if (!light.firstChildElement("maximum").isNull()) { |
4139 | max = WWidget::selectNodeFloat(light, "maximum"); |
4140 | } |
4141 | - qDebug() << "Creating LED handler hook for:" << group << key << "between"<< min << "and" << max << "to midi out:" << status << midino << "on" << device << "on/off:" << on << off; |
4142 | - allhandlers.append(new MidiLedHandler(group, key, midi, min, max, status, midino, device, on, off)); |
4143 | + qDebug() << "Creating LED handler hook for:" << group << key << "between"<< min << "and" << max << "to midi out:" << status << midino << "on/off:" << on << off; |
4144 | + allhandlers.append(new MidiLedHandler(group, key, midi, min, max, status, midino, on, off)); |
4145 | } |
4146 | light = light.nextSibling(); |
4147 | } |
4148 | |
4149 | === renamed file 'mixxx/src/midiledhandler.h' => 'mixxx/src/midi/midiledhandler.h' |
4150 | --- mixxx/src/midiledhandler.h 2009-04-19 18:54:16 +0000 |
4151 | +++ mixxx/src/midi/midiledhandler.h 2009-11-10 00:33:17 +0000 |
4152 | @@ -5,7 +5,7 @@ |
4153 | #define MIDILEDHANDLER_H |
4154 | |
4155 | #include "controlobject.h" |
4156 | -#include "midiobject.h" |
4157 | +#include "mididevice.h" |
4158 | |
4159 | #include <q3ptrlist.h> |
4160 | |
4161 | @@ -13,11 +13,11 @@ |
4162 | { |
4163 | Q_OBJECT |
4164 | public: |
4165 | - MidiLedHandler(QString group, QString key, MidiObject& midi, double min, double max, |
4166 | - unsigned char status, unsigned char midino, QString device, unsigned char on, unsigned char off); |
4167 | + MidiLedHandler(QString group, QString key, MidiDevice & midi, double min, double max, |
4168 | + unsigned char status, unsigned char midino, unsigned char on, unsigned char off); |
4169 | ~MidiLedHandler(); |
4170 | |
4171 | - static void createHandlers(QDomNode node, MidiObject& midi, QString device); |
4172 | + static void createHandlers(QDomNode node, MidiDevice & midi); |
4173 | static void destroyHandlers(); |
4174 | static Q3PtrList<MidiLedHandler> allhandlers; |
4175 | |
4176 | @@ -25,13 +25,12 @@ |
4177 | void controlChanged(double value); |
4178 | |
4179 | private: |
4180 | - MidiObject& m_midi; |
4181 | + MidiDevice& m_midi; |
4182 | double m_min; |
4183 | double m_max; |
4184 | ControlObject* m_cobj; |
4185 | unsigned char m_status; |
4186 | unsigned char m_midino; |
4187 | - QString m_device; |
4188 | unsigned char m_on; |
4189 | unsigned char m_off; |
4190 | unsigned char lastStatus; |
4191 | |
4192 | === renamed file 'mixxx/src/midimapping.cpp' => 'mixxx/src/midi/midimapping.cpp' |
4193 | --- mixxx/src/midimapping.cpp 2009-11-10 00:33:14 +0000 |
4194 | +++ mixxx/src/midi/midimapping.cpp 2009-11-10 00:33:17 +0000 |
4195 | @@ -4,6 +4,7 @@ |
4196 | ------------------- |
4197 | begin : Sat Jan 17 2009 |
4198 | copyright : (C) 2009 Sean M. Pappalardo |
4199 | + (C) 2009 Albert Santoni |
4200 | email : pegasus@c64.org |
4201 | |
4202 | ***************************************************************************/ |
4203 | @@ -22,40 +23,139 @@ |
4204 | #include "widget/wwidget.h" // FIXME: This should be xmlparse.h |
4205 | #include "mixxxcontrol.h" |
4206 | #include "midimessage.h" |
4207 | +#include "defs.h" |
4208 | #include "midiinputmappingtablemodel.h" |
4209 | #include "midioutputmappingtablemodel.h" |
4210 | #include "midimapping.h" |
4211 | +#include "mididevicedummy.h" |
4212 | #include "midiledhandler.h" |
4213 | #include "configobject.h" |
4214 | |
4215 | -#define REQUIRED_MAPPING_FILE "midi-mappings-scripts.js" |
4216 | +#define REQUIRED_SCRIPT_FILE "midi-mappings-scripts.js" |
4217 | #define XML_SCHEMA_VERSION "1" |
4218 | +#define DEFAULT_DEVICE_PRESET BINDINGS_PATH.append(m_deviceName.right(m_deviceName.size()-m_deviceName.indexOf(" ")-1).replace(" ", "_") + MIDI_MAPPING_EXTENSION) |
4219 | |
4220 | static QString toHex(QString numberStr) { |
4221 | return "0x" + QString("0" + QString::number(numberStr.toUShort(), 16).toUpper()).right(2); |
4222 | } |
4223 | |
4224 | -MidiMapping::MidiMapping(MidiObject& midi_object) : QObject(), m_rMidiObject(midi_object), m_mappingLock(QMutex::Recursive) { |
4225 | +MidiMapping::MidiMapping(MidiDevice* outputMidiDevice) |
4226 | + : QObject(), |
4227 | + m_mappingLock(QMutex::Recursive) { |
4228 | + // If BINDINGS_PATH doesn't exist, create it |
4229 | + if (!QDir(BINDINGS_PATH).exists()) { |
4230 | + qDebug() << "Creating new MIDI presets directory" << BINDINGS_PATH; |
4231 | + QDir().mkpath(BINDINGS_PATH); |
4232 | + } |
4233 | + |
4234 | + //Q_ASSERT(outputMidiDevice); |
4235 | + |
4236 | #ifdef __MIDISCRIPT__ |
4237 | - m_pScriptEngine = midi_object.getMidiScriptEngine(); |
4238 | + //Start the scripting engine. |
4239 | + m_pScriptEngine = NULL; |
4240 | + m_pOutputMidiDevice = outputMidiDevice; |
4241 | + if (m_pOutputMidiDevice) |
4242 | + m_deviceName = m_pOutputMidiDevice->getName(); //Name of the device to look for the <controller> block for in the XML. |
4243 | + |
4244 | + startupScriptEngine(); |
4245 | #endif |
4246 | m_pMidiInputMappingTableModel = new MidiInputMappingTableModel(this); |
4247 | m_pMidiOutputMappingTableModel = new MidiOutputMappingTableModel(this); |
4248 | } |
4249 | |
4250 | MidiMapping::~MidiMapping() { |
4251 | -#ifdef __MIDISCRIPT__ |
4252 | - // Call each script's shutdown function if it exists |
4253 | - QListIterator<QString> prefixIt(m_pScriptFunctionPrefixes); |
4254 | - while (prefixIt.hasNext()) { |
4255 | - QString shutName = prefixIt.next(); |
4256 | - if (shutName!="") { |
4257 | - shutName.append(".shutdown"); |
4258 | - qDebug() << "MidiMapping: Executing" << shutName; |
4259 | - if (!m_pScriptEngine->execute(shutName)) |
4260 | - qWarning() << "MidiMapping: No" << shutName << "function in script"; |
4261 | - } |
4262 | - } |
4263 | + |
4264 | +} |
4265 | + |
4266 | +#ifdef __MIDISCRIPT__ |
4267 | +void MidiMapping::startupScriptEngine() { |
4268 | + |
4269 | + if(m_pScriptEngine) return; |
4270 | + |
4271 | + //XXX Deadly hack attack: |
4272 | + if (m_pOutputMidiDevice == NULL) { |
4273 | + m_pOutputMidiDevice = new MidiDeviceDummy(this); //Just make some dummy device :( |
4274 | + } |
4275 | + //XXX Memory leak :( |
4276 | + |
4277 | + qDebug () << "Starting script engine with output device" << m_pOutputMidiDevice->getName(); |
4278 | + |
4279 | + m_pScriptEngine = new MidiScriptEngine(m_pOutputMidiDevice); |
4280 | + |
4281 | + m_pScriptEngine->moveToThread(m_pScriptEngine); |
4282 | + |
4283 | + connect(m_pScriptEngine, SIGNAL(initialized()), |
4284 | + this, SLOT(slotScriptEngineReady()), |
4285 | + Qt::DirectConnection); |
4286 | + m_scriptEngineInitializedMutex.lock(); |
4287 | + m_pScriptEngine->start(); |
4288 | + // Wait until the script engine is initialized |
4289 | + m_scriptEngineInitializedCondition.wait(&m_scriptEngineInitializedMutex); |
4290 | + m_scriptEngineInitializedMutex.unlock(); |
4291 | + |
4292 | +} |
4293 | + |
4294 | +void MidiMapping::loadScriptCode() { |
4295 | + ConfigObject<ConfigValue> *config = new ConfigObject<ConfigValue>(QDir::homePath().append("/").append(SETTINGS_PATH).append(SETTINGS_FILE)); |
4296 | + |
4297 | + qDebug() << "MidiMapping: Loading & evaluating all MIDI script code"; |
4298 | + |
4299 | + QListIterator<QString> it(m_pScriptFileNames); |
4300 | + while (it.hasNext()) { |
4301 | + QString curScriptFileName = it.next(); |
4302 | + m_pScriptEngine->evaluate(config->getConfigPath().append("midi/").append(curScriptFileName)); |
4303 | + |
4304 | + if(m_pScriptEngine->hasErrors(curScriptFileName)) { |
4305 | + qDebug() << "Errors occured while loading " << curScriptFileName; |
4306 | + } |
4307 | + } |
4308 | +} |
4309 | + |
4310 | +void MidiMapping::initializeScripts() { |
4311 | + if(m_pScriptEngine) { |
4312 | + // Call each script's init function if it exists |
4313 | + QListIterator<QString> prefixIt(m_pScriptFunctionPrefixes); |
4314 | + while (prefixIt.hasNext()) { |
4315 | + QString initName = prefixIt.next(); |
4316 | + if (initName!="") { |
4317 | + initName.append(".init"); |
4318 | + qDebug() << "MidiMapping: Executing" << initName; |
4319 | + if (!m_pScriptEngine->execute(initName, m_deviceName)) |
4320 | + qWarning() << "MidiMapping: No" << initName << "function in script"; |
4321 | + } |
4322 | + } |
4323 | + } |
4324 | +} |
4325 | + |
4326 | +void MidiMapping::shutdownScriptEngine() { |
4327 | + if(m_pScriptEngine) { |
4328 | + // Call each script's shutdown function if it exists |
4329 | + QListIterator<QString> prefixIt(m_pScriptFunctionPrefixes); |
4330 | + while (prefixIt.hasNext()) { |
4331 | + QString shutName = prefixIt.next(); |
4332 | + if (shutName!="") { |
4333 | + shutName.append(".shutdown"); |
4334 | + qDebug() << "MidiMapping: Executing" << shutName; |
4335 | + if (!m_pScriptEngine->execute(shutName)) |
4336 | + qWarning() << "MidiMapping: No" << shutName << "function in script"; |
4337 | + } |
4338 | + } |
4339 | + qDebug() << "MidiMapping: Deleting MIDI script engine..."; |
4340 | + MidiScriptEngine *engine = m_pScriptEngine; |
4341 | + m_pScriptEngine = NULL; |
4342 | + delete engine; |
4343 | + } |
4344 | +} |
4345 | +#endif |
4346 | + |
4347 | +void MidiMapping::setOutputMidiDevice(MidiDevice* outputMidiDevice) |
4348 | +{ |
4349 | + m_mappingLock.lock(); |
4350 | + m_pOutputMidiDevice = outputMidiDevice; |
4351 | + m_mappingLock.unlock(); |
4352 | +#ifdef __MIDISCRIPT__ |
4353 | + //Restart the script engine so it gets its pointer to the output MIDI device updated. |
4354 | + restartScriptEngine(); |
4355 | #endif |
4356 | } |
4357 | |
4358 | @@ -384,34 +484,41 @@ |
4359 | } |
4360 | #endif |
4361 | |
4362 | -/* loadInitialPreset() |
4363 | - * Loads a set of MIDI bindings from either the default file or one specified on the command line. |
4364 | +/* setName(QString) |
4365 | + * Sets the controller name this mapping corresponds to |
4366 | + * @param name The controller name this mapping is hooked to |
4367 | */ |
4368 | -void MidiMapping::loadInitialPreset() { |
4369 | - // Try to read in the current XML bindings file, one from the command line, or create one if nothing is available |
4370 | - QStringList commandLineArgs = QApplication::arguments(); |
4371 | - int loadXML = commandLineArgs.indexOf("--loadXMLfile"); |
4372 | +void MidiMapping::setName(QString name) { |
4373 | + m_deviceName = name; |
4374 | +} |
4375 | |
4376 | - if (loadXML!=-1) { |
4377 | - qDebug() << "MidiMapping: Loading custom MIDI mapping file:" << commandLineArgs.at(loadXML+1); |
4378 | - loadPreset(commandLineArgs.at(loadXML+1)); |
4379 | - } |
4380 | - else loadPreset(BINDINGS_PATH); |
4381 | - applyPreset(); |
4382 | +/* loadPreset() |
4383 | + * Overloaded function for convenience, uses the default device path |
4384 | + * @param forceLoad Forces the MIDI mapping to be loaded, regardless of whether or not the controller id |
4385 | + * specified in the mapping matches the device this MidiMapping object is hooked up to. |
4386 | + */ |
4387 | +void MidiMapping::loadPreset(bool forceLoad) { |
4388 | + loadPreset(DEFAULT_DEVICE_PRESET, forceLoad); |
4389 | } |
4390 | |
4391 | /* loadPreset(QString) |
4392 | * Overloaded function for convenience |
4393 | + * @param path The path to a MIDI mapping XML file. |
4394 | + * @param forceLoad Forces the MIDI mapping to be loaded, regardless of whether or not the controller id |
4395 | + * specified in the mapping matches the device this MidiMapping object is hooked up to. |
4396 | */ |
4397 | -void MidiMapping::loadPreset(QString path) { |
4398 | - qDebug() << "MidiMapping: Loading MIDI XML from" << path; |
4399 | - loadPreset(WWidget::openXMLFile(path, "controller")); |
4400 | +void MidiMapping::loadPreset(QString path, bool forceLoad) { |
4401 | + qDebug() << "MidiMapping: Loading MIDI preset from" << path; |
4402 | + loadPreset(WWidget::openXMLFile(path, "controller"), forceLoad); |
4403 | } |
4404 | |
4405 | /* loadPreset(QDomElement) |
4406 | * Loads a set of MIDI bindings from a QDomElement structure. |
4407 | + * @param root The root node of the XML document for the MIDI mapping. |
4408 | + * @param forceLoad Forces the MIDI mapping to be loaded, regardless of whether or not the controller id |
4409 | + * specified in the mapping matches the device this MidiMapping object is hooked up to. |
4410 | */ |
4411 | -void MidiMapping::loadPreset(QDomElement root) { |
4412 | +void MidiMapping::loadPreset(QDomElement root, bool forceLoad) { |
4413 | //qDebug() << QString("MidiMapping: loadPreset() called in thread ID=%1").arg(this->thread()->currentThreadId(),0,16); |
4414 | |
4415 | if (root.isNull()) return; |
4416 | @@ -426,8 +533,6 @@ |
4417 | m_mappingLock.lock(); |
4418 | |
4419 | #ifdef __MIDISCRIPT__ |
4420 | - m_rMidiObject.restartScriptEngine(); |
4421 | - m_pScriptEngine = m_rMidiObject.getMidiScriptEngine(); |
4422 | m_pScriptFileNames.clear(); |
4423 | m_pScriptFunctionPrefixes.clear(); |
4424 | #endif |
4425 | @@ -435,19 +540,31 @@ |
4426 | // For each controller in the DOM |
4427 | m_Bindings = root; |
4428 | QDomElement controller = m_Bindings.firstChildElement("controller"); |
4429 | + |
4430 | + // For each controller in the MIDI mapping XML... |
4431 | + //(Only parse the <controller> block if it's id matches our device name, otherwise |
4432 | + //keep looking at the next controller blocks....) |
4433 | + QString device; |
4434 | while (!controller.isNull()) { |
4435 | - // For each controller |
4436 | // Get deviceid |
4437 | - QString device = controller.attribute("id",""); |
4438 | - qDebug() << device << " settings found" << endl; |
4439 | - |
4440 | + device = controller.attribute("id",""); |
4441 | + if (device != m_deviceName && !forceLoad) { |
4442 | + controller = controller.nextSiblingElement("controller"); |
4443 | + } |
4444 | + else |
4445 | + break; |
4446 | + } |
4447 | + |
4448 | + if (!controller.isNull()) { |
4449 | + |
4450 | + qDebug() << device << " settings found"; |
4451 | #ifdef __MIDISCRIPT__ |
4452 | + // Build a list of MIDI script files to load |
4453 | |
4454 | - // Get a list of MIDI script files to load |
4455 | QDomElement scriptFile = controller.firstChildElement("scriptfiles").firstChildElement("file"); |
4456 | |
4457 | // Default currently required file |
4458 | - addScriptFile(REQUIRED_MAPPING_FILE,""); |
4459 | + addScriptFile(REQUIRED_SCRIPT_FILE,""); |
4460 | |
4461 | // Look for additional ones |
4462 | while (!scriptFile.isNull()) { |
4463 | @@ -458,33 +575,7 @@ |
4464 | scriptFile = scriptFile.nextSiblingElement("file"); |
4465 | } |
4466 | |
4467 | - // Load Script files |
4468 | - ConfigObject<ConfigValue> *config = new ConfigObject<ConfigValue>(QDir::homePath().append("/").append(SETTINGS_PATH).append(SETTINGS_FILE)); |
4469 | - |
4470 | - qDebug() << "MidiMapping: Loading & evaluating all MIDI script code"; |
4471 | - |
4472 | - QListIterator<QString> it(m_pScriptFileNames); |
4473 | - while (it.hasNext()) { |
4474 | - QString curScriptFileName = it.next(); |
4475 | - m_pScriptEngine->evaluate(config->getConfigPath().append("midi/").append(curScriptFileName)); |
4476 | - |
4477 | - if(m_pScriptEngine->hasErrors(curScriptFileName)) { |
4478 | - qDebug() << "Errors occured while loading " << curScriptFileName; |
4479 | - } |
4480 | - |
4481 | - } |
4482 | - |
4483 | - // Call each script's init function if it exists |
4484 | - QListIterator<QString> prefixIt(m_pScriptFunctionPrefixes); |
4485 | - while (prefixIt.hasNext()) { |
4486 | - QString initName = prefixIt.next(); |
4487 | - if (initName!="") { |
4488 | - initName.append(".init"); |
4489 | - qDebug() << "MidiMapping: Executing" << initName; |
4490 | - if (!m_pScriptEngine->execute(initName, device)) |
4491 | - qWarning() << "MidiMapping: No" << initName << "function in script"; |
4492 | - } |
4493 | - } |
4494 | + loadScriptCode(); // Actually load code from the list built above |
4495 | |
4496 | QStringList scriptFunctions = m_pScriptEngine->getScriptFunctions(); |
4497 | |
4498 | @@ -543,25 +634,33 @@ |
4499 | } |
4500 | |
4501 | qDebug() << "MidiMapping: Output parsed!"; |
4502 | - controller = controller.nextSiblingElement("controller"); |
4503 | + //controller = controller.nextSiblingElement("controller"); //FIXME: Remove this line of code permanently - Albert |
4504 | } |
4505 | |
4506 | m_mappingLock.unlock(); |
4507 | |
4508 | } // END loadPreset(QDomElement) |
4509 | |
4510 | +/* savePreset() |
4511 | + * Saves the current table of bindings to the default device XML file. |
4512 | + */ |
4513 | +void MidiMapping::savePreset() { |
4514 | + savePreset(DEFAULT_DEVICE_PRESET); |
4515 | +} |
4516 | + |
4517 | /* savePreset(QString) |
4518 | * Given a path, saves the current table of bindings to an XML file. |
4519 | */ |
4520 | void MidiMapping::savePreset(QString path) { |
4521 | + qDebug() << "Writing MIDI preset file" << path; |
4522 | m_mappingLock.lock(); |
4523 | QFile output(path); |
4524 | if (!output.open(QIODevice::WriteOnly | QIODevice::Truncate)) return; |
4525 | QTextStream outputstream(&output); |
4526 | // Construct the DOM from the table |
4527 | - buildDomElement(); |
4528 | + QDomDocument docBindings = buildDomElement(); |
4529 | // Save the DOM to the XML file |
4530 | - m_Bindings.save(outputstream, 4); |
4531 | + docBindings.save(outputstream, 4); |
4532 | output.close(); |
4533 | m_mappingLock.unlock(); |
4534 | } |
4535 | @@ -571,21 +670,36 @@ |
4536 | * the LED handler. |
4537 | */ |
4538 | void MidiMapping::applyPreset() { |
4539 | + qDebug() << "MidiMapping::applyPreset()"; |
4540 | m_mappingLock.lock(); |
4541 | MidiLedHandler::destroyHandlers(); |
4542 | |
4543 | - QDomElement controller = m_Bindings.firstChildElement("controller"); |
4544 | - // For each device |
4545 | - while (!controller.isNull()) { |
4546 | - // Device Outputs - LEDs |
4547 | - QString deviceId = controller.attribute("id",""); |
4548 | - |
4549 | - qDebug() << "MidiMapping: Processing MIDI Output Bindings for" << deviceId; |
4550 | - MidiLedHandler::createHandlers(controller.namedItem("outputs").firstChild(), |
4551 | - m_rMidiObject, deviceId); |
4552 | - |
4553 | - // Next device |
4554 | - controller = controller.nextSiblingElement("controller"); |
4555 | +#ifdef __MIDISCRIPT__ |
4556 | + // Since this can be called after re-enabling a device without reloading the XML preset, |
4557 | + // the script engine must have its code loaded here as well |
4558 | + QStringList scriptFunctions = m_pScriptEngine->getScriptFunctions(); |
4559 | + if (scriptFunctions.isEmpty()) loadScriptCode(); |
4560 | + |
4561 | + initializeScripts(); |
4562 | +#endif |
4563 | + |
4564 | + if (m_pOutputMidiDevice != NULL) { |
4565 | + //^^^ Only execute this code if we have an output device hooked up |
4566 | + // to this MidiMapping... |
4567 | + |
4568 | + QDomElement controller = m_Bindings.firstChildElement("controller"); |
4569 | + // For each device |
4570 | + while (!controller.isNull()) { |
4571 | + // Device Outputs - LEDs |
4572 | + QString deviceId = controller.attribute("id",""); |
4573 | + |
4574 | + qDebug() << "MidiMapping: Processing MIDI Output Bindings for" << deviceId; |
4575 | + MidiLedHandler::createHandlers(controller.namedItem("outputs").firstChild(), |
4576 | + *m_pOutputMidiDevice); |
4577 | + |
4578 | + // Next device |
4579 | + controller = controller.nextSiblingElement("controller"); |
4580 | + } |
4581 | } |
4582 | m_mappingLock.unlock(); |
4583 | } |
4584 | @@ -606,69 +720,96 @@ |
4585 | /* buildDomElement() |
4586 | * Updates the DOM with what is currently in the table |
4587 | */ |
4588 | - void MidiMapping::buildDomElement() { |
4589 | - // We should hold the mapping lock. |
4590 | - |
4591 | - clearPreset(); // Create blank document |
4592 | - |
4593 | - const QString wtfbbqdevicename = "Last used"; |
4594 | + QDomDocument MidiMapping::buildDomElement() { |
4595 | + // We should hold the mapping lock. The lock is recursive so if we already |
4596 | + // hold it it will relock. |
4597 | + m_mappingLock.lock(); |
4598 | + |
4599 | + clearPreset(); // Create blank document |
4600 | + |
4601 | + QDomDocument doc("Bindings"); |
4602 | + QString blank = "<MixxxMIDIPreset schemaVersion=\"" + QString(XML_SCHEMA_VERSION) + "\" mixxxVersion=\"" + QString(VERSION) + "+\">\n" |
4603 | + "</MixxxMIDIPreset>\n"; |
4604 | + |
4605 | + doc.setContent(blank); |
4606 | + |
4607 | + QDomElement rootNode = doc.documentElement(); |
4608 | + QDomElement controller = doc.createElement("controller"); |
4609 | + controller.setAttribute("id", m_deviceName.right(m_deviceName.size()-m_deviceName.indexOf(" ")-1)); |
4610 | + rootNode.appendChild(controller); |
4611 | + |
4612 | #ifdef __MIDISCRIPT__ |
4613 | - //This sucks, put this code inside MidiScriptEngine instead of here, |
4614 | - // and just ask MidiScriptEngine to spit it out for us. |
4615 | - qDebug() << "MidiMapping: Writing script block!"; |
4616 | - for (int i = 0; i < m_pScriptFileNames.count(); i++) { |
4617 | - qDebug() << "MidiMapping: writing script block for" << m_pScriptFileNames[i]; |
4618 | - QString filename = m_pScriptFileNames[i]; |
4619 | - if (filename != REQUIRED_MAPPING_FILE) { //Don't need to write anything for the required mapping file. |
4620 | - QString functionPrefix = m_pScriptFunctionPrefixes[i]; |
4621 | - //and now for the worst XML code since... WWidget... |
4622 | - QDomDocument sucksBalls; |
4623 | - QDomElement scriptFile = sucksBalls.createElement("file"); |
4624 | - scriptFile.setAttribute("filename", filename); |
4625 | - scriptFile.setAttribute("functionprefix", functionPrefix); |
4626 | - |
4627 | - //Add the XML dom element to the right spot in the XML document. |
4628 | - addMidiScriptInfo(scriptFile, wtfbbqdevicename); |
4629 | - } |
4630 | - } |
4631 | + //This sucks, put this code inside MidiScriptEngine instead of here, |
4632 | + // and just ask MidiScriptEngine to spit it out for us. |
4633 | +// qDebug() << "MidiMapping: Writing script block!"; |
4634 | + |
4635 | + QDomElement scriptFiles = doc.createElement("scriptfiles"); |
4636 | + controller.appendChild(scriptFiles); |
4637 | + |
4638 | + |
4639 | + for (int i = 0; i < m_pScriptFileNames.count(); i++) { |
4640 | +// qDebug() << "MidiMapping: writing script block for" << m_pScriptFileNames[i]; |
4641 | + QString filename = m_pScriptFileNames[i]; |
4642 | + |
4643 | + |
4644 | + //Don't need to write anything for the required mapping file. |
4645 | + if (filename != REQUIRED_SCRIPT_FILE) { |
4646 | + qDebug() << "MidiMapping: writing script block for" << filename; |
4647 | + QString functionPrefix = m_pScriptFunctionPrefixes[i]; |
4648 | + QDomElement scriptFile = doc.createElement("file"); |
4649 | + |
4650 | + |
4651 | + scriptFile.setAttribute("filename", filename); |
4652 | + scriptFile.setAttribute("functionprefix", functionPrefix); |
4653 | + |
4654 | + scriptFiles.appendChild(scriptFile); |
4655 | + } |
4656 | + } |
4657 | #endif |
4658 | |
4659 | + QDomElement controls = doc.createElement("controls"); |
4660 | + controller.appendChild(controls); |
4661 | + |
4662 | + |
4663 | //Iterate over all of the command/control pairs in the input mapping |
4664 | - QHashIterator<MidiMessage, MixxxControl> it(m_inputMapping); |
4665 | - while (it.hasNext()) { |
4666 | - it.next(); |
4667 | - QDomElement controlNode; |
4668 | - QDomDocument nodeMaker; |
4669 | - |
4670 | - //Create <control> block |
4671 | - controlNode = nodeMaker.createElement("control"); |
4672 | - |
4673 | - //Save the MidiMessage and MixxxControl objects as XML |
4674 | - it.key().serializeToXML(controlNode); |
4675 | - it.value().serializeToXML(controlNode); |
4676 | - |
4677 | - //Add the control node we just created to the XML document in the proper spot |
4678 | - addControl(controlNode, wtfbbqdevicename); //FIXME: Remove this device shit until we have multiple device support. |
4679 | - } |
4680 | - |
4681 | - //Iterate over all of the control/command pairs in the OUTPUT mapping |
4682 | - QHashIterator<MixxxControl, MidiMessage> outIt(m_outputMapping); |
4683 | - while (outIt.hasNext()) { |
4684 | - outIt.next(); |
4685 | - QDomElement outputNode; |
4686 | - QDomDocument nodeMaker; |
4687 | - |
4688 | - //Create <output> block |
4689 | - outputNode = nodeMaker.createElement("output"); |
4690 | - |
4691 | - //Save the MidiMessage and MixxxControl objects as XML |
4692 | - outIt.key().serializeToXML(outputNode, true); |
4693 | - outIt.value().serializeToXML(outputNode, true); |
4694 | - |
4695 | - //Add the control node we just created to the XML document in the proper spot |
4696 | - addOutput(outputNode, wtfbbqdevicename); //FIXME: Remove this device shit until we have multiple device support. |
4697 | - } |
4698 | - } |
4699 | + QHashIterator<MidiMessage, MixxxControl> it(m_inputMapping); |
4700 | + while (it.hasNext()) { |
4701 | + it.next(); |
4702 | + |
4703 | + QDomElement controlNode = doc.createElement("control"); |
4704 | + |
4705 | + //Save the MidiMessage and MixxxControl objects as XML |
4706 | + it.key().serializeToXML(controlNode); |
4707 | + it.value().serializeToXML(controlNode); |
4708 | + |
4709 | + //Add the control node we just created to the XML document in the proper spot |
4710 | + controls.appendChild(controlNode); |
4711 | + } |
4712 | + |
4713 | + |
4714 | + QDomElement outputs = doc.createElement("outputs"); |
4715 | + controller.appendChild(outputs); |
4716 | + |
4717 | + //Iterate over all of the control/command pairs in the OUTPUT mapping |
4718 | + QHashIterator<MixxxControl, MidiMessage> outIt(m_outputMapping); |
4719 | + while (outIt.hasNext()) { |
4720 | + outIt.next(); |
4721 | + |
4722 | + QDomElement outputNode = doc.createElement("output"); |
4723 | + |
4724 | + |
4725 | + //Save the MidiMessage and MixxxControl objects as XML |
4726 | + outIt.key().serializeToXML(outputNode, true); |
4727 | + outIt.value().serializeToXML(outputNode, true); |
4728 | + |
4729 | + //Add the control node we just created to the XML document in the proper spot |
4730 | + outputs.appendChild(outputNode); |
4731 | + } |
4732 | + |
4733 | + m_mappingLock.unlock(); |
4734 | + |
4735 | + return doc; |
4736 | +} |
4737 | |
4738 | /* -------- ------------------------------------------------------ |
4739 | Purpose: Adds an input MIDI mapping block to the XML. |
4740 | @@ -954,3 +1095,23 @@ |
4741 | m_controlToLearn = MixxxControl(); |
4742 | m_mappingLock.unlock(); |
4743 | } |
4744 | + |
4745 | +#ifdef __MIDISCRIPT__ |
4746 | +void MidiMapping::restartScriptEngine() |
4747 | +{ |
4748 | + shutdownScriptEngine(); |
4749 | + startupScriptEngine(); |
4750 | +} |
4751 | +#endif |
4752 | + |
4753 | +void MidiMapping::slotScriptEngineReady() { |
4754 | +#ifdef __MIDISCRIPT__ // Can't ifdef slots in the .h file, so we just do the body. |
4755 | + |
4756 | + // The lock prevents us from waking before the main thread is waiting on the |
4757 | + // condition. |
4758 | + m_scriptEngineInitializedMutex.lock(); |
4759 | + m_scriptEngineInitializedCondition.wakeAll(); |
4760 | + m_scriptEngineInitializedMutex.unlock(); |
4761 | + |
4762 | +#endif |
4763 | +} |
4764 | |
4765 | === renamed file 'mixxx/src/midimapping.h' => 'mixxx/src/midi/midimapping.h' |
4766 | --- mixxx/src/midimapping.h 2009-07-15 02:32:40 +0000 |
4767 | +++ mixxx/src/midi/midimapping.h 2009-11-10 00:33:17 +0000 |
4768 | @@ -4,6 +4,7 @@ |
4769 | ------------------- |
4770 | begin : Sat Jan 17 2009 |
4771 | copyright : (C) 2009 Sean M. Pappalardo |
4772 | + (C) 2009 Albert Santoni |
4773 | email : pegasus@c64.org |
4774 | |
4775 | ***************************************************************************/ |
4776 | @@ -20,7 +21,7 @@ |
4777 | #ifndef MIDIMAPPING_H |
4778 | #define MIDIMAPPING_H |
4779 | |
4780 | -#include "midiobject.h" |
4781 | +#include "mididevice.h" |
4782 | #include "midimessage.h" |
4783 | #include "mixxxcontrol.h" |
4784 | #include "midiinputmapping.h" |
4785 | @@ -28,14 +29,14 @@ |
4786 | #include <QTableWidget> |
4787 | |
4788 | #ifdef __MIDISCRIPT__ |
4789 | -#include "script/midiscriptengine.h" |
4790 | +#include "midiscriptengine.h" |
4791 | #endif |
4792 | |
4793 | //Forward declarations |
4794 | class MidiInputMappingTableModel; |
4795 | class MidiOutputMappingTableModel; |
4796 | |
4797 | -#define BINDINGS_PATH QDir::homePath().append("/").append(SETTINGS_PATH).append("MixxxMIDIBindings.xml") |
4798 | +#define BINDINGS_PATH QDir::homePath().append("/").append(SETTINGS_PATH).append("midi/") |
4799 | #define MIDI_MAPPING_EXTENSION ".midi.xml" |
4800 | |
4801 | class MidiMapping : public QObject |
4802 | @@ -44,14 +45,18 @@ |
4803 | |
4804 | public: |
4805 | /** Constructor also loads & applies the default XML MIDI mapping file */ |
4806 | - MidiMapping(MidiObject &midi_object); |
4807 | + MidiMapping(MidiDevice* outputMidiDevice=NULL); |
4808 | ~MidiMapping(); |
4809 | - |
4810 | - void loadInitialPreset(); |
4811 | - void loadPreset(QString path); |
4812 | - void loadPreset(QDomElement root); |
4813 | - |
4814 | - void savePreset(QString path = BINDINGS_PATH); |
4815 | + void setOutputMidiDevice(MidiDevice* outputMidiDevice); |
4816 | + |
4817 | + void setName(QString name); |
4818 | + |
4819 | + void loadPreset(bool forceLoad=false); |
4820 | + void loadPreset(QString path, bool forceLoad=false); |
4821 | + void loadPreset(QDomElement root, bool forceLoad=false); |
4822 | + |
4823 | + void savePreset(); |
4824 | + void savePreset(QString path); |
4825 | void applyPreset(); |
4826 | |
4827 | |
4828 | @@ -87,12 +92,20 @@ |
4829 | void clearOutputMidiMapping(int index); |
4830 | void clearOutputMidiMapping(MixxxControl control); |
4831 | void clearOutputMidiMapping(int index, int count); |
4832 | +#ifdef __MIDISCRIPT__ |
4833 | + void initializeScripts(); |
4834 | + void startupScriptEngine(); |
4835 | + void shutdownScriptEngine(); |
4836 | + void restartScriptEngine(); |
4837 | + MidiScriptEngine *getMidiScriptEngine() { return m_pScriptEngine; }; |
4838 | +#endif |
4839 | |
4840 | public slots: |
4841 | void finishMidiLearn(MidiMessage message); |
4842 | void beginMidiLearn(MixxxControl control); |
4843 | void cancelMidiLearn(); |
4844 | - |
4845 | + void slotScriptEngineReady(); |
4846 | + |
4847 | signals: |
4848 | void inputMappingChanged(); |
4849 | void inputMappingChanged(int startIndex, int endIndex); |
4850 | @@ -115,7 +128,7 @@ |
4851 | MidiMessage command, |
4852 | bool shouldEmit); |
4853 | void clearPreset(); |
4854 | - void buildDomElement(); |
4855 | + QDomDocument buildDomElement(); |
4856 | void addControl(QDomElement& control, QString device); |
4857 | void addOutput(QDomElement& output, QString device); |
4858 | void addMidiScriptInfo(QDomElement &scriptFile, QString device); //Sucks |
4859 | @@ -129,19 +142,27 @@ |
4860 | #ifdef __MIDISCRIPT__ |
4861 | /** Adds a script file name and function prefix to the list to be loaded */ |
4862 | void addScriptFile(QString filename, QString functionprefix); |
4863 | + /** Actually loads script code from the files in the list */ |
4864 | + void loadScriptCode(); |
4865 | |
4866 | QList<QString> m_pScriptFileNames; |
4867 | QList<QString> m_pScriptFunctionPrefixes; |
4868 | MidiScriptEngine *m_pScriptEngine; |
4869 | + |
4870 | + QMutex m_scriptEngineInitializedMutex; |
4871 | + QWaitCondition m_scriptEngineInitializedCondition; |
4872 | #endif |
4873 | QMutex m_mappingLock; |
4874 | QDomElement m_Bindings; |
4875 | - MidiObject &m_rMidiObject; |
4876 | MidiInputMapping m_inputMapping; |
4877 | MidiOutputMapping m_outputMapping; |
4878 | MidiInputMappingTableModel* m_pMidiInputMappingTableModel; |
4879 | MidiOutputMappingTableModel* m_pMidiOutputMappingTableModel; |
4880 | MixxxControl m_controlToLearn; |
4881 | + QString m_deviceName; /** Name of the device to look for in the <controller> XML nodes... */ |
4882 | + MidiDevice* m_pOutputMidiDevice; /** We need a pointer back to an _output_ MIDI device so our |
4883 | + LED handlers know where to fire MIDI messages. Note that |
4884 | + this can be NULL if there is no output MIDI device! */ |
4885 | }; |
4886 | |
4887 | #endif |
4888 | |
4889 | === renamed file 'mixxx/src/midimessage.cpp' => 'mixxx/src/midi/midimessage.cpp' |
4890 | === renamed file 'mixxx/src/midimessage.h' => 'mixxx/src/midi/midimessage.h' |
4891 | --- mixxx/src/midimessage.h 2009-04-19 17:25:23 +0000 |
4892 | +++ mixxx/src/midi/midimessage.h 2009-11-10 00:33:17 +0000 |
4893 | @@ -16,6 +16,18 @@ |
4894 | MIDI_STATUS_PITCH_BEND = 0xE0, |
4895 | } MidiStatusByte; |
4896 | |
4897 | + // This enum is used in the decoding of the status message into voice categories |
4898 | +// This is just cruft leftover from ages ago, unfortunately. Hasn't been properly removed/refactored yet. |
4899 | +typedef enum { |
4900 | + NOTE_OFF = 0x80, |
4901 | + NOTE_ON = 0x90, |
4902 | + AFTERTOUCH = 0xA0, |
4903 | + CTRL_CHANGE = 0xB0, |
4904 | + PROG_CHANGE = 0xC0, |
4905 | + CHANNEL_PRESSURE = 0xD0, |
4906 | + PITCH_WHEEL = 0xE0, |
4907 | +} MidiCategory; |
4908 | + |
4909 | /** The key used in the MIDI mapping hash table */ |
4910 | class MidiMessage |
4911 | { |
4912 | |
4913 | === renamed file 'mixxx/src/midinodelegate.cpp' => 'mixxx/src/midi/midinodelegate.cpp' |
4914 | === renamed file 'mixxx/src/midinodelegate.h' => 'mixxx/src/midi/midinodelegate.h' |
4915 | === renamed file 'mixxx/src/midioptiondelegate.cpp' => 'mixxx/src/midi/midioptiondelegate.cpp' |
4916 | === renamed file 'mixxx/src/midioptiondelegate.h' => 'mixxx/src/midi/midioptiondelegate.h' |
4917 | === renamed file 'mixxx/src/midioutputmapping.h' => 'mixxx/src/midi/midioutputmapping.h' |
4918 | === renamed file 'mixxx/src/midioutputmappingtablemodel.cpp' => 'mixxx/src/midi/midioutputmappingtablemodel.cpp' |
4919 | === renamed file 'mixxx/src/midioutputmappingtablemodel.h' => 'mixxx/src/midi/midioutputmappingtablemodel.h' |
4920 | === renamed file 'mixxx/src/script/midiscriptengine.cpp' => 'mixxx/src/midi/midiscriptengine.cpp' |
4921 | --- mixxx/src/script/midiscriptengine.cpp 2009-08-11 04:38:51 +0000 |
4922 | +++ mixxx/src/midi/midiscriptengine.cpp 2009-11-10 00:33:17 +0000 |
4923 | @@ -16,11 +16,11 @@ |
4924 | * * |
4925 | ***************************************************************************/ |
4926 | |
4927 | -#include <qapplication.h> |
4928 | - |
4929 | -#include "midiscriptengine.h" |
4930 | #include "controlobject.h" |
4931 | #include "controlobjectthread.h" |
4932 | +#include "mididevice.h" |
4933 | +#include "midiscriptengine.h" |
4934 | + |
4935 | |
4936 | #ifdef _MSC_VER |
4937 | #include <float.h> // for _isnan() on VC++ |
4938 | @@ -30,9 +30,9 @@ |
4939 | #endif |
4940 | |
4941 | |
4942 | -MidiScriptEngine::MidiScriptEngine(MidiObject* midi_object) : |
4943 | +MidiScriptEngine::MidiScriptEngine(MidiDevice* midiDevice) : |
4944 | m_pEngine(NULL), |
4945 | - m_pMidiObject(midi_object) |
4946 | + m_pMidiDevice(midiDevice) |
4947 | { |
4948 | } |
4949 | |
4950 | @@ -88,11 +88,13 @@ |
4951 | //qDebug() << "MidiScriptEngine::run() m_pEngine->parent() is " << m_pEngine->parent(); |
4952 | //qDebug() << "MidiScriptEngine::run() m_pEngine->thread() is " << m_pEngine->thread(); |
4953 | |
4954 | + qDebug() << "MIDI Device in script engine is:" << m_pMidiDevice->getName(); |
4955 | + |
4956 | // Make this MidiScriptEngine instance available to scripts as |
4957 | // 'engine'. |
4958 | QScriptValue engineGlobalObject = m_pEngine->globalObject(); |
4959 | engineGlobalObject.setProperty("engine", m_pEngine->newQObject(this)); |
4960 | - engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pMidiObject)); |
4961 | + engineGlobalObject.setProperty("midi", m_pEngine->newQObject(m_pMidiDevice)); |
4962 | |
4963 | } |
4964 | |
4965 | @@ -162,9 +164,10 @@ |
4966 | -------- ------------------------------------------------------ */ |
4967 | bool MidiScriptEngine::execute(QString function, char channel, |
4968 | char control, char value, |
4969 | - MidiStatusByte status) { |
4970 | + MidiStatusByte status, |
4971 | + QString group) { |
4972 | m_scriptEngineLock.lock(); |
4973 | - bool ret = safeExecute(function, channel, control, value, status); |
4974 | + bool ret = safeExecute(function, channel, control, value, status, group); |
4975 | m_scriptEngineLock.unlock(); |
4976 | return ret; |
4977 | } |
4978 | @@ -237,7 +240,8 @@ |
4979 | -------- ------------------------------------------------------ */ |
4980 | bool MidiScriptEngine::safeExecute(QString function, char channel, |
4981 | char control, char value, |
4982 | - MidiStatusByte status) { |
4983 | + MidiStatusByte status, |
4984 | + QString group) { |
4985 | //qDebug() << QString("MidiScriptEngine: Exec2 Thread ID=%1").arg(QThread::currentThreadId(),0,16); |
4986 | |
4987 | if(m_pEngine == NULL) { |
4988 | @@ -261,6 +265,7 @@ |
4989 | args << QScriptValue(m_pEngine, control); |
4990 | args << QScriptValue(m_pEngine, value); |
4991 | args << QScriptValue(m_pEngine, status); |
4992 | + args << QScriptValue(m_pEngine, group); |
4993 | |
4994 | scriptFunction.call(QScriptValue(), args); |
4995 | if (checkException()) |
4996 | @@ -338,7 +343,7 @@ |
4997 | |
4998 | if (line.indexOf('#') != 0 && line.indexOf("//") != 0) { // ignore commented out lines |
4999 | QStringList field = line.split(" "); |
5000 | - qDebug() << "MidiScriptEngine: Found function:" << field[0] << "at line" << position; |
Mutliple MIDI device support has been working for a few weeks now with limited testing by myself and Phillip. The essential user-facing GUI changes are now complete, so this is ready to be considered for merging to trunk in time for v1.8.