1 /* pmwinmm.c -- system specific definitions */
2 
3 #ifdef _MSC_VER
4  #pragma warning(disable: 4133) // stop warnings about implicit typecasts
5 #endif
6 
7 #ifndef _WIN32_WINNT
8     /* without this define, InitializeCriticalSectionAndSpinCount is
9      * undefined. This version level means "Windows 2000 and higher"
10      */
11     #define _WIN32_WINNT 0x0500
12 #endif
13 
14 #include "windows.h"
15 #include "mmsystem.h"
16 #include "portmidi.h"
17 #include "pmutil.h"
18 #include "pminternal.h"
19 #include "pmwinmm.h"
20 #include <string.h>
21 #include "porttime.h"
22 
23 /* asserts used to verify portMidi code logic is sound; later may want
24     something more graceful */
25 #include <assert.h>
26 #ifdef DEBUG
27 /* this printf stuff really important for debugging client app w/host errors.
28     probably want to do something else besides read/write from/to console
29     for portability, however */
30 #define STRING_MAX 80
31 #include "stdio.h"
32 #endif
33 
34 #define streql(x, y) (strcmp(x, y) == 0)
35 
36 #define MIDI_SYSEX      0xf0
37 #define MIDI_EOX        0xf7
38 
39 
max(int32_t a,int32_t b)40 static int32_t max( int32_t a, int32_t b)
41 {
42 	return (a > b) ? a : b;
43 }
44 
45 /* callback routines */
46 static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn,
47                                        UINT wMsg, DWORD_PTR dwInstance,
48                                        DWORD_PTR dwParam1, DWORD_PTR dwParam2);
49 static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
50                                               DWORD_PTR dwInstance,
51                                               DWORD_PTR dwParam1,
52                                               DWORD_PTR dwParam2);
53 
54 extern pm_fns_node pm_winmm_in_dictionary;
55 extern pm_fns_node pm_winmm_out_dictionary;
56 
57 static void winmm_out_delete(PmInternal *midi); /* forward reference */
58 
59 /*
60 A note about buffers: WinMM seems to hold onto buffers longer than
61 one would expect, e.g. when I tried using 2 small buffers to send
62 long sysex messages, at some point WinMM held both buffers. This problem
63 was fixed by making buffers bigger. Therefore, it seems that there should
64 be enough buffer space to hold a whole sysex message.
65 
66 The bufferSize passed into Pm_OpenInput (passed into here as buffer_len)
67 will be used to estimate the largest sysex message (= buffer_len * 4 bytes).
68 Call that the max_sysex_len = buffer_len * 4.
69 
70 For simple midi output (latency == 0), allocate 3 buffers, each with half
71 the size of max_sysex_len, but each at least 256 bytes.
72 
73 For stream output, there will already be enough space in very short
74 buffers, so use them, but make sure there are at least 16.
75 
76 For input, use many small buffers rather than 2 large ones so that when
77 there are short sysex messages arriving frequently (as in control surfaces)
78 there will be more free buffers to fill. Use max_sysex_len / 64 buffers,
79 but at least 16, of size 64 bytes each.
80 
81 The following constants help to represent these design parameters:
82 */
83 #define NUM_SIMPLE_SYSEX_BUFFERS 3
84 #define MIN_SIMPLE_SYSEX_LEN 256
85 
86 #define MIN_STREAM_BUFFERS 16
87 #define STREAM_BUFFER_LEN 24
88 
89 #define INPUT_SYSEX_LEN 64
90 #define MIN_INPUT_BUFFERS 16
91 
92 /* if we run out of space for output (assume this is due to a sysex msg,
93    expand by up to NUM_EXPANSION_BUFFERS in increments of EXPANSION_BUFFER_LEN
94  */
95 #define NUM_EXPANSION_BUFFERS 128
96 #define EXPANSION_BUFFER_LEN 1024
97 
98 /* A sysex buffer has 3 DWORDS as a header plus the actual message size */
99 #define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3)
100 /* A MIDIHDR with a sysex message is the buffer length plus the header size */
101 #define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
102 
103 /*
104 ==============================================================================
105 win32 mmedia system specific structure passed to midi callbacks
106 ==============================================================================
107 */
108 
109 /* global winmm device info */
110 MIDIINCAPS *midi_in_caps = NULL;
111 MIDIINCAPS midi_in_mapper_caps;
112 UINT midi_num_inputs = 0;
113 MIDIOUTCAPS *midi_out_caps = NULL;
114 MIDIOUTCAPS midi_out_mapper_caps;
115 UINT midi_num_outputs = 0;
116 
117 /* per device info */
118 typedef struct midiwinmm_struct {
119     union {
120         HMIDISTRM stream;   /* windows handle for stream */
121         HMIDIOUT out;       /* windows handle for out calls */
122         HMIDIIN in;         /* windows handle for in calls */
123     } handle;
124 
125     /* midi output messages are sent in these buffers, which are allocated
126      * in a round-robin fashion, using next_buffer as an index
127      */
128     LPMIDIHDR *buffers;     /* pool of buffers for midi in or out data */
129     int max_buffers;        /* length of buffers array */
130     int buffers_expanded;   /* buffers array expanded for extra msgs? */
131     int num_buffers;        /* how many buffers allocated in buffers array */
132     int next_buffer;        /* index of next buffer to send */
133     HANDLE buffer_signal;   /* used to wait for buffer to become free */
134     unsigned long last_time;    /* last output time */
135     int first_message;          /* flag: treat first message differently */
136     int sysex_mode;             /* middle of sending sysex */
137     unsigned long sysex_word;   /* accumulate data when receiving sysex */
138     unsigned int sysex_byte_count; /* count how many received */
139     LPMIDIHDR hdr;              /* the message accumulating sysex to send */
140     unsigned long sync_time;    /* when did we last determine delta? */
141     long delta;                 /* difference between stream time and
142                                        real time */
143     int error;                  /* host error from doing port midi call */
144     CRITICAL_SECTION lock;      /* prevents reentrant callbacks (input only) */
145 } midiwinmm_node, *midiwinmm_type;
146 
147 
148 /*
149 =============================================================================
150 general MIDI device queries
151 =============================================================================
152 */
pm_winmm_general_inputs()153 static void pm_winmm_general_inputs()
154 {
155     UINT i;
156     WORD wRtn;
157     midi_num_inputs = midiInGetNumDevs();
158     midi_in_caps = (MIDIINCAPS *) pm_alloc(sizeof(MIDIINCAPS) *
159                                            midi_num_inputs);
160     if (midi_in_caps == NULL) {
161         /* if you can't open a particular system-level midi interface
162          * (such as winmm), we just consider that system or API to be
163          * unavailable and move on without reporting an error.
164          */
165         return;
166     }
167 
168     for (i = 0; i < midi_num_inputs; i++) {
169         wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) & midi_in_caps[i],
170                                 sizeof(MIDIINCAPS));
171         if (wRtn == MMSYSERR_NOERROR) {
172             /* ignore errors here -- if pm_descriptor_max is exceeded, some
173                devices will not be accessible. */
174             pm_add_device("MMSystem", midi_in_caps[i].szPname, TRUE,
175                           (void *) i, &pm_winmm_in_dictionary);
176         }
177     }
178 }
179 
180 
pm_winmm_mapper_input()181 static void pm_winmm_mapper_input()
182 {
183     WORD wRtn;
184     /* Note: if MIDIMAPPER opened as input (documentation implies you
185         can, but current system fails to retrieve input mapper
186         capabilities) then you still should retrieve some formof
187         setup info. */
188     wRtn = midiInGetDevCaps((UINT) MIDIMAPPER,
189                             (LPMIDIINCAPS) & midi_in_mapper_caps,
190                             sizeof(MIDIINCAPS));
191     if (wRtn == MMSYSERR_NOERROR) {
192         pm_add_device("MMSystem", midi_in_mapper_caps.szPname, TRUE,
193                       (void *) MIDIMAPPER, &pm_winmm_in_dictionary);
194     }
195 }
196 
197 
pm_winmm_general_outputs()198 static void pm_winmm_general_outputs()
199 {
200     UINT i;
201     DWORD wRtn;
202     midi_num_outputs = midiOutGetNumDevs();
203     midi_out_caps = pm_alloc( sizeof(MIDIOUTCAPS) * midi_num_outputs );
204 
205     if (midi_out_caps == NULL) {
206         /* no error is reported -- see pm_winmm_general_inputs */
207         return ;
208     }
209 
210     for (i = 0; i < midi_num_outputs; i++) {
211         wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) & midi_out_caps[i],
212                                  sizeof(MIDIOUTCAPS));
213         if (wRtn == MMSYSERR_NOERROR) {
214             pm_add_device("MMSystem", midi_out_caps[i].szPname, FALSE,
215                           (void *) i, &pm_winmm_out_dictionary);
216         }
217     }
218 }
219 
220 
pm_winmm_mapper_output()221 static void pm_winmm_mapper_output()
222 {
223     WORD wRtn;
224     /* Note: if MIDIMAPPER opened as output (pseudo MIDI device
225         maps device independent messages into device dependant ones,
226         via NT midimapper program) you still should get some setup info */
227     wRtn = midiOutGetDevCaps((UINT) MIDIMAPPER, (LPMIDIOUTCAPS)
228                              & midi_out_mapper_caps, sizeof(MIDIOUTCAPS));
229     if (wRtn == MMSYSERR_NOERROR) {
230         pm_add_device("MMSystem", midi_out_mapper_caps.szPname, FALSE,
231                       (void *) MIDIMAPPER, &pm_winmm_out_dictionary);
232     }
233 }
234 
235 
236 /*
237 =========================================================================================
238 host error handling
239 =========================================================================================
240 */
winmm_has_host_error(PmInternal * midi)241 static unsigned int winmm_has_host_error(PmInternal * midi)
242 {
243     midiwinmm_type m = (midiwinmm_type)midi->descriptor;
244     return m->error;
245 }
246 
247 
248 /* str_copy_len -- like strcat, but won't overrun the destination string */
249 /*
250  * returns length of resulting string
251  */
str_copy_len(char * dst,char * src,int len)252 static int str_copy_len(char *dst, char *src, int len)
253 {
254     // Note: Visual C will suggest using a non-portable strncpy_s here
255     strncpy(dst, src, len);
256     /* just in case suffex is greater then len, terminate with zero */
257     dst[len - 1] = 0;
258     return strlen(dst);
259 }
260 
261 
winmm_get_host_error(PmInternal * midi,char * msg,UINT len)262 static void winmm_get_host_error(PmInternal * midi, char * msg, UINT len)
263 {
264     /* precondition: midi != NULL */
265     midiwinmm_node * m = (midiwinmm_node *) midi->descriptor;
266     char *hdr1 = "Host error: ";
267     char *hdr2 = "Host callback error: ";
268 
269     msg[0] = 0; /* initialize result string to empty */
270 
271     if (descriptors[midi->device_id].pub.input) {
272         /* input and output use different winmm API calls */
273         if (m) { /* make sure there is an open device to examine */
274             if (m->error != MMSYSERR_NOERROR) {
275                 int n = str_copy_len(msg, hdr1, len);
276                 /* read and record host error */
277                 int err = midiInGetErrorText(m->error, msg + n, len - n);
278                 assert(err == MMSYSERR_NOERROR);
279                 m->error = MMSYSERR_NOERROR;
280             }
281         }
282     } else { /* output port */
283         if (m) {
284             if (m->error != MMSYSERR_NOERROR) {
285                 int n = str_copy_len(msg, hdr1, len);
286                 int err = midiOutGetErrorText(m->error, msg + n, len - n);
287                 assert(err == MMSYSERR_NOERROR);
288                 m->error = MMSYSERR_NOERROR;
289             }
290         }
291     }
292 }
293 
294 
295 /*
296 =============================================================================
297 buffer handling
298 =============================================================================
299 */
allocate_buffer(long data_size)300 static MIDIHDR *allocate_buffer(long data_size)
301 {
302     LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
303     MIDIEVENT *evt;
304     if (!hdr) return NULL;
305     evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
306     hdr->lpData = (LPSTR) evt;
307     hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size);
308     hdr->dwBytesRecorded = 0;
309     hdr->dwFlags = 0;
310     hdr->dwUser = hdr->dwBufferLength;
311     return hdr;
312 }
313 
314 
allocate_buffers(midiwinmm_type m,long data_size,long count)315 static PmError allocate_buffers(midiwinmm_type m, long data_size, long count)
316 {
317     int i;
318     /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
319     m->num_buffers = 0; /* in case no memory can be allocated */
320     m->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
321     if (!m->buffers) return pmInsufficientMemory;
322     m->max_buffers = count;
323     for (i = 0; i < count; i++) {
324         LPMIDIHDR hdr = allocate_buffer(data_size);
325         if (!hdr) { /* free everything allocated so far and return */
326             for (i = i - 1; i >= 0; i--) pm_free(m->buffers[i]);
327             pm_free(m->buffers);
328             m->max_buffers = 0;
329             return pmInsufficientMemory;
330         }
331         m->buffers[i] = hdr; /* this may be NULL if allocation fails */
332     }
333     m->num_buffers = count;
334     return pmNoError;
335 }
336 
337 
get_free_output_buffer(PmInternal * midi)338 static LPMIDIHDR get_free_output_buffer(PmInternal *midi)
339 {
340     LPMIDIHDR r = NULL;
341     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
342     while (TRUE) {
343         int i;
344         for (i = 0; i < m->num_buffers; i++) {
345             /* cycle through buffers, modulo m->num_buffers */
346             m->next_buffer++;
347             if (m->next_buffer >= m->num_buffers) m->next_buffer = 0;
348             r = m->buffers[m->next_buffer];
349             if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer;
350         }
351         /* after scanning every buffer and not finding anything, block */
352         if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
353 #ifdef DEBUG
354             printf("PortMidi warning: get_free_output_buffer() wait timed out after 1000ms\n");
355 #endif
356             /* if we're trying to send a sysex message, maybe the
357              * message is too big and we need more message buffers.
358              * Expand the buffer pool by 128KB using 1024-byte buffers.
359              */
360             /* first, expand the buffers array if necessary */
361             if (!m->buffers_expanded) {
362                 LPMIDIHDR *new_buffers = (LPMIDIHDR *) pm_alloc(
363                         (m->num_buffers + NUM_EXPANSION_BUFFERS) *
364                         sizeof(LPMIDIHDR));
365                 /* if no memory, we could return a no-memory error, but user
366                  * probably will be unprepared to deal with it. Maybe the
367                  * MIDI driver is temporarily hung so we should just wait.
368                  * I don't know the right answer, but waiting is easier.
369                  */
370                 if (!new_buffers) continue;
371                 /* copy buffers to new_buffers and replace buffers */
372                 memcpy(new_buffers, m->buffers,
373                        m->num_buffers * sizeof(LPMIDIHDR));
374                 pm_free(m->buffers);
375                 m->buffers = new_buffers;
376                 m->max_buffers = m->num_buffers + NUM_EXPANSION_BUFFERS;
377                 m->buffers_expanded = TRUE;
378             }
379             /* next, add one buffer and return it */
380             if (m->num_buffers < m->max_buffers) {
381                 r = allocate_buffer(EXPANSION_BUFFER_LEN);
382                 /* again, if there's no memory, we may not really be
383                  * dead -- maybe the system is temporarily hung and
384                  * we can just wait longer for a message buffer */
385                 if (!r) continue;
386                 m->buffers[m->num_buffers++] = r;
387                 goto found_buffer; /* break out of 2 loops */
388             }
389             /* else, we've allocated all NUM_EXPANSION_BUFFERS buffers,
390              * and we have no free buffers to send. We'll just keep
391              * polling to see if any buffers show up.
392              */
393         }
394     }
395 found_buffer:
396     r->dwBytesRecorded = 0;
397     /* actual buffer length is saved in dwUser field */
398     r->dwBufferLength = (DWORD) r->dwUser;
399     return r;
400 }
401 
402 #ifdef EXPANDING_SYSEX_BUFFERS
403 note: this is not working code, but might be useful if you want
404       to grow sysex buffers.
405 static PmError resize_sysex_buffer(PmInternal *midi, long old_size, long new_size)
406 {
407     LPMIDIHDR big;
408     int i;
409     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
410     /* buffer must be smaller than 64k, but be also a multiple of 4 */
411     if (new_size > 65520) {
412         if (old_size >= 65520)
413             return pmBufferMaxSize;
414         else
415             new_size = 65520;
416     }
417     /* allocate a bigger message  */
418     big = allocate_sysex_buffer(new_size);
419     /* printf("expand to %d bytes\n", new_size);*/
420     if (!big) return pmInsufficientMemory;
421     m->error = midiOutPrepareHeader(m->handle.out, big, sizeof(MIDIHDR));
422     if (m->error) {
423         pm_free(big);
424         return pmHostError;
425     }
426     /* make sure we're not going to overwrite any memory */
427     assert(old_size <= new_size);
428     memcpy(big->lpData, m->hdr->lpData, old_size);
429     /* keep track of how many sysex bytes are in message so far */
430     big->dwBytesRecorded = m->hdr->dwBytesRecorded;
431     big->dwBufferLength = new_size;
432     /* find which buffer this was, and replace it */
433     for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
434         if (m->sysex_buffers[i] == m->hdr) {
435             m->sysex_buffers[i] = big;
436             m->sysex_buffer_size[i] = new_size;
437             pm_free(m->hdr);
438             m->hdr = big;
439             break;
440         }
441     }
442     assert(i != NUM_SYSEX_BUFFERS);
443 
444     return pmNoError;
445 }
446 #endif
447 
448 /*
449 =========================================================================================
450 begin midi input implementation
451 =========================================================================================
452 */
453 
454 
allocate_input_buffer(HMIDIIN h,long buffer_len)455 static PmError allocate_input_buffer(HMIDIIN h, long buffer_len)
456 {
457     LPMIDIHDR hdr = allocate_buffer(buffer_len);
458     if (!hdr) return pmInsufficientMemory;
459     pm_hosterror = midiInPrepareHeader(h, hdr, sizeof(MIDIHDR));
460     if (pm_hosterror) {
461         pm_free(hdr);
462         return pm_hosterror;
463     }
464     pm_hosterror = midiInAddBuffer(h, hdr, sizeof(MIDIHDR));
465     return pm_hosterror;
466 }
467 
468 
winmm_in_open(PmInternal * midi,void * driverInfo)469 static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
470 {
471     DWORD dwDevice;
472     int i = midi->device_id;
473     int max_sysex_len = midi->buffer_len * 4;
474     int num_input_buffers = max_sysex_len / INPUT_SYSEX_LEN;
475     midiwinmm_type m;
476 
477     dwDevice = (DWORD) descriptors[i].descriptor;
478 
479     /* create system dependent device data */
480     m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
481     midi->descriptor = m;
482     if (!m) goto no_memory;
483     m->handle.in = NULL;
484     m->buffers = NULL; /* not used for input */
485     m->num_buffers = 0; /* not used for input */
486     m->max_buffers = FALSE; /* not used for input */
487     m->buffers_expanded = 0; /* not used for input */
488     m->next_buffer = 0; /* not used for input */
489     m->buffer_signal = 0; /* not used for input */
490     m->last_time = 0;
491     m->first_message = TRUE; /* not used for input */
492     m->sysex_mode = FALSE;
493     m->sysex_word = 0;
494     m->sysex_byte_count = 0;
495     m->hdr = NULL; /* not used for input */
496     m->sync_time = 0;
497     m->delta = 0;
498     m->error = MMSYSERR_NOERROR;
499     /* 4000 is based on Windows documentation -- that's the value used in the
500        memory manager. It's small enough that it should not hurt performance even
501        if it's not optimal.
502      */
503     InitializeCriticalSectionAndSpinCount(&m->lock, 4000);
504     /* open device */
505     pm_hosterror = midiInOpen(
506 	    &(m->handle.in),  /* input device handle */
507 	    dwDevice,  /* device ID */
508 	    (DWORD_PTR) winmm_in_callback,  /* callback address */
509 	    (DWORD_PTR) midi,  /* callback instance data */
510 	    CALLBACK_FUNCTION); /* callback is a procedure */
511     if (pm_hosterror) goto free_descriptor;
512 
513     if (num_input_buffers < MIN_INPUT_BUFFERS)
514         num_input_buffers = MIN_INPUT_BUFFERS;
515     for (i = 0; i < num_input_buffers; i++) {
516         if (allocate_input_buffer(m->handle.in, INPUT_SYSEX_LEN)) {
517             /* either pm_hosterror was set, or the proper return code
518                is pmInsufficientMemory */
519             goto close_device;
520         }
521     }
522     /* start device */
523     pm_hosterror = midiInStart(m->handle.in);
524     if (pm_hosterror) goto reset_device;
525     return pmNoError;
526 
527     /* undo steps leading up to the detected error */
528 reset_device:
529     /* ignore return code (we already have an error to report) */
530     midiInReset(m->handle.in);
531 close_device:
532     midiInClose(m->handle.in); /* ignore return code */
533 free_descriptor:
534     midi->descriptor = NULL;
535     pm_free(m);
536 no_memory:
537     if (pm_hosterror) {
538         int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
539                                      PM_HOST_ERROR_MSG_LEN);
540         assert(err == MMSYSERR_NOERROR);
541         return pmHostError;
542     }
543     /* if !pm_hosterror, then the error must be pmInsufficientMemory */
544     return pmInsufficientMemory;
545     /* note: if we return an error code, the device will be
546        closed and memory will be freed. It's up to the caller
547        to free the parameter midi */
548 }
549 
winmm_in_poll(PmInternal * midi)550 static PmError winmm_in_poll(PmInternal *midi) {
551     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
552     return m->error;
553 }
554 
555 
556 
557 /* winmm_in_close -- close an open midi input device */
558 /*
559  * assume midi is non-null (checked by caller)
560  */
winmm_in_close(PmInternal * midi)561 static PmError winmm_in_close(PmInternal *midi)
562 {
563     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
564     if (!m) return pmBadPtr;
565     /* device to close */
566     if (pm_hosterror = midiInStop(m->handle.in)) {
567         midiInReset(m->handle.in); /* try to reset and close port */
568         midiInClose(m->handle.in);
569     } else if (pm_hosterror = midiInReset(m->handle.in)) {
570         midiInClose(m->handle.in); /* best effort to close midi port */
571     } else {
572         pm_hosterror = midiInClose(m->handle.in);
573     }
574     midi->descriptor = NULL;
575     DeleteCriticalSection(&m->lock);
576     pm_free(m); /* delete */
577     if (pm_hosterror) {
578         int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
579                                      PM_HOST_ERROR_MSG_LEN);
580         assert(err == MMSYSERR_NOERROR);
581         return pmHostError;
582     }
583     return pmNoError;
584 }
585 
586 
587 /* Callback function executed via midiInput SW interrupt (via midiInOpen). */
winmm_in_callback(HMIDIIN hMidiIn,UINT wMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)588 static void FAR PASCAL winmm_in_callback(
589     HMIDIIN hMidiIn,       /* midiInput device Handle */
590     UINT wMsg,             /* midi msg */
591     DWORD_PTR dwInstance,  /* application data */
592     DWORD_PTR dwParam1,    /* MIDI data */
593     DWORD_PTR dwParam2)    /* device timestamp (wrt most recent midiInStart) */
594 {
595     static int entry = 0;
596     PmInternal *midi = (PmInternal *) dwInstance;
597     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
598 
599     /* NOTE: we do not just EnterCriticalSection() here because an
600      * MIM_CLOSE message arrives when the port is closed, but then
601      * the m->lock has been destroyed.
602      */
603 
604     switch (wMsg) {
605     case MIM_DATA: {
606         /* if this callback is reentered with data, we're in trouble.
607          * It's hard to imagine that Microsoft would allow callbacks
608          * to be reentrant -- isn't the model that this is like a
609          * hardware interrupt? -- but I've seen reentrant behavior
610          * using a debugger, so it happens.
611          */
612         EnterCriticalSection(&m->lock);
613 
614         /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
615                 message LOB;
616            dwParam2 is time message received by input device driver, specified
617             in [ms] from when midiInStart called.
618            each message is expanded to include the status byte */
619 
620         if ((dwParam1 & 0x80) == 0) {
621             /* not a status byte -- ignore it. This happened running the
622                sysex.c test under Win2K with MidiMan USB 1x1 interface,
623                but I can't reproduce it. -RBD
624              */
625             /* printf("non-status byte found\n"); */
626         } else { /* data to process */
627             PmEvent event;
628             if (midi->time_proc)
629                 dwParam2 = (*midi->time_proc)(midi->time_info);
630             event.timestamp = (PmTimestamp)dwParam2;
631             event.message = (PmMessage)dwParam1;
632             pm_read_short(midi, &event);
633         }
634         LeaveCriticalSection(&m->lock);
635         break;
636     }
637     case MIM_LONGDATA: {
638         MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1;
639         unsigned char *data = (unsigned char *) lpMidiHdr->lpData;
640         unsigned int processed = 0;
641         int remaining = lpMidiHdr->dwBytesRecorded;
642 
643         EnterCriticalSection(&m->lock);
644         /* printf("midi_in_callback -- lpMidiHdr %x, %d bytes, %2x...\n",
645                 lpMidiHdr, lpMidiHdr->dwBytesRecorded, *data); */
646         if (midi->time_proc)
647             dwParam2 = (*midi->time_proc)(midi->time_info);
648         /* can there be more than one message in one buffer? */
649         /* assume yes and iterate through them */
650         while (remaining > 0) {
651             unsigned int amt = pm_read_bytes(midi, data + processed,
652                                              remaining, (PmTimestamp)dwParam2);
653             remaining -= amt;
654             processed += amt;
655         }
656 
657         /* when a device is closed, the pending MIM_LONGDATA buffers are
658            returned to this callback with dwBytesRecorded == 0. In this
659            case, we do not want to send them back to the interface (if
660            we do, the interface will not close, and Windows OS may hang). */
661         if (lpMidiHdr->dwBytesRecorded > 0) {
662             MMRESULT rslt;
663             lpMidiHdr->dwBytesRecorded = 0;
664             lpMidiHdr->dwFlags = 0;
665 
666             /* note: no error checking -- can this actually fail? */
667             rslt = midiInPrepareHeader(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
668             assert(rslt == MMSYSERR_NOERROR);
669             /* note: I don't think this can fail except possibly for
670              * MMSYSERR_NOMEM, but the pain of reporting this
671              * unlikely but probably catastrophic error does not seem
672              * worth it.
673              */
674             rslt = midiInAddBuffer(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
675             assert(rslt == MMSYSERR_NOERROR);
676             LeaveCriticalSection(&m->lock);
677         } else {
678             midiInUnprepareHeader(hMidiIn,lpMidiHdr,sizeof(MIDIHDR));
679             LeaveCriticalSection(&m->lock);
680             pm_free(lpMidiHdr);
681         }
682         break;
683     }
684     case MIM_OPEN:
685         break;
686     case MIM_CLOSE:
687         break;
688     case MIM_ERROR:
689         /* printf("MIM_ERROR\n"); */
690         break;
691     case MIM_LONGERROR:
692         /* printf("MIM_LONGERROR\n"); */
693         break;
694     default:
695         break;
696     }
697 }
698 
699 /*
700 =========================================================================================
701 begin midi output implementation
702 =========================================================================================
703 */
704 
705 /* begin helper routines used by midiOutStream interface */
706 
707 /* add_to_buffer -- adds timestamped short msg to buffer, returns fullp */
add_to_buffer(midiwinmm_type m,LPMIDIHDR hdr,unsigned long delta,unsigned long msg)708 static int add_to_buffer(midiwinmm_type m, LPMIDIHDR hdr,
709                          unsigned long delta, unsigned long msg)
710 {
711     unsigned long *ptr = (unsigned long *)
712                          (hdr->lpData + hdr->dwBytesRecorded);
713     *ptr++ = delta; /* dwDeltaTime */
714     *ptr++ = 0;     /* dwStream */
715     *ptr++ = msg;   /* dwEvent */
716     hdr->dwBytesRecorded += 3 * sizeof(long);
717     /* if the addition of three more words (a message) would extend beyond
718        the buffer length, then return TRUE (full)
719      */
720     return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength;
721 }
722 
723 
pm_time_get(midiwinmm_type m)724 static PmTimestamp pm_time_get(midiwinmm_type m)
725 {
726     MMTIME mmtime;
727     MMRESULT wRtn;
728     mmtime.wType = TIME_TICKS;
729     mmtime.u.ticks = 0;
730     wRtn = midiStreamPosition(m->handle.stream, &mmtime, sizeof(mmtime));
731     assert(wRtn == MMSYSERR_NOERROR);
732     return mmtime.u.ticks;
733 }
734 
735 
736 /* end helper routines used by midiOutStream interface */
737 
738 
winmm_out_open(PmInternal * midi,void * driverInfo)739 static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
740 {
741     DWORD dwDevice;
742     int i = midi->device_id;
743     midiwinmm_type m;
744     MIDIPROPTEMPO propdata;
745     MIDIPROPTIMEDIV divdata;
746     int max_sysex_len = midi->buffer_len * 4;
747     int output_buffer_len;
748     int num_buffers;
749     dwDevice = (DWORD) descriptors[i].descriptor;
750 
751     /* create system dependent device data */
752     m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
753     midi->descriptor = m;
754     if (!m) goto no_memory;
755     m->handle.out = NULL;
756     m->buffers = NULL;
757     m->num_buffers = 0;
758     m->max_buffers = 0;
759     m->buffers_expanded = FALSE;
760     m->next_buffer = 0;
761     m->last_time = 0;
762     m->first_message = TRUE; /* we treat first message as special case */
763     m->sysex_mode = FALSE;
764     m->sysex_word = 0;
765     m->sysex_byte_count = 0;
766     m->hdr = NULL;
767     m->sync_time = 0;
768     m->delta = 0;
769     m->error = MMSYSERR_NOERROR;
770 
771     /* create a signal */
772     m->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
773 
774     /* this should only fail when there are very serious problems */
775     assert(m->buffer_signal);
776 
777     /* open device */
778     if (midi->latency == 0) {
779         /* use simple midi out calls */
780         pm_hosterror = midiOutOpen(
781                 (LPHMIDIOUT) & m->handle.out,  /* device Handle */
782 		dwDevice,  /* device ID  */
783 		/* note: same callback fn as for StreamOpen: */
784 		(DWORD_PTR) winmm_streamout_callback, /* callback fn */
785 		(DWORD_PTR) midi,  /* callback instance data */
786 		CALLBACK_FUNCTION); /* callback type */
787     } else {
788         /* use stream-based midi output (schedulable in future) */
789         pm_hosterror = midiStreamOpen(
790 	        &m->handle.stream,  /* device Handle */
791 		(LPUINT) & dwDevice,  /* device ID pointer */
792 		1,  /* reserved, must be 1 */
793 		(DWORD_PTR) winmm_streamout_callback,
794 		(DWORD_PTR) midi,  /* callback instance data */
795 		CALLBACK_FUNCTION);
796     }
797     if (pm_hosterror != MMSYSERR_NOERROR) {
798         goto free_descriptor;
799     }
800 
801     if (midi->latency == 0) {
802         num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
803         output_buffer_len = max_sysex_len / num_buffers;
804         if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
805             output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
806     } else {
807         long dur = 0;
808         num_buffers = max(midi->buffer_len, midi->latency / 2);
809         if (num_buffers < MIN_STREAM_BUFFERS)
810             num_buffers = MIN_STREAM_BUFFERS;
811         output_buffer_len = STREAM_BUFFER_LEN;
812 
813         propdata.cbStruct = sizeof(MIDIPROPTEMPO);
814         propdata.dwTempo = 480000; /* microseconds per quarter */
815         pm_hosterror = midiStreamProperty(m->handle.stream,
816                                           (LPBYTE) & propdata,
817                                           MIDIPROP_SET | MIDIPROP_TEMPO);
818         if (pm_hosterror) goto close_device;
819 
820         divdata.cbStruct = sizeof(MIDIPROPTEMPO);
821         divdata.dwTimeDiv = 480;   /* divisions per quarter */
822         pm_hosterror = midiStreamProperty(m->handle.stream,
823                                           (LPBYTE) & divdata,
824                                           MIDIPROP_SET | MIDIPROP_TIMEDIV);
825         if (pm_hosterror) goto close_device;
826     }
827     /* allocate buffers */
828     if (allocate_buffers(m, output_buffer_len, num_buffers))
829         goto free_buffers;
830     /* start device */
831     if (midi->latency != 0) {
832         pm_hosterror = midiStreamRestart(m->handle.stream);
833         if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
834     }
835     return pmNoError;
836 
837 free_buffers:
838     /* buffers are freed below by winmm_out_delete */
839 close_device:
840     midiOutClose(m->handle.out);
841 free_descriptor:
842     midi->descriptor = NULL;
843     winmm_out_delete(midi); /* frees buffers and m */
844 no_memory:
845     if (pm_hosterror) {
846         int err = midiOutGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
847                                       PM_HOST_ERROR_MSG_LEN);
848         assert(err == MMSYSERR_NOERROR);
849         return pmHostError;
850     }
851     return pmInsufficientMemory;
852 }
853 
854 
855 /* winmm_out_delete -- carefully free data associated with midi */
856 /**/
winmm_out_delete(PmInternal * midi)857 static void winmm_out_delete(PmInternal *midi)
858 {
859     int i;
860     /* delete system dependent device data */
861     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
862     if (m) {
863         if (m->buffer_signal) {
864             /* don't report errors -- better not to stop cleanup */
865             CloseHandle(m->buffer_signal);
866         }
867         /* if using stream output, free buffers */
868         for (i = 0; i < m->num_buffers; i++) {
869             if (m->buffers[i]) pm_free(m->buffers[i]);
870         }
871         m->num_buffers = 0;
872         pm_free(m->buffers);
873         m->max_buffers = 0;
874     }
875     midi->descriptor = NULL;
876     pm_free(m); /* delete */
877 }
878 
879 
880 /* see comments for winmm_in_close */
winmm_out_close(PmInternal * midi)881 static PmError winmm_out_close(PmInternal *midi)
882 {
883     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
884     if (m->handle.out) {
885         /* device to close */
886         if (midi->latency == 0) {
887             pm_hosterror = midiOutClose(m->handle.out);
888         } else {
889             pm_hosterror = midiStreamClose(m->handle.stream);
890         }
891         /* regardless of outcome, free memory */
892         winmm_out_delete(midi);
893     }
894     if (pm_hosterror) {
895         int err = midiOutGetErrorText(pm_hosterror,
896                                       (char *) pm_hosterror_text,
897                                       PM_HOST_ERROR_MSG_LEN);
898         assert(err == MMSYSERR_NOERROR);
899         return pmHostError;
900     }
901     return pmNoError;
902 }
903 
904 
winmm_out_abort(PmInternal * midi)905 static PmError winmm_out_abort(PmInternal *midi)
906 {
907     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
908     m->error = MMSYSERR_NOERROR;
909 
910     /* only stop output streams */
911     if (midi->latency > 0) {
912         m->error = midiStreamStop(m->handle.stream);
913     }
914     return m->error ? pmHostError : pmNoError;
915 }
916 
917 
winmm_write_flush(PmInternal * midi,PmTimestamp timestamp)918 static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp)
919 {
920     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
921     assert(m);
922     if (m->hdr) {
923         m->error = midiOutPrepareHeader(m->handle.out, m->hdr,
924                                         sizeof(MIDIHDR));
925         if (m->error) {
926             /* do not send message */
927         } else if (midi->latency == 0) {
928             /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded
929              * should be zero. This is set in get_free_sysex_buffer().
930              * The msg length goes in dwBufferLength in spite of what
931              * Microsoft documentation says (or doesn't say). */
932             m->hdr->dwBufferLength = m->hdr->dwBytesRecorded;
933             m->hdr->dwBytesRecorded = 0;
934             m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
935         } else {
936             m->error = midiStreamOut(m->handle.stream, m->hdr,
937                                      sizeof(MIDIHDR));
938         }
939         midi->fill_base = NULL;
940         m->hdr = NULL;
941         if (m->error) {
942             m->hdr->dwFlags = 0; /* release the buffer */
943             return pmHostError;
944         }
945     }
946     return pmNoError;
947 }
948 
949 
winmm_write_short(PmInternal * midi,PmEvent * event)950 static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
951 {
952     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
953     PmError rslt = pmNoError;
954     assert(m);
955 
956     if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
957         m->error = midiOutShortMsg(m->handle.out, event->message);
958         if (m->error) rslt = pmHostError;
959     } else {  /* use midiStream interface -- pass data through buffers */
960         unsigned long when = event->timestamp;
961         unsigned long delta;
962         int full;
963         if (when == 0) when = midi->now;
964         /* when is in real_time; translate to intended stream time */
965         when = when + m->delta + midi->latency;
966         /* make sure we don't go backward in time */
967         if (when < m->last_time) when = m->last_time;
968         delta = when - m->last_time;
969         m->last_time = when;
970         /* before we insert any data, we must have a buffer */
971         if (m->hdr == NULL) {
972             /* stream interface: buffers allocated when stream is opened */
973             m->hdr = get_free_output_buffer(midi);
974         }
975         full = add_to_buffer(m, m->hdr, delta, event->message);
976         if (full) rslt = winmm_write_flush(midi, when);
977     }
978     return rslt;
979 }
980 
981 #define winmm_begin_sysex winmm_write_flush
982 #ifndef winmm_begin_sysex
winmm_begin_sysex(PmInternal * midi,PmTimestamp timestamp)983 static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp)
984 {
985     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
986     PmError rslt = pmNoError;
987 
988     if (midi->latency == 0) {
989         /* do nothing -- it's handled in winmm_write_byte */
990     } else {
991         /* sysex expects an empty sysex buffer, so send whatever is here */
992         rslt = winmm_write_flush(midi);
993     }
994     return rslt;
995 }
996 #endif
997 
winmm_end_sysex(PmInternal * midi,PmTimestamp timestamp)998 static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp)
999 {
1000     /* could check for callback_error here, but I haven't checked
1001      * what happens if we exit early and don't finish the sysex msg
1002      * and clean up
1003      */
1004     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1005     PmError rslt = pmNoError;
1006     LPMIDIHDR hdr = m->hdr;
1007     if (!hdr) return rslt; /* something bad happened earlier,
1008             do not report an error because it would have been
1009             reported (at least) once already */
1010     /* a(n old) version of MIDI YOKE requires a zero byte after
1011      * the sysex message, but do not increment dwBytesRecorded: */
1012     hdr->lpData[hdr->dwBytesRecorded] = 0;
1013     if (midi->latency == 0) {
1014 #ifdef DEBUG_PRINT_BEFORE_SENDING_SYSEX
1015         /* DEBUG CODE: */
1016         { int i; int len = m->hdr->dwBufferLength;
1017           printf("OutLongMsg %d ", len);
1018           for (i = 0; i < len; i++) {
1019               printf("%2x ", (unsigned char) (m->hdr->lpData[i]));
1020           }
1021         }
1022 #endif
1023     } else {
1024         /* Using stream interface. There are accumulated bytes in m->hdr
1025            to send using midiStreamOut
1026          */
1027         /* add bytes recorded to MIDIEVENT length, but don't
1028            count the MIDIEVENT data (3 longs) */
1029         MIDIEVENT *evt = (MIDIEVENT *) (hdr->lpData);
1030         evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
1031         /* round up BytesRecorded to multiple of 4 */
1032         hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
1033     }
1034     rslt = winmm_write_flush(midi, timestamp);
1035     return rslt;
1036 }
1037 
1038 
winmm_write_byte(PmInternal * midi,unsigned char byte,PmTimestamp timestamp)1039 static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
1040                                 PmTimestamp timestamp)
1041 {
1042     /* write a sysex byte */
1043     PmError rslt = pmNoError;
1044     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1045     LPMIDIHDR hdr = m->hdr;
1046     unsigned char *msg_buffer;
1047     assert(m);
1048     if (!hdr) {
1049         m->hdr = hdr = get_free_output_buffer(midi);
1050         assert(hdr);
1051         midi->fill_base = (unsigned char *) m->hdr->lpData;
1052         midi->fill_offset_ptr = &(hdr->dwBytesRecorded);
1053         /* when buffer fills, Pm_WriteSysEx will revert to calling
1054          * pmwin_write_byte, which expect to have space, so leave
1055          * one byte free for pmwin_write_byte. Leave another byte
1056          * of space for zero after message to make early version of
1057          * MIDI YOKE driver happy -- therefore dwBufferLength - 2 */
1058         midi->fill_length = hdr->dwBufferLength - 2;
1059         if (midi->latency != 0) {
1060             unsigned long when = (unsigned long) timestamp;
1061             unsigned long delta;
1062             unsigned long *ptr;
1063             if (when == 0) when = midi->now;
1064             /* when is in real_time; translate to intended stream time */
1065             when = when + m->delta + midi->latency;
1066             /* make sure we don't go backward in time */
1067             if (when < m->last_time) when = m->last_time;
1068             delta = when - m->last_time;
1069             m->last_time = when;
1070 
1071             ptr = (unsigned long *) hdr->lpData;
1072             *ptr++ = delta;
1073             *ptr++ = 0;
1074             *ptr = MEVT_F_LONG;
1075             hdr->dwBytesRecorded = 3 * sizeof(long);
1076             /* data will be added at an offset of dwBytesRecorded ... */
1077         }
1078     }
1079     /* add the data byte */
1080     msg_buffer = (unsigned char *) (hdr->lpData);
1081     msg_buffer[hdr->dwBytesRecorded++] = byte;
1082 
1083     /* see if buffer is full, leave one byte extra for pad */
1084     if (hdr->dwBytesRecorded >= hdr->dwBufferLength - 1) {
1085         /* write what we've got and continue */
1086         rslt = winmm_end_sysex(midi, timestamp);
1087     }
1088     return rslt;
1089 }
1090 
1091 
winmm_synchronize(PmInternal * midi)1092 static PmTimestamp winmm_synchronize(PmInternal *midi)
1093 {
1094     midiwinmm_type m;
1095     unsigned long pm_stream_time_2;
1096     unsigned long real_time;
1097     unsigned long pm_stream_time;
1098 
1099     /* only synchronize if we are using stream interface */
1100     if (midi->latency == 0) return 0;
1101 
1102     /* figure out the time */
1103     m = (midiwinmm_type) midi->descriptor;
1104     pm_stream_time_2 = pm_time_get(m);
1105 
1106     do {
1107         /* read real_time between two reads of stream time */
1108         pm_stream_time = pm_stream_time_2;
1109         real_time = (*midi->time_proc)(midi->time_info);
1110         pm_stream_time_2 = pm_time_get(m);
1111         /* repeat if more than 1ms elapsed */
1112     } while (pm_stream_time_2 > pm_stream_time + 1);
1113     m->delta = pm_stream_time - real_time;
1114     m->sync_time = real_time;
1115     return real_time;
1116 }
1117 
1118 
1119 /* winmm_streamout_callback -- unprepare (free) buffer header */
winmm_streamout_callback(HMIDIOUT hmo,UINT wMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)1120 static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
1121         DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1122 {
1123     PmInternal *midi = (PmInternal *) dwInstance;
1124     midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1125     LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
1126     int err;
1127 
1128     /* Even if an error is pending, I think we should unprepare msgs and
1129        signal their arrival
1130      */
1131     /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
1132            hdr, wMsg, MOM_DONE); */
1133     if (wMsg == MOM_DONE) {
1134         MMRESULT ret = midiOutUnprepareHeader(m->handle.out, hdr,
1135                                               sizeof(MIDIHDR));
1136         assert(ret == MMSYSERR_NOERROR);
1137     }
1138     /* signal client in case it is blocked waiting for buffer */
1139     err = SetEvent(m->buffer_signal);
1140     assert(err); /* false -> error */
1141 }
1142 
1143 
1144 /*
1145 =========================================================================================
1146 begin exported functions
1147 =========================================================================================
1148 */
1149 
1150 #define winmm_in_abort pm_fail_fn
1151 pm_fns_node pm_winmm_in_dictionary = {
1152                                          none_write_short,
1153                                          none_sysex,
1154                                          none_sysex,
1155                                          none_write_byte,
1156                                          none_write_short,
1157                                          none_write_flush,
1158                                          winmm_synchronize,
1159                                          winmm_in_open,
1160                                          winmm_in_abort,
1161                                          winmm_in_close,
1162                                          winmm_in_poll,
1163                                          winmm_has_host_error,
1164                                          winmm_get_host_error
1165                                      };
1166 
1167 pm_fns_node pm_winmm_out_dictionary = {
1168                                           winmm_write_short,
1169                                           winmm_begin_sysex,
1170                                           winmm_end_sysex,
1171                                           winmm_write_byte,
1172                                           winmm_write_short,  /* short realtime message */
1173                                           winmm_write_flush,
1174                                           winmm_synchronize,
1175                                           winmm_out_open,
1176                                           winmm_out_abort,
1177                                           winmm_out_close,
1178                                           none_poll,
1179                                           winmm_has_host_error,
1180                                           winmm_get_host_error
1181                                       };
1182 
1183 
1184 /* initialize winmm interface. Note that if there is something wrong
1185    with winmm (e.g. it is not supported or installed), it is not an
1186    error. We should simply return without having added any devices to
1187    the table. Hence, no error code is returned. Furthermore, this init
1188    code is called along with every other supported interface, so the
1189    user would have a very hard time figuring out what hardware and API
1190    generated the error. Finally, it would add complexity to pmwin.c to
1191    remember where the error code came from in order to convert to text.
1192  */
pm_winmm_init(void)1193 void pm_winmm_init( void )
1194 {
1195     pm_winmm_mapper_input();
1196     pm_winmm_mapper_output();
1197     pm_winmm_general_inputs();
1198     pm_winmm_general_outputs();
1199 }
1200 
1201 
1202 /* no error codes are returned, even if errors are encountered, because
1203    there is probably nothing the user could do (e.g. it would be an error
1204    to retry.
1205  */
pm_winmm_term(void)1206 void pm_winmm_term( void )
1207 {
1208     int i;
1209 #ifdef DEBUG
1210     char msg[PM_HOST_ERROR_MSG_LEN];
1211 #endif
1212     int doneAny = 0;
1213 #ifdef DEBUG
1214     printf("pm_winmm_term called\n");
1215 #endif
1216     for (i = 0; i < pm_descriptor_index; i++) {
1217         PmInternal * midi = descriptors[i].internalDescriptor;
1218         if (midi) {
1219             midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1220             if (m->handle.out) {
1221                 /* close next open device*/
1222 #ifdef DEBUG
1223                 if (doneAny == 0) {
1224                     printf("begin closing open devices...\n");
1225                     doneAny = 1;
1226                 }
1227                 /* report any host errors; this EXTEREMELY useful when
1228                    trying to debug client app */
1229                 if (winmm_has_host_error(midi)) {
1230                     winmm_get_host_error(midi, msg, PM_HOST_ERROR_MSG_LEN);
1231                     printf("%s\n", msg);
1232                 }
1233 #endif
1234                 /* close all open ports */
1235                 (*midi->dictionary->close)(midi);
1236             }
1237         }
1238     }
1239     if (midi_in_caps) {
1240         pm_free(midi_in_caps);
1241         midi_in_caps = NULL;
1242     }
1243     if (midi_out_caps) {
1244         pm_free(midi_out_caps);
1245         midi_out_caps = NULL;
1246     }
1247 #ifdef DEBUG
1248     if (doneAny) {
1249         printf("warning: devices were left open. They have been closed.\n");
1250     }
1251     printf("pm_winmm_term exiting\n");
1252 #endif
1253     pm_descriptor_index = 0;
1254 }
1255