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