[pulseaudio-discuss] RFC: New volume functionality for PulseAudio

David Henningsson david.henningsson at canonical.com
Fri Feb 14 11:55:14 CET 2014

On 02/14/2014 10:49 AM, Tanu Kaskinen wrote:
> On Fri, 2014-02-14 at 06:23 +0100, David Henningsson wrote:
>> On 02/11/2014 03:03 PM, Tanu Kaskinen wrote:
>>> Hi all!
>>> I'm working (together with other people at Intel) on adding system
>>> volume control functionality to Tizen (primarily the IVI profile,
>>> although there's nothing inherently IVI specific here). Tizen uses web
>>> APIs for the UI, and since there are no existing web API standards for
>>> this functionality, we need to design the API from scratch. The current
>>> design can be seen at [1], in case you're interested (it's good
>>> background material for this discussion). What I'd like to discuss in
>>> this thread, however, is not the web API as such, but the use cases
>>> behind that design and what those use cases ultimately require from
>>> PulseAudio. (Feedback for the web API proposal is welcome too, but I
>>> think that should be discussed in a separate thread.)
>>> We want to support:
>>>  * A single main output volume control. Think of things like the Gnome
>>> volume applet: when you click the applet icon, you get a single volume
>>> slider for controlling the "current output", whatever that means. The
>>> Gnome volume applet implements the main volume by using the default sink
>>> volume, but we'd like to make the main volume contextual, so that the
>>> main volume can control different things at different times. Ideally the
>>> contextual logic would be implemented in PulseAudio (by a policy
>>> module), not in the volume applet. The volume applet should know when
>>> the main volume controls e.g. the headphones volume or the phone call
>>> volume.
>>>  * Volume classes. A car manufacturer may want to present only two
>>> volume controls for the user: "system" and "entertainment". A policy
>>> module in PulseAudio should classify streams based on whether their
>>> volume should be controlled by the "system" volume or by the
>>> "entertainment" volume. The volume classification should be separate
>>> from the routing classification (module-stream-restore, among other
>>> problems, doesn't allow separate classification for volume and routing).
>>>  * Fine-grained volume UIs that show all application and device volumes,
>>> like pavucontrol. PulseAudio already handles this pretty well.
>>> The first question: do you agree that PulseAudio should support those
>>> use cases in one way or another?
>>> If the answer to the first question is "yes", then we can start
>>> discussing the details of the implementation. How would you implement
>>> the support for the listed requirements?
>>> I propose that we introduce a new concept: volume control. It would be a
>>> separate object that could be referenced from other objects. For
>>> example, all devices would have their own volume control objects. The
>>> main volume could point to some of the device volume controls, or any
>>> other volume control object. Streams could have their own volume control
>>> objects, or multiple streams could reference the shared volume control
>>> of their volume class.
>>> introspect.h could have something like this:
>>> typedef struct pa_volume_control_info {
>>>     uint32_t index;
>>>     char *name;
>>>     char *description;
>>>     pa_volume_t volume;
>>>     pa_channel_map channel_map;
>>>     double *balance; /* per-channel balance, values between 0.0 and 1.0 */
>>> } pa_volume_control_info;
>>> The alternative for separate volume controls is that we keep embedding
>>> the volume in various other objects, like sinks, sources, ports (which,
>>> btw, are currently missing volume control altogether when they are
>>> inactive), sink inputs and source outputs. This is certainly a possible
>>> approach, but I very much prefer the separate volume control object idea.
>>> Benefits of separate volume control objects:
>>>  * Solves the problem of figuring out what the main volume controls. For
>>> example, the Gnome volume applet shows a headphone icon when the main
>>> volume controls the headphones volume. The volume applet can check if
>>> the main volume points to the same volume control object as the headset
>>> port.
>>>  * If two streams share the same volume, a pavucontrol-like application
>>> can group the two streams under one volume slider.
>>>  * Opportunity to separate the overall volume from the channel balance.
>>> The pa_cvolume volume representation loses all balance information when
>>> the user slides the overall volume to zero.
>>>  * Flexibility to easily add new volume control objects for whatever
>>> purpose in the future. For example, there could be a volume debug mode
>>> where the individual alsa volume elements would be exposed as read-only
>>> volume controls, or separate read-only volume control objects for the
>>> "reference", "real" and "soft" components of sink volumes. pactl could
>>> show and control all volumes without understanding what the volumes are
>>> associated with.
>>> Drawbacks of separate volume control objects:
>>>  * Duplication in the API. The old volume functionality in the public
>>> API can't be removed, so there would be two ways to control most volumes.
>>> The main volume would be added to pa_server_info as a new field (index
>>> referencing the appropriate volume control object).
>>> The volume classes would be separate objects:
>>> typedef struct pa_volume_class {
>>>     uint32_t index;
>>>     char *name;
>>>     char *description;
>>>     uint32_t volume_control;
>>> } pa_volume_class;
>>> I would add the volume control objects, the main volume and the volume
>>> classes to the core API, because this is functionality that is used
>>> pretty much everywhere (desktop environments, mobile devices, IVI
>>> systems), but I'm open to implementing this in a module too, with a
>>> protocol extension.
>>> [1] https://wiki.tizen.org/wiki/User:Tanuk/AudioSystemAPI
>> Hi Tanu,
>> my gut feeling would say:
>> The volume objects, I think I understand the usefulness of those. I also
>> acknowledge the problem of having balance information lost when main
>> slider is zero.
> Excellent!
>> However we currently have the pa_cvolume type as well, which is sort of
>> the same as the new fields volume+channel_map+balance. I wonder if it
>> makes sense to group these together and call it pa_cvolume2 or something.
> Does the grouping have any benefits? If pa_volume_control_info has a
> field with this pa_cvolume2 type, then it could be argued that for
> symmetry there should also be function
> pa_context_set_volume_control_volume() that takes a pa_cvolume2 struct
> as a parameter. However, when an application sets the attributes of a
> volume control object, usually it will only modify the overall volume or
> the balance, not both, and the channel map is immutable, so passing a
> pa_cvolume2 struct to the setter function is inconvenient.

A volume control UI with presets would typically set both. So would any
volume control UI do that does not look exactly the way you anticipate.

E g, pavucontrol has two sliders, one left and one right, and gnome
volume control has main volume + balance.

The grouping would also enable potential helper functions such as those
we have for pa_cvolume today.

> If we forget about the symmetry argument and pass something else than
> pa_cvolume2 to pa_context_set_volume_control_volume(), then the only
> purpose of pa_cvolume2 seems to be to reduce the number of fields in the
> pa_volume_control_info struct.
>> And as somebody already pointed out, having either a global or
>> per-channel mute in that struct as well would probably be a good thing too.
> I'd like to keep mute controls completely separate from volume. They are
> not inherently linked to each other (even though they very often appear
> together).

I don't understand this argument. A mute control *is* a volume control
(with two distinct values). In this case, it's a way to avoid volume
information lost on muted audio.
Well, and that everything said so far in this thread would apply equally
well to mute controls, so I guess you will come back in a few months and
want to add another core object for mute controls if we don't get it in
now ;-)

