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