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