3 * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
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.
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.
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.
22 * SECTION:element-audioreverb
25 * audioreverb adds an echo or revert effect to an audio stream. The echo
26 * reverb, intensity and the percentage of feedback can be configured.
29 * gst-launch filesrc location="melo1.ogg" ! audioconvert ! audioreverb reverb=500000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink
30 * gst-launch filesrc location="melo1.ogg" ! decodebin ! audioconvert ! audioreverb reverb=50000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink
43 #include <gst/base/gstbasetransform.h>
44 #include <gst/audio/audio.h>
45 #include <gst/audio/gstaudiofilter.h>
46 #include <gst/controller/gstcontroller.h>
48 #include "audioreverb.h"
50 #define GST_CAT_DEFAULT gst_audio_reverb_debug
51 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
61 #define ALLOWED_CAPS \
62 "audio/x-raw-float," \
63 " width=(int) { 32, 64 }, " \
64 " endianness=(int)BYTE_ORDER," \
65 " rate=(int)[1,MAX]," \
66 " channels=(int)[1,MAX]"
68 #define DEBUG_INIT(bla) \
69 GST_DEBUG_CATEGORY_INIT (gst_audio_reverb_debug, "audioreverb", 0, "audioreverb element");
71 GST_BOILERPLATE_FULL (GstAudioReverb, gst_audio_reverb, GstAudioFilter,
72 GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
74 static void gst_audio_reverb_set_property (GObject * object, guint prop_id,
75 const GValue * value, GParamSpec * pspec);
76 static void gst_audio_reverb_get_property (GObject * object, guint prop_id,
77 GValue * value, GParamSpec * pspec);
78 static void gst_audio_reverb_finalize (GObject * object);
80 static gboolean gst_audio_reverb_setup (GstAudioFilter * self,
81 GstRingBufferSpec * format);
82 static gboolean gst_audio_reverb_stop (GstBaseTransform * base);
83 static GstFlowReturn gst_audio_reverb_transform_ip (GstBaseTransform * base,
86 static void gst_audio_reverb_transform_float (GstAudioReverb * self,
87 gfloat * data, guint num_samples);
88 static void gst_audio_reverb_transform_double (GstAudioReverb * self,
89 gdouble * data, guint num_samples);
91 /* GObject vmethod implementations */
94 gst_audio_reverb_base_init (gpointer klass)
96 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
99 gst_element_class_set_details_simple (element_class, "Audio reverb",
100 "Filter/Effect/Audio",
101 "Adds an echo or reverb effect to an audio stream",
102 "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
104 caps = gst_caps_from_string (ALLOWED_CAPS);
105 gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
107 gst_caps_unref (caps);
111 gst_audio_reverb_class_init (GstAudioReverbClass * klass)
113 GObjectClass *gobject_class = (GObjectClass *) klass;
114 GstBaseTransformClass *basetransform_class = (GstBaseTransformClass *) klass;
115 GstAudioFilterClass *audioself_class = (GstAudioFilterClass *) klass;
117 gobject_class->set_property = gst_audio_reverb_set_property;
118 gobject_class->get_property = gst_audio_reverb_get_property;
119 gobject_class->finalize = gst_audio_reverb_finalize;
121 g_object_class_install_property (gobject_class, PROP_DELAY,
122 g_param_spec_uint64 ("delay", "Delay",
123 "Delay of the echo in nanoseconds", 1, G_MAXUINT64,
124 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
125 | GST_PARAM_CONTROLLABLE));
127 g_object_class_install_property (gobject_class, PROP_INTENSITY,
128 g_param_spec_float ("intensity", "Intensity",
129 "Intensity of the echo", 0.0, 1.0,
130 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
131 | GST_PARAM_CONTROLLABLE));
133 g_object_class_install_property (gobject_class, PROP_FEEDBACK,
134 g_param_spec_float ("feedback", "Feedback",
135 "Amount of feedback", 0.0, 1.0,
136 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
137 | GST_PARAM_CONTROLLABLE));
139 audioself_class->setup = GST_DEBUG_FUNCPTR (gst_audio_reverb_setup);
140 basetransform_class->transform_ip =
141 GST_DEBUG_FUNCPTR (gst_audio_reverb_transform_ip);
142 basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_audio_reverb_stop);
146 gst_audio_reverb_init (GstAudioReverb * self, GstAudioReverbClass * klass)
149 self->intensity = 0.0;
150 self->feedback = 0.0;
152 gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
156 gst_audio_reverb_finalize (GObject * object)
158 GstAudioReverb *self = GST_AUDIO_REVERB (object);
160 g_free (self->buffer);
163 G_OBJECT_CLASS (parent_class)->finalize (object);
167 gst_audio_reverb_set_property (GObject * object, guint prop_id,
168 const GValue * value, GParamSpec * pspec)
170 GstAudioReverb *self = GST_AUDIO_REVERB (object);
174 guint rate, width, channels;
176 GST_BASE_TRANSFORM_LOCK (self);
177 self->delay = g_value_get_uint64 (value);
179 rate = GST_AUDIO_FILTER (self)->format.rate;
180 width = GST_AUDIO_FILTER (self)->format.width / 8;
181 channels = GST_AUDIO_FILTER (self)->format.channels;
183 if (self->buffer && rate > 0) {
185 MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
186 guint new_size = new_reverb * width * channels;
188 if (new_size > self->buffer_size) {
190 guint8 *old_buffer = self->buffer;
192 self->buffer_size = new_size;
193 self->buffer = g_malloc0 (new_size);
195 for (i = 0; i < self->buffer_size_frames; i++) {
196 memcpy (&self->buffer[i * width * channels],
198 self->buffer_pos) % self->buffer_size_frames) *
199 width * channels], channels * width);
201 self->buffer_size_frames = self->delay_frames = new_reverb;
202 self->buffer_pos = 0;
204 } else if (self->buffer) {
205 g_free (self->buffer);
209 GST_BASE_TRANSFORM_UNLOCK (self);
212 case PROP_INTENSITY:{
213 GST_BASE_TRANSFORM_LOCK (self);
214 self->intensity = g_value_get_float (value);
215 GST_BASE_TRANSFORM_UNLOCK (self);
219 GST_BASE_TRANSFORM_LOCK (self);
220 self->feedback = g_value_get_float (value);
221 GST_BASE_TRANSFORM_UNLOCK (self);
225 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231 gst_audio_reverb_get_property (GObject * object, guint prop_id,
232 GValue * value, GParamSpec * pspec)
234 GstAudioReverb *self = GST_AUDIO_REVERB (object);
238 GST_BASE_TRANSFORM_LOCK (self);
239 g_value_set_uint64 (value, self->delay);
240 GST_BASE_TRANSFORM_UNLOCK (self);
243 GST_BASE_TRANSFORM_LOCK (self);
244 g_value_set_float (value, self->intensity);
245 GST_BASE_TRANSFORM_UNLOCK (self);
248 GST_BASE_TRANSFORM_LOCK (self);
249 g_value_set_float (value, self->feedback);
250 GST_BASE_TRANSFORM_UNLOCK (self);
253 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
258 /* GstAudioFilter vmethod implementations */
261 gst_audio_reverb_setup (GstAudioFilter * base, GstRingBufferSpec * format)
263 GstAudioReverb *self = GST_AUDIO_REVERB (base);
266 if (format->type == GST_BUFTYPE_FLOAT && format->width == 32)
267 self->process = (GstAudioReverbProcessFunc)
268 gst_audio_reverb_transform_float;
269 else if (format->type == GST_BUFTYPE_FLOAT && format->width == 64)
270 self->process = (GstAudioReverbProcessFunc)
271 gst_audio_reverb_transform_double;
275 g_free (self->buffer);
277 self->buffer_pos = 0;
278 self->buffer_size = 0;
279 self->buffer_size_frames = 0;
285 gst_audio_reverb_stop (GstBaseTransform * base)
287 GstAudioReverb *self = GST_AUDIO_REVERB (base);
289 g_free (self->buffer);
291 self->buffer_pos = 0;
292 self->buffer_size = 0;
293 self->buffer_size_frames = 0;
298 #define TRANSFORM_FUNC(name, type) \
300 gst_audio_reverb_transform_##name (GstAudioReverb * self, \
301 type * data, guint num_samples) \
303 type *buffer = (type *) self->buffer; \
304 guint channels = GST_AUDIO_FILTER (self)->format.channels; \
305 guint rate = GST_AUDIO_FILTER (self)->format.rate; \
307 guint reverb_index = self->buffer_size_frames - self->delay_frames; \
308 gdouble reverb_off = ((((gdouble) self->delay) * rate) / GST_SECOND) - self->delay_frames; \
310 if (reverb_off < 0.0) \
313 num_samples /= channels; \
315 for (i = 0; i < num_samples; i++) { \
316 guint echo0_index = ((reverb_index + self->buffer_pos) % self->buffer_size_frames) * channels; \
317 guint echo1_index = ((reverb_index + self->buffer_pos +1) % self->buffer_size_frames) * channels; \
318 guint rbout_index = (self->buffer_pos % self->buffer_size_frames) * channels; \
319 for (j = 0; j < channels; j++) { \
320 gdouble in = data[i*channels + j]; \
321 gdouble echo0 = buffer[echo0_index + j]; \
322 gdouble echo1 = buffer[echo1_index + j]; \
323 gdouble echo = echo0 + (echo1-echo0)*reverb_off; \
324 type out = in + self->intensity * echo; \
326 data[i*channels + j] = out; \
328 buffer[rbout_index + j] = in + self->feedback * echo; \
330 self->buffer_pos = (self->buffer_pos + 1) % self->buffer_size_frames; \
334 TRANSFORM_FUNC (float, gfloat);
335 TRANSFORM_FUNC (double, gdouble);
337 /* GstBaseTransform vmethod implementations */
339 gst_audio_reverb_transform_ip (GstBaseTransform * base, GstBuffer * buf)
341 GstAudioReverb *self = GST_AUDIO_REVERB (base);
343 GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (self)->format.width / 8);
345 if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
346 gst_object_sync_values (G_OBJECT (self), GST_BUFFER_TIMESTAMP (buf));
348 if (self->buffer == NULL) {
349 guint width, rate, channels;
351 width = GST_AUDIO_FILTER (self)->format.width / 8;
352 rate = GST_AUDIO_FILTER (self)->format.rate;
353 channels = GST_AUDIO_FILTER (self)->format.channels;
356 MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
358 self->buffer_size_frames = MAX (self->delay_frames, 1000);
359 self->buffer_size = self->buffer_size_frames * width * channels;
360 self->buffer = g_malloc0 (self->buffer_size);
361 self->buffer_pos = 0;
364 self->process (self, GST_BUFFER_DATA (buf), num_samples);