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