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