After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 753304 - pulsesink: volume does not appear to be linear; violates playbin contract
pulsesink: volume does not appear to be linear; violates playbin contract
Status: RESOLVED INVALID
Product: GStreamer
Classification: Platform
Component: gst-plugins-good
1.5.2
Other Linux
: Normal normal
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2015-08-06 03:51 UTC by Dustin Spicuzza
Modified: 2015-09-09 13:34 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
pulse: Make volume property behave in a linear fashion (2.25 KB, patch)
2015-09-09 10:32 UTC, Arun Raghavan
rejected Details | Review

Description Dustin Spicuzza 2015-08-06 03:51:17 UTC
When I set the volume of a pulsesink, it does not appear to be linear, as documented at [1].

Let's start by listing the current system volume "pactl list sinks | grep Volume"... looks like 16%. Ok, play a file with playbin:

gst-launch-1.0 playbin uri=file:///file.ogg volume=0.1

When I do 'pactl list sink-inputs' it shows the volume set to 46%. Doesn't appear to be linear to me... a side effect of this is that if my application tries to set the volume to some N, the only really usable values for the user are somewhere between 0 and 0.1/0.2ish, as everything else will tend to be too loud.

Probably related to bug 680779.

[1] http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-base-plugins/html/gst-plugins-base-plugins-playbin.html#GstPlayBin--volume
Comment 1 Sebastian Dröge (slomo) 2015-08-06 09:15:14 UTC
If you set volume to 0.1, what is the volume in pactl? What is it for 0.2? What is it for 0.3 and 1.0?

Let's get an idea about how linear or not it is :)
Comment 2 Nicolas Dufresne (ndufresne) 2015-08-06 13:02:11 UTC
Also, if you can share you code setting the volume that could help too.
Comment 3 Dustin Spicuzza 2015-08-06 14:41:38 UTC
The code setting the volume is the gst-launch line above. ;)

I start out by setting the system volume to 3%, then running the command line above, incrementing the volume parameter each time.

volume=0.01, system is 22%
volume=0.02, system is 27%
volume=0.04, system is 34%
volume=0.08, system is 43%

volume=0.1, system is 46%
volume=0.2, system is 58%
volume=0.3, system is 67%
volume=0.4, system is 74%
volume=0.5, system is 79%
volume=0.6, system is 84%
volume=0.7, system is 89%
volume=0.8, system is 93%
volume=0.9, system is 97%
volume=1.0, system is 100%
Comment 4 Sebastian Dröge (slomo) 2015-08-06 14:53:00 UTC
That's definitely not linear :)
Comment 5 Dustin Spicuzza 2015-08-07 03:31:49 UTC
I agree. It surprises me that nobody else has ran into this problem, though I suspect it's really only significant when the "flat volume" option in PA is enabled -- which seems to be the default now.
Comment 6 Dustin Spicuzza 2015-08-07 04:17:52 UTC
Ok, so looking at pulsesink.c, we see that gst_pulsesink_set_volume calls gst_pulse_cvolume_from_linear, which calls pa_sw_volume_from_linear to convert a [0, 1.0] to pulse's [0, 0x10000]. 

Opening up pulse's source, in src/pulse/volume.c we see the following code for pa_sw_volume_from_linear:

return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM));

So, it's doing a cube root on the volume. I can run that through python:

for i in range(1,10):
    print (i/10.0) ** (1/3.0)

0.464158883361
0.584803547643
0.669432950082
0.736806299728
0.793700525984
0.843432665302
0.887904001743
0.928317766723
0.965489384606

Looks like we've found our culprit! So the docs for that says:

/** Convert a linear factor to a volume.  0.0 and less is muted while
 * 1.0 is PA_VOLUME_NORM.  This is only valid for software volumes! */

My guess is that the value that playbin is representing as a linear range (volume) is *not* the same as a "linear factor" in pulse's terminology... but then looking around at what to set the volume to, there doesn't seem to be a function to set it to x*PA_VOLUME_NORM, though that would seem to be the desired behavior.

Ok, so they have more docs (http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/WritingVolumeControlUIs/), and there's this:

"The actual mapping of the gradient of pa_volume_t in relation to the linear volume is hidden inside of PA. It's chosen in a way that makes a volume of 32768 actually feel "half as loud" as a volume of 65536. (Or at least we try to map it like this.) The actual algorithm is not considered part of the ABI. I take the freedom to change it any time. You should not assume that the mapping between linear/decibel volumes on one hand and pa_volume_t volumes on the other will always stay the same in future versions. "

The problem of course is that setting my system's volume to 50% when my slider says 0.1, seems a bit off.
Comment 7 Arun Raghavan 2015-09-09 10:32:49 UTC
Created attachment 310963 [details] [review]
pulse: Make volume property behave in a linear fashion

The volume property on our elements is supposed to apply as a purely
linear function, and the application should use the GstStreamVolume
interface to apply a cubic mapping to a UI slider if required.

Using pa_sw_volume_to/from_linear() does not work as this performs a
cubic mapping under the hood, which is not what we want.
Comment 8 Sebastian Dröge (slomo) 2015-09-09 11:21:20 UTC
Why does the from/to linear API for pulseaudio do cubic stuff under the hood? That seems very confusing
Comment 9 Arun Raghavan 2015-09-09 13:34:21 UTC
This one is my bad. The cubic mapping is done under the hood while storing the volume _but_, while applying the volume, it is undone. From an API perspective, pa_sw_volume_from_linear() is what we provide to apply a linear multiplication factor. So there is nothing to do here.

(this internal conversion exists because if you directly set the volume as slider_value * PA_VOLUME_NORM instead of using the from_linear() API, PA will apply a cubic mapping for you assuming that is what you wanted to do)
Comment 10 Arun Raghavan 2015-09-09 13:34:56 UTC
Comment on attachment 310963 [details] [review]
pulse: Make volume property behave in a linear fashion

As mentioned in the previous comment, this patch is wrong, and pa_sw_volume_from/to_linear() is what we should be using.