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