"Fossies" - the Fresh Open Source Software Archive

Member "gst-plugins-good-1.16.1/gst/audiofx/audiodynamic.c" (19 Apr 2019, 21887 Bytes) of package /linux/misc/gst-plugins-good-1.16.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "audiodynamic.c" see the Fossies "Dox" file reference documentation.

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