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