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