diff options
author | Sebastian Dröge <slomo@circular-chaos.org> | 2009-01-05 10:13:29 +0000 |
---|---|---|
committer | Sebastian Dröge <slomo@circular-chaos.org> | 2009-01-05 10:13:29 +0000 |
commit | 4c7c4c00dacff24aaef55b46a8bc31687d833ca7 (patch) | |
tree | faf0ffeb8a97888d8132e2c03ca89dd7b34d155b | |
parent | 17bb67f873a0bd3c4e631ed510a15799c253fc18 (diff) |
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.
-rw-r--r-- | ChangeLog | 28 | ||||
-rw-r--r-- | gst/audiofx/Makefile.am | 2 | ||||
-rw-r--r-- | gst/audiofx/audiochebband.c | 400 | ||||
-rw-r--r-- | gst/audiofx/audiochebband.h | 28 | ||||
-rw-r--r-- | gst/audiofx/audiocheblimit.c | 385 | ||||
-rw-r--r-- | gst/audiofx/audiocheblimit.h | 31 | ||||
-rw-r--r-- | gst/audiofx/audiofxbaseiirfilter.c | 396 | ||||
-rw-r--r-- | gst/audiofx/audiofxbaseiirfilter.h | 77 |
8 files changed, 648 insertions, 699 deletions
@@ -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 diff --git a/gst/audiofx/Makefile.am b/gst/audiofx/Makefile.am index 364e827a..ac6439bb 100644 --- a/gst/audiofx/Makefile.am +++ b/gst/audiofx/Makefile.am @@ -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 \ diff --git a/gst/audiofx/audiochebband.c b/gst/audiofx/audiochebband.c index 40f35d20..36aeb8de 100644 --- a/gst/audiofx/audiochebband.c +++ b/gst/audiofx/audiochebband.c @@ -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; - - 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; -} + generate_coefficients (filter); -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); } diff --git a/gst/audiofx/audiochebband.h b/gst/audiofx/audiochebband.h index ece011ab..fae1a0c6 100644 --- a/gst/audiofx/audiochebband.h +++ b/gst/audiofx/audiochebband.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 @@ -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); diff --git a/gst/audiofx/audiocheblimit.c b/gst/audiofx/audiocheblimit.c index e4da0ca1..4d8d311d 100644 --- a/gst/audiofx/audiocheblimit.c +++ b/gst/audiofx/audiocheblimit.c @@ -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); - } + if (GST_AUDIO_FILTER (filter)->format.rate == 0) { + gdouble *a = g_new0 (gdouble, 1); - g_free (filter->channels); - filter->channels = NULL; - } + a[0] = 1.0; + gst_audio_fx_base_iir_filter_set_coefficients (GST_AUDIO_FX_BASE_IIR_FILTER + (filter), a, 1, NULL, 0); - 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); 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; - - 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)); + generate_coefficients (filter); - 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); } diff --git a/gst/audiofx/audiocheblimit.h b/gst/audiofx/audiocheblimit.h index c2fe6c2e..491f7494 100644 --- a/gst/audiofx/audiocheblimit.h +++ b/gst/audiofx/audiocheblimit.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 @@ -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__ */ diff --git a/gst/audiofx/audiofxbaseiirfilter.c b/gst/audiofx/audiofxbaseiirfilter.c new file mode 100644 index 00000000..29cb2440 --- /dev/null +++ b/gst/audiofx/audiofxbaseiirfilter.c @@ -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; +} diff --git a/gst/audiofx/audiofxbaseiirfilter.h b/gst/audiofx/audiofxbaseiirfilter.h new file mode 100644 index 00000000..0534343c --- /dev/null +++ b/gst/audiofx/audiofxbaseiirfilter.h @@ -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__ */ |