gst/filter/: Fix processing with buffer sizes that are larger than the filter kernel...
[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  *               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 "gstbpwsinc.h"
47
48 #define GST_CAT_DEFAULT gst_bpwsinc_debug
49 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
50
51 static const GstElementDetails bpwsinc_details =
52 GST_ELEMENT_DETAILS ("Band-pass Windowed sinc filter",
53     "Filter/Effect/Audio",
54     "Band-pass Windowed sinc filter",
55     "Thomas <thomas@apestaart.org>, "
56     "Steven W. Smith, "
57     "Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>, "
58     "Sebastian Dröge <slomo@circular-chaos.org>");
59
60 /* Filter signals and args */
61 enum
62 {
63   /* FILL ME */
64   LAST_SIGNAL
65 };
66
67 enum
68 {
69   PROP_0,
70   PROP_LENGTH,
71   PROP_LOWER_FREQUENCY,
72   PROP_UPPER_FREQUENCY,
73   PROP_MODE,
74   PROP_WINDOW
75 };
76
77 enum
78 {
79   MODE_BAND_PASS = 0,
80   MODE_BAND_REJECT
81 };
82
83 #define GST_TYPE_BPWSINC_MODE (gst_bpwsinc_mode_get_type ())
84 static GType
85 gst_bpwsinc_mode_get_type (void)
86 {
87   static GType gtype = 0;
88
89   if (gtype == 0) {
90     static const GEnumValue values[] = {
91       {MODE_BAND_PASS, "Band pass (default)",
92           "band-pass"},
93       {MODE_BAND_REJECT, "Band reject",
94           "band-reject"},
95       {0, NULL, NULL}
96     };
97
98     gtype = g_enum_register_static ("GstBPWSincMode", values);
99   }
100   return gtype;
101 }
102
103 enum
104 {
105   WINDOW_HAMMING = 0,
106   WINDOW_BLACKMAN
107 };
108
109 #define GST_TYPE_BPWSINC_WINDOW (gst_bpwsinc_window_get_type ())
110 static GType
111 gst_bpwsinc_window_get_type (void)
112 {
113   static GType gtype = 0;
114
115   if (gtype == 0) {
116     static const GEnumValue values[] = {
117       {WINDOW_HAMMING, "Hamming window (default)",
118           "hamming"},
119       {WINDOW_BLACKMAN, "Blackman window",
120           "blackman"},
121       {0, NULL, NULL}
122     };
123
124     gtype = g_enum_register_static ("GstBPWSincWindow", values);
125   }
126   return gtype;
127 }
128
129 #define ALLOWED_CAPS \
130     "audio/x-raw-float, "                                             \
131     " width = (int) { 32, 64 }, "                                     \
132     " endianness = (int) BYTE_ORDER, "                                \
133     " rate = (int) [ 1, MAX ], "                                      \
134     " channels = (int) [ 1, MAX ] "
135
136 #define DEBUG_INIT(bla) \
137   GST_DEBUG_CATEGORY_INIT (gst_bpwsinc_debug, "bpwsinc", 0, "Band-pass Windowed sinc filter plugin");
138
139 GST_BOILERPLATE_FULL (GstBPWSinc, gst_bpwsinc, GstAudioFilter,
140     GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
141
142 static void bpwsinc_set_property (GObject * object, guint prop_id,
143     const GValue * value, GParamSpec * pspec);
144 static void bpwsinc_get_property (GObject * object, guint prop_id,
145     GValue * value, GParamSpec * pspec);
146
147 static GstFlowReturn bpwsinc_transform (GstBaseTransform * base,
148     GstBuffer * inbuf, GstBuffer * outbuf);
149 static gboolean bpwsinc_get_unit_size (GstBaseTransform * base, GstCaps * caps,
150     guint * size);
151 static gboolean bpwsinc_setup (GstAudioFilter * base,
152     GstRingBufferSpec * format);
153
154 /* Element class */
155
156 static void
157 gst_bpwsinc_dispose (GObject * object)
158 {
159   GstBPWSinc *self = GST_BPWSINC (object);
160
161   if (self->residue) {
162     g_free (self->residue);
163     self->residue = NULL;
164   }
165
166   if (self->kernel) {
167     g_free (self->kernel);
168     self->kernel = NULL;
169   }
170
171   G_OBJECT_CLASS (parent_class)->dispose (object);
172 }
173
174 static void
175 gst_bpwsinc_base_init (gpointer g_class)
176 {
177   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
178   GstCaps *caps;
179
180   gst_element_class_set_details (element_class, &bpwsinc_details);
181
182   caps = gst_caps_from_string (ALLOWED_CAPS);
183   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
184       caps);
185   gst_caps_unref (caps);
186 }
187
188 static void
189 gst_bpwsinc_class_init (GstBPWSincClass * klass)
190 {
191   GObjectClass *gobject_class;
192   GstBaseTransformClass *trans_class;
193
194   gobject_class = (GObjectClass *) klass;
195   trans_class = (GstBaseTransformClass *) klass;
196
197   gobject_class->set_property = bpwsinc_set_property;
198   gobject_class->get_property = bpwsinc_get_property;
199   gobject_class->dispose = gst_bpwsinc_dispose;
200
201   g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY,
202       g_param_spec_double ("lower-frequency", "Lower Frequency",
203           "Cut-off lower frequency (Hz)",
204           0.0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
205   g_object_class_install_property (gobject_class, PROP_UPPER_FREQUENCY,
206       g_param_spec_double ("upper-frequency", "Upper Frequency",
207           "Cut-off upper frequency (Hz)",
208           0.0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
209   g_object_class_install_property (gobject_class, PROP_LENGTH,
210       g_param_spec_int ("length", "Length",
211           "Filter kernel length, will be rounded to the next odd number",
212           3, G_MAXINT, 101, G_PARAM_READWRITE));
213
214   g_object_class_install_property (gobject_class, PROP_MODE,
215       g_param_spec_enum ("mode", "Mode",
216           "Band pass or band reject mode", GST_TYPE_BPWSINC_MODE,
217           MODE_BAND_PASS, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
218
219   g_object_class_install_property (gobject_class, PROP_WINDOW,
220       g_param_spec_enum ("window", "Window",
221           "Window function to use", GST_TYPE_BPWSINC_WINDOW,
222           WINDOW_HAMMING, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
223
224   trans_class->transform = GST_DEBUG_FUNCPTR (bpwsinc_transform);
225   trans_class->get_unit_size = GST_DEBUG_FUNCPTR (bpwsinc_get_unit_size);
226   GST_AUDIO_FILTER_CLASS (klass)->setup = GST_DEBUG_FUNCPTR (bpwsinc_setup);
227 }
228
229 static void
230 gst_bpwsinc_init (GstBPWSinc * self, GstBPWSincClass * g_class)
231 {
232   self->kernel_length = 101;
233   self->lower_frequency = 0.0;
234   self->upper_frequency = 0.0;
235   self->mode = MODE_BAND_PASS;
236   self->window = WINDOW_HAMMING;
237   self->kernel = NULL;
238   self->have_kernel = FALSE;
239   self->residue = NULL;
240 }
241
242 static void
243 process_32 (GstBPWSinc * self, gfloat * src, gfloat * dst, guint input_samples)
244 {
245   gint kernel_length = self->kernel_length;
246   gint i, j, k, l;
247   gint channels = GST_AUDIO_FILTER (self)->format.channels;
248   gint res_start;
249
250   /* convolution */
251   for (i = 0; i < input_samples; i++) {
252     dst[i] = 0.0;
253     k = i % channels;
254     l = i / channels;
255     for (j = 0; j < kernel_length; j++)
256       if (l < j)
257         dst[i] +=
258             self->residue[(kernel_length + l - j) * channels +
259             k] * self->kernel[j];
260       else
261         dst[i] += src[(l - j) * channels + k] * self->kernel[j];
262   }
263
264   /* copy the tail of the current input buffer to the residue, while
265    * keeping parts of the residue if the input buffer is smaller than
266    * the kernel length */
267   if (input_samples < kernel_length * channels)
268     res_start = kernel_length * channels - input_samples;
269   else
270     res_start = 0;
271
272   for (i = 0; i < res_start; i++)
273     self->residue[i] = self->residue[i + input_samples];
274   for (i = res_start; i < kernel_length * channels; i++)
275     self->residue[i] = src[input_samples - kernel_length * channels + i];
276 }
277
278 static void
279 process_64 (GstBPWSinc * self, gdouble * src, gdouble * dst,
280     guint input_samples)
281 {
282   gint kernel_length = self->kernel_length;
283   gint i, j, k, l;
284   gint channels = GST_AUDIO_FILTER (self)->format.channels;
285   gint res_start;
286
287   /* convolution */
288   for (i = 0; i < input_samples; i++) {
289     dst[i] = 0.0;
290     k = i % channels;
291     l = i / channels;
292     for (j = 0; j < kernel_length; j++)
293       if (l < j)
294         dst[i] +=
295             self->residue[(kernel_length + l - j) * channels +
296             k] * self->kernel[j];
297       else
298         dst[i] += src[(l - j) * channels + k] * self->kernel[j];
299   }
300
301   /* copy the tail of the current input buffer to the residue, while
302    * keeping parts of the residue if the input buffer is smaller than
303    * the kernel length */
304   if (input_samples < kernel_length * channels)
305     res_start = kernel_length * channels - input_samples;
306   else
307     res_start = 0;
308
309   for (i = 0; i < res_start; i++)
310     self->residue[i] = self->residue[i + input_samples];
311   for (i = res_start; i < kernel_length * channels; i++)
312     self->residue[i] = src[input_samples - kernel_length * channels + i];
313 }
314
315 static void
316 bpwsinc_build_kernel (GstBPWSinc * self)
317 {
318   gint i = 0;
319   gdouble sum = 0.0;
320   gint len = 0;
321   gdouble *kernel_lp, *kernel_hp;
322   gdouble w;
323
324   len = self->kernel_length;
325
326   if (GST_AUDIO_FILTER (self)->format.rate == 0) {
327     GST_DEBUG ("rate not set yet");
328     return;
329   }
330
331   if (GST_AUDIO_FILTER (self)->format.channels == 0) {
332     GST_DEBUG ("channels not set yet");
333     return;
334   }
335
336   self->have_kernel = TRUE;
337
338   /* Clamp frequencies */
339   self->lower_frequency =
340       CLAMP (self->lower_frequency, 0.0,
341       GST_AUDIO_FILTER (self)->format.rate / 2);
342   self->upper_frequency =
343       CLAMP (self->upper_frequency, 0.0,
344       GST_AUDIO_FILTER (self)->format.rate / 2);
345   if (self->lower_frequency > self->upper_frequency) {
346     gint tmp = self->lower_frequency;
347
348     self->lower_frequency = self->upper_frequency;
349     self->upper_frequency = tmp;
350   }
351
352   /* fill the lp kernel */
353   GST_DEBUG ("bpwsinc: initializing LP kernel of length %d with cut-off %f",
354       len, self->lower_frequency);
355
356   w = 2 * M_PI * (self->lower_frequency / GST_AUDIO_FILTER (self)->format.rate);
357   kernel_lp = g_new (gdouble, len);
358   for (i = 0; i < len; ++i) {
359     if (i == len / 2)
360       kernel_lp[i] = w;
361     else
362       kernel_lp[i] = sin (w * (i - len / 2))
363           / (i - len / 2);
364     /* Windowing */
365     if (self->window == WINDOW_HAMMING)
366       kernel_lp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
367     else
368       kernel_lp[i] *=
369           (0.42 - 0.5 * cos (2 * M_PI * i / len) +
370           0.08 * cos (4 * M_PI * i / len));
371   }
372
373   /* normalize for unity gain at DC */
374   sum = 0.0;
375   for (i = 0; i < len; ++i)
376     sum += kernel_lp[i];
377   for (i = 0; i < len; ++i)
378     kernel_lp[i] /= sum;
379
380   /* fill the hp kernel */
381   GST_DEBUG ("bpwsinc: initializing HP kernel of length %d with cut-off %f",
382       len, self->upper_frequency);
383
384   w = 2 * M_PI * (self->upper_frequency / GST_AUDIO_FILTER (self)->format.rate);
385   kernel_hp = g_new (gdouble, len);
386   for (i = 0; i < len; ++i) {
387     if (i == len / 2)
388       kernel_hp[i] = w;
389     else
390       kernel_hp[i] = sin (w * (i - len / 2))
391           / (i - len / 2);
392     /* Windowing */
393     if (self->window == WINDOW_HAMMING)
394       kernel_hp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
395     else
396       kernel_hp[i] *=
397           (0.42 - 0.5 * cos (2 * M_PI * i / len) +
398           0.08 * cos (4 * M_PI * i / len));
399   }
400
401   /* normalize for unity gain at DC */
402   sum = 0.0;
403   for (i = 0; i < len; ++i)
404     sum += kernel_hp[i];
405   for (i = 0; i < len; ++i)
406     kernel_hp[i] /= sum;
407
408   /* do spectral inversion to go from lowpass to highpass */
409   for (i = 0; i < len; ++i)
410     kernel_hp[i] = -kernel_hp[i];
411   kernel_hp[len / 2] += 1;
412
413   /* combine the two kernels */
414   if (self->kernel)
415     g_free (self->kernel);
416   self->kernel = g_new (gdouble, len);
417
418   for (i = 0; i < len; ++i)
419     self->kernel[i] = kernel_lp[i] + kernel_hp[i];
420
421   /* free the helper kernels */
422   g_free (kernel_lp);
423   g_free (kernel_hp);
424
425   /* do spectral inversion to go from bandreject to bandpass
426    * if specified */
427   if (self->mode == MODE_BAND_PASS) {
428     for (i = 0; i < len; ++i)
429       self->kernel[i] = -self->kernel[i];
430     self->kernel[len / 2] += 1;
431   }
432
433   /* set up the residue memory space */
434   if (self->residue)
435     g_free (self->residue);
436
437   self->residue =
438       g_new0 (gdouble, len * GST_AUDIO_FILTER (self)->format.channels);
439 }
440
441 /* GstAudioFilter vmethod implementations */
442
443 /* get notified of caps and plug in the correct process function */
444 static gboolean
445 bpwsinc_setup (GstAudioFilter * base, GstRingBufferSpec * format)
446 {
447   GstBPWSinc *self = GST_BPWSINC (base);
448
449   gboolean ret = TRUE;
450
451   if (format->width == 32)
452     self->process = (GstBPWSincProcessFunc) process_32;
453   else if (format->width == 64)
454     self->process = (GstBPWSincProcessFunc) process_64;
455   else
456     ret = FALSE;
457
458   self->have_kernel = FALSE;
459
460   return TRUE;
461 }
462
463 /* GstBaseTransform vmethod implementations */
464
465 static gboolean
466 bpwsinc_get_unit_size (GstBaseTransform * base, GstCaps * caps, guint * size)
467 {
468   gint width, channels;
469   GstStructure *structure;
470   gboolean ret;
471
472   g_assert (size);
473
474   structure = gst_caps_get_structure (caps, 0);
475   ret = gst_structure_get_int (structure, "width", &width);
476   ret &= gst_structure_get_int (structure, "channels", &channels);
477
478   *size = width * channels / 8;
479
480   return ret;
481 }
482
483 static GstFlowReturn
484 bpwsinc_transform (GstBaseTransform * base, GstBuffer * inbuf,
485     GstBuffer * outbuf)
486 {
487   GstBPWSinc *self = GST_BPWSINC (base);
488   GstClockTime timestamp;
489   gint input_samples =
490       GST_BUFFER_SIZE (outbuf) / (GST_AUDIO_FILTER (self)->format.width / 8);
491
492   /* don't process data in passthrough-mode */
493   if (gst_base_transform_is_passthrough (base))
494     return GST_FLOW_OK;
495
496   /* FIXME: subdivide GST_BUFFER_SIZE into small chunks for smooth fades */
497   timestamp = GST_BUFFER_TIMESTAMP (outbuf);
498
499   if (GST_CLOCK_TIME_IS_VALID (timestamp))
500     gst_object_sync_values (G_OBJECT (self), timestamp);
501
502   if (!self->have_kernel)
503     bpwsinc_build_kernel (self);
504
505   self->process (self, GST_BUFFER_DATA (inbuf), GST_BUFFER_DATA (outbuf),
506       input_samples);
507
508   return GST_FLOW_OK;
509 }
510
511 static void
512 bpwsinc_set_property (GObject * object, guint prop_id, const GValue * value,
513     GParamSpec * pspec)
514 {
515   GstBPWSinc *self = GST_BPWSINC (object);
516
517   g_return_if_fail (GST_IS_BPWSINC (self));
518
519   switch (prop_id) {
520     case PROP_LENGTH:{
521       gint val;
522
523       GST_BASE_TRANSFORM_LOCK (self);
524       val = g_value_get_int (value);
525       if (val % 2 == 0)
526         val++;
527       self->kernel_length = val;
528       bpwsinc_build_kernel (self);
529       GST_BASE_TRANSFORM_UNLOCK (self);
530       break;
531     }
532     case PROP_LOWER_FREQUENCY:
533       GST_BASE_TRANSFORM_LOCK (self);
534       self->lower_frequency = g_value_get_double (value);
535       bpwsinc_build_kernel (self);
536       GST_BASE_TRANSFORM_UNLOCK (self);
537       break;
538     case PROP_UPPER_FREQUENCY:
539       GST_BASE_TRANSFORM_LOCK (self);
540       self->upper_frequency = g_value_get_double (value);
541       bpwsinc_build_kernel (self);
542       GST_BASE_TRANSFORM_UNLOCK (self);
543       break;
544     case PROP_MODE:
545       GST_BASE_TRANSFORM_LOCK (self);
546       self->mode = g_value_get_enum (value);
547       bpwsinc_build_kernel (self);
548       GST_BASE_TRANSFORM_UNLOCK (self);
549       break;
550     case PROP_WINDOW:
551       GST_BASE_TRANSFORM_LOCK (self);
552       self->window = g_value_get_enum (value);
553       bpwsinc_build_kernel (self);
554       GST_BASE_TRANSFORM_UNLOCK (self);
555       break;
556     default:
557       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
558       break;
559   }
560 }
561
562 static void
563 bpwsinc_get_property (GObject * object, guint prop_id, GValue * value,
564     GParamSpec * pspec)
565 {
566   GstBPWSinc *self = GST_BPWSINC (object);
567
568   switch (prop_id) {
569     case PROP_LENGTH:
570       g_value_set_int (value, self->kernel_length);
571       break;
572     case PROP_LOWER_FREQUENCY:
573       g_value_set_double (value, self->lower_frequency);
574       break;
575     case PROP_UPPER_FREQUENCY:
576       g_value_set_double (value, self->upper_frequency);
577       break;
578     case PROP_MODE:
579       g_value_set_enum (value, self->mode);
580       break;
581     case PROP_WINDOW:
582       g_value_set_enum (value, self->window);
583       break;
584     default:
585       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586       break;
587   }
588 }