gst/audiofx/: Use generator macros for the process functions for the different sample...
[platform/upstream/gst-plugins-good.git] / gst / audiofx / audiocheblimit.c
1 /* 
2  * GStreamer
3  * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /* 
22  * Chebyshev type 1 filter design based on
23  * "The Scientist and Engineer's Guide to DSP", Chapter 20.
24  * http://www.dspguide.com/
25  *
26  * For type 2 and Chebyshev filters in general read
27  * http://en.wikipedia.org/wiki/Chebyshev_filter
28  *
29  */
30
31 /**
32  * SECTION:element-audiochebyshevfreqlimit
33  * @short_description: Chebyshev low pass and high pass filter
34  *
35  * <refsect2>
36  * <para>
37  * Attenuates all frequencies above the cutoff frequency (low-pass) or all frequencies below the
38  * cutoff frequency (high-pass). The number of poles and the ripple parameter control the rolloff.
39  * </para>
40  * <para>
41  * For type 1 the ripple parameter specifies how much ripple in dB is allowed in the passband, i.e.
42  * some frequencies in the passband will be amplified by that value. A higher ripple value will allow
43  * a faster rolloff.
44  * </para>
45  * <para>
46  * For type 2 the ripple parameter specifies the stopband attenuation. In the stopband the gain will
47  * be at most this value. A lower ripple value will allow a faster rolloff.
48  * </para>
49  * <para>
50  * As a special case, a Chebyshev type 1 filter with no ripple is a Butterworth filter.
51  * </para>
52  * <para><note>
53  * Be warned that a too large number of poles can produce noise. The most poles are possible with
54  * a cutoff frequency at a quarter of the sampling rate.
55  * </note></para>
56  * <title>Example launch line</title>
57  * <para>
58  * <programlisting>
59  * gst-launch audiotestsrc freq=1500 ! audioconvert ! audiochebyshevfreqlimit mode=low-pass cutoff=1000 poles=4 ! audioconvert ! alsasink
60  * gst-launch filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audiochebyshevfreqlimit mode=high-pass cutoff=400 ripple=0.2 ! audioconvert ! alsasink
61  * gst-launch audiotestsrc wave=white-noise ! audioconvert ! audiochebyshevfreqlimit mode=low-pass cutoff=800 type=2 ! audioconvert ! alsasink
62  * </programlisting>
63  * </para>
64  * </refsect2>
65  */
66
67 #ifdef HAVE_CONFIG_H
68 #include "config.h"
69 #endif
70
71 #include <gst/gst.h>
72 #include <gst/base/gstbasetransform.h>
73 #include <gst/audio/audio.h>
74 #include <gst/audio/gstaudiofilter.h>
75 #include <gst/controller/gstcontroller.h>
76
77 #include <math.h>
78
79 #include "audiochebyshevfreqlimit.h"
80
81 #define GST_CAT_DEFAULT gst_audio_chebyshev_freq_limit_debug
82 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
83
84 static const GstElementDetails element_details =
85 GST_ELEMENT_DETAILS ("AudioChebyshevFreqLimit",
86     "Filter/Effect/Audio",
87     "Chebyshev low pass and high pass filter",
88     "Sebastian Dröge <slomo@circular-chaos.org>");
89
90 /* Filter signals and args */
91 enum
92 {
93   /* FILL ME */
94   LAST_SIGNAL
95 };
96
97 enum
98 {
99   PROP_0,
100   PROP_MODE,
101   PROP_TYPE,
102   PROP_CUTOFF,
103   PROP_RIPPLE,
104   PROP_POLES
105 };
106
107 #define ALLOWED_CAPS \
108     "audio/x-raw-float,"                                              \
109     " width = (int) { 32, 64 }, "                                     \
110     " endianness = (int) BYTE_ORDER,"                                 \
111     " rate = (int) [ 1, MAX ],"                                       \
112     " channels = (int) [ 1, MAX ]"
113
114 #define DEBUG_INIT(bla) \
115   GST_DEBUG_CATEGORY_INIT (gst_audio_chebyshev_freq_limit_debug, "audiochebyshevfreqlimit", 0, "audiochebyshevfreqlimit element");
116
117 GST_BOILERPLATE_FULL (GstAudioChebyshevFreqLimit,
118     gst_audio_chebyshev_freq_limit, GstAudioFilter, GST_TYPE_AUDIO_FILTER,
119     DEBUG_INIT);
120
121 static void gst_audio_chebyshev_freq_limit_set_property (GObject * object,
122     guint prop_id, const GValue * value, GParamSpec * pspec);
123 static void gst_audio_chebyshev_freq_limit_get_property (GObject * object,
124     guint prop_id, GValue * value, GParamSpec * pspec);
125
126 static gboolean gst_audio_chebyshev_freq_limit_setup (GstAudioFilter * filter,
127     GstRingBufferSpec * format);
128 static GstFlowReturn
129 gst_audio_chebyshev_freq_limit_transform_ip (GstBaseTransform * base,
130     GstBuffer * buf);
131 static gboolean gst_audio_chebyshev_freq_limit_start (GstBaseTransform * base);
132
133 static void process_64 (GstAudioChebyshevFreqLimit * filter,
134     gdouble * data, guint num_samples);
135 static void process_32 (GstAudioChebyshevFreqLimit * filter,
136     gfloat * data, guint num_samples);
137
138 enum
139 {
140   MODE_LOW_PASS = 0,
141   MODE_HIGH_PASS
142 };
143
144 #define GST_TYPE_AUDIO_CHEBYSHEV_FREQ_LIMIT_MODE (gst_audio_chebyshev_freq_limit_mode_get_type ())
145 static GType
146 gst_audio_chebyshev_freq_limit_mode_get_type (void)
147 {
148   static GType gtype = 0;
149
150   if (gtype == 0) {
151     static const GEnumValue values[] = {
152       {MODE_LOW_PASS, "Low pass (default)",
153           "low-pass"},
154       {MODE_HIGH_PASS, "High pass",
155           "high-pass"},
156       {0, NULL, NULL}
157     };
158
159     gtype = g_enum_register_static ("GstAudioChebyshevFreqLimitMode", values);
160   }
161   return gtype;
162 }
163
164 /* GObject vmethod implementations */
165
166 static void
167 gst_audio_chebyshev_freq_limit_base_init (gpointer klass)
168 {
169   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
170   GstCaps *caps;
171
172   gst_element_class_set_details (element_class, &element_details);
173
174   caps = gst_caps_from_string (ALLOWED_CAPS);
175   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
176       caps);
177   gst_caps_unref (caps);
178 }
179
180 static void
181 gst_audio_chebyshev_freq_limit_dispose (GObject * object)
182 {
183   GstAudioChebyshevFreqLimit *filter = GST_AUDIO_CHEBYSHEV_FREQ_LIMIT (object);
184
185   if (filter->a) {
186     g_free (filter->a);
187     filter->a = NULL;
188   }
189
190   if (filter->b) {
191     g_free (filter->b);
192     filter->b = NULL;
193   }
194
195   if (filter->channels) {
196     GstAudioChebyshevFreqLimitChannelCtx *ctx;
197     gint i, channels = GST_AUDIO_FILTER (filter)->format.channels;
198
199     for (i = 0; i < channels; i++) {
200       ctx = &filter->channels[i];
201       g_free (ctx->x);
202       g_free (ctx->y);
203     }
204
205     g_free (filter->channels);
206     filter->channels = NULL;
207   }
208
209   G_OBJECT_CLASS (parent_class)->dispose (object);
210 }
211
212 static void
213 gst_audio_chebyshev_freq_limit_class_init (GstAudioChebyshevFreqLimitClass *
214     klass)
215 {
216   GObjectClass *gobject_class;
217   GstBaseTransformClass *trans_class;
218   GstAudioFilterClass *filter_class;
219
220   gobject_class = (GObjectClass *) klass;
221   trans_class = (GstBaseTransformClass *) klass;
222   filter_class = (GstAudioFilterClass *) klass;
223
224   gobject_class->set_property = gst_audio_chebyshev_freq_limit_set_property;
225   gobject_class->get_property = gst_audio_chebyshev_freq_limit_get_property;
226   gobject_class->dispose = gst_audio_chebyshev_freq_limit_dispose;
227
228   g_object_class_install_property (gobject_class, PROP_MODE,
229       g_param_spec_enum ("mode", "Mode",
230           "Low pass or high pass mode",
231           GST_TYPE_AUDIO_CHEBYSHEV_FREQ_LIMIT_MODE, MODE_LOW_PASS,
232           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
233   g_object_class_install_property (gobject_class, PROP_TYPE,
234       g_param_spec_int ("type", "Type", "Type of the chebychev filter", 1, 2, 1,
235           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
236
237   /* FIXME: Don't use the complete possible range but restrict the upper boundary
238    * so automatically generated UIs can use a slider without */
239   g_object_class_install_property (gobject_class, PROP_CUTOFF,
240       g_param_spec_float ("cutoff", "Cutoff", "Cut off frequency (Hz)", 0.0,
241           100000.0, 0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
242   g_object_class_install_property (gobject_class, PROP_RIPPLE,
243       g_param_spec_float ("ripple", "Ripple", "Amount of ripple (dB)", 0.0,
244           200.0, 0.25, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
245
246   /* FIXME: What to do about this upper boundary? With a cutoff frequency of
247    * rate/4 32 poles are completely possible, with a cutoff frequency very low
248    * or very high 16 poles already produces only noise */
249   g_object_class_install_property (gobject_class, PROP_POLES,
250       g_param_spec_int ("poles", "Poles",
251           "Number of poles to use, will be rounded up to the next even number",
252           2, 32, 4, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
253
254   filter_class->setup =
255       GST_DEBUG_FUNCPTR (gst_audio_chebyshev_freq_limit_setup);
256   trans_class->transform_ip =
257       GST_DEBUG_FUNCPTR (gst_audio_chebyshev_freq_limit_transform_ip);
258   trans_class->start = GST_DEBUG_FUNCPTR (gst_audio_chebyshev_freq_limit_start);
259 }
260
261 static void
262 gst_audio_chebyshev_freq_limit_init (GstAudioChebyshevFreqLimit * filter,
263     GstAudioChebyshevFreqLimitClass * klass)
264 {
265   filter->cutoff = 0.0;
266   filter->mode = MODE_LOW_PASS;
267   filter->type = 1;
268   filter->poles = 4;
269   filter->ripple = 0.25;
270   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
271
272   filter->have_coeffs = FALSE;
273   filter->num_a = 0;
274   filter->num_b = 0;
275   filter->channels = NULL;
276 }
277
278 static void
279 generate_biquad_coefficients (GstAudioChebyshevFreqLimit * filter,
280     gint p, gdouble * a0, gdouble * a1, gdouble * a2,
281     gdouble * b1, gdouble * b2)
282 {
283   gint np = filter->poles;
284   gdouble ripple = filter->ripple;
285
286   /* pole location in s-plane */
287   gdouble rp, ip;
288
289   /* zero location in s-plane */
290   gdouble rz = 0.0, iz = 0.0;
291
292   /* transfer function coefficients for the z-plane */
293   gdouble x0, x1, x2, y1, y2;
294   gint type = filter->type;
295
296   /* Calculate pole location for lowpass at frequency 1 */
297   {
298     gdouble angle = (M_PI / 2.0) * (2.0 * p - 1) / np;
299
300     rp = -sin (angle);
301     ip = cos (angle);
302   }
303
304   /* If we allow ripple, move the pole from the unit
305    * circle to an ellipse and keep cutoff at frequency 1 */
306   if (ripple > 0 && type == 1) {
307     gdouble es, vx;
308
309     es = sqrt (pow (10.0, ripple / 10.0) - 1.0);
310
311     vx = (1.0 / np) * asinh (1.0 / es);
312     rp = rp * sinh (vx);
313     ip = ip * cosh (vx);
314   } else if (type == 2) {
315     gdouble es, vx;
316
317     es = sqrt (pow (10.0, ripple / 10.0) - 1.0);
318     vx = (1.0 / np) * asinh (es);
319     rp = rp * sinh (vx);
320     ip = ip * cosh (vx);
321   }
322
323   /* Calculate inverse of the pole location to convert from
324    * type I to type II */
325   if (type == 2) {
326     gdouble mag2 = rp * rp + ip * ip;
327
328     rp /= mag2;
329     ip /= mag2;
330   }
331
332   /* Calculate zero location for frequency 1 on the
333    * unit circle for type 2 */
334   if (type == 2) {
335     gdouble angle = M_PI / (np * 2.0) + ((p - 1) * M_PI) / (np);
336     gdouble mag2;
337
338     rz = 0.0;
339     iz = cos (angle);
340     mag2 = rz * rz + iz * iz;
341     rz /= mag2;
342     iz /= mag2;
343   }
344
345   /* Convert from s-domain to z-domain by
346    * using the bilinear Z-transform, i.e.
347    * substitute s by (2/t)*((z-1)/(z+1))
348    * with t = 2 * tan(0.5).
349    */
350   if (type == 1) {
351     gdouble t, m, d;
352
353     t = 2.0 * tan (0.5);
354     m = rp * rp + ip * ip;
355     d = 4.0 - 4.0 * rp * t + m * t * t;
356
357     x0 = (t * t) / d;
358     x1 = 2.0 * x0;
359     x2 = x0;
360     y1 = (8.0 - 2.0 * m * t * t) / d;
361     y2 = (-4.0 - 4.0 * rp * t - m * t * t) / d;
362   } else {
363     gdouble t, m, d;
364
365     t = 2.0 * tan (0.5);
366     m = rp * rp + ip * ip;
367     d = 4.0 - 4.0 * rp * t + m * t * t;
368
369     x0 = (t * t * iz * iz + 4.0) / d;
370     x1 = (-8.0 + 2.0 * iz * iz * t * t) / d;
371     x2 = x0;
372     y1 = (8.0 - 2.0 * m * t * t) / d;
373     y2 = (-4.0 - 4.0 * rp * t - m * t * t) / d;
374   }
375
376   /* Convert from lowpass at frequency 1 to either lowpass
377    * or highpass.
378    *
379    * For lowpass substitute z^(-1) with:
380    *  -1
381    * z   - k
382    * ------------
383    *          -1
384    * 1 - k * z
385    *
386    * k = sin((1-w)/2) / sin((1+w)/2)
387    *
388    * For highpass substitute z^(-1) with:
389    *
390    *   -1
391    * -z   - k
392    * ------------
393    *          -1
394    * 1 + k * z
395    *
396    * k = -cos((1+w)/2) / cos((1-w)/2)
397    *
398    */
399   {
400     gdouble k, d;
401     gdouble omega =
402         2.0 * M_PI * (filter->cutoff / GST_AUDIO_FILTER (filter)->format.rate);
403
404     if (filter->mode == MODE_LOW_PASS)
405       k = sin ((1.0 - omega) / 2.0) / sin ((1.0 + omega) / 2.0);
406     else
407       k = -cos ((omega + 1.0) / 2.0) / cos ((omega - 1.0) / 2.0);
408
409     d = 1.0 + y1 * k - y2 * k * k;
410     *a0 = (x0 + k * (-x1 + k * x2)) / d;
411     *a1 = (x1 + k * k * x1 - 2.0 * k * (x0 + x2)) / d;
412     *a2 = (x0 * k * k - x1 * k + x2) / d;
413     *b1 = (2.0 * k + y1 + y1 * k * k - 2.0 * y2 * k) / d;
414     *b2 = (-k * k - y1 * k + y2) / d;
415
416     if (filter->mode == MODE_HIGH_PASS) {
417       *a1 = -*a1;
418       *b1 = -*b1;
419     }
420   }
421 }
422
423 /* Evaluate the transfer function that corresponds to the IIR
424  * coefficients at zr + zi*I and return the magnitude */
425 static gdouble
426 calculate_gain (gdouble * a, gdouble * b, gint num_a, gint num_b, gdouble zr,
427     gdouble zi)
428 {
429   gdouble sum_ar, sum_ai;
430   gdouble sum_br, sum_bi;
431   gdouble gain_r, gain_i;
432
433   gdouble sum_r_old;
434   gdouble sum_i_old;
435
436   gint i;
437
438   sum_ar = 0.0;
439   sum_ai = 0.0;
440   for (i = num_a; i >= 0; i--) {
441     sum_r_old = sum_ar;
442     sum_i_old = sum_ai;
443
444     sum_ar = (sum_r_old * zr - sum_i_old * zi) + a[i];
445     sum_ai = (sum_r_old * zi + sum_i_old * zr) + 0.0;
446   }
447
448   sum_br = 0.0;
449   sum_bi = 0.0;
450   for (i = num_b; i >= 0; i--) {
451     sum_r_old = sum_br;
452     sum_i_old = sum_bi;
453
454     sum_br = (sum_r_old * zr - sum_i_old * zi) - b[i];
455     sum_bi = (sum_r_old * zi + sum_i_old * zr) - 0.0;
456   }
457   sum_br += 1.0;
458   sum_bi += 0.0;
459
460   gain_r =
461       (sum_ar * sum_br + sum_ai * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
462   gain_i =
463       (sum_ai * sum_br - sum_ar * sum_bi) / (sum_br * sum_br + sum_bi * sum_bi);
464
465   return (sqrt (gain_r * gain_r + gain_i * gain_i));
466 }
467
468 static void
469 generate_coefficients (GstAudioChebyshevFreqLimit * filter)
470 {
471   gint channels = GST_AUDIO_FILTER (filter)->format.channels;
472
473   if (filter->a) {
474     g_free (filter->a);
475     filter->a = NULL;
476   }
477
478   if (filter->b) {
479     g_free (filter->b);
480     filter->b = NULL;
481   }
482
483   if (filter->channels) {
484     GstAudioChebyshevFreqLimitChannelCtx *ctx;
485     gint i;
486
487     for (i = 0; i < channels; i++) {
488       ctx = &filter->channels[i];
489       g_free (ctx->x);
490       g_free (ctx->y);
491     }
492
493     g_free (filter->channels);
494     filter->channels = NULL;
495   }
496
497   if (GST_AUDIO_FILTER (filter)->format.rate == 0) {
498     filter->num_a = 1;
499     filter->a = g_new0 (gdouble, 1);
500     filter->a[0] = 1.0;
501     filter->num_b = 0;
502     filter->channels = g_new0 (GstAudioChebyshevFreqLimitChannelCtx, channels);
503     GST_LOG_OBJECT (filter, "rate was not set yet");
504     return;
505   }
506
507   filter->have_coeffs = TRUE;
508
509   if (filter->cutoff >= GST_AUDIO_FILTER (filter)->format.rate / 2.0) {
510     filter->num_a = 1;
511     filter->a = g_new0 (gdouble, 1);
512     filter->a[0] = (filter->mode == MODE_LOW_PASS) ? 1.0 : 0.0;
513     filter->num_b = 0;
514     filter->channels = g_new0 (GstAudioChebyshevFreqLimitChannelCtx, channels);
515     GST_LOG_OBJECT (filter, "cutoff was higher than nyquist frequency");
516     return;
517   } else if (filter->cutoff <= 0.0) {
518     filter->num_a = 1;
519     filter->a = g_new0 (gdouble, 1);
520     filter->a[0] = (filter->mode == MODE_LOW_PASS) ? 0.0 : 1.0;
521     filter->num_b = 0;
522     filter->channels = g_new0 (GstAudioChebyshevFreqLimitChannelCtx, channels);
523     GST_LOG_OBJECT (filter, "cutoff is lower than zero");
524     return;
525   }
526
527   /* Calculate coefficients for the chebyshev filter */
528   {
529     gint np = filter->poles;
530     gdouble *a, *b;
531     gint i, p;
532
533     filter->num_a = np + 1;
534     filter->a = a = g_new0 (gdouble, np + 3);
535     filter->num_b = np + 1;
536     filter->b = b = g_new0 (gdouble, np + 3);
537
538     filter->channels = g_new0 (GstAudioChebyshevFreqLimitChannelCtx, channels);
539     for (i = 0; i < channels; i++) {
540       GstAudioChebyshevFreqLimitChannelCtx *ctx = &filter->channels[i];
541
542       ctx->x = g_new0 (gdouble, np + 1);
543       ctx->y = g_new0 (gdouble, np + 1);
544     }
545
546     /* Calculate transfer function coefficients */
547     a[2] = 1.0;
548     b[2] = 1.0;
549
550     for (p = 1; p <= np / 2; p++) {
551       gdouble a0, a1, a2, b1, b2;
552       gdouble *ta = g_new0 (gdouble, np + 3);
553       gdouble *tb = g_new0 (gdouble, np + 3);
554
555       generate_biquad_coefficients (filter, p, &a0, &a1, &a2, &b1, &b2);
556
557       memcpy (ta, a, sizeof (gdouble) * (np + 3));
558       memcpy (tb, b, sizeof (gdouble) * (np + 3));
559
560       /* add the new coefficients for the new two poles
561        * to the cascade by multiplication of the transfer
562        * functions */
563       for (i = 2; i < np + 3; i++) {
564         a[i] = a0 * ta[i] + a1 * ta[i - 1] + a2 * ta[i - 2];
565         b[i] = tb[i] - b1 * tb[i - 1] - b2 * tb[i - 2];
566       }
567       g_free (ta);
568       g_free (tb);
569     }
570
571     /* Move coefficients to the beginning of the array
572      * and multiply the b coefficients with -1 to move from
573      * the transfer function's coefficients to the difference
574      * equation's coefficients */
575     b[2] = 0.0;
576     for (i = 0; i <= np; i++) {
577       a[i] = a[i + 2];
578       b[i] = -b[i + 2];
579     }
580
581     /* Normalize to unity gain at frequency 0 for lowpass
582      * and frequency 0.5 for highpass */
583     {
584       gdouble gain;
585
586       if (filter->mode == MODE_LOW_PASS)
587         gain = calculate_gain (a, b, np, np, 1.0, 0.0);
588       else
589         gain = calculate_gain (a, b, np, np, -1.0, 0.0);
590
591       for (i = 0; i <= np; i++) {
592         a[i] /= gain;
593       }
594     }
595
596     GST_LOG_OBJECT (filter,
597         "Generated IIR coefficients for the Chebyshev filter");
598     GST_LOG_OBJECT (filter,
599         "mode: %s, type: %d, poles: %d, cutoff: %.2f Hz, ripple: %.2f dB",
600         (filter->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass",
601         filter->type, filter->poles, filter->cutoff, filter->ripple);
602     GST_LOG_OBJECT (filter, "%.2f dB gain @ 0 Hz",
603         20.0 * log10 (calculate_gain (a, b, np, np, 1.0, 0.0)));
604     {
605       gdouble wc =
606           2.0 * M_PI * (filter->cutoff /
607           GST_AUDIO_FILTER (filter)->format.rate);
608       gdouble zr = cos (wc), zi = sin (wc);
609
610       GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz",
611           20.0 * log10 (calculate_gain (a, b, np, np, zr, zi)),
612           (int) filter->cutoff);
613     }
614     GST_LOG_OBJECT (filter, "%.2f dB gain @ %d Hz",
615         20.0 * log10 (calculate_gain (a, b, np, np, -1.0, 0.0)),
616         GST_AUDIO_FILTER (filter)->format.rate / 2);
617   }
618 }
619
620 static void
621 gst_audio_chebyshev_freq_limit_set_property (GObject * object, guint prop_id,
622     const GValue * value, GParamSpec * pspec)
623 {
624   GstAudioChebyshevFreqLimit *filter = GST_AUDIO_CHEBYSHEV_FREQ_LIMIT (object);
625
626   switch (prop_id) {
627     case PROP_MODE:
628       GST_BASE_TRANSFORM_LOCK (filter);
629       filter->mode = g_value_get_enum (value);
630       generate_coefficients (filter);
631       GST_BASE_TRANSFORM_UNLOCK (filter);
632       break;
633     case PROP_TYPE:
634       GST_BASE_TRANSFORM_LOCK (filter);
635       filter->type = g_value_get_int (value);
636       generate_coefficients (filter);
637       GST_BASE_TRANSFORM_UNLOCK (filter);
638       break;
639     case PROP_CUTOFF:
640       GST_BASE_TRANSFORM_LOCK (filter);
641       filter->cutoff = g_value_get_float (value);
642       generate_coefficients (filter);
643       GST_BASE_TRANSFORM_UNLOCK (filter);
644       break;
645     case PROP_RIPPLE:
646       GST_BASE_TRANSFORM_LOCK (filter);
647       filter->ripple = g_value_get_float (value);
648       generate_coefficients (filter);
649       GST_BASE_TRANSFORM_UNLOCK (filter);
650       break;
651     case PROP_POLES:
652       GST_BASE_TRANSFORM_LOCK (filter);
653       filter->poles = GST_ROUND_UP_2 (g_value_get_int (value));
654       generate_coefficients (filter);
655       GST_BASE_TRANSFORM_UNLOCK (filter);
656       break;
657     default:
658       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
659       break;
660   }
661 }
662
663 static void
664 gst_audio_chebyshev_freq_limit_get_property (GObject * object, guint prop_id,
665     GValue * value, GParamSpec * pspec)
666 {
667   GstAudioChebyshevFreqLimit *filter = GST_AUDIO_CHEBYSHEV_FREQ_LIMIT (object);
668
669   switch (prop_id) {
670     case PROP_MODE:
671       g_value_set_enum (value, filter->mode);
672       break;
673     case PROP_TYPE:
674       g_value_set_int (value, filter->type);
675       break;
676     case PROP_CUTOFF:
677       g_value_set_float (value, filter->cutoff);
678       break;
679     case PROP_RIPPLE:
680       g_value_set_float (value, filter->ripple);
681       break;
682     case PROP_POLES:
683       g_value_set_int (value, filter->poles);
684       break;
685     default:
686       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
687       break;
688   }
689 }
690
691 /* GstAudioFilter vmethod implementations */
692
693 static gboolean
694 gst_audio_chebyshev_freq_limit_setup (GstAudioFilter * base,
695     GstRingBufferSpec * format)
696 {
697   GstAudioChebyshevFreqLimit *filter = GST_AUDIO_CHEBYSHEV_FREQ_LIMIT (base);
698   gboolean ret = TRUE;
699
700   if (format->width == 32)
701     filter->process = (GstAudioChebyshevFreqLimitProcessFunc)
702         process_32;
703   else if (format->width == 64)
704     filter->process = (GstAudioChebyshevFreqLimitProcessFunc)
705         process_64;
706   else
707     ret = FALSE;
708
709   filter->have_coeffs = FALSE;
710
711   return ret;
712 }
713
714 static inline gdouble
715 process (GstAudioChebyshevFreqLimit * filter,
716     GstAudioChebyshevFreqLimitChannelCtx * ctx, gdouble x0)
717 {
718   gdouble val = filter->a[0] * x0;
719   gint i, j;
720
721   for (i = 1, j = ctx->x_pos; i < filter->num_a; i++) {
722     val += filter->a[i] * ctx->x[j];
723     j--;
724     if (j < 0)
725       j = filter->num_a - 1;
726   }
727
728   for (i = 1, j = ctx->y_pos; i < filter->num_b; i++) {
729     val += filter->b[i] * ctx->y[j];
730     j--;
731     if (j < 0)
732       j = filter->num_b - 1;
733   }
734
735   if (ctx->x) {
736     ctx->x_pos++;
737     if (ctx->x_pos > filter->num_a - 1)
738       ctx->x_pos = 0;
739     ctx->x[ctx->x_pos] = x0;
740   }
741
742   if (ctx->y) {
743     ctx->y_pos++;
744     if (ctx->y_pos > filter->num_b - 1)
745       ctx->y_pos = 0;
746
747     ctx->y[ctx->y_pos] = val;
748   }
749
750   return val;
751 }
752
753 #define DEFINE_PROCESS_FUNC(width,ctype) \
754 static void \
755 process_##width (GstAudioChebyshevFreqLimit * filter, \
756     g##ctype * data, guint num_samples) \
757 { \
758   gint i, j, channels = GST_AUDIO_FILTER (filter)->format.channels; \
759   gdouble val; \
760   \
761   for (i = 0; i < num_samples / channels; i++) { \
762     for (j = 0; j < channels; j++) { \
763       val = process (filter, &filter->channels[j], *data); \
764       *data++ = val; \
765     } \
766   } \
767 }
768
769 DEFINE_PROCESS_FUNC (32, float);
770 DEFINE_PROCESS_FUNC (64, double);
771
772 #undef DEFINE_PROCESS_FUNC
773
774 /* GstBaseTransform vmethod implementations */
775 static GstFlowReturn
776 gst_audio_chebyshev_freq_limit_transform_ip (GstBaseTransform * base,
777     GstBuffer * buf)
778 {
779   GstAudioChebyshevFreqLimit *filter = GST_AUDIO_CHEBYSHEV_FREQ_LIMIT (base);
780   guint num_samples =
781       GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);
782
783   if (!gst_buffer_is_writable (buf))
784     return GST_FLOW_OK;
785
786   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
787     gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (buf));
788
789   if (!filter->have_coeffs)
790     generate_coefficients (filter);
791
792   filter->process (filter, GST_BUFFER_DATA (buf), num_samples);
793
794   return GST_FLOW_OK;
795 }
796
797
798 static gboolean
799 gst_audio_chebyshev_freq_limit_start (GstBaseTransform * base)
800 {
801   GstAudioChebyshevFreqLimit *filter = GST_AUDIO_CHEBYSHEV_FREQ_LIMIT (base);
802   gint channels = GST_AUDIO_FILTER (filter)->format.channels;
803   GstAudioChebyshevFreqLimitChannelCtx *ctx;
804   gint i;
805
806   /* Reset the history of input and output values if
807    * already existing */
808   if (channels && filter->channels) {
809     for (i = 0; i < channels; i++) {
810       ctx = &filter->channels[i];
811       if (ctx->x)
812         memset (ctx->x, 0, (filter->poles + 1) * sizeof (gdouble));
813       if (ctx->y)
814         memset (ctx->y, 0, (filter->poles + 1) * sizeof (gdouble));
815     }
816   }
817   return TRUE;
818 }