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