winealsa: Implement Get/SetControlDetails
Maarten Lankhorst
m.b.lankhorst at gmail.com
Fri Apr 20 13:35:52 CDT 2007
-------------- next part --------------
>From ff1f5d6eb59969a6caacca761cb753f6c84fbe94 Mon Sep 17 00:00:00 2001
From: maarten lankhorst <m.b.lankhorst at gmail.com>
Date: Fri, 13 Apr 2007 20:02:38 +0200
Subject: [PATCH] winealsa: Implement Get/SetControlDetails
---
dlls/winealsa.drv/mixer.c | 439 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 439 insertions(+), 0 deletions(-)
diff --git a/dlls/winealsa.drv/mixer.c b/dlls/winealsa.drv/mixer.c
index d791fe5..173e2dc 100644
--- a/dlls/winealsa.drv/mixer.c
+++ b/dlls/winealsa.drv/mixer.c
@@ -344,6 +344,7 @@ static void ALSA_MixerInit(void)
snd_ctl_card_info_alloca(&info);
mixdev[mixnum].lines = NULL;
mixdev[mixnum].callback = 0;
+ mixdev[mixnum].controls = NULL;
snprintf(cardind, sizeof(cardind), "%d", x);
card = snd_card_get_index(cardind);
@@ -748,6 +749,22 @@ static DWORD MIX_GetDevCaps(UINT wDevID, LPMIXERCAPS2W caps, DWORD_PTR parm2)
return MMSYSERR_NOERROR;
}
+/* convert win32 volume to alsa volume, and vice versa */
+static DWORD normalized(long value, long prevmax, long nextmax)
+{
+ double ret = (double)value;
+ ret *= (double)nextmax;
+ ret /= (double)prevmax;
+ ret += .5;
+ TRACE("%ld/%ld -> %ld/%ld\n", value, prevmax, (long)ret, nextmax);
+
+ if (ret > nextmax)
+ ret = nextmax;
+ else if (ret < 0)
+ ret = 0;
+
+ return (DWORD)ret;
+}
/* get amount of sources for dest */
static int getsrccntfromchan(mixer *mmixer, int dad)
@@ -795,6 +812,422 @@ static int getsrcfromline(mixer *mmixer, int line)
return 0;
}
+/* Get volume/muted/capture channel */
+static DWORD MIX_GetControlDetails(UINT wDevID, LPMIXERCONTROLDETAILS mctrld, DWORD_PTR flags)
+{
+ mixer *mmixer = MIX_GetMix(wDevID);
+ DWORD ctrl;
+ DWORD line;
+ control *ct;
+
+ if (!mctrld)
+ return MMSYSERR_INVALPARAM;
+
+ ctrl = mctrld->dwControlID;
+ line = ctrl/CONTROLSPERLINE;
+
+ if (mctrld->cbStruct != sizeof(*mctrld))
+ return MMSYSERR_INVALPARAM;
+
+ if (!mmixer)
+ return MMSYSERR_BADDEVICEID;
+
+ if (line < 0 || line >= mmixer->chans || !mmixer->controls[ctrl].enabled)
+ return MIXERR_INVALCONTROL;
+
+ ct = &mmixer->controls[ctrl];
+
+ flags &= MIXER_GETCONTROLDETAILSF_QUERYMASK;
+
+ switch (flags) {
+ case MIXER_GETCONTROLDETAILSF_VALUE:
+ TRACE("MIXER_GETCONTROLDETAILSF_VALUE (%d/%d)\n", ctrl, line);
+ switch (ct->c.dwControlType)
+ {
+ case MIXERCONTROL_CONTROLTYPE_VOLUME:
+ {
+ long min = 0, max = 0, vol = 0;
+ int chn;
+ LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
+ snd_mixer_elem_t * elem = mmixer->lines[line].elem;
+
+ if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED))
+ {
+ WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
+
+ mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)mctrld->paDetails;
+
+ if (mctrld->cChannels != 1 && mmixer->lines[line].chans != mctrld->cChannels)
+ {
+ WARN("Unsupported cChannels (%d instead of %d)\n", mctrld->cChannels, mmixer->lines[line].chans);
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (mmixer->lines[line].capt && snd_mixer_selem_has_capture_volume(elem)) {
+ snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ if (snd_mixer_selem_has_capture_channel(elem, chn))
+ {
+ snd_mixer_selem_get_capture_volume(elem, chn, &vol);
+ mcdu->dwValue = normalized(vol - min, max, 65535);
+ if (mctrld->cChannels == 1)
+ break;
+ ++mcdu;
+ }
+ } else {
+ snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
+
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ if (snd_mixer_selem_has_playback_channel(elem, chn))
+ {
+ snd_mixer_selem_get_playback_volume(elem, chn, &vol);
+ mcdu->dwValue = normalized(vol - min, max, 65535);
+ if (mctrld->cChannels == 1)
+ break;
+ ++mcdu;
+ }
+ }
+
+ return MMSYSERR_NOERROR;
+ }
+
+ case MIXERCONTROL_CONTROLTYPE_ONOFF:
+ case MIXERCONTROL_CONTROLTYPE_MUTE:
+ {
+ LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ int chn, ival;
+ snd_mixer_elem_t * elem = mmixer->lines[line].elem;
+
+ if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
+ {
+ WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
+
+ mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)mctrld->paDetails;
+
+ if (line == 1)
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ {
+ if (!snd_mixer_selem_has_capture_channel(elem, chn))
+ continue;
+ snd_mixer_selem_get_capture_switch(elem, chn, &ival);
+ break;
+ }
+ else
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ {
+ if (!snd_mixer_selem_has_playback_channel(elem, chn))
+ continue;
+ snd_mixer_selem_get_playback_switch(elem, chn, &ival);
+ break;
+ }
+
+ mcdb->fValue = !ival;
+ TRACE("=> %s\n", mcdb->fValue ? "on" : "off");
+ return MMSYSERR_NOERROR;
+ }
+ case MIXERCONTROL_CONTROLTYPE_MIXER:
+ case MIXERCONTROL_CONTROLTYPE_MUX:
+ {
+ LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ int x, i=0, ival = 0, chn;
+
+ if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
+ {
+ WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
+
+ mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)mctrld->paDetails;
+
+ for (x = 0; x<mmixer->chans; ++x)
+ if (line != x && mmixer->lines[x].dst == line)
+ {
+ ival = 0;
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ {
+ if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
+ continue;
+ snd_mixer_selem_get_capture_switch(mmixer->lines[x].elem, chn, &ival);
+ if (ival)
+ break;
+ }
+ if (i >= mctrld->u.cMultipleItems)
+ {
+ TRACE("overflow\n");
+ return MMSYSERR_INVALPARAM;
+ }
+ TRACE("fVal[%i] = %sselected\n", i, (!ival ? "un" : ""));
+ mcdb[i++].fValue = ival;
+ }
+ break;
+ }
+ default:
+
+ FIXME("Unhandled controltype %s\n", getControlType(ct->c.dwControlType));
+ return MMSYSERR_INVALPARAM;
+ }
+ return MMSYSERR_NOERROR;
+
+ case MIXER_GETCONTROLDETAILSF_LISTTEXT:
+ TRACE("MIXER_GETCONTROLDETAILSF_LISTTEXT (%d)\n", ctrl);
+
+ if (ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX || ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MIXER)
+ {
+ LPMIXERCONTROLDETAILS_LISTTEXTW mcdlt = (LPMIXERCONTROLDETAILS_LISTTEXTW)mctrld->paDetails;
+ int i, j;
+
+ for (i = j = 0; j < mmixer->chans; ++j)
+ if (j != line && mmixer->lines[j].dst == line)
+ {
+ if (i > mctrld->u.cMultipleItems)
+ return MMSYSERR_INVALPARAM;
+ mcdlt->dwParam1 = j;
+ mcdlt->dwParam2 = mmixer->lines[j].component;
+ lstrcpynW(mcdlt->szName, mmixer->lines[j].name, sizeof(mcdlt->szName) / sizeof(WCHAR));
+ TRACE("Adding %i as %s\n", j, debugstr_w(mcdlt->szName));
+ ++i; ++mcdlt;
+ }
+ if (i < mctrld->u.cMultipleItems)
+ return MMSYSERR_INVALPARAM;
+ return MMSYSERR_NOERROR;
+ }
+ FIXME ("Imagine this code being horribly broken and incomplete, introducing: reality\n");
+ return MMSYSERR_INVALPARAM;
+
+ default:
+ WARN("Unknown flag (%08lx)\n", flags);
+ return MMSYSERR_INVALPARAM;
+ }
+}
+
+/* Set volume/capture channel/muted for control */
+static DWORD MIX_SetControlDetails(UINT wDevID, LPMIXERCONTROLDETAILS mctrld, DWORD_PTR flags)
+{
+ mixer *mmixer = MIX_GetMix(wDevID);
+ DWORD ctrl, line, i;
+ control *ct;
+ snd_mixer_elem_t * elem;
+
+ if (!mctrld)
+ return MMSYSERR_INVALPARAM;
+
+ ctrl = mctrld->dwControlID;
+ line = ctrl/CONTROLSPERLINE;
+
+ if (mctrld->cbStruct != sizeof(*mctrld))
+ {
+ WARN("Invalid size of mctrld %d instead of %d\n", mctrld->cbStruct, sizeof(*mctrld));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (!mmixer)
+ return MMSYSERR_BADDEVICEID;
+
+ if (line < 0 || line >= mmixer->chans)
+ {
+ WARN("Invalid line id: %d not in range of 0-%d\n", line, mmixer->chans-1);
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (!mmixer->controls[ctrl].enabled)
+ {
+ WARN("Control %d not enabled\n", ctrl);
+ return MIXERR_INVALCONTROL;
+ }
+
+ ct = &mmixer->controls[ctrl];
+ elem = mmixer->lines[line].elem;
+ flags &= MIXER_SETCONTROLDETAILSF_QUERYMASK;
+
+ switch (flags) {
+ case MIXER_SETCONTROLDETAILSF_VALUE:
+ TRACE("MIXER_SETCONTROLDETAILSF_VALUE (%d)\n", ctrl);
+ break;
+
+ default:
+ WARN("Unknown flag (%08lx)\n", flags);
+ return MMSYSERR_INVALPARAM;
+ }
+
+ switch (ct->c.dwControlType)
+ {
+ case MIXERCONTROL_CONTROLTYPE_VOLUME:
+ {
+ long min = 0, max = 0;
+ int chn;
+ LPMIXERCONTROLDETAILS_UNSIGNED mcdu;
+ snd_mixer_elem_t * elem = mmixer->lines[line].elem;
+
+ if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_UNSIGNED))
+ {
+ WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_UNSIGNED));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ if (mctrld->cChannels != 1 && mmixer->lines[line].chans != mctrld->cChannels)
+ {
+ WARN("Unsupported cChannels (%d instead of %d)\n", mctrld->cChannels, mmixer->lines[line].chans);
+ return MMSYSERR_INVALPARAM;
+ }
+
+ TRACE("%s MIXERCONTROLDETAILS_UNSIGNED[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
+ mcdu = (LPMIXERCONTROLDETAILS_UNSIGNED)mctrld->paDetails;
+
+ for (chn=0; chn<mctrld->cChannels;++chn)
+ {
+ TRACE("Chan %d value %d\n", chn, mcdu[chn].dwValue);
+ }
+
+ /* There isn't always a capture volume, so in that case change playback volume */
+ if (mmixer->lines[line].capt && snd_mixer_selem_has_capture_volume(elem))
+ {
+ snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
+
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ if (snd_mixer_selem_has_capture_channel(elem, chn))
+ {
+ snd_mixer_selem_set_capture_volume(elem, chn, min+normalized(mcdu->dwValue, 65535, max));
+ if (mctrld->cChannels != 1)
+ mcdu++;
+ }
+ }
+ else
+ {
+ snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
+
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ if (snd_mixer_selem_has_playback_channel(elem, chn))
+ {
+ snd_mixer_selem_set_playback_volume(elem, chn, min+normalized(mcdu->dwValue, 65535, max));
+ if (mctrld->cChannels != 1)
+ mcdu++;
+ }
+ }
+
+ break;
+ }
+ case MIXERCONTROL_CONTROLTYPE_MUTE:
+ case MIXERCONTROL_CONTROLTYPE_ONOFF:
+ {
+ LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+
+ if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
+ {
+ WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
+
+ mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)mctrld->paDetails;
+ if (line == 1) /* Mute/unmute capturing */
+ for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
+ {
+ if (snd_mixer_selem_has_capture_channel(elem, i))
+ snd_mixer_selem_set_capture_switch(elem, i, !mcdb->fValue);
+ }
+ else
+ for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i)
+ if (snd_mixer_selem_has_playback_channel(elem, i))
+ snd_mixer_selem_set_playback_switch(elem, i, !mcdb->fValue);
+ break;
+ }
+
+ case MIXERCONTROL_CONTROLTYPE_MIXER:
+ case MIXERCONTROL_CONTROLTYPE_MUX:
+ {
+ LPMIXERCONTROLDETAILS_BOOLEAN mcdb;
+ int x, i=0, chn;
+ int didone = 0, canone = (ct->c.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX);
+
+ if (mctrld->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
+ {
+ WARN("invalid parameter: cbDetails != %d\n", sizeof(MIXERCONTROLDETAILS_BOOLEAN));
+ return MMSYSERR_INVALPARAM;
+ }
+
+ TRACE("%s MIXERCONTROLDETAILS_BOOLEAN[%u]\n", getControlType(ct->c.dwControlType), mctrld->cChannels);
+ mcdb = (LPMIXERCONTROLDETAILS_BOOLEAN)mctrld->paDetails;
+
+ for (x=i=0; x < mmixer->chans; ++x)
+ if (line != x && mmixer->lines[x].dst == line)
+ {
+ TRACE("fVal[%i] (%s) = %i\n", i, debugstr_w(mmixer->lines[x].name), mcdb[i].fValue);
+ if (i >= mctrld->u.cMultipleItems)
+ {
+ TRACE("Too many items to fit, overflowing\n");
+ return MIXERR_INVALVALUE;
+ }
+ if (mcdb[i].fValue && canone && didone)
+ {
+ TRACE("Nice try, but it's not going to work\n");
+ elem_callback(mmixer->lines[1].elem, SND_CTL_EVENT_MASK_VALUE);
+ return MIXERR_INVALVALUE;
+ }
+ if (mcdb[i].fValue)
+ didone = 1;
+ ++i;
+ }
+
+ if (canone && !didone)
+ {
+ TRACE("Nice try, this is not going to work either\n");
+ elem_callback(mmixer->lines[1].elem, SND_CTL_EVENT_MASK_VALUE);
+ return MIXERR_INVALVALUE;
+ }
+
+ for (x = i = 0; x<mmixer->chans; ++x)
+ if (line != x && mmixer->lines[x].dst == line)
+ {
+ if (mcdb[i].fValue)
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ {
+ if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
+ continue;
+ snd_mixer_selem_set_capture_switch(mmixer->lines[x].elem, chn, mcdb[i].fValue);
+ }
+ ++i;
+ }
+
+ /* If it's a MUX, it means that only 1 channel can be selected
+ * and the other channels are unselected
+ *
+ * For MIXER multiple sources are allowed, so unselect here
+ */
+ if (canone)
+ break;
+
+ for (x = i = 0; x<mmixer->chans; ++x)
+ if (line != x && mmixer->lines[x].dst == line)
+ {
+ if (!mcdb[i].fValue)
+ for (chn = 0; chn <= SND_MIXER_SCHN_LAST; ++chn)
+ {
+ if (!snd_mixer_selem_has_capture_channel(mmixer->lines[x].elem, chn))
+ continue;
+ snd_mixer_selem_set_capture_switch(mmixer->lines[x].elem, chn, mcdb[i].fValue);
+ }
+ ++i;
+ }
+ break;
+ }
+ default:
+ FIXME("Unhandled type %s\n", getControlType(ct->c.dwControlType));
+ return MMSYSERR_INVALPARAM;
+ }
+ return MMSYSERR_NOERROR;
+}
+
/* Here we give info over the source/dest line given by dwSource+dwDest or dwDest, respectively
* It is also possible that a line is found by componenttype or target type, latter is not implemented yet
* Most important values returned in struct:
@@ -1060,6 +1493,12 @@ DWORD WINAPI ALSA_mxdMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
case MXDM_GETLINECONTROLS:
ret = MIX_GetLineControls(wDevID, (LPMIXERLINECONTROLSW)dwParam1, dwParam2); break;
+ case MXDM_GETCONTROLDETAILS:
+ ret = MIX_GetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); break;
+
+ case MXDM_SETCONTROLDETAILS:
+ ret = MIX_SetControlDetails(wDevID, (LPMIXERCONTROLDETAILS)dwParam1, dwParam2); break;
+
case MXDM_GETNUMDEVS:
ret = cards; break;
--
1.4.4.2
More information about the wine-patches
mailing list