[pulseaudio-discuss] [PATCH] Absolute volume control for A2DP transport

ValdikSS iam at valdikss.org.ru
Sat Oct 20 12:34:17 UTC 2018


There are Bluetooth devices which support absolute volume but does not handle it correctly. For example, some Bluetooth headsets are unacceptable loud on the lowest volume, that's why it's better to completely disable absolute volume control for such devices.
Google created a database for broken devices for Android, which I have linked. Please add these exceptions if possible.

On 14.10.2018 21:54, ValdikSS wrote:
> Can you please integrate exceptions from the Android database?
> https://android.googlesource.com/platform/system/bt/+/master/device/include/interop_database.h
> Ones with INTEROP_DISABLE_ABSOLUTE_VOLUME
> 
> 
> On 03/10/2018 16:12, EHfive wrote:
>> On 10/3/18 8:37 PM, EHfive wrote:
>>> Require bluez-tools/mpris-proxy running. (No hurt if dosen't)
>>>
>>> If you need play/pause/next... controls , add configurations below to /etc/dbus-1/system.d/
>>>
>>> ------------------------ mpris.conf
>>>
>>> <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
>>>  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
>>> <busconfig>
>>>   <policy context="default">
>>>     <allow send_interface="org.mpris.MediaPlayer2.Player"/>
>>>   </policy>
>>> </busconfig>
>>>
>>> ------------------------
>>>
>> An alternative
>>
>> https://gist.github.com/EHfive/e2a28d0279a6247fab4bac93d73b8571
>>
>> A python script which implement org.mpris.MediaPlayer2.Player and register mpris player object by calling org.bluez.Media1.RegisterPlayer.
>>
>>> ===================
>>>
>>> diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
>>> index 2d83373..72cd05a 100644
>>> --- a/src/modules/bluetooth/bluez5-util.c
>>> +++ b/src/modules/bluetooth/bluez5-util.c
>>> @@ -348,6 +348,50 @@ void pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
>>>      pa_xfree(t);
>>>  }
>>>
>>> +static int bluez5_transport_set_property(pa_bluetooth_transport *t, const char *prop_name, int prop_type, void *prop_value){
>>> +    DBusMessage *m, *r;
>>> +    DBusError err;
>>> +    DBusMessageIter i;
>>> +    const char * interface = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
>>> +
>>> +    pa_log_debug("Setting property, Owner: %s; Path: %s; Property: %s",t->owner, t->path, prop_name);
>>> +
>>> +    pa_assert(t);
>>> +    pa_assert(t->device);
>>> +    pa_assert(t->device->discovery);
>>> +
>>> +    dbus_error_init(&err);
>>> +
>>> +    pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.freedesktop.DBus.Properties", "Set"));
>>> +
>>> +    dbus_message_iter_init_append(m, &i);
>>> +
>>> +    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &interface));
>>> +    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name));
>>> +    pa_dbus_append_basic_variant(&i, prop_type, prop_value);
>>> +
>>> +    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
>>> +    dbus_message_unref(m);
>>> +    m = NULL;
>>> +    if(r) {
>>> +        dbus_message_unref(r);
>>> +        r = NULL;
>>> +    }
>>> +
>>> +    if(dbus_error_is_set(&err)) {
>>> +        pa_log_debug("Failed to set property \"%s.%s\"", interface, prop_name);
>>> +        return -1;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int bluez5_transport_set_volume(pa_bluetooth_transport *t, uint16_t volume){
>>> +    if(t->a2dp_gain == volume)
>>> +        return 0;
>>> +    return bluez5_transport_set_property(t, "Volume", DBUS_TYPE_UINT16, &volume);
>>> +}
>>> +
>>>  static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
>>>      DBusMessage *m, *r;
>>>      DBusError err;
>>> @@ -441,6 +485,14 @@ bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
>>>      return false;
>>>  }
>>>
>>> +void pa_transport_set_a2dp_gain(pa_bluetooth_transport *t, uint16_t a2dp_gain){
>>> +    if(t->a2dp_gain == a2dp_gain)
>>> +        return;
>>> +    t->a2dp_gain = a2dp_gain;
>>> + pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_A2DP_GAIN_CHANGED), t);
>>> +}
>>> +
>>> +
>>>  static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
>>>      pa_assert(value);
>>>      pa_assert(state);
>>> @@ -483,6 +535,18 @@ static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter
>>>                  pa_bluetooth_transport_set_state(t, state);
>>>              }
>>>
>>> +            break;
>>> +        }
>>> +        case DBUS_TYPE_UINT16: {
>>> +
>>> +            uint16_t value;
>>> +            dbus_message_iter_get_basic(&variant_i, &value);
>>> +
>>> +            if (pa_streq(key, "Volume")) {
>>> +                pa_log_debug("Transport Volume Changed to %u ", value);
>>> +                pa_transport_set_a2dp_gain(t, value);
>>> +            }
>>> +
>>>              break;
>>>          }
>>>      }
>>> @@ -1468,6 +1532,7 @@ 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;
>>> +    t->set_volume = bluez5_transport_set_volume;
>>>      pa_bluetooth_transport_put(t);
>>>
>>>      pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
>>> diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
>>> index ad30708..5b8149d 100644
>>> --- a/src/modules/bluetooth/bluez5-util.h
>>> +++ b/src/modules/bluetooth/bluez5-util.h
>>> @@ -47,6 +47,7 @@ typedef enum pa_bluetooth_hook {
>>>      PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED,            /* Call data: pa_bluetooth_transport */
>>>      PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED,  /* Call data: pa_bluetooth_transport */
>>>      PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED,     /* Call data: pa_bluetooth_transport */
>>> +    PA_BLUETOOTH_HOOK_TRANSPORT_A2DP_GAIN_CHANGED,        /* Call data: pa_bluetooth_transport */
>>>      PA_BLUETOOTH_HOOK_MAX
>>>  } pa_bluetooth_hook_t;
>>>
>>> @@ -70,6 +71,7 @@ typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t);
>>>  typedef void (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t);
>>>  typedef void (*pa_bluetooth_transport_set_speaker_gain_cb)(pa_bluetooth_transport *t, uint16_t gain);
>>>  typedef void (*pa_bluetooth_transport_set_microphone_gain_cb)(pa_bluetooth_transport *t, uint16_t gain);
>>> +typedef int (*pa_bluetooth_transport_set_volume_cb)(pa_bluetooth_transport *t, uint16_t volume);
>>>
>>>  struct pa_bluetooth_transport {
>>>      pa_bluetooth_device *device;
>>> @@ -84,6 +86,7 @@ struct pa_bluetooth_transport {
>>>
>>>      uint16_t microphone_gain;
>>>      uint16_t speaker_gain;
>>> +    uint16_t a2dp_gain;
>>>
>>>      pa_bluetooth_transport_state_t state;
>>>
>>> @@ -92,6 +95,7 @@ struct pa_bluetooth_transport {
>>>      pa_bluetooth_transport_destroy_cb destroy;
>>>      pa_bluetooth_transport_set_speaker_gain_cb set_speaker_gain;
>>>      pa_bluetooth_transport_set_microphone_gain_cb set_microphone_gain;
>>> +    pa_bluetooth_transport_set_volume_cb set_volume;
>>>      void *userdata;
>>>  };
>>>
>>> diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
>>> index 351ad12..49a6dba 100644
>>> --- a/src/modules/bluetooth/module-bluez5-device.c
>>> +++ b/src/modules/bluetooth/module-bluez5-device.c
>>> @@ -64,6 +64,7 @@ PA_MODULE_USAGE("path=<device object path>"
>>>  #define BITPOOL_DEC_LIMIT 32
>>>  #define BITPOOL_DEC_STEP 5
>>>  #define HSP_MAX_GAIN 15
>>> +#define BLUEZ_MAX_GAIN 127
>>>
>>>  static const char* const valid_modargs[] = {
>>>      "path",
>>> @@ -113,6 +114,7 @@ struct userdata {
>>>      pa_hook_slot *transport_state_changed_slot;
>>>      pa_hook_slot *transport_speaker_gain_changed_slot;
>>>      pa_hook_slot *transport_microphone_gain_changed_slot;
>>> +    pa_hook_slot *transport_a2dp_gain_changed_slot;
>>>
>>>      pa_bluetooth_discovery *discovery;
>>>      pa_bluetooth_device *device;
>>> @@ -1056,6 +1058,37 @@ static void source_set_volume_cb(pa_source *s) {
>>>      u->transport->set_microphone_gain(u->transport, gain);
>>>  }
>>>
>>> +static void source_set_a2dp_volume_cb(pa_source *s) {
>>> +    uint16_t gain;
>>> +    pa_volume_t volume;
>>> +    struct userdata *u;
>>> +
>>> +    pa_assert(s);
>>> +    pa_assert(s->core);
>>> +
>>> +    u = s->userdata;
>>> +
>>> +    pa_assert(u);
>>> +    pa_assert(u->source == s);
>>> +
>>> +    if (u->transport->set_volume == NULL)
>>> +        return;
>>> +
>>> +    gain = (uint16_t) ((pa_cvolume_max(&s->real_volume) * BLUEZ_MAX_GAIN) / PA_VOLUME_NORM);
>>> +
>>> +    pa_log_debug("Real Volume Gain:%u", gain);
>>> +
>>> +    if (gain > BLUEZ_MAX_GAIN)
>>> +        gain = BLUEZ_MAX_GAIN;
>>> +
>>> +    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / BLUEZ_MAX_GAIN);
>>> +
>>> +    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
>>> +    pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, volume);
>>> +
>>> +    u->transport->set_volume(u->transport, gain);
>>> +}
>>> +
>>>  /* Run from main thread */
>>>  static int add_source(struct userdata *u) {
>>>      pa_source_new_data data;
>>> @@ -1109,6 +1142,9 @@ static int add_source(struct userdata *u) {
>>>      if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
>>>          pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
>>>          u->source->n_volume_steps = 16;
>>> +    } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
>>> +        pa_source_set_set_volume_callback(u->source, source_set_a2dp_volume_cb);
>>> +        u->source->n_volume_steps = 1;
>>>      }
>>>      return 0;
>>>  }
>>> @@ -1230,6 +1266,40 @@ static void sink_set_volume_cb(pa_sink *s) {
>>>      u->transport->set_speaker_gain(u->transport, gain);
>>>  }
>>>
>>> +static void sink_set_a2dp_volume_cb(pa_sink *s) {
>>> +    uint16_t gain;
>>> +    pa_volume_t volume;
>>> +    struct userdata *u;
>>> +
>>> +    pa_assert(s);
>>> +    pa_assert(s->core);
>>> +
>>> +    u = s->userdata;
>>> +
>>> +    pa_assert(u);
>>> +    pa_assert(u->sink == s);
>>> +
>>> +    if (u->transport->set_volume == NULL)
>>> +        return;
>>> +
>>> +    gain = (uint16_t) ((pa_cvolume_max(&s->real_volume) * BLUEZ_MAX_GAIN) / PA_VOLUME_NORM);
>>> +
>>> +    pa_log_debug("Real Volume Gain:%u", gain);
>>> +
>>> +    if (gain > BLUEZ_MAX_GAIN)
>>> +        gain = BLUEZ_MAX_GAIN;
>>> +
>>> +    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / BLUEZ_MAX_GAIN);
>>> +
>>> +    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
>>> +
>>> +    if(u->transport->set_volume(u->transport, gain) < 0) {
>>> +        pa_cvolume_set(&s->soft_volume, u->sample_spec.channels, PA_VOLUME_NORM);
>>> +        pa_sink_set_set_volume_callback(s, NULL);
>>> +        u->transport->a2dp_gain = 0xFFu;
>>> +    }
>>> +}
>>> +
>>>  /* Run from main thread */
>>>  static int add_sink(struct userdata *u) {
>>>      pa_sink_new_data data;
>>> @@ -1284,6 +1354,9 @@ static int add_sink(struct userdata *u) {
>>>      if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
>>>          pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
>>>          u->sink->n_volume_steps = 16;
>>> +    } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
>>> +        pa_sink_set_set_volume_callback(u->sink, sink_set_a2dp_volume_cb);
>>> +        u->sink->n_volume_steps = 1;
>>>      }
>>>      return 0;
>>>  }
>>> @@ -2340,6 +2413,53 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov
>>>      return PA_HOOK_OK;
>>>  }
>>>
>>> +static pa_hook_result_t transport_a2dp_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) {
>>> +    pa_volume_t volume;
>>> +    pa_cvolume v;
>>> +    uint16_t gain;
>>> +
>>> +    pa_assert(t);
>>> +    pa_assert(u);
>>> +
>>> +    if (t != u->transport)
>>> +        return PA_HOOK_OK;
>>> +
>>> +    gain = t->a2dp_gain;
>>> +    volume = (pa_volume_t) (gain * PA_VOLUME_NORM / BLUEZ_MAX_GAIN);
>>> +
>>> +    pa_cvolume_set(&v, u->sample_spec.channels, volume);
>>> +
>>> +    if(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK){
>>> +        pa_assert(u->sink);
>>> +
>>> +        if(!u->sink->set_volume){
>>> +            pa_cvolume_set(&u->sink->soft_volume, u->sample_spec.channels, PA_VOLUME_NORM);
>>> +            pa_sink_set_set_volume_callback(u->sink, sink_set_a2dp_volume_cb);
>>> +        }
>>> +
>>> +
>>> +        if (gain == 0)
>>> +            pa_sink_mute_changed(u->sink, true);
>>> +        else if(u->sink->muted)
>>> +            pa_sink_mute_changed(u->sink, false);
>>> +
>>> +        pa_sink_volume_changed(u->sink, &v);
>>> +    } else if(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE){
>>> +        pa_assert(u->source);
>>> +
>>> +
>>> +        if (gain == 0)
>>> +            pa_source_mute_changed(u->source, true);
>>> +        else if(u->source->muted)
>>> +            pa_source_mute_changed(u->source, false);
>>> +
>>> +        pa_source_volume_changed(u->source, &v);
>>> +    }
>>> +
>>> +
>>> +    return PA_HOOK_OK;
>>> +}
>>> +
>>>  /* Run from main thread context */
>>>  static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
>>>      struct bluetooth_msg *m = BLUETOOTH_MSG(obj);
>>> @@ -2430,6 +2550,9 @@ int pa__init(pa_module* m) {
>>>      u->transport_microphone_gain_changed_slot =
>>> pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_gain_changed_cb, u);
>>>
>>> +    u->transport_a2dp_gain_changed_slot =
>>> + pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_A2DP_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_a2dp_gain_changed_cb, u);
>>> +
>>>      if (add_card(u) < 0)
>>>          goto fail;
>>>
>>> @@ -2491,6 +2614,9 @@ void pa__done(pa_module *m) {
>>>      if (u->transport_microphone_gain_changed_slot)
>>> pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
>>>
>>> +    if (u->transport_a2dp_gain_changed_slot)
>>> + pa_hook_slot_free(u->transport_a2dp_gain_changed_slot);
>>> +
>>>      if (u->sbc_info.buffer)
>>>          pa_xfree(u->sbc_info.buffer);
>>>
>>>
>>> _______________________________________________
>>> pulseaudio-discuss mailing list
>>> pulseaudio-discuss at lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
>>
>> _______________________________________________
>> pulseaudio-discuss mailing list
>> pulseaudio-discuss at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
>>
> 
> 
> 
> _______________________________________________
> pulseaudio-discuss mailing list
> pulseaudio-discuss at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss
> 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 868 bytes
Desc: OpenPGP digital signature
URL: <https://lists.freedesktop.org/archives/pulseaudio-discuss/attachments/20181020/224a0c95/attachment.sig>


More information about the pulseaudio-discuss mailing list