Release 1.5.29.
[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 ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
480         return MMSYSERR_INVALHANDLE;
481     /* FIXME: detect MIDIStream handles and enforce 64KB buffer limit on those */
482
483     return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
484 }
485
486 /**************************************************************************
487  *                              midiOutUnprepareHeader  [WINMM.@]
488  */
489 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
490                                    MIDIHDR* lpMidiOutHdr, UINT uSize)
491 {
492     LPWINE_MLD          wmld;
493
494     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
495
496     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
497         return MMSYSERR_INVALHANDLE;
498
499     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
500 }
501
502 /**************************************************************************
503  *                              midiOutShortMsg         [WINMM.@]
504  */
505 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
506 {
507     LPWINE_MLD          wmld;
508
509     TRACE("(%p, %08X)\n", hMidiOut, dwMsg);
510
511     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
512         return MMSYSERR_INVALHANDLE;
513
514     return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L);
515 }
516
517 /**************************************************************************
518  *                              midiOutLongMsg          [WINMM.@]
519  */
520 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
521                            MIDIHDR* lpMidiOutHdr, UINT uSize)
522 {
523     LPWINE_MLD          wmld;
524
525     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
526
527     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
528         return MMSYSERR_INVALHANDLE;
529
530     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize);
531 }
532
533 /**************************************************************************
534  *                              midiOutReset            [WINMM.@]
535  */
536 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
537 {
538     LPWINE_MLD          wmld;
539
540     TRACE("(%p)\n", hMidiOut);
541
542     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
543         return MMSYSERR_INVALHANDLE;
544
545     return MMDRV_Message(wmld, MODM_RESET, 0L, 0L);
546 }
547
548 /**************************************************************************
549  *                              midiOutGetVolume        [WINMM.@]
550  */
551 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
552 {
553     LPWINE_MLD          wmld;
554
555     TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
556
557     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
558         return MMSYSERR_INVALHANDLE;
559
560     return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L);
561 }
562
563 /**************************************************************************
564  *                              midiOutSetVolume        [WINMM.@]
565  */
566 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
567 {
568     LPWINE_MLD          wmld;
569
570     TRACE("(%p, %d);\n", hMidiOut, dwVolume);
571
572     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
573         return MMSYSERR_INVALHANDLE;
574
575     return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L);
576 }
577
578 /**************************************************************************
579  *                              midiOutCachePatches             [WINMM.@]
580  */
581 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
582                                 WORD* lpwPatchArray, UINT uFlags)
583 {
584     /* not really necessary to support this */
585     FIXME("(%p, %u, %p, %x): Stub\n", hMidiOut, uBank, lpwPatchArray, uFlags);
586     return MMSYSERR_NOTSUPPORTED;
587 }
588
589 /**************************************************************************
590  *                              midiOutCacheDrumPatches [WINMM.@]
591  */
592 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
593                                     WORD* lpwKeyArray, UINT uFlags)
594 {
595     FIXME("(%p, %u, %p, %x): Stub\n", hMidiOut, uPatch, lpwKeyArray, uFlags);
596     return MMSYSERR_NOTSUPPORTED;
597 }
598
599 /**************************************************************************
600  *                              midiOutGetID            [WINMM.@]
601  */
602 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
603 {
604     LPWINE_MLD          wmld;
605
606     TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
607
608     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
609     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
610         return MMSYSERR_INVALHANDLE;
611
612     *lpuDeviceID = wmld->uDeviceID;
613     return MMSYSERR_NOERROR;
614 }
615
616 /**************************************************************************
617  *                              midiOutMessage          [WINMM.@]
618  */
619 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
620                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
621 {
622     LPWINE_MLD          wmld;
623
624     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
625
626     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
627         /* HACK... */
628         if (uMessage == 0x0001) {
629             *(LPDWORD)dwParam1 = 1;
630             return 0;
631         }
632         if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
633             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
634         }
635         return MMSYSERR_INVALHANDLE;
636     }
637
638     switch (uMessage) {
639     case MODM_OPEN:
640     case MODM_CLOSE:
641         FIXME("can't handle OPEN or CLOSE message!\n");
642         return MMSYSERR_NOTSUPPORTED;
643     }
644     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
645 }
646
647 /**************************************************************************
648  *                              midiInGetNumDevs        [WINMM.@]
649  */
650 UINT WINAPI midiInGetNumDevs(void)
651 {
652     return MMDRV_GetNum(MMDRV_MIDIIN);
653 }
654
655 /**************************************************************************
656  *                              midiInGetDevCapsW       [WINMM.@]
657  */
658 UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
659 {
660     LPWINE_MLD  wmld;
661
662     TRACE("(%ld, %p, %d);\n", uDeviceID, lpCaps, uSize);
663
664     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
665
666     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
667         return MMSYSERR_BADDEVICEID;
668
669    return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
670 }
671
672 /**************************************************************************
673  *                              midiInGetDevCapsA       [WINMM.@]
674  */
675 UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
676 {
677     MIDIINCAPSW         micW;
678     UINT                ret;
679
680     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
681
682     ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));
683
684     if (ret == MMSYSERR_NOERROR) {
685         MIDIINCAPSA micA;
686         micA.wMid           = micW.wMid;
687         micA.wPid           = micW.wPid;
688         micA.vDriverVersion = micW.vDriverVersion;
689         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
690                              sizeof(micA.szPname), NULL, NULL );
691         micA.dwSupport      = micW.dwSupport;
692         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
693     }
694     return ret;
695 }
696
697 /**************************************************************************
698  *                              midiInOpen              [WINMM.@]
699  */
700 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
701                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
702 {
703     HANDLE              hMidiIn;
704     LPWINE_MIDI         lpwm;
705     DWORD               dwRet;
706
707     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
708           lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
709
710     if (lphMidiIn != NULL) *lphMidiIn = 0;
711
712     dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
713     if (dwRet != MMSYSERR_NOERROR)
714         return dwRet;
715
716     lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
717                                     &dwFlags, &dwCallback, &dwInstance);
718
719     if (lpwm == NULL)
720         return MMSYSERR_NOMEM;
721
722     lpwm->mod.hMidi = hMidiIn;
723     lpwm->mod.dwCallback = dwCallback;
724     lpwm->mod.dwInstance = dwInstance;
725
726     lpwm->mld.uDeviceID = uDeviceID;
727     dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
728
729     if (dwRet != MMSYSERR_NOERROR) {
730         MMDRV_Free(hMidiIn, &lpwm->mld);
731         hMidiIn = 0;
732     }
733     if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
734     TRACE("=> %d hMidi=%p\n", dwRet, hMidiIn);
735
736     return dwRet;
737 }
738
739 /**************************************************************************
740  *                              midiInClose             [WINMM.@]
741  */
742 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
743 {
744     LPWINE_MLD          wmld;
745     DWORD               dwRet;
746
747     TRACE("(%p)\n", hMidiIn);
748
749     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
750         return MMSYSERR_INVALHANDLE;
751
752     dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
753     MMDRV_Free(hMidiIn, wmld);
754     return dwRet;
755 }
756
757 /**************************************************************************
758  *                              midiInPrepareHeader     [WINMM.@]
759  */
760 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
761                                 MIDIHDR* lpMidiInHdr, UINT uSize)
762 {
763     LPWINE_MLD          wmld;
764
765     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
766
767     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
768         return MMSYSERR_INVALHANDLE;
769
770     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
771 }
772
773 /**************************************************************************
774  *                              midiInUnprepareHeader   [WINMM.@]
775  */
776 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
777                                   MIDIHDR* lpMidiInHdr, UINT uSize)
778 {
779     LPWINE_MLD          wmld;
780
781     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
782
783     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
784         return MMSYSERR_INVALHANDLE;
785
786     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
787 }
788
789 /**************************************************************************
790  *                              midiInAddBuffer         [WINMM.@]
791  */
792 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
793                             MIDIHDR* lpMidiInHdr, UINT uSize)
794 {
795     LPWINE_MLD          wmld;
796
797     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
798
799     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
800         return MMSYSERR_INVALHANDLE;
801
802     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize);
803 }
804
805 /**************************************************************************
806  *                              midiInStart                     [WINMM.@]
807  */
808 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
809 {
810     LPWINE_MLD          wmld;
811
812     TRACE("(%p)\n", hMidiIn);
813
814     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
815         return MMSYSERR_INVALHANDLE;
816
817     return MMDRV_Message(wmld, MIDM_START, 0L, 0L);
818 }
819
820 /**************************************************************************
821  *                              midiInStop                      [WINMM.@]
822  */
823 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
824 {
825     LPWINE_MLD          wmld;
826
827     TRACE("(%p)\n", hMidiIn);
828
829     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
830         return MMSYSERR_INVALHANDLE;
831
832     return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L);
833 }
834
835 /**************************************************************************
836  *                              midiInReset                     [WINMM.@]
837  */
838 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
839 {
840     LPWINE_MLD          wmld;
841
842     TRACE("(%p)\n", hMidiIn);
843
844     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
845         return MMSYSERR_INVALHANDLE;
846
847     return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L);
848 }
849
850 /**************************************************************************
851  *                              midiInGetID                     [WINMM.@]
852  */
853 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
854 {
855     LPWINE_MLD          wmld;
856
857     TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
858
859     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
860
861     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
862         return MMSYSERR_INVALHANDLE;
863
864     *lpuDeviceID = wmld->uDeviceID;
865
866     return MMSYSERR_NOERROR;
867 }
868
869 /**************************************************************************
870  *                              midiInMessage           [WINMM.@]
871  */
872 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
873                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
874 {
875     LPWINE_MLD          wmld;
876
877     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
878
879     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
880         return MMSYSERR_INVALHANDLE;
881
882     switch (uMessage) {
883     case MIDM_OPEN:
884     case MIDM_CLOSE:
885         FIXME("can't handle OPEN or CLOSE message!\n");
886         return MMSYSERR_NOTSUPPORTED;
887     }
888     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
889 }
890
891 /**************************************************************************
892  *                              midiConnect                     [WINMM.@]
893  */
894 MMRESULT WINAPI midiConnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
895 {
896     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
897     return MMSYSERR_ERROR;
898 }
899
900 /**************************************************************************
901  *                              midiDisconnect                  [WINMM.@]
902  */
903 MMRESULT WINAPI midiDisconnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
904 {
905     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
906     return MMSYSERR_ERROR;
907 }
908
909 typedef struct WINE_MIDIStream {
910     HMIDIOUT                    hDevice;
911     HANDLE                      hThread;
912     DWORD                       dwThreadID;
913     DWORD                       dwTempo;
914     DWORD                       dwTimeDiv;
915     DWORD                       dwPositionMS;
916     DWORD                       dwPulses;
917     DWORD                       dwStartTicks;
918     WORD                        wFlags;
919     HANDLE                      hEvent;
920     LPMIDIHDR                   lpMidiHdr;
921 } WINE_MIDIStream;
922
923 #define WINE_MSM_HEADER         (WM_USER+0)
924 #define WINE_MSM_STOP           (WM_USER+1)
925 #define WINE_MSM_PAUSE          (WM_USER+2)
926 #define WINE_MSM_RESUME         (WM_USER+3)
927
928 /**************************************************************************
929  *                              MMSYSTEM_GetMidiStream          [internal]
930  */
931 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
932 {
933     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
934
935     if (lplpwm)
936         *lplpwm = lpwm;
937
938     if (lpwm == NULL) {
939         return FALSE;
940     }
941
942     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
943
944     return *lpMidiStrm != NULL;
945 }
946
947 /**************************************************************************
948  *                              MMSYSTEM_MidiStream_Convert     [internal]
949  */
950 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
951 {
952     DWORD       ret = 0;
953
954     if (lpMidiStrm->dwTimeDiv == 0) {
955         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
956     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
957         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
958         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
959         ret = (pulse * 1000) / (nf * nsf);
960     } else {
961         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
962                       (double)lpMidiStrm->dwTimeDiv);
963     }
964
965     return ret;
966 }
967
968 /**************************************************************************
969  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
970  */
971 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
972 {
973     LPMIDIHDR   lpMidiHdr;
974     LPMIDIHDR*  lpmh;
975     LPBYTE      lpData;
976     BOOL        paused = FALSE;
977
978     for (;;) {
979         switch (msg->message) {
980         case WM_QUIT:
981             return FALSE;
982         case WINE_MSM_STOP:
983             TRACE("STOP\n");
984             /* this is not quite what MS doc says... */
985             midiOutReset(lpMidiStrm->hDevice);
986             /* empty list of already submitted buffers */
987             lpMidiHdr = lpMidiStrm->lpMidiHdr;
988             lpMidiStrm->lpMidiHdr = NULL;
989             while (lpMidiHdr) {
990                 LPMIDIHDR lphdr = lpMidiHdr;
991                 lpMidiHdr = lpMidiHdr->lpNext;
992                 lphdr->dwFlags |= MHDR_DONE;
993                 lphdr->dwFlags &= ~MHDR_INQUEUE;
994
995                 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
996                                (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
997                                lpwm->mod.dwInstance, (DWORD_PTR)lphdr, 0);
998             }
999             return TRUE;
1000         case WINE_MSM_RESUME:
1001             /* FIXME: send out cc64 0 (turn off sustain pedal) on every channel */
1002             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
1003             return TRUE;
1004         case WINE_MSM_PAUSE:
1005             /* FIXME: send out cc64 0 (turn off sustain pedal) on every channel */
1006             paused = TRUE;
1007             break;
1008         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1009          * by native mcimidi, it doesn't look like a correct one".
1010          * this trick allows us to throw it away... but I don't like it.
1011          * It looks like part of the file I'm trying to play and definitively looks
1012          * like raw midi content.
1013          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1014          * synchronization issue where native mcimidi is still processing raw MIDI
1015          * content before generating MIDIEVENTs ?
1016          *
1017          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1018          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1019          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1020          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1021          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1022          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1023          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1024          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1025          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1026          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1027          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1028          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1029          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1030          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1031          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1032          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1033          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1034          */
1035         case WINE_MSM_HEADER:
1036             /* sets initial tick count for first MIDIHDR */
1037             if (!lpMidiStrm->dwStartTicks)
1038                 lpMidiStrm->dwStartTicks = GetTickCount();
1039             lpMidiHdr = (LPMIDIHDR)msg->lParam;
1040             lpData = (LPBYTE)lpMidiHdr->lpData;
1041             TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBytesRecorded=%u/%u dwFlags=0x%08x size=%lu]\n",
1042                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1043                   lpData, lpMidiHdr->dwBytesRecorded, lpMidiHdr->dwBufferLength,
1044                   lpMidiHdr->dwFlags, msg->wParam);
1045 #if 0
1046             /* dumps content of lpMidiHdr->lpData
1047              * FIXME: there should be a debug routine somewhere that already does this
1048              */
1049             for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1050                 DWORD       i;
1051                 BYTE        ch;
1052
1053                 for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1054                     printf("%02x ", lpData[dwToGo + i]);
1055                 for (; i < 16; i++)
1056                     printf("   ");
1057                 for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1058                     ch = lpData[dwToGo + i];
1059                     printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1060                 }
1061                 printf("\n");
1062             }
1063 #endif
1064             if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1065                 ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1066                 ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
1067                 FIXME("Dropping bad %s lpMidiHdr (streamID=%08x)\n",
1068                       (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1069                       ((LPMIDIEVENT)lpData)->dwStreamID);
1070                 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1071                 lpMidiHdr->dwFlags |= MHDR_DONE;
1072
1073                 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1074                                (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1075                                lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1076                 break;
1077             }
1078
1079             lpMidiHdr->lpNext = 0;
1080             for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = &(*lpmh)->lpNext);
1081             *lpmh = lpMidiHdr;
1082             break;
1083         default:
1084             FIXME("Unknown message %d\n", msg->message);
1085             break;
1086         }
1087         if (!paused)
1088             return TRUE;
1089         GetMessageA(msg, 0, 0, 0);
1090     }
1091 }
1092
1093 /**************************************************************************
1094  *                              MMSYSTEM_MidiStream_Player      [internal]
1095  */
1096 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
1097 {
1098     WINE_MIDIStream*    lpMidiStrm = pmt;
1099     WINE_MIDI*          lpwm;
1100     MSG                 msg;
1101     DWORD               dwToGo;
1102     DWORD               dwCurrTC;
1103     LPMIDIHDR           lpMidiHdr;
1104     DWORD               dwOffset;
1105
1106     TRACE("(%p)!\n", lpMidiStrm);
1107
1108     if (!lpMidiStrm ||
1109         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1110         goto the_end;
1111
1112     /* force thread's queue creation */
1113     PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
1114
1115     lpMidiStrm->dwStartTicks = 0;
1116     lpMidiStrm->dwPulses = 0;
1117
1118     lpMidiStrm->lpMidiHdr = 0;
1119
1120     /* midiStreamOpen is waiting for ack */
1121     SetEvent(lpMidiStrm->hEvent);
1122
1123 start_header:
1124     lpMidiHdr = lpMidiStrm->lpMidiHdr;
1125     if (!lpMidiHdr) {
1126         /* for first message, block until one arrives, then process all that are available */
1127         GetMessageA(&msg, 0, 0, 0);
1128         do {
1129             if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1130                 goto the_end;
1131         } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
1132         goto start_header;
1133     }
1134
1135     dwOffset = 0;
1136     while (dwOffset + offsetof(MIDIEVENT,dwParms) <= lpMidiHdr->dwBytesRecorded) {
1137         LPMIDIEVENT me = (LPMIDIEVENT)(lpMidiHdr->lpData+dwOffset);
1138
1139         /* do we have to wait ? */
1140         if (me->dwDeltaTime) {
1141             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1142             lpMidiStrm->dwPulses += me->dwDeltaTime;
1143
1144             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
1145
1146             TRACE("%u/%u/%u\n", dwToGo, GetTickCount(), me->dwDeltaTime);
1147             while (dwToGo - (dwCurrTC = GetTickCount()) <= MAXLONG) {
1148                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1149                     /* got a message, handle it */
1150                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1151                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1152                             goto the_end;
1153                         /* is lpMidiHdr still current? */
1154                         if (lpMidiHdr != lpMidiStrm->lpMidiHdr) {
1155                             goto start_header;
1156                         }
1157                     }
1158                 } else {
1159                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1160                     break;
1161                 }
1162             }
1163         }
1164         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1165         case MEVT_COMMENT:
1166             FIXME("NIY: MEVT_COMMENT\n");
1167             /* do nothing, skip bytes */
1168             break;
1169         case MEVT_LONGMSG:
1170             midiOutLongMsg(lpMidiStrm->hDevice, lpMidiStrm->lpMidiHdr, MEVT_EVENTPARM(me->dwEvent));
1171             break;
1172         case MEVT_NOP:
1173             break;
1174         case MEVT_SHORTMSG:
1175             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1176             break;
1177         case MEVT_TEMPO:
1178             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1179             break;
1180         case MEVT_VERSION:
1181             break;
1182         default:
1183             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1184             break;
1185         }
1186         if (me->dwEvent & MEVT_F_CALLBACK) {
1187             /* native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
1188             lpMidiHdr->dwOffset = dwOffset;
1189             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1190                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1191                            lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1192         }
1193         dwOffset += offsetof(MIDIEVENT,dwParms);
1194         if (me->dwEvent & MEVT_F_LONG)
1195             dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1196     }
1197     /* done with this header */
1198     lpMidiStrm->lpMidiHdr = lpMidiHdr->lpNext;
1199     lpMidiHdr->dwFlags |= MHDR_DONE;
1200     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1201
1202     DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1203                    (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1204                    lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1205     goto start_header;
1206
1207 the_end:
1208     TRACE("End of thread\n");
1209     return 0;
1210 }
1211
1212 /**************************************************************************
1213  *                              midiStreamClose                 [WINMM.@]
1214  */
1215 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1216 {
1217     WINE_MIDIStream*    lpMidiStrm;
1218     MMRESULT            ret = 0;
1219
1220     TRACE("(%p)!\n", hMidiStrm);
1221
1222     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1223         return MMSYSERR_INVALHANDLE;
1224
1225     midiStreamStop(hMidiStrm);
1226     PostThreadMessageA(lpMidiStrm->dwThreadID, WM_QUIT, 0, 0);
1227     CloseHandle(lpMidiStrm->hEvent);
1228     if (lpMidiStrm->hThread) {
1229         if (GetCurrentThreadId() != lpMidiStrm->dwThreadID)
1230             WaitForSingleObject(lpMidiStrm->hThread, INFINITE);
1231         else {
1232             FIXME("leak from call within function callback\n");
1233             ret = MMSYSERR_HANDLEBUSY; /* yet don't signal it to app */
1234         }
1235         CloseHandle(lpMidiStrm->hThread);
1236     }
1237     if(!ret)
1238         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1239
1240     return midiOutClose((HMIDIOUT)hMidiStrm);
1241 }
1242
1243 /**************************************************************************
1244  *                              midiStreamOpen                  [WINMM.@]
1245  */
1246 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1247                                DWORD cMidi, DWORD_PTR dwCallback,
1248                                DWORD_PTR dwInstance, DWORD fdwOpen)
1249 {
1250     WINE_MIDIStream*    lpMidiStrm;
1251     MMRESULT            ret;
1252     MIDIOPENSTRMID      mosm;
1253     LPWINE_MIDI         lpwm;
1254     HMIDIOUT            hMidiOut;
1255
1256     TRACE("(%p, %p, %d, 0x%08lx, 0x%08lx, 0x%08x)!\n",
1257           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1258
1259     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1260         return MMSYSERR_INVALPARAM;
1261
1262     ret = WINMM_CheckCallback(dwCallback, fdwOpen, FALSE);
1263     if (ret != MMSYSERR_NOERROR)
1264         return ret;
1265
1266     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
1267     if (!lpMidiStrm)
1268         return MMSYSERR_NOMEM;
1269
1270     lpMidiStrm->dwTempo = 500000;
1271     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quarter notes per minute *//* FIXME ??*/
1272     lpMidiStrm->dwPositionMS = 0;
1273
1274     mosm.dwStreamID = (DWORD)lpMidiStrm;
1275     /* FIXME: the correct value is not allocated yet for MAPPER */
1276     mosm.wDeviceID  = *lpuDeviceID;
1277     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm);
1278     if (!lpwm) {
1279         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1280         return MMSYSERR_NOMEM;
1281     }
1282     lpMidiStrm->hDevice = hMidiOut;
1283     *lphMidiStrm = (HMIDISTRM)hMidiOut;
1284
1285     lpwm->mld.uDeviceID = *lpuDeviceID;
1286
1287     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD_PTR)&lpwm->mod, fdwOpen);
1288     if (ret != MMSYSERR_NOERROR) {
1289         MMDRV_Free(hMidiOut, &lpwm->mld);
1290         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1291         return ret;
1292     }
1293
1294     lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1295     lpMidiStrm->wFlags = HIWORD(fdwOpen);
1296
1297     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1298                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1299
1300     if (!lpMidiStrm->hThread) {
1301         midiStreamClose((HMIDISTRM)hMidiOut);
1302         return MMSYSERR_NOMEM;
1303     }
1304     SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
1305
1306     /* wait for thread to have started, and for its queue to be created */
1307     WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1308
1309     PostThreadMessageA(lpMidiStrm->dwThreadID, WINE_MSM_PAUSE, 0, 0);
1310
1311     TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1312           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1313     return ret;
1314 }
1315
1316 /**************************************************************************
1317  *                              midiStreamOut                   [WINMM.@]
1318  */
1319 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
1320                               UINT cbMidiHdr)
1321 {
1322     WINE_MIDIStream*    lpMidiStrm;
1323     DWORD               ret = MMSYSERR_NOERROR;
1324
1325     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
1326
1327     if (cbMidiHdr < offsetof(MIDIHDR,dwOffset) || !lpMidiHdr || !lpMidiHdr->lpData
1328         || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded
1329         || lpMidiHdr->dwBytesRecorded % 4 /* player expects DWORD padding */)
1330         return MMSYSERR_INVALPARAM;
1331     /* FIXME: Native additionally checks if the MIDIEVENTs in lpData
1332      * exactly fit dwBytesRecorded. */
1333
1334     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1335         return MIDIERR_UNPREPARED;
1336
1337     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1338         return MIDIERR_STILLPLAYING;
1339
1340     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1341         ret = MMSYSERR_INVALHANDLE;
1342     } else {
1343         lpMidiHdr->dwFlags |= MHDR_ISSTRM | MHDR_INQUEUE;
1344         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1345         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
1346                                 WINE_MSM_HEADER, cbMidiHdr,
1347                                 (LPARAM)lpMidiHdr)) {
1348             ERR("bad PostThreadMessageA\n");
1349             ret = MMSYSERR_ERROR;
1350         }
1351     }
1352     return ret;
1353 }
1354
1355 /**************************************************************************
1356  *                              midiStreamPause                 [WINMM.@]
1357  */
1358 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
1359 {
1360     WINE_MIDIStream*    lpMidiStrm;
1361     DWORD               ret = MMSYSERR_NOERROR;
1362
1363     TRACE("(%p)!\n", hMidiStrm);
1364
1365     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1366         return MMSYSERR_INVALHANDLE;
1367     PostThreadMessageA(lpMidiStrm->dwThreadID, WINE_MSM_PAUSE, 0, 0);
1368     return ret;
1369 }
1370
1371 /**************************************************************************
1372  *                              midiStreamPosition              [WINMM.@]
1373  */
1374 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
1375 {
1376     WINE_MIDIStream*    lpMidiStrm;
1377     DWORD               ret = MMSYSERR_NOERROR;
1378
1379     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
1380
1381     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1382         ret = MMSYSERR_INVALHANDLE;
1383     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
1384         ret = MMSYSERR_INVALPARAM;
1385     } else {
1386         switch (lpMMT->wType) {
1387         default:
1388             FIXME("Unsupported time type %x\n", lpMMT->wType);
1389         /* fall through */
1390         case TIME_BYTES:
1391         case TIME_SAMPLES:
1392             lpMMT->wType = TIME_MS;
1393             /* fall through to alternative format */
1394         case TIME_MS:
1395             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
1396             TRACE("=> %d ms\n", lpMMT->u.ms);
1397             break;
1398         case TIME_TICKS:
1399             lpMMT->u.ticks = lpMidiStrm->dwPulses;
1400             TRACE("=> %d ticks\n", lpMMT->u.ticks);
1401             break;
1402         }
1403     }
1404     return ret;
1405 }
1406
1407 /**************************************************************************
1408  *                              midiStreamProperty              [WINMM.@]
1409  */
1410 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
1411 {
1412     WINE_MIDIStream*    lpMidiStrm;
1413     MMRESULT            ret = MMSYSERR_NOERROR;
1414
1415     TRACE("(%p, %p, %x)\n", hMidiStrm, lpPropData, dwProperty);
1416
1417     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1418         ret = MMSYSERR_INVALHANDLE;
1419     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
1420         ret = MMSYSERR_INVALPARAM;
1421     } else if (dwProperty & MIDIPROP_TEMPO) {
1422         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
1423
1424         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
1425             ret = MMSYSERR_INVALPARAM;
1426         } else if (dwProperty & MIDIPROP_SET) {
1427             lpMidiStrm->dwTempo = mpt->dwTempo;
1428             TRACE("Setting tempo to %d\n", mpt->dwTempo);
1429         } else if (dwProperty & MIDIPROP_GET) {
1430             mpt->dwTempo = lpMidiStrm->dwTempo;
1431             TRACE("Getting tempo <= %d\n", mpt->dwTempo);
1432         }
1433     } else if (dwProperty & MIDIPROP_TIMEDIV) {
1434         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
1435
1436         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
1437             ret = MMSYSERR_INVALPARAM;
1438         } else if (dwProperty & MIDIPROP_SET) {
1439             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
1440             TRACE("Setting time div to %d\n", mptd->dwTimeDiv);
1441         } else if (dwProperty & MIDIPROP_GET) {
1442             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
1443             TRACE("Getting time div <= %d\n", mptd->dwTimeDiv);
1444         }
1445     } else {
1446         ret = MMSYSERR_INVALPARAM;
1447     }
1448
1449     return ret;
1450 }
1451
1452 /**************************************************************************
1453  *                              midiStreamRestart               [WINMM.@]
1454  */
1455 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
1456 {
1457     WINE_MIDIStream*    lpMidiStrm;
1458     MMRESULT            ret = MMSYSERR_NOERROR;
1459
1460     TRACE("(%p)!\n", hMidiStrm);
1461
1462     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1463         return MMSYSERR_INVALHANDLE;
1464     PostThreadMessageA(lpMidiStrm->dwThreadID, WINE_MSM_RESUME, 0, 0);
1465     return ret;
1466 }
1467
1468 /**************************************************************************
1469  *                              midiStreamStop                  [WINMM.@]
1470  */
1471 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
1472 {
1473     WINE_MIDIStream*    lpMidiStrm;
1474     MMRESULT            ret = MMSYSERR_NOERROR;
1475
1476     TRACE("(%p)!\n", hMidiStrm);
1477
1478     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1479         return MMSYSERR_INVALHANDLE;
1480     PostThreadMessageA(lpMidiStrm->dwThreadID, WINE_MSM_STOP, 0, 0);
1481     return ret;
1482 }
1483
1484 struct mm_starter
1485 {
1486     LPTASKCALLBACK      cb;
1487     DWORD               client;
1488     HANDLE              event;
1489 };
1490
1491 static DWORD WINAPI mmTaskRun(void* pmt)
1492 {
1493     struct mm_starter mms;
1494
1495     memcpy(&mms, pmt, sizeof(struct mm_starter));
1496     HeapFree(GetProcessHeap(), 0, pmt);
1497     mms.cb(mms.client);
1498     if (mms.event) SetEvent(mms.event);
1499     return 0;
1500 }
1501
1502 /******************************************************************
1503  *              mmTaskCreate (WINMM.@)
1504  */
1505 UINT     WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR client)
1506 {
1507     HANDLE               hThread;
1508     HANDLE               hEvent = 0;
1509     struct mm_starter   *mms;
1510
1511     mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));
1512     if (mms == NULL) return TASKERR_OUTOFMEMORY;
1513
1514     mms->cb = cb;
1515     mms->client = client;
1516     if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1517     mms->event = hEvent;
1518
1519     hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
1520     if (!hThread) {
1521         HeapFree(GetProcessHeap(), 0, mms);
1522         if (hEvent) CloseHandle(hEvent);
1523         return TASKERR_OUTOFMEMORY;
1524     }
1525     SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
1526     if (ph) *ph = hEvent;
1527     CloseHandle(hThread);
1528     return 0;
1529 }
1530
1531 /******************************************************************
1532  *              mmTaskBlock (WINMM.@)
1533  */
1534 VOID     WINAPI mmTaskBlock(DWORD tid)
1535 {
1536     MSG         msg;
1537
1538     do
1539     {
1540         GetMessageA(&msg, 0, 0, 0);
1541         if (msg.hwnd) DispatchMessageA(&msg);
1542     } while (msg.message != WM_USER);
1543 }
1544
1545 /******************************************************************
1546  *              mmTaskSignal (WINMM.@)
1547  */
1548 BOOL     WINAPI mmTaskSignal(DWORD tid)
1549 {
1550     return PostThreadMessageW(tid, WM_USER, 0, 0);
1551 }
1552
1553 /******************************************************************
1554  *              mmTaskYield (WINMM.@)
1555  */
1556 VOID     WINAPI mmTaskYield(VOID) {}
1557
1558 /******************************************************************
1559  *              mmGetCurrentTask (WINMM.@)
1560  */
1561 DWORD    WINAPI mmGetCurrentTask(VOID)
1562 {
1563     return GetCurrentThreadId();
1564 }