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