Skip to content

Commit

Permalink
topology: pre-processor: Introduce a new feature for subtree copies
Browse files Browse the repository at this point in the history
Introduce a new kyword "SubTreeCopy" for extneding existing conf nodes
with additional nodes. This feature is useful for extending previous
pipeline class definitions with the addition of one or more widgets
without having to duplicate everything in the new class definition.

For example: Consider a pipeline class definition as below. Note that
only the widgets & routes are shown here.

Class.Pipeline.mixout-gain-dai-copier-playback {
	Object.Widget {
		mixout."1" {}
		dai-copier."1" {}
		gain."1" {}
		pipeline."1" {}
	}

	Object.Base {
		!route [
			{
				source mixout.$index.1
				sink	gain.$index.1
			}
		]
	}
}

If we want to extend this pipeline with the addition of an eqiir/eqfir,
we can create a SubTreeCopy node with type extend as follows:

Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback {
	SubTreeCopy.baseclass {
		source "Class.Pipeline.mixout-gain-dai-copier-playback"
		type extend

		Object.Widget {
			eqiir.1 {}
			eqfir.1 {}
		}

		Object.Base {
			!route [
				{
					source gain.$index.1
					sink   eqiir.$index.1
				}
				{
					source eqiir.$index.1
					sink   eqfir.$index.1
				}
			]
		}
	}
}

Note that the target is left undefined, which means that the newly
created subtree will be merged to the parent node that contains the
"SubTreeCopy" node i.e. in this case
Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback".

But if we want to modify an existing pipeline class while modifying the
order of widgets and/or inserting new widgets, we should use the type
"override" instead. This allows for adding new widgets to the list of
widgets in the base class definition while also allowing overriding the
routes to allow inserting the new widgets and reordering the widgets in
the base class. For example, if we want to add a drc widget between the
gain and the eqiir modules in the above class, we can do the following:

Class.Pipeline.mixout-efx-dai-copier-playback {
	# This copy will override all widgets/routes in the base class
	SubTreeCopy.baseclass {
		source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback"
		target "Class.Pipeline"
		type override

		Object.Widget {
			drc.1 {}
		}

		Object.Base {
			!route [
				{
					source mixout.$index.1
					sink	gain.$index.1
				}
				{
					source gain.$index.1
					sink	drc.$index.1
				}
				{
					source	drc.$index.1
					sink	eqiir.$index.1
				}
				{
					source	eqiir.$index.1
					sink	eqfir.$index.1
				}
			]
		}
	}

	# So explicitly copy the widgets from the base class
	SubTreeCopy.widgets {
		source "Class.Pipeline.mixout-gain-eqiir-eqfir-dai-copier-playback.Object.Widget"
		target "Class.Pipeline.mixout-efx-dai-copier-playback.Object.Widget"
	}
}

Signed-off-by: Ranjani Sridharan <[email protected]>
  • Loading branch information
ranj063 committed May 23, 2024
1 parent cc0bcef commit d1fefac
Showing 1 changed file with 160 additions and 0 deletions.
160 changes: 160 additions & 0 deletions topology/pre-processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,160 @@ static int pre_process_arrays(struct tplg_pre_processor *tplg_pp, snd_config_t *
return 0;
}

