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