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