Move usage of 16 bit specific functions (MapSL) in 16 bit code only.
[wine.git] / dlls / winmm / winmm.c
blob82b4df64aed425c1000f20a4a90e4ce6fc7210e0
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
4 * WINMM functions
6 * Copyright 1993 Martin Ayotte
7 * 1998-2002 Eric Pouech
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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
31 #include <stdarg.h>
32 #include <string.h>
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
36 #include "windef.h"
37 #include "winbase.h"
38 #include "mmsystem.h"
39 #include "winuser.h"
40 #include "winnls.h"
41 #include "winreg.h"
42 #include "winternl.h"
43 #include "winemm.h"
44 #include "wownt32.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
50 /******************************************************************
51 * MyUserYield
53 * Internal wrapper to call USER.UserYield16 (in fact through a Wine only export from USER32).
55 static void MyUserYield(void)
57 HMODULE mod = GetModuleHandleA( "user32.dll" );
58 if (mod)
60 FARPROC proc = GetProcAddress( mod, "UserYield16" );
61 if (proc) proc();
65 /* ========================================================================
66 * G L O B A L S E T T I N G S
67 * ========================================================================*/
69 LPWINE_MM_IDATA WINMM_IData /* = NULL */;
71 /**************************************************************************
72 * WINMM_CreateIData [internal]
74 static BOOL WINMM_CreateIData(HINSTANCE hInstDLL)
76 WINMM_IData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MM_IDATA));
78 if (!WINMM_IData)
79 return FALSE;
80 WINMM_IData->hWinMM32Instance = hInstDLL;
81 InitializeCriticalSection(&WINMM_IData->cs);
82 WINMM_IData->cs.DebugInfo = (void*)__FILE__ ": WinMM";
83 WINMM_IData->psStopEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
84 WINMM_IData->psLastEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
85 TRACE("Created IData (%p)\n", WINMM_IData);
86 return TRUE;
89 /**************************************************************************
90 * WINMM_DeleteIData [internal]
92 static void WINMM_DeleteIData(void)
94 if (WINMM_IData) {
95 TIME_MMTimeStop();
97 /* FIXME: should also free content and resources allocated
98 * inside WINMM_IData */
99 CloseHandle(WINMM_IData->psStopEvent);
100 CloseHandle(WINMM_IData->psLastEvent);
101 DeleteCriticalSection(&WINMM_IData->cs);
102 HeapFree(GetProcessHeap(), 0, WINMM_IData);
103 WINMM_IData = NULL;
107 /******************************************************************
108 * WINMM_LoadMMSystem
111 static HANDLE (WINAPI *pGetModuleHandle16)(LPCSTR);
112 static DWORD (WINAPI *pLoadLibrary16)(LPCSTR);
114 BOOL WINMM_CheckForMMSystem(void)
116 /* 0 is not checked yet, -1 is not present, 1 is present */
117 static int loaded /* = 0 */;
119 if (loaded == 0)
121 HANDLE h = GetModuleHandleA("kernel32");
122 loaded = -1;
123 if (h)
125 pGetModuleHandle16 = (void*)GetProcAddress(h, "GetModuleHandle16");
126 pLoadLibrary16 = (void*)GetProcAddress(h, "LoadLibrary16");
127 if (pGetModuleHandle16 && pLoadLibrary16 &&
128 (pGetModuleHandle16("MMSYSTEM.DLL") || pLoadLibrary16("MMSYSTEM.DLL")))
129 loaded = 1;
132 return loaded > 0;
135 /**************************************************************************
136 * DllMain (WINMM.init)
138 * WINMM DLL entry point
141 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
143 TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, fImpLoad);
145 switch (fdwReason) {
146 case DLL_PROCESS_ATTACH:
147 DisableThreadLibraryCalls(hInstDLL);
149 if (!WINMM_CreateIData(hInstDLL))
150 return FALSE;
151 if (!MCI_Init() || !MMDRV_Init()) {
152 WINMM_DeleteIData();
153 return FALSE;
155 break;
156 case DLL_PROCESS_DETACH:
157 /* close all opened MCI drivers */
158 MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L, TRUE);
159 MMDRV_Exit();
160 /* now unload all remaining drivers... */
161 DRIVER_UnloadAll();
163 WINMM_DeleteIData();
164 break;
166 return TRUE;
169 /**************************************************************************
170 * Mixer devices. New to Win95
173 /**************************************************************************
174 * find out the real mixer ID depending on hmix (depends on dwFlags)
176 static LPWINE_MIXER MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags)
178 LPWINE_MIXER lpwm = NULL;
180 switch (dwFlags & 0xF0000000ul) {
181 case MIXER_OBJECTF_MIXER:
182 lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);
183 break;
184 case MIXER_OBJECTF_HMIXER:
185 lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);
186 break;
187 case MIXER_OBJECTF_WAVEOUT:
188 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE, MMDRV_MIXER);
189 break;
190 case MIXER_OBJECTF_HWAVEOUT:
191 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);
192 break;
193 case MIXER_OBJECTF_WAVEIN:
194 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, TRUE, MMDRV_MIXER);
195 break;
196 case MIXER_OBJECTF_HWAVEIN:
197 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, FALSE, MMDRV_MIXER);
198 break;
199 case MIXER_OBJECTF_MIDIOUT:
200 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE, MMDRV_MIXER);
201 break;
202 case MIXER_OBJECTF_HMIDIOUT:
203 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);
204 break;
205 case MIXER_OBJECTF_MIDIIN:
206 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, TRUE, MMDRV_MIXER);
207 break;
208 case MIXER_OBJECTF_HMIDIIN:
209 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, FALSE, MMDRV_MIXER);
210 break;
211 case MIXER_OBJECTF_AUX:
212 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX, TRUE, MMDRV_MIXER);
213 break;
214 default:
215 FIXME("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);
216 break;
218 return lpwm;
221 /**************************************************************************
222 * mixerGetNumDevs [WINMM.@]
224 UINT WINAPI mixerGetNumDevs(void)
226 return MMDRV_GetNum(MMDRV_MIXER);
229 /**************************************************************************
230 * mixerGetDevCapsA [WINMM.@]
232 UINT WINAPI mixerGetDevCapsA(UINT uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
234 LPWINE_MLD wmld;
236 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
238 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIXER, TRUE)) == NULL)
239 return MMSYSERR_BADDEVICEID;
241 return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
244 /**************************************************************************
245 * mixerGetDevCapsW [WINMM.@]
247 UINT WINAPI mixerGetDevCapsW(UINT uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
249 MIXERCAPSA micA;
250 UINT ret = mixerGetDevCapsA(uDeviceID, &micA, sizeof(micA));
252 if (ret == MMSYSERR_NOERROR) {
253 MIXERCAPSW micW;
254 micW.wMid = micA.wMid;
255 micW.wPid = micA.wPid;
256 micW.vDriverVersion = micA.vDriverVersion;
257 MultiByteToWideChar( CP_ACP, 0, micA.szPname, -1, micW.szPname,
258 sizeof(micW.szPname)/sizeof(WCHAR) );
259 micW.fdwSupport = micA.fdwSupport;
260 micW.cDestinations = micA.cDestinations;
261 memcpy(lpCaps, &micW, min(uSize, sizeof(micW)));
263 return ret;
266 UINT MIXER_Open(LPHMIXER lphMix, UINT uDeviceID, DWORD dwCallback,
267 DWORD dwInstance, DWORD fdwOpen, BOOL bFrom32)
269 HANDLE hMix;
270 LPWINE_MLD wmld;
271 DWORD dwRet = 0;
272 MIXEROPENDESC mod;
274 TRACE("(%p, %d, %08lx, %08lx, %08lx)\n",
275 lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
277 wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,
278 &dwCallback, &dwInstance, bFrom32);
280 wmld->uDeviceID = uDeviceID;
281 mod.hmx = (HMIXEROBJ)hMix;
282 mod.dwCallback = dwCallback;
283 mod.dwInstance = dwInstance;
285 dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD)&mod, fdwOpen);
287 if (dwRet != MMSYSERR_NOERROR) {
288 MMDRV_Free(hMix, wmld);
289 hMix = 0;
291 if (lphMix) *lphMix = hMix;
292 TRACE("=> %ld hMixer=%p\n", dwRet, hMix);
294 return dwRet;
297 /**************************************************************************
298 * mixerOpen [WINMM.@]
300 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD dwCallback,
301 DWORD dwInstance, DWORD fdwOpen)
303 return MIXER_Open(lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen, TRUE);
306 /**************************************************************************
307 * mixerClose [WINMM.@]
309 UINT WINAPI mixerClose(HMIXER hMix)
311 LPWINE_MLD wmld;
312 DWORD dwRet;
314 TRACE("(%p)\n", hMix);
316 if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;
318 dwRet = MMDRV_Close(wmld, MXDM_CLOSE);
319 MMDRV_Free(hMix, wmld);
321 return dwRet;
324 /**************************************************************************
325 * mixerGetID [WINMM.@]
327 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
329 LPWINE_MIXER lpwm;
331 TRACE("(%p %p %08lx)\n", hmix, lpid, fdwID);
333 if ((lpwm = MIXER_GetDev(hmix, fdwID)) == NULL) {
334 return MMSYSERR_INVALHANDLE;
337 if (lpid)
338 *lpid = lpwm->mld.uDeviceID;
340 return MMSYSERR_NOERROR;
343 /**************************************************************************
344 * mixerGetControlDetailsA [WINMM.@]
346 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
347 DWORD fdwDetails)
349 LPWINE_MIXER lpwm;
351 TRACE("(%p, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
353 if ((lpwm = MIXER_GetDev(hmix, fdwDetails)) == NULL)
354 return MMSYSERR_INVALHANDLE;
356 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
357 return MMSYSERR_INVALPARAM;
359 return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD)lpmcdA,
360 fdwDetails, TRUE);
363 /**************************************************************************
364 * mixerGetControlDetailsW [WINMM.@]
366 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd, DWORD fdwDetails)
368 DWORD ret = MMSYSERR_NOTENABLED;
370 TRACE("(%p, %p, %08lx)\n", hmix, lpmcd, fdwDetails);
372 if (lpmcd == NULL || lpmcd->cbStruct != sizeof(*lpmcd))
373 return MMSYSERR_INVALPARAM;
375 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
376 case MIXER_GETCONTROLDETAILSF_VALUE:
377 /* can savely use W structure as it is, no string inside */
378 ret = mixerGetControlDetailsA(hmix, lpmcd, fdwDetails);
379 break;
380 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
382 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW = (MIXERCONTROLDETAILS_LISTTEXTW *)lpmcd->paDetails;
383 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA;
384 int size = max(1, lpmcd->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
385 int i;
387 if (lpmcd->u.cMultipleItems != 0) {
388 size *= lpmcd->u.cMultipleItems;
390 pDetailsA = (MIXERCONTROLDETAILS_LISTTEXTA *)HeapAlloc(GetProcessHeap(), 0, size);
391 lpmcd->paDetails = pDetailsA;
392 lpmcd->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
393 /* set up lpmcd->paDetails */
394 ret = mixerGetControlDetailsA(hmix, lpmcd, fdwDetails);
395 /* copy from lpmcd->paDetails back to paDetailsW; */
396 if(ret == MMSYSERR_NOERROR) {
397 for(i=0;i<lpmcd->u.cMultipleItems*lpmcd->cChannels;i++) {
398 pDetailsW->dwParam1 = pDetailsA->dwParam1;
399 pDetailsW->dwParam2 = pDetailsA->dwParam2;
400 MultiByteToWideChar( CP_ACP, 0, pDetailsA->szName, -1,
401 pDetailsW->szName,
402 sizeof(pDetailsW->szName)/sizeof(WCHAR) );
403 pDetailsA++;
404 pDetailsW++;
406 pDetailsA -= lpmcd->u.cMultipleItems*lpmcd->cChannels;
407 pDetailsW -= lpmcd->u.cMultipleItems*lpmcd->cChannels;
409 HeapFree(GetProcessHeap(), 0, pDetailsA);
410 lpmcd->paDetails = pDetailsW;
411 lpmcd->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
413 break;
414 default:
415 ERR("Unsupported fdwDetails=0x%08lx\n", fdwDetails);
418 return ret;
421 /**************************************************************************
422 * mixerGetLineControlsA [WINMM.@]
424 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
425 DWORD fdwControls)
427 LPWINE_MIXER lpwm;
429 TRACE("(%p, %p, %08lx)\n", hmix, lpmlcA, fdwControls);
431 if ((lpwm = MIXER_GetDev(hmix, fdwControls)) == NULL)
432 return MMSYSERR_INVALHANDLE;
434 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA))
435 return MMSYSERR_INVALPARAM;
437 return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD)lpmlcA,
438 fdwControls, TRUE);
441 /**************************************************************************
442 * mixerGetLineControlsW [WINMM.@]
444 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
445 DWORD fdwControls)
447 MIXERLINECONTROLSA mlcA;
448 DWORD ret;
449 int i;
451 TRACE("(%p, %p, %08lx)\n", hmix, lpmlcW, fdwControls);
453 if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW) ||
454 lpmlcW->cbmxctrl != sizeof(MIXERCONTROLW))
455 return MMSYSERR_INVALPARAM;
457 mlcA.cbStruct = sizeof(mlcA);
458 mlcA.dwLineID = lpmlcW->dwLineID;
459 mlcA.u.dwControlID = lpmlcW->u.dwControlID;
460 mlcA.u.dwControlType = lpmlcW->u.dwControlType;
461 mlcA.cControls = lpmlcW->cControls;
462 mlcA.cbmxctrl = sizeof(MIXERCONTROLA);
463 mlcA.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
464 mlcA.cControls * mlcA.cbmxctrl);
466 ret = mixerGetLineControlsA(hmix, &mlcA, fdwControls);
468 if (ret == MMSYSERR_NOERROR) {
469 lpmlcW->dwLineID = mlcA.dwLineID;
470 lpmlcW->u.dwControlID = mlcA.u.dwControlID;
471 lpmlcW->u.dwControlType = mlcA.u.dwControlType;
472 lpmlcW->cControls = mlcA.cControls;
474 for (i = 0; i < mlcA.cControls; i++) {
475 lpmlcW->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLW);
476 lpmlcW->pamxctrl[i].dwControlID = mlcA.pamxctrl[i].dwControlID;
477 lpmlcW->pamxctrl[i].dwControlType = mlcA.pamxctrl[i].dwControlType;
478 lpmlcW->pamxctrl[i].fdwControl = mlcA.pamxctrl[i].fdwControl;
479 lpmlcW->pamxctrl[i].cMultipleItems = mlcA.pamxctrl[i].cMultipleItems;
480 MultiByteToWideChar( CP_ACP, 0, mlcA.pamxctrl[i].szShortName, -1,
481 lpmlcW->pamxctrl[i].szShortName,
482 sizeof(lpmlcW->pamxctrl[i].szShortName)/sizeof(WCHAR) );
483 MultiByteToWideChar( CP_ACP, 0, mlcA.pamxctrl[i].szName, -1,
484 lpmlcW->pamxctrl[i].szName,
485 sizeof(lpmlcW->pamxctrl[i].szName)/sizeof(WCHAR) );
486 /* sizeof(lpmlcW->pamxctrl[i].Bounds) ==
487 * sizeof(mlcA.pamxctrl[i].Bounds) */
488 memcpy(&lpmlcW->pamxctrl[i].Bounds, &mlcA.pamxctrl[i].Bounds,
489 sizeof(mlcA.pamxctrl[i].Bounds));
490 /* sizeof(lpmlcW->pamxctrl[i].Metrics) ==
491 * sizeof(mlcA.pamxctrl[i].Metrics) */
492 memcpy(&lpmlcW->pamxctrl[i].Metrics, &mlcA.pamxctrl[i].Metrics,
493 sizeof(mlcA.pamxctrl[i].Metrics));
497 HeapFree(GetProcessHeap(), 0, mlcA.pamxctrl);
499 return ret;
502 /**************************************************************************
503 * mixerGetLineInfoA [WINMM.@]
505 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliW, DWORD fdwInfo)
507 LPWINE_MIXER lpwm;
509 TRACE("(%p, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
511 if ((lpwm = MIXER_GetDev(hmix, fdwInfo)) == NULL)
512 return MMSYSERR_INVALHANDLE;
514 return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD)lpmliW,
515 fdwInfo, TRUE);
518 /**************************************************************************
519 * mixerGetLineInfoW [WINMM.@]
521 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW,
522 DWORD fdwInfo)
524 MIXERLINEA mliA;
525 UINT ret;
527 TRACE("(%p, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
529 if (lpmliW == NULL || lpmliW->cbStruct != sizeof(*lpmliW))
530 return MMSYSERR_INVALPARAM;
532 mliA.cbStruct = sizeof(mliA);
533 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
534 case MIXER_GETLINEINFOF_COMPONENTTYPE:
535 mliA.dwComponentType = lpmliW->dwComponentType;
536 break;
537 case MIXER_GETLINEINFOF_DESTINATION:
538 mliA.dwDestination = lpmliW->dwDestination;
539 break;
540 case MIXER_GETLINEINFOF_LINEID:
541 mliA.dwLineID = lpmliW->dwLineID;
542 break;
543 case MIXER_GETLINEINFOF_SOURCE:
544 mliA.dwDestination = lpmliW->dwDestination;
545 mliA.dwSource = lpmliW->dwSource;
546 break;
547 case MIXER_GETLINEINFOF_TARGETTYPE:
548 mliA.Target.dwType = lpmliW->Target.dwType;
549 mliA.Target.wMid = lpmliW->Target.wMid;
550 mliA.Target.wPid = lpmliW->Target.wPid;
551 mliA.Target.vDriverVersion = lpmliW->Target.vDriverVersion;
552 WideCharToMultiByte( CP_ACP, 0, lpmliW->Target.szPname, -1, mliA.Target.szPname, sizeof(mliA.Target.szPname), NULL, NULL);
553 break;
554 default:
555 FIXME("Unsupported fdwControls=0x%08lx\n", fdwInfo);
558 ret = mixerGetLineInfoA(hmix, &mliA, fdwInfo);
560 lpmliW->dwDestination = mliA.dwDestination;
561 lpmliW->dwSource = mliA.dwSource;
562 lpmliW->dwLineID = mliA.dwLineID;
563 lpmliW->fdwLine = mliA.fdwLine;
564 lpmliW->dwUser = mliA.dwUser;
565 lpmliW->dwComponentType = mliA.dwComponentType;
566 lpmliW->cChannels = mliA.cChannels;
567 lpmliW->cConnections = mliA.cConnections;
568 lpmliW->cControls = mliA.cControls;
569 MultiByteToWideChar( CP_ACP, 0, mliA.szShortName, -1, lpmliW->szShortName,
570 sizeof(lpmliW->szShortName)/sizeof(WCHAR) );
571 MultiByteToWideChar( CP_ACP, 0, mliA.szName, -1, lpmliW->szName,
572 sizeof(lpmliW->szName)/sizeof(WCHAR) );
573 lpmliW->Target.dwType = mliA.Target.dwType;
574 lpmliW->Target.dwDeviceID = mliA.Target.dwDeviceID;
575 lpmliW->Target.wMid = mliA.Target.wMid;
576 lpmliW->Target.wPid = mliA.Target.wPid;
577 lpmliW->Target.vDriverVersion = mliA.Target.vDriverVersion;
578 MultiByteToWideChar( CP_ACP, 0, mliA.Target.szPname, -1, lpmliW->Target.szPname,
579 sizeof(lpmliW->Target.szPname)/sizeof(WCHAR) );
581 return ret;
584 /**************************************************************************
585 * mixerSetControlDetails [WINMM.@]
587 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
588 DWORD fdwDetails)
590 LPWINE_MIXER lpwm;
592 TRACE("(%p, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
594 if ((lpwm = MIXER_GetDev(hmix, fdwDetails)) == NULL)
595 return MMSYSERR_INVALHANDLE;
597 return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD)lpmcdA,
598 fdwDetails, TRUE);
601 /**************************************************************************
602 * mixerMessage [WINMM.@]
604 UINT WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
606 LPWINE_MLD wmld;
608 TRACE("(%04lx, %d, %08lx, %08lx): semi-stub?\n",
609 (DWORD)hmix, uMsg, dwParam1, dwParam2);
611 if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)
612 return MMSYSERR_INVALHANDLE;
614 return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2, TRUE);
617 /**************************************************************************
618 * auxGetNumDevs [WINMM.@]
620 UINT WINAPI auxGetNumDevs(void)
622 return MMDRV_GetNum(MMDRV_AUX);
625 /**************************************************************************
626 * auxGetDevCapsW [WINMM.@]
628 UINT WINAPI auxGetDevCapsW(UINT uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
630 AUXCAPSA acA;
631 UINT ret = auxGetDevCapsA(uDeviceID, &acA, sizeof(acA));
633 if (ret == MMSYSERR_NOERROR) {
634 AUXCAPSW acW;
635 acW.wMid = acA.wMid;
636 acW.wPid = acA.wPid;
637 acW.vDriverVersion = acA.vDriverVersion;
638 MultiByteToWideChar( CP_ACP, 0, acA.szPname, -1, acW.szPname,
639 sizeof(acW.szPname)/sizeof(WCHAR) );
640 acW.wTechnology = acA.wTechnology;
641 acW.dwSupport = acA.dwSupport;
642 memcpy(lpCaps, &acW, min(uSize, sizeof(acW)));
644 return ret;
647 /**************************************************************************
648 * auxGetDevCapsA [WINMM.@]
650 UINT WINAPI auxGetDevCapsA(UINT uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
652 LPWINE_MLD wmld;
654 TRACE("(%04X, %p, %d) !\n", uDeviceID, lpCaps, uSize);
656 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
658 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
659 return MMSYSERR_INVALHANDLE;
660 return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
663 /**************************************************************************
664 * auxGetVolume [WINMM.@]
666 UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
668 LPWINE_MLD wmld;
670 TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
672 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
673 return MMSYSERR_INVALHANDLE;
674 return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD)lpdwVolume, 0L, TRUE);
677 /**************************************************************************
678 * auxSetVolume [WINMM.@]
680 UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
682 LPWINE_MLD wmld;
684 TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);
686 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
687 return MMSYSERR_INVALHANDLE;
688 return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);
691 /**************************************************************************
692 * auxOutMessage [WINMM.@]
694 UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD dw1, DWORD dw2)
696 LPWINE_MLD wmld;
698 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
699 return MMSYSERR_INVALHANDLE;
701 return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);
704 /**************************************************************************
705 * mciGetErrorStringW [WINMM.@]
707 BOOL WINAPI mciGetErrorStringW(DWORD wError, LPWSTR lpstrBuffer, UINT uLength)
709 LPSTR bufstr = HeapAlloc(GetProcessHeap(), 0, uLength);
710 BOOL ret = mciGetErrorStringA(wError, bufstr, uLength);
712 MultiByteToWideChar( CP_ACP, 0, bufstr, -1, lpstrBuffer, uLength );
713 HeapFree(GetProcessHeap(), 0, bufstr);
714 return ret;
717 /**************************************************************************
718 * mciGetErrorStringA [WINMM.@]
720 BOOL WINAPI mciGetErrorStringA(DWORD dwError, LPSTR lpstrBuffer, UINT uLength)
722 BOOL ret = FALSE;
724 if (lpstrBuffer != NULL && uLength > 0 &&
725 dwError >= MCIERR_BASE && dwError <= MCIERR_CUSTOM_DRIVER_BASE) {
727 if (LoadStringA(WINMM_IData->hWinMM32Instance,
728 dwError, lpstrBuffer, uLength) > 0) {
729 ret = TRUE;
732 return ret;
735 /**************************************************************************
736 * mciDriverNotify [WINMM.@]
738 BOOL WINAPI mciDriverNotify(HWND hWndCallBack, UINT wDevID, UINT wStatus)
741 TRACE("(%p, %04x, %04X)\n", hWndCallBack, wDevID, wStatus);
743 return PostMessageA(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID);
746 /**************************************************************************
747 * mciGetDriverData [WINMM.@]
749 DWORD WINAPI mciGetDriverData(UINT uDeviceID)
751 LPWINE_MCIDRIVER wmd;
753 TRACE("(%04x)\n", uDeviceID);
755 wmd = MCI_GetDriver(uDeviceID);
757 if (!wmd) {
758 WARN("Bad uDeviceID\n");
759 return 0L;
762 return wmd->dwPrivate;
765 /**************************************************************************
766 * mciSetDriverData [WINMM.@]
768 BOOL WINAPI mciSetDriverData(UINT uDeviceID, DWORD data)
770 LPWINE_MCIDRIVER wmd;
772 TRACE("(%04x, %08lx)\n", uDeviceID, data);
774 wmd = MCI_GetDriver(uDeviceID);
776 if (!wmd) {
777 WARN("Bad uDeviceID\n");
778 return FALSE;
781 wmd->dwPrivate = data;
782 return TRUE;
785 /**************************************************************************
786 * mciSendCommandA [WINMM.@]
788 DWORD WINAPI mciSendCommandA(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2)
790 DWORD dwRet;
792 TRACE("(%08x, %s, %08lx, %08lx)\n",
793 wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
795 dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2, TRUE);
796 dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2);
797 TRACE("=> %08lx\n", dwRet);
798 return dwRet;
801 /**************************************************************************
802 * mciSendCommandW [WINMM.@]
804 DWORD WINAPI mciSendCommandW(UINT wDevID, UINT wMsg, DWORD dwParam1, DWORD dwParam2)
806 FIXME("(%08x, %s, %08lx, %08lx): stub\n",
807 wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2);
808 return MCIERR_UNSUPPORTED_FUNCTION;
811 /**************************************************************************
812 * mciGetDeviceIDA [WINMM.@]
814 UINT WINAPI mciGetDeviceIDA(LPCSTR lpstrName)
816 return MCI_GetDriverFromString(lpstrName);
819 /**************************************************************************
820 * mciGetDeviceIDW [WINMM.@]
822 UINT WINAPI mciGetDeviceIDW(LPCWSTR lpwstrName)
824 LPSTR lpstrName = NULL;
825 UINT ret;
826 INT len;
828 if (lpwstrName) {
829 len = WideCharToMultiByte( CP_ACP, 0, lpwstrName, -1, NULL, 0, NULL, NULL );
830 lpstrName = HeapAlloc( GetProcessHeap(), 0, len );
831 if (lpstrName) WideCharToMultiByte( CP_ACP, 0, lpwstrName, -1, lpstrName, len, NULL, NULL );
833 ret = MCI_GetDriverFromString(lpstrName);
834 HeapFree(GetProcessHeap(), 0, lpstrName);
835 return ret;
838 /**************************************************************************
839 * MCI_DefYieldProc [internal]
841 UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data)
843 INT16 ret;
845 TRACE("(0x%04x, 0x%08lx)\n", wDevID, data);
847 if ((HIWORD(data) != 0 && HWND_16(GetActiveWindow()) != HIWORD(data)) ||
848 (GetAsyncKeyState(LOWORD(data)) & 1) == 0) {
849 MyUserYield();
850 ret = 0;
851 } else {
852 MSG msg;
854 msg.hwnd = HWND_32(HIWORD(data));
855 while (!PeekMessageA(&msg, msg.hwnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE));
856 ret = -1;
858 return ret;
861 /**************************************************************************
862 * mciSetYieldProc [WINMM.@]
864 BOOL WINAPI mciSetYieldProc(UINT uDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData)
866 LPWINE_MCIDRIVER wmd;
868 TRACE("(%u, %p, %08lx)\n", uDeviceID, fpYieldProc, dwYieldData);
870 if (!(wmd = MCI_GetDriver(uDeviceID))) {
871 WARN("Bad uDeviceID\n");
872 return FALSE;
875 wmd->lpfnYieldProc = fpYieldProc;
876 wmd->dwYieldData = dwYieldData;
877 wmd->bIs32 = TRUE;
879 return TRUE;
882 /**************************************************************************
883 * mciGetDeviceIDFromElementIDW [WINMM.@]
885 UINT WINAPI mciGetDeviceIDFromElementIDW(DWORD dwElementID, LPCWSTR lpstrType)
887 /* FIXME: that's rather strange, there is no
888 * mciGetDeviceIDFromElementID32A in winmm.spec
890 FIXME("(%lu, %p) stub\n", dwElementID, lpstrType);
891 return 0;
894 /**************************************************************************
895 * mciGetYieldProc [WINMM.@]
897 YIELDPROC WINAPI mciGetYieldProc(UINT uDeviceID, DWORD* lpdwYieldData)
899 LPWINE_MCIDRIVER wmd;
901 TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData);
903 if (!(wmd = MCI_GetDriver(uDeviceID))) {
904 WARN("Bad uDeviceID\n");
905 return NULL;
907 if (!wmd->lpfnYieldProc) {
908 WARN("No proc set\n");
909 return NULL;
911 if (!wmd->bIs32) {
912 WARN("Proc is 32 bit\n");
913 return NULL;
915 return wmd->lpfnYieldProc;
918 /**************************************************************************
919 * mciGetCreatorTask [WINMM.@]
921 HTASK WINAPI mciGetCreatorTask(UINT uDeviceID)
923 LPWINE_MCIDRIVER wmd;
924 HTASK ret = 0;
926 if ((wmd = MCI_GetDriver(uDeviceID))) ret = (HTASK)wmd->CreatorThread;
928 TRACE("(%u) => %p\n", uDeviceID, ret);
929 return ret;
932 /**************************************************************************
933 * mciDriverYield [WINMM.@]
935 UINT WINAPI mciDriverYield(UINT uDeviceID)
937 LPWINE_MCIDRIVER wmd;
938 UINT ret = 0;
940 TRACE("(%04x)\n", uDeviceID);
942 if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc || !wmd->bIs32) {
943 MyUserYield();
944 } else {
945 ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData);
948 return ret;
951 /**************************************************************************
952 * midiOutGetNumDevs [WINMM.@]
954 UINT WINAPI midiOutGetNumDevs(void)
956 return MMDRV_GetNum(MMDRV_MIDIOUT);
959 /**************************************************************************
960 * midiOutGetDevCapsW [WINMM.@]
962 UINT WINAPI midiOutGetDevCapsW(UINT uDeviceID, LPMIDIOUTCAPSW lpCaps,
963 UINT uSize)
965 MIDIOUTCAPSA mocA;
966 UINT ret = midiOutGetDevCapsA(uDeviceID, &mocA, sizeof(mocA));
968 if (ret == MMSYSERR_NOERROR) {
969 MIDIOUTCAPSW mocW;
970 mocW.wMid = mocA.wMid;
971 mocW.wPid = mocA.wPid;
972 mocW.vDriverVersion = mocA.vDriverVersion;
973 MultiByteToWideChar( CP_ACP, 0, mocA.szPname, -1, mocW.szPname,
974 sizeof(mocW.szPname)/sizeof(WCHAR) );
975 mocW.wTechnology = mocA.wTechnology;
976 mocW.wVoices = mocA.wVoices;
977 mocW.wNotes = mocA.wNotes;
978 mocW.wChannelMask = mocA.wChannelMask;
979 mocW.dwSupport = mocA.dwSupport;
980 memcpy(lpCaps, &mocW, min(uSize, sizeof(mocW)));
982 return ret;
985 /**************************************************************************
986 * midiOutGetDevCapsA [WINMM.@]
988 UINT WINAPI midiOutGetDevCapsA(UINT uDeviceID, LPMIDIOUTCAPSA lpCaps,
989 UINT uSize)
991 LPWINE_MLD wmld;
993 TRACE("(%u, %p, %u);\n", uDeviceID, lpCaps, uSize);
995 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
997 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
998 return MMSYSERR_INVALHANDLE;
1000 return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
1003 /**************************************************************************
1004 * MIDI_GetErrorText [internal]
1006 static UINT16 MIDI_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
1008 UINT16 ret = MMSYSERR_BADERRNUM;
1010 if (lpText == NULL) {
1011 ret = MMSYSERR_INVALPARAM;
1012 } else if (uSize == 0) {
1013 ret = MMSYSERR_NOERROR;
1014 } else if (
1015 /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
1016 * a warning for the test was always true */
1017 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
1018 (uError >= MIDIERR_BASE && uError <= MIDIERR_LASTERROR)) {
1020 if (LoadStringA(WINMM_IData->hWinMM32Instance,
1021 uError, lpText, uSize) > 0) {
1022 ret = MMSYSERR_NOERROR;
1025 return ret;
1028 /**************************************************************************
1029 * midiOutGetErrorTextA [WINMM.@]
1031 UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
1033 return MIDI_GetErrorText(uError, lpText, uSize);
1036 /**************************************************************************
1037 * midiOutGetErrorTextW [WINMM.@]
1039 UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
1041 LPSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
1042 UINT ret;
1044 ret = MIDI_GetErrorText(uError, xstr, uSize);
1045 MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
1046 HeapFree(GetProcessHeap(), 0, xstr);
1047 return ret;
1050 /**************************************************************************
1051 * MIDI_OutAlloc [internal]
1053 static LPWINE_MIDI MIDI_OutAlloc(HMIDIOUT* lphMidiOut, LPDWORD lpdwCallback,
1054 LPDWORD lpdwInstance, LPDWORD lpdwFlags,
1055 DWORD cIDs, MIDIOPENSTRMID* lpIDs, BOOL bFrom32)
1057 HANDLE hMidiOut;
1058 LPWINE_MIDI lpwm;
1059 UINT size;
1061 size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
1063 lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
1064 lpdwCallback, lpdwInstance, bFrom32);
1066 if (lphMidiOut != NULL)
1067 *lphMidiOut = hMidiOut;
1069 if (lpwm) {
1070 lpwm->mod.hMidi = (HMIDI) hMidiOut;
1071 lpwm->mod.dwCallback = *lpdwCallback;
1072 lpwm->mod.dwInstance = *lpdwInstance;
1073 lpwm->mod.dnDevNode = 0;
1074 lpwm->mod.cIds = cIDs;
1075 if (cIDs)
1076 memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
1078 return lpwm;
1081 UINT MIDI_OutOpen(HMIDIOUT* lphMidiOut, UINT uDeviceID, DWORD dwCallback,
1082 DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
1084 HMIDIOUT hMidiOut;
1085 LPWINE_MIDI lpwm;
1086 UINT dwRet = 0;
1088 TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
1089 lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
1091 if (lphMidiOut != NULL) *lphMidiOut = 0;
1093 lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags,
1094 0, NULL, bFrom32);
1096 if (lpwm == NULL)
1097 return MMSYSERR_NOMEM;
1099 lpwm->mld.uDeviceID = uDeviceID;
1101 dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD)&lpwm->mod, dwFlags);
1103 if (dwRet != MMSYSERR_NOERROR) {
1104 MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
1105 hMidiOut = 0;
1108 if (lphMidiOut) *lphMidiOut = hMidiOut;
1109 TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
1111 return dwRet;
1114 /**************************************************************************
1115 * midiOutOpen [WINMM.@]
1117 UINT WINAPI midiOutOpen(HMIDIOUT* lphMidiOut, UINT uDeviceID,
1118 DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
1120 return MIDI_OutOpen(lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
1123 /**************************************************************************
1124 * midiOutClose [WINMM.@]
1126 UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
1128 LPWINE_MLD wmld;
1129 DWORD dwRet;
1131 TRACE("(%p)\n", hMidiOut);
1133 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1134 return MMSYSERR_INVALHANDLE;
1136 dwRet = MMDRV_Close(wmld, MODM_CLOSE);
1137 MMDRV_Free(hMidiOut, wmld);
1139 return dwRet;
1142 /**************************************************************************
1143 * midiOutPrepareHeader [WINMM.@]
1145 UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
1146 MIDIHDR* lpMidiOutHdr, UINT uSize)
1148 LPWINE_MLD wmld;
1150 TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1152 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1153 return MMSYSERR_INVALHANDLE;
1155 return MMDRV_Message(wmld, MODM_PREPARE, (DWORD)lpMidiOutHdr, uSize, TRUE);
1158 /**************************************************************************
1159 * midiOutUnprepareHeader [WINMM.@]
1161 UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
1162 MIDIHDR* lpMidiOutHdr, UINT uSize)
1164 LPWINE_MLD wmld;
1166 TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1168 if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
1169 return MMSYSERR_NOERROR;
1172 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1173 return MMSYSERR_INVALHANDLE;
1175 return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD)lpMidiOutHdr, uSize, TRUE);
1178 /**************************************************************************
1179 * midiOutShortMsg [WINMM.@]
1181 UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
1183 LPWINE_MLD wmld;
1185 TRACE("(%p, %08lX)\n", hMidiOut, dwMsg);
1187 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1188 return MMSYSERR_INVALHANDLE;
1190 return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L, TRUE);
1193 /**************************************************************************
1194 * midiOutLongMsg [WINMM.@]
1196 UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
1197 MIDIHDR* lpMidiOutHdr, UINT uSize)
1199 LPWINE_MLD wmld;
1201 TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
1203 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1204 return MMSYSERR_INVALHANDLE;
1206 return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD)lpMidiOutHdr, uSize, TRUE);
1209 /**************************************************************************
1210 * midiOutReset [WINMM.@]
1212 UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
1214 LPWINE_MLD wmld;
1216 TRACE("(%p)\n", hMidiOut);
1218 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1219 return MMSYSERR_INVALHANDLE;
1221 return MMDRV_Message(wmld, MODM_RESET, 0L, 0L, TRUE);
1224 /**************************************************************************
1225 * midiOutGetVolume [WINMM.@]
1227 UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
1229 LPWINE_MLD wmld;
1231 TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
1233 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1234 return MMSYSERR_INVALHANDLE;
1236 return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD)lpdwVolume, 0L, TRUE);
1239 /**************************************************************************
1240 * midiOutSetVolume [WINMM.@]
1242 UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
1244 LPWINE_MLD wmld;
1246 TRACE("(%p, %ld);\n", hMidiOut, dwVolume);
1248 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
1249 return MMSYSERR_INVALHANDLE;
1251 return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L, TRUE);
1254 /**************************************************************************
1255 * midiOutCachePatches [WINMM.@]
1257 UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
1258 WORD* lpwPatchArray, UINT uFlags)
1260 /* not really necessary to support this */
1261 FIXME("not supported yet\n");
1262 return MMSYSERR_NOTSUPPORTED;
1265 /**************************************************************************
1266 * midiOutCacheDrumPatches [WINMM.@]
1268 UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
1269 WORD* lpwKeyArray, UINT uFlags)
1271 FIXME("not supported yet\n");
1272 return MMSYSERR_NOTSUPPORTED;
1275 /**************************************************************************
1276 * midiOutGetID [WINMM.@]
1278 UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
1280 LPWINE_MLD wmld;
1282 TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
1284 if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1285 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
1286 return MMSYSERR_INVALHANDLE;
1288 *lpuDeviceID = wmld->uDeviceID;
1289 return MMSYSERR_NOERROR;
1292 /**************************************************************************
1293 * midiOutMessage [WINMM.@]
1295 UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
1296 DWORD dwParam1, DWORD dwParam2)
1298 LPWINE_MLD wmld;
1300 TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
1302 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
1303 /* HACK... */
1304 if (uMessage == 0x0001) {
1305 *(LPDWORD)dwParam1 = 1;
1306 return 0;
1308 if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
1309 return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
1311 return MMSYSERR_INVALHANDLE;
1314 switch (uMessage) {
1315 case MODM_OPEN:
1316 case MODM_CLOSE:
1317 FIXME("can't handle OPEN or CLOSE message!\n");
1318 return MMSYSERR_NOTSUPPORTED;
1320 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
1323 /**************************************************************************
1324 * midiInGetNumDevs [WINMM.@]
1326 UINT WINAPI midiInGetNumDevs(void)
1328 return MMDRV_GetNum(MMDRV_MIDIIN);
1331 /**************************************************************************
1332 * midiInGetDevCapsW [WINMM.@]
1334 UINT WINAPI midiInGetDevCapsW(UINT uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
1336 MIDIINCAPSA micA;
1337 UINT ret = midiInGetDevCapsA(uDeviceID, &micA, uSize);
1339 if (ret == MMSYSERR_NOERROR) {
1340 MIDIINCAPSW micW;
1341 micW.wMid = micA.wMid;
1342 micW.wPid = micA.wPid;
1343 micW.vDriverVersion = micA.vDriverVersion;
1344 MultiByteToWideChar( CP_ACP, 0, micA.szPname, -1, micW.szPname,
1345 sizeof(micW.szPname)/sizeof(WCHAR) );
1346 micW.dwSupport = micA.dwSupport;
1347 memcpy(lpCaps, &micW, min(uSize, sizeof(micW)));
1349 return ret;
1352 /**************************************************************************
1353 * midiInGetDevCapsA [WINMM.@]
1355 UINT WINAPI midiInGetDevCapsA(UINT uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
1357 LPWINE_MLD wmld;
1359 TRACE("(%d, %p, %d);\n", uDeviceID, lpCaps, uSize);
1361 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1363 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
1364 return MMSYSERR_INVALHANDLE;
1366 return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
1369 /**************************************************************************
1370 * midiInGetErrorTextW [WINMM.@]
1372 UINT WINAPI midiInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
1374 LPSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
1375 UINT ret = MIDI_GetErrorText(uError, xstr, uSize);
1377 MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
1378 HeapFree(GetProcessHeap(), 0, xstr);
1379 return ret;
1382 /**************************************************************************
1383 * midiInGetErrorTextA [WINMM.@]
1385 UINT WINAPI midiInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
1387 return MIDI_GetErrorText(uError, lpText, uSize);
1390 UINT MIDI_InOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD dwCallback,
1391 DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
1393 HANDLE hMidiIn;
1394 LPWINE_MIDI lpwm;
1395 DWORD dwRet = 0;
1397 TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
1398 lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
1400 if (lphMidiIn != NULL) *lphMidiIn = 0;
1402 lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
1403 &dwFlags, &dwCallback, &dwInstance, bFrom32);
1405 if (lpwm == NULL)
1406 return MMSYSERR_NOMEM;
1408 lpwm->mod.hMidi = (HMIDI) hMidiIn;
1409 lpwm->mod.dwCallback = dwCallback;
1410 lpwm->mod.dwInstance = dwInstance;
1412 lpwm->mld.uDeviceID = uDeviceID;
1413 dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);
1415 if (dwRet != MMSYSERR_NOERROR) {
1416 MMDRV_Free(hMidiIn, &lpwm->mld);
1417 hMidiIn = 0;
1419 if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
1420 TRACE("=> %ld hMidi=%p\n", dwRet, hMidiIn);
1422 return dwRet;
1425 /**************************************************************************
1426 * midiInOpen [WINMM.@]
1428 UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
1429 DWORD dwCallback, DWORD dwInstance, DWORD dwFlags)
1431 return MIDI_InOpen(lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
1434 /**************************************************************************
1435 * midiInClose [WINMM.@]
1437 UINT WINAPI midiInClose(HMIDIIN hMidiIn)
1439 LPWINE_MLD wmld;
1440 DWORD dwRet;
1442 TRACE("(%p)\n", hMidiIn);
1444 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1445 return MMSYSERR_INVALHANDLE;
1447 dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
1448 MMDRV_Free(hMidiIn, wmld);
1449 return dwRet;
1452 /**************************************************************************
1453 * midiInPrepareHeader [WINMM.@]
1455 UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
1456 MIDIHDR* lpMidiInHdr, UINT uSize)
1458 LPWINE_MLD wmld;
1460 TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1462 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1463 return MMSYSERR_INVALHANDLE;
1465 return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD)lpMidiInHdr, uSize, TRUE);
1468 /**************************************************************************
1469 * midiInUnprepareHeader [WINMM.@]
1471 UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
1472 MIDIHDR* lpMidiInHdr, UINT uSize)
1474 LPWINE_MLD wmld;
1476 TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1478 if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
1479 return MMSYSERR_NOERROR;
1482 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1483 return MMSYSERR_INVALHANDLE;
1485 return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD)lpMidiInHdr, uSize, TRUE);
1488 /**************************************************************************
1489 * midiInAddBuffer [WINMM.@]
1491 UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
1492 MIDIHDR* lpMidiInHdr, UINT uSize)
1494 LPWINE_MLD wmld;
1496 TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
1498 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1499 return MMSYSERR_INVALHANDLE;
1501 return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD)lpMidiInHdr, uSize, TRUE);
1504 /**************************************************************************
1505 * midiInStart [WINMM.@]
1507 UINT WINAPI midiInStart(HMIDIIN hMidiIn)
1509 LPWINE_MLD wmld;
1511 TRACE("(%p)\n", hMidiIn);
1513 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1514 return MMSYSERR_INVALHANDLE;
1516 return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);
1519 /**************************************************************************
1520 * midiInStop [WINMM.@]
1522 UINT WINAPI midiInStop(HMIDIIN hMidiIn)
1524 LPWINE_MLD wmld;
1526 TRACE("(%p)\n", hMidiIn);
1528 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1529 return MMSYSERR_INVALHANDLE;
1531 return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);
1534 /**************************************************************************
1535 * midiInReset [WINMM.@]
1537 UINT WINAPI midiInReset(HMIDIIN hMidiIn)
1539 LPWINE_MLD wmld;
1541 TRACE("(%p)\n", hMidiIn);
1543 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1544 return MMSYSERR_INVALHANDLE;
1546 return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);
1549 /**************************************************************************
1550 * midiInGetID [WINMM.@]
1552 UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
1554 LPWINE_MLD wmld;
1556 TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
1558 if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
1560 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
1561 return MMSYSERR_INVALHANDLE;
1563 *lpuDeviceID = wmld->uDeviceID;
1565 return MMSYSERR_NOERROR;
1568 /**************************************************************************
1569 * midiInMessage [WINMM.@]
1571 UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
1572 DWORD dwParam1, DWORD dwParam2)
1574 LPWINE_MLD wmld;
1576 TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
1578 if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
1579 return MMSYSERR_INVALHANDLE;
1581 switch (uMessage) {
1582 case MIDM_OPEN:
1583 case MIDM_CLOSE:
1584 FIXME("can't handle OPEN or CLOSE message!\n");
1585 return MMSYSERR_NOTSUPPORTED;
1587 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
1590 typedef struct WINE_MIDIStream {
1591 HMIDIOUT hDevice;
1592 HANDLE hThread;
1593 DWORD dwThreadID;
1594 DWORD dwTempo;
1595 DWORD dwTimeDiv;
1596 DWORD dwPositionMS;
1597 DWORD dwPulses;
1598 DWORD dwStartTicks;
1599 WORD wFlags;
1600 HANDLE hEvent;
1601 LPMIDIHDR lpMidiHdr;
1602 } WINE_MIDIStream;
1604 #define WINE_MSM_HEADER (WM_USER+0)
1605 #define WINE_MSM_STOP (WM_USER+1)
1607 /**************************************************************************
1608 * MMSYSTEM_GetMidiStream [internal]
1610 static BOOL MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
1612 WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
1614 if (lplpwm)
1615 *lplpwm = lpwm;
1617 if (lpwm == NULL) {
1618 return FALSE;
1621 *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
1623 return *lpMidiStrm != NULL;
1626 /**************************************************************************
1627 * MMSYSTEM_MidiStream_Convert [internal]
1629 static DWORD MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
1631 DWORD ret = 0;
1633 if (lpMidiStrm->dwTimeDiv == 0) {
1634 FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
1635 } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
1636 int nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv); /* number of frames */
1637 int nsf = LOBYTE(lpMidiStrm->dwTimeDiv); /* number of sub-frames */
1638 ret = (pulse * 1000) / (nf * nsf);
1639 } else {
1640 ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
1641 (double)lpMidiStrm->dwTimeDiv);
1644 return ret;
1647 /**************************************************************************
1648 * MMSYSTEM_MidiStream_MessageHandler [internal]
1650 static BOOL MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
1652 LPMIDIHDR lpMidiHdr;
1653 LPMIDIHDR* lpmh;
1654 LPBYTE lpData;
1656 switch (msg->message) {
1657 case WM_QUIT:
1658 SetEvent(lpMidiStrm->hEvent);
1659 return FALSE;
1660 case WINE_MSM_STOP:
1661 TRACE("STOP\n");
1662 /* this is not quite what MS doc says... */
1663 midiOutReset(lpMidiStrm->hDevice);
1664 /* empty list of already submitted buffers */
1665 for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {
1666 lpMidiHdr->dwFlags |= MHDR_DONE;
1667 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1669 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1670 (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1671 lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1673 lpMidiStrm->lpMidiHdr = 0;
1674 SetEvent(lpMidiStrm->hEvent);
1675 break;
1676 case WINE_MSM_HEADER:
1677 /* sets initial tick count for first MIDIHDR */
1678 if (!lpMidiStrm->dwStartTicks)
1679 lpMidiStrm->dwStartTicks = GetTickCount();
1681 /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
1682 * by native mcimidi, it doesn't look like a correct one".
1683 * this trick allows to throw it away... but I don't like it.
1684 * It looks like part of the file I'm trying to play and definitively looks
1685 * like raw midi content
1686 * I'd really like to understand why native mcimidi sends it. Perhaps a bad
1687 * synchronization issue where native mcimidi is still processing raw MIDI
1688 * content before generating MIDIEVENTs ?
1690 * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
1691 * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
1692 * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
1693 * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
1694 * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
1695 * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
1696 * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
1697 * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
1698 * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
1699 * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
1700 * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
1701 * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
1702 * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
1703 * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
1704 * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
1705 * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
1706 * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
1708 lpMidiHdr = (LPMIDIHDR)msg->lParam;
1709 lpData = lpMidiHdr->lpData;
1710 TRACE("Adding %s lpMidiHdr=%p [lpData=0x%08lx dwBufferLength=%lu/%lu dwFlags=0x%08lx size=%u]\n",
1711 (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
1712 (DWORD)lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
1713 lpMidiHdr->dwFlags, msg->wParam);
1714 #if 0
1715 /* dumps content of lpMidiHdr->lpData
1716 * FIXME: there should be a debug routine somewhere that already does this
1717 * I hate spreading this type of shit all around the code
1719 for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
1720 DWORD i;
1721 BYTE ch;
1723 for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
1724 printf("%02x ", lpData[dwToGo + i]);
1725 for (; i < 16; i++)
1726 printf(" ");
1727 for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
1728 ch = lpData[dwToGo + i];
1729 printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
1731 printf("\n");
1733 #endif
1734 if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
1735 ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
1736 ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
1737 FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",
1738 (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
1739 ((LPMIDIEVENT)lpData)->dwStreamID);
1740 lpMidiHdr->dwFlags |= MHDR_DONE;
1741 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1743 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1744 (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1745 lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1746 break;
1749 for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));
1750 *lpmh = lpMidiHdr;
1751 lpMidiHdr = (LPMIDIHDR)msg->lParam;
1752 lpMidiHdr->lpNext = 0;
1753 lpMidiHdr->dwFlags |= MHDR_INQUEUE;
1754 lpMidiHdr->dwFlags &= MHDR_DONE;
1755 lpMidiHdr->dwOffset = 0;
1757 break;
1758 default:
1759 FIXME("Unknown message %d\n", msg->message);
1760 break;
1762 return TRUE;
1765 /**************************************************************************
1766 * MMSYSTEM_MidiStream_Player [internal]
1768 static DWORD CALLBACK MMSYSTEM_MidiStream_Player(LPVOID pmt)
1770 WINE_MIDIStream* lpMidiStrm = pmt;
1771 WINE_MIDI* lpwm;
1772 MSG msg;
1773 DWORD dwToGo;
1774 DWORD dwCurrTC;
1775 LPMIDIHDR lpMidiHdr;
1776 LPMIDIEVENT me;
1777 LPBYTE lpData = 0;
1779 TRACE("(%p)!\n", lpMidiStrm);
1781 if (!lpMidiStrm ||
1782 (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
1783 goto the_end;
1785 /* force thread's queue creation */
1786 /* Used to be InitThreadInput16(0, 5); */
1787 /* but following works also with hack in midiStreamOpen */
1788 PeekMessageA(&msg, 0, 0, 0, 0);
1790 /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
1791 SetEvent(lpMidiStrm->hEvent);
1792 TRACE("Ready to go 1\n");
1793 /* thread is started in paused mode */
1794 SuspendThread(lpMidiStrm->hThread);
1795 TRACE("Ready to go 2\n");
1797 lpMidiStrm->dwStartTicks = 0;
1798 lpMidiStrm->dwPulses = 0;
1800 lpMidiStrm->lpMidiHdr = 0;
1802 for (;;) {
1803 lpMidiHdr = lpMidiStrm->lpMidiHdr;
1804 if (!lpMidiHdr) {
1805 /* for first message, block until one arrives, then process all that are available */
1806 GetMessageA(&msg, 0, 0, 0);
1807 do {
1808 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1809 goto the_end;
1810 } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
1811 lpData = 0;
1812 continue;
1815 if (!lpData)
1816 lpData = lpMidiHdr->lpData;
1818 me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
1820 /* do we have to wait ? */
1821 if (me->dwDeltaTime) {
1822 lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
1823 lpMidiStrm->dwPulses += me->dwDeltaTime;
1825 dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
1827 TRACE("%ld/%ld/%ld\n", dwToGo, GetTickCount(), me->dwDeltaTime);
1828 while ((dwCurrTC = GetTickCount()) < dwToGo) {
1829 if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
1830 /* got a message, handle it */
1831 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
1832 if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
1833 goto the_end;
1835 lpData = 0;
1836 } else {
1837 /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
1838 break;
1842 switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
1843 case MEVT_COMMENT:
1844 FIXME("NIY: MEVT_COMMENT\n");
1845 /* do nothing, skip bytes */
1846 break;
1847 case MEVT_LONGMSG:
1848 FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
1849 break;
1850 case MEVT_NOP:
1851 break;
1852 case MEVT_SHORTMSG:
1853 midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
1854 break;
1855 case MEVT_TEMPO:
1856 lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
1857 break;
1858 case MEVT_VERSION:
1859 break;
1860 default:
1861 FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
1862 break;
1864 if (me->dwEvent & MEVT_F_CALLBACK) {
1865 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1866 (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
1867 lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
1869 lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
1870 if (me->dwEvent & MEVT_F_LONG)
1871 lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
1872 if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
1873 /* done with this header */
1874 lpMidiHdr->dwFlags |= MHDR_DONE;
1875 lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
1877 lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
1878 DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
1879 (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
1880 lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
1881 lpData = 0;
1884 the_end:
1885 TRACE("End of thread\n");
1886 ExitThread(0);
1887 return 0; /* for removing the warning, never executed */
1890 /**************************************************************************
1891 * MMSYSTEM_MidiStream_PostMessage [internal]
1893 static BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
1895 if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
1896 DWORD count;
1898 ReleaseThunkLock(&count);
1899 WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1900 RestoreThunkLock(count);
1901 } else {
1902 WARN("bad PostThreadMessageA\n");
1903 return FALSE;
1905 return TRUE;
1908 /**************************************************************************
1909 * midiStreamClose [WINMM.@]
1911 MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
1913 WINE_MIDIStream* lpMidiStrm;
1915 TRACE("(%p)!\n", hMidiStrm);
1917 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
1918 return MMSYSERR_INVALHANDLE;
1920 midiStreamStop(hMidiStrm);
1921 MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
1922 HeapFree(GetProcessHeap(), 0, lpMidiStrm);
1923 CloseHandle(lpMidiStrm->hEvent);
1925 return midiOutClose((HMIDIOUT)hMidiStrm);
1928 /**************************************************************************
1929 * MMSYSTEM_MidiStream_Open [internal]
1931 MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,
1932 DWORD dwCallback, DWORD dwInstance, DWORD fdwOpen,
1933 BOOL bFrom32)
1935 WINE_MIDIStream* lpMidiStrm;
1936 MMRESULT ret;
1937 MIDIOPENSTRMID mosm;
1938 LPWINE_MIDI lpwm;
1939 HMIDIOUT hMidiOut;
1941 TRACE("(%p, %p, %ld, 0x%08lx, 0x%08lx, 0x%08lx)!\n",
1942 lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
1944 if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
1945 return MMSYSERR_INVALPARAM;
1947 lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
1948 if (!lpMidiStrm)
1949 return MMSYSERR_NOMEM;
1951 lpMidiStrm->dwTempo = 500000;
1952 lpMidiStrm->dwTimeDiv = 480; /* 480 is 120 quater notes per minute *//* FIXME ??*/
1953 lpMidiStrm->dwPositionMS = 0;
1955 mosm.dwStreamID = (DWORD)lpMidiStrm;
1956 /* FIXME: the correct value is not allocated yet for MAPPER */
1957 mosm.wDeviceID = *lpuDeviceID;
1958 lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
1959 lpMidiStrm->hDevice = hMidiOut;
1960 if (lphMidiStrm)
1961 *lphMidiStrm = (HMIDISTRM)hMidiOut;
1963 lpwm->mld.uDeviceID = *lpuDeviceID;
1965 ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
1966 lpMidiStrm->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1967 lpMidiStrm->wFlags = HIWORD(fdwOpen);
1969 lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
1970 lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
1972 if (!lpMidiStrm->hThread) {
1973 midiStreamClose((HMIDISTRM)hMidiOut);
1974 return MMSYSERR_NOMEM;
1977 /* wait for thread to have started, and for its queue to be created */
1979 DWORD count;
1981 /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
1982 * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
1983 * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
1985 ReleaseThunkLock(&count);
1986 WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
1987 RestoreThunkLock(count);
1990 TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
1991 *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
1992 return ret;
1995 /**************************************************************************
1996 * midiStreamOpen [WINMM.@]
1998 MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
1999 DWORD cMidi, DWORD dwCallback,
2000 DWORD dwInstance, DWORD fdwOpen)
2002 return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
2003 dwInstance, fdwOpen, TRUE);
2006 /**************************************************************************
2007 * midiStreamOut [WINMM.@]
2009 MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
2010 UINT cbMidiHdr)
2012 WINE_MIDIStream* lpMidiStrm;
2013 DWORD ret = MMSYSERR_NOERROR;
2015 TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
2017 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2018 ret = MMSYSERR_INVALHANDLE;
2019 } else if (!lpMidiHdr) {
2020 ret = MMSYSERR_INVALPARAM;
2021 } else {
2022 if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
2023 WINE_MSM_HEADER, cbMidiHdr,
2024 (DWORD)lpMidiHdr)) {
2025 WARN("bad PostThreadMessageA\n");
2026 ret = MMSYSERR_ERROR;
2029 return ret;
2032 /**************************************************************************
2033 * midiStreamPause [WINMM.@]
2035 MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
2037 WINE_MIDIStream* lpMidiStrm;
2038 DWORD ret = MMSYSERR_NOERROR;
2040 TRACE("(%p)!\n", hMidiStrm);
2042 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2043 ret = MMSYSERR_INVALHANDLE;
2044 } else {
2045 if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
2046 WARN("bad Suspend (%ld)\n", GetLastError());
2047 ret = MMSYSERR_ERROR;
2050 return ret;
2053 /**************************************************************************
2054 * midiStreamPosition [WINMM.@]
2056 MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
2058 WINE_MIDIStream* lpMidiStrm;
2059 DWORD ret = MMSYSERR_NOERROR;
2061 TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
2063 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2064 ret = MMSYSERR_INVALHANDLE;
2065 } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
2066 ret = MMSYSERR_INVALPARAM;
2067 } else {
2068 switch (lpMMT->wType) {
2069 case TIME_MS:
2070 lpMMT->u.ms = lpMidiStrm->dwPositionMS;
2071 TRACE("=> %ld ms\n", lpMMT->u.ms);
2072 break;
2073 case TIME_TICKS:
2074 lpMMT->u.ticks = lpMidiStrm->dwPulses;
2075 TRACE("=> %ld ticks\n", lpMMT->u.ticks);
2076 break;
2077 default:
2078 WARN("Unsupported time type %d\n", lpMMT->wType);
2079 lpMMT->wType = TIME_MS;
2080 ret = MMSYSERR_INVALPARAM;
2081 break;
2084 return ret;
2087 /**************************************************************************
2088 * midiStreamProperty [WINMM.@]
2090 MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
2092 WINE_MIDIStream* lpMidiStrm;
2093 MMRESULT ret = MMSYSERR_NOERROR;
2095 TRACE("(%p, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);
2097 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2098 ret = MMSYSERR_INVALHANDLE;
2099 } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
2100 ret = MMSYSERR_INVALPARAM;
2101 } else if (dwProperty & MIDIPROP_TEMPO) {
2102 MIDIPROPTEMPO* mpt = (MIDIPROPTEMPO*)lpPropData;
2104 if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
2105 ret = MMSYSERR_INVALPARAM;
2106 } else if (dwProperty & MIDIPROP_SET) {
2107 lpMidiStrm->dwTempo = mpt->dwTempo;
2108 TRACE("Setting tempo to %ld\n", mpt->dwTempo);
2109 } else if (dwProperty & MIDIPROP_GET) {
2110 mpt->dwTempo = lpMidiStrm->dwTempo;
2111 TRACE("Getting tempo <= %ld\n", mpt->dwTempo);
2113 } else if (dwProperty & MIDIPROP_TIMEDIV) {
2114 MIDIPROPTIMEDIV* mptd = (MIDIPROPTIMEDIV*)lpPropData;
2116 if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
2117 ret = MMSYSERR_INVALPARAM;
2118 } else if (dwProperty & MIDIPROP_SET) {
2119 lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
2120 TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);
2121 } else if (dwProperty & MIDIPROP_GET) {
2122 mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
2123 TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);
2125 } else {
2126 ret = MMSYSERR_INVALPARAM;
2129 return ret;
2132 /**************************************************************************
2133 * midiStreamRestart [WINMM.@]
2135 MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
2137 WINE_MIDIStream* lpMidiStrm;
2138 MMRESULT ret = MMSYSERR_NOERROR;
2140 TRACE("(%p)!\n", hMidiStrm);
2142 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2143 ret = MMSYSERR_INVALHANDLE;
2144 } else {
2145 DWORD ret;
2147 /* since we increase the thread suspend count on each midiStreamPause
2148 * there may be a need for several midiStreamResume
2150 do {
2151 ret = ResumeThread(lpMidiStrm->hThread);
2152 } while (ret != 0xFFFFFFFF && ret != 0);
2153 if (ret == 0xFFFFFFFF) {
2154 WARN("bad Resume (%ld)\n", GetLastError());
2155 ret = MMSYSERR_ERROR;
2156 } else {
2157 lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
2160 return ret;
2163 /**************************************************************************
2164 * midiStreamStop [WINMM.@]
2166 MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
2168 WINE_MIDIStream* lpMidiStrm;
2169 MMRESULT ret = MMSYSERR_NOERROR;
2171 TRACE("(%p)!\n", hMidiStrm);
2173 if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
2174 ret = MMSYSERR_INVALHANDLE;
2175 } else {
2176 /* in case stream has been paused... FIXME is the current state correct ? */
2177 midiStreamRestart(hMidiStrm);
2178 MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
2180 return ret;
2183 UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType,
2184 const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
2185 DWORD dwInstance, DWORD dwFlags, BOOL bFrom32)
2187 HANDLE handle;
2188 LPWINE_MLD wmld;
2189 DWORD dwRet = MMSYSERR_NOERROR;
2190 WAVEOPENDESC wod;
2192 TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08lX, %d);\n",
2193 lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
2194 dwInstance, dwFlags, bFrom32?32:16);
2196 if (dwFlags & WAVE_FORMAT_QUERY) TRACE("WAVE_FORMAT_QUERY requested !\n");
2198 if (lpFormat == NULL) return WAVERR_BADFORMAT;
2199 if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1))
2200 return MMSYSERR_INVALPARAM;
2202 TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u\n",
2203 lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
2204 lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample, lpFormat->cbSize);
2206 if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
2207 &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL)
2208 return MMSYSERR_NOMEM;
2210 wod.hWave = handle;
2211 wod.lpFormat = lpFormat; /* should the struct be copied iso pointer? */
2212 wod.dwCallback = dwCallback;
2213 wod.dwInstance = dwInstance;
2214 wod.dnDevNode = 0L;
2216 TRACE("cb=%08lx\n", wod.dwCallback);
2218 for (;;) {
2219 if (dwFlags & WAVE_MAPPED) {
2220 wod.uMappedDeviceID = uDeviceID;
2221 uDeviceID = WAVE_MAPPER;
2222 } else {
2223 wod.uMappedDeviceID = -1;
2225 wmld->uDeviceID = uDeviceID;
2227 dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN,
2228 (DWORD)&wod, dwFlags);
2230 if (dwRet != WAVERR_BADFORMAT ||
2231 (dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) break;
2232 /* if we ask for a format which isn't supported by the physical driver,
2233 * let's try to map it through the wave mapper (except, if we already tried
2234 * or user didn't allow us to use acm codecs)
2236 dwFlags |= WAVE_MAPPED;
2237 /* we shall loop only one */
2240 if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
2241 MMDRV_Free(handle, wmld);
2242 handle = 0;
2245 if (lphndl != NULL) *lphndl = handle;
2246 TRACE("=> %ld hWave=%p\n", dwRet, handle);
2248 return dwRet;
2251 /**************************************************************************
2252 * waveOutGetNumDevs [WINMM.@]
2254 UINT WINAPI waveOutGetNumDevs(void)
2256 return MMDRV_GetNum(MMDRV_WAVEOUT);
2259 /**************************************************************************
2260 * waveOutGetDevCapsA [WINMM.@]
2262 UINT WINAPI waveOutGetDevCapsA(UINT uDeviceID, LPWAVEOUTCAPSA lpCaps,
2263 UINT uSize)
2265 LPWINE_MLD wmld;
2267 TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
2269 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2271 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
2272 return MMSYSERR_BADDEVICEID;
2274 return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
2278 /**************************************************************************
2279 * waveOutGetDevCapsW [WINMM.@]
2281 UINT WINAPI waveOutGetDevCapsW(UINT uDeviceID, LPWAVEOUTCAPSW lpCaps,
2282 UINT uSize)
2284 WAVEOUTCAPSA wocA;
2285 UINT ret = waveOutGetDevCapsA(uDeviceID, &wocA, sizeof(wocA));
2287 if (ret == MMSYSERR_NOERROR) {
2288 WAVEOUTCAPSW wocW;
2289 wocW.wMid = wocA.wMid;
2290 wocW.wPid = wocA.wPid;
2291 wocW.vDriverVersion = wocA.vDriverVersion;
2292 MultiByteToWideChar( CP_ACP, 0, wocA.szPname, -1, wocW.szPname,
2293 sizeof(wocW.szPname)/sizeof(WCHAR) );
2294 wocW.dwFormats = wocA.dwFormats;
2295 wocW.wChannels = wocA.wChannels;
2296 wocW.dwSupport = wocA.dwSupport;
2297 memcpy(lpCaps, &wocW, min(uSize, sizeof(wocW)));
2299 return ret;
2302 /**************************************************************************
2303 * WAVE_GetErrorText [internal]
2305 static UINT16 WAVE_GetErrorText(UINT16 uError, LPSTR lpText, UINT16 uSize)
2307 UINT16 ret = MMSYSERR_BADERRNUM;
2309 if (lpText == NULL) {
2310 ret = MMSYSERR_INVALPARAM;
2311 } else if (uSize == 0) {
2312 ret = MMSYSERR_NOERROR;
2313 } else if (
2314 /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
2315 * a warning for the test was always true */
2316 (/*uError >= MMSYSERR_BASE && */uError <= MMSYSERR_LASTERROR) ||
2317 (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
2319 if (LoadStringA(WINMM_IData->hWinMM32Instance,
2320 uError, lpText, uSize) > 0) {
2321 ret = MMSYSERR_NOERROR;
2324 return ret;
2327 /**************************************************************************
2328 * waveOutGetErrorTextA [WINMM.@]
2330 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2332 return WAVE_GetErrorText(uError, lpText, uSize);
2335 /**************************************************************************
2336 * waveOutGetErrorTextW [WINMM.@]
2338 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2340 LPSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize);
2341 UINT ret = WAVE_GetErrorText(uError, xstr, uSize);
2343 MultiByteToWideChar( CP_ACP, 0, xstr, -1, lpText, uSize );
2344 HeapFree(GetProcessHeap(), 0, xstr);
2345 return ret;
2348 /**************************************************************************
2349 * waveOutOpen [WINMM.@]
2350 * All the args/structs have the same layout as the win16 equivalents
2352 UINT WINAPI waveOutOpen(HWAVEOUT* lphWaveOut, UINT uDeviceID,
2353 const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
2354 DWORD dwInstance, DWORD dwFlags)
2356 return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
2357 dwCallback, dwInstance, dwFlags, TRUE);
2360 /**************************************************************************
2361 * waveOutClose [WINMM.@]
2363 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2365 LPWINE_MLD wmld;
2366 DWORD dwRet;
2368 TRACE("(%p)\n", hWaveOut);
2370 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2371 return MMSYSERR_INVALHANDLE;
2373 dwRet = MMDRV_Close(wmld, WODM_CLOSE);
2374 if (dwRet != WAVERR_STILLPLAYING)
2375 MMDRV_Free(hWaveOut, wmld);
2377 return dwRet;
2380 /**************************************************************************
2381 * waveOutPrepareHeader [WINMM.@]
2383 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2384 WAVEHDR* lpWaveOutHdr, UINT uSize)
2386 LPWINE_MLD wmld;
2388 TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2390 if (lpWaveOutHdr == NULL) return MMSYSERR_INVALPARAM;
2392 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2393 return MMSYSERR_INVALHANDLE;
2395 return MMDRV_Message(wmld, WODM_PREPARE, (DWORD)lpWaveOutHdr, uSize, TRUE);
2398 /**************************************************************************
2399 * waveOutUnprepareHeader [WINMM.@]
2401 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2402 LPWAVEHDR lpWaveOutHdr, UINT uSize)
2404 LPWINE_MLD wmld;
2406 TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2408 if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
2409 return MMSYSERR_NOERROR;
2412 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2413 return MMSYSERR_INVALHANDLE;
2415 return MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD)lpWaveOutHdr, uSize, TRUE);
2418 /**************************************************************************
2419 * waveOutWrite [WINMM.@]
2421 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
2422 UINT uSize)
2424 LPWINE_MLD wmld;
2426 TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
2428 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2429 return MMSYSERR_INVALHANDLE;
2431 return MMDRV_Message(wmld, WODM_WRITE, (DWORD)lpWaveOutHdr, uSize, TRUE);
2434 /**************************************************************************
2435 * waveOutBreakLoop [WINMM.@]
2437 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2439 LPWINE_MLD wmld;
2441 TRACE("(%p);\n", hWaveOut);
2443 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2444 return MMSYSERR_INVALHANDLE;
2445 return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
2448 /**************************************************************************
2449 * waveOutPause [WINMM.@]
2451 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2453 LPWINE_MLD wmld;
2455 TRACE("(%p);\n", hWaveOut);
2457 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2458 return MMSYSERR_INVALHANDLE;
2459 return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
2462 /**************************************************************************
2463 * waveOutReset [WINMM.@]
2465 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2467 LPWINE_MLD wmld;
2469 TRACE("(%p);\n", hWaveOut);
2471 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2472 return MMSYSERR_INVALHANDLE;
2473 return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
2476 /**************************************************************************
2477 * waveOutRestart [WINMM.@]
2479 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2481 LPWINE_MLD wmld;
2483 TRACE("(%p);\n", hWaveOut);
2485 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2486 return MMSYSERR_INVALHANDLE;
2487 return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
2490 /**************************************************************************
2491 * waveOutGetPosition [WINMM.@]
2493 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2494 UINT uSize)
2496 LPWINE_MLD wmld;
2498 TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);
2500 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2501 return MMSYSERR_INVALHANDLE;
2503 return MMDRV_Message(wmld, WODM_GETPOS, (DWORD)lpTime, uSize, TRUE);
2506 /**************************************************************************
2507 * waveOutGetPitch [WINMM.@]
2509 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2511 LPWINE_MLD wmld;
2513 TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
2515 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2516 return MMSYSERR_INVALHANDLE;
2517 return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD)lpdw, 0L, TRUE);
2520 /**************************************************************************
2521 * waveOutSetPitch [WINMM.@]
2523 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2525 LPWINE_MLD wmld;
2527 TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);
2529 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2530 return MMSYSERR_INVALHANDLE;
2531 return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
2534 /**************************************************************************
2535 * waveOutGetPlaybackRate [WINMM.@]
2537 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2539 LPWINE_MLD wmld;
2541 TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
2543 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2544 return MMSYSERR_INVALHANDLE;
2545 return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD)lpdw, 0L, TRUE);
2548 /**************************************************************************
2549 * waveOutSetPlaybackRate [WINMM.@]
2551 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2553 LPWINE_MLD wmld;
2555 TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);
2557 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2558 return MMSYSERR_INVALHANDLE;
2559 return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
2562 /**************************************************************************
2563 * waveOutGetVolume [WINMM.@]
2565 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)
2567 LPWINE_MLD wmld;
2569 TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
2571 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2572 return MMSYSERR_INVALHANDLE;
2574 return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD)lpdw, 0L, TRUE);
2577 /**************************************************************************
2578 * waveOutSetVolume [WINMM.@]
2580 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)
2582 LPWINE_MLD wmld;
2584 TRACE("(%p, %08lx);\n", hWaveOut, dw);
2586 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
2587 return MMSYSERR_INVALHANDLE;
2589 return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
2592 /**************************************************************************
2593 * waveOutGetID [WINMM.@]
2595 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2597 LPWINE_MLD wmld;
2599 TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);
2601 if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2603 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
2604 return MMSYSERR_INVALHANDLE;
2606 *lpuDeviceID = wmld->uDeviceID;
2607 return 0;
2610 /**************************************************************************
2611 * waveOutMessage [WINMM.@]
2613 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2614 DWORD dwParam1, DWORD dwParam2)
2616 LPWINE_MLD wmld;
2618 TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2620 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
2621 if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
2622 return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2624 return MMSYSERR_INVALHANDLE;
2627 /* from M$ KB */
2628 if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
2629 return MMSYSERR_INVALPARAM;
2631 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
2634 /**************************************************************************
2635 * waveInGetNumDevs [WINMM.@]
2637 UINT WINAPI waveInGetNumDevs(void)
2639 return MMDRV_GetNum(MMDRV_WAVEIN);
2642 /**************************************************************************
2643 * waveInGetDevCapsW [WINMM.@]
2645 UINT WINAPI waveInGetDevCapsW(UINT uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2647 WAVEINCAPSA wicA;
2648 UINT ret = waveInGetDevCapsA(uDeviceID, &wicA, uSize);
2650 if (ret == MMSYSERR_NOERROR) {
2651 WAVEINCAPSW wicW;
2652 wicW.wMid = wicA.wMid;
2653 wicW.wPid = wicA.wPid;
2654 wicW.vDriverVersion = wicA.vDriverVersion;
2655 MultiByteToWideChar( CP_ACP, 0, wicA.szPname, -1, wicW.szPname,
2656 sizeof(wicW.szPname)/sizeof(WCHAR) );
2657 wicW.dwFormats = wicA.dwFormats;
2658 wicW.wChannels = wicA.wChannels;
2659 memcpy(lpCaps, &wicW, min(uSize, sizeof(wicW)));
2661 return ret;
2664 /**************************************************************************
2665 * waveInGetDevCapsA [WINMM.@]
2667 UINT WINAPI waveInGetDevCapsA(UINT uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2669 LPWINE_MLD wmld;
2671 TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
2673 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2675 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
2676 return MMSYSERR_BADDEVICEID;
2678 return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD)lpCaps, uSize, TRUE);
2681 /**************************************************************************
2682 * waveInGetErrorTextA [WINMM.@]
2684 UINT WINAPI waveInGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2686 return WAVE_GetErrorText(uError, lpText, uSize);
2689 /**************************************************************************
2690 * waveInGetErrorTextW [WINMM.@]
2692 UINT WINAPI waveInGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2694 LPSTR txt = HeapAlloc(GetProcessHeap(), 0, uSize);
2695 UINT ret = WAVE_GetErrorText(uError, txt, uSize);
2697 MultiByteToWideChar( CP_ACP, 0, txt, -1, lpText, uSize );
2698 HeapFree(GetProcessHeap(), 0, txt);
2699 return ret;
2702 /**************************************************************************
2703 * waveInOpen [WINMM.@]
2705 UINT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2706 const LPWAVEFORMATEX lpFormat, DWORD dwCallback,
2707 DWORD dwInstance, DWORD dwFlags)
2709 return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
2710 dwCallback, dwInstance, dwFlags, TRUE);
2713 /**************************************************************************
2714 * waveInClose [WINMM.@]
2716 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2718 LPWINE_MLD wmld;
2719 DWORD dwRet;
2721 TRACE("(%p)\n", hWaveIn);
2723 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2724 return MMSYSERR_INVALHANDLE;
2726 dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
2727 if (dwRet != WAVERR_STILLPLAYING)
2728 MMDRV_Free(hWaveIn, wmld);
2729 return dwRet;
2732 /**************************************************************************
2733 * waveInPrepareHeader [WINMM.@]
2735 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2736 UINT uSize)
2738 LPWINE_MLD wmld;
2740 TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2742 if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2743 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2744 return MMSYSERR_INVALHANDLE;
2746 lpWaveInHdr->dwBytesRecorded = 0;
2748 return MMDRV_Message(wmld, WIDM_PREPARE, (DWORD)lpWaveInHdr, uSize, TRUE);
2751 /**************************************************************************
2752 * waveInUnprepareHeader [WINMM.@]
2754 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2755 UINT uSize)
2757 LPWINE_MLD wmld;
2759 TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2761 if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2762 if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED)) {
2763 return MMSYSERR_NOERROR;
2766 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2767 return MMSYSERR_INVALHANDLE;
2769 return MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD)lpWaveInHdr, uSize, TRUE);
2772 /**************************************************************************
2773 * waveInAddBuffer [WINMM.@]
2775 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
2776 WAVEHDR* lpWaveInHdr, UINT uSize)
2778 LPWINE_MLD wmld;
2780 TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2782 if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2783 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2784 return MMSYSERR_INVALHANDLE;
2786 return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD)lpWaveInHdr, uSize, TRUE);
2789 /**************************************************************************
2790 * waveInReset [WINMM.@]
2792 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
2794 LPWINE_MLD wmld;
2796 TRACE("(%p);\n", hWaveIn);
2798 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2799 return MMSYSERR_INVALHANDLE;
2801 return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
2804 /**************************************************************************
2805 * waveInStart [WINMM.@]
2807 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
2809 LPWINE_MLD wmld;
2811 TRACE("(%p);\n", hWaveIn);
2813 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2814 return MMSYSERR_INVALHANDLE;
2816 return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
2819 /**************************************************************************
2820 * waveInStop [WINMM.@]
2822 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
2824 LPWINE_MLD wmld;
2826 TRACE("(%p);\n", hWaveIn);
2828 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2829 return MMSYSERR_INVALHANDLE;
2831 return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
2834 /**************************************************************************
2835 * waveInGetPosition [WINMM.@]
2837 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
2838 UINT uSize)
2840 LPWINE_MLD wmld;
2842 TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
2844 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2845 return MMSYSERR_INVALHANDLE;
2847 return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD)lpTime, uSize, TRUE);
2850 /**************************************************************************
2851 * waveInGetID [WINMM.@]
2853 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
2855 LPWINE_MLD wmld;
2857 TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
2859 if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2861 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2862 return MMSYSERR_INVALHANDLE;
2864 *lpuDeviceID = wmld->uDeviceID;
2865 return MMSYSERR_NOERROR;
2868 /**************************************************************************
2869 * waveInMessage [WINMM.@]
2871 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
2872 DWORD dwParam1, DWORD dwParam2)
2874 LPWINE_MLD wmld;
2876 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
2878 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
2879 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
2880 return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2882 return MMSYSERR_INVALHANDLE;
2885 /* from M$ KB */
2886 if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
2887 return MMSYSERR_INVALPARAM;
2890 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);