gst_plugins_good/gst/audiofx/audiowsincband.c
changeset 26 69c7080681bf
parent 24 bc39b352897e
child 28 4ed5253bb6ba
equal deleted inserted replaced
24:bc39b352897e 26:69c7080681bf
     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-2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
       
     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  */
       
    30 
       
    31 /**
       
    32  * SECTION:element-audiowsincband
       
    33  *
       
    34  * Attenuates all frequencies outside (bandpass) or inside (bandreject) of a frequency
       
    35  * band. The length parameter controls the rolloff, the window parameter
       
    36  * controls rolloff and stopband attenuation. The Hamming window provides a faster rolloff but a bit
       
    37  * worse stopband attenuation, the other way around for the Blackman window.
       
    38  *
       
    39  * This element has the advantage over the Chebyshev bandpass and bandreject filter that it has
       
    40  * a much better rolloff when using a larger kernel size and almost linear phase. The only
       
    41  * disadvantage is the much slower execution time with larger kernels.
       
    42  *
       
    43  * <refsect2>
       
    44  * <title>Example launch line</title>
       
    45  * |[
       
    46  * gst-launch audiotestsrc freq=1500 ! audioconvert ! audiosincband mode=band-pass lower-frequency=3000 upper-frequency=10000 length=501 window=blackman ! audioconvert ! alsasink
       
    47  * gst-launch filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audiowsincband mode=band-reject lower-frequency=59 upper-frequency=61 length=10001 window=hamming ! audioconvert ! alsasink
       
    48  * gst-launch audiotestsrc wave=white-noise ! audioconvert ! audiowsincband mode=band-pass lower-frequency=1000 upper-frequency=2000 length=31 ! audioconvert ! alsasink
       
    49  * ]|
       
    50  * </refsect2>
       
    51  */
       
    52 
       
    53 #ifdef HAVE_CONFIG_H
       
    54 #include "config.h"
       
    55 #endif
       
    56 
       
    57 #include <string.h>
       
    58 #include <math.h>
       
    59 #include <gst/gst.h>
       
    60 #include <gst/audio/gstaudiofilter.h>
       
    61 #include <gst/controller/gstcontroller.h>
       
    62 
       
    63 #include "audiowsincband.h"
       
    64 
       
    65 #define GST_CAT_DEFAULT gst_gst_audio_wsincband_debug
       
    66 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
       
    67 
       
    68 enum
       
    69 {
       
    70   PROP_0,
       
    71   PROP_LENGTH,
       
    72   PROP_LOWER_FREQUENCY,
       
    73   PROP_UPPER_FREQUENCY,
       
    74   PROP_MODE,
       
    75   PROP_WINDOW
       
    76 };
       
    77 
       
    78 enum
       
    79 {
       
    80   MODE_BAND_PASS = 0,
       
    81   MODE_BAND_REJECT
       
    82 };
       
    83 
       
    84 #define GST_TYPE_AUDIO_WSINC_BAND_MODE (gst_gst_audio_wsincband_mode_get_type ())
       
    85 static GType
       
    86 gst_gst_audio_wsincband_mode_get_type (void)
       
    87 {
       
    88   static GType gtype = 0;
       
    89 
       
    90   if (gtype == 0) {
       
    91     static const GEnumValue values[] = {
       
    92       {MODE_BAND_PASS, "Band pass (default)",
       
    93           "band-pass"},
       
    94       {MODE_BAND_REJECT, "Band reject",
       
    95           "band-reject"},
       
    96       {0, NULL, NULL}
       
    97     };
       
    98 
       
    99     gtype = g_enum_register_static ("GstAudioWSincBandMode", values);
       
   100   }
       
   101   return gtype;
       
   102 }
       
   103 
       
   104 enum
       
   105 {
       
   106   WINDOW_HAMMING = 0,
       
   107   WINDOW_BLACKMAN
       
   108 };
       
   109 
       
   110 #define GST_TYPE_AUDIO_WSINC_BAND_WINDOW (gst_gst_audio_wsincband_window_get_type ())
       
   111 static GType
       
   112 gst_gst_audio_wsincband_window_get_type (void)
       
   113 {
       
   114   static GType gtype = 0;
       
   115 
       
   116   if (gtype == 0) {
       
   117     static const GEnumValue values[] = {
       
   118       {WINDOW_HAMMING, "Hamming window (default)",
       
   119           "hamming"},
       
   120       {WINDOW_BLACKMAN, "Blackman window",
       
   121           "blackman"},
       
   122       {0, NULL, NULL}
       
   123     };
       
   124 
       
   125     gtype = g_enum_register_static ("GstAudioWSincBandWindow", values);
       
   126   }
       
   127   return gtype;
       
   128 }
       
   129 
       
   130 #define DEBUG_INIT(bla) \
       
   131   GST_DEBUG_CATEGORY_INIT (gst_gst_audio_wsincband_debug, "audiowsincband", 0, \
       
   132       "Band-pass and Band-reject Windowed sinc filter plugin");
       
   133 
       
   134 GST_BOILERPLATE_FULL (GstAudioWSincBand, gst_audio_wsincband, GstAudioFilter,
       
   135     GST_TYPE_AUDIO_FX_BASE_FIR_FILTER, DEBUG_INIT);
       
   136 
       
   137 static void gst_audio_wsincband_set_property (GObject * object, guint prop_id,
       
   138     const GValue * value, GParamSpec * pspec);
       
   139 static void gst_audio_wsincband_get_property (GObject * object, guint prop_id,
       
   140     GValue * value, GParamSpec * pspec);
       
   141 static void gst_audio_wsincband_finalize (GObject * object);
       
   142 
       
   143 static gboolean gst_audio_wsincband_setup (GstAudioFilter * base,
       
   144     GstRingBufferSpec * format);
       
   145 
       
   146 /* Element class */
       
   147 static void
       
   148 gst_audio_wsincband_base_init (gpointer g_class)
       
   149 {
       
   150   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
       
   151 
       
   152   gst_element_class_set_details_simple (element_class,
       
   153       "Band pass & band reject filter", "Filter/Effect/Audio",
       
   154       "Band pass and band reject windowed sinc filter",
       
   155       "Thomas Vander Stichele <thomas at apestaart dot org>, "
       
   156       "Steven W. Smith, "
       
   157       "Dreamlab Technologies Ltd. <mathis.hofer@dreamlab.net>, "
       
   158       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
       
   159 }
       
   160 
       
   161 static void
       
   162 gst_audio_wsincband_class_init (GstAudioWSincBandClass * klass)
       
   163 {
       
   164   GObjectClass *gobject_class = (GObjectClass *) klass;
       
   165   GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
       
   166 
       
   167   gobject_class->set_property = gst_audio_wsincband_set_property;
       
   168   gobject_class->get_property = gst_audio_wsincband_get_property;
       
   169   gobject_class->finalize = gst_audio_wsincband_finalize;
       
   170 
       
   171   /* FIXME: Don't use the complete possible range but restrict the upper boundary
       
   172    * so automatically generated UIs can use a slider */
       
   173   g_object_class_install_property (gobject_class, PROP_LOWER_FREQUENCY,
       
   174       g_param_spec_float ("lower-frequency", "Lower Frequency",
       
   175           "Cut-off lower frequency (Hz)", 0.0, 100000.0, 0,
       
   176           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   177   g_object_class_install_property (gobject_class, PROP_UPPER_FREQUENCY,
       
   178       g_param_spec_float ("upper-frequency", "Upper Frequency",
       
   179           "Cut-off upper frequency (Hz)", 0.0, 100000.0, 0,
       
   180           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   181   g_object_class_install_property (gobject_class, PROP_LENGTH,
       
   182       g_param_spec_int ("length", "Length",
       
   183           "Filter kernel length, will be rounded to the next odd number", 3,
       
   184           50000, 101,
       
   185           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   186 
       
   187   g_object_class_install_property (gobject_class, PROP_MODE,
       
   188       g_param_spec_enum ("mode", "Mode",
       
   189           "Band pass or band reject mode", GST_TYPE_AUDIO_WSINC_BAND_MODE,
       
   190           MODE_BAND_PASS,
       
   191           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   192 
       
   193   g_object_class_install_property (gobject_class, PROP_WINDOW,
       
   194       g_param_spec_enum ("window", "Window",
       
   195           "Window function to use", GST_TYPE_AUDIO_WSINC_BAND_WINDOW,
       
   196           WINDOW_HAMMING,
       
   197           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   198 
       
   199   filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_wsincband_setup);
       
   200 }
       
   201 
       
   202 static void
       
   203 gst_audio_wsincband_init (GstAudioWSincBand * self,
       
   204     GstAudioWSincBandClass * g_class)
       
   205 {
       
   206   self->kernel_length = 101;
       
   207   self->lower_frequency = 0.0;
       
   208   self->upper_frequency = 0.0;
       
   209   self->mode = MODE_BAND_PASS;
       
   210   self->window = WINDOW_HAMMING;
       
   211 
       
   212   self->lock = g_mutex_new ();
       
   213 }
       
   214 
       
   215 static void
       
   216 gst_audio_wsincband_build_kernel (GstAudioWSincBand * self)
       
   217 {
       
   218   gint i = 0;
       
   219   gdouble sum = 0.0;
       
   220   gint len = 0;
       
   221   gdouble *kernel_lp, *kernel_hp;
       
   222   gdouble w;
       
   223   gdouble *kernel;
       
   224 
       
   225   len = self->kernel_length;
       
   226 
       
   227   if (GST_AUDIO_FILTER (self)->format.rate == 0) {
       
   228     GST_DEBUG ("rate not set yet");
       
   229     return;
       
   230   }
       
   231 
       
   232   if (GST_AUDIO_FILTER (self)->format.channels == 0) {
       
   233     GST_DEBUG ("channels not set yet");
       
   234     return;
       
   235   }
       
   236 
       
   237   /* Clamp frequencies */
       
   238   self->lower_frequency =
       
   239       CLAMP (self->lower_frequency, 0.0,
       
   240       GST_AUDIO_FILTER (self)->format.rate / 2);
       
   241   self->upper_frequency =
       
   242       CLAMP (self->upper_frequency, 0.0,
       
   243       GST_AUDIO_FILTER (self)->format.rate / 2);
       
   244   if (self->lower_frequency > self->upper_frequency) {
       
   245     gint tmp = self->lower_frequency;
       
   246 
       
   247     self->lower_frequency = self->upper_frequency;
       
   248     self->upper_frequency = tmp;
       
   249   }
       
   250 
       
   251   GST_DEBUG ("gst_audio_wsincband: initializing filter kernel of length %d "
       
   252       "with lower frequency %.2lf Hz "
       
   253       ", upper frequency %.2lf Hz for mode %s",
       
   254       len, self->lower_frequency, self->upper_frequency,
       
   255       (self->mode == MODE_BAND_PASS) ? "band-pass" : "band-reject");
       
   256 
       
   257   /* fill the lp kernel */
       
   258   w = 2 * M_PI * (self->lower_frequency / GST_AUDIO_FILTER (self)->format.rate);
       
   259   kernel_lp = g_new (gdouble, len);
       
   260   for (i = 0; i < len; ++i) {
       
   261     if (i == len / 2)
       
   262       kernel_lp[i] = w;
       
   263     else
       
   264       kernel_lp[i] = sin (w * (i - len / 2))
       
   265           / (i - len / 2);
       
   266     /* Windowing */
       
   267     if (self->window == WINDOW_HAMMING)
       
   268       kernel_lp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
       
   269     else
       
   270       kernel_lp[i] *=
       
   271           (0.42 - 0.5 * cos (2 * M_PI * i / len) +
       
   272           0.08 * cos (4 * M_PI * i / len));
       
   273   }
       
   274 
       
   275   /* normalize for unity gain at DC */
       
   276   sum = 0.0;
       
   277   for (i = 0; i < len; ++i)
       
   278     sum += kernel_lp[i];
       
   279   for (i = 0; i < len; ++i)
       
   280     kernel_lp[i] /= sum;
       
   281 
       
   282   /* fill the hp kernel */
       
   283   w = 2 * M_PI * (self->upper_frequency / GST_AUDIO_FILTER (self)->format.rate);
       
   284   kernel_hp = g_new (gdouble, len);
       
   285   for (i = 0; i < len; ++i) {
       
   286     if (i == len / 2)
       
   287       kernel_hp[i] = w;
       
   288     else
       
   289       kernel_hp[i] = sin (w * (i - len / 2))
       
   290           / (i - len / 2);
       
   291     /* Windowing */
       
   292     if (self->window == WINDOW_HAMMING)
       
   293       kernel_hp[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
       
   294     else
       
   295       kernel_hp[i] *=
       
   296           (0.42 - 0.5 * cos (2 * M_PI * i / len) +
       
   297           0.08 * cos (4 * M_PI * i / len));
       
   298   }
       
   299 
       
   300   /* normalize for unity gain at DC */
       
   301   sum = 0.0;
       
   302   for (i = 0; i < len; ++i)
       
   303     sum += kernel_hp[i];
       
   304   for (i = 0; i < len; ++i)
       
   305     kernel_hp[i] /= sum;
       
   306 
       
   307   /* do spectral inversion to go from lowpass to highpass */
       
   308   for (i = 0; i < len; ++i)
       
   309     kernel_hp[i] = -kernel_hp[i];
       
   310   kernel_hp[len / 2] += 1;
       
   311 
       
   312   /* combine the two kernels */
       
   313   kernel = g_new (gdouble, len);
       
   314 
       
   315   for (i = 0; i < len; ++i)
       
   316     kernel[i] = kernel_lp[i] + kernel_hp[i];
       
   317 
       
   318   /* free the helper kernels */
       
   319   g_free (kernel_lp);
       
   320   g_free (kernel_hp);
       
   321 
       
   322   /* do spectral inversion to go from bandreject to bandpass
       
   323    * if specified */
       
   324   if (self->mode == MODE_BAND_PASS) {
       
   325     for (i = 0; i < len; ++i)
       
   326       kernel[i] = -kernel[i];
       
   327     kernel[len / 2] += 1;
       
   328   }
       
   329 
       
   330   gst_audio_fx_base_fir_filter_set_kernel (GST_AUDIO_FX_BASE_FIR_FILTER (self),
       
   331       kernel, self->kernel_length, (len - 1) / 2);
       
   332 }
       
   333 
       
   334 /* GstAudioFilter vmethod implementations */
       
   335 
       
   336 /* get notified of caps and plug in the correct process function */
       
   337 static gboolean
       
   338 gst_audio_wsincband_setup (GstAudioFilter * base, GstRingBufferSpec * format)
       
   339 {
       
   340   GstAudioWSincBand *self = GST_AUDIO_WSINC_BAND (base);
       
   341 
       
   342   gst_audio_wsincband_build_kernel (self);
       
   343 
       
   344   return GST_AUDIO_FILTER_CLASS (parent_class)->setup (base, format);
       
   345 }
       
   346 
       
   347 static void
       
   348 gst_audio_wsincband_finalize (GObject * object)
       
   349 {
       
   350   GstAudioWSincBand *self = GST_AUDIO_WSINC_BAND (object);
       
   351 
       
   352   g_mutex_free (self->lock);
       
   353   self->lock = NULL;
       
   354 
       
   355   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   356 }
       
   357 
       
   358 static void
       
   359 gst_audio_wsincband_set_property (GObject * object, guint prop_id,
       
   360     const GValue * value, GParamSpec * pspec)
       
   361 {
       
   362   GstAudioWSincBand *self = GST_AUDIO_WSINC_BAND (object);
       
   363 
       
   364   g_return_if_fail (GST_IS_AUDIO_WSINC_BAND (self));
       
   365 
       
   366   switch (prop_id) {
       
   367     case PROP_LENGTH:{
       
   368       gint val;
       
   369 
       
   370       g_mutex_lock (self->lock);
       
   371       val = g_value_get_int (value);
       
   372       if (val % 2 == 0)
       
   373         val++;
       
   374 
       
   375       if (val != self->kernel_length) {
       
   376         gst_audio_fx_base_fir_filter_push_residue (GST_AUDIO_FX_BASE_FIR_FILTER
       
   377             (self));
       
   378         self->kernel_length = val;
       
   379         gst_audio_wsincband_build_kernel (self);
       
   380       }
       
   381       g_mutex_unlock (self->lock);
       
   382       break;
       
   383     }
       
   384     case PROP_LOWER_FREQUENCY:
       
   385       g_mutex_lock (self->lock);
       
   386       self->lower_frequency = g_value_get_float (value);
       
   387       gst_audio_wsincband_build_kernel (self);
       
   388       g_mutex_unlock (self->lock);
       
   389       break;
       
   390     case PROP_UPPER_FREQUENCY:
       
   391       g_mutex_lock (self->lock);
       
   392       self->upper_frequency = g_value_get_float (value);
       
   393       gst_audio_wsincband_build_kernel (self);
       
   394       g_mutex_unlock (self->lock);
       
   395       break;
       
   396     case PROP_MODE:
       
   397       g_mutex_lock (self->lock);
       
   398       self->mode = g_value_get_enum (value);
       
   399       gst_audio_wsincband_build_kernel (self);
       
   400       g_mutex_unlock (self->lock);
       
   401       break;
       
   402     case PROP_WINDOW:
       
   403       g_mutex_lock (self->lock);
       
   404       self->window = g_value_get_enum (value);
       
   405       gst_audio_wsincband_build_kernel (self);
       
   406       g_mutex_unlock (self->lock);
       
   407       break;
       
   408     default:
       
   409       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   410       break;
       
   411   }
       
   412 }
       
   413 
       
   414 static void
       
   415 gst_audio_wsincband_get_property (GObject * object, guint prop_id,
       
   416     GValue * value, GParamSpec * pspec)
       
   417 {
       
   418   GstAudioWSincBand *self = GST_AUDIO_WSINC_BAND (object);
       
   419 
       
   420   switch (prop_id) {
       
   421     case PROP_LENGTH:
       
   422       g_value_set_int (value, self->kernel_length);
       
   423       break;
       
   424     case PROP_LOWER_FREQUENCY:
       
   425       g_value_set_float (value, self->lower_frequency);
       
   426       break;
       
   427     case PROP_UPPER_FREQUENCY:
       
   428       g_value_set_float (value, self->upper_frequency);
       
   429       break;
       
   430     case PROP_MODE:
       
   431       g_value_set_enum (value, self->mode);
       
   432       break;
       
   433     case PROP_WINDOW:
       
   434       g_value_set_enum (value, self->window);
       
   435       break;
       
   436     default:
       
   437       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   438       break;
       
   439   }
       
   440 }