[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 6 commits: bluetooth: Move HSP_MAX_GAIN to header for reuse in n_volume_steps
PulseAudio Marge Bot (@pulseaudio-merge-bot)
gitlab at gitlab.freedesktop.org
Mon May 17 14:53:02 UTC 2021
PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio
Commits:
c098a75d by Marijn Suijten at 2021-05-17T14:50:03+00:00
bluetooth: Move HSP_MAX_GAIN to header for reuse in n_volume_steps
Instead of hardcoding the number `16`, use `HSP_MAX_GAIN + 1`.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
- - - - -
710a35cd by Marijn Suijten at 2021-05-17T14:50:03+00:00
bluetooth: Update source software volume on AVRCP SetAbsoluteVolume
The A2DP spec mandates that the audio rendering device - the device
receiving audio, in our case a `pa_source` - is responsible for
performing attenuation:
AVRCP v1.6.2, §5.8:
The SetAbsoluteVolume command is used to set an absolute volume to be used by the rendering device.
BlueZ models this call as a change of the `Volume` property on the
`org.bluez.MediaTransport1` interface. Supporting Absolute Volume is
optional but BlueZ unconditionally reports feature category 2 in its
profile, mandating support. Hence remote devices (ie. a phone) playing
back audio to a machine running PulseAudio assume volume is to be
changed through SetAbsoluteVolume, without performing any local
attenuation.
Future changes will implement this feature the other way around: setting
an initial value for the `Volume` property as well as propagating
`pa_source` volume changes back to the peer.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
- - - - -
ac21b07a by Marijn Suijten at 2021-05-17T14:50:03+00:00
bluetooth: Synchronize AVRCP Absolute Volume with A2DP sink
Like the previous commit this handles `Volume` property changes but
applies them to an A2DP sink instead of source stream. As mentioned in
the AVRCP spec v1.6.2 §5.8 the rendering device (A2DP sink) is
responsible for performing volume attenuation meaning PulseAudio should
pass through audio as-is without performing any attenuation in SW.
Setting a valid pointer to `set_sink_volume` and returning `true` from
`should_attenuate_volume` attaches a hardware callback to `pa_sink` such
that no volume attenuation is performed anymore.
In addition to receiving volume change notifications it is also possible
to control remote volume by writing a new value to the DBus property.
This is especially useful when playing back to in-ear audio devices
which usually lack physical buttons to adjust the final volume on the
sink.
While software volume (used before this patch) is generally fine it is
annoying to crank it up all the way to 100% when a previous connection
to a different device left saved volume on the peer at a low volume.
Providing this bidirectional synchronization is most natural to users
who wish to use physical controls on their headphones, are used to this
from their smartphone, or aforementioned volume mismatches where both PA
as source and the peer as sink/rendering device are performing
attenutation.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
- - - - -
c6b77153 by Marijn Suijten at 2021-05-17T14:50:03+00:00
bluetooth: Report a2dp_source volume changes to the source device
Write the current volume to the `Volume` DBus property to keep the
volume on the remote in sync. Without this the remote device shows the
wrong volume, and any attempts to change it will cause an unexpected
jump when the local volume has also been adjusted.
Thanks to prior investments to improve volume synchronization, setting
up callbacks and sending initial volume to the peer for HFP/HSP
implementing this feature is as easy as unconditionally assigning a
valid function to `set_source_volume`. `source_setup_volume_callback`
is already responsible for attaching a `SOURCE_VOLUME_CHANGED` hook and
sending initial (restored) volume to the peer (signifying support for
Absolute Volume - if not derived from the presence of FEATURE_CATEGORY_2
on the profile yet).
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
- - - - -
25426bc0 by Marijn Suijten at 2021-05-17T14:50:03+00:00
bluetooth: Delay A2DP Absolute Volume setup until property is available
The Volume property on org.bluez.MediaTransport1 is required to utilize
Absolute Volume, but it will only become availabe if the peer device
supports the feature. This happens asynchronously somewhere after the
transport itself has been acquired, after which the callbacks are
attached and software volume is reset.
To prevent race conditions availability of the property is also checked
on startup through a "Get" call.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
- - - - -
d9db47bd by Marijn Suijten at 2021-05-17T14:50:03+00:00
bluetooth: Add avrcp_absolute_volume module flag for disablement
Not all peers might work fine with Absolute Volume, provide the user
with an option to disable it without impairing other AVRCP-related
commands like media status and playback controls.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
- - - - -
5 changed files:
- src/modules/bluetooth/backend-native.c
- src/modules/bluetooth/bluez5-util.c
- src/modules/bluetooth/bluez5-util.h
- src/modules/bluetooth/module-bluez5-device.c
- src/modules/bluetooth/module-bluez5-discover.c
Changes:
=====================================
src/modules/bluetooth/backend-native.c
=====================================
@@ -37,8 +37,6 @@
#include "bluez5-util.h"
#include "bt-codec-msbc.h"
-#define HSP_MAX_GAIN 15
-
struct pa_bluetooth_backend {
pa_core *core;
pa_dbus_connection *connection;
=====================================
src/modules/bluetooth/bluez5-util.c
=====================================
@@ -101,6 +101,32 @@
" </interface>" \
"</node>"
+static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
+ pa_volume_t volume = (pa_volume_t) ((
+ gain * PA_VOLUME_NORM
+ /* Round to closest by adding half the denominator */
+ + A2DP_MAX_GAIN / 2
+ ) / A2DP_MAX_GAIN);
+
+ if (volume > PA_VOLUME_NORM)
+ volume = PA_VOLUME_NORM;
+
+ return volume;
+}
+
+static uint16_t volume_to_a2dp_gain(pa_volume_t volume) {
+ uint16_t gain = (uint16_t) ((
+ volume * A2DP_MAX_GAIN
+ /* Round to closest by adding half the denominator */
+ + PA_VOLUME_NORM / 2
+ ) / PA_VOLUME_NORM);
+
+ if (gain > A2DP_MAX_GAIN)
+ gain = A2DP_MAX_GAIN;
+
+ return gain;
+}
+
struct pa_bluetooth_discovery {
PA_REFCNT_DECLARE;
@@ -174,6 +200,9 @@ pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const
t->path = pa_xstrdup(path);
t->profile = p;
t->config_size = size;
+ /* Always force initial volume to be set/propagated correctly */
+ t->sink_volume = PA_VOLUME_INVALID;
+ t->source_volume = PA_VOLUME_INVALID;
if (size > 0) {
t->config = pa_xnew(uint8_t, size);
@@ -496,6 +525,112 @@ void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_tr
}
}
+static pa_volume_t pa_bluetooth_transport_set_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
+ static const char *volume_str = "Volume";
+ static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
+ DBusMessage *m;
+ DBusMessageIter iter;
+ uint16_t gain;
+
+ pa_assert(t);
+ pa_assert(t->device);
+ pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
+ pa_assert(t->device->discovery);
+
+ gain = volume_to_a2dp_gain(volume);
+ /* Propagate rounding and bound checks */
+ volume = a2dp_gain_to_volume(gain);
+
+ if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE && t->source_volume == volume)
+ return volume;
+ else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && t->sink_volume == volume)
+ return volume;
+
+ if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
+ t->source_volume = volume;
+ else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+ t->sink_volume = volume;
+
+ pa_log_debug("Sending A2DP volume %d/127 to peer", gain);
+
+ pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Set"));
+
+ dbus_message_iter_init_append(m, &iter);
+ pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &mediatransport_str));
+ pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &volume_str));
+ pa_dbus_append_basic_variant(&iter, DBUS_TYPE_UINT16, &gain);
+
+ /* Ignore replies, wait for the Volume property to change (generally arrives
+ * before this function replies).
+ *
+ * In an ideal world BlueZ exposes a function to change volume, that returns
+ * with the actual volume set by the peer as returned by the SetAbsoluteVolume
+ * AVRCP command. That is required later to perform software volume compensation
+ * based on actual playback volume.
+ */
+ dbus_message_set_no_reply(m, true);
+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(t->device->discovery->connection), m, NULL));
+ dbus_message_unref(m);
+
+ return volume;
+}
+
+static pa_volume_t pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
+ pa_assert(t);
+ pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
+ return pa_bluetooth_transport_set_volume(t, volume);
+}
+
+static pa_volume_t pa_bluetooth_transport_set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
+ pa_assert(t);
+ pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+ return pa_bluetooth_transport_set_volume(t, volume);
+}
+
+static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport *t, pa_volume_t volume) {
+ pa_bluetooth_hook_t hook;
+ bool is_source;
+ char volume_str[PA_VOLUME_SNPRINT_MAX];
+
+ pa_assert(t);
+ pa_assert(t->device);
+
+ if (!t->device->avrcp_absolute_volume)
+ return;
+
+ is_source = t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+
+ if (is_source) {
+ if (t->source_volume == volume)
+ return;
+ t->source_volume = volume;
+ hook = PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED;
+ } else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
+ if (t->sink_volume == volume)
+ return;
+ t->sink_volume = volume;
+ hook = PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED;
+
+ /* A2DP Absolute Volume is optional. This callback is only
+ * attached when the peer supports it, and the hook handler
+ * further attaches the necessary hardware callback to the
+ * pa_sink and disables software attenuation.
+ */
+ if (!t->set_sink_volume) {
+ pa_log_debug("A2DP sink supports volume control");
+ t->set_sink_volume = pa_bluetooth_transport_set_sink_volume;
+ }
+ } else {
+ pa_assert_not_reached();
+ }
+
+ pa_log_debug("Reporting volume change %s for %s",
+ pa_volume_snprint(volume_str, sizeof(volume_str), volume),
+ is_source ? "source" : "sink");
+
+ pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, hook), t);
+}
+
void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
pa_assert(t);
@@ -603,6 +738,82 @@ static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
pa_log_info("Transport %s released", t->path);
}
+static void get_volume_reply(DBusPendingCall *pending, void *userdata) {
+ DBusMessage *r;
+ DBusMessageIter iter, variant;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+ pa_bluetooth_transport *t;
+ uint16_t gain;
+ pa_volume_t volume;
+
+ pa_assert(pending);
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(t = p->call_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log_error(DBUS_INTERFACE_PROPERTIES ".Get %s Volume failed: %s: %s",
+ dbus_message_get_path(p->message),
+ dbus_message_get_error_name(r),
+ pa_dbus_get_error_message(r));
+ goto finish;
+ }
+ dbus_message_iter_init(r, &iter);
+ pa_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT);
+ dbus_message_iter_recurse(&iter, &variant);
+ pa_assert(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_UINT16);
+ dbus_message_iter_get_basic(&variant, &gain);
+
+ if (gain > A2DP_MAX_GAIN)
+ gain = A2DP_MAX_GAIN;
+
+ pa_log_debug("Received A2DP Absolute Volume %d", gain);
+
+ volume = a2dp_gain_to_volume(gain);
+
+ pa_bluetooth_transport_remote_volume_changed(t, volume);
+
+finish:
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void bluez5_transport_get_volume(pa_bluetooth_transport *t) {
+ static const char *volume_str = "Volume";
+ static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
+ DBusMessage *m;
+
+ pa_assert(t);
+ pa_assert(t->device);
+ pa_assert(t->device->discovery);
+
+ pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
+
+ pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Get"));
+ pa_assert_se(dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &mediatransport_str,
+ DBUS_TYPE_STRING, &volume_str,
+ DBUS_TYPE_INVALID));
+
+ send_and_add_to_pending(t->device->discovery, m, get_volume_reply, t);
+}
+
+void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t) {
+ pa_assert(t);
+ pa_assert(t->device);
+
+ if (!t->device->avrcp_absolute_volume)
+ return;
+
+ if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+ /* A2DP Absolute Volume control (AVRCP 1.4) is optional */
+ bluez5_transport_get_volume(t);
+}
+
static ssize_t a2dp_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) {
ssize_t l = 0;
size_t written = 0;
@@ -679,6 +890,8 @@ static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter
if (key == NULL)
return;
+ pa_log_debug("Transport property %s changed", key);
+
dbus_message_iter_recurse(i, &variant_i);
switch (dbus_message_iter_get_arg_type(&variant_i)) {
@@ -701,6 +914,17 @@ static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter
break;
}
+
+ case DBUS_TYPE_UINT16: {
+ uint16_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Volume")) {
+ pa_volume_t volume = a2dp_gain_to_volume(value);
+ pa_bluetooth_transport_remote_volume_changed(t, volume);
+ }
+ break;
+ }
}
return;
@@ -1826,8 +2050,7 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile) {
switch(peer_profile) {
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
- /* Will be set to false when A2DP absolute volume is supported */
- return true;
+ return false;
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
return true;
case PA_BLUETOOTH_PROFILE_HFP_HF:
@@ -1842,6 +2065,10 @@ bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_pr
pa_assert_not_reached();
}
+bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile) {
+ return profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
+}
+
static const pa_a2dp_endpoint_conf *a2dp_sep_to_a2dp_endpoint_conf(const char *endpoint) {
const char *codec_name;
@@ -1981,6 +2208,13 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
t->acquire = bluez5_transport_acquire_cb;
t->release = bluez5_transport_release_cb;
+ /* A2DP Absolute Volume is optional but BlueZ unconditionally reports
+ * feature category 2, meaning supporting it is mandatory.
+ * PulseAudio can and should perform the attenuation anyway in
+ * the source role as it is the audio rendering device.
+ */
+ t->set_source_volume = pa_bluetooth_transport_set_source_volume;
+
pa_bluetooth_transport_reconfigure(t, &endpoint_conf->bt_codec, a2dp_transport_write, NULL);
pa_bluetooth_transport_put(t);
=====================================
src/modules/bluetooth/bluez5-util.h
=====================================
@@ -52,6 +52,9 @@
#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb"
#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb"
+#define A2DP_MAX_GAIN 127
+#define HSP_MAX_GAIN 15
+
typedef struct pa_bluetooth_transport pa_bluetooth_transport;
typedef struct pa_bluetooth_device pa_bluetooth_device;
typedef struct pa_bluetooth_adapter pa_bluetooth_adapter;
@@ -130,6 +133,7 @@ struct pa_bluetooth_device {
bool valid;
bool autodetect_mtu;
bool codec_switching_in_progress;
+ bool avrcp_absolute_volume;
uint32_t output_rate_refresh_interval_ms;
/* Device information */
@@ -189,6 +193,7 @@ void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_tr
void pa_bluetooth_transport_put(pa_bluetooth_transport *t);
void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t);
void pa_bluetooth_transport_free(pa_bluetooth_transport *t);
+void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t);
bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d);
bool pa_bluetooth_device_switch_codec(pa_bluetooth_device *device, pa_bluetooth_profile_t profile, pa_hashmap *capabilities_hashmap, const pa_a2dp_endpoint_conf *endpoint_conf, void (*codec_switch_cb)(bool, pa_bluetooth_profile_t profile, void *), void *userdata);
@@ -200,6 +205,7 @@ pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hoo
const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile);
bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t profile);
+bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile);
static inline bool pa_bluetooth_uuid_is_hsp_hs(const char *uuid) {
return pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS_ALT);
=====================================
src/modules/bluetooth/module-bluez5-device.c
=====================================
@@ -60,6 +60,7 @@ PA_MODULE_USAGE(
"path=<device object path>"
"autodetect_mtu=<boolean>"
"output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>"
+ "avrcp_absolute_volume=<synchronize volume with peer, true by default>"
);
#define FIXED_LATENCY_PLAYBACK_A2DP (25 * PA_USEC_PER_MSEC)
@@ -71,6 +72,7 @@ static const char* const valid_modargs[] = {
"path",
"autodetect_mtu",
"output_rate_refresh_interval_ms",
+ "avrcp_absolute_volume",
NULL
};
@@ -899,12 +901,17 @@ static void source_setup_volume_callback(pa_source *s) {
pa_assert(u->source == s);
pa_assert(u->transport);
+ if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
+ return;
+
/* Remote volume control has to be supported for the callback to make sense,
* otherwise this source should continue performing attenuation in software
* without HW_VOLUME_CTL.
* If the peer is an AG however backend-native unconditionally provides this
* function, PA in the role of HS/HF is responsible for signalling support
* by emitting an initial volume command.
+ * For A2DP bluez-util also unconditionally provides this function to keep
+ * the peer informed about volume changes.
*/
if (!u->transport->set_source_volume)
return;
@@ -921,6 +928,13 @@ static void source_setup_volume_callback(pa_source *s) {
/* Send initial volume to peer, signalling support for volume control */
u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume));
} else {
+ /* It is yet unknown how (if at all) volume is synchronized for bidirectional
+ * A2DP codecs. Disallow attaching callbacks (and using HFP n_volume_steps)
+ * below to a pa_source if the peer is in A2DP_SINK role. This assert should
+ * be replaced with the proper logic when bidirectional codecs are implemented.
+ */
+ pa_assert(u->profile != PA_BLUETOOTH_PROFILE_A2DP_SINK);
+
if (s->set_volume == source_set_volume_cb)
return;
@@ -930,7 +944,7 @@ static void source_setup_volume_callback(pa_source *s) {
pa_source_set_soft_volume(s, NULL);
pa_source_set_set_volume_callback(s, source_set_volume_cb);
- s->n_volume_steps = 16;
+ s->n_volume_steps = HSP_MAX_GAIN + 1;
}
}
@@ -1115,6 +1129,9 @@ static void sink_setup_volume_callback(pa_sink *s) {
pa_assert(u->sink == s);
pa_assert(u->transport);
+ if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
+ return;
+
/* Remote volume control has to be supported for the callback to make sense,
* otherwise this sink should continue performing attenuation in software
* without HW_VOLUME_CTL.
@@ -1126,6 +1143,13 @@ static void sink_setup_volume_callback(pa_sink *s) {
return;
if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) {
+ /* It is yet unknown how (if at all) volume is synchronized for bidirectional
+ * A2DP codecs. Disallow attaching hooks to a pa_sink if the peer is in
+ * A2DP_SOURCE role. This assert should be replaced with the proper logic
+ * when bidirectional codecs are implemented.
+ */
+ pa_assert(u->profile != PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
+
if (u->sink_volume_changed_slot)
return;
@@ -1146,7 +1170,11 @@ static void sink_setup_volume_callback(pa_sink *s) {
pa_sink_set_soft_volume(s, NULL);
pa_sink_set_set_volume_callback(s, sink_set_volume_cb);
- s->n_volume_steps = 16;
+
+ if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
+ s->n_volume_steps = A2DP_MAX_GAIN + 1;
+ else
+ s->n_volume_steps = HSP_MAX_GAIN + 1;
}
}
@@ -1668,6 +1696,22 @@ static int start_thread(struct userdata *u) {
if (u->bt_codec)
pa_proplist_sets(u->card->proplist, PA_PROP_BLUETOOTH_CODEC, u->bt_codec->name);
+ /* Now that everything is set up we are ready to check for the Volume property.
+ * Sometimes its initial "change" notification arrives too early when the sink
+ * is not available or still in UNLINKED state; check it again here to know if
+ * our sink peer supports Absolute Volume; in that case we should not perform
+ * any attenuation but delegate all set_volume calls to the peer through this
+ * Volume property.
+ *
+ * Note that this works the other way around if the peer is in source profile:
+ * we are rendering audio and hence responsible for applying attenuation. The
+ * set_volume callback is always registered, and Volume is always passed to
+ * BlueZ unconditionally. BlueZ only sends a notification to the peer if it
+ * registered a notification request for absolute volume previously.
+ */
+ if (u->transport && u->sink)
+ pa_bluetooth_transport_load_a2dp_sink_volume(u->transport);
+
return 0;
}
@@ -2368,7 +2412,7 @@ static char *list_codecs(struct userdata *u) {
pa_json_encoder_begin_element_array(encoder);
- if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
+ if (pa_bluetooth_profile_is_a2dp(u->profile)) {
is_a2dp_sink = u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints;
@@ -2584,7 +2628,7 @@ int pa__init(pa_module* m) {
struct userdata *u;
const char *path;
pa_modargs *ma;
- bool autodetect_mtu;
+ bool autodetect_mtu, avrcp_absolute_volume;
char *message_handler_path;
uint32_t output_rate_refresh_interval_ms;
@@ -2633,6 +2677,14 @@ int pa__init(pa_module* m) {
u->device->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms;
+ avrcp_absolute_volume = true;
+ if (pa_modargs_get_value_boolean(ma, "avrcp_absolute_volume", &avrcp_absolute_volume) < 0) {
+ pa_log("Invalid boolean value for avrcp_absolute_volume parameter");
+ goto fail_free_modargs;
+ }
+
+ u->device->avrcp_absolute_volume = avrcp_absolute_volume;
+
pa_modargs_free(ma);
u->device_connection_changed_slot =
=====================================
src/modules/bluetooth/module-bluez5-discover.c
=====================================
@@ -41,6 +41,7 @@ PA_MODULE_USAGE(
"output_rate_refresh_interval_ms=<interval between attempts to improve output rate in milliseconds>"
"enable_native_hsp_hs=<boolean, enable HSP support in native backend>"
"enable_native_hfp_hf=<boolean, enable HFP support in native backend>"
+ "avrcp_absolute_volume=<synchronize volume with peer, true by default>"
);
static const char* const valid_modargs[] = {
@@ -50,6 +51,7 @@ static const char* const valid_modargs[] = {
"output_rate_refresh_interval_ms",
"enable_native_hsp_hs",
"enable_native_hfp_hf",
+ "avrcp_absolute_volume",
NULL
};
@@ -60,6 +62,7 @@ struct userdata {
pa_hook_slot *device_connection_changed_slot;
pa_bluetooth_discovery *discovery;
bool autodetect_mtu;
+ bool avrcp_absolute_volume;
uint32_t output_rate_refresh_interval_ms;
};
@@ -83,8 +86,12 @@ static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y,
if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
/* a new device has been connected */
pa_module *m;
- char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i output_rate_refresh_interval_ms=%u",
- d->path, (int)u->autodetect_mtu, u->output_rate_refresh_interval_ms);
+ char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i output_rate_refresh_interval_ms=%u"
+ " avrcp_absolute_volume=%i",
+ d->path,
+ (int)u->autodetect_mtu,
+ u->output_rate_refresh_interval_ms,
+ (int)u->avrcp_absolute_volume);
pa_log_debug("Loading module-bluez5-device %s", args);
pa_module_load(&m, u->module->core, "module-bluez5-device", args);
@@ -116,6 +123,7 @@ int pa__init(pa_module *m) {
int headset_backend;
bool autodetect_mtu;
bool enable_msbc;
+ bool avrcp_absolute_volume;
uint32_t output_rate_refresh_interval_ms;
bool enable_native_hsp_hs;
bool enable_native_hfp_hf;
@@ -161,6 +169,12 @@ int pa__init(pa_module *m) {
goto fail;
}
+ avrcp_absolute_volume = true;
+ if (pa_modargs_get_value_boolean(ma, "avrcp_absolute_volume", &avrcp_absolute_volume) < 0) {
+ pa_log("avrcp_absolute_volume must be true or false");
+ goto fail;
+ }
+
output_rate_refresh_interval_ms = DEFAULT_OUTPUT_RATE_REFRESH_INTERVAL_MS;
if (pa_modargs_get_value_u32(ma, "output_rate_refresh_interval_ms", &output_rate_refresh_interval_ms) < 0) {
pa_log("Invalid value for output_rate_refresh_interval parameter.");
@@ -171,6 +185,7 @@ int pa__init(pa_module *m) {
u->module = m;
u->core = m->core;
u->autodetect_mtu = autodetect_mtu;
+ u->avrcp_absolute_volume = avrcp_absolute_volume;
u->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms;
u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/dbaf450394ddb4eef8a45bdedcb7ae7a569e0304...d9db47bdb5074316d195357cff4eca98e208a405
--
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/dbaf450394ddb4eef8a45bdedcb7ae7a569e0304...d9db47bdb5074316d195357cff4eca98e208a405
You're receiving this email because of your account on gitlab.freedesktop.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20210517/72aeed9d/attachment-0001.htm>
More information about the pulseaudio-commits
mailing list