#462 [lxpanel] volumealsa plugin volume mapping from alsamixer

lxpanel (10)

Use alsamixer's volume mapping for lxpanel volumealsa plugin.

"The mapping is designed so that the position in the interval is proportional to the volume as a human ear would perceive it (i.e., the position is the cubic root of the linear sample multiplication factor). For controls with a small range (24 dB or less), the mapping is linear in the dB values so that each step has the same size visually. Only for controls without dB information, a linear mapping of the hardware volume register values is used ... "

1 Attachments


lxpanel: lxpanel


  • The attached patch is the same as before, but can be applied onto current master.

  • The attached patch is the same as before, but can be applied onto current master (changing volume in popup works again).

    Last edit: Raimar Bühmann 2014-07-24
  • Generally it looks OK but few things are bothering me:
    1) using _GNU_SOURCE will limit platforms range where it can be build a lot.
    2) this behavior should be made optional (configurable).
    What would you say?
    Thank you very much.

  • Peter

    1) Using _GNU_SOURCE can be avoided by replacing exp10. I'm not sure if it is necessary though. _GNU_SOURCE is also used by alsamixer and amixer from alsa-utils package. Does volumealsa plugin need to work on more platforms than the two above? Are there any platforms that have alsa, but do not have alsa-utils? Please let me know your opinion and I make changes accordingly.
    2) You are right. Let the user choose. However it seems volumealsa plugin does not have a configuration dialog yet.

  • 1) You are right, thank you very much. ALSA is Linux-specific and since Linux is GNU, using _GNU_SOURCE should be OK.
    2) as matter of fact, each plugin has configuration, some plugins simply have it empty, and volumealsa also has one setting in it already. Yes, it does not have GUI to set options now, but that will be changed later, there are a lot of changes planned for it, so you can add a configuration option for plugin setup now and it will be included in config GUI when it is implemented. And you know, user can choose options without GUI - just edit config then restart LXPanel (lxpanelctl restart) is enough to let changes be applied.
    Thank you for your contribution.

  • Peter

    Here it is. Updated to current git and added "LinearVolumeMapping" option to change mapping from configuration file. Is there anything else I can do to improve it?

  • Thank you very much. I looked into that patch and I got lost trying to understand how volume is mapped to level, and what term "LinearVolumeScaling" means - ALSA linear, converted linear, or alternate converted linear. Multiple add, subtract, multiply, divide operations... I would be very glad if you make it clear like that:

    volume = level;
    if (vol->alternate_mapping)
        volume = altlevel2volume(level);

    on mapping displayed level to ALSA volume before snd_mixer_selem_set_playback_volume() call, and like that:

    level = volume;
    if (vol->alternate_mapping)
        level = volume2altlevel(volume);

    on mapping ALSA volume to displayed level after snd_mixer_selem_get_playback_volume() call.

    Reason to not use your mapping for me is simple - at least one of my machines has good logarithmic volume mixer, and may be some other have too. It's why it should be optional so people can turn it on if they are not satisfied with their sound mixer, and leave off if they are.

    Also I don't like that you replaced volumealsa_update_display() call with code duplication. Any code duplications should be avoided, not added.

    Thank you very much for your contribution.

  • Peter

    Those calculation, where you were lost are explained in alsamixer's man page as
    "the position is the cubic root of the linear sample multiplication factor."
    Please have a look at the attached image. The light blue line tries to show this
    in theory. Red and green points do this in practice by volume2level and
    level2volume functions suggested by you. Note that this figure is specific for
    my computer, which has volume range of only 0...63.

    It is good for you to have such a mixer. When increasing volume, my does simply
    add 1.5 dB for every step. As a result, I start to hear any sound around ~40%
    (as indicated by volumealsa plugin without patch). Since we have already agreed
    on making this optional, I believe everyone can choose according to his/her

    I can understand, that you don't like code duplication. However in this case
    it is necessary. When I set volume on the vertical scale
    volumealsa_update_display() is called, and that updates the position of
    the vertical scale, that I have just set to some level.

    The real problem begins here. Even though the vertical scale's value changed
    signal is blocked, when it is updated, it still emits the signal, when it is
    unblocked. I don't know if it is a bug or an intended behaviour. Anyways, it
    sets the volume, uptdates display, emits value changed signal, then sets the
    volume, updates display, emits signal and goes like this for a few cycles
    until the vertical scale would be repositioned to its current level, so it
    would not emit the value changed signal.

    This problem is hidden for a mapping like level=volume and volume=level. Even
    in this case it updates display twice for every time the user sets volume.
    This however causes more trouble if the mapping is different.

    We have a limited number of valid level values and volume values as well.
    As long as we use level=volume, volume=level mapping there is only one level
    value that maps to one volume and this can be converted back and forth as
    this is actually the same. Now if we make the slightest change in the mapping,
    then when we convert volume to level there will be more than one levels that
    map to the same volume and when we convert level to volume, there will be
    more than one volumes that map to the same level. If you look at the attached
    graph, you can see this more clear than I can explain.

    What this causes for users, let me show you on my example. I increase the
    level to 72. Volume is then set to 58 (my mixer's maximum is 63).
    volumealsa_update_display() checks for current volume and sets
    vertical scale to 74, that, as a result, emits value changed signal and sets
    volume to 57. Display is updated and vertical scale's level is moved to 70.
    Value changed signal emited again, but this time volume of 57 is set again and
    finally the vertical scale stops moving around.

    To prevent this, I changed it to update everything, but the scale. Since it was
    set by the user and that made this signal emitted, it does not need to be
    updated anyway. Of course, there are other options than code duplication. Can
    introduce reduced update display function or add another argument to it, which
    makes it skip the code to update vertical scale. For this latest patch I added
    a check to asound_set_volume() to return, when the volume to be set is set
    already. This way there is no code duplication. This solves the problem, but it
    still updates display twice per change of level. It would be better not to
    change the vertical scale when not necessary. Even better, stop it from emiting
    signals, when move it programmatically. Please consider which of my suggestions
    you find acceptable, or what other solution you may come up with.

    I had to rearrange initialisation of the plugin so config is available during
    initialisation of ALSA. I renamed this setting to "UseAlsamixerVolumeMapping".
    Hope this is clear wording enough. If not, please suggest one.

    I attached two versions of this patch here. One, "A" follows the original idea,
    with keeping as much of alsamixer's mapping as possible. The other one, "B" is
    the one you described in your last post. I think the first one is better, as it
    calculates dB value and sets it, while the second one calculates dB, asks ALSA
    what volume belongs to this dB value, then sets ALSA volume. So this second one
    runs a few extra rounds, but actually both do what these have to do. There is no
    difference from the users' perspective.

  • Thank you very much for explanations. I believe though, this kind of discussion is not suitable for this place and better be going in the lxde mailing list.

    Your explanation reveals obvious fact that mixer has latency. That is fairly reasonable knowing mixer is an analog device, and even can be connected via some bus (USB, etc.), thus you'll get changes after some delay (thus after signal was unblocked). Therefore volume changing application (our plugin) should take this latency into consideration.

    Case when volume was changed not from our side (external mixer application for example) is simple: we got a volume change, we updated the scale indicator.

    Case when plugin changes the volume becomes more tricky then:
    1) got a change from user (mouse, keys);
    2) update scale, freeze it now;
    3) calculate volume level, send to ALSA;
    4) ignore any changes from ALSA for some latency period;
    5) after that period (50-100-200 ms) read ALSA, update scale;
    6) accept ALSA changes again.

    So if we take latency into consideration then no duplicate updates will appear I believe. Anyway, it's better to continue discussion in the mailing list.
    Thank you very much. :)

  • Peter

    Please consider accepting one of the two latest patches, as these both address all your concerns:
    - do not reduce portability of the plugin
    - optional, can be turned on/off from config file
    - do not have code duplication, while taking duplicate updates into account
    Duplicate updates are not introduced by these patches, nor are specific to them.

  • Well, you said yourself duplicated update happens behind the scenes so I thought that it could be good to fix that along. But of course, that can be fixed later, and since you say patch A is better then I pushed it into GIT sources. Thank you very much for contributions!

    • status: open --> closed-accepted
    • assigned_to: Lonely Stranger