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