mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-08 18:55:42 +01:00
topology: pre-processor: Move the call to expand variables
Remove the call to snd_config_expand_custom() to expand the top-level input config. And replace it with calls to snd_config_evaluate_string() for each non-compound config while processing individual objects. This will allow retreving variable definitions from object attribute values and global definitions. Add a new field "current_obj_cfg" to hold the current object config being pre-processed. This will facilitate adding simple math expressions for computing attribute values for objects based on other attributes. For ex: we can set the expression for buffer size as follows: buffer_size "$[($in_channels * 48) * 4]" The buffer_size attribute value will be computed with the attribute value "in_channels" based on the expression above. So if $in_channels = 2, buffer_size will be evaluated to 384. Additionally this change also permits computing attribute values based on previously computed values. For example: buffer_size "$[($in_channels * 48) * 4]" dma_buffer_size "$[$buffer_size * 2]" dma_buffer_size will be computed as 768. Note that the order of definitions for buffer_size and dma_buffer_size matters because the evaluation for dma_buffer_size depends on the evaluation of buffer_size. In order to conform to this, the tplg_object_copy_and_add_param() is modified to add attribute configs from class config to an object using snd_config_before() instead of snd_config_add(). With this change, we no longer need to set the auto_attr_updater for buffer type widget objects. So remove it. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
8e71fba810
commit
90f5967178
7 changed files with 158 additions and 179 deletions
|
@ -3,9 +3,13 @@
|
|||
#GDB="gdb --args"
|
||||
#GDB="strace"
|
||||
#GDB="valgrind --leak-check=yes --show-reachable=yes"
|
||||
GDB="perf stat"
|
||||
#GDB="perf stat"
|
||||
PROG=./alsaucm
|
||||
PROG=/home/perex/git/pipewire/builddir/spa/plugins/alsa/spa-acp-tool
|
||||
PROG="$HOME/git/pulseaudio/build/src/daemon/pulseaudio -n -F $HOME/git/pulseaudio/build/src/daemon/default.pa -p $HOME/git/pulseaudio/build/src/modules/"
|
||||
#PROG=pulseaudio
|
||||
|
||||
#ALSA_CONFIG_UCM="$HOME/alsa/alsa-ucm-conf/ucm" \
|
||||
ALSA_CONFIG_UCM2="$HOME/alsa/alsa-ucm-conf/ucm2" \
|
||||
LD_PRELOAD="$HOME/alsa/alsa-lib/src/.libs/libasound.so" \
|
||||
$GDB ./alsaucm "$@"
|
||||
$GDB $PROG "$@"
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <time.h>
|
||||
#include <locale.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <alsa/use-case.h>
|
||||
#include <assert.h>
|
||||
#include <termios.h>
|
||||
#include <signal.h>
|
||||
|
@ -452,6 +453,30 @@ static ssize_t xwrite(int fd, const void *buf, size_t count)
|
|||
return offset;
|
||||
}
|
||||
|
||||
static int open_ucm(snd_use_case_mgr_t **uc_mgr, char **pcm_name, const char *name)
|
||||
{
|
||||
char *s, *p;
|
||||
int err;
|
||||
|
||||
s = strdup(name);
|
||||
if (s == NULL)
|
||||
return -ENOMEM;
|
||||
p = strchr(s, '.');
|
||||
if (p == NULL)
|
||||
return -EINVAL;
|
||||
*p = '\0';
|
||||
err = snd_use_case_mgr_open(uc_mgr, s);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_use_case_get(*uc_mgr, p + 1, (const char **)pcm_name);
|
||||
if (err < 0) {
|
||||
error(_("UCM value '%s' error: %s"), p + 1, snd_strerror(err));
|
||||
snd_use_case_mgr_close(*uc_mgr);
|
||||
return err;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static long parse_long(const char *str, int *err)
|
||||
{
|
||||
long val;
|
||||
|
@ -528,6 +553,7 @@ int main(int argc, char *argv[])
|
|||
int do_device_list = 0, do_pcm_list = 0, force_sample_format = 0;
|
||||
snd_pcm_info_t *info;
|
||||
FILE *direction;
|
||||
snd_use_case_mgr_t *uc_mgr = NULL;
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
setlocale(LC_ALL, "");
|
||||
|
@ -826,6 +852,16 @@ int main(int argc, char *argv[])
|
|||
goto __end;
|
||||
}
|
||||
|
||||
if (strncmp(pcm_name, "ucm.", 4) == 0) {
|
||||
err = open_ucm(&uc_mgr, &pcm_name, pcm_name + 4);
|
||||
if (err < 0) {
|
||||
error(_("UCM open error: %s"), snd_strerror(err));
|
||||
return 1;
|
||||
}
|
||||
if (verbose)
|
||||
fprintf(stderr, _("Found UCM PCM device: %s\n"), pcm_name);
|
||||
}
|
||||
|
||||
err = snd_pcm_open(&handle, pcm_name, stream, open_mode);
|
||||
if (err < 0) {
|
||||
error(_("audio open error: %s"), snd_strerror(err));
|
||||
|
@ -915,6 +951,8 @@ int main(int argc, char *argv[])
|
|||
if (verbose==2)
|
||||
putchar('\n');
|
||||
snd_pcm_close(handle);
|
||||
if (uc_mgr)
|
||||
snd_use_case_mgr_close(uc_mgr);
|
||||
handle = NULL;
|
||||
free(audiobuf);
|
||||
__end:
|
||||
|
|
|
@ -423,112 +423,3 @@ err:
|
|||
free(sink_widget_name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tplg_get_sample_size_from_format(const char *format)
|
||||
{
|
||||
if (!strcmp(format, "s32le") || !strcmp(format, "s24le") || !strcmp(format, "float"))
|
||||
return 4;
|
||||
|
||||
if (!strcmp(format, "s16le"))
|
||||
return 2;
|
||||
|
||||
SNDERR("Unsupported format: %s\n", format);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp,
|
||||
snd_config_t *buffer_cfg, snd_config_t *parent)
|
||||
{
|
||||
snd_config_iterator_t i, next;
|
||||
snd_config_t *n, *pipeline_cfg, *child;
|
||||
const char *buffer_id, *format;
|
||||
long periods, channels, sample_size;
|
||||
long sched_period, rate, frames;
|
||||
long buffer_size;
|
||||
int err;
|
||||
|
||||
if (snd_config_get_id(buffer_cfg, &buffer_id) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!parent) {
|
||||
SNDERR("No parent for buffer %s\n", buffer_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* acquire attributes from buffer config */
|
||||
snd_config_for_each(i, next, buffer_cfg) {
|
||||
const char *id;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
|
||||
if (!strcmp(id, "periods")) {
|
||||
if (snd_config_get_integer(n, &periods)) {
|
||||
SNDERR("Invalid number of periods for buffer %s\n", buffer_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(id, "channels")) {
|
||||
if (snd_config_get_integer(n, &channels)) {
|
||||
SNDERR("Invalid number of channels for buffer %s\n", buffer_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(id, "format")) {
|
||||
if (snd_config_get_string(n, &format)) {
|
||||
SNDERR("Invalid format for buffer %s\n", buffer_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline_cfg = tplg_object_get_instance_config(tplg_pp, parent);
|
||||
|
||||
/* acquire some other attributes from parent pipeline config */
|
||||
snd_config_for_each(i, next, pipeline_cfg) {
|
||||
const char *id;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
|
||||
if (!strcmp(id, "period")) {
|
||||
if (snd_config_get_integer(n, &sched_period)) {
|
||||
SNDERR("Invalid period for buffer %s\n", buffer_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(id, "rate")) {
|
||||
if (snd_config_get_integer(n, &rate)) {
|
||||
SNDERR("Invalid rate for buffer %s\n", buffer_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate buffer size */
|
||||
sample_size = tplg_get_sample_size_from_format(format);
|
||||
if (sample_size < 0) {
|
||||
SNDERR("Invalid sample size value for %s\n", buffer_id);
|
||||
return sample_size;
|
||||
}
|
||||
frames = (rate * sched_period) / 1000000;
|
||||
buffer_size = periods * sample_size * channels * frames;
|
||||
|
||||
/* add size child config to buffer config */
|
||||
err = tplg_config_make_add(&child, "size", SND_CONFIG_TYPE_INTEGER, buffer_cfg);
|
||||
if (err < 0) {
|
||||
SNDERR("Error creating size config for %s\n", buffer_id);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = snd_config_set_integer(child, buffer_size);
|
||||
if (err < 0)
|
||||
SNDERR("Error setting size config for %s\n", buffer_id);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -1014,8 +1014,7 @@ const struct build_function_map object_build_map[] = {
|
|||
&hwcfg_config},
|
||||
{"Base", "fe_dai", "dai", &tplg_build_fe_dai_object, NULL, &fe_dai_config},
|
||||
{"Base", "route", "SectionGraph", &tplg_build_dapm_route_object, NULL, NULL},
|
||||
{"Widget", "buffer", "SectionWidget", &tplg_build_generic_object,
|
||||
tplg_update_buffer_auto_attr, &widget_config},
|
||||
{"Widget", "buffer", "SectionWidget", &tplg_build_generic_object, NULL, &widget_config},
|
||||
{"Widget", "", "SectionWidget", &tplg_build_generic_object, NULL, &widget_config},
|
||||
{"Control", "mixer", "SectionControlMixer", &tplg_build_mixer_control, NULL,
|
||||
&mixer_control_config},
|
||||
|
@ -1087,10 +1086,13 @@ static int tplg_object_copy_and_add_param(struct tplg_pre_processor *tplg_pp,
|
|||
snd_config_t *attr_cfg,
|
||||
snd_config_t *search_config)
|
||||
{
|
||||
snd_config_t *attr, *new;
|
||||
snd_config_iterator_t first = snd_config_iterator_first(obj);
|
||||
snd_config_t *attr, *new, *first_cfg;
|
||||
const char *id, *search_id;
|
||||
int ret;
|
||||
|
||||
first_cfg = snd_config_iterator_entry(first);
|
||||
|
||||
if (snd_config_get_id(attr_cfg, &id) < 0)
|
||||
return 0;
|
||||
|
||||
|
@ -1108,10 +1110,19 @@ static int tplg_object_copy_and_add_param(struct tplg_pre_processor *tplg_pp,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_config_add(obj, new);
|
||||
if (ret < 0) {
|
||||
snd_config_delete(new);
|
||||
SNDERR("error adding attribute '%s' value to %s\n", id, search_id);
|
||||
if (first_cfg) {
|
||||
/* prepend the new config */
|
||||
ret = snd_config_add_before(first_cfg, new);
|
||||
if (ret < 0) {
|
||||
snd_config_delete(new);
|
||||
SNDERR("error prepending attribute '%s' value to %s\n", id, search_id);
|
||||
}
|
||||
} else {
|
||||
ret = snd_config_add(obj, new);
|
||||
if (ret < 0) {
|
||||
snd_config_delete(new);
|
||||
SNDERR("error adding attribute '%s' value to %s\n", id, search_id);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1488,12 +1499,67 @@ snd_config_t *tplg_object_get_instance_config(struct tplg_pre_processor *tplg_pp
|
|||
return snd_config_iterator_entry(first);
|
||||
}
|
||||
|
||||
#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
|
||||
static int pre_process_find_variable(snd_config_t **dst, const char *str, snd_config_t *config)
|
||||
{
|
||||
snd_config_iterator_t i, next;
|
||||
|
||||
snd_config_for_each(i, next, config) {
|
||||
snd_config_t *n;
|
||||
const char *id;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
|
||||
if (strcmp(id, str))
|
||||
continue;
|
||||
|
||||
/* found definition, copy config */
|
||||
return snd_config_copy(dst, n);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
static int
|
||||
pre_process_object_variables_expand_fcn(snd_config_t **dst, const char *str, void *private_data)
|
||||
{
|
||||
|
||||
struct tplg_pre_processor *tplg_pp = private_data;
|
||||
snd_config_t *object_cfg = tplg_pp->current_obj_cfg;
|
||||
snd_config_t *conf_defines;
|
||||
const char *object_id;
|
||||
int ret;
|
||||
|
||||
ret = snd_config_search(tplg_pp->input_cfg, "Define", &conf_defines);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
/* find variable from global definitions first */
|
||||
ret = pre_process_find_variable(dst, str, conf_defines);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (snd_config_get_id(object_cfg, &object_id) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* find variable from object attribute values if not found in global definitions */
|
||||
ret = pre_process_find_variable(dst, str, object_cfg);
|
||||
if (ret < 0)
|
||||
SNDERR("Failed to find definition for attribute %s in '%s' object\n",
|
||||
str, object_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* build object config and its child objects recursively */
|
||||
static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *new_obj,
|
||||
snd_config_t *parent)
|
||||
{
|
||||
snd_config_t *obj_local, *class_cfg;
|
||||
const struct build_function_map *map;
|
||||
snd_config_iterator_t i, next;
|
||||
build_func builder;
|
||||
update_auto_attr_func auto_attr_updater;
|
||||
const char *id, *class_id;
|
||||
|
@ -1534,6 +1600,46 @@ static int tplg_build_object(struct tplg_pre_processor *tplg_pp, snd_config_t *n
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if SND_LIB_VER(1, 2, 5) < SND_LIB_VERSION
|
||||
tplg_pp_config_debug(tplg_pp, obj_local);
|
||||
|
||||
/* expand all non-compound type child configs in object */
|
||||
snd_config_for_each(i, next, obj_local) {
|
||||
snd_config_t *n, *new;
|
||||
const char *id, *s;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
|
||||
if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND)
|
||||
continue;
|
||||
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
|
||||
if (snd_config_get_string(n, &s) < 0)
|
||||
continue;
|
||||
|
||||
if (*s != '$')
|
||||
continue;
|
||||
|
||||
tplg_pp->current_obj_cfg = obj_local;
|
||||
|
||||
/* expand config */
|
||||
ret = snd_config_evaluate_string(&new, s, pre_process_object_variables_expand_fcn,
|
||||
tplg_pp);
|
||||
if (ret < 0) {
|
||||
SNDERR("Failed to evaluate attributes %s in %s\n", id, class_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_config_set_id(new, id);
|
||||
|
||||
ret = snd_config_merge(n, new, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Build objects if object type is supported.
|
||||
* If not, process object attributes and add to parent's data section
|
||||
|
|
|
@ -259,57 +259,6 @@ static int pre_process_defines(struct tplg_pre_processor *tplg_pp, const char *p
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pre_process_variables_expand_fcn(snd_config_t **dst, const char *str,
|
||||
void *private_data)
|
||||
{
|
||||
struct tplg_pre_processor *tplg_pp = private_data;
|
||||
snd_config_iterator_t i, next;
|
||||
snd_config_t *conf_defines;
|
||||
int ret;
|
||||
|
||||
ret = snd_config_search(tplg_pp->input_cfg, "Define", &conf_defines);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
/* find variable definition */
|
||||
snd_config_for_each(i, next, conf_defines) {
|
||||
snd_config_t *n;
|
||||
const char *id;
|
||||
|
||||
n = snd_config_iterator_entry(i);
|
||||
if (snd_config_get_id(n, &id) < 0)
|
||||
continue;
|
||||
|
||||
if (strcmp(id, str))
|
||||
continue;
|
||||
|
||||
/* found definition. Match type and return appropriate config */
|
||||
if (snd_config_get_type(n) == SND_CONFIG_TYPE_STRING) {
|
||||
const char *s;
|
||||
|
||||
if (snd_config_get_string(n, &s) < 0)
|
||||
continue;
|
||||
|
||||
return snd_config_imake_string(dst, NULL, s);
|
||||
}
|
||||
|
||||
if (snd_config_get_type(n) == SND_CONFIG_TYPE_INTEGER) {
|
||||
long v;
|
||||
|
||||
if (snd_config_get_integer(n, &v) < 0)
|
||||
continue;
|
||||
|
||||
ret = snd_config_imake_integer(dst, NULL, v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fprintf(stderr, "No definition for variable %s\n", str);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int pre_process_includes(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
|
||||
const char *pre_processor_defs, const char *inc_path);
|
||||
|
||||
|
@ -558,14 +507,6 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_
|
|||
fprintf(stderr, "Failed to process conditional includes in input config\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* expand pre-processor variables */
|
||||
err = snd_config_expand_custom(tplg_pp->input_cfg, tplg_pp->input_cfg, pre_process_variables_expand_fcn,
|
||||
tplg_pp, &tplg_pp->input_cfg);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "Failed to expand pre-processor definitions in input config\n");
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
|
||||
|
|
|
@ -81,8 +81,6 @@ int tplg_build_pcm_caps_object(struct tplg_pre_processor *tplg_pp,
|
|||
snd_config_t *obj_cfg, snd_config_t *parent);
|
||||
int tplg_parent_update(struct tplg_pre_processor *tplg_pp, snd_config_t *parent,
|
||||
const char *section_name, const char *item_name);
|
||||
int tplg_update_buffer_auto_attr(struct tplg_pre_processor *tplg_pp,
|
||||
snd_config_t *buffer_cfg, snd_config_t *parent);
|
||||
int tplg_add_object_data(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg,
|
||||
snd_config_t *top, const char *array_name);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ struct tplg_pre_processor {
|
|||
snd_config_t *output_cfg;
|
||||
snd_output_t *output;
|
||||
snd_output_t *dbg_output;
|
||||
snd_config_t *current_obj_cfg;
|
||||
};
|
||||
|
||||
int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
|
||||
|
|
Loading…
Reference in a new issue