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