> Another question is that should mute controls have per-channel
> granularity. I don't remember anyone ever asking for that feature, and
> it would add complexity to the mute controls (clients need to deal with
> partially-muted controls, and we need to attach channel map information
> to the controls). On the other hand, it doesn't seem entirely pointless
> as a concept either.

Yeah, I'm not sure whether mute should be global or per channel either.
More opinions?

>> If you want main volume to represent one of these, you might want to add
>> a property list too, to contain the icon property (the UI could show a
>> headphone icon when controlling headphone volume).
> Did this paragraph contain implied approval for adding main input and
> output volume control references to the pa_server_info struct?

Sure, we have default sink and source there already, so it seems to make
sense to me.

> What would you think about putting the icon name as a separate field to
> the pa_volume_control_info struct? I'm not against adding a proplist for
> undocumented metadata, but let's not abuse the proplist for things that
> are part of the documented stable API (any more than what we already
> do).

IIRC, icon names are in proplists everywhere else, so having them in a
proplist here as well would avoid inconsistency.

>> The volume classes however seem more like routing/policy implementation
>> internals. They could be kept inside the routing/policy module, exported
>> through property lists, or something like that. Either that, or I didn't
>> really understand the concept.
> Applications should have a unified way to query what volume classes
> exist and what volume control they are associated with. An example of a
> volume class is the event volume class. If applications want to access
> that, they currently need to use a module-stream-restore specific API,
> even though the concept of an event volume class is not specific to
> module-stream-restore.

The concept "volume class" does not exist in module-stream-restore, at
least not with that name.

Could you give a clear definition of what "volume classes" are? If it's
just a way of grouping volume control objects, then volume control
objects could just have some property of what class they belong to.

David Henningsson, Canonical Ltd.

More information about the pulseaudio-discuss mailing list