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