From b3dddfed6a6c13e0fe69cfa2e661e9c2857ba084 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 17 Jul 2025 12:10:25 +0200 Subject: [PATCH] filter-chain: add Level control input port for noisegate This makes it possible to use another volume measurement algorithm to drive the noise gate, such as a VAD algorithm. --- spa/plugins/filter-graph/plugin_builtin.c | 45 ++++++++++++++--------- src/modules/module-filter-chain.c | 4 ++ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/spa/plugins/filter-graph/plugin_builtin.c b/spa/plugins/filter-graph/plugin_builtin.c index ffef8adfe..bdc2e71de 100644 --- a/spa/plugins/filter-graph/plugin_builtin.c +++ b/spa/plugins/filter-graph/plugin_builtin.c @@ -2892,26 +2892,31 @@ static struct spa_fga_port noisegate_ports[] = { .flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO, }, { .index = 2, - .name = "Open Threshold", + .name = "Level", .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, - .def = 0.004f, .min = 0.0f, .max = 1.0f + .def = NAN }, { .index = 3, - .name = "Close Threshold", + .name = "Open Threshold", .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, - .def = 0.003f, .min = 0.0f, .max = 1.0f + .def = 0.04f, .min = 0.0f, .max = 1.0f }, { .index = 4, + .name = "Close Threshold", + .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, + .def = 0.03f, .min = 0.0f, .max = 1.0f + }, + { .index = 5, .name = "Attack (s)", .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, .def = 0.005f, .min = 0.0f, .max = 1.0f }, - { .index = 5, + { .index = 6, .name = "Hold (s)", .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, .def = 0.050f, .min = 0.0f, .max = 1.0f }, - { .index = 6, + { .index = 7, .name = "Release (s)", .flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL, .def = 0.010f, .min = 0.0f, .max = 1.0f @@ -2923,9 +2928,10 @@ static void noisegate_run(void * Instance, unsigned long SampleCount) struct builtin *impl = Instance; float *in = impl->port[0]; float *out = impl->port[1]; + float in_lev = impl->port[2][0]; unsigned long n; - float o_thres = impl->port[2][0]; - float c_thres = impl->port[3][0]; + float o_thres = impl->port[3][0]; + float c_thres = impl->port[4][0]; float gate, hold, o_rate, c_rate, level; int mode; @@ -2937,20 +2943,25 @@ static void noisegate_run(void * Instance, unsigned long SampleCount) return; } - o_rate = 1.0f / (impl->port[4][0] * impl->rate); - c_rate = 1.0f / (impl->port[6][0] * impl->rate); + o_rate = 1.0f / (impl->port[5][0] * impl->rate); + c_rate = 1.0f / (impl->port[7][0] * impl->rate); gate = impl->gate; hold = impl->hold; mode = impl->mode; level = impl->last; - for (n = 0; n < SampleCount; n++) { - float lev = fabsf(in[n]); + spa_log_trace_fp(impl->log, "%f %d %f", level, mode, gate); - if (lev > level) - level = lev; - else - level = lev * 0.05f + level * 0.95f; + for (n = 0; n < SampleCount; n++) { + if (isnan(in_lev)) { + float lev = fabsf(in[n]); + if (lev > level) + level = lev; + else + level = lev * 0.05f + level * 0.95f; + } else { + level = in_lev; + } switch (mode) { case 0: @@ -2964,7 +2975,7 @@ static void noisegate_run(void * Instance, unsigned long SampleCount) if (gate >= 1.0f) { gate = 1.0f; mode = 2; - hold = impl->port[5][0] * impl->rate; + hold = impl->port[6][0] * impl->rate; } break; case 2: diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index 263767068..a02364abd 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -630,6 +630,10 @@ extern struct spa_handle_factory spa_filter_graph_factory; * It has an "In" input port and an "Out" output data ports. Normally the input * data is passed directly to the output. * + * The "Level" control port can be used to control the measured volume of the "In" + * port. When not connected, a simple volume algorithm on the "In" port will be + * used. + * * If the volume drops below "Close threshold", the noisegate will ramp down the * volume to zero for a duration of "Release (s)" seconds. When the volume is above * "Open threshold", the noisegate will ramp up the volume to 1 for a duration