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