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