static int pre_process_subtree_copy(struct tplg_pre_processor *tplg_pp, snd_config_t *curr,
snd_config_t *top)
{
snd_config_iterator_t i, next;
snd_config_t *subtrees;
int ret;

ret = snd_config_search(curr, "SubTreeCopy", &subtrees);
if (ret < 0)
return 0;

snd_config_for_each(i, next, subtrees) {
snd_config_t *n, *source_cfg, *target_cfg, *type_cfg;
snd_config_t *source_tree, *target_tree, *tmp;
const char *id, *source_id;
const char *type = NULL;
const char *target_id = NULL;
bool override = false;

n = snd_config_iterator_entry(i);
ret = snd_config_get_id(n, &id);
if (ret < 0) {
SNDERR("Failed to get ID for subtree copy\n");
return ret;
}

/* get the type of copy ex: override/extend if set */
ret = snd_config_search(n, "type", &type_cfg);
if (ret >= 0) {
ret = snd_config_get_string(type_cfg, &type);
if (ret >= 0) {
if (strcmp(type, "override") && strcmp(type, "extend")) {
SNDERR("Invalid value for sub tree copy type %s\n", type);
return ret;
}

if (!strcmp(type, "override"))
override = true;
}
}

ret = snd_config_search(n, "source", &source_cfg);
if (ret < 0) {
SNDERR("failed to get source config for subtree %s\n", id);
return ret;
}

/* if the target node is not set, the subtree will be copied to current node */
ret = snd_config_search(n, "target", &target_cfg);
if (ret >= 0) {
ret = snd_config_get_string(target_cfg, &target_id);
if (ret < 0) {
SNDERR("Invalid source node id for subtree %s\n", id);
return ret;
}

ret = snd_config_search(top, target_id, &target_tree);
if (ret < 0) {
SNDERR("failed to get target tree %s\n", target_id);
return ret;
}
} else {
target_tree = curr;
}

/* get the source tree node */
ret = snd_config_get_string(source_cfg, &source_id);
if (ret < 0) {
SNDERR("Invalid source node id for subtree %s\n", id);
return ret;
}

ret = snd_config_search(top, source_id, &source_tree);
if (ret < 0) {
SNDERR("failed to get source tree %s\n", source_id);
return ret;
}

/* delete the source/target/type nodes */
ret = snd_config_delete(source_cfg);
if (ret < 0)
return ret;

if (type) {
ret = snd_config_delete(type_cfg);
if (ret < 0)
return ret;
}

if (target_id) {
ret = snd_config_delete(target_cfg);
if (ret < 0)
return ret;
}

/* make a temp copy of the source tree */
ret = snd_config_copy(&tmp, source_tree);
if (ret < 0) {
SNDERR("failed to copy source tree for subtreecopy %s\n", id);
return ret;
}

/* merge the current block with the source tree */
ret = snd_config_merge(tmp, n, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge source tree w/ subtree %s\n", id);
return ret;
}

/* merge the merged block to the target tree */
ret = snd_config_merge(target_tree, tmp, override);
if (ret < 0) {
snd_config_delete(tmp);
SNDERR("Failed to merge subtree w/ target for %s\n", id);
return ret;
}
}

/* all subtree copies have been processed, remove the node */
snd_config_delete(subtrees);

return 0;
}

static int pre_process_subtree_copies(struct tplg_pre_processor *tplg_pp, snd_config_t *top,
snd_config_t *curr)
{
snd_config_iterator_t i, next;
int ret;

if (snd_config_get_type(curr) != SND_CONFIG_TYPE_COMPOUND)
return 0;

/* process subtreecopies at this node */
ret = pre_process_subtree_copy(tplg_pp, curr, top);
if (ret < 0)
return ret;

/* process subtreecopies at all child nodes */
snd_config_for_each(i, next, curr) {
snd_config_t *n;

n = snd_config_iterator_entry(i);

/* process subtreecopies at this node */
ret = pre_process_subtree_copies(tplg_pp, top, n);
if (ret < 0)
return ret;
}

return 0;
}

#endif /* version < 1.2.6 */

int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size,
Expand Down Expand Up @@ -980,6 +1134,12 @@ int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_
fprintf(stderr, "Failed to process object arrays in input config\n");
goto err;
}

err = pre_process_subtree_copies(tplg_pp, tplg_pp->input_cfg, tplg_pp->input_cfg);
if (err < 0) {
SNDERR("Failed to process subtree copies in input config\n");
goto err;
}
#endif

err = pre_process_config(tplg_pp, tplg_pp->input_cfg);
Expand Down

0 comments on commit d1fefac

Please sign in to comment.