Handle case where partial structure update requested.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 #include <stdio.h>
32 #include <stdarg.h>
33 #include <string.h>
34
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "mmsystem.h"
40 #include "winuser.h"
41 #include "winnls.h"
42 #include "winreg.h"
43 #include "winternl.h"
44 #include "winemm.h"
45 #include "wownt32.h"
46
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
50
51 /******************************************************************
52  *              MyUserYield
53  *
54  * Internal wrapper to call USER.UserYield16 (in fact through a Wine only export from USER32).
55  */
56 static void MyUserYield(void)
57 {
58     HMODULE mod = GetModuleHandleA( "user32.dll" );
59     if (mod)
60     {
61         FARPROC proc = GetProcAddress( mod, "UserYield16" );
62         if (proc) proc();
63     }
64 }
65
66 void    (WINAPI *pFnReleaseThunkLock)(DWORD*);
67 void    (WINAPI *pFnRestoreThunkLock)(DWORD);
68
69 /* ========================================================================
70  *                   G L O B A L   S E T T I N G S
71  * ========================================================================*/
72
73 LPWINE_MM_IDATA         WINMM_IData /* = NULL */;
74
75 /**************************************************************************
76  *                      WINMM_CreateIData                       [internal]
77  */
78 static  BOOL    WINMM_CreateIData(HINSTANCE hInstDLL)
79 {
80     WINMM_IData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MM_IDATA));
81
82     if (!WINMM_IData)
83         return FALSE;
84     WINMM_IData->hWinMM32Instance = hInstDLL;
85     InitializeCriticalSection(&WINMM_IData->cs);
86     WINMM_IData->cs.DebugInfo = (void*)__FILE__ ": WinMM";
87     WINMM_IData->psStopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
88     WINMM_IData->psLastEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
89     TRACE("Created IData (%p)\n", WINMM_IData);
90     return TRUE;
91 }
92
93 /**************************************************************************
94  *                      WINMM_DeleteIData                       [internal]
95  */
96 static  void WINMM_DeleteIData(void)
97 {
98     if (WINMM_IData) {
99         TIME_MMTimeStop();
100
101         /* FIXME: should also free content and resources allocated
102          * inside WINMM_IData */
103         CloseHandle(WINMM_IData->psStopEvent);
104         CloseHandle(WINMM_IData->psLastEvent);
105         DeleteCriticalSection(&WINMM_IData->cs);
106         HeapFree(GetProcessHeap(), 0, WINMM_IData);
107         WINMM_IData = NULL;
108     }
109 }
110
111 /******************************************************************
112  *             WINMM_LoadMMSystem
113  *
114  */
115 static HANDLE (WINAPI *pGetModuleHandle16)(LPCSTR);
116 static DWORD (WINAPI *pLoadLibrary16)(LPCSTR);
117
118 BOOL WINMM_CheckForMMSystem(void)
119 {
120     /* 0 is not checked yet, -1 is not present, 1 is present */
121     static      int    loaded /* = 0 */;
122
123     if (loaded == 0)
124     {
125         HANDLE      h = GetModuleHandleA("kernel32");
126         loaded = -1;
127         if (h)
128         {
129             pGetModuleHandle16 = (void*)GetProcAddress(h, "GetModuleHandle16");
130             pLoadLibrary16 = (void*)GetProcAddress(h, "LoadLibrary16");
131             if (pGetModuleHandle16 && pLoadLibrary16 &&
132                 (pGetModuleHandle16("MMSYSTEM.DLL") || pLoadLibrary16("MMSYSTEM.DLL")))
133                 loaded = 1;
134         }
135     }
136     return loaded > 0;
137 }
138
139 /******************************************************************
140  *             WINMM_ErrorToString
141  */
142 const char* WINMM_ErrorToString(MMRESULT error)
143 {
144 #define ERR_TO_STR(dev) case dev: return #dev
145     static char unknown[32];
146     switch (error) {
147     ERR_TO_STR(MMSYSERR_NOERROR);
148     ERR_TO_STR(MMSYSERR_ERROR);
149     ERR_TO_STR(MMSYSERR_BADDEVICEID);
150     ERR_TO_STR(MMSYSERR_NOTENABLED);
151     ERR_TO_STR(MMSYSERR_ALLOCATED);
152     ERR_TO_STR(MMSYSERR_INVALHANDLE);
153     ERR_TO_STR(MMSYSERR_NODRIVER);
154     ERR_TO_STR(MMSYSERR_NOMEM);
155     ERR_TO_STR(MMSYSERR_NOTSUPPORTED);
156     ERR_TO_STR(MMSYSERR_BADERRNUM);
157     ERR_TO_STR(MMSYSERR_INVALFLAG);
158     ERR_TO_STR(MMSYSERR_INVALPARAM);
159     ERR_TO_STR(WAVERR_BADFORMAT);
160     ERR_TO_STR(WAVERR_STILLPLAYING);
161     ERR_TO_STR(WAVERR_UNPREPARED);
162     ERR_TO_STR(WAVERR_SYNC);
163     }
164     sprintf(unknown, "Unknown(0x%08x)", error);
165     return unknown;
166 #undef ERR_TO_STR
167 }
168
169 /**************************************************************************
170  *              DllMain (WINMM.init)
171  *
172  * WINMM DLL entry point
173  *
174  */
175 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
176 {
177     TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, fImpLoad);
178
179     switch (fdwReason) {
180     case DLL_PROCESS_ATTACH:
181         DisableThreadLibraryCalls(hInstDLL);
182
183         if (!WINMM_CreateIData(hInstDLL))
184             return FALSE;
185         if (!MCI_Init() || !MMDRV_Init()) {
186             WINMM_DeleteIData();
187             return FALSE;
188         }
189         break;
190     case DLL_PROCESS_DETACH:
191         /* close all opened MCI drivers */
192         MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L, TRUE);
193         MMDRV_Exit();
194         /* now unload all remaining drivers... */
195         DRIVER_UnloadAll();
196
197         WINMM_DeleteIData();
198         break;
199     }
200     return TRUE;
201 }
202
203 /**************************************************************************
204  *      Mixer devices. New to Win95
205  */
206
207 /**************************************************************************
208  * find out the real mixer ID depending on hmix (depends on dwFlags)
209  */
210 static LPWINE_MIXER MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags)
211 {
212     LPWINE_MIXER        lpwm = NULL;
213
214     switch (dwFlags & 0xF0000000ul) {
215     case MIXER_OBJECTF_MIXER:
216         lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);
217         break;
218     case MIXER_OBJECTF_HMIXER:
219         lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);
220         break;
221     case MIXER_OBJECTF_WAVEOUT:
222         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE,  MMDRV_MIXER);
223         break;
224     case MIXER_OBJECTF_HWAVEOUT:
225         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);
226         break;
227     case MIXER_OBJECTF_WAVEIN:
228         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN,  TRUE,  MMDRV_MIXER);
229         break;
230     case MIXER_OBJECTF_HWAVEIN:
231         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN,  FALSE, MMDRV_MIXER);
232         break;
233     case MIXER_OBJECTF_MIDIOUT:
234         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE,  MMDRV_MIXER);
235         break;
236     case MIXER_OBJECTF_HMIDIOUT:
237         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);
238         break;
239     case MIXER_OBJECTF_MIDIIN:
240         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN,  TRUE,  MMDRV_MIXER);
241         break;
242     case MIXER_OBJECTF_HMIDIIN:
243         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN,  FALSE, MMDRV_MIXER);
244         break;
245     case MIXER_OBJECTF_AUX:
246         lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX,     TRUE,  MMDRV_MIXER);
247         break;
248     default:
249         FIXME("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);
250         break;
251     }
252     return lpwm;
253 }
254
255 /**************************************************************************
256  *                              mixerGetNumDevs                 [WINMM.@]
257  */
258 UINT WINAPI mixerGetNumDevs(void)
259 {
260     return MMDRV_GetNum(MMDRV_MIXER);
261 }
262
263 /**************************************************************************
264  *                              mixerGetDevCapsA                [WINMM.@]
265  */
266 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
267 {
268     LPWINE_MLD  wmld;
269
270     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
271
272     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIXER, TRUE)) == NULL)
273         return MMSYSERR_BADDEVICEID;
274
275     return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
276 }
277
278 /**************************************************************************
279  *                              mixerGetDevCapsW                [WINMM.@]
280  */
281 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
282 {
283     MIXERCAPSA  micA;
284     UINT        ret = mixerGetDevCapsA(uDeviceID, &micA, sizeof(micA));
285
286     if (ret == MMSYSERR_NOERROR) {
287         MIXERCAPSW micW;
288         micW.wMid           = micA.wMid;
289         micW.wPid           = micA.wPid;
290         micW.vDriverVersion = micA.vDriverVersion;
291         MultiByteToWideChar( CP_ACP, 0, micA.szPname, -1, micW.szPname,
292                              sizeof(micW.szPname)/sizeof(WCHAR) );
293         micW.fdwSupport     = micA.fdwSupport;
294         micW.cDestinations  = micA.cDestinations;
295         memcpy(lpCaps, &micW, min(uSize, sizeof(micW)));
296     }
297     return ret;
298 }
299
300 UINT  MIXER_Open(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
301                  DWORD_PTR dwInstance, DWORD fdwOpen, BOOL bFrom32)
302 {
303     HANDLE              hMix;
304     LPWINE_MLD          wmld;
305     DWORD               dwRet = 0;
306     MIXEROPENDESC       mod;
307
308     TRACE("(%p, %d, %08lx, %08lx, %08lx)\n",
309           lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
310
311     wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,
312                        &dwCallback, &dwInstance, bFrom32);
313
314     wmld->uDeviceID = uDeviceID;
315     mod.hmx = (HMIXEROBJ)hMix;
316     mod.dwCallback = dwCallback;
317     mod.dwInstance = dwInstance;
318
319     dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD)&mod, fdwOpen);
320
321     if (dwRet != MMSYSERR_NOERROR) {
322         MMDRV_Free(hMix, wmld);
323         hMix = 0;
324     }
325     if (lphMix) *lphMix = hMix;
326     TRACE("=> %ld hMixer=%p\n", dwRet, hMix);
327
328     return dwRet;
329 }
330
331 /**************************************************************************
332  *                              mixerOpen                       [WINMM.@]
333  */
334 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
335                      DWORD_PTR dwInstance, DWORD fdwOpen)
336 {
337     return MIXER_Open(lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen, TRUE);
338 }
339
340 /**************************************************************************
341  *                              mixerClose                      [WINMM.@]
342  */
343 UINT WINAPI mixerClose(HMIXER hMix)
344 {
345     LPWINE_MLD          wmld;
346     DWORD               dwRet;
347
348     TRACE("(%p)\n", hMix);
349
350     if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;
351
352     dwRet = MMDRV_Close(wmld, MXDM_CLOSE);
353     MMDRV_Free(hMix, wmld);
354
355     return dwRet;
356 }
357
358 /**************************************************************************
359  *                              mixerGetID                      [WINMM.@]
360  */
361 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
362 {
363     LPWINE_MIXER        lpwm;
364
365     TRACE("(%p %p %08lx)\n", hmix, lpid, fdwID);
366
367     if ((lpwm = MIXER_GetDev(hmix, fdwID)) == NULL) {
368         return MMSYSERR_INVALHANDLE;
369     }
370
371     if (lpid)
372       *lpid = lpwm->mld.uDeviceID;
373
374     return MMSYSERR_NOERROR;
375 }
376
377 /**************************************************************************
378  *                              mixerGetControlDetailsA         [WINMM.@]
379  */
380 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
381                                     DWORD fdwDetails)
382 {
383     LPWINE_MIXER        lpwm;
384
385     TRACE("(%p, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
386
387     if ((lpwm = MIXER_GetDev(hmix, fdwDetails)) == NULL)
388         return MMSYSERR_INVALHANDLE;
389
390     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
391         return MMSYSERR_INVALPARAM;
392
393     return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD_PTR)lpmcdA,
394                          fdwDetails, TRUE);
395 }
396
397 /**************************************************************************
398  *                              mixerGetControlDetailsW [WINMM.@]
399  */
400 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
401 {
402     DWORD                       ret = MMSYSERR_NOTENABLED;
403
404     TRACE("(%p, %p, %08lx)\n", hmix, lpmcd, fdwDetails);
405
406     if (lpmcd == NULL || lpmcd->cbStruct != sizeof(*lpmcd))
407         return MMSYSERR_INVALPARAM;
408
409     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
410     case MIXER_GETCONTROLDETAILSF_VALUE:
411         /* can savely use W structure as it is, no string inside */
412         ret = mixerGetControlDetailsA(hmix, lpmcd, fdwDetails);
413         break;
414     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
415         {
416             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW = (MIXERCONTROLDETAILS_LISTTEXTW *)lpmcd->paDetails;
417             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA;
418             int size = max(1, lpmcd->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
419             int i;
420
421             if (lpmcd->u.cMultipleItems != 0) {
422                 size *= lpmcd->u.cMultipleItems;
423             }
424             pDetailsA = (MIXERCONTROLDETAILS_LISTTEXTA *)HeapAlloc(GetProcessHeap(), 0, size);
425             lpmcd->paDetails = pDetailsA;
426             lpmcd->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
427             /* set up lpmcd->paDetails */
428             ret = mixerGetControlDetailsA(hmix, lpmcd, fdwDetails);
429             /* copy from lpmcd->paDetails back to paDetailsW; */
430             if(ret == MMSYSERR_NOERROR) {
431                 for(i=0;i<lpmcd->u.cMultipleItems*lpmcd->cChannels;i++) {
432                     pDetailsW->dwParam1 = pDetailsA->dwParam1;
433                     pDetailsW->dwParam2 = pDetailsA->dwParam2;
434                     MultiByteToWideChar( CP_ACP, 0, pDetailsA->szName, -1,
435                                          pDetailsW->szName,
436                                          sizeof(pDetailsW->szName)/sizeof(WCHAR) );
437                     pDetailsA++;
438                     pDetailsW++;
439                 }
440                 pDetailsA -= lpmcd->u.cMultipleItems*lpmcd->cChannels;
441                 pDetailsW -= lpmcd->u.cMultipleItems*lpmcd->cChannels;
442             }
443             HeapFree(GetProcessHeap(), 0, pDetailsA);
444             lpmcd->paDetails = pDetailsW;
445             lpmcd->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
446         }
447         break;
448     default:
449         ERR("Unsupported fdwDetails=0x%08lx\n", fdwDetails);
450     }
451
452     return ret;
453 }
454
455 /**************************************************************************
456  *                              mixerGetLineControlsA   [WINMM.@]
457  */
458 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
459                                   DWORD fdwControls)
460 {
461     LPWINE_MIXER        lpwm;
462
463     TRACE("(%p, %p, %08lx)\n", hmix, lpmlcA, fdwControls);
464
465     if ((lpwm = MIXER_GetDev(hmix, fdwControls)) == NULL)
466         return MMSYSERR_INVALHANDLE;
467
468     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA))
469         return MMSYSERR_INVALPARAM;
470
471     return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD_PTR)lpmlcA,
472                          fdwControls, TRUE);
473 }
474
475 /**************************************************************************
476  *                              mixerGetLineControlsW           [WINMM.@]
477  */
478 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
479                                   DWORD fdwControls)
480 {
481     MIXERLINECONTROLSA  mlcA;
482     DWORD               ret;
483     int                 i;
484
485     TRACE("(%p, %p, %08lx)\n", hmix, lpmlcW, fdwControls);
486
487     if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW) ||
488         lpmlcW->cbmxctrl != sizeof(MIXERCONTROLW))
489         return MMSYSERR_INVALPARAM;
490
491     mlcA.cbStruct = sizeof(mlcA);
492     mlcA.dwLineID = lpmlcW->dwLineID;
493     mlcA.u.dwControlID = lpmlcW->u.dwControlID;
494     mlcA.u.dwControlType = lpmlcW->u.dwControlType;
495     mlcA.cControls = lpmlcW->cControls;
496     mlcA.cbmxctrl = sizeof(MIXERCONTROLA);
497     mlcA.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
498                               mlcA.cControls * mlcA.cbmxctrl);
499
500     ret = mixerGetLineControlsA(hmix, &mlcA, fdwControls);
501
502     if (ret == MMSYSERR_NOERROR) {
503         lpmlcW->dwLineID = mlcA.dwLineID;
504         lpmlcW->u.dwControlID = mlcA.u.dwControlID;
505         lpmlcW->u.dwControlType = mlcA.u.dwControlType;
506         lpmlcW->cControls = mlcA.cControls;
507
508         for (i = 0; i < mlcA.cControls; i++) {
509             lpmlcW->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLW);
510             lpmlcW->pamxctrl[i].dwControlID = mlcA.pamxctrl[i].dwControlID;
511             lpmlcW->pamxctrl[i].dwControlType = mlcA.pamxctrl[i].dwControlType;
512             lpmlcW->pamxctrl[i].fdwControl = mlcA.pamxctrl[i].fdwControl;
513             lpmlcW->pamxctrl[i].cMultipleItems = mlcA.pamxctrl[i].cMultipleItems;
514             MultiByteToWideChar( CP_ACP, 0, mlcA.pamxctrl[i].szShortName, -1,
515                                  lpmlcW->pamxctrl[i].szShortName,
516                                  sizeof(lpmlcW->pamxctrl[i].szShortName)/sizeof(WCHAR) );
517             MultiByteToWideChar( CP_ACP, 0, mlcA.pamxctrl[i].szName, -1,
518                                  lpmlcW->pamxctrl[i].szName,
519                                  sizeof(lpmlcW->pamxctrl[i].szName)/sizeof(WCHAR) );
520             /* sizeof(lpmlcW->pamxctrl[i].Bounds) ==
521              * sizeof(mlcA.pamxctrl[i].Bounds) */
522             memcpy(&lpmlcW->pamxctrl[i].Bounds, &mlcA.pamxctrl[i].Bounds,
523                    sizeof(mlcA.pamxctrl[i].Bounds));
524             /* sizeof(lpmlcW->pamxctrl[i].Metrics) ==
525              * sizeof(mlcA.pamxctrl[i].Metrics) */
526             memcpy(&lpmlcW->pamxctrl[i].Metrics, &mlcA.pamxctrl[i].Metrics,
527                    sizeof(mlcA.pamxctrl[i].Metrics));
528         }
529     }
530
531     HeapFree(GetProcessHeap(), 0, mlcA.pamxctrl);
532
533     return ret;
534 }
535
536 /**************************************************************************
537  *                              mixerGetLineInfoA               [WINMM.@]
538  */
539 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliW, DWORD fdwInfo)
540 {
541     LPWINE_MIXER        lpwm;
542
543     TRACE("(%p, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
544
545     if ((lpwm = MIXER_GetDev(hmix, fdwInfo)) == NULL)
546         return MMSYSERR_INVALHANDLE;
547
548     return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD_PTR)lpmliW,
549                          fdwInfo, TRUE);
550 }
551
552 /**************************************************************************
553  *                              mixerGetLineInfoW               [WINMM.@]
554  */
555 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW,
556                               DWORD fdwInfo)
557 {
558     MIXERLINEA          mliA;
559     UINT                ret;
560
561     TRACE("(%p, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
562
563     if (lpmliW == NULL || lpmliW->cbStruct != sizeof(*lpmliW))
564         return MMSYSERR_INVALPARAM;
565
566     mliA.cbStruct = sizeof(mliA);
567     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
568     case MIXER_GETLINEINFOF_COMPONENTTYPE:
569         mliA.dwComponentType = lpmliW->dwComponentType;
570         break;
571     case MIXER_GETLINEINFOF_DESTINATION:
572         mliA.dwDestination = lpmliW->dwDestination;
573         break;
574     case MIXER_GETLINEINFOF_LINEID:
575         mliA.dwLineID = lpmliW->dwLineID;
576         break;
577     case MIXER_GETLINEINFOF_SOURCE:
578         mliA.dwDestination = lpmliW->dwDestination;
579         mliA.dwSource = lpmliW->dwSource;
580         break;
581     case MIXER_GETLINEINFOF_TARGETTYPE:
582         mliA.Target.dwType = lpmliW->Target.dwType;
583         mliA.Target.wMid = lpmliW->Target.wMid;
584         mliA.Target.wPid = lpmliW->Target.wPid;
585         mliA.Target.vDriverVersion = lpmliW->Target.vDriverVersion;
586         WideCharToMultiByte( CP_ACP, 0, lpmliW->Target.szPname, -1, mliA.Target.szPname, sizeof(mliA.Target.szPname), NULL, NULL);
587         break;
588     default:
589         FIXME("Unsupported fdwControls=0x%08lx\n", fdwInfo);
590     }
591
592     ret = mixerGetLineInfoA(hmix, &mliA, fdwInfo);
593
594     lpmliW->dwDestination = mliA.dwDestination;
595     lpmliW->dwSource = mliA.dwSource;
596     lpmliW->dwLineID = mliA.dwLineID;
597     lpmliW->fdwLine = mliA.fdwLine;
598     lpmliW->dwUser = mliA.dwUser;
599     lpmliW->dwComponentType = mliA.dwComponentType;
600     lpmliW->cChannels = mliA.cChannels;
601     lpmliW->cConnections = mliA.cConnections;
602     lpmliW->cControls = mliA.cControls;
603     MultiByteToWideChar( CP_ACP, 0, mliA.szShortName, -1, lpmliW->szShortName,
604                          sizeof(lpmliW->szShortName)/sizeof(WCHAR) );
605     MultiByteToWideChar( CP_ACP, 0, mliA.szName, -1, lpmliW->szName,
606                          sizeof(lpmliW->szName)/sizeof(WCHAR) );
607     lpmliW->Target.dwType = mliA.Target.dwType;
608     lpmliW->Target.dwDeviceID = mliA.Target.dwDeviceID;
609     lpmliW->Target.wMid = mliA.Target.wMid;
610     lpmliW->Target.wPid = mliA.Target.wPid;
611     lpmliW->Target.vDriverVersion = mliA.Target.vDriverVersion;
612     MultiByteToWideChar( CP_ACP, 0, mliA.Target.szPname, -1, lpmliW->Target.szPname,
613                          sizeof(lpmliW->Target.szPname)/sizeof(WCHAR) );
614
615     return ret;
616 }
617
618 /**************************************************************************
619  *                              mixerSetControlDetails  [WINMM.@]
620  */
621 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
622                                    DWORD fdwDetails)
623 {
624     LPWINE_MIXER        lpwm;
625
626     TRACE("(%p, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
627
628     if ((lpwm = MIXER_GetDev(hmix, fdwDetails)) == NULL)
629         return MMSYSERR_INVALHANDLE;
630
631     return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD_PTR)lpmcdA,
632                          fdwDetails, TRUE);
633 }
634
635 /**************************************************************************
636  *                              mixerMessage            [WINMM.@]
637  */
638 UINT WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
639 {
640     LPWINE_MLD          wmld;
641
642     TRACE("(%04lx, %d, %08lx, %08lx): semi-stub?\n",
643           (DWORD)hmix, uMsg, dwParam1, dwParam2);
644
645     if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)
646         return MMSYSERR_INVALHANDLE;
647
648     return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2, TRUE);
649 }
650
651 /**************************************************************************
652  *                              auxGetNumDevs           [WINMM.@]
653  */
654 UINT WINAPI auxGetNumDevs(void)
655 {
656     return MMDRV_GetNum(MMDRV_AUX);
657 }
658
659 /**************************************************************************
660  *                              auxGetDevCapsW          [WINMM.@]
661  */
662 UINT WINAPI auxGetDevCapsW(UINT_PTR uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
663 {
664     AUXCAPSA    acA;
665     UINT        ret = auxGetDevCapsA(uDeviceID, &acA, sizeof(acA));
666
667     if (ret == MMSYSERR_NOERROR) {
668         AUXCAPSW acW;
669         acW.wMid           = acA.wMid;
670         acW.wPid           = acA.wPid;
671         acW.vDriverVersion = acA.vDriverVersion;
672         MultiByteToWideChar( CP_ACP, 0, acA.szPname, -1, acW.szPname,
673                              sizeof(acW.szPname)/sizeof(WCHAR) );
674         acW.wTechnology    = acA.wTechnology;
675         acW.dwSupport      = acA.dwSupport;
676         memcpy(lpCaps, &acW, min(uSize, sizeof(acW)));
677     }
678     return ret;
679 }
680
681 /**************************************************************************
682  *                              auxGetDevCapsA          [WINMM.@]
683  */
684 UINT WINAPI auxGetDevCapsA(UINT_PTR uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
685 {
686     LPWINE_MLD          wmld;
687
688     TRACE("(%04X, %p, %d) !\n", uDeviceID, lpCaps, uSize);
689
690     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
691
692     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
693         return MMSYSERR_INVALHANDLE;
694     return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
695 }
696
697 /**************************************************************************
698  *                              auxGetVolume            [WINMM.@]
699  */
700 UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
701 {
702     LPWINE_MLD          wmld;
703
704     TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
705
706     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
707         return MMSYSERR_INVALHANDLE;
708     return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);
709 }
710
711 /**************************************************************************
712  *                              auxSetVolume            [WINMM.@]
713  */
714 UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
715 {
716     LPWINE_MLD          wmld;
717
718     TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);
719
720     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
721         return MMSYSERR_INVALHANDLE;
722     return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);
723 }
724
725 /**************************************************************************
726  *                              auxOutMessage           [WINMM.@]
727  */
728 UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2)
729 {
730     LPWINE_MLD          wmld;
731
732     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
733         return MMSYSERR_INVALHANDLE;
734
735     return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);
736 }
737
738 /**************************************************************************
739  *                              mciGetErrorStringW              [WINMM.@]
740  */
741 BOOL WINAPI mciGetErrorStringW(MCIERROR wError, LPWSTR lpstrBuffer, UINT uLength)
742 {
743     char       bufstr[MAXERRORLENGTH];
744     BOOL       ret = mciGetErrorStringA(wError, bufstr, MAXERRORLENGTH);
745
746     MultiByteToWideChar( CP_ACP, 0, bufstr, -1, lpstrBuffer, uLength );
747     return ret;
748 }
749
750 /**************************************************************************
751  *                              mciGetErrorStringA              [WINMM.@]
752  */
753 BOOL WINAPI mciGetErrorStringA(MCIERROR dwError, LPSTR lpstrBuffer, UINT uLength)
754 {
755     BOOL                ret = FALSE;
756
757     if (lpstrBuffer != NULL && uLength > 0 &&
758         dwError >= MCIERR_BASE && dwError <= MCIERR_CUSTOM_DRIVER_BASE) {
759
760         if (LoadStringA(WINMM_IData->hWinMM32Instance,
761                         dwError, lpstrBuffer, uLength) > 0) {
762             ret = TRUE;
763         }
764     }
765     return ret;
766 }
767
768 /**************************************************************************
769  *                      mciDriverNotify                         [WINMM.@]
770  */
771 BOOL WINAPI mciDriverNotify(HWND hWndCallBack, MCIDEVICEID wDevID, UINT wStatus)
772 {
773     TRACE("(%p, %04x, %04X)\n", hWndCallBack, wDevID, wStatus);
774
775     return PostMessageW(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID);
776 }
777
778 /**************************************************************************
779  *                      mciGetDriverData                        [WINMM.@]
780  */
781 DWORD WINAPI mciGetDriverData(MCIDEVICEID uDeviceID)
782 {
783     LPWINE_MCIDRIVER    wmd;
784
785     TRACE("(%04x)\n", uDeviceID);
786
787     wmd = MCI_GetDriver(uDeviceID);
788
789     if (!wmd) {
790         WARN("Bad uDeviceID\n");
791         return 0L;
792     }
793
794     return wmd->dwPrivate;
795 }
796
797 /**************************************************************************
798  *                      mciSetDriverData                        [WINMM.@]
799  */
800 BOOL WINAPI mciSetDriverData(MCIDEVICEID uDeviceID, DWORD data)
801 {
802     LPWINE_MCIDRIVER    wmd;
803
804     TRACE("(%04x, %08lx)\n", uDeviceID, data);
805
806     wmd = MCI_GetDriver(uDeviceID);
807
808     if (!wmd) {
809         WARN("Bad uDeviceID\n");
810         return FALSE;
811     }
812
813     wmd->dwPrivate = data;
814     return TRUE;
815 }
816
817 /**************************************************************************
818  *                              mciSendCommandA                 [WINMM.@]
819  */
820 DWORD WINAPI mciSendCommandA(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
821 {
822     DWORD       dwRet;
823
824     TRACE("(%08x, %s, %08lx, %08lx)\n",
825           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
826
827     dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2, TRUE);
828     dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2);
829     TRACE("=> %08lx\n", dwRet);
830     return dwRet;
831 }
832
833 inline static LPSTR strdupWtoA( LPCWSTR str )
834 {
835     LPSTR ret;
836     INT len;
837
838     if (!str) return NULL;
839     len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
840     ret = HeapAlloc( GetProcessHeap(), 0, len );
841     if(ret) WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
842     return ret;
843 }
844
845 static int MCI_MapMsgWtoA(UINT msg, DWORD_PTR dwParam1, DWORD_PTR *dwParam2)
846 {
847     switch(msg)
848     {
849     case MCI_CLOSE:
850     case MCI_PLAY:
851     case MCI_SEEK:
852     case MCI_STOP:
853     case MCI_PAUSE:
854     case MCI_GETDEVCAPS:
855     case MCI_SPIN:
856     case MCI_SET:
857     case MCI_STEP:
858     case MCI_RECORD:
859     case MCI_BREAK:
860     case MCI_SOUND:
861     case MCI_STATUS:
862     case MCI_CUE:
863     case MCI_REALIZE:
864     case MCI_PUT:
865     case MCI_WHERE:
866     case MCI_FREEZE:
867     case MCI_UNFREEZE:
868     case MCI_CUT:
869     case MCI_COPY:
870     case MCI_PASTE:
871     case MCI_UPDATE:
872     case MCI_RESUME:
873     case MCI_DELETE:
874         return 0;
875
876     case MCI_OPEN:
877         {
878             MCI_OPEN_PARMSW *mci_openW = (MCI_OPEN_PARMSW *)*dwParam2;
879             MCI_OPEN_PARMSA *mci_openA;
880             DWORD_PTR *ptr;
881
882             ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_openA) + sizeof(DWORD_PTR));
883             if (!ptr) return -1;
884
885             *ptr++ = *dwParam2; /* save the previous pointer */
886             *dwParam2 = (DWORD_PTR)ptr;
887             mci_openA = (MCI_OPEN_PARMSA *)ptr;
888
889             if (dwParam1 & MCI_NOTIFY)
890                 mci_openA->dwCallback = mci_openW->dwCallback;
891
892             if (dwParam1 & MCI_OPEN_TYPE)
893             {
894                 if (dwParam1 & MCI_OPEN_TYPE_ID)
895                     mci_openA->lpstrDeviceType = (LPSTR)mci_openW->lpstrDeviceType;
896                 else
897                     mci_openA->lpstrDeviceType = strdupWtoA(mci_openW->lpstrDeviceType);
898             }
899             if (dwParam1 & MCI_OPEN_ELEMENT)
900             {
901                 if (dwParam1 & MCI_OPEN_ELEMENT_ID)
902                     mci_openA->lpstrElementName = (LPSTR)mci_openW->lpstrElementName;
903                 else
904                     mci_openA->lpstrElementName = strdupWtoA(mci_openW->lpstrElementName);
905             }
906             if (dwParam1 & MCI_OPEN_ALIAS)
907                 mci_openA->lpstrAlias = strdupWtoA(mci_openW->lpstrAlias);
908         }
909         return 1;
910
911     case MCI_WINDOW:
912         if (dwParam1 & MCI_ANIM_WINDOW_TEXT)
913         {
914             MCI_ANIM_WINDOW_PARMSW *mci_windowW = (MCI_ANIM_WINDOW_PARMSW *)*dwParam2;
915             MCI_ANIM_WINDOW_PARMSA *mci_windowA;
916
917             mci_windowA = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_windowA));
918             if (!mci_windowA) return -1;
919
920             *dwParam2 = (DWORD_PTR)mci_windowA;
921
922             mci_windowA->lpstrText = strdupWtoA(mci_windowW->lpstrText);
923
924             if (dwParam1 & MCI_NOTIFY)
925                 mci_windowA->dwCallback = mci_windowW->dwCallback;
926             if (dwParam1 & MCI_ANIM_WINDOW_HWND)
927                 mci_windowA->hWnd = mci_windowW->hWnd;
928             if (dwParam1 & MCI_ANIM_WINDOW_STATE)
929                 mci_windowA->nCmdShow = mci_windowW->nCmdShow;
930
931             return 1;
932         }
933         return 0;
934
935     case MCI_SYSINFO:
936         {
937             MCI_SYSINFO_PARMSW *mci_sysinfoW = (MCI_SYSINFO_PARMSW *)*dwParam2;
938             MCI_SYSINFO_PARMSA *mci_sysinfoA;
939             DWORD_PTR *ptr;
940
941             ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_sysinfoA) + sizeof(DWORD_PTR));
942             if (!ptr) return -1;
943
944             *ptr++ = *dwParam2; /* save the previous pointer */
945             *dwParam2 = (DWORD_PTR)ptr;
946             mci_sysinfoA = (MCI_SYSINFO_PARMSA *)ptr;
947
948             if (dwParam1 & MCI_NOTIFY)
949                 mci_sysinfoA->dwCallback = mci_sysinfoW->dwCallback;
950
951             mci_sysinfoA->dwRetSize = mci_sysinfoW->dwRetSize; /* FIXME */
952             mci_sysinfoA->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_sysinfoA->dwRetSize);
953
954             return 1;
955         }
956
957     case MCI_INFO:
958     case MCI_SAVE:
959     case MCI_LOAD:
960     case MCI_ESCAPE:
961     default:
962         FIXME("Message 0x%04x needs translation\n", msg);
963         return -1;
964     }
965     return 0;
966 }
967
968 static DWORD MCI_UnmapMsgWtoA(UINT msg, DWORD_PTR dwParam1, DWORD_PTR dwParam2,
969                               DWORD result)
970 {
971     switch(msg)
972     {
973     case MCI_OPEN:
974         {
975             DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
976             MCI_OPEN_PARMSW *mci_openW = (MCI_OPEN_PARMSW *)*ptr;
977             MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA *)(ptr + 1);
978
979             mci_openW->wDeviceID = mci_openA->wDeviceID;
980
981             if (dwParam1 & MCI_OPEN_TYPE)
982             {
983                 if (!(dwParam1 & MCI_OPEN_TYPE_ID))
984                     HeapFree(GetProcessHeap(), 0, mci_openA->lpstrDeviceType);
985             }
986             if (dwParam1 & MCI_OPEN_ELEMENT)
987             {
988                 if (!(dwParam1 & MCI_OPEN_ELEMENT_ID))
989                     HeapFree(GetProcessHeap(), 0, mci_openA->lpstrElementName);
990             }
991             if (dwParam1 & MCI_OPEN_ALIAS)
992                 HeapFree(GetProcessHeap(), 0, mci_openA->lpstrAlias);
993             HeapFree(GetProcessHeap(), 0, ptr);
994         }
995         break;
996
997     case MCI_WINDOW:
998         if (dwParam1 & MCI_ANIM_WINDOW_TEXT)
999         {
1000             MCI_ANIM_WINDOW_PARMSA *mci_windowA = (MCI_ANIM_WINDOW_PARMSA *)dwParam2;
1001
1002             HeapFree(GetProcessHeap(), 0, (void *)mci_windowA->lpstrText);
1003             HeapFree(GetProcessHeap(), 0, mci_windowA);
1004         }
1005         break;
1006
1007     case MCI_SYSINFO:
1008         {
1009             DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1;
1010             MCI_SYSINFO_PARMSW *mci_sysinfoW = (MCI_SYSINFO_PARMSW *)*ptr;
1011             MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)(ptr + 1);
1012
1013             if (!result)
1014             {
1015                 mci_sysinfoW->dwNumber = mci_sysinfoA->dwNumber;
1016                 mci_sysinfoW->wDeviceType = mci_sysinfoA->wDeviceType;
1017                 MultiByteToWideChar(CP_ACP, 0,
1018                                     mci_sysinfoA->lpstrReturn, mci_sysinfoA->dwRetSize,
1019                                     mci_sysinfoW->lpstrReturn, mci_sysinfoW->dwRetSize);
1020             }
1021
1022             HeapFree(GetProcessHeap(), 0, mci_sysinfoA->lpstrReturn);
1023             HeapFree(GetProcessHeap(), 0, ptr);
1024         }
1025         break;
1026
1027     default:
1028         FIXME("Message 0x%04x needs unmapping\n", msg);
1029         break;
1030     }
1031
1032     return result;
1033 }
1034
1035
1036 /**************************************************************************
1037  *                              mciSendCommandW                 [WINMM.@]
1038  *
1039  * FIXME: we should do the things other way around, but since our
1040  * MM subsystem is not unicode aware...
1041  */
1042 DWORD WINAPI mciSendCommandW(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1043 {
1044     DWORD ret;
1045     int mapped;
1046
1047     TRACE("(%08x, %s, %08lx, %08lx)\n",
1048           wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
1049
1050     mapped = MCI_MapMsgWtoA(wMsg, dwParam1, &dwParam2);
1051     if (mapped == -1)
1052     {
1053         FIXME("message %04x mapping failed\n", wMsg);
1054         return MMSYSERR_NOMEM;
1055     }
1056     ret = mciSendCommandA(wDevID, wMsg, dwParam1, dwParam2);
1057     if (mapped)
1058         MCI_UnmapMsgWtoA(wMsg, dwParam1, dwParam2, ret);
1059     return ret;
1060 }
1061
1062 /**************************************************************************
1063  *                              mciGetDeviceIDA                 [WINMM.@]
1064  */
1065 UINT WINAPI mciGetDeviceIDA(LPCSTR lpstrName)
1066 {
1067     return MCI_GetDriverFromString(lpstrName);
1068 }
1069
1070 /**************************************************************************
1071  *                              mciGetDeviceIDW                 [WINMM.@]
1072  */
1073 UINT WINAPI mciGetDeviceIDW(LPCWSTR lpwstrName)
1074 {
1075     LPSTR       lpstrName = NULL;
1076     UINT        ret;
1077     INT         len;
1078
1079     if (lpwstrName) {
1080         len = WideCharToMultiByte( CP_ACP, 0, lpwstrName, -1, NULL, 0, NULL, NULL );
1081         lpstrName = HeapAlloc( GetProcessHeap(), 0, len );
1082         if (lpstrName) WideCharToMultiByte( CP_ACP, 0, lpwstrName, -1, lpstrName, len, NULL, NULL );
1083     }
1084     ret = MCI_GetDriverFromString(lpstrName);
1085     HeapFree(GetProcessHeap(), 0, lpstrName);
1086     return ret;
1087 }
1088
1089 /**************************************************************************
1090  *                              MCI_DefYieldProc                [internal]
1091  */
1092 UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data)
1093 {
1094     INT16       ret;
1095
1096     TRACE("(0x%04x, 0x%08lx)\n", wDevID, data);
1097
1098     if ((HIWORD(data) != 0 && HWND_16(GetActiveWindow()) != HIWORD(data)) ||
1099         (GetAsyncKeyState(LOWORD(data)) & 1) == 0) {
1100         MyUserYield();
1101         ret = 0;
1102     } else {
1103         MSG             msg;
1104
1105         msg.hwnd = HWND_32(HIWORD(data));
1106         while (!PeekMessageA(&msg, msg.hwnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));
1107         ret = -1;
1108     }
1109     return ret;
1110 }
1111
1112 /**************************************************************************
1113  *                              mciSetYieldProc                 [WINMM.@]
1114  */
1115 BOOL WINAPI mciSetYieldProc(MCIDEVICEID uDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData)
1116 {
1117     LPWINE_MCIDRIVER    wmd;
1118
1119     TRACE("(%u, %p, %08lx)\n", uDeviceID, fpYieldProc, dwYieldData);
1120
1121     if (!(wmd = MCI_GetDriver(uDeviceID))) {
1122         WARN("Bad uDeviceID\n");
1123         return FALSE;
1124     }
1125
1126     wmd->lpfnYieldProc = fpYieldProc;
1127     wmd->dwYieldData   = dwYieldData;
1128     wmd->bIs32         = TRUE;
1129
1130     return TRUE;
1131 }
1132
1133 /**************************************************************************
1134  *                              mciGetDeviceIDFromElementIDW    [WINMM.@]
1135  */
1136 UINT WINAPI mciGetDeviceIDFromElementIDW(DWORD dwElementID, LPCWSTR lpstrType)
1137 {
1138     /* FIXME: that's rather strange, there is no
1139      * mciGetDeviceIDFromElementID32A in winmm.spec
1140      */
1141     FIXME("(%lu, %p) stub\n", dwElementID, lpstrType);
1142     return 0;
1143 }
1144
1145 /**************************************************************************
1146  *                              mciGetYieldProc                 [WINMM.@]
1147  */
1148 YIELDPROC WINAPI mciGetYieldProc(MCIDEVICEID uDeviceID, DWORD* lpdwYieldData)
1149 {
1150     LPWINE_MCIDRIVER    wmd;
1151
1152     TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData);
1153
1154     if (!(wmd = MCI_GetDriver(uDeviceID))) {
1155         WARN("Bad uDeviceID\n");
1156         return NULL;
1157     }
1158     if (!wmd->lpfnYieldProc) {
1159         WARN("No proc set\n");
1160         return NULL;
1161     }
1162     if (!wmd->bIs32) {
1163         WARN("Proc is 32 bit\n");
1164         return NULL;
1165     }
1166     return wmd->lpfnYieldProc;
1167 }
1168
1169 /**************************************************************************
1170  *                              mciGetCreatorTask               [WINMM.@]
1171  */
1172 HTASK WINAPI mciGetCreatorTask(MCIDEVICEID uDeviceID)
1173 {
1174     LPWINE_MCIDRIVER    wmd;
1175     HTASK ret = 0;
1176
1177     if ((wmd = MCI_GetDriver(uDeviceID))) ret = (HTASK)wmd->CreatorThread;
1178
1179     TRACE("(%u) => %p\n", uDeviceID, ret);
1180     return ret;
1181 }
1182
1183 /**************************************************************************
1184  *                      mciDriverYield                          [WINMM.@]
1185  */
1186 UINT WINAPI mciDriverYield(MCIDEVICEID uDeviceID)
1187 {
1188     LPWINE_MCIDRIVER    wmd;
1189     UINT                ret = 0;
1190
1191     TRACE("(%04x)\n", uDeviceID);
1192
1193     if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc || !wmd->bIs32) {
1194         MyUserYield();
1195     } else {
1196         ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
1197     }
1198
1199     return ret;
1200 }
1201
1202 /**************************************************************************
1203  *                              midiOutGetNumDevs       [WINMM.@]
1204  */
1205 UINT WINAPI midiOutGetNumDevs(void)
1206 {
1207     return MMDRV_GetNum(MMDRV_MIDIOUT);
1208 }
1209
1210 /**************************************************************************
1211  *                              midiOutGetDevCapsW      [WINMM.@]
1212  */
1213 UINT WINAPI midiOutGetDevCapsW(UINT_PTR uDeviceID, LPMIDIOUTCAPSW lpCaps,
1214                                UINT uSize)
1215 {
1216     MIDIOUTCAPSA        mocA;
1217     UINT                ret = midiOutGetDevCapsA(uDeviceID, &mocA, sizeof(mocA));
1218
1219     if (ret == MMSYSERR_NOERROR) {
1220         MIDIOUTCAPSW mocW;
1221         mocW.wMid               = mocA.wMid;
1222         mocW.wPid               = mocA.wPid;
1223         mocW.vDriverVersion     = mocA.vDriverVersion;
1224         MultiByteToWideChar( CP_ACP, 0, mocA.szPname, -1, mocW.szPname,
1225                              sizeof(mocW.szPname)/sizeof(WCHAR) );
1226         mocW.wTechnology        = mocA.wTechnology;
1227         mocW.wVoices            = mocA.wVoices;
1228         mocW.wNotes             = mocA.wNotes;
1229         mocW.wChannelMask       = mocA.wChannelMask;
1230         mocW.dwSupport          = mocA.dwSupport;
1231         memcpy(lpCaps, &mocW, min(uSize, sizeof(mocW)));
1232     }
1233     return ret;
1234 }
1235
1236 /**************************************************************************
1237  *                              midiOutGetDevCapsA      [WINMM.@]
1238  */
1239 UINT WINAPI midiOutGetDevCapsA(UINT_PTR uDeviceID, LPMIDIOUTCAPSA lpCaps,
1240                                UINT uSize)
1241 {
1242     LPWINE_MLD  wmld;
1243
1244     TRACE("(%u, %p, %u);\n", uDeviceID, lpCaps, uSize);
1245
1246     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1247
1248     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
1249         return MMSYSERR_INVALHANDLE;
1250
1251     return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
1252 }
1253
1254 /**************************************************************************
1255  *                              MIDI_GetErrorText               [internal]
1256  */
1257 static  UINT16  MIDI_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
1258 {
1259     UINT16              ret = MMSYSERR_BADERRNUM;
1260
1261     if (lpText == NULL) {
1262         ret = MMSYSERR_INVALPARAM;
1263     } else if (uSize == 0) {
1264         ret = MMSYSERR_NOERROR;
1265     } else if (
1266                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
1267                 * a warning for the test was always true */
1268                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
1269                (uError >= MIDIERR_BASE  && uError <= MIDIERR_LASTERROR)) {
1270
1271         if (LoadStringA(WINMM_IData->hWinMM32Instance,
1272                         uError, lpText, uSize) > 0) {
1273             ret = MMSYSERR_NOERROR;
1274         }
1275     }
1276     return ret;
1277 }
1278
1279 /**************************************************************************
1280  *                              midiOutGetErrorTextA    [WINMM.@]
1281  */
1282 UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
1283 {
1284     return MIDI_GetErrorText(uError, lpText, uSize);
1285 }
1286
1287 /**************************************************************************
1288  *                              midiOutGetErrorTextW    [WINMM.@]
1289  */
1290 UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
1291 {
1292     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
1293     UINT        ret;
1294
1295     ret = MIDI_GetErrorText(uError, xstr, uSize);
1296     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
1297     HeapFree(GetProcessHeap(), 0, xstr);
1298     return ret;
1299 }
1300
1301 /**************************************************************************
1302  *                              MIDI_OutAlloc                   [internal]
1303  */
1304 static  LPWINE_MIDI     MIDI_OutAlloc(HMIDIOUT* lphMidiOut, LPDWORD lpdwCallback,
1305                                       LPDWORD lpdwInstance, LPDWORD lpdwFlags,
1306                                       DWORD cIDs, MIDIOPENSTRMID* lpIDs, BOOL bFrom32)
1307 {
1308     HANDLE              hMidiOut;
1309     LPWINE_MIDI         lpwm;
1310     UINT                size;
1311
1312     size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
1313
1314     lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
1315                                     lpdwCallback, lpdwInstance, bFrom32);
1316
1317     if (lphMidiOut != NULL)
1318         *lphMidiOut = hMidiOut;
1319
1320     if (lpwm) {
1321         lpwm->mod.hMidi = (HMIDI) hMidiOut;
1322         lpwm->mod.dwCallback = *lpdwCallback;
1323         lpwm->mod.dwInstance = *lpdwInstance;
1324         lpwm->mod.dnDevNode = 0;
1325         lpwm->mod.cIds = cIDs;
1326         if (cIDs)
1327             memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
1328     }
1329     return lpwm;
1330 }
1331
1332 UINT MIDI_OutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID, DWORD_PTR dwCallback,
1333                   DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
1334 {
1335     HMIDIOUT            hMidiOut;
1336     LPWINE_MIDI         lpwm;
1337     UINT                dwRet = 0;
1338
1339     TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
1340           lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
1341
1342     if (lphMidiOut != NULL) *lphMidiOut = 0;
1343
1344     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags,
1345                          0, NULL, bFrom32);
1346
1347     if (lpwm == NULL)
1348         return MMSYSERR_NOMEM;
1349
1350     lpwm->mld.uDeviceID = uDeviceID;
1351
1352     dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD)&lpwm->mod, dwFlags);
1353
1354     if (dwRet != MMSYSERR_NOERROR) {
1355         MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
1356         hMidiOut = 0;
1357     }
1358
1359     if (lphMidiOut) *lphMidiOut = hMidiOut;
1360     TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
1361
1362     return dwRet;
1363 }
1364
1365 /**************************************************************************
1366  *                              midiOutOpen             [WINMM.@]
1367  */
1368 UINT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,
1369                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
1370 {
1371     return MIDI_OutOpen(lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
1372 }
1373
1374 /**************************************************************************
1375  *                              midiOutClose            [WINMM.@]
1376  */
1377 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
1378 {
1379     LPWINE_MLD          wmld;
1380     DWORD               dwRet;
1381
1382     TRACE("(%p)\n", hMidiOut);
1383
1384     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1385         return MMSYSERR_INVALHANDLE;
1386
1387     dwRet = MMDRV_Close(wmld, MODM_CLOSE);
1388     MMDRV_Free(hMidiOut, wmld);
1389
1390     return dwRet;
1391 }
1392
1393 /**************************************************************************
1394  *                              midiOutPrepareHeader    [WINMM.@]
1395  */
1396 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
1397                                  MIDIHDR* lpMidiOutHdr, UINT uSize)
1398 {
1399     LPWINE_MLD          wmld;
1400
1401     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1402
1403     if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))
1404         return MMSYSERR_INVALPARAM;
1405
1406     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1407         return MMSYSERR_INVALHANDLE;
1408
1409     return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
1410 }
1411
1412 /**************************************************************************
1413  *                              midiOutUnprepareHeader  [WINMM.@]
1414  */
1415 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
1416                                    MIDIHDR* lpMidiOutHdr, UINT uSize)
1417 {
1418     LPWINE_MLD          wmld;
1419
1420     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1421
1422     if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))
1423         return MMSYSERR_INVALPARAM;
1424
1425     if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
1426         return MMSYSERR_NOERROR;
1427     }
1428
1429     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1430         return MMSYSERR_INVALHANDLE;
1431
1432     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
1433 }
1434
1435 /**************************************************************************
1436  *                              midiOutShortMsg         [WINMM.@]
1437  */
1438 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
1439 {
1440     LPWINE_MLD          wmld;
1441
1442     TRACE("(%p, %08lX)\n", hMidiOut, dwMsg);
1443
1444     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1445         return MMSYSERR_INVALHANDLE;
1446
1447     return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L, TRUE);
1448 }
1449
1450 /**************************************************************************
1451  *                              midiOutLongMsg          [WINMM.@]
1452  */
1453 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
1454                            MIDIHDR* lpMidiOutHdr, UINT uSize)
1455 {
1456     LPWINE_MLD          wmld;
1457
1458     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1459
1460     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1461         return MMSYSERR_INVALHANDLE;
1462
1463     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
1464 }
1465
1466 /**************************************************************************
1467  *                              midiOutReset            [WINMM.@]
1468  */
1469 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
1470 {
1471     LPWINE_MLD          wmld;
1472
1473     TRACE("(%p)\n", hMidiOut);
1474
1475     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1476         return MMSYSERR_INVALHANDLE;
1477
1478     return MMDRV_Message(wmld, MODM_RESET, 0L, 0L, TRUE);
1479 }
1480
1481 /**************************************************************************
1482  *                              midiOutGetVolume        [WINMM.@]
1483  */
1484 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
1485 {
1486     LPWINE_MLD          wmld;
1487
1488     TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
1489
1490     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1491         return MMSYSERR_INVALHANDLE;
1492
1493     return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);
1494 }
1495
1496 /**************************************************************************
1497  *                              midiOutSetVolume        [WINMM.@]
1498  */
1499 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
1500 {
1501     LPWINE_MLD          wmld;
1502
1503     TRACE("(%p, %ld);\n", hMidiOut, dwVolume);
1504
1505     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1506         return MMSYSERR_INVALHANDLE;
1507
1508     return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L, TRUE);
1509 }
1510
1511 /**************************************************************************
1512  *                              midiOutCachePatches             [WINMM.@]
1513  */
1514 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
1515                                 WORD* lpwPatchArray, UINT uFlags)
1516 {
1517     /* not really necessary to support this */
1518     FIXME("not supported yet\n");
1519     return MMSYSERR_NOTSUPPORTED;
1520 }
1521
1522 /**************************************************************************
1523  *                              midiOutCacheDrumPatches [WINMM.@]
1524  */
1525 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
1526                                     WORD* lpwKeyArray, UINT uFlags)
1527 {
1528     FIXME("not supported yet\n");
1529     return MMSYSERR_NOTSUPPORTED;
1530 }
1531
1532 /**************************************************************************
1533  *                              midiOutGetID            [WINMM.@]
1534  */
1535 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
1536 {
1537     LPWINE_MLD          wmld;
1538
1539     TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
1540
1541     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1542     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1543         return MMSYSERR_INVALHANDLE;
1544
1545     *lpuDeviceID = wmld->uDeviceID;
1546     return MMSYSERR_NOERROR;
1547 }
1548
1549 /**************************************************************************
1550  *                              midiOutMessage          [WINMM.@]
1551  */
1552 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
1553                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1554 {
1555     LPWINE_MLD          wmld;
1556
1557     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
1558
1559     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
1560         /* HACK... */
1561         if (uMessage == 0x0001) {
1562             *(LPDWORD)dwParam1 = 1;
1563             return 0;
1564         }
1565         if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
1566             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
1567         }
1568         return MMSYSERR_INVALHANDLE;
1569     }
1570
1571     switch (uMessage) {
1572     case MODM_OPEN:
1573     case MODM_CLOSE:
1574         FIXME("can't handle OPEN or CLOSE message!\n");
1575         return MMSYSERR_NOTSUPPORTED;
1576     }
1577     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
1578 }
1579
1580 /**************************************************************************
1581  *                              midiInGetNumDevs        [WINMM.@]
1582  */
1583 UINT WINAPI midiInGetNumDevs(void)
1584 {
1585     return MMDRV_GetNum(MMDRV_MIDIIN);
1586 }
1587
1588 /**************************************************************************
1589  *                              midiInGetDevCapsW       [WINMM.@]
1590  */
1591 UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
1592 {
1593     MIDIINCAPSA         micA;
1594     UINT                ret = midiInGetDevCapsA(uDeviceID, &micA, sizeof(micA));
1595
1596     if (ret == MMSYSERR_NOERROR) {
1597         MIDIINCAPSW micW;
1598         micW.wMid           = micA.wMid;
1599         micW.wPid           = micA.wPid;
1600         micW.vDriverVersion = micA.vDriverVersion;
1601         MultiByteToWideChar( CP_ACP, 0, micA.szPname, -1, micW.szPname,
1602                              sizeof(micW.szPname)/sizeof(WCHAR) );
1603         micW.dwSupport      = micA.dwSupport;
1604         memcpy(lpCaps, &micW, min(uSize, sizeof(micW)));
1605     }
1606     return ret;
1607 }
1608
1609 /**************************************************************************
1610  *                              midiInGetDevCapsA       [WINMM.@]
1611  */
1612 UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
1613 {
1614     LPWINE_MLD  wmld;
1615
1616     TRACE("(%d, %p, %d);\n", uDeviceID, lpCaps, uSize);
1617
1618     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1619
1620     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
1621         return MMSYSERR_INVALHANDLE;
1622
1623    return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
1624 }
1625
1626 /**************************************************************************
1627  *                              midiInGetErrorTextW             [WINMM.@]
1628  */
1629 UINT WINAPI midiInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
1630 {
1631     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
1632     UINT        ret = MIDI_GetErrorText(uError, xstr, uSize);
1633
1634     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
1635     HeapFree(GetProcessHeap(), 0, xstr);
1636     return ret;
1637 }
1638
1639 /**************************************************************************
1640  *                              midiInGetErrorTextA             [WINMM.@]
1641  */
1642 UINT WINAPI midiInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
1643 {
1644     return MIDI_GetErrorText(uError, lpText, uSize);
1645 }
1646
1647 UINT MIDI_InOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD dwCallback,
1648                  DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
1649 {
1650     HANDLE              hMidiIn;
1651     LPWINE_MIDI         lpwm;
1652     DWORD               dwRet = 0;
1653
1654     TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
1655           lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
1656
1657     if (lphMidiIn != NULL) *lphMidiIn = 0;
1658
1659     lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
1660                                     &dwFlags, &dwCallback, &dwInstance, bFrom32);
1661
1662     if (lpwm == NULL)
1663         return MMSYSERR_NOMEM;
1664
1665     lpwm->mod.hMidi = (HMIDI) hMidiIn;
1666     lpwm->mod.dwCallback = dwCallback;
1667     lpwm->mod.dwInstance = dwInstance;
1668
1669     lpwm->mld.uDeviceID = uDeviceID;
1670     dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);
1671
1672     if (dwRet != MMSYSERR_NOERROR) {
1673         MMDRV_Free(hMidiIn, &lpwm->mld);
1674         hMidiIn = 0;
1675     }
1676     if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
1677     TRACE("=> %ld hMidi=%p\n", dwRet, hMidiIn);
1678
1679     return dwRet;
1680 }
1681
1682 /**************************************************************************
1683  *                              midiInOpen              [WINMM.@]
1684  */
1685 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
1686                        DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
1687 {
1688     return MIDI_InOpen(lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
1689 }
1690
1691 /**************************************************************************
1692  *                              midiInClose             [WINMM.@]
1693  */
1694 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
1695 {
1696     LPWINE_MLD          wmld;
1697     DWORD               dwRet;
1698
1699     TRACE("(%p)\n", hMidiIn);
1700
1701     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1702         return MMSYSERR_INVALHANDLE;
1703
1704     dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
1705     MMDRV_Free(hMidiIn, wmld);
1706     return dwRet;
1707 }
1708
1709 /**************************************************************************
1710  *                              midiInPrepareHeader     [WINMM.@]
1711  */
1712 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
1713                                 MIDIHDR* lpMidiInHdr, UINT uSize)
1714 {
1715     LPWINE_MLD          wmld;
1716
1717     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1718
1719     if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
1720         return MMSYSERR_INVALPARAM;
1721
1722     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1723         return MMSYSERR_INVALHANDLE;
1724
1725     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
1726 }
1727
1728 /**************************************************************************
1729  *                              midiInUnprepareHeader   [WINMM.@]
1730  */
1731 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
1732                                   MIDIHDR* lpMidiInHdr, UINT uSize)
1733 {
1734     LPWINE_MLD          wmld;
1735
1736     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1737
1738     if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
1739         return MMSYSERR_INVALPARAM;
1740
1741     if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
1742         return MMSYSERR_NOERROR;
1743     }
1744
1745     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1746         return MMSYSERR_INVALHANDLE;
1747
1748     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
1749 }
1750
1751 /**************************************************************************
1752  *                              midiInAddBuffer         [WINMM.@]
1753  */
1754 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
1755                             MIDIHDR* lpMidiInHdr, UINT uSize)
1756 {
1757     LPWINE_MLD          wmld;
1758
1759     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1760
1761     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1762         return MMSYSERR_INVALHANDLE;
1763
1764     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
1765 }
1766
1767 /**************************************************************************
1768  *                              midiInStart                     [WINMM.@]
1769  */
1770 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
1771 {
1772     LPWINE_MLD          wmld;
1773
1774     TRACE("(%p)\n", hMidiIn);
1775
1776     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1777         return MMSYSERR_INVALHANDLE;
1778
1779     return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);
1780 }
1781
1782 /**************************************************************************
1783  *                              midiInStop                      [WINMM.@]
1784  */
1785 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
1786 {
1787     LPWINE_MLD          wmld;
1788
1789     TRACE("(%p)\n", hMidiIn);
1790
1791     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1792         return MMSYSERR_INVALHANDLE;
1793
1794     return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);
1795 }
1796
1797 /**************************************************************************
1798  *                              midiInReset                     [WINMM.@]
1799  */
1800 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
1801 {
1802     LPWINE_MLD          wmld;
1803
1804     TRACE("(%p)\n", hMidiIn);
1805
1806     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1807         return MMSYSERR_INVALHANDLE;
1808
1809     return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);
1810 }
1811
1812 /**************************************************************************
1813  *                              midiInGetID                     [WINMM.@]
1814  */
1815 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
1816 {
1817     LPWINE_MLD          wmld;
1818
1819     TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
1820
1821     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1822
1823     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
1824         return MMSYSERR_INVALHANDLE;
1825
1826     *lpuDeviceID = wmld->uDeviceID;
1827
1828     return MMSYSERR_NOERROR;
1829 }
1830
1831 /**************************************************************************
1832  *                              midiInMessage           [WINMM.@]
1833  */
1834 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
1835                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1836 {
1837     LPWINE_MLD          wmld;
1838
1839     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
1840
1841     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1842         return MMSYSERR_INVALHANDLE;
1843
1844     switch (uMessage) {
1845     case MIDM_OPEN:
1846     case MIDM_CLOSE:
1847         FIXME("can't handle OPEN or CLOSE message!\n");
1848         return MMSYSERR_NOTSUPPORTED;
1849     }
1850     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
1851 }
1852
1853 typedef struct WINE_MIDIStream {
1854     HMIDIOUT                    hDevice;
1855     HANDLE                      hThread;
1856     DWORD                       dwThreadID;
1857     DWORD                       dwTempo;
1858     DWORD                       dwTimeDiv;
1859     DWORD                       dwPositionMS;
1860     DWORD                       dwPulses;
1861     DWORD                       dwStartTicks;
1862     WORD                        wFlags;
1863     HANDLE                      hEvent;
1864     LPMIDIHDR                   lpMidiHdr;
1865 } WINE_MIDIStream;
1866
1867 #define WINE_MSM_HEADER         (WM_USER+0)
1868 #define WINE_MSM_STOP           (WM_USER+1)
1869
1870 /**************************************************************************
1871  *                              MMSYSTEM_GetMidiStream          [internal]
1872  */
1873 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
1874 {
1875     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
1876
1877     if (lplpwm)
1878         *lplpwm = lpwm;
1879
1880     if (lpwm == NULL) {
1881         return FALSE;
1882     }
1883
1884     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
1885
1886     return *lpMidiStrm != NULL;
1887 }
1888
1889 /**************************************************************************
1890  *                              MMSYSTEM_MidiStream_Convert     [internal]
1891  */
1892 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
1893 {
1894     DWORD       ret = 0;
1895
1896     if (lpMidiStrm->dwTimeDiv == 0) {
1897         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
1898     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
1899         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
1900         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
1901         ret = (pulse * 1000) / (nf * nsf);
1902     } else {
1903         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
1904                       (double)lpMidiStrm->dwTimeDiv);
1905     }
1906
1907     return ret;
1908 }
1909
1910 /**************************************************************************
1911  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
1912  */
1913 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1914 {
1915     LPMIDIHDR   lpMidiHdr;
1916     LPMIDIHDR*  lpmh;
1917     LPBYTE      lpData;
1918
1919     switch (msg->message) {
1920     case WM_QUIT:
1921         SetEvent(lpMidiStrm->hEvent);
1922         return FALSE;
1923     case WINE_MSM_STOP:
1924         TRACE("STOP\n");
1925         /* this is not quite what MS doc says... */
1926         midiOutReset(lpMidiStrm->hDevice);
1927         /* empty list of already submitted buffers */
1928         for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {
1929             lpMidiHdr->dwFlags |= MHDR_DONE;
1930             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1931
1932             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1933                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1934                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1935         }
1936         lpMidiStrm->lpMidiHdr = 0;
1937         SetEvent(lpMidiStrm->hEvent);
1938         break;
1939     case WINE_MSM_HEADER:
1940         /* sets initial tick count for first MIDIHDR */
1941         if (!lpMidiStrm->dwStartTicks)
1942             lpMidiStrm->dwStartTicks = GetTickCount();
1943
1944         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1945          * by native mcimidi, it doesn't look like a correct one".
1946          * this trick allows to throw it away... but I don't like it.
1947          * It looks like part of the file I'm trying to play and definitively looks
1948          * like raw midi content
1949          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1950          * synchronization issue where native mcimidi is still processing raw MIDI
1951          * content before generating MIDIEVENTs ?
1952          *
1953          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1954          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1955          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1956          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1957          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1958          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1959          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1960          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1961          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1962          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1963          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1964          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1965          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1966          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1967          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1968          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1969          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1970          */
1971         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1972         lpData = lpMidiHdr->lpData;
1973         TRACE("Adding %s lpMidiHdr=%p [lpData=0x%08lx dwBufferLength=%lu/%lu dwFlags=0x%08lx size=%u]\n",
1974               (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1975               (DWORD)lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
1976               lpMidiHdr->dwFlags, msg->wParam);
1977 #if 0
1978         /* dumps content of lpMidiHdr->lpData
1979          * FIXME: there should be a debug routine somewhere that already does this
1980          * I hate spreading this type of shit all around the code
1981          */
1982         for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1983             DWORD       i;
1984             BYTE        ch;
1985
1986             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1987                 printf("%02x ", lpData[dwToGo + i]);
1988             for (; i < 16; i++)
1989                 printf("   ");
1990             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1991                 ch = lpData[dwToGo + i];
1992                 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1993             }
1994             printf("\n");
1995         }
1996 #endif
1997         if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1998             ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1999             ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
2000             FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",
2001                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
2002                   ((LPMIDIEVENT)lpData)->dwStreamID);
2003             lpMidiHdr->dwFlags |= MHDR_DONE;
2004             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
2005
2006             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
2007                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
2008                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
2009             break;
2010         }
2011
2012         for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));
2013         *lpmh = lpMidiHdr;
2014         lpMidiHdr = (LPMIDIHDR)msg->lParam;
2015         lpMidiHdr->lpNext = 0;
2016         lpMidiHdr->dwFlags |= MHDR_INQUEUE;
2017         lpMidiHdr->dwFlags &= ~MHDR_DONE;
2018         lpMidiHdr->dwOffset = 0;
2019
2020         break;
2021     default:
2022         FIXME("Unknown message %d\n", msg->message);
2023         break;
2024     }
2025     return TRUE;
2026 }
2027
2028 /**************************************************************************
2029  *                              MMSYSTEM_MidiStream_Player      [internal]
2030  */
2031 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
2032 {
2033     WINE_MIDIStream*    lpMidiStrm = pmt;
2034     WINE_MIDI*          lpwm;
2035     MSG                 msg;
2036     DWORD               dwToGo;
2037     DWORD               dwCurrTC;
2038     LPMIDIHDR           lpMidiHdr;
2039     LPMIDIEVENT         me;
2040     LPBYTE              lpData = 0;
2041
2042     TRACE("(%p)!\n", lpMidiStrm);
2043
2044     if (!lpMidiStrm ||
2045         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
2046         goto the_end;
2047
2048     /* force thread's queue creation */
2049     /* Used to be InitThreadInput16(0, 5); */
2050     /* but following works also with hack in midiStreamOpen */
2051     PeekMessageA(&msg, 0, 0, 0, 0);
2052
2053     /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
2054     SetEvent(lpMidiStrm->hEvent);
2055     TRACE("Ready to go 1\n");
2056     /* thread is started in paused mode */
2057     SuspendThread(lpMidiStrm->hThread);
2058     TRACE("Ready to go 2\n");
2059
2060     lpMidiStrm->dwStartTicks = 0;
2061     lpMidiStrm->dwPulses = 0;
2062
2063     lpMidiStrm->lpMidiHdr = 0;
2064
2065     for (;;) {
2066         lpMidiHdr = lpMidiStrm->lpMidiHdr;
2067         if (!lpMidiHdr) {
2068             /* for first message, block until one arrives, then process all that are available */
2069             GetMessageA(&msg, 0, 0, 0);
2070             do {
2071                 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
2072                     goto the_end;
2073             } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
2074             lpData = 0;
2075             continue;
2076         }
2077
2078         if (!lpData)
2079             lpData = lpMidiHdr->lpData;
2080
2081         me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
2082
2083         /* do we have to wait ? */
2084         if (me->dwDeltaTime) {
2085             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
2086             lpMidiStrm->dwPulses += me->dwDeltaTime;
2087
2088             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
2089
2090             TRACE("%ld/%ld/%ld\n", dwToGo, GetTickCount(), me->dwDeltaTime);
2091             while ((dwCurrTC = GetTickCount()) < dwToGo) {
2092                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
2093                     /* got a message, handle it */
2094                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
2095                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
2096                             goto the_end;
2097                     }
2098                     lpData = 0;
2099                 } else {
2100                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
2101                     break;
2102                 }
2103             }
2104         }
2105         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
2106         case MEVT_COMMENT:
2107             FIXME("NIY: MEVT_COMMENT\n");
2108             /* do nothing, skip bytes */
2109             break;
2110         case MEVT_LONGMSG:
2111             FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
2112             break;
2113         case MEVT_NOP:
2114             break;
2115         case MEVT_SHORTMSG:
2116             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
2117             break;
2118         case MEVT_TEMPO:
2119             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
2120             break;
2121         case MEVT_VERSION:
2122             break;
2123         default:
2124             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
2125             break;
2126         }
2127         if (me->dwEvent & MEVT_F_CALLBACK) {
2128             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
2129                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
2130                            lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
2131         }
2132         lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
2133         if (me->dwEvent & MEVT_F_LONG)
2134             lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
2135         if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
2136             /* done with this header */
2137             lpMidiHdr->dwFlags |= MHDR_DONE;
2138             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
2139
2140             lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
2141             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
2142                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
2143                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
2144             lpData = 0;
2145         }
2146     }
2147 the_end:
2148     TRACE("End of thread\n");
2149     ExitThread(0);
2150     return 0;   /* for removing the warning, never executed */
2151 }
2152
2153 /**************************************************************************
2154  *                              MMSYSTEM_MidiStream_PostMessage [internal]
2155  */
2156 static  BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
2157 {
2158     if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
2159         DWORD   count;
2160
2161         if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
2162         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
2163         if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
2164     } else {
2165         WARN("bad PostThreadMessageA\n");
2166         return FALSE;
2167     }
2168     return TRUE;
2169 }
2170
2171 /**************************************************************************
2172  *                              midiStreamClose                 [WINMM.@]
2173  */
2174 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
2175 {
2176     WINE_MIDIStream*    lpMidiStrm;
2177
2178     TRACE("(%p)!\n", hMidiStrm);
2179
2180     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
2181         return MMSYSERR_INVALHANDLE;
2182
2183     midiStreamStop(hMidiStrm);
2184     MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
2185     HeapFree(GetProcessHeap(), 0, lpMidiStrm);
2186     CloseHandle(lpMidiStrm->hEvent);
2187
2188     return midiOutClose((HMIDIOUT)hMidiStrm);
2189 }
2190
2191 /**************************************************************************
2192  *                              MMSYSTEM_MidiStream_Open        [internal]
2193  */
2194 MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,
2195                          DWORD dwCallback, DWORD dwInstance, DWORD fdwOpen, 
2196                          BOOL bFrom32)
2197 {
2198     WINE_MIDIStream*    lpMidiStrm;
2199     MMRESULT            ret;
2200     MIDIOPENSTRMID      mosm;
2201     LPWINE_MIDI         lpwm;
2202     HMIDIOUT            hMidiOut;
2203
2204     TRACE("(%p, %p, %ld, 0x%08lx, 0x%08lx, 0x%08lx)!\n",
2205           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
2206
2207     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
2208         return MMSYSERR_INVALPARAM;
2209
2210     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
2211     if (!lpMidiStrm)
2212         return MMSYSERR_NOMEM;
2213
2214     lpMidiStrm->dwTempo = 500000;
2215     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quater notes per minute *//* FIXME ??*/
2216     lpMidiStrm->dwPositionMS = 0;
2217
2218     mosm.dwStreamID = (DWORD)lpMidiStrm;
2219     /* FIXME: the correct value is not allocated yet for MAPPER */
2220     mosm.wDeviceID  = *lpuDeviceID;
2221     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
2222     lpMidiStrm->hDevice = hMidiOut;
2223     if (lphMidiStrm)
2224         *lphMidiStrm = (HMIDISTRM)hMidiOut;
2225
2226     lpwm->mld.uDeviceID = *lpuDeviceID;
2227
2228     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
2229     lpMidiStrm->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
2230     lpMidiStrm->wFlags = HIWORD(fdwOpen);
2231
2232     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
2233                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
2234
2235     if (!lpMidiStrm->hThread) {
2236         midiStreamClose((HMIDISTRM)hMidiOut);
2237         return MMSYSERR_NOMEM;
2238     }
2239
2240     /* wait for thread to have started, and for its queue to be created */
2241     {
2242         DWORD   count;
2243
2244         /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
2245          * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
2246          * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
2247          */
2248         if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
2249         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
2250         if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
2251     }
2252
2253     TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
2254           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
2255     return ret;
2256 }
2257
2258 /**************************************************************************
2259  *                              midiStreamOpen                  [WINMM.@]
2260  */
2261 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
2262                                DWORD cMidi, DWORD dwCallback,
2263                                DWORD dwInstance, DWORD fdwOpen)
2264 {
2265     return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
2266                            dwInstance, fdwOpen, TRUE);
2267 }
2268
2269 /**************************************************************************
2270  *                              midiStreamOut                   [WINMM.@]
2271  */
2272 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
2273                               UINT cbMidiHdr)
2274 {
2275     WINE_MIDIStream*    lpMidiStrm;
2276     DWORD               ret = MMSYSERR_NOERROR;
2277
2278     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
2279
2280     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2281         ret = MMSYSERR_INVALHANDLE;
2282     } else if (!lpMidiHdr) {
2283         ret = MMSYSERR_INVALPARAM;
2284     } else {
2285         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
2286                                 WINE_MSM_HEADER, cbMidiHdr,
2287                                 (DWORD)lpMidiHdr)) {
2288             WARN("bad PostThreadMessageA\n");
2289             ret = MMSYSERR_ERROR;
2290         }
2291     }
2292     return ret;
2293 }
2294
2295 /**************************************************************************
2296  *                              midiStreamPause                 [WINMM.@]
2297  */
2298 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
2299 {
2300     WINE_MIDIStream*    lpMidiStrm;
2301     DWORD               ret = MMSYSERR_NOERROR;
2302
2303     TRACE("(%p)!\n", hMidiStrm);
2304
2305     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2306         ret = MMSYSERR_INVALHANDLE;
2307     } else {
2308         if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
2309             WARN("bad Suspend (%ld)\n", GetLastError());
2310             ret = MMSYSERR_ERROR;
2311         }
2312     }
2313     return ret;
2314 }
2315
2316 /**************************************************************************
2317  *                              midiStreamPosition              [WINMM.@]
2318  */
2319 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
2320 {
2321     WINE_MIDIStream*    lpMidiStrm;
2322     DWORD               ret = MMSYSERR_NOERROR;
2323
2324     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
2325
2326     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2327         ret = MMSYSERR_INVALHANDLE;
2328     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
2329         ret = MMSYSERR_INVALPARAM;
2330     } else {
2331         switch (lpMMT->wType) {
2332         case TIME_MS:
2333             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
2334             TRACE("=> %ld ms\n", lpMMT->u.ms);
2335             break;
2336         case TIME_TICKS:
2337             lpMMT->u.ticks = lpMidiStrm->dwPulses;
2338             TRACE("=> %ld ticks\n", lpMMT->u.ticks);
2339             break;
2340         default:
2341             WARN("Unsupported time type %d\n", lpMMT->wType);
2342             lpMMT->wType = TIME_MS;
2343             ret = MMSYSERR_INVALPARAM;
2344             break;
2345         }
2346     }
2347     return ret;
2348 }
2349
2350 /**************************************************************************
2351  *                              midiStreamProperty              [WINMM.@]
2352  */
2353 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
2354 {
2355     WINE_MIDIStream*    lpMidiStrm;
2356     MMRESULT            ret = MMSYSERR_NOERROR;
2357
2358     TRACE("(%p, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);
2359
2360     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2361         ret = MMSYSERR_INVALHANDLE;
2362     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
2363         ret = MMSYSERR_INVALPARAM;
2364     } else if (dwProperty & MIDIPROP_TEMPO) {
2365         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
2366
2367         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
2368             ret = MMSYSERR_INVALPARAM;
2369         } else if (dwProperty & MIDIPROP_SET) {
2370             lpMidiStrm->dwTempo = mpt->dwTempo;
2371             TRACE("Setting tempo to %ld\n", mpt->dwTempo);
2372         } else if (dwProperty & MIDIPROP_GET) {
2373             mpt->dwTempo = lpMidiStrm->dwTempo;
2374             TRACE("Getting tempo <= %ld\n", mpt->dwTempo);
2375         }
2376     } else if (dwProperty & MIDIPROP_TIMEDIV) {
2377         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
2378
2379         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
2380             ret = MMSYSERR_INVALPARAM;
2381         } else if (dwProperty & MIDIPROP_SET) {
2382             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
2383             TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);
2384         } else if (dwProperty & MIDIPROP_GET) {
2385             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
2386             TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);
2387         }
2388     } else {
2389         ret = MMSYSERR_INVALPARAM;
2390     }
2391
2392     return ret;
2393 }
2394
2395 /**************************************************************************
2396  *                              midiStreamRestart               [WINMM.@]
2397  */
2398 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
2399 {
2400     WINE_MIDIStream*    lpMidiStrm;
2401     MMRESULT            ret = MMSYSERR_NOERROR;
2402
2403     TRACE("(%p)!\n", hMidiStrm);
2404
2405     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2406         ret = MMSYSERR_INVALHANDLE;
2407     } else {
2408         DWORD   ret;
2409
2410         /* since we increase the thread suspend count on each midiStreamPause
2411          * there may be a need for several midiStreamResume
2412          */
2413         do {
2414             ret = ResumeThread(lpMidiStrm->hThread);
2415         } while (ret != 0xFFFFFFFF && ret != 0);
2416         if (ret == 0xFFFFFFFF) {
2417             WARN("bad Resume (%ld)\n", GetLastError());
2418             ret = MMSYSERR_ERROR;
2419         } else {
2420             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
2421         }
2422     }
2423     return ret;
2424 }
2425
2426 /**************************************************************************
2427  *                              midiStreamStop                  [WINMM.@]
2428  */
2429 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
2430 {
2431     WINE_MIDIStream*    lpMidiStrm;
2432     MMRESULT            ret = MMSYSERR_NOERROR;
2433
2434     TRACE("(%p)!\n", hMidiStrm);
2435
2436     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2437         ret = MMSYSERR_INVALHANDLE;
2438     } else {
2439         /* in case stream has been paused... FIXME is the current state correct ? */
2440         midiStreamRestart(hMidiStrm);
2441         MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
2442     }
2443     return ret;
2444 }
2445
2446 UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType, 
2447                const LPWAVEFORMATEX lpFormat, DWORD dwCallback, 
2448                DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
2449 {
2450     HANDLE              handle;
2451     LPWINE_MLD          wmld;
2452     DWORD               dwRet = MMSYSERR_NOERROR;
2453     WAVEOPENDESC        wod;
2454
2455     TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08lX, %d);\n",
2456           lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
2457           dwInstance, dwFlags, bFrom32?32:16);
2458
2459     if (dwFlags & WAVE_FORMAT_QUERY)
2460         TRACE("WAVE_FORMAT_QUERY requested !\n");
2461
2462     if (lpFormat == NULL) {
2463         WARN("bad format\n");
2464         return WAVERR_BADFORMAT;
2465     }
2466
2467     if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
2468         WARN("invalid parameter\n");
2469         return MMSYSERR_INVALPARAM;
2470     }
2471
2472     /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
2473     TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u\n",
2474           lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
2475           lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);
2476
2477     if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
2478                             &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL) {
2479         WARN("no memory\n");
2480         return MMSYSERR_NOMEM;
2481     }
2482
2483     wod.hWave = handle;
2484     wod.lpFormat = lpFormat;  /* should the struct be copied iso pointer? */
2485     wod.dwCallback = dwCallback;
2486     wod.dwInstance = dwInstance;
2487     wod.dnDevNode = 0L;
2488
2489     TRACE("cb=%08lx\n", wod.dwCallback);
2490
2491     for (;;) {
2492         if (dwFlags & WAVE_MAPPED) {
2493             wod.uMappedDeviceID = uDeviceID;
2494             uDeviceID = WAVE_MAPPER;
2495         } else {
2496             wod.uMappedDeviceID = -1;
2497         }
2498         wmld->uDeviceID = uDeviceID;
2499     
2500         dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN, 
2501                            (DWORD)&wod, dwFlags);
2502
2503         TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));
2504         if (dwRet != WAVERR_BADFORMAT ||
2505             (dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) break;
2506         /* if we ask for a format which isn't supported by the physical driver, 
2507          * let's try to map it through the wave mapper (except, if we already tried
2508          * or user didn't allow us to use acm codecs)
2509          */
2510         dwFlags |= WAVE_MAPPED;
2511         /* we shall loop only one */
2512     }
2513
2514     if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
2515         MMDRV_Free(handle, wmld);
2516         handle = 0;
2517     }
2518
2519     if (lphndl != NULL) *lphndl = handle;
2520     TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);
2521
2522     return dwRet;
2523 }
2524
2525 /**************************************************************************
2526  *                              waveOutGetNumDevs               [WINMM.@]
2527  */
2528 UINT WINAPI waveOutGetNumDevs(void)
2529 {
2530     return MMDRV_GetNum(MMDRV_WAVEOUT);
2531 }
2532
2533 /**************************************************************************
2534  *                              waveOutGetDevCapsA              [WINMM.@]
2535  */
2536 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2537                                UINT uSize)
2538 {
2539     LPWINE_MLD          wmld;
2540
2541     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
2542
2543     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2544
2545     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
2546         return MMSYSERR_BADDEVICEID;
2547
2548     return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
2549
2550 }
2551
2552 /**************************************************************************
2553  *                              waveOutGetDevCapsW              [WINMM.@]
2554  */
2555 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2556                                UINT uSize)
2557 {
2558     WAVEOUTCAPSA        wocA;
2559     UINT                ret = waveOutGetDevCapsA(uDeviceID, &wocA, sizeof(wocA));
2560
2561     if (ret == MMSYSERR_NOERROR) {
2562         WAVEOUTCAPSW wocW;
2563         wocW.wMid           = wocA.wMid;
2564         wocW.wPid           = wocA.wPid;
2565         wocW.vDriverVersion = wocA.vDriverVersion;
2566         MultiByteToWideChar( CP_ACP, 0, wocA.szPname, -1, wocW.szPname,
2567                              sizeof(wocW.szPname)/sizeof(WCHAR) );
2568         wocW.dwFormats      = wocA.dwFormats;
2569         wocW.wChannels      = wocA.wChannels;
2570         wocW.dwSupport      = wocA.dwSupport;
2571         memcpy(lpCaps, &wocW, min(uSize, sizeof(wocW)));
2572     }
2573     return ret;
2574 }
2575
2576 /**************************************************************************
2577  *                              WAVE_GetErrorText               [internal]
2578  */
2579 static  UINT16  WAVE_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
2580 {
2581     UINT16              ret = MMSYSERR_BADERRNUM;
2582
2583     if (lpText == NULL) {
2584         ret = MMSYSERR_INVALPARAM;
2585     } else if (uSize == 0) {
2586         ret = MMSYSERR_NOERROR;
2587     } else if (
2588                /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
2589                 * a warning for the test was always true */
2590                (/*uError >= MMSYSERR_BASE && */uError <= MMSYSERR_LASTERROR) ||
2591                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
2592
2593         if (LoadStringA(WINMM_IData->hWinMM32Instance,
2594                         uError, lpText, uSize) > 0) {
2595             ret = MMSYSERR_NOERROR;
2596         }
2597     }
2598     return ret;
2599 }
2600
2601 /**************************************************************************
2602  *                              waveOutGetErrorTextA    [WINMM.@]
2603  */
2604 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2605 {
2606     return WAVE_GetErrorText(uError, lpText, uSize);
2607 }
2608
2609 /**************************************************************************
2610  *                              waveOutGetErrorTextW    [WINMM.@]
2611  */
2612 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2613 {
2614     LPSTR       xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
2615     UINT        ret = WAVE_GetErrorText(uError, xstr, uSize);
2616
2617     MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
2618     HeapFree(GetProcessHeap(), 0, xstr);
2619     return ret;
2620 }
2621
2622 /**************************************************************************
2623  *                      waveOutOpen                     [WINMM.@]
2624  * All the args/structs have the same layout as the win16 equivalents
2625  */
2626 UINT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2627                        const LPWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2628                        DWORD_PTR dwInstance, DWORD dwFlags)
2629 {
2630     return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
2631                      dwCallback, dwInstance, dwFlags, TRUE);
2632 }
2633
2634 /**************************************************************************
2635  *                              waveOutClose            [WINMM.@]
2636  */
2637 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2638 {
2639     LPWINE_MLD          wmld;
2640     DWORD               dwRet;
2641
2642     TRACE("(%p)\n", hWaveOut);
2643
2644     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2645         return MMSYSERR_INVALHANDLE;
2646
2647     dwRet = MMDRV_Close(wmld, WODM_CLOSE);
2648     if (dwRet != WAVERR_STILLPLAYING)
2649         MMDRV_Free(hWaveOut, wmld);
2650
2651     return dwRet;
2652 }
2653
2654 /**************************************************************************
2655  *                              waveOutPrepareHeader    [WINMM.@]
2656  */
2657 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2658                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
2659 {
2660     LPWINE_MLD          wmld;
2661
2662     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2663
2664     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2665         return MMSYSERR_INVALPARAM;
2666
2667     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2668         return MMSYSERR_INVALHANDLE;
2669
2670     return MMDRV_Message(wmld, WODM_PREPARE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);
2671 }
2672
2673 /**************************************************************************
2674  *                              waveOutUnprepareHeader  [WINMM.@]
2675  */
2676 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2677                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
2678 {
2679     LPWINE_MLD          wmld;
2680
2681     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2682
2683     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2684         return MMSYSERR_INVALPARAM;
2685     
2686     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
2687         return MMSYSERR_NOERROR;
2688     }
2689
2690     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2691         return MMSYSERR_INVALHANDLE;
2692
2693     return MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);
2694 }
2695
2696 /**************************************************************************
2697  *                              waveOutWrite            [WINMM.@]
2698  */
2699 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
2700                          UINT uSize)
2701 {
2702     LPWINE_MLD          wmld;
2703
2704     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2705
2706     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2707         return MMSYSERR_INVALHANDLE;
2708
2709     return MMDRV_Message(wmld, WODM_WRITE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);
2710 }
2711
2712 /**************************************************************************
2713  *                              waveOutBreakLoop        [WINMM.@]
2714  */
2715 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2716 {
2717     LPWINE_MLD          wmld;
2718
2719     TRACE("(%p);\n", hWaveOut);
2720
2721     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2722         return MMSYSERR_INVALHANDLE;
2723     return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
2724 }
2725
2726 /**************************************************************************
2727  *                              waveOutPause            [WINMM.@]
2728  */
2729 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2730 {
2731     LPWINE_MLD          wmld;
2732
2733     TRACE("(%p);\n", hWaveOut);
2734
2735     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2736         return MMSYSERR_INVALHANDLE;
2737     return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
2738 }
2739
2740 /**************************************************************************
2741  *                              waveOutReset            [WINMM.@]
2742  */
2743 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2744 {
2745     LPWINE_MLD          wmld;
2746
2747     TRACE("(%p);\n", hWaveOut);
2748
2749     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2750         return MMSYSERR_INVALHANDLE;
2751     return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
2752 }
2753
2754 /**************************************************************************
2755  *                              waveOutRestart          [WINMM.@]
2756  */
2757 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2758 {
2759     LPWINE_MLD          wmld;
2760
2761     TRACE("(%p);\n", hWaveOut);
2762
2763     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2764         return MMSYSERR_INVALHANDLE;
2765     return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
2766 }
2767
2768 /**************************************************************************
2769  *                              waveOutGetPosition      [WINMM.@]
2770  */
2771 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2772                                UINT uSize)
2773 {
2774     LPWINE_MLD          wmld;
2775
2776     TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);
2777
2778     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2779         return MMSYSERR_INVALHANDLE;
2780
2781     return MMDRV_Message(wmld, WODM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
2782 }
2783
2784 /**************************************************************************
2785  *                              waveOutGetPitch         [WINMM.@]
2786  */
2787 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2788 {
2789     LPWINE_MLD          wmld;
2790
2791     TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
2792
2793     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2794         return MMSYSERR_INVALHANDLE;
2795     return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L, TRUE);
2796 }
2797
2798 /**************************************************************************
2799  *                              waveOutSetPitch         [WINMM.@]
2800  */
2801 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2802 {
2803     LPWINE_MLD          wmld;
2804
2805     TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);
2806
2807     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2808         return MMSYSERR_INVALHANDLE;
2809     return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
2810 }
2811
2812 /**************************************************************************
2813  *                              waveOutGetPlaybackRate  [WINMM.@]
2814  */
2815 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2816 {
2817     LPWINE_MLD          wmld;
2818
2819     TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
2820
2821     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2822         return MMSYSERR_INVALHANDLE;
2823     return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L, TRUE);
2824 }
2825
2826 /**************************************************************************
2827  *                              waveOutSetPlaybackRate  [WINMM.@]
2828  */
2829 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2830 {
2831     LPWINE_MLD          wmld;
2832
2833     TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);
2834
2835     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2836         return MMSYSERR_INVALHANDLE;
2837     return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
2838 }
2839
2840 /**************************************************************************
2841  *                              waveOutGetVolume        [WINMM.@]
2842  */
2843 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)
2844 {
2845     LPWINE_MLD          wmld;
2846
2847     TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
2848
2849      if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2850         return MMSYSERR_INVALHANDLE;
2851
2852     return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L, TRUE);
2853 }
2854
2855 /**************************************************************************
2856  *                              waveOutSetVolume        [WINMM.@]
2857  */
2858 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)
2859 {
2860     LPWINE_MLD          wmld;
2861
2862     TRACE("(%p, %08lx);\n", hWaveOut, dw);
2863
2864      if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2865         return MMSYSERR_INVALHANDLE;
2866
2867     return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
2868 }
2869
2870 /**************************************************************************
2871  *                              waveOutGetID            [WINMM.@]
2872  */
2873 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2874 {
2875     LPWINE_MLD          wmld;
2876
2877     TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);
2878
2879     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2880
2881     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2882         return MMSYSERR_INVALHANDLE;
2883
2884     *lpuDeviceID = wmld->uDeviceID;
2885     return 0;
2886 }
2887
2888 /**************************************************************************
2889  *                              waveOutMessage          [WINMM.@]
2890  */
2891 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2892                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2893 {
2894     LPWINE_MLD          wmld;
2895
2896     TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2897
2898     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
2899         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
2900             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2901         }
2902         WARN("invalid handle\n");
2903         return MMSYSERR_INVALHANDLE;
2904     }
2905
2906     /* from M$ KB */
2907     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER)) {
2908         WARN("invalid parameter\n");
2909         return MMSYSERR_INVALPARAM;
2910     }
2911
2912     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2913 }
2914
2915 /**************************************************************************
2916  *                              waveInGetNumDevs                [WINMM.@]
2917  */
2918 UINT WINAPI waveInGetNumDevs(void)
2919 {
2920     return MMDRV_GetNum(MMDRV_WAVEIN);
2921 }
2922
2923 /**************************************************************************
2924  *                              waveInGetDevCapsW               [WINMM.@]
2925  */
2926 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2927 {
2928     WAVEINCAPSA         wicA;
2929     UINT                ret = waveInGetDevCapsA(uDeviceID, &wicA, sizeof(wicA));
2930
2931     if (ret == MMSYSERR_NOERROR) {
2932         WAVEINCAPSW wicW;
2933         wicW.wMid           = wicA.wMid;
2934         wicW.wPid           = wicA.wPid;
2935         wicW.vDriverVersion = wicA.vDriverVersion;
2936         MultiByteToWideChar( CP_ACP, 0, wicA.szPname, -1, wicW.szPname,
2937                              sizeof(wicW.szPname)/sizeof(WCHAR) );
2938         wicW.dwFormats      = wicA.dwFormats;
2939         wicW.wChannels      = wicA.wChannels;
2940         memcpy(lpCaps, &wicW, min(uSize, sizeof(wicW)));
2941     }
2942     return ret;
2943 }
2944
2945 /**************************************************************************
2946  *                              waveInGetDevCapsA               [WINMM.@]
2947  */
2948 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2949 {
2950     LPWINE_MLD          wmld;
2951
2952     TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
2953
2954     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2955
2956     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
2957         return MMSYSERR_BADDEVICEID;
2958
2959     return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
2960 }
2961
2962 /**************************************************************************
2963  *                              waveInGetErrorTextA     [WINMM.@]
2964  */
2965 UINT WINAPI waveInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2966 {
2967     return WAVE_GetErrorText(uError, lpText, uSize);
2968 }
2969
2970 /**************************************************************************
2971  *                              waveInGetErrorTextW     [WINMM.@]
2972  */
2973 UINT WINAPI waveInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2974 {
2975     LPSTR txt = HeapAlloc(GetProcessHeap(), 0, uSize);
2976     UINT        ret = WAVE_GetErrorText(uError, txt, uSize);
2977
2978     MultiByteToWideChar( CP_ACP, 0, txt, -1, lpText, uSize );
2979     HeapFree(GetProcessHeap(), 0, txt);
2980     return ret;
2981 }
2982
2983 /**************************************************************************
2984  *                              waveInOpen                      [WINMM.@]
2985  */
2986 UINT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2987                        const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
2988                        DWORD dwInstance, DWORD dwFlags)
2989 {
2990     return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
2991                      dwCallback, dwInstance, dwFlags, TRUE);
2992 }
2993
2994 /**************************************************************************
2995  *                              waveInClose                     [WINMM.@]
2996  */
2997 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2998 {
2999     LPWINE_MLD          wmld;
3000     DWORD               dwRet;
3001
3002     TRACE("(%p)\n", hWaveIn);
3003
3004     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3005         return MMSYSERR_INVALHANDLE;
3006
3007     dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
3008     if (dwRet != WAVERR_STILLPLAYING)
3009         MMDRV_Free(hWaveIn, wmld);
3010     return dwRet;
3011 }
3012
3013 /**************************************************************************
3014  *                              waveInPrepareHeader             [WINMM.@]
3015  */
3016 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3017                                 UINT uSize)
3018 {
3019     LPWINE_MLD          wmld;
3020
3021     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
3022
3023     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
3024         return MMSYSERR_INVALPARAM;
3025
3026     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3027         return MMSYSERR_INVALHANDLE;
3028
3029     lpWaveInHdr->dwBytesRecorded = 0;
3030
3031     return MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);
3032 }
3033
3034 /**************************************************************************
3035  *                              waveInUnprepareHeader   [WINMM.@]
3036  */
3037 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3038                                   UINT uSize)
3039 {
3040     LPWINE_MLD          wmld;
3041
3042     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
3043
3044     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
3045         return MMSYSERR_INVALPARAM;
3046
3047     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {
3048         return MMSYSERR_NOERROR;
3049     }
3050
3051     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3052         return MMSYSERR_INVALHANDLE;
3053
3054     return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);
3055 }
3056
3057 /**************************************************************************
3058  *                              waveInAddBuffer         [WINMM.@]
3059  */
3060 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
3061                             WAVEHDR* lpWaveInHdr, UINT uSize)
3062 {
3063     LPWINE_MLD          wmld;
3064
3065     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
3066
3067     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
3068     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3069         return MMSYSERR_INVALHANDLE;
3070
3071     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);
3072 }
3073
3074 /**************************************************************************
3075  *                              waveInReset             [WINMM.@]
3076  */
3077 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3078 {
3079     LPWINE_MLD          wmld;
3080
3081     TRACE("(%p);\n", hWaveIn);
3082
3083     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3084         return MMSYSERR_INVALHANDLE;
3085
3086     return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
3087 }
3088
3089 /**************************************************************************
3090  *                              waveInStart             [WINMM.@]
3091  */
3092 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3093 {
3094     LPWINE_MLD          wmld;
3095
3096     TRACE("(%p);\n", hWaveIn);
3097
3098     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3099         return MMSYSERR_INVALHANDLE;
3100
3101     return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
3102 }
3103
3104 /**************************************************************************
3105  *                              waveInStop              [WINMM.@]
3106  */
3107 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3108 {
3109     LPWINE_MLD          wmld;
3110
3111     TRACE("(%p);\n", hWaveIn);
3112
3113     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3114         return MMSYSERR_INVALHANDLE;
3115
3116     return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
3117 }
3118
3119 /**************************************************************************
3120  *                              waveInGetPosition       [WINMM.@]
3121  */
3122 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3123                               UINT uSize)
3124 {
3125     LPWINE_MLD          wmld;
3126
3127     TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
3128
3129     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3130         return MMSYSERR_INVALHANDLE;
3131
3132     return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
3133 }
3134
3135 /**************************************************************************
3136  *                              waveInGetID                     [WINMM.@]
3137  */
3138 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3139 {
3140     LPWINE_MLD          wmld;
3141
3142     TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
3143
3144     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
3145
3146     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
3147         return MMSYSERR_INVALHANDLE;
3148
3149     *lpuDeviceID = wmld->uDeviceID;
3150     return MMSYSERR_NOERROR;
3151 }
3152
3153 /**************************************************************************
3154  *                              waveInMessage           [WINMM.@]
3155  */
3156 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3157                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3158 {
3159     LPWINE_MLD          wmld;
3160
3161     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3162
3163     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
3164         if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
3165             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
3166         }
3167         return MMSYSERR_INVALHANDLE;
3168     }
3169
3170     /* from M$ KB */
3171     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
3172         return MMSYSERR_INVALPARAM;
3173
3174
3175     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
3176 }