50d0c0f95f14daa50a81fe666594f876025c17da
[platform/upstream/gst-plugins-good.git] / gst / audiofx / audiowsinclimit.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  *               2007 Sebastian Dröge <slomo@circular-chaos.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  * 
23  * 
24  * this windowed sinc filter is taken from the freely downloadable DSP book,
25  * "The Scientist and Engineer's Guide to Digital Signal Processing",
26  * chapter 16
27  * available at http://www.dspguide.com/
28  *
29  * TODO:  - Implement the convolution in place, probably only makes sense
30  *          when using FFT convolution as currently the convolution itself
31  *          is probably the bottleneck
32  *        - Maybe allow cascading the filter to get a better stopband attenuation.
33  *          Can be done by convolving a filter kernel with itself.
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <string.h>
41 #include <math.h>
42 #include <gst/gst.h>
43 #include <gst/audio/gstaudiofilter.h>
44 #include <gst/controller/gstcontroller.h>
45
46 #include "gstlpwsinc.h"
47
48 #define GST_CAT_DEFAULT gst_lpwsinc_debug
49 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
50
51 static const GstElementDetails lpwsinc_details = GST_ELEMENT_DETAILS ("LPWSinc",
52     "Filter/Effect/Audio",
53     "Low-pass and High-pass Windowed sinc filter",
54     "Thomas <thomas@apestaart.org>, "
55     "Steven W. Smith, "
56     "Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>, "
57     "Sebastian Dröge <slomo@circular-chaos.org>");
58
59 /* Filter signals and args */
60 enum
61 {
62   /* FILL ME */
63   LAST_SIGNAL
64 };
65
66 enum
67 {
68   PROP_0,
69   PROP_LENGTH,
70   PROP_FREQUENCY,
71   PROP_MODE,
72   PROP_WINDOW
73 };
74
75 enum
76 {
77   MODE_LOW_PASS = 0,
78   MODE_HIGH_PASS
79 };
80
81 #define GST_TYPE_LPWSINC_MODE (gst_lpwsinc_mode_get_type ())
82 static GType
83 gst_lpwsinc_mode_get_type (void)
84 {
85   static GType gtype = 0;
86
87   if (gtype == 0) {
88     static const GEnumValue values[] = {
89       {MODE_LOW_PASS, "Low pass (default)",
90           "low-pass"},
91       {MODE_HIGH_PASS, "High pass",
92           "high-pass"},
93       {0, NULL, NULL}
94     };
95
96     gtype = g_enum_register_static ("GstLPWSincMode", values);
97   }
98   return gtype;
99 }
100
101 enum
102 {
103   WINDOW_HAMMING = 0,
104   WINDOW_BLACKMAN
105 };
106
107 #define GST_TYPE_LPWSINC_WINDOW (gst_lpwsinc_window_get_type ())
108 static GType
109 gst_lpwsinc_window_get_type (void)
110 {
111   static GType gtype = 0;
112
113   if (gtype == 0) {
114     static const GEnumValue values[] = {
115       {WINDOW_HAMMING, "Hamming window (default)",
116           "hamming"},
117       {WINDOW_BLACKMAN, "Blackman window",
118           "blackman"},
119       {0, NULL, NULL}
120     };
121
122     gtype = g_enum_register_static ("GstLPWSincWindow", values);
123   }
124   return gtype;
125 }
126
127 #define ALLOWED_CAPS \
128     "audio/x-raw-float, "                                             \
129     " width = (int) { 32, 64 }, "                                     \
130     " endianness = (int) BYTE_ORDER, "                                \
131     " rate = (int) [ 1, MAX ], "                                      \
132     " channels = (int) [ 1, MAX ]"
133
134 #define DEBUG_INIT(bla) \
135   GST_DEBUG_CATEGORY_INIT (gst_lpwsinc_debug, "lpwsinc", 0, "Low-pass and High-pass Windowed sinc filter plugin");
136
137 GST_BOILERPLATE_FULL (GstLPWSinc, gst_lpwsinc, GstAudioFilter,
138     GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
139
140 static void lpwsinc_set_property (GObject * object, guint prop_id,
141     const GValue * value, GParamSpec * pspec);
142 static void lpwsinc_get_property (GObject * object, guint prop_id,
143     GValue * value, GParamSpec * pspec);
144
145 static GstFlowReturn lpwsinc_transform (GstBaseTransform * base,
146     GstBuffer * inbuf, GstBuffer * outbuf);
147 static gboolean lpwsinc_get_unit_size (GstBaseTransform * base, GstCaps * caps,
148     guint * size);
149 static gboolean lpwsinc_start (GstBaseTransform * base);
150 static gboolean lpwsinc_setup (GstAudioFilter * base,
151     GstRingBufferSpec * format);
152
153 /* Element class */
154
155 static void
156 gst_lpwsinc_dispose (GObject * object)
157 {
158   GstLPWSinc *self = GST_LPWSINC (object);
159
160   if (self->residue) {
161     g_free (self->residue);
162     self->residue = NULL;
163   }
164
165   if (self->kernel) {
166     g_free (self->kernel);
167     self->kernel = NULL;
168   }
169
170   G_OBJECT_CLASS (parent_class)->dispose (object);
171 }
172
173 static void
174 gst_lpwsinc_base_init (gpointer g_class)
175 {
176   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
177   GstCaps *caps;
178
179   gst_element_class_set_details (element_class, &lpwsinc_details);
180
181   caps = gst_caps_from_string (ALLOWED_CAPS);
182   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
183       caps);
184   gst_caps_unref (caps);
185 }
186
187 static void
188 gst_lpwsinc_class_init (GstLPWSincClass * klass)
189 {
190   GObjectClass *gobject_class;
191   GstBaseTransformClass *trans_class;
192   GstAudioFilterClass *filter_class;
193
194   gobject_class = (GObjectClass *) klass;
195   trans_class = (GstBaseTransformClass *) klass;
196   filter_class = (GstAudioFilterClass *) klass;
197
198   gobject_class->set_property = lpwsinc_set_property;
199   gobject_class->get_property = lpwsinc_get_property;
200   gobject_class->dispose = gst_lpwsinc_dispose;
201
202   g_object_class_install_property (gobject_class, PROP_FREQUENCY,
203       g_param_spec_double ("frequency", "Frequency",
204           "Cut-off Frequency (Hz)", 0.0, G_MAXDOUBLE, 0.0,
205           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
206
207   g_object_class_install_property (gobject_class, PROP_LENGTH,
208       g_param_spec_int ("length", "Length",
209           "Filter kernel length, will be rounded to the next odd number",
210           3, G_MAXINT, 101, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
211
212   g_object_class_install_property (gobject_class, PROP_MODE,
213       g_param_spec_enum ("mode", "Mode",
214           "Low pass or high pass mode", GST_TYPE_LPWSINC_MODE,
215           MODE_LOW_PASS, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
216
217   g_object_class_install_property (gobject_class, PROP_WINDOW,
218       g_param_spec_enum ("window", "Window",
219           "Window function to use", GST_TYPE_LPWSINC_WINDOW,
220           WINDOW_HAMMING, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
221
222   trans_class->transform = GST_DEBUG_FUNCPTR (lpwsinc_transform);
223   trans_class->get_unit_size = GST_DEBUG_FUNCPTR (lpwsinc_get_unit_size);
224   trans_class->start = GST_DEBUG_FUNCPTR (lpwsinc_start);
225   filter_class->setup = GST_DEBUG_FUNCPTR (lpwsinc_setup);
226 }
227
228 static void
229 gst_lpwsinc_init (GstLPWSinc * self, GstLPWSincClass * g_class)
230 {
231   self->mode = MODE_LOW_PASS;
232   self->window = WINDOW_HAMMING;
233   self->kernel_length = 101;
234   self->frequency = 0.0;
235   self->kernel = NULL;
236   self->residue = NULL;
237
238   self->have_kernel = FALSE;
239 }
240
241 static void
242 process_32 (GstLPWSinc * self, gfloat * src, gfloat * dst, guint input_samples)
243 {
244   gint kernel_length = self->kernel_length;
245   gint i, j, k, l;
246   gint channels = GST_AUDIO_FILTER (self)->format.channels;
247   gint res_start;
248
249   /* convolution */
250   for (i = 0; i < input_samples; i++) {
251     dst[i] = 0.0;
252     k = i % channels;
253     l = i / channels;
254     for (j = 0; j < kernel_length; j++)
255       if (l < j)
256         dst[i] +=
257             self->residue[(kernel_length + l - j) * channels +
258             k] * self->kernel[j];
259       else
260         dst[i] += src[(l - j) * channels + k] * self->kernel[j];
261   }
262
263   /* copy the tail of the current input buffer to the residue, while
264    * keeping parts of the residue if the input buffer is smaller than
265    * the kernel length */
266   if (input_samples < kernel_length * channels)
267     res_start = kernel_length * channels - input_samples;
268   else
269     res_start = 0;
270
271   for (i = 0; i < res_start; i++)
272     self->residue[i] = self->residue[i + input_samples];
273   for (i = res_start; i < kernel_length * channels; i++)
274     self->residue[i] = src[input_samples - kernel_length * channels + i];
275 }
276
277 static void
278 process_64 (GstLPWSinc * self, gdouble * src, gdouble * dst,
279     guint input_samples)
280 {
281   gint kernel_length = self->kernel_length;
282   gint i, j, k, l;
283   gint channels = GST_AUDIO_FILTER (self)->format.channels;
284   gint res_start;
285
286   /* convolution */
287   for (i = 0; i < input_samples; i++) {
288     dst[i] = 0.0;
289     k = i % channels;
290     l = i / channels;
291     for (j = 0; j < kernel_length; j++)
292       if (l < j)
293         dst[i] +=
294             self->residue[(kernel_length + l - j) * channels +
295             k] * self->kernel[j];
296       else
297         dst[i] += src[(l - j) * channels + k] * self->kernel[j];
298   }
299
300   /* copy the tail of the current input buffer to the residue, while
301    * keeping parts of the residue if the input buffer is smaller than
302    * the kernel length */
303   if (input_samples < kernel_length * channels)
304     res_start = kernel_length * channels - input_samples;
305   else
306     res_start = 0;
307
308   for (i = 0; i < res_start; i++)
309     self->residue[i] = self->residue[i + input_samples];
310   for (i = res_start; i < kernel_length * channels; i++)
311     self->residue[i] = src[input_samples - kernel_length * channels + i];
312 }
313
314 static void
315 lpwsinc_build_kernel (GstLPWSinc * self)
316 {
317   gint i = 0;
318   gdouble sum = 0.0;
319   gint len = 0;
320   gdouble w;
321
322   len = self->kernel_length;
323
324   if (GST_AUDIO_FILTER (self)->format.rate == 0) {
325     GST_DEBUG ("rate not set yet");
326     return;
327   }
328
329   if (GST_AUDIO_FILTER (self)->format.channels == 0) {
330     GST_DEBUG ("channels not set yet");
331     return;
332   }
333
334   /* Clamp cutoff frequency between 0 and the nyquist frequency */
335   self->frequency =
336       CLAMP (self->frequency, 0.0, GST_AUDIO_FILTER (self)->format.rate / 2);
337
338   GST_DEBUG ("lpwsinc: initializing filter kernel of length %d "
339       "with cutoff %.2lf Hz "
340       "for mode %s",
341       len, self->frequency,
342       (self->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass");
343
344   /* fill the kernel */
345   w = 2 * M_PI * (self->frequency / GST_AUDIO_FILTER (self)->format.rate);
346
347   if (self->kernel)
348     g_free (self->kernel);
349   self->kernel = g_new (gdouble, len);
350
351   for (i = 0; i < len; ++i) {
352     if (i == len / 2)
353       self->kernel[i] = w;
354     else
355       self->kernel[i] = sin (w * (i - len / 2)) / (i - len / 2);
356     /* windowing */
357     if (self->window == WINDOW_HAMMING)
358       self->kernel[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
359     else
360       self->kernel[i] *=
361           (0.42 - 0.5 * cos (2 * M_PI * i / len) +
362           0.08 * cos (4 * M_PI * i / len));
363   }
364
365   /* normalize for unity gain at DC */
366   for (i = 0; i < len; ++i)
367     sum += self->kernel[i];
368   for (i = 0; i < len; ++i)
369     self->kernel[i] /= sum;
370
371   /* convert to highpass if specified */
372   if (self->mode == MODE_HIGH_PASS) {
373     for (i = 0; i < len; ++i)
374       self->kernel[i] = -self->kernel[i];
375     self->kernel[len / 2] += 1.0;
376   }
377
378   /* set up the residue memory space */
379   if (self->residue)
380     g_free (self->residue);
381   self->residue =
382       g_new0 (gdouble, len * GST_AUDIO_FILTER (self)->format.channels);
383
384   self->have_kernel = TRUE;
385 }
386
387 /* GstAudioFilter vmethod implementations */
388
389 /* get notified of caps and plug in the correct process function */
390 static gboolean
391 lpwsinc_setup (GstAudioFilter * base, GstRingBufferSpec * format)
392 {
393   GstLPWSinc *self = GST_LPWSINC (base);
394
395   gboolean ret = TRUE;
396
397   if (format->width == 32)
398     self->process = (GstLPWSincProcessFunc) process_32;
399   else if (format->width == 64)
400     self->process = (GstLPWSincProcessFunc) process_64;
401   else
402     ret = FALSE;
403
404   self->have_kernel = FALSE;
405
406   return TRUE;
407 }
408
409 /* GstBaseTransform vmethod implementations */
410
411 static gboolean
412 lpwsinc_get_unit_size (GstBaseTransform * base, GstCaps * caps, guint * size)
413 {
414   gint width, channels;
415   GstStructure *structure;
416   gboolean ret;
417
418   g_assert (size);
419
420   structure = gst_caps_get_structure (caps, 0);
421   ret = gst_structure_get_int (structure, "width", &width);
422   ret &= gst_structure_get_int (structure, "channels", &channels);
423
424   *size = width * channels / 8;
425
426   return ret;
427 }
428
429 static GstFlowReturn
430 lpwsinc_transform (GstBaseTransform * base, GstBuffer * inbuf,
431     GstBuffer * outbuf)
432 {
433   GstLPWSinc *self = GST_LPWSINC (base);
434   GstClockTime timestamp;
435   gint input_samples =
436       GST_BUFFER_SIZE (outbuf) / (GST_AUDIO_FILTER (self)->format.width / 8);
437
438   /* don't process data in passthrough-mode */
439   if (gst_base_transform_is_passthrough (base))
440     return GST_FLOW_OK;
441
442   /* FIXME: subdivide GST_BUFFER_SIZE into small chunks for smooth fades */
443   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
444   if (GST_CLOCK_TIME_IS_VALID (timestamp))
445     gst_object_sync_values (G_OBJECT (self), timestamp);
446
447   if (!self->have_kernel)
448     lpwsinc_build_kernel (self);
449
450   self->process (self, GST_BUFFER_DATA (inbuf), GST_BUFFER_DATA (outbuf),
451       input_samples);
452
453   return GST_FLOW_OK;
454 }
455
456 static gboolean
457 lpwsinc_start (GstBaseTransform * base)
458 {
459   GstLPWSinc *self = GST_LPWSINC (base);
460   gint channels = GST_AUDIO_FILTER (self)->format.channels;
461
462   /* Reset the residue if already existing */
463   if (channels && self->residue)
464     memset (self->residue, 0, channels *
465         self->kernel_length * sizeof (gdouble));
466
467   return TRUE;
468 }
469
470 static void
471 lpwsinc_set_property (GObject * object, guint prop_id, const GValue * value,
472     GParamSpec * pspec)
473 {
474   GstLPWSinc *self = GST_LPWSINC (object);
475
476   g_return_if_fail (GST_IS_LPWSINC (self));
477
478   switch (prop_id) {
479     case PROP_LENGTH:{
480       gint val;
481
482       GST_BASE_TRANSFORM_LOCK (self);
483       val = g_value_get_int (value);
484       if (val % 2 == 0)
485         val++;
486       self->kernel_length = val;
487       lpwsinc_build_kernel (self);
488       GST_BASE_TRANSFORM_UNLOCK (self);
489       break;
490     }
491     case PROP_FREQUENCY:
492       GST_BASE_TRANSFORM_LOCK (self);
493       self->frequency = g_value_get_double (value);
494       lpwsinc_build_kernel (self);
495       GST_BASE_TRANSFORM_UNLOCK (self);
496       break;
497     case PROP_MODE:
498       GST_BASE_TRANSFORM_LOCK (self);
499       self->mode = g_value_get_enum (value);
500       lpwsinc_build_kernel (self);
501       GST_BASE_TRANSFORM_UNLOCK (self);
502       break;
503     case PROP_WINDOW:
504       GST_BASE_TRANSFORM_LOCK (self);
505       self->window = g_value_get_enum (value);
506       lpwsinc_build_kernel (self);
507       GST_BASE_TRANSFORM_UNLOCK (self);
508       break;
509     default:
510       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
511       break;
512   }
513 }
514
515 static void
516 lpwsinc_get_property (GObject * object, guint prop_id, GValue * value,
517     GParamSpec * pspec)
518 {
519   GstLPWSinc *self = GST_LPWSINC (object);
520
521   switch (prop_id) {
522     case PROP_LENGTH:
523       g_value_set_int (value, self->kernel_length);
524       break;
525     case PROP_FREQUENCY:
526       g_value_set_double (value, self->frequency);
527       break;
528     case PROP_MODE:
529       g_value_set_enum (value, self->mode);
530       break;
531     case PROP_WINDOW:
532       g_value_set_enum (value, self->window);
533       break;
534     default:
535       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
536       break;
537   }
538 }