mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
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:
parent
8911e22793
commit
bfbd6d74ef
7 changed files with 798 additions and 2 deletions
|
|
@ -1,5 +1,6 @@
|
|||
conf_files = [
|
||||
[ 'demonic.conf', 'demonic.conf' ],
|
||||
[ 'sink-eq6.conf', 'sink-eq6.conf' ],
|
||||
[ 'source-rnnoise.conf', 'source-rnnoise.conf' ],
|
||||
]
|
||||
|
||||
|
|
|
|||
88
src/daemon/filter-chain/sink-eq6.conf
Normal file
88
src/daemon/filter-chain/sink-eq6.conf
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -25,7 +25,8 @@ pipewire_module_loopback = shared_library('pipewire-module-loopback',
|
|||
)
|
||||
|
||||
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,
|
||||
include_directories : [configinc, spa_inc],
|
||||
install : true,
|
||||
|
|
|
|||
|
|
@ -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_LATENCY);
|
||||
|
||||
if ((str = pw_properties_get(props, "aec,method")) == NULL)
|
||||
if ((str = pw_properties_get(props, "aec.method")) == NULL)
|
||||
str = "null";
|
||||
if (strcmp(str, "webrtc") == 0)
|
||||
impl->aec_info = echo_cancel_webrtc;
|
||||
|
|
|
|||
381
src/modules/module-filter-chain/biquad.c
Normal file
381
src/modules/module-filter-chain/biquad.c
Normal 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;
|
||||
}
|
||||
}
|
||||
57
src/modules/module-filter-chain/biquad.h
Normal file
57
src/modules/module-filter-chain/biquad.h
Normal 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_ */
|
||||
|
|
@ -22,8 +22,16 @@
|
|||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "biquad.h"
|
||||
|
||||
struct builtin {
|
||||
unsigned long rate;
|
||||
LADSPA_Data *port[64];
|
||||
|
||||
struct biquad bq;
|
||||
float freq;
|
||||
float Q;
|
||||
float gain;
|
||||
};
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
|
||||
impl->rate = SampleRate;
|
||||
|
||||
return impl;
|
||||
}
|
||||
|
||||
|
|
@ -104,11 +114,269 @@ static const LADSPA_Descriptor mixer_desc = {
|
|||
.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)
|
||||
{
|
||||
switch(Index) {
|
||||
case 0:
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue