fluid_winmidi.c
上传用户:tjmskj2
上传日期:2020-08-17
资源大小:577k
文件大小:11k
源码类别:

midi

开发平台:

C/C++

  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 Library General Public License
  7.  * as published by the Free Software Foundation; either version 2 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.  * Library General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Library General Public
  16.  * License along with this library; if not, write to the Free
  17.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  18.  * 02111-1307, USA
  19.  */
  20. /* fluid_winmidi.c
  21.  *
  22.  * Driver for Windows MIDI
  23.  *
  24.  * NOTE: Unfortunately midiInAddBuffer(), for SYSEX data, should not be called
  25.  * from within the MIDI input callback, despite many examples contrary to that
  26.  * on the Internet.  Some MIDI devices will deadlock.  Therefore we add MIDIHDR
  27.  * pointers to a queue and re-add them in a separate thread, using a mutex and
  28.  * conditional to wake up the thread.  Lame-o API! :(
  29.  */
  30. #include "fluidsynth_priv.h"
  31. #if WINMIDI_SUPPORT
  32. #include "fluid_midi.h"
  33. #include "fluid_mdriver.h"
  34. #include "fluid_settings.h"
  35. #define MIDI_SYSEX_MAX_SIZE     512
  36. #define MIDI_SYSEX_BUF_COUNT    16
  37. typedef struct {
  38.   fluid_midi_driver_t driver;
  39.   HMIDIIN hmidiin;
  40.   int closing;                  /* Set to TRUE when closing driver, to prevent endless SYSEX lockup loop */
  41.   fluid_thread_t *sysExAddThread;       /* Thread for SYSEX re-add thread */
  42.   fluid_cond_mutex_t *mutex;    /* Lock for condition */
  43.   fluid_cond_t *cond;           /* Condition to signal MIDI event thread of available events */
  44.   /* MIDI HDR for SYSEX buffer */
  45.   MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT];
  46.   /* TRUE for each MIDIHDR buffer which should be re-added to MIDI device */
  47.   int sysExHdrAdd[MIDI_SYSEX_BUF_COUNT];
  48.   /* Sysex data buffer */
  49.   unsigned char sysExBuf[MIDI_SYSEX_BUF_COUNT * MIDI_SYSEX_MAX_SIZE];
  50.   int sysExOffset;              /* Current offset in sysex buffer (for message continuation) */
  51. } fluid_winmidi_driver_t;
  52. static char fluid_winmidi_error_buffer[256];
  53. #define msg_type(_m)  ((unsigned char)(_m & 0xf0))
  54. #define msg_chan(_m)  ((unsigned char)(_m & 0x0f))
  55. #define msg_p1(_m)    ((_m >> 8) & 0x7f)
  56. #define msg_p2(_m)    ((_m >> 16) & 0x7f)
  57. fluid_midi_driver_t* new_fluid_winmidi_driver(fluid_settings_t* settings,
  58.     handle_midi_event_func_t handler, void* data);
  59. int delete_fluid_winmidi_driver(fluid_midi_driver_t* p);
  60. void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD dwInstance,
  61.     DWORD msg, DWORD extra);
  62. static void fluid_winmidi_add_sysex_thread (void *data);
  63. static char* fluid_winmidi_input_error(int no);
  64. int fluid_winmidi_driver_status(fluid_midi_driver_t* p);
  65. void fluid_winmidi_midi_driver_settings(fluid_settings_t* settings)
  66. {
  67.   MMRESULT res;
  68.   MIDIINCAPS in_caps;
  69.   UINT i, num;
  70.   fluid_settings_register_str(settings, "midi.winmidi.device", "default", 0, NULL, NULL);
  71.   num = midiInGetNumDevs();
  72.   if (num > 0) {
  73.     fluid_settings_add_option(settings, "midi.winmidi.device", "default");
  74.     for (i = 0; i < num; i++) {
  75.       res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
  76.       if (res == MMSYSERR_NOERROR) {
  77.         fluid_settings_add_option(settings, "midi.winmidi.device", in_caps.szPname);
  78.       }
  79.     }
  80.   }
  81. }
  82. /*
  83.  * new_fluid_winmidi_driver
  84.  */
  85. fluid_midi_driver_t*
  86. new_fluid_winmidi_driver(fluid_settings_t* settings,
  87. handle_midi_event_func_t handler, void* data)
  88. {
  89.   fluid_winmidi_driver_t* dev;
  90.   MIDIHDR *hdr;
  91.   MMRESULT res;
  92.   UINT i, err, num, midi_num = 0;
  93.   MIDIINCAPS in_caps;
  94.   char* devname = NULL;
  95.   /* not much use doing anything */
  96.   if (handler == NULL) {
  97.     FLUID_LOG(FLUID_ERR, "Invalid argument");
  98.     return NULL;
  99.   }
  100.   dev = FLUID_MALLOC(sizeof(fluid_winmidi_driver_t));
  101.   if (dev == NULL) {
  102.     return NULL;
  103.   }
  104.   memset (dev, 0, sizeof (fluid_winmidi_driver_t));
  105.   dev->hmidiin = NULL;
  106.   dev->driver.handler = handler;
  107.   dev->driver.data = data;
  108.   dev->closing = FALSE;
  109.   /* get the device name. if none is specified, use the default device. */
  110.   if(!fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) || !devname) {
  111.     devname = FLUID_STRDUP ("default");
  112.     if (!devname)
  113.     {
  114.       FLUID_LOG(FLUID_ERR, "Out of memory");
  115.       goto error_recovery;
  116.     }
  117.   }
  118.   
  119.   /* check if there any midi devices installed */
  120.   num = midiInGetNumDevs();
  121.   if (num == 0) {
  122.     FLUID_LOG(FLUID_ERR, "no MIDI in devices found");
  123.     goto error_recovery;
  124.   }
  125.   /* find the device */
  126.   if (strcasecmp("default", devname) != 0) {
  127.     for (i = 0; i < num; i++) {
  128.       res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
  129.       if (res == MMSYSERR_NOERROR) {
  130.         FLUID_LOG(FLUID_DBG, "Testing midi device: %sn", in_caps.szPname);
  131.         if (strcasecmp(devname, in_caps.szPname) == 0) {
  132.           FLUID_LOG(FLUID_DBG, "Selected midi device number: %dn", i);
  133.           midi_num = i;
  134.           break;
  135.         }
  136.       }
  137.     }
  138.     if (midi_num != i) {
  139.       FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname);
  140.       goto error_recovery;
  141.     }
  142.   }
  143.   /* try opening the device */
  144.   err = midiInOpen(&dev->hmidiin, midi_num,
  145.    (DWORD) fluid_winmidi_callback,
  146.    (DWORD) dev, CALLBACK_FUNCTION);
  147.   if (err != MMSYSERR_NOERROR) {
  148.     FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)",
  149.      fluid_winmidi_input_error(err), err);
  150.     goto error_recovery;
  151.   }
  152.   /* Prepare and add SYSEX buffers */
  153.   for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++)
  154.   {
  155.     dev->sysExHdrAdd[i] = FALSE;
  156.     hdr = &dev->sysExHdrs[i];
  157.     hdr->lpData = &dev->sysExBuf[i * MIDI_SYSEX_MAX_SIZE];
  158.     hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE;
  159.     /* Prepare a buffer for SYSEX data and add it */
  160.     err = midiInPrepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR));
  161.     if (err == MMSYSERR_NOERROR)
  162.     {
  163.       err = midiInAddBuffer (dev->hmidiin, hdr, sizeof (MIDIHDR));
  164.       if (err != MMSYSERR_NOERROR)
  165.       {
  166.         FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
  167.                    fluid_winmidi_input_error (err), err);
  168.         midiInUnprepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR));
  169.       }
  170.     }
  171.     else FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
  172.                     fluid_winmidi_input_error (err), err);
  173.   }
  174.   /* Start the MIDI input interface */
  175.   if (midiInStart(dev->hmidiin) != MMSYSERR_NOERROR) {
  176.     FLUID_LOG(FLUID_ERR, "Failed to start the MIDI input. MIDI input not available.");
  177.     goto error_recovery;
  178.   }
  179.   /* Create mutex and condition */
  180.   dev->mutex = new_fluid_cond_mutex ();
  181.   dev->cond = new_fluid_cond ();
  182.   if (!dev->mutex || !dev->cond)
  183.   {
  184.     FLUID_LOG(FLUID_ERR, "Out of memory");
  185.     goto error_recovery;
  186.   }
  187.   /* Create thread which processes re-adding SYSEX buffers */
  188.   dev->sysExAddThread = new_fluid_thread (fluid_winmidi_add_sysex_thread,
  189.                                           dev, 0, FALSE);
  190.   if (!dev->sysExAddThread)
  191.   {
  192.     FLUID_LOG(FLUID_ERR, "Failed to create SYSEX buffer processing thread");
  193.     goto error_recovery;
  194.   }
  195.   if (devname) FLUID_FREE (devname);    /* -- free device name */
  196.   return (fluid_midi_driver_t*) dev;
  197.  error_recovery:
  198.   if (devname) FLUID_FREE (devname);    /* -- free device name */
  199.   delete_fluid_winmidi_driver((fluid_midi_driver_t*) dev);
  200.   return NULL;
  201. }
  202. /*
  203.  * delete_fluid_winmidi_driver
  204.  */
  205. int
  206. delete_fluid_winmidi_driver(fluid_midi_driver_t* p)
  207. {
  208.   fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t*) p;
  209.   if (dev->hmidiin != NULL) {
  210.     fluid_atomic_int_set (&dev->closing, TRUE);
  211.     if (dev->sysExAddThread)
  212.     {
  213.       fluid_cond_mutex_lock (dev->mutex);         /* ++ lock */
  214.       fluid_cond_signal (dev->cond);
  215.       fluid_cond_mutex_unlock (dev->mutex);       /* -- unlock */
  216.       fluid_thread_join (dev->sysExAddThread);
  217.     }
  218.     midiInStop(dev->hmidiin);
  219.     midiInReset(dev->hmidiin);
  220.     midiInClose(dev->hmidiin);
  221.   }
  222.   if (dev->mutex) delete_fluid_cond_mutex (dev->mutex);
  223.   if (dev->cond) delete_fluid_cond (dev->cond);
  224.   FLUID_FREE(dev);
  225.   return 0;
  226. }
  227. void CALLBACK
  228. fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD dwInstance,
  229.                        DWORD dwParam1, DWORD dwParam2)
  230. {
  231.   fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t *) dwInstance;
  232.   fluid_midi_event_t event;
  233.   LPMIDIHDR pMidiHdr;
  234.   unsigned char *data;
  235.   int index;
  236.   switch (wMsg) {
  237.   case MIM_OPEN:
  238.     break;
  239.   case MIM_CLOSE:
  240.     break;
  241.   case MIM_DATA:
  242.     event.type = msg_type(dwParam1);
  243.     event.channel = msg_chan(dwParam1);
  244.     if (event.type != PITCH_BEND) {
  245.       event.param1 = msg_p1(dwParam1);
  246.       event.param2 = msg_p2(dwParam1);
  247.     } else {  /* Pitch bend is a 14 bit value */
  248.       event.param1 = (msg_p2 (dwParam1) << 7) | msg_p1 (dwParam1);
  249.       event.param2 = 0;
  250.     }
  251.     (*dev->driver.handler)(dev->driver.data, &event);
  252.     break;
  253.   case MIM_LONGDATA:    /* SYSEX data */
  254.     if (dev->closing) break;    /* Prevent MIM_LONGDATA endless loop, don't re-add buffer if closing */
  255.     pMidiHdr = (LPMIDIHDR)dwParam1;
  256.     data = (unsigned char *)(pMidiHdr->lpData);
  257.     /* We only process complete SYSEX messages (discard those that are too small or too large) */
  258.     if (pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0
  259.         && data[pMidiHdr->dwBytesRecorded - 1] == 0xF7)
  260.     {
  261.       fluid_midi_event_set_sysex (&event, pMidiHdr->lpData + 1,
  262.                                   pMidiHdr->dwBytesRecorded - 2, FALSE);
  263.       (*dev->driver.handler)(dev->driver.data, &event);
  264.     }
  265.     index = (pMidiHdr - dev->sysExHdrs) / sizeof (MIDIHDR);
  266.     fluid_atomic_int_set (&dev->sysExHdrAdd[index], TRUE);
  267.     fluid_cond_mutex_lock (dev->mutex);         /* ++ lock */
  268.     fluid_cond_signal (dev->cond);
  269.     fluid_cond_mutex_unlock (dev->mutex);       /* -- unlock */
  270.     break;
  271.   case MIM_ERROR:
  272.     break;
  273.   case MIM_LONGERROR:
  274.     break;
  275.   case MIM_MOREDATA:
  276.     break;
  277.   }
  278. }
  279. /* Thread for re-adding SYSEX buffers */
  280. static void
  281. fluid_winmidi_add_sysex_thread (void *data)
  282. {
  283.   fluid_winmidi_driver_t *dev = data;
  284.   int i;
  285.   while (!fluid_atomic_int_get (&dev->closing))
  286.   {
  287.     fluid_cond_mutex_lock (dev->mutex);         /* ++ lock */
  288.     fluid_cond_wait (dev->cond, dev->mutex);
  289.     fluid_cond_mutex_unlock (dev->mutex);       /* -- unlock */
  290.     for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++)
  291.     {
  292.       if (fluid_atomic_int_get (&dev->sysExHdrAdd[i]))
  293.       {
  294.         fluid_atomic_int_set (&dev->sysExHdrAdd[i], FALSE);
  295.         midiInAddBuffer (dev->hmidiin, &dev->sysExHdrs[i], sizeof (MIDIHDR));
  296.       }
  297.     }
  298.   }
  299. }
  300. int
  301. fluid_winmidi_driver_status(fluid_midi_driver_t* p)
  302. {
  303.   return 0;
  304. }
  305. static char*
  306. fluid_winmidi_input_error(int no)
  307. {
  308.   midiInGetErrorText(no, fluid_winmidi_error_buffer, 256);
  309.   return fluid_winmidi_error_buffer;
  310. }
  311. #endif /* WINMIDI_SUPPORT */