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