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