filter-chain: add biquads to filter-chain

Add some biquad filters to the builtin filters.
Add an example 6 band equalizer sink filter chain.
This commit is contained in:
Wim Taymans 2021-05-10 18:09:55 +02:00
parent 8911e22793
commit bfbd6d74ef
7 changed files with 798 additions and 2 deletions

View file

@ -1,5 +1,6 @@
conf_files = [ conf_files = [
[ 'demonic.conf', 'demonic.conf' ], [ 'demonic.conf', 'demonic.conf' ],
[ 'sink-eq6.conf', 'sink-eq6.conf' ],
[ 'source-rnnoise.conf', 'source-rnnoise.conf' ], [ 'source-rnnoise.conf', 'source-rnnoise.conf' ],
] ]

View file

@ -0,0 +1,88 @@
# 6 band sink equalizer
#
# start with pipewire -c filter-chain/sink-eq6.conf
#
context.properties = {
log.level = 0
}
context.spa-libs = {
audio.convert.* = audioconvert/libspa-audioconvert
support.* = support/libspa-support
}
context.modules = [
{ name = libpipewire-module-rtkit
args = {
#nice.level = -11
#rt.prio = 88
#rt.time.soft = 200000
#rt.time.hard = 200000
}
flags = [ ifexists nofail ]
}
{ name = libpipewire-module-protocol-native }
{ name = libpipewire-module-client-node }
{ name = libpipewire-module-adapter }
{ name = libpipewire-module-filter-chain
args = {
node.name = "eq_sink"
node.description = "Equalizer Sink"
media.name = "Equalizer Sink"
filter.graph = {
nodes = [
{
type = builtin
name = eq_band_1
label = bq_lowshelf
control = { "Freq" = 100.0 "Q" = 1.0 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_2
label = bq_peaking
control = { "Freq" = 100.0 "Q" = 1.0 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_3
label = bq_peaking
control = { "Freq" = 500.0 "Q" = 1.0 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_4
label = bq_peaking
control = { "Freq" = 2000.0 "Q" = 1.0 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_5
label = bq_peaking
control = { "Freq" = 5000.0 "Q" = 1.0 "Gain" = 0.0 }
}
{
type = builtin
name = eq_band_6
label = bq_highshelf
control = { "Freq" = 5000.0 "Q" = 1.0 "Gain" = 0.0 }
}
]
links = [
{ output = "eq_band_1:Out" input = "eq_band_2:In" }
{ output = "eq_band_2:Out" input = "eq_band_3:In" }
{ output = "eq_band_3:Out" input = "eq_band_4:In" }
{ output = "eq_band_4:Out" input = "eq_band_5:In" }
{ output = "eq_band_5:Out" input = "eq_band_6:In" }
]
}
capture.props = {
media.class = Audio/Sink
}
playback.props = {
node.passive = true
}
}
}
]

View file

@ -25,7 +25,8 @@ pipewire_module_loopback = shared_library('pipewire-module-loopback',
) )
pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain', pipewire_module_filter_chain = shared_library('pipewire-module-filter-chain',
[ 'module-filter-chain.c' ], [ 'module-filter-chain.c',
'module-filter-chain/biquad.c' ],
c_args : pipewire_module_c_args, c_args : pipewire_module_c_args,
include_directories : [configinc, spa_inc], include_directories : [configinc, spa_inc],
install : true, install : true,

View file

@ -534,7 +534,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_NODE_VIRTUAL);
copy_props(impl, props, PW_KEY_NODE_LATENCY); copy_props(impl, props, PW_KEY_NODE_LATENCY);
if ((str = pw_properties_get(props, "aec,method")) == NULL) if ((str = pw_properties_get(props, "aec.method")) == NULL)
str = "null"; str = "null";
if (strcmp(str, "webrtc") == 0) if (strcmp(str, "webrtc") == 0)
impl->aec_info = echo_cancel_webrtc; impl->aec_info = echo_cancel_webrtc;

View file

@ -0,0 +1,381 @@
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Copyright (C) 2010 Google Inc. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE.WEBKIT file.
*/
#include <math.h>
#include "biquad.h"
#ifndef max
#define max(a, b) \
({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
_a > _b ? _a : _b; \
})
#endif
#ifndef min
#define min(a, b) \
({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
_a < _b ? _a : _b; \
})
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
static void set_coefficient(struct biquad *bq, double b0, double b1, double b2,
double a0, double a1, double a2)
{
double a0_inv = 1 / a0;
bq->b0 = b0 * a0_inv;
bq->b1 = b1 * a0_inv;
bq->b2 = b2 * a0_inv;
bq->a1 = a1 * a0_inv;
bq->a2 = a2 * a0_inv;
}
static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance)
{
/* Limit cutoff to 0 to 1. */
cutoff = max(0.0, min(cutoff, 1.0));
if (cutoff == 1 || cutoff == 0) {
/* When cutoff is 1, the z-transform is 1.
* When cutoff is zero, nothing gets through the filter, so set
* coefficients up correctly.
*/
set_coefficient(bq, cutoff, 0, 0, 1, 0, 0);
return;
}
/* Compute biquad coefficients for lowpass filter */
resonance = max(0.0, resonance); /* can't go negative */
double g = pow(10.0, 0.05 * resonance);
double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
double theta = M_PI * cutoff;
double sn = 0.5 * d * sin(theta);
double beta = 0.5 * (1 - sn) / (1 + sn);
double gamma = (0.5 + beta) * cos(theta);
double alpha = 0.25 * (0.5 + beta - gamma);
double b0 = 2 * alpha;
double b1 = 2 * 2 * alpha;
double b2 = 2 * alpha;
double a1 = 2 * -gamma;
double a2 = 2 * beta;
set_coefficient(bq, b0, b1, b2, 1, a1, a2);
}
static void biquad_highpass(struct biquad *bq, double cutoff, double resonance)
{
/* Limit cutoff to 0 to 1. */
cutoff = max(0.0, min(cutoff, 1.0));
if (cutoff == 1 || cutoff == 0) {
/* When cutoff is one, the z-transform is 0. */
/* When cutoff is zero, we need to be careful because the above
* gives a quadratic divided by the same quadratic, with poles
* and zeros on the unit circle in the same place. When cutoff
* is zero, the z-transform is 1.
*/
set_coefficient(bq, 1 - cutoff, 0, 0, 1, 0, 0);
return;
}
/* Compute biquad coefficients for highpass filter */
resonance = max(0.0, resonance); /* can't go negative */
double g = pow(10.0, 0.05 * resonance);
double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2);
double theta = M_PI * cutoff;
double sn = 0.5 * d * sin(theta);
double beta = 0.5 * (1 - sn) / (1 + sn);
double gamma = (0.5 + beta) * cos(theta);
double alpha = 0.25 * (0.5 + beta + gamma);
double b0 = 2 * alpha;
double b1 = 2 * -2 * alpha;
double b2 = 2 * alpha;
double a1 = 2 * -gamma;
double a2 = 2 * beta;
set_coefficient(bq, b0, b1, b2, 1, a1, a2);
}
static void biquad_bandpass(struct biquad *bq, double frequency, double Q)
{
/* No negative frequencies allowed. */
frequency = max(0.0, frequency);
/* Don't let Q go negative, which causes an unstable filter. */
Q = max(0.0, Q);
if (frequency <= 0 || frequency >= 1) {
/* When the cutoff is zero, the z-transform approaches 0, if Q
* > 0. When both Q and cutoff are zero, the z-transform is
* pretty much undefined. What should we do in this case?
* For now, just make the filter 0. When the cutoff is 1, the
* z-transform also approaches 0.
*/
set_coefficient(bq, 0, 0, 0, 1, 0, 0);
return;
}
if (Q <= 0) {
/* When Q = 0, the above formulas have problems. If we
* look at the z-transform, we can see that the limit
* as Q->0 is 1, so set the filter that way.
*/
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
return;
}
double w0 = M_PI * frequency;
double alpha = sin(w0) / (2 * Q);
double k = cos(w0);
double b0 = alpha;
double b1 = 0;
double b2 = -alpha;
double a0 = 1 + alpha;
double a1 = -2 * k;
double a2 = 1 - alpha;
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
}
static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain)
{
/* Clip frequencies to between 0 and 1, inclusive. */
frequency = max(0.0, min(frequency, 1.0));
double A = pow(10.0, db_gain / 40);
if (frequency == 1) {
/* The z-transform is a constant gain. */
set_coefficient(bq, A * A, 0, 0, 1, 0, 0);
return;
}
if (frequency <= 0) {
/* When frequency is 0, the z-transform is 1. */
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
return;
}
double w0 = M_PI * frequency;
double S = 1; /* filter slope (1 is max value) */
double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
double k = cos(w0);
double k2 = 2 * sqrt(A) * alpha;
double a_plus_one = A + 1;
double a_minus_one = A - 1;
double b0 = A * (a_plus_one - a_minus_one * k + k2);
double b1 = 2 * A * (a_minus_one - a_plus_one * k);
double b2 = A * (a_plus_one - a_minus_one * k - k2);
double a0 = a_plus_one + a_minus_one * k + k2;
double a1 = -2 * (a_minus_one + a_plus_one * k);
double a2 = a_plus_one + a_minus_one * k - k2;
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
}
static void biquad_highshelf(struct biquad *bq, double frequency,
double db_gain)
{
/* Clip frequencies to between 0 and 1, inclusive. */
frequency = max(0.0, min(frequency, 1.0));
double A = pow(10.0, db_gain / 40);
if (frequency == 1) {
/* The z-transform is 1. */
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
return;
}
if (frequency <= 0) {
/* When frequency = 0, the filter is just a gain, A^2. */
set_coefficient(bq, A * A, 0, 0, 1, 0, 0);
return;
}
double w0 = M_PI * frequency;
double S = 1; /* filter slope (1 is max value) */
double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
double k = cos(w0);
double k2 = 2 * sqrt(A) * alpha;
double a_plus_one = A + 1;
double a_minus_one = A - 1;
double b0 = A * (a_plus_one + a_minus_one * k + k2);
double b1 = -2 * A * (a_minus_one + a_plus_one * k);
double b2 = A * (a_plus_one + a_minus_one * k - k2);
double a0 = a_plus_one - a_minus_one * k + k2;
double a1 = 2 * (a_minus_one - a_plus_one * k);
double a2 = a_plus_one - a_minus_one * k - k2;
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
}
static void biquad_peaking(struct biquad *bq, double frequency, double Q,
double db_gain)
{
/* Clip frequencies to between 0 and 1, inclusive. */
frequency = max(0.0, min(frequency, 1.0));
/* Don't let Q go negative, which causes an unstable filter. */
Q = max(0.0, Q);
double A = pow(10.0, db_gain / 40);
if (frequency <= 0 || frequency >= 1) {
/* When frequency is 0 or 1, the z-transform is 1. */
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
return;
}
if (Q <= 0) {
/* When Q = 0, the above formulas have problems. If we
* look at the z-transform, we can see that the limit
* as Q->0 is A^2, so set the filter that way.
*/
set_coefficient(bq, A * A, 0, 0, 1, 0, 0);
return;
}
double w0 = M_PI * frequency;
double alpha = sin(w0) / (2 * Q);
double k = cos(w0);
double b0 = 1 + alpha * A;
double b1 = -2 * k;
double b2 = 1 - alpha * A;
double a0 = 1 + alpha / A;
double a1 = -2 * k;
double a2 = 1 - alpha / A;
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
}
static void biquad_notch(struct biquad *bq, double frequency, double Q)
{
/* Clip frequencies to between 0 and 1, inclusive. */
frequency = max(0.0, min(frequency, 1.0));
/* Don't let Q go negative, which causes an unstable filter. */
Q = max(0.0, Q);
if (frequency <= 0 || frequency >= 1) {
/* When frequency is 0 or 1, the z-transform is 1. */
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
return;
}
if (Q <= 0) {
/* When Q = 0, the above formulas have problems. If we
* look at the z-transform, we can see that the limit
* as Q->0 is 0, so set the filter that way.
*/
set_coefficient(bq, 0, 0, 0, 1, 0, 0);
return;
}
double w0 = M_PI * frequency;
double alpha = sin(w0) / (2 * Q);
double k = cos(w0);
double b0 = 1;
double b1 = -2 * k;
double b2 = 1;
double a0 = 1 + alpha;
double a1 = -2 * k;
double a2 = 1 - alpha;
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
}
static void biquad_allpass(struct biquad *bq, double frequency, double Q)
{
/* Clip frequencies to between 0 and 1, inclusive. */
frequency = max(0.0, min(frequency, 1.0));
/* Don't let Q go negative, which causes an unstable filter. */
Q = max(0.0, Q);
if (frequency <= 0 || frequency >= 1) {
/* When frequency is 0 or 1, the z-transform is 1. */
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
return;
}
if (Q <= 0) {
/* When Q = 0, the above formulas have problems. If we
* look at the z-transform, we can see that the limit
* as Q->0 is -1, so set the filter that way.
*/
set_coefficient(bq, -1, 0, 0, 1, 0, 0);
return;
}
double w0 = M_PI * frequency;
double alpha = sin(w0) / (2 * Q);
double k = cos(w0);
double b0 = 1 - alpha;
double b1 = -2 * k;
double b2 = 1 + alpha;
double a0 = 1 + alpha;
double a1 = -2 * k;
double a2 = 1 - alpha;
set_coefficient(bq, b0, b1, b2, a0, a1, a2);
}
void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
double gain)
{
/* Default is an identity filter. Also clear history values. */
set_coefficient(bq, 1, 0, 0, 1, 0, 0);
bq->x1 = 0;
bq->x2 = 0;
bq->y1 = 0;
bq->y2 = 0;
switch (type) {
case BQ_LOWPASS:
biquad_lowpass(bq, freq, Q);
break;
case BQ_HIGHPASS:
biquad_highpass(bq, freq, Q);
break;
case BQ_BANDPASS:
biquad_bandpass(bq, freq, Q);
break;
case BQ_LOWSHELF:
biquad_lowshelf(bq, freq, gain);
break;
case BQ_HIGHSHELF:
biquad_highshelf(bq, freq, gain);
break;
case BQ_PEAKING:
biquad_peaking(bq, freq, Q, gain);
break;
case BQ_NOTCH:
biquad_notch(bq, freq, Q);
break;
case BQ_ALLPASS:
biquad_allpass(bq, freq, Q);
break;
case BQ_NONE:
break;
}
}

View file

@ -0,0 +1,57 @@
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef BIQUAD_H_
#define BIQUAD_H_
#ifdef __cplusplus
extern "C" {
#endif
/* The biquad filter parameters. The transfer function H(z) is (b0 + b1 * z^(-1)
* + b2 * z^(-2)) / (1 + a1 * z^(-1) + a2 * z^(-2)). The previous two inputs
* are stored in x1 and x2, and the previous two outputs are stored in y1 and
* y2.
*
* We use double during the coefficients calculation for better accurary, but
* float is used during the actual filtering for faster computation.
*/
struct biquad {
float b0, b1, b2;
float a1, a2;
float x1, x2;
float y1, y2;
};
/* The type of the biquad filters */
enum biquad_type {
BQ_NONE,
BQ_LOWPASS,
BQ_HIGHPASS,
BQ_BANDPASS,
BQ_LOWSHELF,
BQ_HIGHSHELF,
BQ_PEAKING,
BQ_NOTCH,
BQ_ALLPASS
};
/* Initialize a biquad filter parameters from its type and parameters.
* Args:
* bq - The biquad filter we want to set.
* type - The type of the biquad filter.
* frequency - The value should be in the range [0, 1]. It is relative to
* half of the sampling rate.
* Q - Quality factor. See Web Audio API for details.
* gain - The value is in dB. See Web Audio API for details.
*/
void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
double gain);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* BIQUAD_H_ */

