filter-graph: Make a new control_sync function

This function is run for all the nodes with the data loop locked. It can
be used to atomically update multiple node controls.

We can't use the control_changed function because this one runs without
the lock and might do slow things, like what the sofa plugin currently
does.

See #5019
This commit is contained in:
Wim Taymans 2025-12-10 12:15:38 +01:00
parent e7ca02c4d8
commit 7887c365d1
3 changed files with 52 additions and 38 deletions

View file

@ -69,6 +69,7 @@ struct spa_fga_descriptor {
void (*connect_port) (void *instance, unsigned long port, void *data);
void (*control_changed) (void *instance);
void (*control_sync) (void *instance);
void (*activate) (void *instance);
void (*deactivate) (void *instance);

View file

@ -19,6 +19,7 @@
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/support/cpu.h>
#include <spa/support/loop.h>
#include <spa/support/plugin-loader.h>
#include <spa/param/latency-utils.h>
#include <spa/param/tag-utils.h>
@ -221,6 +222,7 @@ struct impl {
struct spa_cpu *cpu;
struct spa_fga_dsp *dsp;
struct spa_plugin_loader *loader;
struct spa_loop *data_loop;
uint64_t info_all;
struct spa_filter_graph_info info;
@ -698,21 +700,46 @@ static int impl_reset(void *object)
return 0;
}
static void node_control_changed(struct node *node)
static int
do_emit_node_control_sync(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
size_t size, void *user_data)
{
const struct spa_fga_descriptor *d = node->desc->desc;
struct impl *impl = user_data;
struct graph *graph = &impl->graph;
struct node *node;
uint32_t i;
spa_list_for_each(node, &graph->node_list, link) {
const struct spa_fga_descriptor *d = node->desc->desc;
if (!node->control_changed || d->control_sync == NULL)
continue;
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] != NULL)
d->control_sync(node->hndl[i]);
}
}
return 0;
}
static void emit_node_control_changed(struct impl *impl)
{
struct graph *graph = &impl->graph;
struct node *node;
uint32_t i;
if (!node->control_changed)
return;
spa_loop_locked(impl->data_loop, do_emit_node_control_sync, 1, NULL, 0, impl);
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] == NULL)
spa_list_for_each(node, &graph->node_list, link) {
const struct spa_fga_descriptor *d = node->desc->desc;
if (!node->control_changed)
continue;
if (d->control_changed)
d->control_changed(node->hndl[i]);
if (d->control_changed != NULL) {
for (i = 0; i < node->n_hndl; i++) {
if (node->hndl[i] != NULL)
d->control_changed(node->hndl[i]);
}
}
node->control_changed = false;
}
node->control_changed = false;
}
static int sync_volume(struct graph *graph, struct volume *vol)
@ -826,11 +853,7 @@ static int impl_set_props(void *object, enum spa_direction direction, const stru
spa_pod_dynamic_builder_clean(&b);
if (changed > 0) {
struct node *node;
spa_list_for_each(node, &graph->node_list, link)
node_control_changed(node);
emit_node_control_changed(impl);
spa_filter_graph_emit_props_changed(&impl->hooks, SPA_DIRECTION_INPUT);
}
return 0;
@ -1695,10 +1718,10 @@ static int impl_activate(void *object, const struct spa_dict *props)
for (i = 0; i < node->n_hndl; i++) {
if (d->activate)
d->activate(node->hndl[i]);
if (node->control_changed && d->control_changed)
d->control_changed(node->hndl[i]);
}
}
emit_node_control_changed(impl);
/* calculate latency */
sort_reset(graph);
while ((node = sort_next_node(graph)) != NULL) {
@ -2346,6 +2369,7 @@ impl_init(const struct spa_handle_factory *factory,
spa_log_topic_init(impl->log, &log_topic);
impl->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
impl->max_align = spa_cpu_get_max_align(impl->cpu);
impl->dsp = spa_fga_dsp_new(impl->cpu ? spa_cpu_get_flags(impl->cpu) : 0);

View file

@ -39,7 +39,6 @@ struct plugin {
struct spa_fga_dsp *dsp;
struct spa_log *log;
struct spa_loop *data_loop;
};
struct builtin {
@ -547,11 +546,9 @@ static void bq_run(void *Instance, unsigned long samples)
spa_fga_dsp_biquad_run(impl->dsp, bq, 1, 0, &out, (const float **)&in, 1, samples);
}
static int
do_bq_control_changed(struct spa_loop *loop, bool async, uint32_t seq, const void *data,
size_t size, void *user_data)
static void bq_control_sync(void * Instance)
{
struct builtin *impl = user_data;
struct builtin *impl = Instance;
if (impl->type == BQ_NONE) {
float b0, b1, b2, a0, a1, a2;
b0 = impl->port[5][0];
@ -571,13 +568,6 @@ do_bq_control_changed(struct spa_loop *loop, bool async, uint32_t seq, const voi
if (impl->freq != freq || impl->Q != Q || impl->gain != gain)
bq_freq_update(impl, impl->type, freq, Q, gain);
}
return 0;
}
static void bq_control_changed(void * Instance)
{
struct builtin *impl = Instance;
spa_loop_locked(impl->plugin->data_loop, do_bq_control_changed, 1, NULL, 0, impl);
}
/** bq_lowpass */
@ -589,7 +579,7 @@ static const struct spa_fga_descriptor bq_lowpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -604,7 +594,7 @@ static const struct spa_fga_descriptor bq_highpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -619,7 +609,7 @@ static const struct spa_fga_descriptor bq_bandpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -634,7 +624,7 @@ static const struct spa_fga_descriptor bq_lowshelf_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -649,7 +639,7 @@ static const struct spa_fga_descriptor bq_highshelf_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -664,7 +654,7 @@ static const struct spa_fga_descriptor bq_peaking_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -679,7 +669,7 @@ static const struct spa_fga_descriptor bq_notch_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -695,7 +685,7 @@ static const struct spa_fga_descriptor bq_allpass_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -710,7 +700,7 @@ static const struct spa_fga_descriptor bq_raw_desc = {
.instantiate = bq_instantiate,
.connect_port = builtin_connect_port,
.control_changed = bq_control_changed,
.control_sync = bq_control_sync,
.activate = bq_activate,
.run = bq_run,
.cleanup = builtin_cleanup,
@ -3411,7 +3401,6 @@ impl_init(const struct spa_handle_factory *factory,
impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
impl->dsp = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_FILTER_GRAPH_AudioDSP);
impl->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
for (uint32_t i = 0; info && i < info->n_items; i++) {
const char *k = info->items[i].key;