gst/filter/: Use GstAudioFilter as base class and don't leak the memory of the filter...
[platform/upstream/gst-plugins-good.git] / gst / audiofx / audiowsincband.c
1 /* -*- c-basic-offset: 2 -*-
2  * 
3  * GStreamer
4  * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
5  *               2006 Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  * 
22  * 
23  * this windowed sinc filter is taken from the freely downloadable DSP book,
24  * "The Scientist and Engineer's Guide to Digital Signal Processing",
25  * chapter 16
26  * available at http://www.dspguide.com/
27  *
28  * FIXME:
29  * - this filter is totally unoptimized !
30  * - this might be improved upon with bytestream
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <string.h>
38 #include <math.h>
39 #include <gst/gst.h>
40 #include <gst/audio/gstaudiofilter.h>
41 #include <gst/controller/gstcontroller.h>
42
43 #include "gstbpwsinc.h"
44
45 #define GST_CAT_DEFAULT gst_bpwsinc_debug
46 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
47
48 static const GstElementDetails bpwsinc_details =
49 GST_ELEMENT_DETAILS ("Band-pass Windowed sinc filter",
50     "Filter/Effect/Audio",
51     "Band-pass Windowed sinc filter",
52     "Thomas <thomas@apestaart.org>, "
53     "Steven W. Smith, "
54     "Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>");
55
56 /* Filter signals and args */
57 enum
58 {
59   /* FILL ME */
60   LAST_SIGNAL
61 };
62
63 enum
64 {
65   PROP_0,
66   PROP_LENGTH,
67   PROP_LOWER_FREQUENCY,
68   PROP_UPPER_FREQUENCY
69 };
70
71 #define ALLOWED_CAPS \
72     "audio/x-raw-float,"                                              \
73     " width = (int) 32, "                                             \
74     " endianness = (int) BYTE_ORDER,"                                 \
75     " rate = (int) [ 1, MAX ],"                                       \
76     " channels = (int) [ 1, MAX ]"
77
78 #define DEBUG_INIT(bla) \
79   GST_DEBUG_CATEGORY_INIT (gst_bpwsinc_debug, "bpwsinc", 0, "Band-pass Windowed sinc filter plugin");
80
81 GST_BOILERPLATE_FULL (GstBPWSinc, gst_bpwsinc, GstAudioFilter,
82     GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
83
84 static void bpwsinc_set_property (GObject * object, guint prop_id,
85     const GValue * value, GParamSpec * pspec);
86 static void bpwsinc_get_property (GObject * object, guint prop_id,
87     GValue * value, GParamSpec * pspec);
88
89 static GstFlowReturn bpwsinc_transform_ip (GstBaseTransform * base,
90     GstBuffer * outbuf);
91 static gboolean bpwsinc_setup (GstAudioFilter * base,
92     GstRingBufferSpec * format);
93
94 /* Element class */
95
96 static void
97 gst_bpwsinc_dispose (GObject * object)
98 {
99   GstBPWSinc *this = GST_BPWSINC (object);
100
101   if (this->residue) {
102     g_free (this->residue);
103     this->residue = NULL;
104   }
105
106   if (this->kernel) {
107     g_free (this->kernel);
108     this->kernel = NULL;
109   }
110
111   G_OBJECT_CLASS (parent_class)->dispose (object);
112 }
113
114 static void
115 gst_bpwsinc_base_init (gpointer g_class)
116 {
117   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
118   GstCaps *caps;
119
120   gst_element_class_set_details (element_class, &bpwsinc_details);
121
122   caps = gst_caps_from_string (ALLOWED_CAPS);
123   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
124       caps);
125   gst_caps_unref (caps);
126 }
127
128 static void
129 gst_bpwsinc_class_init (GstBPWSincClass * klass)
130 {
131   GObjectClass *gobject_class;
132   GstBaseTransformClass *trans_class;
133
134   gobject_class = (GObjectClass *) klass;
135   trans_class = (GstBaseTransformClass *) klass;
136
137   gobject_class->set_property = bpwsinc_set_property;
138   gobject_class->get_property = bpwsinc_get_property;
139   gobject_class->dispose = gst_bpwsinc_dispose;
140
141   g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY,
142       g_param_spec_double ("lower-frequency", "Lower Frequency",
143           "Cut-off lower frequency (relative to sample rate)",
144           0.0, 0.5, 0, G_PARAM_READWRITE));
145   g_object_class_install_property (gobject_class, PROP_UPPER_FREQUENCY,
146       g_param_spec_double ("upper-frequency", "Upper Frequency",
147           "Cut-off upper frequency (relative to sample rate)",
148           0.0, 0.5, 0, G_PARAM_READWRITE));
149   g_object_class_install_property (gobject_class, PROP_LENGTH,
150       g_param_spec_int ("length", "Length",
151           "N such that the filter length = 2N + 1",
152           1, G_MAXINT, 1, G_PARAM_READWRITE));
153
154   trans_class->transform_ip = GST_DEBUG_FUNCPTR (bpwsinc_transform_ip);
155   GST_AUDIO_FILTER_CLASS (klass)->setup = GST_DEBUG_FUNCPTR (bpwsinc_setup);
156 }
157
158 static void
159 gst_bpwsinc_init (GstBPWSinc * this, GstBPWSincClass * g_class)
160 {
161   this->wing_size = 50;
162   this->lower_frequency = 0.25;
163   this->upper_frequency = 0.3;
164   this->kernel = NULL;
165   this->residue = NULL;
166 }
167
168
169 /* GstAudioFilter vmethod implementations */
170
171 /* get notified of caps and plug in the correct process function */
172 static gboolean
173 bpwsinc_setup (GstAudioFilter * base, GstRingBufferSpec * format)
174 {
175   int i = 0;
176   double sum = 0.0;
177   int len = 0;
178   double *kernel_lp, *kernel_hp;
179   GstBPWSinc *this = GST_BPWSINC (base);
180
181   len = this->wing_size;
182   /* fill the lp kernel
183    * FIXME: refactor to own function, this is not caps related
184    */
185   GST_DEBUG ("bpwsinc: initializing LP kernel of length %d with cut-off %f",
186       len * 2 + 1, this->lower_frequency);
187   kernel_lp = (double *) g_malloc (sizeof (double) * (2 * len + 1));
188   for (i = 0; i <= len * 2; ++i) {
189     if (i == len)
190       kernel_lp[i] = 2 * M_PI * this->lower_frequency;
191     else
192       kernel_lp[i] = sin (2 * M_PI * this->lower_frequency * (i - len))
193           / (i - len);
194     /* Blackman windowing */
195     kernel_lp[i] *= (0.42 - 0.5 * cos (M_PI * i / len)
196         + 0.08 * cos (2 * M_PI * i / len));
197   }
198
199   /* normalize for unity gain at DC
200    * FIXME: sure this is not supposed to be quadratic ? */
201   sum = 0.0;
202   for (i = 0; i <= len * 2; ++i)
203     sum += kernel_lp[i];
204   for (i = 0; i <= len * 2; ++i)
205     kernel_lp[i] /= sum;
206
207   /* fill the hp kernel */
208   GST_DEBUG ("bpwsinc: initializing HP kernel of length %d with cut-off %f",
209       len * 2 + 1, this->upper_frequency);
210   kernel_hp = (double *) g_malloc (sizeof (double) * (2 * len + 1));
211   for (i = 0; i <= len * 2; ++i) {
212     if (i == len)
213       kernel_hp[i] = 2 * M_PI * this->upper_frequency;
214     else
215       kernel_hp[i] = sin (2 * M_PI * this->upper_frequency * (i - len))
216           / (i - len);
217     /* Blackman windowing */
218     kernel_hp[i] *= (0.42 - 0.5 * cos (M_PI * i / len)
219         + 0.08 * cos (2 * M_PI * i / len));
220   }
221
222   /* normalize for unity gain at DC
223    * FIXME: sure this is not supposed to be quadratic ? */
224   sum = 0.0;
225   for (i = 0; i <= len * 2; ++i)
226     sum += kernel_hp[i];
227   for (i = 0; i <= len * 2; ++i)
228     kernel_hp[i] /= sum;
229
230   /* combine the two thiss */
231   if (this->kernel)
232     g_free (this->kernel);
233   this->kernel = (double *) g_malloc (sizeof (double) * (2 * len + 1));
234
235   for (i = 0; i <= len * 2; ++i)
236     this->kernel[i] = kernel_lp[i] + kernel_hp[i];
237
238   /* do spectral inversion to go from band reject to bandpass */
239   for (i = 0; i <= len * 2; ++i)
240     this->kernel[i] = -this->kernel[i];
241   this->kernel[len] += 1;
242
243   /* free the helper kernels */
244   g_free (kernel_lp);
245   g_free (kernel_hp);
246
247   /* set up the residue memory space */
248   if (this->residue)
249     g_free (this->residue);
250
251   this->residue = (gfloat *) g_malloc (sizeof (gfloat) * (len * 2 + 1));
252   for (i = 0; i <= len * 2; ++i)
253     this->residue[i] = 0.0;
254
255   return TRUE;
256 }
257
258 /* GstBaseTransform vmethod implementations */
259
260 static GstFlowReturn
261 bpwsinc_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
262 {
263   GstBPWSinc *this = GST_BPWSINC (base);
264   GstClockTime timestamp;
265
266   gfloat *src;
267   gfloat *input;
268   int residue_samples;
269   gint input_samples;
270   gint total_samples;
271   int i, j;
272
273   /* don't process data in passthrough-mode */
274   if (gst_base_transform_is_passthrough (base))
275     return GST_FLOW_OK;
276
277   /* FIXME: subdivide GST_BUFFER_SIZE into small chunks for smooth fades */
278   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
279
280   if (GST_CLOCK_TIME_IS_VALID (timestamp))
281     gst_object_sync_values (G_OBJECT (this), timestamp);
282
283   /* FIXME: out of laziness, we copy the left-over bit from last buffer
284    * together with the incoming buffer to a new buffer to make the loop
285    * easy; this could be a lot more optimized though
286    * to make amends we keep the incoming buffer around and write our
287    * output samples there */
288
289   src = (gfloat *) GST_BUFFER_DATA (outbuf);
290   residue_samples = this->wing_size * 2 + 1;
291   input_samples = GST_BUFFER_SIZE (outbuf) / sizeof (gfloat);
292   total_samples = residue_samples + input_samples;
293
294   input = (gfloat *) g_malloc (sizeof (gfloat) * total_samples);
295
296   /* copy the left-over bit */
297   memcpy (input, this->residue, sizeof (gfloat) * residue_samples);
298
299   /* copy the new buffer */
300   memcpy (&input[residue_samples], src, sizeof (gfloat) * input_samples);
301   /* copy the tail of the current input buffer to the residue */
302   memcpy (this->residue, &src[input_samples - residue_samples],
303       sizeof (gfloat) * residue_samples);
304
305   /* convolution */
306   /* since we copied the previous set of samples we needed before the actual
307    * input data, we need to add the filter length to our indices for input */
308   for (i = 0; i < input_samples; ++i) {
309     src[i] = 0.0;
310     for (j = 0; j < residue_samples; ++j)
311       src[i] += input[i - j + residue_samples] * this->kernel[j];
312   }
313
314   g_free (input);
315
316   return GST_FLOW_OK;
317 }
318
319 static void
320 bpwsinc_set_property (GObject * object, guint prop_id, const GValue * value,
321     GParamSpec * pspec)
322 {
323   GstBPWSinc *this = GST_BPWSINC (object);
324
325   g_return_if_fail (GST_IS_BPWSINC (this));
326
327   switch (prop_id) {
328     case PROP_LENGTH:
329       this->wing_size = g_value_get_int (value);
330       break;
331     case PROP_LOWER_FREQUENCY:
332       this->lower_frequency = g_value_get_double (value);
333       break;
334     case PROP_UPPER_FREQUENCY:
335       this->upper_frequency = g_value_get_double (value);
336       break;
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340   }
341 }
342
343 static void
344 bpwsinc_get_property (GObject * object, guint prop_id, GValue * value,
345     GParamSpec * pspec)
346 {
347   GstBPWSinc *this = GST_BPWSINC (object);
348
349   switch (prop_id) {
350     case PROP_LENGTH:
351       g_value_set_int (value, this->wing_size);
352       break;
353     case PROP_LOWER_FREQUENCY:
354       g_value_set_double (value, this->lower_frequency);
355       break;
356     case PROP_UPPER_FREQUENCY:
357       g_value_set_double (value, this->upper_frequency);
358       break;
359     default:
360       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
361       break;
362   }
363 }