-/* -*- tab-width: 8; c-basic-offset: 4 -*- */\r
-\r
-/*\r
- * WINMM functions\r
- *\r
- * Copyright 1993 Martin Ayotte\r
- * 1998-2002 Eric Pouech\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-/*\r
- * Eric POUECH :\r
- * 98/9 added Win32 MCI support\r
- * 99/4 added midiStream support\r
- * 99/9 added support for loadable low level drivers\r
- */\r
-\r
-/* TODO\r
- * + it seems that some programs check what's installed in\r
- * registry against the value returned by drivers. Wine is\r
- * currently broken regarding this point.\r
- * + check thread-safeness for MMSYSTEM and WINMM entry points\r
- * + unicode entry points are badly supported (would require\r
- * moving 32 bit drivers as Unicode as they are supposed to be)\r
- * + allow joystick and timer external calls as we do for wave,\r
- * midi, mixer and aux\r
- */\r
-\r
-#include <stdio.h>\r
-#include <stdarg.h>\r
-#include <string.h>\r
-\r
-#define NONAMELESSUNION\r
-#define NONAMELESSSTRUCT\r
-#include "windef.h"\r
-#include "winbase.h"\r
-#include "mmsystem.h"\r
-#include "winuser.h"\r
-#include "winnls.h"\r
-#include "winreg.h"\r
-#include "winternl.h"\r
-#include "winemm.h"\r
-\r
-#include "wine/debug.h"\r
-\r
-WINE_DEFAULT_DEBUG_CHANNEL(winmm);\r
-\r
-void (WINAPI *pFnReleaseThunkLock)(DWORD*);\r
-void (WINAPI *pFnRestoreThunkLock)(DWORD);\r
-\r
-/* ========================================================================\r
- * G L O B A L S E T T I N G S\r
- * ========================================================================*/\r
-\r
-WINE_MM_IDATA WINMM_IData;\r
-\r
-/**************************************************************************\r
- * WINMM_CreateIData [internal]\r
- */\r
-static BOOL WINMM_CreateIData(HINSTANCE hInstDLL)\r
-{\r
- memset( &WINMM_IData, 0, sizeof WINMM_IData );\r
-\r
- WINMM_IData.hWinMM32Instance = hInstDLL;\r
- InitializeCriticalSection(&WINMM_IData.cs);\r
-/* FIXME crashes in ReactOS\r
- WINMM_IData.cs.DebugInfo->Spare[1] = (DWORD)"WINMM_IData";\r
-*/\r
- WINMM_IData.psStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);\r
- WINMM_IData.psLastEvent = CreateEventW(NULL, TRUE, FALSE, NULL);\r
- TRACE("Initialized IData (%p)\n", &WINMM_IData);\r
- return TRUE;\r
-}\r
-\r
-/**************************************************************************\r
- * WINMM_DeleteIData [internal]\r
- */\r
-static void WINMM_DeleteIData(void)\r
-{\r
- TIME_MMTimeStop();\r
-\r
- /* FIXME: should also free content and resources allocated\r
- * inside WINMM_IData */\r
- CloseHandle(WINMM_IData.psStopEvent);\r
- CloseHandle(WINMM_IData.psLastEvent);\r
- DeleteCriticalSection(&WINMM_IData.cs);\r
-}\r
-\r
-/******************************************************************\r
- * WINMM_LoadMMSystem\r
- *\r
- */\r
-#ifndef __REACTOS__\r
-static HANDLE (WINAPI *pGetModuleHandle16)(LPCSTR);\r
-static DWORD (WINAPI *pLoadLibrary16)(LPCSTR);\r
-#endif\r
-\r
-BOOL WINMM_CheckForMMSystem(void)\r
-{\r
- /* 0 is not checked yet, -1 is not present, 1 is present */\r
- static int loaded /* = 0 */;\r
-\r
- if (loaded == 0)\r
- {\r
- HANDLE h = GetModuleHandleA("kernel32");\r
- loaded = -1;\r
- if (h)\r
- {\r
-#ifndef __REACTOS__\r
- pGetModuleHandle16 = (void*)GetProcAddress(h, "GetModuleHandle16");\r
- pLoadLibrary16 = (void*)GetProcAddress(h, "LoadLibrary16");\r
- if (pGetModuleHandle16 && pLoadLibrary16 &&\r
- (pGetModuleHandle16("MMSYSTEM.DLL") || pLoadLibrary16("MMSYSTEM.DLL")))\r
-#endif /* __REACTOS__ */\r
- loaded = 1;\r
- }\r
- }\r
- return loaded > 0;\r
-}\r
-\r
-/******************************************************************\r
- * WINMM_ErrorToString\r
- */\r
-const char* WINMM_ErrorToString(MMRESULT error)\r
-{\r
-#define ERR_TO_STR(dev) case dev: return #dev\r
- static char unknown[32];\r
- switch (error) {\r
- ERR_TO_STR(MMSYSERR_NOERROR);\r
- ERR_TO_STR(MMSYSERR_ERROR);\r
- ERR_TO_STR(MMSYSERR_BADDEVICEID);\r
- ERR_TO_STR(MMSYSERR_NOTENABLED);\r
- ERR_TO_STR(MMSYSERR_ALLOCATED);\r
- ERR_TO_STR(MMSYSERR_INVALHANDLE);\r
- ERR_TO_STR(MMSYSERR_NODRIVER);\r
- ERR_TO_STR(MMSYSERR_NOMEM);\r
- ERR_TO_STR(MMSYSERR_NOTSUPPORTED);\r
- ERR_TO_STR(MMSYSERR_BADERRNUM);\r
- ERR_TO_STR(MMSYSERR_INVALFLAG);\r
- ERR_TO_STR(MMSYSERR_INVALPARAM);\r
- ERR_TO_STR(MMSYSERR_HANDLEBUSY);\r
- ERR_TO_STR(MMSYSERR_INVALIDALIAS);\r
- ERR_TO_STR(MMSYSERR_BADDB);\r
- ERR_TO_STR(MMSYSERR_KEYNOTFOUND);\r
- ERR_TO_STR(MMSYSERR_READERROR);\r
- ERR_TO_STR(MMSYSERR_WRITEERROR);\r
- ERR_TO_STR(MMSYSERR_DELETEERROR);\r
- ERR_TO_STR(MMSYSERR_VALNOTFOUND);\r
- ERR_TO_STR(MMSYSERR_NODRIVERCB);\r
- ERR_TO_STR(WAVERR_BADFORMAT);\r
- ERR_TO_STR(WAVERR_STILLPLAYING);\r
- ERR_TO_STR(WAVERR_UNPREPARED);\r
- ERR_TO_STR(WAVERR_SYNC);\r
- }\r
- sprintf(unknown, "Unknown(0x%08x)", error);\r
- return unknown;\r
-#undef ERR_TO_STR\r
-}\r
-\r
-/**************************************************************************\r
- * DllMain (WINMM.init)\r
- *\r
- * WINMM DLL entry point\r
- *\r
- */\r
-BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)\r
-{\r
- TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, fImpLoad);\r
-\r
- switch (fdwReason) {\r
- case DLL_PROCESS_ATTACH:\r
- DisableThreadLibraryCalls(hInstDLL);\r
-\r
- if (!WINMM_CreateIData(hInstDLL))\r
- return FALSE;\r
- if (!MMDRV_Init()) {\r
- WINMM_DeleteIData();\r
- return FALSE;\r
- }\r
- break;\r
- case DLL_PROCESS_DETACH:\r
- /* close all opened MCI drivers */\r
- MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L, TRUE);\r
- MMDRV_Exit();\r
- /* now unload all remaining drivers... */\r
- DRIVER_UnloadAll();\r
-\r
- WINMM_DeleteIData();\r
- break;\r
- }\r
- return TRUE;\r
-}\r
-\r
-/**************************************************************************\r
- * Mixer devices. New to Win95\r
- */\r
-\r
-/**************************************************************************\r
- * find out the real mixer ID depending on hmix (depends on dwFlags)\r
- */\r
-static UINT MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags, LPWINE_MIXER * lplpwm)\r
-{\r
- LPWINE_MIXER lpwm = NULL;\r
- UINT uRet = MMSYSERR_NOERROR;\r
-\r
- switch (dwFlags & 0xF0000000ul) {\r
- case MIXER_OBJECTF_MIXER:\r
- lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);\r
- break;\r
- case MIXER_OBJECTF_HMIXER:\r
- lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);\r
- break;\r
- case MIXER_OBJECTF_WAVEOUT:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_HWAVEOUT:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_WAVEIN:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, TRUE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_HWAVEIN:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, FALSE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_MIDIOUT:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_HMIDIOUT:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_MIDIIN:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, TRUE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_HMIDIIN:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, FALSE, MMDRV_MIXER);\r
- break;\r
- case MIXER_OBJECTF_AUX:\r
- lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX, TRUE, MMDRV_MIXER);\r
- break;\r
- default:\r
- WARN("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);\r
- lpwm = 0;\r
- uRet = MMSYSERR_INVALFLAG;\r
- break;\r
- }\r
- *lplpwm = lpwm;\r
- if (lpwm == 0 && uRet == MMSYSERR_NOERROR)\r
- uRet = MMSYSERR_INVALPARAM;\r
- return uRet;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetNumDevs [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetNumDevs(void)\r
-{\r
- return MMDRV_GetNum(MMDRV_MIXER);\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetDevCapsA [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)\r
-{\r
- MIXERCAPSW micW;\r
- UINT ret;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- MIXERCAPSA micA;\r
- micA.wMid = micW.wMid;\r
- micA.wPid = micW.wPid;\r
- micA.vDriverVersion = micW.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,\r
- sizeof(micA.szPname), NULL, NULL );\r
- micA.fdwSupport = micW.fdwSupport;\r
- micA.cDestinations = micW.cDestinations;\r
- memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetDevCapsW [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIXER, TRUE)) == NULL)\r
- return MMSYSERR_BADDEVICEID;\r
-\r
- return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);\r
-}\r
-\r
-UINT MIXER_Open(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD fdwOpen, BOOL bFrom32)\r
-{\r
- HANDLE hMix;\r
- LPWINE_MLD wmld;\r
- DWORD dwRet = 0;\r
- MIXEROPENDESC mod;\r
-\r
- TRACE("(%p, %d, %08lx, %08lx, %08lx)\n",\r
- lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);\r
-\r
- wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,\r
- &dwCallback, &dwInstance, bFrom32);\r
-\r
- wmld->uDeviceID = uDeviceID;\r
- mod.hmx = (HMIXEROBJ)hMix;\r
- mod.dwCallback = dwCallback;\r
- mod.dwInstance = dwInstance;\r
-\r
- dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD)&mod, fdwOpen);\r
-\r
- if (dwRet != MMSYSERR_NOERROR) {\r
- MMDRV_Free(hMix, wmld);\r
- hMix = 0;\r
- }\r
- if (lphMix) *lphMix = hMix;\r
- TRACE("=> %ld hMixer=%p\n", dwRet, hMix);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerOpen [WINMM.@]\r
- */\r
-UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD fdwOpen)\r
-{\r
- return MIXER_Open(lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * mixerClose [WINMM.@]\r
- */\r
-UINT WINAPI mixerClose(HMIXER hMix)\r
-{\r
- LPWINE_MLD wmld;\r
- DWORD dwRet;\r
-\r
- TRACE("(%p)\n", hMix);\r
-\r
- if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;\r
-\r
- dwRet = MMDRV_Close(wmld, MXDM_CLOSE);\r
- MMDRV_Free(hMix, wmld);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetID [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)\r
-{\r
- LPWINE_MIXER lpwm;\r
- UINT uRet = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p %p %08lx)\n", hmix, lpid, fdwID);\r
-\r
- if ((uRet = MIXER_GetDev(hmix, fdwID, &lpwm)) != MMSYSERR_NOERROR)\r
- return uRet;\r
-\r
- if (lpid)\r
- *lpid = lpwm->mld.uDeviceID;\r
-\r
- return uRet;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetControlDetailsW [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,\r
- DWORD fdwDetails)\r
-{\r
- LPWINE_MIXER lpwm;\r
- UINT uRet = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmcdW, fdwDetails);\r
-\r
- if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)\r
- return uRet;\r
-\r
- if (lpmcdW == NULL || lpmcdW->cbStruct != sizeof(*lpmcdW))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD_PTR)lpmcdW,\r
- fdwDetails, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetControlDetailsA [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,\r
- DWORD fdwDetails)\r
-{\r
- DWORD ret = MMSYSERR_NOTENABLED;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);\r
-\r
- if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {\r
- case MIXER_GETCONTROLDETAILSF_VALUE:\r
- /* can savely use A structure as it is, no string inside */\r
- ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);\r
- break;\r
- case MIXER_GETCONTROLDETAILSF_LISTTEXT:\r
- {\r
- MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = (MIXERCONTROLDETAILS_LISTTEXTA *)lpmcdA->paDetails;\r
- MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;\r
- int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);\r
- unsigned int i;\r
-\r
- if (lpmcdA->u.cMultipleItems != 0) {\r
- size *= lpmcdA->u.cMultipleItems;\r
- }\r
- pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);\r
- lpmcdA->paDetails = pDetailsW;\r
- lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);\r
- /* set up lpmcd->paDetails */\r
- ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);\r
- /* copy from lpmcd->paDetails back to paDetailsW; */\r
- if (ret == MMSYSERR_NOERROR) {\r
- for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {\r
- pDetailsA->dwParam1 = pDetailsW->dwParam1;\r
- pDetailsA->dwParam2 = pDetailsW->dwParam2;\r
- WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,\r
- pDetailsA->szName,\r
- sizeof(pDetailsA->szName), NULL, NULL );\r
- pDetailsA++;\r
- pDetailsW++;\r
- }\r
- pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;\r
- pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;\r
- }\r
- HeapFree(GetProcessHeap(), 0, pDetailsW);\r
- lpmcdA->paDetails = pDetailsA;\r
- lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);\r
- }\r
- break;\r
- default:\r
- ERR("Unsupported fdwDetails=0x%08lx\n", fdwDetails);\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetLineControlsA [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,\r
- DWORD fdwControls)\r
-{\r
- MIXERLINECONTROLSW mlcW;\r
- DWORD ret;\r
- unsigned int i;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmlcA, fdwControls);\r
-\r
- if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||\r
- lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- mlcW.cbStruct = sizeof(mlcW);\r
- mlcW.dwLineID = lpmlcA->dwLineID;\r
- mlcW.u.dwControlID = lpmlcA->u.dwControlID;\r
- mlcW.u.dwControlType = lpmlcA->u.dwControlType;\r
-\r
- /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,\r
- the control count is assumed to be 1 - This is relied upon by a game,\r
- "Dynomite Deluze" */\r
- if (MIXER_GETLINECONTROLSF_ONEBYTYPE == fdwControls) {\r
- mlcW.cControls = 1;\r
- } else {\r
- mlcW.cControls = lpmlcA->cControls;\r
- }\r
- mlcW.cbmxctrl = sizeof(MIXERCONTROLW);\r
- mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,\r
- mlcW.cControls * mlcW.cbmxctrl);\r
-\r
- ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- lpmlcA->dwLineID = mlcW.dwLineID;\r
- lpmlcA->u.dwControlID = mlcW.u.dwControlID;\r
- lpmlcA->u.dwControlType = mlcW.u.dwControlType;\r
- lpmlcA->cControls = mlcW.cControls;\r
-\r
- for (i = 0; i < mlcW.cControls; i++) {\r
- lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);\r
- lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;\r
- lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;\r
- lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;\r
- lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;\r
- WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,\r
- lpmlcA->pamxctrl[i].szShortName,\r
- sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );\r
- WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,\r
- lpmlcA->pamxctrl[i].szName,\r
- sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );\r
- /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==\r
- * sizeof(mlcW.pamxctrl[i].Bounds) */\r
- memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,\r
- sizeof(mlcW.pamxctrl[i].Bounds));\r
- /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==\r
- * sizeof(mlcW.pamxctrl[i].Metrics) */\r
- memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,\r
- sizeof(mlcW.pamxctrl[i].Metrics));\r
- }\r
- }\r
-\r
- HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);\r
-\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetLineControlsW [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,\r
- DWORD fdwControls)\r
-{\r
- LPWINE_MIXER lpwm;\r
- UINT uRet = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmlcW, fdwControls);\r
-\r
- if ((uRet = MIXER_GetDev(hmix, fdwControls, &lpwm)) != MMSYSERR_NOERROR)\r
- return uRet;\r
-\r
- if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD_PTR)lpmlcW,\r
- fdwControls, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetLineInfoW [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)\r
-{\r
- LPWINE_MIXER lpwm;\r
- UINT uRet = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmliW, fdwInfo);\r
-\r
- if ((uRet = MIXER_GetDev(hmix, fdwInfo, &lpwm)) != MMSYSERR_NOERROR)\r
- return uRet;\r
-\r
- return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD_PTR)lpmliW,\r
- fdwInfo, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * mixerGetLineInfoA [WINMM.@]\r
- */\r
-UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,\r
- DWORD fdwInfo)\r
-{\r
- MIXERLINEW mliW;\r
- UINT ret;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmliA, fdwInfo);\r
-\r
- if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- mliW.cbStruct = sizeof(mliW);\r
- switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {\r
- case MIXER_GETLINEINFOF_COMPONENTTYPE:\r
- mliW.dwComponentType = lpmliA->dwComponentType;\r
- break;\r
- case MIXER_GETLINEINFOF_DESTINATION:\r
- mliW.dwDestination = lpmliA->dwDestination;\r
- break;\r
- case MIXER_GETLINEINFOF_LINEID:\r
- mliW.dwLineID = lpmliA->dwLineID;\r
- break;\r
- case MIXER_GETLINEINFOF_SOURCE:\r
- mliW.dwDestination = lpmliA->dwDestination;\r
- mliW.dwSource = lpmliA->dwSource;\r
- break;\r
- case MIXER_GETLINEINFOF_TARGETTYPE:\r
- mliW.Target.dwType = lpmliA->Target.dwType;\r
- mliW.Target.wMid = lpmliA->Target.wMid;\r
- mliW.Target.wPid = lpmliA->Target.wPid;\r
- mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;\r
- MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname));\r
- break;\r
- default:\r
- WARN("Unsupported fdwControls=0x%08lx\n", fdwInfo);\r
- return MMSYSERR_INVALFLAG;\r
- }\r
-\r
- ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);\r
-\r
- lpmliA->dwDestination = mliW.dwDestination;\r
- lpmliA->dwSource = mliW.dwSource;\r
- lpmliA->dwLineID = mliW.dwLineID;\r
- lpmliA->fdwLine = mliW.fdwLine;\r
- lpmliA->dwUser = mliW.dwUser;\r
- lpmliA->dwComponentType = mliW.dwComponentType;\r
- lpmliA->cChannels = mliW.cChannels;\r
- lpmliA->cConnections = mliW.cConnections;\r
- lpmliA->cControls = mliW.cControls;\r
- WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,\r
- sizeof(lpmliA->szShortName), NULL, NULL);\r
- WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,\r
- sizeof(lpmliA->szName), NULL, NULL );\r
- lpmliA->Target.dwType = mliW.Target.dwType;\r
- lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;\r
- lpmliA->Target.wMid = mliW.Target.wMid;\r
- lpmliA->Target.wPid = mliW.Target.wPid;\r
- lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,\r
- sizeof(lpmliA->Target.szPname), NULL, NULL );\r
-\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * mixerSetControlDetails [WINMM.@]\r
- */\r
-UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,\r
- DWORD fdwDetails)\r
-{\r
- LPWINE_MIXER lpwm;\r
- UINT uRet = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %08lx)\n", hmix, lpmcd, fdwDetails);\r
-\r
- if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)\r
- return uRet;\r
-\r
- return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD_PTR)lpmcd,\r
- fdwDetails, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * mixerMessage [WINMM.@]\r
- */\r
-DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%04lx, %d, %08lx, %08lx): semi-stub?\n",\r
- (DWORD)hmix, uMsg, dwParam1, dwParam2);\r
-\r
- if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * auxGetNumDevs [WINMM.@]\r
- */\r
-UINT WINAPI auxGetNumDevs(void)\r
-{\r
- return MMDRV_GetNum(MMDRV_AUX);\r
-}\r
-\r
-/**************************************************************************\r
- * auxGetDevCapsW [WINMM.@]\r
- */\r
-UINT WINAPI auxGetDevCapsW(UINT_PTR uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%04X, %p, %d) !\n", uDeviceID, lpCaps, uSize);\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * auxGetDevCapsA [WINMM.@]\r
- */\r
-UINT WINAPI auxGetDevCapsA(UINT_PTR uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)\r
-{\r
- AUXCAPSW acW;\r
- UINT ret;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- ret = auxGetDevCapsW(uDeviceID, &acW, sizeof(acW));\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- AUXCAPSA acA;\r
- acA.wMid = acW.wMid;\r
- acA.wPid = acW.wPid;\r
- acA.vDriverVersion = acW.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, acW.szPname, -1, acA.szPname,\r
- sizeof(acA.szPname), NULL, NULL );\r
- acA.wTechnology = acW.wTechnology;\r
- acA.dwSupport = acW.dwSupport;\r
- memcpy(lpCaps, &acA, min(uSize, sizeof(acA)));\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * auxGetVolume [WINMM.@]\r
- */\r
-UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * auxSetVolume [WINMM.@]\r
- */\r
-UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * auxOutMessage [WINMM.@]\r
- */\r
-UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetNumDevs [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetNumDevs(void)\r
-{\r
- return MMDRV_GetNum(MMDRV_MIDIOUT);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetDevCapsW [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetDevCapsW(UINT_PTR uDeviceID, LPMIDIOUTCAPSW lpCaps,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%u, %p, %u);\n", uDeviceID, lpCaps, uSize);\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetDevCapsA [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetDevCapsA(UINT_PTR uDeviceID, LPMIDIOUTCAPSA lpCaps,\r
- UINT uSize)\r
-{\r
- MIDIOUTCAPSW mocW;\r
- UINT ret;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- ret = midiOutGetDevCapsW(uDeviceID, &mocW, sizeof(mocW));\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- MIDIOUTCAPSA mocA;\r
- mocA.wMid = mocW.wMid;\r
- mocA.wPid = mocW.wPid;\r
- mocA.vDriverVersion = mocW.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, mocW.szPname, -1, mocA.szPname,\r
- sizeof(mocA.szPname), NULL, NULL );\r
- mocA.wTechnology = mocW.wTechnology;\r
- mocA.wVoices = mocW.wVoices;\r
- mocA.wNotes = mocW.wNotes;\r
- mocA.wChannelMask = mocW.wChannelMask;\r
- mocA.dwSupport = mocW.dwSupport;\r
- memcpy(lpCaps, &mocA, min(uSize, sizeof(mocA)));\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetErrorTextA [WINMM.@]\r
- * midiInGetErrorTextA [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)\r
-{\r
- UINT ret;\r
-\r
- if (lpText == NULL) ret = MMSYSERR_INVALPARAM;\r
- else if (uSize == 0) ret = MMSYSERR_NOERROR;\r
- else\r
- {\r
- LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));\r
- if (!xstr) ret = MMSYSERR_NOMEM;\r
- else\r
- {\r
- ret = midiOutGetErrorTextW(uError, xstr, uSize);\r
- if (ret == MMSYSERR_NOERROR)\r
- WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);\r
- HeapFree(GetProcessHeap(), 0, xstr);\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetErrorTextW [WINMM.@]\r
- * midiInGetErrorTextW [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)\r
-{\r
- UINT ret = MMSYSERR_BADERRNUM;\r
-\r
- if (lpText == NULL) ret = MMSYSERR_INVALPARAM;\r
- else if (uSize == 0) ret = MMSYSERR_NOERROR;\r
- else if (\r
- /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit\r
- * a warning for the test was always true */\r
- (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||\r
- (uError >= MIDIERR_BASE && uError <= MIDIERR_LASTERROR)) {\r
- if (LoadStringW(WINMM_IData.hWinMM32Instance,\r
- uError, lpText, uSize) > 0) {\r
- ret = MMSYSERR_NOERROR;\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * MIDI_OutAlloc [internal]\r
- */\r
-static LPWINE_MIDI MIDI_OutAlloc(HMIDIOUT* lphMidiOut, LPDWORD lpdwCallback,\r
- LPDWORD lpdwInstance, LPDWORD lpdwFlags,\r
- DWORD cIDs, MIDIOPENSTRMID* lpIDs, BOOL bFrom32)\r
-{\r
- HANDLE hMidiOut;\r
- LPWINE_MIDI lpwm;\r
- UINT size;\r
-\r
- size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);\r
-\r
- lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,\r
- lpdwCallback, lpdwInstance, bFrom32);\r
-\r
- if (lphMidiOut != NULL)\r
- *lphMidiOut = hMidiOut;\r
-\r
- if (lpwm) {\r
- lpwm->mod.hMidi = (HMIDI) hMidiOut;\r
- lpwm->mod.dwCallback = *lpdwCallback;\r
- lpwm->mod.dwInstance = *lpdwInstance;\r
- lpwm->mod.dnDevNode = 0;\r
- lpwm->mod.cIds = cIDs;\r
- if (cIDs)\r
- memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));\r
- }\r
- return lpwm;\r
-}\r
-\r
-UINT MIDI_OutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)\r
-{\r
- HMIDIOUT hMidiOut;\r
- LPWINE_MIDI lpwm;\r
- UINT dwRet = 0;\r
-\r
- TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",\r
- lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);\r
-\r
- if (lphMidiOut != NULL) *lphMidiOut = 0;\r
-\r
- lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags,\r
- 0, NULL, bFrom32);\r
-\r
- if (lpwm == NULL)\r
- return MMSYSERR_NOMEM;\r
-\r
- lpwm->mld.uDeviceID = uDeviceID;\r
-\r
- dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD)&lpwm->mod, dwFlags);\r
-\r
- if (dwRet != MMSYSERR_NOERROR) {\r
- MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);\r
- hMidiOut = 0;\r
- }\r
-\r
- if (lphMidiOut) *lphMidiOut = hMidiOut;\r
- TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutOpen [WINMM.@]\r
- */\r
-UINT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,\r
- DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)\r
-{\r
- return MIDI_OutOpen(lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutClose [WINMM.@]\r
- */\r
-UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)\r
-{\r
- LPWINE_MLD wmld;\r
- DWORD dwRet;\r
-\r
- TRACE("(%p)\n", hMidiOut);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- dwRet = MMDRV_Close(wmld, MODM_CLOSE);\r
- MMDRV_Free(hMidiOut, wmld);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutPrepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,\r
- MIDIHDR* lpMidiOutHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);\r
-\r
- if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutUnprepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,\r
- MIDIHDR* lpMidiOutHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);\r
-\r
- if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {\r
- return MMSYSERR_NOERROR;\r
- }\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutShortMsg [WINMM.@]\r
- */\r
-UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lX)\n", hMidiOut, dwMsg);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutLongMsg [WINMM.@]\r
- */\r
-UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,\r
- MIDIHDR* lpMidiOutHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutReset [WINMM.@]\r
- */\r
-UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p)\n", hMidiOut);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_RESET, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetVolume [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutSetVolume [WINMM.@]\r
- */\r
-UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %ld);\n", hMidiOut, dwVolume);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutCachePatches [WINMM.@]\r
- */\r
-UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,\r
- WORD* lpwPatchArray, UINT uFlags)\r
-{\r
- /* not really necessary to support this */\r
- FIXME("not supported yet\n");\r
- return MMSYSERR_NOTSUPPORTED;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutCacheDrumPatches [WINMM.@]\r
- */\r
-UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,\r
- WORD* lpwKeyArray, UINT uFlags)\r
-{\r
- FIXME("not supported yet\n");\r
- return MMSYSERR_NOTSUPPORTED;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutGetID [WINMM.@]\r
- */\r
-UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);\r
-\r
- if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- *lpuDeviceID = wmld->uDeviceID;\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * midiOutMessage [WINMM.@]\r
- */\r
-UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,\r
- DWORD_PTR dwParam1, DWORD_PTR dwParam2)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);\r
-\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {\r
- /* HACK... */\r
- if (uMessage == 0x0001) {\r
- *(LPDWORD)dwParam1 = 1;\r
- return 0;\r
- }\r
- if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {\r
- return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);\r
- }\r
- return MMSYSERR_INVALHANDLE;\r
- }\r
-\r
- switch (uMessage) {\r
- case MODM_OPEN:\r
- case MODM_CLOSE:\r
- FIXME("can't handle OPEN or CLOSE message!\n");\r
- return MMSYSERR_NOTSUPPORTED;\r
- }\r
- return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInGetNumDevs [WINMM.@]\r
- */\r
-UINT WINAPI midiInGetNumDevs(void)\r
-{\r
- return MMDRV_GetNum(MMDRV_MIDIIN);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInGetDevCapsW [WINMM.@]\r
- */\r
-UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%d, %p, %d);\n", uDeviceID, lpCaps, uSize);\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInGetDevCapsA [WINMM.@]\r
- */\r
-UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)\r
-{\r
- MIDIINCAPSW micW;\r
- UINT ret;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- MIDIINCAPSA micA;\r
- micA.wMid = micW.wMid;\r
- micA.wPid = micW.wPid;\r
- micA.vDriverVersion = micW.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,\r
- sizeof(micA.szPname), NULL, NULL );\r
- micA.dwSupport = micW.dwSupport;\r
- memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));\r
- }\r
- return ret;\r
-}\r
-\r
-UINT MIDI_InOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)\r
-{\r
- HANDLE hMidiIn;\r
- LPWINE_MIDI lpwm;\r
- DWORD dwRet = 0;\r
-\r
- TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",\r
- lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);\r
-\r
- if (lphMidiIn != NULL) *lphMidiIn = 0;\r
-\r
- lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,\r
- &dwFlags, &dwCallback, &dwInstance, bFrom32);\r
-\r
- if (lpwm == NULL)\r
- return MMSYSERR_NOMEM;\r
-\r
- lpwm->mod.hMidi = (HMIDI) hMidiIn;\r
- lpwm->mod.dwCallback = dwCallback;\r
- lpwm->mod.dwInstance = dwInstance;\r
-\r
- lpwm->mld.uDeviceID = uDeviceID;\r
- dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);\r
-\r
- if (dwRet != MMSYSERR_NOERROR) {\r
- MMDRV_Free(hMidiIn, &lpwm->mld);\r
- hMidiIn = 0;\r
- }\r
- if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;\r
- TRACE("=> %ld hMidi=%p\n", dwRet, hMidiIn);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * midiInOpen [WINMM.@]\r
- */\r
-UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,\r
- DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)\r
-{\r
- return MIDI_InOpen(lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInClose [WINMM.@]\r
- */\r
-UINT WINAPI midiInClose(HMIDIIN hMidiIn)\r
-{\r
- LPWINE_MLD wmld;\r
- DWORD dwRet;\r
-\r
- TRACE("(%p)\n", hMidiIn);\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- dwRet = MMDRV_Close(wmld, MIDM_CLOSE);\r
- MMDRV_Free(hMidiIn, wmld);\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * midiInPrepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,\r
- MIDIHDR* lpMidiInHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);\r
-\r
- if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInUnprepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,\r
- MIDIHDR* lpMidiInHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);\r
-\r
- if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {\r
- return MMSYSERR_NOERROR;\r
- }\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInAddBuffer [WINMM.@]\r
- */\r
-UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,\r
- MIDIHDR* lpMidiInHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInStart [WINMM.@]\r
- */\r
-UINT WINAPI midiInStart(HMIDIIN hMidiIn)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p)\n", hMidiIn);\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInStop [WINMM.@]\r
- */\r
-UINT WINAPI midiInStop(HMIDIIN hMidiIn)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p)\n", hMidiIn);\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInReset [WINMM.@]\r
- */\r
-UINT WINAPI midiInReset(HMIDIIN hMidiIn)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p)\n", hMidiIn);\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiInGetID [WINMM.@]\r
- */\r
-UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);\r
-\r
- if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- *lpuDeviceID = wmld->uDeviceID;\r
-\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * midiInMessage [WINMM.@]\r
- */\r
-UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,\r
- DWORD_PTR dwParam1, DWORD_PTR dwParam2)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);\r
-\r
- if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- switch (uMessage) {\r
- case MIDM_OPEN:\r
- case MIDM_CLOSE:\r
- FIXME("can't handle OPEN or CLOSE message!\n");\r
- return MMSYSERR_NOTSUPPORTED;\r
- }\r
- return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);\r
-}\r
-\r
-typedef struct WINE_MIDIStream {\r
- HMIDIOUT hDevice;\r
- HANDLE hThread;\r
- DWORD dwThreadID;\r
- DWORD dwTempo;\r
- DWORD dwTimeDiv;\r
- DWORD dwPositionMS;\r
- DWORD dwPulses;\r
- DWORD dwStartTicks;\r
- WORD wFlags;\r
- HANDLE hEvent;\r
- LPMIDIHDR lpMidiHdr;\r
-} WINE_MIDIStream;\r
-\r
-#define WINE_MSM_HEADER (WM_USER+0)\r
-#define WINE_MSM_STOP (WM_USER+1)\r
-\r
-/**************************************************************************\r
- * MMSYSTEM_GetMidiStream [internal]\r
- */\r
-static BOOL MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)\r
-{\r
- WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);\r
-\r
- if (lplpwm)\r
- *lplpwm = lpwm;\r
-\r
- if (lpwm == NULL) {\r
- return FALSE;\r
- }\r
-\r
- *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;\r
-\r
- return *lpMidiStrm != NULL;\r
-}\r
-\r
-/**************************************************************************\r
- * MMSYSTEM_MidiStream_Convert [internal]\r
- */\r
-static DWORD MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)\r
-{\r
- DWORD ret = 0;\r
-\r
- if (lpMidiStrm->dwTimeDiv == 0) {\r
- FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");\r
- } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */\r
- int nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv); /* number of frames */\r
- int nsf = LOBYTE(lpMidiStrm->dwTimeDiv); /* number of sub-frames */\r
- ret = (pulse * 1000) / (nf * nsf);\r
- } else {\r
- ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /\r
- (double)lpMidiStrm->dwTimeDiv);\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * MMSYSTEM_MidiStream_MessageHandler [internal]\r
- */\r
-static BOOL MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)\r
-{\r
- LPMIDIHDR lpMidiHdr;\r
- LPMIDIHDR* lpmh;\r
- LPBYTE lpData;\r
-\r
- switch (msg->message) {\r
- case WM_QUIT:\r
- SetEvent(lpMidiStrm->hEvent);\r
- return FALSE;\r
- case WINE_MSM_STOP:\r
- TRACE("STOP\n");\r
- /* this is not quite what MS doc says... */\r
- midiOutReset(lpMidiStrm->hDevice);\r
- /* empty list of already submitted buffers */\r
- for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {\r
- lpMidiHdr->dwFlags |= MHDR_DONE;\r
- lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;\r
-\r
- DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,\r
- (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,\r
- lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);\r
- }\r
- lpMidiStrm->lpMidiHdr = 0;\r
- SetEvent(lpMidiStrm->hEvent);\r
- break;\r
- case WINE_MSM_HEADER:\r
- /* sets initial tick count for first MIDIHDR */\r
- if (!lpMidiStrm->dwStartTicks)\r
- lpMidiStrm->dwStartTicks = GetTickCount();\r
-\r
- /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent\r
- * by native mcimidi, it doesn't look like a correct one".\r
- * this trick allows to throw it away... but I don't like it.\r
- * It looks like part of the file I'm trying to play and definitively looks\r
- * like raw midi content\r
- * I'd really like to understand why native mcimidi sends it. Perhaps a bad\r
- * synchronization issue where native mcimidi is still processing raw MIDI\r
- * content before generating MIDIEVENTs ?\r
- *\r
- * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..\r
- * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..\r
- * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.\r
- * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.\r
- * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^\r
- * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b\r
- * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#\r
- * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..\r
- * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..\r
- * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.\r
- * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.\r
- * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F\r
- * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H\r
- * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;\r
- * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;\r
- * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.\r
- * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.\r
- */\r
- lpMidiHdr = (LPMIDIHDR)msg->lParam;\r
- lpData = lpMidiHdr->lpData;\r
- TRACE("Adding %s lpMidiHdr=%p [lpData=0x%08lx dwBufferLength=%lu/%lu dwFlags=0x%08lx size=%u]\n",\r
- (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,\r
- (DWORD)lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,\r
- lpMidiHdr->dwFlags, msg->wParam);\r
-#if 0\r
- /* dumps content of lpMidiHdr->lpData\r
- * FIXME: there should be a debug routine somewhere that already does this\r
- * I hate spreading this type of shit all around the code\r
- */\r
- for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {\r
- DWORD i;\r
- BYTE ch;\r
-\r
- for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)\r
- printf("%02x ", lpData[dwToGo + i]);\r
- for (; i < 16; i++)\r
- printf(" ");\r
- for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {\r
- ch = lpData[dwToGo + i];\r
- printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');\r
- }\r
- printf("\n");\r
- }\r
-#endif\r
- if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&\r
- ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&\r
- ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {\r
- FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",\r
- (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",\r
- ((LPMIDIEVENT)lpData)->dwStreamID);\r
- lpMidiHdr->dwFlags |= MHDR_DONE;\r
- lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;\r
-\r
- DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,\r
- (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,\r
- lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);\r
- break;\r
- }\r
-\r
- for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));\r
- *lpmh = lpMidiHdr;\r
- lpMidiHdr = (LPMIDIHDR)msg->lParam;\r
- lpMidiHdr->lpNext = 0;\r
- lpMidiHdr->dwFlags |= MHDR_INQUEUE;\r
- lpMidiHdr->dwFlags &= ~MHDR_DONE;\r
- lpMidiHdr->dwOffset = 0;\r
-\r
- break;\r
- default:\r
- FIXME("Unknown message %d\n", msg->message);\r
- break;\r
- }\r
- return TRUE;\r
-}\r
-\r
-/**************************************************************************\r
- * MMSYSTEM_MidiStream_Player [internal]\r
- */\r
-static DWORD CALLBACK MMSYSTEM_MidiStream_Player(LPVOID pmt)\r
-{\r
- WINE_MIDIStream* lpMidiStrm = pmt;\r
- WINE_MIDI* lpwm;\r
- MSG msg;\r
- DWORD dwToGo;\r
- DWORD dwCurrTC;\r
- LPMIDIHDR lpMidiHdr;\r
- LPMIDIEVENT me;\r
- LPBYTE lpData = 0;\r
-\r
- TRACE("(%p)!\n", lpMidiStrm);\r
-\r
- if (!lpMidiStrm ||\r
- (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)\r
- goto the_end;\r
-\r
- /* force thread's queue creation */\r
- /* Used to be InitThreadInput16(0, 5); */\r
- /* but following works also with hack in midiStreamOpen */\r
- PeekMessageA(&msg, 0, 0, 0, 0);\r
-\r
- /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */\r
- SetEvent(lpMidiStrm->hEvent);\r
- TRACE("Ready to go 1\n");\r
- /* thread is started in paused mode */\r
- SuspendThread(lpMidiStrm->hThread);\r
- TRACE("Ready to go 2\n");\r
-\r
- lpMidiStrm->dwStartTicks = 0;\r
- lpMidiStrm->dwPulses = 0;\r
-\r
- lpMidiStrm->lpMidiHdr = 0;\r
-\r
- for (;;) {\r
- lpMidiHdr = lpMidiStrm->lpMidiHdr;\r
- if (!lpMidiHdr) {\r
- /* for first message, block until one arrives, then process all that are available */\r
- GetMessageA(&msg, 0, 0, 0);\r
- do {\r
- if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))\r
- goto the_end;\r
- } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));\r
- lpData = 0;\r
- continue;\r
- }\r
-\r
- if (!lpData)\r
- lpData = lpMidiHdr->lpData;\r
-\r
- me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);\r
-\r
- /* do we have to wait ? */\r
- if (me->dwDeltaTime) {\r
- lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);\r
- lpMidiStrm->dwPulses += me->dwDeltaTime;\r
-\r
- dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;\r
-\r
- TRACE("%ld/%ld/%ld\n", dwToGo, GetTickCount(), me->dwDeltaTime);\r
- while ((dwCurrTC = GetTickCount()) < dwToGo) {\r
- if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {\r
- /* got a message, handle it */\r
- while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {\r
- if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))\r
- goto the_end;\r
- }\r
- lpData = 0;\r
- } else {\r
- /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */\r
- break;\r
- }\r
- }\r
- }\r
- switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {\r
- case MEVT_COMMENT:\r
- FIXME("NIY: MEVT_COMMENT\n");\r
- /* do nothing, skip bytes */\r
- break;\r
- case MEVT_LONGMSG:\r
- FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");\r
- break;\r
- case MEVT_NOP:\r
- break;\r
- case MEVT_SHORTMSG:\r
- midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));\r
- break;\r
- case MEVT_TEMPO:\r
- lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);\r
- break;\r
- case MEVT_VERSION:\r
- break;\r
- default:\r
- FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));\r
- break;\r
- }\r
- if (me->dwEvent & MEVT_F_CALLBACK) {\r
- DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,\r
- (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,\r
- lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);\r
- }\r
- lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);\r
- if (me->dwEvent & MEVT_F_LONG)\r
- lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;\r
- if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {\r
- /* done with this header */\r
- lpMidiHdr->dwFlags |= MHDR_DONE;\r
- lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;\r
-\r
- lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;\r
- DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,\r
- (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,\r
- lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);\r
- lpData = 0;\r
- }\r
- }\r
-the_end:\r
- TRACE("End of thread\n");\r
- ExitThread(0);\r
- return 0; /* for removing the warning, never executed */\r
-}\r
-\r
-/**************************************************************************\r
- * MMSYSTEM_MidiStream_PostMessage [internal]\r
- */\r
-static BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)\r
-{\r
- if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {\r
- DWORD count;\r
-\r
- if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);\r
- WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);\r
- if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);\r
- } else {\r
- WARN("bad PostThreadMessageA\n");\r
- return FALSE;\r
- }\r
- return TRUE;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamClose [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
-\r
- TRACE("(%p)!\n", hMidiStrm);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- midiStreamStop(hMidiStrm);\r
- MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);\r
- HeapFree(GetProcessHeap(), 0, lpMidiStrm);\r
- CloseHandle(lpMidiStrm->hEvent);\r
-\r
- return midiOutClose((HMIDIOUT)hMidiStrm);\r
-}\r
-\r
-/**************************************************************************\r
- * MMSYSTEM_MidiStream_Open [internal]\r
- */\r
-MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,\r
- DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen, \r
- BOOL bFrom32)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- MMRESULT ret;\r
- MIDIOPENSTRMID mosm;\r
- LPWINE_MIDI lpwm;\r
- HMIDIOUT hMidiOut;\r
-\r
- TRACE("(%p, %p, %ld, 0x%08lx, 0x%08lx, 0x%08lx)!\n",\r
- lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);\r
-\r
- if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)\r
- return MMSYSERR_INVALPARAM;\r
-\r
- lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));\r
- if (!lpMidiStrm)\r
- return MMSYSERR_NOMEM;\r
-\r
- lpMidiStrm->dwTempo = 500000;\r
- lpMidiStrm->dwTimeDiv = 480; /* 480 is 120 quater notes per minute *//* FIXME ??*/\r
- lpMidiStrm->dwPositionMS = 0;\r
-\r
- mosm.dwStreamID = (DWORD)lpMidiStrm;\r
- /* FIXME: the correct value is not allocated yet for MAPPER */\r
- mosm.wDeviceID = *lpuDeviceID;\r
- lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);\r
- lpMidiStrm->hDevice = hMidiOut;\r
- if (lphMidiStrm)\r
- *lphMidiStrm = (HMIDISTRM)hMidiOut;\r
-\r
- lpwm->mld.uDeviceID = *lpuDeviceID;\r
-\r
- ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);\r
- lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);\r
- lpMidiStrm->wFlags = HIWORD(fdwOpen);\r
-\r
- lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,\r
- lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));\r
-\r
- if (!lpMidiStrm->hThread) {\r
- midiStreamClose((HMIDISTRM)hMidiOut);\r
- return MMSYSERR_NOMEM;\r
- }\r
- SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);\r
-\r
- /* wait for thread to have started, and for its queue to be created */\r
- {\r
- DWORD count;\r
-\r
- /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,\r
- * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running\r
- * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.\r
- */\r
- if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);\r
- WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);\r
- if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);\r
- }\r
-\r
- TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",\r
- *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamOpen [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,\r
- DWORD cMidi, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD fdwOpen)\r
-{\r
- return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,\r
- dwInstance, fdwOpen, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamOut [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,\r
- UINT cbMidiHdr)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- DWORD ret = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {\r
- ret = MMSYSERR_INVALHANDLE;\r
- } else if (!lpMidiHdr) {\r
- ret = MMSYSERR_INVALPARAM;\r
- } else {\r
- if (!PostThreadMessageA(lpMidiStrm->dwThreadID,\r
- WINE_MSM_HEADER, cbMidiHdr,\r
- (DWORD)lpMidiHdr)) {\r
- WARN("bad PostThreadMessageA\n");\r
- ret = MMSYSERR_ERROR;\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamPause [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- DWORD ret = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p)!\n", hMidiStrm);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {\r
- ret = MMSYSERR_INVALHANDLE;\r
- } else {\r
- if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {\r
- WARN("bad Suspend (%ld)\n", GetLastError());\r
- ret = MMSYSERR_ERROR;\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamPosition [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- DWORD ret = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {\r
- ret = MMSYSERR_INVALHANDLE;\r
- } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {\r
- ret = MMSYSERR_INVALPARAM;\r
- } else {\r
- switch (lpMMT->wType) {\r
- case TIME_MS:\r
- lpMMT->u.ms = lpMidiStrm->dwPositionMS;\r
- TRACE("=> %ld ms\n", lpMMT->u.ms);\r
- break;\r
- case TIME_TICKS:\r
- lpMMT->u.ticks = lpMidiStrm->dwPulses;\r
- TRACE("=> %ld ticks\n", lpMMT->u.ticks);\r
- break;\r
- default:\r
- WARN("Unsupported time type %d\n", lpMMT->wType);\r
- lpMMT->wType = TIME_MS;\r
- ret = MMSYSERR_INVALPARAM;\r
- break;\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamProperty [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- MMRESULT ret = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {\r
- ret = MMSYSERR_INVALHANDLE;\r
- } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {\r
- ret = MMSYSERR_INVALPARAM;\r
- } else if (dwProperty & MIDIPROP_TEMPO) {\r
- MIDIPROPTEMPO* mpt = (MIDIPROPTEMPO*)lpPropData;\r
-\r
- if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {\r
- ret = MMSYSERR_INVALPARAM;\r
- } else if (dwProperty & MIDIPROP_SET) {\r
- lpMidiStrm->dwTempo = mpt->dwTempo;\r
- TRACE("Setting tempo to %ld\n", mpt->dwTempo);\r
- } else if (dwProperty & MIDIPROP_GET) {\r
- mpt->dwTempo = lpMidiStrm->dwTempo;\r
- TRACE("Getting tempo <= %ld\n", mpt->dwTempo);\r
- }\r
- } else if (dwProperty & MIDIPROP_TIMEDIV) {\r
- MIDIPROPTIMEDIV* mptd = (MIDIPROPTIMEDIV*)lpPropData;\r
-\r
- if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {\r
- ret = MMSYSERR_INVALPARAM;\r
- } else if (dwProperty & MIDIPROP_SET) {\r
- lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;\r
- TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);\r
- } else if (dwProperty & MIDIPROP_GET) {\r
- mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;\r
- TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);\r
- }\r
- } else {\r
- ret = MMSYSERR_INVALPARAM;\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamRestart [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- MMRESULT ret = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p)!\n", hMidiStrm);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {\r
- ret = MMSYSERR_INVALHANDLE;\r
- } else {\r
- DWORD ret;\r
-\r
- /* since we increase the thread suspend count on each midiStreamPause\r
- * there may be a need for several midiStreamResume\r
- */\r
- do {\r
- ret = ResumeThread(lpMidiStrm->hThread);\r
- } while (ret != 0xFFFFFFFF && ret != 0);\r
- if (ret == 0xFFFFFFFF) {\r
- WARN("bad Resume (%ld)\n", GetLastError());\r
- ret = MMSYSERR_ERROR;\r
- } else {\r
- lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * midiStreamStop [WINMM.@]\r
- */\r
-MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)\r
-{\r
- WINE_MIDIStream* lpMidiStrm;\r
- MMRESULT ret = MMSYSERR_NOERROR;\r
-\r
- TRACE("(%p)!\n", hMidiStrm);\r
-\r
- if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {\r
- ret = MMSYSERR_INVALHANDLE;\r
- } else {\r
- /* in case stream has been paused... FIXME is the current state correct ? */\r
- midiStreamRestart(hMidiStrm);\r
- MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);\r
- }\r
- return ret;\r
-}\r
-\r
-UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType, \r
- LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback, \r
- DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)\r
-{\r
- HANDLE handle;\r
- LPWINE_MLD wmld;\r
- DWORD dwRet = MMSYSERR_NOERROR;\r
- WAVEOPENDESC wod;\r
-\r
- TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08lX, %d);\n",\r
- lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,\r
- dwInstance, dwFlags, bFrom32?32:16);\r
-\r
- if (dwFlags & WAVE_FORMAT_QUERY)\r
- TRACE("WAVE_FORMAT_QUERY requested !\n");\r
-\r
- if (lpFormat == NULL) {\r
- WARN("bad format\n");\r
- return WAVERR_BADFORMAT;\r
- }\r
-\r
- if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {\r
- WARN("invalid parameter\n");\r
- return MMSYSERR_INVALPARAM;\r
- }\r
-\r
- /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */\r
- TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u\n",\r
- lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,\r
- lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);\r
-\r
- if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,\r
- &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL) {\r
- WARN("no memory\n");\r
- return MMSYSERR_NOMEM;\r
- }\r
-\r
- wod.hWave = handle;\r
- wod.lpFormat = (LPWAVEFORMATEX)lpFormat; /* should the struct be copied iso pointer? */\r
- wod.dwCallback = dwCallback;\r
- wod.dwInstance = dwInstance;\r
- wod.dnDevNode = 0L;\r
-\r
- TRACE("cb=%08lx\n", wod.dwCallback);\r
-\r
- for (;;) {\r
- if (dwFlags & WAVE_MAPPED) {\r
- wod.uMappedDeviceID = uDeviceID;\r
- uDeviceID = WAVE_MAPPER;\r
- } else {\r
- wod.uMappedDeviceID = -1;\r
- }\r
- wmld->uDeviceID = uDeviceID;\r
- \r
- dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN, \r
- (DWORD)&wod, dwFlags);\r
-\r
- TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));\r
- if (dwRet != WAVERR_BADFORMAT ||\r
- ((dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) || (uDeviceID == WAVE_MAPPER)) break;\r
- /* if we ask for a format which isn't supported by the physical driver, \r
- * let's try to map it through the wave mapper (except, if we already tried\r
- * or user didn't allow us to use acm codecs or the device is already the mapper)\r
- */\r
- dwFlags |= WAVE_MAPPED;\r
- /* we shall loop only one */\r
- }\r
-\r
- if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {\r
- MMDRV_Free(handle, wmld);\r
- handle = 0;\r
- }\r
-\r
- if (lphndl != NULL) *lphndl = handle;\r
- TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetNumDevs [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetNumDevs(void)\r
-{\r
- return MMDRV_GetNum(MMDRV_WAVEOUT);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetDevCapsA [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,\r
- UINT uSize)\r
-{\r
- WAVEOUTCAPSW wocW;\r
- UINT ret;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- WAVEOUTCAPSA wocA;\r
- wocA.wMid = wocW.wMid;\r
- wocA.wPid = wocW.wPid;\r
- wocA.vDriverVersion = wocW.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,\r
- sizeof(wocA.szPname), NULL, NULL );\r
- wocA.dwFormats = wocW.dwFormats;\r
- wocA.wChannels = wocW.wChannels;\r
- wocA.dwSupport = wocW.dwSupport;\r
- memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetDevCapsW [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)\r
- return MMSYSERR_BADDEVICEID;\r
-\r
- return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);\r
-\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetErrorTextA [WINMM.@]\r
- * waveInGetErrorTextA [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)\r
-{\r
- UINT ret;\r
-\r
- if (lpText == NULL) ret = MMSYSERR_INVALPARAM;\r
- else if (uSize == 0) ret = MMSYSERR_NOERROR;\r
- else\r
- {\r
- LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));\r
- if (!xstr) ret = MMSYSERR_NOMEM;\r
- else\r
- {\r
- ret = waveOutGetErrorTextW(uError, xstr, uSize);\r
- if (ret == MMSYSERR_NOERROR)\r
- WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);\r
- HeapFree(GetProcessHeap(), 0, xstr);\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetErrorTextW [WINMM.@]\r
- * waveInGetErrorTextW [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)\r
-{\r
- UINT ret = MMSYSERR_BADERRNUM;\r
-\r
- if (lpText == NULL) ret = MMSYSERR_INVALPARAM;\r
- else if (uSize == 0) ret = MMSYSERR_NOERROR;\r
- else if (\r
- /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit\r
- * a warning for the test was always true */\r
- (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||\r
- (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {\r
- if (LoadStringW(WINMM_IData.hWinMM32Instance,\r
- uError, lpText, uSize) > 0) {\r
- ret = MMSYSERR_NOERROR;\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutOpen [WINMM.@]\r
- * All the args/structs have the same layout as the win16 equivalents\r
- */\r
-MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,\r
- LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD dwFlags)\r
-{\r
- return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,\r
- dwCallback, dwInstance, dwFlags, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutClose [WINMM.@]\r
- */\r
-UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)\r
-{\r
- LPWINE_MLD wmld;\r
- DWORD dwRet;\r
-\r
- TRACE("(%p)\n", hWaveOut);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- dwRet = MMDRV_Close(wmld, WODM_CLOSE);\r
- if (dwRet != WAVERR_STILLPLAYING)\r
- MMDRV_Free(hWaveOut, wmld);\r
-\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutPrepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,\r
- WAVEHDR* lpWaveOutHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
- UINT result;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);\r
-\r
- if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- if ((result = MMDRV_Message(wmld, WODM_PREPARE, (DWORD_PTR)lpWaveOutHdr,\r
- uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)\r
- return result;\r
-\r
- if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)\r
- return WAVERR_STILLPLAYING;\r
-\r
- lpWaveOutHdr->dwFlags |= WHDR_PREPARED;\r
- lpWaveOutHdr->dwFlags &= ~WHDR_DONE;\r
-\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutUnprepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,\r
- LPWAVEHDR lpWaveOutHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
- UINT result;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);\r
-\r
- if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))\r
- return MMSYSERR_INVALPARAM;\r
- \r
- if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {\r
- return MMSYSERR_NOERROR;\r
- }\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- if ((result = MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD_PTR)lpWaveOutHdr,\r
- uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)\r
- return result;\r
-\r
- if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)\r
- return WAVERR_STILLPLAYING;\r
-\r
- lpWaveOutHdr->dwFlags &= ~WHDR_PREPARED;\r
- lpWaveOutHdr->dwFlags |= WHDR_DONE;\r
-\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutWrite [WINMM.@]\r
- */\r
-UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WODM_WRITE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutBreakLoop [WINMM.@]\r
- */\r
-UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveOut);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutPause [WINMM.@]\r
- */\r
-UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveOut);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutReset [WINMM.@]\r
- */\r
-UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveOut);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutRestart [WINMM.@]\r
- */\r
-UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveOut);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetPosition [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WODM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetPitch [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutSetPitch [WINMM.@]\r
- */\r
-UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetPlaybackRate [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutSetPlaybackRate [WINMM.@]\r
- */\r
-UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
- return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetVolume [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutSetVolume [WINMM.@]\r
- */\r
-UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %08lx);\n", hWaveOut, dw);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutGetID [WINMM.@]\r
- */\r
-UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);\r
-\r
- if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- *lpuDeviceID = wmld->uDeviceID;\r
- return 0;\r
-}\r
-\r
-/**************************************************************************\r
- * waveOutMessage [WINMM.@]\r
- */\r
-UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,\r
- DWORD_PTR dwParam1, DWORD_PTR dwParam2)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);\r
-\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {\r
- if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {\r
- return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);\r
- }\r
- WARN("invalid handle\n");\r
- return MMSYSERR_INVALHANDLE;\r
- }\r
-\r
- /* from M$ KB */\r
- if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER)) {\r
- WARN("invalid parameter\n");\r
- return MMSYSERR_INVALPARAM;\r
- }\r
-\r
- return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInGetNumDevs [WINMM.@]\r
- */\r
-UINT WINAPI waveInGetNumDevs(void)\r
-{\r
- return MMDRV_GetNum(MMDRV_WAVEIN);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInGetDevCapsW [WINMM.@]\r
- */\r
-UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)\r
- return MMSYSERR_BADDEVICEID;\r
-\r
- return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInGetDevCapsA [WINMM.@]\r
- */\r
-UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)\r
-{\r
- WAVEINCAPSW wicW;\r
- UINT ret;\r
-\r
- if (lpCaps == NULL) return MMSYSERR_INVALPARAM;\r
-\r
- ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));\r
-\r
- if (ret == MMSYSERR_NOERROR) {\r
- WAVEINCAPSA wicA;\r
- wicA.wMid = wicW.wMid;\r
- wicA.wPid = wicW.wPid;\r
- wicA.vDriverVersion = wicW.vDriverVersion;\r
- WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,\r
- sizeof(wicA.szPname), NULL, NULL );\r
- wicA.dwFormats = wicW.dwFormats;\r
- wicA.wChannels = wicW.wChannels;\r
- memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));\r
- }\r
- return ret;\r
-}\r
-\r
-/**************************************************************************\r
- * waveInOpen [WINMM.@]\r
- */\r
-MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,\r
- LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,\r
- DWORD_PTR dwInstance, DWORD dwFlags)\r
-{\r
- return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,\r
- dwCallback, dwInstance, dwFlags, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInClose [WINMM.@]\r
- */\r
-UINT WINAPI waveInClose(HWAVEIN hWaveIn)\r
-{\r
- LPWINE_MLD wmld;\r
- DWORD dwRet;\r
-\r
- TRACE("(%p)\n", hWaveIn);\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);\r
- if (dwRet != WAVERR_STILLPLAYING)\r
- MMDRV_Free(hWaveIn, wmld);\r
- return dwRet;\r
-}\r
-\r
-/**************************************************************************\r
- * waveInPrepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
- UINT result;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
-\r
- if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,\r
- uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)\r
- return result;\r
-\r
- if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)\r
- return WAVERR_STILLPLAYING;\r
-\r
- lpWaveInHdr->dwFlags |= WHDR_PREPARED;\r
- lpWaveInHdr->dwFlags &= ~WHDR_DONE;\r
- lpWaveInHdr->dwBytesRecorded = 0;\r
-\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * waveInUnprepareHeader [WINMM.@]\r
- */\r
-UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
- UINT result;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
-\r
- if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))\r
- return MMSYSERR_INVALPARAM;\r
-\r
- if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))\r
- return MMSYSERR_NOERROR;\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,\r
- uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)\r
- return result;\r
-\r
- if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)\r
- return WAVERR_STILLPLAYING;\r
-\r
- lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;\r
- lpWaveInHdr->dwFlags |= WHDR_DONE;\r
-\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * waveInAddBuffer [WINMM.@]\r
- */\r
-UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,\r
- WAVEHDR* lpWaveInHdr, UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);\r
-\r
- if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInReset [WINMM.@]\r
- */\r
-UINT WINAPI waveInReset(HWAVEIN hWaveIn)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveIn);\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInStart [WINMM.@]\r
- */\r
-UINT WINAPI waveInStart(HWAVEIN hWaveIn)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveIn);\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInStop [WINMM.@]\r
- */\r
-UINT WINAPI waveInStop(HWAVEIN hWaveIn)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p);\n", hWaveIn);\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInGetPosition [WINMM.@]\r
- */\r
-UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,\r
- UINT uSize)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);\r
-}\r
-\r
-/**************************************************************************\r
- * waveInGetID [WINMM.@]\r
- */\r
-UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);\r
-\r
- if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)\r
- return MMSYSERR_INVALHANDLE;\r
-\r
- *lpuDeviceID = wmld->uDeviceID;\r
- return MMSYSERR_NOERROR;\r
-}\r
-\r
-/**************************************************************************\r
- * waveInMessage [WINMM.@]\r
- */\r
-UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,\r
- DWORD_PTR dwParam1, DWORD_PTR dwParam2)\r
-{\r
- LPWINE_MLD wmld;\r
-\r
- TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);\r
-\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {\r
- if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {\r
- return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);\r
- }\r
- return MMSYSERR_INVALHANDLE;\r
- }\r
-\r
- /* from M$ KB */\r
- if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))\r
- return MMSYSERR_INVALPARAM;\r
-\r
-\r
- return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);\r
-}\r
-\r
-struct mm_starter\r
-{\r
- LPTASKCALLBACK cb;\r
- DWORD client;\r
- HANDLE event;\r
-};\r
-\r
-static DWORD WINAPI mmTaskRun(void* pmt)\r
-{\r
- struct mm_starter mms;\r
-\r
- memcpy(&mms, pmt, sizeof(struct mm_starter));\r
- HeapFree(GetProcessHeap(), 0, pmt);\r
- mms.cb(mms.client);\r
- if (mms.event) SetEvent(mms.event);\r
- return 0;\r
-}\r
-\r
-/******************************************************************\r
- * mmTaskCreate (WINMM.@)\r
- */\r
-MMRESULT WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD client)\r
-{\r
- HANDLE hThread;\r
- HANDLE hEvent = 0;\r
- struct mm_starter *mms;\r
-\r
- mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));\r
- if (mms == NULL) return TASKERR_OUTOFMEMORY;\r
-\r
- mms->cb = cb;\r
- mms->client = client;\r
- if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);\r
- mms->event = hEvent;\r
-\r
- hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);\r
- if (!hThread) {\r
- HeapFree(GetProcessHeap(), 0, mms);\r
- if (hEvent) CloseHandle(hEvent);\r
- return TASKERR_OUTOFMEMORY;\r
- }\r
- SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);\r
- if (ph) *ph = hEvent;\r
- CloseHandle(hThread);\r
- return 0;\r
-}\r
-\r
-/******************************************************************\r
- * mmTaskBlock (WINMM.@)\r
- */\r
-void WINAPI mmTaskBlock(HANDLE tid)\r
-{\r
- MSG msg;\r
-\r
- do\r
- {\r
- GetMessageA(&msg, 0, 0, 0);\r
- if (msg.hwnd) DispatchMessageA(&msg);\r
- } while (msg.message != WM_USER);\r
-}\r
-\r
-/******************************************************************\r
- * mmTaskSignal (WINMM.@)\r
- */\r
-BOOL WINAPI mmTaskSignal(HANDLE tid)\r
-{\r
- return PostThreadMessageW((DWORD)tid, WM_USER, 0, 0);\r
-}\r
-\r
-/******************************************************************\r
- * mmTaskYield (WINMM.@)\r
- */\r
-void WINAPI mmTaskYield(void) {}\r
-\r
-/******************************************************************\r
- * mmGetCurrentTask (WINMM.@)\r
- */\r
-HANDLE WINAPI mmGetCurrentTask(void)\r
-{\r
- return (HANDLE)GetCurrentThreadId();\r
-}\r
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ * WINMM functions
+ *
+ * Copyright 1993 Martin Ayotte
+ * 1998-2002 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Eric POUECH :
+ * 98/9 added Win32 MCI support
+ * 99/4 added midiStream support
+ * 99/9 added support for loadable low level drivers
+ */
+
+/* TODO
+ * + it seems that some programs check what's installed in
+ * registry against the value returned by drivers. Wine is
+ * currently broken regarding this point.
+ * + check thread-safeness for MMSYSTEM and WINMM entry points
+ * + unicode entry points are badly supported (would require
+ * moving 32 bit drivers as Unicode as they are supposed to be)
+ * + allow joystick and timer external calls as we do for wave,
+ * midi, mixer and aux
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "windef.h"
+#include "winbase.h"
+#include "mmsystem.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "winemm.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(winmm);
+
+void (WINAPI *pFnReleaseThunkLock)(DWORD*);
+void (WINAPI *pFnRestoreThunkLock)(DWORD);
+
+/* ========================================================================
+ * G L O B A L S E T T I N G S
+ * ========================================================================*/
+
+WINE_MM_IDATA WINMM_IData;
+
+/**************************************************************************
+ * WINMM_CreateIData [internal]
+ */
+static BOOL WINMM_CreateIData(HINSTANCE hInstDLL)
+{
+ memset( &WINMM_IData, 0, sizeof WINMM_IData );
+
+ WINMM_IData.hWinMM32Instance = hInstDLL;
+ InitializeCriticalSection(&WINMM_IData.cs);
+/* FIXME crashes in ReactOS
+ WINMM_IData.cs.DebugInfo->Spare[1] = (DWORD)"WINMM_IData";
+*/
+ WINMM_IData.psStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ WINMM_IData.psLastEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+ TRACE("Initialized IData (%p)\n", &WINMM_IData);
+ return TRUE;
+}
+
+/**************************************************************************
+ * WINMM_DeleteIData [internal]
+ */
+static void WINMM_DeleteIData(void)
+{
+ TIME_MMTimeStop();
+
+ /* FIXME: should also free content and resources allocated
+ * inside WINMM_IData */
+ CloseHandle(WINMM_IData.psStopEvent);
+ CloseHandle(WINMM_IData.psLastEvent);
+ DeleteCriticalSection(&WINMM_IData.cs);
+}
+
+/******************************************************************
+ * WINMM_LoadMMSystem
+ *
+ */
+#ifndef __REACTOS__
+static HANDLE (WINAPI *pGetModuleHandle16)(LPCSTR);
+static DWORD (WINAPI *pLoadLibrary16)(LPCSTR);
+#endif
+
+BOOL WINMM_CheckForMMSystem(void)
+{
+ /* 0 is not checked yet, -1 is not present, 1 is present */
+ static int loaded /* = 0 */;
+
+ if (loaded == 0)
+ {
+ HANDLE h = GetModuleHandleA("kernel32");
+ loaded = -1;
+ if (h)
+ {
+#ifndef __REACTOS__
+ pGetModuleHandle16 = (void*)GetProcAddress(h, "GetModuleHandle16");
+ pLoadLibrary16 = (void*)GetProcAddress(h, "LoadLibrary16");
+ if (pGetModuleHandle16 && pLoadLibrary16 &&
+ (pGetModuleHandle16("MMSYSTEM.DLL") || pLoadLibrary16("MMSYSTEM.DLL")))
+#endif /* __REACTOS__ */
+ loaded = 1;
+ }
+ }
+ return loaded > 0;
+}
+
+/******************************************************************
+ * WINMM_ErrorToString
+ */
+const char* WINMM_ErrorToString(MMRESULT error)
+{
+#define ERR_TO_STR(dev) case dev: return #dev
+ static char unknown[32];
+ switch (error) {
+ ERR_TO_STR(MMSYSERR_NOERROR);
+ ERR_TO_STR(MMSYSERR_ERROR);
+ ERR_TO_STR(MMSYSERR_BADDEVICEID);
+ ERR_TO_STR(MMSYSERR_NOTENABLED);
+ ERR_TO_STR(MMSYSERR_ALLOCATED);
+ ERR_TO_STR(MMSYSERR_INVALHANDLE);
+ ERR_TO_STR(MMSYSERR_NODRIVER);
+ ERR_TO_STR(MMSYSERR_NOMEM);
+ ERR_TO_STR(MMSYSERR_NOTSUPPORTED);
+ ERR_TO_STR(MMSYSERR_BADERRNUM);
+ ERR_TO_STR(MMSYSERR_INVALFLAG);
+ ERR_TO_STR(MMSYSERR_INVALPARAM);
+ ERR_TO_STR(MMSYSERR_HANDLEBUSY);
+ ERR_TO_STR(MMSYSERR_INVALIDALIAS);
+ ERR_TO_STR(MMSYSERR_BADDB);
+ ERR_TO_STR(MMSYSERR_KEYNOTFOUND);
+ ERR_TO_STR(MMSYSERR_READERROR);
+ ERR_TO_STR(MMSYSERR_WRITEERROR);
+ ERR_TO_STR(MMSYSERR_DELETEERROR);
+ ERR_TO_STR(MMSYSERR_VALNOTFOUND);
+ ERR_TO_STR(MMSYSERR_NODRIVERCB);
+ ERR_TO_STR(WAVERR_BADFORMAT);
+ ERR_TO_STR(WAVERR_STILLPLAYING);
+ ERR_TO_STR(WAVERR_UNPREPARED);
+ ERR_TO_STR(WAVERR_SYNC);
+ }
+ sprintf(unknown, "Unknown(0x%08x)", error);
+ return unknown;
+#undef ERR_TO_STR
+}
+
+/**************************************************************************
+ * DllMain (WINMM.init)
+ *
+ * WINMM DLL entry point
+ *
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID fImpLoad)
+{
+ TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, fImpLoad);
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(hInstDLL);
+
+ if (!WINMM_CreateIData(hInstDLL))
+ return FALSE;
+ if (!MMDRV_Init()) {
+ WINMM_DeleteIData();
+ return FALSE;
+ }
+ break;
+ case DLL_PROCESS_DETACH:
+ /* close all opened MCI drivers */
+ MCI_SendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, MCI_WAIT, 0L, TRUE);
+ MMDRV_Exit();
+ /* now unload all remaining drivers... */
+ DRIVER_UnloadAll();
+
+ WINMM_DeleteIData();
+ break;
+ }
+ return TRUE;
+}
+
+/**************************************************************************
+ * Mixer devices. New to Win95
+ */
+
+/**************************************************************************
+ * find out the real mixer ID depending on hmix (depends on dwFlags)
+ */
+static UINT MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags, LPWINE_MIXER * lplpwm)
+{
+ LPWINE_MIXER lpwm = NULL;
+ UINT uRet = MMSYSERR_NOERROR;
+
+ switch (dwFlags & 0xF0000000ul) {
+ case MIXER_OBJECTF_MIXER:
+ lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);
+ break;
+ case MIXER_OBJECTF_HMIXER:
+ lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);
+ break;
+ case MIXER_OBJECTF_WAVEOUT:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_HWAVEOUT:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_WAVEIN:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, TRUE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_HWAVEIN:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, FALSE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_MIDIOUT:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_HMIDIOUT:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_MIDIIN:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, TRUE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_HMIDIIN:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, FALSE, MMDRV_MIXER);
+ break;
+ case MIXER_OBJECTF_AUX:
+ lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX, TRUE, MMDRV_MIXER);
+ break;
+ default:
+ WARN("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);
+ lpwm = 0;
+ uRet = MMSYSERR_INVALFLAG;
+ break;
+ }
+ *lplpwm = lpwm;
+ if (lpwm == 0 && uRet == MMSYSERR_NOERROR)
+ uRet = MMSYSERR_INVALPARAM;
+ return uRet;
+}
+
+/**************************************************************************
+ * mixerGetNumDevs [WINMM.@]
+ */
+UINT WINAPI mixerGetNumDevs(void)
+{
+ return MMDRV_GetNum(MMDRV_MIXER);
+}
+
+/**************************************************************************
+ * mixerGetDevCapsA [WINMM.@]
+ */
+UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
+{
+ MIXERCAPSW micW;
+ UINT ret;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
+
+ if (ret == MMSYSERR_NOERROR) {
+ MIXERCAPSA micA;
+ micA.wMid = micW.wMid;
+ micA.wPid = micW.wPid;
+ micA.vDriverVersion = micW.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
+ sizeof(micA.szPname), NULL, NULL );
+ micA.fdwSupport = micW.fdwSupport;
+ micA.cDestinations = micW.cDestinations;
+ memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * mixerGetDevCapsW [WINMM.@]
+ */
+UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIXER, TRUE)) == NULL)
+ return MMSYSERR_BADDEVICEID;
+
+ return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
+}
+
+UINT MIXER_Open(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD fdwOpen, BOOL bFrom32)
+{
+ HANDLE hMix;
+ LPWINE_MLD wmld;
+ DWORD dwRet = 0;
+ MIXEROPENDESC mod;
+
+ TRACE("(%p, %d, %08lx, %08lx, %08lx)\n",
+ lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
+
+ wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,
+ &dwCallback, &dwInstance, bFrom32);
+
+ wmld->uDeviceID = uDeviceID;
+ mod.hmx = (HMIXEROBJ)hMix;
+ mod.dwCallback = dwCallback;
+ mod.dwInstance = dwInstance;
+
+ dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD)&mod, fdwOpen);
+
+ if (dwRet != MMSYSERR_NOERROR) {
+ MMDRV_Free(hMix, wmld);
+ hMix = 0;
+ }
+ if (lphMix) *lphMix = hMix;
+ TRACE("=> %ld hMixer=%p\n", dwRet, hMix);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * mixerOpen [WINMM.@]
+ */
+UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD fdwOpen)
+{
+ return MIXER_Open(lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen, TRUE);
+}
+
+/**************************************************************************
+ * mixerClose [WINMM.@]
+ */
+UINT WINAPI mixerClose(HMIXER hMix)
+{
+ LPWINE_MLD wmld;
+ DWORD dwRet;
+
+ TRACE("(%p)\n", hMix);
+
+ if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;
+
+ dwRet = MMDRV_Close(wmld, MXDM_CLOSE);
+ MMDRV_Free(hMix, wmld);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * mixerGetID [WINMM.@]
+ */
+UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
+{
+ LPWINE_MIXER lpwm;
+ UINT uRet = MMSYSERR_NOERROR;
+
+ TRACE("(%p %p %08lx)\n", hmix, lpid, fdwID);
+
+ if ((uRet = MIXER_GetDev(hmix, fdwID, &lpwm)) != MMSYSERR_NOERROR)
+ return uRet;
+
+ if (lpid)
+ *lpid = lpwm->mld.uDeviceID;
+
+ return uRet;
+}
+
+/**************************************************************************
+ * mixerGetControlDetailsW [WINMM.@]
+ */
+UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
+ DWORD fdwDetails)
+{
+ LPWINE_MIXER lpwm;
+ UINT uRet = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmcdW, fdwDetails);
+
+ if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)
+ return uRet;
+
+ if (lpmcdW == NULL || lpmcdW->cbStruct != sizeof(*lpmcdW))
+ return MMSYSERR_INVALPARAM;
+
+ return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD_PTR)lpmcdW,
+ fdwDetails, TRUE);
+}
+
+/**************************************************************************
+ * mixerGetControlDetailsA [WINMM.@]
+ */
+UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
+ DWORD fdwDetails)
+{
+ DWORD ret = MMSYSERR_NOTENABLED;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmcdA, fdwDetails);
+
+ if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
+ return MMSYSERR_INVALPARAM;
+
+ switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
+ case MIXER_GETCONTROLDETAILSF_VALUE:
+ /* can savely use A structure as it is, no string inside */
+ ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
+ break;
+ case MIXER_GETCONTROLDETAILSF_LISTTEXT:
+ {
+ MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = (MIXERCONTROLDETAILS_LISTTEXTA *)lpmcdA->paDetails;
+ MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
+ int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
+ unsigned int i;
+
+ if (lpmcdA->u.cMultipleItems != 0) {
+ size *= lpmcdA->u.cMultipleItems;
+ }
+ pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
+ lpmcdA->paDetails = pDetailsW;
+ lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
+ /* set up lpmcd->paDetails */
+ ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
+ /* copy from lpmcd->paDetails back to paDetailsW; */
+ if (ret == MMSYSERR_NOERROR) {
+ for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
+ pDetailsA->dwParam1 = pDetailsW->dwParam1;
+ pDetailsA->dwParam2 = pDetailsW->dwParam2;
+ WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
+ pDetailsA->szName,
+ sizeof(pDetailsA->szName), NULL, NULL );
+ pDetailsA++;
+ pDetailsW++;
+ }
+ pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
+ pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
+ }
+ HeapFree(GetProcessHeap(), 0, pDetailsW);
+ lpmcdA->paDetails = pDetailsA;
+ lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
+ }
+ break;
+ default:
+ ERR("Unsupported fdwDetails=0x%08lx\n", fdwDetails);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ * mixerGetLineControlsA [WINMM.@]
+ */
+UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
+ DWORD fdwControls)
+{
+ MIXERLINECONTROLSW mlcW;
+ DWORD ret;
+ unsigned int i;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmlcA, fdwControls);
+
+ if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
+ lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
+ return MMSYSERR_INVALPARAM;
+
+ mlcW.cbStruct = sizeof(mlcW);
+ mlcW.dwLineID = lpmlcA->dwLineID;
+ mlcW.u.dwControlID = lpmlcA->u.dwControlID;
+ mlcW.u.dwControlType = lpmlcA->u.dwControlType;
+
+ /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
+ the control count is assumed to be 1 - This is relied upon by a game,
+ "Dynomite Deluze" */
+ if (MIXER_GETLINECONTROLSF_ONEBYTYPE == fdwControls) {
+ mlcW.cControls = 1;
+ } else {
+ mlcW.cControls = lpmlcA->cControls;
+ }
+ mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
+ mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
+ mlcW.cControls * mlcW.cbmxctrl);
+
+ ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
+
+ if (ret == MMSYSERR_NOERROR) {
+ lpmlcA->dwLineID = mlcW.dwLineID;
+ lpmlcA->u.dwControlID = mlcW.u.dwControlID;
+ lpmlcA->u.dwControlType = mlcW.u.dwControlType;
+ lpmlcA->cControls = mlcW.cControls;
+
+ for (i = 0; i < mlcW.cControls; i++) {
+ lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
+ lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
+ lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
+ lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
+ lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
+ WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
+ lpmlcA->pamxctrl[i].szShortName,
+ sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
+ WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
+ lpmlcA->pamxctrl[i].szName,
+ sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
+ /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
+ * sizeof(mlcW.pamxctrl[i].Bounds) */
+ memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
+ sizeof(mlcW.pamxctrl[i].Bounds));
+ /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
+ * sizeof(mlcW.pamxctrl[i].Metrics) */
+ memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
+ sizeof(mlcW.pamxctrl[i].Metrics));
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
+
+ return ret;
+}
+
+/**************************************************************************
+ * mixerGetLineControlsW [WINMM.@]
+ */
+UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
+ DWORD fdwControls)
+{
+ LPWINE_MIXER lpwm;
+ UINT uRet = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmlcW, fdwControls);
+
+ if ((uRet = MIXER_GetDev(hmix, fdwControls, &lpwm)) != MMSYSERR_NOERROR)
+ return uRet;
+
+ if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW))
+ return MMSYSERR_INVALPARAM;
+
+ return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD_PTR)lpmlcW,
+ fdwControls, TRUE);
+}
+
+/**************************************************************************
+ * mixerGetLineInfoW [WINMM.@]
+ */
+UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
+{
+ LPWINE_MIXER lpwm;
+ UINT uRet = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmliW, fdwInfo);
+
+ if ((uRet = MIXER_GetDev(hmix, fdwInfo, &lpwm)) != MMSYSERR_NOERROR)
+ return uRet;
+
+ return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD_PTR)lpmliW,
+ fdwInfo, TRUE);
+}
+
+/**************************************************************************
+ * mixerGetLineInfoA [WINMM.@]
+ */
+UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
+ DWORD fdwInfo)
+{
+ MIXERLINEW mliW;
+ UINT ret;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmliA, fdwInfo);
+
+ if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
+ return MMSYSERR_INVALPARAM;
+
+ mliW.cbStruct = sizeof(mliW);
+ switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
+ case MIXER_GETLINEINFOF_COMPONENTTYPE:
+ mliW.dwComponentType = lpmliA->dwComponentType;
+ break;
+ case MIXER_GETLINEINFOF_DESTINATION:
+ mliW.dwDestination = lpmliA->dwDestination;
+ break;
+ case MIXER_GETLINEINFOF_LINEID:
+ mliW.dwLineID = lpmliA->dwLineID;
+ break;
+ case MIXER_GETLINEINFOF_SOURCE:
+ mliW.dwDestination = lpmliA->dwDestination;
+ mliW.dwSource = lpmliA->dwSource;
+ break;
+ case MIXER_GETLINEINFOF_TARGETTYPE:
+ mliW.Target.dwType = lpmliA->Target.dwType;
+ mliW.Target.wMid = lpmliA->Target.wMid;
+ mliW.Target.wPid = lpmliA->Target.wPid;
+ mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
+ MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname));
+ break;
+ default:
+ WARN("Unsupported fdwControls=0x%08lx\n", fdwInfo);
+ return MMSYSERR_INVALFLAG;
+ }
+
+ ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
+
+ lpmliA->dwDestination = mliW.dwDestination;
+ lpmliA->dwSource = mliW.dwSource;
+ lpmliA->dwLineID = mliW.dwLineID;
+ lpmliA->fdwLine = mliW.fdwLine;
+ lpmliA->dwUser = mliW.dwUser;
+ lpmliA->dwComponentType = mliW.dwComponentType;
+ lpmliA->cChannels = mliW.cChannels;
+ lpmliA->cConnections = mliW.cConnections;
+ lpmliA->cControls = mliW.cControls;
+ WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
+ sizeof(lpmliA->szShortName), NULL, NULL);
+ WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
+ sizeof(lpmliA->szName), NULL, NULL );
+ lpmliA->Target.dwType = mliW.Target.dwType;
+ lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
+ lpmliA->Target.wMid = mliW.Target.wMid;
+ lpmliA->Target.wPid = mliW.Target.wPid;
+ lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
+ sizeof(lpmliA->Target.szPname), NULL, NULL );
+
+ return ret;
+}
+
+/**************************************************************************
+ * mixerSetControlDetails [WINMM.@]
+ */
+UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
+ DWORD fdwDetails)
+{
+ LPWINE_MIXER lpwm;
+ UINT uRet = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %08lx)\n", hmix, lpmcd, fdwDetails);
+
+ if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)
+ return uRet;
+
+ return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD_PTR)lpmcd,
+ fdwDetails, TRUE);
+}
+
+/**************************************************************************
+ * mixerMessage [WINMM.@]
+ */
+DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%04lx, %d, %08lx, %08lx): semi-stub?\n",
+ (DWORD)hmix, uMsg, dwParam1, dwParam2);
+
+ if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2, TRUE);
+}
+
+/**************************************************************************
+ * auxGetNumDevs [WINMM.@]
+ */
+UINT WINAPI auxGetNumDevs(void)
+{
+ return MMDRV_GetNum(MMDRV_AUX);
+}
+
+/**************************************************************************
+ * auxGetDevCapsW [WINMM.@]
+ */
+UINT WINAPI auxGetDevCapsW(UINT_PTR uDeviceID, LPAUXCAPSW lpCaps, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%04X, %p, %d) !\n", uDeviceID, lpCaps, uSize);
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, AUXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
+}
+
+/**************************************************************************
+ * auxGetDevCapsA [WINMM.@]
+ */
+UINT WINAPI auxGetDevCapsA(UINT_PTR uDeviceID, LPAUXCAPSA lpCaps, UINT uSize)
+{
+ AUXCAPSW acW;
+ UINT ret;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ ret = auxGetDevCapsW(uDeviceID, &acW, sizeof(acW));
+
+ if (ret == MMSYSERR_NOERROR) {
+ AUXCAPSA acA;
+ acA.wMid = acW.wMid;
+ acA.wPid = acW.wPid;
+ acA.vDriverVersion = acW.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, acW.szPname, -1, acA.szPname,
+ sizeof(acA.szPname), NULL, NULL );
+ acA.wTechnology = acW.wTechnology;
+ acA.dwSupport = acW.dwSupport;
+ memcpy(lpCaps, &acA, min(uSize, sizeof(acA)));
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * auxGetVolume [WINMM.@]
+ */
+UINT WINAPI auxGetVolume(UINT uDeviceID, DWORD* lpdwVolume)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%04X, %p) !\n", uDeviceID, lpdwVolume);
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);
+}
+
+/**************************************************************************
+ * auxSetVolume [WINMM.@]
+ */
+UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%04X, %lu) !\n", uDeviceID, dwVolume);
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L, TRUE);
+}
+
+/**************************************************************************
+ * auxOutMessage [WINMM.@]
+ */
+UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2)
+{
+ LPWINE_MLD wmld;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_AUX, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, uMessage, dw1, dw2, TRUE);
+}
+
+/**************************************************************************
+ * midiOutGetNumDevs [WINMM.@]
+ */
+UINT WINAPI midiOutGetNumDevs(void)
+{
+ return MMDRV_GetNum(MMDRV_MIDIOUT);
+}
+
+/**************************************************************************
+ * midiOutGetDevCapsW [WINMM.@]
+ */
+UINT WINAPI midiOutGetDevCapsW(UINT_PTR uDeviceID, LPMIDIOUTCAPSW lpCaps,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%u, %p, %u);\n", uDeviceID, lpCaps, uSize);
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIOUT, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiOutGetDevCapsA [WINMM.@]
+ */
+UINT WINAPI midiOutGetDevCapsA(UINT_PTR uDeviceID, LPMIDIOUTCAPSA lpCaps,
+ UINT uSize)
+{
+ MIDIOUTCAPSW mocW;
+ UINT ret;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ ret = midiOutGetDevCapsW(uDeviceID, &mocW, sizeof(mocW));
+
+ if (ret == MMSYSERR_NOERROR) {
+ MIDIOUTCAPSA mocA;
+ mocA.wMid = mocW.wMid;
+ mocA.wPid = mocW.wPid;
+ mocA.vDriverVersion = mocW.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, mocW.szPname, -1, mocA.szPname,
+ sizeof(mocA.szPname), NULL, NULL );
+ mocA.wTechnology = mocW.wTechnology;
+ mocA.wVoices = mocW.wVoices;
+ mocA.wNotes = mocW.wNotes;
+ mocA.wChannelMask = mocW.wChannelMask;
+ mocA.dwSupport = mocW.dwSupport;
+ memcpy(lpCaps, &mocA, min(uSize, sizeof(mocA)));
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * midiOutGetErrorTextA [WINMM.@]
+ * midiInGetErrorTextA [WINMM.@]
+ */
+UINT WINAPI midiOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
+{
+ UINT ret;
+
+ if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
+ else if (uSize == 0) ret = MMSYSERR_NOERROR;
+ else
+ {
+ LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
+ if (!xstr) ret = MMSYSERR_NOMEM;
+ else
+ {
+ ret = midiOutGetErrorTextW(uError, xstr, uSize);
+ if (ret == MMSYSERR_NOERROR)
+ WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
+ HeapFree(GetProcessHeap(), 0, xstr);
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * midiOutGetErrorTextW [WINMM.@]
+ * midiInGetErrorTextW [WINMM.@]
+ */
+UINT WINAPI midiOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
+{
+ UINT ret = MMSYSERR_BADERRNUM;
+
+ if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
+ else if (uSize == 0) ret = MMSYSERR_NOERROR;
+ else if (
+ /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
+ * a warning for the test was always true */
+ (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
+ (uError >= MIDIERR_BASE && uError <= MIDIERR_LASTERROR)) {
+ if (LoadStringW(WINMM_IData.hWinMM32Instance,
+ uError, lpText, uSize) > 0) {
+ ret = MMSYSERR_NOERROR;
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * MIDI_OutAlloc [internal]
+ */
+static LPWINE_MIDI MIDI_OutAlloc(HMIDIOUT* lphMidiOut, LPDWORD lpdwCallback,
+ LPDWORD lpdwInstance, LPDWORD lpdwFlags,
+ DWORD cIDs, MIDIOPENSTRMID* lpIDs, BOOL bFrom32)
+{
+ HANDLE hMidiOut;
+ LPWINE_MIDI lpwm;
+ UINT size;
+
+ size = sizeof(WINE_MIDI) + (cIDs ? (cIDs-1) : 0) * sizeof(MIDIOPENSTRMID);
+
+ lpwm = (LPWINE_MIDI)MMDRV_Alloc(size, MMDRV_MIDIOUT, &hMidiOut, lpdwFlags,
+ lpdwCallback, lpdwInstance, bFrom32);
+
+ if (lphMidiOut != NULL)
+ *lphMidiOut = hMidiOut;
+
+ if (lpwm) {
+ lpwm->mod.hMidi = (HMIDI) hMidiOut;
+ lpwm->mod.dwCallback = *lpdwCallback;
+ lpwm->mod.dwInstance = *lpdwInstance;
+ lpwm->mod.dnDevNode = 0;
+ lpwm->mod.cIds = cIDs;
+ if (cIDs)
+ memcpy(&(lpwm->mod.rgIds), lpIDs, cIDs * sizeof(MIDIOPENSTRMID));
+ }
+ return lpwm;
+}
+
+UINT MIDI_OutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
+{
+ HMIDIOUT hMidiOut;
+ LPWINE_MIDI lpwm;
+ UINT dwRet = 0;
+
+ TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
+ lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags);
+
+ if (lphMidiOut != NULL) *lphMidiOut = 0;
+
+ lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags,
+ 0, NULL, bFrom32);
+
+ if (lpwm == NULL)
+ return MMSYSERR_NOMEM;
+
+ lpwm->mld.uDeviceID = uDeviceID;
+
+ dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD)&lpwm->mod, dwFlags);
+
+ if (dwRet != MMSYSERR_NOERROR) {
+ MMDRV_Free(hMidiOut, (LPWINE_MLD)lpwm);
+ hMidiOut = 0;
+ }
+
+ if (lphMidiOut) *lphMidiOut = hMidiOut;
+ TRACE("=> %d hMidi=%p\n", dwRet, hMidiOut);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * midiOutOpen [WINMM.@]
+ */
+UINT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID,
+ DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
+{
+ return MIDI_OutOpen(lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
+}
+
+/**************************************************************************
+ * midiOutClose [WINMM.@]
+ */
+UINT WINAPI midiOutClose(HMIDIOUT hMidiOut)
+{
+ LPWINE_MLD wmld;
+ DWORD dwRet;
+
+ TRACE("(%p)\n", hMidiOut);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ dwRet = MMDRV_Close(wmld, MODM_CLOSE);
+ MMDRV_Free(hMidiOut, wmld);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * midiOutPrepareHeader [WINMM.@]
+ */
+UINT WINAPI midiOutPrepareHeader(HMIDIOUT hMidiOut,
+ MIDIHDR* lpMidiOutHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
+
+ if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_PREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiOutUnprepareHeader [WINMM.@]
+ */
+UINT WINAPI midiOutUnprepareHeader(HMIDIOUT hMidiOut,
+ MIDIHDR* lpMidiOutHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
+
+ if (lpMidiOutHdr == NULL || uSize < sizeof (MIDIHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if (!(lpMidiOutHdr->dwFlags & MHDR_PREPARED)) {
+ return MMSYSERR_NOERROR;
+ }
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_UNPREPARE, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiOutShortMsg [WINMM.@]
+ */
+UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lX)\n", hMidiOut, dwMsg);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiOutLongMsg [WINMM.@]
+ */
+UINT WINAPI midiOutLongMsg(HMIDIOUT hMidiOut,
+ MIDIHDR* lpMidiOutHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %d)\n", hMidiOut, lpMidiOutHdr, uSize);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_LONGDATA, (DWORD_PTR)lpMidiOutHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiOutReset [WINMM.@]
+ */
+UINT WINAPI midiOutReset(HMIDIOUT hMidiOut)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p)\n", hMidiOut);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_RESET, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiOutGetVolume [WINMM.@]
+ */
+UINT WINAPI midiOutGetVolume(HMIDIOUT hMidiOut, DWORD* lpdwVolume)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p);\n", hMidiOut, lpdwVolume);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiOutSetVolume [WINMM.@]
+ */
+UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %ld);\n", hMidiOut, dwVolume);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiOutCachePatches [WINMM.@]
+ */
+UINT WINAPI midiOutCachePatches(HMIDIOUT hMidiOut, UINT uBank,
+ WORD* lpwPatchArray, UINT uFlags)
+{
+ /* not really necessary to support this */
+ FIXME("not supported yet\n");
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+/**************************************************************************
+ * midiOutCacheDrumPatches [WINMM.@]
+ */
+UINT WINAPI midiOutCacheDrumPatches(HMIDIOUT hMidiOut, UINT uPatch,
+ WORD* lpwKeyArray, UINT uFlags)
+{
+ FIXME("not supported yet\n");
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+/**************************************************************************
+ * midiOutGetID [WINMM.@]
+ */
+UINT WINAPI midiOutGetID(HMIDIOUT hMidiOut, UINT* lpuDeviceID)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p)\n", hMidiOut, lpuDeviceID);
+
+ if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ *lpuDeviceID = wmld->uDeviceID;
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * midiOutMessage [WINMM.@]
+ */
+UINT WINAPI midiOutMessage(HMIDIOUT hMidiOut, UINT uMessage,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiOut, uMessage, dwParam1, dwParam2);
+
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) {
+ /* HACK... */
+ if (uMessage == 0x0001) {
+ *(LPDWORD)dwParam1 = 1;
+ return 0;
+ }
+ if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) != NULL) {
+ return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
+ }
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ switch (uMessage) {
+ case MODM_OPEN:
+ case MODM_CLOSE:
+ FIXME("can't handle OPEN or CLOSE message!\n");
+ return MMSYSERR_NOTSUPPORTED;
+ }
+ return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
+}
+
+/**************************************************************************
+ * midiInGetNumDevs [WINMM.@]
+ */
+UINT WINAPI midiInGetNumDevs(void)
+{
+ return MMDRV_GetNum(MMDRV_MIDIIN);
+}
+
+/**************************************************************************
+ * midiInGetDevCapsW [WINMM.@]
+ */
+UINT WINAPI midiInGetDevCapsW(UINT_PTR uDeviceID, LPMIDIINCAPSW lpCaps, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%d, %p, %d);\n", uDeviceID, lpCaps, uSize);
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIDIIN, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiInGetDevCapsA [WINMM.@]
+ */
+UINT WINAPI midiInGetDevCapsA(UINT_PTR uDeviceID, LPMIDIINCAPSA lpCaps, UINT uSize)
+{
+ MIDIINCAPSW micW;
+ UINT ret;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ ret = midiInGetDevCapsW(uDeviceID, &micW, sizeof(micW));
+
+ if (ret == MMSYSERR_NOERROR) {
+ MIDIINCAPSA micA;
+ micA.wMid = micW.wMid;
+ micA.wPid = micW.wPid;
+ micA.vDriverVersion = micW.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
+ sizeof(micA.szPname), NULL, NULL );
+ micA.dwSupport = micW.dwSupport;
+ memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
+ }
+ return ret;
+}
+
+UINT MIDI_InOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
+{
+ HANDLE hMidiIn;
+ LPWINE_MIDI lpwm;
+ DWORD dwRet = 0;
+
+ TRACE("(%p, %d, %08lX, %08lX, %08lX);\n",
+ lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags);
+
+ if (lphMidiIn != NULL) *lphMidiIn = 0;
+
+ lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn,
+ &dwFlags, &dwCallback, &dwInstance, bFrom32);
+
+ if (lpwm == NULL)
+ return MMSYSERR_NOMEM;
+
+ lpwm->mod.hMidi = (HMIDI) hMidiIn;
+ lpwm->mod.dwCallback = dwCallback;
+ lpwm->mod.dwInstance = dwInstance;
+
+ lpwm->mld.uDeviceID = uDeviceID;
+ dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD)&lpwm->mod, dwFlags);
+
+ if (dwRet != MMSYSERR_NOERROR) {
+ MMDRV_Free(hMidiIn, &lpwm->mld);
+ hMidiIn = 0;
+ }
+ if (lphMidiIn != NULL) *lphMidiIn = hMidiIn;
+ TRACE("=> %ld hMidi=%p\n", dwRet, hMidiIn);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * midiInOpen [WINMM.@]
+ */
+UINT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID,
+ DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags)
+{
+ return MIDI_InOpen(lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags, TRUE);
+}
+
+/**************************************************************************
+ * midiInClose [WINMM.@]
+ */
+UINT WINAPI midiInClose(HMIDIIN hMidiIn)
+{
+ LPWINE_MLD wmld;
+ DWORD dwRet;
+
+ TRACE("(%p)\n", hMidiIn);
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ dwRet = MMDRV_Close(wmld, MIDM_CLOSE);
+ MMDRV_Free(hMidiIn, wmld);
+ return dwRet;
+}
+
+/**************************************************************************
+ * midiInPrepareHeader [WINMM.@]
+ */
+UINT WINAPI midiInPrepareHeader(HMIDIIN hMidiIn,
+ MIDIHDR* lpMidiInHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
+
+ if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_PREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiInUnprepareHeader [WINMM.@]
+ */
+UINT WINAPI midiInUnprepareHeader(HMIDIIN hMidiIn,
+ MIDIHDR* lpMidiInHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
+
+ if (lpMidiInHdr == NULL || uSize < sizeof (MIDIHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if (!(lpMidiInHdr->dwFlags & MHDR_PREPARED)) {
+ return MMSYSERR_NOERROR;
+ }
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_UNPREPARE, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiInAddBuffer [WINMM.@]
+ */
+UINT WINAPI midiInAddBuffer(HMIDIIN hMidiIn,
+ MIDIHDR* lpMidiInHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %d)\n", hMidiIn, lpMidiInHdr, uSize);
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_ADDBUFFER, (DWORD_PTR)lpMidiInHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * midiInStart [WINMM.@]
+ */
+UINT WINAPI midiInStart(HMIDIIN hMidiIn)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p)\n", hMidiIn);
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_START, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiInStop [WINMM.@]
+ */
+UINT WINAPI midiInStop(HMIDIIN hMidiIn)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p)\n", hMidiIn);
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_STOP, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiInReset [WINMM.@]
+ */
+UINT WINAPI midiInReset(HMIDIIN hMidiIn)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p)\n", hMidiIn);
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, MIDM_RESET, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * midiInGetID [WINMM.@]
+ */
+UINT WINAPI midiInGetID(HMIDIIN hMidiIn, UINT* lpuDeviceID)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p)\n", hMidiIn, lpuDeviceID);
+
+ if (lpuDeviceID == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ *lpuDeviceID = wmld->uDeviceID;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * midiInMessage [WINMM.@]
+ */
+UINT WINAPI midiInMessage(HMIDIIN hMidiIn, UINT uMessage,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %04X, %08lX, %08lX)\n", hMidiIn, uMessage, dwParam1, dwParam2);
+
+ if ((wmld = MMDRV_Get(hMidiIn, MMDRV_MIDIIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ switch (uMessage) {
+ case MIDM_OPEN:
+ case MIDM_CLOSE:
+ FIXME("can't handle OPEN or CLOSE message!\n");
+ return MMSYSERR_NOTSUPPORTED;
+ }
+ return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
+}
+
+typedef struct WINE_MIDIStream {
+ HMIDIOUT hDevice;
+ HANDLE hThread;
+ DWORD dwThreadID;
+ DWORD dwTempo;
+ DWORD dwTimeDiv;
+ DWORD dwPositionMS;
+ DWORD dwPulses;
+ DWORD dwStartTicks;
+ WORD wFlags;
+ HANDLE hEvent;
+ LPMIDIHDR lpMidiHdr;
+} WINE_MIDIStream;
+
+#define WINE_MSM_HEADER (WM_USER+0)
+#define WINE_MSM_STOP (WM_USER+1)
+
+/**************************************************************************
+ * MMSYSTEM_GetMidiStream [internal]
+ */
+static BOOL MMSYSTEM_GetMidiStream(HMIDISTRM hMidiStrm, WINE_MIDIStream** lpMidiStrm, WINE_MIDI** lplpwm)
+{
+ WINE_MIDI* lpwm = (LPWINE_MIDI)MMDRV_Get(hMidiStrm, MMDRV_MIDIOUT, FALSE);
+
+ if (lplpwm)
+ *lplpwm = lpwm;
+
+ if (lpwm == NULL) {
+ return FALSE;
+ }
+
+ *lpMidiStrm = (WINE_MIDIStream*)lpwm->mod.rgIds.dwStreamID;
+
+ return *lpMidiStrm != NULL;
+}
+
+/**************************************************************************
+ * MMSYSTEM_MidiStream_Convert [internal]
+ */
+static DWORD MMSYSTEM_MidiStream_Convert(WINE_MIDIStream* lpMidiStrm, DWORD pulse)
+{
+ DWORD ret = 0;
+
+ if (lpMidiStrm->dwTimeDiv == 0) {
+ FIXME("Shouldn't happen. lpMidiStrm->dwTimeDiv = 0\n");
+ } else if (lpMidiStrm->dwTimeDiv > 0x8000) { /* SMPTE, unchecked FIXME? */
+ int nf = -(char)HIBYTE(lpMidiStrm->dwTimeDiv); /* number of frames */
+ int nsf = LOBYTE(lpMidiStrm->dwTimeDiv); /* number of sub-frames */
+ ret = (pulse * 1000) / (nf * nsf);
+ } else {
+ ret = (DWORD)((double)pulse * ((double)lpMidiStrm->dwTempo / 1000) /
+ (double)lpMidiStrm->dwTimeDiv);
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ * MMSYSTEM_MidiStream_MessageHandler [internal]
+ */
+static BOOL MMSYSTEM_MidiStream_MessageHandler(WINE_MIDIStream* lpMidiStrm, LPWINE_MIDI lpwm, LPMSG msg)
+{
+ LPMIDIHDR lpMidiHdr;
+ LPMIDIHDR* lpmh;
+ LPBYTE lpData;
+
+ switch (msg->message) {
+ case WM_QUIT:
+ SetEvent(lpMidiStrm->hEvent);
+ return FALSE;
+ case WINE_MSM_STOP:
+ TRACE("STOP\n");
+ /* this is not quite what MS doc says... */
+ midiOutReset(lpMidiStrm->hDevice);
+ /* empty list of already submitted buffers */
+ for (lpMidiHdr = lpMidiStrm->lpMidiHdr; lpMidiHdr; lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext) {
+ lpMidiHdr->dwFlags |= MHDR_DONE;
+ lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
+
+ DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
+ (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
+ lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
+ }
+ lpMidiStrm->lpMidiHdr = 0;
+ SetEvent(lpMidiStrm->hEvent);
+ break;
+ case WINE_MSM_HEADER:
+ /* sets initial tick count for first MIDIHDR */
+ if (!lpMidiStrm->dwStartTicks)
+ lpMidiStrm->dwStartTicks = GetTickCount();
+
+ /* FIXME(EPP): "I don't understand the content of the first MIDIHDR sent
+ * by native mcimidi, it doesn't look like a correct one".
+ * this trick allows to throw it away... but I don't like it.
+ * It looks like part of the file I'm trying to play and definitively looks
+ * like raw midi content
+ * I'd really like to understand why native mcimidi sends it. Perhaps a bad
+ * synchronization issue where native mcimidi is still processing raw MIDI
+ * content before generating MIDIEVENTs ?
+ *
+ * 4c 04 89 3b 00 81 7c 99 3b 43 00 99 23 5e 04 89 L..;..|.;C..#^..
+ * 3b 00 00 89 23 00 7c 99 3b 45 00 99 28 62 04 89 ;...#.|.;E..(b..
+ * 3b 00 00 89 28 00 81 7c 99 3b 4e 00 99 23 5e 04 ;...(..|.;N..#^.
+ * 89 3b 00 00 89 23 00 7c 99 3b 45 00 99 23 78 04 .;...#.|.;E..#x.
+ * 89 3b 00 00 89 23 00 81 7c 99 3b 48 00 99 23 5e .;...#..|.;H..#^
+ * 04 89 3b 00 00 89 23 00 7c 99 3b 4e 00 99 28 62 ..;...#.|.;N..(b
+ * 04 89 3b 00 00 89 28 00 81 7c 99 39 4c 00 99 23 ..;...(..|.9L..#
+ * 5e 04 89 39 00 00 89 23 00 82 7c 99 3b 4c 00 99 ^..9...#..|.;L..
+ * 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 00 99 #^..;...#.|.;H..
+ * 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b 3f 04 (b..;...(..|.;?.
+ * 89 3b 00 1c 99 23 5e 04 89 23 00 5c 99 3b 45 00 .;...#^..#.\.;E.
+ * 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 3b 46 .#x..;...#..|.;F
+ * 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b 48 ..#^..;...#.|.;H
+ * 00 99 28 62 04 89 3b 00 00 89 28 00 81 7c 99 3b ..(b..;...(..|.;
+ * 46 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 3b F..#^..;...#.|.;
+ * 48 00 99 23 78 04 89 3b 00 00 89 23 00 81 7c 99 H..#x..;...#..|.
+ * 3b 4c 00 99 23 5e 04 89 3b 00 00 89 23 00 7c 99 ;L..#^..;...#.|.
+ */
+ lpMidiHdr = (LPMIDIHDR)msg->lParam;
+ lpData = lpMidiHdr->lpData;
+ TRACE("Adding %s lpMidiHdr=%p [lpData=0x%08lx dwBufferLength=%lu/%lu dwFlags=0x%08lx size=%u]\n",
+ (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr,
+ (DWORD)lpMidiHdr, lpMidiHdr->dwBufferLength, lpMidiHdr->dwBytesRecorded,
+ lpMidiHdr->dwFlags, msg->wParam);
+#if 0
+ /* dumps content of lpMidiHdr->lpData
+ * FIXME: there should be a debug routine somewhere that already does this
+ * I hate spreading this type of shit all around the code
+ */
+ for (dwToGo = 0; dwToGo < lpMidiHdr->dwBufferLength; dwToGo += 16) {
+ DWORD i;
+ BYTE ch;
+
+ for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++)
+ printf("%02x ", lpData[dwToGo + i]);
+ for (; i < 16; i++)
+ printf(" ");
+ for (i = 0; i < min(16, lpMidiHdr->dwBufferLength - dwToGo); i++) {
+ ch = lpData[dwToGo + i];
+ printf("%c", (ch >= 0x20 && ch <= 0x7F) ? ch : '.');
+ }
+ printf("\n");
+ }
+#endif
+ if (((LPMIDIEVENT)lpData)->dwStreamID != 0 &&
+ ((LPMIDIEVENT)lpData)->dwStreamID != 0xFFFFFFFF &&
+ ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)lpMidiStrm) {
+ FIXME("Dropping bad %s lpMidiHdr (streamID=%08lx)\n",
+ (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular",
+ ((LPMIDIEVENT)lpData)->dwStreamID);
+ lpMidiHdr->dwFlags |= MHDR_DONE;
+ lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
+
+ DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
+ (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
+ lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
+ break;
+ }
+
+ for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = (LPMIDIHDR*)&((*lpmh)->lpNext));
+ *lpmh = lpMidiHdr;
+ lpMidiHdr = (LPMIDIHDR)msg->lParam;
+ lpMidiHdr->lpNext = 0;
+ lpMidiHdr->dwFlags |= MHDR_INQUEUE;
+ lpMidiHdr->dwFlags &= ~MHDR_DONE;
+ lpMidiHdr->dwOffset = 0;
+
+ break;
+ default:
+ FIXME("Unknown message %d\n", msg->message);
+ break;
+ }
+ return TRUE;
+}
+
+/**************************************************************************
+ * MMSYSTEM_MidiStream_Player [internal]
+ */
+static DWORD CALLBACK MMSYSTEM_MidiStream_Player(LPVOID pmt)
+{
+ WINE_MIDIStream* lpMidiStrm = pmt;
+ WINE_MIDI* lpwm;
+ MSG msg;
+ DWORD dwToGo;
+ DWORD dwCurrTC;
+ LPMIDIHDR lpMidiHdr;
+ LPMIDIEVENT me;
+ LPBYTE lpData = 0;
+
+ TRACE("(%p)!\n", lpMidiStrm);
+
+ if (!lpMidiStrm ||
+ (lpwm = (LPWINE_MIDI)MMDRV_Get(lpMidiStrm->hDevice, MMDRV_MIDIOUT, FALSE)) == NULL)
+ goto the_end;
+
+ /* force thread's queue creation */
+ /* Used to be InitThreadInput16(0, 5); */
+ /* but following works also with hack in midiStreamOpen */
+ PeekMessageA(&msg, 0, 0, 0, 0);
+
+ /* FIXME: this next line must be called before midiStreamOut or midiStreamRestart are called */
+ SetEvent(lpMidiStrm->hEvent);
+ TRACE("Ready to go 1\n");
+ /* thread is started in paused mode */
+ SuspendThread(lpMidiStrm->hThread);
+ TRACE("Ready to go 2\n");
+
+ lpMidiStrm->dwStartTicks = 0;
+ lpMidiStrm->dwPulses = 0;
+
+ lpMidiStrm->lpMidiHdr = 0;
+
+ for (;;) {
+ lpMidiHdr = lpMidiStrm->lpMidiHdr;
+ if (!lpMidiHdr) {
+ /* for first message, block until one arrives, then process all that are available */
+ GetMessageA(&msg, 0, 0, 0);
+ do {
+ if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
+ goto the_end;
+ } while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE));
+ lpData = 0;
+ continue;
+ }
+
+ if (!lpData)
+ lpData = lpMidiHdr->lpData;
+
+ me = (LPMIDIEVENT)(lpData + lpMidiHdr->dwOffset);
+
+ /* do we have to wait ? */
+ if (me->dwDeltaTime) {
+ lpMidiStrm->dwPositionMS += MMSYSTEM_MidiStream_Convert(lpMidiStrm, me->dwDeltaTime);
+ lpMidiStrm->dwPulses += me->dwDeltaTime;
+
+ dwToGo = lpMidiStrm->dwStartTicks + lpMidiStrm->dwPositionMS;
+
+ TRACE("%ld/%ld/%ld\n", dwToGo, GetTickCount(), me->dwDeltaTime);
+ while ((dwCurrTC = GetTickCount()) < dwToGo) {
+ if (MsgWaitForMultipleObjects(0, NULL, FALSE, dwToGo - dwCurrTC, QS_ALLINPUT) == WAIT_OBJECT_0) {
+ /* got a message, handle it */
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
+ if (!MMSYSTEM_MidiStream_MessageHandler(lpMidiStrm, lpwm, &msg))
+ goto the_end;
+ }
+ lpData = 0;
+ } else {
+ /* timeout, so me->dwDeltaTime is elapsed, can break the while loop */
+ break;
+ }
+ }
+ }
+ switch (MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK)) {
+ case MEVT_COMMENT:
+ FIXME("NIY: MEVT_COMMENT\n");
+ /* do nothing, skip bytes */
+ break;
+ case MEVT_LONGMSG:
+ FIXME("NIY: MEVT_LONGMSG, aka sending Sysex event\n");
+ break;
+ case MEVT_NOP:
+ break;
+ case MEVT_SHORTMSG:
+ midiOutShortMsg(lpMidiStrm->hDevice, MEVT_EVENTPARM(me->dwEvent));
+ break;
+ case MEVT_TEMPO:
+ lpMidiStrm->dwTempo = MEVT_EVENTPARM(me->dwEvent);
+ break;
+ case MEVT_VERSION:
+ break;
+ default:
+ FIXME("Unknown MEVT (0x%02x)\n", MEVT_EVENTTYPE(me->dwEvent & ~MEVT_F_CALLBACK));
+ break;
+ }
+ if (me->dwEvent & MEVT_F_CALLBACK) {
+ DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
+ (HDRVR)lpMidiStrm->hDevice, MM_MOM_POSITIONCB,
+ lpwm->mod.dwInstance, (LPARAM)lpMidiHdr, 0L);
+ }
+ lpMidiHdr->dwOffset += sizeof(MIDIEVENT) - sizeof(me->dwParms);
+ if (me->dwEvent & MEVT_F_LONG)
+ lpMidiHdr->dwOffset += (MEVT_EVENTPARM(me->dwEvent) + 3) & ~3;
+ if (lpMidiHdr->dwOffset >= lpMidiHdr->dwBufferLength) {
+ /* done with this header */
+ lpMidiHdr->dwFlags |= MHDR_DONE;
+ lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
+
+ lpMidiStrm->lpMidiHdr = (LPMIDIHDR)lpMidiHdr->lpNext;
+ DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags,
+ (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE,
+ lpwm->mod.dwInstance, (DWORD)lpMidiHdr, 0L);
+ lpData = 0;
+ }
+ }
+the_end:
+ TRACE("End of thread\n");
+ ExitThread(0);
+ return 0; /* for removing the warning, never executed */
+}
+
+/**************************************************************************
+ * MMSYSTEM_MidiStream_PostMessage [internal]
+ */
+static BOOL MMSYSTEM_MidiStream_PostMessage(WINE_MIDIStream* lpMidiStrm, WORD msg, DWORD pmt1, DWORD pmt2)
+{
+ if (PostThreadMessageA(lpMidiStrm->dwThreadID, msg, pmt1, pmt2)) {
+ DWORD count;
+
+ if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
+ WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
+ if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
+ } else {
+ WARN("bad PostThreadMessageA\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**************************************************************************
+ * midiStreamClose [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamClose(HMIDISTRM hMidiStrm)
+{
+ WINE_MIDIStream* lpMidiStrm;
+
+ TRACE("(%p)!\n", hMidiStrm);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL))
+ return MMSYSERR_INVALHANDLE;
+
+ midiStreamStop(hMidiStrm);
+ MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WM_QUIT, 0, 0);
+ HeapFree(GetProcessHeap(), 0, lpMidiStrm);
+ CloseHandle(lpMidiStrm->hEvent);
+
+ return midiOutClose((HMIDIOUT)hMidiStrm);
+}
+
+/**************************************************************************
+ * MMSYSTEM_MidiStream_Open [internal]
+ */
+MMRESULT MIDI_StreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi,
+ DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen,
+ BOOL bFrom32)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ MMRESULT ret;
+ MIDIOPENSTRMID mosm;
+ LPWINE_MIDI lpwm;
+ HMIDIOUT hMidiOut;
+
+ TRACE("(%p, %p, %ld, 0x%08lx, 0x%08lx, 0x%08lx)!\n",
+ lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen);
+
+ if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL)
+ return MMSYSERR_INVALPARAM;
+
+ lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream));
+ if (!lpMidiStrm)
+ return MMSYSERR_NOMEM;
+
+ lpMidiStrm->dwTempo = 500000;
+ lpMidiStrm->dwTimeDiv = 480; /* 480 is 120 quater notes per minute *//* FIXME ??*/
+ lpMidiStrm->dwPositionMS = 0;
+
+ mosm.dwStreamID = (DWORD)lpMidiStrm;
+ /* FIXME: the correct value is not allocated yet for MAPPER */
+ mosm.wDeviceID = *lpuDeviceID;
+ lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm, bFrom32);
+ lpMidiStrm->hDevice = hMidiOut;
+ if (lphMidiStrm)
+ *lphMidiStrm = (HMIDISTRM)hMidiOut;
+
+ lpwm->mld.uDeviceID = *lpuDeviceID;
+
+ ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD)&lpwm->mod, fdwOpen);
+ lpMidiStrm->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ lpMidiStrm->wFlags = HIWORD(fdwOpen);
+
+ lpMidiStrm->hThread = CreateThread(NULL, 0, MMSYSTEM_MidiStream_Player,
+ lpMidiStrm, 0, &(lpMidiStrm->dwThreadID));
+
+ if (!lpMidiStrm->hThread) {
+ midiStreamClose((HMIDISTRM)hMidiOut);
+ return MMSYSERR_NOMEM;
+ }
+ SetThreadPriority(lpMidiStrm->hThread, THREAD_PRIORITY_TIME_CRITICAL);
+
+ /* wait for thread to have started, and for its queue to be created */
+ {
+ DWORD count;
+
+ /* (Release|Restore)ThunkLock() is needed when this method is called from 16 bit code,
+ * (meaning the Win16Lock is set), so that it's released and the 32 bit thread running
+ * MMSYSTEM_MidiStreamPlayer can acquire Win16Lock to create its queue.
+ */
+ if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
+ WaitForSingleObject(lpMidiStrm->hEvent, INFINITE);
+ if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
+ }
+
+ TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n",
+ *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm);
+ return ret;
+}
+
+/**************************************************************************
+ * midiStreamOpen [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID,
+ DWORD cMidi, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD fdwOpen)
+{
+ return MIDI_StreamOpen(lphMidiStrm, lpuDeviceID, cMidi, dwCallback,
+ dwInstance, fdwOpen, TRUE);
+}
+
+/**************************************************************************
+ * midiStreamOut [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamOut(HMIDISTRM hMidiStrm, LPMIDIHDR lpMidiHdr,
+ UINT cbMidiHdr)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ DWORD ret = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMidiHdr, cbMidiHdr);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
+ ret = MMSYSERR_INVALHANDLE;
+ } else if (!lpMidiHdr) {
+ ret = MMSYSERR_INVALPARAM;
+ } else {
+ if (!PostThreadMessageA(lpMidiStrm->dwThreadID,
+ WINE_MSM_HEADER, cbMidiHdr,
+ (DWORD)lpMidiHdr)) {
+ WARN("bad PostThreadMessageA\n");
+ ret = MMSYSERR_ERROR;
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * midiStreamPause [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamPause(HMIDISTRM hMidiStrm)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ DWORD ret = MMSYSERR_NOERROR;
+
+ TRACE("(%p)!\n", hMidiStrm);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
+ ret = MMSYSERR_INVALHANDLE;
+ } else {
+ if (SuspendThread(lpMidiStrm->hThread) == 0xFFFFFFFF) {
+ WARN("bad Suspend (%ld)\n", GetLastError());
+ ret = MMSYSERR_ERROR;
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * midiStreamPosition [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamPosition(HMIDISTRM hMidiStrm, LPMMTIME lpMMT, UINT cbmmt)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ DWORD ret = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %u)!\n", hMidiStrm, lpMMT, cbmmt);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
+ ret = MMSYSERR_INVALHANDLE;
+ } else if (lpMMT == NULL || cbmmt != sizeof(MMTIME)) {
+ ret = MMSYSERR_INVALPARAM;
+ } else {
+ switch (lpMMT->wType) {
+ case TIME_MS:
+ lpMMT->u.ms = lpMidiStrm->dwPositionMS;
+ TRACE("=> %ld ms\n", lpMMT->u.ms);
+ break;
+ case TIME_TICKS:
+ lpMMT->u.ticks = lpMidiStrm->dwPulses;
+ TRACE("=> %ld ticks\n", lpMMT->u.ticks);
+ break;
+ default:
+ WARN("Unsupported time type %d\n", lpMMT->wType);
+ lpMMT->wType = TIME_MS;
+ ret = MMSYSERR_INVALPARAM;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * midiStreamProperty [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamProperty(HMIDISTRM hMidiStrm, LPBYTE lpPropData, DWORD dwProperty)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ MMRESULT ret = MMSYSERR_NOERROR;
+
+ TRACE("(%p, %p, %lx)\n", hMidiStrm, lpPropData, dwProperty);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
+ ret = MMSYSERR_INVALHANDLE;
+ } else if ((dwProperty & (MIDIPROP_GET|MIDIPROP_SET)) == 0) {
+ ret = MMSYSERR_INVALPARAM;
+ } else if (dwProperty & MIDIPROP_TEMPO) {
+ MIDIPROPTEMPO* mpt = (MIDIPROPTEMPO*)lpPropData;
+
+ if (sizeof(MIDIPROPTEMPO) != mpt->cbStruct) {
+ ret = MMSYSERR_INVALPARAM;
+ } else if (dwProperty & MIDIPROP_SET) {
+ lpMidiStrm->dwTempo = mpt->dwTempo;
+ TRACE("Setting tempo to %ld\n", mpt->dwTempo);
+ } else if (dwProperty & MIDIPROP_GET) {
+ mpt->dwTempo = lpMidiStrm->dwTempo;
+ TRACE("Getting tempo <= %ld\n", mpt->dwTempo);
+ }
+ } else if (dwProperty & MIDIPROP_TIMEDIV) {
+ MIDIPROPTIMEDIV* mptd = (MIDIPROPTIMEDIV*)lpPropData;
+
+ if (sizeof(MIDIPROPTIMEDIV) != mptd->cbStruct) {
+ ret = MMSYSERR_INVALPARAM;
+ } else if (dwProperty & MIDIPROP_SET) {
+ lpMidiStrm->dwTimeDiv = mptd->dwTimeDiv;
+ TRACE("Setting time div to %ld\n", mptd->dwTimeDiv);
+ } else if (dwProperty & MIDIPROP_GET) {
+ mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv;
+ TRACE("Getting time div <= %ld\n", mptd->dwTimeDiv);
+ }
+ } else {
+ ret = MMSYSERR_INVALPARAM;
+ }
+
+ return ret;
+}
+
+/**************************************************************************
+ * midiStreamRestart [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamRestart(HMIDISTRM hMidiStrm)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ MMRESULT ret = MMSYSERR_NOERROR;
+
+ TRACE("(%p)!\n", hMidiStrm);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
+ ret = MMSYSERR_INVALHANDLE;
+ } else {
+ DWORD ret;
+
+ /* since we increase the thread suspend count on each midiStreamPause
+ * there may be a need for several midiStreamResume
+ */
+ do {
+ ret = ResumeThread(lpMidiStrm->hThread);
+ } while (ret != 0xFFFFFFFF && ret != 0);
+ if (ret == 0xFFFFFFFF) {
+ WARN("bad Resume (%ld)\n", GetLastError());
+ ret = MMSYSERR_ERROR;
+ } else {
+ lpMidiStrm->dwStartTicks = GetTickCount() - lpMidiStrm->dwPositionMS;
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * midiStreamStop [WINMM.@]
+ */
+MMRESULT WINAPI midiStreamStop(HMIDISTRM hMidiStrm)
+{
+ WINE_MIDIStream* lpMidiStrm;
+ MMRESULT ret = MMSYSERR_NOERROR;
+
+ TRACE("(%p)!\n", hMidiStrm);
+
+ if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) {
+ ret = MMSYSERR_INVALHANDLE;
+ } else {
+ /* in case stream has been paused... FIXME is the current state correct ? */
+ midiStreamRestart(hMidiStrm);
+ MMSYSTEM_MidiStream_PostMessage(lpMidiStrm, WINE_MSM_STOP, 0, 0);
+ }
+ return ret;
+}
+
+UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType,
+ LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD dwFlags, BOOL bFrom32)
+{
+ HANDLE handle;
+ LPWINE_MLD wmld;
+ DWORD dwRet = MMSYSERR_NOERROR;
+ WAVEOPENDESC wod;
+
+ TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08lX, %d);\n",
+ lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
+ dwInstance, dwFlags, bFrom32?32:16);
+
+ if (dwFlags & WAVE_FORMAT_QUERY)
+ TRACE("WAVE_FORMAT_QUERY requested !\n");
+
+ if (lpFormat == NULL) {
+ WARN("bad format\n");
+ return WAVERR_BADFORMAT;
+ }
+
+ if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
+ WARN("invalid parameter\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
+ TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u\n",
+ lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
+ lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);
+
+ if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
+ &dwFlags, &dwCallback, &dwInstance, bFrom32)) == NULL) {
+ WARN("no memory\n");
+ return MMSYSERR_NOMEM;
+ }
+
+ wod.hWave = handle;
+ wod.lpFormat = (LPWAVEFORMATEX)lpFormat; /* should the struct be copied iso pointer? */
+ wod.dwCallback = dwCallback;
+ wod.dwInstance = dwInstance;
+ wod.dnDevNode = 0L;
+
+ TRACE("cb=%08lx\n", wod.dwCallback);
+
+ for (;;) {
+ if (dwFlags & WAVE_MAPPED) {
+ wod.uMappedDeviceID = uDeviceID;
+ uDeviceID = WAVE_MAPPER;
+ } else {
+ wod.uMappedDeviceID = -1;
+ }
+ wmld->uDeviceID = uDeviceID;
+
+ dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN,
+ (DWORD)&wod, dwFlags);
+
+ TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));
+ if (dwRet != WAVERR_BADFORMAT ||
+ ((dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) || (uDeviceID == WAVE_MAPPER)) break;
+ /* if we ask for a format which isn't supported by the physical driver,
+ * let's try to map it through the wave mapper (except, if we already tried
+ * or user didn't allow us to use acm codecs or the device is already the mapper)
+ */
+ dwFlags |= WAVE_MAPPED;
+ /* we shall loop only one */
+ }
+
+ if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
+ MMDRV_Free(handle, wmld);
+ handle = 0;
+ }
+
+ if (lphndl != NULL) *lphndl = handle;
+ TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * waveOutGetNumDevs [WINMM.@]
+ */
+UINT WINAPI waveOutGetNumDevs(void)
+{
+ return MMDRV_GetNum(MMDRV_WAVEOUT);
+}
+
+/**************************************************************************
+ * waveOutGetDevCapsA [WINMM.@]
+ */
+UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
+ UINT uSize)
+{
+ WAVEOUTCAPSW wocW;
+ UINT ret;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
+
+ if (ret == MMSYSERR_NOERROR) {
+ WAVEOUTCAPSA wocA;
+ wocA.wMid = wocW.wMid;
+ wocA.wPid = wocW.wPid;
+ wocA.vDriverVersion = wocW.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
+ sizeof(wocA.szPname), NULL, NULL );
+ wocA.dwFormats = wocW.dwFormats;
+ wocA.wChannels = wocW.wChannels;
+ wocA.dwSupport = wocW.dwSupport;
+ memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * waveOutGetDevCapsW [WINMM.@]
+ */
+UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL)
+ return MMSYSERR_BADDEVICEID;
+
+ return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
+
+}
+
+/**************************************************************************
+ * waveOutGetErrorTextA [WINMM.@]
+ * waveInGetErrorTextA [WINMM.@]
+ */
+UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
+{
+ UINT ret;
+
+ if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
+ else if (uSize == 0) ret = MMSYSERR_NOERROR;
+ else
+ {
+ LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
+ if (!xstr) ret = MMSYSERR_NOMEM;
+ else
+ {
+ ret = waveOutGetErrorTextW(uError, xstr, uSize);
+ if (ret == MMSYSERR_NOERROR)
+ WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
+ HeapFree(GetProcessHeap(), 0, xstr);
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * waveOutGetErrorTextW [WINMM.@]
+ * waveInGetErrorTextW [WINMM.@]
+ */
+UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
+{
+ UINT ret = MMSYSERR_BADERRNUM;
+
+ if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
+ else if (uSize == 0) ret = MMSYSERR_NOERROR;
+ else if (
+ /* test has been removed 'coz MMSYSERR_BASE is 0, and gcc did emit
+ * a warning for the test was always true */
+ (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
+ (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
+ if (LoadStringW(WINMM_IData.hWinMM32Instance,
+ uError, lpText, uSize) > 0) {
+ ret = MMSYSERR_NOERROR;
+ }
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * waveOutOpen [WINMM.@]
+ * All the args/structs have the same layout as the win16 equivalents
+ */
+MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
+ LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD dwFlags)
+{
+ return WAVE_Open((HANDLE*)lphWaveOut, uDeviceID, MMDRV_WAVEOUT, lpFormat,
+ dwCallback, dwInstance, dwFlags, TRUE);
+}
+
+/**************************************************************************
+ * waveOutClose [WINMM.@]
+ */
+UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
+{
+ LPWINE_MLD wmld;
+ DWORD dwRet;
+
+ TRACE("(%p)\n", hWaveOut);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ dwRet = MMDRV_Close(wmld, WODM_CLOSE);
+ if (dwRet != WAVERR_STILLPLAYING)
+ MMDRV_Free(hWaveOut, wmld);
+
+ return dwRet;
+}
+
+/**************************************************************************
+ * waveOutPrepareHeader [WINMM.@]
+ */
+UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
+ WAVEHDR* lpWaveOutHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+ UINT result;
+
+ TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
+
+ if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ if ((result = MMDRV_Message(wmld, WODM_PREPARE, (DWORD_PTR)lpWaveOutHdr,
+ uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
+ return result;
+
+ if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveOutHdr->dwFlags |= WHDR_PREPARED;
+ lpWaveOutHdr->dwFlags &= ~WHDR_DONE;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * waveOutUnprepareHeader [WINMM.@]
+ */
+UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
+ LPWAVEHDR lpWaveOutHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+ UINT result;
+
+ TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
+
+ if (lpWaveOutHdr == NULL || uSize < sizeof (WAVEHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if (!(lpWaveOutHdr->dwFlags & WHDR_PREPARED)) {
+ return MMSYSERR_NOERROR;
+ }
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ if ((result = MMDRV_Message(wmld, WODM_UNPREPARE, (DWORD_PTR)lpWaveOutHdr,
+ uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
+ return result;
+
+ if (lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveOutHdr->dwFlags &= ~WHDR_PREPARED;
+ lpWaveOutHdr->dwFlags |= WHDR_DONE;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * waveOutWrite [WINMM.@]
+ */
+UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveOutHdr,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %u);\n", hWaveOut, lpWaveOutHdr, uSize);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WODM_WRITE, (DWORD_PTR)lpWaveOutHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * waveOutBreakLoop [WINMM.@]
+ */
+UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveOut);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_BREAKLOOP, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutPause [WINMM.@]
+ */
+UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveOut);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_PAUSE, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutReset [WINMM.@]
+ */
+UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveOut);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_RESET, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutRestart [WINMM.@]
+ */
+UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveOut);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_RESTART, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutGetPosition [WINMM.@]
+ */
+UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %u);\n", hWaveOut, lpTime, uSize);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WODM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
+}
+
+/**************************************************************************
+ * waveOutGetPitch [WINMM.@]
+ */
+UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutSetPitch [WINMM.@]
+ */
+UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutGetPlaybackRate [WINMM.@]
+ */
+UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutSetPlaybackRate [WINMM.@]
+ */
+UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)dw);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+ return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutGetVolume [WINMM.@]
+ */
+UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lx);\n", hWaveOut, (DWORD)lpdw);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutSetVolume [WINMM.@]
+ */
+UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %08lx);\n", hWaveOut, dw);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveOutGetID [WINMM.@]
+ */
+UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p);\n", hWaveOut, lpuDeviceID);
+
+ if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ *lpuDeviceID = wmld->uDeviceID;
+ return 0;
+}
+
+/**************************************************************************
+ * waveOutMessage [WINMM.@]
+ */
+UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %u, %ld, %ld)\n", hWaveOut, uMessage, dwParam1, dwParam2);
+
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) {
+ if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) != NULL) {
+ return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
+ }
+ WARN("invalid handle\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ /* from M$ KB */
+ if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER)) {
+ WARN("invalid parameter\n");
+ return MMSYSERR_INVALPARAM;
+ }
+
+ return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
+}
+
+/**************************************************************************
+ * waveInGetNumDevs [WINMM.@]
+ */
+UINT WINAPI waveInGetNumDevs(void)
+{
+ return MMDRV_GetNum(MMDRV_WAVEIN);
+}
+
+/**************************************************************************
+ * waveInGetDevCapsW [WINMM.@]
+ */
+UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%u %p %u)!\n", uDeviceID, lpCaps, uSize);
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
+ return MMSYSERR_BADDEVICEID;
+
+ return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize, TRUE);
+}
+
+/**************************************************************************
+ * waveInGetDevCapsA [WINMM.@]
+ */
+UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
+{
+ WAVEINCAPSW wicW;
+ UINT ret;
+
+ if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
+
+ ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
+
+ if (ret == MMSYSERR_NOERROR) {
+ WAVEINCAPSA wicA;
+ wicA.wMid = wicW.wMid;
+ wicA.wPid = wicW.wPid;
+ wicA.vDriverVersion = wicW.vDriverVersion;
+ WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
+ sizeof(wicA.szPname), NULL, NULL );
+ wicA.dwFormats = wicW.dwFormats;
+ wicA.wChannels = wicW.wChannels;
+ memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * waveInOpen [WINMM.@]
+ */
+MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
+ LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
+ DWORD_PTR dwInstance, DWORD dwFlags)
+{
+ return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
+ dwCallback, dwInstance, dwFlags, TRUE);
+}
+
+/**************************************************************************
+ * waveInClose [WINMM.@]
+ */
+UINT WINAPI waveInClose(HWAVEIN hWaveIn)
+{
+ LPWINE_MLD wmld;
+ DWORD dwRet;
+
+ TRACE("(%p)\n", hWaveIn);
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L, TRUE);
+ if (dwRet != WAVERR_STILLPLAYING)
+ MMDRV_Free(hWaveIn, wmld);
+ return dwRet;
+}
+
+/**************************************************************************
+ * waveInPrepareHeader [WINMM.@]
+ */
+UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+ UINT result;
+
+ TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
+
+ if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,
+ uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
+ return result;
+
+ if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveInHdr->dwFlags |= WHDR_PREPARED;
+ lpWaveInHdr->dwFlags &= ~WHDR_DONE;
+ lpWaveInHdr->dwBytesRecorded = 0;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * waveInUnprepareHeader [WINMM.@]
+ */
+UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+ UINT result;
+
+ TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
+
+ if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
+ return MMSYSERR_INVALPARAM;
+
+ if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
+ return MMSYSERR_NOERROR;
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,
+ uSize, TRUE)) != MMSYSERR_NOTSUPPORTED)
+ return result;
+
+ if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;
+ lpWaveInHdr->dwFlags |= WHDR_DONE;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * waveInAddBuffer [WINMM.@]
+ */
+UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
+ WAVEHDR* lpWaveInHdr, UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
+
+ if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize, TRUE);
+}
+
+/**************************************************************************
+ * waveInReset [WINMM.@]
+ */
+UINT WINAPI waveInReset(HWAVEIN hWaveIn)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveIn);
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveInStart [WINMM.@]
+ */
+UINT WINAPI waveInStart(HWAVEIN hWaveIn)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveIn);
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WIDM_START, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveInStop [WINMM.@]
+ */
+UINT WINAPI waveInStop(HWAVEIN hWaveIn)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p);\n", hWaveIn);
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L, TRUE);
+}
+
+/**************************************************************************
+ * waveInGetPosition [WINMM.@]
+ */
+UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
+ UINT uSize)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize, TRUE);
+}
+
+/**************************************************************************
+ * waveInGetID [WINMM.@]
+ */
+UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
+
+ if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
+ return MMSYSERR_INVALHANDLE;
+
+ *lpuDeviceID = wmld->uDeviceID;
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * waveInMessage [WINMM.@]
+ */
+UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
+ DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+ LPWINE_MLD wmld;
+
+ TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
+
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
+ if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
+ return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
+ }
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ /* from M$ KB */
+ if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
+ return MMSYSERR_INVALPARAM;
+
+
+ return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2, TRUE);
+}
+
+struct mm_starter
+{
+ LPTASKCALLBACK cb;
+ DWORD client;
+ HANDLE event;
+};
+
+static DWORD WINAPI mmTaskRun(void* pmt)
+{
+ struct mm_starter mms;
+
+ memcpy(&mms, pmt, sizeof(struct mm_starter));
+ HeapFree(GetProcessHeap(), 0, pmt);
+ mms.cb(mms.client);
+ if (mms.event) SetEvent(mms.event);
+ return 0;
+}
+
+/******************************************************************
+ * mmTaskCreate (WINMM.@)
+ */
+MMRESULT WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD client)
+{
+ HANDLE hThread;
+ HANDLE hEvent = 0;
+ struct mm_starter *mms;
+
+ mms = HeapAlloc(GetProcessHeap(), 0, sizeof(struct mm_starter));
+ if (mms == NULL) return TASKERR_OUTOFMEMORY;
+
+ mms->cb = cb;
+ mms->client = client;
+ if (ph) hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+ mms->event = hEvent;
+
+ hThread = CreateThread(0, 0, mmTaskRun, mms, 0, NULL);
+ if (!hThread) {
+ HeapFree(GetProcessHeap(), 0, mms);
+ if (hEvent) CloseHandle(hEvent);
+ return TASKERR_OUTOFMEMORY;
+ }
+ SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
+ if (ph) *ph = hEvent;
+ CloseHandle(hThread);
+ return 0;
+}
+
+/******************************************************************
+ * mmTaskBlock (WINMM.@)
+ */
+void WINAPI mmTaskBlock(HANDLE tid)
+{
+ MSG msg;
+
+ do
+ {
+ GetMessageA(&msg, 0, 0, 0);
+ if (msg.hwnd) DispatchMessageA(&msg);
+ } while (msg.message != WM_USER);
+}
+
+/******************************************************************
+ * mmTaskSignal (WINMM.@)
+ */
+BOOL WINAPI mmTaskSignal(HANDLE tid)
+{
+ return PostThreadMessageW((DWORD)tid, WM_USER, 0, 0);
+}
+
+/******************************************************************
+ * mmTaskYield (WINMM.@)
+ */
+void WINAPI mmTaskYield(void) {}
+
+/******************************************************************
+ * mmGetCurrentTask (WINMM.@)
+ */
+HANDLE WINAPI mmGetCurrentTask(void)
+{
+ return (HANDLE)GetCurrentThreadId();
+}