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