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