| /* -*- 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 <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); |
| WINMM_IData.cs.DebugInfo->Spare[0] = (DWORD_PTR)"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 |
| * |
| */ |
| 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); |
| } |
| 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, 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, %08x)\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("=> %d 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 %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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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 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%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) { |
| 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, %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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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 ((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, %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); |
| |
| 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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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, 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, %u) !\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, DWORD_PTR* lpdwCallback, |
| DWORD_PTR* 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, %08X);\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, %08X)\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, %d);\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, %08X);\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("=> %d 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 = (LPBYTE)lpMidiHdr->lpData; |
| TRACE("Adding %s lpMidiHdr=%p [lpData=0x%p dwBufferLength=%u/%u dwFlags=0x%08x size=%u]\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 && |
| ((LPMIDIEVENT)lpData)->dwStreamID != (DWORD)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)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 = (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)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, %d, 0x%08lx, 0x%08lx, 0x%08x)!\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 (%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; |
| } |
| |
| 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, %08X, %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=%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, 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=%08x\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, %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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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, TRUE); |
| } |
| |
| /************************************************************************** |
| * 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, 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(); |
| } |