View file

@ -22,8 +22,16 @@
* DEALINGS IN THE SOFTWARE. * DEALINGS IN THE SOFTWARE.
*/ */
#include "biquad.h"
struct builtin { struct builtin {
unsigned long rate;
LADSPA_Data *port[64]; LADSPA_Data *port[64];
struct biquad bq;
float freq;
float Q;
float gain;
}; };
static LADSPA_Handle builtin_instantiate(const struct _LADSPA_Descriptor * Descriptor, static LADSPA_Handle builtin_instantiate(const struct _LADSPA_Descriptor * Descriptor,
@ -35,6 +43,8 @@ static LADSPA_Handle builtin_instantiate(const struct _LADSPA_Descriptor * Descr
if (impl == NULL) if (impl == NULL)
return NULL; return NULL;
impl->rate = SampleRate;
return impl; return impl;
} }
@ -104,11 +114,269 @@ static const LADSPA_Descriptor mixer_desc = {
.cleanup = builtin_cleanup, .cleanup = builtin_cleanup,
}; };
static const LADSPA_PortDescriptor bq_port_desc[] = {
LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
};
static const char * const bq_port_names[] = {
"Out", "In", "Freq", "Q", "Gain"
};
static const LADSPA_PortRangeHint bq_range_hints[] = {
{ 0, }, { 0, },
{ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_SAMPLE_RATE | LADSPA_HINT_DEFAULT_LOW, 0.0, 1.0 },
{ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0, 0.0, 10.0 },
{ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_DEFAULT_0, -120.0, 5.0 },
};
static void bq_run(struct builtin *impl, unsigned long samples, int type)
{
struct biquad *bq = &impl->bq;
unsigned long i;
float *out = impl->port[0];
float *in = impl->port[1];
float freq = impl->port[2][0];
float Q = impl->port[3][0];
float gain = impl->port[4][0];
float x1, x2, y1, y2;
float b0, b1, b2, a1, a2;
if (impl->freq != freq || impl->Q != Q || impl->gain != gain) {
impl->freq = freq;
impl->Q = Q;
impl->gain = gain;
biquad_set(bq, type, freq / impl->rate, Q, gain);
}
x1 = bq->x1;
x2 = bq->x2;
y1 = bq->y1;
y2 = bq->y2;
b0 = bq->b0;
b1 = bq->b1;
b2 = bq->b2;
a1 = bq->a1;
a2 = bq->a2;
for (i = 0; i < samples; i++) {
float x = in[i];
float y = b0 * x + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
out[i] = y;
x2 = x1;
x1 = x;
y2 = y1;
y1 = y;
}
bq->x1 = x1;
bq->x2 = x2;
bq->y1 = y1;
bq->y2 = y2;
}
/** bq_lowpass */
static void bq_lowpass_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_LOWPASS);
}
static const LADSPA_Descriptor bq_lowpass_desc = {
.Label = "bq_lowpass",
.Name = "Biquad lowpass filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_lowpass_run,
.cleanup = builtin_cleanup,
};
/** bq_highpass */
static void bq_highpass_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_HIGHPASS);
}
static const LADSPA_Descriptor bq_highpass_desc = {
.Label = "bq_highpass",
.Name = "Biquad highpass filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_highpass_run,
.cleanup = builtin_cleanup,
};
/** bq_bandpass */
static void bq_bandpass_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_BANDPASS);
}
static const LADSPA_Descriptor bq_bandpass_desc = {
.Label = "bq_bandpass",
.Name = "Biquad bandpass filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_bandpass_run,
.cleanup = builtin_cleanup,
};
/** bq_lowshelf */
static void bq_lowshelf_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_LOWSHELF);
}
static const LADSPA_Descriptor bq_lowshelf_desc = {
.Label = "bq_lowshelf",
.Name = "Biquad lowshelf filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_lowshelf_run,
.cleanup = builtin_cleanup,
};
/** bq_highshelf */
static void bq_highshelf_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_HIGHSHELF);
}
static const LADSPA_Descriptor bq_highshelf_desc = {
.Label = "bq_highshelf",
.Name = "Biquad highshelf filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_highshelf_run,
.cleanup = builtin_cleanup,
};
/** bq_peaking */
static void bq_peaking_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_PEAKING);
}
static const LADSPA_Descriptor bq_peaking_desc = {
.Label = "bq_peaking",
.Name = "Biquad peaking filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_peaking_run,
.cleanup = builtin_cleanup,
};
/** bq_notch */
static void bq_notch_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_NOTCH);
}
static const LADSPA_Descriptor bq_notch_desc = {
.Label = "bq_notch",
.Name = "Biquad notch filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_notch_run,
.cleanup = builtin_cleanup,
};
/** bq_allpass */
static void bq_allpass_run(LADSPA_Handle Instance, unsigned long SampleCount)
{
struct builtin *impl = Instance;
bq_run(impl, SampleCount, BQ_ALLPASS);
}
static const LADSPA_Descriptor bq_allpass_desc = {
.Label = "bq_allpass",
.Name = "Biquad allpass filter",
.Maker = "PipeWire",
.Copyright = "MIT",
.PortCount = 5,
.PortDescriptors = bq_port_desc,
.PortNames = bq_port_names,
.PortRangeHints = bq_range_hints,
.instantiate = builtin_instantiate,
.connect_port = builtin_connect_port,
.run = bq_allpass_run,
.cleanup = builtin_cleanup,
};
static const LADSPA_Descriptor * builtin_ladspa_descriptor(unsigned long Index) static const LADSPA_Descriptor * builtin_ladspa_descriptor(unsigned long Index)
{ {
switch(Index) { switch(Index) {
case 0: case 0:
return &mixer_desc; return &mixer_desc;
case 1:
return &bq_lowpass_desc;
case 2:
return &bq_highpass_desc;
case 3:
return &bq_bandpass_desc;
case 4:
return &bq_lowshelf_desc;
case 5:
return &bq_highshelf_desc;
case 6:
return &bq_peaking_desc;
case 7:
return &bq_notch_desc;
case 8:
return &bq_allpass_desc;
} }
return NULL; return NULL;
} }