diff options
Diffstat (limited to 'gst/audiofx/audiocheblimit.c')
-rw-r--r-- | gst/audiofx/audiocheblimit.c | 385 |
1 files changed, 62 insertions, 323 deletions
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); } |