mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-26 17:18:15 +00:00
gst/audiofx/: Implement a base class for IIR filters.
Original commit message from CVS: * gst/audiofx/Makefile.am: * gst/audiofx/audiofxbaseiirfilter.c: (gst_audio_fx_base_iir_filter_base_init), (gst_audio_fx_base_iir_filter_dispose), (gst_audio_fx_base_iir_filter_class_init), (gst_audio_fx_base_iir_filter_init), (gst_audio_fx_base_iir_filter_calculate_gain), (gst_audio_fx_base_iir_filter_set_coefficients), (gst_audio_fx_base_iir_filter_setup), (process), (gst_audio_fx_base_iir_filter_transform_ip), (gst_audio_fx_base_iir_filter_stop): * gst/audiofx/audiofxbaseiirfilter.h: Implement a base class for IIR filters. * gst/audiofx/audiochebband.c: (gst_audio_cheb_band_base_init), (gst_audio_cheb_band_class_init), (gst_audio_cheb_band_init), (generate_coefficients), (gst_audio_cheb_band_set_property), (gst_audio_cheb_band_setup): * gst/audiofx/audiochebband.h: * gst/audiofx/audiocheblimit.c: (gst_audio_cheb_limit_base_init), (gst_audio_cheb_limit_class_init), (gst_audio_cheb_limit_init), (generate_coefficients), (gst_audio_cheb_limit_set_property), (gst_audio_cheb_limit_setup): * gst/audiofx/audiocheblimit.h: Use the IIR filter base class for the chebyshev filters.
This commit is contained in:
parent
17bb67f873
commit
4c7c4c00da
8 changed files with 649 additions and 700 deletions
28
ChangeLog
28
ChangeLog
|
@ -1,3 +1,31 @@
|
|||
2009-01-05 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
|
||||
* gst/audiofx/Makefile.am:
|
||||
* gst/audiofx/audiofxbaseiirfilter.c:
|
||||
(gst_audio_fx_base_iir_filter_base_init),
|
||||
(gst_audio_fx_base_iir_filter_dispose),
|
||||
(gst_audio_fx_base_iir_filter_class_init),
|
||||
(gst_audio_fx_base_iir_filter_init),
|
||||
(gst_audio_fx_base_iir_filter_calculate_gain),
|
||||
(gst_audio_fx_base_iir_filter_set_coefficients),
|
||||
(gst_audio_fx_base_iir_filter_setup), (process),
|
||||
(gst_audio_fx_base_iir_filter_transform_ip),
|
||||
(gst_audio_fx_base_iir_filter_stop):
|
||||
* gst/audiofx/audiofxbaseiirfilter.h:
|
||||
Implement a base class for IIR filters.
|
||||
|
||||
* gst/audiofx/audiochebband.c: (gst_audio_cheb_band_base_init),
|
||||
(gst_audio_cheb_band_class_init), (gst_audio_cheb_band_init),
|
||||
(generate_coefficients), (gst_audio_cheb_band_set_property),
|
||||
(gst_audio_cheb_band_setup):
|
||||
* gst/audiofx/audiochebband.h:
|
||||
* gst/audiofx/audiocheblimit.c: (gst_audio_cheb_limit_base_init),
|
||||
(gst_audio_cheb_limit_class_init), (gst_audio_cheb_limit_init),
|
||||
(generate_coefficients), (gst_audio_cheb_limit_set_property),
|
||||
(gst_audio_cheb_limit_setup):
|
||||
* gst/audiofx/audiocheblimit.h:
|
||||
Use the IIR filter base class for the chebyshev filters.
|
||||
|
||||
2009-01-02 Michael Smith <msmith@songbirdnest.com>
|
||||
|
||||
Patch by: Justin Karnegas <justin@affinix.com> and
|
||||
|
|
|
@ -9,6 +9,7 @@ libgstaudiofx_la_SOURCES = audiofx.c\
|
|||
audioamplify.c \
|
||||
audiodynamic.c \
|
||||
audiokaraoke.c \
|
||||
audiofxbaseiirfilter.c \
|
||||
audiocheblimit.c \
|
||||
audiochebband.c \
|
||||
audiowsincband.c \
|
||||
|
@ -34,6 +35,7 @@ noinst_HEADERS = audiopanorama.h \
|
|||
audioamplify.h \
|
||||
audiodynamic.h \
|
||||
audiokaraoke.h \
|
||||
audiofxbaseiirfilter.h \
|
||||
audiocheblimit.h \
|
||||
audiochebband.h \
|
||||
audiowsincband.h \
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
* Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -92,19 +92,6 @@
|
|||
#define GST_CAT_DEFAULT gst_audio_cheb_band_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static const GstElementDetails element_details =
|
||||
GST_ELEMENT_DETAILS ("Band pass & band reject filter",
|
||||
"Filter/Effect/Audio",
|
||||
"Chebyshev band pass and band reject filter",
|
||||
"Sebastian Dröge <slomo@circular-chaos.org>");
|
||||
|
||||
/* Filter signals and args */
|
||||
enum
|
||||
{
|
||||
/* FILL ME */
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
@ -116,18 +103,11 @@ enum
|
|||
PROP_POLES
|
||||
};
|
||||
|
||||
#define ALLOWED_CAPS \
|
||||
"audio/x-raw-float," \
|
||||
" width = (int) { 32, 64 }, " \
|
||||
" endianness = (int) BYTE_ORDER," \
|
||||
" rate = (int) [ 1, MAX ]," \
|
||||
" channels = (int) [ 1, MAX ]"
|
||||
|
||||
#define DEBUG_INIT(bla) \
|
||||
GST_DEBUG_CATEGORY_INIT (gst_audio_cheb_band_debug, "audiochebband", 0, "audiochebband element");
|
||||
|
||||
GST_BOILERPLATE_FULL (GstAudioChebBand, gst_audio_cheb_band,
|
||||
GstAudioFilter, GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
|
||||
GstAudioFXBaseIIRFilter, GST_TYPE_AUDIO_FX_BASE_IIR_FILTER, DEBUG_INIT);
|
||||
|
||||
static void gst_audio_cheb_band_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
|
@ -136,14 +116,6 @@ static void gst_audio_cheb_band_get_property (GObject * object,
|
|||
|
||||
static gboolean gst_audio_cheb_band_setup (GstAudioFilter * filter,
|
||||
GstRingBufferSpec * format);
|
||||
static GstFlowReturn
|
||||
gst_audio_cheb_band_transform_ip (GstBaseTransform * base, GstBuffer * buf);
|
||||
static gboolean gst_audio_cheb_band_start (GstBaseTransform * base);
|
||||
|
||||
static void process_64 (GstAudioChebBand * filter,
|
||||
gdouble * data, guint num_samples);
|
||||
static void process_32 (GstAudioChebBand * filter,
|
||||
gfloat * data, guint num_samples);
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -177,98 +149,56 @@ static void
|
|||
gst_audio_cheb_band_base_init (gpointer klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstCaps *caps;
|
||||
|
||||
gst_element_class_set_details (element_class, &element_details);
|
||||
|
||||
caps = gst_caps_from_string (ALLOWED_CAPS);
|
||||
gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
|
||||
caps);
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_cheb_band_dispose (GObject * object)
|
||||
{
|
||||
GstAudioChebBand *filter = GST_AUDIO_CHEB_BAND (object);
|
||||
|
||||
if (filter->a) {
|
||||
g_free (filter->a);
|
||||
filter->a = NULL;
|
||||
}
|
||||
|
||||
if (filter->b) {
|
||||
g_free (filter->b);
|
||||
filter->b = NULL;
|
||||
}
|
||||
|
||||
if (filter->channels) {
|
||||
GstAudioChebBandChannelCtx *ctx;
|
||||
gint i, channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
gst_element_class_set_details_simple (element_class,
|
||||
"Band pass & band reject filter", "Filter/Effect/Audio",
|
||||
"Chebyshev band pass and band reject filter",
|
||||
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_cheb_band_class_init (GstAudioChebBandClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstBaseTransformClass *trans_class;
|
||||
GstAudioFilterClass *filter_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
trans_class = (GstBaseTransformClass *) klass;
|
||||
filter_class = (GstAudioFilterClass *) klass;
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
|
||||
|
||||
gobject_class->set_property = gst_audio_cheb_band_set_property;
|
||||
gobject_class->get_property = gst_audio_cheb_band_get_property;
|
||||
gobject_class->dispose = gst_audio_cheb_band_dispose;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_MODE,
|
||||
g_param_spec_enum ("mode", "Mode",
|
||||
"Low pass or high pass mode", GST_TYPE_AUDIO_CHEBYSHEV_FREQ_BAND_MODE,
|
||||
MODE_BAND_PASS, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
MODE_BAND_PASS,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_TYPE,
|
||||
g_param_spec_int ("type", "Type",
|
||||
"Type of the chebychev filter", 1, 2,
|
||||
1, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
g_param_spec_int ("type", "Type", "Type of the chebychev filter", 1, 2, 1,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* FIXME: Don't use the complete possible range but restrict the upper boundary
|
||||
* so automatically generated UIs can use a slider without */
|
||||
g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY,
|
||||
g_param_spec_float ("lower-frequency", "Lower frequency",
|
||||
"Start frequency of the band (Hz)", 0.0, 100000.0,
|
||||
0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
0.0,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_UPPER_FREQUENCY,
|
||||
g_param_spec_float ("upper-frequency", "Upper frequency",
|
||||
"Stop frequency of the band (Hz)", 0.0, 100000.0,
|
||||
0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
"Stop frequency of the band (Hz)", 0.0, 100000.0, 0.0,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_RIPPLE,
|
||||
g_param_spec_float ("ripple", "Ripple",
|
||||
"Amount of ripple (dB)", 0.0, 200.0,
|
||||
0.25, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
g_param_spec_float ("ripple", "Ripple", "Amount of ripple (dB)", 0.0,
|
||||
200.0, 0.25,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
/* FIXME: What to do about this upper boundary? With a frequencies near
|
||||
* rate/4 32 poles are completely possible, with frequencies very low
|
||||
* or very high 16 poles already produces only noise */
|
||||
g_object_class_install_property (gobject_class, PROP_POLES,
|
||||
g_param_spec_int ("poles", "Poles",
|
||||
"Number of poles to use, will be rounded up to the next multiply of four",
|
||||
4, 32, 4, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
4, 32, 4,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_cheb_band_setup);
|
||||
trans_class->transform_ip =
|
||||
GST_DEBUG_FUNCPTR (gst_audio_cheb_band_transform_ip);
|
||||
trans_class->start = GST_DEBUG_FUNCPTR (gst_audio_cheb_band_start);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -280,12 +210,6 @@ gst_audio_cheb_band_init (GstAudioChebBand * filter,
|
|||
filter->type = 1;
|
||||
filter->poles = 4;
|
||||
filter->ripple = 0.25;
|
||||
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
|
||||
|
||||
filter->have_coeffs = FALSE;
|
||||
filter->num_a = 0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -474,98 +398,26 @@ generate_biquad_coefficients (GstAudioChebBand * filter,
|
|||
}
|
||||
}
|
||||
|
||||
/* Evaluate the transfer function that corresponds to the IIR
|
||||
* coefficients at zr + zi*I and return the magnitude */
|
||||
static gdouble
|
||||
calculate_gain (gdouble * a, gdouble * b, gint num_a, gint num_b, gdouble zr,
|
||||
gdouble zi)
|
||||
{
|
||||
gdouble sum_ar, sum_ai;
|
||||
gdouble sum_br, sum_bi;
|
||||
gdouble gain_r, gain_i;
|
||||
|
||||
gdouble sum_r_old;
|
||||
gdouble sum_i_old;
|
||||
|
||||
gint i;
|
||||
|
||||
sum_ar = 0.0;
|
||||
sum_ai = 0.0;
|
||||
for (i = num_a; i >= 0; i--) {
|
||||
sum_r_old = sum_ar;
|
||||
sum_i_old = sum_ai;
|
||||
|
||||
sum_ar = (sum_r_old * zr - sum_i_old * zi) + a[i];
|
||||
sum_ai = (sum_r_old * zi + sum_i_old * zr) + 0.0;
|
||||
}
|
||||
|
||||
sum_br = 0.0;
|
||||
sum_bi = 0.0;
|
||||
for (i = num_b; i >= 0; i--) {
|
||||
sum_r_old = sum_br;
|
||||
sum_i_old = sum_bi;
|
||||
|
||||
sum_br = (sum_r_old * zr - sum_i_old * zi) - b[i];
|
||||
sum_bi = (sum_r_old * zi + sum_i_old * zr) - 0.0;
|
||||
}
|
||||
sum_br += 1.0;
|
||||
sum_bi += 0.0;
|
||||
|
||||
gain_r =
|
||||
(sum_ar * sum_br + sum_ai * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
|
||||
gain_i =
|
||||
(sum_ai * sum_br - sum_ar * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
|
||||
|
||||
return (sqrt (gain_r * gain_r + gain_i * gain_i));
|
||||
}
|
||||
|
||||
static void
|
||||
generate_coefficients (GstAudioChebBand * filter)
|
||||
{
|
||||
gint channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
|
||||
if (filter->a) {
|
||||
g_free (filter->a);
|
||||
filter->a = NULL;
|
||||
}
|
||||
|
||||
if (filter->b) {
|
||||
g_free (filter->b);
|
||||
filter->b = NULL;
|
||||
}
|
||||
|
||||
if (filter->channels) {
|
||||
GstAudioChebBandChannelCtx *ctx;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
if (GST_AUDIO_FILTER (filter)->format.rate == 0) {
|
||||
filter->num_a = 1;
|
||||
filter->a = g_new0 (gdouble, 1);
|
||||
filter->a[0] = 1.0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = g_new0 (GstAudioChebBandChannelCtx, channels);
|
||||
gdouble *a = g_new0 (gdouble, 1);
|
||||
|
||||
a[0] = 1.0;
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, 1, NULL, 0);
|
||||
GST_LOG_OBJECT (filter, "rate was not set yet");
|
||||
return;
|
||||
}
|
||||
|
||||
filter->have_coeffs = TRUE;
|
||||
|
||||
if (filter->upper_frequency <= filter->lower_frequency) {
|
||||
filter->num_a = 1;
|
||||
filter->a = g_new0 (gdouble, 1);
|
||||
filter->a[0] = (filter->mode == MODE_BAND_PASS) ? 0.0 : 1.0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = g_new0 (GstAudioChebBandChannelCtx, channels);
|
||||
gdouble *a = g_new0 (gdouble, 1);
|
||||
|
||||
a[0] = (filter->mode == MODE_BAND_PASS) ? 0.0 : 1.0;
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, 1, NULL, 0);
|
||||
|
||||
GST_LOG_OBJECT (filter, "frequency band had no or negative dimension");
|
||||
return;
|
||||
}
|
||||
|
@ -586,18 +438,8 @@ generate_coefficients (GstAudioChebBand * filter)
|
|||
gdouble *a, *b;
|
||||
gint i, p;
|
||||
|
||||
filter->num_a = np + 1;
|
||||
filter->a = a = g_new0 (gdouble, np + 5);
|
||||
filter->num_b = np + 1;
|
||||
filter->b = b = g_new0 (gdouble, np + 5);
|
||||
|
||||
filter->channels = g_new0 (GstAudioChebBandChannelCtx, channels);
|
||||
for (i = 0; i < channels; i++) {
|
||||
GstAudioChebBandChannelCtx *ctx = &filter->channels[i];
|
||||
|
||||
ctx->x = g_new0 (gdouble, np + 1);
|
||||
ctx->y = g_new0 (gdouble, np + 1);
|
||||
}
|
||||
a = g_new0 (gdouble, np + 5);
|
||||
b = g_new0 (gdouble, np + 5);
|
||||
|
||||
/* Calculate transfer function coefficients */
|
||||
a[4] = 1.0;
|
||||
|
@ -645,8 +487,12 @@ generate_coefficients (GstAudioChebBand * filter)
|
|||
if (filter->mode == MODE_BAND_REJECT) {
|
||||
/* gain is sqrt(H(0)*H(0.5)) */
|
||||
|
||||
gdouble gain1 = calculate_gain (a, b, np, np, 1.0, 0.0);
|
||||
gdouble gain2 = calculate_gain (a, b, np, np, -1.0, 0.0);
|
||||
gdouble gain1 =
|
||||
gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b, np + 1,
|
||||
1.0, 0.0);
|
||||
gdouble gain2 =
|
||||
gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b, np + 1,
|
||||
-1.0, 0.0);
|
||||
|
||||
gain1 = sqrt (gain1 * gain2);
|
||||
|
||||
|
@ -664,13 +510,18 @@ generate_coefficients (GstAudioChebBand * filter)
|
|||
GST_AUDIO_FILTER (filter)->format.rate);
|
||||
gdouble w0 = (w2 + w1) / 2.0;
|
||||
gdouble zr = cos (w0), zi = sin (w0);
|
||||
gdouble gain = calculate_gain (a, b, np, np, zr, zi);
|
||||
gdouble gain =
|
||||
gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b, np + 1, zr,
|
||||
zi);
|
||||
|
||||
for (i = 0; i <= np; i++) {
|
||||
a[i] /= gain;
|
||||
}
|
||||
}
|
||||
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, np + 1, b, np + 1);
|
||||
|
||||
GST_LOG_OBJECT (filter,
|
||||
"Generated IIR coefficients for the Chebyshev filter");
|
||||
GST_LOG_OBJECT (filter,
|
||||
|
@ -680,7 +531,8 @@ generate_coefficients (GstAudioChebBand * filter)
|
|||
filter->upper_frequency, filter->ripple);
|
||||
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ 0Hz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, 1.0, 0.0)));
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b,
|
||||
np + 1, 1.0, 0.0)));
|
||||
{
|
||||
gdouble w1 =
|
||||
2.0 * M_PI * (filter->lower_frequency /
|
||||
|
@ -694,21 +546,23 @@ generate_coefficients (GstAudioChebBand * filter)
|
|||
zr = cos (w1);
|
||||
zi = sin (w1);
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, zr, zi)),
|
||||
(int) filter->lower_frequency);
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1,
|
||||
b, np + 1, zr, zi)), (int) filter->lower_frequency);
|
||||
zr = cos (w0);
|
||||
zi = sin (w0);
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, zr, zi)),
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1,
|
||||
b, np + 1, zr, zi)),
|
||||
(int) ((filter->lower_frequency + filter->upper_frequency) / 2.0));
|
||||
zr = cos (w2);
|
||||
zi = sin (w2);
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, zr, zi)),
|
||||
(int) filter->upper_frequency);
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1,
|
||||
b, np + 1, zr, zi)), (int) filter->upper_frequency);
|
||||
}
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, -1.0, 0.0)),
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b,
|
||||
np + 1, -1.0, 0.0)),
|
||||
GST_AUDIO_FILTER (filter)->format.rate / 2);
|
||||
}
|
||||
}
|
||||
|
@ -721,40 +575,40 @@ gst_audio_cheb_band_set_property (GObject * object, guint prop_id,
|
|||
|
||||
switch (prop_id) {
|
||||
case PROP_MODE:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->mode = g_value_get_enum (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_TYPE:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->type = g_value_get_int (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_LOWER_FREQUENCY:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->lower_frequency = g_value_get_float (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_UPPER_FREQUENCY:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->upper_frequency = g_value_get_float (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_RIPPLE:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->ripple = g_value_get_float (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_POLES:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->poles = GST_ROUND_UP_4 (g_value_get_int (value));
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
@ -799,122 +653,8 @@ static gboolean
|
|||
gst_audio_cheb_band_setup (GstAudioFilter * base, GstRingBufferSpec * format)
|
||||
{
|
||||
GstAudioChebBand *filter = GST_AUDIO_CHEB_BAND (base);
|
||||
gboolean ret = TRUE;
|
||||
|
||||
if (format->width == 32)
|
||||
filter->process = (GstAudioChebBandProcessFunc)
|
||||
process_32;
|
||||
else if (format->width == 64)
|
||||
filter->process = (GstAudioChebBandProcessFunc)
|
||||
process_64;
|
||||
else
|
||||
ret = FALSE;
|
||||
generate_coefficients (filter);
|
||||
|
||||
filter->have_coeffs = FALSE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
process (GstAudioChebBand * filter,
|
||||
GstAudioChebBandChannelCtx * ctx, gdouble x0)
|
||||
{
|
||||
gdouble val = filter->a[0] * x0;
|
||||
gint i, j;
|
||||
|
||||
for (i = 1, j = ctx->x_pos; i < filter->num_a; i++) {
|
||||
val += filter->a[i] * ctx->x[j];
|
||||
j--;
|
||||
if (j < 0)
|
||||
j = filter->num_a - 1;
|
||||
}
|
||||
|
||||
for (i = 1, j = ctx->y_pos; i < filter->num_b; i++) {
|
||||
val += filter->b[i] * ctx->y[j];
|
||||
j--;
|
||||
if (j < 0)
|
||||
j = filter->num_b - 1;
|
||||
}
|
||||
|
||||
if (ctx->x) {
|
||||
ctx->x_pos++;
|
||||
if (ctx->x_pos > filter->num_a - 1)
|
||||
ctx->x_pos = 0;
|
||||
ctx->x[ctx->x_pos] = x0;
|
||||
}
|
||||
|
||||
if (ctx->y) {
|
||||
ctx->y_pos++;
|
||||
if (ctx->y_pos > filter->num_b - 1)
|
||||
ctx->y_pos = 0;
|
||||
|
||||
ctx->y[ctx->y_pos] = val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define DEFINE_PROCESS_FUNC(width,ctype) \
|
||||
static void \
|
||||
process_##width (GstAudioChebBand * filter, \
|
||||
g##ctype * data, guint num_samples) \
|
||||
{ \
|
||||
gint i, j, channels = GST_AUDIO_FILTER (filter)->format.channels; \
|
||||
gdouble val; \
|
||||
\
|
||||
for (i = 0; i < num_samples / channels; i++) { \
|
||||
for (j = 0; j < channels; j++) { \
|
||||
val = process (filter, &filter->channels[j], *data); \
|
||||
*data++ = val; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_PROCESS_FUNC (32, float);
|
||||
DEFINE_PROCESS_FUNC (64, double);
|
||||
|
||||
#undef DEFINE_PROCESS_FUNC
|
||||
|
||||
/* GstBaseTransform vmethod implementations */
|
||||
static GstFlowReturn
|
||||
gst_audio_cheb_band_transform_ip (GstBaseTransform * base, GstBuffer * buf)
|
||||
{
|
||||
GstAudioChebBand *filter = GST_AUDIO_CHEB_BAND (base);
|
||||
guint num_samples =
|
||||
GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
|
||||
gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (buf));
|
||||
|
||||
if (gst_base_transform_is_passthrough (base))
|
||||
return GST_FLOW_OK;
|
||||
|
||||
if (!filter->have_coeffs)
|
||||
generate_coefficients (filter);
|
||||
|
||||
filter->process (filter, GST_BUFFER_DATA (buf), num_samples);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_audio_cheb_band_start (GstBaseTransform * base)
|
||||
{
|
||||
GstAudioChebBand *filter = GST_AUDIO_CHEB_BAND (base);
|
||||
gint channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
GstAudioChebBandChannelCtx *ctx;
|
||||
gint i;
|
||||
|
||||
/* Reset the history of input and output values if
|
||||
* already existing */
|
||||
if (channels && filter->channels) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
if (ctx->x)
|
||||
memset (ctx->x, 0, (filter->poles + 1) * sizeof (gdouble));
|
||||
if (ctx->y)
|
||||
memset (ctx->y, 0, (filter->poles + 1) * sizeof (gdouble));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
return GST_AUDIO_FILTER_CLASS (parent_class)->setup (base, format);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
* Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -26,6 +26,8 @@
|
|||
#include <gst/audio/audio.h>
|
||||
#include <gst/audio/gstaudiofilter.h>
|
||||
|
||||
#include "audiofxbaseiirfilter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GST_TYPE_AUDIO_CHEB_BAND (gst_audio_cheb_band_get_type())
|
||||
#define GST_AUDIO_CHEB_BAND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CHEB_BAND,GstAudioChebBand))
|
||||
|
@ -36,19 +38,9 @@ G_BEGIN_DECLS
|
|||
typedef struct _GstAudioChebBand GstAudioChebBand;
|
||||
typedef struct _GstAudioChebBandClass GstAudioChebBandClass;
|
||||
|
||||
typedef void (*GstAudioChebBandProcessFunc) (GstAudioChebBand *, guint8 *, guint);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble *x;
|
||||
gint x_pos;
|
||||
gdouble *y;
|
||||
gint y_pos;
|
||||
} GstAudioChebBandChannelCtx;
|
||||
|
||||
struct _GstAudioChebBand
|
||||
{
|
||||
GstAudioFilter audiofilter;
|
||||
GstAudioFXBaseIIRFilter parent;
|
||||
|
||||
gint mode;
|
||||
gint type;
|
||||
|
@ -56,21 +48,11 @@ struct _GstAudioChebBand
|
|||
gfloat lower_frequency;
|
||||
gfloat upper_frequency;
|
||||
gfloat ripple;
|
||||
|
||||
/* < private > */
|
||||
GstAudioChebBandProcessFunc process;
|
||||
|
||||
gboolean have_coeffs;
|
||||
gdouble *a;
|
||||
gint num_a;
|
||||
gdouble *b;
|
||||
gint num_b;
|
||||
GstAudioChebBandChannelCtx *channels;
|
||||
};
|
||||
|
||||
struct _GstAudioChebBandClass
|
||||
{
|
||||
GstAudioFilterClass parent;
|
||||
GstAudioFXBaseIIRFilterClass parent;
|
||||
};
|
||||
|
||||
GType gst_audio_cheb_band_get_type (void);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
* Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -88,19 +88,6 @@
|
|||
#define GST_CAT_DEFAULT gst_audio_cheb_limit_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static const GstElementDetails element_details =
|
||||
GST_ELEMENT_DETAILS ("Low pass & high pass filter",
|
||||
"Filter/Effect/Audio",
|
||||
"Chebyshev low pass and high pass filter",
|
||||
"Sebastian Dröge <slomo@circular-chaos.org>");
|
||||
|
||||
/* Filter signals and args */
|
||||
enum
|
||||
{
|
||||
/* FILL ME */
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
@ -111,18 +98,12 @@ enum
|
|||
PROP_POLES
|
||||
};
|
||||
|
||||
#define ALLOWED_CAPS \
|
||||
"audio/x-raw-float," \
|
||||
" width = (int) { 32, 64 }, " \
|
||||
" endianness = (int) BYTE_ORDER," \
|
||||
" rate = (int) [ 1, MAX ]," \
|
||||
" channels = (int) [ 1, MAX ]"
|
||||
|
||||
#define DEBUG_INIT(bla) \
|
||||
GST_DEBUG_CATEGORY_INIT (gst_audio_cheb_limit_debug, "audiocheblimit", 0, "audiocheblimit element");
|
||||
|
||||
GST_BOILERPLATE_FULL (GstAudioChebLimit,
|
||||
gst_audio_cheb_limit, GstAudioFilter, GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
|
||||
gst_audio_cheb_limit, GstAudioFXBaseIIRFilter,
|
||||
GST_TYPE_AUDIO_FX_BASE_IIR_FILTER, DEBUG_INIT);
|
||||
|
||||
static void gst_audio_cheb_limit_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
|
@ -131,14 +112,6 @@ static void gst_audio_cheb_limit_get_property (GObject * object,
|
|||
|
||||
static gboolean gst_audio_cheb_limit_setup (GstAudioFilter * filter,
|
||||
GstRingBufferSpec * format);
|
||||
static GstFlowReturn
|
||||
gst_audio_cheb_limit_transform_ip (GstBaseTransform * base, GstBuffer * buf);
|
||||
static gboolean gst_audio_cheb_limit_start (GstBaseTransform * base);
|
||||
|
||||
static void process_64 (GstAudioChebLimit * filter,
|
||||
gdouble * data, guint num_samples);
|
||||
static void process_32 (GstAudioChebLimit * filter,
|
||||
gfloat * data, guint num_samples);
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -172,80 +145,42 @@ static void
|
|||
gst_audio_cheb_limit_base_init (gpointer klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
GstCaps *caps;
|
||||
|
||||
gst_element_class_set_details (element_class, &element_details);
|
||||
|
||||
caps = gst_caps_from_string (ALLOWED_CAPS);
|
||||
gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
|
||||
caps);
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_cheb_limit_dispose (GObject * object)
|
||||
{
|
||||
GstAudioChebLimit *filter = GST_AUDIO_CHEB_LIMIT (object);
|
||||
|
||||
if (filter->a) {
|
||||
g_free (filter->a);
|
||||
filter->a = NULL;
|
||||
}
|
||||
|
||||
if (filter->b) {
|
||||
g_free (filter->b);
|
||||
filter->b = NULL;
|
||||
}
|
||||
|
||||
if (filter->channels) {
|
||||
GstAudioChebLimitChannelCtx *ctx;
|
||||
gint i, channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
gst_element_class_set_details_simple (element_class,
|
||||
"Low pass & high pass filter",
|
||||
"Filter/Effect/Audio",
|
||||
"Chebyshev low pass and high pass filter",
|
||||
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_cheb_limit_class_init (GstAudioChebLimitClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstBaseTransformClass *trans_class;
|
||||
GstAudioFilterClass *filter_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
trans_class = (GstBaseTransformClass *) klass;
|
||||
filter_class = (GstAudioFilterClass *) klass;
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
|
||||
|
||||
gobject_class->set_property = gst_audio_cheb_limit_set_property;
|
||||
gobject_class->get_property = gst_audio_cheb_limit_get_property;
|
||||
gobject_class->dispose = gst_audio_cheb_limit_dispose;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_MODE,
|
||||
g_param_spec_enum ("mode", "Mode",
|
||||
"Low pass or high pass mode",
|
||||
GST_TYPE_AUDIO_CHEBYSHEV_FREQ_LIMIT_MODE, MODE_LOW_PASS,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_TYPE,
|
||||
g_param_spec_int ("type", "Type", "Type of the chebychev filter", 1, 2, 1,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* FIXME: Don't use the complete possible range but restrict the upper boundary
|
||||
* so automatically generated UIs can use a slider without */
|
||||
g_object_class_install_property (gobject_class, PROP_CUTOFF,
|
||||
g_param_spec_float ("cutoff", "Cutoff", "Cut off frequency (Hz)", 0.0,
|
||||
100000.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
100000.0, 0.0,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (gobject_class, PROP_RIPPLE,
|
||||
g_param_spec_float ("ripple", "Ripple", "Amount of ripple (dB)", 0.0,
|
||||
200.0, 0.25, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
200.0, 0.25,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* FIXME: What to do about this upper boundary? With a cutoff frequency of
|
||||
* rate/4 32 poles are completely possible, with a cutoff frequency very low
|
||||
|
@ -253,12 +188,10 @@ gst_audio_cheb_limit_class_init (GstAudioChebLimitClass * klass)
|
|||
g_object_class_install_property (gobject_class, PROP_POLES,
|
||||
g_param_spec_int ("poles", "Poles",
|
||||
"Number of poles to use, will be rounded up to the next even number",
|
||||
2, 32, 4, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
|
||||
2, 32, 4,
|
||||
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_cheb_limit_setup);
|
||||
trans_class->transform_ip =
|
||||
GST_DEBUG_FUNCPTR (gst_audio_cheb_limit_transform_ip);
|
||||
trans_class->start = GST_DEBUG_FUNCPTR (gst_audio_cheb_limit_start);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -270,12 +203,6 @@ gst_audio_cheb_limit_init (GstAudioChebLimit * filter,
|
|||
filter->type = 1;
|
||||
filter->poles = 4;
|
||||
filter->ripple = 0.25;
|
||||
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
|
||||
|
||||
filter->have_coeffs = FALSE;
|
||||
filter->num_a = 0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -423,106 +350,34 @@ generate_biquad_coefficients (GstAudioChebLimit * filter,
|
|||
}
|
||||
}
|
||||
|
||||
/* Evaluate the transfer function that corresponds to the IIR
|
||||
* coefficients at zr + zi*I and return the magnitude */
|
||||
static gdouble
|
||||
calculate_gain (gdouble * a, gdouble * b, gint num_a, gint num_b, gdouble zr,
|
||||
gdouble zi)
|
||||
{
|
||||
gdouble sum_ar, sum_ai;
|
||||
gdouble sum_br, sum_bi;
|
||||
gdouble gain_r, gain_i;
|
||||
|
||||
gdouble sum_r_old;
|
||||
gdouble sum_i_old;
|
||||
|
||||
gint i;
|
||||
|
||||
sum_ar = 0.0;
|
||||
sum_ai = 0.0;
|
||||
for (i = num_a; i >= 0; i--) {
|
||||
sum_r_old = sum_ar;
|
||||
sum_i_old = sum_ai;
|
||||
|
||||
sum_ar = (sum_r_old * zr - sum_i_old * zi) + a[i];
|
||||
sum_ai = (sum_r_old * zi + sum_i_old * zr) + 0.0;
|
||||
}
|
||||
|
||||
sum_br = 0.0;
|
||||
sum_bi = 0.0;
|
||||
for (i = num_b; i >= 0; i--) {
|
||||
sum_r_old = sum_br;
|
||||
sum_i_old = sum_bi;
|
||||
|
||||
sum_br = (sum_r_old * zr - sum_i_old * zi) - b[i];
|
||||
sum_bi = (sum_r_old * zi + sum_i_old * zr) - 0.0;
|
||||
}
|
||||
sum_br += 1.0;
|
||||
sum_bi += 0.0;
|
||||
|
||||
gain_r =
|
||||
(sum_ar * sum_br + sum_ai * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
|
||||
gain_i =
|
||||
(sum_ai * sum_br - sum_ar * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
|
||||
|
||||
return (sqrt (gain_r * gain_r + gain_i * gain_i));
|
||||
}
|
||||
|
||||
static void
|
||||
generate_coefficients (GstAudioChebLimit * filter)
|
||||
{
|
||||
gint channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
|
||||
if (filter->a) {
|
||||
g_free (filter->a);
|
||||
filter->a = NULL;
|
||||
}
|
||||
|
||||
if (filter->b) {
|
||||
g_free (filter->b);
|
||||
filter->b = NULL;
|
||||
}
|
||||
|
||||
if (filter->channels) {
|
||||
GstAudioChebLimitChannelCtx *ctx;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
if (GST_AUDIO_FILTER (filter)->format.rate == 0) {
|
||||
filter->num_a = 1;
|
||||
filter->a = g_new0 (gdouble, 1);
|
||||
filter->a[0] = 1.0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels);
|
||||
gdouble *a = g_new0 (gdouble, 1);
|
||||
|
||||
a[0] = 1.0;
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, 1, NULL, 0);
|
||||
|
||||
GST_LOG_OBJECT (filter, "rate was not set yet");
|
||||
return;
|
||||
}
|
||||
|
||||
filter->have_coeffs = TRUE;
|
||||
|
||||
if (filter->cutoff >= GST_AUDIO_FILTER (filter)->format.rate / 2.0) {
|
||||
filter->num_a = 1;
|
||||
filter->a = g_new0 (gdouble, 1);
|
||||
filter->a[0] = (filter->mode == MODE_LOW_PASS) ? 1.0 : 0.0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels);
|
||||
gdouble *a = g_new0 (gdouble, 1);
|
||||
|
||||
a[0] = (filter->mode == MODE_LOW_PASS) ? 1.0 : 0.0;
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, 1, NULL, 0);
|
||||
GST_LOG_OBJECT (filter, "cutoff was higher than nyquist frequency");
|
||||
return;
|
||||
} else if (filter->cutoff <= 0.0) {
|
||||
filter->num_a = 1;
|
||||
filter->a = g_new0 (gdouble, 1);
|
||||
filter->a[0] = (filter->mode == MODE_LOW_PASS) ? 0.0 : 1.0;
|
||||
filter->num_b = 0;
|
||||
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels);
|
||||
gdouble *a = g_new0 (gdouble, 1);
|
||||
|
||||
a[0] = (filter->mode == MODE_LOW_PASS) ? 0.0 : 1.0;
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, 1, NULL, 0);
|
||||
GST_LOG_OBJECT (filter, "cutoff is lower than zero");
|
||||
return;
|
||||
}
|
||||
|
@ -533,18 +388,8 @@ generate_coefficients (GstAudioChebLimit * filter)
|
|||
gdouble *a, *b;
|
||||
gint i, p;
|
||||
|
||||
filter->num_a = np + 1;
|
||||
filter->a = a = g_new0 (gdouble, np + 3);
|
||||
filter->num_b = np + 1;
|
||||
filter->b = b = g_new0 (gdouble, np + 3);
|
||||
|
||||
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels);
|
||||
for (i = 0; i < channels; i++) {
|
||||
GstAudioChebLimitChannelCtx *ctx = &filter->channels[i];
|
||||
|
||||
ctx->x = g_new0 (gdouble, np + 1);
|
||||
ctx->y = g_new0 (gdouble, np + 1);
|
||||
}
|
||||
a = g_new0 (gdouble, np + 3);
|
||||
b = g_new0 (gdouble, np + 3);
|
||||
|
||||
/* Calculate transfer function coefficients */
|
||||
a[2] = 1.0;
|
||||
|
@ -587,15 +432,22 @@ generate_coefficients (GstAudioChebLimit * filter)
|
|||
gdouble gain;
|
||||
|
||||
if (filter->mode == MODE_LOW_PASS)
|
||||
gain = calculate_gain (a, b, np, np, 1.0, 0.0);
|
||||
gain =
|
||||
gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b, np + 1,
|
||||
1.0, 0.0);
|
||||
else
|
||||
gain = calculate_gain (a, b, np, np, -1.0, 0.0);
|
||||
gain =
|
||||
gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b, np + 1,
|
||||
-1.0, 0.0);
|
||||
|
||||
for (i = 0; i <= np; i++) {
|
||||
a[i] /= gain;
|
||||
}
|
||||
}
|
||||
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
|
||||
(filter), a, np + 1, b, np + 1);
|
||||
|
||||
GST_LOG_OBJECT (filter,
|
||||
"Generated IIR coefficients for the Chebyshev filter");
|
||||
GST_LOG_OBJECT (filter,
|
||||
|
@ -603,7 +455,8 @@ generate_coefficients (GstAudioChebLimit * filter)
|
|||
(filter->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass",
|
||||
filter->type, filter->poles, filter->cutoff, filter->ripple);
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ 0 Hz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, 1.0, 0.0)));
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b,
|
||||
np + 1, 1.0, 0.0)));
|
||||
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
{
|
||||
|
@ -613,13 +466,14 @@ generate_coefficients (GstAudioChebLimit * filter)
|
|||
gdouble zr = cos (wc), zi = sin (wc);
|
||||
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, zr, zi)),
|
||||
(int) filter->cutoff);
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1,
|
||||
b, np + 1, zr, zi)), (int) filter->cutoff);
|
||||
}
|
||||
#endif
|
||||
|
||||
GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz",
|
||||
20.0 * log10 (calculate_gain (a, b, np, np, -1.0, 0.0)),
|
||||
20.0 * log10 (gst_audio_fx_base_iir_filter_calculate_gain (a, np + 1, b,
|
||||
np + 1, -1.0, 0.0)),
|
||||
GST_AUDIO_FILTER (filter)->format.rate / 2);
|
||||
}
|
||||
}
|
||||
|
@ -632,34 +486,34 @@ gst_audio_cheb_limit_set_property (GObject * object, guint prop_id,
|
|||
|
||||
switch (prop_id) {
|
||||
case PROP_MODE:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->mode = g_value_get_enum (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_TYPE:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->type = g_value_get_int (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_CUTOFF:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->cutoff = g_value_get_float (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_RIPPLE:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->ripple = g_value_get_float (value);
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
case PROP_POLES:
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
GST_OBJECT_LOCK (filter);
|
||||
filter->poles = GST_ROUND_UP_2 (g_value_get_int (value));
|
||||
generate_coefficients (filter);
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
GST_OBJECT_UNLOCK (filter);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
|
@ -701,123 +555,8 @@ static gboolean
|
|||
gst_audio_cheb_limit_setup (GstAudioFilter * base, GstRingBufferSpec * format)
|
||||
{
|
||||
GstAudioChebLimit *filter = GST_AUDIO_CHEB_LIMIT (base);
|
||||
gboolean ret = TRUE;
|
||||
|
||||
if (format->width == 32)
|
||||
filter->process = (GstAudioChebLimitProcessFunc)
|
||||
process_32;
|
||||
else if (format->width == 64)
|
||||
filter->process = (GstAudioChebLimitProcessFunc)
|
||||
process_64;
|
||||
else
|
||||
ret = FALSE;
|
||||
generate_coefficients (filter);
|
||||
|
||||
filter->have_coeffs = FALSE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
process (GstAudioChebLimit * filter,
|
||||
GstAudioChebLimitChannelCtx * ctx, gdouble x0)
|
||||
{
|
||||
gdouble val = filter->a[0] * x0;
|
||||
gint i, j;
|
||||
|
||||
for (i = 1, j = ctx->x_pos; i < filter->num_a; i++) {
|
||||
val += filter->a[i] * ctx->x[j];
|
||||
j--;
|
||||
if (j < 0)
|
||||
j = filter->num_a - 1;
|
||||
}
|
||||
|
||||
for (i = 1, j = ctx->y_pos; i < filter->num_b; i++) {
|
||||
val += filter->b[i] * ctx->y[j];
|
||||
j--;
|
||||
if (j < 0)
|
||||
j = filter->num_b - 1;
|
||||
}
|
||||
|
||||
if (ctx->x) {
|
||||
ctx->x_pos++;
|
||||
if (ctx->x_pos > filter->num_a - 1)
|
||||
ctx->x_pos = 0;
|
||||
ctx->x[ctx->x_pos] = x0;
|
||||
}
|
||||
|
||||
if (ctx->y) {
|
||||
ctx->y_pos++;
|
||||
if (ctx->y_pos > filter->num_b - 1)
|
||||
ctx->y_pos = 0;
|
||||
|
||||
ctx->y[ctx->y_pos] = val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define DEFINE_PROCESS_FUNC(width,ctype) \
|
||||
static void \
|
||||
process_##width (GstAudioChebLimit * filter, \
|
||||
g##ctype * data, guint num_samples) \
|
||||
{ \
|
||||
gint i, j, channels = GST_AUDIO_FILTER (filter)->format.channels; \
|
||||
gdouble val; \
|
||||
\
|
||||
for (i = 0; i < num_samples / channels; i++) { \
|
||||
for (j = 0; j < channels; j++) { \
|
||||
val = process (filter, &filter->channels[j], *data); \
|
||||
*data++ = val; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_PROCESS_FUNC (32, float);
|
||||
DEFINE_PROCESS_FUNC (64, double);
|
||||
|
||||
#undef DEFINE_PROCESS_FUNC
|
||||
|
||||
/* GstBaseTransform vmethod implementations */
|
||||
static GstFlowReturn
|
||||
gst_audio_cheb_limit_transform_ip (GstBaseTransform * base, GstBuffer * buf)
|
||||
{
|
||||
GstAudioChebLimit *filter = GST_AUDIO_CHEB_LIMIT (base);
|
||||
guint num_samples =
|
||||
GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
|
||||
gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (buf));
|
||||
|
||||
if (gst_base_transform_is_passthrough (base))
|
||||
return GST_FLOW_OK;
|
||||
|
||||
if (!filter->have_coeffs)
|
||||
generate_coefficients (filter);
|
||||
|
||||
filter->process (filter, GST_BUFFER_DATA (buf), num_samples);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_audio_cheb_limit_start (GstBaseTransform * base)
|
||||
{
|
||||
GstAudioChebLimit *filter = GST_AUDIO_CHEB_LIMIT (base);
|
||||
gint channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
GstAudioChebLimitChannelCtx *ctx;
|
||||
gint i;
|
||||
|
||||
/* Reset the history of input and output values if
|
||||
* already existing */
|
||||
if (channels && filter->channels) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
if (ctx->x)
|
||||
memset (ctx->x, 0, (filter->poles + 1) * sizeof (gdouble));
|
||||
if (ctx->y)
|
||||
memset (ctx->y, 0, (filter->poles + 1) * sizeof (gdouble));
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
return GST_AUDIO_FILTER_CLASS (parent_class)->setup (base, format);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
* Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -26,53 +26,38 @@
|
|||
#include <gst/audio/audio.h>
|
||||
#include <gst/audio/gstaudiofilter.h>
|
||||
|
||||
#include "audiofxbaseiirfilter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_AUDIO_CHEB_LIMIT (gst_audio_cheb_limit_get_type())
|
||||
#define GST_AUDIO_CHEB_LIMIT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CHEB_LIMIT,GstAudioChebLimit))
|
||||
#define GST_IS_AUDIO_CHEB_LIMIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_CHEB_LIMIT))
|
||||
#define GST_AUDIO_CHEB_LIMIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_CHEB_LIMIT,GstAudioChebLimitClass))
|
||||
#define GST_IS_AUDIO_CHEB_LIMIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_CHEB_LIMIT))
|
||||
#define GST_AUDIO_CHEB_LIMIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_CHEB_LIMIT,GstAudioChebLimitClass))
|
||||
|
||||
typedef struct _GstAudioChebLimit GstAudioChebLimit;
|
||||
typedef struct _GstAudioChebLimitClass GstAudioChebLimitClass;
|
||||
|
||||
typedef void (*GstAudioChebLimitProcessFunc) (GstAudioChebLimit *, guint8 *, guint);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble *x;
|
||||
gint x_pos;
|
||||
gdouble *y;
|
||||
gint y_pos;
|
||||
} GstAudioChebLimitChannelCtx;
|
||||
|
||||
struct _GstAudioChebLimit
|
||||
{
|
||||
GstAudioFilter audiofilter;
|
||||
GstAudioFXBaseIIRFilter parent;
|
||||
|
||||
gint mode;
|
||||
gint type;
|
||||
gint poles;
|
||||
gfloat cutoff;
|
||||
gfloat ripple;
|
||||
|
||||
/* < private > */
|
||||
GstAudioChebLimitProcessFunc process;
|
||||
|
||||
gboolean have_coeffs;
|
||||
gdouble *a;
|
||||
gint num_a;
|
||||
gdouble *b;
|
||||
gint num_b;
|
||||
GstAudioChebLimitChannelCtx *channels;
|
||||
};
|
||||
|
||||
struct _GstAudioChebLimitClass
|
||||
{
|
||||
GstAudioFilterClass parent;
|
||||
GstAudioFXBaseIIRFilterClass parent;
|
||||
};
|
||||
|
||||
GType gst_audio_cheb_limit_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_AUDIO_CHEB_LIMIT_H__ */
|
||||
|
|
396
gst/audiofx/audiofxbaseiirfilter.c
Normal file
396
gst/audiofx/audiofxbaseiirfilter.c
Normal file
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <gst/audio/gstaudiofilter.h>
|
||||
#include <gst/controller/gstcontroller.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "audiofxbaseiirfilter.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_audio_fx_base_iir_filter_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define ALLOWED_CAPS \
|
||||
"audio/x-raw-float," \
|
||||
" width = (int) { 32, 64 }, " \
|
||||
" endianness = (int) BYTE_ORDER," \
|
||||
" rate = (int) [ 1, MAX ]," \
|
||||
" channels = (int) [ 1, MAX ]"
|
||||
|
||||
#define DEBUG_INIT(bla) \
|
||||
GST_DEBUG_CATEGORY_INIT (gst_audio_fx_base_iir_filter_debug, "audiobaseiirfilter", 0, "Audio IIR Filter Base Class");
|
||||
|
||||
GST_BOILERPLATE_FULL (GstAudioFXBaseIIRFilter,
|
||||
gst_audio_fx_base_iir_filter, GstAudioFilter, GST_TYPE_AUDIO_FILTER,
|
||||
DEBUG_INIT);
|
||||
|
||||
static gboolean gst_audio_fx_base_iir_filter_setup (GstAudioFilter * filter,
|
||||
GstRingBufferSpec * format);
|
||||
static GstFlowReturn
|
||||
gst_audio_fx_base_iir_filter_transform_ip (GstBaseTransform * base,
|
||||
GstBuffer * buf);
|
||||
static gboolean gst_audio_fx_base_iir_filter_stop (GstBaseTransform * base);
|
||||
|
||||
static void process_64 (GstAudioFXBaseIIRFilter * filter,
|
||||
gdouble * data, guint num_samples);
|
||||
static void process_32 (GstAudioFXBaseIIRFilter * filter,
|
||||
gfloat * data, guint num_samples);
|
||||
|
||||
/* GObject vmethod implementations */
|
||||
|
||||
static void
|
||||
gst_audio_fx_base_iir_filter_base_init (gpointer klass)
|
||||
{
|
||||
GstCaps *caps;
|
||||
|
||||
caps = gst_caps_from_string (ALLOWED_CAPS);
|
||||
gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
|
||||
caps);
|
||||
gst_caps_unref (caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_fx_base_iir_filter_dispose (GObject * object)
|
||||
{
|
||||
GstAudioFXBaseIIRFilter *filter = GST_AUDIO_FX_BASE_IIR_FILTER (object);
|
||||
|
||||
if (filter->a) {
|
||||
g_free (filter->a);
|
||||
filter->a = NULL;
|
||||
}
|
||||
|
||||
if (filter->b) {
|
||||
g_free (filter->b);
|
||||
filter->b = NULL;
|
||||
}
|
||||
|
||||
if (filter->channels) {
|
||||
GstAudioFXBaseIIRFilterChannelCtx *ctx;
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < filter->nchannels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_fx_base_iir_filter_class_init (GstAudioFXBaseIIRFilterClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
|
||||
GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
|
||||
|
||||
gobject_class->dispose = gst_audio_fx_base_iir_filter_dispose;
|
||||
|
||||
filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_fx_base_iir_filter_setup);
|
||||
|
||||
trans_class->transform_ip =
|
||||
GST_DEBUG_FUNCPTR (gst_audio_fx_base_iir_filter_transform_ip);
|
||||
trans_class->stop = GST_DEBUG_FUNCPTR (gst_audio_fx_base_iir_filter_stop);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_audio_fx_base_iir_filter_init (GstAudioFXBaseIIRFilter * filter,
|
||||
GstAudioFXBaseIIRFilterClass * klass)
|
||||
{
|
||||
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
|
||||
|
||||
filter->a = NULL;
|
||||
filter->na = 0;
|
||||
filter->b = NULL;
|
||||
filter->nb = 0;
|
||||
filter->channels = NULL;
|
||||
filter->nchannels = 0;
|
||||
}
|
||||
|
||||
/* Evaluate the transfer function that corresponds to the IIR
|
||||
* coefficients at zr + zi*I and return the magnitude */
|
||||
gdouble
|
||||
gst_audio_fx_base_iir_filter_calculate_gain (gdouble * a, guint na, gdouble * b,
|
||||
guint nb, gdouble zr, gdouble zi)
|
||||
{
|
||||
gdouble sum_ar, sum_ai;
|
||||
gdouble sum_br, sum_bi;
|
||||
gdouble gain_r, gain_i;
|
||||
|
||||
gdouble sum_r_old;
|
||||
gdouble sum_i_old;
|
||||
|
||||
gint i;
|
||||
|
||||
sum_ar = 0.0;
|
||||
sum_ai = 0.0;
|
||||
for (i = na - 1; i >= 0; i--) {
|
||||
sum_r_old = sum_ar;
|
||||
sum_i_old = sum_ai;
|
||||
|
||||
sum_ar = (sum_r_old * zr - sum_i_old * zi) + a[i];
|
||||
sum_ai = (sum_r_old * zi + sum_i_old * zr) + 0.0;
|
||||
}
|
||||
|
||||
sum_br = 0.0;
|
||||
sum_bi = 0.0;
|
||||
for (i = nb - 1; i >= 0; i--) {
|
||||
sum_r_old = sum_br;
|
||||
sum_i_old = sum_bi;
|
||||
|
||||
sum_br = (sum_r_old * zr - sum_i_old * zi) - b[i];
|
||||
sum_bi = (sum_r_old * zi + sum_i_old * zr) - 0.0;
|
||||
}
|
||||
sum_br += 1.0;
|
||||
sum_bi += 0.0;
|
||||
|
||||
gain_r =
|
||||
(sum_ar * sum_br + sum_ai * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
|
||||
gain_i =
|
||||
(sum_ai * sum_br - sum_ar * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
|
||||
|
||||
return (sqrt (gain_r * gain_r + gain_i * gain_i));
|
||||
}
|
||||
|
||||
void
|
||||
gst_audio_fx_base_iir_filter_set_coefficients (GstAudioFXBaseIIRFilter * filter,
|
||||
gdouble * a, guint na, gdouble * b, guint nb)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (GST_IS_AUDIO_FX_BASE_IIR_FILTER (filter));
|
||||
|
||||
GST_BASE_TRANSFORM_LOCK (filter);
|
||||
|
||||
g_free (filter->a);
|
||||
g_free (filter->b);
|
||||
|
||||
filter->a = filter->b = NULL;
|
||||
|
||||
if (filter->channels) {
|
||||
GstAudioFXBaseIIRFilterChannelCtx *ctx;
|
||||
gboolean free = (na != filter->na || nb != filter->nb);
|
||||
|
||||
for (i = 0; i < filter->nchannels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
|
||||
if (free)
|
||||
g_free (ctx->x);
|
||||
else
|
||||
memset (ctx->x, 0, filter->na * sizeof (gdouble));
|
||||
|
||||
if (free)
|
||||
g_free (ctx->y);
|
||||
else
|
||||
memset (ctx->y, 0, filter->nb * sizeof (gdouble));
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
filter->na = na;
|
||||
filter->nb = nb;
|
||||
|
||||
filter->a = a;
|
||||
filter->b = b;
|
||||
|
||||
if (filter->nchannels && !filter->channels) {
|
||||
GstAudioFXBaseIIRFilterChannelCtx *ctx;
|
||||
|
||||
filter->channels =
|
||||
g_new0 (GstAudioFXBaseIIRFilterChannelCtx, filter->nchannels);
|
||||
for (i = 0; i < filter->nchannels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
|
||||
ctx->x = g_new0 (gdouble, filter->na);
|
||||
ctx->y = g_new0 (gdouble, filter->nb);
|
||||
}
|
||||
}
|
||||
|
||||
GST_BASE_TRANSFORM_UNLOCK (filter);
|
||||
}
|
||||
|
||||
/* GstAudioFilter vmethod implementations */
|
||||
|
||||
static gboolean
|
||||
gst_audio_fx_base_iir_filter_setup (GstAudioFilter * base,
|
||||
GstRingBufferSpec * format)
|
||||
{
|
||||
GstAudioFXBaseIIRFilter *filter = GST_AUDIO_FX_BASE_IIR_FILTER (base);
|
||||
gboolean ret = TRUE;
|
||||
|
||||
if (format->width == 32)
|
||||
filter->process = (GstAudioFXBaseIIRFilterProcessFunc)
|
||||
process_32;
|
||||
else if (format->width == 64)
|
||||
filter->process = (GstAudioFXBaseIIRFilterProcessFunc)
|
||||
process_64;
|
||||
else
|
||||
ret = FALSE;
|
||||
|
||||
if (format->channels != filter->nchannels) {
|
||||
guint i;
|
||||
GstAudioFXBaseIIRFilterChannelCtx *ctx;
|
||||
|
||||
if (filter->channels) {
|
||||
|
||||
for (i = 0; i < filter->nchannels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
|
||||
g_free (filter->channels);
|
||||
filter->channels = NULL;
|
||||
}
|
||||
|
||||
filter->nchannels = format->channels;
|
||||
|
||||
filter->channels =
|
||||
g_new0 (GstAudioFXBaseIIRFilterChannelCtx, filter->nchannels);
|
||||
for (i = 0; i < filter->nchannels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
|
||||
ctx->x = g_new0 (gdouble, filter->na);
|
||||
ctx->y = g_new0 (gdouble, filter->nb);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
process (GstAudioFXBaseIIRFilter * filter,
|
||||
GstAudioFXBaseIIRFilterChannelCtx * ctx, gdouble x0)
|
||||
{
|
||||
gdouble val = filter->a[0] * x0;
|
||||
gint i, j;
|
||||
|
||||
for (i = 1, j = ctx->x_pos; i < filter->na; i++) {
|
||||
val += filter->a[i] * ctx->x[j];
|
||||
j--;
|
||||
if (j < 0)
|
||||
j = filter->na - 1;
|
||||
}
|
||||
|
||||
for (i = 1, j = ctx->y_pos; i < filter->nb; i++) {
|
||||
val += filter->b[i] * ctx->y[j];
|
||||
j--;
|
||||
if (j < 0)
|
||||
j = filter->nb - 1;
|
||||
}
|
||||
|
||||
if (ctx->x) {
|
||||
ctx->x_pos++;
|
||||
if (ctx->x_pos >= filter->na)
|
||||
ctx->x_pos = 0;
|
||||
ctx->x[ctx->x_pos] = x0;
|
||||
}
|
||||
if (ctx->y) {
|
||||
ctx->y_pos++;
|
||||
if (ctx->y_pos >= filter->nb)
|
||||
ctx->y_pos = 0;
|
||||
|
||||
ctx->y[ctx->y_pos] = val;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define DEFINE_PROCESS_FUNC(width,ctype) \
|
||||
static void \
|
||||
process_##width (GstAudioFXBaseIIRFilter * filter, \
|
||||
g##ctype * data, guint num_samples) \
|
||||
{ \
|
||||
gint i, j, channels = GST_AUDIO_FILTER (filter)->format.channels; \
|
||||
gdouble val; \
|
||||
\
|
||||
for (i = 0; i < num_samples / channels; i++) { \
|
||||
for (j = 0; j < channels; j++) { \
|
||||
val = process (filter, &filter->channels[j], *data); \
|
||||
*data++ = val; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_PROCESS_FUNC (32, float);
|
||||
DEFINE_PROCESS_FUNC (64, double);
|
||||
|
||||
#undef DEFINE_PROCESS_FUNC
|
||||
|
||||
/* GstBaseTransform vmethod implementations */
|
||||
static GstFlowReturn
|
||||
gst_audio_fx_base_iir_filter_transform_ip (GstBaseTransform * base,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstAudioFXBaseIIRFilter *filter = GST_AUDIO_FX_BASE_IIR_FILTER (base);
|
||||
guint num_samples =
|
||||
GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);
|
||||
|
||||
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
|
||||
gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (buf));
|
||||
|
||||
if (gst_base_transform_is_passthrough (base))
|
||||
return GST_FLOW_OK;
|
||||
|
||||
g_return_val_if_fail (filter->a != NULL, GST_FLOW_ERROR);
|
||||
|
||||
filter->process (filter, GST_BUFFER_DATA (buf), num_samples);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gst_audio_fx_base_iir_filter_stop (GstBaseTransform * base)
|
||||
{
|
||||
GstAudioFXBaseIIRFilter *filter = GST_AUDIO_FX_BASE_IIR_FILTER (base);
|
||||
guint channels = GST_AUDIO_FILTER (filter)->format.channels;
|
||||
GstAudioFXBaseIIRFilterChannelCtx *ctx;
|
||||
guint i;
|
||||
|
||||
/* Reset the history of input and output values if
|
||||
* already existing */
|
||||
if (channels && filter->channels) {
|
||||
for (i = 0; i < channels; i++) {
|
||||
ctx = &filter->channels[i];
|
||||
g_free (ctx->x);
|
||||
g_free (ctx->y);
|
||||
}
|
||||
g_free (filter->channels);
|
||||
}
|
||||
filter->channels = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
77
gst/audiofx/audiofxbaseiirfilter.h
Normal file
77
gst/audiofx/audiofxbaseiirfilter.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2007-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_AUDIO_FX_BASE_IIR_FILTER_H__
|
||||
#define __GST_AUDIO_FX_BASE_IIR_FILTER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <gst/audio/gstaudiofilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_AUDIO_FX_BASE_IIR_FILTER (gst_audio_fx_base_iir_filter_get_type())
|
||||
#define GST_AUDIO_FX_BASE_IIR_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_FX_BASE_IIR_FILTER,GstAudioFXBaseIIRFilter))
|
||||
#define GST_IS_AUDIO_FX_BASE_IIR_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_FX_BASE_IIR_FILTER))
|
||||
#define GST_AUDIO_FX_BASE_IIR_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_FX_BASE_IIR_FILTER,GstAudioFXBaseIIRFilterClass))
|
||||
#define GST_IS_AUDIO_FX_BASE_IIR_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_FX_BASE_IIR_FILTER))
|
||||
#define GST_AUDIO_FX_BASE_IIR_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_FX_BASE_IIR_FILTER,GstAudioFXBaseIIRFilterClass))
|
||||
typedef struct _GstAudioFXBaseIIRFilter GstAudioFXBaseIIRFilter;
|
||||
typedef struct _GstAudioFXBaseIIRFilterClass GstAudioFXBaseIIRFilterClass;
|
||||
|
||||
typedef void (*GstAudioFXBaseIIRFilterProcessFunc) (GstAudioFXBaseIIRFilter *, guint8 *, guint);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gdouble *x;
|
||||
gint x_pos;
|
||||
gdouble *y;
|
||||
gint y_pos;
|
||||
} GstAudioFXBaseIIRFilterChannelCtx;
|
||||
|
||||
struct _GstAudioFXBaseIIRFilter
|
||||
{
|
||||
GstAudioFilter audiofilter;
|
||||
|
||||
/* < private > */
|
||||
GstAudioFXBaseIIRFilterProcessFunc process;
|
||||
|
||||
gboolean have_coeffs;
|
||||
gdouble *a;
|
||||
guint na;
|
||||
gdouble *b;
|
||||
guint nb;
|
||||
GstAudioFXBaseIIRFilterChannelCtx *channels;
|
||||
guint nchannels;
|
||||
};
|
||||
|
||||
struct _GstAudioFXBaseIIRFilterClass
|
||||
{
|
||||
GstAudioFilterClass parent;
|
||||
};
|
||||
|
||||
GType gst_audio_fx_base_iir_filter_get_type (void);
|
||||
void gst_audio_fx_base_iir_filter_set_coefficients (GstAudioFXBaseIIRFilter *filter, gdouble *a, guint na, gdouble *b, guint nb);
|
||||
gdouble gst_audio_fx_base_iir_filter_calculate_gain (gdouble *a, guint na, gdouble *b, guint nb, gdouble zr, gdouble zi);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_AUDIO_FX_BASE_IIR_FILTER_H__ */
|
Loading…
Reference in a new issue