"Fossies" - the Fresh Open Source Software Archive

Member "gst-plugins-good-1.20.3/gst/audiofx/audiodynamic.c" (15 Jun 2022, 22117 Bytes) of package /linux/misc/gst-plugins-good-1.20.3.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 and the last Fossies "Diffs" side-by-side code changes report: 1.18.6_vs_1.20.0.

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