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