Merge pull request #298 from carlo-bramini/winmidi-improvements

Simpler implementation of WINMIDI driver.
This commit is contained in:
Tom M 2017-12-06 19:22:53 +01:00 committed by GitHub
commit e4972c5038
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -26,8 +26,7 @@
* NOTE: Unfortunately midiInAddBuffer(), for SYSEX data, should not be called * NOTE: Unfortunately midiInAddBuffer(), for SYSEX data, should not be called
* from within the MIDI input callback, despite many examples contrary to that * from within the MIDI input callback, despite many examples contrary to that
* on the Internet. Some MIDI devices will deadlock. Therefore we add MIDIHDR * 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 * pointers to a queue and re-add them in a separate thread. Lame-o API! :(
* conditional to wake up the thread. Lame-o API! :(
*/ */
#include "fluidsynth_priv.h" #include "fluidsynth_priv.h"
@ -44,22 +43,17 @@
typedef struct { typedef struct {
fluid_midi_driver_t driver; fluid_midi_driver_t driver;
HMIDIIN hmidiin; HMIDIIN hmidiin;
fluid_atomic_int_t 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 */ /* MIDI HDR for SYSEX buffer */
MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT]; MIDIHDR sysExHdrs[MIDI_SYSEX_BUF_COUNT];
/* TRUE for each MIDIHDR buffer which should be re-added to MIDI device */ /* Thread for SYSEX re-add thread */
fluid_atomic_int_t sysExHdrAdd[MIDI_SYSEX_BUF_COUNT]; HANDLE hThread;
DWORD dwThread;
/* Sysex data buffer */ /* Sysex data buffer */
unsigned char sysExBuf[MIDI_SYSEX_BUF_COUNT * MIDI_SYSEX_MAX_SIZE]; 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; } fluid_winmidi_driver_t;
static char fluid_winmidi_error_buffer[256]; static char fluid_winmidi_error_buffer[256];
@ -76,9 +70,7 @@ void delete_fluid_winmidi_driver(fluid_midi_driver_t* p);
void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance, void CALLBACK fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
DWORD_PTR msg, DWORD_PTR extra); DWORD_PTR msg, DWORD_PTR extra);
static fluid_thread_return_t fluid_winmidi_add_sysex_thread (void *data); static char* fluid_winmidi_input_error(MMRESULT no);
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) void fluid_winmidi_midi_driver_settings(fluid_settings_t* settings)
@ -99,6 +91,34 @@ void fluid_winmidi_midi_driver_settings(fluid_settings_t* settings)
} }
} }
/* Thread for re-adding SYSEX buffers */
static DWORD WINAPI fluid_winmidi_add_sysex_thread(void *data)
{
fluid_winmidi_driver_t *dev = (fluid_winmidi_driver_t *)data;
MSG msg;
int code;
for (;;) {
code = GetMessage(&msg, NULL, 0, 0);
if (code < 0) {
FLUID_LOG(FLUID_ERR, "fluid_winmidi_add_sysex_thread: GetMessage() failed.");
break;
}
if (msg.message == WM_CLOSE)
break;
switch (msg.message) {
case MM_MIM_LONGDATA:
midiInAddBuffer(dev->hmidiin, (LPMIDIHDR)msg.lParam, sizeof(MIDIHDR));
break;
}
}
return 0;
}
/* /*
* new_fluid_winmidi_driver * new_fluid_winmidi_driver
*/ */
@ -109,7 +129,7 @@ new_fluid_winmidi_driver(fluid_settings_t* settings,
fluid_winmidi_driver_t* dev; fluid_winmidi_driver_t* dev;
MIDIHDR *hdr; MIDIHDR *hdr;
MMRESULT res; MMRESULT res;
UINT i, err, num, midi_num = 0; UINT i, num, midi_num = 0;
MIDIINCAPS in_caps; MIDIINCAPS in_caps;
char* devname = NULL; char* devname = NULL;
@ -124,12 +144,11 @@ new_fluid_winmidi_driver(fluid_settings_t* settings,
return NULL; return NULL;
} }
memset (dev, 0, sizeof (fluid_winmidi_driver_t)); FLUID_MEMSET (dev, 0, sizeof (fluid_winmidi_driver_t));
dev->hmidiin = NULL; dev->hmidiin = NULL;
dev->driver.handler = handler; dev->driver.handler = handler;
dev->driver.data = data; dev->driver.data = data;
fluid_atomic_int_set (&dev->closing, FALSE);
/* get the device name. if none is specified, use the default device. */ /* get the device name. if none is specified, use the default device. */
if(fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) != FLUID_OK || !devname) { if(fluid_settings_dupstr(settings, "midi.winmidi.device", &devname) != FLUID_OK || !devname) {
@ -169,40 +188,55 @@ new_fluid_winmidi_driver(fluid_settings_t* settings,
} }
/* try opening the device */ /* try opening the device */
err = midiInOpen(&dev->hmidiin, midi_num, res = midiInOpen(&dev->hmidiin, midi_num,
(DWORD_PTR) fluid_winmidi_callback, (DWORD_PTR) fluid_winmidi_callback,
(DWORD_PTR) dev, CALLBACK_FUNCTION); (DWORD_PTR) dev, CALLBACK_FUNCTION);
if (err != MMSYSERR_NOERROR) { if (res != MMSYSERR_NOERROR) {
FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)", FLUID_LOG(FLUID_ERR, "Couldn't open MIDI input: %s (error %d)",
fluid_winmidi_input_error(err), err); fluid_winmidi_input_error(res), res);
goto error_recovery; goto error_recovery;
} }
/* Prepare and add SYSEX buffers */ /* Prepare and add SYSEX buffers */
for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++) for (i = 0; i < MIDI_SYSEX_BUF_COUNT; i++)
{ {
fluid_atomic_int_set (&dev->sysExHdrAdd[i], FALSE);
hdr = &dev->sysExHdrs[i]; hdr = &dev->sysExHdrs[i];
hdr->lpData = &dev->sysExBuf[i * MIDI_SYSEX_MAX_SIZE]; hdr->lpData = (LPSTR)&dev->sysExBuf[i * MIDI_SYSEX_MAX_SIZE];
hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE; hdr->dwBufferLength = MIDI_SYSEX_MAX_SIZE;
/* Prepare a buffer for SYSEX data and add it */ /* Prepare a buffer for SYSEX data and add it */
err = midiInPrepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR)); res = midiInPrepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR));
if (err == MMSYSERR_NOERROR) if (res == MMSYSERR_NOERROR)
{ {
err = midiInAddBuffer (dev->hmidiin, hdr, sizeof (MIDIHDR)); res = midiInAddBuffer (dev->hmidiin, hdr, sizeof (MIDIHDR));
if (err != MMSYSERR_NOERROR) if (res != MMSYSERR_NOERROR)
{ {
FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
fluid_winmidi_input_error (err), err); fluid_winmidi_input_error (res), res);
midiInUnprepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR)); midiInUnprepareHeader (dev->hmidiin, hdr, sizeof (MIDIHDR));
} }
} }
else FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)", else FLUID_LOG (FLUID_WARN, "Failed to prepare MIDI SYSEX buffer: %s (error %d)",
fluid_winmidi_input_error (err), err); fluid_winmidi_input_error (res), res);
}
/* Create thread which processes re-adding SYSEX buffers */
dev->hThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)
fluid_winmidi_add_sysex_thread,
dev,
0,
&dev->dwThread);
if (dev->hThread == NULL)
{
FLUID_LOG(FLUID_ERR, "Failed to create SYSEX buffer processing thread");
goto error_recovery;
} }
/* Start the MIDI input interface */ /* Start the MIDI input interface */
@ -211,25 +245,6 @@ new_fluid_winmidi_driver(fluid_settings_t* settings,
goto error_recovery; 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 ("winmidi-sysex", 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 */ if (devname) FLUID_FREE (devname); /* -- free device name */
return (fluid_midi_driver_t*) dev; return (fluid_midi_driver_t*) dev;
@ -248,27 +263,22 @@ delete_fluid_winmidi_driver(fluid_midi_driver_t* p)
{ {
fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t*) p; fluid_winmidi_driver_t* dev = (fluid_winmidi_driver_t*) p;
fluid_return_if_fail (dev != NULL); fluid_return_if_fail (dev != NULL);
if (dev->hmidiin != NULL) {
fluid_atomic_int_set (&dev->closing, TRUE);
if (dev->sysExAddThread) if (dev->hThread != NULL)
{ {
fluid_cond_mutex_lock (dev->mutex); /* ++ lock */ PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0);
fluid_cond_signal (dev->cond); WaitForSingleObject(dev->hThread, INFINITE);
fluid_cond_mutex_unlock (dev->mutex); /* -- unlock */
fluid_thread_join (dev->sysExAddThread); dev->hThread = NULL;
} }
if (dev->hmidiin != NULL)
{
midiInStop(dev->hmidiin); midiInStop(dev->hmidiin);
midiInReset(dev->hmidiin); midiInReset(dev->hmidiin);
midiInClose(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); FLUID_FREE(dev);
} }
@ -280,7 +290,6 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
fluid_midi_event_t event; fluid_midi_event_t event;
LPMIDIHDR pMidiHdr; LPMIDIHDR pMidiHdr;
unsigned char *data; unsigned char *data;
int index;
unsigned int msg_param = (unsigned int) dwParam1; unsigned int msg_param = (unsigned int) dwParam1;
switch (wMsg) { switch (wMsg) {
@ -306,7 +315,8 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
break; break;
case MIM_LONGDATA: /* SYSEX data */ case MIM_LONGDATA: /* SYSEX data */
if (fluid_atomic_int_get (&dev->closing)) break; /* Prevent MIM_LONGDATA endless loop, don't re-add buffer if closing */ if (dev->hThread == NULL)
break;
pMidiHdr = (LPMIDIHDR)dwParam1; pMidiHdr = (LPMIDIHDR)dwParam1;
data = (unsigned char *)(pMidiHdr->lpData); data = (unsigned char *)(pMidiHdr->lpData);
@ -320,12 +330,7 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
(*dev->driver.handler)(dev->driver.data, &event); (*dev->driver.handler)(dev->driver.data, &event);
} }
index = (pMidiHdr - dev->sysExHdrs) / sizeof (MIDIHDR); PostThreadMessage(dev->dwThread, MM_MIM_LONGDATA, 0, dwParam1);
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; break;
case MIM_ERROR: case MIM_ERROR:
@ -339,40 +344,8 @@ fluid_winmidi_callback(HMIDIIN hmi, UINT wMsg, DWORD_PTR dwInstance,
} }
} }
/* Thread for re-adding SYSEX buffers */
static fluid_thread_return_t
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));
}
}
}
return FLUID_THREAD_RETURN_VALUE;
}
int
fluid_winmidi_driver_status(fluid_midi_driver_t* p)
{
return 0;
}
static char* static char*
fluid_winmidi_input_error(int no) fluid_winmidi_input_error(MMRESULT no)
{ {
midiInGetErrorText(no, fluid_winmidi_error_buffer, 256); midiInGetErrorText(no, fluid_winmidi_error_buffer, 256);
return fluid_winmidi_error_buffer; return fluid_winmidi_error_buffer;