diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index d597f7c6e..2e4b39b3e 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -36,6 +36,8 @@ #include "bluez5-util.h" +#define HSP_MAX_GAIN 15 + struct pa_bluetooth_backend { pa_core *core; pa_dbus_connection *connection; @@ -121,6 +123,25 @@ static uint32_t hfp_features = " " \ "" +static pa_volume_t hsp_gain_to_volume(uint16_t gain) { + pa_volume_t volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN); + + /* increment volume by one to correct rounding errors */ + if (volume < PA_VOLUME_NORM) + volume++; + + return volume; +} + +static uint16_t volume_to_hsp_gain(pa_volume_t volume) { + uint16_t gain = volume * HSP_MAX_GAIN / PA_VOLUME_NORM; + + if (gain > HSP_MAX_GAIN) + gain = HSP_MAX_GAIN; + + return gain; +} + static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, DBusPendingCallNotifyFunction func, void *call_data) { @@ -516,13 +537,13 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because * it does not expect a reply. */ if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) { - t->speaker_gain = gain; - pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), t); + t->speaker_volume = hsp_gain_to_volume(gain); + pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_VOLUME_CHANGED), t); do_reply = true; } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) { - t->microphone_gain = gain; - pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED), t); + t->microphone_volume = hsp_gain_to_volume(gain); + pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_VOLUME_CHANGED), t); do_reply = true; } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) { do_reply = true; @@ -559,13 +580,17 @@ static void transport_destroy(pa_bluetooth_transport *t) { pa_xfree(trd); } -static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) { +static pa_volume_t set_speaker_volume(pa_bluetooth_transport *t, pa_volume_t volume) { struct transport_data *trd = t->userdata; + uint16_t gain = volume_to_hsp_gain(volume); - if (t->speaker_gain == gain) - return; + /* Propagate rounding and bound checks */ + volume = hsp_gain_to_volume(gain); - t->speaker_gain = gain; + if (t->speaker_volume == volume) + return volume; + + t->speaker_volume = volume; /* If we are in the AG role, we send a command to the head set to change * the speaker gain. In the HS role, source and sink are swapped, so @@ -575,15 +600,21 @@ static void set_speaker_gain(pa_bluetooth_transport *t, uint16_t gain) { } else { rfcomm_write_command(trd->rfcomm_fd, "AT+VGM=%d", gain); } + + return volume; } -static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) { +static pa_volume_t set_microphone_volume(pa_bluetooth_transport *t, pa_volume_t volume) { struct transport_data *trd = t->userdata; + uint16_t gain = volume_to_hsp_gain(volume); - if (t->microphone_gain == gain) - return; + /* Propagate rounding and bound checks */ + volume = hsp_gain_to_volume(gain); - t->microphone_gain = gain; + if (t->microphone_volume == volume) + return volume; + + t->microphone_volume = volume; /* If we are in the AG role, we send a command to the head set to change * the microphone gain. In the HS role, source and sink are swapped, so @@ -593,6 +624,8 @@ static void set_microphone_gain(pa_bluetooth_transport *t, uint16_t gain) { } else { rfcomm_write_command(trd->rfcomm_fd, "AT+VGS=%d", gain); } + + return volume; } static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) { @@ -662,8 +695,8 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, t->acquire = sco_acquire_cb; t->release = sco_release_cb; t->destroy = transport_destroy; - t->set_speaker_gain = set_speaker_gain; - t->set_microphone_gain = set_microphone_gain; + t->set_speaker_volume = set_speaker_volume; + t->set_microphone_volume = set_microphone_volume; trd = pa_xnew0(struct transport_data, 1); trd->rfcomm_fd = fd; diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index b6376b470..d717aa397 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -59,11 +59,11 @@ typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; typedef struct pa_bluetooth_backend pa_bluetooth_backend; typedef enum pa_bluetooth_hook { - PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ - PA_BLUETOOTH_HOOK_DEVICE_UNLINK, /* Call data: pa_bluetooth_device */ - 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_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ + PA_BLUETOOTH_HOOK_DEVICE_UNLINK, /* Call data: pa_bluetooth_device */ + PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ + PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_VOLUME_CHANGED, /* Call data: pa_bluetooth_transport */ + PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_VOLUME_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_MAX } pa_bluetooth_hook_t; @@ -87,8 +87,8 @@ typedef enum pa_bluetooth_transport_state { typedef int (*pa_bluetooth_transport_acquire_cb)(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu); 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 pa_volume_t (*pa_bluetooth_transport_set_speaker_volume_cb)(pa_bluetooth_transport *t, pa_volume_t volume); +typedef pa_volume_t (*pa_bluetooth_transport_set_microphone_volume_cb)(pa_bluetooth_transport *t, pa_volume_t volume); struct pa_bluetooth_transport { pa_bluetooth_device *device; @@ -103,16 +103,16 @@ struct pa_bluetooth_transport { const pa_a2dp_codec *a2dp_codec; - uint16_t microphone_gain; - uint16_t speaker_gain; + pa_volume_t microphone_volume; + pa_volume_t speaker_volume; pa_bluetooth_transport_state_t state; pa_bluetooth_transport_acquire_cb acquire; pa_bluetooth_transport_release_cb release; 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_speaker_volume_cb set_speaker_volume; + pa_bluetooth_transport_set_microphone_volume_cb set_microphone_volume; void *userdata; }; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 61831129d..6813169a9 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -67,8 +67,6 @@ PA_MODULE_USAGE( #define FIXED_LATENCY_RECORD_A2DP (25 * PA_USEC_PER_MSEC) #define FIXED_LATENCY_RECORD_SCO (25 * PA_USEC_PER_MSEC) -#define HSP_MAX_GAIN 15 - static const char* const valid_modargs[] = { "path", "autodetect_mtu", @@ -104,8 +102,8 @@ struct userdata { pa_hook_slot *device_connection_changed_slot; 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_speaker_volume_changed_slot; + pa_hook_slot *transport_microphone_volume_changed_slot; pa_bluetooth_discovery *discovery; pa_bluetooth_device *device; @@ -962,7 +960,6 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_ /* Run from main thread */ static void source_set_volume_cb(pa_source *s) { - uint16_t gain; pa_volume_t volume; struct userdata *u; @@ -974,30 +971,19 @@ static void source_set_volume_cb(pa_source *s) { pa_assert(u); pa_assert(u->source == s); - if (u->transport->set_microphone_gain == NULL) + if (u->transport->set_microphone_volume == NULL) return; - gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM; - - if (gain > HSP_MAX_GAIN) - gain = HSP_MAX_GAIN; - - volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN); - - /* increment volume by one to correct rounding errors */ - if (volume < PA_VOLUME_NORM) - volume++; + /* If we are in the AG role, we send a command to the head set to change + * the microphone gain. In the HS role, source and sink are swapped, so + * in this case we notify the AG that the speaker gain has changed */ + volume = u->transport->set_microphone_volume(u->transport, pa_cvolume_max(&s->real_volume)); pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume); /* Set soft volume when in headset role */ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) pa_cvolume_set(&s->soft_volume, u->decoder_sample_spec.channels, volume); - - /* If we are in the AG role, we send a command to the head set to change - * the microphone gain. In the HS role, source and sink are swapped, so - * in this case we notify the AG that the speaker gain has changed */ - u->transport->set_microphone_gain(u->transport, gain); } /* Run from main thread */ @@ -1154,7 +1140,6 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, /* Run from main thread */ static void sink_set_volume_cb(pa_sink *s) { - uint16_t gain; pa_volume_t volume; struct userdata *u; @@ -1166,30 +1151,19 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert(u); pa_assert(u->sink == s); - if (u->transport->set_speaker_gain == NULL) + if (u->transport->set_speaker_volume == NULL) return; - gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM; - - if (gain > HSP_MAX_GAIN) - gain = HSP_MAX_GAIN; - - volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN); - - /* increment volume by one to correct rounding errors */ - if (volume < PA_VOLUME_NORM) - volume++; + /* If we are in the AG role, we send a command to the head set to change + * the speaker gain. In the HS role, source and sink are swapped, so + * in this case we notify the AG that the microphone gain has changed */ + volume = u->transport->set_speaker_volume(u->transport, pa_cvolume_max(&s->real_volume)); pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume); /* Set soft volume when in headset role */ if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) pa_cvolume_set(&s->soft_volume, u->encoder_sample_spec.channels, volume); - - /* If we are in the AG role, we send a command to the head set to change - * the speaker gain. In the HS role, source and sink are swapped, so - * in this case we notify the AG that the microphone gain has changed */ - u->transport->set_speaker_gain(u->transport, gain); } /* Run from main thread */ @@ -2270,10 +2244,9 @@ static pa_hook_result_t transport_state_changed_cb(pa_bluetooth_discovery *y, pa return PA_HOOK_OK; } -static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { +static pa_hook_result_t transport_speaker_volume_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); @@ -2281,12 +2254,7 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery if (t != u->transport) return PA_HOOK_OK; - gain = t->speaker_gain; - volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN); - - /* increment volume by one to correct rounding errors */ - if (volume < PA_VOLUME_NORM) - volume++; + volume = t->speaker_volume; pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume); if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) @@ -2297,10 +2265,9 @@ static pa_hook_result_t transport_speaker_gain_changed_cb(pa_bluetooth_discovery return PA_HOOK_OK; } -static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { +static pa_hook_result_t transport_microphone_volume_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); @@ -2308,12 +2275,7 @@ static pa_hook_result_t transport_microphone_gain_changed_cb(pa_bluetooth_discov if (t != u->transport) return PA_HOOK_OK; - gain = t->microphone_gain; - volume = (pa_volume_t) (gain * PA_VOLUME_NORM / HSP_MAX_GAIN); - - /* increment volume by one to correct rounding errors */ - if (volume < PA_VOLUME_NORM) - volume++; + volume = t->microphone_volume; pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume); @@ -2620,11 +2582,11 @@ int pa__init(pa_module* m) { pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_state_changed_cb, u); - u->transport_speaker_gain_changed_slot = - pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_speaker_gain_changed_cb, u); + u->transport_speaker_volume_changed_slot = + pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_VOLUME_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_speaker_volume_changed_cb, u); - 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_microphone_volume_changed_slot = + pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_VOLUME_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_microphone_volume_changed_cb, u); if (add_card(u) < 0) goto fail; @@ -2695,11 +2657,11 @@ void pa__done(pa_module *m) { if (u->transport_state_changed_slot) pa_hook_slot_free(u->transport_state_changed_slot); - if (u->transport_speaker_gain_changed_slot) - pa_hook_slot_free(u->transport_speaker_gain_changed_slot); + if (u->transport_speaker_volume_changed_slot) + pa_hook_slot_free(u->transport_speaker_volume_changed_slot); - if (u->transport_microphone_gain_changed_slot) - pa_hook_slot_free(u->transport_microphone_gain_changed_slot); + if (u->transport_microphone_volume_changed_slot) + pa_hook_slot_free(u->transport_microphone_volume_changed_slot); if (u->encoder_buffer) pa_xfree(u->encoder_buffer);