winealsa: Get/Set ControlDetails

Maarten Lankhorst m.b.lankhorst at gmail.com
Thu Apr 19 04:21:05 CDT 2007


-------------- next part --------------
>From 43494e8dd48be09c2f39cfe9880913fade282f0d 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 14d0192..83c0ade 100644
--- a/dlls/winealsa.drv/mixer.c
+++ b/dlls/winealsa.drv/mixer.c
@@ -343,6 +343,7 @@ 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);
@@ -728,6 +729,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)
@@ -775,6 +792,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:
@@ -1040,6 +1473,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