1 /*
2 * GStreamer
3 * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
4 * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 /**
23 * SECTION:element-audioamplify
24 * @title: audioamplify
25 *
26 * Amplifies an audio stream by a given factor and allows the selection of different clipping modes.
27 * The difference between the clipping modes is best evaluated by testing.
28 *
29 * ## Example launch line
30 * |[
31 * gst-launch-1.0 audiotestsrc wave=saw ! audioamplify amplification=1.5 ! alsasink
32 * gst-launch-1.0 filesrc location="melo1.ogg" ! oggdemux ! vorbisdec ! audioconvert ! audioamplify amplification=1.5 clipping-method=wrap-negative ! alsasink
33 * gst-launch-1.0 audiotestsrc wave=saw ! audioconvert ! audioamplify amplification=1.5 clipping-method=wrap-positive ! audioconvert ! alsasink
34 * ]|
35 *
36 */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <gst/gst.h>
43 #include <gst/base/gstbasetransform.h>
44 #include <gst/audio/audio.h>
45 #include <gst/audio/gstaudiofilter.h>
46
47 #include "audioamplify.h"
48
49 #define GST_CAT_DEFAULT gst_audio_amplify_debug
50 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
51
52 /* Filter signals and args */
53 enum
54 {
55 /* FILL ME */
56 LAST_SIGNAL
57 };
58
59 enum
60 {
61 PROP_0,
62 PROP_AMPLIFICATION,
63 PROP_CLIPPING_METHOD
64 };
65
66 enum
67 {
68 METHOD_CLIP = 0,
69 METHOD_WRAP_NEGATIVE,
70 METHOD_WRAP_POSITIVE,
71 METHOD_NOCLIP,
72 NUM_METHODS
73 };
74
75 #define GST_TYPE_AUDIO_AMPLIFY_CLIPPING_METHOD (gst_audio_amplify_clipping_method_get_type ())
76 static GType
gst_audio_amplify_clipping_method_get_type(void)77 gst_audio_amplify_clipping_method_get_type (void)
78 {
79 static GType gtype = 0;
80
81 if (gtype == 0) {
82 static const GEnumValue values[] = {
83 {METHOD_CLIP, "Normal clipping (default)", "clip"},
84 {METHOD_WRAP_NEGATIVE,
85 "Push overdriven values back from the opposite side",
86 "wrap-negative"},
87 {METHOD_WRAP_POSITIVE, "Push overdriven values back from the same side",
88 "wrap-positive"},
89 {METHOD_NOCLIP, "No clipping", "none"},
90 {0, NULL, NULL}
91 };
92 gtype = g_enum_register_static ("GstAudioAmplifyClippingMethod", values);
93 }
94 return gtype;
95 }
96
97 #define ALLOWED_CAPS \
98 "audio/x-raw," \
99 " format=(string) {S8,"GST_AUDIO_NE(S16)","GST_AUDIO_NE(S32)"," \
100 GST_AUDIO_NE(F32)","GST_AUDIO_NE(F64)"}," \
101 " rate=(int)[1,MAX]," \
102 " channels=(int)[1,MAX], " \
103 " layout=(string) {interleaved, non-interleaved}"
104
105 G_DEFINE_TYPE (GstAudioAmplify, gst_audio_amplify, GST_TYPE_AUDIO_FILTER);
106 GST_ELEMENT_REGISTER_DEFINE (audioamplify, "audioamplify",
107 GST_RANK_NONE, GST_TYPE_AUDIO_AMPLIFY);
108
109 static gboolean gst_audio_amplify_set_process_function (GstAudioAmplify *
110 filter, gint clipping, GstAudioFormat format);
111 static void gst_audio_amplify_set_property (GObject * object, guint prop_id,
112 const GValue * value, GParamSpec * pspec);
113 static void gst_audio_amplify_get_property (GObject * object, guint prop_id,
114 GValue * value, GParamSpec * pspec);
115
116 static gboolean gst_audio_amplify_setup (GstAudioFilter * filter,
117 const GstAudioInfo * info);
118 static GstFlowReturn gst_audio_amplify_transform_ip (GstBaseTransform * base,
119 GstBuffer * buf);
120
121 #define MIN_gint8 G_MININT8
122 #define MAX_gint8 G_MAXINT8
123 #define MIN_gint16 G_MININT16
124 #define MAX_gint16 G_MAXINT16
125 #define MIN_gint32 G_MININT32
126 #define MAX_gint32 G_MAXINT32
127
128 #define MAKE_INT_FUNCS(type,largetype) \
129 static void \
130 gst_audio_amplify_transform_##type##_clip (GstAudioAmplify * filter, \
131 void * data, guint num_samples) \
132 { \
133 type *d = data; \
134 \
135 while (num_samples--) { \
136 largetype val = *d * filter->amplification; \
137 *d++ = CLAMP (val, MIN_##type, MAX_##type); \
138 } \
139 } \
140 static void \
141 gst_audio_amplify_transform_##type##_wrap_negative (GstAudioAmplify * filter, \
142 void * data, guint num_samples) \
143 { \
144 type *d = data; \
145 \
146 while (num_samples--) { \
147 largetype val = *d * filter->amplification; \
148 if (val > MAX_##type) \
149 val = MIN_##type + (val - MIN_##type) % ((largetype) MAX_##type + 1 - \
150 MIN_##type); \
151 else if (val < MIN_##type) \
152 val = MAX_##type - (MAX_##type - val) % ((largetype) MAX_##type + 1 - \
153 MIN_##type); \
154 *d++ = val; \
155 } \
156 } \
157 static void \
158 gst_audio_amplify_transform_##type##_wrap_positive (GstAudioAmplify * filter, \
159 void * data, guint num_samples) \
160 { \
161 type *d = data; \
162 \
163 while (num_samples--) { \
164 largetype val = *d * filter->amplification; \
165 do { \
166 if (val > MAX_##type) \
167 val = MAX_##type - (val - MAX_##type); \
168 else if (val < MIN_##type) \
169 val = MIN_##type + (MIN_##type - val); \
170 else \
171 break; \
172 } while (1); \
173 *d++ = val; \
174 } \
175 } \
176 static void \
177 gst_audio_amplify_transform_##type##_noclip (GstAudioAmplify * filter, \
178 void * data, guint num_samples) \
179 { \
180 type *d = data; \
181 \
182 while (num_samples--) \
183 *d++ *= filter->amplification; \
184 }
185
186 #define MAKE_FLOAT_FUNCS(type) \
187 static void \
188 gst_audio_amplify_transform_##type##_clip (GstAudioAmplify * filter, \
189 void * data, guint num_samples) \
190 { \
191 type *d = data; \
192 \
193 while (num_samples--) { \
194 type val = *d* filter->amplification; \
195 *d++ = CLAMP (val, -1.0, +1.0); \
196 } \
197 } \
198 static void \
199 gst_audio_amplify_transform_##type##_wrap_negative (GstAudioAmplify * \
200 filter, void * data, guint num_samples) \
201 { \
202 type *d = data; \
203 \
204 while (num_samples--) { \
205 type val = *d * filter->amplification; \
206 do { \
207 if (val > 1.0) \
208 val = -1.0 + (val - 1.0); \
209 else if (val < -1.0) \
210 val = 1.0 - (1.0 - val); \
211 else \
212 break; \
213 } while (1); \
214 *d++ = val; \
215 } \
216 } \
217 static void \
218 gst_audio_amplify_transform_##type##_wrap_positive (GstAudioAmplify * filter, \
219 void * data, guint num_samples) \
220 { \
221 type *d = data; \
222 \
223 while (num_samples--) { \
224 type val = *d* filter->amplification; \
225 do { \
226 if (val > 1.0) \
227 val = 1.0 - (val - 1.0); \
228 else if (val < -1.0) \
229 val = -1.0 + (-1.0 - val); \
230 else \
231 break; \
232 } while (1); \
233 *d++ = val; \
234 } \
235 } \
236 static void \
237 gst_audio_amplify_transform_##type##_noclip (GstAudioAmplify * filter, \
238 void * data, guint num_samples) \
239 { \
240 type *d = data; \
241 \
242 while (num_samples--) \
243 *d++ *= filter->amplification; \
244 }
245
246 /* *INDENT-OFF* */
MAKE_INT_FUNCS(gint8,gint)247 MAKE_INT_FUNCS (gint8,gint)
248 MAKE_INT_FUNCS (gint16,gint)
249 MAKE_INT_FUNCS (gint32,gint64)
250 MAKE_FLOAT_FUNCS (gfloat)
251 MAKE_FLOAT_FUNCS (gdouble)
252 /* *INDENT-ON* */
253
254 /* GObject vmethod implementations */
255
256 static void
257 gst_audio_amplify_class_init (GstAudioAmplifyClass * klass)
258 {
259 GObjectClass *gobject_class;
260 GstElementClass *gstelement_class;
261 GstCaps *caps;
262
263 GST_DEBUG_CATEGORY_INIT (gst_audio_amplify_debug, "audioamplify", 0,
264 "audioamplify element");
265
266 gobject_class = (GObjectClass *) klass;
267 gstelement_class = (GstElementClass *) klass;
268
269 gobject_class->set_property = gst_audio_amplify_set_property;
270 gobject_class->get_property = gst_audio_amplify_get_property;
271
272 g_object_class_install_property (gobject_class, PROP_AMPLIFICATION,
273 g_param_spec_float ("amplification", "Amplification",
274 "Factor of amplification", -G_MAXFLOAT, G_MAXFLOAT,
275 1.0,
276 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
277
278 /**
279 * GstAudioAmplify:clipping-method
280 *
281 * Clipping method: clip mode set values higher than the maximum to the
282 * maximum. The wrap-negative mode pushes those values back from the
283 * opposite side, wrap-positive pushes them back from the same side.
284 *
285 **/
286 g_object_class_install_property (gobject_class, PROP_CLIPPING_METHOD,
287 g_param_spec_enum ("clipping-method", "Clipping method",
288 "Selects how to handle values higher than the maximum",
289 GST_TYPE_AUDIO_AMPLIFY_CLIPPING_METHOD, METHOD_CLIP,
290 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
291
292 gst_element_class_set_static_metadata (gstelement_class, "Audio amplifier",
293 "Filter/Effect/Audio",
294 "Amplifies an audio stream by a given factor",
295 "Sebastian Dröge <slomo@circular-chaos.org>");
296
297 caps = gst_caps_from_string (ALLOWED_CAPS);
298 gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
299 caps);
300 gst_caps_unref (caps);
301
302 GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
303 GST_DEBUG_FUNCPTR (gst_audio_amplify_transform_ip);
304 GST_BASE_TRANSFORM_CLASS (klass)->transform_ip_on_passthrough = FALSE;
305
306 GST_AUDIO_FILTER_CLASS (klass)->setup =
307 GST_DEBUG_FUNCPTR (gst_audio_amplify_setup);
308
309 gst_type_mark_as_plugin_api (GST_TYPE_AUDIO_AMPLIFY_CLIPPING_METHOD, 0);
310 }
311
312 static void
gst_audio_amplify_init(GstAudioAmplify * filter)313 gst_audio_amplify_init (GstAudioAmplify * filter)
314 {
315 filter->amplification = 1.0;
316 gst_audio_amplify_set_process_function (filter, METHOD_CLIP,
317 GST_AUDIO_FORMAT_S16);
318 gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
319 gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE);
320 }
321
322 static GstAudioAmplifyProcessFunc
gst_audio_amplify_process_function(gint clipping,GstAudioFormat format)323 gst_audio_amplify_process_function (gint clipping, GstAudioFormat format)
324 {
325 static const struct process
326 {
327 GstAudioFormat format;
328 gint clipping;
329 GstAudioAmplifyProcessFunc func;
330 } process[] = {
331 {
332 GST_AUDIO_FORMAT_F32, METHOD_CLIP, gst_audio_amplify_transform_gfloat_clip}, {
333 GST_AUDIO_FORMAT_F32, METHOD_WRAP_NEGATIVE,
334 gst_audio_amplify_transform_gfloat_wrap_negative}, {
335 GST_AUDIO_FORMAT_F32, METHOD_WRAP_POSITIVE,
336 gst_audio_amplify_transform_gfloat_wrap_positive}, {
337 GST_AUDIO_FORMAT_F32, METHOD_NOCLIP,
338 gst_audio_amplify_transform_gfloat_noclip}, {
339 GST_AUDIO_FORMAT_F64, METHOD_CLIP,
340 gst_audio_amplify_transform_gdouble_clip}, {
341 GST_AUDIO_FORMAT_F64, METHOD_WRAP_NEGATIVE,
342 gst_audio_amplify_transform_gdouble_wrap_negative}, {
343 GST_AUDIO_FORMAT_F64, METHOD_WRAP_POSITIVE,
344 gst_audio_amplify_transform_gdouble_wrap_positive}, {
345 GST_AUDIO_FORMAT_F64, METHOD_NOCLIP,
346 gst_audio_amplify_transform_gdouble_noclip}, {
347 GST_AUDIO_FORMAT_S8, METHOD_CLIP, gst_audio_amplify_transform_gint8_clip}, {
348 GST_AUDIO_FORMAT_S8, METHOD_WRAP_NEGATIVE,
349 gst_audio_amplify_transform_gint8_wrap_negative}, {
350 GST_AUDIO_FORMAT_S8, METHOD_WRAP_POSITIVE,
351 gst_audio_amplify_transform_gint8_wrap_positive}, {
352 GST_AUDIO_FORMAT_S8, METHOD_NOCLIP,
353 gst_audio_amplify_transform_gint8_noclip}, {
354 GST_AUDIO_FORMAT_S16, METHOD_CLIP, gst_audio_amplify_transform_gint16_clip}, {
355 GST_AUDIO_FORMAT_S16, METHOD_WRAP_NEGATIVE,
356 gst_audio_amplify_transform_gint16_wrap_negative}, {
357 GST_AUDIO_FORMAT_S16, METHOD_WRAP_POSITIVE,
358 gst_audio_amplify_transform_gint16_wrap_positive}, {
359 GST_AUDIO_FORMAT_S16, METHOD_NOCLIP,
360 gst_audio_amplify_transform_gint16_noclip}, {
361 GST_AUDIO_FORMAT_S32, METHOD_CLIP, gst_audio_amplify_transform_gint32_clip}, {
362 GST_AUDIO_FORMAT_S32, METHOD_WRAP_NEGATIVE,
363 gst_audio_amplify_transform_gint32_wrap_negative}, {
364 GST_AUDIO_FORMAT_S32, METHOD_WRAP_POSITIVE,
365 gst_audio_amplify_transform_gint32_wrap_positive}, {
366 GST_AUDIO_FORMAT_S32, METHOD_NOCLIP,
367 gst_audio_amplify_transform_gint32_noclip}, {
368 0, 0, NULL}
369 };
370 const struct process *p;
371
372 for (p = process; p->func; p++)
373 if (p->format == format && p->clipping == clipping)
374 return p->func;
375 return NULL;
376 }
377
378 static gboolean
gst_audio_amplify_set_process_function(GstAudioAmplify * filter,gint clipping_method,GstAudioFormat format)379 gst_audio_amplify_set_process_function (GstAudioAmplify * filter, gint
380 clipping_method, GstAudioFormat format)
381 {
382 GstAudioAmplifyProcessFunc process;
383
384 /* set processing function */
385
386 process = gst_audio_amplify_process_function (clipping_method, format);
387 if (!process) {
388 GST_DEBUG ("wrong format");
389 return FALSE;
390 }
391
392 filter->process = process;
393 filter->clipping_method = clipping_method;
394 filter->format = format;
395
396 return TRUE;
397 }
398
399 static void
gst_audio_amplify_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)400 gst_audio_amplify_set_property (GObject * object, guint prop_id,
401 const GValue * value, GParamSpec * pspec)
402 {
403 GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (object);
404
405 switch (prop_id) {
406 case PROP_AMPLIFICATION:
407 filter->amplification = g_value_get_float (value);
408 gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter),
409 filter->amplification == 1.0);
410 break;
411 case PROP_CLIPPING_METHOD:
412 gst_audio_amplify_set_process_function (filter, g_value_get_enum (value),
413 filter->format);
414 break;
415 default:
416 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417 break;
418 }
419 }
420
421 static void
gst_audio_amplify_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)422 gst_audio_amplify_get_property (GObject * object, guint prop_id,
423 GValue * value, GParamSpec * pspec)
424 {
425 GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (object);
426
427 switch (prop_id) {
428 case PROP_AMPLIFICATION:
429 g_value_set_float (value, filter->amplification);
430 break;
431 case PROP_CLIPPING_METHOD:
432 g_value_set_enum (value, filter->clipping_method);
433 break;
434 default:
435 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
436 break;
437 }
438 }
439
440 /* GstAudioFilter vmethod implementations */
441 static gboolean
gst_audio_amplify_setup(GstAudioFilter * base,const GstAudioInfo * info)442 gst_audio_amplify_setup (GstAudioFilter * base, const GstAudioInfo * info)
443 {
444 GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (base);
445
446 return gst_audio_amplify_set_process_function (filter,
447 filter->clipping_method, GST_AUDIO_INFO_FORMAT (info));
448 }
449
450 /* GstBaseTransform vmethod implementations */
451 static GstFlowReturn
gst_audio_amplify_transform_ip(GstBaseTransform * base,GstBuffer * buf)452 gst_audio_amplify_transform_ip (GstBaseTransform * base, GstBuffer * buf)
453 {
454 GstAudioAmplify *filter = GST_AUDIO_AMPLIFY (base);
455 guint num_samples;
456 GstClockTime timestamp, stream_time;
457 GstMapInfo map;
458
459 timestamp = GST_BUFFER_TIMESTAMP (buf);
460 stream_time =
461 gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
462
463 GST_DEBUG_OBJECT (filter, "sync to %" GST_TIME_FORMAT,
464 GST_TIME_ARGS (timestamp));
465
466 if (GST_CLOCK_TIME_IS_VALID (stream_time))
467 gst_object_sync_values (GST_OBJECT (filter), stream_time);
468
469 if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_GAP)))
470 return GST_FLOW_OK;
471
472 gst_buffer_map (buf, &map, GST_MAP_READWRITE);
473 num_samples = map.size / GST_AUDIO_FILTER_BPS (filter);
474
475 filter->process (filter, map.data, num_samples);
476
477 gst_buffer_unmap (buf, &map);
478
479 return GST_FLOW_OK;
480 }
481