mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
backend-native: implement volume control
Parse the gain changed AT commands from the headset and fire 2 new hooks as a result. The device will connect to those hooks and change the source/sink volumes. When the source/sink volume changes, set the gain on the microphone or speaker respectively. Make sure we do nothing if the transport can not handle the gain changes.
This commit is contained in:
parent
7d4a497b3d
commit
34a5c754a9
3 changed files with 203 additions and 1 deletions
|
|
@ -66,6 +66,7 @@ PA_MODULE_USAGE("path=<device object path>");
|
|||
|
||||
#define BITPOOL_DEC_LIMIT 32
|
||||
#define BITPOOL_DEC_STEP 5
|
||||
#define HSP_MAX_GAIN 15
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
"path",
|
||||
|
|
@ -103,6 +104,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_bluetooth_discovery *discovery;
|
||||
pa_bluetooth_device *device;
|
||||
|
|
@ -902,6 +905,40 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
|||
return (r < 0 || !failed) ? r : -1;
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void source_set_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);
|
||||
pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
|
||||
|
||||
if (u->transport->set_microphone_gain == 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++;
|
||||
|
||||
pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
|
||||
|
||||
u->transport->set_microphone_gain(u->transport, gain);
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static int add_source(struct userdata *u) {
|
||||
pa_source_new_data data;
|
||||
|
|
@ -944,6 +981,10 @@ static int add_source(struct userdata *u) {
|
|||
u->source->userdata = u;
|
||||
u->source->parent.process_msg = source_process_msg;
|
||||
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
|
||||
pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
|
||||
u->source->n_volume_steps = 16;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1021,6 +1062,40 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
return (r < 0 || !failed) ? r : -1;
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void sink_set_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);
|
||||
pa_assert(u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT);
|
||||
|
||||
if (u->transport->set_speaker_gain == 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++;
|
||||
|
||||
pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
|
||||
|
||||
u->transport->set_speaker_gain(u->transport, gain);
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static int add_sink(struct userdata *u) {
|
||||
pa_sink_new_data data;
|
||||
|
|
@ -1064,6 +1139,10 @@ static int add_sink(struct userdata *u) {
|
|||
u->sink->userdata = u;
|
||||
u->sink->parent.process_msg = sink_process_msg;
|
||||
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT) {
|
||||
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
|
||||
u->sink->n_volume_steps = 16;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1975,6 +2054,54 @@ 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) {
|
||||
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->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++;
|
||||
|
||||
pa_cvolume_set(&v, u->sample_spec.channels, volume);
|
||||
pa_sink_volume_changed(u->sink, &v);
|
||||
|
||||
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) {
|
||||
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->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++;
|
||||
|
||||
pa_cvolume_set(&v, u->sample_spec.channels, volume);
|
||||
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);
|
||||
|
|
@ -2039,6 +2166,13 @@ 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_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);
|
||||
|
||||
|
||||
if (add_card(u) < 0)
|
||||
goto fail;
|
||||
|
||||
|
|
@ -2091,6 +2225,12 @@ 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_microphone_gain_changed_slot)
|
||||
pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
|
||||
|
||||
if (u->sbc_info.buffer)
|
||||
pa_xfree(u->sbc_info.buffer);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue