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