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