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