664df8709dac92ee58d3958d1bd17e3a6c5c053b
[platform/upstream/gst-plugins-good.git] / gst / audiofx / audiowsinclimit.c
1 /* -*- c-basic-offset: 2 -*-
2  * GStreamer
3  * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
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 /* this windowed sinc filter is taken from the freely downloadable DSP book,
22  * "The Scientist and Engineer's Guide to Digital Signal Processing",
23  * chapter 16
24  * available at http://www.dspguide.com/
25  */
26
27 /* FIXME:
28  * - this filter is totally unoptimized !
29  * - we do not destroy the allocated memory for filters and residue
30  * - this might be improved upon with bytestream
31  */
32
33 #include <gst/gst.h>
34 #include "gstfilter.h"
35 #include <math.h>               /* M_PI */
36 #include <string.h>             /* memmove */
37
38 GstElementDetails gst_lpwsinc_details = {
39   "LPWSinc",
40   "Filter/Audio/Effect",
41   "Low-pass Windowed sinc filter",
42   VERSION,
43   "Thomas <thomas@apestaart.org>",
44   "(C) 2002 Steven W. Smith",
45 };
46
47 enum {
48   /* FILL ME */
49   LAST_SIGNAL
50 };
51
52 enum {
53   ARG_0,
54   ARG_LENGTH,
55   ARG_FREQUENCY,
56 };
57
58 #define GST_TYPE_LPWSINC \
59   (gst_lpwsinc_get_type())
60 #define GST_LPWSINC(obj) \
61       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LPWSINC,GstLPWSinc))
62 #define GST_LPWSINC_CLASS(klass) \
63       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstLPWSinc))
64 #define GST_IS_LPWSINC(obj) \
65       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LPWSINC))
66 #define GST_IS_LPWSINC_CLASS(obj) \
67       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LPWSINC))
68
69 typedef struct _GstLPWSinc GstLPWSinc;
70 typedef struct _GstLPWSincClass GstLPWSincClass;
71
72 struct _GstLPWSinc
73 {
74   GstElement element;
75
76   GstPad *sinkpad, *srcpad;
77
78   double frequency;
79   int wing_size;        /* length of a "wing" of the filter; 
80                            actual length is 2 * wing_size + 1 */
81
82   gfloat *residue;      /* buffer for left-over samples from previous buffer */
83   double *kernel;
84 };
85
86 struct _GstLPWSincClass
87 {
88     GstElementClass parent_class;
89 };
90
91 static void gst_lpwsinc_class_init              (GstLPWSincClass * klass);
92 static void gst_lpwsinc_init                    (GstLPWSinc * filter);
93
94 static void gst_lpwsinc_set_property    (GObject * object, guint prop_id,
95                                          const GValue * value, 
96                                          GParamSpec * pspec);
97 static void gst_lpwsinc_get_property    (GObject * object, guint prop_id,
98                                          GValue * value, GParamSpec * pspec);
99
100 static void gst_lpwsinc_chain           (GstPad * pad, GstBuffer * buf);
101 static GstPadConnectReturn
102        gst_lpwsinc_sink_connect                 (GstPad * pad, GstCaps * caps);
103
104 static GstElementClass *parent_class = NULL;
105 /*static guint gst_lpwsinc_signals[LAST_SIGNAL] = { 0 }; */
106
107 GType gst_lpwsinc_get_type (void)
108 {
109   static GType lpwsinc_type = 0;
110
111   if (!lpwsinc_type) {
112     static const GTypeInfo lpwsinc_info = {
113       sizeof (GstLPWSincClass), NULL, NULL,
114       (GClassInitFunc) gst_lpwsinc_class_init, NULL, NULL,
115       sizeof (GstLPWSinc), 0,
116       (GInstanceInitFunc) gst_lpwsinc_init,
117     };
118
119     lpwsinc_type = g_type_register_static (GST_TYPE_ELEMENT, "GstLPWSinc", 
120                                            &lpwsinc_info, 0);
121   }
122   return lpwsinc_type;
123 }
124
125 static void
126 gst_lpwsinc_class_init (GstLPWSincClass * klass)
127 {
128   GObjectClass *gobject_class;
129   GstElementClass *gstelement_class;
130
131   gobject_class = (GObjectClass *) klass;
132   gstelement_class = (GstElementClass *) klass;
133
134   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
135
136   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FREQUENCY,
137       g_param_spec_double ("frequency", "Frequency", 
138                            "Cut-off Frequency relative to sample rate)", 
139                            0.0, 0.5,
140                            0, G_PARAM_READWRITE));
141   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LENGTH,
142       g_param_spec_int ("length", "Length", 
143                         "N such that the filter length = 2N + 1",
144                            1, G_MAXINT, 
145                            1, G_PARAM_READWRITE));
146
147   gobject_class->set_property = gst_lpwsinc_set_property;
148   gobject_class->get_property = gst_lpwsinc_get_property;
149 }
150
151 static void
152 gst_lpwsinc_init (GstLPWSinc * filter)
153 {
154   filter->sinkpad = gst_pad_new_from_template (gst_filter_sink_factory (), "sink");
155   gst_pad_set_chain_function (filter->sinkpad, gst_lpwsinc_chain);
156   gst_pad_set_connect_function (filter->sinkpad, gst_lpwsinc_sink_connect);
157   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
158
159   filter->srcpad = gst_pad_new_from_template (gst_filter_src_factory (), "src");
160   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
161
162   filter->wing_size = 50;
163   filter->frequency = 0.25;
164   filter->kernel = NULL;
165 }
166
167 static GstPadConnectReturn
168 gst_lpwsinc_sink_connect (GstPad * pad, GstCaps * caps)
169 {
170   int i = 0;
171   double sum = 0.0;
172   int len = 0;
173   GstLPWSinc *filter = GST_LPWSINC (gst_pad_get_parent (pad));
174   GstPadConnectReturn set_retval;
175
176   g_assert (GST_IS_PAD (pad));
177   g_assert (caps != NULL);
178
179   if (!GST_CAPS_IS_FIXED (caps))
180     return GST_PAD_CONNECT_DELAYED;
181
182   set_retval = gst_pad_try_set_caps(filter->srcpad, caps);
183   
184   if (set_retval > 0) 
185   {
186     /* connection works, so init the filter */
187     /* FIXME: remember to free it */
188     /* fill the kernel */
189     g_print ("DEBUG: initing filter kernel\n");
190     len = filter->wing_size;
191     GST_DEBUG (GST_CAT_PLUGIN_INFO, 
192                "lpwsinc: initializing filter kernel of length %d", len * 2 + 1);
193     filter->kernel = (double *) g_malloc (sizeof (double) * (2 * len + 1));
194
195     for (i = 0; i <= len * 2; ++i)
196     {
197       if (i == len)
198         filter->kernel[i] = 2 * M_PI * filter->frequency;
199       else
200         filter->kernel[i] = sin (2 * M_PI * filter->frequency * (i - len)) 
201                           / (i - len);
202       /* windowing */
203       filter->kernel[i] *= (0.54 - 0.46 * cos (M_PI * i / len));
204     }
205
206     /* normalize for unity gain at DC
207      * FIXME: sure this is not supposed to be quadratic ? */
208     for (i = 0; i <= len * 2; ++i) sum += filter->kernel[i];
209     for (i = 0; i <= len * 2; ++i) filter->kernel[i] /= sum;
210
211     /* set up the residue memory space */
212     filter->residue = (gfloat *) g_malloc (sizeof (gfloat) * (len * 2 + 1));
213     for (i = 0; i <= len * 2; ++i) filter->residue[i] = 0.0;
214   }
215
216   return set_retval;
217 }
218
219 static void
220 gst_lpwsinc_chain (GstPad * pad, GstBuffer * buf)
221 {
222   GstLPWSinc *filter;
223   gfloat *src;
224   gfloat *input;
225   gint residue_samples;
226   gint input_samples;
227   gint total_samples;
228   int i, j;
229
230   filter = GST_LPWSINC (gst_pad_get_parent (pad));
231
232   /* FIXME: out of laziness, we copy the left-over bit from last buffer
233    * together with the incoming buffer to a new buffer to make the loop
234    * easy; this could be a lot more optimized though
235    * to make amends we keep the incoming buffer around and write our
236    * output samples there */
237
238   /* get a writable buffer */
239   buf = gst_buffer_copy_on_write (buf);
240
241   src = (gfloat *) GST_BUFFER_DATA (buf);
242   residue_samples = filter->wing_size * 2 + 1;
243   input_samples = GST_BUFFER_SIZE (buf) / sizeof (gfloat);
244   total_samples = residue_samples + input_samples;
245
246   input = (gfloat *) g_malloc (sizeof (gfloat) * total_samples);
247
248   /* copy the left-over bit */
249   memcpy (input, filter->residue, sizeof (gfloat) * residue_samples);
250
251   /* copy the new buffer */
252   memcpy (&input[residue_samples], src, sizeof (gfloat) * input_samples);
253   /* copy the tail of the current input buffer to the residue */
254   memcpy (filter->residue, &src[input_samples - residue_samples],
255           sizeof (gfloat) * residue_samples);
256
257   /* convolution */
258   /* since we copied the previous set of samples we needed before the actual
259    * input data, we need to add the filter length to our indices for input */
260   for (i = 0; i < input_samples; ++i)
261   {
262     src[i] = 0.0;
263     for (j = 0; j < residue_samples; ++j)
264       src[i] += input[i - j + residue_samples] * filter->kernel[j];
265   }
266   
267   g_free (input);
268   gst_pad_push (filter->srcpad, buf);
269 }
270
271 static void
272 gst_lpwsinc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
273 {
274   GstLPWSinc *filter;
275
276   /* it's not null if we got it, but it might not be ours */
277   g_return_if_fail (GST_IS_LPWSINC (object));
278
279   filter = GST_LPWSINC (object);
280
281   switch (prop_id) {
282     case ARG_LENGTH:
283      filter->wing_size = g_value_get_int (value);
284      break; 
285     case ARG_FREQUENCY:
286      filter->frequency = g_value_get_double (value);
287      break; 
288     default:
289       break;
290   }
291 }
292
293 static void
294 gst_lpwsinc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
295 {
296   GstLPWSinc *filter;
297
298   /* it's not null if we got it, but it might not be ours */
299   g_return_if_fail (GST_IS_LPWSINC (object));
300   
301   filter = GST_LPWSINC (object);
302
303   switch (prop_id) {
304     case ARG_LENGTH:
305       g_value_set_int (value, filter->wing_size);
306       break;
307     case ARG_FREQUENCY:
308       g_value_set_double (value, filter->frequency);
309       break;
310     default:
311       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
312       break;
313   }
314
315