1 /* 2 * Sample MIDI Wine Driver for Linux 3 * 4 * Copyright 1994 Martin Ayotte 5 * Copyright 1999 Eric Pouech 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 /* TODO: 23 * + implement it correctly 24 * + finish asynchronous commands 25 * + better implement non waiting command (without the MCI_WAIT flag). 26 */ 27 28 #define WIN32_NO_STATUS 29 30 //#include <stdlib.h> 31 #include <stdarg.h> 32 //#include <stdio.h> 33 //#include <string.h> 34 35 #include <windef.h> 36 //#include "winbase.h" 37 //#include "wingdi.h" 38 #include <winuser.h> 39 #include <wownt32.h> 40 #include <mmddk.h> 41 #include <wine/debug.h> 42 #include <wine/unicode.h> 43 44 WINE_DEFAULT_DEBUG_CHANNEL(mcimidi); 45 46 #define MIDI_NOTEOFF 0x80 47 #define MIDI_NOTEON 0x90 48 49 typedef struct { 50 DWORD dwFirst; /* offset in file of track */ 51 DWORD dwLast; /* number of bytes in file of track */ 52 DWORD dwIndex; /* current index in file (dwFirst <= dwIndex < dwLast) */ 53 DWORD dwLength; /* number of pulses in this track */ 54 DWORD dwEventPulse; /* current pulse # (event) pointed by dwIndex */ 55 DWORD dwEventData; /* current data (event) pointed by dwIndex */ 56 WORD wEventLength; /* current length (event) pointed by dwIndex */ 57 WORD wStatus : 1, /* 1 : playing, 0 : done */ 58 wTrackNr : 7, 59 wLastCommand : 8; /* last MIDI command on track */ 60 } MCI_MIDITRACK; 61 62 typedef struct tagWINE_MCIMIDI { 63 UINT wDevID; /* the MCI one */ 64 HMIDI hMidi; 65 int nUseCount; /* Incremented for each shared open */ 66 HANDLE hCallback; /* Callback handle for pending notification */ 67 HANDLE hThread; /* Player thread */ 68 HMMIO hFile; /* mmio file handle open as Element */ 69 LPWSTR lpstrElementName; /* Name of file (if any) */ 70 LPWSTR lpstrCopyright; 71 LPWSTR lpstrName; 72 WORD wPort; /* the WINMM device unit */ 73 WORD dwStatus; /* one from MCI_MODE_xxxx */ 74 DWORD dwMciTimeFormat; /* One of the supported MCI_FORMAT_xxxx */ 75 WORD wFormat; /* Format of MIDI hFile (0, 1 or 2) */ 76 WORD nTracks; /* Number of tracks in hFile */ 77 WORD nDivision; /* Number of division in hFile PPQN or SMPTE */ 78 WORD wStartedPlaying; 79 DWORD dwTempo; /* Tempo (# of 1/4 note per second */ 80 MCI_MIDITRACK* tracks; /* Content of each track */ 81 DWORD dwPulse; 82 DWORD dwPositionMS; 83 DWORD dwEndMS; 84 DWORD dwStartTicks; 85 } WINE_MCIMIDI; 86 87 /*======================================================================* 88 * MCI MIDI implementation * 89 *======================================================================*/ 90 91 static DWORD mmr2mci(DWORD ret) 92 { 93 switch (ret) { 94 case MMSYSERR_ALLOCATED: 95 return MCIERR_SEQ_PORT_INUSE; 96 case MMSYSERR_NOMEM: 97 return MCIERR_OUT_OF_MEMORY; 98 case MMSYSERR_BADDEVICEID: /* wine*.drv disabled */ 99 return MCIERR_SEQ_PORT_NONEXISTENT; 100 case MIDIERR_INVALIDSETUP: /* from midimap.dll without snd-seq module */ 101 return MCIERR_SEQ_PORT_MAPNODEVICE; 102 default: 103 return ret; 104 } 105 } 106 107 static DWORD MIDI_mciResume(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms); 108 109 /************************************************************************** 110 * MIDI_drvOpen [internal] 111 */ 112 static DWORD MIDI_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp) 113 { 114 WINE_MCIMIDI* wmm; 115 116 if (!modp) return 0xFFFFFFFF; 117 118 wmm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIMIDI)); 119 120 if (!wmm) 121 return 0; 122 123 wmm->wDevID = modp->wDeviceID; 124 mciSetDriverData(wmm->wDevID, (DWORD_PTR)wmm); 125 modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE; 126 modp->wType = MCI_DEVTYPE_SEQUENCER; 127 return modp->wDeviceID; 128 } 129 130 /************************************************************************** 131 * MCIMIDI_drvClose [internal] 132 */ 133 static DWORD MIDI_drvClose(DWORD dwDevID) 134 { 135 WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(dwDevID); 136 137 if (wmm) { 138 HeapFree(GetProcessHeap(), 0, wmm); 139 mciSetDriverData(dwDevID, 0); 140 return 1; 141 } 142 return (dwDevID == 0xFFFFFFFF) ? 1 : 0; 143 } 144 145 /************************************************************************** 146 * MIDI_mciGetOpenDev [internal] 147 */ 148 static WINE_MCIMIDI* MIDI_mciGetOpenDev(MCIDEVICEID wDevID, UINT wMsg) 149 { 150 WINE_MCIMIDI* wmm = (WINE_MCIMIDI*)mciGetDriverData(wDevID); 151 152 if (wmm == NULL || ((wmm->nUseCount == 0) ^ (wMsg == MCI_OPEN_DRIVER))) { 153 WARN("Invalid wDevID=%u\n", wDevID); 154 return 0; 155 } 156 return wmm; 157 } 158 159 /************************************************************************** 160 * MIDI_mciNotify [internal] 161 * 162 * Notifications in MCI work like a 1-element queue. 163 * Each new notification request supersedes the previous one. 164 * This affects Play and Record; other commands are immediate. 165 */ 166 static void MIDI_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIMIDI* wmm, UINT wStatus) 167 { 168 /* We simply save one parameter by not passing the wDevID local 169 * to the command. They are the same (via mciGetDriverData). 170 */ 171 MCIDEVICEID wDevID = wmm->wDevID; 172 HANDLE old = InterlockedExchangePointer(&wmm->hCallback, NULL); 173 if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED); 174 mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus); 175 } 176 177 /************************************************************************** 178 * MIDI_mciReadByte [internal] 179 */ 180 static DWORD MIDI_mciReadByte(WINE_MCIMIDI* wmm, BYTE *lpbyt) 181 { 182 DWORD ret = 0; 183 184 if (mmioRead(wmm->hFile, (HPSTR)lpbyt, sizeof(BYTE)) != (long)sizeof(BYTE)) { 185 WARN("Error reading wmm=%p\n", wmm); 186 ret = MCIERR_INVALID_FILE; 187 } 188 189 return ret; 190 } 191 192 /************************************************************************** 193 * MIDI_mciReadWord [internal] 194 */ 195 static DWORD MIDI_mciReadWord(WINE_MCIMIDI* wmm, LPWORD lpw) 196 { 197 BYTE hibyte, lobyte; 198 DWORD ret = MCIERR_INVALID_FILE; 199 200 if (MIDI_mciReadByte(wmm, &hibyte) == 0 && 201 MIDI_mciReadByte(wmm, &lobyte) == 0) { 202 *lpw = ((WORD)hibyte << 8) + lobyte; 203 ret = 0; 204 } 205 return ret; 206 } 207 208 /************************************************************************** 209 * MIDI_mciReadLong [internal] 210 */ 211 static DWORD MIDI_mciReadLong(WINE_MCIMIDI* wmm, LPDWORD lpdw) 212 { 213 WORD hiword, loword; 214 DWORD ret = MCIERR_INVALID_FILE; 215 216 if (MIDI_mciReadWord(wmm, &hiword) == 0 && 217 MIDI_mciReadWord(wmm, &loword) == 0) { 218 *lpdw = MAKELONG(loword, hiword); 219 ret = 0; 220 } 221 return ret; 222 } 223 224 /************************************************************************** 225 * MIDI_mciReadVaryLen [internal] 226 */ 227 static WORD MIDI_mciReadVaryLen(WINE_MCIMIDI* wmm, LPDWORD lpdw) 228 { 229 BYTE byte; 230 DWORD value = 0; 231 WORD len = 0; 232 233 do { 234 if (MIDI_mciReadByte(wmm, &byte) != 0) { 235 return 0; 236 } 237 value = (value << 7) + (byte & 0x7F); 238 len++; 239 } while (byte & 0x80); 240 *lpdw = value; 241 return len; 242 } 243 244 /************************************************************************** 245 * MIDI_mciReadNextEvent [internal] 246 */ 247 static DWORD MIDI_mciReadNextEvent(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt) 248 { 249 BYTE b1, b2 = 0, b3; 250 WORD hw = 0; 251 DWORD evtPulse; 252 DWORD evtLength; 253 DWORD tmp; 254 255 if (mmioSeek(wmm->hFile, mmt->dwIndex, SEEK_SET) != mmt->dwIndex) { 256 WARN("Can't seek at %08X\n", mmt->dwIndex); 257 return MCIERR_INVALID_FILE; 258 } 259 evtLength = MIDI_mciReadVaryLen(wmm, &evtPulse) + 1; /* > 0 */ 260 MIDI_mciReadByte(wmm, &b1); 261 switch (b1) { 262 case 0xF0: 263 case 0xF7: 264 evtLength += MIDI_mciReadVaryLen(wmm, &tmp); 265 evtLength += tmp; 266 break; 267 case 0xFF: 268 MIDI_mciReadByte(wmm, &b2); evtLength++; 269 270 evtLength += MIDI_mciReadVaryLen(wmm, &tmp); 271 if (evtLength >= 0x10000u) { 272 /* this limitation shouldn't be a problem */ 273 WARN("Ouch !! Implementation limitation to 64k bytes for a MIDI event is overflowed\n"); 274 hw = 0xFFFF; 275 } else { 276 hw = LOWORD(evtLength); 277 } 278 evtLength += tmp; 279 break; 280 default: 281 if (b1 & 0x80) { /* use running status ? */ 282 mmt->wLastCommand = b1; 283 MIDI_mciReadByte(wmm, &b2); evtLength++; 284 } else { 285 b2 = b1; 286 b1 = mmt->wLastCommand; 287 } 288 switch ((b1 >> 4) & 0x07) { 289 case 0: case 1: case 2: case 3: case 6: 290 MIDI_mciReadByte(wmm, &b3); evtLength++; 291 hw = b3; 292 break; 293 case 4: case 5: 294 break; 295 case 7: 296 WARN("Strange indeed b1=0x%02x\n", b1); 297 } 298 break; 299 } 300 if (mmt->dwIndex + evtLength > mmt->dwLast) 301 return MCIERR_INTERNAL; 302 303 mmt->dwEventPulse += evtPulse; 304 mmt->dwEventData = (hw << 16) + (b2 << 8) + b1; 305 mmt->wEventLength = evtLength; 306 307 /* 308 TRACE("[%u] => pulse=%08x(%08x), data=%08x, length=%u\n", 309 mmt->wTrackNr, mmt->dwEventPulse, evtPulse, 310 mmt->dwEventData, mmt->wEventLength); 311 */ 312 return 0; 313 } 314 315 /************************************************************************** 316 * MIDI_mciReadMTrk [internal] 317 */ 318 static DWORD MIDI_mciReadMTrk(WINE_MCIMIDI* wmm, MCI_MIDITRACK* mmt) 319 { 320 DWORD toberead; 321 FOURCC fourcc; 322 323 if (mmioRead(wmm->hFile, (HPSTR)&fourcc, (long)sizeof(FOURCC)) != 324 (long)sizeof(FOURCC)) { 325 return MCIERR_INVALID_FILE; 326 } 327 328 if (fourcc != mmioFOURCC('M', 'T', 'r', 'k')) { 329 WARN("Can't synchronize on 'MTrk' !\n"); 330 return MCIERR_INVALID_FILE; 331 } 332 333 if (MIDI_mciReadLong(wmm, &toberead) != 0) { 334 return MCIERR_INVALID_FILE; 335 } 336 mmt->dwFirst = mmioSeek(wmm->hFile, 0, SEEK_CUR); /* >= 0 */ 337 mmt->dwLast = mmt->dwFirst + toberead; 338 339 /* compute # of pulses in this track */ 340 mmt->dwIndex = mmt->dwFirst; 341 mmt->dwEventPulse = 0; 342 343 while (MIDI_mciReadNextEvent(wmm, mmt) == 0 && LOWORD(mmt->dwEventData) != 0x2FFF) { 344 char buf[1024]; 345 WORD len; 346 347 mmt->dwIndex += mmt->wEventLength; 348 349 switch (LOWORD(mmt->dwEventData)) { 350 case 0x02FF: 351 case 0x03FF: 352 len = mmt->wEventLength - HIWORD(mmt->dwEventData); 353 if (len >= sizeof(buf)) { 354 WARN("Buffer for text is too small (%u are needed)\n", len); 355 len = sizeof(buf) - 1; 356 } 357 if (mmioRead(wmm->hFile, buf, len) == len) { 358 buf[len] = 0; /* end string in case */ 359 switch (HIBYTE(LOWORD(mmt->dwEventData))) { 360 case 0x02: 361 if (wmm->lpstrCopyright) { 362 WARN("Two copyright notices (%s|%s)\n", debugstr_w(wmm->lpstrCopyright), buf); 363 HeapFree(GetProcessHeap(), 0, wmm->lpstrCopyright); 364 } 365 len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 ); 366 wmm->lpstrCopyright = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); 367 MultiByteToWideChar( CP_ACP, 0, buf, -1, wmm->lpstrCopyright, len ); 368 break; 369 case 0x03: 370 if (wmm->lpstrName) { 371 WARN("Two names (%s|%s)\n", debugstr_w(wmm->lpstrName), buf); 372 HeapFree(GetProcessHeap(), 0, wmm->lpstrName); 373 } /* last name or name from last track wins */ 374 len = MultiByteToWideChar( CP_ACP, 0, buf, -1, NULL, 0 ); 375 wmm->lpstrName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); 376 MultiByteToWideChar( CP_ACP, 0, buf, -1, wmm->lpstrName, len ); 377 break; 378 } 379 } 380 break; 381 } 382 } 383 mmt->dwLength = mmt->dwEventPulse; 384 385 TRACE("Track %u has %u bytes and %u pulses\n", mmt->wTrackNr, toberead, mmt->dwLength); 386 387 /* reset track data */ 388 mmt->wStatus = 1; /* ok, playing */ 389 mmt->dwIndex = mmt->dwFirst; 390 mmt->dwEventPulse = 0; 391 392 if (mmioSeek(wmm->hFile, 0, SEEK_CUR) != mmt->dwLast) { 393 WARN("Ouch, out of sync seek=%u track=%u\n", 394 mmioSeek(wmm->hFile, 0, SEEK_CUR), mmt->dwLast); 395 /* position at end of this track, to be ready to read next track */ 396 mmioSeek(wmm->hFile, mmt->dwLast, SEEK_SET); 397 } 398 399 return 0; 400 } 401 402 /************************************************************************** 403 * MIDI_mciReadMThd [internal] 404 */ 405 static DWORD MIDI_mciReadMThd(WINE_MCIMIDI* wmm, DWORD dwOffset) 406 { 407 DWORD toberead; 408 FOURCC fourcc; 409 WORD nt; 410 411 TRACE("(%p, %08X);\n", wmm, dwOffset); 412 413 if (mmioSeek(wmm->hFile, dwOffset, SEEK_SET) != dwOffset) { 414 WARN("Can't seek at %08X begin of 'MThd'\n", dwOffset); 415 return MCIERR_INVALID_FILE; 416 } 417 if (mmioRead(wmm->hFile, (HPSTR)&fourcc, 418 (long) sizeof(FOURCC)) != (long) sizeof(FOURCC)) 419 return MCIERR_INVALID_FILE; 420 421 if (fourcc != mmioFOURCC('M', 'T', 'h', 'd')) { 422 WARN("Can't synchronize on 'MThd' !\n"); 423 return MCIERR_INVALID_FILE; 424 } 425 426 if (MIDI_mciReadLong(wmm, &toberead) != 0 || toberead < 3 * sizeof(WORD)) 427 return MCIERR_INVALID_FILE; 428 429 if (MIDI_mciReadWord(wmm, &wmm->wFormat) != 0 || 430 MIDI_mciReadWord(wmm, &wmm->nTracks) != 0 || 431 MIDI_mciReadWord(wmm, &wmm->nDivision) != 0) { 432 return MCIERR_INVALID_FILE; 433 } 434 435 TRACE("toberead=0x%08X, wFormat=0x%04X nTracks=0x%04X nDivision=0x%04X\n", 436 toberead, wmm->wFormat, wmm->nTracks, wmm->nDivision); 437 438 /* MS doc says that the MIDI MCI time format must be put by default to the format 439 * stored in the MIDI file... 440 */ 441 if (wmm->nDivision > 0x8000) { 442 /* eric.pouech@lemel.fr 98/11 443 * I did not check this very code (pulses are expressed as SMPTE sub-frames). 444 * In about 40 MB of MIDI files I have, none was SMPTE based... 445 * I'm just wondering if this is widely used :-). So, if someone has one of 446 * these files, I'd like to know about it. 447 */ 448 FIXME("Handling SMPTE time in MIDI files has not been tested\n" 449 "Please report to comp.emulators.ms-windows.wine with MIDI file !\n"); 450 451 switch (HIBYTE(wmm->nDivision)) { 452 case 0xE8: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24; break; /* -24 */ 453 case 0xE7: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25; break; /* -25 */ 454 case 0xE3: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30DROP; break; /* -29 */ /* is the MCI constant correct ? */ 455 case 0xE2: wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30; break; /* -30 */ 456 default: 457 WARN("Unsupported number of frames %d\n", -(char)HIBYTE(wmm->nDivision)); 458 return MCIERR_INVALID_FILE; 459 } 460 switch (LOBYTE(wmm->nDivision)) { 461 case 4: /* MIDI Time Code */ 462 case 8: 463 case 10: 464 case 80: /* SMPTE bit resolution */ 465 case 100: 466 default: 467 WARN("Unsupported number of sub-frames %d\n", LOBYTE(wmm->nDivision)); 468 return MCIERR_INVALID_FILE; 469 } 470 } else if (wmm->nDivision == 0) { 471 WARN("Number of division is 0, can't support that !!\n"); 472 return MCIERR_INVALID_FILE; 473 } else { 474 wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS; 475 } 476 477 switch (wmm->wFormat) { 478 case 0: 479 if (wmm->nTracks != 1) { 480 WARN("Got type 0 file whose number of track is not 1. Setting it to 1\n"); 481 wmm->nTracks = 1; 482 } 483 break; 484 case 1: 485 case 2: 486 break; 487 default: 488 WARN("Handling MIDI files which format = %d is not (yet) supported\n" 489 "Please report with MIDI file !\n", wmm->wFormat); 490 return MCIERR_INVALID_FILE; 491 } 492 493 if (wmm->nTracks > 0x80) { 494 /* wTrackNr is 7 bits only */ 495 FIXME("Truncating MIDI file with %u tracks\n", wmm->nTracks); 496 wmm->nTracks = 0x80; 497 } 498 499 if ((wmm->tracks = HeapAlloc(GetProcessHeap(), 0, sizeof(MCI_MIDITRACK) * wmm->nTracks)) == NULL) { 500 return MCIERR_OUT_OF_MEMORY; 501 } 502 503 toberead -= 3 * sizeof(WORD); 504 if (toberead > 0) { 505 TRACE("Size of MThd > 6, skipping %d extra bytes\n", toberead); 506 mmioSeek(wmm->hFile, toberead, SEEK_CUR); 507 } 508 509 for (nt = 0; nt < wmm->nTracks; nt++) { 510 wmm->tracks[nt].wTrackNr = nt; 511 if (MIDI_mciReadMTrk(wmm, &wmm->tracks[nt]) != 0) { 512 WARN("Can't read 'MTrk' header\n"); 513 return MCIERR_INVALID_FILE; 514 } 515 } 516 517 wmm->dwTempo = 500000; 518 519 return 0; 520 } 521 522 /************************************************************************** 523 * MIDI_ConvertPulseToMS [internal] 524 */ 525 static DWORD MIDI_ConvertPulseToMS(WINE_MCIMIDI* wmm, DWORD pulse) 526 { 527 DWORD ret = 0; 528 529 /* FIXME: this function may return false values since the tempo (wmm->dwTempo) 530 * may change during file playing 531 */ 532 if (wmm->nDivision == 0) { 533 FIXME("Shouldn't happen. wmm->nDivision = 0\n"); 534 } else if (wmm->nDivision > 0x8000) { /* SMPTE, unchecked FIXME? */ 535 int nf = -(char)HIBYTE(wmm->nDivision); /* number of frames */ 536 int nsf = LOBYTE(wmm->nDivision); /* number of sub-frames */ 537 ret = (pulse * 1000) / (nf * nsf); 538 } else { 539 ret = (DWORD)((double)pulse * ((double)wmm->dwTempo / 1000) / 540 (double)wmm->nDivision); 541 } 542 543 /* 544 TRACE("pulse=%u tempo=%u division=%u=0x%04x => ms=%u\n", 545 pulse, wmm->dwTempo, wmm->nDivision, wmm->nDivision, ret); 546 */ 547 548 return ret; 549 } 550 551 #define TIME_MS_IN_ONE_HOUR (60*60*1000) 552 #define TIME_MS_IN_ONE_MINUTE (60*1000) 553 #define TIME_MS_IN_ONE_SECOND (1000) 554 555 /************************************************************************** 556 * MIDI_ConvertTimeFormatToMS [internal] 557 */ 558 static DWORD MIDI_ConvertTimeFormatToMS(WINE_MCIMIDI* wmm, DWORD val) 559 { 560 DWORD ret = 0; 561 562 switch (wmm->dwMciTimeFormat) { 563 case MCI_FORMAT_MILLISECONDS: 564 ret = val; 565 break; 566 case MCI_FORMAT_SMPTE_24: 567 ret = 568 (HIBYTE(HIWORD(val)) * 125) / 3 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND + 569 HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR; 570 break; 571 case MCI_FORMAT_SMPTE_25: 572 ret = 573 HIBYTE(HIWORD(val)) * 40 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND + 574 HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR; 575 break; 576 case MCI_FORMAT_SMPTE_30: 577 ret = 578 (HIBYTE(HIWORD(val)) * 100) / 3 + LOBYTE(HIWORD(val)) * TIME_MS_IN_ONE_SECOND + 579 HIBYTE(LOWORD(val)) * TIME_MS_IN_ONE_MINUTE + LOBYTE(LOWORD(val)) * TIME_MS_IN_ONE_HOUR; 580 break; 581 default: 582 WARN("Bad time format %u!\n", wmm->dwMciTimeFormat); 583 } 584 /* 585 TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmm->dwMciTimeFormat, ret); 586 */ 587 return ret; 588 } 589 590 /************************************************************************** 591 * MIDI_ConvertMSToTimeFormat [internal] 592 */ 593 static DWORD MIDI_ConvertMSToTimeFormat(WINE_MCIMIDI* wmm, DWORD _val) 594 { 595 DWORD ret = 0, val = _val; 596 DWORD h, m, s, f; 597 598 switch (wmm->dwMciTimeFormat) { 599 case MCI_FORMAT_MILLISECONDS: 600 ret = val; 601 break; 602 case MCI_FORMAT_SMPTE_24: 603 case MCI_FORMAT_SMPTE_25: 604 case MCI_FORMAT_SMPTE_30: 605 h = val / TIME_MS_IN_ONE_HOUR; 606 m = (val -= h * TIME_MS_IN_ONE_HOUR) / TIME_MS_IN_ONE_MINUTE; 607 s = (val -= m * TIME_MS_IN_ONE_MINUTE) / TIME_MS_IN_ONE_SECOND; 608 switch (wmm->dwMciTimeFormat) { 609 case MCI_FORMAT_SMPTE_24: 610 /* one frame is 1000/24 val long, 1000/24 == 125/3 */ 611 f = (val * 3) / 125; val -= (f * 125) / 3; 612 break; 613 case MCI_FORMAT_SMPTE_25: 614 /* one frame is 1000/25 ms long, 1000/25 == 40 */ 615 f = val / 40; val -= f * 40; 616 break; 617 case MCI_FORMAT_SMPTE_30: 618 /* one frame is 1000/30 ms long, 1000/30 == 100/3 */ 619 f = (val * 3) / 100; val -= (f * 100) / 3; 620 break; 621 default: 622 FIXME("There must be some bad bad programmer\n"); 623 f = 0; 624 } 625 /* val contains the number of ms which cannot make a complete frame */ 626 /* FIXME: is this correct ? programs seem to be happy with that */ 627 ret = (f << 24) | (s << 16) | (m << 8) | (h << 0); 628 break; 629 default: 630 WARN("Bad time format %u!\n", wmm->dwMciTimeFormat); 631 } 632 /* 633 TRACE("val=%u [tf=%u] => ret=%u=0x%08x\n", _val, wmm->dwMciTimeFormat, ret, ret); 634 */ 635 return ret; 636 } 637 638 /************************************************************************** 639 * MIDI_GetMThdLengthMS [internal] 640 */ 641 static DWORD MIDI_GetMThdLengthMS(WINE_MCIMIDI* wmm) 642 { 643 WORD nt; 644 DWORD ret = 0; 645 646 for (nt = 0; nt < wmm->nTracks; nt++) { 647 if (wmm->wFormat == 2) { 648 ret += wmm->tracks[nt].dwLength; 649 } else if (wmm->tracks[nt].dwLength > ret) { 650 ret = wmm->tracks[nt].dwLength; 651 } 652 } 653 /* FIXME: this is wrong if there is a tempo change inside the file */ 654 return MIDI_ConvertPulseToMS(wmm, ret); 655 } 656 657 /************************************************************************** 658 * MIDI_mciOpen [internal] 659 */ 660 static DWORD MIDI_mciOpen(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_OPEN_PARMSW lpParms) 661 { 662 DWORD dwRet = 0; 663 664 TRACE("(%d, %08X, %p)\n", wmm->wDevID, dwFlags, lpParms); 665 666 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 667 if (dwFlags & MCI_OPEN_SHAREABLE) 668 return MCIERR_HARDWARE; 669 670 if (wmm->nUseCount > 0) { 671 /* The driver is already opened on this channel 672 * MIDI sequencer cannot be shared 673 */ 674 return MCIERR_DEVICE_OPEN; 675 } 676 wmm->nUseCount++; 677 678 wmm->hFile = 0; 679 wmm->hMidi = 0; 680 wmm->wPort = MIDI_MAPPER; 681 wmm->lpstrElementName = NULL; 682 683 TRACE("wDevID=%d (lpParams->wDeviceID=%d)\n", wmm->wDevID, lpParms->wDeviceID); 684 /* lpParms->wDeviceID = wDevID;*/ 685 686 if (dwFlags & MCI_OPEN_ELEMENT) { 687 TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(lpParms->lpstrElementName)); 688 if (lpParms->lpstrElementName && lpParms->lpstrElementName[0]) { 689 wmm->hFile = mmioOpenW((LPWSTR)lpParms->lpstrElementName, NULL, 690 MMIO_ALLOCBUF | MMIO_READ | MMIO_DENYWRITE); 691 if (wmm->hFile == 0) { 692 WARN("Can't find file %s!\n", debugstr_w(lpParms->lpstrElementName)); 693 wmm->nUseCount--; 694 return MCIERR_FILE_NOT_FOUND; 695 } 696 wmm->lpstrElementName = HeapAlloc(GetProcessHeap(), 0, 697 (strlenW(lpParms->lpstrElementName) + 1) * sizeof(WCHAR)); 698 strcpyW(wmm->lpstrElementName, lpParms->lpstrElementName); 699 } 700 } 701 TRACE("hFile=%p\n", wmm->hFile); 702 703 wmm->lpstrCopyright = NULL; 704 wmm->lpstrName = NULL; 705 706 wmm->dwStatus = MCI_MODE_NOT_READY; /* while loading file contents */ 707 /* spec says it should be the default format from the MIDI file... */ 708 wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS; 709 710 if (wmm->hFile != 0) { 711 MMCKINFO ckMainRIFF; 712 MMCKINFO mmckInfo; 713 DWORD dwOffset = 0; 714 715 if (mmioDescend(wmm->hFile, &ckMainRIFF, NULL, 0) != 0) { 716 dwRet = MCIERR_INVALID_FILE; 717 } else { 718 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n", 719 (LPSTR)&ckMainRIFF.ckid, (LPSTR)&ckMainRIFF.fccType, ckMainRIFF.cksize); 720 721 if (ckMainRIFF.ckid == FOURCC_RIFF && ckMainRIFF.fccType == mmioFOURCC('R', 'M', 'I', 'D')) { 722 mmckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a'); 723 mmioSeek(wmm->hFile, ckMainRIFF.dwDataOffset + ((ckMainRIFF.cksize + 1) & ~1), SEEK_SET); 724 if (mmioDescend(wmm->hFile, &mmckInfo, &ckMainRIFF, MMIO_FINDCHUNK) == 0) { 725 TRACE("... is a 'RMID' file\n"); 726 dwOffset = mmckInfo.dwDataOffset; 727 } else { 728 dwRet = MCIERR_INVALID_FILE; 729 } 730 } 731 if (dwRet == 0 && MIDI_mciReadMThd(wmm, dwOffset) != 0) { 732 WARN("Can't read 'MThd' header\n"); 733 dwRet = MCIERR_INVALID_FILE; 734 } 735 } 736 } else { 737 TRACE("hFile==0, setting #tracks to 0; is this correct ?\n"); 738 wmm->nTracks = 0; 739 wmm->wFormat = 0; 740 wmm->nDivision = 1; 741 } 742 if (dwRet != 0) { 743 wmm->nUseCount--; 744 if (wmm->hFile != 0) 745 mmioClose(wmm->hFile, 0); 746 wmm->hFile = 0; 747 HeapFree(GetProcessHeap(), 0, wmm->tracks); 748 HeapFree(GetProcessHeap(), 0, wmm->lpstrElementName); 749 HeapFree(GetProcessHeap(), 0, wmm->lpstrCopyright); 750 HeapFree(GetProcessHeap(), 0, wmm->lpstrName); 751 } else { 752 wmm->dwPositionMS = 0; 753 wmm->dwStatus = MCI_MODE_STOP; 754 if (dwFlags & MCI_NOTIFY) 755 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 756 } 757 return dwRet; 758 } 759 760 /************************************************************************** 761 * MIDI_mciStop [internal] 762 */ 763 static DWORD MIDI_mciStop(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 764 { 765 DWORD dwRet = 0; 766 767 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 768 769 if (wmm->dwStatus != MCI_MODE_STOP) { 770 HANDLE old = InterlockedExchangePointer(&wmm->hCallback, NULL); 771 if (old) mciDriverNotify(old, wmm->wDevID, MCI_NOTIFY_ABORTED); 772 } 773 774 if (wmm->dwStatus != MCI_MODE_STOP) { 775 int oldstat = wmm->dwStatus; 776 777 wmm->dwStatus = MCI_MODE_NOT_READY; 778 if (oldstat == MCI_MODE_PAUSE) 779 dwRet = midiOutReset((HMIDIOUT)wmm->hMidi); 780 781 if (wmm->hThread) 782 WaitForSingleObject(wmm->hThread, INFINITE); 783 } 784 785 /* sanity reset */ 786 wmm->dwStatus = MCI_MODE_STOP; 787 788 if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet) 789 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 790 return dwRet; 791 } 792 793 /************************************************************************** 794 * MIDI_mciClose [internal] 795 */ 796 static DWORD MIDI_mciClose(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 797 { 798 799 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 800 801 if (wmm->dwStatus != MCI_MODE_STOP) { 802 /* mciStop handles MCI_NOTIFY_ABORTED */ 803 MIDI_mciStop(wmm, MCI_WAIT, lpParms); 804 } 805 806 wmm->nUseCount--; 807 if (wmm->nUseCount == 0) { 808 if (wmm->hFile != 0) { 809 mmioClose(wmm->hFile, 0); 810 wmm->hFile = 0; 811 TRACE("hFile closed !\n"); 812 } 813 if (wmm->hThread) { 814 CloseHandle(wmm->hThread); 815 wmm->hThread = 0; 816 } 817 HeapFree(GetProcessHeap(), 0, wmm->tracks); 818 HeapFree(GetProcessHeap(), 0, wmm->lpstrElementName); 819 HeapFree(GetProcessHeap(), 0, wmm->lpstrCopyright); 820 HeapFree(GetProcessHeap(), 0, wmm->lpstrName); 821 } else { 822 TRACE("Shouldn't happen... nUseCount=%d\n", wmm->nUseCount); 823 return MCIERR_INTERNAL; 824 } 825 826 if ((dwFlags & MCI_NOTIFY) && lpParms) { 827 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 828 } 829 return 0; 830 } 831 832 /************************************************************************** 833 * MIDI_mciFindNextEvent [internal] 834 */ 835 static MCI_MIDITRACK* MIDI_mciFindNextEvent(WINE_MCIMIDI* wmm, LPDWORD hiPulse) 836 { 837 WORD cnt, nt; 838 MCI_MIDITRACK* mmt; 839 840 *hiPulse = 0xFFFFFFFFul; 841 cnt = 0xFFFFu; 842 for (nt = 0; nt < wmm->nTracks; nt++) { 843 mmt = &wmm->tracks[nt]; 844 845 if (mmt->wStatus == 0) 846 continue; 847 if (mmt->dwEventPulse < *hiPulse) { 848 *hiPulse = mmt->dwEventPulse; 849 cnt = nt; 850 } 851 } 852 return (cnt == 0xFFFFu) ? 0 /* no more event on all tracks */ 853 : &wmm->tracks[cnt]; 854 } 855 856 /************************************************************************** 857 * MIDI_player [internal] 858 */ 859 static DWORD MIDI_player(WINE_MCIMIDI* wmm, DWORD dwFlags) 860 { 861 DWORD dwRet; 862 WORD doPlay, nt; 863 MCI_MIDITRACK* mmt; 864 DWORD hiPulse, dwStartMS = wmm->dwPositionMS; 865 HANDLE oldcb = NULL; 866 867 /* init tracks */ 868 for (nt = 0; nt < wmm->nTracks; nt++) { 869 mmt = &wmm->tracks[nt]; 870 871 mmt->wStatus = 1; /* ok, playing */ 872 mmt->dwIndex = mmt->dwFirst; 873 if (wmm->wFormat == 2 && nt > 0) { 874 mmt->dwEventPulse = wmm->tracks[nt - 1].dwLength; 875 } else { 876 mmt->dwEventPulse = 0; 877 } 878 MIDI_mciReadNextEvent(wmm, mmt); /* FIXME == 0 */ 879 } 880 881 dwRet = midiOutOpen((LPHMIDIOUT)&wmm->hMidi, wmm->wPort, 0L, 0L, CALLBACK_NULL); 882 if (dwRet != MMSYSERR_NOERROR) { 883 return mmr2mci(dwRet); 884 } 885 886 wmm->dwPulse = 0; 887 wmm->dwTempo = 500000; 888 wmm->dwPositionMS = 0; 889 wmm->wStartedPlaying = FALSE; 890 891 while (wmm->dwStatus != MCI_MODE_STOP && wmm->dwStatus != MCI_MODE_NOT_READY) { 892 /* it seems that in case of multi-threading, gcc is optimizing just a little bit 893 * too much. Tell gcc not to optimize status value using volatile. 894 */ 895 while (((volatile WINE_MCIMIDI*)wmm)->dwStatus == MCI_MODE_PAUSE); 896 897 doPlay = (wmm->dwPositionMS >= dwStartMS && wmm->dwPositionMS <= wmm->dwEndMS); 898 899 TRACE("wmm->dwStatus=%d, doPlay=%c\n", wmm->dwStatus, doPlay ? 'T' : 'F'); 900 901 if ((mmt = MIDI_mciFindNextEvent(wmm, &hiPulse)) == NULL) 902 break; /* no more event on tracks */ 903 904 /* if starting playing, then set StartTicks to the value it would have had 905 * if play had started at position 0 906 */ 907 if (doPlay && !wmm->wStartedPlaying) { 908 wmm->dwStartTicks = GetTickCount() - MIDI_ConvertPulseToMS(wmm, wmm->dwPulse); 909 wmm->wStartedPlaying = TRUE; 910 TRACE("Setting dwStartTicks to %u\n", wmm->dwStartTicks); 911 } 912 913 if (hiPulse > wmm->dwPulse) { 914 wmm->dwPositionMS += MIDI_ConvertPulseToMS(wmm, hiPulse - wmm->dwPulse); 915 if (doPlay) { 916 DWORD togo = wmm->dwStartTicks + wmm->dwPositionMS; 917 DWORD tc = GetTickCount(); 918 919 TRACE("Pulses hi=0x%08x <> cur=0x%08x\n", hiPulse, wmm->dwPulse); 920 TRACE("Wait until %u => %u ms\n", 921 tc - wmm->dwStartTicks, togo - wmm->dwStartTicks); 922 if (tc < togo) 923 Sleep(togo - tc); 924 } 925 wmm->dwPulse = hiPulse; 926 } 927 928 switch (LOBYTE(LOWORD(mmt->dwEventData))) { 929 case 0xF0: 930 case 0xF7: /* sysex events */ 931 { 932 FIXME("Not handling SysEx events (yet)\n"); 933 } 934 break; 935 case 0xFF: 936 /* position after meta data header */ 937 mmioSeek(wmm->hFile, mmt->dwIndex + HIWORD(mmt->dwEventData), SEEK_SET); 938 switch (HIBYTE(LOWORD(mmt->dwEventData))) { 939 case 0x00: /* 16-bit sequence number */ 940 if (TRACE_ON(mcimidi)) { 941 WORD twd; 942 943 MIDI_mciReadWord(wmm, &twd); /* == 0 */ 944 TRACE("Got sequence number %u\n", twd); 945 } 946 break; 947 case 0x01: /* any text */ 948 case 0x02: /* Copyright Message text */ 949 case 0x03: /* Sequence/Track Name text */ 950 case 0x04: /* Instrument Name text */ 951 case 0x05: /* Lyric text */ 952 case 0x06: /* Marker text */ 953 case 0x07: /* Cue-point text */ 954 if (TRACE_ON(mcimidi)) { 955 char buf[1024]; 956 WORD len = mmt->wEventLength - HIWORD(mmt->dwEventData); 957 static const char* const info[8] = {"", "Text", "Copyright", "Seq/Trk name", 958 "Instrument", "Lyric", "Marker", "Cue-point"}; 959 WORD idx = HIBYTE(LOWORD(mmt->dwEventData)); 960 961 if (len >= sizeof(buf)) { 962 WARN("Buffer for text is too small (%u are needed)\n", len); 963 len = sizeof(buf) - 1; 964 } 965 if (mmioRead(wmm->hFile, buf, len) == len) { 966 buf[len] = 0; /* end string in case */ 967 TRACE("%s => \"%s\"\n", (idx < 8 ) ? info[idx] : "", buf); 968 } else { 969 WARN("Couldn't read data for %s\n", (idx < 8) ? info[idx] : ""); 970 } 971 } 972 break; 973 case 0x20: 974 /* MIDI channel (cc) */ 975 if (FIXME_ON(mcimidi)) { 976 BYTE bt; 977 978 MIDI_mciReadByte(wmm, &bt); /* == 0 */ 979 FIXME("NIY: MIDI channel=%u, track=%u\n", bt, mmt->wTrackNr); 980 } 981 break; 982 case 0x21: 983 /* MIDI port (pp) */ 984 if (FIXME_ON(mcimidi)) { 985 BYTE bt; 986 987 MIDI_mciReadByte(wmm, &bt); /* == 0 */ 988 FIXME("NIY: MIDI port=%u, track=%u\n", bt, mmt->wTrackNr); 989 } 990 break; 991 case 0x2F: /* end of track */ 992 mmt->wStatus = 0; 993 break; 994 case 0x51:/* set tempo */ 995 /* Tempo is expressed in -seconds per midi quarter note 996 * for format 1 MIDI files, this can only be present on track #0 997 */ 998 if (mmt->wTrackNr != 0 && wmm->wFormat == 1) { 999 WARN("For format #1 MIDI files, tempo can only be changed on track #0 (%u)\n", mmt->wTrackNr); 1000 } else { 1001 BYTE tbt; 1002 DWORD value = 0; 1003 1004 MIDI_mciReadByte(wmm, &tbt); value = ((DWORD)tbt) << 16; 1005 MIDI_mciReadByte(wmm, &tbt); value |= ((DWORD)tbt) << 8; 1006 MIDI_mciReadByte(wmm, &tbt); value |= ((DWORD)tbt) << 0; 1007 TRACE("Setting tempo to %d (BPM=%d)\n", wmm->dwTempo, (value) ? (60000000 / value) : 0); 1008 wmm->dwTempo = value; 1009 } 1010 break; 1011 case 0x54: /* (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start */ 1012 if (mmt->wTrackNr != 0 && wmm->wFormat == 1) { 1013 WARN("For format #1 MIDI files, SMPTE track start can only be expressed on track #0 (%u)\n", mmt->wTrackNr); 1014 } if (mmt->dwEventPulse != 0) { 1015 WARN("SMPTE track start can only be expressed at start of track (%u)\n", mmt->dwEventPulse); 1016 } else { 1017 BYTE h, m, s, f, ff; 1018 1019 MIDI_mciReadByte(wmm, &h); 1020 MIDI_mciReadByte(wmm, &m); 1021 MIDI_mciReadByte(wmm, &s); 1022 MIDI_mciReadByte(wmm, &f); 1023 MIDI_mciReadByte(wmm, &ff); 1024 FIXME("NIY: SMPTE track start %u:%u:%u %u.%u\n", h, m, s, f, ff); 1025 } 1026 break; 1027 case 0x58: /* file rhythm */ 1028 if (TRACE_ON(mcimidi)) { 1029 BYTE num, den, cpmc, _32npqn; 1030 1031 MIDI_mciReadByte(wmm, &num); 1032 MIDI_mciReadByte(wmm, &den); /* to notate e.g. 6/8 */ 1033 MIDI_mciReadByte(wmm, &cpmc); /* number of MIDI clocks per metronome click */ 1034 MIDI_mciReadByte(wmm, &_32npqn); /* number of notated 32nd notes per MIDI quarter note */ 1035 1036 TRACE("%u/%u, clock per metronome click=%u, 32nd notes by 1/4 note=%u\n", num, 1 << den, cpmc, _32npqn); 1037 } 1038 break; 1039 case 0x59: /* key signature */ 1040 if (TRACE_ON(mcimidi)) { 1041 BYTE sf, mm; 1042 1043 MIDI_mciReadByte(wmm, &sf); 1044 MIDI_mciReadByte(wmm, &mm); 1045 1046 if (sf >= 0x80) TRACE("%d flats\n", -(char)sf); 1047 else if (sf > 0) TRACE("%d sharps\n", (char)sf); 1048 else TRACE("Key of C\n"); 1049 TRACE("Mode: %s\n", (mm == 0) ? "major" : "minor"); 1050 } 1051 break; 1052 default: 1053 WARN("Unknown MIDI meta event %02x. Skipping...\n", HIBYTE(LOWORD(mmt->dwEventData))); 1054 break; 1055 } 1056 break; 1057 default: 1058 if (doPlay) { 1059 dwRet = midiOutShortMsg((HMIDIOUT)wmm->hMidi, mmt->dwEventData); 1060 } else { 1061 switch (LOBYTE(LOWORD(mmt->dwEventData)) & 0xF0) { 1062 case MIDI_NOTEON: 1063 case MIDI_NOTEOFF: 1064 dwRet = 0; 1065 break; 1066 default: 1067 dwRet = midiOutShortMsg((HMIDIOUT)wmm->hMidi, mmt->dwEventData); 1068 } 1069 } 1070 } 1071 mmt->dwIndex += mmt->wEventLength; 1072 if (mmt->dwIndex < mmt->dwFirst || mmt->dwIndex >= mmt->dwLast) { 1073 mmt->wStatus = 0; 1074 } 1075 if (mmt->wStatus) { 1076 MIDI_mciReadNextEvent(wmm, mmt); 1077 } 1078 } 1079 1080 midiOutReset((HMIDIOUT)wmm->hMidi); 1081 1082 dwRet = midiOutClose((HMIDIOUT)wmm->hMidi); 1083 1084 if (dwFlags & MCI_NOTIFY) 1085 oldcb = InterlockedExchangePointer(&wmm->hCallback, NULL); 1086 1087 wmm->dwStatus = MCI_MODE_STOP; 1088 1089 /* Let the potentially asynchronous commands support FAILURE notification. */ 1090 if (oldcb) mciDriverNotify(oldcb, wmm->wDevID, 1091 dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL); 1092 return mmr2mci(dwRet); 1093 } 1094 1095 static DWORD CALLBACK MIDI_Starter(void *ptr) 1096 { 1097 WINE_MCIMIDI* wmm = ptr; 1098 return MIDI_player(wmm, MCI_NOTIFY); 1099 } 1100 1101 static DWORD ensurePlayerThread(WINE_MCIMIDI* wmm) 1102 { 1103 if (1) { 1104 DWORD dwRet; 1105 1106 switch (wmm->dwStatus) { 1107 default: 1108 return MCIERR_NONAPPLICABLE_FUNCTION; 1109 case MCI_MODE_PAUSE: 1110 return MIDI_mciResume(wmm, 0, NULL); 1111 case MCI_MODE_PLAY: 1112 /* the player was not stopped, use it */ 1113 return 0; 1114 case MCI_MODE_STOP: 1115 break; 1116 } 1117 wmm->dwStatus = MCI_MODE_PLAY; 1118 if (wmm->hThread) { 1119 WaitForSingleObject(wmm->hThread, INFINITE); 1120 CloseHandle(wmm->hThread); 1121 wmm->hThread = 0; 1122 } 1123 wmm->hThread = CreateThread(NULL, 0, MIDI_Starter, wmm, 0, NULL); 1124 if (!wmm->hThread) { 1125 dwRet = MCIERR_OUT_OF_MEMORY; 1126 } else { 1127 SetThreadPriority(wmm->hThread, THREAD_PRIORITY_TIME_CRITICAL); 1128 dwRet = 0; 1129 } 1130 if (dwRet) 1131 wmm->dwStatus = MCI_MODE_STOP; 1132 return dwRet; 1133 } 1134 } 1135 1136 /************************************************************************** 1137 * MIDI_mciPlay [internal] 1138 */ 1139 static DWORD MIDI_mciPlay(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms) 1140 { 1141 DWORD dwStartMS, dwEndMS; 1142 DWORD dwRet; 1143 HANDLE oldcb; 1144 1145 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1146 1147 if (wmm->hFile == 0) { 1148 WARN("Can't play: no file %s!\n", debugstr_w(wmm->lpstrElementName)); 1149 return MCIERR_FILE_NOT_FOUND; 1150 } 1151 1152 if (lpParms && (dwFlags & MCI_TO)) { 1153 dwEndMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo); 1154 /* FIXME: if (dwEndMS > length) return MCIERR_OUTOFRANGE; */ 1155 } else { 1156 dwEndMS = 0xFFFFFFFFul; /* FIXME: dwEndMS = length; */ 1157 } 1158 if (lpParms && (dwFlags & MCI_FROM)) { 1159 dwStartMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwFrom); 1160 } else { 1161 dwStartMS = wmm->dwPositionMS; 1162 } 1163 if (dwEndMS < dwStartMS) 1164 return MCIERR_OUTOFRANGE; 1165 1166 if (dwFlags & MCI_FROM) { 1167 /* Stop with MCI_NOTIFY_ABORTED and set new position. */ 1168 MIDI_mciStop(wmm, MCI_WAIT, NULL); 1169 wmm->dwPositionMS = dwStartMS; 1170 } /* else use existing player. */ 1171 if (wmm->dwEndMS != dwEndMS) { 1172 oldcb = InterlockedExchangePointer(&wmm->hCallback, NULL); 1173 if (oldcb) mciDriverNotify(oldcb, wmm->wDevID, MCI_NOTIFY_ABORTED); 1174 wmm->dwEndMS = dwEndMS; 1175 } 1176 1177 TRACE("Playing from %u to %u\n", dwStartMS, dwEndMS); 1178 1179 if ((dwFlags & MCI_NOTIFY) && lpParms) { 1180 oldcb = InterlockedExchangePointer(&wmm->hCallback, HWND_32(LOWORD(lpParms->dwCallback))); 1181 if (oldcb) mciDriverNotify(oldcb, wmm->wDevID, MCI_NOTIFY_SUPERSEDED); 1182 } 1183 1184 dwRet = ensurePlayerThread(wmm); 1185 1186 if (!dwRet && (dwFlags & MCI_WAIT)) { 1187 WaitForSingleObject(wmm->hThread, INFINITE); 1188 GetExitCodeThread(wmm->hThread, &dwRet); 1189 /* STATUS_PENDING cannot happen. It folds onto MCIERR_UNRECOGNIZED_KEYWORD */ 1190 } 1191 /* The player thread performs notification at exit. */ 1192 return dwRet; 1193 } 1194 1195 /************************************************************************** 1196 * MIDI_mciPause [internal] 1197 */ 1198 static DWORD MIDI_mciPause(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1199 { 1200 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1201 1202 if (wmm->dwStatus == MCI_MODE_PLAY) { 1203 /* stop all notes */ 1204 unsigned chn; 1205 for (chn = 0; chn < 16; chn++) 1206 midiOutShortMsg((HMIDIOUT)(wmm->hMidi), 0x78B0 | chn); 1207 wmm->dwStatus = MCI_MODE_PAUSE; 1208 } 1209 if ((dwFlags & MCI_NOTIFY) && lpParms) 1210 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1211 return 0; 1212 } 1213 1214 /************************************************************************** 1215 * MIDI_mciResume [internal] 1216 */ 1217 static DWORD MIDI_mciResume(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms) 1218 { 1219 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1220 1221 if (wmm->dwStatus == MCI_MODE_PAUSE) { 1222 wmm->wStartedPlaying = FALSE; 1223 wmm->dwStatus = MCI_MODE_PLAY; 1224 } 1225 if ((dwFlags & MCI_NOTIFY) && lpParms) 1226 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1227 return 0; 1228 } 1229 1230 /************************************************************************** 1231 * MIDI_mciSet [internal] 1232 */ 1233 static DWORD MIDI_mciSet(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_SEQ_SET_PARMS lpParms) 1234 { 1235 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1236 1237 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1238 1239 if (dwFlags & MCI_SET_TIME_FORMAT) { 1240 switch (lpParms->dwTimeFormat) { 1241 case MCI_FORMAT_MILLISECONDS: 1242 TRACE("MCI_FORMAT_MILLISECONDS !\n"); 1243 wmm->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS; 1244 break; 1245 case MCI_FORMAT_SMPTE_24: 1246 TRACE("MCI_FORMAT_SMPTE_24 !\n"); 1247 wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_24; 1248 break; 1249 case MCI_FORMAT_SMPTE_25: 1250 TRACE("MCI_FORMAT_SMPTE_25 !\n"); 1251 wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_25; 1252 break; 1253 case MCI_FORMAT_SMPTE_30: 1254 TRACE("MCI_FORMAT_SMPTE_30 !\n"); 1255 wmm->dwMciTimeFormat = MCI_FORMAT_SMPTE_30; 1256 break; 1257 default: 1258 WARN("Bad time format %u!\n", lpParms->dwTimeFormat); 1259 return MCIERR_BAD_TIME_FORMAT; 1260 } 1261 } 1262 if (dwFlags & MCI_SET_VIDEO) { 1263 TRACE("No support for video !\n"); 1264 return MCIERR_UNSUPPORTED_FUNCTION; 1265 } 1266 if (dwFlags & MCI_SET_DOOR_OPEN) { 1267 TRACE("No support for door open !\n"); 1268 return MCIERR_UNSUPPORTED_FUNCTION; 1269 } 1270 if (dwFlags & MCI_SET_DOOR_CLOSED) { 1271 TRACE("No support for door close !\n"); 1272 return MCIERR_UNSUPPORTED_FUNCTION; 1273 } 1274 if (dwFlags & MCI_SET_AUDIO) { 1275 if (dwFlags & MCI_SET_ON) { 1276 TRACE("MCI_SET_ON audio !\n"); 1277 } else if (dwFlags & MCI_SET_OFF) { 1278 TRACE("MCI_SET_OFF audio !\n"); 1279 } else { 1280 WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n"); 1281 return MCIERR_BAD_INTEGER; 1282 } 1283 1284 switch (lpParms->dwAudio) 1285 { 1286 case MCI_SET_AUDIO_ALL: TRACE("MCI_SET_AUDIO_ALL !\n"); break; 1287 case MCI_SET_AUDIO_LEFT: TRACE("MCI_SET_AUDIO_LEFT !\n"); break; 1288 case MCI_SET_AUDIO_RIGHT: TRACE("MCI_SET_AUDIO_RIGHT !\n"); break; 1289 default: WARN("Unknown audio channel %u\n", lpParms->dwAudio); break; 1290 } 1291 } 1292 1293 if (dwFlags & MCI_SEQ_SET_MASTER) 1294 TRACE("MCI_SEQ_SET_MASTER !\n"); 1295 if (dwFlags & MCI_SEQ_SET_SLAVE) 1296 TRACE("MCI_SEQ_SET_SLAVE !\n"); 1297 if (dwFlags & MCI_SEQ_SET_OFFSET) 1298 TRACE("MCI_SEQ_SET_OFFSET !\n"); 1299 if (dwFlags & MCI_SEQ_SET_PORT) { 1300 TRACE("MCI_SEQ_SET_PORT = %d\n", lpParms->dwPort); 1301 if ((UINT16)lpParms->dwPort != (UINT16)MIDI_MAPPER && 1302 (UINT16)lpParms->dwPort >= midiOutGetNumDevs()) 1303 /* FIXME: input/output port distinction? */ 1304 return MCIERR_SEQ_PORT_NONEXISTENT; 1305 /* FIXME: Native manages to swap the device while playing! */ 1306 wmm->wPort = lpParms->dwPort; 1307 } 1308 if (dwFlags & MCI_SEQ_SET_TEMPO) 1309 TRACE("MCI_SEQ_SET_TEMPO !\n"); 1310 if (dwFlags & MCI_NOTIFY) 1311 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1312 return 0; 1313 } 1314 1315 /************************************************************************** 1316 * MIDI_mciStatus [internal] 1317 */ 1318 static DWORD MIDI_mciStatus(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms) 1319 { 1320 DWORD ret = 0; 1321 1322 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1323 1324 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1325 1326 if (dwFlags & MCI_STATUS_ITEM) { 1327 switch (lpParms->dwItem) { 1328 case MCI_STATUS_CURRENT_TRACK: 1329 /* FIXME in Format 2 */ 1330 lpParms->dwReturn = 1; 1331 TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn); 1332 break; 1333 case MCI_STATUS_LENGTH: 1334 if ((dwFlags & MCI_TRACK) && wmm->wFormat == 2) { 1335 if (lpParms->dwTrack >= wmm->nTracks) 1336 return MCIERR_OUTOFRANGE; 1337 /* FIXME: this is wrong if there is a tempo change inside the file */ 1338 lpParms->dwReturn = MIDI_ConvertPulseToMS(wmm, wmm->tracks[lpParms->dwTrack].dwLength); 1339 } else { 1340 lpParms->dwReturn = MIDI_GetMThdLengthMS(wmm); 1341 } 1342 lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm, lpParms->dwReturn); 1343 /* FIXME: ret = MCI_COLONIZED4_RETURN if SMPTE */ 1344 TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn); 1345 break; 1346 case MCI_STATUS_MODE: 1347 TRACE("MCI_STATUS_MODE => %u\n", wmm->dwStatus); 1348 lpParms->dwReturn = MAKEMCIRESOURCE(wmm->dwStatus, wmm->dwStatus); 1349 ret = MCI_RESOURCE_RETURNED; 1350 break; 1351 case MCI_STATUS_MEDIA_PRESENT: 1352 TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE\n"); 1353 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1354 ret = MCI_RESOURCE_RETURNED; 1355 break; 1356 case MCI_STATUS_NUMBER_OF_TRACKS: 1357 lpParms->dwReturn = (wmm->wFormat == 2) ? wmm->nTracks : 1; 1358 TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn); 1359 break; 1360 case MCI_STATUS_POSITION: 1361 /* FIXME: check MCI_TRACK == 1 if set */ 1362 lpParms->dwReturn = MIDI_ConvertMSToTimeFormat(wmm, 1363 (dwFlags & MCI_STATUS_START) ? 0 : wmm->dwPositionMS); 1364 /* FIXME: ret = MCI_COLONIZED4_RETURN if SMPTE */ 1365 TRACE("MCI_STATUS_POSITION %s => %lu\n", 1366 (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn); 1367 break; 1368 case MCI_STATUS_READY: 1369 lpParms->dwReturn = (wmm->dwStatus == MCI_MODE_NOT_READY) ? 1370 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1371 ret = MCI_RESOURCE_RETURNED; 1372 TRACE("MCI_STATUS_READY = %u\n", LOWORD(lpParms->dwReturn)); 1373 break; 1374 case MCI_STATUS_TIME_FORMAT: 1375 lpParms->dwReturn = MAKEMCIRESOURCE(wmm->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmm->dwMciTimeFormat); 1376 TRACE("MCI_STATUS_TIME_FORMAT => %u\n", LOWORD(lpParms->dwReturn)); 1377 ret = MCI_RESOURCE_RETURNED; 1378 break; 1379 case MCI_SEQ_STATUS_DIVTYPE: 1380 TRACE("MCI_SEQ_STATUS_DIVTYPE !\n"); 1381 if (wmm->nDivision > 0x8000) { 1382 switch (HIBYTE(wmm->nDivision)) { 1383 case 0xE8: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_24; break; /* -24 */ 1384 case 0xE7: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_25; break; /* -25 */ 1385 case 0xE3: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30DROP; break; /* -29 */ /* is the MCI constant correct ? */ 1386 case 0xE2: lpParms->dwReturn = MCI_SEQ_DIV_SMPTE_30; break; /* -30 */ 1387 default: FIXME("There is a bad bad programmer\n"); 1388 } 1389 } else { 1390 lpParms->dwReturn = MCI_SEQ_DIV_PPQN; 1391 } 1392 lpParms->dwReturn = MAKEMCIRESOURCE(lpParms->dwReturn,lpParms->dwReturn); 1393 ret = MCI_RESOURCE_RETURNED; 1394 break; 1395 case MCI_SEQ_STATUS_MASTER: 1396 TRACE("MCI_SEQ_STATUS_MASTER !\n"); 1397 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_SEQ_NONE, MCI_SEQ_NONE_S); 1398 ret = MCI_RESOURCE_RETURNED; 1399 break; 1400 case MCI_SEQ_STATUS_SLAVE: 1401 TRACE("MCI_SEQ_STATUS_SLAVE !\n"); 1402 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_SEQ_FILE, MCI_SEQ_FILE_S); 1403 ret = MCI_RESOURCE_RETURNED; 1404 break; 1405 case MCI_SEQ_STATUS_OFFSET: 1406 TRACE("MCI_SEQ_STATUS_OFFSET !\n"); 1407 lpParms->dwReturn = 0; 1408 break; 1409 case MCI_SEQ_STATUS_PORT: 1410 if (wmm->wPort != (UINT16)MIDI_MAPPER) 1411 lpParms->dwReturn = wmm->wPort; 1412 else { 1413 lpParms->dwReturn = MAKEMCIRESOURCE(MIDI_MAPPER, MCI_SEQ_MAPPER_S); 1414 ret = MCI_RESOURCE_RETURNED; 1415 } 1416 TRACE("MCI_SEQ_STATUS_PORT (%u) => %d\n", wmm->wDevID, wmm->wPort); 1417 break; 1418 case MCI_SEQ_STATUS_TEMPO: 1419 TRACE("MCI_SEQ_STATUS_TEMPO !\n"); 1420 lpParms->dwReturn = wmm->dwTempo; 1421 break; 1422 default: 1423 FIXME("Unknown command %08X !\n", lpParms->dwItem); 1424 return MCIERR_UNSUPPORTED_FUNCTION; 1425 } 1426 } else { 1427 return MCIERR_MISSING_PARAMETER; 1428 } 1429 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 1430 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1431 return ret; 1432 } 1433 1434 /************************************************************************** 1435 * MIDI_mciGetDevCaps [internal] 1436 */ 1437 static DWORD MIDI_mciGetDevCaps(WINE_MCIMIDI* wmm, DWORD dwFlags, 1438 LPMCI_GETDEVCAPS_PARMS lpParms) 1439 { 1440 DWORD ret; 1441 1442 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1443 1444 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1445 1446 if (dwFlags & MCI_GETDEVCAPS_ITEM) { 1447 switch (lpParms->dwItem) { 1448 case MCI_GETDEVCAPS_DEVICE_TYPE: 1449 TRACE("MCI_GETDEVCAPS_DEVICE_TYPE !\n"); 1450 lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_SEQUENCER, MCI_DEVTYPE_SEQUENCER); 1451 ret = MCI_RESOURCE_RETURNED; 1452 break; 1453 case MCI_GETDEVCAPS_HAS_AUDIO: 1454 TRACE("MCI_GETDEVCAPS_HAS_AUDIO !\n"); 1455 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1456 ret = MCI_RESOURCE_RETURNED; 1457 break; 1458 case MCI_GETDEVCAPS_HAS_VIDEO: 1459 TRACE("MCI_GETDEVCAPS_HAS_VIDEO !\n"); 1460 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 1461 ret = MCI_RESOURCE_RETURNED; 1462 break; 1463 case MCI_GETDEVCAPS_USES_FILES: 1464 TRACE("MCI_GETDEVCAPS_USES_FILES !\n"); 1465 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1466 ret = MCI_RESOURCE_RETURNED; 1467 break; 1468 case MCI_GETDEVCAPS_COMPOUND_DEVICE: 1469 TRACE("MCI_GETDEVCAPS_COMPOUND_DEVICE !\n"); 1470 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1471 ret = MCI_RESOURCE_RETURNED; 1472 break; 1473 case MCI_GETDEVCAPS_CAN_EJECT: 1474 TRACE("MCI_GETDEVCAPS_CAN_EJECT !\n"); 1475 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 1476 ret = MCI_RESOURCE_RETURNED; 1477 break; 1478 case MCI_GETDEVCAPS_CAN_PLAY: 1479 TRACE("MCI_GETDEVCAPS_CAN_PLAY !\n"); 1480 lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE); 1481 ret = MCI_RESOURCE_RETURNED; 1482 break; 1483 case MCI_GETDEVCAPS_CAN_RECORD: 1484 TRACE("MCI_GETDEVCAPS_CAN_RECORD !\n"); 1485 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 1486 ret = MCI_RESOURCE_RETURNED; 1487 break; 1488 case MCI_GETDEVCAPS_CAN_SAVE: 1489 TRACE("MCI_GETDEVCAPS_CAN_SAVE !\n"); 1490 lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE); 1491 ret = MCI_RESOURCE_RETURNED; 1492 break; 1493 default: 1494 FIXME("Unknown capability (%08x) !\n", lpParms->dwItem); 1495 return MCIERR_UNRECOGNIZED_COMMAND; 1496 } 1497 } else { 1498 WARN("No GetDevCaps-Item !\n"); 1499 return MCIERR_UNRECOGNIZED_COMMAND; 1500 } 1501 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 1502 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1503 return ret; 1504 } 1505 1506 /************************************************************************** 1507 * MIDI_mciInfo [internal] 1508 */ 1509 static DWORD MIDI_mciInfo(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms) 1510 { 1511 LPCWSTR str = 0; 1512 DWORD ret = 0; 1513 static const WCHAR wszMidiSeq[] = {'W','i','n','e','\'','s',' ','M','I','D','I',' ','s','e','q','u','e','n','c','e','r',0}; 1514 1515 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1516 1517 if (lpParms == NULL || lpParms->lpstrReturn == NULL) 1518 return MCIERR_NULL_PARAMETER_BLOCK; 1519 1520 TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize); 1521 1522 switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) { 1523 case MCI_INFO_PRODUCT: str = wszMidiSeq; break; 1524 case MCI_INFO_FILE: str = wmm->lpstrElementName; break; 1525 case MCI_INFO_COPYRIGHT: str = wmm->lpstrCopyright; break; 1526 case MCI_INFO_NAME: str = wmm->lpstrName; break; 1527 default: 1528 WARN("Don't know this info command (%u)\n", dwFlags); 1529 return MCIERR_MISSING_PARAMETER; /* not MCIERR_FLAGS_... */ 1530 } 1531 if (!ret) { 1532 if (lpParms->dwRetSize) { 1533 WCHAR zero = 0; 1534 /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize 1535 * to the number of characters written, excluding \0. */ 1536 lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize); 1537 } else ret = MCIERR_PARAM_OVERFLOW; 1538 } 1539 if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY)) 1540 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1541 return ret; 1542 } 1543 1544 /************************************************************************** 1545 * MIDI_mciSeek [internal] 1546 */ 1547 static DWORD MIDI_mciSeek(WINE_MCIMIDI* wmm, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms) 1548 { 1549 DWORD position; 1550 1551 TRACE("(%d, %08X, %p);\n", wmm->wDevID, dwFlags, lpParms); 1552 1553 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1554 1555 position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO); 1556 if (!position) return MCIERR_MISSING_PARAMETER; 1557 if (position&(position-1)) return MCIERR_FLAGS_NOT_COMPATIBLE; 1558 1559 MIDI_mciStop(wmm, MCI_WAIT, 0); 1560 1561 if (dwFlags & MCI_TO) { /* FIXME: compare with length */ 1562 wmm->dwPositionMS = MIDI_ConvertTimeFormatToMS(wmm, lpParms->dwTo); 1563 } else if (dwFlags & MCI_SEEK_TO_START) { 1564 wmm->dwPositionMS = 0; 1565 } else { 1566 wmm->dwPositionMS = 0xFFFFFFFF; /* FIXME */ 1567 } 1568 1569 TRACE("Seeking to position=%u ms\n", wmm->dwPositionMS); 1570 1571 if (dwFlags & MCI_NOTIFY) 1572 MIDI_mciNotify(lpParms->dwCallback, wmm, MCI_NOTIFY_SUCCESSFUL); 1573 1574 return 0; 1575 } 1576 1577 /*======================================================================* 1578 * MIDI entry points * 1579 *======================================================================*/ 1580 1581 /************************************************************************** 1582 * DriverProc (MCISEQ.@) 1583 */ 1584 LRESULT CALLBACK MCIMIDI_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 1585 LPARAM dwParam1, LPARAM dwParam2) 1586 { 1587 WINE_MCIMIDI* wmm; 1588 switch (wMsg) { 1589 case DRV_LOAD: return 1; 1590 case DRV_FREE: return 1; 1591 case DRV_ENABLE: return 1; 1592 case DRV_DISABLE: return 1; 1593 case DRV_QUERYCONFIGURE: return 1; 1594 case DRV_CONFIGURE: MessageBoxA(0, "Sample Midi Driver !", "OSS Driver", MB_OK); return 1; 1595 case DRV_INSTALL: return DRVCNF_RESTART; 1596 case DRV_REMOVE: return DRVCNF_RESTART; 1597 case DRV_OPEN: return MIDI_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2); 1598 case DRV_CLOSE: return MIDI_drvClose(dwDevID); 1599 } 1600 if ((wMsg < DRV_MCI_FIRST) || (wMsg > DRV_MCI_LAST)) { 1601 TRACE("Sending msg %04x to default driver proc\n", wMsg); 1602 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); 1603 } 1604 1605 wmm = MIDI_mciGetOpenDev(dwDevID, wMsg); 1606 if (wmm == NULL) return MCIERR_INVALID_DEVICE_ID; 1607 1608 switch (wMsg) { 1609 case MCI_OPEN_DRIVER: return MIDI_mciOpen (wmm, dwParam1, (LPMCI_OPEN_PARMSW) dwParam2); 1610 case MCI_CLOSE_DRIVER: return MIDI_mciClose (wmm, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1611 case MCI_PLAY: return MIDI_mciPlay (wmm, dwParam1, (LPMCI_PLAY_PARMS) dwParam2); 1612 case MCI_STOP: return MIDI_mciStop (wmm, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1613 case MCI_SET: return MIDI_mciSet (wmm, dwParam1, (LPMCI_SEQ_SET_PARMS) dwParam2); 1614 case MCI_PAUSE: return MIDI_mciPause (wmm, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1615 case MCI_RESUME: return MIDI_mciResume (wmm, dwParam1, (LPMCI_GENERIC_PARMS) dwParam2); 1616 case MCI_STATUS: return MIDI_mciStatus (wmm, dwParam1, (LPMCI_STATUS_PARMS) dwParam2); 1617 case MCI_GETDEVCAPS: return MIDI_mciGetDevCaps(wmm, dwParam1, (LPMCI_GETDEVCAPS_PARMS)dwParam2); 1618 case MCI_INFO: return MIDI_mciInfo (wmm, dwParam1, (LPMCI_INFO_PARMSW) dwParam2); 1619 case MCI_SEEK: return MIDI_mciSeek (wmm, dwParam1, (LPMCI_SEEK_PARMS) dwParam2); 1620 case MCI_OPEN: 1621 case MCI_CLOSE: 1622 FIXME("Shouldn't receive a MCI_OPEN or CLOSE message\n"); 1623 /* fall through */ 1624 default: 1625 TRACE("Unsupported command [0x%x]\n", wMsg); 1626 return MCIERR_UNSUPPORTED_FUNCTION; /* Win9x: MCIERR_UNRECOGNIZED_COMMAND */ 1627 } 1628 } 1629