1 /* -*- c-basic-offset: 2 -*-
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>
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.
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.
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.
24 * this windowed sinc filter is taken from the freely downloadable DSP book,
25 * "The Scientist and Engineer's Guide to Digital Signal Processing",
27 * available at http://www.dspguide.com/
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
43 #include <gst/audio/gstaudiofilter.h>
44 #include <gst/controller/gstcontroller.h>
46 #include "gstbpwsinc.h"
48 #define GST_CAT_DEFAULT gst_bpwsinc_debug
49 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
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>, "
57 "Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>, "
58 "Sebastian Dröge <slomo@circular-chaos.org>");
60 /* Filter signals and args */
83 #define GST_TYPE_BPWSINC_MODE (gst_bpwsinc_mode_get_type ())
85 gst_bpwsinc_mode_get_type (void)
87 static GType gtype = 0;
90 static const GEnumValue values[] = {
91 {MODE_BAND_PASS, "Band pass (default)",
93 {MODE_BAND_REJECT, "Band reject",
98 gtype = g_enum_register_static ("GstBPWSincMode", values);
109 #define GST_TYPE_BPWSINC_WINDOW (gst_bpwsinc_window_get_type ())
111 gst_bpwsinc_window_get_type (void)
113 static GType gtype = 0;
116 static const GEnumValue values[] = {
117 {WINDOW_HAMMING, "Hamming window (default)",
119 {WINDOW_BLACKMAN, "Blackman window",
124 gtype = g_enum_register_static ("GstBPWSincWindow", values);
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 ] "
136 #define DEBUG_INIT(bla) \
137 GST_DEBUG_CATEGORY_INIT (gst_bpwsinc_debug, "bpwsinc", 0, "Band-pass Windowed sinc filter plugin");
139 GST_BOILERPLATE_FULL (GstBPWSinc, gst_bpwsinc, GstAudioFilter,
140 GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
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);
147 static GstFlowReturn bpwsinc_transform (GstBaseTransform * base,
148 GstBuffer * inbuf, GstBuffer * outbuf);
149 static gboolean bpwsinc_get_unit_size (GstBaseTransform * base, GstCaps * caps,
151 static gboolean bpwsinc_setup (GstAudioFilter * base,
152 GstRingBufferSpec * format);
157 gst_bpwsinc_dispose (GObject * object)
159 GstBPWSinc *self = GST_BPWSINC (object);
162 g_free (self->residue);
163 self->residue = NULL;
167 g_free (self->kernel);
171 G_OBJECT_CLASS (parent_class)->dispose (object);
175 gst_bpwsinc_base_init (gpointer g_class)
177 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
180 gst_element_class_set_details (element_class, &bpwsinc_details);
182 caps = gst_caps_from_string (ALLOWED_CAPS);
183 gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (g_class),
185 gst_caps_unref (caps);
189 gst_bpwsinc_class_init (GstBPWSincClass * klass)
191 GObjectClass *gobject_class;
192 GstBaseTransformClass *trans_class;
194 gobject_class = (GObjectClass *) klass;
195 trans_class = (GstBaseTransformClass *) klass;
197 gobject_class->set_property = bpwsinc_set_property;
198 gobject_class->get_property = bpwsinc_get_property;
199 gobject_class->dispose = gst_bpwsinc_dispose;
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));
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));
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));
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);
230 gst_bpwsinc_init (GstBPWSinc * self, GstBPWSincClass * g_class)
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;
238 self->have_kernel = FALSE;
239 self->residue = NULL;
243 process_32 (GstBPWSinc * self, gfloat * src, gfloat * dst, guint input_samples)
245 gint kernel_length = self->kernel_length;
247 gint channels = GST_AUDIO_FILTER (self)->format.channels;
251 for (i = 0; i < input_samples; i++) {
255 for (j = 0; j < kernel_length; j++)
258 self->residue[(kernel_length + l - j) * channels +
259 k] * self->kernel[j];
261 dst[i] += src[(l - j) * channels + k] * self->kernel[j];
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;
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];
279 process_64 (GstBPWSinc * self, gdouble * src, gdouble * dst,
282 gint kernel_length = self->kernel_length;
284 gint channels = GST_AUDIO_FILTER (self)->format.channels;
288 for (i = 0; i < input_samples; i++) {
292 for (j = 0; j < kernel_length; j++)
295 self->residue[(kernel_length + l - j) * channels +
296 k] * self->kernel[j];
298 dst[i] += src[(l - j) * channels + k] * self->kernel[j];
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;
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];
316 bpwsinc_build_kernel (GstBPWSinc * self)
321 gdouble *kernel_lp, *kernel_hp;
324 len = self->kernel_length;
326 if (GST_AUDIO_FILTER (self)->format.rate == 0) {
327 GST_DEBUG ("rate not set yet");
331 if (GST_AUDIO_FILTER (self)->format.channels == 0) {
332 GST_DEBUG ("channels not set yet");
336 self->have_kernel = TRUE;
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;
348 self->lower_frequency = self->upper_frequency;
349 self->upper_frequency = tmp;
352 /* fill the lp kernel */
353 GST_DEBUG ("bpwsinc: initializing LP kernel of length %d with cut-off %f",
354 len, self->lower_frequency);
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) {
362 kernel_lp[i] = sin (w * (i - len / 2))
365 if (self->window == WINDOW_HAMMING)
366 kernel_lp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
369 (0.42 - 0.5 * cos (2 * M_PI * i / len) +
370 0.08 * cos (4 * M_PI * i / len));
373 /* normalize for unity gain at DC */
375 for (i = 0; i < len; ++i)
377 for (i = 0; i < len; ++i)
380 /* fill the hp kernel */
381 GST_DEBUG ("bpwsinc: initializing HP kernel of length %d with cut-off %f",
382 len, self->upper_frequency);
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) {
390 kernel_hp[i] = sin (w * (i - len / 2))
393 if (self->window == WINDOW_HAMMING)
394 kernel_hp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
397 (0.42 - 0.5 * cos (2 * M_PI * i / len) +
398 0.08 * cos (4 * M_PI * i / len));
401 /* normalize for unity gain at DC */
403 for (i = 0; i < len; ++i)
405 for (i = 0; i < len; ++i)
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;
413 /* combine the two kernels */
415 g_free (self->kernel);
416 self->kernel = g_new (gdouble, len);
418 for (i = 0; i < len; ++i)
419 self->kernel[i] = kernel_lp[i] + kernel_hp[i];
421 /* free the helper kernels */
425 /* do spectral inversion to go from bandreject to bandpass
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;
433 /* set up the residue memory space */
435 g_free (self->residue);
438 g_new0 (gdouble, len * GST_AUDIO_FILTER (self)->format.channels);
441 /* GstAudioFilter vmethod implementations */
443 /* get notified of caps and plug in the correct process function */
445 bpwsinc_setup (GstAudioFilter * base, GstRingBufferSpec * format)
447 GstBPWSinc *self = GST_BPWSINC (base);
451 if (format->width == 32)
452 self->process = (GstBPWSincProcessFunc) process_32;
453 else if (format->width == 64)
454 self->process = (GstBPWSincProcessFunc) process_64;
458 self->have_kernel = FALSE;
463 /* GstBaseTransform vmethod implementations */
466 bpwsinc_get_unit_size (GstBaseTransform * base, GstCaps * caps, guint * size)
468 gint width, channels;
469 GstStructure *structure;
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);
478 *size = width * channels / 8;
484 bpwsinc_transform (GstBaseTransform * base, GstBuffer * inbuf,
487 GstBPWSinc *self = GST_BPWSINC (base);
488 GstClockTime timestamp;
490 GST_BUFFER_SIZE (outbuf) / (GST_AUDIO_FILTER (self)->format.width / 8);
492 /* don't process data in passthrough-mode */
493 if (gst_base_transform_is_passthrough (base))
496 /* FIXME: subdivide GST_BUFFER_SIZE into small chunks for smooth fades */
497 timestamp = GST_BUFFER_TIMESTAMP (outbuf);
499 if (GST_CLOCK_TIME_IS_VALID (timestamp))
500 gst_object_sync_values (G_OBJECT (self), timestamp);
502 if (!self->have_kernel)
503 bpwsinc_build_kernel (self);
505 self->process (self, GST_BUFFER_DATA (inbuf), GST_BUFFER_DATA (outbuf),
512 bpwsinc_set_property (GObject * object, guint prop_id, const GValue * value,
515 GstBPWSinc *self = GST_BPWSINC (object);
517 g_return_if_fail (GST_IS_BPWSINC (self));
523 GST_BASE_TRANSFORM_LOCK (self);
524 val = g_value_get_int (value);
527 self->kernel_length = val;
528 bpwsinc_build_kernel (self);
529 GST_BASE_TRANSFORM_UNLOCK (self);
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);
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);
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);
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);
557 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
563 bpwsinc_get_property (GObject * object, guint prop_id, GValue * value,
566 GstBPWSinc *self = GST_BPWSINC (object);
570 g_value_set_int (value, self->kernel_length);
572 case PROP_LOWER_FREQUENCY:
573 g_value_set_double (value, self->lower_frequency);
575 case PROP_UPPER_FREQUENCY:
576 g_value_set_double (value, self->upper_frequency);
579 g_value_set_enum (value, self->mode);
582 g_value_set_enum (value, self->window);
585 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);