crypt32: Fix a failing test in Vista.
[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 /**************************************************************************
1445  *                              midiConnect                     [WINMM.@]
1446  */
1447 MMRESULT WINAPI midiConnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
1448 {
1449     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
1450     return MMSYSERR_ERROR;
1451 }
1452
1453 /**************************************************************************
1454  *                              midiDisconnect                  [WINMM.@]
1455  */
1456 MMRESULT WINAPI midiDisconnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
1457 {
1458     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
1459     return MMSYSERR_ERROR;
1460 }
1461
1462 typedef struct WINE_MIDIStream {
1463     HMIDIOUT                    hDevice;
1464     HANDLE                      hThread;
1465     DWORD                       dwThreadID;
1466     DWORD                       dwTempo;
1467     DWORD                       dwTimeDiv;
1468     DWORD                       dwPositionMS;
1469     DWORD                       dwPulses;
1470     DWORD                       dwStartTicks;
1471     WORD                        wFlags;
1472     HANDLE                      hEvent;
1473     LPMIDIHDR                   lpMidiHdr;
1474 } WINE_MIDIStream;
1475
1476 #define WINE_MSM_HEADER         (WM_USER+0)
1477 #define WINE_MSM_STOP           (WM_USER+1)
1478
1479 /**************************************************************************
1480  *                              MMSYSTEM_GetMidiStream          [internal]
1481  */
1482 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
1483 {
1484     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
1485
1486     if (lplpwm)
1487         *lplpwm = lpwm;
1488
1489     if (lpwm == NULL) {
1490         return FALSE;
1491     }
1492
1493     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
1494
1495     return *lpMidiStrm != NULL;
1496 }
1497
1498 /**************************************************************************
1499  *                              MMSYSTEM_MidiStream_Convert     [internal]
1500  */
1501 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
1502 {
1503     DWORD       ret = 0;
1504
1505     if (lpMidiStrm->dwTimeDiv == 0) {
1506         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
1507     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
1508         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
1509         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
1510         ret = (pulse * 1000) / (nf * nsf);
1511     } else {
1512         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
1513                       (double)lpMidiStrm->dwTimeDiv);
1514     }
1515
1516     return ret;
1517 }
1518
1519 /**************************************************************************
1520  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
1521  */
1522 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1523 {
1524     LPMIDIHDR   lpMidiHdr;
1525     LPMIDIHDR*  lpmh;
1526     LPBYTE      lpData;
1527
1528     switch (msg->message) {
1529     case WM_QUIT:
1530         SetEvent(lpMidiStrm->hEvent);
1531         return FALSE;
1532     case WINE_MSM_STOP:
1533         TRACE("STOP\n");
1534         /* this is not quite what MS doc says... */
1535         midiOutReset(lpMidiStrm->hDevice);
1536         /* empty list of already submitted buffers */
1537         for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = lpMidiHdr->lpNext) {
1538             lpMidiHdr->dwFlags |= MHDR_DONE;
1539             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1540
1541             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1542                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1543                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1544         }
1545         lpMidiStrm->lpMidiHdr = 0;
1546         SetEvent(lpMidiStrm->hEvent);
1547         break;
1548     case WINE_MSM_HEADER:
1549         /* sets initial tick count for first MIDIHDR */
1550         if (!lpMidiStrm->dwStartTicks)
1551             lpMidiStrm->dwStartTicks = GetTickCount();
1552
1553         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1554          * by native mcimidi, it doesn't look like a correct one".
1555          * this trick allows to throw it away... but I don't like it.
1556          * It looks like part of the file I'm trying to play and definitively looks
1557          * like raw midi content
1558          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1559          * synchronization issue where native mcimidi is still processing raw MIDI
1560          * content before generating MIDIEVENTs ?
1561          *
1562          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1563          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1564          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1565          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1566          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1567          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1568          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1569          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1570          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1571          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1572          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1573          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1574          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1575          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1576          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1577          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1578          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1579          */
1580         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1581         lpData = (LPBYTE)lpMidiHdr->lpData;
1582         TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBufferLength=%u/%u dwFlags=0x%08x size=%lu]\n",
1583               (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1584               lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
1585               lpMidiHdr->dwFlags, msg->wParam);
1586 #if 0
1587         /* dumps content of lpMidiHdr->lpData
1588          * FIXME: there should be a debug routine somewhere that already does this
1589          * I hate spreading this type of shit all around the code
1590          */
1591         for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1592             DWORD       i;
1593             BYTE        ch;
1594
1595             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1596                 printf("%02x ", lpData[dwToGo + i]);
1597             for (; i < 16; i++)
1598                 printf("   ");
1599             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1600                 ch = lpData[dwToGo + i];
1601                 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1602             }
1603             printf("\n");
1604         }
1605 #endif
1606         if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1607             ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1608             ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
1609             FIXME("Dropping bad %s lpMidiHdr (streamID=%08x)\n",
1610                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1611                   ((LPMIDIEVENT)lpData)->dwStreamID);
1612             lpMidiHdr->dwFlags |= MHDR_DONE;
1613             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1614
1615             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1616                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1617                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1618             break;
1619         }
1620
1621         for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = &(*lpmh)->lpNext);
1622         *lpmh = lpMidiHdr;
1623         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1624         lpMidiHdr->lpNext = 0;
1625         lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1626         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1627         lpMidiHdr->dwOffset = 0;
1628
1629         break;
1630     default:
1631         FIXME("Unknown message %d\n", msg->message);
1632         break;
1633     }
1634     return TRUE;
1635 }
1636
1637 /**************************************************************************
1638  *                              MMSYSTEM_MidiStream_Player      [internal]
1639  */
1640 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
1641 {
1642     WINE_MIDIStream*    lpMidiStrm = pmt;
1643     WINE_MIDI*          lpwm;
1644     MSG                 msg;
1645     DWORD               dwToGo;
1646     DWORD               dwCurrTC;
1647     LPMIDIHDR           lpMidiHdr;
1648     LPMIDIEVENT         me;
1649     LPBYTE              lpData = 0;
1650
1651     TRACE("(%p)!\n", lpMidiStrm);
1652
1653     if (!lpMidiStrm ||
1654         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1655         goto the_end;
1656
1657     /* force thread's queue creation */
1658     /* Used to be InitThreadInput16(0, 5); */
1659     /* but following works also with hack in midiStreamOpen */
1660     PeekMessageA(&msg, 0, 0, 0, 0);
1661
1662     /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
1663     SetEvent(lpMidiStrm->hEvent);
1664     TRACE("Ready to go 1\n");
1665     /* thread is started in paused mode */
1666     SuspendThread(lpMidiStrm->hThread);
1667     TRACE("Ready to go 2\n");
1668
1669     lpMidiStrm->dwStartTicks = 0;
1670     lpMidiStrm->dwPulses = 0;
1671
1672     lpMidiStrm->lpMidiHdr = 0;
1673
1674     for (;;) {
1675         lpMidiHdr = lpMidiStrm->lpMidiHdr;
1676         if (!lpMidiHdr) {
1677             /* for first message, block until one arrives, then process all that are available */
1678             GetMessageA(&msg, 0, 0, 0);
1679             do {
1680                 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1681                     goto the_end;
1682             } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
1683             lpData = 0;
1684             continue;
1685         }
1686
1687         if (!lpData)
1688             lpData = (LPBYTE)lpMidiHdr->lpData;
1689
1690         me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
1691
1692         /* do we have to wait ? */
1693         if (me->dwDeltaTime) {
1694             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1695             lpMidiStrm->dwPulses += me->dwDeltaTime;
1696
1697             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
1698
1699             TRACE("%d/%d/%d\n", dwToGo, GetTickCount(), me->dwDeltaTime);
1700             while ((dwCurrTC = GetTickCount()) < dwToGo) {
1701                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1702                     /* got a message, handle it */
1703                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1704                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1705                             goto the_end;
1706                     }
1707                     lpData = 0;
1708                 } else {
1709                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1710                     break;
1711                 }
1712             }
1713         }
1714         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1715         case MEVT_COMMENT:
1716             FIXME("NIY: MEVT_COMMENT\n");
1717             /* do nothing, skip bytes */
1718             break;
1719         case MEVT_LONGMSG:
1720             FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
1721             break;
1722         case MEVT_NOP:
1723             break;
1724         case MEVT_SHORTMSG:
1725             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1726             break;
1727         case MEVT_TEMPO:
1728             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1729             break;
1730         case MEVT_VERSION:
1731             break;
1732         default:
1733             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1734             break;
1735         }
1736         if (me->dwEvent & MEVT_F_CALLBACK) {
1737             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1738                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1739                            lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1740         }
1741         lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
1742         if (me->dwEvent & MEVT_F_LONG)
1743             lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1744         if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
1745             /* done with this header */
1746             lpMidiHdr->dwFlags |= MHDR_DONE;
1747             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1748
1749             lpMidiStrm->lpMidiHdr = lpMidiHdr->lpNext;
1750             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1751                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1752                            lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1753             lpData = 0;
1754         }
1755     }
1756 the_end:
1757     TRACE("End of thread\n");
1758     ExitThread(0);
1759     return 0;   /* for removing the warning, never executed */
1760 }
1761
1762 /**************************************************************************
1763  *                              MMSYSTEM_MidiStream_PostMessage [internal]
1764  */
1765 static  BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
1766 {
1767     if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
1768         DWORD   count;
1769
1770         if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
1771         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1772         if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
1773     } else {
1774         WARN("bad PostThreadMessageA\n");
1775         return FALSE;
1776     }
1777     return TRUE;
1778 }
1779
1780 /**************************************************************************
1781  *                              midiStreamClose                 [WINMM.@]
1782  */
1783 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1784 {
1785     WINE_MIDIStream*    lpMidiStrm;
1786
1787     TRACE("(%p)!\n", hMidiStrm);
1788
1789     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1790         return MMSYSERR_INVALHANDLE;
1791
1792     midiStreamStop(hMidiStrm);
1793     MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
1794     HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1795     CloseHandle(lpMidiStrm->hEvent);
1796
1797     return midiOutClose((HMIDIOUT)hMidiStrm);
1798 }
1799
1800 /**************************************************************************
1801  *                              MMSYSTEM_MidiStream_Open        [internal]
1802  */
1803 MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,
1804                          DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen, 
1805                          BOOL bFrom32)
1806 {
1807     WINE_MIDIStream*    lpMidiStrm;
1808     MMRESULT            ret;
1809     MIDIOPENSTRMID      mosm;
1810     LPWINE_MIDI         lpwm;
1811     HMIDIOUT            hMidiOut;
1812
1813     TRACE("(%p, %p, %d, 0x%08lx, 0x%08lx, 0x%08x)!\n",
1814           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1815
1816     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1817         return MMSYSERR_INVALPARAM;
1818
1819     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
1820     if (!lpMidiStrm)
1821         return MMSYSERR_NOMEM;
1822
1823     lpMidiStrm->dwTempo = 500000;
1824     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quarter notes per minute *//* FIXME ??*/
1825     lpMidiStrm->dwPositionMS = 0;
1826
1827     mosm.dwStreamID = (DWORD)lpMidiStrm;
1828     /* FIXME: the correct value is not allocated yet for MAPPER */
1829     mosm.wDeviceID  = *lpuDeviceID;
1830     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
1831     lpMidiStrm->hDevice = hMidiOut;
1832     if (lphMidiStrm)
1833         *lphMidiStrm = (HMIDISTRM)hMidiOut;
1834
1835     lpwm->mld.uDeviceID = *lpuDeviceID;
1836
1837     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
1838     lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1839     lpMidiStrm->wFlags = HIWORD(fdwOpen);
1840
1841     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1842                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1843
1844     if (!lpMidiStrm->hThread) {
1845         midiStreamClose((HMIDISTRM)hMidiOut);
1846         return MMSYSERR_NOMEM;
1847     }
1848     SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
1849
1850     /* wait for thread to have started, and for its queue to be created */
1851     {
1852         DWORD   count;
1853
1854         /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
1855          * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
1856          * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
1857          */
1858         if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
1859         WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1860         if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
1861     }
1862
1863     TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1864           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1865     return ret;
1866 }
1867
1868 /**************************************************************************
1869  *                              midiStreamOpen                  [WINMM.@]
1870  */
1871 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1872                                DWORD cMidi, DWORD_PTR dwCallback,
1873                                DWORD_PTR dwInstance, DWORD fdwOpen)
1874 {
1875     return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
1876                            dwInstance, fdwOpen, TRUE);
1877 }
1878
1879 /**************************************************************************
1880  *                              midiStreamOut                   [WINMM.@]
1881  */
1882 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
1883                               UINT cbMidiHdr)
1884 {
1885     WINE_MIDIStream*    lpMidiStrm;
1886     DWORD               ret = MMSYSERR_NOERROR;
1887
1888     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
1889
1890     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1891         ret = MMSYSERR_INVALHANDLE;
1892     } else if (!lpMidiHdr) {
1893         ret = MMSYSERR_INVALPARAM;
1894     } else {
1895         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
1896                                 WINE_MSM_HEADER, cbMidiHdr,
1897                                 (DWORD)lpMidiHdr)) {
1898             WARN("bad PostThreadMessageA\n");
1899             ret = MMSYSERR_ERROR;
1900         }
1901     }
1902     return ret;
1903 }
1904
1905 /**************************************************************************
1906  *                              midiStreamPause                 [WINMM.@]
1907  */
1908 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
1909 {
1910     WINE_MIDIStream*    lpMidiStrm;
1911     DWORD               ret = MMSYSERR_NOERROR;
1912
1913     TRACE("(%p)!\n", hMidiStrm);
1914
1915     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1916         ret = MMSYSERR_INVALHANDLE;
1917     } else {
1918         if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
1919             WARN("bad Suspend (%d)\n", GetLastError());
1920             ret = MMSYSERR_ERROR;
1921         }
1922     }
1923     return ret;
1924 }
1925
1926 /**************************************************************************
1927  *                              midiStreamPosition              [WINMM.@]
1928  */
1929 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
1930 {
1931     WINE_MIDIStream*    lpMidiStrm;
1932     DWORD               ret = MMSYSERR_NOERROR;
1933
1934     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
1935
1936     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1937         ret = MMSYSERR_INVALHANDLE;
1938     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
1939         ret = MMSYSERR_INVALPARAM;
1940     } else {
1941         switch (lpMMT->wType) {
1942         case TIME_MS:
1943             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
1944             TRACE("=> %d ms\n", lpMMT->u.ms);
1945             break;
1946         case TIME_TICKS:
1947             lpMMT->u.ticks = lpMidiStrm->dwPulses;
1948             TRACE("=> %d ticks\n", lpMMT->u.ticks);
1949             break;
1950         default:
1951             WARN("Unsupported time type %d\n", lpMMT->wType);
1952             lpMMT->wType = TIME_MS;
1953             ret = MMSYSERR_INVALPARAM;
1954             break;
1955         }
1956     }
1957     return ret;
1958 }
1959
1960 /**************************************************************************
1961  *                              midiStreamProperty              [WINMM.@]
1962  */
1963 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
1964 {
1965     WINE_MIDIStream*    lpMidiStrm;
1966     MMRESULT            ret = MMSYSERR_NOERROR;
1967
1968     TRACE("(%p, %p, %x)\n", hMidiStrm, lpPropData, dwProperty);
1969
1970     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1971         ret = MMSYSERR_INVALHANDLE;
1972     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
1973         ret = MMSYSERR_INVALPARAM;
1974     } else if (dwProperty & MIDIPROP_TEMPO) {
1975         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
1976
1977         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
1978             ret = MMSYSERR_INVALPARAM;
1979         } else if (dwProperty & MIDIPROP_SET) {
1980             lpMidiStrm->dwTempo = mpt->dwTempo;
1981             TRACE("Setting tempo to %d\n", mpt->dwTempo);
1982         } else if (dwProperty & MIDIPROP_GET) {
1983             mpt->dwTempo = lpMidiStrm->dwTempo;
1984             TRACE("Getting tempo <= %d\n", mpt->dwTempo);
1985         }
1986     } else if (dwProperty & MIDIPROP_TIMEDIV) {
1987         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
1988
1989         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
1990             ret = MMSYSERR_INVALPARAM;
1991         } else if (dwProperty & MIDIPROP_SET) {
1992             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
1993             TRACE("Setting time div to %d\n", mptd->dwTimeDiv);
1994         } else if (dwProperty & MIDIPROP_GET) {
1995             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
1996             TRACE("Getting time div <= %d\n", mptd->dwTimeDiv);
1997         }
1998     } else {
1999         ret = MMSYSERR_INVALPARAM;
2000     }
2001
2002     return ret;
2003 }
2004
2005 /**************************************************************************
2006  *                              midiStreamRestart               [WINMM.@]
2007  */
2008 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
2009 {
2010     WINE_MIDIStream*    lpMidiStrm;
2011     MMRESULT            ret = MMSYSERR_NOERROR;
2012
2013     TRACE("(%p)!\n", hMidiStrm);
2014
2015     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2016         ret = MMSYSERR_INVALHANDLE;
2017     } else {
2018         DWORD   ret;
2019
2020         /* since we increase the thread suspend count on each midiStreamPause
2021          * there may be a need for several midiStreamResume
2022          */
2023         do {
2024             ret = ResumeThread(lpMidiStrm->hThread);
2025         } while (ret != 0xFFFFFFFF && ret != 0);
2026         if (ret == 0xFFFFFFFF) {
2027             WARN("bad Resume (%d)\n", GetLastError());
2028             ret = MMSYSERR_ERROR;
2029         } else {
2030             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
2031         }
2032     }
2033     return ret;
2034 }
2035
2036 /**************************************************************************
2037  *                              midiStreamStop                  [WINMM.@]
2038  */
2039 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
2040 {
2041     WINE_MIDIStream*    lpMidiStrm;
2042     MMRESULT            ret = MMSYSERR_NOERROR;
2043
2044     TRACE("(%p)!\n", hMidiStrm);
2045
2046     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2047         ret = MMSYSERR_INVALHANDLE;
2048     } else {
2049         /* in case stream has been paused... FIXME is the current state correct ? */
2050         midiStreamRestart(hMidiStrm);
2051         MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
2052     }
2053     return ret;
2054 }
2055
2056 UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType, 
2057                LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback, 
2058                DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
2059 {
2060     HANDLE              handle;
2061     LPWINE_MLD          wmld;
2062     DWORD               dwRet = MMSYSERR_NOERROR;
2063     WAVEOPENDESC        wod;
2064
2065     TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08X, %d);\n",
2066           lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
2067           dwInstance, dwFlags, bFrom32?32:16);
2068
2069     if (dwFlags & WAVE_FORMAT_QUERY)
2070         TRACE("WAVE_FORMAT_QUERY requested !\n");
2071
2072     if (lpFormat == NULL) {
2073         WARN("bad format\n");
2074         return WAVERR_BADFORMAT;
2075     }
2076
2077     if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
2078         WARN("invalid parameter\n");
2079         return MMSYSERR_INVALPARAM;
2080     }
2081
2082     /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
2083     TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u\n",
2084           lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
2085           lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);
2086
2087     if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
2088                             &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL) {
2089         WARN("no memory\n");
2090         return MMSYSERR_NOMEM;
2091     }
2092
2093     wod.hWave = handle;
2094     wod.lpFormat = (LPWAVEFORMATEX)lpFormat;  /* should the struct be copied iso pointer? */
2095     wod.dwCallback = dwCallback;
2096     wod.dwInstance = dwInstance;
2097     wod.dnDevNode = 0L;
2098
2099     TRACE("cb=%08x\n", wod.dwCallback);
2100
2101     for (;;) {
2102         if (dwFlags & WAVE_MAPPED) {
2103             wod.uMappedDeviceID = uDeviceID;
2104             uDeviceID = WAVE_MAPPER;
2105         } else {
2106             wod.uMappedDeviceID = -1;
2107         }
2108         wmld->uDeviceID = uDeviceID;
2109     
2110         dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN, 
2111                            (DWORD)&wod, dwFlags);
2112
2113         TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));
2114         if (dwRet != WAVERR_BADFORMAT ||
2115             ((dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) || (uDeviceID == WAVE_MAPPER)) break;
2116         /* if we ask for a format which isn't supported by the physical driver, 
2117          * let's try to map it through the wave mapper (except, if we already tried
2118          * or user didn't allow us to use acm codecs or the device is already the mapper)
2119          */
2120         dwFlags |= WAVE_MAPPED;
2121         /* we shall loop only one */
2122     }
2123
2124     if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
2125         MMDRV_Free(handle, wmld);
2126         handle = 0;
2127     }
2128
2129     if (lphndl != NULL) *lphndl = handle;
2130     TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);
2131
2132     return dwRet;
2133 }
2134
2135 /**************************************************************************
2136  *                              waveOutGetNumDevs               [WINMM.@]
2137  */
2138 UINT WINAPI waveOutGetNumDevs(void)
2139 {
2140     return MMDRV_GetNum(MMDRV_WAVEOUT);
2141 }
2142
2143 /**************************************************************************
2144  *                              waveOutGetDevCapsA              [WINMM.@]
2145  */
2146 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2147                                UINT uSize)
2148 {
2149     WAVEOUTCAPSW        wocW;
2150     UINT                ret;
2151
2152     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
2153
2154     ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2155
2156     if (ret == MMSYSERR_NOERROR) {
2157         WAVEOUTCAPSA wocA;
2158         wocA.wMid           = wocW.wMid;
2159         wocA.wPid           = wocW.wPid;
2160         wocA.vDriverVersion = wocW.vDriverVersion;
2161         WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2162                              sizeof(wocA.szPname), NULL, NULL );
2163         wocA.dwFormats      = wocW.dwFormats;
2164         wocA.wChannels      = wocW.wChannels;
2165         wocA.dwSupport      = wocW.dwSupport;
2166         memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2167     }
2168     return ret;
2169 }
2170
2171 /**************************************************************************
2172  *                              waveOutGetDevCapsW              [WINMM.@]
2173  */
2174 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2175                                UINT uSize)
2176 {
2177     LPWINE_MLD          wmld;
2178
2179     TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2180
2181     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2182
2183     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
2184         return MMSYSERR_BADDEVICEID;
2185
2186     return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
2187
2188 }
2189
2190 /**************************************************************************
2191  *                              waveOutGetErrorTextA    [WINMM.@]
2192  *                              waveInGetErrorTextA     [WINMM.@]
2193  */
2194 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2195 {
2196     UINT        ret;
2197
2198     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2199     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2200     else
2201     {
2202         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2203         if (!xstr) ret = MMSYSERR_NOMEM;
2204         else
2205         {
2206             ret = waveOutGetErrorTextW(uError, xstr, uSize);
2207             if (ret == MMSYSERR_NOERROR)
2208                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2209             HeapFree(GetProcessHeap(), 0, xstr);
2210         }
2211     }
2212     return ret;
2213 }
2214
2215 /**************************************************************************
2216  *                              waveOutGetErrorTextW    [WINMM.@]
2217  *                              waveInGetErrorTextW     [WINMM.@]
2218  */
2219 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2220 {
2221     UINT        ret = MMSYSERR_BADERRNUM;
2222
2223     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2224     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2225     else if (
2226                /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2227                 * a warning for the test was always true */
2228                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2229                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
2230         if (LoadStringW(hWinMM32Instance,
2231                         uError, lpText, uSize) > 0) {
2232             ret = MMSYSERR_NOERROR;
2233         }
2234     }
2235     return ret;
2236 }
2237
2238 /**************************************************************************
2239  *                      waveOutOpen                     [WINMM.@]
2240  * All the args/structs have the same layout as the win16 equivalents
2241  */
2242 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2243                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2244                        DWORD_PTR dwInstance, DWORD dwFlags)
2245 {
2246     return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
2247                      dwCallback, dwInstance, dwFlags, TRUE);
2248 }
2249
2250 /**************************************************************************
2251  *                              waveOutClose            [WINMM.@]
2252  */
2253 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2254 {
2255     LPWINE_MLD          wmld;
2256     DWORD               dwRet;
2257
2258     TRACE("(%p)\n", hWaveOut);
2259
2260     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2261         return MMSYSERR_INVALHANDLE;
2262
2263     dwRet = MMDRV_Close(wmld, WODM_CLOSE);
2264     if (dwRet != WAVERR_STILLPLAYING)
2265         MMDRV_Free(hWaveOut, wmld);
2266
2267     return dwRet;
2268 }
2269
2270 /**************************************************************************
2271  *                              waveOutPrepareHeader    [WINMM.@]
2272  */
2273 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2274                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
2275 {
2276     LPWINE_MLD          wmld;
2277     UINT                result;
2278
2279     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2280
2281     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2282         return MMSYSERR_INVALPARAM;
2283
2284     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2285         return MMSYSERR_INVALHANDLE;
2286
2287     if ((result = MMDRV_Message(wmld, WODM_PREPARE, (DWORD_PTR)lpWaveOutHdr,
2288                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2289         return result;
2290
2291     if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2292         return WAVERR_STILLPLAYING;
2293
2294     lpWaveOutHdr->dwFlags |= WHDR_PREPARED;
2295     lpWaveOutHdr->dwFlags &= ~WHDR_DONE;
2296
2297     return MMSYSERR_NOERROR;
2298 }
2299
2300 /**************************************************************************
2301  *                              waveOutUnprepareHeader  [WINMM.@]
2302  */
2303 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2304                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
2305 {
2306     LPWINE_MLD          wmld;
2307     UINT                result;
2308
2309     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2310
2311     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2312         return MMSYSERR_INVALPARAM;
2313     
2314     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
2315         return MMSYSERR_NOERROR;
2316     }
2317
2318     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2319         return MMSYSERR_INVALHANDLE;
2320
2321     if ((result = MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD_PTR)lpWaveOutHdr,
2322                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2323         return result;
2324
2325     if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2326         return WAVERR_STILLPLAYING;
2327
2328     lpWaveOutHdr->dwFlags &= ~WHDR_PREPARED;
2329     lpWaveOutHdr->dwFlags |= WHDR_DONE;
2330
2331     return MMSYSERR_NOERROR;
2332 }
2333
2334 /**************************************************************************
2335  *                              waveOutWrite            [WINMM.@]
2336  */
2337 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
2338                          UINT uSize)
2339 {
2340     LPWINE_MLD          wmld;
2341
2342     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2343
2344     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2345         return MMSYSERR_INVALHANDLE;
2346
2347     return MMDRV_Message(wmld, WODM_WRITE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);
2348 }
2349
2350 /**************************************************************************
2351  *                              waveOutBreakLoop        [WINMM.@]
2352  */
2353 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2354 {
2355     LPWINE_MLD          wmld;
2356
2357     TRACE("(%p);\n", hWaveOut);
2358
2359     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2360         return MMSYSERR_INVALHANDLE;
2361     return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
2362 }
2363
2364 /**************************************************************************
2365  *                              waveOutPause            [WINMM.@]
2366  */
2367 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2368 {
2369     LPWINE_MLD          wmld;
2370
2371     TRACE("(%p);\n", hWaveOut);
2372
2373     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2374         return MMSYSERR_INVALHANDLE;
2375     return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
2376 }
2377
2378 /**************************************************************************
2379  *                              waveOutReset            [WINMM.@]
2380  */
2381 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2382 {
2383     LPWINE_MLD          wmld;
2384
2385     TRACE("(%p);\n", hWaveOut);
2386
2387     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2388         return MMSYSERR_INVALHANDLE;
2389     return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
2390 }
2391
2392 /**************************************************************************
2393  *                              waveOutRestart          [WINMM.@]
2394  */
2395 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2396 {
2397     LPWINE_MLD          wmld;
2398
2399     TRACE("(%p);\n", hWaveOut);
2400
2401     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2402         return MMSYSERR_INVALHANDLE;
2403     return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
2404 }
2405
2406 /**************************************************************************
2407  *                              waveOutGetPosition      [WINMM.@]
2408  */
2409 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2410                                UINT uSize)
2411 {
2412     LPWINE_MLD          wmld;
2413
2414     TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);
2415
2416     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2417         return MMSYSERR_INVALHANDLE;
2418
2419     return MMDRV_Message(wmld, WODM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
2420 }
2421
2422 /**************************************************************************
2423  *                              waveOutGetPitch         [WINMM.@]
2424  */
2425 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2426 {
2427     LPWINE_MLD          wmld;
2428
2429     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2430
2431     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2432         return MMSYSERR_INVALHANDLE;
2433     return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L, TRUE);
2434 }
2435
2436 /**************************************************************************
2437  *                              waveOutSetPitch         [WINMM.@]
2438  */
2439 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2440 {
2441     LPWINE_MLD          wmld;
2442
2443     TRACE("(%p, %08x);\n", hWaveOut, dw);
2444
2445     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2446         return MMSYSERR_INVALHANDLE;
2447     return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
2448 }
2449
2450 /**************************************************************************
2451  *                              waveOutGetPlaybackRate  [WINMM.@]
2452  */
2453 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2454 {
2455     LPWINE_MLD          wmld;
2456
2457     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2458
2459     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2460         return MMSYSERR_INVALHANDLE;
2461     return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L, TRUE);
2462 }
2463
2464 /**************************************************************************
2465  *                              waveOutSetPlaybackRate  [WINMM.@]
2466  */
2467 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2468 {
2469     LPWINE_MLD          wmld;
2470
2471     TRACE("(%p, %08x);\n", hWaveOut, dw);
2472
2473     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2474         return MMSYSERR_INVALHANDLE;
2475     return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
2476 }
2477
2478 /**************************************************************************
2479  *                              waveOutGetVolume        [WINMM.@]
2480  */
2481 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)
2482 {
2483     LPWINE_MLD          wmld;
2484
2485     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2486
2487     if (lpdw == NULL) {
2488         WARN("invalid parameter\n");
2489         return MMSYSERR_INVALPARAM;
2490     }
2491
2492     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2493         return MMSYSERR_INVALHANDLE;
2494
2495     return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L, TRUE);
2496 }
2497
2498 /**************************************************************************
2499  *                              waveOutSetVolume        [WINMM.@]
2500  */
2501 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)
2502 {
2503     LPWINE_MLD          wmld;
2504
2505     TRACE("(%p, %08x);\n", hWaveOut, dw);
2506
2507      if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2508         return MMSYSERR_INVALHANDLE;
2509
2510     return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
2511 }
2512
2513 /**************************************************************************
2514  *                              waveOutGetID            [WINMM.@]
2515  */
2516 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2517 {
2518     LPWINE_MLD          wmld;
2519
2520     TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);
2521
2522     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2523
2524     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2525         return MMSYSERR_INVALHANDLE;
2526
2527     *lpuDeviceID = wmld->uDeviceID;
2528     return 0;
2529 }
2530
2531 /**************************************************************************
2532  *                              waveOutMessage          [WINMM.@]
2533  */
2534 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2535                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2536 {
2537     LPWINE_MLD          wmld;
2538
2539     TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2540
2541     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
2542         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
2543             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2544         }
2545         WARN("invalid handle\n");
2546         return MMSYSERR_INVALHANDLE;
2547     }
2548
2549     /* from M$ KB */
2550     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER)) {
2551         WARN("invalid parameter\n");
2552         return MMSYSERR_INVALPARAM;
2553     }
2554
2555     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2556 }
2557
2558 /**************************************************************************
2559  *                              waveInGetNumDevs                [WINMM.@]
2560  */
2561 UINT WINAPI waveInGetNumDevs(void)
2562 {
2563     return MMDRV_GetNum(MMDRV_WAVEIN);
2564 }
2565
2566 /**************************************************************************
2567  *                              waveInGetDevCapsW               [WINMM.@]
2568  */
2569 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2570 {
2571     LPWINE_MLD          wmld;
2572
2573     TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2574
2575     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2576
2577     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
2578         return MMSYSERR_BADDEVICEID;
2579
2580     return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
2581 }
2582
2583 /**************************************************************************
2584  *                              waveInGetDevCapsA               [WINMM.@]
2585  */
2586 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2587 {
2588     WAVEINCAPSW         wicW;
2589     UINT                ret;
2590
2591     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2592
2593     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2594
2595     if (ret == MMSYSERR_NOERROR) {
2596         WAVEINCAPSA wicA;
2597         wicA.wMid           = wicW.wMid;
2598         wicA.wPid           = wicW.wPid;
2599         wicA.vDriverVersion = wicW.vDriverVersion;
2600         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2601                              sizeof(wicA.szPname), NULL, NULL );
2602         wicA.dwFormats      = wicW.dwFormats;
2603         wicA.wChannels      = wicW.wChannels;
2604         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2605     }
2606     return ret;
2607 }
2608
2609 /**************************************************************************
2610  *                              waveInOpen                      [WINMM.@]
2611  */
2612 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2613                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2614                        DWORD_PTR dwInstance, DWORD dwFlags)
2615 {
2616     return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
2617                      dwCallback, dwInstance, dwFlags, TRUE);
2618 }
2619
2620 /**************************************************************************
2621  *                              waveInClose                     [WINMM.@]
2622  */
2623 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2624 {
2625     LPWINE_MLD          wmld;
2626     DWORD               dwRet;
2627
2628     TRACE("(%p)\n", hWaveIn);
2629
2630     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2631         return MMSYSERR_INVALHANDLE;
2632
2633     dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
2634     if (dwRet != WAVERR_STILLPLAYING)
2635         MMDRV_Free(hWaveIn, wmld);
2636     return dwRet;
2637 }
2638
2639 /**************************************************************************
2640  *                              waveInPrepareHeader             [WINMM.@]
2641  */
2642 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2643                                 UINT uSize)
2644 {
2645     LPWINE_MLD          wmld;
2646     UINT                result;
2647
2648     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2649
2650     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2651         return MMSYSERR_INVALPARAM;
2652
2653     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2654         return MMSYSERR_INVALHANDLE;
2655
2656     if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,
2657                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2658         return result;
2659
2660     if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2661         return WAVERR_STILLPLAYING;
2662
2663     lpWaveInHdr->dwFlags |= WHDR_PREPARED;
2664     lpWaveInHdr->dwFlags &= ~WHDR_DONE;
2665     lpWaveInHdr->dwBytesRecorded = 0;
2666
2667     return MMSYSERR_NOERROR;
2668 }
2669
2670 /**************************************************************************
2671  *                              waveInUnprepareHeader   [WINMM.@]
2672  */
2673 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2674                                   UINT uSize)
2675 {
2676     LPWINE_MLD          wmld;
2677     UINT                result;
2678
2679     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2680
2681     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2682         return MMSYSERR_INVALPARAM;
2683
2684     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2685         return MMSYSERR_NOERROR;
2686
2687     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2688         return MMSYSERR_INVALHANDLE;
2689
2690     if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,
2691                                 uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
2692         return result;
2693
2694     if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2695         return WAVERR_STILLPLAYING;
2696
2697     lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;
2698     lpWaveInHdr->dwFlags |= WHDR_DONE;
2699
2700     return MMSYSERR_NOERROR;
2701 }
2702
2703 /**************************************************************************
2704  *                              waveInAddBuffer         [WINMM.@]
2705  */
2706 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
2707                             WAVEHDR* lpWaveInHdr, UINT uSize)
2708 {
2709     LPWINE_MLD          wmld;
2710
2711     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2712
2713     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2714     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2715         return MMSYSERR_INVALHANDLE;
2716
2717     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);
2718 }
2719
2720 /**************************************************************************
2721  *                              waveInReset             [WINMM.@]
2722  */
2723 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
2724 {
2725     LPWINE_MLD          wmld;
2726
2727     TRACE("(%p);\n", hWaveIn);
2728
2729     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2730         return MMSYSERR_INVALHANDLE;
2731
2732     return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
2733 }
2734
2735 /**************************************************************************
2736  *                              waveInStart             [WINMM.@]
2737  */
2738 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
2739 {
2740     LPWINE_MLD          wmld;
2741
2742     TRACE("(%p);\n", hWaveIn);
2743
2744     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2745         return MMSYSERR_INVALHANDLE;
2746
2747     return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
2748 }
2749
2750 /**************************************************************************
2751  *                              waveInStop              [WINMM.@]
2752  */
2753 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
2754 {
2755     LPWINE_MLD          wmld;
2756
2757     TRACE("(%p);\n", hWaveIn);
2758
2759     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2760         return MMSYSERR_INVALHANDLE;
2761
2762     return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
2763 }
2764
2765 /**************************************************************************
2766  *                              waveInGetPosition       [WINMM.@]
2767  */
2768 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
2769                               UINT uSize)
2770 {
2771     LPWINE_MLD          wmld;
2772
2773     TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
2774
2775     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2776         return MMSYSERR_INVALHANDLE;
2777
2778     return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
2779 }
2780
2781 /**************************************************************************
2782  *                              waveInGetID                     [WINMM.@]
2783  */
2784 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
2785 {
2786     LPWINE_MLD          wmld;
2787
2788     TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
2789
2790     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2791
2792     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2793         return MMSYSERR_INVALHANDLE;
2794
2795     *lpuDeviceID = wmld->uDeviceID;
2796     return MMSYSERR_NOERROR;
2797 }
2798
2799 /**************************************************************************
2800  *                              waveInMessage           [WINMM.@]
2801  */
2802 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
2803                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2804 {
2805     LPWINE_MLD          wmld;
2806
2807     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
2808
2809     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
2810         if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
2811             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2812         }
2813         return MMSYSERR_INVALHANDLE;
2814     }
2815
2816     /* from M$ KB */
2817     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
2818         return MMSYSERR_INVALPARAM;
2819
2820
2821     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2822 }
2823
2824 struct mm_starter
2825 {
2826     LPTASKCALLBACK      cb;
2827     DWORD               client;
2828     HANDLE              event;
2829 };
2830
2831 static DWORD WINAPI mmTaskRun(void* pmt)
2832 {
2833     struct mm_starter mms;
2834
2835     memcpy(&mms, pmt, sizeof(struct mm_starter));
2836     HeapFree(GetProcessHeap(), 0, pmt);
2837     mms.cb(mms.client);
2838     if (mms.event) SetEvent(mms.event);
2839     return 0;
2840 }
2841
2842 /******************************************************************
2843  *              mmTaskCreate (WINMM.@)
2844  */
2845 UINT     WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR client)
2846 {
2847     HANDLE               hThread;
2848     HANDLE               hEvent = 0;
2849     struct mm_starter   *mms;
2850
2851     mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));
2852     if (mms == NULL) return TASKERR_OUTOFMEMORY;
2853
2854     mms->cb = cb;
2855     mms->client = client;
2856     if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2857     mms->event = hEvent;
2858
2859     hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
2860     if (!hThread) {
2861         HeapFree(GetProcessHeap(), 0, mms);
2862         if (hEvent) CloseHandle(hEvent);
2863         return TASKERR_OUTOFMEMORY;
2864     }
2865     SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
2866     if (ph) *ph = hEvent;
2867     CloseHandle(hThread);
2868     return 0;
2869 }
2870
2871 /******************************************************************
2872  *              mmTaskBlock (WINMM.@)
2873  */
2874 VOID     WINAPI mmTaskBlock(DWORD tid)
2875 {
2876     MSG         msg;
2877
2878     do
2879     {
2880         GetMessageA(&msg, 0, 0, 0);
2881         if (msg.hwnd) DispatchMessageA(&msg);
2882     } while (msg.message != WM_USER);
2883 }
2884
2885 /******************************************************************
2886  *              mmTaskSignal (WINMM.@)
2887  */
2888 BOOL     WINAPI mmTaskSignal(DWORD tid)
2889 {
2890     return PostThreadMessageW(tid, WM_USER, 0, 0);
2891 }
2892
2893 /******************************************************************
2894  *              mmTaskYield (WINMM.@)
2895  */
2896 VOID     WINAPI mmTaskYield(VOID) {}
2897
2898 /******************************************************************
2899  *              mmGetCurrentTask (WINMM.@)
2900  */
2901 DWORD    WINAPI mmGetCurrentTask(VOID)
2902 {
2903     return GetCurrentThreadId();
2904 }