/* -*- 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "winemm.h" WINE_DEFAULT_DEBUG_CHANNEL(winmm); /* ======================================================================== * G L O B A L S E T T I N G S * ========================================================================*/ HINSTANCE hWinMM32Instance; HANDLE psLastEvent; HANDLE psStopEvent; CRITICAL_SECTION WINMM_cs; /************************************************************************** * WINMM_CreateIData [internal] */ static BOOL WINMM_CreateIData(HINSTANCE hInstDLL) { hWinMM32Instance = hInstDLL; psStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL); psLastEvent = CreateEventW(NULL, TRUE, FALSE, NULL); InitializeCriticalSection(&WINMM_cs); return TRUE; } /************************************************************************** * WINMM_DeleteIData [internal] */ static void WINMM_DeleteIData(void) { TIME_MMTimeStop(); /* FIXME: should also free content and resources allocated * inside WINMM_IData */ CloseHandle(psStopEvent); CloseHandle(psLastEvent); DeleteCriticalSection(&WINMM_cs); } /****************************************************************** * WINMM_LoadMMSystem * */ static HANDLE (WINAPI *pGetModuleHandle16)(LPCSTR); static DWORD (WINAPI *pLoadLibrary16)(LPCSTR); 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) { pGetModuleHandle16 = (void*)GetProcAddress(h, "GetModuleHandle16"); pLoadLibrary16 = (void*)GetProcAddress(h, (LPCSTR)35); /* ordinal for LoadLibrary16 */ if (pGetModuleHandle16 && pLoadLibrary16 && (pGetModuleHandle16("MMSYSTEM.DLL") || pLoadLibrary16("MMSYSTEM.DLL"))) 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); ERR_TO_STR(MIDIERR_INVALIDSETUP); ERR_TO_STR(MIDIERR_NODEVICE); ERR_TO_STR(MIDIERR_STILLPLAYING); ERR_TO_STR(MIDIERR_UNPREPARED); } 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%x %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); MMDRV_Exit(); /* There's no guarantee the drivers haven't already been unloaded on * process shutdown. */ if (!fImpLoad) { /* now unload all remaining drivers... */ DRIVER_UnloadAll(); } WINMM_DeleteIData(); break; } return TRUE; } /************************************************************************** * WINMM_CheckCallback [internal] */ MMRESULT WINMM_CheckCallback(DWORD_PTR dwCallback, DWORD fdwOpen, BOOL mixer) { switch (fdwOpen & CALLBACK_TYPEMASK) { case CALLBACK_NULL: /* dwCallback need not be NULL */ break; case CALLBACK_WINDOW: if (dwCallback && !IsWindow((HWND)dwCallback)) return MMSYSERR_INVALPARAM; break; case CALLBACK_FUNCTION: /* a NULL cb is acceptable since w2k, MMSYSERR_INVALPARAM earlier */ if (mixer) return MMSYSERR_INVALFLAG; /* since w2k, MMSYSERR_NOTSUPPORTED earlier */ break; case CALLBACK_THREAD: case CALLBACK_EVENT: if (mixer) /* FIXME: mixer supports THREAD+EVENT since w2k */ return MMSYSERR_NOTSUPPORTED; /* w9X */ break; default: WARN("Unknown callback type %d\n", HIWORD(fdwOpen)); } return MMSYSERR_NOERROR; } /************************************************************************** * 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); } static void CALLBACK MIXER_WCallback(HMIXEROBJ hmx, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam, DWORD_PTR param2) { HWND hWnd = (HWND)dwInstance; if (!dwInstance) return; PostMessageW(hWnd, uMsg, (WPARAM)hmx, (LPARAM)dwParam); } /************************************************************************** * mixerOpen [WINMM.@] */ UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) { HANDLE hMix; LPWINE_MLD wmld; DWORD dwRet; MIXEROPENDESC mod; TRACE("(%p, %d, %08lx, %08lx, %08x)\n", lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen); dwRet = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE); if(dwRet != MMSYSERR_NOERROR) return dwRet; mod.dwCallback = (DWORD_PTR)MIXER_WCallback; if ((fdwOpen & CALLBACK_TYPEMASK) == CALLBACK_WINDOW) mod.dwInstance = dwCallback; else mod.dwInstance = 0; /* We're remapping to CALLBACK_FUNCTION because that's what old winmm is * documented to do when opening the mixer driver. * FIXME: Native supports CALLBACK_EVENT + CALLBACK_THREAD flags since w2k. * FIXME: The non ALSA drivers ignore callback requests - bug. */ wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen, &dwCallback, &dwInstance); wmld->uDeviceID = uDeviceID; mod.hmx = hMix; dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD_PTR)&mod, CALLBACK_FUNCTION); if (dwRet != MMSYSERR_NOERROR) { MMDRV_Free(hMix, wmld); hMix = 0; } if (lphMix) *lphMix = hMix; TRACE("=> %d hMixer=%p\n", dwRet, hMix); return dwRet; } /************************************************************************** * 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 %08x)\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, %08x)\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); } /************************************************************************** * mixerGetControlDetailsA [WINMM.@] */ UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA, DWORD fdwDetails) { DWORD ret = MMSYSERR_NOTENABLED; TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails); if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA)) return MMSYSERR_INVALPARAM; switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) { case MIXER_GETCONTROLDETAILSF_VALUE: /* can safely use A structure as it is, no string inside */ ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails); break; case MIXER_GETCONTROLDETAILSF_LISTTEXT: { MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = 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%08x\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, %08x)\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 & MIXER_GETLINECONTROLSF_QUERYMASK)) { 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; 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, %08x)\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); } /************************************************************************** * mixerGetLineInfoW [WINMM.@] */ UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo) { LPWINE_MIXER lpwm; UINT uRet = MMSYSERR_NOERROR; TRACE("(%p, %p, %08x)\n", hmix, lpmliW, fdwInfo); if (lpmliW == NULL || lpmliW->cbStruct != sizeof(*lpmliW)) return MMSYSERR_INVALPARAM; if ((uRet = MIXER_GetDev(hmix, fdwInfo, &lpwm)) != MMSYSERR_NOERROR) return uRet; return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD_PTR)lpmliW, fdwInfo); } /************************************************************************** * mixerGetLineInfoA [WINMM.@] */ UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA, DWORD fdwInfo) { MIXERLINEW mliW; UINT ret; TRACE("(%p, %p, %08x)\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)/sizeof(WCHAR)); break; default: WARN("Unsupported fdwControls=0x%08x\n", fdwInfo); return MMSYSERR_INVALFLAG; } ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo); if(ret == MMSYSERR_NOERROR) { 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, %08x)\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); } /************************************************************************** * mixerMessage [WINMM.@] */ DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { LPWINE_MLD wmld; TRACE("(%p, %d, %08lx, %08lx): semi-stub?\n", hmix, uMsg, dwParam1, dwParam2); if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2); } /************************************************************************** * 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("(%04lX, %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); } /************************************************************************** * 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(UlongToHandle(uDeviceID), MMDRV_AUX, TRUE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, AUXDM_GETVOLUME, (DWORD_PTR)lpdwVolume, 0L); } /************************************************************************** * auxSetVolume [WINMM.@] */ UINT WINAPI auxSetVolume(UINT uDeviceID, DWORD dwVolume) { LPWINE_MLD wmld; TRACE("(%04X, %u) !\n", uDeviceID, dwVolume); if ((wmld = MMDRV_Get(UlongToHandle(uDeviceID), MMDRV_AUX, TRUE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, AUXDM_SETVOLUME, dwVolume, 0L); } /************************************************************************** * auxOutMessage [WINMM.@] */ UINT WINAPI auxOutMessage(UINT uDeviceID, UINT uMessage, DWORD_PTR dw1, DWORD_PTR dw2) { LPWINE_MLD wmld; if ((wmld = MMDRV_Get(UlongToHandle(uDeviceID), MMDRV_AUX, TRUE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, uMessage, dw1, dw2); } /************************************************************************** * 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("(%lu, %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); } /************************************************************************** * 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 because 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(hWinMM32Instance, uError, lpText, uSize) > 0) { ret = MMSYSERR_NOERROR; } } return ret; } /************************************************************************** * MIDI_OutAlloc [internal] */ static LPWINE_MIDI MIDI_OutAlloc(HMIDIOUT* lphMidiOut, DWORD_PTR* lpdwCallback, DWORD_PTR* lpdwInstance, LPDWORD lpdwFlags, DWORD cIDs, MIDIOPENSTRMID* lpIDs) { 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); *lphMidiOut = hMidiOut; if (lpwm) { lpwm->mod.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; } /************************************************************************** * midiOutOpen [WINMM.@] */ MMRESULT WINAPI midiOutOpen(LPHMIDIOUT lphMidiOut, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags) { HMIDIOUT hMidiOut; LPWINE_MIDI lpwm; UINT dwRet; TRACE("(%p, %d, %08lX, %08lX, %08X);\n", lphMidiOut, uDeviceID, dwCallback, dwInstance, dwFlags); if (lphMidiOut != NULL) *lphMidiOut = 0; dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE); if (dwRet != MMSYSERR_NOERROR) return dwRet; lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &dwFlags, 0, NULL); if (lpwm == NULL) return MMSYSERR_NOMEM; lpwm->mld.uDeviceID = uDeviceID; dwRet = MMDRV_Open((LPWINE_MLD)lpwm, MODM_OPEN, (DWORD_PTR)&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; } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * midiOutShortMsg [WINMM.@] */ UINT WINAPI midiOutShortMsg(HMIDIOUT hMidiOut, DWORD dwMsg) { LPWINE_MLD wmld; TRACE("(%p, %08X)\n", hMidiOut, dwMsg); if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, FALSE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, MODM_DATA, dwMsg, 0L); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * midiOutSetVolume [WINMM.@] */ UINT WINAPI midiOutSetVolume(HMIDIOUT hMidiOut, DWORD dwVolume) { LPWINE_MLD wmld; TRACE("(%p, %d);\n", hMidiOut, dwVolume); if ((wmld = MMDRV_Get(hMidiOut, MMDRV_MIDIOUT, TRUE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, MODM_SETVOLUME, dwVolume, 0L); } /************************************************************************** * 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); } /************************************************************************** * 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("(%ld, %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); } /************************************************************************** * 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; } /************************************************************************** * midiInOpen [WINMM.@] */ MMRESULT WINAPI midiInOpen(HMIDIIN* lphMidiIn, UINT uDeviceID, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags) { HANDLE hMidiIn; LPWINE_MIDI lpwm; DWORD dwRet; TRACE("(%p, %d, %08lX, %08lX, %08X);\n", lphMidiIn, uDeviceID, dwCallback, dwInstance, dwFlags); if (lphMidiIn != NULL) *lphMidiIn = 0; dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE); if (dwRet != MMSYSERR_NOERROR) return dwRet; lpwm = (LPWINE_MIDI)MMDRV_Alloc(sizeof(WINE_MIDI), MMDRV_MIDIIN, &hMidiIn, &dwFlags, &dwCallback, &dwInstance); if (lpwm == NULL) return MMSYSERR_NOMEM; lpwm->mod.hMidi = hMidiIn; lpwm->mod.dwCallback = dwCallback; lpwm->mod.dwInstance = dwInstance; lpwm->mld.uDeviceID = uDeviceID; dwRet = MMDRV_Open(&lpwm->mld, MIDM_OPEN, (DWORD_PTR)&lpwm->mod, dwFlags); if (dwRet != MMSYSERR_NOERROR) { MMDRV_Free(hMidiIn, &lpwm->mld); hMidiIn = 0; } if (lphMidiIn != NULL) *lphMidiIn = hMidiIn; TRACE("=> %d hMidi=%p\n", dwRet, hMidiIn); return dwRet; } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * midiConnect [WINMM.@] */ MMRESULT WINAPI midiConnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved) { FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved); return MMSYSERR_ERROR; } /************************************************************************** * midiDisconnect [WINMM.@] */ MMRESULT WINAPI midiDisconnect(HMIDI hMidi, HMIDIOUT hmo, LPVOID pReserved) { FIXME("(%p, %p, %p): Stub\n", hMidi, hmo, pReserved); return MMSYSERR_ERROR; } 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*)(ULONG_PTR)lpwm->mod.rgIds.dwStreamID; // FIXME: not 64 bit safe 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->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_PTR)lpMidiHdr, 0); } 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 = (LPBYTE)lpMidiHdr->lpData; TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBufferLength=%u/%u dwFlags=0x%08x size=%lu]\n", (lpMidiHdr->dwFlags & MHDR_ISSTRM) ? "stream" : "regular", lpMidiHdr, 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 && /* FIXME: not 64 bit safe */ ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD_PTR)lpMidiStrm) { FIXME("Dropping bad %s lpMidiHdr (streamID=%08x)\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_PTR)lpMidiHdr, 0); break; } for (lpmh = &lpMidiStrm->lpMidiHdr; *lpmh; lpmh = &(*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 = (LPBYTE)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("%d/%d/%d\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->lpNext; DriverCallback(lpwm->mod.dwCallback, lpMidiStrm->wFlags, (HDRVR)lpMidiStrm->hDevice, MM_MOM_DONE, lpwm->mod.dwInstance, (DWORD_PTR)lpMidiHdr, 0); 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; //ReleaseThunkLock(&count); WaitForSingleObject(lpMidiStrm->hEvent, INFINITE); //RestoreThunkLock(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); } /************************************************************************** * midiStreamOpen [WINMM.@] */ MMRESULT WINAPI midiStreamOpen(HMIDISTRM* lphMidiStrm, LPUINT lpuDeviceID, DWORD cMidi, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen) { WINE_MIDIStream* lpMidiStrm; MMRESULT ret; MIDIOPENSTRMID mosm; LPWINE_MIDI lpwm; HMIDIOUT hMidiOut; TRACE("(%p, %p, %d, 0x%08lx, 0x%08lx, 0x%08x)!\n", lphMidiStrm, lpuDeviceID, cMidi, dwCallback, dwInstance, fdwOpen); if (cMidi != 1 || lphMidiStrm == NULL || lpuDeviceID == NULL) return MMSYSERR_INVALPARAM; ret = WINMM_CheckCallback(dwCallback, fdwOpen, FALSE); if (ret != MMSYSERR_NOERROR) return ret; lpMidiStrm = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_MIDIStream)); if (!lpMidiStrm) return MMSYSERR_NOMEM; lpMidiStrm->dwTempo = 500000; lpMidiStrm->dwTimeDiv = 480; /* 480 is 120 quarter notes per minute *//* FIXME ??*/ lpMidiStrm->dwPositionMS = 0; mosm.dwStreamID = (DWORD_PTR)lpMidiStrm; // FIXME: not 64 bit safe /* FIXME: the correct value is not allocated yet for MAPPER */ mosm.wDeviceID = *lpuDeviceID; lpwm = MIDI_OutAlloc(&hMidiOut, &dwCallback, &dwInstance, &fdwOpen, 1, &mosm); lpMidiStrm->hDevice = hMidiOut; *lphMidiStrm = (HMIDISTRM)hMidiOut; lpwm->mld.uDeviceID = *lpuDeviceID; ret = MMDRV_Open(&lpwm->mld, MODM_OPEN, (DWORD_PTR)&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. */ //ReleaseThunkLock(&count); WaitForSingleObject(lpMidiStrm->hEvent, INFINITE); //RestoreThunkLock(count); } TRACE("=> (%u/%d) hMidi=%p ret=%d lpMidiStrm=%p\n", *lpuDeviceID, lpwm->mld.uDeviceID, *lphMidiStrm, ret, lpMidiStrm); return ret; } /************************************************************************** * 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 (cbMidiHdr < offsetof(MIDIHDR,dwOffset) || !lpMidiHdr || !lpMidiHdr->lpData || lpMidiHdr->dwBufferLength < lpMidiHdr->dwBytesRecorded || lpMidiHdr->dwBytesRecorded % 4 /* player expects DWORD padding */) return MMSYSERR_INVALPARAM; /* FIXME: Native additionally checks if the MIDIEVENTs in lpData * exactly fit dwBytesRecorded. */ if (!(lpMidiHdr->dwFlags & MHDR_PREPARED)) return MIDIERR_UNPREPARED; if (lpMidiHdr->dwFlags & MHDR_INQUEUE) return MIDIERR_STILLPLAYING; if (!MMSYSTEM_GetMidiStream(hMidiStrm, &lpMidiStrm, NULL)) { ret = MMSYSERR_INVALHANDLE; } else { lpMidiHdr->dwFlags |= MHDR_ISSTRM; if (!PostThreadMessageA(lpMidiStrm->dwThreadID, WINE_MSM_HEADER, cbMidiHdr, (LPARAM)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 (%d)\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("=> %d ms\n", lpMMT->u.ms); break; case TIME_TICKS: lpMMT->u.ticks = lpMidiStrm->dwPulses; TRACE("=> %d 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, %x)\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 %d\n", mpt->dwTempo); } else if (dwProperty & MIDIPROP_GET) { mpt->dwTempo = lpMidiStrm->dwTempo; TRACE("Getting tempo <= %d\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 %d\n", mptd->dwTimeDiv); } else if (dwProperty & MIDIPROP_GET) { mptd->dwTimeDiv = lpMidiStrm->dwTimeDiv; TRACE("Getting time div <= %d\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 (%d)\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; } static UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType, LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD dwFlags) { HANDLE handle; LPWINE_MLD wmld; DWORD dwRet; WAVEOPENDESC wod; TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08X);\n", lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback, dwInstance, dwFlags); if (dwFlags & WAVE_FORMAT_QUERY) TRACE("WAVE_FORMAT_QUERY requested !\n"); dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE); if (dwRet != MMSYSERR_NOERROR) return dwRet; 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=%u, nAvgBytesPerSec=%u, 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)) == NULL) { 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_PTR)&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("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize); if (lpCaps == NULL) return MMSYSERR_INVALPARAM; if (uDeviceID == WAVE_MAPPER) { FIXME("Support WAVE_MAPPER\n"); uDeviceID = 0; } if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEOUT, TRUE)) == NULL) return MMSYSERR_BADDEVICEID; return MMDRV_Message(wmld, WODM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize); } /************************************************************************** * 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 because 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(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); } /************************************************************************** * 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)) != 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)) != 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * waveOutGetPitch [WINMM.@] */ UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw) { LPWINE_MLD wmld; TRACE("(%p, %p);\n", hWaveOut, lpdw); if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, WODM_GETPITCH, (DWORD_PTR)lpdw, 0L); } /************************************************************************** * waveOutSetPitch [WINMM.@] */ UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw) { LPWINE_MLD wmld; TRACE("(%p, %08x);\n", hWaveOut, dw); if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, WODM_SETPITCH, dw, 0L); } /************************************************************************** * waveOutGetPlaybackRate [WINMM.@] */ UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw) { LPWINE_MLD wmld; TRACE("(%p, %p);\n", hWaveOut, lpdw); if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, WODM_GETPLAYBACKRATE, (DWORD_PTR)lpdw, 0L); } /************************************************************************** * waveOutSetPlaybackRate [WINMM.@] */ UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw) { LPWINE_MLD wmld; TRACE("(%p, %08x);\n", hWaveOut, dw); if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, FALSE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, WODM_SETPLAYBACKRATE, dw, 0L); } /************************************************************************** * waveOutGetVolume [WINMM.@] */ UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, LPDWORD lpdw) { LPWINE_MLD wmld; TRACE("(%p, %p);\n", hWaveOut, lpdw); if (lpdw == NULL) { WARN("invalid parameter\n"); return MMSYSERR_INVALPARAM; } if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, WODM_GETVOLUME, (DWORD_PTR)lpdw, 0L); } /************************************************************************** * waveOutSetVolume [WINMM.@] */ UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD dw) { LPWINE_MLD wmld; TRACE("(%p, %08x);\n", hWaveOut, dw); if ((wmld = MMDRV_Get(hWaveOut, MMDRV_WAVEOUT, TRUE)) == NULL) return MMSYSERR_INVALHANDLE; return MMDRV_Message(wmld, WODM_SETVOLUME, dw, 0L); } /************************************************************************** * 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); } /************************************************************************** * 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("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize); if (lpCaps == NULL) return MMSYSERR_INVALPARAM; if (uDeviceID == WAVE_MAPPER) { FIXME("Support WAVE_MAPPER\n"); uDeviceID = 0; } if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL) return MMSYSERR_BADDEVICEID; return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize); } /************************************************************************** * 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); } /************************************************************************** * 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); 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)) != 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)) != 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } /************************************************************************** * 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); } 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.@) */ UINT WINAPI mmTaskCreate(LPTASKCALLBACK cb, HANDLE* ph, DWORD_PTR 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(DWORD tid) { MSG msg; do { GetMessageA(&msg, 0, 0, 0); if (msg.hwnd) DispatchMessageA(&msg); } while (msg.message != WM_USER); } /****************************************************************** * mmTaskSignal (WINMM.@) */ BOOL WINAPI mmTaskSignal(DWORD tid) { return PostThreadMessageW(tid, WM_USER, 0, 0); } /****************************************************************** * mmTaskYield (WINMM.@) */ VOID WINAPI mmTaskYield(VOID) {} /****************************************************************** * mmGetCurrentTask (WINMM.@) */ DWORD WINAPI mmGetCurrentTask(VOID) { return GetCurrentThreadId(); }