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