diff options
author | Sebastian Dröge <slomo@circular-chaos.org> | 2009-01-19 11:19:08 +0000 |
---|---|---|
committer | Sebastian Dröge <slomo@circular-chaos.org> | 2009-01-19 11:19:08 +0000 |
commit | 344a9f4229620d90b92e0fffe99069fb9c410d6f (patch) | |
tree | b2e16b5d7c874b91113967c3697cd11759963dd0 | |
parent | d912a42065647cc6baa00b8eab8f6e173286033a (diff) |
Add an echo/reverb filter to the audiofx plugin, with configurable echo delay, intensity and feedback. Fixes bug #567...
Original commit message from CVS:
* docs/plugins/Makefile.am:
* docs/plugins/gst-plugins-good-plugins-docs.sgml:
* docs/plugins/gst-plugins-good-plugins-sections.txt:
* docs/plugins/gst-plugins-good-plugins.args:
* docs/plugins/gst-plugins-good-plugins.hierarchy:
* docs/plugins/inspect/plugin-audiofx.xml:
* docs/plugins/inspect/plugin-spectrum.xml:
* gst/audiofx/Makefile.am:
* gst/audiofx/audiofx.c: (plugin_init):
* gst/audiofx/audioreverb.c: (gst_audio_reverb_base_init),
(gst_audio_reverb_class_init), (gst_audio_reverb_init),
(gst_audio_reverb_finalize), (gst_audio_reverb_set_property),
(gst_audio_reverb_get_property), (gst_audio_reverb_setup),
(gst_audio_reverb_stop), (gst_audio_reverb_transform_ip):
* gst/audiofx/audioreverb.h:
* tests/check/Makefile.am:
* tests/check/elements/audioreverb.c: (setup_reverb),
(cleanup_reverb), (GST_START_TEST), (audioreverb_suite):
Add an echo/reverb filter to the audiofx plugin, with configurable
echo delay, intensity and feedback. Fixes bug #567874.
-rw-r--r-- | ChangeLog | 23 | ||||
-rw-r--r-- | docs/plugins/Makefile.am | 1 | ||||
-rw-r--r-- | docs/plugins/gst-plugins-good-plugins-docs.sgml | 1 | ||||
-rw-r--r-- | docs/plugins/gst-plugins-good-plugins-sections.txt | 16 | ||||
-rw-r--r-- | docs/plugins/gst-plugins-good-plugins.args | 60 | ||||
-rw-r--r-- | docs/plugins/gst-plugins-good-plugins.hierarchy | 3 | ||||
-rw-r--r-- | docs/plugins/inspect/plugin-audiofx.xml | 21 | ||||
-rw-r--r-- | docs/plugins/inspect/plugin-spectrum.xml | 2 | ||||
-rw-r--r-- | gst/audiofx/Makefile.am | 4 | ||||
-rw-r--r-- | gst/audiofx/audiofx.c | 5 | ||||
-rw-r--r-- | gst/audiofx/audioreverb.c | 367 | ||||
-rw-r--r-- | gst/audiofx/audioreverb.h | 68 | ||||
-rw-r--r-- | tests/check/Makefile.am | 1 | ||||
-rw-r--r-- | tests/check/elements/audioreverb.c | 229 |
14 files changed, 798 insertions, 3 deletions
@@ -1,5 +1,28 @@ 2009-01-19 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * docs/plugins/Makefile.am: + * docs/plugins/gst-plugins-good-plugins-docs.sgml: + * docs/plugins/gst-plugins-good-plugins-sections.txt: + * docs/plugins/gst-plugins-good-plugins.args: + * docs/plugins/gst-plugins-good-plugins.hierarchy: + * docs/plugins/inspect/plugin-audiofx.xml: + * docs/plugins/inspect/plugin-spectrum.xml: + * gst/audiofx/Makefile.am: + * gst/audiofx/audiofx.c: (plugin_init): + * gst/audiofx/audioreverb.c: (gst_audio_reverb_base_init), + (gst_audio_reverb_class_init), (gst_audio_reverb_init), + (gst_audio_reverb_finalize), (gst_audio_reverb_set_property), + (gst_audio_reverb_get_property), (gst_audio_reverb_setup), + (gst_audio_reverb_stop), (gst_audio_reverb_transform_ip): + * gst/audiofx/audioreverb.h: + * tests/check/Makefile.am: + * tests/check/elements/audioreverb.c: (setup_reverb), + (cleanup_reverb), (GST_START_TEST), (audioreverb_suite): + Add an echo/reverb filter to the audiofx plugin, with configurable + echo delay, intensity and feedback. Fixes bug #567874. + +2009-01-19 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * gst/spectrum/gstspectrum.c: (gst_spectrum_reset_state), (gst_spectrum_transform_ip): * gst/spectrum/gstspectrum.h: diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 8549ed3ea..1c03636b3 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -106,6 +106,7 @@ EXTRA_HFILES = \ $(top_srcdir)/gst/alpha/gstalphacolor.h \ $(top_srcdir)/gst/apetag/gstapedemux.h \ $(top_srcdir)/gst/audiofx/audioamplify.h \ + $(top_srcdir)/gst/audiofx/audioreverb.h \ $(top_srcdir)/gst/audiofx/audiodynamic.h \ $(top_srcdir)/gst/audiofx/audioinvert.h \ $(top_srcdir)/gst/audiofx/audiokaraoke.h \ diff --git a/docs/plugins/gst-plugins-good-plugins-docs.sgml b/docs/plugins/gst-plugins-good-plugins-docs.sgml index 8fedb7fa9..231017d7e 100644 --- a/docs/plugins/gst-plugins-good-plugins-docs.sgml +++ b/docs/plugins/gst-plugins-good-plugins-docs.sgml @@ -23,6 +23,7 @@ <xi:include href="xml/element-audiowsincband.xml" /> <xi:include href="xml/element-audiowsinclimit.xml" /> <xi:include href="xml/element-audiofirfilter.xml" /> + <xi:include href="xml/element-audioreverb.xml" /> <xi:include href="xml/element-audiodynamic.xml" /> <xi:include href="xml/element-audioinvert.xml" /> <xi:include href="xml/element-audiopanorama.xml" /> diff --git a/docs/plugins/gst-plugins-good-plugins-sections.txt b/docs/plugins/gst-plugins-good-plugins-sections.txt index d2de0b053..03790c5bc 100644 --- a/docs/plugins/gst-plugins-good-plugins-sections.txt +++ b/docs/plugins/gst-plugins-good-plugins-sections.txt @@ -117,6 +117,22 @@ gst_audio_iir_filter_get_type </SECTION> <SECTION> +<FILE>element-audioreverb</FILE> +<TITLE>audioreverb</TITLE> +GstAudioReverb +<SUBSECTION Standard> +GstAudioReverbClass +GstAudioReverbProcessFunc +GST_AUDIO_REVERB +GST_AUDIO_REVERB_CLASS +GST_AUDIO_REVERB_GET_CLASS +GST_IS_AUDIO_REVERB +GST_IS_AUDIO_REVERB_CLASS +GST_TYPE_AUDIO_REVERB +gst_audio_reverb_get_type +</SECTION> + +<SECTION> <FILE>element-audiodynamic</FILE> <TITLE>audiodynamic</TITLE> GstAudioDynamic diff --git a/docs/plugins/gst-plugins-good-plugins.args b/docs/plugins/gst-plugins-good-plugins.args index 0064b6605..7a2603d58 100644 --- a/docs/plugins/gst-plugins-good-plugins.args +++ b/docs/plugins/gst-plugins-good-plugins.args @@ -19708,3 +19708,63 @@ <DEFAULT></DEFAULT> </ARG> +<ARG> +<NAME>GstAudioDelay::delay</NAME> +<TYPE>guint64</TYPE> +<RANGE>>= 1</RANGE> +<FLAGS>rw</FLAGS> +<NICK>Delay</NICK> +<BLURB>Delay in nanoseconds.</BLURB> +<DEFAULT>1</DEFAULT> +</ARG> + +<ARG> +<NAME>GstAudioDelay::feedback</NAME> +<TYPE>gfloat</TYPE> +<RANGE>[0,1]</RANGE> +<FLAGS>rw</FLAGS> +<NICK>Feedback</NICK> +<BLURB>Amount of feedback.</BLURB> +<DEFAULT>0</DEFAULT> +</ARG> + +<ARG> +<NAME>GstAudioDelay::intensity</NAME> +<TYPE>gfloat</TYPE> +<RANGE>[0,1]</RANGE> +<FLAGS>rw</FLAGS> +<NICK>Intensity</NICK> +<BLURB>Intensity of the echo.</BLURB> +<DEFAULT>0</DEFAULT> +</ARG> + +<ARG> +<NAME>GstAudioReverb::delay</NAME> +<TYPE>guint64</TYPE> +<RANGE>>= 1</RANGE> +<FLAGS>rw</FLAGS> +<NICK>Delay</NICK> +<BLURB>Delay of the echo in nanoseconds.</BLURB> +<DEFAULT>1</DEFAULT> +</ARG> + +<ARG> +<NAME>GstAudioReverb::feedback</NAME> +<TYPE>gfloat</TYPE> +<RANGE>[0,1]</RANGE> +<FLAGS>rw</FLAGS> +<NICK>Feedback</NICK> +<BLURB>Amount of feedback.</BLURB> +<DEFAULT>0</DEFAULT> +</ARG> + +<ARG> +<NAME>GstAudioReverb::intensity</NAME> +<TYPE>gfloat</TYPE> +<RANGE>[0,1]</RANGE> +<FLAGS>rw</FLAGS> +<NICK>Intensity</NICK> +<BLURB>Intensity of the echo.</BLURB> +<DEFAULT>0</DEFAULT> +</ARG> + diff --git a/docs/plugins/gst-plugins-good-plugins.hierarchy b/docs/plugins/gst-plugins-good-plugins.hierarchy index 55e1cdc29..e02a501e2 100644 --- a/docs/plugins/gst-plugins-good-plugins.hierarchy +++ b/docs/plugins/gst-plugins-good-plugins.hierarchy @@ -64,6 +64,7 @@ GObject GstAudioWSincLimit GstAudioWSincBand GstAudioFIRFilter + GstAudioReverb GstIirEqualizer GstIirEqualizerNBands GstIirEqualizer3Bands @@ -221,6 +222,8 @@ GObject GstRegistry GstRingBuffer GstSignalObject + GstMixerTrack + GstMixerOptions GstCmmlTagStream GstCmmlTagHead GstCmmlTagClip diff --git a/docs/plugins/inspect/plugin-audiofx.xml b/docs/plugins/inspect/plugin-audiofx.xml index 7ca2add6f..171d8b43f 100644 --- a/docs/plugins/inspect/plugin-audiofx.xml +++ b/docs/plugins/inspect/plugin-audiofx.xml @@ -199,6 +199,27 @@ </pads> </element> <element> + <name>audioreverb</name> + <longname>Audio reverb</longname> + <class>Filter/Effect/Audio</class> + <description>Adds an echo or reverb effect to an audio stream</description> + <author>Sebastian Dröge <sebastian.droege@collabora.co.uk></author> + <pads> + <caps> + <name>sink</name> + <direction>sink</direction> + <presence>always</presence> + <details>audio/x-raw-float, width=(int){ 32, 64 }, endianness=(int)1234, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ]</details> + </caps> + <caps> + <name>src</name> + <direction>source</direction> + <presence>always</presence> + <details>audio/x-raw-float, width=(int){ 32, 64 }, endianness=(int)1234, rate=(int)[ 1, 2147483647 ], channels=(int)[ 1, 2147483647 ]</details> + </caps> + </pads> + </element> + <element> <name>audiowsincband</name> <longname>Band pass & band reject filter</longname> <class>Filter/Effect/Audio</class> diff --git a/docs/plugins/inspect/plugin-spectrum.xml b/docs/plugins/inspect/plugin-spectrum.xml index deae6a447..e88d99013 100644 --- a/docs/plugins/inspect/plugin-spectrum.xml +++ b/docs/plugins/inspect/plugin-spectrum.xml @@ -14,7 +14,7 @@ <longname>Spectrum analyzer</longname> <class>Filter/Analyzer/Audio</class> <description>Run an FFT on the audio signal, output spectrum data</description> - <author>Erik Walthinsen <omega@cse.ogi.edu>, Stefan Kost <ensonic@users.sf.net>, Sebastian Dröge <slomo@circular-chaos.org></author> + <author>Erik Walthinsen <omega@cse.ogi.edu>, Stefan Kost <ensonic@users.sf.net>, Sebastian Dröge <sebastian.droege@collabora.co.uk></author> <pads> <caps> <name>sink</name> diff --git a/gst/audiofx/Makefile.am b/gst/audiofx/Makefile.am index 0ba4f1f34..f4f02be2c 100644 --- a/gst/audiofx/Makefile.am +++ b/gst/audiofx/Makefile.am @@ -16,7 +16,8 @@ libgstaudiofx_la_SOURCES = audiofx.c\ audiofxbasefirfilter.c \ audiowsincband.c \ audiowsinclimit.c \ - audiofirfilter.c + audiofirfilter.c \ + audioreverb.c # flags used to compile this plugin libgstaudiofx_la_CFLAGS = $(GST_CFLAGS) \ @@ -46,5 +47,6 @@ noinst_HEADERS = audiopanorama.h \ audiowsincband.h \ audiowsinclimit.h \ audiofirfilter.h \ + audioreverb.h \ math_compat.h diff --git a/gst/audiofx/audiofx.c b/gst/audiofx/audiofx.c index 62b70761f..e23a638f1 100644 --- a/gst/audiofx/audiofx.c +++ b/gst/audiofx/audiofx.c @@ -36,6 +36,7 @@ #include "audiowsincband.h" #include "audiowsinclimit.h" #include "audiofirfilter.h" +#include "audioreverb.h" /* entry point to initialize the plug-in * initialize the plug-in itself @@ -69,7 +70,9 @@ plugin_init (GstPlugin * plugin) gst_element_register (plugin, "audiowsincband", GST_RANK_NONE, GST_TYPE_AUDIO_WSINC_BAND) && gst_element_register (plugin, "audiofirfilter", GST_RANK_NONE, - GST_TYPE_AUDIO_FIR_FILTER)); + GST_TYPE_AUDIO_FIR_FILTER) && + gst_element_register (plugin, "audioreverb", GST_RANK_NONE, + GST_TYPE_AUDIO_REVERB)); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, diff --git a/gst/audiofx/audioreverb.c b/gst/audiofx/audioreverb.c new file mode 100644 index 000000000..703a4ef0b --- /dev/null +++ b/gst/audiofx/audioreverb.c @@ -0,0 +1,367 @@ +/* + * GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-audioreverb + * + * <refsect2> + * audioreverb adds an echo or revert effect to an audio stream. The echo + * reverb, intensity and the percentage of feedback can be configured. + * <para> + * <programlisting> + * gst-launch filesrc location="melo1.ogg" ! audioconvert ! audioreverb reverb=500000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink + * gst-launch filesrc location="melo1.ogg" ! decodebin ! audioconvert ! audioreverb reverb=50000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink + * </programlisting> + * </para> + * </refsect2> + * + * Since: 0.10.12 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/gst.h> +#include <gst/base/gstbasetransform.h> +#include <gst/audio/audio.h> +#include <gst/audio/gstaudiofilter.h> +#include <gst/controller/gstcontroller.h> + +#include "audioreverb.h" + +#define GST_CAT_DEFAULT gst_audio_reverb_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +enum +{ + PROP_0, + PROP_DELAY, + PROP_INTENSITY, + PROP_FEEDBACK +}; + +#define ALLOWED_CAPS \ + "audio/x-raw-float," \ + " width=(int) { 32, 64 }, " \ + " endianness=(int)BYTE_ORDER," \ + " rate=(int)[1,MAX]," \ + " channels=(int)[1,MAX]" + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_audio_reverb_debug, "audioreverb", 0, "audioreverb element"); + +GST_BOILERPLATE_FULL (GstAudioReverb, gst_audio_reverb, GstAudioFilter, + GST_TYPE_AUDIO_FILTER, DEBUG_INIT); + +static void gst_audio_reverb_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_audio_reverb_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_audio_reverb_finalize (GObject * object); + +static gboolean gst_audio_reverb_setup (GstAudioFilter * self, + GstRingBufferSpec * format); +static gboolean gst_audio_reverb_stop (GstBaseTransform * base); +static GstFlowReturn gst_audio_reverb_transform_ip (GstBaseTransform * base, + GstBuffer * buf); + +static void gst_audio_reverb_transform_float (GstAudioReverb * self, + gfloat * data, guint num_samples); +static void gst_audio_reverb_transform_double (GstAudioReverb * self, + gdouble * data, guint num_samples); + +/* GObject vmethod implementations */ + +static void +gst_audio_reverb_base_init (gpointer klass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GstCaps *caps; + + gst_element_class_set_details_simple (element_class, "Audio reverb", + "Filter/Effect/Audio", + "Adds an echo or reverb effect to an audio stream", + "Sebastian Dröge <sebastian.droege@collabora.co.uk>"); + + caps = gst_caps_from_string (ALLOWED_CAPS); + gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass), + caps); + gst_caps_unref (caps); +} + +static void +gst_audio_reverb_class_init (GstAudioReverbClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstBaseTransformClass *basetransform_class = (GstBaseTransformClass *) klass; + GstAudioFilterClass *audioself_class = (GstAudioFilterClass *) klass; + + gobject_class->set_property = gst_audio_reverb_set_property; + gobject_class->get_property = gst_audio_reverb_get_property; + gobject_class->finalize = gst_audio_reverb_finalize; + + g_object_class_install_property (gobject_class, PROP_DELAY, + g_param_spec_uint64 ("delay", "Delay", + "Delay of the echo in nanoseconds", 1, G_MAXUINT64, + 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS + | GST_PARAM_CONTROLLABLE)); + + g_object_class_install_property (gobject_class, PROP_INTENSITY, + g_param_spec_float ("intensity", "Intensity", + "Intensity of the echo", 0.0, 1.0, + 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS + | GST_PARAM_CONTROLLABLE)); + + g_object_class_install_property (gobject_class, PROP_FEEDBACK, + g_param_spec_float ("feedback", "Feedback", + "Amount of feedback", 0.0, 1.0, + 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS + | GST_PARAM_CONTROLLABLE)); + + audioself_class->setup = GST_DEBUG_FUNCPTR (gst_audio_reverb_setup); + basetransform_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_audio_reverb_transform_ip); + basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_audio_reverb_stop); +} + +static void +gst_audio_reverb_init (GstAudioReverb * self, GstAudioReverbClass * klass) +{ + self->delay = 0; + self->intensity = 0.0; + self->feedback = 0.0; + + gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE); +} + +static void +gst_audio_reverb_finalize (GObject * object) +{ + GstAudioReverb *self = GST_AUDIO_REVERB (object); + + g_free (self->buffer); + self->buffer = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_audio_reverb_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioReverb *self = GST_AUDIO_REVERB (object); + + switch (prop_id) { + case PROP_DELAY:{ + guint rate, width, channels; + + GST_BASE_TRANSFORM_LOCK (self); + self->delay = g_value_get_uint64 (value); + + rate = GST_AUDIO_FILTER (self)->format.rate; + width = GST_AUDIO_FILTER (self)->format.width / 8; + channels = GST_AUDIO_FILTER (self)->format.channels; + + if (self->buffer && rate > 0) { + guint new_reverb = + MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1); + guint new_size = new_reverb * width * channels; + + if (new_size > self->buffer_size) { + guint i; + guint8 *old_buffer = self->buffer; + + self->buffer_size = new_size; + self->buffer = g_malloc0 (new_size); + + for (i = 0; i < self->buffer_size_frames; i++) { + memcpy (&self->buffer[i * width * channels], + &old_buffer[((i + + self->buffer_pos) % self->buffer_size_frames) * + width * channels], channels * width); + } + self->buffer_size_frames = self->delay_frames = new_reverb; + self->buffer_pos = 0; + } + } else if (self->buffer) { + g_free (self->buffer); + self->buffer = NULL; + } + + GST_BASE_TRANSFORM_UNLOCK (self); + } + break; + case PROP_INTENSITY:{ + GST_BASE_TRANSFORM_LOCK (self); + self->intensity = g_value_get_float (value); + GST_BASE_TRANSFORM_UNLOCK (self); + } + break; + case PROP_FEEDBACK:{ + GST_BASE_TRANSFORM_LOCK (self); + self->feedback = g_value_get_float (value); + GST_BASE_TRANSFORM_UNLOCK (self); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audio_reverb_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioReverb *self = GST_AUDIO_REVERB (object); + + switch (prop_id) { + case PROP_DELAY: + GST_BASE_TRANSFORM_LOCK (self); + g_value_set_uint64 (value, self->delay); + GST_BASE_TRANSFORM_UNLOCK (self); + break; + case PROP_INTENSITY: + GST_BASE_TRANSFORM_LOCK (self); + g_value_set_float (value, self->intensity); + GST_BASE_TRANSFORM_UNLOCK (self); + break; + case PROP_FEEDBACK: + GST_BASE_TRANSFORM_LOCK (self); + g_value_set_float (value, self->feedback); + GST_BASE_TRANSFORM_UNLOCK (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* GstAudioFilter vmethod implementations */ + +static gboolean +gst_audio_reverb_setup (GstAudioFilter * base, GstRingBufferSpec * format) +{ + GstAudioReverb *self = GST_AUDIO_REVERB (base); + gboolean ret = TRUE; + + if (format->type == GST_BUFTYPE_FLOAT && format->width == 32) + self->process = (GstAudioReverbProcessFunc) + gst_audio_reverb_transform_float; + else if (format->type == GST_BUFTYPE_FLOAT && format->width == 64) + self->process = (GstAudioReverbProcessFunc) + gst_audio_reverb_transform_double; + else + ret = FALSE; + + g_free (self->buffer); + self->buffer = NULL; + self->buffer_pos = 0; + self->buffer_size = 0; + self->buffer_size_frames = 0; + + return ret; +} + +static gboolean +gst_audio_reverb_stop (GstBaseTransform * base) +{ + GstAudioReverb *self = GST_AUDIO_REVERB (base); + + g_free (self->buffer); + self->buffer = NULL; + self->buffer_pos = 0; + self->buffer_size = 0; + self->buffer_size_frames = 0; + + return TRUE; +} + +#define TRANSFORM_FUNC(name, type) \ +static void \ +gst_audio_reverb_transform_##name (GstAudioReverb * self, \ + type * data, guint num_samples) \ +{ \ + type *buffer = (type *) self->buffer; \ + guint channels = GST_AUDIO_FILTER (self)->format.channels; \ + guint rate = GST_AUDIO_FILTER (self)->format.rate; \ + guint i, j; \ + guint reverb_index = self->buffer_size_frames - self->delay_frames; \ + gdouble reverb_off = ((((gdouble) self->delay) * rate) / GST_SECOND) - self->delay_frames; \ + \ + if (reverb_off < 0.0) \ + reverb_off = 0.0; \ + \ + num_samples /= channels; \ + \ + for (i = 0; i < num_samples; i++) { \ + guint echo0_index = ((reverb_index + self->buffer_pos) % self->buffer_size_frames) * channels; \ + guint echo1_index = ((reverb_index + self->buffer_pos +1) % self->buffer_size_frames) * channels; \ + guint rbout_index = (self->buffer_pos % self->buffer_size_frames) * channels; \ + for (j = 0; j < channels; j++) { \ + gdouble in = data[i*channels + j]; \ + gdouble echo0 = buffer[echo0_index + j]; \ + gdouble echo1 = buffer[echo1_index + j]; \ + gdouble echo = echo0 + (echo1-echo0)*reverb_off; \ + type out = in + self->intensity * echo; \ + \ + data[i*channels + j] = out; \ + \ + buffer[rbout_index + j] = in + self->feedback * echo; \ + } \ + self->buffer_pos = (self->buffer_pos + 1) % self->buffer_size_frames; \ + } \ +} + +TRANSFORM_FUNC (float, gfloat); +TRANSFORM_FUNC (double, gdouble); + +/* GstBaseTransform vmethod implementations */ +static GstFlowReturn +gst_audio_reverb_transform_ip (GstBaseTransform * base, GstBuffer * buf) +{ + GstAudioReverb *self = GST_AUDIO_REVERB (base); + guint num_samples = + GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (self)->format.width / 8); + + if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) + gst_object_sync_values (G_OBJECT (self), GST_BUFFER_TIMESTAMP (buf)); + + if (self->buffer == NULL) { + guint width, rate, channels; + + width = GST_AUDIO_FILTER (self)->format.width / 8; + rate = GST_AUDIO_FILTER (self)->format.rate; + channels = GST_AUDIO_FILTER (self)->format.channels; + + self->delay_frames = + MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1); + + self->buffer_size_frames = MAX (self->delay_frames, 1000); + self->buffer_size = self->buffer_size_frames * width * channels; + self->buffer = g_malloc0 (self->buffer_size); + self->buffer_pos = 0; + } + + self->process (self, GST_BUFFER_DATA (buf), num_samples); + + return GST_FLOW_OK; +} diff --git a/gst/audiofx/audioreverb.h b/gst/audiofx/audioreverb.h new file mode 100644 index 000000000..3ef5682e8 --- /dev/null +++ b/gst/audiofx/audioreverb.h @@ -0,0 +1,68 @@ +/* + * GStreamer + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_AUDIO_REVERB_H__ +#define __GST_AUDIO_REVERB_H__ + +#include <gst/gst.h> +#include <gst/base/gstbasetransform.h> +#include <gst/audio/audio.h> +#include <gst/audio/gstaudiofilter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_AUDIO_REVERB (gst_audio_reverb_get_type()) +#define GST_AUDIO_REVERB(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_REVERB,GstAudioReverb)) +#define GST_IS_AUDIO_REVERB(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_REVERB)) +#define GST_AUDIO_REVERB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_REVERB,GstAudioReverbClass)) +#define GST_IS_AUDIO_REVERB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_REVERB)) +#define GST_AUDIO_REVERB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_REVERB,GstAudioReverbClass)) +typedef struct _GstAudioReverb GstAudioReverb; +typedef struct _GstAudioReverbClass GstAudioReverbClass; + +typedef void (*GstAudioReverbProcessFunc) (GstAudioReverb *, guint8 *, guint); + +struct _GstAudioReverb +{ + GstAudioFilter audiofilter; + + guint64 delay; + gfloat intensity; + gfloat feedback; + + /* < private > */ + GstAudioReverbProcessFunc process; + guint delay_frames; + guint8 *buffer; + guint buffer_pos; + guint buffer_size; + guint buffer_size_frames; +}; + +struct _GstAudioReverbClass +{ + GstAudioFilterClass parent; +}; + +GType gst_audio_reverb_get_type (void); + +G_END_DECLS + +#endif /* __GST_AUDIO_REVERB_H__ */ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 5cc63dbb9..ee0afdc91 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -74,6 +74,7 @@ check_PROGRAMS = \ elements/audiocheblimit \ elements/audioiirfilter \ elements/audioamplify \ + elements/audioreverb \ elements/audiodynamic \ elements/audiowsincband \ elements/audiowsinclimit \ diff --git a/tests/check/elements/audioreverb.c b/tests/check/elements/audioreverb.c new file mode 100644 index 000000000..cafe8bbe9 --- /dev/null +++ b/tests/check/elements/audioreverb.c @@ -0,0 +1,229 @@ +/* GStreamer + * + * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gst/check/gstcheck.h> + +gboolean have_eos = FALSE; + +/* For ease of programming we use globals to keep refs for our floating + * src and sink pads we create; otherwise we always have to do get_pad, + * get_peer, and then remove references in every test function */ +GstPad *mysrcpad, *mysinkpad; + +#define REVERB_CAPS_STRING \ + "audio/x-raw-float, " \ + "channels = (int) 2, " \ + "rate = (int) 100000, " \ + "endianness = (int) BYTE_ORDER, " \ + "width = (int) 64" + +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "channels = (int) [ 1, 2 ], " + "rate = (int) [ 1, MAX ], " + "endianness = (int) BYTE_ORDER, " "width = (int) { 32, 64 }") + ); +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-float, " + "channels = (int) [ 1, 2 ], " + "rate = (int) [ 1, MAX ], " + "endianness = (int) BYTE_ORDER, " "width = (int) { 32, 64 }") + ); + +GstElement * +setup_reverb () +{ + GstElement *reverb; + + GST_DEBUG ("setup_reverb"); + reverb = gst_check_setup_element ("audioreverb"); + mysrcpad = gst_check_setup_src_pad (reverb, &srctemplate, NULL); + mysinkpad = gst_check_setup_sink_pad (reverb, &sinktemplate, NULL); + gst_pad_set_active (mysrcpad, TRUE); + gst_pad_set_active (mysinkpad, TRUE); + + return reverb; +} + +void +cleanup_reverb (GstElement * reverb) +{ + GST_DEBUG ("cleanup_reverb"); + + g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (buffers); + buffers = NULL; + + gst_pad_set_active (mysrcpad, FALSE); + gst_pad_set_active (mysinkpad, FALSE); + gst_check_teardown_src_pad (reverb); + gst_check_teardown_sink_pad (reverb); + gst_check_teardown_element (reverb); +} + +GST_START_TEST (test_passthrough) +{ + GstElement *reverb; + GstBuffer *inbuffer, *outbuffer; + GstCaps *caps; + gdouble in[] = { 1.0, -1.0, 0.0, 0.5, -0.5, 0.0 }; + gdouble *res; + + reverb = setup_reverb (); + g_object_set (G_OBJECT (reverb), "delay", 1, "intensity", 0.0, "feedback", + 0.0, NULL); + fail_unless (gst_element_set_state (reverb, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (sizeof (in)); + memcpy (GST_BUFFER_DATA (inbuffer), in, sizeof (in)); + fail_unless (memcmp (GST_BUFFER_DATA (inbuffer), in, sizeof (in)) == 0); + caps = gst_caps_from_string (REVERB_CAPS_STRING); + gst_buffer_set_caps (inbuffer, caps); + gst_caps_unref (caps); + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); + /* ... but it ends up being collected on the global buffer list */ + fail_unless_equals_int (g_list_length (buffers), 1); + fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL); + + res = (gdouble *) GST_BUFFER_DATA (outbuffer); + GST_INFO + ("expected %+lf %+lf %+lf %+lf %+lf %+lf real %+lf %+lf %+lf %+lf %+lf %+lf", + in[0], in[1], in[2], in[3], in[4], in[5], res[0], res[1], res[2], res[3], + res[4], res[5]); + fail_unless (memcmp (GST_BUFFER_DATA (outbuffer), in, sizeof (in)) == 0); + + /* cleanup */ + cleanup_reverb (reverb); +} + +GST_END_TEST; + +GST_START_TEST (test_reverb) +{ + GstElement *reverb; + GstBuffer *inbuffer, *outbuffer; + GstCaps *caps; + gdouble in[] = { 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, }; + gdouble out[] = { 1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0 }; + gdouble *res; + + reverb = setup_reverb (); + g_object_set (G_OBJECT (reverb), "delay", 20000, "intensity", 1.0, "feedback", + 0.0, NULL); + fail_unless (gst_element_set_state (reverb, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (sizeof (in)); + memcpy (GST_BUFFER_DATA (inbuffer), in, sizeof (in)); + fail_unless (memcmp (GST_BUFFER_DATA (inbuffer), in, sizeof (in)) == 0); + caps = gst_caps_from_string (REVERB_CAPS_STRING); + gst_buffer_set_caps (inbuffer, caps); + gst_caps_unref (caps); + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); + /* ... but it ends up being collected on the global buffer list */ + fail_unless_equals_int (g_list_length (buffers), 1); + fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL); + + res = (gdouble *) GST_BUFFER_DATA (outbuffer); + GST_INFO + ("expected %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf real %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf", + out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], out[8], + out[9], res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], + res[8], res[9]); + fail_unless (memcmp (GST_BUFFER_DATA (outbuffer), out, sizeof (out)) == 0); + + /* cleanup */ + cleanup_reverb (reverb); +} + +GST_END_TEST; + +GST_START_TEST (test_feedback) +{ + GstElement *reverb; + GstBuffer *inbuffer, *outbuffer; + GstCaps *caps; + gdouble in[] = { 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, }; + gdouble out[] = { 1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, -1.0 }; + gdouble *res; + + reverb = setup_reverb (); + g_object_set (G_OBJECT (reverb), "delay", 20000, "intensity", 1.0, "feedback", + 1.0, NULL); + fail_unless (gst_element_set_state (reverb, + GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, + "could not set to playing"); + + inbuffer = gst_buffer_new_and_alloc (sizeof (in)); + memcpy (GST_BUFFER_DATA (inbuffer), in, sizeof (in)); + fail_unless (memcmp (GST_BUFFER_DATA (inbuffer), in, sizeof (in)) == 0); + caps = gst_caps_from_string (REVERB_CAPS_STRING); + gst_buffer_set_caps (inbuffer, caps); + gst_caps_unref (caps); + ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); + + /* pushing gives away my reference ... */ + fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); + /* ... but it ends up being collected on the global buffer list */ + fail_unless_equals_int (g_list_length (buffers), 1); + fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL); + + res = (gdouble *) GST_BUFFER_DATA (outbuffer); + GST_INFO + ("expected %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf real %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf", + out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], out[8], + out[9], res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], + res[8], res[9]); + fail_unless (memcmp (GST_BUFFER_DATA (outbuffer), out, sizeof (out)) == 0); + + /* cleanup */ + cleanup_reverb (reverb); +} + +GST_END_TEST; + +static Suite * +audioreverb_suite (void) +{ + Suite *s = suite_create ("audioreverb"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_passthrough); + tcase_add_test (tc_chain, test_reverb); + tcase_add_test (tc_chain, test_feedback); + + return s; +} + +GST_CHECK_MAIN (audioreverb); |