Draft: bluetooth: Handle muting over A2DP Absolute Volume

Marijn Suijten requested to merge MarijnS95/pulseaudio:bluetooth-muting into master

Excerpt from !239 (merged): this patch has been removed and submitted separately because it is:

  • Rather large;
  • Controversial;
  • Unfinished;
  • Not necessary for initial A2DP Absolute Volume support.

And this gives more visibility to the setup outlined in the commit description.

Muting is a special case that is explicit and separated from volume within PulseAudio, but merged together into a single variable over the A2DP AVRCP connection.
Most devices report either 0 or 1 as their lowest volume.

Note that capital Volume in the following paragraphs refer to the Volume property on org.bluez.MediaTransport1.

This commit deals with the following cases:

  1. When the PA stream is muted, notify the peer by setting Volume to 0. While the stream is muted real_volume should not be updated in the callback when Volume changes to <= 1, or unmuting will not return to the original volume.

  2. When locally changing stream volume, and muting is enabled, do not send updates to the peer. The peer should stick with Volume = 0.

  3. When locally changing stream volume (and sending that to the peer) any resulting updates on the Volume property should not turn on muting.

  4. When the peer changes Volume, turn off muting (if enabled) when Volume > 1.

  5. When the peer changes Volume, turn on muting when Volume <= 1.

Such an implementation matches what happens on an Android device. Muting sets the shared volume to 0, and upping the volume on the peer (in case of headphones which usually only have up/down buttons) will set it a single increment above that. Unmuting the stream from PA however will return the stream back to the original volume, and notify the peer of the same.

The discrepancy between merged and separate muting+volume results in a conflict between point 3. and 5.: The peer (and/or dbus API) always responds with the Volume property changing after PA writes it. If PA's stream volume is decreased to a point where the callback triggers with Volume <= 1 the stream is consequently muted, in accordance to point 5. This is especially problematic when decreasing the local volume too far (whether intentionally or not), as muting is not turned off by default when the stream volume is increased afterwards.

This case is dealt with by preventing any Volume lower than 2 to be sent to the device at any given point. Only the device itself can return a Volume lower than that to enable implicit muting.
However, when point 5. happens, it is important to note that it can only be unmuted by explicitly unmuting the stream from PA, or increasing the volume on the peer.

Merge request reports