From 4f0d8b0c86d2dfaa7f70ceb0ffe5fdc627af1071 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Tue, 3 Sep 2024 22:03:18 +0200 Subject: [PATCH] Implement channel map query for ALSA PCM plug-in --- src/asound/bluealsa-pcm.c | 101 ++++++++++++++++++++++++++++++++--- src/shared/dbus-client-pcm.c | 59 ++++++++++++++------ src/shared/dbus-client-pcm.h | 2 + 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/asound/bluealsa-pcm.c b/src/asound/bluealsa-pcm.c index 67a14b6bb..fc8b2f249 100644 --- a/src/asound/bluealsa-pcm.c +++ b/src/asound/bluealsa-pcm.c @@ -580,17 +580,34 @@ static int bluealsa_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) debug2("Changing BlueALSA PCM configuration: %u ch, %u Hz -> %u ch, %u Hz", pcm->ba_pcm.channels, pcm->ba_pcm.sampling, channels, sampling); - if (ba_dbus_pcm_select_codec(&pcm->dbus_ctx, pcm->ba_pcm.pcm_path, - pcm->ba_pcm.codec.name, pcm->ba_pcm_codec_config, pcm->ba_pcm_codec_config_len, + const char *codec_name = pcm->ba_pcm.codec.name; + if (!ba_dbus_pcm_select_codec(&pcm->dbus_ctx, pcm->ba_pcm.pcm_path, + codec_name, pcm->ba_pcm_codec_config, pcm->ba_pcm_codec_config_len, channels, sampling, BA_PCM_SELECT_CODEC_FLAG_NONE, &err)) { - pcm->ba_pcm.channels = channels; - pcm->ba_pcm.sampling = sampling; - } - else { SNDERR("Couldn't change BlueALSA PCM configuration: %s", err.message); return -dbus_error_to_errno(&err); } + /* After new codec selection, it is necessary to update the PCM data. + * We will do it the off-line manner (without server interaction) to + * speed up the process. */ + + pcm->ba_pcm.channels = channels; + pcm->ba_pcm.sampling = sampling; + + for (size_t i = 0; i < pcm->ba_pcm_codecs.codecs_len; i++) { + const struct ba_pcm_codec *codec = &pcm->ba_pcm_codecs.codecs[i]; + if (strcmp(codec->name, codec_name) == 0) { + for (size_t j = 0; j < ARRAYSIZE(codec->channel_maps); j++) + if (codec->channels[j] == channels) { + memcpy(pcm->ba_pcm.channel_map, codec->channel_maps[j], + sizeof(pcm->ba_pcm.channel_map)); + break; + } + break; + } + } + } #if BLUEALSA_HW_PARAMS_FIX @@ -1133,6 +1150,76 @@ static int bluealsa_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd, return -ENODEV; } +static enum snd_pcm_chmap_position ba_channel_map_to_position(const char *tag) { + + static const struct { + const char *tag; + enum snd_pcm_chmap_position pos; + } mapping[] = { + { "MONO", SND_CHMAP_MONO }, + { "FL", SND_CHMAP_FL }, + { "FR", SND_CHMAP_FR }, + { "RL", SND_CHMAP_RL }, + { "RR", SND_CHMAP_RR }, + { "FC", SND_CHMAP_FC }, + { "LFE", SND_CHMAP_LFE }, + { "SL", SND_CHMAP_SL }, + { "SR", SND_CHMAP_SR }, + }; + + for (size_t i = 0; i < ARRAYSIZE(mapping); i++) + if (strcmp(tag, mapping[i].tag) == 0) + return mapping[i].pos; + return SND_CHMAP_UNKNOWN; +} + +static snd_pcm_chmap_query_t **bluealsa_query_chmaps(snd_pcm_ioplug_t *io) { + struct bluealsa_pcm *pcm = io->private_data; + + const struct ba_pcm_codec *codec = &pcm->ba_pcm.codec; + for (size_t i = 0; i < pcm->ba_pcm_codecs.codecs_len; i++) + if (strcmp(pcm->ba_pcm_codecs.codecs[i].name, codec->name) == 0) { + codec = &pcm->ba_pcm_codecs.codecs[i]; + break; + } + + snd_pcm_chmap_query_t **maps; + if ((maps = malloc(sizeof(*maps) * (ARRAYSIZE(codec->channel_maps) + 1))) == NULL) + return NULL; + + maps[ARRAYSIZE(codec->channel_maps)] = NULL; + for (size_t i = 0; i < ARRAYSIZE(codec->channel_maps); i++) { + + unsigned int channels; + if ((channels = codec->channels[i]) == 0) + break; + + maps[i] = malloc(sizeof(*maps[i]) + (channels * sizeof(*maps[i]->map.pos))); + maps[i]->type = SND_CHMAP_TYPE_FIXED; + maps[i]->map.channels = channels; + + for (size_t j = 0; j < channels; j++) + maps[i]->map.pos[j] = ba_channel_map_to_position(codec->channel_maps[i][j]); + + } + + return maps; +} + +static snd_pcm_chmap_t *bluealsa_get_chmap(snd_pcm_ioplug_t *io) { + struct bluealsa_pcm *pcm = io->private_data; + + snd_pcm_chmap_t *map; + if ((map = malloc(sizeof(*map) + (io->channels * sizeof(*map->pos)))) == NULL) + return NULL; + + map->channels = io->channels; + for (size_t i = 0; i < io->channels; i++) + map->pos[i] = ba_channel_map_to_position(pcm->ba_pcm.channel_map[i]); + + return map; +} + static const snd_pcm_ioplug_callback_t bluealsa_callback = { .start = bluealsa_start, .stop = bluealsa_stop, @@ -1149,6 +1236,8 @@ static const snd_pcm_ioplug_callback_t bluealsa_callback = { .poll_descriptors_count = bluealsa_poll_descriptors_count, .poll_descriptors = bluealsa_poll_descriptors, .poll_revents = bluealsa_poll_revents, + .query_chmaps = bluealsa_query_chmaps, + .get_chmap = bluealsa_get_chmap, }; static int str2bdaddr(const char *str, bdaddr_t *ba) { diff --git a/src/shared/dbus-client-pcm.c b/src/shared/dbus-client-pcm.c index 5eea33852..7705959de 100644 --- a/src/shared/dbus-client-pcm.c +++ b/src/shared/dbus-client-pcm.c @@ -232,32 +232,54 @@ static void dbus_message_iter_get_codec_data(DBusMessageIter *variant, static void dbus_message_iter_get_codec_supported_channels(DBusMessageIter *variant, struct ba_pcm_codec *codec) { - DBusMessageIter iter; - unsigned char *data; - int len; + DBusMessageIter iter; + unsigned char *data; + int len; - dbus_message_iter_recurse(variant, &iter); - dbus_message_iter_get_fixed_array(&iter, &data, &len); + dbus_message_iter_recurse(variant, &iter); + dbus_message_iter_get_fixed_array(&iter, &data, &len); - len = MIN(len, ARRAYSIZE(codec->channels)); - for (size_t i = 0; i < (size_t)len; i++) - codec->channels[i] = data[i]; + len = MIN(len, ARRAYSIZE(codec->channels)); + for (size_t i = 0; i < (size_t)len; i++) + codec->channels[i] = data[i]; } static void dbus_message_iter_get_codec_supported_sampling(DBusMessageIter *variant, struct ba_pcm_codec *codec) { - DBusMessageIter iter; - dbus_uint32_t *data; - int len; + DBusMessageIter iter; + dbus_uint32_t *data; + int len; - dbus_message_iter_recurse(variant, &iter); - dbus_message_iter_get_fixed_array(&iter, &data, &len); + dbus_message_iter_recurse(variant, &iter); + dbus_message_iter_get_fixed_array(&iter, &data, &len); - len = MIN(len, ARRAYSIZE(codec->sampling)); - for (size_t i = 0; i < (size_t)len; i++) - codec->sampling[i] = data[i]; + len = MIN(len, ARRAYSIZE(codec->sampling)); + for (size_t i = 0; i < (size_t)len; i++) + codec->sampling[i] = data[i]; + +} + +static void dbus_message_iter_get_codec_channel_maps(DBusMessageIter *variant, + struct ba_pcm_codec *codec) { + + size_t i; + DBusMessageIter iter_array; + for (dbus_message_iter_recurse(variant, &iter_array), i = 0; + dbus_message_iter_get_arg_type(&iter_array) != DBUS_TYPE_INVALID; + dbus_message_iter_next(&iter_array)) { + + const char *data[ARRAYSIZE(*codec->channel_maps)]; + size_t length = ARRAYSIZE(data); + + dbus_message_iter_array_get_strings(&iter_array, NULL, data, &length); + + for (size_t j = 0; j < length; j++) + strncpy(codec->channel_maps[i][j], data[j], sizeof(codec->channel_maps[i][j]) - 1); + + i++; + } } @@ -295,6 +317,11 @@ static dbus_bool_t ba_dbus_message_iter_pcm_codec_get_props_cb(const char *key, goto fail; dbus_message_iter_get_codec_supported_sampling(&variant, codec); } + else if (strcmp(key, "ChannelMaps") == 0) { + if (type != (type_expected = DBUS_TYPE_ARRAY)) + goto fail; + dbus_message_iter_get_codec_channel_maps(&variant, codec); + } return TRUE; diff --git a/src/shared/dbus-client-pcm.h b/src/shared/dbus-client-pcm.h index 20eebe875..2d11daedb 100644 --- a/src/shared/dbus-client-pcm.h +++ b/src/shared/dbus-client-pcm.h @@ -89,6 +89,8 @@ struct ba_pcm_codec { unsigned char channels[8]; /* sampling frequencies supported by the codec */ dbus_uint32_t sampling[16]; + /* channel maps associated with supported number of channels */ + char channel_maps[8][8][5]; }; /**