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