mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
filter-graph: add dcblock and ramp plugins
This commit is contained in:
parent
df271d13f3
commit
94e823ddad
3 changed files with 325 additions and 8 deletions
|
|
@ -2123,16 +2123,248 @@ static struct spa_fga_port max_ports[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spa_fga_descriptor max_desc = {
|
static const struct spa_fga_descriptor max_desc = {
|
||||||
.name = "max",
|
.name = "max",
|
||||||
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
.n_ports = SPA_N_ELEMENTS(max_ports),
|
.n_ports = SPA_N_ELEMENTS(max_ports),
|
||||||
.ports = max_ports,
|
.ports = max_ports,
|
||||||
|
|
||||||
.instantiate = builtin_instantiate,
|
.instantiate = builtin_instantiate,
|
||||||
.connect_port = builtin_connect_port,
|
.connect_port = builtin_connect_port,
|
||||||
.run = max_run,
|
.run = max_run,
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* DC blocking */
|
||||||
|
struct dcblock {
|
||||||
|
float xm1;
|
||||||
|
float ym1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dcblock_impl {
|
||||||
|
struct plugin *plugin;
|
||||||
|
|
||||||
|
struct spa_fga_dsp *dsp;
|
||||||
|
struct spa_log *log;
|
||||||
|
|
||||||
|
unsigned long rate;
|
||||||
|
float *port[17];
|
||||||
|
|
||||||
|
struct dcblock dc[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void *dcblock_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor,
|
||||||
|
unsigned long SampleRate, int index, const char *config)
|
||||||
|
{
|
||||||
|
struct plugin *pl = SPA_CONTAINER_OF(plugin, struct plugin, plugin);
|
||||||
|
struct dcblock_impl *impl;
|
||||||
|
|
||||||
|
impl = calloc(1, sizeof(*impl));
|
||||||
|
if (impl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
impl->plugin = pl;
|
||||||
|
impl->dsp = pl->dsp;
|
||||||
|
impl->log = pl->log;
|
||||||
|
impl->rate = SampleRate;
|
||||||
|
return impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcblock_run_n(struct dcblock dc[], float *dst[], const float *src[],
|
||||||
|
uint32_t n_src, float R, uint32_t n_samples)
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
uint32_t i, n;
|
||||||
|
|
||||||
|
for (i = 0; i < n_src; i++) {
|
||||||
|
const float *in = src[i];
|
||||||
|
float *out = dst[i];
|
||||||
|
float xm1 = dc[i].xm1;
|
||||||
|
float ym1 = dc[i].ym1;
|
||||||
|
|
||||||
|
if (out == NULL || in == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (n = 0; n < n_samples; n++) {
|
||||||
|
x = in[n];
|
||||||
|
y = x - xm1 + R * ym1;
|
||||||
|
xm1 = x;
|
||||||
|
ym1 = y;
|
||||||
|
out[n] = y;
|
||||||
|
}
|
||||||
|
dc[i].xm1 = xm1;
|
||||||
|
dc[i].ym1 = ym1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcblock_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct dcblock_impl *impl = Instance;
|
||||||
|
float R = impl->port[16][0];
|
||||||
|
dcblock_run_n(impl->dc, &impl->port[8], (const float**)&impl->port[0], 8,
|
||||||
|
R, SampleCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dcblock_connect_port(void * Instance, unsigned long Port,
|
||||||
|
float * DataLocation)
|
||||||
|
{
|
||||||
|
struct dcblock_impl *impl = Instance;
|
||||||
|
impl->port[Port] = DataLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct spa_fga_port dcblock_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "In 1",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "In 2",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "In 3",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "In 4",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 4,
|
||||||
|
.name = "In 5",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 5,
|
||||||
|
.name = "In 6",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 6,
|
||||||
|
.name = "In 7",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 7,
|
||||||
|
.name = "In 8",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
|
||||||
|
{ .index = 8,
|
||||||
|
.name = "Out 1",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 9,
|
||||||
|
.name = "Out 2",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 10,
|
||||||
|
.name = "Out 3",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 11,
|
||||||
|
.name = "Out 4",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 12,
|
||||||
|
.name = "Out 5",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 13,
|
||||||
|
.name = "Out 6",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 14,
|
||||||
|
.name = "Out 7",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 15,
|
||||||
|
.name = "Out 8",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 16,
|
||||||
|
.name = "R",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
.def = 0.995f, .min = 0.0f, .max = 1.0f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct spa_fga_descriptor dcblock_desc = {
|
||||||
|
.name = "dcblock",
|
||||||
|
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(dcblock_ports),
|
||||||
|
.ports = dcblock_ports,
|
||||||
|
|
||||||
|
.instantiate = dcblock_instantiate,
|
||||||
|
.connect_port = dcblock_connect_port,
|
||||||
|
.run = dcblock_run,
|
||||||
|
.cleanup = free,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ramp */
|
||||||
|
static struct spa_fga_port ramp_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "Out",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "Start",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "Stop",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Current",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 4,
|
||||||
|
.name = "Duration (s)",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ramp_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct builtin *impl = Instance;
|
||||||
|
float *out = impl->port[0];
|
||||||
|
float start = impl->port[1][0];
|
||||||
|
float stop = impl->port[2][0], last;
|
||||||
|
float *current = impl->port[3];
|
||||||
|
float duration = impl->port[4][0];
|
||||||
|
float inc = (stop - start) / (duration * impl->rate);
|
||||||
|
uint32_t n;
|
||||||
|
|
||||||
|
last = stop;
|
||||||
|
if (inc < 0.f)
|
||||||
|
SPA_SWAP(start, stop);
|
||||||
|
|
||||||
|
if (out != NULL) {
|
||||||
|
if (impl->accum == last) {
|
||||||
|
for (n = 0; n < SampleCount; n++)
|
||||||
|
out[n] = last;
|
||||||
|
} else {
|
||||||
|
for (n = 0; n < SampleCount; n++) {
|
||||||
|
out[n] = impl->accum;
|
||||||
|
impl->accum = SPA_CLAMP(impl->accum + inc, start, stop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
impl->accum = SPA_CLAMP(impl->accum + SampleCount * inc, start, stop);
|
||||||
|
}
|
||||||
|
if (current)
|
||||||
|
current[0] = impl->accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct spa_fga_descriptor ramp_desc = {
|
||||||
|
.name = "ramp",
|
||||||
|
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(ramp_ports),
|
||||||
|
.ports = ramp_ports,
|
||||||
|
|
||||||
|
.instantiate = builtin_instantiate,
|
||||||
|
.connect_port = builtin_connect_port,
|
||||||
|
.run = ramp_run,
|
||||||
|
.cleanup = builtin_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
|
|
@ -2184,6 +2416,10 @@ static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
return ¶m_eq_desc;
|
return ¶m_eq_desc;
|
||||||
case 22:
|
case 22:
|
||||||
return &max_desc;
|
return &max_desc;
|
||||||
|
case 23:
|
||||||
|
return &dcblock_desc;
|
||||||
|
case 24:
|
||||||
|
return &ramp_desc;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
59
src/daemon/filter-chain/36-dcblock.conf
Normal file
59
src/daemon/filter-chain/36-dcblock.conf
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
context.modules = [
|
||||||
|
{ name = libpipewire-module-filter-chain
|
||||||
|
args = {
|
||||||
|
node.description = "DCBlock Filter"
|
||||||
|
media.name = "DCBlock Filter"
|
||||||
|
filter.graph = {
|
||||||
|
nodes = [
|
||||||
|
{
|
||||||
|
name = dcblock
|
||||||
|
type = builtin
|
||||||
|
label = dcblock
|
||||||
|
control = {
|
||||||
|
"R" = 0.995
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
# add a short 20ms ramp
|
||||||
|
name = ramp
|
||||||
|
type = builtin
|
||||||
|
label = ramp
|
||||||
|
control = {
|
||||||
|
"Start" = 0.0
|
||||||
|
"Stop" = 1.0
|
||||||
|
"Duration (s)" = 0.020
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = volumeL
|
||||||
|
type = builtin
|
||||||
|
label = mult
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = volumeR
|
||||||
|
type = builtin
|
||||||
|
label = mult
|
||||||
|
}
|
||||||
|
]
|
||||||
|
links = [
|
||||||
|
{ output = "dcblock:Out 1" input = "volumeL:In 1" }
|
||||||
|
{ output = "dcblock:Out 2" input = "volumeR:In 1" }
|
||||||
|
{ output = "ramp:Out" input = "volumeL:In 2" }
|
||||||
|
{ output = "ramp:Out" input = "volumeR:In 2" }
|
||||||
|
]
|
||||||
|
inputs = [ "dcblock:In 1" "dcblock:In 2" ]
|
||||||
|
outputs = [ "volumeL:Out" "volumeR:Out" ]
|
||||||
|
}
|
||||||
|
capture.props = {
|
||||||
|
node.name = "effect_input.dcblock"
|
||||||
|
audio.position = [ FL FR ]
|
||||||
|
media.class = Audio/Sink
|
||||||
|
}
|
||||||
|
playback.props = {
|
||||||
|
node.name = "effect_output.dcblock"
|
||||||
|
audio.position = [ FL FR ]
|
||||||
|
node.passive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -472,6 +472,28 @@ extern struct spa_handle_factory spa_filter_graph_factory;
|
||||||
*
|
*
|
||||||
* It has two input ports "In 1" and "In 2" and one output port "Out".
|
* It has two input ports "In 1" and "In 2" and one output port "Out".
|
||||||
*
|
*
|
||||||
|
* ### dcblock
|
||||||
|
*
|
||||||
|
* Use the `dcblock` plugin implements a [DC blocker](https://www.dsprelated.com/freebooks/filters/DC_Blocker.html).
|
||||||
|
*
|
||||||
|
* It has 8 input ports "In 1" to "In 8" and corresponding output ports "Out 1"
|
||||||
|
* to "Out 8". Not all ports need to be connected.
|
||||||
|
*
|
||||||
|
* It also has 1 control input port "R" that controls the DC block R factor.
|
||||||
|
*
|
||||||
|
* ### Ramp
|
||||||
|
*
|
||||||
|
* Use the `ramp` plugin creates a linear ramp from `Start` to `Stop`.
|
||||||
|
*
|
||||||
|
* It has 3 input control ports "Start", "Stop" and "Duration (s)". It also has one
|
||||||
|
* output port "Out". A linear ramp will be created from "Start" to "Stop" for a duration
|
||||||
|
* given by the "Duration (s)" control in (fractional) seconds. The current value will
|
||||||
|
* be stored in the output notify port "Current".
|
||||||
|
*
|
||||||
|
* The ramp output can, for example, be used as input for the `mult` plugin to create
|
||||||
|
* a volume ramp up or down. For more a more coarse volume ramp, the "Current" value
|
||||||
|
* can be used in the `linear` plugin.
|
||||||
|
*
|
||||||
* ## SOFA filter
|
* ## SOFA filter
|
||||||
*
|
*
|
||||||
* There is an optional builtin SOFA filter available.
|
* There is an optional builtin SOFA filter available.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue