mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
Merge branch 'bluetooth-muting' into 'master'
Draft: bluetooth: Handle muting over A2DP Absolute Volume See merge request pulseaudio/pulseaudio!533
This commit is contained in:
commit
5c0d45342e
3 changed files with 98 additions and 13 deletions
|
|
@ -103,11 +103,7 @@
|
||||||
"</node>"
|
"</node>"
|
||||||
|
|
||||||
static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
|
static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
|
||||||
pa_volume_t volume = (pa_volume_t) ((
|
pa_volume_t volume = A2DP_GAIN_TO_VOLUME(gain);
|
||||||
gain * PA_VOLUME_NORM
|
|
||||||
/* Round to closest by adding half the denominator */
|
|
||||||
+ A2DP_MAX_GAIN / 2
|
|
||||||
) / A2DP_MAX_GAIN);
|
|
||||||
|
|
||||||
if (volume > PA_VOLUME_NORM)
|
if (volume > PA_VOLUME_NORM)
|
||||||
volume = PA_VOLUME_NORM;
|
volume = PA_VOLUME_NORM;
|
||||||
|
|
@ -116,11 +112,7 @@ static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint16_t volume_to_a2dp_gain(pa_volume_t volume) {
|
static uint16_t volume_to_a2dp_gain(pa_volume_t volume) {
|
||||||
uint16_t gain = (uint16_t) ((
|
uint16_t gain = VOLUME_TO_A2DP_GAIN(volume);
|
||||||
volume * A2DP_MAX_GAIN
|
|
||||||
/* Round to closest by adding half the denominator */
|
|
||||||
+ PA_VOLUME_NORM / 2
|
|
||||||
) / PA_VOLUME_NORM);
|
|
||||||
|
|
||||||
if (gain > A2DP_MAX_GAIN)
|
if (gain > A2DP_MAX_GAIN)
|
||||||
gain = A2DP_MAX_GAIN;
|
gain = A2DP_MAX_GAIN;
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,22 @@
|
||||||
#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb"
|
#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb"
|
||||||
#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-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
|
#define HSP_MAX_GAIN 15
|
||||||
|
|
||||||
|
#define A2DP_MAX_GAIN 127
|
||||||
|
/* Some devices only go as low as 1 */
|
||||||
|
#define A2DP_MUTE_GAIN 1
|
||||||
|
#define A2DP_MIN_GAIN (A2DP_MUTE_GAIN + 1)
|
||||||
|
|
||||||
|
/* Round to closest by adding half the denominator */
|
||||||
|
#define A2DP_GAIN_TO_VOLUME(gain) \
|
||||||
|
((pa_volume_t)(((gain)*PA_VOLUME_NORM + A2DP_MAX_GAIN / 2) / A2DP_MAX_GAIN))
|
||||||
|
#define VOLUME_TO_A2DP_GAIN(volume) \
|
||||||
|
((uint16_t)(((volume)*A2DP_MAX_GAIN + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM))
|
||||||
|
|
||||||
|
#define A2DP_MUTE_VOLUME A2DP_GAIN_TO_VOLUME(A2DP_MUTE_GAIN)
|
||||||
|
#define A2DP_MIN_VOLUME A2DP_GAIN_TO_VOLUME(A2DP_MIN_GAIN)
|
||||||
|
|
||||||
typedef struct pa_bluetooth_transport pa_bluetooth_transport;
|
typedef struct pa_bluetooth_transport pa_bluetooth_transport;
|
||||||
typedef struct pa_bluetooth_device pa_bluetooth_device;
|
typedef struct pa_bluetooth_device pa_bluetooth_device;
|
||||||
typedef struct pa_bluetooth_adapter pa_bluetooth_adapter;
|
typedef struct pa_bluetooth_adapter pa_bluetooth_adapter;
|
||||||
|
|
|
||||||
|
|
@ -945,12 +945,36 @@ static void source_set_volume_cb(pa_source *s) {
|
||||||
pa_assert(u->transport);
|
pa_assert(u->transport);
|
||||||
pa_assert(u->transport->set_source_volume);
|
pa_assert(u->transport->set_source_volume);
|
||||||
|
|
||||||
|
volume = pa_cvolume_max(&s->real_volume);
|
||||||
|
|
||||||
|
/* Prevent setting a gain below A2DP_MIN_GAIN, this is used to detect muting */
|
||||||
|
if (volume < A2DP_MIN_VOLUME)
|
||||||
|
volume = A2DP_MIN_VOLUME;
|
||||||
|
|
||||||
/* In the AG role, send a command to change microphone gain on the HS/HF */
|
/* In the AG role, send a command to change microphone gain on the HS/HF */
|
||||||
volume = u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
if (!s->muted)
|
||||||
|
volume = u->transport->set_source_volume(u->transport, volume);
|
||||||
|
|
||||||
pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume);
|
pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void source_set_mute_cb(pa_source *s) {
|
||||||
|
struct userdata *u;
|
||||||
|
|
||||||
|
pa_assert(s);
|
||||||
|
u = s->userdata;
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(u->source == s);
|
||||||
|
pa_assert(!pa_bluetooth_profile_should_attenuate_volume(u->profile));
|
||||||
|
pa_assert(u->transport);
|
||||||
|
pa_assert(u->transport->set_source_volume);
|
||||||
|
|
||||||
|
if (s->muted)
|
||||||
|
u->transport->set_source_volume(u->transport, 0);
|
||||||
|
else
|
||||||
|
source_set_volume_cb(s);
|
||||||
|
}
|
||||||
|
|
||||||
/* Run from main thread */
|
/* Run from main thread */
|
||||||
static void source_setup_volume_callback(pa_source *s) {
|
static void source_setup_volume_callback(pa_source *s) {
|
||||||
struct userdata *u;
|
struct userdata *u;
|
||||||
|
|
@ -993,6 +1017,8 @@ static void source_setup_volume_callback(pa_source *s) {
|
||||||
u->source_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
|
u->source_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
|
||||||
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
||||||
|
|
||||||
|
// TODO: Mute hook!
|
||||||
|
|
||||||
/* Send initial volume to peer, signalling support for volume control */
|
/* Send initial volume to peer, signalling support for volume control */
|
||||||
u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1012,6 +1038,7 @@ static void source_setup_volume_callback(pa_source *s) {
|
||||||
pa_source_set_soft_volume(s, NULL);
|
pa_source_set_soft_volume(s, NULL);
|
||||||
|
|
||||||
pa_source_set_set_volume_callback(s, source_set_volume_cb);
|
pa_source_set_set_volume_callback(s, source_set_volume_cb);
|
||||||
|
pa_source_set_set_mute_callback(s, source_set_mute_cb);
|
||||||
s->n_volume_steps = HSP_MAX_GAIN + 1;
|
s->n_volume_steps = HSP_MAX_GAIN + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1193,12 +1220,36 @@ static void sink_set_volume_cb(pa_sink *s) {
|
||||||
pa_assert(u->transport);
|
pa_assert(u->transport);
|
||||||
pa_assert(u->transport->set_sink_volume);
|
pa_assert(u->transport->set_sink_volume);
|
||||||
|
|
||||||
|
volume = pa_cvolume_max(&s->real_volume);
|
||||||
|
|
||||||
|
/* Prevent setting a gain below A2DP_MIN_GAIN, this is used to detect muting */
|
||||||
|
if (volume < A2DP_MIN_VOLUME)
|
||||||
|
volume = A2DP_MIN_VOLUME;
|
||||||
|
|
||||||
/* In the AG role, send a command to change speaker gain on the HS/HF */
|
/* In the AG role, send a command to change speaker gain on the HS/HF */
|
||||||
volume = u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
if (!s->muted)
|
||||||
|
volume = u->transport->set_sink_volume(u->transport, volume);
|
||||||
|
|
||||||
pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume);
|
pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sink_set_mute_cb(pa_sink *s) {
|
||||||
|
struct userdata *u;
|
||||||
|
|
||||||
|
pa_assert(s);
|
||||||
|
u = s->userdata;
|
||||||
|
pa_assert(u);
|
||||||
|
pa_assert(u->sink == s);
|
||||||
|
pa_assert(!pa_bluetooth_profile_should_attenuate_volume(u->profile));
|
||||||
|
pa_assert(u->transport);
|
||||||
|
pa_assert(u->transport->set_sink_volume);
|
||||||
|
|
||||||
|
if (s->muted)
|
||||||
|
u->transport->set_sink_volume(u->transport, 0);
|
||||||
|
else
|
||||||
|
sink_set_volume_cb(s);
|
||||||
|
}
|
||||||
|
|
||||||
/* Run from main thread */
|
/* Run from main thread */
|
||||||
static void sink_setup_volume_callback(pa_sink *s) {
|
static void sink_setup_volume_callback(pa_sink *s) {
|
||||||
struct userdata *u;
|
struct userdata *u;
|
||||||
|
|
@ -1246,6 +1297,8 @@ static void sink_setup_volume_callback(pa_sink *s) {
|
||||||
u->sink_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
|
u->sink_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
|
||||||
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
||||||
|
|
||||||
|
// TODO: Hook up mute callback!
|
||||||
|
|
||||||
/* Send initial volume to peer, signalling support for volume control */
|
/* Send initial volume to peer, signalling support for volume control */
|
||||||
u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1258,6 +1311,7 @@ static void sink_setup_volume_callback(pa_sink *s) {
|
||||||
pa_sink_set_soft_volume(s, NULL);
|
pa_sink_set_soft_volume(s, NULL);
|
||||||
|
|
||||||
pa_sink_set_set_volume_callback(s, sink_set_volume_cb);
|
pa_sink_set_set_volume_callback(s, sink_set_volume_cb);
|
||||||
|
pa_sink_set_set_mute_callback(s, sink_set_mute_cb);
|
||||||
|
|
||||||
if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
|
if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
|
||||||
s->n_volume_steps = A2DP_MAX_GAIN + 1;
|
s->n_volume_steps = A2DP_MAX_GAIN + 1;
|
||||||
|
|
@ -2467,12 +2521,25 @@ static pa_hook_result_t transport_sink_volume_changed_cb(pa_bluetooth_discovery
|
||||||
|
|
||||||
sink_setup_volume_callback(u->sink);
|
sink_setup_volume_callback(u->sink);
|
||||||
|
|
||||||
|
if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
|
||||||
|
// TODO: Apply to HSP too
|
||||||
|
if (volume <= A2DP_MUTE_VOLUME) {
|
||||||
|
pa_sink_mute_changed(u->sink, true);
|
||||||
|
/* Do not update local volume; unmute should jump back to previous */
|
||||||
|
return PA_HOOK_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume);
|
pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume);
|
||||||
if (pa_bluetooth_profile_should_attenuate_volume(t->profile))
|
if (pa_bluetooth_profile_should_attenuate_volume(t->profile))
|
||||||
pa_sink_set_volume(u->sink, &v, true, true);
|
pa_sink_set_volume(u->sink, &v, true, true);
|
||||||
else
|
else
|
||||||
pa_sink_volume_changed(u->sink, &v);
|
pa_sink_volume_changed(u->sink, &v);
|
||||||
|
|
||||||
|
/* Unmute _after_ reflecting peer volume */
|
||||||
|
if(u->sink->muted)
|
||||||
|
pa_sink_mute_changed(u->sink, false);
|
||||||
|
|
||||||
return PA_HOOK_OK;
|
return PA_HOOK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2495,6 +2562,15 @@ static pa_hook_result_t transport_source_volume_changed_cb(pa_bluetooth_discover
|
||||||
|
|
||||||
source_setup_volume_callback(u->source);
|
source_setup_volume_callback(u->source);
|
||||||
|
|
||||||
|
if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
|
||||||
|
// TODO: Apply to HSP too
|
||||||
|
if (volume <= A2DP_MUTE_VOLUME) {
|
||||||
|
pa_source_mute_changed(u->source, true);
|
||||||
|
/* Do not update local volume; unmute should jump back to previous */
|
||||||
|
return PA_HOOK_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume);
|
pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume);
|
||||||
|
|
||||||
if (pa_bluetooth_profile_should_attenuate_volume(t->profile))
|
if (pa_bluetooth_profile_should_attenuate_volume(t->profile))
|
||||||
|
|
@ -2502,6 +2578,10 @@ static pa_hook_result_t transport_source_volume_changed_cb(pa_bluetooth_discover
|
||||||
else
|
else
|
||||||
pa_source_volume_changed(u->source, &v);
|
pa_source_volume_changed(u->source, &v);
|
||||||
|
|
||||||
|
/* Unmute _after_ reflecting peer volume */
|
||||||
|
if(u->source->muted)
|
||||||
|
pa_source_mute_changed(u->source, false);
|
||||||
|
|
||||||
return PA_HOOK_OK;
|
return PA_HOOK_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue