mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-06 13:30:01 -05:00
filter-chain: add more math functions
Add clamp, recip, exp, log
This commit is contained in:
parent
528c7c0f22
commit
1b09c4a2cc
2 changed files with 288 additions and 18 deletions
|
|
@ -320,6 +320,15 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
*
|
*
|
||||||
* It has an input port "In" and an output port "Out".
|
* It has an input port "In" and an output port "Out".
|
||||||
*
|
*
|
||||||
|
* ### Clamp
|
||||||
|
*
|
||||||
|
* The clamp plugin can be used to clamp samples between min and max values.
|
||||||
|
*
|
||||||
|
* It has an input port "In" and an output port "Out". It also has a "Control"
|
||||||
|
* and "Notify" port for the control values.
|
||||||
|
*
|
||||||
|
* The final result is clamped to the "Min" and "Max" control values.
|
||||||
|
*
|
||||||
* ### Linear
|
* ### Linear
|
||||||
*
|
*
|
||||||
* The linear plugin can be used to apply a linear transformation on samples
|
* The linear plugin can be used to apply a linear transformation on samples
|
||||||
|
|
@ -331,7 +340,34 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
* The control value "Mult" and "Add" are used to configure the linear transform. Each
|
* The control value "Mult" and "Add" are used to configure the linear transform. Each
|
||||||
* sample or control value will be calculated as: new = old * Mult + Add.
|
* sample or control value will be calculated as: new = old * Mult + Add.
|
||||||
*
|
*
|
||||||
* The final result can be clamped to the "Min" and "Max" control values.
|
* ### Reciprocal
|
||||||
|
*
|
||||||
|
* The recip plugin can be used to calculate the reciprocal (1/x) of samples
|
||||||
|
* or control values.
|
||||||
|
*
|
||||||
|
* It has an input port "In" and an output port "Out". It also has a "Control"
|
||||||
|
* and "Notify" port for the control values.
|
||||||
|
*
|
||||||
|
* ### Exp
|
||||||
|
*
|
||||||
|
* The exp plugin can be used to calculate the exponential (base^x) of samples
|
||||||
|
* or control values.
|
||||||
|
*
|
||||||
|
* It has an input port "In" and an output port "Out". It also has a "Control"
|
||||||
|
* and "Notify" port for the control values.
|
||||||
|
*
|
||||||
|
* The control value "Base" is used to calculate base ^ x for each sample.
|
||||||
|
*
|
||||||
|
* ### Log
|
||||||
|
*
|
||||||
|
* The log plugin can be used to calculate the logarithm of samples
|
||||||
|
* or control values.
|
||||||
|
*
|
||||||
|
* It has an input port "In" and an output port "Out". It also has a "Control"
|
||||||
|
* and "Notify" port for the control values.
|
||||||
|
*
|
||||||
|
* The control value "Base", "M1" and "M2" are used to calculate
|
||||||
|
* out = M2 * log2f(fabsf(in * M1)) / log2f(Base) for each sample.
|
||||||
*
|
*
|
||||||
* ## SOFA filter
|
* ## SOFA filter
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1234,22 +1234,80 @@ static const struct fc_descriptor invert_desc = {
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* linear */
|
/* clamp */
|
||||||
static void linear_run(void * Instance, unsigned long SampleCount)
|
static void clamp_run(void * Instance, unsigned long SampleCount)
|
||||||
{
|
{
|
||||||
struct builtin *impl = Instance;
|
struct builtin *impl = Instance;
|
||||||
float mult = impl->port[4][0], add = impl->port[5][0];
|
float min = impl->port[4][0], max = impl->port[5][0];
|
||||||
float min = impl->port[6][0], max = impl->port[7][0];
|
|
||||||
float *in = impl->port[1], *out = impl->port[0];
|
float *in = impl->port[1], *out = impl->port[0];
|
||||||
float *ctrl = impl->port[3], *notify = impl->port[2];
|
float *ctrl = impl->port[3], *notify = impl->port[2];
|
||||||
|
|
||||||
if (in != NULL && out != NULL) {
|
if (in != NULL && out != NULL) {
|
||||||
unsigned long n;
|
unsigned long n;
|
||||||
for (n = 0; n < SampleCount; n++)
|
for (n = 0; n < SampleCount; n++)
|
||||||
out[n] = SPA_CLAMPF(in[n] * mult + add, min, max);
|
out[n] = SPA_CLAMPF(in[n], min, max);
|
||||||
}
|
}
|
||||||
if (ctrl != NULL && notify != NULL)
|
if (ctrl != NULL && notify != NULL)
|
||||||
notify[0] = SPA_CLAMPF(ctrl[0] * mult + add, min, max);
|
notify[0] = SPA_CLAMPF(ctrl[0], min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct fc_port clamp_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "Out",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "In",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "Notify",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Control",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 4,
|
||||||
|
.name = "Min",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 0.0f, .min = -100.0f, .max = 100.0f
|
||||||
|
},
|
||||||
|
{ .index = 5,
|
||||||
|
.name = "Max",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 1.0f, .min = -100.0f, .max = 100.0f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fc_descriptor clamp_desc = {
|
||||||
|
.name = "clamp",
|
||||||
|
.flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(clamp_ports),
|
||||||
|
.ports = clamp_ports,
|
||||||
|
|
||||||
|
.instantiate = builtin_instantiate,
|
||||||
|
.connect_port = builtin_connect_port,
|
||||||
|
.run = clamp_run,
|
||||||
|
.cleanup = builtin_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* linear */
|
||||||
|
static void linear_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct builtin *impl = Instance;
|
||||||
|
float mult = impl->port[4][0], add = impl->port[5][0];
|
||||||
|
float *in = impl->port[1], *out = impl->port[0];
|
||||||
|
float *ctrl = impl->port[3], *notify = impl->port[2];
|
||||||
|
|
||||||
|
if (in != NULL && out != NULL) {
|
||||||
|
unsigned long n;
|
||||||
|
for (n = 0; n < SampleCount; n++)
|
||||||
|
out[n] = in[n] * mult + add;
|
||||||
|
}
|
||||||
|
if (ctrl != NULL && notify != NULL)
|
||||||
|
notify[0] = ctrl[0] * mult + add;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct fc_port linear_ports[] = {
|
static struct fc_port linear_ports[] = {
|
||||||
|
|
@ -1279,23 +1337,13 @@ static struct fc_port linear_ports[] = {
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
.def = 0.0f, .min = -10.0f, .max = 10.0f
|
.def = 0.0f, .min = -10.0f, .max = 10.0f
|
||||||
},
|
},
|
||||||
{ .index = 6,
|
|
||||||
.name = "Min",
|
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
|
||||||
.def = 0.0f, .min = -100.0f, .max = 100.0f
|
|
||||||
},
|
|
||||||
{ .index = 7,
|
|
||||||
.name = "Max",
|
|
||||||
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
|
||||||
.def = 1.0f, .min = -100.0f, .max = 100.0f
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct fc_descriptor linear_desc = {
|
static const struct fc_descriptor linear_desc = {
|
||||||
.name = "linear",
|
.name = "linear",
|
||||||
.flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
.flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
.n_ports = 8,
|
.n_ports = SPA_N_ELEMENTS(linear_ports),
|
||||||
.ports = linear_ports,
|
.ports = linear_ports,
|
||||||
|
|
||||||
.instantiate = builtin_instantiate,
|
.instantiate = builtin_instantiate,
|
||||||
|
|
@ -1304,6 +1352,184 @@ static const struct fc_descriptor linear_desc = {
|
||||||
.cleanup = builtin_cleanup,
|
.cleanup = builtin_cleanup,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* reciprocal */
|
||||||
|
static void recip_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct builtin *impl = Instance;
|
||||||
|
float *in = impl->port[1], *out = impl->port[0];
|
||||||
|
float *ctrl = impl->port[3], *notify = impl->port[2];
|
||||||
|
|
||||||
|
if (in != NULL && out != NULL) {
|
||||||
|
unsigned long n;
|
||||||
|
for (n = 0; n < SampleCount; n++) {
|
||||||
|
if (in[0] == 0.0f)
|
||||||
|
out[n] = 0.0f;
|
||||||
|
else
|
||||||
|
out[n] = 1.0f / in[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ctrl != NULL && notify != NULL) {
|
||||||
|
if (ctrl[0] == 0.0f)
|
||||||
|
notify[0] = 0.0f;
|
||||||
|
else
|
||||||
|
notify[0] = 1.0f / ctrl[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct fc_port recip_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "Out",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "In",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "Notify",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Control",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fc_descriptor recip_desc = {
|
||||||
|
.name = "recip",
|
||||||
|
.flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(recip_ports),
|
||||||
|
.ports = recip_ports,
|
||||||
|
|
||||||
|
.instantiate = builtin_instantiate,
|
||||||
|
.connect_port = builtin_connect_port,
|
||||||
|
.run = recip_run,
|
||||||
|
.cleanup = builtin_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* exp */
|
||||||
|
static void exp_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct builtin *impl = Instance;
|
||||||
|
float base = impl->port[4][0];
|
||||||
|
float *in = impl->port[1], *out = impl->port[0];
|
||||||
|
float *ctrl = impl->port[3], *notify = impl->port[2];
|
||||||
|
|
||||||
|
if (in != NULL && out != NULL) {
|
||||||
|
unsigned long n;
|
||||||
|
for (n = 0; n < SampleCount; n++)
|
||||||
|
out[n] = powf(base, in[n]);
|
||||||
|
}
|
||||||
|
if (ctrl != NULL && notify != NULL)
|
||||||
|
notify[0] = powf(base, ctrl[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct fc_port exp_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "Out",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "In",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "Notify",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Control",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 4,
|
||||||
|
.name = "Base",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = M_E, .min = -10.0f, .max = 10.0f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fc_descriptor exp_desc = {
|
||||||
|
.name = "exp",
|
||||||
|
.flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(exp_ports),
|
||||||
|
.ports = exp_ports,
|
||||||
|
|
||||||
|
.instantiate = builtin_instantiate,
|
||||||
|
.connect_port = builtin_connect_port,
|
||||||
|
.run = exp_run,
|
||||||
|
.cleanup = builtin_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* log */
|
||||||
|
static void log_run(void * Instance, unsigned long SampleCount)
|
||||||
|
{
|
||||||
|
struct builtin *impl = Instance;
|
||||||
|
float base = impl->port[4][0];
|
||||||
|
float m1 = impl->port[5][0];
|
||||||
|
float m2 = impl->port[6][0];
|
||||||
|
float *in = impl->port[1], *out = impl->port[0];
|
||||||
|
float *ctrl = impl->port[3], *notify = impl->port[2];
|
||||||
|
float lb = log2f(base);
|
||||||
|
|
||||||
|
if (in != NULL && out != NULL) {
|
||||||
|
unsigned long n;
|
||||||
|
for (n = 0; n < SampleCount; n++)
|
||||||
|
out[n] = m2 * log2f(fabsf(in[n] * m1)) / lb;
|
||||||
|
}
|
||||||
|
if (ctrl != NULL && notify != NULL)
|
||||||
|
notify[0] = m2 * log2f(fabsf(ctrl[0] * m1)) / lb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct fc_port log_ports[] = {
|
||||||
|
{ .index = 0,
|
||||||
|
.name = "Out",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 1,
|
||||||
|
.name = "In",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_AUDIO,
|
||||||
|
},
|
||||||
|
{ .index = 2,
|
||||||
|
.name = "Notify",
|
||||||
|
.flags = FC_PORT_OUTPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 3,
|
||||||
|
.name = "Control",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
},
|
||||||
|
{ .index = 4,
|
||||||
|
.name = "Base",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = M_E, .min = 2.0f, .max = 100.0f
|
||||||
|
},
|
||||||
|
{ .index = 5,
|
||||||
|
.name = "M1",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 1.0f, .min = -10.0f, .max = 10.0f
|
||||||
|
},
|
||||||
|
{ .index = 6,
|
||||||
|
.name = "M2",
|
||||||
|
.flags = FC_PORT_INPUT | FC_PORT_CONTROL,
|
||||||
|
.def = 1.0f, .min = -10.0f, .max = 10.0f
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fc_descriptor log_desc = {
|
||||||
|
.name = "log",
|
||||||
|
.flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA,
|
||||||
|
|
||||||
|
.n_ports = SPA_N_ELEMENTS(log_ports),
|
||||||
|
.ports = log_ports,
|
||||||
|
|
||||||
|
.instantiate = builtin_instantiate,
|
||||||
|
.connect_port = builtin_connect_port,
|
||||||
|
.run = log_run,
|
||||||
|
.cleanup = builtin_cleanup,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct fc_descriptor * builtin_descriptor(unsigned long Index)
|
static const struct fc_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
{
|
{
|
||||||
switch(Index) {
|
switch(Index) {
|
||||||
|
|
@ -1336,7 +1562,15 @@ static const struct fc_descriptor * builtin_descriptor(unsigned long Index)
|
||||||
case 13:
|
case 13:
|
||||||
return &bq_raw_desc;
|
return &bq_raw_desc;
|
||||||
case 14:
|
case 14:
|
||||||
|
return &clamp_desc;
|
||||||
|
case 15:
|
||||||
return &linear_desc;
|
return &linear_desc;
|
||||||
|
case 16:
|
||||||
|
return &recip_desc;
|
||||||
|
case 17:
|
||||||
|
return &exp_desc;
|
||||||
|
case 18:
|
||||||
|
return &log_desc;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue