gst_plugins_good/gst/audiofx/audiowsinclimit.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     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-audiowsinclimit
       
    33  *
       
    34  * Attenuates all frequencies above the cutoff frequency (low-pass) or all frequencies below the
       
    35  * cutoff frequency (high-pass). 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 lowpass and highpass 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 ! audiowsinclimit mode=low-pass frequency=1000 length=501 ! audioconvert ! alsasink
       
    47  * gst-launch filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audiowsinclimit mode=high-pass frequency=15000 length=501 ! audioconvert ! alsasink
       
    48  * gst-launch audiotestsrc wave=white-noise ! audioconvert ! audiowsinclimit mode=low-pass frequency=1000 length=10001 window=blackman ! 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 "audiowsinclimit.h"
       
    64 
       
    65 #define GST_CAT_DEFAULT gst_audio_wsinclimit_debug
       
    66 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
       
    67 
       
    68 enum
       
    69 {
       
    70   PROP_0,
       
    71   PROP_LENGTH,
       
    72   PROP_FREQUENCY,
       
    73   PROP_MODE,
       
    74   PROP_WINDOW
       
    75 };
       
    76 
       
    77 enum
       
    78 {
       
    79   MODE_LOW_PASS = 0,
       
    80   MODE_HIGH_PASS
       
    81 };
       
    82 
       
    83 #define GST_TYPE_AUDIO_WSINC_LIMIT_MODE (gst_audio_wsinclimit_mode_get_type ())
       
    84 static GType
       
    85 gst_audio_wsinclimit_mode_get_type (void)
       
    86 {
       
    87   static GType gtype = 0;
       
    88 
       
    89   if (gtype == 0) {
       
    90     static const GEnumValue values[] = {
       
    91       {MODE_LOW_PASS, "Low pass (default)",
       
    92           "low-pass"},
       
    93       {MODE_HIGH_PASS, "High pass",
       
    94           "high-pass"},
       
    95       {0, NULL, NULL}
       
    96     };
       
    97 
       
    98     gtype = g_enum_register_static ("GstAudioWSincLimitMode", values);
       
    99   }
       
   100   return gtype;
       
   101 }
       
   102 
       
   103 enum
       
   104 {
       
   105   WINDOW_HAMMING = 0,
       
   106   WINDOW_BLACKMAN
       
   107 };
       
   108 
       
   109 #define GST_TYPE_AUDIO_WSINC_LIMIT_WINDOW (gst_audio_wsinclimit_window_get_type ())
       
   110 static GType
       
   111 gst_audio_wsinclimit_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 ("GstAudioWSincLimitWindow", values);
       
   125   }
       
   126   return gtype;
       
   127 }
       
   128 
       
   129 #define DEBUG_INIT(bla) \
       
   130   GST_DEBUG_CATEGORY_INIT (gst_audio_wsinclimit_debug, "audiowsinclimit", 0, \
       
   131       "Low-pass and High-pass Windowed sinc filter plugin");
       
   132 
       
   133 GST_BOILERPLATE_FULL (GstAudioWSincLimit, gst_audio_wsinclimit, GstAudioFilter,
       
   134     GST_TYPE_AUDIO_FX_BASE_FIR_FILTER, DEBUG_INIT);
       
   135 
       
   136 static void gst_audio_wsinclimit_set_property (GObject * object, guint prop_id,
       
   137     const GValue * value, GParamSpec * pspec);
       
   138 static void gst_audio_wsinclimit_get_property (GObject * object, guint prop_id,
       
   139     GValue * value, GParamSpec * pspec);
       
   140 static void gst_audio_wsinclimit_finalize (GObject * object);
       
   141 
       
   142 static gboolean gst_audio_wsinclimit_setup (GstAudioFilter * base,
       
   143     GstRingBufferSpec * format);
       
   144 
       
   145 /* Element class */
       
   146 
       
   147 static void
       
   148 gst_audio_wsinclimit_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       "Low pass & high pass filter", "Filter/Effect/Audio",
       
   154       "Low pass and high pass 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_wsinclimit_class_init (GstAudioWSincLimitClass * klass)
       
   163 {
       
   164   GObjectClass *gobject_class = (GObjectClass *) klass;
       
   165   GstAudioFilterClass *filter_class = (GstAudioFilterClass *) klass;
       
   166 
       
   167   gobject_class->set_property = gst_audio_wsinclimit_set_property;
       
   168   gobject_class->get_property = gst_audio_wsinclimit_get_property;
       
   169   gobject_class->finalize = gst_audio_wsinclimit_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_FREQUENCY,
       
   174       g_param_spec_float ("cutoff", "Cutoff",
       
   175           "Cut-off Frequency (Hz)", 0.0, 100000.0, 0.0,
       
   176           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   177   g_object_class_install_property (gobject_class, PROP_LENGTH,
       
   178       g_param_spec_int ("length", "Length",
       
   179           "Filter kernel length, will be rounded to the next odd number",
       
   180           3, 50000, 101,
       
   181           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   182 
       
   183   g_object_class_install_property (gobject_class, PROP_MODE,
       
   184       g_param_spec_enum ("mode", "Mode",
       
   185           "Low pass or high pass mode", GST_TYPE_AUDIO_WSINC_LIMIT_MODE,
       
   186           MODE_LOW_PASS,
       
   187           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   188 
       
   189   g_object_class_install_property (gobject_class, PROP_WINDOW,
       
   190       g_param_spec_enum ("window", "Window",
       
   191           "Window function to use", GST_TYPE_AUDIO_WSINC_LIMIT_WINDOW,
       
   192           WINDOW_HAMMING,
       
   193           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
       
   194 
       
   195   filter_class->setup = GST_DEBUG_FUNCPTR (gst_audio_wsinclimit_setup);
       
   196 }
       
   197 
       
   198 static void
       
   199 gst_audio_wsinclimit_init (GstAudioWSincLimit * self,
       
   200     GstAudioWSincLimitClass * g_class)
       
   201 {
       
   202   self->mode = MODE_LOW_PASS;
       
   203   self->window = WINDOW_HAMMING;
       
   204   self->kernel_length = 101;
       
   205   self->cutoff = 0.0;
       
   206 
       
   207   self->lock = g_mutex_new ();
       
   208 }
       
   209 
       
   210 static void
       
   211 gst_audio_wsinclimit_build_kernel (GstAudioWSincLimit * self)
       
   212 {
       
   213   gint i = 0;
       
   214   gdouble sum = 0.0;
       
   215   gint len = 0;
       
   216   gdouble w;
       
   217   gdouble *kernel = NULL;
       
   218 
       
   219   len = self->kernel_length;
       
   220 
       
   221   if (GST_AUDIO_FILTER (self)->format.rate == 0) {
       
   222     GST_DEBUG ("rate not set yet");
       
   223     return;
       
   224   }
       
   225 
       
   226   if (GST_AUDIO_FILTER (self)->format.channels == 0) {
       
   227     GST_DEBUG ("channels not set yet");
       
   228     return;
       
   229   }
       
   230 
       
   231   /* Clamp cutoff frequency between 0 and the nyquist frequency */
       
   232   self->cutoff =
       
   233       CLAMP (self->cutoff, 0.0, GST_AUDIO_FILTER (self)->format.rate / 2);
       
   234 
       
   235   GST_DEBUG ("gst_audio_wsinclimit_: initializing filter kernel of length %d "
       
   236       "with cutoff %.2lf Hz "
       
   237       "for mode %s",
       
   238       len, self->cutoff,
       
   239       (self->mode == MODE_LOW_PASS) ? "low-pass" : "high-pass");
       
   240 
       
   241   /* fill the kernel */
       
   242   w = 2 * M_PI * (self->cutoff / GST_AUDIO_FILTER (self)->format.rate);
       
   243 
       
   244   kernel = g_new (gdouble, len);
       
   245 
       
   246   for (i = 0; i < len; ++i) {
       
   247     if (i == len / 2)
       
   248       kernel[i] = w;
       
   249     else
       
   250       kernel[i] = sin (w * (i - len / 2)) / (i - len / 2);
       
   251     /* windowing */
       
   252     if (self->window == WINDOW_HAMMING)
       
   253       kernel[i] *= (0.54 - 0.46 * cos (2 * M_PI * i / len));
       
   254     else
       
   255       kernel[i] *= (0.42 - 0.5 * cos (2 * M_PI * i / len) +
       
   256           0.08 * cos (4 * M_PI * i / len));
       
   257   }
       
   258 
       
   259   /* normalize for unity gain at DC */
       
   260   for (i = 0; i < len; ++i)
       
   261     sum += kernel[i];
       
   262   for (i = 0; i < len; ++i)
       
   263     kernel[i] /= sum;
       
   264 
       
   265   /* convert to highpass if specified */
       
   266   if (self->mode == MODE_HIGH_PASS) {
       
   267     for (i = 0; i < len; ++i)
       
   268       kernel[i] = -kernel[i];
       
   269     kernel[len / 2] += 1.0;
       
   270   }
       
   271 
       
   272   gst_audio_fx_base_fir_filter_set_kernel (GST_AUDIO_FX_BASE_FIR_FILTER (self),
       
   273       kernel, self->kernel_length, (len - 1) / 2);
       
   274 }
       
   275 
       
   276 /* GstAudioFilter vmethod implementations */
       
   277 
       
   278 /* get notified of caps and plug in the correct process function */
       
   279 static gboolean
       
   280 gst_audio_wsinclimit_setup (GstAudioFilter * base, GstRingBufferSpec * format)
       
   281 {
       
   282   GstAudioWSincLimit *self = GST_AUDIO_WSINC_LIMIT (base);
       
   283 
       
   284   gst_audio_wsinclimit_build_kernel (self);
       
   285 
       
   286   return GST_AUDIO_FILTER_CLASS (parent_class)->setup (base, format);
       
   287 }
       
   288 
       
   289 static void
       
   290 gst_audio_wsinclimit_finalize (GObject * object)
       
   291 {
       
   292   GstAudioWSincLimit *self = GST_AUDIO_WSINC_LIMIT (object);
       
   293 
       
   294   g_mutex_free (self->lock);
       
   295   self->lock = NULL;
       
   296 
       
   297   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   298 }
       
   299 
       
   300 static void
       
   301 gst_audio_wsinclimit_set_property (GObject * object, guint prop_id,
       
   302     const GValue * value, GParamSpec * pspec)
       
   303 {
       
   304   GstAudioWSincLimit *self = GST_AUDIO_WSINC_LIMIT (object);
       
   305 
       
   306   g_return_if_fail (GST_IS_AUDIO_WSINC_LIMIT (self));
       
   307 
       
   308   switch (prop_id) {
       
   309     case PROP_LENGTH:{
       
   310       gint val;
       
   311 
       
   312       g_mutex_lock (self->lock);
       
   313       val = g_value_get_int (value);
       
   314       if (val % 2 == 0)
       
   315         val++;
       
   316 
       
   317       if (val != self->kernel_length) {
       
   318         gst_audio_fx_base_fir_filter_push_residue (GST_AUDIO_FX_BASE_FIR_FILTER
       
   319             (self));
       
   320         self->kernel_length = val;
       
   321         gst_audio_wsinclimit_build_kernel (self);
       
   322       }
       
   323       g_mutex_unlock (self->lock);
       
   324       break;
       
   325     }
       
   326     case PROP_FREQUENCY:
       
   327       g_mutex_lock (self->lock);
       
   328       self->cutoff = g_value_get_float (value);
       
   329       gst_audio_wsinclimit_build_kernel (self);
       
   330       g_mutex_unlock (self->lock);
       
   331       break;
       
   332     case PROP_MODE:
       
   333       g_mutex_lock (self->lock);
       
   334       self->mode = g_value_get_enum (value);
       
   335       gst_audio_wsinclimit_build_kernel (self);
       
   336       g_mutex_unlock (self->lock);
       
   337       break;
       
   338     case PROP_WINDOW:
       
   339       g_mutex_lock (self->lock);
       
   340       self->window = g_value_get_enum (value);
       
   341       gst_audio_wsinclimit_build_kernel (self);
       
   342       g_mutex_unlock (self->lock);
       
   343       break;
       
   344     default:
       
   345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   346       break;
       
   347   }
       
   348 }
       
   349 
       
   350 static void
       
   351 gst_audio_wsinclimit_get_property (GObject * object, guint prop_id,
       
   352     GValue * value, GParamSpec * pspec)
       
   353 {
       
   354   GstAudioWSincLimit *self = GST_AUDIO_WSINC_LIMIT (object);
       
   355 
       
   356   switch (prop_id) {
       
   357     case PROP_LENGTH:
       
   358       g_value_set_int (value, self->kernel_length);
       
   359       break;
       
   360     case PROP_FREQUENCY:
       
   361       g_value_set_float (value, self->cutoff);
       
   362       break;
       
   363     case PROP_MODE:
       
   364       g_value_set_enum (value, self->mode);
       
   365       break;
       
   366     case PROP_WINDOW:
       
   367       g_value_set_enum (value, self->window);
       
   368       break;
       
   369     default:
       
   370       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   371       break;
       
   372   }
       
   373 }