winmm: Implement mixer* on top of MMDevAPI.

This commit is contained in:
Andrew Eikum 2011-07-13 14:20:40 -05:00 committed by Alexandre Julliard
parent ccd2a24139
commit 30eeb996ed
1 changed files with 694 additions and 27 deletions

View File

@ -53,6 +53,12 @@
WINE_DEFAULT_DEBUG_CHANNEL(winmm);
/* FIXME: Should be localized */
static const WCHAR volumeW[] = {'V','o','l','u','m','e',0};
static const WCHAR mastervolumeW[] = {'M','a','s','t','e','r',' ','V','o','l',
'u','m','e',0};
static const WCHAR muteW[] = {'M','u','t','e',0};
/* HWAVE (and HMIXER) format:
*
* XXXX... 1FDD DDDD IIII IIII
@ -117,8 +123,14 @@ struct _WINMM_MMDevice {
WAVEINCAPSW in_caps; /* must not be modified outside of WINMM_InitMMDevices*/
WCHAR *dev_id;
ISimpleAudioVolume *volume;
GUID session;
/* HMIXER format is the same as the HWAVE format, but the I bits are
* replaced by the value of this counter, to keep each HMIXER unique */
UINT mixer_count;
CRITICAL_SECTION lock;
WINMM_Device *devices[MAX_DEVICES];
@ -149,6 +161,12 @@ typedef struct _WINMM_OpenInfo {
DWORD flags;
} WINMM_OpenInfo;
typedef struct _WINMM_ControlDetails {
HMIXEROBJ hmix;
MIXERCONTROLDETAILS *details;
DWORD flags;
} WINMM_ControlDetails;
static LRESULT WOD_Open(WINMM_OpenInfo *info);
static LRESULT WOD_Close(HWAVEOUT hwave);
static LRESULT WID_Open(WINMM_OpenInfo *info);
@ -1783,6 +1801,243 @@ static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
bytes_per_frame);
}
static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
UINT *mmdev_index)
{
UINT mmdev, dev, junk, *out;
BOOL is_out;
if(!mmdev_index)
out = &mmdev;
else
out = mmdev_index;
switch(flags & 0xF0000000){
case MIXER_OBJECTF_MIXER: /* == 0 */
*out = HandleToULong(hmix);
if(*out < g_outmmdevices_count)
return &g_out_mmdevices[*out];
if(*out - g_outmmdevices_count < g_inmmdevices_count){
*out -= g_outmmdevices_count;
return &g_in_mmdevices[*out];
}
/* fall through -- if it's not a valid mixer device, then
* it could be a valid mixer handle. windows seems to do
* this as well. */
case MIXER_OBJECTF_HMIXER:
case MIXER_OBJECTF_HWAVEOUT:
case MIXER_OBJECTF_HWAVEIN:
WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
(!is_out && *out >= g_inmmdevices_count))
return NULL;
if(is_out)
return &g_out_mmdevices[*out];
return &g_in_mmdevices[*out];
case MIXER_OBJECTF_WAVEOUT:
*out = HandleToULong(hmix);
if(*out < g_outmmdevices_count)
return &g_out_mmdevices[*out];
return NULL;
case MIXER_OBJECTF_WAVEIN:
*out = HandleToULong(hmix);
if(*out < g_inmmdevices_count)
return &g_in_mmdevices[*out];
return NULL;
}
return NULL;
}
static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
{
IAudioSessionManager *sesman;
IMMDevice *device;
HRESULT hr;
hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
if(FAILED(hr)){
ERR("Device %s (%s) unavailable: %08x\n",
wine_dbgstr_w(mmdevice->dev_id),
wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
return MMSYSERR_ERROR;
}
hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
if(FAILED(hr)){
ERR("Activate failed: %08x\n", hr);
IMMDevice_Release(device);
return MMSYSERR_ERROR;
}
IMMDevice_Release(device);
hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
FALSE, &mmdevice->volume);
IAudioSessionManager_Release(sesman);
if(FAILED(hr)){
ERR("GetSimpleAudioVolume failed: %08x\n", hr);
return MMSYSERR_ERROR;
}
return MMSYSERR_NOERROR;
}
static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
{
WINMM_MMDevice *mmdevice;
MIXERCONTROLDETAILS *control = details->details;
HRESULT hr;
TRACE("(%p)\n", details->hmix);
mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
if(!mmdevice)
return MMSYSERR_INVALHANDLE;
EnterCriticalSection(&mmdevice->lock);
if(!mmdevice->volume){
MMRESULT mr;
mr = WINMM_SetupMMDeviceVolume(mmdevice);
if(mr != MMSYSERR_NOERROR){
LeaveCriticalSection(&mmdevice->lock);
return mr;
}
}
if(control->dwControlID == 0){
float vol;
MIXERCONTROLDETAILS_UNSIGNED *udet;
if(!control->paDetails ||
control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_INVALPARAM;
}
hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
if(FAILED(hr)){
ERR("GetMasterVolume failed: %08x\n", hr);
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_ERROR;
}
udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
udet->dwValue = vol * ((unsigned int)0xFFFF);
}else if(control->dwControlID == 1){
BOOL mute;
MIXERCONTROLDETAILS_BOOLEAN *bdet;
if(!control->paDetails ||
control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_INVALPARAM;
}
hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
if(FAILED(hr)){
ERR("GetMute failed: %08x\n", hr);
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_ERROR;
}
bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
bdet->fValue = mute;
}else if(control->dwControlID == 2 || control->dwControlID == 3){
FIXME("What should the sw-side mixer controls map to?\n");
}else{
LeaveCriticalSection(&mmdevice->lock);
return MIXERR_INVALCONTROL;
}
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_NOERROR;
}
static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
{
WINMM_MMDevice *mmdevice;
MIXERCONTROLDETAILS *control = details->details;
HRESULT hr;
TRACE("(%p)\n", details->hmix);
mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
if(!mmdevice)
return MMSYSERR_INVALHANDLE;
EnterCriticalSection(&mmdevice->lock);
if(!mmdevice->volume){
MMRESULT mr;
mr = WINMM_SetupMMDeviceVolume(mmdevice);
if(mr != MMSYSERR_NOERROR){
LeaveCriticalSection(&mmdevice->lock);
return mr;
}
}
if(control->dwControlID == 0){
float vol;
MIXERCONTROLDETAILS_UNSIGNED *udet;
if(!control->paDetails ||
control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_INVALPARAM;
}
udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
if(udet->dwValue > 65535){
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_INVALPARAM;
}
vol = udet->dwValue / 65535.f;
hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
if(FAILED(hr)){
ERR("SetMasterVolume failed: %08x\n", hr);
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_ERROR;
}
}else if(control->dwControlID == 1){
BOOL mute;
MIXERCONTROLDETAILS_BOOLEAN *bdet;
if(!control->paDetails ||
control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_INVALPARAM;
}
bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
mute = bdet->fValue;
hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
if(FAILED(hr)){
ERR("SetMute failed: %08x\n", hr);
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_ERROR;
}
}else if(control->dwControlID == 2 || control->dwControlID == 3){
FIXME("What should the sw-side mixer controls map to?\n");
}else{
LeaveCriticalSection(&mmdevice->lock);
return MIXERR_INVALCONTROL;
}
LeaveCriticalSection(&mmdevice->lock);
return MMSYSERR_NOERROR;
}
static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
LPARAM lparam)
{
@ -1795,6 +2050,10 @@ static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
return WID_Open((WINMM_OpenInfo*)wparam);
case WIDM_CLOSE:
return WID_Close((HWAVEIN)wparam);
case MXDM_GETCONTROLDETAILS:
return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
case MXDM_SETCONTROLDETAILS:
return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
}
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
@ -3001,7 +3260,12 @@ UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
UINT WINAPI mixerGetNumDevs(void)
{
return 0;
TRACE("\n");
if(!WINMM_StartDevicesThread())
return 0;
return g_outmmdevices_count + g_inmmdevices_count;
}
/**************************************************************************
@ -3012,7 +3276,10 @@ UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize
MIXERCAPSW micW;
UINT ret;
if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
if(!lpCaps)
return MMSYSERR_INVALPARAM;
ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
@ -3035,10 +3302,40 @@ UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize
*/
UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
{
WINMM_MMDevice *mmdevice;
MIXERCAPSW caps;
TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
if(!lpCaps)
return MMSYSERR_INVALPARAM;
return MMSYSERR_BADDEVICEID;
if(!uSize)
return MMSYSERR_NOERROR;
if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
return MMSYSERR_BADDEVICEID;
if(uDeviceID < g_outmmdevices_count){
mmdevice = &g_out_mmdevices[uDeviceID];
memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
}else{
mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
}
caps.wMid = 0xFF;
caps.wPid = 0xFF;
caps.vDriverVersion = 0x00010001;
caps.fdwSupport = 0;
caps.cDestinations = 1;
memcpy(lpCaps, &caps, uSize);
return MMSYSERR_NOERROR;
}
/**************************************************************************
@ -3047,16 +3344,38 @@ UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize
UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
DWORD_PTR dwInstance, DWORD fdwOpen)
{
DWORD dwRet;
WINMM_MMDevice *mmdevice;
MMRESULT mr;
TRACE("(%p, %d, %08lx, %08lx, %08x)\n",
lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
dwInstance, fdwOpen);
dwRet = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
if (dwRet != MMSYSERR_NOERROR)
return dwRet;
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
return MMSYSERR_BADDEVICEID;
if(!lphMix)
return MMSYSERR_INVALPARAM;
if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
return MMSYSERR_BADDEVICEID;
mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
if(mr != MMSYSERR_NOERROR)
return mr;
if(uDeviceID < g_outmmdevices_count){
mmdevice = &g_out_mmdevices[uDeviceID];
*lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
mmdevice->mixer_count);
}else{
mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
*lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
FALSE, mmdevice->mixer_count);
}
++mmdevice->mixer_count;
return MMSYSERR_NOERROR;
}
/**************************************************************************
@ -3065,7 +3384,8 @@ UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
UINT WINAPI mixerClose(HMIXER hMix)
{
TRACE("(%p)\n", hMix);
return MMSYSERR_INVALHANDLE;
return MMSYSERR_NOERROR;
}
/**************************************************************************
@ -3073,8 +3393,24 @@ UINT WINAPI mixerClose(HMIXER hMix)
*/
UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
{
TRACE("(%p, %p, %08x)\n", hmix, lpid, fdwID);
return MMSYSERR_INVALHANDLE;
WINMM_MMDevice *mmdevice;
TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
if(!lpid)
return MMSYSERR_INVALPARAM;
mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
if(!mmdevice)
return MMSYSERR_INVALHANDLE;
if(mmdevice->in_caps.szPname[0] != '\0')
*lpid += g_outmmdevices_count;
return MMSYSERR_NOERROR;
}
/**************************************************************************
@ -3083,12 +3419,31 @@ UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
DWORD fdwDetails)
{
TRACE("(%p, %p, %08x)\n", hmix, lpmcdW, fdwDetails);
WINMM_ControlDetails details;
HRESULT hr;
if(!lpmcdW || lpmcdW->cbStruct != sizeof(*lpmcdW))
TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
if(!lpmcdW)
return MMSYSERR_INVALPARAM;
return MMSYSERR_INVALHANDLE;
TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
hr = WINMM_StartDevicesThread();
if(FAILED(hr)){
ERR("Couldn't start the device thread: %08x\n", hr);
return MMSYSERR_ERROR;
}
details.hmix = hmix;
details.details = lpmcdW;
details.flags = fdwDetails;
return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
(DWORD_PTR)&details, 0);
}
/**************************************************************************
@ -3097,7 +3452,7 @@ UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW
UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
DWORD fdwDetails)
{
DWORD ret = MMSYSERR_NOTENABLED;
UINT ret = MMSYSERR_NOTSUPPORTED;
TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
@ -3160,7 +3515,7 @@ UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
DWORD ret;
unsigned int i;
TRACE("(%p, %p, %08x)\n", hmix, lpmlcA, fdwControls);
TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
@ -3218,18 +3573,256 @@ UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
return ret;
}
static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
MIXERCONTROLW *ctl, DWORD flags)
{
ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
ctl->cMultipleItems = 0;
lstrcpyW(ctl->szShortName, volumeW);
lstrcpyW(ctl->szName, volumeW);
ctl->Bounds.s1.dwMinimum = 0;
ctl->Bounds.s1.dwMaximum = 0xFFFF;
ctl->Metrics.cSteps = 192;
return MMSYSERR_NOERROR;
}
static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
MIXERCONTROLW *ctl, DWORD flags)
{
ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
ctl->cMultipleItems = 0;
lstrcpyW(ctl->szShortName, muteW);
lstrcpyW(ctl->szName, muteW);
ctl->Bounds.s1.dwMinimum = 0;
ctl->Bounds.s1.dwMaximum = 1;
ctl->Metrics.cSteps = 0;
return MMSYSERR_NOERROR;
}
/**************************************************************************
* mixerGetLineControlsW [WINMM.@]
*/
UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
DWORD fdwControls)
{
WINMM_MMDevice *mmdevice;
TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
if(!lpmlcW || lpmlcW->cbStruct != sizeof(*lpmlcW))
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
MIXER_GETLINECONTROLSF_ONEBYID |
MIXER_GETLINECONTROLSF_ONEBYTYPE |
MIXER_OBJECTF_HMIXER |
MIXER_OBJECTF_MIXER)){
WARN("Unknown GetLineControls flag: %x\n", fdwControls);
return MMSYSERR_INVALFLAG;
}
if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
return MMSYSERR_INVALPARAM;
return MMSYSERR_INVALHANDLE;
TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
TRACE("cControls: %u\n", lpmlcW->cControls);
mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
if(!mmdevice)
return MMSYSERR_INVALHANDLE;
switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
case MIXER_GETLINECONTROLSF_ALL:
if(lpmlcW->cControls != 2)
return MMSYSERR_INVALPARAM;
if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
return MMSYSERR_INVALPARAM;
if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
return MIXERR_INVALLINE;
WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
&lpmlcW->pamxctrl[0], fdwControls);
WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
&lpmlcW->pamxctrl[1], fdwControls);
return MMSYSERR_NOERROR;
case MIXER_GETLINECONTROLSF_ONEBYID:
if(lpmlcW->cControls != 1)
return MMSYSERR_INVALPARAM;
if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
return MMSYSERR_INVALPARAM;
if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
return MIXERR_INVALLINE;
if(lpmlcW->u.dwControlID == 0)
return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
lpmlcW->pamxctrl, fdwControls);
if(lpmlcW->u.dwControlID == 1)
return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
lpmlcW->pamxctrl, fdwControls);
return MMSYSERR_NOTSUPPORTED;
case MIXER_GETLINECONTROLSF_ONEBYTYPE:
if(lpmlcW->cControls != 1)
return MMSYSERR_INVALPARAM;
if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
return MMSYSERR_INVALPARAM;
if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
return MIXERR_INVALLINE;
if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
lpmlcW->pamxctrl, fdwControls);
if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
lpmlcW->pamxctrl, fdwControls);
return MMSYSERR_NOTSUPPORTED;
}
return MMSYSERR_NOTSUPPORTED;
}
static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
MIXERLINEW *info, DWORD flags)
{
BOOL is_out = TRUE;
if(mmdevice->in_caps.szPname[0] != '\0')
is_out = FALSE;
if(info->dwSource != 0)
return MIXERR_INVALLINE;
info->dwDestination = 0;
info->dwLineID = 0;
info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
info->cConnections = 0;
info->cControls = 2;
/* volume & mute always affect all channels, so claim 1 channel */
info->cChannels = 1;
info->Target.dwDeviceID = mmdev_index;
info->Target.wMid = ~0;
info->Target.wPid = ~0;
info->Target.vDriverVersion = 0;
lstrcpyW(info->szShortName, volumeW);
lstrcpyW(info->szName, mastervolumeW);
if(is_out){
info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
sizeof(info->Target.szPname));
}else{
info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
info->Target.szPname[0] = '\0';
}
return MMSYSERR_NOERROR;
}
static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
UINT mmdev_index, MIXERLINEW *info, DWORD flags)
{
BOOL is_out = TRUE;
if(mmdevice->in_caps.szPname[0] != '\0')
is_out = FALSE;
if(info->dwDestination != 0)
return MIXERR_INVALLINE;
info->dwSource = 0xFFFFFFFF;
info->dwLineID = 0xFFFF0000;
info->fdwLine = MIXERLINE_LINEF_ACTIVE;
info->cConnections = 1;
info->cControls = 2;
lstrcpyW(info->szShortName, volumeW);
lstrcpyW(info->szName, mastervolumeW);
info->Target.dwDeviceID = mmdev_index;
info->Target.wMid = ~0;
info->Target.wPid = ~0;
info->Target.vDriverVersion = 0;
if(is_out){
info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
info->cChannels = mmdevice->out_caps.wChannels;
info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
info->Target.szPname[0] = '\0';
}else{
info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
info->cChannels = mmdevice->in_caps.wChannels;
info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
sizeof(info->Target.szPname));
}
return MMSYSERR_NOERROR;
}
static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
UINT mmdev_index, MIXERLINEW *info, DWORD flags)
{
BOOL is_out = TRUE;
if(mmdevice->in_caps.szPname[0] != '\0')
is_out = FALSE;
if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
if(is_out)
return MIXERR_INVALLINE;
info->dwDestination = 0;
return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
}
if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
if(!is_out)
return MIXERR_INVALLINE;
info->dwDestination = 0;
return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
}
if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
if(is_out)
return MIXERR_INVALLINE;
info->dwSource = 0;
return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
}
if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
if(!is_out)
return MIXERR_INVALLINE;
info->dwSource = 0;
return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
}
TRACE("Returning INVALLINE on this component type: %u\n",
info->dwComponentType);
return MIXERR_INVALLINE;
}
static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
UINT mmdev_index, MIXERLINEW *info, DWORD flags)
{
BOOL is_out = TRUE;
if(mmdevice->in_caps.szPname[0] != '\0')
is_out = FALSE;
if(info->dwLineID == 0xFFFF0000){
info->dwDestination = 0;
return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
}
if(info->dwLineID == 0){
info->dwSource = 0;
return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
}
TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
return MIXERR_INVALLINE;
}
/**************************************************************************
@ -3237,9 +3830,57 @@ UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
*/
UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
{
TRACE("(%p, %p, %08x)\n", hmix, lpmliW, fdwInfo);
UINT mmdev_index;
WINMM_MMDevice *mmdevice;
return MMSYSERR_INVALHANDLE;
TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
return MMSYSERR_INVALPARAM;
TRACE("dwDestination: %u\n", lpmliW->dwDestination);
TRACE("dwSource: %u\n", lpmliW->dwSource);
TRACE("dwLineID: %u\n", lpmliW->dwLineID);
TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
MIXER_GETLINEINFOF_DESTINATION |
MIXER_GETLINEINFOF_LINEID |
MIXER_GETLINEINFOF_SOURCE |
MIXER_GETLINEINFOF_TARGETTYPE |
MIXER_OBJECTF_HMIXER |
MIXER_OBJECTF_MIXER)){
WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
return MMSYSERR_INVALFLAG;
}
mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
if(!mmdevice)
return MMSYSERR_INVALHANDLE;
switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
case MIXER_GETLINEINFOF_DESTINATION:
return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
fdwInfo);
case MIXER_GETLINEINFOF_SOURCE:
return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
case MIXER_GETLINEINFOF_COMPONENTTYPE:
return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
fdwInfo);
case MIXER_GETLINEINFOF_LINEID:
return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
case MIXER_GETLINEINFOF_TARGETTYPE:
FIXME("TARGETTYPE flag not implemented!\n");
return MIXERR_INVALLINE;
}
TRACE("Returning INVALFLAG on these flags: %lx\n",
fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
return MMSYSERR_INVALFLAG;
}
/**************************************************************************
@ -3251,7 +3892,7 @@ UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
MIXERLINEW mliW;
UINT ret;
TRACE("(%p, %p, %08x)\n", hmix, lpmliA, fdwInfo);
TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
return MMSYSERR_INVALPARAM;
@ -3317,9 +3958,35 @@ UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
DWORD fdwDetails)
{
TRACE("(%p, %p, %08x)\n", hmix, lpmcd, fdwDetails);
WINMM_ControlDetails details;
HRESULT hr;
return MMSYSERR_INVALHANDLE;
TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
if(!WINMM_StartDevicesThread())
return MMSYSERR_ERROR;
if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
MIXER_SETCONTROLDETAILSF_CUSTOM)
return MMSYSERR_NOTSUPPORTED;
if(!lpmcd)
return MMSYSERR_INVALPARAM;
TRACE("dwControlID: %u\n", lpmcd->dwControlID);
hr = WINMM_StartDevicesThread();
if(FAILED(hr)){
ERR("Couldn't start the device thread: %08x\n", hr);
return MMSYSERR_ERROR;
}
details.hmix = hmix;
details.details = lpmcd;
details.flags = fdwDetails;
return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
(DWORD_PTR)&details, 0);
}
/**************************************************************************
@ -3327,7 +3994,7 @@ UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
*/
DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
TRACE("(%p, %d, %08lx, %08lx)\n", hmix, uMsg, dwParam1, dwParam2);
TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
return MMSYSERR_INVALHANDLE;
return MMSYSERR_NOTSUPPORTED;
}