gst_plugins_good/gst/audiofx/audiodynamic.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     1 /* 
       
     2  * GStreamer
       
     3  * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public
       
    16  * License along with this library; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 /**
       
    22  * SECTION:element-audiodynamic
       
    23  *
       
    24  * This element can act as a compressor or expander. A compressor changes the
       
    25  * amplitude of all samples above a specific threshold with a specific ratio,
       
    26  * a expander does the same for all samples below a specific threshold. If
       
    27  * soft-knee mode is selected the ratio is applied smoothly.
       
    28  *
       
    29  * <refsect2>
       
    30  * <title>Example launch line</title>
       
    31  * |[
       
    32  * gst-launch audiotestsrc wave=saw ! audiodynamic characteristics=soft-knee mode=compressor threshold=0.5 rate=0.5 ! alsasink
       
    33  * gst-launch filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audiodynamic characteristics=hard-knee mode=expander threshold=0.2 rate=4.0 ! alsasink
       
    34  * gst-launch audiotestsrc wave=saw ! audioconvert ! audiodynamic ! audioconvert ! alsasink
       
    35  * ]|
       
    36  * </refsect2>
       
    37  */
       
    38 
       
    39 /* TODO: Implement attack and release parameters */
       
    40 
       
    41 #ifdef HAVE_CONFIG_H
       
    42 #include "config.h"
       
    43 #endif
       
    44 
       
    45 #include <gst/gst.h>
       
    46 #include <gst/base/gstbasetransform.h>
       
    47 #include <gst/audio/audio.h>
       
    48 #include <gst/audio/gstaudiofilter.h>
       
    49 #include <gst/controller/gstcontroller.h>
       
    50 
       
    51 #include "audiodynamic.h"
       
    52 
       
    53 #define GST_CAT_DEFAULT gst_audio_dynamic_debug
       
    54 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
       
    55 
       
    56 static const GstElementDetails element_details =
       
    57 GST_ELEMENT_DETAILS ("Dynamic range controller",
       
    58     "Filter/Effect/Audio",
       
    59     "Compressor and Expander",
       
    60     "Sebastian Dröge <slomo@circular-chaos.org>");
       
    61 
       
    62 /* Filter signals and args */
       
    63 enum
       
    64 {
       
    65   /* FILL ME */
       
    66   LAST_SIGNAL
       
    67 };
       
    68 
       
    69 enum
       
    70 {
       
    71   PROP_0,
       
    72   PROP_CHARACTERISTICS,
       
    73   PROP_MODE,
       
    74   PROP_THRESHOLD,
       
    75   PROP_RATIO
       
    76 };
       
    77 
       
    78 #define ALLOWED_CAPS \
       
    79     "audio/x-raw-int,"                                                \
       
    80     " depth=(int)16,"                                                 \
       
    81     " width=(int)16,"                                                 \
       
    82     " endianness=(int)BYTE_ORDER,"                                    \
       
    83     " signed=(bool)TRUE,"                                             \
       
    84     " rate=(int)[1,MAX],"                                             \
       
    85     " channels=(int)[1,MAX]; "                                        \
       
    86     "audio/x-raw-float,"                                              \
       
    87     " width=(int)32,"                                                 \
       
    88     " endianness=(int)BYTE_ORDER,"                                    \
       
    89     " rate=(int)[1,MAX],"                                             \
       
    90     " channels=(int)[1,MAX]"
       
    91 
       
    92 #define DEBUG_INIT(bla) \
       
    93   GST_DEBUG_CATEGORY_INIT (gst_audio_dynamic_debug, "audiodynamic", 0, "audiodynamic element");
       
    94 
       
    95 GST_BOILERPLATE_FULL (GstAudioDynamic, gst_audio_dynamic, GstAudioFilter,
       
    96     GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
       
    97 
       
    98 static void gst_audio_dynamic_set_property (GObject * object, guint prop_id,
       
    99     const GValue * value, GParamSpec * pspec);
       
   100 static void gst_audio_dynamic_get_property (GObject * object, guint prop_id,
       
   101     GValue * value, GParamSpec * pspec);
       
   102 
       
   103 static gboolean gst_audio_dynamic_setup (GstAudioFilter * filter,
       
   104     GstRingBufferSpec * format);
       
   105 static GstFlowReturn gst_audio_dynamic_transform_ip (GstBaseTransform * base,
       
   106     GstBuffer * buf);
       
   107 
       
   108 static void
       
   109 gst_audio_dynamic_transform_hard_knee_compressor_int (GstAudioDynamic * filter,
       
   110     gint16 * data, guint num_samples);
       
   111 static void
       
   112 gst_audio_dynamic_transform_hard_knee_compressor_float (GstAudioDynamic *
       
   113     filter, gfloat * data, guint num_samples);
       
   114 static void
       
   115 gst_audio_dynamic_transform_soft_knee_compressor_int (GstAudioDynamic * filter,
       
   116     gint16 * data, guint num_samples);
       
   117 static void
       
   118 gst_audio_dynamic_transform_soft_knee_compressor_float (GstAudioDynamic *
       
   119     filter, gfloat * data, guint num_samples);
       
   120 static void gst_audio_dynamic_transform_hard_knee_expander_int (GstAudioDynamic
       
   121     * filter, gint16 * data, guint num_samples);
       
   122 static void
       
   123 gst_audio_dynamic_transform_hard_knee_expander_float (GstAudioDynamic * filter,
       
   124     gfloat * data, guint num_samples);
       
   125 static void gst_audio_dynamic_transform_soft_knee_expander_int (GstAudioDynamic
       
   126     * filter, gint16 * data, guint num_samples);
       
   127 static void
       
   128 gst_audio_dynamic_transform_soft_knee_expander_float (GstAudioDynamic * filter,
       
   129     gfloat * data, guint num_samples);
       
   130 
       
   131 static GstAudioDynamicProcessFunc process_functions[] = {
       
   132   (GstAudioDynamicProcessFunc)
       
   133       gst_audio_dynamic_transform_hard_knee_compressor_int,
       
   134   (GstAudioDynamicProcessFunc)
       
   135       gst_audio_dynamic_transform_hard_knee_compressor_float,
       
   136   (GstAudioDynamicProcessFunc)
       
   137       gst_audio_dynamic_transform_soft_knee_compressor_int,
       
   138   (GstAudioDynamicProcessFunc)
       
   139       gst_audio_dynamic_transform_soft_knee_compressor_float,
       
   140   (GstAudioDynamicProcessFunc)
       
   141       gst_audio_dynamic_transform_hard_knee_expander_int,
       
   142   (GstAudioDynamicProcessFunc)
       
   143       gst_audio_dynamic_transform_hard_knee_expander_float,
       
   144   (GstAudioDynamicProcessFunc)
       
   145       gst_audio_dynamic_transform_soft_knee_expander_int,
       
   146   (GstAudioDynamicProcessFunc)
       
   147   gst_audio_dynamic_transform_soft_knee_expander_float
       
   148 };
       
   149 
       
   150 enum
       
   151 {
       
   152   CHARACTERISTICS_HARD_KNEE = 0,
       
   153   CHARACTERISTICS_SOFT_KNEE
       
   154 };
       
   155 
       
   156 #define GST_TYPE_AUDIO_DYNAMIC_CHARACTERISTICS (gst_audio_dynamic_characteristics_get_type ())
       
   157 static GType
       
   158 gst_audio_dynamic_characteristics_get_type (void)
       
   159 {
       
   160   static GType gtype = 0;
       
   161 
       
   162   if (gtype == 0) {
       
   163     static const GEnumValue values[] = {
       
   164       {CHARACTERISTICS_HARD_KNEE, "Hard Knee (default)",
       
   165           "hard-knee"},
       
   166       {CHARACTERISTICS_SOFT_KNEE, "Soft Knee (smooth)",
       
   167           "soft-knee"},
       
   168       {0, NULL, NULL}
       
   169     };
       
   170 
       
   171     gtype = g_enum_register_static ("GstAudioDynamicCharacteristics", values);
       
   172   }
       
   173   return gtype;
       
   174 }
       
   175 
       
   176 enum
       
   177 {
       
   178   MODE_COMPRESSOR = 0,
       
   179   MODE_EXPANDER
       
   180 };
       
   181 
       
   182 #define GST_TYPE_AUDIO_DYNAMIC_MODE (gst_audio_dynamic_mode_get_type ())
       
   183 static GType
       
   184 gst_audio_dynamic_mode_get_type (void)
       
   185 {
       
   186   static GType gtype = 0;
       
   187 
       
   188   if (gtype == 0) {
       
   189     static const GEnumValue values[] = {
       
   190       {MODE_COMPRESSOR, "Compressor (default)",
       
   191           "compressor"},
       
   192       {MODE_EXPANDER, "Expander", "expander"},
       
   193       {0, NULL, NULL}
       
   194     };
       
   195 
       
   196     gtype = g_enum_register_static ("GstAudioDynamicMode", values);
       
   197   }
       
   198   return gtype;
       
   199 }
       
   200 
       
   201 static gboolean
       
   202 gst_audio_dynamic_set_process_function (GstAudioDynamic * filter)
       
   203 {
       
   204   gint func_index;
       
   205 
       
   206   func_index = (filter->mode == MODE_COMPRESSOR) ? 0 : 4;
       
   207   func_index += (filter->characteristics == CHARACTERISTICS_HARD_KNEE) ? 0 : 2;
       
   208   func_index +=
       
   209       (GST_AUDIO_FILTER (filter)->format.type == GST_BUFTYPE_FLOAT) ? 1 : 0;
       
   210 
       
   211   if (func_index >= 0 && func_index < 8) {
       
   212     filter->process = process_functions[func_index];
       
   213     return TRUE;
       
   214   }
       
   215 
       
   216   return FALSE;
       
   217 }
       
   218 
       
   219 /* GObject vmethod implementations */
       
   220 
       
   221 static void
       
   222 gst_audio_dynamic_base_init (gpointer klass)
       
   223 {
       
   224   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
       
   225   GstCaps *caps;
       
   226 
       
   227   gst_element_class_set_details (element_class, &element_details);
       
   228 
       
   229   caps = gst_caps_from_string (ALLOWED_CAPS);
       
   230   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
       
   231       caps);
       
   232   gst_caps_unref (caps);
       
   233 }
       
   234 
       
   235 static void
       
   236 gst_audio_dynamic_class_init (GstAudioDynamicClass * klass)
       
   237 {
       
   238   GObjectClass *gobject_class;
       
   239 
       
   240   gobject_class = (GObjectClass *) klass;
       
   241   gobject_class->set_property = gst_audio_dynamic_set_property;
       
   242   gobject_class->get_property = gst_audio_dynamic_get_property;
       
   243 
       
   244   g_object_class_install_property (gobject_class, PROP_CHARACTERISTICS,
       
   245       g_param_spec_enum ("characteristics", "Characteristics",
       
   246           "Selects whether the ratio should be applied smooth (soft-knee) "
       
   247           "or hard (hard-knee).",
       
   248           GST_TYPE_AUDIO_DYNAMIC_CHARACTERISTICS, CHARACTERISTICS_HARD_KNEE,
       
   249           G_PARAM_READWRITE));
       
   250 
       
   251   g_object_class_install_property (gobject_class, PROP_MODE,
       
   252       g_param_spec_enum ("mode", "Mode",
       
   253           "Selects whether the filter should work on loud samples (compressor) or"
       
   254           "quiet samples (expander).",
       
   255           GST_TYPE_AUDIO_DYNAMIC_MODE, MODE_COMPRESSOR, G_PARAM_READWRITE));
       
   256 
       
   257   g_object_class_install_property (gobject_class, PROP_THRESHOLD,
       
   258       g_param_spec_float ("threshold", "Threshold",
       
   259           "Threshold until the filter is activated", 0.0, 1.0,
       
   260           0.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   261 
       
   262   g_object_class_install_property (gobject_class, PROP_RATIO,
       
   263       g_param_spec_float ("ratio", "Ratio",
       
   264           "Ratio that should be applied", 0.0, G_MAXFLOAT,
       
   265           1.0, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
       
   266 
       
   267   GST_AUDIO_FILTER_CLASS (klass)->setup =
       
   268       GST_DEBUG_FUNCPTR (gst_audio_dynamic_setup);
       
   269   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
       
   270       GST_DEBUG_FUNCPTR (gst_audio_dynamic_transform_ip);
       
   271 }
       
   272 
       
   273 static void
       
   274 gst_audio_dynamic_init (GstAudioDynamic * filter, GstAudioDynamicClass * klass)
       
   275 {
       
   276   filter->ratio = 1.0;
       
   277   filter->threshold = 0.0;
       
   278   filter->characteristics = CHARACTERISTICS_HARD_KNEE;
       
   279   filter->mode = MODE_COMPRESSOR;
       
   280   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
       
   281   gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
       
   282 }
       
   283 
       
   284 static void
       
   285 gst_audio_dynamic_set_property (GObject * object, guint prop_id,
       
   286     const GValue * value, GParamSpec * pspec)
       
   287 {
       
   288   GstAudioDynamic *filter = GST_AUDIO_DYNAMIC (object);
       
   289 
       
   290   switch (prop_id) {
       
   291     case PROP_CHARACTERISTICS:
       
   292       filter->characteristics = g_value_get_enum (value);
       
   293       gst_audio_dynamic_set_process_function (filter);
       
   294       break;
       
   295     case PROP_MODE:
       
   296       filter->mode = g_value_get_enum (value);
       
   297       gst_audio_dynamic_set_process_function (filter);
       
   298       break;
       
   299     case PROP_THRESHOLD:
       
   300       filter->threshold = g_value_get_float (value);
       
   301       break;
       
   302     case PROP_RATIO:
       
   303       filter->ratio = g_value_get_float (value);
       
   304       break;
       
   305     default:
       
   306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   307       break;
       
   308   }
       
   309 }
       
   310 
       
   311 static void
       
   312 gst_audio_dynamic_get_property (GObject * object, guint prop_id,
       
   313     GValue * value, GParamSpec * pspec)
       
   314 {
       
   315   GstAudioDynamic *filter = GST_AUDIO_DYNAMIC (object);
       
   316 
       
   317   switch (prop_id) {
       
   318     case PROP_CHARACTERISTICS:
       
   319       g_value_set_enum (value, filter->characteristics);
       
   320       break;
       
   321     case PROP_MODE:
       
   322       g_value_set_enum (value, filter->mode);
       
   323       break;
       
   324     case PROP_THRESHOLD:
       
   325       g_value_set_float (value, filter->threshold);
       
   326       break;
       
   327     case PROP_RATIO:
       
   328       g_value_set_float (value, filter->ratio);
       
   329       break;
       
   330     default:
       
   331       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   332       break;
       
   333   }
       
   334 }
       
   335 
       
   336 /* GstAudioFilter vmethod implementations */
       
   337 
       
   338 static gboolean
       
   339 gst_audio_dynamic_setup (GstAudioFilter * base, GstRingBufferSpec * format)
       
   340 {
       
   341   GstAudioDynamic *filter = GST_AUDIO_DYNAMIC (base);
       
   342   gboolean ret = TRUE;
       
   343 
       
   344   ret = gst_audio_dynamic_set_process_function (filter);
       
   345 
       
   346   return ret;
       
   347 }
       
   348 
       
   349 static void
       
   350 gst_audio_dynamic_transform_hard_knee_compressor_int (GstAudioDynamic * filter,
       
   351     gint16 * data, guint num_samples)
       
   352 {
       
   353   glong val;
       
   354   glong thr_p = filter->threshold * G_MAXINT16;
       
   355   glong thr_n = filter->threshold * G_MININT16;
       
   356 
       
   357   /* Nothing to do for us if ratio is 1.0 or if the threshold
       
   358    * equals 1.0. */
       
   359   if (filter->threshold == 1.0 || filter->ratio == 1.0)
       
   360     return;
       
   361 
       
   362   for (; num_samples; num_samples--) {
       
   363     val = *data;
       
   364 
       
   365     if (val > thr_p) {
       
   366       val = thr_p + (val - thr_p) * filter->ratio;
       
   367     } else if (val < thr_n) {
       
   368       val = thr_n + (val - thr_n) * filter->ratio;
       
   369     }
       
   370     *data++ = (gint16) CLAMP (val, G_MININT16, G_MAXINT16);
       
   371   }
       
   372 }
       
   373 
       
   374 static void
       
   375 gst_audio_dynamic_transform_hard_knee_compressor_float (GstAudioDynamic *
       
   376     filter, gfloat * data, guint num_samples)
       
   377 {
       
   378   gdouble val, threshold = filter->threshold;
       
   379 
       
   380   /* Nothing to do for us if ratio == 1.0.
       
   381    * As float values can be above 1.0 we have to do something
       
   382    * if threshold is greater than 1.0. */
       
   383   if (filter->ratio == 1.0)
       
   384     return;
       
   385 
       
   386   for (; num_samples; num_samples--) {
       
   387     val = *data;
       
   388 
       
   389     if (val > threshold) {
       
   390       val = threshold + (val - threshold) * filter->ratio;
       
   391     } else if (val < -threshold) {
       
   392       val = -threshold + (val + threshold) * filter->ratio;
       
   393     }
       
   394     *data++ = (gfloat) val;
       
   395   }
       
   396 }
       
   397 
       
   398 static void
       
   399 gst_audio_dynamic_transform_soft_knee_compressor_int (GstAudioDynamic * filter,
       
   400     gint16 * data, guint num_samples)
       
   401 {
       
   402   glong val;
       
   403   glong thr_p = filter->threshold * G_MAXINT16;
       
   404   glong thr_n = filter->threshold * G_MININT16;
       
   405   gdouble a_p, b_p, c_p;
       
   406   gdouble a_n, b_n, c_n;
       
   407 
       
   408   /* Nothing to do for us if ratio is 1.0 or if the threshold
       
   409    * equals 1.0. */
       
   410   if (filter->threshold == 1.0 || filter->ratio == 1.0)
       
   411     return;
       
   412 
       
   413   /* We build a 2nd degree polynomial here for
       
   414    * values greater than threshold or small than
       
   415    * -threshold with:
       
   416    * f(t) = t, f'(t) = 1, f'(m) = r
       
   417    * =>
       
   418    * a = (1-r)/(2*(t-m))
       
   419    * b = (r*t - m)/(t-m)
       
   420    * c = t * (1 - b - a*t)
       
   421    * f(x) = ax^2 + bx + c
       
   422    */
       
   423 
       
   424   /* shouldn't happen because this would only be the case
       
   425    * for threshold == 1.0 which we catch above */
       
   426   g_assert (thr_p - G_MAXINT16 != 0);
       
   427   g_assert (thr_n - G_MININT != 0);
       
   428 
       
   429   a_p = (1 - filter->ratio) / (2 * (thr_p - G_MAXINT16));
       
   430   b_p = (filter->ratio * thr_p - G_MAXINT16) / (thr_p - G_MAXINT16);
       
   431   c_p = thr_p * (1 - b_p - a_p * thr_p);
       
   432   a_n = (1 - filter->ratio) / (2 * (thr_n - G_MININT16));
       
   433   b_n = (filter->ratio * thr_n - G_MININT16) / (thr_n - G_MININT16);
       
   434   c_n = thr_n * (1 - b_n - a_n * thr_n);
       
   435 
       
   436   for (; num_samples; num_samples--) {
       
   437     val = *data;
       
   438 
       
   439     if (val > thr_p) {
       
   440       val = a_p * val * val + b_p * val + c_p;
       
   441     } else if (val < thr_n) {
       
   442       val = a_n * val * val + b_n * val + c_n;
       
   443     }
       
   444     *data++ = (gint16) CLAMP (val, G_MININT16, G_MAXINT16);
       
   445   }
       
   446 }
       
   447 
       
   448 static void
       
   449 gst_audio_dynamic_transform_soft_knee_compressor_float (GstAudioDynamic *
       
   450     filter, gfloat * data, guint num_samples)
       
   451 {
       
   452   gdouble val;
       
   453   gdouble threshold = filter->threshold;
       
   454   gdouble a_p, b_p, c_p;
       
   455   gdouble a_n, b_n, c_n;
       
   456 
       
   457   /* Nothing to do for us if ratio == 1.0.
       
   458    * As float values can be above 1.0 we have to do something
       
   459    * if threshold is greater than 1.0. */
       
   460   if (filter->ratio == 1.0)
       
   461     return;
       
   462 
       
   463   /* We build a 2nd degree polynomial here for
       
   464    * values greater than threshold or small than
       
   465    * -threshold with:
       
   466    * f(t) = t, f'(t) = 1, f'(m) = r
       
   467    * =>
       
   468    * a = (1-r)/(2*(t-m))
       
   469    * b = (r*t - m)/(t-m)
       
   470    * c = t * (1 - b - a*t)
       
   471    * f(x) = ax^2 + bx + c
       
   472    */
       
   473 
       
   474   /* FIXME: If treshold is the same as the maximum
       
   475    * we need to raise it a bit to prevent
       
   476    * division by zero. */
       
   477   if (threshold == 1.0)
       
   478     threshold = 1.0 + 0.00001;
       
   479 
       
   480   a_p = (1.0 - filter->ratio) / (2.0 * (threshold - 1.0));
       
   481   b_p = (filter->ratio * threshold - 1.0) / (threshold - 1.0);
       
   482   c_p = threshold * (1.0 - b_p - a_p * threshold);
       
   483   a_n = (1.0 - filter->ratio) / (2.0 * (-threshold + 1.0));
       
   484   b_n = (-filter->ratio * threshold + 1.0) / (-threshold + 1.0);
       
   485   c_n = -threshold * (1.0 - b_n + a_n * threshold);
       
   486 
       
   487   for (; num_samples; num_samples--) {
       
   488     val = *data;
       
   489 
       
   490     if (val > 1.0) {
       
   491       val = 1.0 + (val - 1.0) * filter->ratio;
       
   492     } else if (val > threshold) {
       
   493       val = a_p * val * val + b_p * val + c_p;
       
   494     } else if (val < -1.0) {
       
   495       val = -1.0 + (val + 1.0) * filter->ratio;
       
   496     } else if (val < -threshold) {
       
   497       val = a_n * val * val + b_n * val + c_n;
       
   498     }
       
   499     *data++ = (gfloat) val;
       
   500   }
       
   501 }
       
   502 
       
   503 static void
       
   504 gst_audio_dynamic_transform_hard_knee_expander_int (GstAudioDynamic * filter,
       
   505     gint16 * data, guint num_samples)
       
   506 {
       
   507   glong val;
       
   508   glong thr_p = filter->threshold * G_MAXINT16;
       
   509   glong thr_n = filter->threshold * G_MININT16;
       
   510   gdouble zero_p, zero_n;
       
   511 
       
   512   /* Nothing to do for us here if threshold equals 0.0
       
   513    * or ratio equals 1.0 */
       
   514   if (filter->threshold == 0.0 || filter->ratio == 1.0)
       
   515     return;
       
   516 
       
   517   /* zero crossing of our function */
       
   518   if (filter->ratio != 0.0) {
       
   519     zero_p = thr_p - thr_p / filter->ratio;
       
   520     zero_n = thr_n - thr_n / filter->ratio;
       
   521   } else {
       
   522     zero_p = zero_n = 0.0;
       
   523   }
       
   524 
       
   525   if (zero_p < 0.0)
       
   526     zero_p = 0.0;
       
   527   if (zero_n > 0.0)
       
   528     zero_n = 0.0;
       
   529 
       
   530   for (; num_samples; num_samples--) {
       
   531     val = *data;
       
   532 
       
   533     if (val < thr_p && val > zero_p) {
       
   534       val = filter->ratio * val + thr_p * (1 - filter->ratio);
       
   535     } else if ((val <= zero_p && val > 0) || (val >= zero_n && val < 0)) {
       
   536       val = 0;
       
   537     } else if (val > thr_n && val < zero_n) {
       
   538       val = filter->ratio * val + thr_n * (1 - filter->ratio);
       
   539     }
       
   540     *data++ = (gint16) CLAMP (val, G_MININT16, G_MAXINT16);
       
   541   }
       
   542 }
       
   543 
       
   544 static void
       
   545 gst_audio_dynamic_transform_hard_knee_expander_float (GstAudioDynamic * filter,
       
   546     gfloat * data, guint num_samples)
       
   547 {
       
   548   gdouble val, threshold = filter->threshold, zero;
       
   549 
       
   550   /* Nothing to do for us here if threshold equals 0.0
       
   551    * or ratio equals 1.0 */
       
   552   if (filter->threshold == 0.0 || filter->ratio == 1.0)
       
   553     return;
       
   554 
       
   555   /* zero crossing of our function */
       
   556   if (filter->ratio != 0.0)
       
   557     zero = threshold - threshold / filter->ratio;
       
   558   else
       
   559     zero = 0.0;
       
   560 
       
   561   if (zero < 0.0)
       
   562     zero = 0.0;
       
   563 
       
   564   for (; num_samples; num_samples--) {
       
   565     val = *data;
       
   566 
       
   567     if (val < threshold && val > zero) {
       
   568       val = filter->ratio * val + threshold * (1.0 - filter->ratio);
       
   569     } else if ((val <= zero && val > 0.0) || (val >= -zero && val < 0.0)) {
       
   570       val = 0.0;
       
   571     } else if (val > -threshold && val < -zero) {
       
   572       val = filter->ratio * val - threshold * (1.0 - filter->ratio);
       
   573     }
       
   574     *data++ = (gfloat) val;
       
   575   }
       
   576 }
       
   577 
       
   578 static void
       
   579 gst_audio_dynamic_transform_soft_knee_expander_int (GstAudioDynamic * filter,
       
   580     gint16 * data, guint num_samples)
       
   581 {
       
   582   glong val;
       
   583   glong thr_p = filter->threshold * G_MAXINT16;
       
   584   glong thr_n = filter->threshold * G_MININT16;
       
   585   gdouble zero_p, zero_n;
       
   586   gdouble a_p, b_p, c_p;
       
   587   gdouble a_n, b_n, c_n;
       
   588 
       
   589   /* Nothing to do for us here if threshold equals 0.0
       
   590    * or ratio equals 1.0 */
       
   591   if (filter->threshold == 0.0 || filter->ratio == 1.0)
       
   592     return;
       
   593 
       
   594   /* zero crossing of our function */
       
   595   zero_p = (thr_p * (filter->ratio - 1.0)) / (1.0 + filter->ratio);
       
   596   zero_n = (thr_n * (filter->ratio - 1.0)) / (1.0 + filter->ratio);
       
   597 
       
   598   if (zero_p < 0.0)
       
   599     zero_p = 0.0;
       
   600   if (zero_n > 0.0)
       
   601     zero_n = 0.0;
       
   602 
       
   603   /* shouldn't happen as this would only happen
       
   604    * with threshold == 0.0 */
       
   605   g_assert (thr_p != 0);
       
   606   g_assert (thr_n != 0);
       
   607 
       
   608   /* We build a 2n degree polynomial here for values between
       
   609    * 0 and threshold or 0 and -threshold with:
       
   610    * f(t) = t, f'(t) = 1, f(z) = 0, f'(z) = r
       
   611    * z between 0 and t
       
   612    * =>
       
   613    * a = (1 - r^2) / (4 * t)
       
   614    * b = (1 + r^2) / 2
       
   615    * c = t * (1.0 - b - a*t)
       
   616    * f(x) = ax^2 + bx + c */
       
   617   a_p = (1.0 - filter->ratio * filter->ratio) / (4.0 * thr_p);
       
   618   b_p = (1.0 + filter->ratio * filter->ratio) / 2.0;
       
   619   c_p = thr_p * (1.0 - b_p - a_p * thr_p);
       
   620   a_n = (1.0 - filter->ratio * filter->ratio) / (4.0 * thr_n);
       
   621   b_n = (1.0 + filter->ratio * filter->ratio) / 2.0;
       
   622   c_n = thr_n * (1.0 - b_n - a_n * thr_n);
       
   623 
       
   624   for (; num_samples; num_samples--) {
       
   625     val = *data;
       
   626 
       
   627     if (val < thr_p && val > zero_p) {
       
   628       val = a_p * val * val + b_p * val + c_p;
       
   629     } else if ((val <= zero_p && val > 0) || (val >= zero_n && val < 0)) {
       
   630       val = 0;
       
   631     } else if (val > thr_n && val < zero_n) {
       
   632       val = a_n * val * val + b_n * val + c_n;
       
   633     }
       
   634     *data++ = (gint16) CLAMP (val, G_MININT16, G_MAXINT16);
       
   635   }
       
   636 }
       
   637 
       
   638 static void
       
   639 gst_audio_dynamic_transform_soft_knee_expander_float (GstAudioDynamic * filter,
       
   640     gfloat * data, guint num_samples)
       
   641 {
       
   642   gdouble val;
       
   643   gdouble threshold = filter->threshold;
       
   644   gdouble zero;
       
   645   gdouble a_p, b_p, c_p;
       
   646   gdouble a_n, b_n, c_n;
       
   647 
       
   648   /* Nothing to do for us here if threshold equals 0.0
       
   649    * or ratio equals 1.0 */
       
   650   if (filter->threshold == 0.0 || filter->ratio == 1.0)
       
   651     return;
       
   652 
       
   653   /* zero crossing of our function */
       
   654   zero = (threshold * (filter->ratio - 1.0)) / (1.0 + filter->ratio);
       
   655 
       
   656   if (zero < 0.0)
       
   657     zero = 0.0;
       
   658 
       
   659   /* shouldn't happen as this only happens with
       
   660    * threshold == 0.0 */
       
   661   g_assert (threshold != 0.0);
       
   662 
       
   663   /* We build a 2n degree polynomial here for values between
       
   664    * 0 and threshold or 0 and -threshold with:
       
   665    * f(t) = t, f'(t) = 1, f(z) = 0, f'(z) = r
       
   666    * z between 0 and t
       
   667    * =>
       
   668    * a = (1 - r^2) / (4 * t)
       
   669    * b = (1 + r^2) / 2
       
   670    * c = t * (1.0 - b - a*t)
       
   671    * f(x) = ax^2 + bx + c */
       
   672   a_p = (1.0 - filter->ratio * filter->ratio) / (4.0 * threshold);
       
   673   b_p = (1.0 + filter->ratio * filter->ratio) / 2.0;
       
   674   c_p = threshold * (1.0 - b_p - a_p * threshold);
       
   675   a_n = (1.0 - filter->ratio * filter->ratio) / (-4.0 * threshold);
       
   676   b_n = (1.0 + filter->ratio * filter->ratio) / 2.0;
       
   677   c_n = -threshold * (1.0 - b_n + a_n * threshold);
       
   678 
       
   679   for (; num_samples; num_samples--) {
       
   680     val = *data;
       
   681 
       
   682     if (val < threshold && val > zero) {
       
   683       val = a_p * val * val + b_p * val + c_p;
       
   684     } else if ((val <= zero && val > 0.0) || (val >= -zero && val < 0.0)) {
       
   685       val = 0.0;
       
   686     } else if (val > -threshold && val < -zero) {
       
   687       val = a_n * val * val + b_n * val + c_n;
       
   688     }
       
   689     *data++ = (gfloat) val;
       
   690   }
       
   691 }
       
   692 
       
   693 /* GstBaseTransform vmethod implementations */
       
   694 static GstFlowReturn
       
   695 gst_audio_dynamic_transform_ip (GstBaseTransform * base, GstBuffer * buf)
       
   696 {
       
   697   GstAudioDynamic *filter = GST_AUDIO_DYNAMIC (base);
       
   698   guint num_samples =
       
   699       GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (filter)->format.width / 8);
       
   700 
       
   701   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
       
   702     gst_object_sync_values (G_OBJECT (filter), GST_BUFFER_TIMESTAMP (buf));
       
   703 
       
   704   if (gst_base_transform_is_passthrough (base) ||
       
   705       G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
       
   706     return GST_FLOW_OK;
       
   707 
       
   708   filter->process (filter, GST_BUFFER_DATA (buf), num_samples);
       
   709 
       
   710   return GST_FLOW_OK;
       
   711 }