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