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  *
24  * audioecho adds an echo or (simple) reverb effect to an audio stream. The echo
25  * delay, intensity and the percentage of feedback can be configured.
26  *
27  * For getting an echo effect you have to set the delay to a larger value,
28  * for example 200ms and more. Everything below will result in a simple
29  * reverb effect, which results in a slightly metallic sound.
30  *
31  * Use the max-delay property to set the maximum amount of delay that
32  * will be used. This can only be set before going to the PAUSED or PLAYING
33  * state and will be set to the current delay by default.
34  *
35  * audioecho can also be used to apply a configurable delay to audio channels
36  * by setting surround-delay=true. In that mode, it just delays "surround
37  * channels" by the delay amount instead of performing an echo. The
38  * channels that are configured surround channels for the delay are
39  * selected using the surround-channels mask property.
40  *
41  * <refsect2>
42  * <title>Example launch lines</title>
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  * </refsect2>
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 
89 static void gst_audio_echo_set_property (GObject * object, guint prop_id,
90     const GValue * value, GParamSpec * pspec);
91 static void gst_audio_echo_get_property (GObject * object, guint prop_id,
92     GValue * value, GParamSpec * pspec);
93 static void gst_audio_echo_finalize (GObject * object);
94 
95 static gboolean gst_audio_echo_setup (GstAudioFilter * self,
96     const GstAudioInfo * info);
97 static gboolean gst_audio_echo_stop (GstBaseTransform * base);
98 static GstFlowReturn gst_audio_echo_transform_ip (GstBaseTransform * base,
99     GstBuffer * buf);
100 
101 static void gst_audio_echo_transform_float (GstAudioEcho * self,
102     gfloat * data, guint num_samples);
103 static void gst_audio_echo_transform_double (GstAudioEcho * self,
104     gdouble * data, guint num_samples);
105 
106 /* GObject vmethod implementations */
107 
108 static void
gst_audio_echo_class_init(GstAudioEchoClass * klass)109 gst_audio_echo_class_init (GstAudioEchoClass * klass)
110 {
111   GObjectClass *gobject_class = (GObjectClass *) klass;
112   GstElementClass *gstelement_class = (GstElementClass *) klass;
113   GstBaseTransformClass *basetransform_class = (GstBaseTransformClass *) klass;
114   GstAudioFilterClass *audioself_class = (GstAudioFilterClass *) klass;
115   GstCaps *caps;
116 
117   GST_DEBUG_CATEGORY_INIT (gst_audio_echo_debug, "audioecho", 0,
118       "audioecho element");
119 
120   gobject_class->set_property = gst_audio_echo_set_property;
121   gobject_class->get_property = gst_audio_echo_get_property;
122   gobject_class->finalize = gst_audio_echo_finalize;
123 
124   g_object_class_install_property (gobject_class, PROP_DELAY,
125       g_param_spec_uint64 ("delay", "Delay",
126           "Delay of the echo in nanoseconds", 1, G_MAXUINT64,
127           1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
128           | GST_PARAM_CONTROLLABLE));
129 
130   g_object_class_install_property (gobject_class, PROP_MAX_DELAY,
131       g_param_spec_uint64 ("max-delay", "Maximum Delay",
132           "Maximum delay of the echo in nanoseconds"
133           " (can't be changed in PLAYING or PAUSED state)",
134           1, G_MAXUINT64, 1,
135           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
136           GST_PARAM_MUTABLE_READY));
137 
138   g_object_class_install_property (gobject_class, PROP_INTENSITY,
139       g_param_spec_float ("intensity", "Intensity",
140           "Intensity of the echo", 0.0, 1.0,
141           0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
142           | GST_PARAM_CONTROLLABLE));
143 
144   g_object_class_install_property (gobject_class, PROP_FEEDBACK,
145       g_param_spec_float ("feedback", "Feedback",
146           "Amount of feedback", 0.0, 1.0,
147           0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
148           | GST_PARAM_CONTROLLABLE));
149 
150   g_object_class_install_property (gobject_class, PROP_SUR_DELAY,
151       g_param_spec_boolean ("surround-delay", "Enable Surround Delay",
152           "Delay Surround Channels when TRUE instead of applying an echo effect",
153           FALSE,
154           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
155 
156   g_object_class_install_property (gobject_class, PROP_SUR_MASK,
157       g_param_spec_uint64 ("surround-mask", "Surround Mask",
158           "A bitmask of channels that are considered surround and delayed when surround-delay = TRUE",
159           1, G_MAXUINT64, DEFAULT_SURROUND_MASK,
160           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
161           GST_PARAM_MUTABLE_READY));
162 
163   gst_element_class_set_static_metadata (gstelement_class, "Audio echo",
164       "Filter/Effect/Audio",
165       "Adds an echo or reverb effect to an audio stream",
166       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
167 
168   caps = gst_caps_from_string (ALLOWED_CAPS);
169   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
170       caps);
171   gst_caps_unref (caps);
172 
173   audioself_class->setup = GST_DEBUG_FUNCPTR (gst_audio_echo_setup);
174   basetransform_class->transform_ip =
175       GST_DEBUG_FUNCPTR (gst_audio_echo_transform_ip);
176   basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_audio_echo_stop);
177 }
178 
179 static void
gst_audio_echo_init(GstAudioEcho * self)180 gst_audio_echo_init (GstAudioEcho * self)
181 {
182   self->delay = 1;
183   self->max_delay = 1;
184   self->intensity = 0.0;
185   self->feedback = 0.0;
186   self->surdelay = FALSE;
187   self->surround_mask = DEFAULT_SURROUND_MASK;
188 
189   g_mutex_init (&self->lock);
190 
191   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
192 }
193 
194 static void
gst_audio_echo_finalize(GObject * object)195 gst_audio_echo_finalize (GObject * object)
196 {
197   GstAudioEcho *self = GST_AUDIO_ECHO (object);
198 
199   g_free (self->buffer);
200   self->buffer = NULL;
201 
202   g_mutex_clear (&self->lock);
203 
204   G_OBJECT_CLASS (parent_class)->finalize (object);
205 }
206 
207 static void
gst_audio_echo_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)208 gst_audio_echo_set_property (GObject * object, guint prop_id,
209     const GValue * value, GParamSpec * pspec)
210 {
211   GstAudioEcho *self = GST_AUDIO_ECHO (object);
212 
213   switch (prop_id) {
214     case PROP_DELAY:{
215       guint64 max_delay, delay;
216       guint rate;
217 
218       g_mutex_lock (&self->lock);
219       delay = g_value_get_uint64 (value);
220       max_delay = self->max_delay;
221 
222       if (delay > max_delay && GST_STATE (self) > GST_STATE_READY) {
223         GST_WARNING_OBJECT (self, "New delay (%" GST_TIME_FORMAT ") "
224             "is larger than maximum delay (%" GST_TIME_FORMAT ")",
225             GST_TIME_ARGS (delay), GST_TIME_ARGS (max_delay));
226         self->delay = max_delay;
227       } else {
228         self->delay = delay;
229         self->max_delay = MAX (delay, max_delay);
230         if (delay > max_delay) {
231           g_free (self->buffer);
232           self->buffer = NULL;
233         }
234       }
235       rate = GST_AUDIO_FILTER_RATE (self);
236       if (rate > 0)
237         self->delay_frames =
238             MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
239 
240       g_mutex_unlock (&self->lock);
241       break;
242     }
243     case PROP_MAX_DELAY:{
244       guint64 max_delay;
245 
246       g_mutex_lock (&self->lock);
247       max_delay = g_value_get_uint64 (value);
248 
249       if (GST_STATE (self) > GST_STATE_READY) {
250         GST_ERROR_OBJECT (self, "Can't change maximum delay in"
251             " PLAYING or PAUSED state");
252       } else {
253         self->max_delay = max_delay;
254         g_free (self->buffer);
255         self->buffer = NULL;
256       }
257       g_mutex_unlock (&self->lock);
258       break;
259     }
260     case PROP_INTENSITY:{
261       g_mutex_lock (&self->lock);
262       self->intensity = g_value_get_float (value);
263       g_mutex_unlock (&self->lock);
264       break;
265     }
266     case PROP_FEEDBACK:{
267       g_mutex_lock (&self->lock);
268       self->feedback = g_value_get_float (value);
269       g_mutex_unlock (&self->lock);
270       break;
271     }
272     case PROP_SUR_DELAY:{
273       g_mutex_lock (&self->lock);
274       self->surdelay = g_value_get_boolean (value);
275       g_mutex_unlock (&self->lock);
276       break;
277     }
278     case PROP_SUR_MASK:{
279       g_mutex_lock (&self->lock);
280       self->surround_mask = g_value_get_uint64 (value);
281       g_mutex_unlock (&self->lock);
282       break;
283     }
284     default:
285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
286       break;
287   }
288 }
289 
290 static void
gst_audio_echo_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)291 gst_audio_echo_get_property (GObject * object, guint prop_id,
292     GValue * value, GParamSpec * pspec)
293 {
294   GstAudioEcho *self = GST_AUDIO_ECHO (object);
295 
296   switch (prop_id) {
297     case PROP_DELAY:
298       g_mutex_lock (&self->lock);
299       g_value_set_uint64 (value, self->delay);
300       g_mutex_unlock (&self->lock);
301       break;
302     case PROP_MAX_DELAY:
303       g_mutex_lock (&self->lock);
304       g_value_set_uint64 (value, self->max_delay);
305       g_mutex_unlock (&self->lock);
306       break;
307     case PROP_INTENSITY:
308       g_mutex_lock (&self->lock);
309       g_value_set_float (value, self->intensity);
310       g_mutex_unlock (&self->lock);
311       break;
312     case PROP_FEEDBACK:
313       g_mutex_lock (&self->lock);
314       g_value_set_float (value, self->feedback);
315       g_mutex_unlock (&self->lock);
316       break;
317     case PROP_SUR_DELAY:
318       g_mutex_lock (&self->lock);
319       g_value_set_boolean (value, self->surdelay);
320       g_mutex_unlock (&self->lock);
321       break;
322     case PROP_SUR_MASK:{
323       g_mutex_lock (&self->lock);
324       g_value_set_uint64 (value, self->surround_mask);
325       g_mutex_unlock (&self->lock);
326       break;
327     }
328     default:
329       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
330       break;
331   }
332 }
333 
334 /* GstAudioFilter vmethod implementations */
335 
336 static gboolean
gst_audio_echo_setup(GstAudioFilter * base,const GstAudioInfo * info)337 gst_audio_echo_setup (GstAudioFilter * base, const GstAudioInfo * info)
338 {
339   GstAudioEcho *self = GST_AUDIO_ECHO (base);
340   gboolean ret = TRUE;
341 
342   switch (GST_AUDIO_INFO_FORMAT (info)) {
343     case GST_AUDIO_FORMAT_F32:
344       self->process = (GstAudioEchoProcessFunc)
345           gst_audio_echo_transform_float;
346       break;
347     case GST_AUDIO_FORMAT_F64:
348       self->process = (GstAudioEchoProcessFunc)
349           gst_audio_echo_transform_double;
350       break;
351     default:
352       ret = FALSE;
353       break;
354   }
355 
356   g_free (self->buffer);
357   self->buffer = NULL;
358   self->buffer_pos = 0;
359   self->buffer_size = 0;
360   self->buffer_size_frames = 0;
361 
362   return ret;
363 }
364 
365 static gboolean
gst_audio_echo_stop(GstBaseTransform * base)366 gst_audio_echo_stop (GstBaseTransform * base)
367 {
368   GstAudioEcho *self = GST_AUDIO_ECHO (base);
369 
370   g_free (self->buffer);
371   self->buffer = NULL;
372   self->buffer_pos = 0;
373   self->buffer_size = 0;
374   self->buffer_size_frames = 0;
375 
376   return TRUE;
377 }
378 
379 #define TRANSFORM_FUNC(name, type) \
380 static void \
381 gst_audio_echo_transform_##name (GstAudioEcho * self, \
382     type * data, guint num_samples) \
383 { \
384   type *buffer = (type *) self->buffer; \
385   guint channels = GST_AUDIO_FILTER_CHANNELS (self); \
386   guint i, j; \
387   guint echo_offset = self->buffer_size_frames - self->delay_frames; \
388   gdouble intensity = self->intensity; \
389   gdouble feedback = self->feedback; \
390   guint buffer_pos = self->buffer_pos; \
391   guint buffer_size_frames = self->buffer_size_frames; \
392   \
393   if (self->surdelay == FALSE) { \
394     guint read_pos = ((echo_offset + buffer_pos) % buffer_size_frames) * channels; \
395     guint write_pos = (buffer_pos % buffer_size_frames) * channels; \
396     guint buffer_size = buffer_size_frames * channels; \
397     for (i = 0; i < num_samples; i++) { \
398       gdouble in = *data; \
399       gdouble echo = buffer[read_pos]; \
400       type out = in + intensity * echo; \
401       \
402       *data = out; \
403       \
404       buffer[write_pos] = in + feedback * echo; \
405       read_pos = (read_pos + 1) % buffer_size; \
406       write_pos = (write_pos + 1) % buffer_size; \
407       data++; \
408     } \
409     buffer_pos = write_pos / channels; \
410   } else { \
411     guint64 surround_mask = self->surround_mask; \
412     guint read_pos = ((echo_offset + buffer_pos) % buffer_size_frames) * channels; \
413     guint write_pos = (buffer_pos % buffer_size_frames) * channels; \
414     guint buffer_size = buffer_size_frames * channels; \
415     \
416     num_samples /= channels; \
417     \
418     for (i = 0; i < num_samples; i++) { \
419       guint64 channel_mask = 1; \
420       \
421       for (j = 0; j < channels; j++) { \
422         if (channel_mask & surround_mask) { \
423           gdouble in = data[j]; \
424           gdouble echo = buffer[read_pos + j]; \
425           type out = echo; \
426           \
427           data[j] = out; \
428           \
429           buffer[write_pos + j] = in; \
430         } else { \
431           gdouble in = data[j]; \
432           gdouble echo = buffer[read_pos + j]; \
433           type out = in + intensity * echo; \
434           \
435           data[j] = out; \
436           \
437           buffer[write_pos + j] = in + feedback * echo; \
438         } \
439         channel_mask <<= 1; \
440       } \
441       read_pos = (read_pos + channels) % buffer_size; \
442       write_pos = (write_pos + channels) % buffer_size; \
443       data += channels; \
444     } \
445     buffer_pos = write_pos / channels; \
446   } \
447   self->buffer_pos = buffer_pos; \
448 }
449 
450 TRANSFORM_FUNC (float, gfloat);
451 TRANSFORM_FUNC (double, gdouble);
452 
453 /* GstBaseTransform vmethod implementations */
454 static GstFlowReturn
gst_audio_echo_transform_ip(GstBaseTransform * base,GstBuffer * buf)455 gst_audio_echo_transform_ip (GstBaseTransform * base, GstBuffer * buf)
456 {
457   GstAudioEcho *self = GST_AUDIO_ECHO (base);
458   guint num_samples;
459   GstClockTime timestamp, stream_time;
460   GstMapInfo map;
461 
462   g_mutex_lock (&self->lock);
463   timestamp = GST_BUFFER_TIMESTAMP (buf);
464   stream_time =
465       gst_segment_to_stream_time (&base->segment, GST_FORMAT_TIME, timestamp);
466 
467   GST_DEBUG_OBJECT (self, "sync to %" GST_TIME_FORMAT,
468       GST_TIME_ARGS (timestamp));
469 
470   if (GST_CLOCK_TIME_IS_VALID (stream_time))
471     gst_object_sync_values (GST_OBJECT (self), stream_time);
472 
473   if (self->buffer == NULL) {
474     guint bpf, rate;
475 
476     bpf = GST_AUDIO_FILTER_BPF (self);
477     rate = GST_AUDIO_FILTER_RATE (self);
478 
479     self->delay_frames =
480         MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
481     self->buffer_size_frames =
482         MAX (gst_util_uint64_scale (self->max_delay, rate, GST_SECOND), 1);
483 
484     self->buffer_size = self->buffer_size_frames * bpf;
485     self->buffer = g_try_malloc0 (self->buffer_size);
486     self->buffer_pos = 0;
487 
488     if (self->buffer == NULL) {
489       g_mutex_unlock (&self->lock);
490       GST_ERROR_OBJECT (self, "Failed to allocate %u bytes", self->buffer_size);
491       return GST_FLOW_ERROR;
492     }
493   }
494 
495   gst_buffer_map (buf, &map, GST_MAP_READWRITE);
496   num_samples = map.size / GST_AUDIO_FILTER_BPS (self);
497 
498   self->process (self, map.data, num_samples);
499 
500   gst_buffer_unmap (buf, &map);
501   g_mutex_unlock (&self->lock);
502 
503   return GST_FLOW_OK;
504 }
505