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