[GH-ISSUE #45] Add initial support for ALSA mixer #34

Closed
opened 2026-02-27 19:28:27 +03:00 by kerem · 11 comments
Owner

Originally created by @sashahilton00 on GitHub (Jan 29, 2018).
Original GitHub issue: https://github.com/librespot-org/librespot/issues/45

Issue by joerg-krause
Thursday Mar 09, 2017 at 09:35 GMT
Originally opened as https://github.com/plietar/librespot/pull/160


The ALSA mixer module implements a simple linear volume control. Note, that a linear volume control is not ideal for human audio experience as our ears respond to sound on an exponential curve.

The mixer also works fine with the ALSA softvol plugin which offers an logarithmic attenuation curve.

TODO: Use logarithmic curve for hardware mixers.


joerg-krause included the following code: https://github.com/plietar/librespot/pull/160/commits

Originally created by @sashahilton00 on GitHub (Jan 29, 2018). Original GitHub issue: https://github.com/librespot-org/librespot/issues/45 <a href="https://github.com/joerg-krause"><img src="https://avatars2.githubusercontent.com/u/6870896?v=4" align="left" width="96" height="96" hspace="10"></img></a> **Issue by [joerg-krause](https://github.com/joerg-krause)** _Thursday Mar 09, 2017 at 09:35 GMT_ _Originally opened as https://github.com/plietar/librespot/pull/160_ ---- The ALSA mixer module implements a simple linear volume control. Note, that a linear volume control is not ideal for human audio experience as our ears respond to sound on an exponential curve. The mixer also works fine with the ALSA softvol plugin which offers an logarithmic attenuation curve. **TODO**: Use logarithmic curve for hardware mixers. ---- _**[joerg-krause](https://github.com/joerg-krause)** included the following code: https://github.com/plietar/librespot/pull/160/commits_
kerem 2026-02-27 19:28:27 +03:00
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by jsopenrb
Wednesday Mar 15, 2017 at 19:24 GMT


Volume calculation fails for maximum volume values. If the range is 0..255 then at full volume the result will be 256 instead of 255. I suppose it should not use integer math because volume range can have very different values.

<!-- gh-comment-id:361261126 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/jsopenrb"><img src="https://avatars0.githubusercontent.com/u/19466116?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [jsopenrb](https://github.com/jsopenrb)** _Wednesday Mar 15, 2017 at 19:24 GMT_ ---- Volume calculation fails for maximum volume values. If the range is 0..255 then at full volume the result will be 256 instead of 255. I suppose it should not use integer math because volume range can have very different values.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by joerg-krause
Wednesday Mar 15, 2017 at 19:47 GMT


Not sure what do you mean. The mixer uses get_playback_volume_range() to get the actual range.

<!-- gh-comment-id:361261151 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/joerg-krause"><img src="https://avatars2.githubusercontent.com/u/6870896?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [joerg-krause](https://github.com/joerg-krause)** _Wednesday Mar 15, 2017 at 19:47 GMT_ ---- Not sure what do you mean. The mixer uses `get_playback_volume_range()` to get the actual range.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by jsopenrb
Wednesday Mar 15, 2017 at 20:10 GMT


Yes, but then you add one to that range, so for 0..255 at max volume the
resulting volume is 256 instead of 255.

On Wednesday, March 15, 2017, Jörg Krause notifications@github.com wrote:

Not sure what do you mean. The mixer uses get_playback_volume_range() to
get the actual range.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.<
https://ci5.googleusercontent.com/proxy/iVB_IHPdVhUghRc1w6iFL7sz5ZWPm2tTGJe3eGNbAdIXtfxoXO_m9LY1LBXbk8LjAZLtoB6Qm0lwbQj5Nvd6vxiwWKLKdSFklatn_7E-VvqIszXdbrwCIDhmwaAp8R3IsJPsVKwoafEP40x2IuAagk-vysavNQ=s0-d-e1-ft#https://github.com/notifications/beacon/ASkHhFFswsvqcGruje2ivD0jC6XfTkjsks5rmEBSgaJpZM4MX194.gif>

<!-- gh-comment-id:361261169 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/jsopenrb"><img src="https://avatars0.githubusercontent.com/u/19466116?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [jsopenrb](https://github.com/jsopenrb)** _Wednesday Mar 15, 2017 at 20:10 GMT_ ---- Yes, but then you add one to that range, so for 0..255 at max volume the resulting volume is 256 instead of 255. On Wednesday, March 15, 2017, Jörg Krause <notifications@github.com> wrote: > Not sure what do you mean. The mixer uses get_playback_volume_range() to get the actual range. > > — > You are receiving this because you commented. > Reply to this email directly, view it on GitHub, or mute the thread.< https://ci5.googleusercontent.com/proxy/iVB_IHPdVhUghRc1w6iFL7sz5ZWPm2tTGJe3eGNbAdIXtfxoXO_m9LY1LBXbk8LjAZLtoB6Qm0lwbQj5Nvd6vxiwWKLKdSFklatn_7E-VvqIszXdbrwCIDhmwaAp8R3IsJPsVKwoafEP40x2IuAagk-vysavNQ=s0-d-e1-ft#https://github.com/notifications/beacon/ASkHhFFswsvqcGruje2ivD0jC6XfTkjsks5rmEBSgaJpZM4MX194.gif>
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by joerg-krause
Wednesday Mar 15, 2017 at 20:34 GMT


Yes, 256 is the resolution if the range is from 0 to 255. The resolution is used to get the factor wrt the spotify resolution, which is 65536 / 256 = 256 in this case. If the max spotify volume of 65535 is set this results in 65535 / 256 = 255.99..., which is truncated to 255 because of the Integer. But I can see now what you mean. I need to add 1 to the spotify volume and substract it after the division: ((65535 + 1) / 256) - 1 = 255.

I will update the commit tomorrow.

<!-- gh-comment-id:361261189 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/joerg-krause"><img src="https://avatars2.githubusercontent.com/u/6870896?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [joerg-krause](https://github.com/joerg-krause)** _Wednesday Mar 15, 2017 at 20:34 GMT_ ---- Yes, 256 is the resolution if the range is from 0 to 255. The resolution is used to get the factor wrt the spotify resolution, which is 65536 / 256 = 256 in this case. If the max spotify volume of 65535 is set this results in 65535 / 256 = 255.99..., which is truncated to 255 because of the Integer. But I can see now what you mean. I need to add 1 to the spotify volume and substract it after the division: ((65535 + 1) / 256) - 1 = 255. I will update the commit tomorrow.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by joerg-krause
Thursday Mar 16, 2017 at 08:31 GMT


@jsopenrb I've updated the volume calculation...

<!-- gh-comment-id:361261218 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/joerg-krause"><img src="https://avatars2.githubusercontent.com/u/6870896?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [joerg-krause](https://github.com/joerg-krause)** _Thursday Mar 16, 2017 at 08:31 GMT_ ---- @jsopenrb I've updated the volume calculation...
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by jsopenrb
Thursday Mar 16, 2017 at 08:56 GMT


Better, but it will still fail for certain volume ranges like 190. I think the proper solution it to switch to float calculations. Not a big deal at the moment.

<!-- gh-comment-id:361261247 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/jsopenrb"><img src="https://avatars0.githubusercontent.com/u/19466116?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [jsopenrb](https://github.com/jsopenrb)** _Thursday Mar 16, 2017 at 08:56 GMT_ ---- Better, but it will still fail for certain volume ranges like 190. I think the proper solution it to switch to float calculations. Not a big deal at the moment.
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by plietar
Thursday Mar 16, 2017 at 13:33 GMT


Unfortunately I don't have much time at the moment to test this, it'll have to wait another week or two.

FWIW, I've previously used the following code to set the volume, which seemed to produced good results on my Pi's builtin audio output

    let (min, max) = elem.get_playback_db_range();
    let db_volume = if volume == 0 {
        min
    } else {
        let volume = volume as f64 / 65535.;
        let volume = volume.log10() * 6000.;
        MilliBel(volume as i64) + max
    };

    self.elem.set_playback_db_all(db_volume, alsa::Round::Ceil).unwrap();
<!-- gh-comment-id:361261260 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/plietar"><img src="https://avatars0.githubusercontent.com/u/1489775?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [plietar](https://github.com/plietar)** _Thursday Mar 16, 2017 at 13:33 GMT_ ---- Unfortunately I don't have much time at the moment to test this, it'll have to wait another week or two. FWIW, I've previously used the following code to set the volume, which seemed to produced good results on my Pi's builtin audio output ```rust let (min, max) = elem.get_playback_db_range(); let db_volume = if volume == 0 { min } else { let volume = volume as f64 / 65535.; let volume = volume.log10() * 6000.; MilliBel(volume as i64) + max }; self.elem.set_playback_db_all(db_volume, alsa::Round::Ceil).unwrap(); ```
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by joerg-krause
Thursday Mar 16, 2017 at 14:09 GMT


No, problem! Take your time. I'll check your volume setting...

<!-- gh-comment-id:361261286 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/joerg-krause"><img src="https://avatars2.githubusercontent.com/u/6870896?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [joerg-krause](https://github.com/joerg-krause)** _Thursday Mar 16, 2017 at 14:09 GMT_ ---- No, problem! Take your time. I'll check your volume setting...
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by jsopenrb
Friday Mar 17, 2017 at 19:42 GMT


That volume setting assumes that minimal dB value is mute which is not true for all cards.
get_normalized_volume/set_normalized_volume from alsa-utils have full implementation and handle both cases of minimum dB values:
http://git.alsa-project.org/?p=alsa-utils.git;a=blob;f=alsamixer/volume_mapping.c;h=1c0d7c45e6686239464e1b0bbc8983ea57f3914f;hb=HEAD
Shouldn't be too hard to rewrite those :)

<!-- gh-comment-id:361261309 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/jsopenrb"><img src="https://avatars0.githubusercontent.com/u/19466116?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [jsopenrb](https://github.com/jsopenrb)** _Friday Mar 17, 2017 at 19:42 GMT_ ---- That volume setting assumes that minimal dB value is mute which is not true for all cards. get_normalized_volume/set_normalized_volume from alsa-utils have full implementation and handle both cases of minimum dB values: http://git.alsa-project.org/?p=alsa-utils.git;a=blob;f=alsamixer/volume_mapping.c;h=1c0d7c45e6686239464e1b0bbc8983ea57f3914f;hb=HEAD Shouldn't be too hard to rewrite those :)
Author
Owner

@sashahilton00 commented on GitHub (Jan 29, 2018):

Comment by joerg-krause
Thursday Mar 23, 2017 at 08:12 GMT


snd_mixer_selem_get_playback_dB_range() only worka for hardware mixers, but not for the ALSA softvol plugin where snd_ctl_get_dB_range() has to be used to get the db range of the control element.

This is the relevant code, how the handling of hardware and software mixer is done in shairtport-sync: https://github.com/mikebrady/shairport-sync/blob/master/audio_alsa.c#L358-L396. The ALSA softvol plugin is handled as a special case of a hardware mixer. The software mixer is used if no hardware mixer and no ALSA softvol plugin is used.

Note, shairport-sync uses an own attenuation curve, which is mapped onto the mixer. That's make the volume handling more complex than it is necessary for librespot.

For hardware mixer, we need a logarithmic curve, but not for the ALSA softvol plugin, as it already maps the linear volume value to a logarithmic curve.

So, maybe we need to split the mixer into two mixers: alsa and alsasoftvol?

<!-- gh-comment-id:361261329 --> @sashahilton00 commented on GitHub (Jan 29, 2018): <a href="https://github.com/joerg-krause"><img src="https://avatars2.githubusercontent.com/u/6870896?v=4" align="left" width="48" height="48" hspace="10"></img></a> **Comment by [joerg-krause](https://github.com/joerg-krause)** _Thursday Mar 23, 2017 at 08:12 GMT_ ---- `snd_mixer_selem_get_playback_dB_range()` only worka for hardware mixers, but not for the ALSA softvol plugin where `snd_ctl_get_dB_range()` has to be used to get the db range of the control element. This is the relevant code, how the handling of hardware and software mixer is done in shairtport-sync: https://github.com/mikebrady/shairport-sync/blob/master/audio_alsa.c#L358-L396. The ALSA softvol plugin is handled as a special case of a hardware mixer. The software mixer is used if no hardware mixer and no ALSA softvol plugin is used. Note, shairport-sync uses an own attenuation curve, which is mapped onto the mixer. That's make the volume handling more complex than it is necessary for librespot. For hardware mixer, we need a logarithmic curve, but not for the ALSA softvol plugin, as it already maps the linear volume value to a logarithmic curve. So, maybe we need to split the mixer into two mixers: *alsa* and *alsasoftvol*?
Author
Owner

@ytzelf commented on GitHub (Jul 5, 2018):

Hi everyone,

Has anything been done on the topic? I see mixer still has only softmixer.rs (and mod.rs - what's that?). I'm having mpd and librespot on the same machine and the fact that librespot volume is not linked to alsa mixer is a bit annoying. Not much of a coder myself but maybe there's anything I can do to help?

Thanks a lot

<!-- gh-comment-id:402648165 --> @ytzelf commented on GitHub (Jul 5, 2018): Hi everyone, Has anything been done on the topic? I see mixer still has only softmixer.rs (and mod.rs - what's that?). I'm having mpd and librespot on the same machine and the fact that librespot volume is not linked to alsa mixer is a bit annoying. Not much of a coder myself but maybe there's anything I can do to help? Thanks a lot
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/librespot#34
No description provided.