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:
Sebastian Dröge 2009-01-05 10:13:29 +00:00
parent 17bb67f873
commit 4c7c4c00da
8 changed files with 649 additions and 700 deletions

View file

@ -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> 2009-01-02 Michael Smith <msmith@songbirdnest.com>
Patch by: Justin Karnegas <justin@affinix.com> and Patch by: Justin Karnegas <justin@affinix.com> and

View file

@ -9,6 +9,7 @@ libgstaudiofx_la_SOURCES = audiofx.c\
audioamplify.c \ audioamplify.c \
audiodynamic.c \ audiodynamic.c \
audiokaraoke.c \ audiokaraoke.c \
audiofxbaseiirfilter.c \
audiocheblimit.c \ audiocheblimit.c \
audiochebband.c \ audiochebband.c \
audiowsincband.c \ audiowsincband.c \
@ -34,6 +35,7 @@ noinst_HEADERS = audiopanorama.h \
audioamplify.h \ audioamplify.h \
audiodynamic.h \ audiodynamic.h \
audiokaraoke.h \ audiokaraoke.h \
audiofxbaseiirfilter.h \
audiocheblimit.h \ audiocheblimit.h \
audiochebband.h \ audiochebband.h \
audiowsincband.h \ audiowsincband.h \

View file

@ -1,6 +1,6 @@
/* /*
* GStreamer * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -92,19 +92,6 @@
#define GST_CAT_DEFAULT gst_audio_cheb_band_debug #define GST_CAT_DEFAULT gst_audio_cheb_band_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); 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 enum
{ {
PROP_0, PROP_0,
@ -116,18 +103,11 @@ enum
PROP_POLES 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) \ #define DEBUG_INIT(bla) \
GST_DEBUG_CATEGORY_INIT (gst_audio_cheb_band_debug, "audiochebband", 0, "audiochebband element"); GST_DEBUG_CATEGORY_INIT (gst_audio_cheb_band_debug, "audiochebband", 0, "audiochebband element");
GST_BOILERPLATE_FULL (GstAudioChebBand, gst_audio_cheb_band, 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, static void gst_audio_cheb_band_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec); 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, static gboolean gst_audio_cheb_band_setup (GstAudioFilter * filter,
GstRingBufferSpec * format); 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 enum
{ {
@ -177,98 +149,56 @@ static void
gst_audio_cheb_band_base_init (gpointer klass) gst_audio_cheb_band_base_init (gpointer klass)
{ {
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstCaps *caps;
gst_element_class_set_details (element_class, &element_details); gst_element_class_set_details_simple (element_class,
"Band pass & band reject filter", "Filter/Effect/Audio",
caps = gst_caps_from_string (ALLOWED_CAPS); "Chebyshev band pass and band reject filter",
gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass), "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
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);
} }
static void static void
gst_audio_cheb_band_class_init (GstAudioChebBandClass * klass) gst_audio_cheb_band_class_init (GstAudioChebBandClass * klass)
{ {
GObjectClass *gobject_class; GObjectClass *gobject_class = (GObjectClass *) klass;
GstBaseTransformClass *trans_class; GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
GstAudioFilterClass *filter_class;
gobject_class = (GObjectClass *) klass;
trans_class = (GstBaseTransformClass *) klass;
filter_class = (GstAudioFilterClass *) klass;
gobject_class->set_property = gst_audio_cheb_band_set_property; gobject_class->set_property = gst_audio_cheb_band_set_property;
gobject_class->get_property = gst_audio_cheb_band_get_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_object_class_install_property (gobject_class, PROP_MODE,
g_param_spec_enum ("mode", "Mode", g_param_spec_enum ("mode", "Mode",
"Low pass or high pass mode", GST_TYPE_AUDIO_CHEBYSHEV_FREQ_BAND_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_object_class_install_property (gobject_class, PROP_TYPE,
g_param_spec_int ("type", "Type", g_param_spec_int ("type", "Type", "Type of the chebychev filter", 1, 2, 1,
"Type of the chebychev filter", 1, 2, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
1, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
/* FIXME: Don't use the complete possible range but restrict the upper boundary /* FIXME: Don't use the complete possible range but restrict the upper boundary
* so automatically generated UIs can use a slider without */ * so automatically generated UIs can use a slider without */
g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY, g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY,
g_param_spec_float ("lower-frequency", "Lower frequency", g_param_spec_float ("lower-frequency", "Lower frequency",
"Start frequency of the band (Hz)", 0.0, 100000.0, "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_object_class_install_property (gobject_class, PROP_UPPER_FREQUENCY,
g_param_spec_float ("upper-frequency", "Upper frequency", g_param_spec_float ("upper-frequency", "Upper frequency",
"Stop frequency of the band (Hz)", 0.0, 100000.0, "Stop frequency of the band (Hz)", 0.0, 100000.0, 0.0,
0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_RIPPLE, g_object_class_install_property (gobject_class, PROP_RIPPLE,
g_param_spec_float ("ripple", "Ripple", g_param_spec_float ("ripple", "Ripple", "Amount of ripple (dB)", 0.0,
"Amount of ripple (dB)", 0.0, 200.0, 200.0, 0.25,
0.25, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE)); G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
/* FIXME: What to do about this upper boundary? With a frequencies near /* FIXME: What to do about this upper boundary? With a frequencies near
* rate/4 32 poles are completely possible, with frequencies very low * rate/4 32 poles are completely possible, with frequencies very low
* or very high 16 poles already produces only noise */ * or very high 16 poles already produces only noise */
g_object_class_install_property (gobject_class, PROP_POLES, g_object_class_install_property (gobject_class, PROP_POLES,
g_param_spec_int ("poles", "Poles", g_param_spec_int ("poles", "Poles",
"Number of poles to use, will be rounded up to the next multiply of four", "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); 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 static void
@ -280,12 +210,6 @@ gst_audio_cheb_band_init (GstAudioChebBand * filter,
filter->type = 1; filter->type = 1;
filter->poles = 4; filter->poles = 4;
filter->ripple = 0.25; 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 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 static void
generate_coefficients (GstAudioChebBand * filter) 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) { if (GST_AUDIO_FILTER (filter)->format.rate == 0) {
filter->num_a = 1; gdouble *a = g_new0 (gdouble, 1);
filter->a = g_new0 (gdouble, 1);
filter->a[0] = 1.0; a[0] = 1.0;
filter->num_b = 0; gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
filter->channels = g_new0 (GstAudioChebBandChannelCtx, channels); (filter), a, 1, NULL, 0);
GST_LOG_OBJECT (filter, "rate was not set yet"); GST_LOG_OBJECT (filter, "rate was not set yet");
return; return;
} }
filter->have_coeffs = TRUE;
if (filter->upper_frequency <= filter->lower_frequency) { if (filter->upper_frequency <= filter->lower_frequency) {
filter->num_a = 1; gdouble *a = g_new0 (gdouble, 1);
filter->a = g_new0 (gdouble, 1);
filter->a[0] = (filter->mode == MODE_BAND_PASS) ? 0.0 : 1.0; a[0] = (filter->mode == MODE_BAND_PASS) ? 0.0 : 1.0;
filter->num_b = 0; gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
filter->channels = g_new0 (GstAudioChebBandChannelCtx, channels); (filter), a, 1, NULL, 0);
GST_LOG_OBJECT (filter, "frequency band had no or negative dimension"); GST_LOG_OBJECT (filter, "frequency band had no or negative dimension");
return; return;
} }
@ -586,18 +438,8 @@ generate_coefficients (GstAudioChebBand * filter)
gdouble *a, *b; gdouble *a, *b;
gint i, p; gint i, p;
filter->num_a = np + 1; a = g_new0 (gdouble, np + 5);
filter->a = a = g_new0 (gdouble, np + 5); b = 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);
}
/* Calculate transfer function coefficients */ /* Calculate transfer function coefficients */
a[4] = 1.0; a[4] = 1.0;
@ -645,8 +487,12 @@ generate_coefficients (GstAudioChebBand * filter)
if (filter->mode == MODE_BAND_REJECT) { if (filter->mode == MODE_BAND_REJECT) {
/* gain is sqrt(H(0)*H(0.5)) */ /* gain is sqrt(H(0)*H(0.5)) */
gdouble gain1 = calculate_gain (a, b, np, np, 1.0, 0.0); gdouble gain1 =
gdouble gain2 = calculate_gain (a, b, np, np, -1.0, 0.0); 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); gain1 = sqrt (gain1 * gain2);
@ -664,13 +510,18 @@ generate_coefficients (GstAudioChebBand * filter)
GST_AUDIO_FILTER (filter)->format.rate); GST_AUDIO_FILTER (filter)->format.rate);
gdouble w0 = (w2 + w1) / 2.0; gdouble w0 = (w2 + w1) / 2.0;
gdouble zr = cos (w0), zi = sin (w0); 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++) { for (i = 0; i <= np; i++) {
a[i] /= gain; 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, GST_LOG_OBJECT (filter,
"Generated IIR coefficients for the Chebyshev filter"); "Generated IIR coefficients for the Chebyshev filter");
GST_LOG_OBJECT (filter, GST_LOG_OBJECT (filter,
@ -680,7 +531,8 @@ generate_coefficients (GstAudioChebBand * filter)
filter->upper_frequency, filter->ripple); filter->upper_frequency, filter->ripple);
GST_LOG_OBJECT (filter, "%.2f dB gain @ 0Hz", 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 = gdouble w1 =
2.0 * M_PI * (filter->lower_frequency / 2.0 * M_PI * (filter->lower_frequency /
@ -694,21 +546,23 @@ generate_coefficients (GstAudioChebBand * filter)
zr = cos (w1); zr = cos (w1);
zi = sin (w1); zi = sin (w1);
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz", 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,
(int) filter->lower_frequency); b, np + 1, zr, zi)), (int) filter->lower_frequency);
zr = cos (w0); zr = cos (w0);
zi = sin (w0); zi = sin (w0);
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz", 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)); (int) ((filter->lower_frequency + filter->upper_frequency) / 2.0));
zr = cos (w2); zr = cos (w2);
zi = sin (w2); zi = sin (w2);
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz", 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,
(int) filter->upper_frequency); b, np + 1, zr, zi)), (int) filter->upper_frequency);
} }
GST_LOG_OBJECT (filter, "%.2f dB gain @ %dHz", 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); 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) { switch (prop_id) {
case PROP_MODE: case PROP_MODE:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->mode = g_value_get_enum (value); filter->mode = g_value_get_enum (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_TYPE: case PROP_TYPE:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->type = g_value_get_int (value); filter->type = g_value_get_int (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_LOWER_FREQUENCY: case PROP_LOWER_FREQUENCY:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->lower_frequency = g_value_get_float (value); filter->lower_frequency = g_value_get_float (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_UPPER_FREQUENCY: case PROP_UPPER_FREQUENCY:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->upper_frequency = g_value_get_float (value); filter->upper_frequency = g_value_get_float (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_RIPPLE: case PROP_RIPPLE:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->ripple = g_value_get_float (value); filter->ripple = g_value_get_float (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_POLES: case PROP_POLES:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->poles = GST_ROUND_UP_4 (g_value_get_int (value)); filter->poles = GST_ROUND_UP_4 (g_value_get_int (value));
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 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) gst_audio_cheb_band_setup (GstAudioFilter * base, GstRingBufferSpec * format)
{ {
GstAudioChebBand *filter = GST_AUDIO_CHEB_BAND (base); GstAudioChebBand *filter = GST_AUDIO_CHEB_BAND (base);
gboolean ret = TRUE;
if (format->width == 32) generate_coefficients (filter);
filter->process = (GstAudioChebBandProcessFunc)
process_32;
else if (format->width == 64)
filter->process = (GstAudioChebBandProcessFunc)
process_64;
else
ret = FALSE;
filter->have_coeffs = FALSE; return GST_AUDIO_FILTER_CLASS (parent_class)->setup (base, format);
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;
} }

View file

@ -1,6 +1,6 @@
/* /*
* GStreamer * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -26,6 +26,8 @@
#include <gst/audio/audio.h> #include <gst/audio/audio.h>
#include <gst/audio/gstaudiofilter.h> #include <gst/audio/gstaudiofilter.h>
#include "audiofxbaseiirfilter.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define GST_TYPE_AUDIO_CHEB_BAND (gst_audio_cheb_band_get_type()) #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)) #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 _GstAudioChebBand GstAudioChebBand;
typedef struct _GstAudioChebBandClass GstAudioChebBandClass; 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 struct _GstAudioChebBand
{ {
GstAudioFilter audiofilter; GstAudioFXBaseIIRFilter parent;
gint mode; gint mode;
gint type; gint type;
@ -56,21 +48,11 @@ struct _GstAudioChebBand
gfloat lower_frequency; gfloat lower_frequency;
gfloat upper_frequency; gfloat upper_frequency;
gfloat ripple; gfloat ripple;
/* < private > */
GstAudioChebBandProcessFunc process;
gboolean have_coeffs;
gdouble *a;
gint num_a;
gdouble *b;
gint num_b;
GstAudioChebBandChannelCtx *channels;
}; };
struct _GstAudioChebBandClass struct _GstAudioChebBandClass
{ {
GstAudioFilterClass parent; GstAudioFXBaseIIRFilterClass parent;
}; };
GType gst_audio_cheb_band_get_type (void); GType gst_audio_cheb_band_get_type (void);

View file

@ -1,6 +1,6 @@
/* /*
* GStreamer * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -88,19 +88,6 @@
#define GST_CAT_DEFAULT gst_audio_cheb_limit_debug #define GST_CAT_DEFAULT gst_audio_cheb_limit_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); 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 enum
{ {
PROP_0, PROP_0,
@ -111,18 +98,12 @@ enum
PROP_POLES 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) \ #define DEBUG_INIT(bla) \
GST_DEBUG_CATEGORY_INIT (gst_audio_cheb_limit_debug, "audiocheblimit", 0, "audiocheblimit element"); GST_DEBUG_CATEGORY_INIT (gst_audio_cheb_limit_debug, "audiocheblimit", 0, "audiocheblimit element");
GST_BOILERPLATE_FULL (GstAudioChebLimit, 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, static void gst_audio_cheb_limit_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec); 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, static gboolean gst_audio_cheb_limit_setup (GstAudioFilter * filter,
GstRingBufferSpec * format); 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 enum
{ {
@ -172,80 +145,42 @@ static void
gst_audio_cheb_limit_base_init (gpointer klass) gst_audio_cheb_limit_base_init (gpointer klass)
{ {
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
GstCaps *caps;
gst_element_class_set_details (element_class, &element_details); gst_element_class_set_details_simple (element_class,
"Low pass & high pass filter",
caps = gst_caps_from_string (ALLOWED_CAPS); "Filter/Effect/Audio",
gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass), "Chebyshev low pass and high pass filter",
caps); "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
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);
} }
static void static void
gst_audio_cheb_limit_class_init (GstAudioChebLimitClass * klass) gst_audio_cheb_limit_class_init (GstAudioChebLimitClass * klass)
{ {
GObjectClass *gobject_class; GObjectClass *gobject_class = (GObjectClass *) klass;
GstBaseTransformClass *trans_class; GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
GstAudioFilterClass *filter_class;
gobject_class = (GObjectClass *) klass;
trans_class = (GstBaseTransformClass *) klass;
filter_class = (GstAudioFilterClass *) klass;
gobject_class->set_property = gst_audio_cheb_limit_set_property; gobject_class->set_property = gst_audio_cheb_limit_set_property;
gobject_class->get_property = gst_audio_cheb_limit_get_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_object_class_install_property (gobject_class, PROP_MODE,
g_param_spec_enum ("mode", "Mode", g_param_spec_enum ("mode", "Mode",
"Low pass or high pass mode", "Low pass or high pass mode",
GST_TYPE_AUDIO_CHEBYSHEV_FREQ_LIMIT_MODE, MODE_LOW_PASS, 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_object_class_install_property (gobject_class, PROP_TYPE,
g_param_spec_int ("type", "Type", "Type of the chebychev filter", 1, 2, 1, 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 /* FIXME: Don't use the complete possible range but restrict the upper boundary
* so automatically generated UIs can use a slider without */ * so automatically generated UIs can use a slider without */
g_object_class_install_property (gobject_class, PROP_CUTOFF, g_object_class_install_property (gobject_class, PROP_CUTOFF,
g_param_spec_float ("cutoff", "Cutoff", "Cut off frequency (Hz)", 0.0, 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_object_class_install_property (gobject_class, PROP_RIPPLE,
g_param_spec_float ("ripple", "Ripple", "Amount of ripple (dB)", 0.0, 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 /* 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 * 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_object_class_install_property (gobject_class, PROP_POLES,
g_param_spec_int ("poles", "Poles", g_param_spec_int ("poles", "Poles",
"Number of poles to use, will be rounded up to the next even number", "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); 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 static void
@ -270,12 +203,6 @@ gst_audio_cheb_limit_init (GstAudioChebLimit * filter,
filter->type = 1; filter->type = 1;
filter->poles = 4; filter->poles = 4;
filter->ripple = 0.25; 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 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 static void
generate_coefficients (GstAudioChebLimit * filter) 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) { if (GST_AUDIO_FILTER (filter)->format.rate == 0) {
filter->num_a = 1; gdouble *a = g_new0 (gdouble, 1);
filter->a = g_new0 (gdouble, 1);
filter->a[0] = 1.0; a[0] = 1.0;
filter->num_b = 0; gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels); (filter), a, 1, NULL, 0);
GST_LOG_OBJECT (filter, "rate was not set yet"); GST_LOG_OBJECT (filter, "rate was not set yet");
return; return;
} }
filter->have_coeffs = TRUE;
if (filter->cutoff >= GST_AUDIO_FILTER (filter)->format.rate / 2.0) { if (filter->cutoff >= GST_AUDIO_FILTER (filter)->format.rate / 2.0) {
filter->num_a = 1; gdouble *a = g_new0 (gdouble, 1);
filter->a = g_new0 (gdouble, 1);
filter->a[0] = (filter->mode == MODE_LOW_PASS) ? 1.0 : 0.0; a[0] = (filter->mode == MODE_LOW_PASS) ? 1.0 : 0.0;
filter->num_b = 0; gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels); (filter), a, 1, NULL, 0);
GST_LOG_OBJECT (filter, "cutoff was higher than nyquist frequency"); GST_LOG_OBJECT (filter, "cutoff was higher than nyquist frequency");
return; return;
} else if (filter->cutoff <= 0.0) { } else if (filter->cutoff <= 0.0) {
filter->num_a = 1; gdouble *a = g_new0 (gdouble, 1);
filter->a = g_new0 (gdouble, 1);
filter->a[0] = (filter->mode == MODE_LOW_PASS) ? 0.0 : 1.0; a[0] = (filter->mode == MODE_LOW_PASS) ? 0.0 : 1.0;
filter->num_b = 0; gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER
filter->channels = g_new0 (GstAudioChebLimitChannelCtx, channels); (filter), a, 1, NULL, 0);
GST_LOG_OBJECT (filter, "cutoff is lower than zero"); GST_LOG_OBJECT (filter, "cutoff is lower than zero");
return; return;
} }
@ -533,18 +388,8 @@ generate_coefficients (GstAudioChebLimit * filter)
gdouble *a, *b; gdouble *a, *b;
gint i, p; gint i, p;
filter->num_a = np + 1; a = g_new0 (gdouble, np + 3);
filter->a = a = g_new0 (gdouble, np + 3); b = 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);
}
/* Calculate transfer function coefficients */ /* Calculate transfer function coefficients */
a[2] = 1.0; a[2] = 1.0;
@ -587,15 +432,22 @@ generate_coefficients (GstAudioChebLimit * filter)
gdouble gain; gdouble gain;
if (filter->mode == MODE_LOW_PASS) 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 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++) { for (i = 0; i <= np; i++) {
a[i] /= gain; 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, GST_LOG_OBJECT (filter,
"Generated IIR coefficients for the Chebyshev filter"); "Generated IIR coefficients for the Chebyshev filter");
GST_LOG_OBJECT (filter, GST_LOG_OBJECT (filter,
@ -603,7 +455,8 @@ generate_coefficients (GstAudioChebLimit * filter)
(filter->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass", (filter->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass",
filter->type, filter->poles, filter->cutoff, filter->ripple); filter->type, filter->poles, filter->cutoff, filter->ripple);
GST_LOG_OBJECT (filter, "%.2f dB gain @ 0 Hz", 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 #ifndef GST_DISABLE_GST_DEBUG
{ {
@ -613,13 +466,14 @@ generate_coefficients (GstAudioChebLimit * filter)
gdouble zr = cos (wc), zi = sin (wc); gdouble zr = cos (wc), zi = sin (wc);
GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz", GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz",
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,
(int) filter->cutoff); b, np + 1, zr, zi)), (int) filter->cutoff);
} }
#endif #endif
GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz", 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); 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) { switch (prop_id) {
case PROP_MODE: case PROP_MODE:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->mode = g_value_get_enum (value); filter->mode = g_value_get_enum (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_TYPE: case PROP_TYPE:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->type = g_value_get_int (value); filter->type = g_value_get_int (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_CUTOFF: case PROP_CUTOFF:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->cutoff = g_value_get_float (value); filter->cutoff = g_value_get_float (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_RIPPLE: case PROP_RIPPLE:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->ripple = g_value_get_float (value); filter->ripple = g_value_get_float (value);
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
case PROP_POLES: case PROP_POLES:
GST_BASE_TRANSFORM_LOCK (filter); GST_OBJECT_LOCK (filter);
filter->poles = GST_ROUND_UP_2 (g_value_get_int (value)); filter->poles = GST_ROUND_UP_2 (g_value_get_int (value));
generate_coefficients (filter); generate_coefficients (filter);
GST_BASE_TRANSFORM_UNLOCK (filter); GST_OBJECT_UNLOCK (filter);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 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) gst_audio_cheb_limit_setup (GstAudioFilter * base, GstRingBufferSpec * format)
{ {
GstAudioChebLimit *filter = GST_AUDIO_CHEB_LIMIT (base); GstAudioChebLimit *filter = GST_AUDIO_CHEB_LIMIT (base);
gboolean ret = TRUE;
if (format->width == 32) generate_coefficients (filter);
filter->process = (GstAudioChebLimitProcessFunc)
process_32;
else if (format->width == 64)
filter->process = (GstAudioChebLimitProcessFunc)
process_64;
else
ret = FALSE;
filter->have_coeffs = FALSE; return GST_AUDIO_FILTER_CLASS (parent_class)->setup (base, format);
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;
} }

View file

@ -1,6 +1,6 @@
/* /*
* GStreamer * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -26,53 +26,38 @@
#include <gst/audio/audio.h> #include <gst/audio/audio.h>
#include <gst/audio/gstaudiofilter.h> #include <gst/audio/gstaudiofilter.h>
#include "audiofxbaseiirfilter.h"
G_BEGIN_DECLS G_BEGIN_DECLS
#define GST_TYPE_AUDIO_CHEB_LIMIT (gst_audio_cheb_limit_get_type()) #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_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_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_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_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)) #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 _GstAudioChebLimit GstAudioChebLimit;
typedef struct _GstAudioChebLimitClass GstAudioChebLimitClass; 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 struct _GstAudioChebLimit
{ {
GstAudioFilter audiofilter; GstAudioFXBaseIIRFilter parent;
gint mode; gint mode;
gint type; gint type;
gint poles; gint poles;
gfloat cutoff; gfloat cutoff;
gfloat ripple; gfloat ripple;
/* < private > */
GstAudioChebLimitProcessFunc process;
gboolean have_coeffs;
gdouble *a;
gint num_a;
gdouble *b;
gint num_b;
GstAudioChebLimitChannelCtx *channels;
}; };
struct _GstAudioChebLimitClass struct _GstAudioChebLimitClass
{ {
GstAudioFilterClass parent; GstAudioFXBaseIIRFilterClass parent;
}; };
GType gst_audio_cheb_limit_get_type (void); GType gst_audio_cheb_limit_get_type (void);
G_END_DECLS G_END_DECLS
#endif /* __GST_AUDIO_CHEB_LIMIT_H__ */ #endif /* __GST_AUDIO_CHEB_LIMIT_H__ */

View 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;
}

View 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__ */