"Fossies" - the Fresh Open Source Software Archive 
Member "gst-plugins-good-1.20.3/gst/audiofx/audioecho.c" (15 Jun 2022, 16377 Bytes) of package /linux/misc/gst-plugins-good-1.20.3.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "audioecho.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
1.18.6_vs_1.20.0.
1 /*
2 * GStreamer
3 * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:element-audioecho
23 * @title: audioecho
24 *
25 * audioecho adds an echo or (simple) reverb effect to an audio stream. The echo
26 * delay, intensity and the percentage of feedback can be configured.
27 *
28 * For getting an echo effect you have to set the delay to a larger value,
29 * for example 200ms and more. Everything below will result in a simple
30 * reverb effect, which results in a slightly metallic sound.
31 *
32 * Use the max-delay property to set the maximum amount of delay that
33 * will be used. This can only be set before going to the PAUSED or PLAYING
34 * state and will be set to the current delay by default.
35 *
36 * audioecho can also be used to apply a configurable delay to audio channels
37 * by setting surround-delay=true. In that mode, it just delays "surround
38 * channels" by the delay amount instead of performing an echo. The
39 * channels that are configured surround channels for the delay are
40 * selected using the surround-channels mask property.
41 *
42 * ## Example launch lines
43 * |[
44 * gst-launch-1.0 autoaudiosrc ! audioconvert ! audioecho delay=500000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink
45 * gst-launch-1.0 filesrc location="melo1.ogg" ! decodebin ! audioconvert ! audioecho delay=50000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink
46 * gst-launch-1.0 audiotestsrc ! audioconvert ! audio/x-raw,channels=4 ! audioecho surround-delay=true delay=500000000 ! audioconvert ! autoaudiosink
47 * ]|
48 *
49 */
50
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54
55 #include <gst/gst.h>
56 #include <gst/base/gstbasetransform.h>
57 #include <gst/audio/audio.h>
58 #include <gst/audio/gstaudiofilter.h>
59
60 #include "audioecho.h"
61
62 #define GST_CAT_DEFAULT gst_audio_echo_debug
63 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
64
65 /* Everything except the first 2 channels are considered surround */
66 #define DEFAULT_SURROUND_MASK ~((guint64)(0x3))
67
68 enum
69 {
70 PROP_0,
71 PROP_DELAY,
72 PROP_MAX_DELAY,
73 PROP_INTENSITY,
74 PROP_FEEDBACK,
75 PROP_SUR_DELAY,
76 PROP_SUR_MASK
77 };
78
79 #define ALLOWED_CAPS \
80 "audio/x-raw," \
81 " format=(string) {"GST_AUDIO_NE(F32)","GST_AUDIO_NE(F64)"}, " \
82 " rate=(int)[1,MAX]," \
83 " channels=(int)[1,MAX]," \
84 " layout=(string) interleaved"
85
86 #define gst_audio_echo_parent_class parent_class
87 G_DEFINE_TYPE (GstAudioEcho, gst_audio_echo, GST_TYPE_AUDIO_FILTER);
88 GST_ELEMENT_REGISTER_DEFINE (audioecho, "audioecho",
89 GST_RANK_NONE, GST_TYPE_AUDIO_ECHO);
90
91 static void gst_audio_echo_set_property (GObject * object, guint prop_id,
92 const GValue * value, GParamSpec * pspec);
93 static void gst_audio_echo_get_property (GObject * object, guint prop_id,
94 GValue * value, GParamSpec * pspec);
95 static void gst_audio_echo_finalize (GObject * object);
96
97 static gboolean gst_audio_echo_setup (GstAudioFilter * self,
98 const GstAudioInfo * info);
99 static gboolean gst_audio_echo_stop (GstBaseTransform * base);
100 static GstFlowReturn gst_audio_echo_transform_ip (GstBaseTransform * base,
101 GstBuffer * buf);
102
103 static void gst_audio_echo_transform_float (GstAudioEcho * self,
104 gfloat * data, guint num_samples);
105 static void gst_audio_echo_transform_double (GstAudioEcho * self,
106 gdouble * data, guint num_samples);
107
108 /* GObject vmethod implementations */
109
110 static void
111 gst_audio_echo_class_init (GstAudioEchoClass * klass)
112 {
113 GObjectClass *gobject_class = (GObjectClass *) klass;
114 GstElementClass *gstelement_class = (GstElementClass *) klass;
115 GstBaseTransformClass *basetransform_class = (GstBaseTransformClass *) klass;
116 GstAudioFilterClass *audioself_class = (GstAudioFilterClass *) klass;
117 GstCaps *caps;
118
119 GST_DEBUG_CATEGORY_INIT (gst_audio_echo_debug, "audioecho", 0,
120 "audioecho element");
121
122 gobject_class->set_property = gst_audio_echo_set_property;
123 gobject_class->get_property = gst_audio_echo_get_property;
124 gobject_class->finalize = gst_audio_echo_finalize;
125
126 g_object_class_install_property (gobject_class, PROP_DELAY,
127 g_param_spec_uint64 ("delay", "Delay",
128 "Delay of the echo in nanoseconds", 1, G_MAXUINT64,
129 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
130 | GST_PARAM_CONTROLLABLE));
131
132 g_object_class_install_property (gobject_class, PROP_MAX_DELAY,
133 g_param_spec_uint64 ("max-delay", "Maximum Delay",
134 "Maximum delay of the echo in nanoseconds"
135 " (can't be changed in PLAYING or PAUSED state)",
136 1, G_MAXUINT64, 1,
137 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
138 GST_PARAM_MUTABLE_READY));
139
140 g_object_class_install_property (gobject_class, PROP_INTENSITY,
141 g_param_spec_float ("intensity", "Intensity",
142 "Intensity of the echo", 0.0, 1.0,
143 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
144 | GST_PARAM_CONTROLLABLE));
145
146 g_object_class_install_property (gobject_class, PROP_FEEDBACK,
147 g_param_spec_float ("feedback", "Feedback",
148 "Amount of feedback", 0.0, 1.0,
149 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
150 | GST_PARAM_CONTROLLABLE));
151
152 g_object_class_install_property (gobject_class, PROP_SUR_DELAY,
153 g_param_spec_boolean ("surround-delay", "Enable Surround Delay",
154 "Delay Surround Channels when TRUE instead of applying an echo effect",
155 FALSE,
156 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
157
158 g_object_class_install_property (gobject_class, PROP_SUR_MASK,
159 g_param_spec_uint64 ("surround-mask", "Surround Mask",
160 "A bitmask of channels that are considered surround and delayed when surround-delay = TRUE",
161 1, G_MAXUINT64, DEFAULT_SURROUND_MASK,
162 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
163 GST_PARAM_MUTABLE_READY));
164
165 gst_element_class_set_static_metadata (gstelement_class, "Audio echo",
166 "Filter/Effect/Audio",
167 "Adds an echo or reverb effect to an audio stream",
168 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
169
170 caps = gst_caps_from_string (ALLOWED_CAPS);
171 gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
172 caps);
173 gst_caps_unref (caps);
174
175 audioself_class->setup = GST_DEBUG_FUNCPTR (gst_audio_echo_setup);
176 basetransform_class->transform_ip =
177 GST_DEBUG_FUNCPTR (gst_audio_echo_transform_ip);
178 basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_audio_echo_stop);
179 }
180
181 static void
182 gst_audio_echo_init (GstAudioEcho * self)
183 {
184 self->delay = 1;
185 self->max_delay = 1;
186 self->intensity = 0.0;
187 self->feedback = 0.0;
188 self->surdelay = FALSE;
189 self->surround_mask = DEFAULT_SURROUND_MASK;
190
191 g_mutex_init (&self->lock);
192
193 gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
194 }
195
196 static void
197 gst_audio_echo_finalize (GObject * object)
198 {
199 GstAudioEcho *self = GST_AUDIO_ECHO (object);
200
201 g_free (self->buffer);
202 self->buffer = NULL;
203
204 g_mutex_clear (&self->lock);
205
206 G_OBJECT_CLASS (parent_class)->finalize (object);
207 }
208
209 static void
210 gst_audio_echo_set_property (GObject * object, guint prop_id,
211 const GValue * value, GParamSpec * pspec)
212 {
213 GstAudioEcho *self = GST_AUDIO_ECHO (object);
214
215 switch (prop_id) {
216 case PROP_DELAY:{
217 guint64 max_delay, delay;
218 guint rate;
219
220 g_mutex_lock (&self->lock);
221 delay = g_value_get_uint64 (value);
222 max_delay = self->max_delay;
223
224 if (delay > max_delay && GST_STATE (self) > GST_STATE_READY) {
225 GST_WARNING_OBJECT (self, "New delay (%" GST_TIME_FORMAT ") "
226 "is larger than maximum delay (%" GST_TIME_FORMAT ")",
227 GST_TIME_ARGS (delay), GST_TIME_ARGS (max_delay));
228 self->delay = max_delay;
229 } else {
230 self->delay = delay;
231 self->max_delay = MAX (delay, max_delay);
232 if (delay > max_delay) {
233 g_free (self->buffer);
234 self->buffer = NULL;
235 }
236 }
237 rate = GST_AUDIO_FILTER_RATE (self);
238 if (rate > 0)
239 self->delay_frames =
240 MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
241
242 g_mutex_unlock (&self->lock);
243 break;
244 }
245 case PROP_MAX_DELAY:{
246 guint64 max_delay;
247
248 g_mutex_lock (&self->lock);
249 max_delay = g_value_get_uint64 (value);
250
251 if (GST_STATE (self) > GST_STATE_READY) {
252 GST_ERROR_OBJECT (self, "Can't change maximum delay in"
253 " PLAYING or PAUSED state");
254 } else {
255 self->max_delay = max_delay;
256 g_free (self->buffer);
257 self->buffer = NULL;
258 }
259 g_mutex_unlock (&self->lock);
260 break;
261 }
262 case PROP_INTENSITY:{
263 g_mutex_lock (&self->lock);
264 self->intensity = g_value_get_float (value);
265 g_mutex_unlock (&self->lock);
266 break;
267 }
268 case PROP_FEEDBACK:{
269 g_mutex_lock (&self->lock);
270 self->feedback = g_value_get_float (value);
271 g_mutex_unlock (&self->lock);
272 break;
273 }
274 case PROP_SUR_DELAY:{
275 g_mutex_lock (&self->lock);
276 self->surdelay = g_value_get_boolean (value);
277 g_mutex_unlock (&self->lock);
278 break;
279 }
280 case PROP_SUR_MASK:{
281 g_mutex_lock (&self->lock);
282 self->surround_mask = g_value_get_uint64 (value);
283 g_mutex_unlock (&self->lock);
284 break;
285 }
286 default:
287 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288 break;
289 }
290 }
291
292 static void
293 gst_audio_echo_get_property (GObject * object, guint prop_id,
294 GValue * value, GParamSpec * pspec)
295 {
296 GstAudioEcho *self = GST_AUDIO_ECHO (object);
297
298 switch (prop_id) {
299 case PROP_DELAY:
300 g_mutex_lock (&self->lock);
301 g_value_set_uint64 (value, self->delay);
302 g_mutex_unlock (&self->lock);
303 break;
304 case PROP_MAX_DELAY:
305 g_mutex_lock (&self->lock);
306 g_value_set_uint64 (value, self->max_delay);
307 g_mutex_unlock (&self->lock);
308 break;
309 case PROP_INTENSITY:
310 g_mutex_lock (&self->lock);
311 g_value_set_float (value, self->intensity);
312 g_mutex_unlock (&self->lock);
313 break;
314 case PROP_FEEDBACK:
315 g_mutex_lock (&self->lock);
316 g_value_set_float (value, self->feedback);
317 g_mutex_unlock (&self->lock);
318 break;
319 case PROP_SUR_DELAY:
320 g_mutex_lock (&self->lock);
321 g_value_set_boolean (value, self->surdelay);
322 g_mutex_unlock (&self->lock);
323 break;
324 case PROP_SUR_MASK:{
325 g_mutex_lock (&self->lock);
326 g_value_set_uint64 (value, self->surround_mask);
327 g_mutex_unlock (&self->lock);
328 break;
329 }
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_echo_setup (GstAudioFilter * base, const GstAudioInfo * info)
340 {
341 GstAudioEcho *self = GST_AUDIO_ECHO (base);
342 gboolean ret = TRUE;
343
344 switch (GST_AUDIO_INFO_FORMAT (info)) {
345 case GST_AUDIO_FORMAT_F32:
346 self->process = (GstAudioEchoProcessFunc)
347 gst_audio_echo_transform_float;
348 break;
349 case GST_AUDIO_FORMAT_F64:
350 self->process = (GstAudioEchoProcessFunc)
351 gst_audio_echo_transform_double;
352 break;
353 default:
354 ret = FALSE;
355 break;
356 }
357
358 g_free (self->buffer);
359 self->buffer = NULL;
360 self->buffer_pos = 0;
361 self->buffer_size = 0;
362 self->buffer_size_frames = 0;
363
364 return ret;
365 }
366
367 static gboolean
368 gst_audio_echo_stop (GstBaseTransform * base)
369 {
370 GstAudioEcho *self = GST_AUDIO_ECHO (base);
371
372 g_free (self->buffer);
373 self->buffer = NULL;
374 self->buffer_pos = 0;
375 self->buffer_size = 0;
376 self->buffer_size_frames = 0;
377
378 return TRUE;
379 }
380
381 #define TRANSFORM_FUNC(name, type) \
382 static void \
383 gst_audio_echo_transform_##name (GstAudioEcho * self, \
384 type * data, guint num_samples) \
385 { \
386 type *buffer = (type *) self->buffer; \
387 guint channels = GST_AUDIO_FILTER_CHANNELS (self); \
388 guint i, j; \
389 guint echo_offset = self->buffer_size_frames - self->delay_frames; \
390 gdouble intensity = self->intensity; \
391 gdouble feedback = self->feedback; \
392 guint buffer_pos = self->buffer_pos; \
393 guint buffer_size_frames = self->buffer_size_frames; \
394 \
395 if (self->surdelay == FALSE) { \
396 guint read_pos = ((echo_offset + buffer_pos) % buffer_size_frames) * channels; \
397 guint write_pos = (buffer_pos % buffer_size_frames) * channels; \
398 guint buffer_size = buffer_size_frames * channels; \
399 for (i = 0; i < num_samples; i++) { \
400 gdouble in = *data; \
401 gdouble echo = buffer[read_pos]; \
402 type out = in + intensity * echo; \
403 \
404 *data = out; \
405 \
406 buffer[write_pos] = in + feedback * echo; \
407 read_pos = (read_pos + 1) % buffer_size; \
408 write_pos = (write_pos + 1) % buffer_size; \
409 data++; \
410 } \
411 buffer_pos = write_pos / channels; \
412 } else { \
413 guint64 surround_mask = self->surround_mask; \
414 guint read_pos = ((echo_offset + buffer_pos) % buffer_size_frames) * channels; \
415 guint write_pos = (buffer_pos % buffer_size_frames) * channels; \
416 guint buffer_size = buffer_size_frames * channels; \
417 \
418 num_samples /= channels; \
419 \
420 for (i = 0; i < num_samples; i++) { \
421 guint64 channel_mask = 1; \
422 \
423 for (j = 0; j < channels; j++) { \
424 if (channel_mask & surround_mask) { \
425 gdouble in = data[j]; \
426 gdouble echo = buffer[read_pos + j]; \
427 type out = echo; \
428 \
429 data[j] = out; \
430 \
431 buffer[write_pos + j] = in; \
432 } else { \
433 gdouble in = data[j]; \
434 gdouble echo = buffer[read_pos + j]; \
435 type out = in + intensity * echo; \
436 \
437 data[j] = out; \
438 \
439 buffer[write_pos + j] = in + feedback * echo; \
440 } \
441 channel_mask <<= 1; \
442 } \
443 read_pos = (read_pos + channels) % buffer_size; \
444 write_pos = (write_pos + channels) % buffer_size; \
445 data += channels; \
446 } \
447 buffer_pos = write_pos / channels; \
448 } \
449 self->buffer_pos = buffer_pos; \
450 }
451
452 TRANSFORM_FUNC (float, gfloat);
453 TRANSFORM_FUNC (double, gdouble);
454
455 /* GstBaseTransform vmethod implementations */
456 static GstFlowReturn
457 gst_audio_echo_transform_ip (GstBaseTransform * base, GstBuffer * buf)
458 {
459 GstAudioEcho *self = GST_AUDIO_ECHO (base);
460 guint num_samples;
461 GstClockTime timestamp, stream_time;
462 GstMapInfo map;
463
464 g_mutex_lock (&self->lock);
465 timestamp = GST_BUFFER_TIMESTAMP (buf);
466 stream_time =
467 gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
468
469 GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT,
470 GST_TIME_ARGS (timestamp));
471
472 if (GST_CLOCK_TIME_IS_VALID (stream_time))
473 gst_object_sync_values (GST_OBJECT (self), stream_time);
474
475 if (self->buffer == NULL) {
476 guint bpf, rate;
477
478 bpf = GST_AUDIO_FILTER_BPF (self);
479 rate = GST_AUDIO_FILTER_RATE (self);
480
481 self->delay_frames =
482 MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
483 self->buffer_size_frames =
484 MAX (gst_util_uint64_scale (self->max_delay, rate, GST_SECOND), 1);
485
486 self->buffer_size = self->buffer_size_frames * bpf;
487 self->buffer = g_try_malloc0 (self->buffer_size);
488 self->buffer_pos = 0;
489
490 if (self->buffer == NULL) {
491 g_mutex_unlock (&self->lock);
492 GST_ERROR_OBJECT (self, "Failed to allocate %u bytes", self->buffer_size);
493 return GST_FLOW_ERROR;
494 }
495 }
496
497 gst_buffer_map (buf, &map, GST_MAP_READWRITE);
498 num_samples = map.size / GST_AUDIO_FILTER_BPS (self);
499
500 self->process (self, map.data, num_samples);
501
502 gst_buffer_unmap (buf, &map);
503 g_mutex_unlock (&self->lock);
504
505 return GST_FLOW_OK;
506 }