1 /* FluidSynth - A Software Synthesizer
2 *
3 * Copyright (C) 2003 Peter Hanappe and others.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA
19 */
20
21
22 /* fluid_winmidi.c
23 *
24 * Driver for Windows MIDI
25 *
26 * NOTE: Unfortunately midiInAddBuffer(), for SYSEX data, should not be called
27 * from within the MIDI input callback, despite many examples contrary to that
28 * on the Internet. Some MIDI devices will deadlock. Therefore we add MIDIHDR
29 * pointers to a queue and re-add them in a separate thread. Lame-o API! :(
30 *
31 * Multiple/single devices handling capabilities:
32 * This driver is able to handle multiple devices chosen by the user through
33 * the settings midi.winmidi.device.
34 * For example, let the following device names:
35 * 0:Port MIDI SB Live! [CE00], 1:SB PCI External MIDI, default, x[;y;z;..]
36 * Then the driver is able receive MIDI messages coming from distinct devices
37 * and forward these messages on distinct MIDI channels set.
38 * 1.1)For example, if the user chooses 2 devices at index 0 and 1, the user
39 * must specify this by putting the name "0;1" in midi.winmidi.device setting.
40 * We get a fictif device composed of real devices (0,1). This fictif device
41 * behaves like a device with 32 MIDI channels whose messages are forwarded to
42 * driver output as this:
43 * - MIDI messages from real device 0 are output to MIDI channels set 0 to 15.
44 * - MIDI messages from real device 1 are output to MIDI channels set 15 to 31.
45 *
46 * 1.2)Now another example with the name "1;0". The driver will forward
47 * MIDI messages as this:
48 * - MIDI messages from real device 1 are output to MIDI channels set 0 to 15.
49 * - MIDI messages from real device 0 are output to MIDI channels set 15 to 31.
50 * So, the device order specified in the setting allows the user to choose the
51 * MIDI channel set associated with this real device at the driver output
52 * according this formula: output_channel = input_channel + device_order * 16.
53 *
54 * 2)Note also that the driver handles single device by putting the device name
55 * in midi.winmidi.device setting.
56 * The user can set the device name "0:Port MIDI SB Live! [CE00]" in the setting.
57 * or use the multi device naming "0" (specifying only device index 0).
58 * Both naming choice allows the driver to handle the same single device.
59 *
60 */
61
62 #include "fluidsynth_priv.h"
63
64 #if WINMIDI_SUPPORT
65
66 #include "fluid_midi.h"
67 #include "fluid_mdriver.h"
68 #include "fluid_settings.h"
69
70 #define MIDI_SYSEX_MAX_SIZE 512
71 #define MIDI_SYSEX_BUF_COUNT 16
72
73 typedef struct fluid_winmidi_driver fluid_winmidi_driver_t;
74
75 /* device infos structure for only one midi device */
76 typedef struct device_infos
77 {
78 fluid_winmidi_driver_t *dev; /* driver structure*/
79 unsigned char midi_num; /* device order number */
80 unsigned char channel_map; /* MIDI channel mapping from input to output */
81 UINT dev_idx; /* device index */
82 HMIDIIN hmidiin; /* device handle */
83 /* MIDI HDR for SYSEX buffer */
84 MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT];
85 /* Sysex data buffer */
86 unsigned char sysExBuf[MIDI_SYSEX_BUF_COUNT * MIDI_SYSEX_MAX_SIZE];
87 } device_infos_t;
88
89 /* driver structure */
90 struct fluid_winmidi_driver
91 {
92 fluid_midi_driver_t driver;
93
94 /* Thread for SYSEX re-add thread */
95 HANDLE hThread;
96 DWORD dwThread;
97
98 /* devices information table */
99 int dev_count; /* device information count in dev_infos[] table */
100 device_infos_t dev_infos[1];
101 };
102
103 #define msg_type(_m) ((unsigned char)(_m & 0xf0))
104 #define msg_chan(_m) ((unsigned char)(_m & 0x0f))
105 #define msg_p1(_m) ((_m >> 8) & 0x7f)
106 #define msg_p2(_m) ((_m >> 16) & 0x7f)
107
108 static char *
fluid_winmidi_input_error(char * strError,MMRESULT no)109 fluid_winmidi_input_error(char *strError, MMRESULT no)
110 {
111 #ifdef _UNICODE
112 WCHAR wStr[MAXERRORLENGTH];
113
114 midiInGetErrorText(no, wStr, MAXERRORLENGTH);
115 WideCharToMultiByte(CP_UTF8, 0, wStr, -1, strError, MAXERRORLENGTH, 0, 0);
116 #else
117 midiInGetErrorText(no, strError, MAXERRORLENGTH);
118 #endif
119
120 return strError;
121 }
122
123 /*
124 callback function called by any MIDI device sending a MIDI message.
125 @param dwInstance, pointer on device_infos structure of this
126 device.
127 */
128 static void CALLBACK
fluid_winmidi_callback(HMIDIIN hmi,UINT wMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)129 fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
130 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
131 {
132 device_infos_t *dev_infos = (device_infos_t *) dwInstance;
133 fluid_winmidi_driver_t *dev = dev_infos->dev;
134 fluid_midi_event_t event;
135 LPMIDIHDR pMidiHdr;
136 unsigned char *data;
137 unsigned int msg_param = (unsigned int) dwParam1;
138
139 switch(wMsg)
140 {
141 case MIM_OPEN:
142 break;
143
144 case MIM_CLOSE:
145 break;
146
147 case MIM_DATA:
148 event.type = msg_type(msg_param);
149 event.channel = msg_chan(msg_param) + dev_infos->channel_map;
150
151 FLUID_LOG(FLUID_DBG, "\ndevice at index %d sending MIDI message on channel %d, forwarded on channel: %d",
152 dev_infos->dev_idx, msg_chan(msg_param), event.channel);
153
154 if(event.type != PITCH_BEND)
155 {
156 event.param1 = msg_p1(msg_param);
157 event.param2 = msg_p2(msg_param);
158 }
159 else /* Pitch bend is a 14 bit value */
160 {
161 event.param1 = (msg_p2(msg_param) << 7) | msg_p1(msg_param);
162 event.param2 = 0;
163 }
164
165 (*dev->driver.handler)(dev->driver.data, &event);
166 break;
167
168 case MIM_LONGDATA: /* SYSEX data */
169 FLUID_LOG(FLUID_DBG, "\ndevice at index %d sending MIDI sysex message",
170 dev_infos->dev_idx);
171
172 if(dev->hThread == NULL)
173 {
174 break;
175 }
176
177 pMidiHdr = (LPMIDIHDR)dwParam1;
178 data = (unsigned char *)(pMidiHdr->lpData);
179
180 /* We only process complete SYSEX messages (discard those that are too small or too large) */
181 if(pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0
182 && data[pMidiHdr->dwBytesRecorded - 1] == 0xF7)
183 {
184 fluid_midi_event_set_sysex(&event, pMidiHdr->lpData + 1,
185 pMidiHdr->dwBytesRecorded - 2, FALSE);
186 (*dev->driver.handler)(dev->driver.data, &event);
187 }
188
189 /* request the sysex thread to re-add this buffer into the device dev_infos->midi_num */
190 PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, dev_infos->midi_num, dwParam1);
191 break;
192
193 case MIM_ERROR:
194 break;
195
196 case MIM_LONGERROR:
197 break;
198
199 case MIM_MOREDATA:
200 break;
201 }
202 }
203
204 /**
205 * build a device name prefixed by its index. The format of the returned
206 * name is: dev_idx:dev_name
207 * The name returned is convenient for midi.winmidi.device setting.
208 * It allows the user to identify a device index through its name or vice
209 * versa. This allows the user to specify a multi device name using a list of
210 * devices index (see fluid_winmidi_midi_driver_settings()).
211 *
212 * @param dev_idx, device index.
213 * @param dev_name, name of the device.
214 * @return the new device name (that must be freed when finish with it) or
215 * NULL if memory allocation error.
216 */
fluid_winmidi_get_device_name(int dev_idx,char * dev_name)217 static char *fluid_winmidi_get_device_name(int dev_idx, char *dev_name)
218 {
219 char *new_dev_name;
220
221 int i = dev_idx;
222 size_t size = 0; /* index size */
223
224 do
225 {
226 size++;
227 i = i / 10 ;
228 }
229 while(i);
230
231 /* index size + separator + name length + zero termination */
232 new_dev_name = FLUID_MALLOC(size + 2 + FLUID_STRLEN(dev_name));
233
234 if(new_dev_name)
235 {
236 /* the name is filled if allocation is successful */
237 FLUID_SPRINTF(new_dev_name, "%d:%s", dev_idx, dev_name);
238 }
239 else
240 {
241 FLUID_LOG(FLUID_ERR, "Out of memory");
242 }
243
244 return new_dev_name;
245 }
246
247 /*
248 Add setting midi.winmidi.device in the settings.
249
250 MIDI devices names are enumerated and added to midi.winmidi.device setting
251 options. Example:
252 0:Port MIDI SB Live! [CE00], 1:SB PCI External MIDI, default, x[;y;z;..]
253
254 Devices name prefixed by index (i.e 1:SB PCI External MIDI) are real devices.
255 "default" name is the default device.
256 "x[;y;z;..]" is the multi device naming. Its purpose is to indicate to
257 the user how he must specify a multi device name in the setting.
258 A multi devices name must be a list of real devices index separated by semicolon:
259 Example: "5;3;0"
260 */
fluid_winmidi_midi_driver_settings(fluid_settings_t * settings)261 void fluid_winmidi_midi_driver_settings(fluid_settings_t *settings)
262 {
263 MMRESULT res;
264 MIDIINCAPS in_caps;
265 UINT i, num;
266
267 /* register midi.winmidi.device */
268 fluid_settings_register_str(settings, "midi.winmidi.device", "default", 0);
269 num = midiInGetNumDevs();
270
271 if(num > 0)
272 {
273 fluid_settings_add_option(settings, "midi.winmidi.device", "default");
274
275 /* add real devices names in options list */
276 for(i = 0; i < num; i++)
277 {
278 res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
279
280 if(res == MMSYSERR_NOERROR)
281 {
282 /* add new device name (prefixed by its index) */
283 char *new_dev_name = fluid_winmidi_get_device_name(i, in_caps.szPname);
284
285 if(!new_dev_name)
286 {
287 break;
288 }
289
290 fluid_settings_add_option(settings, "midi.winmidi.device",
291 new_dev_name);
292 FLUID_FREE(new_dev_name);
293 }
294 }
295 }
296 }
297
298 /* Thread for re-adding SYSEX buffers */
fluid_winmidi_add_sysex_thread(void * data)299 static DWORD WINAPI fluid_winmidi_add_sysex_thread(void *data)
300 {
301 fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *)data;
302 MSG msg;
303 int code;
304
305 for(;;)
306 {
307 code = GetMessage(&msg, NULL, 0, 0);
308
309 if(code < 0)
310 {
311 FLUID_LOG(FLUID_ERR, "fluid_winmidi_add_sysex_thread: GetMessage() failed.");
312 break;
313 }
314
315 if(msg.message == WM_CLOSE)
316 {
317 break;
318 }
319
320 switch(msg.message)
321 {
322 case MM_MIM_LONGDATA:
323 /* re-add the buffer into the device designed by msg.wParam parameter */
324 midiInAddBuffer(dev->dev_infos[msg.wParam].hmidiin,
325 (LPMIDIHDR)msg.lParam, sizeof(MIDIHDR));
326 break;
327 }
328 }
329
330 return 0;
331 }
332
333 /**
334 * Parse device name
335 * @param dev if not NULL pointer on driver structure in which device index
336 * are returned.
337 * @param dev_name device name which is expected to be:
338 * - a multi devices naming (i.e "1;0;2") or
339 * - a single device name (i.e "0:Port MIDI SB Live! [CE00]"
340 * @return count of devices parsed or 0 if device name doesn't exist.
341 */
342 static int
fluid_winmidi_parse_device_name(fluid_winmidi_driver_t * dev,char * dev_name)343 fluid_winmidi_parse_device_name(fluid_winmidi_driver_t *dev, char *dev_name)
344 {
345 int dev_count = 0; /* device count */
346 int dev_idx; /* device index */
347 char *cur_idx, *next_idx; /* current and next ascii index pointer */
348 char cpy_dev_name[MAXPNAMELEN];
349 int num = midiInGetNumDevs(); /* get number of real devices installed */
350
351 /* look for a multi device naming */
352 /* multi devices name "x;[y;..]". parse devices index: x;y;..
353 Each ascii index are separated by a semicolon character.
354 */
355 FLUID_STRCPY(cpy_dev_name, dev_name); /* fluid_strtok() will overwrite */
356 next_idx = cpy_dev_name;
357
358 while(NULL != (cur_idx = fluid_strtok(&next_idx, " ;")))
359 {
360 /* try to convert current ascii index */
361 char *end_idx = cur_idx;
362 dev_idx = FLUID_STRTOL(cur_idx, &end_idx, 10);
363
364 if(cur_idx == end_idx /* not an integer number */
365 || dev_idx < 0 /* invalid device index */
366 || dev_idx >= num /* invalid device index */
367 )
368 {
369 if(dev)
370 {
371 dev->dev_count = 0;
372 }
373
374 dev_count = 0; /* error, end of parsing */
375 break;
376 }
377
378 /* memorize device index in dev_infos table */
379 if(dev)
380 {
381 dev->dev_infos[dev->dev_count++].dev_idx = dev_idx;
382 }
383
384 dev_count++;
385 }
386
387 /* look for single device if multi devices not found */
388 if(!dev_count)
389 {
390 /* default device index: dev_idx = 0, dev_count = 1 */
391 dev_count = 1;
392 dev_idx = 0;
393
394 if(FLUID_STRCASECMP("default", dev_name) != 0)
395 {
396 int i;
397 dev_count = 0; /* reset count of devices found */
398
399 for(i = 0; i < num; i++)
400 {
401 char strError[MAXERRORLENGTH];
402 MIDIINCAPS in_caps;
403 MMRESULT res;
404 res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
405
406 if(res == MMSYSERR_NOERROR)
407 {
408 int str_cmp_res;
409 char *new_dev_name = fluid_winmidi_get_device_name(i, in_caps.szPname);
410
411 if(!new_dev_name)
412 {
413 break;
414 }
415
416 #ifdef _UNICODE
417 WCHAR wDevName[MAXPNAMELEN];
418 MultiByteToWideChar(CP_UTF8, 0, dev_name, -1, wDevName, MAXPNAMELEN);
419
420 str_cmp_res = wcsicmp(wDevName, new_dev_name);
421 #else
422 str_cmp_res = FLUID_STRCASECMP(dev_name, new_dev_name);
423 #endif
424
425 FLUID_LOG(FLUID_DBG, "Testing midi device \"%s\"", new_dev_name);
426 FLUID_FREE(new_dev_name);
427
428 if(str_cmp_res == 0)
429 {
430 FLUID_LOG(FLUID_DBG, "Selected midi device number: %u", i);
431 dev_idx = i;
432 dev_count = 1;
433 break;
434 }
435 }
436 else
437 {
438 FLUID_LOG(FLUID_DBG, "Error testing midi device %u of %u: %s (error %d)",
439 i, num, fluid_winmidi_input_error(strError, res), res);
440 }
441 }
442 }
443
444 if(dev && dev_count)
445 {
446 dev->dev_infos[0].dev_idx = dev_idx;
447 dev->dev_count = 1;
448 }
449 }
450
451 if(num < dev_count)
452 {
453 FLUID_LOG(FLUID_ERR, "not enough MIDI in devices found. Expected:%d found:%d",
454 dev_count, num);
455 dev_count = 0;
456 }
457
458 return dev_count;
459 }
460
461 /*
462 * new_fluid_winmidi_driver
463 */
464 fluid_midi_driver_t *
new_fluid_winmidi_driver(fluid_settings_t * settings,handle_midi_event_func_t handler,void * data)465 new_fluid_winmidi_driver(fluid_settings_t *settings,
466 handle_midi_event_func_t handler, void *data)
467 {
468 fluid_winmidi_driver_t *dev;
469 MMRESULT res;
470 int i, j;
471 int max_devices; /* maximum number of devices to handle */
472 char strError[MAXERRORLENGTH];
473 char dev_name[MAXPNAMELEN];
474
475 /* not much use doing anything */
476 if(handler == NULL)
477 {
478 FLUID_LOG(FLUID_ERR, "Invalid argument");
479 return NULL;
480 }
481
482 /* get the device name. if none is specified, use the default device. */
483 if(fluid_settings_copystr(settings, "midi.winmidi.device", dev_name, MAXPNAMELEN) != FLUID_OK)
484 {
485 FLUID_LOG(FLUID_DBG, "No MIDI in device selected, using \"default\"");
486 FLUID_STRCPY(dev_name, "default");
487 }
488
489 /* parse device name, get the maximum number of devices to handle */
490 max_devices = fluid_winmidi_parse_device_name(NULL, dev_name);
491
492 /* check if any device has be found */
493 if(!max_devices)
494 {
495 FLUID_LOG(FLUID_ERR, "Device \"%s\" does not exists", dev_name);
496 return NULL;
497 }
498
499 /* allocation of driver structure size dependent of max_devices */
500 i = sizeof(fluid_winmidi_driver_t) + (max_devices - 1) * sizeof(device_infos_t);
501 dev = FLUID_MALLOC(i);
502
503 if(dev == NULL)
504 {
505 return NULL;
506 }
507
508 FLUID_MEMSET(dev, 0, i); /* reset structure members */
509
510 /* parse device name, get devices index */
511 fluid_winmidi_parse_device_name(dev, dev_name);
512
513 dev->driver.handler = handler;
514 dev->driver.data = data;
515
516 /* try opening the devices */
517 for(i = 0; i < dev->dev_count; i++)
518 {
519 device_infos_t *dev_infos = &dev->dev_infos[i];
520 dev_infos->dev = dev; /* driver structure */
521 dev_infos->midi_num = i; /* device order number */
522 dev_infos->channel_map = i * 16; /* map from input to output */
523 FLUID_LOG(FLUID_DBG, "opening device at index %d", dev_infos->dev_idx);
524 res = midiInOpen(&dev_infos->hmidiin, dev_infos->dev_idx,
525 (DWORD_PTR) fluid_winmidi_callback,
526 (DWORD_PTR) dev_infos, CALLBACK_FUNCTION);
527
528 if(res != MMSYSERR_NOERROR)
529 {
530 FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)",
531 fluid_winmidi_input_error(strError, res), res);
532 goto error_recovery;
533 }
534
535 /* Prepare and add SYSEX buffers */
536 for(j = 0; j < MIDI_SYSEX_BUF_COUNT; j++)
537 {
538 MIDIHDR *hdr = &dev_infos->sysExHdrs[j];
539
540 hdr->lpData = (LPSTR)&dev_infos->sysExBuf[j * MIDI_SYSEX_MAX_SIZE];
541 hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE;
542
543 /* Prepare a buffer for SYSEX data and add it */
544 res = midiInPrepareHeader(dev_infos->hmidiin, hdr, sizeof(MIDIHDR));
545
546 if(res == MMSYSERR_NOERROR)
547 {
548 res = midiInAddBuffer(dev_infos->hmidiin, hdr, sizeof(MIDIHDR));
549
550 if(res != MMSYSERR_NOERROR)
551 {
552 FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
553 fluid_winmidi_input_error(strError, res), res);
554 midiInUnprepareHeader(dev_infos->hmidiin, hdr, sizeof(MIDIHDR));
555 }
556 }
557 else
558 FLUID_LOG(FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
559 fluid_winmidi_input_error(strError, res), res);
560 }
561 }
562
563
564 /* Create thread which processes re-adding SYSEX buffers */
565 dev->hThread = CreateThread(
566 NULL,
567 0,
568 (LPTHREAD_START_ROUTINE)
569 fluid_winmidi_add_sysex_thread,
570 dev,
571 0,
572 &dev->dwThread);
573
574 if(dev->hThread == NULL)
575 {
576 FLUID_LOG(FLUID_ERR, "Failed to create SYSEX buffer processing thread");
577 goto error_recovery;
578 }
579
580 /* Start the MIDI input interface */
581 for(i = 0; i < dev->dev_count; i++)
582 {
583 if(midiInStart(dev->dev_infos[i].hmidiin) != MMSYSERR_NOERROR)
584 {
585 FLUID_LOG(FLUID_ERR, "Failed to start the MIDI input. MIDI input not available.");
586 goto error_recovery;
587 }
588 }
589
590 return (fluid_midi_driver_t *) dev;
591
592 error_recovery:
593
594 delete_fluid_winmidi_driver((fluid_midi_driver_t *) dev);
595 return NULL;
596 }
597
598 /*
599 * delete_fluid_winmidi_driver
600 */
601 void
delete_fluid_winmidi_driver(fluid_midi_driver_t * p)602 delete_fluid_winmidi_driver(fluid_midi_driver_t *p)
603 {
604 int i, j;
605
606 fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *) p;
607 fluid_return_if_fail(dev != NULL);
608
609 /* request the sysex thread to terminate */
610 if(dev->hThread != NULL)
611 {
612 PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0);
613 WaitForSingleObject(dev->hThread, INFINITE);
614
615 CloseHandle(dev->hThread);
616 dev->hThread = NULL;
617 }
618
619 /* stop MIDI in devices and free allocated buffers */
620 for(i = 0; i < dev->dev_count; i++)
621 {
622 device_infos_t *dev_infos = &dev->dev_infos[i];
623
624 if(dev_infos->hmidiin != NULL)
625 {
626 /* stop the device and mark any pending data blocks as being done */
627 midiInReset(dev_infos->hmidiin);
628
629 /* free allocated buffers associated to this device */
630 for(j = 0; j < MIDI_SYSEX_BUF_COUNT; j++)
631 {
632 MIDIHDR *hdr = &dev_infos->sysExHdrs[j];
633
634 if((hdr->dwFlags & MHDR_PREPARED))
635 {
636 midiInUnprepareHeader(dev_infos->hmidiin, hdr, sizeof(MIDIHDR));
637 }
638 }
639
640 /* close the device */
641 midiInClose(dev_infos->hmidiin);
642 }
643 }
644
645 FLUID_FREE(dev);
646 }
647
648 #endif /* WINMIDI_SUPPORT */
649