gst/filter/gstbpwsinc.c: "this" is a C++ keyword, use "self" instead.
[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  * TODO:  - Implement the convolution in place, probably only makes sense
29  *          when using FFT convolution as currently the convolution itself
30  *          is probably the bottleneck
31  *        - Implement a band reject mode (spectral inversion)
32  *        - Allow choosing between different windows (blackman, hanning, ...)
33  *        - Specify filter length instead of 2*N+1
34  * FIXME: - Doesn't work at all with >1 channels
35  *        - Is bandreject, not bandpass
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <string.h>
43 #include <math.h>
44 #include <gst/gst.h>
45 #include <gst/audio/gstaudiofilter.h>
46 #include <gst/controller/gstcontroller.h>
47
48 #include "gstbpwsinc.h"
49
50 #define GST_CAT_DEFAULT gst_bpwsinc_debug
51 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
52
53 static const GstElementDetails bpwsinc_details =
54 GST_ELEMENT_DETAILS ("Band-pass Windowed sinc filter",
55     "Filter/Effect/Audio",
56     "Band-pass Windowed sinc filter",
57     "Thomas <thomas@apestaart.org>, "
58     "Steven W. Smith, "
59     "Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>");
60
61 /* Filter signals and args */
62 enum
63 {
64   /* FILL ME */
65   LAST_SIGNAL
66 };
67
68 enum
69 {
70   PROP_0,
71   PROP_LENGTH,
72   PROP_LOWER_FREQUENCY,
73   PROP_UPPER_FREQUENCY
74 };
75
76 #define ALLOWED_CAPS \
77     "audio/x-raw-float,"                                              \
78     " width = (int) 32, "                                             \
79     " endianness = (int) BYTE_ORDER,"                                 \
80     " rate = (int) [ 1, MAX ],"                                       \
81     " channels = (int) [ 1, MAX ]"
82
83 #define DEBUG_INIT(bla) \
84   GST_DEBUG_CATEGORY_INIT (gst_bpwsinc_debug, "bpwsinc", 0, "Band-pass Windowed sinc filter plugin");
85
86 GST_BOILERPLATE_FULL (GstBPWSinc, gst_bpwsinc, GstAudioFilter,
87     GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
88
89 static void bpwsinc_set_property (GObject * object, guint prop_id,
90     const GValue * value, GParamSpec * pspec);
91 static void bpwsinc_get_property (GObject * object, guint prop_id,
92     GValue * value, GParamSpec * pspec);
93
94 static GstFlowReturn bpwsinc_transform_ip (GstBaseTransform * base,
95     GstBuffer * outbuf);
96 static gboolean bpwsinc_setup (GstAudioFilter * base,
97     GstRingBufferSpec * format);
98
99 /* Element class */
100
101 static void
102 gst_bpwsinc_dispose (GObject * object)
103 {
104   GstBPWSinc *self = GST_BPWSINC (object);
105
106   if (self->residue) {
107     g_free (self->residue);
108     self->residue = NULL;
109   }
110
111   if (self->kernel) {
112     g_free (self->kernel);
113     self->kernel = NULL;
114   }
115
116   G_OBJECT_CLASS (parent_class)->dispose (object);
117 }
118
119 static void
120 gst_bpwsinc_base_init (gpointer g_class)
121 {
122   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
123   GstCaps *caps;
124
125   gst_element_class_set_details (element_class, &bpwsinc_details);
126
127   caps = gst_caps_from_string (ALLOWED_CAPS);
128   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
129       caps);
130   gst_caps_unref (caps);
131 }
132
133 static void
134 gst_bpwsinc_class_init (GstBPWSincClass * klass)
135 {
136   GObjectClass *gobject_class;
137   GstBaseTransformClass *trans_class;
138
139   gobject_class = (GObjectClass *) klass;
140   trans_class = (GstBaseTransformClass *) klass;
141
142   gobject_class->set_property = bpwsinc_set_property;
143   gobject_class->get_property = bpwsinc_get_property;
144   gobject_class->dispose = gst_bpwsinc_dispose;
145
146   g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY,
147       g_param_spec_double ("lower-frequency", "Lower Frequency",
148           "Cut-off lower frequency (relative to sample rate)",
149           0.0, 0.5, 0, G_PARAM_READWRITE));
150   g_object_class_install_property (gobject_class, PROP_UPPER_FREQUENCY,
151       g_param_spec_double ("upper-frequency", "Upper Frequency",
152           "Cut-off upper frequency (relative to sample rate)",
153           0.0, 0.5, 0, G_PARAM_READWRITE));
154   g_object_class_install_property (gobject_class, PROP_LENGTH,
155       g_param_spec_int ("length", "Length",
156           "N such that the filter length = 2N + 1",
157           1, G_MAXINT, 1, G_PARAM_READWRITE));
158
159   trans_class->transform_ip = GST_DEBUG_FUNCPTR (bpwsinc_transform_ip);
160   GST_AUDIO_FILTER_CLASS (klass)->setup = GST_DEBUG_FUNCPTR (bpwsinc_setup);
161 }
162
163 static void
164 gst_bpwsinc_init (GstBPWSinc * self, GstBPWSincClass * g_class)
165 {
166   self->wing_size = 50;
167   self->lower_frequency = 0.25;
168   self->upper_frequency = 0.3;
169   self->kernel = NULL;
170   self->residue = NULL;
171 }
172
173
174 /* GstAudioFilter vmethod implementations */
175
176 /* get notified of caps and plug in the correct process function */
177 static gboolean
178 bpwsinc_setup (GstAudioFilter * base, GstRingBufferSpec * format)
179 {
180   int i = 0;
181   double sum = 0.0;
182   int len = 0;
183   double *kernel_lp, *kernel_hp;
184   GstBPWSinc *self = GST_BPWSINC (base);
185
186   len = self->wing_size;
187   /* fill the lp kernel
188    * FIXME: refactor to own function, this is not caps related
189    */
190   GST_DEBUG ("bpwsinc: initializing LP kernel of length %d with cut-off %f",
191       len * 2 + 1, self->lower_frequency);
192   kernel_lp = (double *) g_malloc (sizeof (double) * (2 * len + 1));
193   for (i = 0; i <= len * 2; ++i) {
194     if (i == len)
195       kernel_lp[i] = 2 * M_PI * self->lower_frequency;
196     else
197       kernel_lp[i] = sin (2 * M_PI * self->lower_frequency * (i - len))
198           / (i - len);
199     /* Blackman windowing */
200     kernel_lp[i] *= (0.42 - 0.5 * cos (M_PI * i / len)
201         + 0.08 * cos (2 * M_PI * i / len));
202   }
203
204   /* normalize for unity gain at DC */
205   sum = 0.0;
206   for (i = 0; i <= len * 2; ++i)
207     sum += kernel_lp[i];
208   for (i = 0; i <= len * 2; ++i)
209     kernel_lp[i] /= sum;
210
211   /* fill the hp kernel */
212   GST_DEBUG ("bpwsinc: initializing HP kernel of length %d with cut-off %f",
213       len * 2 + 1, self->upper_frequency);
214   kernel_hp = (double *) g_malloc (sizeof (double) * (2 * len + 1));
215   for (i = 0; i <= len * 2; ++i) {
216     if (i == len)
217       kernel_hp[i] = 2 * M_PI * self->upper_frequency;
218     else
219       kernel_hp[i] = sin (2 * M_PI * self->upper_frequency * (i - len))
220           / (i - len);
221     /* Blackman windowing */
222     kernel_hp[i] *= (0.42 - 0.5 * cos (M_PI * i / len)
223         + 0.08 * cos (2 * M_PI * i / len));
224   }
225
226   /* normalize for unity gain at DC */
227   sum = 0.0;
228   for (i = 0; i <= len * 2; ++i)
229     sum += kernel_hp[i];
230   for (i = 0; i <= len * 2; ++i)
231     kernel_hp[i] /= sum;
232
233   /* combine the two kernels */
234   if (self->kernel)
235     g_free (self->kernel);
236   self->kernel = (double *) g_malloc (sizeof (double) * (2 * len + 1));
237
238   for (i = 0; i <= len * 2; ++i)
239     self->kernel[i] = kernel_lp[i] + kernel_hp[i];
240
241   /* do spectral inversion to go from band reject to bandpass */
242   for (i = 0; i <= len * 2; ++i)
243     self->kernel[i] = -self->kernel[i];
244   self->kernel[len] += 1;
245
246   /* free the helper kernels */
247   g_free (kernel_lp);
248   g_free (kernel_hp);
249
250   /* set up the residue memory space */
251   if (self->residue)
252     g_free (self->residue);
253
254   self->residue = (gfloat *) g_malloc (sizeof (gfloat) * (len * 2 + 1));
255   for (i = 0; i <= len * 2; ++i)
256     self->residue[i] = 0.0;
257
258   return TRUE;
259 }
260
261 /* GstBaseTransform vmethod implementations */
262
263 static GstFlowReturn
264 bpwsinc_transform_ip (GstBaseTransform * base, GstBuffer * outbuf)
265 {
266   GstBPWSinc *self = GST_BPWSINC (base);
267   GstClockTime timestamp;
268
269   gfloat *src;
270   gfloat *input;
271   int residue_samples;
272   gint input_samples;
273   gint total_samples;
274   int i, j;
275
276   /* don't process data in passthrough-mode */
277   if (gst_base_transform_is_passthrough (base))
278     return GST_FLOW_OK;
279
280   /* FIXME: subdivide GST_BUFFER_SIZE into small chunks for smooth fades */
281   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
282
283   if (GST_CLOCK_TIME_IS_VALID (timestamp))
284     gst_object_sync_values (G_OBJECT (self), timestamp);
285
286   /* FIXME: out of laziness, we copy the left-over bit from last buffer
287    * together with the incoming buffer to a new buffer to make the loop
288    * easy; self could be a lot more optimized though
289    * to make amends we keep the incoming buffer around and write our
290    * output samples there */
291
292   src = (gfloat *) GST_BUFFER_DATA (outbuf);
293   residue_samples = self->wing_size * 2 + 1;
294   input_samples = GST_BUFFER_SIZE (outbuf) / sizeof (gfloat);
295   total_samples = residue_samples + input_samples;
296
297   input = (gfloat *) g_malloc (sizeof (gfloat) * total_samples);
298
299   /* copy the left-over bit */
300   memcpy (input, self->residue, sizeof (gfloat) * residue_samples);
301
302   /* copy the new buffer */
303   memcpy (&input[residue_samples], src, sizeof (gfloat) * input_samples);
304   /* copy the tail of the current input buffer to the residue */
305   memcpy (self->residue, &src[input_samples - residue_samples],
306       sizeof (gfloat) * residue_samples);
307
308   /* convolution */
309   /* since we copied the previous set of samples we needed before the actual
310    * input data, we need to add the filter length to our indices for input */
311   for (i = 0; i < input_samples; ++i) {
312     src[i] = 0.0;
313     for (j = 0; j < residue_samples; ++j)
314       src[i] += input[i - j + residue_samples] * self->kernel[j];
315   }
316
317   g_free (input);
318
319   return GST_FLOW_OK;
320 }
321
322 static void
323 bpwsinc_set_property (GObject * object, guint prop_id, const GValue * value,
324     GParamSpec * pspec)
325 {
326   GstBPWSinc *self = GST_BPWSINC (object);
327
328   g_return_if_fail (GST_IS_BPWSINC (self));
329
330   switch (prop_id) {
331     case PROP_LENGTH:
332       self->wing_size = g_value_get_int (value);
333       break;
334     case PROP_LOWER_FREQUENCY:
335       self->lower_frequency = g_value_get_double (value);
336       break;
337     case PROP_UPPER_FREQUENCY:
338       self->upper_frequency = g_value_get_double (value);
339       break;
340     default:
341       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
342       break;
343   }
344 }
345
346 static void
347 bpwsinc_get_property (GObject * object, guint prop_id, GValue * value,
348     GParamSpec * pspec)
349 {
350   GstBPWSinc *self = GST_BPWSINC (object);
351
352   switch (prop_id) {
353     case PROP_LENGTH:
354       g_value_set_int (value, self->wing_size);
355       break;
356     case PROP_LOWER_FREQUENCY:
357       g_value_set_double (value, self->lower_frequency);
358       break;
359     case PROP_UPPER_FREQUENCY:
360       g_value_set_double (value, self->upper_frequency);
361       break;
362     default:
363       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
364       break;
365   }
366 }