aboutsummaryrefslogtreecommitdiff
path: root/pd/portmidi/pm_win/pmwinmm.c
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portmidi/pm_win/pmwinmm.c')
-rw-r--r--pd/portmidi/pm_win/pmwinmm.c1100
1 files changed, 510 insertions, 590 deletions
diff --git a/pd/portmidi/pm_win/pmwinmm.c b/pd/portmidi/pm_win/pmwinmm.c
index 5bfb0cff..395b1134 100644
--- a/pd/portmidi/pm_win/pmwinmm.c
+++ b/pd/portmidi/pm_win/pmwinmm.c
@@ -1,8 +1,15 @@
/* pmwinmm.c -- system specific definitions */
+/* without this define, InitializeCriticalSectionAndSpinCount is undefined */
+/* this version level means "Windows 2000 and higher" */
+#define _WIN32_WINNT 0x0500
+
#include "windows.h"
#include "mmsystem.h"
#include "portmidi.h"
+#ifdef NEWBUFFER
+#include "pmutil.h"
+#endif
#include "pminternal.h"
#include "pmwinmm.h"
#include "string.h"
@@ -11,7 +18,7 @@
/* asserts used to verify portMidi code logic is sound; later may want
something more graceful */
#include <assert.h>
-
+#define DEBUG 1
#ifdef DEBUG
/* this printf stuff really important for debugging client app w/host errors.
probably want to do something else besides read/write from/to console
@@ -27,25 +34,67 @@
/* callback routines */
static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn,
- WORD wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
+ WORD wMsg, DWORD dwInstance,
+ DWORD dwParam1, DWORD dwParam2);
static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
+ DWORD dwInstance, DWORD dwParam1,
+ DWORD dwParam2);
static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
+ DWORD dwInstance, DWORD dwParam1,
+ DWORD dwParam2);
extern pm_fns_node pm_winmm_in_dictionary;
extern pm_fns_node pm_winmm_out_dictionary;
static void winmm_out_delete(PmInternal *midi); /* forward reference */
-#define SYSEX_BYTES_PER_BUFFER 1024
-/* 3 midi messages per buffer */
-#define OUTPUT_BYTES_PER_BUFFER 36
+/*
+A note about buffers: WinMM seems to hold onto buffers longer than
+one would expect, e.g. when I tried using 2 small buffers to send
+long sysex messages, at some point WinMM held both buffers. This problem
+was fixed by making buffers bigger. Therefore, it seems that there should
+be enough buffer space to hold a whole sysex message.
+
+The bufferSize passed into Pm_OpenInput (passed into here as buffer_len)
+will be used to estimate the largest sysex message (= buffer_len * 4 bytes).
+Call that the max_sysex_len = buffer_len * 4.
+
+For simple midi output (latency == 0), allocate 3 buffers, each with half
+the size of max_sysex_len, but each at least 256 bytes.
+
+For stream output, there will already be enough space in very short
+buffers, so use them, but make sure there are at least 16.
+
+For input, use many small buffers rather than 2 large ones so that when
+there are short sysex messages arriving frequently (as in control surfaces)
+there will be more free buffers to fill. Use max_sysex_len / 64 buffers,
+but at least 16, of size 64 bytes each.
+
+The following constants help to represent these design parameters:
+*/
+#define NUM_SIMPLE_SYSEX_BUFFERS 3
+#define MIN_SIMPLE_SYSEX_LEN 256
+
+#define MIN_STREAM_BUFFERS 16
+#define STREAM_BUFFER_LEN 24
+
+#define INPUT_SYSEX_LEN 64
+#define MIN_INPUT_BUFFERS 16
+/* if we run out of space for output (assume this is due to a sysex msg,
+ expand by up to NUM_EXPANSION_BUFFERS in increments of EXPANSION_BUFFER_LEN
+ */
+#define NUM_EXPANSION_BUFFERS 128
+#define EXPANSION_BUFFER_LEN 1024
+
+/* A sysex buffer has 3 DWORDS as a header plus the actual message size */
#define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3)
+/* A MIDIHDR with a sysex message is the buffer length plus the header size */
#define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
-#define MIDIHDR_BUFFER_LENGTH(x) (x)
-#define MIDIHDR_SIZE(x) (MIDIHDR_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
+#ifdef USE_SYSEX_BUFFERS
+/* Size of a MIDIHDR with a buffer contaning multiple MIDIEVENT structures */
+#define MIDIHDR_SIZE(x) ((x) + sizeof(MIDIHDR))
+#endif
/*
==============================================================================
@@ -62,9 +111,7 @@ MIDIOUTCAPS midi_out_mapper_caps;
UINT midi_num_outputs = 0;
/* per device info */
-typedef struct midiwinmm_struct
-{
-
+typedef struct midiwinmm_struct {
union {
HMIDISTRM stream; /* windows handle for stream */
HMIDIOUT out; /* windows handle for out calls */
@@ -75,28 +122,30 @@ typedef struct midiwinmm_struct
* in a round-robin fashion, using next_buffer as an index
*/
LPMIDIHDR *buffers; /* pool of buffers for midi in or out data */
- int num_buffers; /* how many buffers */
+ int max_buffers; /* length of buffers array */
+ int buffers_expanded; /* buffers array expanded for extra msgs? */
+ int num_buffers; /* how many buffers allocated in buffers array */
int next_buffer; /* index of next buffer to send */
HANDLE buffer_signal; /* used to wait for buffer to become free */
-
- LPMIDIHDR *sysex_buffers; /* pool of buffers for sysex data */
- int num_sysex_buffers; /* how many sysex buffers */
+#ifdef USE_SYSEX_BUFFERS
+ /* sysex buffers will be allocated only when
+ * a sysex message is sent. The size of the buffer is fixed.
+ */
+ LPMIDIHDR sysex_buffers[NUM_SYSEX_BUFFERS]; /* pool of buffers for sysex data */
int next_sysex_buffer; /* index of next sysexbuffer to send */
- HANDLE sysex_buffer_signal; /* used to wait for sysex buffer to become free */
-
+#endif
unsigned long last_time; /* last output time */
int first_message; /* flag: treat first message differently */
int sysex_mode; /* middle of sending sysex */
unsigned long sysex_word; /* accumulate data when receiving sysex */
- unsigned int sysex_byte_count; /* count how many received or to send */
+ unsigned int sysex_byte_count; /* count how many received */
LPMIDIHDR hdr; /* the message accumulating sysex to send */
unsigned long sync_time; /* when did we last determine delta? */
long delta; /* difference between stream time and
real time */
int error; /* host error from doing port midi call */
- int callback_error; /* host error from midi in or out callback */
-}
-midiwinmm_node, *midiwinmm_type;
+ CRITICAL_SECTION lock; /* prevents reentrant callbacks (input only) */
+} midiwinmm_node, *midiwinmm_type;
/*
@@ -112,11 +161,11 @@ static void pm_winmm_general_inputs()
midi_in_caps = pm_alloc(sizeof(MIDIINCAPS) * midi_num_inputs);
if (midi_in_caps == NULL) {
- // if you can't open a particular system-level midi interface
- // (such as winmm), we just consider that system or API to be
- // unavailable and move on without reporting an error. This
- // may be the wrong thing to do, especially in this case.
- return ;
+ /* if you can't open a particular system-level midi interface
+ * (such as winmm), we just consider that system or API to be
+ * unavailable and move on without reporting an error.
+ */
+ return;
}
for (i = 0; i < midi_num_inputs; i++) {
@@ -140,7 +189,8 @@ static void pm_winmm_mapper_input()
capabilities) then you still should retrieve some formof
setup info. */
wRtn = midiInGetDevCaps((UINT) MIDIMAPPER,
- (LPMIDIINCAPS) & midi_in_mapper_caps, sizeof(MIDIINCAPS));
+ (LPMIDIINCAPS) & midi_in_mapper_caps,
+ sizeof(MIDIINCAPS));
if (wRtn == MMSYSERR_NOERROR) {
pm_add_device("MMSystem", midi_in_mapper_caps.szPname, TRUE,
(void *) MIDIMAPPER, &pm_winmm_in_dictionary);
@@ -156,7 +206,7 @@ static void pm_winmm_general_outputs()
midi_out_caps = pm_alloc( sizeof(MIDIOUTCAPS) * midi_num_outputs );
if (midi_out_caps == NULL) {
- // no error is reported -- see pm_winmm_general_inputs
+ /* no error is reported -- see pm_winmm_general_inputs */
return ;
}
@@ -194,7 +244,7 @@ host error handling
static unsigned int winmm_has_host_error(PmInternal * midi)
{
midiwinmm_type m = (midiwinmm_type)midi->descriptor;
- return m->callback_error || m->error;
+ return m->error;
}
@@ -229,12 +279,6 @@ static void winmm_get_host_error(PmInternal * midi, char * msg, UINT len)
int err = midiInGetErrorText(m->error, msg + n, len - n);
assert(err == MMSYSERR_NOERROR);
m->error = MMSYSERR_NOERROR;
- } else if (m->callback_error != MMSYSERR_NOERROR) {
- int n = str_copy_len(msg, hdr2, len);
- int err = midiInGetErrorText(m->callback_error, msg + n,
- len - n);
- assert(err == MMSYSERR_NOERROR);
- m->callback_error = MMSYSERR_NOERROR;
}
}
} else { /* output port */
@@ -244,12 +288,6 @@ static void winmm_get_host_error(PmInternal * midi, char * msg, UINT len)
int err = midiOutGetErrorText(m->error, msg + n, len - n);
assert(err == MMSYSERR_NOERROR);
m->error = MMSYSERR_NOERROR;
- } else if (m->callback_error != MMSYSERR_NOERROR) {
- int n = str_copy_len(msg, hdr2, len);
- int err = midiOutGetErrorText(m->callback_error, msg + n,
- len = n);
- assert(err == MMSYSERR_NOERROR);
- m->callback_error = MMSYSERR_NOERROR;
}
}
}
@@ -263,132 +301,181 @@ buffer handling
*/
static MIDIHDR *allocate_buffer(long data_size)
{
- /*
- * with short messages, the MIDIEVENT structure contains the midi message,
- * so there is no need for additional data
- */
-
- LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SIZE(data_size));
+ LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
MIDIEVENT *evt;
if (!hdr) return NULL;
evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
hdr->lpData = (LPSTR) evt;
- hdr->dwBufferLength = MIDIHDR_BUFFER_LENGTH(data_size); /* was: sizeof(MIDIEVENT) + data_size; */
+ hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size);
+ hdr->dwBytesRecorded = 0;
hdr->dwFlags = 0;
- hdr->dwUser = 0;
+ hdr->dwUser = hdr->dwBufferLength;
return hdr;
}
+#ifdef USE_SYSEX_BUFFERS
static MIDIHDR *allocate_sysex_buffer(long data_size)
{
- /* we're actually allocating slightly more than data_size because one more word of
- * data is contained in MIDIEVENT. We include the size of MIDIEVENT because we need
- * the MIDIEVENT header in addition to the data
+ /* we're actually allocating more than data_size because the buffer
+ * will include the MIDIEVENT header in addition to the data
*/
LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
MIDIEVENT *evt;
if (!hdr) return NULL;
evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
hdr->lpData = (LPSTR) evt;
- hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size); /* was: sizeof(MIDIEVENT) + data_size; */
hdr->dwFlags = 0;
hdr->dwUser = 0;
return hdr;
}
+#endif
static PmError allocate_buffers(midiwinmm_type m, long data_size, long count)
{
- PmError rslt = pmNoError;
+ int i;
/* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
+ m->num_buffers = 0; /* in case no memory can be allocated */
m->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
if (!m->buffers) return pmInsufficientMemory;
- m->num_buffers = count;
- while (count > 0) {
+ m->max_buffers = count;
+ for (i = 0; i < count; i++) {
LPMIDIHDR hdr = allocate_buffer(data_size);
- if (!hdr) rslt = pmInsufficientMemory;
- count--;
- m->buffers[count] = hdr; /* this may be NULL if allocation fails */
+ if (!hdr) { /* free everything allocated so far and return */
+ for (i = i - 1; i >= 0; i--) pm_free(m->buffers[i]);
+ pm_free(m->buffers);
+ m->max_buffers = 0;
+ return pmInsufficientMemory;
+ }
+ m->buffers[i] = hdr; /* this may be NULL if allocation fails */
}
- return rslt;
+ m->num_buffers = count;
+ return pmNoError;
}
-static PmError allocate_sysex_buffers(midiwinmm_type m, long data_size, long count)
+#ifdef USE_SYSEX_BUFFERS
+static PmError allocate_sysex_buffers(midiwinmm_type m, long data_size)
{
PmError rslt = pmNoError;
- /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
- m->sysex_buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
- if (!m->sysex_buffers) return pmInsufficientMemory;
- m->num_sysex_buffers = count;
- while (count > 0) {
+ /* sysex_buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
+ int i;
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
LPMIDIHDR hdr = allocate_sysex_buffer(data_size);
+
if (!hdr) rslt = pmInsufficientMemory;
- count--;
- m->sysex_buffers[count] = hdr; /* this may be NULL if allocation fails */
+ m->sysex_buffers[i] = hdr; /* this may be NULL if allocation fails */
+ hdr->dwFlags = 0; /* mark as free */
}
return rslt;
}
+#endif
+#ifdef USE_SYSEX_BUFFERS
static LPMIDIHDR get_free_sysex_buffer(PmInternal *midi)
{
LPMIDIHDR r = NULL;
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- if (!m->sysex_buffers) {
- if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER, 2)) {
+ if (!m->sysex_buffers[0]) {
+ if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER)) {
return NULL;
}
}
/* busy wait until we find a free buffer */
while (TRUE) {
int i;
- for (i = 0; i < m->num_sysex_buffers; i++) {
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
+ /* cycle through buffers, modulo NUM_SYSEX_BUFFERS */
m->next_sysex_buffer++;
- if (m->next_sysex_buffer >= m->num_sysex_buffers) m->next_sysex_buffer = 0;
+ if (m->next_sysex_buffer >= NUM_SYSEX_BUFFERS) m->next_sysex_buffer = 0;
r = m->sysex_buffers[m->next_sysex_buffer];
if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_sysex_buffer;
}
/* after scanning every buffer and not finding anything, block */
- WaitForSingleObject(m->sysex_buffer_signal, INFINITE);
+ if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
+#ifdef DEBUG
+ printf("PortMidi warning: get_free_sysex_buffer() wait timed out after 1000ms\n");
+#endif
+ }
}
found_sysex_buffer:
r->dwBytesRecorded = 0;
- m->error = midiOutPrepareHeader(m->handle.out, r, sizeof(MIDIHDR));
+ r->dwBufferLength = 0; /* changed to correct value later */
return r;
}
-
+#endif
static LPMIDIHDR get_free_output_buffer(PmInternal *midi)
{
LPMIDIHDR r = NULL;
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- if (!m->buffers) {
- if (allocate_buffers(m, OUTPUT_BYTES_PER_BUFFER, 2)) {
- return NULL;
- }
- }
- /* busy wait until we find a free buffer */
while (TRUE) {
int i;
for (i = 0; i < m->num_buffers; i++) {
+ /* cycle through buffers, modulo m->num_buffers */
m->next_buffer++;
if (m->next_buffer >= m->num_buffers) m->next_buffer = 0;
r = m->buffers[m->next_buffer];
if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer;
}
/* after scanning every buffer and not finding anything, block */
- WaitForSingleObject(m->buffer_signal, INFINITE);
+ if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
+#ifdef DEBUG
+ printf("PortMidi warning: get_free_output_buffer() wait timed out after 1000ms\n");
+#endif
+ /* if we're trying to send a sysex message, maybe the
+ * message is too big and we need more message buffers.
+ * Expand the buffer pool by 128KB using 1024-byte buffers.
+ */
+ /* first, expand the buffers array if necessary */
+ if (!m->buffers_expanded) {
+ LPMIDIHDR *new_buffers = (LPMIDIHDR *) pm_alloc(
+ (m->num_buffers + NUM_EXPANSION_BUFFERS) *
+ sizeof(LPMIDIHDR));
+ /* if no memory, we could return a no-memory error, but user
+ * probably will be unprepared to deal with it. Maybe the
+ * MIDI driver is temporarily hung so we should just wait.
+ * I don't know the right answer, but waiting is easier.
+ */
+ if (!new_buffers) continue;
+ /* copy buffers to new_buffers and replace buffers */
+ memcpy(new_buffers, m->buffers,
+ m->num_buffers * sizeof(LPMIDIHDR));
+ pm_free(m->buffers);
+ m->buffers = new_buffers;
+ m->max_buffers = m->num_buffers + NUM_EXPANSION_BUFFERS;
+ m->buffers_expanded = TRUE;
+ }
+ /* next, add one buffer and return it */
+ if (m->num_buffers < m->max_buffers) {
+ r = allocate_buffer(EXPANSION_BUFFER_LEN);
+ /* again, if there's no memory, we may not really be
+ * dead -- maybe the system is temporarily hung and
+ * we can just wait longer for a message buffer */
+ if (!r) continue;
+ m->buffers[m->num_buffers++] = r;
+ goto found_buffer; /* break out of 2 loops */
+ }
+ /* else, we've allocated all NUM_EXPANSION_BUFFERS buffers,
+ * and we have no free buffers to send. We'll just keep
+ * polling to see if any buffers show up.
+ */
+ }
}
found_buffer:
r->dwBytesRecorded = 0;
- m->error = midiOutPrepareHeader(m->handle.out, r, sizeof(MIDIHDR));
+ /* actual buffer length is saved in dwUser field */
+ r->dwBufferLength = (DWORD) r->dwUser;
return r;
}
+#ifdef EXPANDING_SYSEX_BUFFERS
+note: this is not working code, but might be useful if you want
+ to grow sysex buffers.
static PmError resize_sysex_buffer(PmInternal *midi, long old_size, long new_size)
{
LPMIDIHDR big;
int i;
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- /* buffer must be smaller than 64k, but be also be a multiple of 4 */
+ /* buffer must be smaller than 64k, but be also a multiple of 4 */
if (new_size > 65520) {
if (old_size >= 65520)
return pmBufferMaxSize;
@@ -407,35 +494,54 @@ static PmError resize_sysex_buffer(PmInternal *midi, long old_size, long new_siz
/* make sure we're not going to overwrite any memory */
assert(old_size <= new_size);
memcpy(big->lpData, m->hdr->lpData, old_size);
-
+ /* keep track of how many sysex bytes are in message so far */
+ big->dwBytesRecorded = m->hdr->dwBytesRecorded;
+ big->dwBufferLength = new_size;
/* find which buffer this was, and replace it */
-
- for (i = 0;i < m->num_sysex_buffers;i++) {
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
if (m->sysex_buffers[i] == m->hdr) {
m->sysex_buffers[i] = big;
+ m->sysex_buffer_size[i] = new_size;
pm_free(m->hdr);
m->hdr = big;
break;
}
}
- assert(i != m->num_sysex_buffers);
+ assert(i != NUM_SYSEX_BUFFERS);
return pmNoError;
-
}
+#endif
+
/*
=========================================================================================
begin midi input implementation
=========================================================================================
*/
+
+static PmError allocate_input_buffer(HMIDIIN h, long buffer_len)
+{
+ LPMIDIHDR hdr = allocate_buffer(buffer_len);
+ if (!hdr) return pmInsufficientMemory;
+ pm_hosterror = midiInPrepareHeader(h, hdr, sizeof(MIDIHDR));
+ if (pm_hosterror) {
+ pm_free(hdr);
+ return pm_hosterror;
+ }
+ pm_hosterror = midiInAddBuffer(h, hdr, sizeof(MIDIHDR));
+ return pm_hosterror;
+}
+
+
static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
{
DWORD dwDevice;
int i = midi->device_id;
+ int max_sysex_len = midi->buffer_len * 4;
+ int num_input_buffers = max_sysex_len / INPUT_SYSEX_LEN;
midiwinmm_type m;
- LPMIDIHDR hdr;
- long buffer_len;
+
dwDevice = (DWORD) descriptors[i].descriptor;
/* create system dependent device data */
@@ -443,22 +549,31 @@ static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
midi->descriptor = m;
if (!m) goto no_memory;
m->handle.in = NULL;
- m->buffers = NULL;
- m->num_buffers = 0;
- m->next_buffer = 0;
- m->sysex_buffers = NULL;
- m->num_sysex_buffers = 0;
- m->next_sysex_buffer = 0;
+ m->buffers = NULL; /* not used for input */
+ m->num_buffers = 0; /* not used for input */
+ m->max_buffers = FALSE; /* not used for input */
+ m->buffers_expanded = 0; /* not used for input */
+ m->next_buffer = 0; /* not used for input */
+ m->buffer_signal = 0; /* not used for input */
+#ifdef USE_SYSEX_BUFFERS
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++)
+ m->sysex_buffers[i] = NULL; /* not used for input */
+ m->next_sysex_buffer = 0; /* not used for input */
+#endif
m->last_time = 0;
m->first_message = TRUE; /* not used for input */
m->sysex_mode = FALSE;
m->sysex_word = 0;
m->sysex_byte_count = 0;
+ m->hdr = NULL; /* not used for input */
m->sync_time = 0;
m->delta = 0;
m->error = MMSYSERR_NOERROR;
- m->callback_error = MMSYSERR_NOERROR;
-
+ /* 4000 is based on Windows documentation -- that's the value used in the
+ memory manager. It's small enough that it should not hurt performance even
+ if it's not optimal.
+ */
+ InitializeCriticalSectionAndSpinCount(&m->lock, 4000);
/* open device */
pm_hosterror = midiInOpen(&(m->handle.in), /* input device handle */
dwDevice, /* device ID */
@@ -467,32 +582,15 @@ static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
CALLBACK_FUNCTION); /* callback is a procedure */
if (pm_hosterror) goto free_descriptor;
- /* allocate first buffer for sysex data */
- buffer_len = midi->buffer_len - 1;
- if (midi->buffer_len < 32)
- buffer_len = PM_DEFAULT_SYSEX_BUFFER_SIZE;
-
- hdr = allocate_sysex_buffer(buffer_len);
- if (!hdr) goto close_device;
- pm_hosterror = midiInPrepareHeader(m->handle.in, hdr, sizeof(MIDIHDR));
- if (pm_hosterror) {
- pm_free(hdr);
- goto close_device;
- }
- pm_hosterror = midiInAddBuffer(m->handle.in, hdr, sizeof(MIDIHDR));
- if (pm_hosterror) goto close_device;
-
- /* allocate second buffer */
- hdr = allocate_sysex_buffer(buffer_len);
- if (!hdr) goto close_device;
- pm_hosterror = midiInPrepareHeader(m->handle.in, hdr, sizeof(MIDIHDR));
- if (pm_hosterror) {
- pm_free(hdr);
- goto reset_device; /* because first buffer was added */
+ if (num_input_buffers < MIN_INPUT_BUFFERS)
+ num_input_buffers = MIN_INPUT_BUFFERS;
+ for (i = 0; i < num_input_buffers; i++) {
+ if (allocate_input_buffer(m->handle.in, INPUT_SYSEX_LEN)) {
+ /* either pm_hosterror was set, or the proper return code
+ is pmInsufficientMemory */
+ goto close_device;
+ }
}
- pm_hosterror = midiInAddBuffer(m->handle.in, hdr, sizeof(MIDIHDR));
- if (pm_hosterror) goto reset_device;
-
/* start device */
pm_hosterror = midiInStart(m->handle.in);
if (pm_hosterror) goto reset_device;
@@ -521,6 +619,12 @@ no_memory:
to free the parameter midi */
}
+static PmError winmm_in_poll(PmInternal *midi) {
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ return m->error;
+}
+
+
/* winmm_in_close -- close an open midi input device */
/*
@@ -540,6 +644,7 @@ static PmError winmm_in_close(PmInternal *midi)
pm_hosterror = midiInClose(m->handle.in);
}
midi->descriptor = NULL;
+ DeleteCriticalSection(&m->lock);
pm_free(m); /* delete */
if (pm_hosterror) {
int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
@@ -563,85 +668,124 @@ static void FAR PASCAL winmm_in_callback(
PmInternal *midi = (PmInternal *) dwInstance;
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- if (++entry > 1) {
- assert(FALSE);
- }
-
- /* for simplicity, this logic perhaps overly conservative */
- /* note also that this might leak memory if buffers are being
- returned as a result of midiInReset */
- if (m->callback_error) {
- entry--;
- return ;
- }
+ /* if this callback is reentered with data, we're in trouble. It's hard
+ * to imagine that Microsoft would allow callbacks to be reentrant --
+ * isn't the model that this is like a hardware interrupt? -- but I've
+ * seen reentrant behavior using a debugger, so it happens.
+ */
+ EnterCriticalSection(&m->lock);
switch (wMsg) {
case MIM_DATA: {
- /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
- message LOB;
- dwParam2 is time message received by input device driver, specified
- in [ms] from when midiInStart called.
- each message is expanded to include the status byte */
-
- long new_driver_time = dwParam2;
-
- if ((dwParam1 & 0x80) == 0) {
- /* not a status byte -- ignore it. This happens running the
- sysex.c test under Win2K with MidiMan USB 1x1 interface.
- Is it a driver bug or a Win32 bug? If not, there's a bug
- here somewhere. -RBD
- */
- } else { /* data to process */
- PmEvent event;
- if (midi->time_proc)
- dwParam2 = (*midi->time_proc)(midi->time_info);
- event.timestamp = dwParam2;
- event.message = dwParam1;
- pm_read_short(midi, &event);
- }
- break;
- }
- case MIM_LONGDATA: {
- MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1;
- unsigned char *data = lpMidiHdr->lpData;
- unsigned int i = 0;
- long size = sizeof(MIDIHDR) + lpMidiHdr->dwBufferLength;
- /* ignore sysex data, but free returned buffers */
- if (lpMidiHdr->dwBytesRecorded > 0 &&
- midi->filters & PM_FILT_SYSEX) {
- m->callback_error = midiInAddBuffer(hMidiIn, lpMidiHdr,
- sizeof(MIDIHDR));
- break;
- }
+ /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
+ message LOB;
+ dwParam2 is time message received by input device driver, specified
+ in [ms] from when midiInStart called.
+ each message is expanded to include the status byte */
+
+ long new_driver_time = dwParam2;
+
+ if ((dwParam1 & 0x80) == 0) {
+ /* not a status byte -- ignore it. This happened running the
+ sysex.c test under Win2K with MidiMan USB 1x1 interface,
+ but I can't reproduce it. -RBD
+ */
+ /* printf("non-status byte found\n"); */
+ } else { /* data to process */
+ PmEvent event;
if (midi->time_proc)
dwParam2 = (*midi->time_proc)(midi->time_info);
-
- while (i < lpMidiHdr->dwBytesRecorded) {
- /* collect bytes from *data into a word */
+ event.timestamp = dwParam2;
+ event.message = dwParam1;
+ pm_read_short(midi, &event);
+ }
+ break;
+ }
+ case MIM_LONGDATA: {
+ MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1;
+ unsigned char *data = lpMidiHdr->lpData;
+ unsigned int processed = 0;
+ int remaining = lpMidiHdr->dwBytesRecorded;
+ /* printf("midi_in_callback -- lpMidiHdr %x, %d bytes, %2x...\n",
+ lpMidiHdr, lpMidiHdr->dwBytesRecorded, *data); */
+ if (midi->time_proc)
+ dwParam2 = (*midi->time_proc)(midi->time_info);
+ /* can there be more than one message in one buffer? */
+ /* assume yes and iterate through them */
+ while (remaining > 0) {
+ unsigned int amt = pm_read_bytes(midi, data + processed,
+ remaining, dwParam2);
+ remaining -= amt;
+ processed += amt;
+ }
+#ifdef DELETE_THIS
+ unsigned int i = 0;
+ long size = sizeof(MIDIHDR) + lpMidiHdr->dwBufferLength;
+
+ while (i < lpMidiHdr->dwBytesRecorded) {
+ /* optimization: if message_count == 0, we are on an (output)
+ * message boundary so we can transfer data directly to the
+ * queue
+ */
+ PmEvent event;
+ if (midi->sysex_message_count == 0 &&
+ !midi->flush &&
+ i <= lpMidiHdr->dwBytesRecorded - 4 &&
+ ((event.message = (((long) data[0]) |
+ (((long) data[1]) << 8) | (((long) data[2]) << 16) |
+ (((long) data[3]) << 24))) &
+ 0x80808080) == 0) { /* all data, no status */
+ event.timestamp = dwParam2;
+ if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
+ midi->flush = TRUE;
+ }
+ i += 4;
+ data += 4;
+ /* non-optimized: process one byte at a time. This is used to
+ * handle any embedded SYSEX or EOX bytes and to finish */
+ } else {
pm_read_byte(midi, *data, dwParam2);
data++;
i++;
}
- /* when a device is closed, the pending MIM_LONGDATA buffers are
- returned to this callback with dwBytesRecorded == 0. In this
- case, we do not want to send them back to the interface (if
- we do, the interface will not close, and Windows OS may hang). */
- if (lpMidiHdr->dwBytesRecorded > 0) {
- m->callback_error = midiInAddBuffer(hMidiIn, lpMidiHdr,
- sizeof(MIDIHDR));
- } else {
- pm_free(lpMidiHdr);
- }
- break;
}
- case MIM_OPEN: /* fall thru */
+#endif
+ /* when a device is closed, the pending MIM_LONGDATA buffers are
+ returned to this callback with dwBytesRecorded == 0. In this
+ case, we do not want to send them back to the interface (if
+ we do, the interface will not close, and Windows OS may hang). */
+ if (lpMidiHdr->dwBytesRecorded > 0) {
+ lpMidiHdr->dwBytesRecorded = 0;
+ lpMidiHdr->dwFlags = 0;
+ /* note: no error checking -- can this actually fail? */
+ assert(midiInPrepareHeader(hMidiIn, lpMidiHdr,
+ sizeof(MIDIHDR)) == MMSYSERR_NOERROR);
+ /* note: I don't think this can fail except possibly for
+ * MMSYSERR_NOMEM, but the pain of reporting this
+ * unlikely but probably catastrophic error does not seem
+ * worth it.
+ */
+ assert(midiInAddBuffer(hMidiIn, lpMidiHdr,
+ sizeof(MIDIHDR)) == MMSYSERR_NOERROR);
+ } else {
+ pm_free(lpMidiHdr);
+ }
+ break;
+ }
+ case MIM_OPEN:
+ break;
case MIM_CLOSE:
+ break;
case MIM_ERROR:
+ /* printf("MIM_ERROR\n"); */
+ break;
case MIM_LONGERROR:
+ /* printf("MIM_LONGERROR\n"); */
+ break;
default:
break;
}
- entry--;
+ LeaveCriticalSection(&m->lock);
}
/*
@@ -668,28 +812,6 @@ static int add_to_buffer(midiwinmm_type m, LPMIDIHDR hdr,
return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength;
}
-#ifdef GARBAGE
-static void start_sysex_buffer(LPMIDIHDR hdr, unsigned long delta)
-{
- unsigned long *ptr = (unsigned long *) hdr->lpData;
- *ptr++ = delta;
- *ptr++ = 0;
- *ptr = MEVT_F_LONG;
- hdr->dwBytesRecorded = 3 * sizeof(long);
-}
-
-static int add_byte_to_buffer(midiwinmm_type m, LPMIDIHDR hdr,
- unsigned char midi_byte)
-{
- allocate message if hdr is null
- send message if it is full
- add byte to non - full message
- unsigned char *ptr = (unsigned char *) (hdr->lpData + hdr->dwBytesRecorded);
- *ptr = midi_byte;
- return ++hdr->dwBytesRecorded >= hdr->dwBufferLength;
-}
-#endif
-
static PmTimestamp pm_time_get(midiwinmm_type m)
{
@@ -702,26 +824,6 @@ static PmTimestamp pm_time_get(midiwinmm_type m)
return mmtime.u.ticks;
}
-#ifdef GARBAGE
-static unsigned long synchronize(PmInternal *midi, midiwinmm_type m)
-{
- unsigned long pm_stream_time_2 = pm_time_get(m);
- unsigned long real_time;
- unsigned long pm_stream_time;
- /* figure out the time */
- do {
- /* read real_time between two reads of stream time */
- pm_stream_time = pm_stream_time_2;
- real_time = (*midi->time_proc)(midi->time_info);
- pm_stream_time_2 = pm_time_get(m);
- /* repeat if more than 1ms elapsed */
- } while (pm_stream_time_2 > pm_stream_time + 1);
- m->delta = pm_stream_time - real_time;
- m->sync_time = real_time;
- return real_time;
-}
-#endif
-
/* end helper routines used by midiOutStream interface */
@@ -733,6 +835,9 @@ static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
midiwinmm_type m;
MIDIPROPTEMPO propdata;
MIDIPROPTIMEDIV divdata;
+ int max_sysex_len = midi->buffer_len * 4;
+ int output_buffer_len;
+ int num_buffers;
dwDevice = (DWORD) descriptors[i].descriptor;
/* create system dependent device data */
@@ -742,10 +847,14 @@ static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
m->handle.out = NULL;
m->buffers = NULL;
m->num_buffers = 0;
+ m->max_buffers = 0;
+ m->buffers_expanded = FALSE;
m->next_buffer = 0;
- m->sysex_buffers = NULL;
- m->num_sysex_buffers = 0;
+#ifdef USE_SYSEX_BUFFERS
+ m->sysex_buffers[0] = NULL;
+ m->sysex_buffers[1] = NULL;
m->next_sysex_buffer = 0;
+#endif
m->last_time = 0;
m->first_message = TRUE; /* we treat first message as special case */
m->sysex_mode = FALSE;
@@ -755,22 +864,20 @@ static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
m->sync_time = 0;
m->delta = 0;
m->error = MMSYSERR_NOERROR;
- m->callback_error = MMSYSERR_NOERROR;
/* create a signal */
m->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
- m->sysex_buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
/* this should only fail when there are very serious problems */
assert(m->buffer_signal);
- assert(m->sysex_buffer_signal);
/* open device */
if (midi->latency == 0) {
/* use simple midi out calls */
pm_hosterror = midiOutOpen((LPHMIDIOUT) & m->handle.out, /* device Handle */
dwDevice, /* device ID */
- (DWORD) winmm_out_callback,
+ /* note: same callback fn as for StreamOpen: */
+ (DWORD) winmm_streamout_callback, /* callback fn */
(DWORD) midi, /* callback instance data */
CALLBACK_FUNCTION); /* callback type */
} else {
@@ -786,12 +893,17 @@ static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
goto free_descriptor;
}
- if (midi->latency != 0) {
+ if (midi->latency == 0) {
+ num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
+ output_buffer_len = max_sysex_len / num_buffers;
+ if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
+ output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
+ } else {
long dur = 0;
- /* with stream output, specified number of buffers allocated here */
- int count = midi->buffer_len;
- if (count == 0)
- count = midi->latency / 2; /* how many buffers to get */
+ num_buffers = max(midi->buffer_len, midi->latency / 2);
+ if (num_buffers < MIN_STREAM_BUFFERS)
+ num_buffers = MIN_STREAM_BUFFERS;
+ output_buffer_len = STREAM_BUFFER_LEN;
propdata.cbStruct = sizeof(MIDIPROPTEMPO);
propdata.dwTempo = 480000; /* microseconds per quarter */
@@ -806,12 +918,12 @@ static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
(LPBYTE) & divdata,
MIDIPROP_SET | MIDIPROP_TIMEDIV);
if (pm_hosterror) goto close_device;
-
- /* allocate at least 3 buffers */
- if (count < 3) count = 3;
- if (allocate_buffers(m, OUTPUT_BYTES_PER_BUFFER, count)) goto free_buffers;
- if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER, 2)) goto free_buffers;
- /* start device */
+ }
+ /* allocate buffers */
+ if (allocate_buffers(m, output_buffer_len, num_buffers))
+ goto free_buffers;
+ /* start device */
+ if (midi->latency != 0) {
pm_hosterror = midiStreamRestart(m->handle.stream);
if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
}
@@ -839,6 +951,7 @@ no_memory:
/**/
static void winmm_out_delete(PmInternal *midi)
{
+ int i;
/* delete system dependent device data */
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
if (m) {
@@ -846,27 +959,19 @@ static void winmm_out_delete(PmInternal *midi)
/* don't report errors -- better not to stop cleanup */
CloseHandle(m->buffer_signal);
}
- if (m->sysex_buffer_signal) {
- /* don't report errors -- better not to stop cleanup */
- CloseHandle(m->sysex_buffer_signal);
- }
- if (m->buffers) {
- /* if using stream output, free buffers */
- int i;
- for (i = 0; i < m->num_buffers; i++) {
- if (m->buffers[i]) pm_free(m->buffers[i]);
- }
- pm_free(m->buffers);
+ /* if using stream output, free buffers */
+ for (i = 0; i < m->num_buffers; i++) {
+ if (m->buffers[i]) pm_free(m->buffers[i]);
}
-
- if (m->sysex_buffers) {
- /* free sysex buffers */
- int i;
- for (i = 0; i < m->num_sysex_buffers; i++) {
- if (m->sysex_buffers[i]) pm_free(m->sysex_buffers[i]);
- }
- pm_free(m->sysex_buffers);
+ m->num_buffers = 0;
+ pm_free(m->buffers);
+ m->max_buffers = 0;
+#ifdef USE_SYSEX_BUFFERS
+ /* free sysex buffers */
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
+ if (m->sysex_buffers[i]) pm_free(m->sysex_buffers[i]);
}
+#endif
}
midi->descriptor = NULL;
pm_free(m); /* delete */
@@ -910,6 +1015,40 @@ static PmError winmm_out_abort(PmInternal *midi)
return m->error ? pmHostError : pmNoError;
}
+
+static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ assert(m);
+ if (m->hdr) {
+ m->error = midiOutPrepareHeader(m->handle.out, m->hdr,
+ sizeof(MIDIHDR));
+ if (m->error) {
+ /* do not send message */
+ } else if (midi->latency == 0) {
+ /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded
+ * should be zero. This is set in get_free_sysex_buffer().
+ * The msg length goes in dwBufferLength in spite of what
+ * Microsoft documentation says (or doesn't say). */
+ m->hdr->dwBufferLength = m->hdr->dwBytesRecorded;
+ m->hdr->dwBytesRecorded = 0;
+ m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
+ } else {
+ m->error = midiStreamOut(m->handle.stream, m->hdr,
+ sizeof(MIDIHDR));
+ }
+ midi->fill_base = NULL;
+ m->hdr = NULL;
+ if (m->error) {
+ m->hdr->dwFlags = 0; /* release the buffer */
+ return pmHostError;
+ }
+ }
+ return pmNoError;
+}
+
+
+
#ifdef GARBAGE
static PmError winmm_write_sysex_byte(PmInternal *midi, unsigned char byte)
{
@@ -980,6 +1119,7 @@ static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
PmError rslt = pmNoError;
assert(m);
+
if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
m->error = midiOutShortMsg(m->handle.out, event->message);
if (m->error) rslt = pmHostError;
@@ -1000,73 +1140,65 @@ static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
m->hdr = get_free_output_buffer(midi);
}
full = add_to_buffer(m, m->hdr, delta, event->message);
- if (full) {
- m->error = midiStreamOut(m->handle.stream, m->hdr,
- sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
- m->hdr = NULL;
- }
+ if (full) rslt = winmm_write_flush(midi, when);
}
return rslt;
}
-
+#define winmm_begin_sysex winmm_write_flush
+#ifndef winmm_begin_sysex
static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp)
{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
PmError rslt = pmNoError;
+
if (midi->latency == 0) {
/* do nothing -- it's handled in winmm_write_byte */
} else {
- midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- /* sysex expects an empty buffer */
- if (m->hdr) {
- m->error = midiStreamOut(m->handle.stream, m->hdr, sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
- }
- m->hdr = NULL;
+ /* sysex expects an empty sysex buffer, so send whatever is here */
+ rslt = winmm_write_flush(midi);
}
return rslt;
}
-
+#endif
static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp)
{
+ /* could check for callback_error here, but I haven't checked
+ * what happens if we exit early and don't finish the sysex msg
+ * and clean up
+ */
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
PmError rslt = pmNoError;
- assert(m);
-
+ LPMIDIHDR hdr = m->hdr;
+ if (!hdr) return rslt; /* something bad happened earlier,
+ do not report an error because it would have been
+ reported (at least) once already */
+ /* a(n old) version of MIDI YOKE requires a zero byte after
+ * the sysex message, but do not increment dwBytesRecorded: */
+ hdr->lpData[hdr->dwBytesRecorded] = 0;
if (midi->latency == 0) {
- /* Not using the stream interface. The entire sysex message is
- in m->hdr, and we send it using midiOutLongMsg.
- */
- m->hdr->dwBytesRecorded = m->sysex_byte_count;
- /*
- { int i; int len = m->hdr->dwBytesRecorded;
+#ifdef DEBUG_PRINT_BEFORE_SENDING_SYSEX
+ /* DEBUG CODE: */
+ { int i; int len = m->hdr->dwBufferLength;
printf("OutLongMsg %d ", len);
for (i = 0; i < len; i++) {
- printf("%2x ", msg_buffer[i]);
+ printf("%2x ", (unsigned char) (m->hdr->lpData[i]));
}
}
- */
-
- m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
- } else if (m->hdr) {
+#endif
+ } else {
/* Using stream interface. There are accumulated bytes in m->hdr
to send using midiStreamOut
*/
/* add bytes recorded to MIDIEVENT length, but don't
count the MIDIEVENT data (3 longs) */
- MIDIEVENT *evt = (MIDIEVENT *) m->hdr->lpData;
- evt->dwEvent += m->hdr->dwBytesRecorded - 3 * sizeof(long);
+ MIDIEVENT *evt = (MIDIEVENT *) (hdr->lpData);
+ evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
/* round up BytesRecorded to multiple of 4 */
- m->hdr->dwBytesRecorded = (m->hdr->dwBytesRecorded + 3) & ~3;
-
- m->error = midiStreamOut(m->handle.stream, m->hdr,
- sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
+ hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
}
- m->hdr = NULL; /* make sure we don't send it again */
+ rslt = winmm_write_flush(midi, timestamp);
return rslt;
}
@@ -1077,40 +1209,21 @@ static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
/* write a sysex byte */
PmError rslt = pmNoError;
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ LPMIDIHDR hdr = m->hdr;
+ unsigned char *msg_buffer;
assert(m);
- if (midi->latency == 0) {
- /* Not using stream interface. Accumulate the entire message into
- m->hdr */
- unsigned char *msg_buffer;
- /* at the beginning of sysex, m->hdr is NULL */
- if (!m->hdr) { /* allocate a buffer if none allocated yet */
- m->hdr = get_free_sysex_buffer(midi);
- if (!m->hdr) return pmInsufficientMemory;
- m->sysex_byte_count = 0;
- }
- /* figure out where to write byte */
- msg_buffer = (unsigned char *) (m->hdr->lpData);
- assert(m->hdr->lpData == (char *) (m->hdr + 1));
-
- /* append byte to message */
- msg_buffer[m->sysex_byte_count++] = byte;
-
- /* check for overflow */
- if (m->sysex_byte_count >= m->hdr->dwBufferLength) {
- rslt = resize_sysex_buffer(midi, m->sysex_byte_count, m->sysex_byte_count * 2);
-
- if (rslt == pmBufferMaxSize) /* if the buffer can't be resized */
- rslt = winmm_end_sysex(midi, timestamp); /* write what we've got and continue */
-
- }
-
- } else { /* latency is not zero, use stream interface: accumulate
- sysex data in m->hdr and send whenever the buffer fills */
- int full;
- unsigned char *ptr;
-
- /* if m->hdr does not exist, allocate it */
- if (m->hdr == NULL) {
+ if (!hdr) {
+ m->hdr = hdr = get_free_output_buffer(midi);
+ assert(hdr);
+ midi->fill_base = m->hdr->lpData;
+ midi->fill_offset_ptr = &(hdr->dwBytesRecorded);
+ /* when buffer fills, Pm_WriteSysEx will revert to calling
+ * pmwin_write_byte, which expect to have space, so leave
+ * one byte free for pmwin_write_byte. Leave another byte
+ * of space for zero after message to make early version of
+ * MIDI YOKE driver happy -- therefore dwBufferLength - 2 */
+ midi->fill_length = hdr->dwBufferLength - 2;
+ if (midi->latency != 0) {
unsigned long when = (unsigned long) timestamp;
unsigned long delta;
unsigned long *ptr;
@@ -1122,52 +1235,47 @@ static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
delta = when - m->last_time;
m->last_time = when;
- m->hdr = get_free_sysex_buffer(midi);
- assert(m->hdr);
- ptr = (unsigned long *) m->hdr->lpData;
+ ptr = (unsigned long *) hdr->lpData;
*ptr++ = delta;
*ptr++ = 0;
*ptr = MEVT_F_LONG;
- m->hdr->dwBytesRecorded = 3 * sizeof(long);
+ hdr->dwBytesRecorded = 3 * sizeof(long);
+ /* data will be added at an offset of dwBytesRecorded ... */
}
+ }
+ /* add the data byte */
+ msg_buffer = (unsigned char *) (hdr->lpData);
+ msg_buffer[hdr->dwBytesRecorded++] = byte;
+
+ /* see if buffer is full, leave one byte extra for pad */
+ if (hdr->dwBytesRecorded >= hdr->dwBufferLength - 1) {
+ /* write what we've got and continue */
+ rslt = winmm_end_sysex(midi, timestamp);
+ }
+ return rslt;
+}
+
+#ifdef EXPANDING_SYSEX_BUFFERS
+note: this code is here as an aid in case you want sysex buffers
+ to expand to hold large messages completely. If so, you
+ will want to change SYSEX_BYTES_PER_BUFFER above to some
+ variable that remembers the buffer size. A good place to
+ put this value would be in the hdr->dwUser field.
- /* add the data byte */
- ptr = (unsigned char *) (m->hdr->lpData + m->hdr->dwBytesRecorded);
- *ptr = byte;
- full = ++m->hdr->dwBytesRecorded >= m->hdr->dwBufferLength;
+ rslt = resize_sysex_buffer(midi, m->sysex_byte_count,
+ m->sysex_byte_count * 2);
- /* see if we need to resize */
- if (full) {
- int bytesRecorded = m->hdr->dwBytesRecorded; /* this field gets wiped out, so we'll save it */
+ if (rslt == pmBufferMaxSize) /* if the buffer can't be resized */
+#endif
+#ifdef EXPANDING_SYSEX_BUFFERS
+ int bytesRecorded = hdr->dwBytesRecorded; /* this field gets wiped out, so we'll save it */
rslt = resize_sysex_buffer(midi, bytesRecorded, 2 * bytesRecorded);
- m->hdr->dwBytesRecorded = bytesRecorded;
+ hdr->dwBytesRecorded = bytesRecorded;
if (rslt == pmBufferMaxSize) /* if buffer can't be resized */
- rslt = winmm_end_sysex(midi, timestamp); /* write what we've got and continue */
- }
- }
- return rslt;
-}
-
+#endif
-static PmError winmm_write_flush(PmInternal *midi)
-{
- PmError rslt = pmNoError;
- midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- assert(m);
- if (midi->latency == 0) {
- /* all messages are sent immediately */
- } else if ((m->hdr) && (!midi->sysex_in_progress)) {
- /* sysex messages are sent upon completion, but ordinary messages
- may be sitting in a buffer
- */
- m->error = midiStreamOut(m->handle.stream, m->hdr, sizeof(MIDIHDR));
- m->hdr = NULL;
- if (m->error) rslt = pmHostError;
- }
- return rslt;
-}
static PmTimestamp winmm_synchronize(PmInternal *midi)
{
@@ -1195,223 +1303,32 @@ static PmTimestamp winmm_synchronize(PmInternal *midi)
return real_time;
}
-
-#ifdef GARBAGE
-static PmError winmm_write(PmInternal *midi,
- PmEvent *buffer,
- long length)
-{
- midiwinmm_type m = (midiwinmm_type) midi->descriptor;
- unsigned long now;
- int i;
- long msg;
- PmError rslt = pmNoError;
-
- m->error = MMSYSERR_NOERROR;
- if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
- for (i = 0; (i < length) && (rslt == pmNoError); i++) {
- int b = 0; /* count sysex bytes as they are handled */
- msg = buffer[i].message;
- if ((msg & 0xFF) == MIDI_SYSEX) {
- /* start a sysex message */
- m->sysex_mode = TRUE;
- unsigned char midi_byte = (unsigned char) msg;
- rslt = winmm_write_sysex_byte(midi, midi_byte);
- b = 8;
- } else if ((msg & 0x80) && ((msg & 0xFF) != MIDI_EOX)) {
- /* a non-sysex message */
- m->error = midiOutShortMsg(m->handle.out, msg);
- if (m->error) rslt = pmHostError;
- /* any non-real-time message will terminate sysex message */
- if (!is_real_time(msg)) m->sysex_mode = FALSE;
- }
- /* transmit sysex bytes until we find EOX */
- if (m->sysex_mode) {
- while (b < 32 /*bits*/ && (rslt == pmNoError)) {
- unsigned char midi_byte = (unsigned char) (msg >> b);
- rslt = winmm_write_sysex_byte(midi, midi_byte);
- if (midi_byte == MIDI_EOX) {
- b = 24; /* end of message */
- m->sysex_mode = FALSE;
- }
- b += 8;
- }
- }
- }
- } else { /* use midiStream interface -- pass data through buffers */
- LPMIDIHDR hdr = NULL;
- now = (*midi->time_proc)(midi->time_info);
- if (m->first_message || m->sync_time + 100 /*ms*/ < now) {
- /* time to resync */
- now = synchronize(midi, m);
- m->first_message = FALSE;
- }
- for (i = 0; i < length && rslt == pmNoError; i++) {
- unsigned long when = buffer[i].timestamp;
- unsigned long delta;
- if (when == 0) when = now;
- /* when is in real_time; translate to intended stream time */
- when = when + m->delta + midi->latency;
- /* make sure we don't go backward in time */
- if (when < m->last_time) when = m->last_time;
- delta = when - m->last_time;
- m->last_time = when;
- /* before we insert any data, we must have a buffer */
- if (hdr == NULL) {
- /* stream interface: buffers allocated when stream is opened */
- hdr = get_free_output_buffer(midi);
- assert(hdr);
- if (m->sysex_mode) {
- /* we are in the middle of a sysex message */
- start_sysex_buffer(hdr, delta);
- }
- }
- msg = buffer[i].message;
- if ((msg & 0xFF) == MIDI_SYSEX) {
- /* sysex expects an empty buffer */
- if (hdr->dwBytesRecorded != 0) {
- m->error = midiStreamOut(m->handle.stream, hdr, sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
- hdr = get_free_output_buffer(midi);
- assert(hdr);
- }
- /* when we see a MIDI_SYSEX, we always enter sysex mode and call
- start_sysex_buffer() */
- start_sysex_buffer(hdr, delta);
- m->sysex_mode = TRUE;
- }
- /* allow a non-real-time status byte to terminate sysex message */
- if (m->sysex_mode && (msg & 0x80) && (msg & 0xFF) != MIDI_SYSEX &&
- !is_real_time(msg)) {
- /* I'm not sure what WinMM does if you send an incomplete sysex
- message, but the best way out of this mess seems to be to
- recreate the code used when you encounter an EOX, so ...
- */
- MIDIEVENT *evt = (MIDIEVENT) hdr->lpData;
- evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
- /* round up BytesRecorded to multiple of 4 */
- hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
- m->error = midiStreamOut(m->handle.stream, hdr,
- sizeof(MIDIHDR));
- if (m->error) {
- rslt = pmHostError;
- }
- hdr = NULL; /* make sure we don't send it again */
- m->sysex_mode = FALSE; /* skip to normal message send code */
- }
- if (m->sysex_mode) {
- int b = 0; /* count bytes as they are handled */
- while (b < 32 /* bits per word */ && (rslt == pmNoError)) {
- int full;
- unsigned char midi_byte = (unsigned char) (msg >> b);
- if (!hdr) {
- hdr = get_free_output_buffer(midi);
- assert(hdr);
- /* get ready to put sysex bytes in buffer */
- start_sysex_buffer(hdr, delta);
- }
- full = add_byte_to_buffer(m, hdr, midi_byte);
- if (midi_byte == MIDI_EOX) {
- b = 24; /* pretend this is last byte to exit loop */
- m->sysex_mode = FALSE;
- }
- /* see if it's time to send buffer, note that by always
- sending complete sysex message right away, we can use
- this code to set up the MIDIEVENT properly
- */
- if (full || midi_byte == MIDI_EOX) {
- /* add bytes recorded to MIDIEVENT length, but don't
- count the MIDIEVENT data (3 longs) */
- MIDIEVENT *evt = (MIDIEVENT *) hdr->lpData;
- evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
- /* round up BytesRecorded to multiple of 4 */
- hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
- m->error = midiStreamOut(m->handle.stream, hdr,
- sizeof(MIDIHDR));
- if (m->error) {
- rslt = pmHostError;
- }
- hdr = NULL; /* make sure we don't send it again */
- }
- b += 8; /* shift to next byte */
- }
- /* test rslt here in case it was set when we terminated a sysex early
- (see above) */
- } else if (rslt == pmNoError) {
- int full = add_to_buffer(m, hdr, delta, msg);
- if (full) {
- m->error = midiStreamOut(m->handle.stream, hdr,
- sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
- hdr = NULL;
- }
- }
- }
- if (hdr && rslt == pmNoError) {
- if (m->sysex_mode) {
- MIDIEVENT *evt = (MIDIEVENT *) hdr->lpData;
- evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
- /* round up BytesRecorded to multiple of 4 */
- hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
- }
- m->error = midiStreamOut(m->handle.stream, hdr, sizeof(MIDIHDR));
- if (m->error) rslt = pmHostError;
- }
- }
- return rslt;
-}
-#endif
-
-
+#ifdef USE_SYSEX_BUFFERS
/* winmm_out_callback -- recycle sysex buffers */
static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg,
- DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
+ DWORD dwInstance, DWORD dwParam1,
+ DWORD dwParam2)
{
- int i;
PmInternal *midi = (PmInternal *) dwInstance;
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
int err = 0; /* set to 0 so that no buffer match will also be an error */
- static int entry = 0;
- if (++entry > 1) {
- assert(FALSE);
- }
- if (m->callback_error || wMsg != MOM_DONE) {
- entry--;
- return ;
- }
+
/* Future optimization: eliminate UnprepareHeader calls -- they aren't
necessary; however, this code uses the prepared-flag to indicate which
buffers are free, so we need to do something to flag empty buffers if
we leave them prepared
*/
- m->callback_error = midiOutUnprepareHeader(m->handle.out, hdr,
- sizeof(MIDIHDR));
+ printf("out_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
+ hdr, wMsg, MOM_DONE);
+ if (wMsg == MOM_DONE)
+ assert(midiOutUnprepareHeader(m->handle.out, hdr,
+ sizeof(MIDIHDR)) == MMSYSERR_NOERROR);
/* notify waiting sender that a buffer is available */
- /* any errors could be reported via callback_error, but this is always
- treated as a Midi error, so we'd have to write a lot more code to
- detect that a non-Midi error occurred and do the right thing to find
- the corresponding error message text. Therefore, just use assert()
- */
-
- /* determine if this is an output buffer or a sysex buffer */
-
- for (i = 0 ;i < m->num_buffers;i++) {
- if (hdr == m->buffers[i]) {
- err = SetEvent(m->buffer_signal);
- break;
- }
- }
- for (i = 0 ;i < m->num_sysex_buffers;i++) {
- if (hdr == m->sysex_buffers[i]) {
- err = SetEvent(m->sysex_buffer_signal);
- break;
- }
- }
+ err = SetEvent(m->buffer_signal);
assert(err); /* false -> error */
- entry--;
}
-
+#endif
/* winmm_streamout_callback -- unprepare (free) buffer header */
static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
@@ -1421,24 +1338,19 @@ static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
midiwinmm_type m = (midiwinmm_type) midi->descriptor;
LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
int err;
- static int entry = 0;
- if (++entry > 1) {
- /* We've reentered this routine. I assume this never happens, but
- check to make sure. Apparently, it is possible that this callback
- can be called reentrantly because it happened once while debugging.
- It looks like this routine is actually reentrant so we can remove
- the assertion if necessary. */
- assert(FALSE);
- }
- if (m->callback_error || wMsg != MOM_DONE) {
- entry--;
- return ;
+
+ /* Even if an error is pending, I think we should unprepare msgs and
+ signal their arrival
+ */
+ /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
+ hdr, wMsg, MOM_DONE); */
+ if (wMsg == MOM_DONE) {
+ assert(midiOutUnprepareHeader(m->handle.out, hdr,
+ sizeof(MIDIHDR)) == MMSYSERR_NOERROR);
}
- m->callback_error = midiOutUnprepareHeader(m->handle.out, hdr,
- sizeof(MIDIHDR));
+ /* signal client in case it is blocked waiting for buffer */
err = SetEvent(m->buffer_signal);
assert(err); /* false -> error */
- entry--;
}
@@ -1460,7 +1372,7 @@ pm_fns_node pm_winmm_in_dictionary = {
winmm_in_open,
winmm_in_abort,
winmm_in_close,
- success_poll,
+ winmm_in_poll,
winmm_has_host_error,
winmm_get_host_error
};
@@ -1529,7 +1441,7 @@ void pm_winmm_term( void )
trying to debug client app */
if (winmm_has_host_error(midi)) {
winmm_get_host_error(midi, msg, PM_HOST_ERROR_MSG_LEN);
- printf(msg);
+ printf("%s\n", msg);
}
#endif
/* close all open ports */
@@ -1537,6 +1449,14 @@ void pm_winmm_term( void )
}
}
}
+ if (midi_in_caps) {
+ pm_free(midi_in_caps);
+ midi_in_caps = NULL;
+ }
+ if (midi_out_caps) {
+ pm_free(midi_out_caps);
+ midi_out_caps = NULL;
+ }
#ifdef DEBUG
if (doneAny) {
printf("warning: devices were left open. They have been closed.\n");