winmm: Remove driver implementation of waveIn*.
[wine] / dlls / winmm / winmm.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * WINMM functions
5  *
6  * Copyright 1993      Martin Ayotte
7  *           1998-2002 Eric Pouech
8  *
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.
13  *
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.
18  *
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
22  */
23
24 /*
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
29  */
30
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
40  */
41
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <string.h>
45
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
48 #include "windef.h"
49 #include "winbase.h"
50 #include "mmsystem.h"
51 #include "winuser.h"
52 #include "winnls.h"
53 #include "winternl.h"
54 #include "winemm.h"
55
56 #include "wine/debug.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
59
60 /* ========================================================================
61  *                   G L O B A L   S E T T I N G S
62  * ========================================================================*/
63
64 HINSTANCE hWinMM32Instance;
65 HANDLE psLastEvent;
66
67 static CRITICAL_SECTION_DEBUG critsect_debug =
68 {
69     0, 0, &WINMM_cs,
70     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
71       0, 0, { (DWORD_PTR)(__FILE__ ": WINMM_cs") }
72 };
73 CRITICAL_SECTION WINMM_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
74
75 /**************************************************************************
76  *                      WINMM_CreateIData                       [internal]
77  */
78 static  BOOL    WINMM_CreateIData(HINSTANCE hInstDLL)
79 {
80     hWinMM32Instance = hInstDLL;
81     psLastEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
82     return WINMM_InitWaveform();
83 }
84
85 /**************************************************************************
86  *                      WINMM_DeleteIData                       [internal]
87  */
88 static  void WINMM_DeleteIData(void)
89 {
90     TIME_MMTimeStop();
91
92     /* FIXME: should also free content and resources allocated
93      * inside WINMM_IData */
94     CloseHandle(psLastEvent);
95     DeleteCriticalSection(&WINMM_cs);
96 }
97
98 /******************************************************************
99  *             WINMM_ErrorToString
100  */
101 const char* WINMM_ErrorToString(MMRESULT error)
102 {
103 #define ERR_TO_STR(dev) case dev: return #dev
104     switch (error) {
105     ERR_TO_STR(MMSYSERR_NOERROR);
106     ERR_TO_STR(MMSYSERR_ERROR);
107     ERR_TO_STR(MMSYSERR_BADDEVICEID);
108     ERR_TO_STR(MMSYSERR_NOTENABLED);
109     ERR_TO_STR(MMSYSERR_ALLOCATED);
110     ERR_TO_STR(MMSYSERR_INVALHANDLE);
111     ERR_TO_STR(MMSYSERR_NODRIVER);
112     ERR_TO_STR(MMSYSERR_NOMEM);
113     ERR_TO_STR(MMSYSERR_NOTSUPPORTED);
114     ERR_TO_STR(MMSYSERR_BADERRNUM);
115     ERR_TO_STR(MMSYSERR_INVALFLAG);
116     ERR_TO_STR(MMSYSERR_INVALPARAM);
117     ERR_TO_STR(MMSYSERR_HANDLEBUSY);
118     ERR_TO_STR(MMSYSERR_INVALIDALIAS);
119     ERR_TO_STR(MMSYSERR_BADDB);
120     ERR_TO_STR(MMSYSERR_KEYNOTFOUND);
121     ERR_TO_STR(MMSYSERR_READERROR);
122     ERR_TO_STR(MMSYSERR_WRITEERROR);
123     ERR_TO_STR(MMSYSERR_DELETEERROR);
124     ERR_TO_STR(MMSYSERR_VALNOTFOUND);
125     ERR_TO_STR(MMSYSERR_NODRIVERCB);
126     ERR_TO_STR(WAVERR_BADFORMAT);
127     ERR_TO_STR(WAVERR_STILLPLAYING);
128     ERR_TO_STR(WAVERR_UNPREPARED);
129     ERR_TO_STR(WAVERR_SYNC);
130     ERR_TO_STR(MIDIERR_INVALIDSETUP);
131     ERR_TO_STR(MIDIERR_NODEVICE);
132     ERR_TO_STR(MIDIERR_STILLPLAYING);
133     ERR_TO_STR(MIDIERR_UNPREPARED);
134     }
135 #undef ERR_TO_STR
136     return wine_dbg_sprintf("Unknown(0x%08x)", error);
137 }
138
139 /**************************************************************************
140  *              DllMain (WINMM.init)
141  *
142  * WINMM DLL entry point
143  *
144  */
145 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
146 {
147     TRACE("%p 0x%x %p\n", hInstDLL, fdwReason, fImpLoad);
148
149     switch (fdwReason) {
150     case DLL_PROCESS_ATTACH:
151         DisableThreadLibraryCalls(hInstDLL);
152
153         if (!WINMM_CreateIData(hInstDLL))
154             return FALSE;
155         break;
156     case DLL_PROCESS_DETACH:
157         /* close all opened MCI drivers */
158         MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L);
159         MMDRV_Exit();
160         /* There's no guarantee the drivers haven't already been unloaded on
161          * process shutdown.
162          */
163         if (!fImpLoad)
164         {
165             /* now unload all remaining drivers... */
166             DRIVER_UnloadAll();
167         }
168
169         WINMM_DeleteIData();
170         break;
171     }
172     return TRUE;
173 }
174
175 /**************************************************************************
176  *                      WINMM_CheckCallback                     [internal]
177  */
178 MMRESULT WINMM_CheckCallback(DWORD_PTR dwCallback, DWORD fdwOpen, BOOL mixer)
179 {
180     switch (fdwOpen & CALLBACK_TYPEMASK) {
181     case CALLBACK_NULL:     /* dwCallback need not be NULL */
182         break;
183     case CALLBACK_WINDOW:
184         if (dwCallback && !IsWindow((HWND)dwCallback))
185             return MMSYSERR_INVALPARAM;
186         break;
187
188     case CALLBACK_FUNCTION:
189         /* a NULL cb is acceptable since w2k, MMSYSERR_INVALPARAM earlier */
190         if (mixer)
191             return MMSYSERR_INVALFLAG; /* since w2k, MMSYSERR_NOTSUPPORTED earlier */
192         break;
193     case CALLBACK_THREAD:
194     case CALLBACK_EVENT:
195         if (mixer) /* FIXME: mixer supports THREAD+EVENT since w2k */
196             return MMSYSERR_NOTSUPPORTED; /* w9X */
197         break;
198     default:
199         WARN("Unknown callback type %d\n", HIWORD(fdwOpen));
200     }
201     return MMSYSERR_NOERROR;
202 }
203
204 /**************************************************************************
205  *                              auxGetNumDevs           [WINMM.@]
206  */
207 UINT WINAPI auxGetNumDevs(void)
208 {
209     return MMDRV_GetNum(MMDRV_AUX);
210 }
211
212 /**************************************************************************
213  *                              auxGetDevCapsW          [WINMM.@]
214  */
215 UINT WINAPI auxGetDevCapsW(UINT_PTR uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
216 {
217     LPWINE_MLD          wmld;
218
219     TRACE("(%04lX, %p, %d) !\n", uDeviceID, lpCaps, uSize);
220
221     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
222
223     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
224         return MMSYSERR_BADDEVICEID;
225     return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
226 }
227
228 /**************************************************************************
229  *                              auxGetDevCapsA          [WINMM.@]
230  */
231 UINT WINAPI auxGetDevCapsA(UINT_PTR uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
232 {
233     AUXCAPSW    acW;
234     UINT        ret;
235
236     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
237
238     ret = auxGetDevCapsW(uDeviceID, &acW, sizeof(acW));
239
240     if (ret == MMSYSERR_NOERROR) {
241         AUXCAPSA acA;
242         acA.wMid           = acW.wMid;
243         acA.wPid           = acW.wPid;
244         acA.vDriverVersion = acW.vDriverVersion;
245         WideCharToMultiByte( CP_ACP, 0, acW.szPname, -1, acA.szPname,
246                              sizeof(acA.szPname), NULL, NULL );
247         acA.wTechnology    = acW.wTechnology;
248         acA.dwSupport      = acW.dwSupport;
249         memcpy(lpCaps, &acA, min(uSize, sizeof(acA)));
250     }
251     return ret;
252 }
253
254 /**************************************************************************
255  *                              auxGetVolume            [WINMM.@]
256  */
257 UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
258 {
259     LPWINE_MLD          wmld;
260
261     TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
262
263     if ((wmld = MMDRV_Get((HANDLE)(DWORD_PTR)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
264         return MMSYSERR_INVALHANDLE;
265     return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L);
266 }
267
268 /**************************************************************************
269  *                              auxSetVolume            [WINMM.@]
270  */
271 UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
272 {
273     LPWINE_MLD          wmld;
274
275     TRACE("(%04X, %u) !\n", uDeviceID, dwVolume);
276
277     if ((wmld = MMDRV_Get((HANDLE)(DWORD_PTR)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
278         return MMSYSERR_INVALHANDLE;
279     return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L);
280 }
281
282 /**************************************************************************
283  *                              auxOutMessage           [WINMM.@]
284  */
285 UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2)
286 {
287     LPWINE_MLD          wmld;
288
289     if ((wmld = MMDRV_Get((HANDLE)(DWORD_PTR)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
290         return MMSYSERR_INVALHANDLE;
291
292     return MMDRV_Message(wmld, uMessage, dw1, dw2);
293 }
294
295 /**************************************************************************
296  *                              midiOutGetNumDevs       [WINMM.@]
297  */
298 UINT WINAPI midiOutGetNumDevs(void)
299 {
300     return MMDRV_GetNum(MMDRV_MIDIOUT);
301 }
302
303 /**************************************************************************
304  *                              midiOutGetDevCapsW      [WINMM.@]
305  */
306 UINT WINAPI midiOutGetDevCapsW(UINT_PTR uDeviceID, LPMIDIOUTCAPSW lpCaps,
307                                UINT uSize)
308 {
309     LPWINE_MLD  wmld;
310
311     TRACE("(%lu, %p, %u);\n", uDeviceID, lpCaps, uSize);
312
313     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
314
315     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
316         return MMSYSERR_BADDEVICEID;
317
318     return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
319 }
320
321 /**************************************************************************
322  *                              midiOutGetDevCapsA      [WINMM.@]
323  */
324 UINT WINAPI midiOutGetDevCapsA(UINT_PTR uDeviceID, LPMIDIOUTCAPSA lpCaps,
325                                UINT uSize)
326 {
327     MIDIOUTCAPSW        mocW;
328     UINT                ret;
329
330     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
331
332     ret = midiOutGetDevCapsW(uDeviceID, &mocW, sizeof(mocW));
333
334     if (ret == MMSYSERR_NOERROR) {
335         MIDIOUTCAPSA mocA;
336         mocA.wMid               = mocW.wMid;
337         mocA.wPid               = mocW.wPid;
338         mocA.vDriverVersion     = mocW.vDriverVersion;
339         WideCharToMultiByte( CP_ACP, 0, mocW.szPname, -1, mocA.szPname,
340                              sizeof(mocA.szPname), NULL, NULL );
341         mocA.wTechnology        = mocW.wTechnology;
342         mocA.wVoices            = mocW.wVoices;
343         mocA.wNotes             = mocW.wNotes;
344         mocA.wChannelMask       = mocW.wChannelMask;
345         mocA.dwSupport          = mocW.dwSupport;
346         memcpy(lpCaps, &mocA, min(uSize, sizeof(mocA)));
347     }
348     return ret;
349 }
350
351 /**************************************************************************
352  *                              midiOutGetErrorTextA    [WINMM.@]
353  *                              midiInGetErrorTextA     [WINMM.@]
354  */
355 UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
356 {
357     UINT        ret;
358
359     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
360     else if (uSize == 0) ret = MMSYSERR_NOERROR;
361     else
362     {
363         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
364         if (!xstr) ret = MMSYSERR_NOMEM;
365         else
366         {
367             ret = midiOutGetErrorTextW(uError, xstr, uSize);
368             if (ret == MMSYSERR_NOERROR)
369                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
370             HeapFree(GetProcessHeap(), 0, xstr);
371         }
372     }
373     return ret;
374 }
375
376 /**************************************************************************
377  *                              midiOutGetErrorTextW    [WINMM.@]
378  *                              midiInGetErrorTextW     [WINMM.@]
379  */
380 UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
381 {
382     UINT        ret = MMSYSERR_BADERRNUM;
383
384     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
385     else if (uSize == 0) ret = MMSYSERR_NOERROR;
386     else if (
387                /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
388                 * a warning for the test was always true */
389                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
390                (uError >= MIDIERR_BASE  && uError <= MIDIERR_LASTERROR)) {
391         if (LoadStringW(hWinMM32Instance, uError, lpText, uSize) > 0) {
392             ret = MMSYSERR_NOERROR;
393         }
394     }
395     return ret;
396 }
397
398 /**************************************************************************
399  *                              MIDI_OutAlloc                   [internal]
400  */
401 static  LPWINE_MIDI     MIDI_OutAlloc(HMIDIOUT* lphMidiOut, DWORD_PTR* lpdwCallback,
402                                       DWORD_PTR* lpdwInstance, LPDWORD lpdwFlags,
403                                       DWORD cIDs, MIDIOPENSTRMID* lpIDs)
404 {
405     HANDLE              hMidiOut;
406     LPWINE_MIDI         lpwm;
407     UINT                size;
408
409     size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
410
411     lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
412                                     lpdwCallback, lpdwInstance);
413     *lphMidiOut = hMidiOut;
414
415     if (lpwm) {
416         lpwm->mod.hMidi = hMidiOut;
417         lpwm->mod.dwCallback = *lpdwCallback;
418         lpwm->mod.dwInstance = *lpdwInstance;
419         lpwm->mod.dnDevNode = 0;
420         lpwm->mod.cIds = cIDs;
421         if (cIDs)
422             memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
423     }
424     return lpwm;
425 }
426
427 /**************************************************************************
428  *                              midiOutOpen             [WINMM.@]
429  */
430 UINT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,
431                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
432 {
433     HMIDIOUT            hMidiOut;
434     LPWINE_MIDI         lpwm;
435     UINT                dwRet;
436
437     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
438           lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
439
440     if (lphMidiOut != NULL) *lphMidiOut = 0;
441
442     dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
443     if (dwRet != MMSYSERR_NOERROR)
444         return dwRet;
445
446     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags, 0, NULL);
447
448     if (lpwm == NULL)
449         return MMSYSERR_NOMEM;
450
451     lpwm->mld.uDeviceID = uDeviceID;
452
453     dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
454
455     if (dwRet != MMSYSERR_NOERROR) {
456         MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
457         hMidiOut = 0;
458     }
459
460     if (lphMidiOut) *lphMidiOut = hMidiOut;
461     TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
462
463     return dwRet;
464 }
465
466 /**************************************************************************
467  *                              midiOutClose            [WINMM.@]
468  */
469 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
470 {
471     LPWINE_MLD          wmld;
472     DWORD               dwRet;
473
474     TRACE("(%p)\n", hMidiOut);
475
476     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
477         return MMSYSERR_INVALHANDLE;
478
479     dwRet = MMDRV_Close(wmld, MODM_CLOSE);
480     MMDRV_Free(hMidiOut, wmld);
481
482     return dwRet;
483 }
484
485 /**************************************************************************
486  *                              midiOutPrepareHeader    [WINMM.@]
487  */
488 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
489                                  MIDIHDR* lpMidiOutHdr, UINT uSize)
490 {
491     LPWINE_MLD          wmld;
492
493     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
494
495     if (lpMidiOutHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
496         return MMSYSERR_INVALPARAM;
497
498     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
499         return MMSYSERR_INVALHANDLE;
500     /* FIXME: detect MIDIStream handles and enforce 64KB buffer limit on those */
501
502     return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
503 }
504
505 /**************************************************************************
506  *                              midiOutUnprepareHeader  [WINMM.@]
507  */
508 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
509                                    MIDIHDR* lpMidiOutHdr, UINT uSize)
510 {
511     LPWINE_MLD          wmld;
512
513     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
514
515     if (lpMidiOutHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
516         return MMSYSERR_INVALPARAM;
517
518     if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
519         return MMSYSERR_NOERROR;
520     }
521
522     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
523         return MMSYSERR_INVALHANDLE;
524
525     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
526 }
527
528 /**************************************************************************
529  *                              midiOutShortMsg         [WINMM.@]
530  */
531 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
532 {
533     LPWINE_MLD          wmld;
534
535     TRACE("(%p, %08X)\n", hMidiOut, dwMsg);
536
537     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
538         return MMSYSERR_INVALHANDLE;
539
540     return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L);
541 }
542
543 /**************************************************************************
544  *                              midiOutLongMsg          [WINMM.@]
545  */
546 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
547                            MIDIHDR* lpMidiOutHdr, UINT uSize)
548 {
549     LPWINE_MLD          wmld;
550
551     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
552
553     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
554         return MMSYSERR_INVALHANDLE;
555
556     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize);
557 }
558
559 /**************************************************************************
560  *                              midiOutReset            [WINMM.@]
561  */
562 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
563 {
564     LPWINE_MLD          wmld;
565
566     TRACE("(%p)\n", hMidiOut);
567
568     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
569         return MMSYSERR_INVALHANDLE;
570
571     return MMDRV_Message(wmld, MODM_RESET, 0L, 0L);
572 }
573
574 /**************************************************************************
575  *                              midiOutGetVolume        [WINMM.@]
576  */
577 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
578 {
579     LPWINE_MLD          wmld;
580
581     TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
582
583     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
584         return MMSYSERR_INVALHANDLE;
585
586     return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L);
587 }
588
589 /**************************************************************************
590  *                              midiOutSetVolume        [WINMM.@]
591  */
592 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
593 {
594     LPWINE_MLD          wmld;
595
596     TRACE("(%p, %d);\n", hMidiOut, dwVolume);
597
598     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
599         return MMSYSERR_INVALHANDLE;
600
601     return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L);
602 }
603
604 /**************************************************************************
605  *                              midiOutCachePatches             [WINMM.@]
606  */
607 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
608                                 WORD* lpwPatchArray, UINT uFlags)
609 {
610     /* not really necessary to support this */
611     FIXME("not supported yet\n");
612     return MMSYSERR_NOTSUPPORTED;
613 }
614
615 /**************************************************************************
616  *                              midiOutCacheDrumPatches [WINMM.@]
617  */
618 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
619                                     WORD* lpwKeyArray, UINT uFlags)
620 {
621     FIXME("not supported yet\n");
622     return MMSYSERR_NOTSUPPORTED;
623 }
624
625 /**************************************************************************
626  *                              midiOutGetID            [WINMM.@]
627  */
628 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
629 {
630     LPWINE_MLD          wmld;
631
632     TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
633
634     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
635     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
636         return MMSYSERR_INVALHANDLE;
637
638     *lpuDeviceID = wmld->uDeviceID;
639     return MMSYSERR_NOERROR;
640 }
641
642 /**************************************************************************
643  *                              midiOutMessage          [WINMM.@]
644  */
645 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
646                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
647 {
648     LPWINE_MLD          wmld;
649
650     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
651
652     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
653         /* HACK... */
654         if (uMessage == 0x0001) {
655             *(LPDWORD)dwParam1 = 1;
656             return 0;
657         }
658         if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
659             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
660         }
661         return MMSYSERR_INVALHANDLE;
662     }
663
664     switch (uMessage) {
665     case MODM_OPEN:
666     case MODM_CLOSE:
667         FIXME("can't handle OPEN or CLOSE message!\n");
668         return MMSYSERR_NOTSUPPORTED;
669     }
670     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
671 }
672
673 /**************************************************************************
674  *                              midiInGetNumDevs        [WINMM.@]
675  */
676 UINT WINAPI midiInGetNumDevs(void)
677 {
678     return MMDRV_GetNum(MMDRV_MIDIIN);
679 }
680
681 /**************************************************************************
682  *                              midiInGetDevCapsW       [WINMM.@]
683  */
684 UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
685 {
686     LPWINE_MLD  wmld;
687
688     TRACE("(%ld, %p, %d);\n", uDeviceID, lpCaps, uSize);
689
690     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
691
692     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
693         return MMSYSERR_BADDEVICEID;
694
695    return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
696 }
697
698 /**************************************************************************
699  *                              midiInGetDevCapsA       [WINMM.@]
700  */
701 UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
702 {
703     MIDIINCAPSW         micW;
704     UINT                ret;
705
706     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
707
708     ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));
709
710     if (ret == MMSYSERR_NOERROR) {
711         MIDIINCAPSA micA;
712         micA.wMid           = micW.wMid;
713         micA.wPid           = micW.wPid;
714         micA.vDriverVersion = micW.vDriverVersion;
715         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
716                              sizeof(micA.szPname), NULL, NULL );
717         micA.dwSupport      = micW.dwSupport;
718         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
719     }
720     return ret;
721 }
722
723 /**************************************************************************
724  *                              midiInOpen              [WINMM.@]
725  */
726 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
727                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
728 {
729     HANDLE              hMidiIn;
730     LPWINE_MIDI         lpwm;
731     DWORD               dwRet;
732
733     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
734           lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
735
736     if (lphMidiIn != NULL) *lphMidiIn = 0;
737
738     dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
739     if (dwRet != MMSYSERR_NOERROR)
740         return dwRet;
741
742     lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
743                                     &dwFlags, &dwCallback, &dwInstance);
744
745     if (lpwm == NULL)
746         return MMSYSERR_NOMEM;
747
748     lpwm->mod.hMidi = hMidiIn;
749     lpwm->mod.dwCallback = dwCallback;
750     lpwm->mod.dwInstance = dwInstance;
751
752     lpwm->mld.uDeviceID = uDeviceID;
753     dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
754
755     if (dwRet != MMSYSERR_NOERROR) {
756         MMDRV_Free(hMidiIn, &lpwm->mld);
757         hMidiIn = 0;
758     }
759     if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
760     TRACE("=> %d hMidi=%p\n", dwRet, hMidiIn);
761
762     return dwRet;
763 }
764
765 /**************************************************************************
766  *                              midiInClose             [WINMM.@]
767  */
768 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
769 {
770     LPWINE_MLD          wmld;
771     DWORD               dwRet;
772
773     TRACE("(%p)\n", hMidiIn);
774
775     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
776         return MMSYSERR_INVALHANDLE;
777
778     dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
779     MMDRV_Free(hMidiIn, wmld);
780     return dwRet;
781 }
782
783 /**************************************************************************
784  *                              midiInPrepareHeader     [WINMM.@]
785  */
786 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
787                                 MIDIHDR* lpMidiInHdr, UINT uSize)
788 {
789     LPWINE_MLD          wmld;
790
791     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
792
793     if (lpMidiInHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
794         return MMSYSERR_INVALPARAM;
795
796     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
797         return MMSYSERR_INVALHANDLE;
798
799     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
800 }
801
802 /**************************************************************************
803  *                              midiInUnprepareHeader   [WINMM.@]
804  */
805 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
806                                   MIDIHDR* lpMidiInHdr, UINT uSize)
807 {
808     LPWINE_MLD          wmld;
809
810     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
811
812     if (lpMidiInHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
813         return MMSYSERR_INVALPARAM;
814
815     if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
816         return MMSYSERR_NOERROR;
817     }
818
819     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
820         return MMSYSERR_INVALHANDLE;
821
822     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
823 }
824
825 /**************************************************************************
826  *                              midiInAddBuffer         [WINMM.@]
827  */
828 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
829                             MIDIHDR* lpMidiInHdr, UINT uSize)
830 {
831     LPWINE_MLD          wmld;
832
833     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
834
835     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
836         return MMSYSERR_INVALHANDLE;
837
838     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize);
839 }
840
841 /**************************************************************************
842  *                              midiInStart                     [WINMM.@]
843  */
844 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
845 {
846     LPWINE_MLD          wmld;
847
848     TRACE("(%p)\n", hMidiIn);
849
850     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
851         return MMSYSERR_INVALHANDLE;
852
853     return MMDRV_Message(wmld, MIDM_START, 0L, 0L);
854 }
855
856 /**************************************************************************
857  *                              midiInStop                      [WINMM.@]
858  */
859 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
860 {
861     LPWINE_MLD          wmld;
862
863     TRACE("(%p)\n", hMidiIn);
864
865     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
866         return MMSYSERR_INVALHANDLE;
867
868     return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L);
869 }
870
871 /**************************************************************************
872  *                              midiInReset                     [WINMM.@]
873  */
874 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
875 {
876     LPWINE_MLD          wmld;
877
878     TRACE("(%p)\n", hMidiIn);
879
880     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
881         return MMSYSERR_INVALHANDLE;
882
883     return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L);
884 }
885
886 /**************************************************************************
887  *                              midiInGetID                     [WINMM.@]
888  */
889 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
890 {
891     LPWINE_MLD          wmld;
892
893     TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
894
895     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
896
897     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
898         return MMSYSERR_INVALHANDLE;
899
900     *lpuDeviceID = wmld->uDeviceID;
901
902     return MMSYSERR_NOERROR;
903 }
904
905 /**************************************************************************
906  *                              midiInMessage           [WINMM.@]
907  */
908 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
909                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
910 {
911     LPWINE_MLD          wmld;
912
913     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
914
915     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
916         return MMSYSERR_INVALHANDLE;
917
918     switch (uMessage) {
919     case MIDM_OPEN:
920     case MIDM_CLOSE:
921         FIXME("can't handle OPEN or CLOSE message!\n");
922         return MMSYSERR_NOTSUPPORTED;
923     }
924     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
925 }
926
927 /**************************************************************************
928  *                              midiConnect                     [WINMM.@]
929  */
930 MMRESULT WINAPI midiConnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
931 {
932     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
933     return MMSYSERR_ERROR;
934 }
935
936 /**************************************************************************
937  *                              midiDisconnect                  [WINMM.@]
938  */
939 MMRESULT WINAPI midiDisconnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
940 {
941     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
942     return MMSYSERR_ERROR;
943 }
944
945 typedef struct WINE_MIDIStream {
946     HMIDIOUT                    hDevice;
947     HANDLE                      hThread;
948     DWORD                       dwThreadID;
949     DWORD                       dwTempo;
950     DWORD                       dwTimeDiv;
951     DWORD                       dwPositionMS;
952     DWORD                       dwPulses;
953     DWORD                       dwStartTicks;
954     WORD                        wFlags;
955     HANDLE                      hEvent;
956     LPMIDIHDR                   lpMidiHdr;
957 } WINE_MIDIStream;
958
959 #define WINE_MSM_HEADER         (WM_USER+0)
960 #define WINE_MSM_STOP           (WM_USER+1)
961
962 /**************************************************************************
963  *                              MMSYSTEM_GetMidiStream          [internal]
964  */
965 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
966 {
967     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
968
969     if (lplpwm)
970         *lplpwm = lpwm;
971
972     if (lpwm == NULL) {
973         return FALSE;
974     }
975
976     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
977
978     return *lpMidiStrm != NULL;
979 }
980
981 /**************************************************************************
982  *                              MMSYSTEM_MidiStream_Convert     [internal]
983  */
984 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
985 {
986     DWORD       ret = 0;
987
988     if (lpMidiStrm->dwTimeDiv == 0) {
989         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
990     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
991         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
992         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
993         ret = (pulse * 1000) / (nf * nsf);
994     } else {
995         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
996                       (double)lpMidiStrm->dwTimeDiv);
997     }
998
999     return ret;
1000 }
1001
1002 /**************************************************************************
1003  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
1004  */
1005 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1006 {
1007     LPMIDIHDR   lpMidiHdr;
1008     LPMIDIHDR*  lpmh;
1009     LPBYTE      lpData;
1010
1011     switch (msg->message) {
1012     case WM_QUIT:
1013         SetEvent(lpMidiStrm->hEvent);
1014         return FALSE;
1015     case WINE_MSM_STOP:
1016         TRACE("STOP\n");
1017         /* this is not quite what MS doc says... */
1018         midiOutReset(lpMidiStrm->hDevice);
1019         /* empty list of already submitted buffers */
1020         for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; ) {
1021             LPMIDIHDR lphdr = lpMidiHdr;
1022             lpMidiHdr = lpMidiHdr->lpNext;
1023             lphdr->dwFlags |= MHDR_DONE;
1024             lphdr->dwFlags &= ~MHDR_INQUEUE;
1025
1026             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1027                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1028                            lpwm->mod.dwInstance, (DWORD_PTR)lphdr, 0);
1029         }
1030         lpMidiStrm->lpMidiHdr = 0;
1031         SetEvent(lpMidiStrm->hEvent);
1032         break;
1033     case WINE_MSM_HEADER:
1034         /* sets initial tick count for first MIDIHDR */
1035         if (!lpMidiStrm->dwStartTicks)
1036             lpMidiStrm->dwStartTicks = GetTickCount();
1037
1038         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1039          * by native mcimidi, it doesn't look like a correct one".
1040          * this trick allows to throw it away... but I don't like it.
1041          * It looks like part of the file I'm trying to play and definitively looks
1042          * like raw midi content
1043          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1044          * synchronization issue where native mcimidi is still processing raw MIDI
1045          * content before generating MIDIEVENTs ?
1046          *
1047          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1048          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1049          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1050          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1051          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1052          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1053          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1054          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1055          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1056          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1057          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1058          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1059          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1060          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1061          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1062          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1063          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1064          */
1065         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1066         lpData = (LPBYTE)lpMidiHdr->lpData;
1067         TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBytesRecorded=%u/%u dwFlags=0x%08x size=%lu]\n",
1068               (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1069               lpMidiHdr, lpMidiHdr->dwBytesRecorded, lpMidiHdr->dwBufferLength,
1070               lpMidiHdr->dwFlags, msg->wParam);
1071 #if 0
1072         /* dumps content of lpMidiHdr->lpData
1073          * FIXME: there should be a debug routine somewhere that already does this
1074          * I hate spreading this type of shit all around the code
1075          */
1076         for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1077             DWORD       i;
1078             BYTE        ch;
1079
1080             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1081                 printf("%02x ", lpData[dwToGo + i]);
1082             for (; i < 16; i++)
1083                 printf("   ");
1084             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1085                 ch = lpData[dwToGo + i];
1086                 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1087             }
1088             printf("\n");
1089         }
1090 #endif
1091         if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1092             ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1093             ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
1094             FIXME("Dropping bad %s lpMidiHdr (streamID=%08x)\n",
1095                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1096                   ((LPMIDIEVENT)lpData)->dwStreamID);
1097             lpMidiHdr->dwFlags |= MHDR_DONE;
1098             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1099
1100             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1101                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1102                            lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1103             break;
1104         }
1105
1106         for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = &(*lpmh)->lpNext);
1107         *lpmh = lpMidiHdr;
1108         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1109         lpMidiHdr->lpNext = 0;
1110         lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1111         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1112
1113         break;
1114     default:
1115         FIXME("Unknown message %d\n", msg->message);
1116         break;
1117     }
1118     return TRUE;
1119 }
1120
1121 /**************************************************************************
1122  *                              MMSYSTEM_MidiStream_Player      [internal]
1123  */
1124 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
1125 {
1126     WINE_MIDIStream*    lpMidiStrm = pmt;
1127     WINE_MIDI*          lpwm;
1128     MSG                 msg;
1129     DWORD               dwToGo;
1130     DWORD               dwCurrTC;
1131     LPMIDIHDR           lpMidiHdr;
1132     DWORD               dwOffset;
1133
1134     TRACE("(%p)!\n", lpMidiStrm);
1135
1136     if (!lpMidiStrm ||
1137         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1138         goto the_end;
1139
1140     /* force thread's queue creation */
1141     PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
1142
1143     lpMidiStrm->dwStartTicks = 0;
1144     lpMidiStrm->dwPulses = 0;
1145
1146     lpMidiStrm->lpMidiHdr = 0;
1147
1148     /* midiStreamOpen is waiting for ack */
1149     SetEvent(lpMidiStrm->hEvent);
1150
1151 start_header:
1152     lpMidiHdr = lpMidiStrm->lpMidiHdr;
1153     if (!lpMidiHdr) {
1154         /* for first message, block until one arrives, then process all that are available */
1155         GetMessageA(&msg, 0, 0, 0);
1156         do {
1157             if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1158                 goto the_end;
1159         } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
1160         goto start_header;
1161     }
1162
1163     dwOffset = 0;
1164     while (dwOffset + offsetof(MIDIEVENT,dwParms) <= lpMidiHdr->dwBytesRecorded) {
1165         LPMIDIEVENT me = (LPMIDIEVENT)(lpMidiHdr->lpData+dwOffset);
1166
1167         /* do we have to wait ? */
1168         if (me->dwDeltaTime) {
1169             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1170             lpMidiStrm->dwPulses += me->dwDeltaTime;
1171
1172             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
1173
1174             TRACE("%d/%d/%d\n", dwToGo, GetTickCount(), me->dwDeltaTime);
1175             while ((dwCurrTC = GetTickCount()) < dwToGo) {
1176                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1177                     /* got a message, handle it */
1178                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1179                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1180                             goto the_end;
1181                         /* is lpMidiHdr still current? */
1182                         if (lpMidiHdr != lpMidiStrm->lpMidiHdr) {
1183                             goto start_header;
1184                         }
1185                     }
1186                 } else {
1187                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1188                     break;
1189                 }
1190             }
1191         }
1192         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1193         case MEVT_COMMENT:
1194             FIXME("NIY: MEVT_COMMENT\n");
1195             /* do nothing, skip bytes */
1196             break;
1197         case MEVT_LONGMSG:
1198             FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
1199             break;
1200         case MEVT_NOP:
1201             break;
1202         case MEVT_SHORTMSG:
1203             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1204             break;
1205         case MEVT_TEMPO:
1206             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1207             break;
1208         case MEVT_VERSION:
1209             break;
1210         default:
1211             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1212             break;
1213         }
1214         if (me->dwEvent & MEVT_F_CALLBACK) {
1215             /* native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
1216             lpMidiHdr->dwOffset = dwOffset;
1217             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1218                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1219                            lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1220         }
1221         dwOffset += offsetof(MIDIEVENT,dwParms);
1222         if (me->dwEvent & MEVT_F_LONG)
1223             dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1224     }
1225     /* done with this header */
1226     lpMidiStrm->lpMidiHdr = lpMidiHdr->lpNext;
1227     lpMidiHdr->dwFlags |= MHDR_DONE;
1228     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1229
1230     DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1231                    (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1232                    lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1233     goto start_header;
1234
1235 the_end:
1236     TRACE("End of thread\n");
1237     return 0;
1238 }
1239
1240 /**************************************************************************
1241  *                              MMSYSTEM_MidiStream_PostMessage [internal]
1242  */
1243 static  BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
1244 {
1245     if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
1246         MsgWaitForMultipleObjects( 1, &lpMidiStrm->hEvent, FALSE, INFINITE, 0 );
1247     } else {
1248         ERR("bad PostThreadMessageA\n");
1249         return FALSE;
1250     }
1251     return TRUE;
1252 }
1253
1254 /**************************************************************************
1255  *                              midiStreamClose                 [WINMM.@]
1256  */
1257 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1258 {
1259     WINE_MIDIStream*    lpMidiStrm;
1260
1261     TRACE("(%p)!\n", hMidiStrm);
1262
1263     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1264         return MMSYSERR_INVALHANDLE;
1265
1266     midiStreamStop(hMidiStrm);
1267     MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
1268     CloseHandle(lpMidiStrm->hEvent);
1269     HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1270
1271     return midiOutClose((HMIDIOUT)hMidiStrm);
1272 }
1273
1274 /**************************************************************************
1275  *                              midiStreamOpen                  [WINMM.@]
1276  */
1277 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1278                                DWORD cMidi, DWORD_PTR dwCallback,
1279                                DWORD_PTR dwInstance, DWORD fdwOpen)
1280 {
1281     WINE_MIDIStream*    lpMidiStrm;
1282     MMRESULT            ret;
1283     MIDIOPENSTRMID      mosm;
1284     LPWINE_MIDI         lpwm;
1285     HMIDIOUT            hMidiOut;
1286
1287     TRACE("(%p, %p, %d, 0x%08lx, 0x%08lx, 0x%08x)!\n",
1288           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1289
1290     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1291         return MMSYSERR_INVALPARAM;
1292
1293     ret = WINMM_CheckCallback(dwCallback, fdwOpen, FALSE);
1294     if (ret != MMSYSERR_NOERROR)
1295         return ret;
1296
1297     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
1298     if (!lpMidiStrm)
1299         return MMSYSERR_NOMEM;
1300
1301     lpMidiStrm->dwTempo = 500000;
1302     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quarter notes per minute *//* FIXME ??*/
1303     lpMidiStrm->dwPositionMS = 0;
1304
1305     mosm.dwStreamID = (DWORD)lpMidiStrm;
1306     /* FIXME: the correct value is not allocated yet for MAPPER */
1307     mosm.wDeviceID  = *lpuDeviceID;
1308     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm);
1309     if (!lpwm) {
1310         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1311         return MMSYSERR_NOMEM;
1312     }
1313     lpMidiStrm->hDevice = hMidiOut;
1314     *lphMidiStrm = (HMIDISTRM)hMidiOut;
1315
1316     lpwm->mld.uDeviceID = *lpuDeviceID;
1317
1318     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD_PTR)&lpwm->mod, fdwOpen);
1319     if (ret != MMSYSERR_NOERROR) {
1320         MMDRV_Free(hMidiOut, &lpwm->mld);
1321         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1322         return ret;
1323     }
1324
1325     lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1326     lpMidiStrm->wFlags = HIWORD(fdwOpen);
1327
1328     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1329                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1330
1331     if (!lpMidiStrm->hThread) {
1332         midiStreamClose((HMIDISTRM)hMidiOut);
1333         return MMSYSERR_NOMEM;
1334     }
1335     SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
1336
1337     /* wait for thread to have started, and for its queue to be created */
1338     WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1339     /* start in paused mode */
1340     SuspendThread(lpMidiStrm->hThread);
1341
1342     TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1343           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1344     return ret;
1345 }
1346
1347 /**************************************************************************
1348  *                              midiStreamOut                   [WINMM.@]
1349  */
1350 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
1351                               UINT cbMidiHdr)
1352 {
1353     WINE_MIDIStream*    lpMidiStrm;
1354     DWORD               ret = MMSYSERR_NOERROR;
1355
1356     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
1357
1358     if (cbMidiHdr < offsetof(MIDIHDR,dwOffset) || !lpMidiHdr || !lpMidiHdr->lpData
1359         || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded
1360         || lpMidiHdr->dwBytesRecorded % 4 /* player expects DWORD padding */)
1361         return MMSYSERR_INVALPARAM;
1362     /* FIXME: Native additionally checks if the MIDIEVENTs in lpData
1363      * exactly fit dwBytesRecorded. */
1364
1365     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1366         return MIDIERR_UNPREPARED;
1367
1368     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1369         return MIDIERR_STILLPLAYING;
1370
1371     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1372         ret = MMSYSERR_INVALHANDLE;
1373     } else {
1374         lpMidiHdr->dwFlags |= MHDR_ISSTRM | MHDR_INQUEUE;
1375         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1376         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
1377                                 WINE_MSM_HEADER, cbMidiHdr,
1378                                 (LPARAM)lpMidiHdr)) {
1379             ERR("bad PostThreadMessageA\n");
1380             ret = MMSYSERR_ERROR;
1381         }
1382     }
1383     return ret;
1384 }
1385
1386 /**************************************************************************
1387  *                              midiStreamPause                 [WINMM.@]
1388  */
1389 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
1390 {
1391     WINE_MIDIStream*    lpMidiStrm;
1392     DWORD               ret = MMSYSERR_NOERROR;
1393
1394     TRACE("(%p)!\n", hMidiStrm);
1395
1396     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1397         ret = MMSYSERR_INVALHANDLE;
1398     } else {
1399         if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
1400             ERR("bad Suspend (%d)\n", GetLastError());
1401             ret = MMSYSERR_ERROR;
1402         }
1403     }
1404     return ret;
1405 }
1406
1407 /**************************************************************************
1408  *                              midiStreamPosition              [WINMM.@]
1409  */
1410 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
1411 {
1412     WINE_MIDIStream*    lpMidiStrm;
1413     DWORD               ret = MMSYSERR_NOERROR;
1414
1415     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
1416
1417     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1418         ret = MMSYSERR_INVALHANDLE;
1419     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
1420         ret = MMSYSERR_INVALPARAM;
1421     } else {
1422         switch (lpMMT->wType) {
1423         default:
1424             FIXME("Unsupported time type %x\n", lpMMT->wType);
1425         case TIME_BYTES:
1426         case TIME_SAMPLES:
1427             lpMMT->wType = TIME_MS;
1428             /* fall through to alternative format */
1429         case TIME_MS:
1430             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
1431             TRACE("=> %d ms\n", lpMMT->u.ms);
1432             break;
1433         case TIME_TICKS:
1434             lpMMT->u.ticks = lpMidiStrm->dwPulses;
1435             TRACE("=> %d ticks\n", lpMMT->u.ticks);
1436             break;
1437         }
1438     }
1439     return ret;
1440 }
1441
1442 /**************************************************************************
1443  *                              midiStreamProperty              [WINMM.@]
1444  */
1445 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
1446 {
1447     WINE_MIDIStream*    lpMidiStrm;
1448     MMRESULT            ret = MMSYSERR_NOERROR;
1449
1450     TRACE("(%p, %p, %x)\n", hMidiStrm, lpPropData, dwProperty);
1451
1452     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1453         ret = MMSYSERR_INVALHANDLE;
1454     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
1455         ret = MMSYSERR_INVALPARAM;
1456     } else if (dwProperty & MIDIPROP_TEMPO) {
1457         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
1458
1459         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
1460             ret = MMSYSERR_INVALPARAM;
1461         } else if (dwProperty & MIDIPROP_SET) {
1462             lpMidiStrm->dwTempo = mpt->dwTempo;
1463             TRACE("Setting tempo to %d\n", mpt->dwTempo);
1464         } else if (dwProperty & MIDIPROP_GET) {
1465             mpt->dwTempo = lpMidiStrm->dwTempo;
1466             TRACE("Getting tempo <= %d\n", mpt->dwTempo);
1467         }
1468     } else if (dwProperty & MIDIPROP_TIMEDIV) {
1469         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
1470
1471         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
1472             ret = MMSYSERR_INVALPARAM;
1473         } else if (dwProperty & MIDIPROP_SET) {
1474             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
1475             TRACE("Setting time div to %d\n", mptd->dwTimeDiv);
1476         } else if (dwProperty & MIDIPROP_GET) {
1477             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
1478             TRACE("Getting time div <= %d\n", mptd->dwTimeDiv);
1479         }
1480     } else {
1481         ret = MMSYSERR_INVALPARAM;
1482     }
1483
1484     return ret;
1485 }
1486
1487 /**************************************************************************
1488  *                              midiStreamRestart               [WINMM.@]
1489  */
1490 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
1491 {
1492     WINE_MIDIStream*    lpMidiStrm;
1493     MMRESULT            ret = MMSYSERR_NOERROR;
1494
1495     TRACE("(%p)!\n", hMidiStrm);
1496
1497     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1498         ret = MMSYSERR_INVALHANDLE;
1499     } else {
1500         DWORD   ret;
1501
1502         /* since we increase the thread suspend count on each midiStreamPause
1503          * there may be a need for several ResumeThread
1504          */
1505         do {
1506             ret = ResumeThread(lpMidiStrm->hThread);
1507         } while (ret != 0xFFFFFFFF && ret > 1);
1508         if (ret == 0xFFFFFFFF) {
1509             ERR("bad Resume (%d)\n", GetLastError());
1510             ret = MMSYSERR_ERROR;
1511         } else {
1512             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
1513         }
1514     }
1515     return ret;
1516 }
1517
1518 /**************************************************************************
1519  *                              midiStreamStop                  [WINMM.@]
1520  */
1521 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
1522 {
1523     WINE_MIDIStream*    lpMidiStrm;
1524     MMRESULT            ret = MMSYSERR_NOERROR;
1525
1526     TRACE("(%p)!\n", hMidiStrm);
1527
1528     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1529         ret = MMSYSERR_INVALHANDLE;
1530     } else {
1531         /* in case stream has been paused... FIXME is the current state correct ? */
1532         midiStreamRestart(hMidiStrm);
1533         MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
1534     }
1535     return ret;
1536 }
1537
1538 struct mm_starter
1539 {
1540     LPTASKCALLBACK      cb;
1541     DWORD               client;
1542     HANDLE              event;
1543 };
1544
1545 static DWORD WINAPI mmTaskRun(void* pmt)
1546 {
1547     struct mm_starter mms;
1548
1549     memcpy(&mms, pmt, sizeof(struct mm_starter));
1550     HeapFree(GetProcessHeap(), 0, pmt);
1551     mms.cb(mms.client);
1552     if (mms.event) SetEvent(mms.event);
1553     return 0;
1554 }
1555
1556 /******************************************************************
1557  *              mmTaskCreate (WINMM.@)
1558  */
1559 UINT     WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR client)
1560 {
1561     HANDLE               hThread;
1562     HANDLE               hEvent = 0;
1563     struct mm_starter   *mms;
1564
1565     mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));
1566     if (mms == NULL) return TASKERR_OUTOFMEMORY;
1567
1568     mms->cb = cb;
1569     mms->client = client;
1570     if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1571     mms->event = hEvent;
1572
1573     hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
1574     if (!hThread) {
1575         HeapFree(GetProcessHeap(), 0, mms);
1576         if (hEvent) CloseHandle(hEvent);
1577         return TASKERR_OUTOFMEMORY;
1578     }
1579     SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
1580     if (ph) *ph = hEvent;
1581     CloseHandle(hThread);
1582     return 0;
1583 }
1584
1585 /******************************************************************
1586  *              mmTaskBlock (WINMM.@)
1587  */
1588 VOID     WINAPI mmTaskBlock(DWORD tid)
1589 {
1590     MSG         msg;
1591
1592     do
1593     {
1594         GetMessageA(&msg, 0, 0, 0);
1595         if (msg.hwnd) DispatchMessageA(&msg);
1596     } while (msg.message != WM_USER);
1597 }
1598
1599 /******************************************************************
1600  *              mmTaskSignal (WINMM.@)
1601  */
1602 BOOL     WINAPI mmTaskSignal(DWORD tid)
1603 {
1604     return PostThreadMessageW(tid, WM_USER, 0, 0);
1605 }
1606
1607 /******************************************************************
1608  *              mmTaskYield (WINMM.@)
1609  */
1610 VOID     WINAPI mmTaskYield(VOID) {}
1611
1612 /******************************************************************
1613  *              mmGetCurrentTask (WINMM.@)
1614  */
1615 DWORD    WINAPI mmGetCurrentTask(VOID)
1616 {
1617     return GetCurrentThreadId();
1618 }