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