'Twas brillig, and Clemens Ladisch at 08/10/10 15:42 did gyre and gimble:
Colin Guthrie wrote:
Hmm, just thinking about this (as I don't know the volume control logic particularly well in PA), the call snd_mixer_selem_set_playback_dB() is used with a dir argument of +1.
From what I understand, this would allow me to say "set the volume to 50dB" and due to the +1 dir, it should select -46.499999 ( because -46.5 mutes it).
In this case however, the value of -99999.999dB is ultimately selected (aka 0).
I'm wondering if the TLV fix actually affects how snd_mixer_selem_set_playback_dB() call works with the dir argument.
Uh, oh. snd_tlv_convert_from_dB() ignores the minimum-is-mute flag.
Ahh good, I analysed that kinda correctly then :D
Please try this hack:
--- alsa-lib/src/control/tlv.c +++ alsa-lib/src/control/tlv.c @@ -335,6 +335,9 @@ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, if (xdir > 0) v += (max - min) - 1; v = v / (max - min) + rangemin;
if (v == rangemin && xdir > 0 && (tlv[3] & 0x10000) &&
db_gain > SND_CTL_TLV_DB_GAIN_MUTE)
} return 0;v++; *value = v;
Didn't help sadly, but then looking at the code there it seems a little odd.
Firstly I tried this (extended) patch - I'm not sure it's needed but when the first one didn't work I tried to experiment a bit:
--- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -323,15 +323,20 @@ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, int min, max; min = tlv[2]; max = tlv[3]; - if (db_gain <= min) + if (db_gain <= min) { *value = rangemin; - else if (db_gain >= max) + if (xdir > 0 && (tlv[3] & 0x10000) && db_gain > SND_CTL_TLV_DB_GAIN_MUTE) + *value = rangemin + 1; + } else if (db_gain >= max) *value = rangemax; else { long v = (db_gain - min) * (rangemax - rangemin); if (xdir > 0) v += (max - min) - 1; v = v / (max - min) + rangemin; + if (v == rangemin && xdir > 0 && (tlv[3] & 0x10000) && + db_gain > SND_CTL_TLV_DB_GAIN_MUTE) + v++; *value = v; } return 0;
This also catches the case when I try to set the volume to e.g. -5000 (-50.00dB) as it is < min (which I presume is still -4650, but actually didn't check) then it will be set to rangemin and no further checks are done.
What is odd about this tho', is that the flag for min is mute (0x10000) is checked on tlv[3] which is also used here for the "max" value.
Is the use of position 3 correct (both here and on the kernel side) or perhaps the max line needs to be:
max = (tlv[3] & 0xffff);
instead? Either way it didn't help, but I thought the strangeness with the tlv[3] value was worth pointing out.
Many thanks for debugging this with me :)
Col
PS, happy to chat on IRC if you prefer - coling