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