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