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