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