mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
bluetooth: Move HSP gain conversions into backend-native
For the upcoming A2DP AVRCP Absolute Volume feature the code in BlueZ5 has to be generic to be reusable. Move this conversion so that it becomes possible to implement A2DP volume - which uses different values - on top without duplicating existing callback functionality. Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/521>
This commit is contained in:
parent
cefee393fb
commit
a575006aa8
3 changed files with 82 additions and 87 deletions
|
|
@ -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 =
|
|||
" </interface>" \
|
||||
"</node>"
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ 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_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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue