mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
bluez5: add property to enable hardware volume control
This commit is contained in:
parent
387f7e327f
commit
edee633f96
5 changed files with 67 additions and 18 deletions
|
|
@ -268,10 +268,19 @@ static int rfcomm_send_reply(struct spa_source *source, const char *data)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool rfcomm_volume_enabled(struct rfcomm *rfcomm)
|
||||||
|
{
|
||||||
|
return rfcomm->device != NULL
|
||||||
|
&& (rfcomm->device->hw_volume_profiles & rfcomm->profile);
|
||||||
|
}
|
||||||
|
|
||||||
static void rfcomm_emit_volume_changed(struct rfcomm *rfcomm, int id, int hw_volume)
|
static void rfcomm_emit_volume_changed(struct rfcomm *rfcomm, int id, int hw_volume)
|
||||||
{
|
{
|
||||||
struct spa_bt_transport_volume *t_volume;
|
struct spa_bt_transport_volume *t_volume;
|
||||||
|
|
||||||
|
if (!rfcomm_volume_enabled(rfcomm))
|
||||||
|
return;
|
||||||
|
|
||||||
if ((id == SPA_BT_VOLUME_ID_RX || id == SPA_BT_VOLUME_ID_TX) && hw_volume >= 0) {
|
if ((id == SPA_BT_VOLUME_ID_RX || id == SPA_BT_VOLUME_ID_TX) && hw_volume >= 0) {
|
||||||
rfcomm->volumes[id].active = true;
|
rfcomm->volumes[id].active = true;
|
||||||
rfcomm->volumes[id].hw_volume = hw_volume;
|
rfcomm->volumes[id].hw_volume = hw_volume;
|
||||||
|
|
@ -335,6 +344,9 @@ static bool rfcomm_send_volume_cmd(struct spa_source *source, int id)
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int hw_volume;
|
int hw_volume;
|
||||||
|
|
||||||
|
if (!rfcomm_volume_enabled(rfcomm))
|
||||||
|
return false;
|
||||||
|
|
||||||
t_volume = rfcomm->transport ? &rfcomm->transport->volumes[id] : NULL;
|
t_volume = rfcomm->transport ? &rfcomm->transport->volumes[id] : NULL;
|
||||||
|
|
||||||
if (!(t_volume && t_volume->active))
|
if (!(t_volume && t_volume->active))
|
||||||
|
|
@ -1185,10 +1197,9 @@ static int sco_set_volume_cb(void *data, int id, float volume)
|
||||||
char *msg;
|
char *msg;
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (!(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT))
|
if (!rfcomm_volume_enabled(rfcomm)
|
||||||
return -ENOTSUP;
|
|| !(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT)
|
||||||
|
|| !(rfcomm->has_volume && rfcomm->volumes[id].active))
|
||||||
if (!(rfcomm->has_volume && rfcomm->volumes[id].active))
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
value = spa_bt_volume_linear_to_hw(volume, t_volume->hw_volume_max);
|
value = spa_bt_volume_linear_to_hw(volume, t_volume->hw_volume_max);
|
||||||
|
|
@ -1473,7 +1484,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
|
||||||
rfcomm->transport = t;
|
rfcomm->transport = t;
|
||||||
|
|
||||||
if (profile == SPA_BT_PROFILE_HSP_AG) {
|
if (profile == SPA_BT_PROFILE_HSP_AG) {
|
||||||
rfcomm->has_volume = true;
|
rfcomm->has_volume = rfcomm_volume_enabled(rfcomm);
|
||||||
rfcomm->hs_state = hsp_hs_init1;
|
rfcomm->hs_state = hsp_hs_init1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1500,8 +1511,10 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag
|
||||||
rfcomm->codec_negotiation_supported = false;
|
rfcomm->codec_negotiation_supported = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rfcomm->has_volume = true;
|
if (rfcomm_volume_enabled(rfcomm)) {
|
||||||
hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL;
|
rfcomm->has_volume = true;
|
||||||
|
hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL;
|
||||||
|
}
|
||||||
|
|
||||||
/* send command to AG with the features supported by Hands-Free */
|
/* send command to AG with the features supported by Hands-Free */
|
||||||
cmd = spa_aprintf("AT+BRSF=%u", hf_features);
|
cmd = spa_aprintf("AT+BRSF=%u", hf_features);
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,9 @@ struct spa_bt_a2dp_codec_switch {
|
||||||
size_t num_paths;
|
size_t num_paths;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define DEFAULT_RECONNECT_PROFILES SPA_BT_PROFILE_NULL
|
||||||
|
#define DEFAULT_HW_VOLUME_PROFILES (SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY | SPA_BT_PROFILE_A2DP_SOURCE)
|
||||||
|
|
||||||
#define BT_DEVICE_DISCONNECTED 0
|
#define BT_DEVICE_DISCONNECTED 0
|
||||||
#define BT_DEVICE_CONNECTED 1
|
#define BT_DEVICE_CONNECTED 1
|
||||||
#define BT_DEVICE_INIT -1
|
#define BT_DEVICE_INIT -1
|
||||||
|
|
@ -665,6 +668,9 @@ static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const
|
||||||
d->monitor = monitor;
|
d->monitor = monitor;
|
||||||
d->path = strdup(path);
|
d->path = strdup(path);
|
||||||
d->battery_path = battery_get_name(d->path);
|
d->battery_path = battery_get_name(d->path);
|
||||||
|
d->reconnect_profiles = DEFAULT_RECONNECT_PROFILES;
|
||||||
|
d->hw_volume_profiles = DEFAULT_HW_VOLUME_PROFILES;
|
||||||
|
|
||||||
spa_list_init(&d->remote_endpoint_list);
|
spa_list_init(&d->remote_endpoint_list);
|
||||||
spa_list_init(&d->transport_list);
|
spa_list_init(&d->transport_list);
|
||||||
spa_list_init(&d->codec_switch_list);
|
spa_list_init(&d->codec_switch_list);
|
||||||
|
|
@ -1464,8 +1470,17 @@ struct spa_bt_transport *spa_bt_transport_create(struct spa_bt_monitor *monitor,
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool spa_bt_transport_volume_enabled(struct spa_bt_transport *transport)
|
||||||
|
{
|
||||||
|
return transport->device != NULL
|
||||||
|
&& (transport->device->hw_volume_profiles & transport->profile);
|
||||||
|
}
|
||||||
|
|
||||||
static void transport_sync_volume(struct spa_bt_transport *transport)
|
static void transport_sync_volume(struct spa_bt_transport *transport)
|
||||||
{
|
{
|
||||||
|
if (!spa_bt_transport_volume_enabled(transport))
|
||||||
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < SPA_BT_VOLUME_ID_TERM; ++i)
|
for (int i = 0; i < SPA_BT_VOLUME_ID_TERM; ++i)
|
||||||
spa_bt_transport_set_volume(transport, i, transport->volumes[i].volume);
|
spa_bt_transport_set_volume(transport, i, transport->volumes[i].volume);
|
||||||
spa_bt_transport_emit_volume_changed(transport);
|
spa_bt_transport_emit_volume_changed(transport);
|
||||||
|
|
@ -1687,7 +1702,8 @@ static void spa_bt_transport_volume_changed(struct spa_bt_transport *transport)
|
||||||
t_volume->hw_volume_max);
|
t_volume->hw_volume_max);
|
||||||
spa_log_debug(monitor->log, "transport %p: volume changed %d(%f) ",
|
spa_log_debug(monitor->log, "transport %p: volume changed %d(%f) ",
|
||||||
transport, t_volume->new_hw_volume, t_volume->volume);
|
transport, t_volume->new_hw_volume, t_volume->volume);
|
||||||
spa_bt_transport_emit_volume_changed(transport);
|
if (spa_bt_transport_volume_enabled(transport))
|
||||||
|
spa_bt_transport_emit_volume_changed(transport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1942,7 +1958,7 @@ static int transport_set_volume(void *data, int id, float volume)
|
||||||
struct spa_bt_transport_volume *t_volume = &transport->volumes[id];
|
struct spa_bt_transport_volume *t_volume = &transport->volumes[id];
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
|
|
||||||
if (!t_volume->active)
|
if (!t_volume->active || !spa_bt_transport_volume_enabled(transport))
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
|
|
||||||
value = spa_bt_volume_linear_to_hw(volume, 127);
|
value = spa_bt_volume_linear_to_hw(volume, 127);
|
||||||
|
|
|
||||||
|
|
@ -288,7 +288,7 @@ static void volume_changed(void *userdata)
|
||||||
struct spa_bt_transport_volume *t_volume;
|
struct spa_bt_transport_volume *t_volume;
|
||||||
float prev_hw_volume;
|
float prev_hw_volume;
|
||||||
|
|
||||||
if (!node->transport)
|
if (!node->transport || !spa_bt_transport_volume_enabled(node->transport))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* PW is the controller for remote device. */
|
/* PW is the controller for remote device. */
|
||||||
|
|
@ -445,7 +445,8 @@ static void dynamic_node_volume_changed(void *data)
|
||||||
int id = SPA_FLAG_CLEAR(node->id, DYNAMIC_NODE_ID_FLAG), volume_id;
|
int id = SPA_FLAG_CLEAR(node->id, DYNAMIC_NODE_ID_FLAG), volume_id;
|
||||||
|
|
||||||
/* Remote device is the controller */
|
/* Remote device is the controller */
|
||||||
if (!node->transport || impl->profile != DEVICE_PROFILE_AG)
|
if (!node->transport || impl->profile != DEVICE_PROFILE_AG
|
||||||
|
|| !spa_bt_transport_volume_enabled(node->transport))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (id == 0 || id == 2)
|
if (id == 0 || id == 2)
|
||||||
|
|
@ -1514,7 +1515,8 @@ static int node_set_volume(struct impl *this, struct node *node, float volumes[]
|
||||||
|
|
||||||
t_volume = node->transport ? &node->transport->volumes[node->id]: NULL;
|
t_volume = node->transport ? &node->transport->volumes[node->id]: NULL;
|
||||||
|
|
||||||
if (t_volume && t_volume->active) {
|
if (t_volume && t_volume->active
|
||||||
|
&& spa_bt_transport_volume_enabled(node->transport)) {
|
||||||
float hw_volume = node_get_hw_volume(node);
|
float hw_volume = node_get_hw_volume(node);
|
||||||
spa_log_debug(this->log, "node %p hardware volume %f", node, hw_volume);
|
spa_log_debug(this->log, "node %p hardware volume %f", node, hw_volume);
|
||||||
|
|
||||||
|
|
@ -1866,11 +1868,16 @@ impl_init(const struct spa_handle_factory *factory,
|
||||||
if (info) {
|
if (info) {
|
||||||
int profiles;
|
int profiles;
|
||||||
this->bt_dev->settings = filter_bluez_device_setting(this, info);
|
this->bt_dev->settings = filter_bluez_device_setting(this, info);
|
||||||
if ((str = spa_dict_lookup(info, "bluez5.reconnect-profiles")) != NULL)
|
|
||||||
profiles = spa_bt_profiles_from_json_array(str);
|
if ((str = spa_dict_lookup(info, "bluez5.reconnect-profiles")) != NULL) {
|
||||||
if (str == NULL || profiles < 0)
|
if ((profiles = spa_bt_profiles_from_json_array(str)) >= 0)
|
||||||
profiles = SPA_BT_PROFILE_NULL;
|
this->bt_dev->reconnect_profiles = profiles;
|
||||||
this->bt_dev->reconnect_profiles = profiles;
|
}
|
||||||
|
|
||||||
|
if ((str = spa_dict_lookup(info, "bluez5.hw-volume")) != NULL) {
|
||||||
|
if ((profiles = spa_bt_profiles_from_json_array(str)) >= 0)
|
||||||
|
this->bt_dev->hw_volume_profiles = profiles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->device.iface = SPA_INTERFACE_INIT(
|
this->device.iface = SPA_INTERFACE_INIT(
|
||||||
|
|
|
||||||
|
|
@ -423,6 +423,8 @@ struct spa_bt_device {
|
||||||
uint8_t battery;
|
uint8_t battery;
|
||||||
int has_battery;
|
int has_battery;
|
||||||
|
|
||||||
|
uint32_t hw_volume_profiles;
|
||||||
|
|
||||||
struct spa_hook_list listener_list;
|
struct spa_hook_list listener_list;
|
||||||
bool added;
|
bool added;
|
||||||
|
|
||||||
|
|
@ -551,7 +553,8 @@ struct spa_bt_transport *spa_bt_transport_find(struct spa_bt_monitor *monitor, c
|
||||||
struct spa_bt_transport *spa_bt_transport_find_full(struct spa_bt_monitor *monitor,
|
struct spa_bt_transport *spa_bt_transport_find_full(struct spa_bt_monitor *monitor,
|
||||||
bool (*callback) (struct spa_bt_transport *t, const void *data),
|
bool (*callback) (struct spa_bt_transport *t, const void *data),
|
||||||
const void *data);
|
const void *data);
|
||||||
int64_t spa_bt_transport_get_delay_nsec(struct spa_bt_transport *t);
|
int64_t spa_bt_transport_get_delay_nsec(struct spa_bt_transport *transport);
|
||||||
|
bool spa_bt_transport_volume_enabled(struct spa_bt_transport *transport);
|
||||||
|
|
||||||
int spa_bt_transport_acquire(struct spa_bt_transport *t, bool optional);
|
int spa_bt_transport_acquire(struct spa_bt_transport *t, bool optional);
|
||||||
int spa_bt_transport_release(struct spa_bt_transport *t);
|
int spa_bt_transport_release(struct spa_bt_transport *t);
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,16 @@ rules = [
|
||||||
# Overload mSBC support for native backend and a specific device.
|
# Overload mSBC support for native backend and a specific device.
|
||||||
#bluez5.msbc-support = false
|
#bluez5.msbc-support = false
|
||||||
|
|
||||||
|
# Hardware volume control (default: [ hfp_ag hsp_ag a2dp_source ])
|
||||||
|
#bluez5.hw-volume = [
|
||||||
|
# hfp_hf
|
||||||
|
# hsp_hs
|
||||||
|
# a2dp_sink
|
||||||
|
# hfp_ag
|
||||||
|
# hsp_ag
|
||||||
|
# a2dp_source
|
||||||
|
#]
|
||||||
|
|
||||||
# LDAC encoding quality
|
# LDAC encoding quality
|
||||||
# Available values: auto (Adaptive Bitrate, default)
|
# Available values: auto (Adaptive Bitrate, default)
|
||||||
# hq (High Quality, 990/909kbps)
|
# hq (High Quality, 990/909kbps)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue