From 1b09c4a2cc621f42ccef77452267c95372ec4a68 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Sun, 22 Oct 2023 09:41:05 +0200 Subject: [PATCH] filter-chain: add more math functions Add clamp, recip, exp, log --- src/modules/module-filter-chain.c | 38 ++- .../module-filter-chain/builtin_plugin.c | 268 ++++++++++++++++-- 2 files changed, 288 insertions(+), 18 deletions(-) diff --git a/src/modules/module-filter-chain.c b/src/modules/module-filter-chain.c index 2cd4384c3..ebfcd20a6 100644 --- a/src/modules/module-filter-chain.c +++ b/src/modules/module-filter-chain.c @@ -320,6 +320,15 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); * * 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 * * 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 * 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 * diff --git a/src/modules/module-filter-chain/builtin_plugin.c b/src/modules/module-filter-chain/builtin_plugin.c index 2bb57f0c9..527ad52cb 100644 --- a/src/modules/module-filter-chain/builtin_plugin.c +++ b/src/modules/module-filter-chain/builtin_plugin.c @@ -1234,22 +1234,80 @@ static const struct fc_descriptor invert_desc = { .cleanup = builtin_cleanup, }; -/* linear */ -static void linear_run(void * Instance, unsigned long SampleCount) +/* clamp */ +static void clamp_run(void * Instance, unsigned long SampleCount) { struct builtin *impl = Instance; - float mult = impl->port[4][0], add = impl->port[5][0]; - float min = impl->port[6][0], max = impl->port[7][0]; + float min = impl->port[4][0], max = 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] = SPA_CLAMPF(in[n] * mult + add, min, max); + out[n] = SPA_CLAMPF(in[n], min, max); } 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[] = { @@ -1279,23 +1337,13 @@ static struct fc_port linear_ports[] = { .flags = FC_PORT_INPUT | FC_PORT_CONTROL, .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 = { .name = "linear", .flags = FC_DESCRIPTOR_SUPPORTS_NULL_DATA, - .n_ports = 8, + .n_ports = SPA_N_ELEMENTS(linear_ports), .ports = linear_ports, .instantiate = builtin_instantiate, @@ -1304,6 +1352,184 @@ static const struct fc_descriptor linear_desc = { .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) { switch(Index) { @@ -1336,7 +1562,15 @@ static const struct fc_descriptor * builtin_descriptor(unsigned long Index) case 13: return &bq_raw_desc; case 14: + return &clamp_desc; + case 15: return &linear_desc; + case 16: + return &recip_desc; + case 17: + return &exp_desc; + case 18: + return &log_desc; } return NULL; }