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:
Ranjani Sridharan 2021-12-11 10:45:43 -08:00 committed by Jaroslav Kysela
parent 8e71fba810
commit 90f5967178
7 changed files with 158 additions and 179 deletions

View file

@ -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 "$@"

View file

@ -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:

View file

@ -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;
}

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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,