gst_plugins_good/gst/audiofx/audioecho.c
changeset 27 d43ce56a1534
parent 23 29ecd5cb86b3
child 31 aec498aab1d3
equal deleted inserted replaced
23:29ecd5cb86b3 27:d43ce56a1534
     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., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 /**
       
    22  * SECTION:element-audioecho
       
    23  * @Since: 0.10.14
       
    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  * <refsect2>
       
    37  * <title>Example launch line</title>
       
    38  * |[
       
    39  * gst-launch filesrc location="melo1.ogg" ! audioconvert ! audioecho delay=500000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink
       
    40  * gst-launch filesrc location="melo1.ogg" ! decodebin ! audioconvert ! audioecho delay=50000000 intensity=0.6 feedback=0.4 ! audioconvert ! autoaudiosink
       
    41  * ]|
       
    42  * </refsect2>
       
    43  */
       
    44 
       
    45 #ifdef HAVE_CONFIG_H
       
    46 #include "config.h"
       
    47 #endif
       
    48 
       
    49 #include <gst/gst.h>
       
    50 #include <gst/base/gstbasetransform.h>
       
    51 #include <gst/audio/audio.h>
       
    52 #include <gst/audio/gstaudiofilter.h>
       
    53 #include <gst/controller/gstcontroller.h>
       
    54 
       
    55 #include "audioecho.h"
       
    56 
       
    57 #define GST_CAT_DEFAULT gst_audio_echo_debug
       
    58 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
       
    59 
       
    60 enum
       
    61 {
       
    62   PROP_0,
       
    63   PROP_DELAY,
       
    64   PROP_MAX_DELAY,
       
    65   PROP_INTENSITY,
       
    66   PROP_FEEDBACK
       
    67 };
       
    68 
       
    69 #define ALLOWED_CAPS \
       
    70     "audio/x-raw-float,"                                              \
       
    71     " width=(int) { 32, 64 }, "                                       \
       
    72     " endianness=(int)BYTE_ORDER,"                                    \
       
    73     " rate=(int)[1,MAX],"                                             \
       
    74     " channels=(int)[1,MAX]"
       
    75 
       
    76 #define DEBUG_INIT(bla) \
       
    77   GST_DEBUG_CATEGORY_INIT (gst_audio_echo_debug, "audioecho", 0, "audioecho element");
       
    78 
       
    79 GST_BOILERPLATE_FULL (GstAudioEcho, gst_audio_echo, GstAudioFilter,
       
    80     GST_TYPE_AUDIO_FILTER, DEBUG_INIT);
       
    81 
       
    82 static void gst_audio_echo_set_property (GObject * object, guint prop_id,
       
    83     const GValue * value, GParamSpec * pspec);
       
    84 static void gst_audio_echo_get_property (GObject * object, guint prop_id,
       
    85     GValue * value, GParamSpec * pspec);
       
    86 static void gst_audio_echo_finalize (GObject * object);
       
    87 
       
    88 static gboolean gst_audio_echo_setup (GstAudioFilter * self,
       
    89     GstRingBufferSpec * format);
       
    90 static gboolean gst_audio_echo_stop (GstBaseTransform * base);
       
    91 static GstFlowReturn gst_audio_echo_transform_ip (GstBaseTransform * base,
       
    92     GstBuffer * buf);
       
    93 
       
    94 static void gst_audio_echo_transform_float (GstAudioEcho * self,
       
    95     gfloat * data, guint num_samples);
       
    96 static void gst_audio_echo_transform_double (GstAudioEcho * self,
       
    97     gdouble * data, guint num_samples);
       
    98 
       
    99 /* GObject vmethod implementations */
       
   100 
       
   101 static void
       
   102 gst_audio_echo_base_init (gpointer klass)
       
   103 {
       
   104   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
       
   105   GstCaps *caps;
       
   106 
       
   107   gst_element_class_set_details_simple (element_class, "Audio echo",
       
   108       "Filter/Effect/Audio",
       
   109       "Adds an echo or reverb effect to an audio stream",
       
   110       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
       
   111 
       
   112   caps = gst_caps_from_string (ALLOWED_CAPS);
       
   113   gst_audio_filter_class_add_pad_templates (GST_AUDIO_FILTER_CLASS (klass),
       
   114       caps);
       
   115   gst_caps_unref (caps);
       
   116 }
       
   117 
       
   118 static void
       
   119 gst_audio_echo_class_init (GstAudioEchoClass * klass)
       
   120 {
       
   121   GObjectClass *gobject_class = (GObjectClass *) klass;
       
   122   GstBaseTransformClass *basetransform_class = (GstBaseTransformClass *) klass;
       
   123   GstAudioFilterClass *audioself_class = (GstAudioFilterClass *) klass;
       
   124 
       
   125   gobject_class->set_property = gst_audio_echo_set_property;
       
   126   gobject_class->get_property = gst_audio_echo_get_property;
       
   127   gobject_class->finalize = gst_audio_echo_finalize;
       
   128 
       
   129   g_object_class_install_property (gobject_class, PROP_DELAY,
       
   130       g_param_spec_uint64 ("delay", "Delay",
       
   131           "Delay of the echo in nanoseconds", 1, G_MAXUINT64,
       
   132           1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
       
   133           | GST_PARAM_CONTROLLABLE));
       
   134 
       
   135   g_object_class_install_property (gobject_class, PROP_MAX_DELAY,
       
   136       g_param_spec_uint64 ("max-delay", "Maximum Delay",
       
   137           "Maximum delay of the echo in nanoseconds"
       
   138           " (can't be changed in PLAYING or PAUSED state)",
       
   139           1, G_MAXUINT64, 1,
       
   140           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_CONTROLLABLE));
       
   141 
       
   142   g_object_class_install_property (gobject_class, PROP_INTENSITY,
       
   143       g_param_spec_float ("intensity", "Intensity",
       
   144           "Intensity of the echo", 0.0, 1.0,
       
   145           0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
       
   146           | GST_PARAM_CONTROLLABLE));
       
   147 
       
   148   g_object_class_install_property (gobject_class, PROP_FEEDBACK,
       
   149       g_param_spec_float ("feedback", "Feedback",
       
   150           "Amount of feedback", 0.0, 1.0,
       
   151           0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS
       
   152           | GST_PARAM_CONTROLLABLE));
       
   153 
       
   154   audioself_class->setup = GST_DEBUG_FUNCPTR (gst_audio_echo_setup);
       
   155   basetransform_class->transform_ip =
       
   156       GST_DEBUG_FUNCPTR (gst_audio_echo_transform_ip);
       
   157   basetransform_class->stop = GST_DEBUG_FUNCPTR (gst_audio_echo_stop);
       
   158 }
       
   159 
       
   160 static void
       
   161 gst_audio_echo_init (GstAudioEcho * self, GstAudioEchoClass * klass)
       
   162 {
       
   163   self->delay = 1;
       
   164   self->max_delay = 1;
       
   165   self->intensity = 0.0;
       
   166   self->feedback = 0.0;
       
   167 
       
   168   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
       
   169 }
       
   170 
       
   171 static void
       
   172 gst_audio_echo_finalize (GObject * object)
       
   173 {
       
   174   GstAudioEcho *self = GST_AUDIO_ECHO (object);
       
   175 
       
   176   g_free (self->buffer);
       
   177   self->buffer = NULL;
       
   178 
       
   179   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   180 }
       
   181 
       
   182 static void
       
   183 gst_audio_echo_set_property (GObject * object, guint prop_id,
       
   184     const GValue * value, GParamSpec * pspec)
       
   185 {
       
   186   GstAudioEcho *self = GST_AUDIO_ECHO (object);
       
   187 
       
   188   switch (prop_id) {
       
   189     case PROP_DELAY:{
       
   190       guint64 max_delay, delay;
       
   191 
       
   192       GST_BASE_TRANSFORM_LOCK (self);
       
   193       delay = g_value_get_uint64 (value);
       
   194       max_delay = self->max_delay;
       
   195 
       
   196       if (delay > max_delay && GST_STATE (self) > GST_STATE_READY) {
       
   197         GST_WARNING_OBJECT (self, "New delay (%" GST_TIME_FORMAT ") "
       
   198             "is larger than maximum delay (%" GST_TIME_FORMAT ")",
       
   199             GST_TIME_ARGS (delay), GST_TIME_ARGS (max_delay));
       
   200         self->delay = max_delay;
       
   201       } else {
       
   202         self->delay = delay;
       
   203         self->max_delay = MAX (delay, max_delay);
       
   204       }
       
   205       GST_BASE_TRANSFORM_UNLOCK (self);
       
   206     }
       
   207       break;
       
   208     case PROP_MAX_DELAY:{
       
   209       guint64 max_delay, delay;
       
   210 
       
   211       GST_BASE_TRANSFORM_LOCK (self);
       
   212       max_delay = g_value_get_uint64 (value);
       
   213       delay = self->delay;
       
   214 
       
   215       if (GST_STATE (self) > GST_STATE_READY) {
       
   216         GST_ERROR_OBJECT (self, "Can't change maximum delay in"
       
   217             " PLAYING or PAUSED state");
       
   218       } else {
       
   219         self->delay = delay;
       
   220         self->max_delay = max_delay;
       
   221       }
       
   222       GST_BASE_TRANSFORM_UNLOCK (self);
       
   223     }
       
   224       break;
       
   225     case PROP_INTENSITY:{
       
   226       GST_BASE_TRANSFORM_LOCK (self);
       
   227       self->intensity = g_value_get_float (value);
       
   228       GST_BASE_TRANSFORM_UNLOCK (self);
       
   229     }
       
   230       break;
       
   231     case PROP_FEEDBACK:{
       
   232       GST_BASE_TRANSFORM_LOCK (self);
       
   233       self->feedback = g_value_get_float (value);
       
   234       GST_BASE_TRANSFORM_UNLOCK (self);
       
   235     }
       
   236       break;
       
   237     default:
       
   238       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   239       break;
       
   240   }
       
   241 }
       
   242 
       
   243 static void
       
   244 gst_audio_echo_get_property (GObject * object, guint prop_id,
       
   245     GValue * value, GParamSpec * pspec)
       
   246 {
       
   247   GstAudioEcho *self = GST_AUDIO_ECHO (object);
       
   248 
       
   249   switch (prop_id) {
       
   250     case PROP_DELAY:
       
   251       GST_BASE_TRANSFORM_LOCK (self);
       
   252       g_value_set_uint64 (value, self->delay);
       
   253       GST_BASE_TRANSFORM_UNLOCK (self);
       
   254       break;
       
   255     case PROP_MAX_DELAY:
       
   256       GST_BASE_TRANSFORM_LOCK (self);
       
   257       g_value_set_uint64 (value, self->max_delay);
       
   258       GST_BASE_TRANSFORM_UNLOCK (self);
       
   259       break;
       
   260     case PROP_INTENSITY:
       
   261       GST_BASE_TRANSFORM_LOCK (self);
       
   262       g_value_set_float (value, self->intensity);
       
   263       GST_BASE_TRANSFORM_UNLOCK (self);
       
   264       break;
       
   265     case PROP_FEEDBACK:
       
   266       GST_BASE_TRANSFORM_LOCK (self);
       
   267       g_value_set_float (value, self->feedback);
       
   268       GST_BASE_TRANSFORM_UNLOCK (self);
       
   269       break;
       
   270     default:
       
   271       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   272       break;
       
   273   }
       
   274 }
       
   275 
       
   276 /* GstAudioFilter vmethod implementations */
       
   277 
       
   278 static gboolean
       
   279 gst_audio_echo_setup (GstAudioFilter * base, GstRingBufferSpec * format)
       
   280 {
       
   281   GstAudioEcho *self = GST_AUDIO_ECHO (base);
       
   282   gboolean ret = TRUE;
       
   283 
       
   284   if (format->type == GST_BUFTYPE_FLOAT && format->width == 32)
       
   285     self->process = (GstAudioEchoProcessFunc)
       
   286         gst_audio_echo_transform_float;
       
   287   else if (format->type == GST_BUFTYPE_FLOAT && format->width == 64)
       
   288     self->process = (GstAudioEchoProcessFunc)
       
   289         gst_audio_echo_transform_double;
       
   290   else
       
   291     ret = FALSE;
       
   292 
       
   293   g_free (self->buffer);
       
   294   self->buffer = NULL;
       
   295   self->buffer_pos = 0;
       
   296   self->buffer_size = 0;
       
   297   self->buffer_size_frames = 0;
       
   298 
       
   299   return ret;
       
   300 }
       
   301 
       
   302 static gboolean
       
   303 gst_audio_echo_stop (GstBaseTransform * base)
       
   304 {
       
   305   GstAudioEcho *self = GST_AUDIO_ECHO (base);
       
   306 
       
   307   g_free (self->buffer);
       
   308   self->buffer = NULL;
       
   309   self->buffer_pos = 0;
       
   310   self->buffer_size = 0;
       
   311   self->buffer_size_frames = 0;
       
   312 
       
   313   return TRUE;
       
   314 }
       
   315 
       
   316 #define TRANSFORM_FUNC(name, type) \
       
   317 static void \
       
   318 gst_audio_echo_transform_##name (GstAudioEcho * self, \
       
   319     type * data, guint num_samples) \
       
   320 { \
       
   321   type *buffer = (type *) self->buffer; \
       
   322   guint channels = GST_AUDIO_FILTER (self)->format.channels; \
       
   323   guint rate = GST_AUDIO_FILTER (self)->format.rate; \
       
   324   guint i, j; \
       
   325   guint echo_index = self->buffer_size_frames - self->delay_frames; \
       
   326   gdouble echo_off = ((((gdouble) self->delay) * rate) / GST_SECOND) - self->delay_frames; \
       
   327   \
       
   328   if (echo_off < 0.0) \
       
   329     echo_off = 0.0; \
       
   330   \
       
   331   num_samples /= channels; \
       
   332   \
       
   333   for (i = 0; i < num_samples; i++) { \
       
   334     guint echo0_index = ((echo_index + self->buffer_pos) % self->buffer_size_frames) * channels; \
       
   335     guint echo1_index = ((echo_index + self->buffer_pos +1) % self->buffer_size_frames) * channels; \
       
   336     guint rbout_index = (self->buffer_pos % self->buffer_size_frames) * channels; \
       
   337     for (j = 0; j < channels; j++) { \
       
   338       gdouble in = data[i*channels + j]; \
       
   339       gdouble echo0 = buffer[echo0_index + j]; \
       
   340       gdouble echo1 = buffer[echo1_index + j]; \
       
   341       gdouble echo = echo0 + (echo1-echo0)*echo_off; \
       
   342       type out = in + self->intensity * echo; \
       
   343       \
       
   344       data[i*channels + j] = out; \
       
   345       \
       
   346       buffer[rbout_index + j] = in + self->feedback * echo; \
       
   347     } \
       
   348     self->buffer_pos = (self->buffer_pos + 1) % self->buffer_size_frames; \
       
   349   } \
       
   350 }
       
   351 
       
   352 TRANSFORM_FUNC (float, gfloat);
       
   353 TRANSFORM_FUNC (double, gdouble);
       
   354 
       
   355 /* GstBaseTransform vmethod implementations */
       
   356 static GstFlowReturn
       
   357 gst_audio_echo_transform_ip (GstBaseTransform * base, GstBuffer * buf)
       
   358 {
       
   359   GstAudioEcho *self = GST_AUDIO_ECHO (base);
       
   360   guint num_samples =
       
   361       GST_BUFFER_SIZE (buf) / (GST_AUDIO_FILTER (self)->format.width / 8);
       
   362 
       
   363   if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf)))
       
   364     gst_object_sync_values (G_OBJECT (self), GST_BUFFER_TIMESTAMP (buf));
       
   365 
       
   366   if (self->buffer == NULL) {
       
   367     guint width, rate, channels;
       
   368 
       
   369     width = GST_AUDIO_FILTER (self)->format.width / 8;
       
   370     rate = GST_AUDIO_FILTER (self)->format.rate;
       
   371     channels = GST_AUDIO_FILTER (self)->format.channels;
       
   372 
       
   373     self->delay_frames =
       
   374         MAX (gst_util_uint64_scale (self->delay, rate, GST_SECOND), 1);
       
   375     self->buffer_size_frames =
       
   376         MAX (gst_util_uint64_scale (self->max_delay, rate, GST_SECOND), 1);
       
   377 
       
   378     self->buffer_size = self->buffer_size_frames * width * channels;
       
   379     self->buffer = g_try_malloc0 (self->buffer_size);
       
   380     self->buffer_pos = 0;
       
   381 
       
   382     if (self->buffer == NULL) {
       
   383       GST_ERROR_OBJECT (self, "Failed to allocate %u bytes", self->buffer_size);
       
   384       return GST_FLOW_ERROR;
       
   385     }
       
   386   }
       
   387 
       
   388   self->process (self, GST_BUFFER_DATA (buf), num_samples);
       
   389 
       
   390   return GST_FLOW_OK;
       
   391 }