ntdll: Buffer pagemap reads in fill_working_set_info().
[wine.git] / dlls / winmm / winmm.c
blob73050e0affb76b98c2175c70ea6c51fef2410472
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * WINMM functions
6 * Copyright 1993 Martin Ayotte
7 * 1998-2002 Eric Pouech
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 * Eric POUECH :
26 * 98/9 added Win32 MCI support
27 * 99/4 added midiStream support
28 * 99/9 added support for loadable low level drivers
31 /* TODO
32 * + it seems that some programs check what's installed in
33 * registry against the value returned by drivers. Wine is
34 * currently broken regarding this point.
35 * + check thread-safeness for MMSYSTEM and WINMM entry points
36 * + unicode entry points are badly supported (would require
37 * moving 32 bit drivers as Unicode as they are supposed to be)
38 * + allow joystick and timer external calls as we do for wave,
39 * midi, mixer and aux
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <string.h>
46 #include "windef.h"
47 #include "winbase.h"
48 #include "mmsystem.h"
49 #include "winuser.h"
50 #include "winnls.h"
51 #include "winternl.h"
52 #include "winemm.h"
54 #include "wine/debug.h"
55 #include "wine/rbtree.h"
57 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
59 /* ========================================================================
60 * G L O B A L S E T T I N G S
61 * ========================================================================*/
63 HINSTANCE hWinMM32Instance;
64 HANDLE psLastEvent;
66 static CRITICAL_SECTION_DEBUG critsect_debug =
68 0, 0, &WINMM_cs,
69 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
70 0, 0, { (DWORD_PTR)(__FILE__ ": WINMM_cs") }
72 CRITICAL_SECTION WINMM_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
74 static struct wine_rb_tree wine_midi_streams;
75 static int wine_midi_stream_compare(const void *key, const struct wine_rb_entry *entry);
77 /**************************************************************************
78 * WINMM_CreateIData [internal]
80 static BOOL WINMM_CreateIData(HINSTANCE hInstDLL)
82 hWinMM32Instance = hInstDLL;
83 wine_rb_init(&wine_midi_streams, wine_midi_stream_compare);
84 psLastEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
85 return psLastEvent != NULL;
88 /******************************************************************
89 * WINMM_ErrorToString
91 const char* WINMM_ErrorToString(MMRESULT error)
93 #define ERR_TO_STR(dev) case dev: return #dev
94 switch (error) {
95 ERR_TO_STR(MMSYSERR_NOERROR);
96 ERR_TO_STR(MMSYSERR_ERROR);
97 ERR_TO_STR(MMSYSERR_BADDEVICEID);
98 ERR_TO_STR(MMSYSERR_NOTENABLED);
99 ERR_TO_STR(MMSYSERR_ALLOCATED);
100 ERR_TO_STR(MMSYSERR_INVALHANDLE);
101 ERR_TO_STR(MMSYSERR_NODRIVER);
102 ERR_TO_STR(MMSYSERR_NOMEM);
103 ERR_TO_STR(MMSYSERR_NOTSUPPORTED);
104 ERR_TO_STR(MMSYSERR_BADERRNUM);
105 ERR_TO_STR(MMSYSERR_INVALFLAG);
106 ERR_TO_STR(MMSYSERR_INVALPARAM);
107 ERR_TO_STR(MMSYSERR_HANDLEBUSY);
108 ERR_TO_STR(MMSYSERR_INVALIDALIAS);
109 ERR_TO_STR(MMSYSERR_BADDB);
110 ERR_TO_STR(MMSYSERR_KEYNOTFOUND);
111 ERR_TO_STR(MMSYSERR_READERROR);
112 ERR_TO_STR(MMSYSERR_WRITEERROR);
113 ERR_TO_STR(MMSYSERR_DELETEERROR);
114 ERR_TO_STR(MMSYSERR_VALNOTFOUND);
115 ERR_TO_STR(MMSYSERR_NODRIVERCB);
116 ERR_TO_STR(WAVERR_BADFORMAT);
117 ERR_TO_STR(WAVERR_STILLPLAYING);
118 ERR_TO_STR(WAVERR_UNPREPARED);
119 ERR_TO_STR(WAVERR_SYNC);
120 ERR_TO_STR(MIDIERR_INVALIDSETUP);
121 ERR_TO_STR(MIDIERR_NODEVICE);
122 ERR_TO_STR(MIDIERR_STILLPLAYING);
123 ERR_TO_STR(MIDIERR_UNPREPARED);
125 #undef ERR_TO_STR
126 return wine_dbg_sprintf("Unknown(0x%08x)", error);
129 /**************************************************************************
130 * DllMain (WINMM.init)
132 * WINMM DLL entry point
135 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
137 TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, fImpLoad);
139 switch (fdwReason) {
140 case DLL_PROCESS_ATTACH:
141 DisableThreadLibraryCalls(hInstDLL);
143 if (!WINMM_CreateIData(hInstDLL))
144 return FALSE;
145 break;
146 case DLL_PROCESS_DETACH:
147 if(fImpLoad)
148 break;
150 joystick_unload();
151 MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L);
152 MMDRV_Exit();
153 DRIVER_UnloadAll();
154 WINMM_DeleteWaveform();
155 TIME_MMTimeStop();
156 CloseHandle(psLastEvent);
157 DeleteCriticalSection(&WINMM_cs);
158 break;
160 return TRUE;
163 /**************************************************************************
164 * WINMM_CheckCallback [internal]
166 MMRESULT WINMM_CheckCallback(DWORD_PTR dwCallback, DWORD fdwOpen, BOOL mixer)
168 switch (fdwOpen & CALLBACK_TYPEMASK) {
169 case CALLBACK_NULL: /* dwCallback need not be NULL */
170 break;
171 case CALLBACK_WINDOW:
172 if (dwCallback && !IsWindow((HWND)dwCallback))
173 return MMSYSERR_INVALPARAM;
174 break;
176 case CALLBACK_FUNCTION:
177 /* a NULL cb is acceptable since w2k, MMSYSERR_INVALPARAM earlier */
178 if (mixer)
179 return MMSYSERR_INVALFLAG; /* since w2k, MMSYSERR_NOTSUPPORTED earlier */
180 break;
181 case CALLBACK_THREAD:
182 case CALLBACK_EVENT:
183 if (mixer) /* FIXME: mixer supports THREAD+EVENT since w2k */
184 return MMSYSERR_NOTSUPPORTED; /* w9X */
185 break;
186 default:
187 WARN("Unknown callback type %d\n", HIWORD(fdwOpen));
189 return MMSYSERR_NOERROR;
192 /**************************************************************************
193 * auxGetNumDevs [WINMM.@]
195 UINT WINAPI auxGetNumDevs(void)
197 return MMDRV_GetNum(MMDRV_AUX);
200 /**************************************************************************
201 * auxGetDevCapsW [WINMM.@]
203 UINT WINAPI auxGetDevCapsW(UINT_PTR uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
205 LPWINE_MLD wmld;
207 TRACE("(%04IX, %p, %d) !\n", uDeviceID, lpCaps, uSize);
209 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
211 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
212 return MMSYSERR_BADDEVICEID;
213 return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
216 /**************************************************************************
217 * auxGetDevCapsA [WINMM.@]
219 UINT WINAPI auxGetDevCapsA(UINT_PTR uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
221 AUXCAPSW acW;
222 UINT ret;
224 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
226 ret = auxGetDevCapsW(uDeviceID, &acW, sizeof(acW));
228 if (ret == MMSYSERR_NOERROR) {
229 AUXCAPSA acA;
230 acA.wMid = acW.wMid;
231 acA.wPid = acW.wPid;
232 acA.vDriverVersion = acW.vDriverVersion;
233 WideCharToMultiByte( CP_ACP, 0, acW.szPname, -1, acA.szPname,
234 sizeof(acA.szPname), NULL, NULL );
235 acA.wTechnology = acW.wTechnology;
236 acA.wReserved1 = acW.wReserved1;
237 acA.dwSupport = acW.dwSupport;
238 memcpy(lpCaps, &acA, min(uSize, sizeof(acA)));
240 return ret;
243 /**************************************************************************
244 * auxGetVolume [WINMM.@]
246 UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
248 LPWINE_MLD wmld;
250 TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
252 if ((wmld = MMDRV_Get((HANDLE)(DWORD_PTR)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
253 return MMSYSERR_INVALHANDLE;
254 return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L);
257 /**************************************************************************
258 * auxSetVolume [WINMM.@]
260 UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
262 LPWINE_MLD wmld;
264 TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);
266 if ((wmld = MMDRV_Get((HANDLE)(DWORD_PTR)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
267 return MMSYSERR_INVALHANDLE;
268 return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L);
271 /**************************************************************************
272 * auxOutMessage [WINMM.@]
274 UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2)
276 LPWINE_MLD wmld;
278 if ((wmld = MMDRV_Get((HANDLE)(DWORD_PTR)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
279 return MMSYSERR_INVALHANDLE;
281 return MMDRV_Message(wmld, uMessage, dw1, dw2);
284 /**************************************************************************
285 * midiOutGetNumDevs [WINMM.@]
287 UINT WINAPI midiOutGetNumDevs(void)
289 return MMDRV_GetNum(MMDRV_MIDIOUT);
292 /**************************************************************************
293 * midiOutGetDevCapsW [WINMM.@]
295 UINT WINAPI midiOutGetDevCapsW(UINT_PTR uDeviceID, LPMIDIOUTCAPSW lpCaps,
296 UINT uSize)
298 LPWINE_MLD wmld;
300 TRACE("(%Iu, %p, %u);\n", uDeviceID, lpCaps, uSize);
302 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
304 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
305 return MMSYSERR_BADDEVICEID;
307 return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
310 /**************************************************************************
311 * midiOutGetDevCapsA [WINMM.@]
313 UINT WINAPI midiOutGetDevCapsA(UINT_PTR uDeviceID, LPMIDIOUTCAPSA lpCaps,
314 UINT uSize)
316 MIDIOUTCAPSW mocW;
317 UINT ret;
319 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
321 ret = midiOutGetDevCapsW(uDeviceID, &mocW, sizeof(mocW));
323 if (ret == MMSYSERR_NOERROR) {
324 MIDIOUTCAPSA mocA;
325 mocA.wMid = mocW.wMid;
326 mocA.wPid = mocW.wPid;
327 mocA.vDriverVersion = mocW.vDriverVersion;
328 WideCharToMultiByte( CP_ACP, 0, mocW.szPname, -1, mocA.szPname,
329 sizeof(mocA.szPname), NULL, NULL );
330 mocA.wTechnology = mocW.wTechnology;
331 mocA.wVoices = mocW.wVoices;
332 mocA.wNotes = mocW.wNotes;
333 mocA.wChannelMask = mocW.wChannelMask;
334 mocA.dwSupport = mocW.dwSupport;
335 memcpy(lpCaps, &mocA, min(uSize, sizeof(mocA)));
337 return ret;
340 /**************************************************************************
341 * midiOutGetErrorTextA [WINMM.@]
342 * midiInGetErrorTextA [WINMM.@]
344 UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
346 UINT ret;
348 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
349 else if (uSize == 0) ret = MMSYSERR_NOERROR;
350 else
352 WCHAR *xstr = malloc(uSize * sizeof(WCHAR));
353 if (!xstr) ret = MMSYSERR_NOMEM;
354 else
356 ret = midiOutGetErrorTextW(uError, xstr, uSize);
357 if (ret == MMSYSERR_NOERROR)
358 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
359 free(xstr);
362 return ret;
365 /**************************************************************************
366 * midiOutGetErrorTextW [WINMM.@]
367 * midiInGetErrorTextW [WINMM.@]
369 UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
371 UINT ret = MMSYSERR_BADERRNUM;
373 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
374 else if (uSize == 0) ret = MMSYSERR_NOERROR;
375 else if (
376 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
377 * a warning for the test was always true */
378 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
379 (uError >= MIDIERR_BASE && uError <= MIDIERR_LASTERROR)) {
380 if (LoadStringW(hWinMM32Instance, uError, lpText, uSize) > 0) {
381 ret = MMSYSERR_NOERROR;
384 return ret;
387 /**************************************************************************
388 * MIDI_OutAlloc [internal]
390 static LPWINE_MIDI MIDI_OutAlloc(HMIDIOUT* lphMidiOut, DWORD_PTR* lpdwCallback,
391 DWORD_PTR* lpdwInstance, LPDWORD lpdwFlags,
392 DWORD cIDs, MIDIOPENSTRMID* lpIDs)
394 HANDLE hMidiOut;
395 LPWINE_MIDI lpwm;
396 UINT size;
398 size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
400 lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
401 lpdwCallback, lpdwInstance);
402 *lphMidiOut = hMidiOut;
404 if (lpwm) {
405 lpwm->mod.hMidi = hMidiOut;
406 lpwm->mod.dwCallback = *lpdwCallback;
407 lpwm->mod.dwInstance = *lpdwInstance;
408 lpwm->mod.dnDevNode = 0;
409 lpwm->mod.cIds = cIDs;
410 if (cIDs)
411 memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
413 return lpwm;
416 /**************************************************************************
417 * midiOutOpen [WINMM.@]
419 MMRESULT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,
420 DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
422 HMIDIOUT hMidiOut;
423 LPWINE_MIDI lpwm;
424 MMRESULT dwRet;
426 TRACE("(%p, %d, %08IX, %08IX, %08lX);\n",
427 lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
429 if (lphMidiOut != NULL) *lphMidiOut = 0;
431 dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
432 if (dwRet != MMSYSERR_NOERROR)
433 return dwRet;
435 lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags, 0, NULL);
437 if (lpwm == NULL)
438 return MMSYSERR_NOMEM;
440 lpwm->mld.uDeviceID = uDeviceID;
442 dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
444 if (dwRet != MMSYSERR_NOERROR) {
445 MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
446 hMidiOut = 0;
449 if (lphMidiOut) *lphMidiOut = hMidiOut;
450 TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
452 return dwRet;
455 /**************************************************************************
456 * midiOutClose [WINMM.@]
458 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
460 LPWINE_MLD wmld;
461 DWORD dwRet;
463 TRACE("(%p)\n", hMidiOut);
465 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
466 return MMSYSERR_INVALHANDLE;
468 dwRet = MMDRV_Close(wmld, MODM_CLOSE);
469 MMDRV_Free(hMidiOut, wmld);
471 return dwRet;
474 /**************************************************************************
475 * midiOutPrepareHeader [WINMM.@]
477 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
478 MIDIHDR* lpMidiOutHdr, UINT uSize)
480 LPWINE_MLD wmld;
482 TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
484 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
485 return MMSYSERR_INVALHANDLE;
486 /* FIXME: detect MIDIStream handles and enforce 64KB buffer limit on those */
488 return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
491 /**************************************************************************
492 * midiOutUnprepareHeader [WINMM.@]
494 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
495 MIDIHDR* lpMidiOutHdr, UINT uSize)
497 LPWINE_MLD wmld;
499 TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
501 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
502 return MMSYSERR_INVALHANDLE;
504 return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
507 /**************************************************************************
508 * midiOutShortMsg [WINMM.@]
510 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
512 LPWINE_MLD wmld;
514 TRACE("(%p, %08lX)\n", hMidiOut, dwMsg);
516 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
517 return MMSYSERR_INVALHANDLE;
519 return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L);
522 /**************************************************************************
523 * midiOutLongMsg [WINMM.@]
525 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
526 MIDIHDR* lpMidiOutHdr, UINT uSize)
528 LPWINE_MLD wmld;
530 TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
532 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
533 return MMSYSERR_INVALHANDLE;
535 return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize);
538 /**************************************************************************
539 * midiOutReset [WINMM.@]
541 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
543 LPWINE_MLD wmld;
545 TRACE("(%p)\n", hMidiOut);
547 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
548 return MMSYSERR_INVALHANDLE;
550 return MMDRV_Message(wmld, MODM_RESET, 0L, 0L);
553 /**************************************************************************
554 * midiOutGetVolume [WINMM.@]
556 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
558 LPWINE_MLD wmld;
560 TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
562 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
563 return MMSYSERR_INVALHANDLE;
565 return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L);
568 /**************************************************************************
569 * midiOutSetVolume [WINMM.@]
571 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
573 LPWINE_MLD wmld;
575 TRACE("(%p, %ld);\n", hMidiOut, dwVolume);
577 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
578 return MMSYSERR_INVALHANDLE;
580 return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L);
583 /**************************************************************************
584 * midiOutCachePatches [WINMM.@]
586 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
587 WORD* lpwPatchArray, UINT uFlags)
589 /* not really necessary to support this */
590 FIXME("(%p, %u, %p, %x): Stub\n", hMidiOut, uBank, lpwPatchArray, uFlags);
591 return MMSYSERR_NOTSUPPORTED;
594 /**************************************************************************
595 * midiOutCacheDrumPatches [WINMM.@]
597 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
598 WORD* lpwKeyArray, UINT uFlags)
600 FIXME("(%p, %u, %p, %x): Stub\n", hMidiOut, uPatch, lpwKeyArray, uFlags);
601 return MMSYSERR_NOTSUPPORTED;
604 /**************************************************************************
605 * midiOutGetID [WINMM.@]
607 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
609 LPWINE_MLD wmld;
611 TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
613 if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
614 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
615 return MMSYSERR_INVALHANDLE;
617 *lpuDeviceID = wmld->uDeviceID;
618 return MMSYSERR_NOERROR;
621 /**************************************************************************
622 * midiOutMessage [WINMM.@]
624 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
625 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
627 LPWINE_MLD wmld;
629 TRACE("(%p, %04X, %08IX, %08IX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
631 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
632 /* HACK... */
633 if (uMessage == 0x0001) {
634 *(LPDWORD)dwParam1 = 1;
635 return 0;
637 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
638 return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
640 return MMSYSERR_INVALHANDLE;
643 switch (uMessage) {
644 case MODM_OPEN:
645 case MODM_CLOSE:
646 FIXME("can't handle OPEN or CLOSE message!\n");
647 return MMSYSERR_NOTSUPPORTED;
649 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
652 /**************************************************************************
653 * midiInGetNumDevs [WINMM.@]
655 UINT WINAPI midiInGetNumDevs(void)
657 return MMDRV_GetNum(MMDRV_MIDIIN);
660 /**************************************************************************
661 * midiInGetDevCapsW [WINMM.@]
663 UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
665 LPWINE_MLD wmld;
667 TRACE("(%Id, %p, %d);\n", uDeviceID, lpCaps, uSize);
669 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
671 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
672 return MMSYSERR_BADDEVICEID;
674 return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
677 /**************************************************************************
678 * midiInGetDevCapsA [WINMM.@]
680 UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
682 MIDIINCAPSW micW;
683 UINT ret;
685 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
687 ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));
689 if (ret == MMSYSERR_NOERROR) {
690 MIDIINCAPSA micA;
691 micA.wMid = micW.wMid;
692 micA.wPid = micW.wPid;
693 micA.vDriverVersion = micW.vDriverVersion;
694 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
695 sizeof(micA.szPname), NULL, NULL );
696 micA.dwSupport = micW.dwSupport;
697 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
699 return ret;
702 /**************************************************************************
703 * midiInOpen [WINMM.@]
705 MMRESULT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
706 DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
708 HANDLE hMidiIn;
709 LPWINE_MIDI lpwm;
710 MMRESULT dwRet;
712 TRACE("(%p, %d, %08IX, %08IX, %08lX);\n",
713 lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
715 if (lphMidiIn != NULL) *lphMidiIn = 0;
717 dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
718 if (dwRet != MMSYSERR_NOERROR)
719 return dwRet;
721 lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
722 &dwFlags, &dwCallback, &dwInstance);
724 if (lpwm == NULL)
725 return MMSYSERR_NOMEM;
727 lpwm->mod.hMidi = hMidiIn;
728 lpwm->mod.dwCallback = dwCallback;
729 lpwm->mod.dwInstance = dwInstance;
731 lpwm->mld.uDeviceID = uDeviceID;
732 dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
734 if (dwRet != MMSYSERR_NOERROR) {
735 MMDRV_Free(hMidiIn, &lpwm->mld);
736 hMidiIn = 0;
738 if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
739 TRACE("=> %d hMidi=%p\n", dwRet, hMidiIn);
741 return dwRet;
744 /**************************************************************************
745 * midiInClose [WINMM.@]
747 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
749 LPWINE_MLD wmld;
750 DWORD dwRet;
752 TRACE("(%p)\n", hMidiIn);
754 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
755 return MMSYSERR_INVALHANDLE;
757 dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
758 MMDRV_Free(hMidiIn, wmld);
759 return dwRet;
762 /**************************************************************************
763 * midiInPrepareHeader [WINMM.@]
765 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
766 MIDIHDR* lpMidiInHdr, UINT uSize)
768 LPWINE_MLD wmld;
770 TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
772 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
773 return MMSYSERR_INVALHANDLE;
775 return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
778 /**************************************************************************
779 * midiInUnprepareHeader [WINMM.@]
781 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
782 MIDIHDR* lpMidiInHdr, UINT uSize)
784 LPWINE_MLD wmld;
786 TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
788 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
789 return MMSYSERR_INVALHANDLE;
791 return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
794 /**************************************************************************
795 * midiInAddBuffer [WINMM.@]
797 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
798 MIDIHDR* lpMidiInHdr, UINT uSize)
800 LPWINE_MLD wmld;
802 TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
804 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
805 return MMSYSERR_INVALHANDLE;
807 return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize);
810 /**************************************************************************
811 * midiInStart [WINMM.@]
813 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
815 LPWINE_MLD wmld;
817 TRACE("(%p)\n", hMidiIn);
819 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
820 return MMSYSERR_INVALHANDLE;
822 return MMDRV_Message(wmld, MIDM_START, 0L, 0L);
825 /**************************************************************************
826 * midiInStop [WINMM.@]
828 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
830 LPWINE_MLD wmld;
832 TRACE("(%p)\n", hMidiIn);
834 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
835 return MMSYSERR_INVALHANDLE;
837 return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L);
840 /**************************************************************************
841 * midiInReset [WINMM.@]
843 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
845 LPWINE_MLD wmld;
847 TRACE("(%p)\n", hMidiIn);
849 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
850 return MMSYSERR_INVALHANDLE;
852 return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L);
855 /**************************************************************************
856 * midiInGetID [WINMM.@]
858 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
860 LPWINE_MLD wmld;
862 TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
864 if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
866 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
867 return MMSYSERR_INVALHANDLE;
869 *lpuDeviceID = wmld->uDeviceID;
871 return MMSYSERR_NOERROR;
874 /**************************************************************************
875 * midiInMessage [WINMM.@]
877 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
878 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
880 LPWINE_MLD wmld;
882 TRACE("(%p, %04X, %08IX, %08IX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
884 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
885 return MMSYSERR_INVALHANDLE;
887 switch (uMessage) {
888 case MIDM_OPEN:
889 case MIDM_CLOSE:
890 FIXME("can't handle OPEN or CLOSE message!\n");
891 return MMSYSERR_NOTSUPPORTED;
893 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
896 /**************************************************************************
897 * midiConnect [WINMM.@]
899 MMRESULT WINAPI midiConnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
901 FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
902 return MMSYSERR_ERROR;
905 /**************************************************************************
906 * midiDisconnect [WINMM.@]
908 MMRESULT WINAPI midiDisconnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
910 FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
911 return MMSYSERR_ERROR;
914 typedef struct WINE_MIDIStream {
915 HMIDIOUT hDevice;
916 HANDLE hThread;
917 DWORD dwThreadID;
918 CRITICAL_SECTION lock;
919 DWORD dwTempo;
920 DWORD dwTimeDiv;
921 ULONGLONG position_usec;
922 DWORD dwPulses;
923 DWORD dwStartTicks;
924 DWORD dwElapsedMS;
925 DWORD dwLastPositionMS;
926 WORD wFlags;
927 WORD status;
928 HANDLE hEvent;
929 LPMIDIHDR lpMidiHdr;
930 DWORD dwStreamID;
931 struct wine_rb_entry entry;
932 } WINE_MIDIStream;
934 #define WINE_MSM_HEADER (WM_USER+0)
935 #define WINE_MSM_STOP (WM_USER+1)
936 #define WINE_MSM_PAUSE (WM_USER+2)
937 #define WINE_MSM_RESUME (WM_USER+3)
939 #define MSM_STATUS_STOPPED WINE_MSM_STOP
940 #define MSM_STATUS_PAUSED WINE_MSM_PAUSE
941 #define MSM_STATUS_PLAYING WINE_MSM_RESUME
943 static int wine_midi_stream_compare(const void *key, const struct wine_rb_entry *entry)
945 WINE_MIDIStream *stream = WINE_RB_ENTRY_VALUE(entry, struct WINE_MIDIStream, entry);
946 return memcmp(key, &stream->dwStreamID, sizeof(stream->dwStreamID));
949 static WINE_MIDIStream *wine_midi_stream_allocate(void)
951 DWORD stream_id = 1;
952 WINE_MIDIStream *stream = NULL;
954 EnterCriticalSection(&WINMM_cs);
956 while (stream_id < 0xFFFFFFFF && wine_rb_get(&wine_midi_streams, &stream_id))
957 stream_id++;
959 if (stream_id < 0xFFFFFFFF &&
960 (stream = malloc(sizeof(WINE_MIDIStream))))
962 stream->dwStreamID = stream_id;
963 wine_rb_put(&wine_midi_streams, &stream_id, &stream->entry);
966 LeaveCriticalSection(&WINMM_cs);
967 return stream;
970 static void wine_midi_stream_free(WINE_MIDIStream *stream)
972 EnterCriticalSection(&WINMM_cs);
974 wine_rb_remove(&wine_midi_streams, &stream->entry);
975 free(stream);
977 LeaveCriticalSection(&WINMM_cs);
980 /**************************************************************************
981 * MMSYSTEM_GetMidiStream [internal]
983 static BOOL MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
985 WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
986 struct wine_rb_entry *entry;
988 if (lplpwm)
989 *lplpwm = lpwm;
991 if (lpwm == NULL) {
992 return FALSE;
995 EnterCriticalSection(&WINMM_cs);
996 if ((entry = wine_rb_get(&wine_midi_streams, &lpwm->mod.rgIds.dwStreamID)))
997 *lpMidiStrm = WINE_RB_ENTRY_VALUE(entry, struct WINE_MIDIStream, entry);
998 LeaveCriticalSection(&WINMM_cs);
1000 return *lpMidiStrm != NULL;
1003 /**************************************************************************
1004 * MMSYSTEM_MidiStream_Convert [internal]
1006 static DWORD MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
1008 DWORD ret = 0;
1010 if (lpMidiStrm->dwTimeDiv == 0) {
1011 FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
1012 } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
1013 int nf = 256 - HIBYTE(lpMidiStrm->dwTimeDiv); /* number of frames */
1014 int nsf = LOBYTE(lpMidiStrm->dwTimeDiv); /* number of sub-frames */
1015 ret = (pulse * 1000000) / (nf * nsf);
1016 } else {
1017 ret = (DWORD)((double)pulse * (double)lpMidiStrm->dwTempo /
1018 (double)lpMidiStrm->dwTimeDiv);
1021 return ret; /* in microseconds */
1024 static DWORD midistream_get_playing_position(WINE_MIDIStream* lpMidiStrm)
1026 switch (lpMidiStrm->status) {
1027 case MSM_STATUS_STOPPED:
1028 case MSM_STATUS_PAUSED:
1029 return lpMidiStrm->dwElapsedMS;
1030 case MSM_STATUS_PLAYING:
1031 return timeGetTime() - lpMidiStrm->dwStartTicks;
1032 default:
1033 FIXME("Unknown playing status %hu\n", lpMidiStrm->status);
1034 return 0;
1038 /**************************************************************************
1039 * MMSYSTEM_MidiStream_MessageHandler [internal]
1041 static BOOL MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1043 LPMIDIHDR lpMidiHdr;
1044 LPMIDIHDR* lpmh;
1045 LPBYTE lpData;
1047 for (;;) {
1048 switch (msg->message) {
1049 case WM_QUIT:
1050 return FALSE;
1051 case WINE_MSM_STOP:
1052 TRACE("STOP\n");
1053 EnterCriticalSection(&lpMidiStrm->lock);
1054 lpMidiStrm->status = MSM_STATUS_STOPPED;
1055 lpMidiStrm->dwPulses = 0;
1056 lpMidiStrm->dwElapsedMS = 0;
1057 lpMidiStrm->position_usec = 0;
1058 lpMidiStrm->dwLastPositionMS = 0;
1059 LeaveCriticalSection(&lpMidiStrm->lock);
1060 /* this is not quite what MS doc says... */
1061 midiOutReset(lpMidiStrm->hDevice);
1062 /* empty list of already submitted buffers */
1063 lpMidiHdr = lpMidiStrm->lpMidiHdr;
1064 lpMidiStrm->lpMidiHdr = NULL;
1065 while (lpMidiHdr) {
1066 LPMIDIHDR lphdr = lpMidiHdr;
1067 lpMidiHdr = lpMidiHdr->lpNext;
1068 lphdr->dwFlags |= MHDR_DONE;
1069 lphdr->dwFlags &= ~MHDR_INQUEUE;
1071 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1072 (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1073 lpwm->mod.dwInstance, (DWORD_PTR)lphdr, 0);
1075 SetEvent((HANDLE)msg->wParam);
1076 return TRUE;
1077 case WINE_MSM_RESUME:
1078 /* FIXME: send out cc64 0 (turn off sustain pedal) on every channel */
1079 if (lpMidiStrm->status != MSM_STATUS_PLAYING) {
1080 EnterCriticalSection(&lpMidiStrm->lock);
1081 lpMidiStrm->dwStartTicks = timeGetTime() - lpMidiStrm->dwElapsedMS;
1082 lpMidiStrm->status = MSM_STATUS_PLAYING;
1083 LeaveCriticalSection(&lpMidiStrm->lock);
1085 SetEvent((HANDLE)msg->wParam);
1086 return TRUE;
1087 case WINE_MSM_PAUSE:
1088 /* FIXME: send out cc64 0 (turn off sustain pedal) on every channel */
1089 if (lpMidiStrm->status != MSM_STATUS_PAUSED) {
1090 EnterCriticalSection(&lpMidiStrm->lock);
1091 lpMidiStrm->dwElapsedMS = timeGetTime() - lpMidiStrm->dwStartTicks;
1092 lpMidiStrm->status = MSM_STATUS_PAUSED;
1093 LeaveCriticalSection(&lpMidiStrm->lock);
1095 SetEvent((HANDLE)msg->wParam);
1096 break;
1097 /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1098 * by native mcimidi, it doesn't look like a correct one".
1099 * this trick allows us to throw it away... but I don't like it.
1100 * It looks like part of the file I'm trying to play and definitively looks
1101 * like raw midi content.
1102 * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1103 * synchronization issue where native mcimidi is still processing raw MIDI
1104 * content before generating MIDIEVENTs ?
1106 * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1107 * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1108 * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1109 * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1110 * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1111 * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1112 * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1113 * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1114 * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1115 * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1116 * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1117 * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1118 * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1119 * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1120 * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1121 * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1122 * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1124 case WINE_MSM_HEADER:
1125 /* sets initial tick count for first MIDIHDR */
1126 if (!lpMidiStrm->dwStartTicks)
1127 lpMidiStrm->dwStartTicks = timeGetTime();
1128 lpMidiHdr = (LPMIDIHDR)msg->lParam;
1129 lpData = (LPBYTE)lpMidiHdr->lpData;
1130 TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBytesRecorded=%lu/%lu dwFlags=0x%08lx size=%Iu]\n",
1131 (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1132 lpData, lpMidiHdr->dwBytesRecorded, lpMidiHdr->dwBufferLength,
1133 lpMidiHdr->dwFlags, msg->wParam);
1134 #if 0
1135 /* dumps content of lpMidiHdr->lpData
1136 * FIXME: there should be a debug routine somewhere that already does this
1138 for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1139 DWORD i;
1140 BYTE ch;
1142 for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1143 printf("%02x ", lpData[dwToGo + i]);
1144 for (; i < 16; i++)
1145 printf(" ");
1146 for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1147 ch = lpData[dwToGo + i];
1148 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1150 printf("\n");
1152 #endif
1153 if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1154 ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1155 ((LPMIDIEVENT)lpData)->dwStreamID != lpMidiStrm->dwStreamID) {
1156 FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",
1157 (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1158 ((LPMIDIEVENT)lpData)->dwStreamID);
1159 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1160 lpMidiHdr->dwFlags |= MHDR_DONE;
1162 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1163 (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1164 lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1165 break;
1168 lpMidiHdr->lpNext = 0;
1169 for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = &(*lpmh)->lpNext);
1170 *lpmh = lpMidiHdr;
1171 break;
1172 default:
1173 FIXME("Unknown message %d\n", msg->message);
1174 break;
1176 if (lpMidiStrm->status != MSM_STATUS_PAUSED)
1177 return TRUE;
1178 GetMessageA(msg, 0, 0, 0);
1182 /**************************************************************************
1183 * MMSYSTEM_MidiStream_Player [internal]
1185 static DWORD CALLBACK MMSYSTEM_MidiStream_Player(LPVOID pmt)
1187 WINE_MIDIStream* lpMidiStrm = pmt;
1188 WINE_MIDI* lpwm;
1189 MSG msg;
1190 DWORD dwToGo;
1191 DWORD dwCurrTC;
1192 LPMIDIHDR lpMidiHdr;
1193 DWORD dwOffset;
1195 TRACE("(%p)!\n", lpMidiStrm);
1197 if (!lpMidiStrm ||
1198 (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1199 goto the_end;
1201 /* force thread's queue creation */
1202 PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
1204 lpMidiStrm->dwStartTicks = 0;
1205 lpMidiStrm->dwPulses = 0;
1207 lpMidiStrm->lpMidiHdr = 0;
1209 /* midiStreamOpen is waiting for ack */
1210 SetEvent(lpMidiStrm->hEvent);
1212 start_header:
1213 lpMidiHdr = lpMidiStrm->lpMidiHdr;
1214 if (!lpMidiHdr) {
1215 /* for first message, block until one arrives, then process all that are available */
1216 GetMessageA(&msg, 0, 0, 0);
1217 do {
1218 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1219 goto the_end;
1220 } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
1221 goto start_header;
1224 dwOffset = 0;
1225 while (dwOffset + offsetof(MIDIEVENT,dwParms) <= lpMidiHdr->dwBytesRecorded) {
1226 LPMIDIEVENT me = (LPMIDIEVENT)(lpMidiHdr->lpData+dwOffset);
1228 /* do we have to wait ? */
1229 if (me->dwDeltaTime) {
1230 EnterCriticalSection(&lpMidiStrm->lock);
1231 lpMidiStrm->position_usec += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1232 LeaveCriticalSection(&lpMidiStrm->lock);
1234 dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->position_usec / 1000;
1236 TRACE("%lu/%lu/%lu\n", dwToGo, timeGetTime(), me->dwDeltaTime);
1237 while (dwToGo - (dwCurrTC = timeGetTime()) <= MAXLONG) {
1238 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1239 /* got a message, handle it */
1240 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1241 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1242 goto the_end;
1243 /* is lpMidiHdr still current? */
1244 if (lpMidiHdr != lpMidiStrm->lpMidiHdr) {
1245 goto start_header;
1248 /* reset dwToGo because dwStartTicks might be updated */
1249 dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->position_usec / 1000;
1250 } else {
1251 /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1252 break;
1255 EnterCriticalSection(&lpMidiStrm->lock);
1256 lpMidiStrm->dwPulses += me->dwDeltaTime;
1257 lpMidiStrm->dwLastPositionMS = midistream_get_playing_position(lpMidiStrm);
1258 LeaveCriticalSection(&lpMidiStrm->lock);
1260 switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1261 case MEVT_COMMENT:
1262 FIXME("NIY: MEVT_COMMENT\n");
1263 /* do nothing, skip bytes */
1264 break;
1265 case MEVT_LONGMSG:
1267 MIDIHDR mh;
1268 memset(&mh, 0, sizeof(mh));
1269 mh.lpData = (LPSTR)me->dwParms;
1270 mh.dwBufferLength = MEVT_EVENTPARM(me->dwEvent);
1271 midiOutPrepareHeader(lpMidiStrm->hDevice, &mh, sizeof(mh));
1272 midiOutLongMsg(lpMidiStrm->hDevice, &mh, sizeof(mh));
1273 midiOutUnprepareHeader(lpMidiStrm->hDevice, &mh, sizeof(mh));
1274 break;
1276 case MEVT_NOP:
1277 break;
1278 case MEVT_SHORTMSG:
1279 midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1280 break;
1281 case MEVT_TEMPO:
1282 EnterCriticalSection(&lpMidiStrm->lock);
1283 lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1284 LeaveCriticalSection(&lpMidiStrm->lock);
1285 break;
1286 case MEVT_VERSION:
1287 break;
1288 default:
1289 FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1290 break;
1292 if (me->dwEvent & MEVT_F_CALLBACK) {
1293 /* native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
1294 lpMidiHdr->dwOffset = dwOffset;
1295 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1296 (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1297 lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1299 dwOffset += offsetof(MIDIEVENT,dwParms);
1300 if (me->dwEvent & MEVT_F_LONG)
1301 dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1303 /* done with this header */
1304 lpMidiStrm->lpMidiHdr = lpMidiHdr->lpNext;
1305 lpMidiHdr->dwFlags |= MHDR_DONE;
1306 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1308 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1309 (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1310 lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1311 goto start_header;
1313 the_end:
1314 TRACE("End of thread\n");
1315 return 0;
1318 /**************************************************************************
1319 * midiStreamClose [WINMM.@]
1321 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1323 WINE_MIDI* lpwm;
1324 WINE_MIDIStream* lpMidiStrm;
1325 MMRESULT ret = 0;
1327 TRACE("(%p)!\n", hMidiStrm);
1329 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, &lpwm))
1330 return MMSYSERR_INVALHANDLE;
1332 midiStreamStop(hMidiStrm);
1333 PostThreadMessageA(lpMidiStrm->dwThreadID, WM_QUIT, 0, 0);
1334 CloseHandle(lpMidiStrm->hEvent);
1335 if (lpMidiStrm->hThread) {
1336 if (GetCurrentThreadId() != lpMidiStrm->dwThreadID)
1337 WaitForSingleObject(lpMidiStrm->hThread, INFINITE);
1338 else {
1339 FIXME("leak from call within function callback\n");
1340 ret = MMSYSERR_HANDLEBUSY; /* yet don't signal it to app */
1342 CloseHandle(lpMidiStrm->hThread);
1344 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1345 (HDRVR)lpMidiStrm->hDevice, MM_MOM_CLOSE,
1346 lpwm->mod.dwInstance, 0, 0);
1347 if(!ret) {
1348 lpMidiStrm->lock.DebugInfo->Spare[0] = 0;
1349 DeleteCriticalSection(&lpMidiStrm->lock);
1350 wine_midi_stream_free(lpMidiStrm);
1353 return midiOutClose((HMIDIOUT)hMidiStrm);
1356 /**************************************************************************
1357 * midiStreamOpen [WINMM.@]
1359 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1360 DWORD cMidi, DWORD_PTR dwCallback,
1361 DWORD_PTR dwInstance, DWORD fdwOpen)
1363 WINE_MIDIStream* lpMidiStrm;
1364 MMRESULT ret;
1365 MIDIOPENSTRMID mosm;
1366 LPWINE_MIDI lpwm;
1367 HMIDIOUT hMidiOut;
1369 TRACE("(%p, %p, %ld, 0x%08Ix, 0x%08Ix, 0x%08lx)!\n",
1370 lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1372 if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1373 return MMSYSERR_INVALPARAM;
1375 ret = WINMM_CheckCallback(dwCallback, fdwOpen, FALSE);
1376 if (ret != MMSYSERR_NOERROR)
1377 return ret;
1379 lpMidiStrm = wine_midi_stream_allocate();
1380 if (!lpMidiStrm)
1381 return MMSYSERR_NOMEM;
1383 lpMidiStrm->dwTempo = 500000; /* microseconds per quarter note, i.e. 120 BPM */
1384 lpMidiStrm->dwTimeDiv = 24; /* ticks per quarter note */
1385 lpMidiStrm->position_usec = 0;
1386 lpMidiStrm->dwLastPositionMS = 0;
1387 lpMidiStrm->status = MSM_STATUS_PAUSED;
1388 lpMidiStrm->dwElapsedMS = 0;
1390 mosm.dwStreamID = lpMidiStrm->dwStreamID;
1391 /* FIXME: the correct value is not allocated yet for MAPPER */
1392 mosm.wDeviceID = *lpuDeviceID;
1393 lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm);
1394 if (!lpwm) {
1395 free(lpMidiStrm);
1396 return MMSYSERR_NOMEM;
1398 lpMidiStrm->hDevice = hMidiOut;
1399 *lphMidiStrm = (HMIDISTRM)hMidiOut;
1401 lpwm->mld.uDeviceID = *lpuDeviceID;
1403 /* don't rely on midiOut callbacks */
1404 ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD_PTR)&lpwm->mod, CALLBACK_NULL);
1405 if (ret != MMSYSERR_NOERROR) {
1406 MMDRV_Free(hMidiOut, &lpwm->mld);
1407 free(lpMidiStrm);
1408 return ret;
1411 InitializeCriticalSectionEx(&lpMidiStrm->lock, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
1412 lpMidiStrm->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_MidiStream.lock");
1414 lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1415 lpMidiStrm->wFlags = HIWORD(fdwOpen);
1417 lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1418 lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1420 if (!lpMidiStrm->hThread) {
1421 midiStreamClose((HMIDISTRM)hMidiOut);
1422 return MMSYSERR_NOMEM;
1424 SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
1426 /* wait for thread to have started, and for its queue to be created */
1427 WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1429 TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1430 *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1432 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1433 (HDRVR)lpMidiStrm->hDevice, MM_MOM_OPEN,
1434 lpwm->mod.dwInstance, 0, 0);
1435 return ret;
1438 /**************************************************************************
1439 * midiStreamOut [WINMM.@]
1441 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
1442 UINT cbMidiHdr)
1444 WINE_MIDIStream* lpMidiStrm;
1445 DWORD ret = MMSYSERR_NOERROR;
1447 TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
1449 if (cbMidiHdr < offsetof(MIDIHDR,dwOffset) || !lpMidiHdr || !lpMidiHdr->lpData
1450 || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded
1451 || lpMidiHdr->dwBytesRecorded % 4 /* player expects DWORD padding */)
1452 return MMSYSERR_INVALPARAM;
1453 /* FIXME: Native additionally checks if the MIDIEVENTs in lpData
1454 * exactly fit dwBytesRecorded. */
1456 if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1457 return MIDIERR_UNPREPARED;
1459 if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1460 return MIDIERR_STILLPLAYING;
1462 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1463 ret = MMSYSERR_INVALHANDLE;
1464 } else {
1465 lpMidiHdr->dwFlags |= MHDR_ISSTRM | MHDR_INQUEUE;
1466 lpMidiHdr->dwFlags &= ~MHDR_DONE;
1467 if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
1468 WINE_MSM_HEADER, cbMidiHdr,
1469 (LPARAM)lpMidiHdr)) {
1470 ERR("bad PostThreadMessageA\n");
1471 ret = MMSYSERR_ERROR;
1474 return ret;
1477 static MMRESULT midistream_post_message_and_wait(WINE_MIDIStream* lpMidiStrm, UINT msg, LPARAM lParam)
1479 HANDLE hObjects[2];
1481 hObjects[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
1482 if (!hObjects[0])
1483 return MMSYSERR_ERROR;
1485 if (!PostThreadMessageA(lpMidiStrm->dwThreadID, msg, (WPARAM)hObjects[0], lParam)) {
1486 WARN("bad PostThreadMessage\n");
1487 CloseHandle(hObjects[0]);
1488 return MMSYSERR_ERROR;
1491 if (GetCurrentThreadId() != lpMidiStrm->dwThreadID) {
1492 DWORD ret;
1493 hObjects[1] = lpMidiStrm->hThread;
1494 ret = WaitForMultipleObjects(ARRAY_SIZE(hObjects), hObjects, FALSE, INFINITE);
1495 if (ret != WAIT_OBJECT_0) {
1496 CloseHandle(hObjects[0]);
1497 WARN("bad WaitForSingleObject (%lu)\n", ret);
1498 return MMSYSERR_ERROR;
1502 CloseHandle(hObjects[0]);
1504 return MMSYSERR_NOERROR;
1507 /**************************************************************************
1508 * midiStreamPause [WINMM.@]
1510 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
1512 WINE_MIDIStream* lpMidiStrm;
1514 TRACE("(%p)!\n", hMidiStrm);
1516 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1517 return MMSYSERR_INVALHANDLE;
1518 return midistream_post_message_and_wait(lpMidiStrm, WINE_MSM_PAUSE, 0);
1521 static DWORD midistream_get_current_pulse(WINE_MIDIStream* lpMidiStrm)
1523 DWORD pulses = 0;
1524 DWORD now = midistream_get_playing_position(lpMidiStrm);
1525 DWORD delta = now - lpMidiStrm->dwLastPositionMS;
1526 if (lpMidiStrm->dwTimeDiv > 0x8000) {
1527 /* SMPTE, unchecked FIXME */
1528 BYTE nf = 256 - HIBYTE(lpMidiStrm->dwTimeDiv);
1529 BYTE nsf = LOBYTE(lpMidiStrm->dwTimeDiv);
1530 pulses = (delta * nf * nsf) / 1000;
1532 else if (lpMidiStrm->dwTimeDiv) {
1533 pulses = (DWORD)((double)(delta * lpMidiStrm->dwTimeDiv) *
1534 1000.0 / (double)lpMidiStrm->dwTempo);
1536 return lpMidiStrm->dwPulses + pulses;
1539 /**************************************************************************
1540 * midiStreamPosition [WINMM.@]
1542 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
1544 WINE_MIDIStream* lpMidiStrm;
1545 DWORD ret = MMSYSERR_NOERROR;
1547 TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
1549 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1550 ret = MMSYSERR_INVALHANDLE;
1551 } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
1552 ret = MMSYSERR_INVALPARAM;
1553 } else {
1554 EnterCriticalSection(&lpMidiStrm->lock);
1555 switch (lpMMT->wType) {
1556 case TIME_MIDI:
1557 if (lpMidiStrm->dwTimeDiv < 0x8000) {
1558 DWORD tdiv, pulses;
1559 tdiv = (lpMidiStrm->dwTimeDiv > 24) ? lpMidiStrm->dwTimeDiv : 24;
1560 pulses = midistream_get_current_pulse(lpMidiStrm);
1561 lpMMT->u.midi.songptrpos = (pulses + tdiv/8) / (tdiv/4);
1562 if (!lpMMT->u.midi.songptrpos && pulses) lpMMT->u.midi.songptrpos++;
1563 TRACE("=> song position %ld (pulses %lu, tdiv %lu)\n", lpMMT->u.midi.songptrpos, pulses, tdiv);
1564 break;
1566 /* fall through */
1567 case TIME_BYTES:
1568 case TIME_SAMPLES:
1569 lpMMT->wType = TIME_MS;
1570 /* fall through to alternative format */
1571 case TIME_MS:
1572 lpMMT->u.ms = midistream_get_playing_position(lpMidiStrm);
1573 TRACE("=> %ld ms\n", lpMMT->u.ms);
1574 break;
1575 case TIME_TICKS:
1576 lpMMT->u.ticks = midistream_get_current_pulse(lpMidiStrm);
1577 TRACE("=> %ld ticks\n", lpMMT->u.ticks);
1578 break;
1579 default:
1580 FIXME("Unsupported time type %x\n", lpMMT->wType);
1581 /* use TIME_MS instead */
1582 lpMMT->wType = TIME_MS;
1583 lpMMT->u.ms = midistream_get_playing_position(lpMidiStrm);
1584 TRACE("=> %ld ms\n", lpMMT->u.ms);
1585 break;
1587 LeaveCriticalSection(&lpMidiStrm->lock);
1589 return ret;
1592 /**************************************************************************
1593 * midiStreamProperty [WINMM.@]
1595 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
1597 WINE_MIDIStream* lpMidiStrm;
1598 MMRESULT ret = MMSYSERR_NOERROR;
1600 TRACE("(%p, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);
1602 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1603 ret = MMSYSERR_INVALHANDLE;
1604 } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
1605 ret = MMSYSERR_INVALPARAM;
1606 } else if (dwProperty & MIDIPROP_TEMPO) {
1607 MIDIPROPTEMPO* mpt = (MIDIPROPTEMPO*)lpPropData;
1609 EnterCriticalSection(&lpMidiStrm->lock);
1610 if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
1611 ret = MMSYSERR_INVALPARAM;
1612 } else if (dwProperty & MIDIPROP_SET) {
1613 lpMidiStrm->dwTempo = mpt->dwTempo;
1614 TRACE("Setting tempo to %ld\n", mpt->dwTempo);
1615 } else if (dwProperty & MIDIPROP_GET) {
1616 mpt->dwTempo = lpMidiStrm->dwTempo;
1617 TRACE("Getting tempo <= %ld\n", mpt->dwTempo);
1619 LeaveCriticalSection(&lpMidiStrm->lock);
1620 } else if (dwProperty & MIDIPROP_TIMEDIV) {
1621 MIDIPROPTIMEDIV* mptd = (MIDIPROPTIMEDIV*)lpPropData;
1623 if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
1624 ret = MMSYSERR_INVALPARAM;
1625 } else if (dwProperty & MIDIPROP_SET) {
1626 EnterCriticalSection(&lpMidiStrm->lock);
1627 if (lpMidiStrm->status != MSM_STATUS_PLAYING) {
1628 lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
1629 TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);
1631 else
1632 ret = MMSYSERR_INVALPARAM;
1633 LeaveCriticalSection(&lpMidiStrm->lock);
1634 } else if (dwProperty & MIDIPROP_GET) {
1635 mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
1636 TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);
1638 } else {
1639 ret = MMSYSERR_INVALPARAM;
1642 return ret;
1645 /**************************************************************************
1646 * midiStreamRestart [WINMM.@]
1648 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
1650 WINE_MIDIStream* lpMidiStrm;
1652 TRACE("(%p)!\n", hMidiStrm);
1654 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1655 return MMSYSERR_INVALHANDLE;
1656 return midistream_post_message_and_wait(lpMidiStrm, WINE_MSM_RESUME, 0);
1659 /**************************************************************************
1660 * midiStreamStop [WINMM.@]
1662 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
1664 WINE_MIDIStream* lpMidiStrm;
1666 TRACE("(%p)!\n", hMidiStrm);
1668 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1669 return MMSYSERR_INVALHANDLE;
1670 return midistream_post_message_and_wait(lpMidiStrm, WINE_MSM_STOP, 0);
1673 struct mm_starter
1675 LPTASKCALLBACK cb;
1676 DWORD client;
1677 HANDLE event;
1680 static DWORD WINAPI mmTaskRun(void* pmt)
1682 struct mm_starter mms;
1684 memcpy(&mms, pmt, sizeof(struct mm_starter));
1685 free(pmt);
1686 mms.cb(mms.client);
1687 if (mms.event) SetEvent(mms.event);
1688 return 0;
1691 /******************************************************************
1692 * mmTaskCreate (WINMM.@)
1694 UINT WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR client)
1696 HANDLE hThread;
1697 HANDLE hEvent = 0;
1698 struct mm_starter *mms;
1700 mms = malloc(sizeof(struct mm_starter));
1701 if (mms == NULL) return TASKERR_OUTOFMEMORY;
1703 mms->cb = cb;
1704 mms->client = client;
1705 if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1706 mms->event = hEvent;
1708 hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
1709 if (!hThread) {
1710 free(mms);
1711 if (hEvent) CloseHandle(hEvent);
1712 return TASKERR_OUTOFMEMORY;
1714 SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
1715 if (ph) *ph = hEvent;
1716 CloseHandle(hThread);
1717 return 0;
1720 /******************************************************************
1721 * mmTaskBlock (WINMM.@)
1723 VOID WINAPI mmTaskBlock(DWORD tid)
1725 MSG msg;
1729 GetMessageA(&msg, 0, 0, 0);
1730 if (msg.hwnd) DispatchMessageA(&msg);
1731 } while (msg.message != WM_USER);
1734 /******************************************************************
1735 * mmTaskSignal (WINMM.@)
1737 BOOL WINAPI mmTaskSignal(DWORD tid)
1739 return PostThreadMessageW(tid, WM_USER, 0, 0);
1742 /******************************************************************
1743 * mmTaskYield (WINMM.@)
1745 VOID WINAPI mmTaskYield(VOID) {}
1747 /******************************************************************
1748 * mmGetCurrentTask (WINMM.@)
1750 DWORD WINAPI mmGetCurrentTask(VOID)
1752 return GetCurrentThreadId();