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