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