fluid_winmidi.c
上传用户:tjmskj2
上传日期:2020-08-17
资源大小:577k
文件大小:11k
- /* FluidSynth - A Software Synthesizer
- *
- * Copyright (C) 2003 Peter Hanappe and others.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public License
- * as published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307, USA
- */
- /* fluid_winmidi.c
- *
- * Driver for Windows MIDI
- *
- * NOTE: Unfortunately midiInAddBuffer(), for SYSEX data, should not be called
- * from within the MIDI input callback, despite many examples contrary to that
- * on the Internet. Some MIDI devices will deadlock. Therefore we add MIDIHDR
- * pointers to a queue and re-add them in a separate thread, using a mutex and
- * conditional to wake up the thread. Lame-o API! :(
- */
- #include "fluidsynth_priv.h"
- #if WINMIDI_SUPPORT
- #include "fluid_midi.h"
- #include "fluid_mdriver.h"
- #include "fluid_settings.h"
- #define MIDI_SYSEX_MAX_SIZE 512
- #define MIDI_SYSEX_BUF_COUNT 16
- typedef struct {
- fluid_midi_driver_t driver;
- HMIDIIN hmidiin;
- int closing; /* Set to TRUE when closing driver, to prevent endless SYSEX lockup loop */
- fluid_thread_t *sysExAddThread; /* Thread for SYSEX re-add thread */
- fluid_cond_mutex_t *mutex; /* Lock for condition */
- fluid_cond_t *cond; /* Condition to signal MIDI event thread of available events */
- /* MIDI HDR for SYSEX buffer */
- MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT];
- /* TRUE for each MIDIHDR buffer which should be re-added to MIDI device */
- int sysExHdrAdd[MIDI_SYSEX_BUF_COUNT];
- /* Sysex data buffer */
- unsigned char sysExBuf[MIDI_SYSEX_BUF_COUNT * MIDI_SYSEX_MAX_SIZE];
- int sysExOffset; /* Current offset in sysex buffer (for message continuation) */
- } fluid_winmidi_driver_t;
- static char fluid_winmidi_error_buffer[256];
- #define msg_type(_m) ((unsigned char)(_m & 0xf0))
- #define msg_chan(_m) ((unsigned char)(_m & 0x0f))
- #define msg_p1(_m) ((_m >> 8) & 0x7f)
- #define msg_p2(_m) ((_m >> 16) & 0x7f)
- fluid_midi_driver_t* new_fluid_winmidi_driver(fluid_settings_t* settings,
- handle_midi_event_func_t handler, void* data);
- int delete_fluid_winmidi_driver(fluid_midi_driver_t* p);
- void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD dwInstance,
- DWORD msg, DWORD extra);
- static void fluid_winmidi_add_sysex_thread (void *data);
- static char* fluid_winmidi_input_error(int no);
- int fluid_winmidi_driver_status(fluid_midi_driver_t* p);
- void fluid_winmidi_midi_driver_settings(fluid_settings_t* settings)
- {
- MMRESULT res;
- MIDIINCAPS in_caps;
- UINT i, num;
- fluid_settings_register_str(settings, "midi.winmidi.device", "default", 0, NULL, NULL);
- num = midiInGetNumDevs();
- if (num > 0) {
- fluid_settings_add_option(settings, "midi.winmidi.device", "default");
- for (i = 0; i < num; i++) {
- res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
- if (res == MMSYSERR_NOERROR) {
- fluid_settings_add_option(settings, "midi.winmidi.device", in_caps.szPname);
- }
- }
- }
- }
- /*
- * new_fluid_winmidi_driver
- */
- fluid_midi_driver_t*
- new_fluid_winmidi_driver(fluid_settings_t* settings,
- handle_midi_event_func_t handler, void* data)
- {
- fluid_winmidi_driver_t* dev;
- MIDIHDR *hdr;
- MMRESULT res;
- UINT i, err, num, midi_num = 0;
- MIDIINCAPS in_caps;
- char* devname = NULL;
- /* not much use doing anything */
- if (handler == NULL) {
- FLUID_LOG(FLUID_ERR, "Invalid argument");
- return NULL;
- }
- dev = FLUID_MALLOC(sizeof(fluid_winmidi_driver_t));
- if (dev == NULL) {
- return NULL;
- }
- memset (dev, 0, sizeof (fluid_winmidi_driver_t));
- dev->hmidiin = NULL;
- dev->driver.handler = handler;
- dev->driver.data = data;
- dev->closing = FALSE;
- /* get the device name. if none is specified, use the default device. */
- if(!fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) || !devname) {
- devname = FLUID_STRDUP ("default");
- if (!devname)
- {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- goto error_recovery;
- }
- }
-
- /* check if there any midi devices installed */
- num = midiInGetNumDevs();
- if (num == 0) {
- FLUID_LOG(FLUID_ERR, "no MIDI in devices found");
- goto error_recovery;
- }
- /* find the device */
- if (strcasecmp("default", devname) != 0) {
- for (i = 0; i < num; i++) {
- res = midiInGetDevCaps(i, &in_caps, sizeof(MIDIINCAPS));
- if (res == MMSYSERR_NOERROR) {
- FLUID_LOG(FLUID_DBG, "Testing midi device: %sn", in_caps.szPname);
- if (strcasecmp(devname, in_caps.szPname) == 0) {
- FLUID_LOG(FLUID_DBG, "Selected midi device number: %dn", i);
- midi_num = i;
- break;
- }
- }
- }
- if (midi_num != i) {
- FLUID_LOG(FLUID_ERR, "Device <%s> does not exists", devname);
- goto error_recovery;
- }
- }
- /* try opening the device */
- err = midiInOpen(&dev->hmidiin, midi_num,
- (DWORD) fluid_winmidi_callback,
- (DWORD) dev, CALLBACK_FUNCTION);
- if (err != MMSYSERR_NOERROR) {
- FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)",
- fluid_winmidi_input_error(err), err);
- goto error_recovery;
- }
- /* Prepare and add SYSEX buffers */
- for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++)
- {
- dev->sysExHdrAdd[i] = FALSE;
- hdr = &dev->sysExHdrs[i];
- hdr->lpData = &dev->sysExBuf[i * MIDI_SYSEX_MAX_SIZE];
- hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE;
- /* Prepare a buffer for SYSEX data and add it */
- err = midiInPrepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR));
- if (err == MMSYSERR_NOERROR)
- {
- err = midiInAddBuffer (dev->hmidiin, hdr, sizeof (MIDIHDR));
- if (err != MMSYSERR_NOERROR)
- {
- FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
- fluid_winmidi_input_error (err), err);
- midiInUnprepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR));
- }
- }
- else FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
- fluid_winmidi_input_error (err), err);
- }
- /* Start the MIDI input interface */
- if (midiInStart(dev->hmidiin) != MMSYSERR_NOERROR) {
- FLUID_LOG(FLUID_ERR, "Failed to start the MIDI input. MIDI input not available.");
- goto error_recovery;
- }
- /* Create mutex and condition */
- dev->mutex = new_fluid_cond_mutex ();
- dev->cond = new_fluid_cond ();
- if (!dev->mutex || !dev->cond)
- {
- FLUID_LOG(FLUID_ERR, "Out of memory");
- goto error_recovery;
- }
- /* Create thread which processes re-adding SYSEX buffers */
- dev->sysExAddThread = new_fluid_thread (fluid_winmidi_add_sysex_thread,
- dev, 0, FALSE);
- if (!dev->sysExAddThread)
- {
- FLUID_LOG(FLUID_ERR, "Failed to create SYSEX buffer processing thread");
- goto error_recovery;
- }
- if (devname) FLUID_FREE (devname); /* -- free device name */
- return (fluid_midi_driver_t*) dev;
- error_recovery:
- if (devname) FLUID_FREE (devname); /* -- free device name */
- delete_fluid_winmidi_driver((fluid_midi_driver_t*) dev);
- return NULL;
- }
- /*
- * delete_fluid_winmidi_driver
- */
- int
- delete_fluid_winmidi_driver(fluid_midi_driver_t* p)
- {
- fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t*) p;
- if (dev->hmidiin != NULL) {
- fluid_atomic_int_set (&dev->closing, TRUE);
- if (dev->sysExAddThread)
- {
- fluid_cond_mutex_lock (dev->mutex); /* ++ lock */
- fluid_cond_signal (dev->cond);
- fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */
- fluid_thread_join (dev->sysExAddThread);
- }
- midiInStop(dev->hmidiin);
- midiInReset(dev->hmidiin);
- midiInClose(dev->hmidiin);
- }
- if (dev->mutex) delete_fluid_cond_mutex (dev->mutex);
- if (dev->cond) delete_fluid_cond (dev->cond);
- FLUID_FREE(dev);
- return 0;
- }
- void CALLBACK
- fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD dwInstance,
- DWORD dwParam1, DWORD dwParam2)
- {
- fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t *) dwInstance;
- fluid_midi_event_t event;
- LPMIDIHDR pMidiHdr;
- unsigned char *data;
- int index;
- switch (wMsg) {
- case MIM_OPEN:
- break;
- case MIM_CLOSE:
- break;
- case MIM_DATA:
- event.type = msg_type(dwParam1);
- event.channel = msg_chan(dwParam1);
- if (event.type != PITCH_BEND) {
- event.param1 = msg_p1(dwParam1);
- event.param2 = msg_p2(dwParam1);
- } else { /* Pitch bend is a 14 bit value */
- event.param1 = (msg_p2 (dwParam1) << 7) | msg_p1 (dwParam1);
- event.param2 = 0;
- }
- (*dev->driver.handler)(dev->driver.data, &event);
- break;
- case MIM_LONGDATA: /* SYSEX data */
- if (dev->closing) break; /* Prevent MIM_LONGDATA endless loop, don't re-add buffer if closing */
- pMidiHdr = (LPMIDIHDR)dwParam1;
- data = (unsigned char *)(pMidiHdr->lpData);
- /* We only process complete SYSEX messages (discard those that are too small or too large) */
- if (pMidiHdr->dwBytesRecorded > 2 && data[0] == 0xF0
- && data[pMidiHdr->dwBytesRecorded - 1] == 0xF7)
- {
- fluid_midi_event_set_sysex (&event, pMidiHdr->lpData + 1,
- pMidiHdr->dwBytesRecorded - 2, FALSE);
- (*dev->driver.handler)(dev->driver.data, &event);
- }
- index = (pMidiHdr - dev->sysExHdrs) / sizeof (MIDIHDR);
- fluid_atomic_int_set (&dev->sysExHdrAdd[index], TRUE);
- fluid_cond_mutex_lock (dev->mutex); /* ++ lock */
- fluid_cond_signal (dev->cond);
- fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */
- break;
- case MIM_ERROR:
- break;
- case MIM_LONGERROR:
- break;
- case MIM_MOREDATA:
- break;
- }
- }
- /* Thread for re-adding SYSEX buffers */
- static void
- fluid_winmidi_add_sysex_thread (void *data)
- {
- fluid_winmidi_driver_t *dev = data;
- int i;
- while (!fluid_atomic_int_get (&dev->closing))
- {
- fluid_cond_mutex_lock (dev->mutex); /* ++ lock */
- fluid_cond_wait (dev->cond, dev->mutex);
- fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */
- for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++)
- {
- if (fluid_atomic_int_get (&dev->sysExHdrAdd[i]))
- {
- fluid_atomic_int_set (&dev->sysExHdrAdd[i], FALSE);
- midiInAddBuffer (dev->hmidiin, &dev->sysExHdrs[i], sizeof (MIDIHDR));
- }
- }
- }
- }
- int
- fluid_winmidi_driver_status(fluid_midi_driver_t* p)
- {
- return 0;
- }
- static char*
- fluid_winmidi_input_error(int no)
- {
- midiInGetErrorText(no, fluid_winmidi_error_buffer, 256);
- return fluid_winmidi_error_buffer;
- }
- #endif /* WINMIDI_SUPPORT */