mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
filter-graph: add a zeroramp plugin
The filter detects unnatural gaps (consisting of 0.0 values) and will ramp-down or ramp-up the volume when entering/leaving those gaps. This makes it filter out the pops and clicks you typically get when pausing and resuming a stream. See #4745
This commit is contained in:
parent
75b4c3379d
commit
0b2b723a0e
2 changed files with 116 additions and 0 deletions
|
|
@ -56,6 +56,10 @@ struct builtin {
|
||||||
float b0, b1, b2;
|
float b0, b1, b2;
|
||||||
float a0, a1, a2;
|
float a0, a1, a2;
|
||||||
float accum;
|
float accum;
|
||||||
|
|
||||||
|
int mode;
|
||||||
|
uint32_t count;
|
||||||
|
float last;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *builtin_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor,
|
static void *builtin_instantiate(const struct spa_fga_plugin *plugin, const struct spa_fga_descriptor * Descriptor,
|
||||||
|
|
@ -2776,6 +2780,98 @@ static const struct spa_fga_descriptor pipe_desc = {
|
||||||
.cleanup = pipe_cleanup,
|
.cleanup = pipe_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* zeroramp */
|
||||||
|
static struct spa_fga_port zeroramp_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "In",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "Out",
|
||||||
|
.flags = SPA_FGA_PORT_OUTPUT | SPA_FGA_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "Gap (s)",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
.def = 0.000666f, .min = 0.0f, .max = 1.0f
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Duration (s)",
|
||||||
|
.flags = SPA_FGA_PORT_INPUT | SPA_FGA_PORT_CONTROL,
|
||||||
|
.def = 0.000666f, .min = 0.0f, .max = 1.0f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void zeroramp_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct builtin *impl = Instance;
|
||||||
|
float *in = impl->port[0];
|
||||||
|
float *out = impl->port[1];
|
||||||
|
uint32_t n, i, c;
|
||||||
|
uint32_t gap = (uint32_t)(impl->port[2][0] * impl->rate);
|
||||||
|
uint32_t duration = (uint32_t)(impl->port[3][0] * impl->rate);
|
||||||
|
|
||||||
|
if (out == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (in != NULL) {
|
||||||
|
for (n = 0; n < SampleCount; n++) {
|
||||||
|
if (impl->mode == 0) {
|
||||||
|
/* normal mode, finding gaps */
|
||||||
|
out[n] = in[n];
|
||||||
|
if (in[n] == 0.0) {
|
||||||
|
if (++impl->count == gap) {
|
||||||
|
/* we found gap zeroes, fade out last
|
||||||
|
* sample and go into zero mode */
|
||||||
|
for (c = 1, i = n; c < duration && i > 0; i--, c++)
|
||||||
|
out[i-1] = impl->last *
|
||||||
|
(0.5f + 0.5f * cosf(M_PIf + M_PIf * c / duration));
|
||||||
|
impl->mode = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* keep last sample to fade out when needed */
|
||||||
|
impl->count = 0;
|
||||||
|
impl->last = in[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (impl->mode == 1) {
|
||||||
|
/* zero mode */
|
||||||
|
if (in[n] != 0.0f) {
|
||||||
|
/* gap ended, move to fade-in mode */
|
||||||
|
impl->mode = 2;
|
||||||
|
impl->count = 0;
|
||||||
|
} else {
|
||||||
|
out[n] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (impl->mode == 2) {
|
||||||
|
/* fade-in mode */
|
||||||
|
out[n] = in[n] * (0.5f + 0.5f * cosf(M_PIf + (M_PIf * ++impl->count / duration)));
|
||||||
|
if (impl->count == duration) {
|
||||||
|
/* fade in complete, back to normal mode */
|
||||||
|
impl->count = 0;
|
||||||
|
impl->mode = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memset(out, 0, SampleCount * sizeof(float));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct spa_fga_descriptor zeroramp_desc = {
|
||||||
|
.name = "zeroramp",
|
||||||
|
.flags = SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(zeroramp_ports),
|
||||||
|
.ports = zeroramp_ports,
|
||||||
|
|
||||||
|
.instantiate = builtin_instantiate,
|
||||||
|
.connect_port = builtin_connect_port,
|
||||||
|
.run = zeroramp_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)
|
||||||
{
|
{
|
||||||
switch(Index) {
|
switch(Index) {
|
||||||
|
|
@ -2837,6 +2933,8 @@ static const struct spa_fga_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
return &debug_desc;
|
return &debug_desc;
|
||||||
case 28:
|
case 28:
|
||||||
return &pipe_desc;
|
return &pipe_desc;
|
||||||
|
case 29:
|
||||||
|
return &zeroramp_desc;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -606,6 +606,24 @@ extern struct spa_handle_factory spa_filter_graph_factory;
|
||||||
* - `command` the command to execute. It should consume samples from stdin and produce
|
* - `command` the command to execute. It should consume samples from stdin and produce
|
||||||
* samples on stdout.
|
* samples on stdout.
|
||||||
*
|
*
|
||||||
|
* ### Zeroramp
|
||||||
|
*
|
||||||
|
* The `zeroramp` plugin can be used to detect unnatural silence parts in the audio
|
||||||
|
* stream and ramp the volume down or up when entering or leaving the silent area
|
||||||
|
* respectively.
|
||||||
|
* This can be used to avoid loud pops and clicks that occur when the sample values
|
||||||
|
* suddenly drop to zero or jump from zero to a large value caused by a pause,
|
||||||
|
* resume or an error of the stream. It only detect areas where the sample values
|
||||||
|
* are absolute zero values, such as those inserted when pausing a stream.
|
||||||
|
*
|
||||||
|
* It has an "In" input port and an "Out" output data ports.
|
||||||
|
*
|
||||||
|
* There are also "Gap (s)" and an "Duration (s)" input control ports. "Gap (s)"
|
||||||
|
* determines how long the silence gap is in seconds (default 0.000666) and
|
||||||
|
* "Duration (s)" determines how long the fade-in and fade-out should last
|
||||||
|
* (default 0.000666).
|
||||||
|
*
|
||||||
|
*
|
||||||
* ## SOFA filters
|
* ## SOFA filters
|
||||||
*
|
*
|
||||||
* There is an optional `sofa` type available (when compiled with `libmysofa`).
|
* There is an optional `sofa` type available (when compiled with `libmysofa`).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue