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