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