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