winmm: The default I/O procedure should return MMIOERR_FILENOTFOUND if the openable...
[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     ERR_TO_STR(MIDIERR_INVALIDSETUP);
134     ERR_TO_STR(MIDIERR_NODEVICE);
135     ERR_TO_STR(MIDIERR_STILLPLAYING);
136     ERR_TO_STR(MIDIERR_UNPREPARED);
137     }
138 #undef ERR_TO_STR
139     return wine_dbg_sprintf("Unknown(0x%08x)", error);
140 }
141
142 /**************************************************************************
143  *              DllMain (WINMM.init)
144  *
145  * WINMM DLL entry point
146  *
147  */
148 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
149 {
150     TRACE("%p 0x%x %p\n", hInstDLL, fdwReason, fImpLoad);
151
152     switch (fdwReason) {
153     case DLL_PROCESS_ATTACH:
154         DisableThreadLibraryCalls(hInstDLL);
155
156         if (!WINMM_CreateIData(hInstDLL))
157             return FALSE;
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_BADDEVICEID;
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)(DWORD_PTR)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)(DWORD_PTR)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)(DWORD_PTR)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_BADDEVICEID;
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     *lphMidiOut = hMidiOut;
886
887     if (lpwm) {
888         lpwm->mod.hMidi = hMidiOut;
889         lpwm->mod.dwCallback = *lpdwCallback;
890         lpwm->mod.dwInstance = *lpdwInstance;
891         lpwm->mod.dnDevNode = 0;
892         lpwm->mod.cIds = cIDs;
893         if (cIDs)
894             memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
895     }
896     return lpwm;
897 }
898
899 /**************************************************************************
900  *                              midiOutOpen             [WINMM.@]
901  */
902 UINT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,
903                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
904 {
905     HMIDIOUT            hMidiOut;
906     LPWINE_MIDI         lpwm;
907     UINT                dwRet = 0;
908
909     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
910           lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
911
912     if (lphMidiOut != NULL) *lphMidiOut = 0;
913
914     switch (dwFlags & CALLBACK_TYPEMASK) {
915     case CALLBACK_WINDOW:
916         if (dwCallback && !IsWindow((HWND)dwCallback))
917             return MMSYSERR_INVALPARAM;
918         break;
919     }
920
921     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags, 0, NULL);
922
923     if (lpwm == NULL)
924         return MMSYSERR_NOMEM;
925
926     lpwm->mld.uDeviceID = uDeviceID;
927
928     dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
929
930     if (dwRet != MMSYSERR_NOERROR) {
931         MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
932         hMidiOut = 0;
933     }
934
935     if (lphMidiOut) *lphMidiOut = hMidiOut;
936     TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
937
938     return dwRet;
939 }
940
941 /**************************************************************************
942  *                              midiOutClose            [WINMM.@]
943  */
944 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
945 {
946     LPWINE_MLD          wmld;
947     DWORD               dwRet;
948
949     TRACE("(%p)\n", hMidiOut);
950
951     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
952         return MMSYSERR_INVALHANDLE;
953
954     dwRet = MMDRV_Close(wmld, MODM_CLOSE);
955     MMDRV_Free(hMidiOut, wmld);
956
957     return dwRet;
958 }
959
960 /**************************************************************************
961  *                              midiOutPrepareHeader    [WINMM.@]
962  */
963 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
964                                  MIDIHDR* lpMidiOutHdr, UINT uSize)
965 {
966     LPWINE_MLD          wmld;
967
968     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
969
970     if (lpMidiOutHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
971         return MMSYSERR_INVALPARAM;
972
973     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
974         return MMSYSERR_INVALHANDLE;
975     /* FIXME: detect MIDIStream handles and enforce 64KB buffer limit on those */
976
977     return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
978 }
979
980 /**************************************************************************
981  *                              midiOutUnprepareHeader  [WINMM.@]
982  */
983 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
984                                    MIDIHDR* lpMidiOutHdr, UINT uSize)
985 {
986     LPWINE_MLD          wmld;
987
988     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
989
990     if (lpMidiOutHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
991         return MMSYSERR_INVALPARAM;
992
993     if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
994         return MMSYSERR_NOERROR;
995     }
996
997     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
998         return MMSYSERR_INVALHANDLE;
999
1000     return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize);
1001 }
1002
1003 /**************************************************************************
1004  *                              midiOutShortMsg         [WINMM.@]
1005  */
1006 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
1007 {
1008     LPWINE_MLD          wmld;
1009
1010     TRACE("(%p, %08X)\n", hMidiOut, dwMsg);
1011
1012     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1013         return MMSYSERR_INVALHANDLE;
1014
1015     return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L);
1016 }
1017
1018 /**************************************************************************
1019  *                              midiOutLongMsg          [WINMM.@]
1020  */
1021 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
1022                            MIDIHDR* lpMidiOutHdr, UINT uSize)
1023 {
1024     LPWINE_MLD          wmld;
1025
1026     TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1027
1028     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1029         return MMSYSERR_INVALHANDLE;
1030
1031     return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize);
1032 }
1033
1034 /**************************************************************************
1035  *                              midiOutReset            [WINMM.@]
1036  */
1037 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
1038 {
1039     LPWINE_MLD          wmld;
1040
1041     TRACE("(%p)\n", hMidiOut);
1042
1043     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1044         return MMSYSERR_INVALHANDLE;
1045
1046     return MMDRV_Message(wmld, MODM_RESET, 0L, 0L);
1047 }
1048
1049 /**************************************************************************
1050  *                              midiOutGetVolume        [WINMM.@]
1051  */
1052 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
1053 {
1054     LPWINE_MLD          wmld;
1055
1056     TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
1057
1058     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1059         return MMSYSERR_INVALHANDLE;
1060
1061     return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L);
1062 }
1063
1064 /**************************************************************************
1065  *                              midiOutSetVolume        [WINMM.@]
1066  */
1067 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
1068 {
1069     LPWINE_MLD          wmld;
1070
1071     TRACE("(%p, %d);\n", hMidiOut, dwVolume);
1072
1073     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1074         return MMSYSERR_INVALHANDLE;
1075
1076     return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L);
1077 }
1078
1079 /**************************************************************************
1080  *                              midiOutCachePatches             [WINMM.@]
1081  */
1082 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
1083                                 WORD* lpwPatchArray, UINT uFlags)
1084 {
1085     /* not really necessary to support this */
1086     FIXME("not supported yet\n");
1087     return MMSYSERR_NOTSUPPORTED;
1088 }
1089
1090 /**************************************************************************
1091  *                              midiOutCacheDrumPatches [WINMM.@]
1092  */
1093 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
1094                                     WORD* lpwKeyArray, UINT uFlags)
1095 {
1096     FIXME("not supported yet\n");
1097     return MMSYSERR_NOTSUPPORTED;
1098 }
1099
1100 /**************************************************************************
1101  *                              midiOutGetID            [WINMM.@]
1102  */
1103 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
1104 {
1105     LPWINE_MLD          wmld;
1106
1107     TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
1108
1109     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1110     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1111         return MMSYSERR_INVALHANDLE;
1112
1113     *lpuDeviceID = wmld->uDeviceID;
1114     return MMSYSERR_NOERROR;
1115 }
1116
1117 /**************************************************************************
1118  *                              midiOutMessage          [WINMM.@]
1119  */
1120 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
1121                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1122 {
1123     LPWINE_MLD          wmld;
1124
1125     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
1126
1127     if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
1128         /* HACK... */
1129         if (uMessage == 0x0001) {
1130             *(LPDWORD)dwParam1 = 1;
1131             return 0;
1132         }
1133         if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
1134             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
1135         }
1136         return MMSYSERR_INVALHANDLE;
1137     }
1138
1139     switch (uMessage) {
1140     case MODM_OPEN:
1141     case MODM_CLOSE:
1142         FIXME("can't handle OPEN or CLOSE message!\n");
1143         return MMSYSERR_NOTSUPPORTED;
1144     }
1145     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
1146 }
1147
1148 /**************************************************************************
1149  *                              midiInGetNumDevs        [WINMM.@]
1150  */
1151 UINT WINAPI midiInGetNumDevs(void)
1152 {
1153     return MMDRV_GetNum(MMDRV_MIDIIN);
1154 }
1155
1156 /**************************************************************************
1157  *                              midiInGetDevCapsW       [WINMM.@]
1158  */
1159 UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
1160 {
1161     LPWINE_MLD  wmld;
1162
1163     TRACE("(%ld, %p, %d);\n", uDeviceID, lpCaps, uSize);
1164
1165     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1166
1167     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
1168         return MMSYSERR_BADDEVICEID;
1169
1170    return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
1171 }
1172
1173 /**************************************************************************
1174  *                              midiInGetDevCapsA       [WINMM.@]
1175  */
1176 UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
1177 {
1178     MIDIINCAPSW         micW;
1179     UINT                ret;
1180
1181     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
1182
1183     ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));
1184
1185     if (ret == MMSYSERR_NOERROR) {
1186         MIDIINCAPSA micA;
1187         micA.wMid           = micW.wMid;
1188         micA.wPid           = micW.wPid;
1189         micA.vDriverVersion = micW.vDriverVersion;
1190         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
1191                              sizeof(micA.szPname), NULL, NULL );
1192         micA.dwSupport      = micW.dwSupport;
1193         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
1194     }
1195     return ret;
1196 }
1197
1198 /**************************************************************************
1199  *                              midiInOpen              [WINMM.@]
1200  */
1201 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
1202                        DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
1203 {
1204     HANDLE              hMidiIn;
1205     LPWINE_MIDI         lpwm;
1206     DWORD               dwRet = 0;
1207
1208     TRACE("(%p, %d, %08lX, %08lX, %08X);\n",
1209           lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
1210
1211     if (lphMidiIn != NULL) *lphMidiIn = 0;
1212
1213     lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
1214                                     &dwFlags, &dwCallback, &dwInstance);
1215
1216     if (lpwm == NULL)
1217         return MMSYSERR_NOMEM;
1218
1219     lpwm->mod.hMidi = hMidiIn;
1220     lpwm->mod.dwCallback = dwCallback;
1221     lpwm->mod.dwInstance = dwInstance;
1222
1223     lpwm->mld.uDeviceID = uDeviceID;
1224     dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags);
1225
1226     if (dwRet != MMSYSERR_NOERROR) {
1227         MMDRV_Free(hMidiIn, &lpwm->mld);
1228         hMidiIn = 0;
1229     }
1230     if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
1231     TRACE("=> %d hMidi=%p\n", dwRet, hMidiIn);
1232
1233     return dwRet;
1234 }
1235
1236 /**************************************************************************
1237  *                              midiInClose             [WINMM.@]
1238  */
1239 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
1240 {
1241     LPWINE_MLD          wmld;
1242     DWORD               dwRet;
1243
1244     TRACE("(%p)\n", hMidiIn);
1245
1246     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1247         return MMSYSERR_INVALHANDLE;
1248
1249     dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
1250     MMDRV_Free(hMidiIn, wmld);
1251     return dwRet;
1252 }
1253
1254 /**************************************************************************
1255  *                              midiInPrepareHeader     [WINMM.@]
1256  */
1257 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
1258                                 MIDIHDR* lpMidiInHdr, UINT uSize)
1259 {
1260     LPWINE_MLD          wmld;
1261
1262     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1263
1264     if (lpMidiInHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
1265         return MMSYSERR_INVALPARAM;
1266
1267     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1268         return MMSYSERR_INVALHANDLE;
1269
1270     return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
1271 }
1272
1273 /**************************************************************************
1274  *                              midiInUnprepareHeader   [WINMM.@]
1275  */
1276 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
1277                                   MIDIHDR* lpMidiInHdr, UINT uSize)
1278 {
1279     LPWINE_MLD          wmld;
1280
1281     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1282
1283     if (lpMidiInHdr == NULL || uSize < offsetof(MIDIHDR,dwOffset))
1284         return MMSYSERR_INVALPARAM;
1285
1286     if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
1287         return MMSYSERR_NOERROR;
1288     }
1289
1290     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1291         return MMSYSERR_INVALHANDLE;
1292
1293     return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize);
1294 }
1295
1296 /**************************************************************************
1297  *                              midiInAddBuffer         [WINMM.@]
1298  */
1299 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
1300                             MIDIHDR* lpMidiInHdr, UINT uSize)
1301 {
1302     LPWINE_MLD          wmld;
1303
1304     TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1305
1306     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1307         return MMSYSERR_INVALHANDLE;
1308
1309     return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize);
1310 }
1311
1312 /**************************************************************************
1313  *                              midiInStart                     [WINMM.@]
1314  */
1315 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
1316 {
1317     LPWINE_MLD          wmld;
1318
1319     TRACE("(%p)\n", hMidiIn);
1320
1321     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1322         return MMSYSERR_INVALHANDLE;
1323
1324     return MMDRV_Message(wmld, MIDM_START, 0L, 0L);
1325 }
1326
1327 /**************************************************************************
1328  *                              midiInStop                      [WINMM.@]
1329  */
1330 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
1331 {
1332     LPWINE_MLD          wmld;
1333
1334     TRACE("(%p)\n", hMidiIn);
1335
1336     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1337         return MMSYSERR_INVALHANDLE;
1338
1339     return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L);
1340 }
1341
1342 /**************************************************************************
1343  *                              midiInReset                     [WINMM.@]
1344  */
1345 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
1346 {
1347     LPWINE_MLD          wmld;
1348
1349     TRACE("(%p)\n", hMidiIn);
1350
1351     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1352         return MMSYSERR_INVALHANDLE;
1353
1354     return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L);
1355 }
1356
1357 /**************************************************************************
1358  *                              midiInGetID                     [WINMM.@]
1359  */
1360 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
1361 {
1362     LPWINE_MLD          wmld;
1363
1364     TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
1365
1366     if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1367
1368     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
1369         return MMSYSERR_INVALHANDLE;
1370
1371     *lpuDeviceID = wmld->uDeviceID;
1372
1373     return MMSYSERR_NOERROR;
1374 }
1375
1376 /**************************************************************************
1377  *                              midiInMessage           [WINMM.@]
1378  */
1379 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
1380                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1381 {
1382     LPWINE_MLD          wmld;
1383
1384     TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
1385
1386     if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1387         return MMSYSERR_INVALHANDLE;
1388
1389     switch (uMessage) {
1390     case MIDM_OPEN:
1391     case MIDM_CLOSE:
1392         FIXME("can't handle OPEN or CLOSE message!\n");
1393         return MMSYSERR_NOTSUPPORTED;
1394     }
1395     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
1396 }
1397
1398 /**************************************************************************
1399  *                              midiConnect                     [WINMM.@]
1400  */
1401 MMRESULT WINAPI midiConnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
1402 {
1403     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
1404     return MMSYSERR_ERROR;
1405 }
1406
1407 /**************************************************************************
1408  *                              midiDisconnect                  [WINMM.@]
1409  */
1410 MMRESULT WINAPI midiDisconnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved)
1411 {
1412     FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved);
1413     return MMSYSERR_ERROR;
1414 }
1415
1416 typedef struct WINE_MIDIStream {
1417     HMIDIOUT                    hDevice;
1418     HANDLE                      hThread;
1419     DWORD                       dwThreadID;
1420     DWORD                       dwTempo;
1421     DWORD                       dwTimeDiv;
1422     DWORD                       dwPositionMS;
1423     DWORD                       dwPulses;
1424     DWORD                       dwStartTicks;
1425     WORD                        wFlags;
1426     HANDLE                      hEvent;
1427     LPMIDIHDR                   lpMidiHdr;
1428 } WINE_MIDIStream;
1429
1430 #define WINE_MSM_HEADER         (WM_USER+0)
1431 #define WINE_MSM_STOP           (WM_USER+1)
1432
1433 /**************************************************************************
1434  *                              MMSYSTEM_GetMidiStream          [internal]
1435  */
1436 static  BOOL    MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
1437 {
1438     WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
1439
1440     if (lplpwm)
1441         *lplpwm = lpwm;
1442
1443     if (lpwm == NULL) {
1444         return FALSE;
1445     }
1446
1447     *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
1448
1449     return *lpMidiStrm != NULL;
1450 }
1451
1452 /**************************************************************************
1453  *                              MMSYSTEM_MidiStream_Convert     [internal]
1454  */
1455 static  DWORD   MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
1456 {
1457     DWORD       ret = 0;
1458
1459     if (lpMidiStrm->dwTimeDiv == 0) {
1460         FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
1461     } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
1462         int     nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv);      /* number of frames     */
1463         int     nsf = LOBYTE(lpMidiStrm->dwTimeDiv);            /* number of sub-frames */
1464         ret = (pulse * 1000) / (nf * nsf);
1465     } else {
1466         ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
1467                       (double)lpMidiStrm->dwTimeDiv);
1468     }
1469
1470     return ret;
1471 }
1472
1473 /**************************************************************************
1474  *                      MMSYSTEM_MidiStream_MessageHandler      [internal]
1475  */
1476 static  BOOL    MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1477 {
1478     LPMIDIHDR   lpMidiHdr;
1479     LPMIDIHDR*  lpmh;
1480     LPBYTE      lpData;
1481
1482     switch (msg->message) {
1483     case WM_QUIT:
1484         SetEvent(lpMidiStrm->hEvent);
1485         return FALSE;
1486     case WINE_MSM_STOP:
1487         TRACE("STOP\n");
1488         /* this is not quite what MS doc says... */
1489         midiOutReset(lpMidiStrm->hDevice);
1490         /* empty list of already submitted buffers */
1491         for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; ) {
1492             LPMIDIHDR lphdr = lpMidiHdr;
1493             lpMidiHdr = lpMidiHdr->lpNext;
1494             lphdr->dwFlags |= MHDR_DONE;
1495             lphdr->dwFlags &= ~MHDR_INQUEUE;
1496
1497             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1498                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1499                            lpwm->mod.dwInstance, (DWORD_PTR)lphdr, 0);
1500         }
1501         lpMidiStrm->lpMidiHdr = 0;
1502         SetEvent(lpMidiStrm->hEvent);
1503         break;
1504     case WINE_MSM_HEADER:
1505         /* sets initial tick count for first MIDIHDR */
1506         if (!lpMidiStrm->dwStartTicks)
1507             lpMidiStrm->dwStartTicks = GetTickCount();
1508
1509         /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1510          * by native mcimidi, it doesn't look like a correct one".
1511          * this trick allows to throw it away... but I don't like it.
1512          * It looks like part of the file I'm trying to play and definitively looks
1513          * like raw midi content
1514          * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1515          * synchronization issue where native mcimidi is still processing raw MIDI
1516          * content before generating MIDIEVENTs ?
1517          *
1518          * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1519          * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1520          * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1521          * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1522          * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1523          * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1524          * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1525          * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1526          * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1527          * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1528          * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1529          * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1530          * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1531          * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1532          * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1533          * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1534          * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1535          */
1536         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1537         lpData = (LPBYTE)lpMidiHdr->lpData;
1538         TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBytesRecorded=%u/%u dwFlags=0x%08x size=%lu]\n",
1539               (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1540               lpMidiHdr, lpMidiHdr->dwBytesRecorded, lpMidiHdr->dwBufferLength,
1541               lpMidiHdr->dwFlags, msg->wParam);
1542 #if 0
1543         /* dumps content of lpMidiHdr->lpData
1544          * FIXME: there should be a debug routine somewhere that already does this
1545          * I hate spreading this type of shit all around the code
1546          */
1547         for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1548             DWORD       i;
1549             BYTE        ch;
1550
1551             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1552                 printf("%02x ", lpData[dwToGo + i]);
1553             for (; i < 16; i++)
1554                 printf("   ");
1555             for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1556                 ch = lpData[dwToGo + i];
1557                 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1558             }
1559             printf("\n");
1560         }
1561 #endif
1562         if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1563             ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1564             ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
1565             FIXME("Dropping bad %s lpMidiHdr (streamID=%08x)\n",
1566                   (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1567                   ((LPMIDIEVENT)lpData)->dwStreamID);
1568             lpMidiHdr->dwFlags |= MHDR_DONE;
1569             lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1570
1571             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1572                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1573                            lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1574             break;
1575         }
1576
1577         for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = &(*lpmh)->lpNext);
1578         *lpmh = lpMidiHdr;
1579         lpMidiHdr = (LPMIDIHDR)msg->lParam;
1580         lpMidiHdr->lpNext = 0;
1581         lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1582         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1583
1584         break;
1585     default:
1586         FIXME("Unknown message %d\n", msg->message);
1587         break;
1588     }
1589     return TRUE;
1590 }
1591
1592 /**************************************************************************
1593  *                              MMSYSTEM_MidiStream_Player      [internal]
1594  */
1595 static  DWORD   CALLBACK        MMSYSTEM_MidiStream_Player(LPVOID pmt)
1596 {
1597     WINE_MIDIStream*    lpMidiStrm = pmt;
1598     WINE_MIDI*          lpwm;
1599     MSG                 msg;
1600     DWORD               dwToGo;
1601     DWORD               dwCurrTC;
1602     LPMIDIHDR           lpMidiHdr;
1603     DWORD               dwOffset;
1604
1605     TRACE("(%p)!\n", lpMidiStrm);
1606
1607     if (!lpMidiStrm ||
1608         (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1609         goto the_end;
1610
1611     /* force thread's queue creation */
1612     PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE);
1613
1614     lpMidiStrm->dwStartTicks = 0;
1615     lpMidiStrm->dwPulses = 0;
1616
1617     lpMidiStrm->lpMidiHdr = 0;
1618
1619     /* midiStreamOpen is waiting for ack */
1620     SetEvent(lpMidiStrm->hEvent);
1621
1622 start_header:
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         goto start_header;
1632     }
1633
1634     dwOffset = 0;
1635     while (dwOffset + offsetof(MIDIEVENT,dwParms) <= lpMidiHdr->dwBytesRecorded) {
1636         LPMIDIEVENT me = (LPMIDIEVENT)(lpMidiHdr->lpData+dwOffset);
1637
1638         /* do we have to wait ? */
1639         if (me->dwDeltaTime) {
1640             lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1641             lpMidiStrm->dwPulses += me->dwDeltaTime;
1642
1643             dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
1644
1645             TRACE("%d/%d/%d\n", dwToGo, GetTickCount(), me->dwDeltaTime);
1646             while ((dwCurrTC = GetTickCount()) < dwToGo) {
1647                 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1648                     /* got a message, handle it */
1649                     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1650                         if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1651                             goto the_end;
1652                         /* is lpMidiHdr still current? */
1653                         if (lpMidiHdr != lpMidiStrm->lpMidiHdr) {
1654                             goto start_header;
1655                         }
1656                     }
1657                 } else {
1658                     /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1659                     break;
1660                 }
1661             }
1662         }
1663         switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1664         case MEVT_COMMENT:
1665             FIXME("NIY: MEVT_COMMENT\n");
1666             /* do nothing, skip bytes */
1667             break;
1668         case MEVT_LONGMSG:
1669             FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
1670             break;
1671         case MEVT_NOP:
1672             break;
1673         case MEVT_SHORTMSG:
1674             midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1675             break;
1676         case MEVT_TEMPO:
1677             lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1678             break;
1679         case MEVT_VERSION:
1680             break;
1681         default:
1682             FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1683             break;
1684         }
1685         if (me->dwEvent & MEVT_F_CALLBACK) {
1686             /* native fills dwOffset regardless of the cbMidiHdr size argument to midiStreamOut */
1687             lpMidiHdr->dwOffset = dwOffset;
1688             DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1689                            (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1690                            lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1691         }
1692         dwOffset += offsetof(MIDIEVENT,dwParms);
1693         if (me->dwEvent & MEVT_F_LONG)
1694             dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1695     }
1696     /* done with this header */
1697     lpMidiStrm->lpMidiHdr = lpMidiHdr->lpNext;
1698     lpMidiHdr->dwFlags |= MHDR_DONE;
1699     lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1700
1701     DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1702                    (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1703                    lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0);
1704     goto start_header;
1705
1706 the_end:
1707     TRACE("End of thread\n");
1708     return 0;
1709 }
1710
1711 /**************************************************************************
1712  *                              MMSYSTEM_MidiStream_PostMessage [internal]
1713  */
1714 static  BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
1715 {
1716     if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
1717         MsgWaitForMultipleObjects( 1, &lpMidiStrm->hEvent, FALSE, INFINITE, 0 );
1718     } else {
1719         ERR("bad PostThreadMessageA\n");
1720         return FALSE;
1721     }
1722     return TRUE;
1723 }
1724
1725 /**************************************************************************
1726  *                              midiStreamClose                 [WINMM.@]
1727  */
1728 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1729 {
1730     WINE_MIDIStream*    lpMidiStrm;
1731
1732     TRACE("(%p)!\n", hMidiStrm);
1733
1734     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1735         return MMSYSERR_INVALHANDLE;
1736
1737     midiStreamStop(hMidiStrm);
1738     MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
1739     CloseHandle(lpMidiStrm->hEvent);
1740     HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1741
1742     return midiOutClose((HMIDIOUT)hMidiStrm);
1743 }
1744
1745 /**************************************************************************
1746  *                              midiStreamOpen                  [WINMM.@]
1747  */
1748 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1749                                DWORD cMidi, DWORD_PTR dwCallback,
1750                                DWORD_PTR dwInstance, DWORD fdwOpen)
1751 {
1752     WINE_MIDIStream*    lpMidiStrm;
1753     MMRESULT            ret;
1754     MIDIOPENSTRMID      mosm;
1755     LPWINE_MIDI         lpwm;
1756     HMIDIOUT            hMidiOut;
1757
1758     TRACE("(%p, %p, %d, 0x%08lx, 0x%08lx, 0x%08x)!\n",
1759           lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1760
1761     if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1762         return MMSYSERR_INVALPARAM;
1763
1764     switch (fdwOpen & CALLBACK_TYPEMASK) {
1765     case CALLBACK_WINDOW:
1766         if (dwCallback && !IsWindow((HWND)dwCallback))
1767             return MMSYSERR_INVALPARAM;
1768         break;
1769     }
1770
1771     lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
1772     if (!lpMidiStrm)
1773         return MMSYSERR_NOMEM;
1774
1775     lpMidiStrm->dwTempo = 500000;
1776     lpMidiStrm->dwTimeDiv = 480;        /* 480 is 120 quarter notes per minute *//* FIXME ??*/
1777     lpMidiStrm->dwPositionMS = 0;
1778
1779     mosm.dwStreamID = (DWORD)lpMidiStrm;
1780     /* FIXME: the correct value is not allocated yet for MAPPER */
1781     mosm.wDeviceID  = *lpuDeviceID;
1782     lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm);
1783     if (!lpwm) {
1784         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1785         return MMSYSERR_NOMEM;
1786     }
1787     lpMidiStrm->hDevice = hMidiOut;
1788     *lphMidiStrm = (HMIDISTRM)hMidiOut;
1789
1790     lpwm->mld.uDeviceID = *lpuDeviceID;
1791
1792     ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD_PTR)&lpwm->mod, fdwOpen);
1793     if (ret != MMSYSERR_NOERROR) {
1794         MMDRV_Free(hMidiOut, &lpwm->mld);
1795         HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1796         return ret;
1797     }
1798
1799     lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1800     lpMidiStrm->wFlags = HIWORD(fdwOpen);
1801
1802     lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1803                                        lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1804
1805     if (!lpMidiStrm->hThread) {
1806         midiStreamClose((HMIDISTRM)hMidiOut);
1807         return MMSYSERR_NOMEM;
1808     }
1809     SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
1810
1811     /* wait for thread to have started, and for its queue to be created */
1812     WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1813     /* start in paused mode */
1814     SuspendThread(lpMidiStrm->hThread);
1815
1816     TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1817           *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1818     return ret;
1819 }
1820
1821 /**************************************************************************
1822  *                              midiStreamOut                   [WINMM.@]
1823  */
1824 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
1825                               UINT cbMidiHdr)
1826 {
1827     WINE_MIDIStream*    lpMidiStrm;
1828     DWORD               ret = MMSYSERR_NOERROR;
1829
1830     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
1831
1832     if (cbMidiHdr < offsetof(MIDIHDR,dwOffset) || !lpMidiHdr || !lpMidiHdr->lpData
1833         || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded
1834         || lpMidiHdr->dwBytesRecorded % 4 /* player expects DWORD padding */)
1835         return MMSYSERR_INVALPARAM;
1836     /* FIXME: Native additionally checks if the MIDIEVENTs in lpData
1837      * exactly fit dwBytesRecorded. */
1838
1839     if (!(lpMidiHdr->dwFlags & MHDR_PREPARED))
1840         return MIDIERR_UNPREPARED;
1841
1842     if (lpMidiHdr->dwFlags & MHDR_INQUEUE)
1843         return MIDIERR_STILLPLAYING;
1844
1845     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1846         ret = MMSYSERR_INVALHANDLE;
1847     } else {
1848         lpMidiHdr->dwFlags |= MHDR_ISSTRM | MHDR_INQUEUE;
1849         lpMidiHdr->dwFlags &= ~MHDR_DONE;
1850         if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
1851                                 WINE_MSM_HEADER, cbMidiHdr,
1852                                 (LPARAM)lpMidiHdr)) {
1853             ERR("bad PostThreadMessageA\n");
1854             ret = MMSYSERR_ERROR;
1855         }
1856     }
1857     return ret;
1858 }
1859
1860 /**************************************************************************
1861  *                              midiStreamPause                 [WINMM.@]
1862  */
1863 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
1864 {
1865     WINE_MIDIStream*    lpMidiStrm;
1866     DWORD               ret = MMSYSERR_NOERROR;
1867
1868     TRACE("(%p)!\n", hMidiStrm);
1869
1870     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1871         ret = MMSYSERR_INVALHANDLE;
1872     } else {
1873         if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
1874             ERR("bad Suspend (%d)\n", GetLastError());
1875             ret = MMSYSERR_ERROR;
1876         }
1877     }
1878     return ret;
1879 }
1880
1881 /**************************************************************************
1882  *                              midiStreamPosition              [WINMM.@]
1883  */
1884 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
1885 {
1886     WINE_MIDIStream*    lpMidiStrm;
1887     DWORD               ret = MMSYSERR_NOERROR;
1888
1889     TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
1890
1891     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1892         ret = MMSYSERR_INVALHANDLE;
1893     } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
1894         ret = MMSYSERR_INVALPARAM;
1895     } else {
1896         switch (lpMMT->wType) {
1897         default:
1898             FIXME("Unsupported time type %x\n", lpMMT->wType);
1899         case TIME_BYTES:
1900         case TIME_SAMPLES:
1901             lpMMT->wType = TIME_MS;
1902             /* fall through to alternative format */
1903         case TIME_MS:
1904             lpMMT->u.ms = lpMidiStrm->dwPositionMS;
1905             TRACE("=> %d ms\n", lpMMT->u.ms);
1906             break;
1907         case TIME_TICKS:
1908             lpMMT->u.ticks = lpMidiStrm->dwPulses;
1909             TRACE("=> %d ticks\n", lpMMT->u.ticks);
1910             break;
1911         }
1912     }
1913     return ret;
1914 }
1915
1916 /**************************************************************************
1917  *                              midiStreamProperty              [WINMM.@]
1918  */
1919 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
1920 {
1921     WINE_MIDIStream*    lpMidiStrm;
1922     MMRESULT            ret = MMSYSERR_NOERROR;
1923
1924     TRACE("(%p, %p, %x)\n", hMidiStrm, lpPropData, dwProperty);
1925
1926     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1927         ret = MMSYSERR_INVALHANDLE;
1928     } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
1929         ret = MMSYSERR_INVALPARAM;
1930     } else if (dwProperty & MIDIPROP_TEMPO) {
1931         MIDIPROPTEMPO*  mpt = (MIDIPROPTEMPO*)lpPropData;
1932
1933         if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
1934             ret = MMSYSERR_INVALPARAM;
1935         } else if (dwProperty & MIDIPROP_SET) {
1936             lpMidiStrm->dwTempo = mpt->dwTempo;
1937             TRACE("Setting tempo to %d\n", mpt->dwTempo);
1938         } else if (dwProperty & MIDIPROP_GET) {
1939             mpt->dwTempo = lpMidiStrm->dwTempo;
1940             TRACE("Getting tempo <= %d\n", mpt->dwTempo);
1941         }
1942     } else if (dwProperty & MIDIPROP_TIMEDIV) {
1943         MIDIPROPTIMEDIV*        mptd = (MIDIPROPTIMEDIV*)lpPropData;
1944
1945         if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
1946             ret = MMSYSERR_INVALPARAM;
1947         } else if (dwProperty & MIDIPROP_SET) {
1948             lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
1949             TRACE("Setting time div to %d\n", mptd->dwTimeDiv);
1950         } else if (dwProperty & MIDIPROP_GET) {
1951             mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
1952             TRACE("Getting time div <= %d\n", mptd->dwTimeDiv);
1953         }
1954     } else {
1955         ret = MMSYSERR_INVALPARAM;
1956     }
1957
1958     return ret;
1959 }
1960
1961 /**************************************************************************
1962  *                              midiStreamRestart               [WINMM.@]
1963  */
1964 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
1965 {
1966     WINE_MIDIStream*    lpMidiStrm;
1967     MMRESULT            ret = MMSYSERR_NOERROR;
1968
1969     TRACE("(%p)!\n", hMidiStrm);
1970
1971     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
1972         ret = MMSYSERR_INVALHANDLE;
1973     } else {
1974         DWORD   ret;
1975
1976         /* since we increase the thread suspend count on each midiStreamPause
1977          * there may be a need for several ResumeThread
1978          */
1979         do {
1980             ret = ResumeThread(lpMidiStrm->hThread);
1981         } while (ret != 0xFFFFFFFF && ret > 1);
1982         if (ret == 0xFFFFFFFF) {
1983             ERR("bad Resume (%d)\n", GetLastError());
1984             ret = MMSYSERR_ERROR;
1985         } else {
1986             lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
1987         }
1988     }
1989     return ret;
1990 }
1991
1992 /**************************************************************************
1993  *                              midiStreamStop                  [WINMM.@]
1994  */
1995 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
1996 {
1997     WINE_MIDIStream*    lpMidiStrm;
1998     MMRESULT            ret = MMSYSERR_NOERROR;
1999
2000     TRACE("(%p)!\n", hMidiStrm);
2001
2002     if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2003         ret = MMSYSERR_INVALHANDLE;
2004     } else {
2005         /* in case stream has been paused... FIXME is the current state correct ? */
2006         midiStreamRestart(hMidiStrm);
2007         MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
2008     }
2009     return ret;
2010 }
2011
2012 static UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType,
2013                       LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2014                       DWORD_PTR dwInstance, DWORD dwFlags)
2015 {
2016     HANDLE              handle;
2017     LPWINE_MLD          wmld;
2018     DWORD               dwRet = MMSYSERR_NOERROR;
2019     WAVEOPENDESC        wod;
2020
2021     TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08X);\n",
2022           lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
2023           dwInstance, dwFlags);
2024
2025     if (dwFlags & WAVE_FORMAT_QUERY)
2026         TRACE("WAVE_FORMAT_QUERY requested !\n");
2027
2028     if (lpFormat == NULL) {
2029         WARN("bad format\n");
2030         return WAVERR_BADFORMAT;
2031     }
2032
2033     if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
2034         WARN("invalid parameter\n");
2035         return MMSYSERR_INVALPARAM;
2036     }
2037
2038     /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
2039     TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u\n",
2040           lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
2041           lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);
2042
2043     if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
2044                             &dwFlags, &dwCallback, &dwInstance)) == NULL) {
2045         WARN("no memory\n");
2046         return MMSYSERR_NOMEM;
2047     }
2048
2049     wod.hWave = handle;
2050     wod.lpFormat = (LPWAVEFORMATEX)lpFormat;  /* should the struct be copied iso pointer? */
2051     wod.dwCallback = dwCallback;
2052     wod.dwInstance = dwInstance;
2053     wod.dnDevNode = 0L;
2054
2055     TRACE("cb=%08lx\n", wod.dwCallback);
2056
2057     for (;;) {
2058         if (dwFlags & WAVE_MAPPED) {
2059             wod.uMappedDeviceID = uDeviceID;
2060             uDeviceID = WAVE_MAPPER;
2061         } else {
2062             wod.uMappedDeviceID = -1;
2063         }
2064         wmld->uDeviceID = uDeviceID;
2065     
2066         dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN,
2067                            (DWORD_PTR)&wod, dwFlags);
2068
2069         TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));
2070         if (dwRet != WAVERR_BADFORMAT ||
2071             ((dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) || (uDeviceID == WAVE_MAPPER)) break;
2072         /* if we ask for a format which isn't supported by the physical driver, 
2073          * let's try to map it through the wave mapper (except, if we already tried
2074          * or user didn't allow us to use acm codecs or the device is already the mapper)
2075          */
2076         dwFlags |= WAVE_MAPPED;
2077         /* we shall loop only one */
2078     }
2079
2080     if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
2081         MMDRV_Free(handle, wmld);
2082         handle = 0;
2083     }
2084
2085     if (lphndl != NULL) *lphndl = handle;
2086     TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);
2087
2088     return dwRet;
2089 }
2090
2091 /**************************************************************************
2092  *                              waveOutGetNumDevs               [WINMM.@]
2093  */
2094 UINT WINAPI waveOutGetNumDevs(void)
2095 {
2096     return MMDRV_GetNum(MMDRV_WAVEOUT);
2097 }
2098
2099 /**************************************************************************
2100  *                              waveOutGetDevCapsA              [WINMM.@]
2101  */
2102 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2103                                UINT uSize)
2104 {
2105     WAVEOUTCAPSW        wocW;
2106     UINT                ret;
2107
2108     if (lpCaps == NULL)        return MMSYSERR_INVALPARAM;
2109
2110     ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2111
2112     if (ret == MMSYSERR_NOERROR) {
2113         WAVEOUTCAPSA wocA;
2114         wocA.wMid           = wocW.wMid;
2115         wocA.wPid           = wocW.wPid;
2116         wocA.vDriverVersion = wocW.vDriverVersion;
2117         WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2118                              sizeof(wocA.szPname), NULL, NULL );
2119         wocA.dwFormats      = wocW.dwFormats;
2120         wocA.wChannels      = wocW.wChannels;
2121         wocA.dwSupport      = wocW.dwSupport;
2122         memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2123     }
2124     return ret;
2125 }
2126
2127 /**************************************************************************
2128  *                              waveOutGetDevCapsW              [WINMM.@]
2129  */
2130 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2131                                UINT uSize)
2132 {
2133     LPWINE_MLD          wmld;
2134
2135     TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2136
2137     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2138
2139     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
2140         return MMSYSERR_BADDEVICEID;
2141
2142     return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
2143 }
2144
2145 /**************************************************************************
2146  *                              waveOutGetErrorTextA    [WINMM.@]
2147  *                              waveInGetErrorTextA     [WINMM.@]
2148  */
2149 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2150 {
2151     UINT        ret;
2152
2153     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2154     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2155     else
2156     {
2157         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2158         if (!xstr) ret = MMSYSERR_NOMEM;
2159         else
2160         {
2161             ret = waveOutGetErrorTextW(uError, xstr, uSize);
2162             if (ret == MMSYSERR_NOERROR)
2163                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2164             HeapFree(GetProcessHeap(), 0, xstr);
2165         }
2166     }
2167     return ret;
2168 }
2169
2170 /**************************************************************************
2171  *                              waveOutGetErrorTextW    [WINMM.@]
2172  *                              waveInGetErrorTextW     [WINMM.@]
2173  */
2174 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2175 {
2176     UINT        ret = MMSYSERR_BADERRNUM;
2177
2178     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2179     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2180     else if (
2181                /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2182                 * a warning for the test was always true */
2183                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2184                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
2185         if (LoadStringW(hWinMM32Instance,
2186                         uError, lpText, uSize) > 0) {
2187             ret = MMSYSERR_NOERROR;
2188         }
2189     }
2190     return ret;
2191 }
2192
2193 /**************************************************************************
2194  *                      waveOutOpen                     [WINMM.@]
2195  * All the args/structs have the same layout as the win16 equivalents
2196  */
2197 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2198                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2199                        DWORD_PTR dwInstance, DWORD dwFlags)
2200 {
2201     return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
2202                      dwCallback, dwInstance, dwFlags);
2203 }
2204
2205 /**************************************************************************
2206  *                              waveOutClose            [WINMM.@]
2207  */
2208 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2209 {
2210     LPWINE_MLD          wmld;
2211     DWORD               dwRet;
2212
2213     TRACE("(%p)\n", hWaveOut);
2214
2215     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2216         return MMSYSERR_INVALHANDLE;
2217
2218     dwRet = MMDRV_Close(wmld, WODM_CLOSE);
2219     if (dwRet != WAVERR_STILLPLAYING)
2220         MMDRV_Free(hWaveOut, wmld);
2221
2222     return dwRet;
2223 }
2224
2225 /**************************************************************************
2226  *                              waveOutPrepareHeader    [WINMM.@]
2227  */
2228 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2229                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
2230 {
2231     LPWINE_MLD          wmld;
2232     UINT                result;
2233
2234     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2235
2236     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2237         return MMSYSERR_INVALPARAM;
2238
2239     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2240         return MMSYSERR_INVALHANDLE;
2241
2242     if ((result = MMDRV_Message(wmld, WODM_PREPARE, (DWORD_PTR)lpWaveOutHdr,
2243                                 uSize)) != MMSYSERR_NOTSUPPORTED)
2244         return result;
2245
2246     if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2247         return WAVERR_STILLPLAYING;
2248
2249     lpWaveOutHdr->dwFlags |= WHDR_PREPARED;
2250     lpWaveOutHdr->dwFlags &= ~WHDR_DONE;
2251
2252     return MMSYSERR_NOERROR;
2253 }
2254
2255 /**************************************************************************
2256  *                              waveOutUnprepareHeader  [WINMM.@]
2257  */
2258 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2259                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
2260 {
2261     LPWINE_MLD          wmld;
2262     UINT                result;
2263
2264     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2265
2266     if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
2267         return MMSYSERR_INVALPARAM;
2268     
2269     if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
2270         return MMSYSERR_NOERROR;
2271     }
2272
2273     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2274         return MMSYSERR_INVALHANDLE;
2275
2276     if ((result = MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD_PTR)lpWaveOutHdr,
2277                                 uSize)) != MMSYSERR_NOTSUPPORTED)
2278         return result;
2279
2280     if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2281         return WAVERR_STILLPLAYING;
2282
2283     lpWaveOutHdr->dwFlags &= ~WHDR_PREPARED;
2284     lpWaveOutHdr->dwFlags |= WHDR_DONE;
2285
2286     return MMSYSERR_NOERROR;
2287 }
2288
2289 /**************************************************************************
2290  *                              waveOutWrite            [WINMM.@]
2291  */
2292 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
2293                          UINT uSize)
2294 {
2295     LPWINE_MLD          wmld;
2296
2297     TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2298
2299     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2300         return MMSYSERR_INVALHANDLE;
2301
2302     return MMDRV_Message(wmld, WODM_WRITE, (DWORD_PTR)lpWaveOutHdr, uSize);
2303 }
2304
2305 /**************************************************************************
2306  *                              waveOutBreakLoop        [WINMM.@]
2307  */
2308 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2309 {
2310     LPWINE_MLD          wmld;
2311
2312     TRACE("(%p);\n", hWaveOut);
2313
2314     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2315         return MMSYSERR_INVALHANDLE;
2316     return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L);
2317 }
2318
2319 /**************************************************************************
2320  *                              waveOutPause            [WINMM.@]
2321  */
2322 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2323 {
2324     LPWINE_MLD          wmld;
2325
2326     TRACE("(%p);\n", hWaveOut);
2327
2328     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2329         return MMSYSERR_INVALHANDLE;
2330     return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L);
2331 }
2332
2333 /**************************************************************************
2334  *                              waveOutReset            [WINMM.@]
2335  */
2336 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2337 {
2338     LPWINE_MLD          wmld;
2339
2340     TRACE("(%p);\n", hWaveOut);
2341
2342     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2343         return MMSYSERR_INVALHANDLE;
2344     return MMDRV_Message(wmld, WODM_RESET, 0L, 0L);
2345 }
2346
2347 /**************************************************************************
2348  *                              waveOutRestart          [WINMM.@]
2349  */
2350 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2351 {
2352     LPWINE_MLD          wmld;
2353
2354     TRACE("(%p);\n", hWaveOut);
2355
2356     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2357         return MMSYSERR_INVALHANDLE;
2358     return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L);
2359 }
2360
2361 /**************************************************************************
2362  *                              waveOutGetPosition      [WINMM.@]
2363  */
2364 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2365                                UINT uSize)
2366 {
2367     LPWINE_MLD          wmld;
2368
2369     TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);
2370
2371     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2372         return MMSYSERR_INVALHANDLE;
2373
2374     return MMDRV_Message(wmld, WODM_GETPOS, (DWORD_PTR)lpTime, uSize);
2375 }
2376
2377 /**************************************************************************
2378  *                              waveOutGetPitch         [WINMM.@]
2379  */
2380 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2381 {
2382     LPWINE_MLD          wmld;
2383
2384     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2385
2386     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2387         return MMSYSERR_INVALHANDLE;
2388     return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L);
2389 }
2390
2391 /**************************************************************************
2392  *                              waveOutSetPitch         [WINMM.@]
2393  */
2394 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2395 {
2396     LPWINE_MLD          wmld;
2397
2398     TRACE("(%p, %08x);\n", hWaveOut, dw);
2399
2400     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2401         return MMSYSERR_INVALHANDLE;
2402     return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L);
2403 }
2404
2405 /**************************************************************************
2406  *                              waveOutGetPlaybackRate  [WINMM.@]
2407  */
2408 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2409 {
2410     LPWINE_MLD          wmld;
2411
2412     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2413
2414     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2415         return MMSYSERR_INVALHANDLE;
2416     return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L);
2417 }
2418
2419 /**************************************************************************
2420  *                              waveOutSetPlaybackRate  [WINMM.@]
2421  */
2422 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2423 {
2424     LPWINE_MLD          wmld;
2425
2426     TRACE("(%p, %08x);\n", hWaveOut, dw);
2427
2428     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2429         return MMSYSERR_INVALHANDLE;
2430     return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L);
2431 }
2432
2433 /**************************************************************************
2434  *                              waveOutGetVolume        [WINMM.@]
2435  */
2436 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)
2437 {
2438     LPWINE_MLD          wmld;
2439
2440     TRACE("(%p, %p);\n", hWaveOut, lpdw);
2441
2442     if (lpdw == NULL) {
2443         WARN("invalid parameter\n");
2444         return MMSYSERR_INVALPARAM;
2445     }
2446
2447     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2448         return MMSYSERR_INVALHANDLE;
2449
2450     return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L);
2451 }
2452
2453 /**************************************************************************
2454  *                              waveOutSetVolume        [WINMM.@]
2455  */
2456 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)
2457 {
2458     LPWINE_MLD          wmld;
2459
2460     TRACE("(%p, %08x);\n", hWaveOut, dw);
2461
2462      if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2463         return MMSYSERR_INVALHANDLE;
2464
2465     return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L);
2466 }
2467
2468 /**************************************************************************
2469  *                              waveOutGetID            [WINMM.@]
2470  */
2471 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2472 {
2473     LPWINE_MLD          wmld;
2474
2475     TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);
2476
2477     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2478
2479     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2480         return MMSYSERR_INVALHANDLE;
2481
2482     *lpuDeviceID = wmld->uDeviceID;
2483     return 0;
2484 }
2485
2486 /**************************************************************************
2487  *                              waveOutMessage          [WINMM.@]
2488  */
2489 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2490                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2491 {
2492     LPWINE_MLD          wmld;
2493
2494     TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2495
2496     if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
2497         if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
2498             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2499         }
2500         WARN("invalid handle\n");
2501         return MMSYSERR_INVALHANDLE;
2502     }
2503
2504     /* from M$ KB */
2505     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER)) {
2506         WARN("invalid parameter\n");
2507         return MMSYSERR_INVALPARAM;
2508     }
2509
2510     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
2511 }
2512
2513 /**************************************************************************
2514  *                              waveInGetNumDevs                [WINMM.@]
2515  */
2516 UINT WINAPI waveInGetNumDevs(void)
2517 {
2518     return MMDRV_GetNum(MMDRV_WAVEIN);
2519 }
2520
2521 /**************************************************************************
2522  *                              waveInGetDevCapsW               [WINMM.@]
2523  */
2524 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2525 {
2526     LPWINE_MLD          wmld;
2527
2528     TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2529
2530     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2531
2532     if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
2533         return MMSYSERR_BADDEVICEID;
2534
2535     return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
2536 }
2537
2538 /**************************************************************************
2539  *                              waveInGetDevCapsA               [WINMM.@]
2540  */
2541 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2542 {
2543     WAVEINCAPSW         wicW;
2544     UINT                ret;
2545
2546     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2547
2548     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2549
2550     if (ret == MMSYSERR_NOERROR) {
2551         WAVEINCAPSA wicA;
2552         wicA.wMid           = wicW.wMid;
2553         wicA.wPid           = wicW.wPid;
2554         wicA.vDriverVersion = wicW.vDriverVersion;
2555         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2556                              sizeof(wicA.szPname), NULL, NULL );
2557         wicA.dwFormats      = wicW.dwFormats;
2558         wicA.wChannels      = wicW.wChannels;
2559         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2560     }
2561     return ret;
2562 }
2563
2564 /**************************************************************************
2565  *                              waveInOpen                      [WINMM.@]
2566  */
2567 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2568                            LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2569                            DWORD_PTR dwInstance, DWORD dwFlags)
2570 {
2571     return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
2572                      dwCallback, dwInstance, dwFlags);
2573 }
2574
2575 /**************************************************************************
2576  *                              waveInClose                     [WINMM.@]
2577  */
2578 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2579 {
2580     LPWINE_MLD          wmld;
2581     DWORD               dwRet;
2582
2583     TRACE("(%p)\n", hWaveIn);
2584
2585     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2586         return MMSYSERR_INVALHANDLE;
2587
2588     dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L);
2589     if (dwRet != WAVERR_STILLPLAYING)
2590         MMDRV_Free(hWaveIn, wmld);
2591     return dwRet;
2592 }
2593
2594 /**************************************************************************
2595  *                              waveInPrepareHeader             [WINMM.@]
2596  */
2597 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2598                                 UINT uSize)
2599 {
2600     LPWINE_MLD          wmld;
2601     UINT                result;
2602
2603     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2604
2605     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2606         return MMSYSERR_INVALPARAM;
2607
2608     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2609         return MMSYSERR_INVALHANDLE;
2610
2611     if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,
2612                                 uSize)) != MMSYSERR_NOTSUPPORTED)
2613         return result;
2614
2615     if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2616         return WAVERR_STILLPLAYING;
2617
2618     lpWaveInHdr->dwFlags |= WHDR_PREPARED;
2619     lpWaveInHdr->dwFlags &= ~WHDR_DONE;
2620     lpWaveInHdr->dwBytesRecorded = 0;
2621
2622     return MMSYSERR_NOERROR;
2623 }
2624
2625 /**************************************************************************
2626  *                              waveInUnprepareHeader   [WINMM.@]
2627  */
2628 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2629                                   UINT uSize)
2630 {
2631     LPWINE_MLD          wmld;
2632     UINT                result;
2633
2634     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2635
2636     if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2637         return MMSYSERR_INVALPARAM;
2638
2639     if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2640         return MMSYSERR_NOERROR;
2641
2642     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2643         return MMSYSERR_INVALHANDLE;
2644
2645     if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,
2646                                 uSize)) != MMSYSERR_NOTSUPPORTED)
2647         return result;
2648
2649     if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2650         return WAVERR_STILLPLAYING;
2651
2652     lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;
2653     lpWaveInHdr->dwFlags |= WHDR_DONE;
2654
2655     return MMSYSERR_NOERROR;
2656 }
2657
2658 /**************************************************************************
2659  *                              waveInAddBuffer         [WINMM.@]
2660  */
2661 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
2662                             WAVEHDR* lpWaveInHdr, UINT uSize)
2663 {
2664     LPWINE_MLD          wmld;
2665
2666     TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2667
2668     if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2669     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2670         return MMSYSERR_INVALHANDLE;
2671
2672     return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize);
2673 }
2674
2675 /**************************************************************************
2676  *                              waveInReset             [WINMM.@]
2677  */
2678 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
2679 {
2680     LPWINE_MLD          wmld;
2681
2682     TRACE("(%p);\n", hWaveIn);
2683
2684     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2685         return MMSYSERR_INVALHANDLE;
2686
2687     return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L);
2688 }
2689
2690 /**************************************************************************
2691  *                              waveInStart             [WINMM.@]
2692  */
2693 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
2694 {
2695     LPWINE_MLD          wmld;
2696
2697     TRACE("(%p);\n", hWaveIn);
2698
2699     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2700         return MMSYSERR_INVALHANDLE;
2701
2702     return MMDRV_Message(wmld, WIDM_START, 0L, 0L);
2703 }
2704
2705 /**************************************************************************
2706  *                              waveInStop              [WINMM.@]
2707  */
2708 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
2709 {
2710     LPWINE_MLD          wmld;
2711
2712     TRACE("(%p);\n", hWaveIn);
2713
2714     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2715         return MMSYSERR_INVALHANDLE;
2716
2717     return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L);
2718 }
2719
2720 /**************************************************************************
2721  *                              waveInGetPosition       [WINMM.@]
2722  */
2723 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
2724                               UINT uSize)
2725 {
2726     LPWINE_MLD          wmld;
2727
2728     TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
2729
2730     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2731         return MMSYSERR_INVALHANDLE;
2732
2733     return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize);
2734 }
2735
2736 /**************************************************************************
2737  *                              waveInGetID                     [WINMM.@]
2738  */
2739 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
2740 {
2741     LPWINE_MLD          wmld;
2742
2743     TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
2744
2745     if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2746
2747     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2748         return MMSYSERR_INVALHANDLE;
2749
2750     *lpuDeviceID = wmld->uDeviceID;
2751     return MMSYSERR_NOERROR;
2752 }
2753
2754 /**************************************************************************
2755  *                              waveInMessage           [WINMM.@]
2756  */
2757 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
2758                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2759 {
2760     LPWINE_MLD          wmld;
2761
2762     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
2763
2764     if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
2765         if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
2766             return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2767         }
2768         return MMSYSERR_INVALHANDLE;
2769     }
2770
2771     /* from M$ KB */
2772     if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
2773         return MMSYSERR_INVALPARAM;
2774
2775
2776     return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
2777 }
2778
2779 struct mm_starter
2780 {
2781     LPTASKCALLBACK      cb;
2782     DWORD               client;
2783     HANDLE              event;
2784 };
2785
2786 static DWORD WINAPI mmTaskRun(void* pmt)
2787 {
2788     struct mm_starter mms;
2789
2790     memcpy(&mms, pmt, sizeof(struct mm_starter));
2791     HeapFree(GetProcessHeap(), 0, pmt);
2792     mms.cb(mms.client);
2793     if (mms.event) SetEvent(mms.event);
2794     return 0;
2795 }
2796
2797 /******************************************************************
2798  *              mmTaskCreate (WINMM.@)
2799  */
2800 UINT     WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR client)
2801 {
2802     HANDLE               hThread;
2803     HANDLE               hEvent = 0;
2804     struct mm_starter   *mms;
2805
2806     mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));
2807     if (mms == NULL) return TASKERR_OUTOFMEMORY;
2808
2809     mms->cb = cb;
2810     mms->client = client;
2811     if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
2812     mms->event = hEvent;
2813
2814     hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
2815     if (!hThread) {
2816         HeapFree(GetProcessHeap(), 0, mms);
2817         if (hEvent) CloseHandle(hEvent);
2818         return TASKERR_OUTOFMEMORY;
2819     }
2820     SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
2821     if (ph) *ph = hEvent;
2822     CloseHandle(hThread);
2823     return 0;
2824 }
2825
2826 /******************************************************************
2827  *              mmTaskBlock (WINMM.@)
2828  */
2829 VOID     WINAPI mmTaskBlock(DWORD tid)
2830 {
2831     MSG         msg;
2832
2833     do
2834     {
2835         GetMessageA(&msg, 0, 0, 0);
2836         if (msg.hwnd) DispatchMessageA(&msg);
2837     } while (msg.message != WM_USER);
2838 }
2839
2840 /******************************************************************
2841  *              mmTaskSignal (WINMM.@)
2842  */
2843 BOOL     WINAPI mmTaskSignal(DWORD tid)
2844 {
2845     return PostThreadMessageW(tid, WM_USER, 0, 0);
2846 }
2847
2848 /******************************************************************
2849  *              mmTaskYield (WINMM.@)
2850  */
2851 VOID     WINAPI mmTaskYield(VOID) {}
2852
2853 /******************************************************************
2854  *              mmGetCurrentTask (WINMM.@)
2855  */
2856 DWORD    WINAPI mmGetCurrentTask(VOID)
2857 {
2858     return GetCurrentThreadId();
2859 }