"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 }