ladspa: improved port to gstreamer 1.0
authorJuan Manuel Borges Caño <juanmabcmail@gmail.com>
Fri, 3 May 2013 09:34:34 +0000 (11:34 +0200)
committerStefan Sauer <ensonic@users.sf.net>
Fri, 3 May 2013 10:36:44 +0000 (12:36 +0200)
Fixes: #698927

12 files changed:
configure.ac
ext/ladspa/Makefile.am
ext/ladspa/gstladspa.c
ext/ladspa/gstladspa.h
ext/ladspa/gstladspafilter.c [new file with mode: 0644]
ext/ladspa/gstladspafilter.h [new file with mode: 0644]
ext/ladspa/gstladspasink.c [new file with mode: 0644]
ext/ladspa/gstladspasink.h [new file with mode: 0644]
ext/ladspa/gstladspasource.c [new file with mode: 0644]
ext/ladspa/gstladspasource.h [new file with mode: 0644]
ext/ladspa/gstladspautils.c [new file with mode: 0644]
ext/ladspa/gstladspautils.h [new file with mode: 0644]

index 2bc3219..6e0a5e8 100644 (file)
@@ -346,7 +346,7 @@ GST_PLUGINS_NONPORTED=" cdxaparse \
  videomeasure videosignal vmnc \
  linsys vcd \
  apexsink cdaudio dc1394 dirac directfb \
- gsettings ladspa \
+ gsettings \
  musepack musicbrainz nas neon ofa openal sdl sndfile timidity \
  directdraw direct3d9 acm wininet \
  xvid lv2 teletextdec sndio osx_video quicktime"
index a6e2024..d9b9c2f 100644 (file)
@@ -1,15 +1,32 @@
 plugin_LTLIBRARIES = libgstladspa.la
 
-libgstladspa_la_SOURCES = gstladspa.c
+libgstladspa_la_SOURCES = \
+       gstladspautils.c \
+       gstladspafilter.c \
+       gstladspasource.c \
+       gstladspasink.c \
+       gstladspa.c
 libgstladspa_la_CFLAGS = \
        -I$(top_srcdir)/gst-libs \
        $(GST_PLUGINS_BASE_CFLAGS) \
-       $(GST_CFLAGS) $(LRDF_CFLAGS)
+       $(GST_BASE_CFLAGS) \
+       $(GST_CFLAGS) \
+       $(LRDF_CFLAGS) \
+       $(GST_PLUGINS_BAD_CFLAGS)
 libgstladspa_la_LIBADD = \
-       $(top_builddir)/gst-libs/gst/signalprocessor/libgstsignalprocessor-@GST_API_VERSION@.la \
-       $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \
-       $(LIBM) $(LRDF_LIBS)
+       $(GST_PLUGINS_BASE_LIBS) \
+       -lgstaudio-$(GST_API_VERSION) \
+       $(GST_BASE_LIBS) \
+       $(GST_LIBS) \
+       $(LIBM) \
+       $(LRDF_LIBS) \
+       $(GST_LIBS) 
 libgstladspa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstladspa_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 
-noinst_HEADERS = gstladspa.h
+noinst_HEADERS = \
+       gstladspautils.h \
+       gstladspafilter.h \
+       gstladspasource.h \
+       gstladspasink.h \
+       gstladspa.h
index f79a051..0acb245 100644 (file)
@@ -1,7 +1,8 @@
-/* GStreamer
+/* GStreamer LADSPA plugin
  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
  *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
  *               2003 Andy Wingo <wingo at pobox.com>
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
+
 /**
  * SECTION:element-ladspa
- * @short_description: bridge for ladspa (Linux Audio Developer's Simple Plugin API)
+ * @short_description: bridge for LADSPA (Linux Audio Developer's Simple Plugin API)
+ * @see_also: #GstAudioConvert #GstAudioResample, #GstAudioTestSrc, #GstAutoAudioSink
+ * 
+ * The LADSPA (Linux Audio Developer's Simple Plugin API) element is a bridge
+ * for plugins using the <ulink url="http://www.ladspa.org/">LADSPA</ulink> API.
+ * It scans all installed LADSPA plugins and registers them as gstreamer
+ * elements. If available it can also parse LRDF files and use the metadata for
+ * element classification. The functionality you get depends on the LADSPA plugins
+ * you have installed.
  * 
- * The ladspa (Linux Audio Developer's Simple Plugin API) element is a bridge
- * for plugins using the <ulink url="http://www.ladspa.org/">ladspa</ulink> API.
- * It scans all installed ladspa plugins and registers them as gstreamer
- * elements. If available it can also parse lrdf files and use the metadata for
- * element classification.
+ * First off all you can apply not live LADSPA filters without this plugin:
+ * 
+ * <refsect2>
+ * <title>Example LADSPA line without this plugins</title>
+ * |[
+ * (padsp) listplugins
+ * (padsp) analyseplugin cmt.so amp_mono
+ * gst-launch -e filesrc location="$myfile" ! decodebin ! audioconvert ! audioresample ! "audio/x-raw,format=S16LE,rate=48000,channels=1" ! wavenc ! filesink location="testin.wav"
+ * (padsp) applyplugin testin.wav testout.wav cmt.so amp_mono 2 
+ * gst-launch playbin uri=file://"$PWD"/testout.wav
+ * ]| Decode any audio file into wav with the format expected for the specific ladspa plugin to be applied, apply the ladspa filter and play it.
+ * </refsect2>
+ *
+ * Now with this plugin:
+ *
+ * <refsect2>
+ * <title>Example LADSPA line with this plugins</title>
+ * |[
+ * gst-launch autoaudiosrc ! ladspa-cmt-so-amp-mono gain=2 ! ladspa-caps-so-plate ! ladspa-tap-echo-so-tap-stereo-echo l-delay=500 r-haas-delay=500 ! tee name=myT myT. ! queue ! autoaudiosink myT. ! queue ! audioconvert ! goom ! videoconvert ! xvimagesink pixel-aspect-ratio=3/4
+ * ]| Get audio input, filter it through CAPS Plate and TAP Stereo Echo, play it and show a visualization (recommended hearphones).
+ * </refsect2>
+ *
+ * In case you wonder the plugin naming scheme, quoting ladspa.h:
+ *   "Plugin types should be identified by file and label rather than by
+ *   index or plugin name, which may be changed in new plugin versions."
+ * This is really the best way then, and so it is less prone to conflicts.
+ *
+ * Also it is worth noting that LADSPA provides a control in and out interface,
+ * on top of the audio in and out one, so some parameters are readable too.
+ *
+ * You can see the listing of plugins available with:
+ * <refsect2>
+ * <title>Inspecting the plugins list</title>
+ * |[
+ * gst-inspect ladspa
+ * ]| List available LADSPA plugins on gstreamer.
+ * </refsect2>
+ *
+ * You can see the parameters of any plugin with:
+ * <refsect2>
+ * <title>Inspecting the plugins</title>
+ * |[
+ * gst-inspect ladspa-retro-flange-1208-so-retroFlange
+ * ]| List details of the plugin, parameters, range and defaults included.
+ * </refsect2>
+ *
+ * The elements categorize in: 
+ * <itemizedlist>
+ * <listitem><para>Filter/Effect/Audio/LADSPA:</para>
+ * <refsect2>
+ * <title>Example Filter/Effect/Audio/LADSPA line with this plugins</title>
+ * |[
+ * gst-launch filesrc location="$myfile" ! decodebin ! audioconvert ! audioresample ! ladspa-calf-so-reverb decay-time=15 high-frq-damp=20000 room-size=5 diffusion=1 wet-amount=2 dry-amount=2 pre-delay=50 bass-cut=20000 treble-cut=20000 ! ladspa-tap-echo-so-tap-stereo-echo l-delay=500 r-haas-delay=500 ! autoaudiosink
+ * ]| Decode any audio file, filter it through Calf Reverb LADSPA then TAP Stereo Echo, and play it.
+ * </refsect2>
+ * <refsect2>
+ * </listitem>
+ * <listitem><para>Source/Audio/LADSPA:</para> 
+ * <refsect2>
+ * <title>Example Source/Audio/LADSPA line with this plugins</title>
+ * |[
+ * gst-launch -e ladspasrc-sine-so-sine-fcac frequency=220 amplitude=100 ! audioconvert ! "audio/x-raw,rate=22050" ! autoaudiosink
+ * ]| Generate a sine wave with Sine Oscillator (Freq:control, Amp:control), convert it to 22050 Hz and play it.
+ * </refsect2>
+ * <refsect2>
+ * <title>Example Source/Audio/LADSPA line with this plugins</title>
+ * |[
+ * gst-launch -e ladspasrc-caps-so-click bpm=240 volume=1 ! autoaudiosink
+ * ]| Generate clicks with CAPS Click - Metronome at 240 beats per minute and play it.
+ * </refsect2>
+ * <refsect2>
+ * <title>Example Source/Audio/LADSPA line with this plugins</title>
+ * |[
+ * gst-launch -e ladspasrc-random-1661-so-random-fcsc-oa ! ladspa-cmt-so-amp-mono gain=1.5 ! ladspa-caps-so-plate ! tee name=myT myT. ! queue ! autoaudiosink myT. ! queue ! audioconvert ! wavescope ! videoconvert ! autovideosink
+ * ]| Generate random wave, filter it trhough Mono Amplifier and Versatile Plate Reverb, and play, while showing, it.
+ * </refsect2>
+ * </listitem>
+ * <listitem><para>Sink/Audio/LADSPA:</para>
+ * <refsect2>
+ * <title>Example Sink/Audio/LADSPA line with this plugins</title>
+ * |[
+ * gst-launch -e autoaudiosrc ! ladspa-cmt-so-amp-mono gain=2 ! ladspa-caps-so-plate ! ladspa-tap-echo-so-tap-stereo-echo l-delay=500 r-haas-delay=500 ! tee name=myT myT. ! audioconvert ! audioresample ! queue ! ladspasink-cmt-so-null-ai myT. ! audioconvert ! audioresample ! queue ! goom ! videoconvert ! xvimagesink pixel-aspect-ratio=3/4
+ * ]| Get audio input, filter it trhough Mono Amplifier, CAPS Plate LADSPA and TAP Stereo Echo, explicitily anulate audio with Null (Audio Input), and play a visualization (recommended hearphones).
+ * </refsect2>
+ * </listitem>
+ * </itemizedlist>
  */
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include <string.h>
-#include <math.h>
-#include <gst/audio/audio.h>
 
-#include "gstladspa.h"
-#include <ladspa.h>             /* main ladspa sdk include file */
+#include "gstladspautils.h"
+#include "gstladspafilter.h"
+#include "gstladspasource.h"
+#include "gstladspasink.h"
+#include <gst/gst-i18n-plugin.h>
+
+#include <gmodule.h>
+#include <string.h>
+#include <ladspa.h>
 #ifdef HAVE_LRDF
 #include <lrdf.h>
 #endif
 
-/* 1.0 and the 1.1 preliminary headers don't define a version, but 1.1 final
-   does */
+GST_DEBUG_CATEGORY (ladspa_debug);
+#define GST_CAT_DEFAULT ladspa_debug
+
+/*
+ * 1.0 and the 1.1 preliminary headers don't define a version, but
+ * 1.1 finally does
+ */
 #ifndef LADSPA_VERSION
 #define LADSPA_VERSION "1.0"
 #endif
   "/usr/local/lib/ladspa" G_SEARCHPATH_SEPARATOR_S \
   LIBDIR "/ladspa"
 
-static void gst_ladspa_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec);
-static void gst_ladspa_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec);
-
-static gboolean gst_ladspa_setup (GstSignalProcessor * sigproc, GstCaps * caps);
-static gboolean gst_ladspa_start (GstSignalProcessor * sigproc);
-static void gst_ladspa_stop (GstSignalProcessor * sigproc);
-static void gst_ladspa_cleanup (GstSignalProcessor * sigproc);
-static void gst_ladspa_process (GstSignalProcessor * sigproc, guint nframes);
-
-static GstSignalProcessorClass *parent_class;
-
-static GstPlugin *ladspa_plugin;
-
-GST_DEBUG_CATEGORY_STATIC (ladspa_debug);
-#define GST_CAT_DEFAULT ladspa_debug
-
-static GQuark descriptor_quark = 0;
-
-
-static void
-gst_ladspa_base_init (gpointer g_class)
-{
-  GstLADSPAClass *klass = (GstLADSPAClass *) g_class;
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-  GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class);
-  LADSPA_Descriptor *desc;
-  guint j, audio_in_count, audio_out_count, control_in_count, control_out_count;
-  const gchar *klass_tags;
-  gchar *longname, *author;
-#ifdef HAVE_LRDF
-  gchar *uri;
-#endif
-  gchar *extra_klass_tags = NULL;
-
-  GST_DEBUG ("base_init %p", g_class);
-
-  desc = (LADSPA_Descriptor *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
-      descriptor_quark);
-  g_assert (desc);
-  klass->descriptor = desc;
-
-  /* pad templates */
-  gsp_class->num_audio_in = 0;
-  gsp_class->num_audio_out = 0;
-  /* properties */
-  gsp_class->num_control_in = 0;
-  gsp_class->num_control_out = 0;
-
-  for (j = 0; j < desc->PortCount; j++) {
-    LADSPA_PortDescriptor p = desc->PortDescriptors[j];
-
-    if (LADSPA_IS_PORT_AUDIO (p)) {
-      gchar *name = g_strdup ((gchar *) desc->PortNames[j]);
-
-      /* FIXME: group stereo pairs into a stereo pad
-       * ladspa-fx have "XXX (Left)" and "XXX (Right)"
-       * where XXX={In,Input,Out,Output}
-       */
-
-      GST_DEBUG ("LADSPA port name: \"%s\"", name);
-      /* replaces all spaces with underscores, and then remaining special chars
-       * with '-'
-       * FIXME: why, pads can have any name
-       */
-      g_strdelimit (name, " ", '_');
-      g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_-><=", '-');
-      GST_DEBUG ("GStreamer pad name: \"%s\"", name);
-
-      if (LADSPA_IS_PORT_INPUT (p))
-        gst_signal_processor_class_add_pad_template (gsp_class, name,
-            GST_PAD_SINK, gsp_class->num_audio_in++, 1);
-      else
-        gst_signal_processor_class_add_pad_template (gsp_class, name,
-            GST_PAD_SRC, gsp_class->num_audio_out++, 1);
-
-      g_free (name);
-    } else if (LADSPA_IS_PORT_CONTROL (p)) {
-      if (LADSPA_IS_PORT_INPUT (p))
-        gsp_class->num_control_in++;
-      else
-        gsp_class->num_control_out++;
-    }
-  }
-
-  longname = g_locale_to_utf8 (desc->Name, -1, NULL, NULL, NULL);
-  if (!longname)
-    longname = g_strdup ("no description available");
-  author = g_locale_to_utf8 (desc->Maker, -1, NULL, NULL, NULL);
-  if (!author)
-    author = g_strdup ("no author available");
-
-#ifdef HAVE_LRDF
-  /* libldrf support, we want to get extra class information here */
-  uri = g_strdup_printf (LADSPA_BASE "%ld", desc->UniqueID);
-  if (uri) {
-    lrdf_statement query = { 0, };
-    lrdf_uris *uris;
-    gchar *str, *base_type = NULL;
-
-    GST_DEBUG ("uri (id=%d) : %s", desc->UniqueID, uri);
-    /* we can take this directly from 'desc', keep this example for future
-       attributes. 
-       if ((str = lrdf_get_setting_metadata (uri, "title"))) {
-       GST_DEBUG ("title : %s", str);
-       }
-       if ((str = lrdf_get_setting_metadata (uri, "creator"))) {
-       GST_DEBUG ("creator : %s", str);
-       }
-     */
-
-    /* get the rdf:type for this plugin */
-    query.subject = uri;
-    query.predicate = (char *) RDF_BASE "type";
-    query.object = (char *) "?";
-    query.next = NULL;
-    uris = lrdf_match_multi (&query);
-    if (uris) {
-      if (uris->count == 1) {
-        base_type = g_strdup (uris->items[0]);
-        GST_DEBUG ("base_type :  %s", base_type);
-      }
-      lrdf_free_uris (uris);
-    }
-
-    /* query taxonomy */
-    if (base_type) {
-      uris = lrdf_get_all_superclasses (base_type);
-      if (uris) {
-        guint32 j;
-
-        for (j = 0; j < uris->count; j++) {
-          GST_LOG ("parent_type_uri : %s", uris->items[j]);
-          if ((str = lrdf_get_label (uris->items[j]))) {
-            GST_DEBUG ("parent_type_label : %s", str);
-            if (extra_klass_tags) {
-              gchar *old_tags = extra_klass_tags;
-              extra_klass_tags = g_strconcat (extra_klass_tags, "/", str, NULL);
-              g_free (old_tags);
-            } else {
-              extra_klass_tags = g_strconcat ("/", str, NULL);
-            }
-          }
-        }
-        lrdf_free_uris (uris);
-      }
-      g_free (base_type);
-    }
-
-    /* we can use this for the presets
-       uris = lrdf_get_setting_uris (desc->UniqueID);
-       if (uris) {
-       guint32 j;
-
-       for (j = 0; j < uris->count; j++) {
-       GST_INFO ("setting_uri : %s", uris->items[j]);
-       if ((str = lrdf_get_label (uris->items[j]))) {
-       GST_INFO ("setting_label : %s", str);
-       }
-       }
-       lrdf_free_uris (uris);
-       }
-     */
-
-  }
-  g_free (uri);
-#endif
-
-  if (gsp_class->num_audio_in == 0)
-    klass_tags = "Source/Audio/LADSPA";
-  else if (gsp_class->num_audio_out == 0) {
-    if (gsp_class->num_control_out == 0)
-      klass_tags = "Sink/Audio/LADSPA";
-    else
-      klass_tags = "Sink/Analyzer/Audio/LADSPA";
-  } else
-    klass_tags = "Filter/Effect/Audio/LADSPA";
-
-#ifdef HAVE_LRDF
-  if (extra_klass_tags) {
-    char *s = g_strconcat (klass_tags, extra_klass_tags, NULL);
-    g_free (extra_klass_tags);
-    extra_klass_tags = s;
-  }
-#endif
-  GST_INFO ("tags : %s", klass_tags);
-  gst_element_class_set_metadata (element_class, longname,
-      extra_klass_tags ? extra_klass_tags : klass_tags, longname, author);
-  g_free (longname);
-  g_free (author);
-  g_free (extra_klass_tags);
-
-  klass->audio_in_portnums = g_new0 (gint, gsp_class->num_audio_in);
-  klass->audio_out_portnums = g_new0 (gint, gsp_class->num_audio_out);
-  klass->control_in_portnums = g_new0 (gint, gsp_class->num_control_in);
-  klass->control_out_portnums = g_new0 (gint, gsp_class->num_control_out);
-
-  audio_in_count = audio_out_count = control_in_count = control_out_count = 0;
-
-  for (j = 0; j < desc->PortCount; j++) {
-    LADSPA_PortDescriptor p = desc->PortDescriptors[j];
-
-    if (LADSPA_IS_PORT_AUDIO (p)) {
-      if (LADSPA_IS_PORT_INPUT (p))
-        klass->audio_in_portnums[audio_in_count++] = j;
-      else
-        klass->audio_out_portnums[audio_out_count++] = j;
-    } else if (LADSPA_IS_PORT_CONTROL (p)) {
-      if (LADSPA_IS_PORT_INPUT (p))
-        klass->control_in_portnums[control_in_count++] = j;
-      else
-        klass->control_out_portnums[control_out_count++] = j;
-    }
-  }
-
-  g_assert (audio_in_count == gsp_class->num_audio_in);
-  g_assert (audio_out_count == gsp_class->num_audio_out);
-  g_assert (control_in_count == gsp_class->num_control_in);
-  g_assert (control_out_count == gsp_class->num_control_out);
-
-  if (!LADSPA_IS_INPLACE_BROKEN (desc->Properties))
-    GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass);
-
-  klass->descriptor = desc;
-}
-
-static gchar *
-gst_ladspa_class_get_param_name (GstLADSPAClass * klass, gint portnum)
-{
-  LADSPA_Descriptor *desc;
-  gchar *ret, *paren;
-
-  desc = klass->descriptor;
-
-  ret = g_strdup (desc->PortNames[portnum]);
-
-  paren = g_strrstr (ret, " (");
-  if (paren != NULL)
-    *paren = '\0';
-
-  /* this is the same thing that param_spec_* will do */
-  g_strcanon (ret, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
-  /* satisfy glib2 (argname[0] must be [A-Za-z]) */
-  if (!((ret[0] >= 'a' && ret[0] <= 'z') || (ret[0] >= 'A' && ret[0] <= 'Z'))) {
-    gchar *tempstr = ret;
-
-    ret = g_strconcat ("param-", ret, NULL);
-    g_free (tempstr);
-  }
-
-  /* check for duplicate property names */
-  if (g_object_class_find_property (G_OBJECT_CLASS (klass), ret)) {
-    gint n = 1;
-    gchar *nret = g_strdup_printf ("%s-%d", ret, n++);
-
-    while (g_object_class_find_property (G_OBJECT_CLASS (klass), nret)) {
-      g_free (nret);
-      nret = g_strdup_printf ("%s-%d", ret, n++);
-    }
-    g_free (ret);
-    ret = nret;
-  }
-
-  GST_DEBUG ("built property name '%s' from port name '%s'", ret,
-      desc->PortNames[portnum]);
-
-  return ret;
-}
-
-static GParamSpec *
-gst_ladspa_class_get_param_spec (GstLADSPAClass * klass, gint portnum)
-{
-  LADSPA_Descriptor *desc;
-  GParamSpec *ret;
-  gchar *name;
-  gint hintdesc, perms;
-  gfloat lower, upper, def;
-
-  desc = klass->descriptor;
-
-  name = gst_ladspa_class_get_param_name (klass, portnum);
-  perms = G_PARAM_READABLE;
-  if (LADSPA_IS_PORT_INPUT (desc->PortDescriptors[portnum]))
-    perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
-  if (LADSPA_IS_PORT_CONTROL (desc->PortDescriptors[portnum]))
-    perms |= GST_PARAM_CONTROLLABLE;
-
-  /* short name for hint descriptor */
-  hintdesc = desc->PortRangeHints[portnum].HintDescriptor;
-
-  if (LADSPA_IS_HINT_TOGGLED (hintdesc)) {
-    ret = g_param_spec_boolean (name, name, name, FALSE, perms);
-    g_free (name);
-    return ret;
-  }
-
-  if (LADSPA_IS_HINT_BOUNDED_BELOW (hintdesc))
-    lower = desc->PortRangeHints[portnum].LowerBound;
-  else
-    lower = -G_MAXFLOAT;
-
-  if (LADSPA_IS_HINT_BOUNDED_ABOVE (hintdesc))
-    upper = desc->PortRangeHints[portnum].UpperBound;
-  else
-    upper = G_MAXFLOAT;
-
-  if (LADSPA_IS_HINT_SAMPLE_RATE (hintdesc)) {
-    /* FIXME! */
-    lower *= 44100;
-    upper *= 44100;
-  }
-
-  if (LADSPA_IS_HINT_INTEGER (hintdesc)) {
-    lower = CLAMP (lower, G_MININT, G_MAXINT);
-    upper = CLAMP (upper, G_MININT, G_MAXINT);
-  }
-
-  /* default to lower bound */
-  def = lower;
-
-#ifdef LADSPA_IS_HINT_HAS_DEFAULT
-  if (LADSPA_IS_HINT_HAS_DEFAULT (hintdesc)) {
-    if (LADSPA_IS_HINT_DEFAULT_0 (hintdesc))
-      def = 0.0;
-    else if (LADSPA_IS_HINT_DEFAULT_1 (hintdesc))
-      def = 1.0;
-    else if (LADSPA_IS_HINT_DEFAULT_100 (hintdesc))
-      def = 100.0;
-    else if (LADSPA_IS_HINT_DEFAULT_440 (hintdesc))
-      def = 440.0;
-    if (LADSPA_IS_HINT_DEFAULT_MINIMUM (hintdesc))
-      def = lower;
-    else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM (hintdesc))
-      def = upper;
-    else if (LADSPA_IS_HINT_LOGARITHMIC (hintdesc)) {
-      if (LADSPA_IS_HINT_DEFAULT_LOW (hintdesc))
-        def = exp (0.75 * log (lower) + 0.25 * log (upper));
-      else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hintdesc))
-        def = exp (0.5 * log (lower) + 0.5 * log (upper));
-      else if (LADSPA_IS_HINT_DEFAULT_HIGH (hintdesc))
-        def = exp (0.25 * log (lower) + 0.75 * log (upper));
-    } else {
-      if (LADSPA_IS_HINT_DEFAULT_LOW (hintdesc))
-        def = 0.75 * lower + 0.25 * upper;
-      else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hintdesc))
-        def = 0.5 * lower + 0.5 * upper;
-      else if (LADSPA_IS_HINT_DEFAULT_HIGH (hintdesc))
-        def = 0.25 * lower + 0.75 * upper;
-    }
-  }
-#endif /* LADSPA_IS_HINT_HAS_DEFAULT */
-
-  if (lower > upper) {
-    gfloat tmp;
-
-    /* silently swap */
-    tmp = lower;
-    lower = upper;
-    upper = tmp;
-  }
-
-  def = CLAMP (def, lower, upper);
-
-  if (LADSPA_IS_HINT_INTEGER (hintdesc)) {
-    ret = g_param_spec_int (name, name, name, lower, upper, def, perms);
-  } else {
-    ret = g_param_spec_float (name, name, name, lower, upper, def, perms);
-  }
-
-  g_free (name);
-
-  return ret;
-}
-
-static void
-gst_ladspa_class_init (GstLADSPAClass * klass, LADSPA_Descriptor * desc)
-{
-  GObjectClass *gobject_class;
-  GstSignalProcessorClass *gsp_class;
-  GParamSpec *p;
-  gint i, ix;
-
-  GST_DEBUG ("class_init %p", klass);
-
-  gobject_class = (GObjectClass *) klass;
-  gobject_class->set_property = gst_ladspa_set_property;
-  gobject_class->get_property = gst_ladspa_get_property;
-
-  gsp_class = GST_SIGNAL_PROCESSOR_CLASS (klass);
-  gsp_class->setup = gst_ladspa_setup;
-  gsp_class->start = gst_ladspa_start;
-  gsp_class->stop = gst_ladspa_stop;
-  gsp_class->cleanup = gst_ladspa_cleanup;
-  gsp_class->process = gst_ladspa_process;
-
-  /* properties have an offset of 1 */
-  ix = 1;
-
-  /* register properties */
-
-  for (i = 0; i < gsp_class->num_control_in; i++, ix++) {
-    p = gst_ladspa_class_get_param_spec (klass, klass->control_in_portnums[i]);
-    g_object_class_install_property (gobject_class, ix, p);
-  }
-
-  for (i = 0; i < gsp_class->num_control_out; i++, ix++) {
-    p = gst_ladspa_class_get_param_spec (klass, klass->control_out_portnums[i]);
-    g_object_class_install_property (gobject_class, ix, p);
-  }
-}
+GQuark descriptor_quark = 0;
 
 static void
-gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * klass)
+ladspa_describe_plugin (GstPlugin * plugin,
+    const gchar * filename, LADSPA_Descriptor_Function descriptor_function)
 {
-  ladspa->descriptor = klass->descriptor;
-  ladspa->activated = FALSE;
-  ladspa->inplace_broken =
-      LADSPA_IS_INPLACE_BROKEN (ladspa->descriptor->Properties);
-}
-
-static void
-gst_ladspa_set_property (GObject * object, guint prop_id, const GValue * value,
-    GParamSpec * pspec)
-{
-  GstSignalProcessor *gsp;
-  GstSignalProcessorClass *gsp_class;
-
-  gsp = GST_SIGNAL_PROCESSOR (object);
-  gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object);
-
-  /* remember, properties have an offset of 1 */
-  prop_id--;
-
-  /* only input ports */
-  g_return_if_fail (prop_id < gsp_class->num_control_in);
-
-  /* now see what type it is */
-  switch (pspec->value_type) {
-    case G_TYPE_BOOLEAN:
-      gsp->control_in[prop_id] = g_value_get_boolean (value) ? 1.f : 0.f;
-      break;
-    case G_TYPE_INT:
-      gsp->control_in[prop_id] = g_value_get_int (value);
-      break;
-    case G_TYPE_FLOAT:
-      gsp->control_in[prop_id] = g_value_get_float (value);
-      break;
-    default:
-      g_assert_not_reached ();
-  }
-}
-
-static void
-gst_ladspa_get_property (GObject * object, guint prop_id, GValue * value,
-    GParamSpec * pspec)
-{
-  GstSignalProcessor *gsp;
-  GstSignalProcessorClass *gsp_class;
-  gfloat *controls;
-
-  gsp = GST_SIGNAL_PROCESSOR (object);
-  gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object);
-
-  /* remember, properties have an offset of 1 */
-  prop_id--;
-
-  if (prop_id < gsp_class->num_control_in) {
-    controls = gsp->control_in;
-  } else if (prop_id < gsp_class->num_control_in + gsp_class->num_control_out) {
-    controls = gsp->control_out;
-    prop_id -= gsp_class->num_control_in;
-  } else {
-    g_return_if_reached ();
-  }
-
-  /* now see what type it is */
-  switch (pspec->value_type) {
-    case G_TYPE_BOOLEAN:
-      g_value_set_boolean (value, controls[prop_id] > 0.5);
-      break;
-    case G_TYPE_INT:
-      g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT));
-      break;
-    case G_TYPE_FLOAT:
-      g_value_set_float (value, controls[prop_id]);
-      break;
-    default:
-      g_return_if_reached ();
-  }
-}
-
-static gboolean
-gst_ladspa_setup (GstSignalProcessor * gsp, GstCaps * caps)
-{
-  GstLADSPA *ladspa;
-  GstLADSPAClass *oclass;
-  GstSignalProcessorClass *gsp_class;
-  LADSPA_Descriptor *desc;
-  gint i;
-
-  gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
-  ladspa = (GstLADSPA *) gsp;
-  oclass = (GstLADSPAClass *) gsp_class;
-  desc = ladspa->descriptor;
-
-  g_return_val_if_fail (ladspa->handle == NULL, FALSE);
-  g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
-
-  GST_DEBUG_OBJECT (ladspa, "instantiating the plugin at %d Hz",
-      gsp->sample_rate);
-
-  if (!(ladspa->handle = desc->instantiate (desc, gsp->sample_rate)))
-    goto no_instance;
-
-  /* connect the control ports */
-  for (i = 0; i < gsp_class->num_control_in; i++)
-    desc->connect_port (ladspa->handle,
-        oclass->control_in_portnums[i], &(gsp->control_in[i]));
-  for (i = 0; i < gsp_class->num_control_out; i++)
-    desc->connect_port (ladspa->handle,
-        oclass->control_out_portnums[i], &(gsp->control_out[i]));
-
-  return TRUE;
-
-no_instance:
-  {
-    GST_WARNING_OBJECT (gsp, "could not create instance");
-    return FALSE;
-  }
-}
-
-static gboolean
-gst_ladspa_start (GstSignalProcessor * gsp)
-{
-  GstLADSPA *ladspa;
-  LADSPA_Descriptor *desc;
-
-  ladspa = (GstLADSPA *) gsp;
-  desc = ladspa->descriptor;
-
-  g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
-  g_return_val_if_fail (ladspa->handle != NULL, FALSE);
-
-  GST_DEBUG_OBJECT (ladspa, "activating");
-
-  if (desc->activate)
-    desc->activate (ladspa->handle);
-
-  ladspa->activated = TRUE;
-
-  return TRUE;
-}
-
-static void
-gst_ladspa_stop (GstSignalProcessor * gsp)
-{
-  GstLADSPA *ladspa;
-  LADSPA_Descriptor *desc;
-
-  ladspa = (GstLADSPA *) gsp;
-  desc = ladspa->descriptor;
-
-  g_return_if_fail (ladspa->activated == TRUE);
-  g_return_if_fail (ladspa->handle != NULL);
-
-  GST_DEBUG_OBJECT (ladspa, "deactivating");
-
-  if (desc->activate)
-    desc->activate (ladspa->handle);
-
-  ladspa->activated = FALSE;
-}
-
-static void
-gst_ladspa_cleanup (GstSignalProcessor * gsp)
-{
-  GstLADSPA *ladspa;
-  LADSPA_Descriptor *desc;
-
-  ladspa = (GstLADSPA *) gsp;
-  desc = ladspa->descriptor;
-
-  g_return_if_fail (ladspa->activated == FALSE);
-  g_return_if_fail (ladspa->handle != NULL);
-
-  GST_DEBUG_OBJECT (ladspa, "cleaning up");
-
-  if (desc->cleanup)
-    desc->cleanup (ladspa->handle);
-
-  ladspa->handle = NULL;
-}
-
-static void
-gst_ladspa_process (GstSignalProcessor * gsp, guint nframes)
-{
-  GstSignalProcessorClass *gsp_class;
-  GstLADSPA *ladspa;
-  GstLADSPAClass *oclass;
-  LADSPA_Descriptor *desc;
+  const LADSPA_Descriptor *desc;
   guint i;
 
-  gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
-  ladspa = (GstLADSPA *) gsp;
-  oclass = (GstLADSPAClass *) gsp_class;
-  desc = ladspa->descriptor;
-
-  for (i = 0; i < gsp_class->num_audio_in; i++)
-    desc->connect_port (ladspa->handle, oclass->audio_in_portnums[i],
-        gsp->audio_in[i]);
-  for (i = 0; i < gsp_class->num_audio_out; i++)
-    desc->connect_port (ladspa->handle, oclass->audio_out_portnums[i],
-        gsp->audio_out[i]);
+  /* walk through all the plugins in this plugin library */
+  for (i = 0; (desc = descriptor_function (i)); i++) {
+    guint audio_in, audio_out, control_in, control_out;
 
-  desc->run (ladspa->handle, nframes);
-}
+    /* count ports of this plugin */
+    ladspa_count_ports (desc, &audio_in, &audio_out, &control_in, &control_out);
 
-static void
-ladspa_describe_plugin (LADSPA_Descriptor_Function descriptor_function)
-{
-  const LADSPA_Descriptor *desc;
-  gint i;
-
-  /* walk through all the plugins in this pluginlibrary */
-  i = 0;
-  while ((desc = descriptor_function (i++))) {
-    gchar *type_name;
-    GTypeInfo typeinfo = {
-      sizeof (GstLADSPAClass),
-      (GBaseInitFunc) gst_ladspa_base_init,
-      NULL,
-      (GClassInitFunc) gst_ladspa_class_init,
-      NULL,
-      desc,
-      sizeof (GstLADSPA),
-      0,
-      (GInstanceInitFunc) gst_ladspa_init,
-    };
-    GType type;
-    /* construct the type */
-    type_name = g_strdup_printf ("ladspa-%s", desc->Label);
-    g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
-    /* if it's already registered, drop it */
-    if (g_type_from_name (type_name))
-      goto next;
-
-    /* create the type now */
-    type =
-        g_type_register_static (GST_TYPE_SIGNAL_PROCESSOR, type_name, &typeinfo,
-        0);
-    /* FIXME: not needed anymore when we can add pad templates, etc in class_init
-     * as class_data contains the LADSPA_Descriptor too */
-    g_type_set_qdata (type, descriptor_quark, (gpointer) desc);
-
-    if (!gst_element_register (ladspa_plugin, type_name, GST_RANK_NONE, type))
-      goto next;
-
-  next:
-    g_free (type_name);
+    /* categorize and register it */
+    if (audio_in == 0)
+      ladspa_describe_source_plugin (plugin, filename, desc);
+    else if (audio_out == 0)
+      ladspa_describe_sink_plugin (plugin, filename, desc);
+    else
+      ladspa_describe_filter_plugin (plugin, filename, desc);
   }
 }
 
@@ -742,13 +206,11 @@ ladspa_rdf_directory_search (const char *dir_name)
 
   return TRUE;
 }
-
 #endif
 
-/* search just the one directory.
- */
+/* search just the one directory */
 static gboolean
-ladspa_plugin_directory_search (const char *dir_name)
+ladspa_plugin_directory_search (GstPlugin * ladspa_plugin, const char *dir_name)
 {
   GDir *dir;
   gchar *file_name;
@@ -773,7 +235,7 @@ ladspa_plugin_directory_search (const char *dir_name)
               (gpointer *) & descriptor_function)) {
         /* we've found a ladspa_descriptor function, now introspect it. */
         GST_INFO ("describe %s", file_name);
-        ladspa_describe_plugin (descriptor_function);
+        ladspa_describe_plugin (ladspa_plugin, entry_name, descriptor_function);
         ok = TRUE;
       } else {
         /* it was a library, but not a LADSPA one. Unload it. */
@@ -787,10 +249,9 @@ ladspa_plugin_directory_search (const char *dir_name)
   return ok;
 }
 
-/* search the plugin path
- */
+/* search the plugin path */
 static gboolean
-ladspa_plugin_path_search (void)
+ladspa_plugin_path_search (GstPlugin * plugin)
 {
   const gchar *search_path;
   gchar *ladspa_path;
@@ -825,7 +286,8 @@ ladspa_plugin_path_search (void)
     }
     if (skip)
       break;
-    /* transform path: /usr/lib/ladspa -> /usr/share/ladspa/rdf/
+    /* 
+     * transform path: /usr/lib/ladspa -> /usr/share/ladspa/rdf/
      * yes, this is ugly, but lrdf has not searchpath
      */
     if ((pos = strstr (paths[i], "/lib/ladspa"))) {
@@ -848,7 +310,7 @@ ladspa_plugin_path_search (void)
     }
     if (skip)
       break;
-    res |= ladspa_plugin_directory_search (paths[i]);
+    res |= ladspa_plugin_directory_search (plugin, paths[i]);
   }
   g_strfreev (paths);
 
@@ -860,8 +322,16 @@ ladspa_plugin_path_search (void)
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  GST_DEBUG_CATEGORY_INIT (ladspa_debug, "ladspa",
-      GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LADSPA");
+#ifdef ENABLE_NLS
+  GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+      LOCALEDIR);
+  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+  GST_DEBUG_CATEGORY_INIT (ladspa_debug, "ladspa", 0, "LADSPA plugins");
+
+  descriptor_quark = g_quark_from_static_string ("ladspa-descriptor");
 
   gst_plugin_add_dependency_simple (plugin,
       "LADSPA_PATH",
@@ -871,14 +341,8 @@ plugin_init (GstPlugin * plugin)
   lrdf_init ();
 #endif
 
-  parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR);
-
-  ladspa_plugin = plugin;
-  descriptor_quark = g_quark_from_static_string ("ladspa-descriptor");
-
-  if (!ladspa_plugin_path_search ()) {
-    GST_WARNING ("no ladspa plugins found, check LADSPA_PATH");
-  }
+  if (!ladspa_plugin_path_search (plugin))
+    GST_WARNING ("no LADSPA plugins found, check LADSPA_PATH");
 
   /* we don't want to fail, even if there are no elements registered */
   return TRUE;
@@ -887,5 +351,5 @@ plugin_init (GstPlugin * plugin)
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
     ladspa,
-    "All LADSPA plugins",
+    "LADSPA plugin",
     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
index fe78d17..727b56c 100644 (file)
@@ -1,5 +1,6 @@
 /* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
  *
  * gstladspa.h: Header for LADSPA plugin
  *
  * Boston, MA 02110-1301, USA.
  */
 
-
 #ifndef __GST_LADSPA_H__
 #define __GST_LADSPA_H__
 
-
-#include <ladspa.h>
-
 #include <gst/gst.h>
 
-#include <gst/signalprocessor/gstsignalprocessor.h>
-
-
 G_BEGIN_DECLS
 
-
-typedef struct _ladspa_control_info {
-  gchar *name;
-  gchar *param_name;
-  gfloat lowerbound, upperbound;
-  gfloat def;
-  gboolean lower, upper, samplerate;
-  gboolean toggled, logarithmic, integer, writable;
-} ladspa_control_info;
-
-
-typedef struct _GstLADSPA GstLADSPA;
-typedef struct _GstLADSPAClass GstLADSPAClass;
-
-
-struct _GstLADSPA {
-  GstSignalProcessor parent;
-
-  LADSPA_Descriptor *descriptor;
-  LADSPA_Handle *handle;
-
-  gboolean activated;
-  gboolean inplace_broken;
-};
-
-struct _GstLADSPAClass {
-  GstSignalProcessorClass parent_class;
-
-  LADSPA_Descriptor *descriptor;
-
-  gint *audio_in_portnums;
-  gint *audio_out_portnums;
-  gint *control_in_portnums;
-  gint *control_out_portnums;
-};
-
+extern GQuark descriptor_quark;
 
 G_END_DECLS
 
-
 #endif /* __GST_LADSPA_H__ */
diff --git a/ext/ladspa/gstladspafilter.c b/ext/ladspa/gstladspafilter.c
new file mode 100644 (file)
index 0000000..2411c06
--- /dev/null
@@ -0,0 +1,374 @@
+/* GStreamer LADSPA filter category
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
+ *               2003 Andy Wingo <wingo at pobox.com>
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstladspafilter.h"
+#include "gstladspa.h"
+#include "gstladspautils.h"
+
+GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
+#define GST_CAT_DEFAULT ladspa_debug
+
+#define GST_LADSPA_FILTER_CLASS_TAGS "Filter/Effect/Audio/LADSPA"
+
+static GstLADSPAFilterClass *gst_ladspa_filter_type_parent_class = NULL;
+
+/*
+ * Assumes only same format (base of AudioFilter), not same channels.
+ */
+void
+gst_my_audio_filter_class_add_pad_templates (GstAudioFilterClass * audio_class,
+    GstCaps * srccaps, GstCaps * sinkcaps)
+{
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (audio_class);
+  GstPadTemplate *pad_template;
+
+  g_return_if_fail (GST_IS_CAPS (srccaps) && GST_IS_CAPS (sinkcaps));
+
+  pad_template =
+      gst_pad_template_new (GST_BASE_TRANSFORM_SRC_NAME, GST_PAD_SRC,
+      GST_PAD_ALWAYS, srccaps);
+  gst_element_class_add_pad_template (elem_class, pad_template);
+
+  pad_template =
+      gst_pad_template_new (GST_BASE_TRANSFORM_SINK_NAME, GST_PAD_SINK,
+      GST_PAD_ALWAYS, sinkcaps);
+  gst_element_class_add_pad_template (elem_class, pad_template);
+}
+
+static GstCaps *
+gst_ladspa_filter_type_fixate_caps (GstBaseTransform * base,
+    GstPadDirection direction, GstCaps * caps, GstCaps * othercaps)
+{
+  GstStructure *structure;
+  gint rate;
+
+  structure = gst_caps_get_structure (caps, 0);
+  if (G_UNLIKELY (!gst_structure_get_int (structure, "rate", &rate)))
+    return othercaps;
+
+  othercaps = gst_caps_truncate (othercaps);
+  othercaps = gst_caps_make_writable (othercaps);
+  structure = gst_caps_get_structure (othercaps, 0);
+
+  gst_structure_fixate_field_nearest_int (structure, "rate", rate);
+
+  return othercaps;
+}
+
+static GstCaps *
+gst_ladspa_filter_type_transform_caps (GstBaseTransform * base,
+    GstPadDirection direction, GstCaps * caps, GstCaps * filter)
+{
+  GstCaps *srccaps, *sinkcaps;
+  GstCaps *ret = NULL;
+
+  srccaps = gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SRC_PAD (base));
+  sinkcaps = gst_pad_get_pad_template_caps (GST_BASE_TRANSFORM_SINK_PAD (base));
+
+  switch (direction) {
+    case GST_PAD_SINK:
+      if (gst_caps_can_intersect (caps, sinkcaps))
+        ret = gst_caps_copy (srccaps);
+      else
+        ret = gst_caps_new_empty ();
+      break;
+    case GST_PAD_SRC:
+      if (gst_caps_can_intersect (caps, srccaps))
+        ret = gst_caps_copy (sinkcaps);
+      else
+        ret = gst_caps_new_empty ();
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  GST_DEBUG_OBJECT (ladspa_debug, "transformed %" GST_PTR_FORMAT, ret);
+
+  if (filter) {
+    GstCaps *intersection;
+
+    GST_DEBUG_OBJECT (ladspa_debug, "Using filter caps %" GST_PTR_FORMAT,
+        filter);
+
+    intersection =
+        gst_caps_intersect_full (filter, ret, GST_CAPS_INTERSECT_FIRST);
+    gst_caps_unref (ret);
+    ret = intersection;
+
+    GST_DEBUG_OBJECT (ladspa_debug, "Intersection %" GST_PTR_FORMAT, ret);
+  }
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_ladspa_filter_type_prepare_output_buffer (GstBaseTransform * base,
+    GstBuffer * inbuf, GstBuffer ** outbuf)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
+  GstLADSPAFilterClass *ladspa_class = GST_LADSPA_FILTER_GET_CLASS (ladspa);
+  guint samples;
+
+  samples =
+      gst_buffer_get_size (inbuf) / sizeof (LADSPA_Data) /
+      ladspa_class->ladspa.count.audio.in;
+
+  if (!gst_base_transform_is_in_place (base)) {
+    *outbuf =
+        gst_buffer_new_allocate (NULL,
+        samples * sizeof (LADSPA_Data) * ladspa_class->ladspa.count.audio.out,
+        NULL);
+    *outbuf = gst_buffer_make_writable (*outbuf);
+    return GST_FLOW_OK;
+  } else {
+    return
+        GST_BASE_TRANSFORM_CLASS
+        (gst_ladspa_filter_type_parent_class)->prepare_output_buffer (base,
+        inbuf, outbuf);
+  }
+}
+
+static gboolean
+gst_ladspa_filter_type_setup (GstAudioFilter * audio,
+    const GstAudioInfo * info)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (audio);
+
+  return gst_ladspa_setup (&ladspa->ladspa, GST_AUDIO_INFO_RATE (info));
+}
+
+static gboolean
+gst_ladspa_filter_type_cleanup (GstBaseTransform * base)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
+
+  return gst_ladspa_cleanup (&ladspa->ladspa);
+}
+
+static GstFlowReturn
+gst_ladspa_filter_type_transform_ip (GstBaseTransform * base,
+    GstBuffer * buf)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
+  GstMapInfo map;
+  guint samples;
+
+  gst_buffer_map (buf, &map, GST_MAP_READWRITE);
+  samples =
+      map.size / sizeof (LADSPA_Data) / ladspa->ladspa.klass->count.audio.in;
+  gst_ladspa_transform (&ladspa->ladspa, map.data, samples, map.data);
+  gst_buffer_unmap (buf, &map);
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_ladspa_filter_type_transform (GstBaseTransform * base,
+    GstBuffer * inbuf, GstBuffer * outbuf)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (base);
+  GstMapInfo inmap, outmap;
+  guint samples;
+
+  gst_buffer_map (inbuf, &inmap, GST_MAP_READ);
+  gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE);
+  samples =
+      inmap.size / sizeof (LADSPA_Data) / ladspa->ladspa.klass->count.audio.in;
+  gst_ladspa_transform (&ladspa->ladspa, outmap.data, samples, inmap.data);
+  gst_buffer_unmap (outbuf, &outmap);
+  gst_buffer_unmap (inbuf, &inmap);
+
+  return GST_FLOW_OK;
+}
+
+static void
+gst_ladspa_filter_type_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (object);
+
+  gst_ladspa_object_set_property (&ladspa->ladspa, object, prop_id, value,
+      pspec);
+}
+
+static void
+gst_ladspa_filter_type_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (object);
+
+  gst_ladspa_object_get_property (&ladspa->ladspa, object, prop_id, value,
+      pspec);
+}
+
+static void
+gst_ladspa_filter_type_init (GstLADSPAFilter * ladspa,
+    LADSPA_Descriptor * desc)
+{
+  GstBaseTransform *base = GST_BASE_TRANSFORM (ladspa);
+  GstLADSPAFilterClass *ladspa_class = GST_LADSPA_FILTER_GET_CLASS (ladspa);
+
+  gst_ladspa_init (&ladspa->ladspa, &ladspa_class->ladspa);
+
+  /* even if channels are different LADSPA still maintains same samples */
+  gst_base_transform_set_in_place (base,
+      ladspa_class->ladspa.count.audio.in ==
+      ladspa_class->ladspa.count.audio.out
+      && !LADSPA_IS_INPLACE_BROKEN (ladspa_class->ladspa.descriptor->
+          Properties));
+
+}
+
+static void
+gst_ladspa_filter_type_dispose (GObject * object)
+{
+  GstBaseTransform *base = GST_BASE_TRANSFORM (object);
+
+  gst_ladspa_filter_type_cleanup (base);
+
+  G_OBJECT_CLASS (gst_ladspa_filter_type_parent_class)->dispose (object);
+}
+
+static void
+gst_ladspa_filter_type_finalize (GObject * object)
+{
+  GstLADSPAFilter *ladspa = GST_LADSPA_FILTER (object);
+
+  gst_ladspa_finalize (&ladspa->ladspa);
+
+  G_OBJECT_CLASS (gst_ladspa_filter_type_parent_class)->finalize (object);
+}
+
+/*
+ * It is okay for plugins to 'leak' a one-time allocation. This will be freed when
+ * the application exits. When the plugins are scanned for the first time, this is
+ * done from a separate process to not impose the memory overhead on the calling
+ * application (among other reasons). Hence no need for class_finalize.
+ */
+static void
+gst_ladspa_filter_type_base_init (GstLADSPAFilterClass * ladspa_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
+  GstAudioFilterClass *audio_class = GST_AUDIO_FILTER_CLASS (ladspa_class);
+  LADSPA_Descriptor *desc;
+
+  desc =
+      g_type_get_qdata (G_OBJECT_CLASS_TYPE (object_class), descriptor_quark);
+  g_assert (desc);
+
+  gst_ladspa_class_init (&ladspa_class->ladspa, desc);
+
+  gst_ladspa_element_class_set_metadata (&ladspa_class->ladspa, elem_class,
+      GST_LADSPA_FILTER_CLASS_TAGS);
+  gst_ladspa_filter_type_class_add_pad_templates (&ladspa_class->ladspa,
+      audio_class);
+}
+
+static void
+gst_ladspa_filter_type_base_finalize (GstLADSPAFilterClass * ladspa_class)
+{
+  gst_ladspa_class_finalize (&ladspa_class->ladspa);
+}
+
+static void
+gst_ladspa_filter_type_class_init (GstLADSPAFilterClass * ladspa_class,
+    LADSPA_Descriptor * desc)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
+  GstBaseTransformClass *base_class = GST_BASE_TRANSFORM_CLASS (ladspa_class);
+  GstAudioFilterClass *audio_class = GST_AUDIO_FILTER_CLASS (ladspa_class);
+
+  GST_DEBUG ("LADSPA filter class %p", ladspa_class);
+
+  gst_ladspa_filter_type_parent_class =
+      g_type_class_peek_parent (ladspa_class);
+
+  object_class->dispose =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_dispose);
+  object_class->finalize =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_finalize);
+  object_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_set_property);
+  object_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_get_property);
+
+  base_class->fixate_caps =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_fixate_caps);
+  base_class->transform_caps =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_transform_caps);
+  base_class->prepare_output_buffer =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_prepare_output_buffer);
+  base_class->transform =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_transform);
+  base_class->transform_ip =
+      GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_transform_ip);
+
+  audio_class->setup = GST_DEBUG_FUNCPTR (gst_ladspa_filter_type_setup);
+
+  gst_ladspa_object_class_install_properties (&ladspa_class->ladspa,
+      object_class, 1);
+}
+
+G_DEFINE_ABSTRACT_TYPE (GstLADSPAFilter, gst_ladspa_filter,
+    GST_TYPE_AUDIO_FILTER);
+
+static void
+gst_ladspa_filter_init (GstLADSPAFilter * ladspa)
+{
+}
+
+static void
+gst_ladspa_filter_class_init (GstLADSPAFilterClass * ladspa_class)
+{
+}
+
+/* 
+ * Construct the type.
+ */
+void
+ladspa_describe_filter_plugin (GstPlugin * plugin,
+    const gchar * filename, const LADSPA_Descriptor * desc)
+{
+  GTypeInfo info = {
+    sizeof (GstLADSPAFilterClass),
+    (GBaseInitFunc) gst_ladspa_filter_type_base_init,
+    (GBaseFinalizeFunc) gst_ladspa_filter_type_base_finalize,
+    (GClassInitFunc) gst_ladspa_filter_type_class_init,
+    NULL,
+    desc,
+    sizeof (GstLADSPAFilter),
+    0,
+    (GInstanceInitFunc) gst_ladspa_filter_type_init,
+    NULL
+  };
+  gchar *tmp;
+
+  tmp = g_strdup_printf ("ladspa-%s-%s", filename, desc->Label);
+  ladspa_register_plugin (plugin, GST_TYPE_LADSPA_FILTER, tmp, &info,
+      descriptor_quark, filename, desc);
+  g_free (tmp);
+}
diff --git a/ext/ladspa/gstladspafilter.h b/ext/ladspa/gstladspafilter.h
new file mode 100644 (file)
index 0000000..94c0dda
--- /dev/null
@@ -0,0 +1,72 @@
+/* GStreamer 
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * gstladspafilter.h: Header for LADSPA filter 
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_LADSPA_FILTER_H__
+#define __GST_LADSPA_FILTER_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiofilter.h>
+#include "gstladspautils.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_LADSPA_FILTER              (gst_ladspa_filter_get_type())
+#define GST_LADSPA_FILTER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA_FILTER,GstLADSPAFilter))
+#define GST_LADSPA_FILTER_CAST(obj)         ((GstLADSPAFilter *) (obj))
+#define GST_LADSPA_FILTER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA_FILTER,GstLADSPAFilterClass))
+#define GST_LADSPA_FILTER_CLASS_CAST(klass) ((GstLADSPAFilterClass *) (klass))
+#define GST_LADSPA_FILTER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_LADSPA_FILTER,GstLADSPAFilterClass))
+#define GST_IS_LADSPA_FILTER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA_FILTER))
+#define GST_IS_LADSPA_FILTER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA_FILTER))
+
+typedef struct _GstLADSPAFilter GstLADSPAFilter;
+
+typedef struct _GstLADSPAFilterClass GstLADSPAFilterClass;
+
+struct _GstLADSPAFilter
+{
+  GstAudioFilter parent;
+
+  GstLADSPA ladspa;
+};
+
+struct _GstLADSPAFilterClass
+{
+  GstAudioFilterClass parent_class;
+
+  GstLADSPAClass ladspa;
+};
+
+GType
+gst_ladspa_filter_get_type (void);
+
+void
+ladspa_describe_filter_plugin (GstPlugin * plugin,
+    const gchar * filename, const LADSPA_Descriptor * desc);
+
+void
+gst_my_audio_filter_class_add_pad_templates (GstAudioFilterClass * audio_class,
+    GstCaps * srccaps, GstCaps * sinkcaps);
+
+G_END_DECLS
+
+#endif /* __GST_LADSPA_FILTER_H__ */
diff --git a/ext/ladspa/gstladspasink.c b/ext/ladspa/gstladspasink.c
new file mode 100644 (file)
index 0000000..db91b01
--- /dev/null
@@ -0,0 +1,386 @@
+/* GStreamer LADSPA sink category
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
+ *               2003 Andy Wingo <wingo at pobox.com>
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com> (fakesink)
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstladspasink.h"
+#include "gstladspa.h"
+#include "gstladspautils.h"
+#include <gst/base/gstbasetransform.h>
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
+#define GST_CAT_DEFAULT ladspa_debug
+
+#define GST_LADSPA_SINK_CLASS_TAGS                   "Sink/Audio/LADSPA"
+#define GST_LADSPA_SINK_DEFAULT_SYNC                 TRUE
+#define GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PUSH    TRUE
+#define GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PULL    FALSE
+#define GST_LADSPA_SINK_DEFAULT_NUM_BUFFERS          -1
+
+enum
+{
+  GST_LADSPA_SINK_PROP_0,
+  GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH,
+  GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL,
+  GST_LADSPA_SINK_PROP_NUM_BUFFERS,
+  GST_LADSPA_SINK_PROP_LAST
+};
+
+static GstLADSPASinkClass *gst_ladspa_sink_type_parent_class = NULL;
+
+/*
+ * Boilerplates BaseSink add pad.
+ */
+void
+gst_my_base_sink_class_add_pad_template (GstBaseSinkClass * base_class,
+    GstCaps * sinkcaps)
+{
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (base_class);
+  GstPadTemplate *pad_template;
+
+  g_return_if_fail (GST_IS_CAPS (sinkcaps));
+
+  pad_template =
+      gst_pad_template_new (GST_BASE_TRANSFORM_SINK_NAME, GST_PAD_SINK,
+      GST_PAD_ALWAYS, sinkcaps);
+  gst_element_class_add_pad_template (elem_class, pad_template);
+}
+
+static gboolean
+gst_ladspa_sink_type_set_caps (GstBaseSink * base, GstCaps * caps)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (base);
+  GstAudioInfo info;
+
+  if (!gst_audio_info_from_caps (&info, caps)) {
+    GST_ERROR_OBJECT (base, "received invalid caps");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (ladspa, "negotiated to caps %" GST_PTR_FORMAT, caps);
+
+  ladspa->info = info;
+
+  return gst_ladspa_setup (&ladspa->ladspa, GST_AUDIO_INFO_RATE (&info));
+}
+
+static gboolean
+gst_ladspa_sink_type_query (GstBaseSink * base, GstQuery * query)
+{
+  gboolean ret;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_SEEKING:{
+      GstFormat fmt;
+
+      /* we don't supporting seeking */
+      gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
+      gst_query_set_seeking (query, fmt, FALSE, 0, -1);
+      ret = TRUE;
+      break;
+    }
+    default:
+      ret =
+          GST_BASE_SINK_CLASS (gst_ladspa_sink_type_parent_class)->query
+          (base, query);
+      break;
+  }
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_ladspa_sink_type_preroll (GstBaseSink * base, GstBuffer * buffer)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (base);
+
+  if (ladspa->num_buffers_left == 0) {
+    GST_DEBUG_OBJECT (ladspa, "we are EOS");
+    return GST_FLOW_EOS;
+  }
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_ladspa_sink_type_render (GstBaseSink * base, GstBuffer * buf)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (base);
+  GstMapInfo info;
+
+  if (ladspa->num_buffers_left == 0)
+    goto eos;
+
+  if (ladspa->num_buffers_left != -1)
+    ladspa->num_buffers_left--;
+
+  gst_buffer_map (buf, &info, GST_MAP_READ);
+  gst_ladspa_transform (&ladspa->ladspa, NULL,
+      info.size / sizeof (LADSPA_Data) / ladspa->ladspa.klass->count.audio.in,
+      info.data);
+  gst_buffer_unmap (buf, &info);
+
+  if (ladspa->num_buffers_left == 0)
+    goto eos;
+
+  return GST_FLOW_OK;
+
+  /* ERRORS */
+eos:
+  {
+    GST_DEBUG_OBJECT (ladspa, "we are EOS");
+    return GST_FLOW_EOS;
+  }
+}
+
+static GstStateChangeReturn
+gst_ladspa_sink_type_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      ladspa->num_buffers_left = ladspa->num_buffers;
+      break;
+    default:
+      break;
+  }
+
+  ret =
+      GST_ELEMENT_CLASS (gst_ladspa_sink_type_parent_class)->change_state
+      (element, transition);
+
+  return ret;
+}
+
+
+static void
+gst_ladspa_sink_type_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
+
+  switch (prop_id) {
+    case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH:
+      GST_BASE_SINK (ladspa)->can_activate_push = g_value_get_boolean (value);
+      break;
+    case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL:
+      GST_BASE_SINK (ladspa)->can_activate_pull = g_value_get_boolean (value);
+      break;
+    case GST_LADSPA_SINK_PROP_NUM_BUFFERS:
+      ladspa->num_buffers = g_value_get_int (value);
+      break;
+    default:
+      gst_ladspa_object_set_property (&ladspa->ladspa, object, prop_id, value,
+          pspec);
+      break;
+  }
+}
+
+static void
+gst_ladspa_sink_type_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
+
+  switch (prop_id) {
+    case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH:
+      g_value_set_boolean (value, GST_BASE_SINK (ladspa)->can_activate_push);
+      break;
+    case GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL:
+      g_value_set_boolean (value, GST_BASE_SINK (ladspa)->can_activate_pull);
+      break;
+    case GST_LADSPA_SINK_PROP_NUM_BUFFERS:
+      g_value_set_int (value, ladspa->num_buffers);
+      break;
+    default:
+      gst_ladspa_object_get_property (&ladspa->ladspa, object, prop_id, value,
+          pspec);
+      break;
+  }
+}
+
+static void
+gst_ladspa_sink_type_init (GstLADSPASink * ladspa, LADSPA_Descriptor * desc)
+{
+  GstLADSPASinkClass *ladspa_class = GST_LADSPA_SINK_GET_CLASS (ladspa);
+  GstBaseSink *base = GST_BASE_SINK (ladspa);
+
+  gst_ladspa_init (&ladspa->ladspa, &ladspa_class->ladspa);
+
+  ladspa->num_buffers = GST_LADSPA_SINK_DEFAULT_NUM_BUFFERS;
+
+  gst_base_sink_set_sync (base, GST_LADSPA_SINK_DEFAULT_SYNC);
+}
+
+static void
+gst_ladspa_sink_type_dispose (GObject * object)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
+
+  gst_ladspa_cleanup (&ladspa->ladspa);
+
+  G_OBJECT_CLASS (gst_ladspa_sink_type_parent_class)->dispose (object);
+}
+
+static void
+gst_ladspa_sink_type_finalize (GObject * object)
+{
+  GstLADSPASink *ladspa = GST_LADSPA_SINK (object);
+
+  gst_ladspa_finalize (&ladspa->ladspa);
+
+  G_OBJECT_CLASS (gst_ladspa_sink_type_parent_class)->finalize (object);
+}
+
+/*
+ * It is okay for plugins to 'leak' a one-time allocation. This will be freed when
+ * the application exits. When the plugins are scanned for the first time, this is
+ * done from a separate process to not impose the memory overhead on the calling
+ * application (among other reasons). Hence no need for class_finalize.
+ */
+static void
+gst_ladspa_sink_type_base_init (GstLADSPASinkClass * ladspa_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
+  GstBaseSinkClass *base_class = GST_BASE_SINK_CLASS (ladspa_class);
+  LADSPA_Descriptor *desc;
+
+  desc =
+      g_type_get_qdata (G_OBJECT_CLASS_TYPE (object_class), descriptor_quark);
+  g_assert (desc);
+
+  gst_ladspa_class_init (&ladspa_class->ladspa, desc);
+
+  gst_ladspa_element_class_set_metadata (&ladspa_class->ladspa, elem_class,
+      GST_LADSPA_SINK_CLASS_TAGS);
+
+  gst_ladspa_sink_type_class_add_pad_template (&ladspa_class->ladspa,
+      base_class);
+}
+
+
+static void
+gst_ladspa_sink_type_base_finalize (GstLADSPASinkClass * ladspa_class)
+{
+  gst_ladspa_class_finalize (&ladspa_class->ladspa);
+}
+
+static void
+gst_ladspa_sink_type_class_init (GstLADSPASinkClass * ladspa_class,
+    LADSPA_Descriptor * desc)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
+  GstBaseSinkClass *base_class = base_class =
+      GST_BASE_SINK_CLASS (ladspa_class);
+
+  gst_ladspa_sink_type_parent_class =
+      g_type_class_peek_parent (ladspa_class);
+
+  object_class->dispose = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_dispose);
+  object_class->finalize =
+      GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_finalize);
+  object_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_set_property);
+  object_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_get_property);
+
+  elem_class->change_state =
+      GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_change_state);
+
+  base_class->set_caps = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_set_caps);
+  base_class->preroll = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_preroll);
+  base_class->render = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_render);
+  base_class->query = GST_DEBUG_FUNCPTR (gst_ladspa_sink_type_query);
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PUSH,
+      g_param_spec_boolean ("can-activate-push", "Can activate push",
+          "Can activate in push mode",
+          GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PUSH,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SINK_PROP_CAN_ACTIVATE_PULL,
+      g_param_spec_boolean ("can-activate-pull", "Can activate pull",
+          "Can activate in pull mode",
+          GST_LADSPA_SINK_DEFAULT_CAN_ACTIVATE_PULL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SINK_PROP_NUM_BUFFERS, g_param_spec_int ("num-buffers",
+          "num-buffers", "Number of buffers to accept going EOS", -1, G_MAXINT,
+          GST_LADSPA_SINK_DEFAULT_NUM_BUFFERS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gst_ladspa_object_class_install_properties (&ladspa_class->ladspa,
+      object_class, GST_LADSPA_SINK_PROP_LAST);
+
+}
+
+G_DEFINE_ABSTRACT_TYPE (GstLADSPASink, gst_ladspa_sink, GST_TYPE_BASE_SINK);
+
+static void
+gst_ladspa_sink_init (GstLADSPASink * ladspa)
+{
+}
+
+static void
+gst_ladspa_sink_class_init (GstLADSPASinkClass * ladspa_class)
+{
+}
+
+/*
+ * Construct the type.
+ */
+void
+ladspa_describe_sink_plugin (GstPlugin * plugin,
+    const gchar * filename, const LADSPA_Descriptor * desc)
+{
+  GTypeInfo info = {
+    sizeof (GstLADSPASinkClass),
+    (GBaseInitFunc) gst_ladspa_sink_type_base_init,
+    (GBaseFinalizeFunc) gst_ladspa_sink_type_base_finalize,
+    (GClassInitFunc) gst_ladspa_sink_type_class_init,
+    NULL,
+    desc,
+    sizeof (GstLADSPASink),
+    0,
+    (GInstanceInitFunc) gst_ladspa_sink_type_init,
+    NULL
+  };
+  gchar *tmp;
+
+  tmp = g_strdup_printf ("ladspasink-%s-%s", filename, desc->Label);
+  ladspa_register_plugin (plugin, GST_TYPE_LADSPA_SINK, tmp, &info,
+      descriptor_quark, filename, desc);
+  g_free (tmp);
+}
diff --git a/ext/ladspa/gstladspasink.h b/ext/ladspa/gstladspasink.h
new file mode 100644 (file)
index 0000000..6bed7f0
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ *                    2000 Wim Taymans <wtay@chello.be>
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * gstladspasink.h: Header for LADSPA sink
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_LADSPA_SINK_H__
+#define __GST_LADSPA_SINK_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+#include "gstladspautils.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_LADSPA_SINK              (gst_ladspa_sink_get_type())
+#define GST_LADSPA_SINK(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA_SINK,GstLADSPASink))
+#define GST_LADSPA_SINK_CAST(obj)         ((GstLADSPASink *) (obj))
+#define GST_LADSPA_SINK_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA_SINK,GstLADSPASinkClass))
+#define GST_LADSPA_SINK_CLASS_CAST(klass) ((GstLADSPASinkClass *) (klass))
+#define GST_LADSPA_SINK_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_LADSPA_SINK,GstLADSPASinkClass))
+#define GST_IS_LADSPA_SINK(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA_SINK))
+#define GST_IS_LADSPA_SINK_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA_SINK))
+
+typedef struct _GstLADSPASink GstLADSPASink;
+
+typedef struct _GstLADSPASinkClass GstLADSPASinkClass;
+
+struct _GstLADSPASink
+{
+  GstBaseSink parent;
+
+  GstLADSPA ladspa;
+
+  GstAudioInfo info;
+
+  gint num_buffers;
+  gint num_buffers_left;
+};
+
+struct _GstLADSPASinkClass
+{
+  GstBaseSinkClass parent_class;
+
+  GstLADSPAClass ladspa;
+};
+
+GType
+gst_ladspa_sink_get_type (void);
+
+void
+ladspa_describe_sink_plugin (GstPlugin * plugin,
+    const gchar * filename, const LADSPA_Descriptor * desc);
+
+void
+gst_my_base_sink_class_add_pad_template (GstBaseSinkClass * base_class,
+    GstCaps * sinkcaps);
+
+G_END_DECLS
+
+#endif /* __GST_LADSPA_SINK_H__ */
diff --git a/ext/ladspa/gstladspasource.c b/ext/ladspa/gstladspasource.c
new file mode 100644 (file)
index 0000000..202004c
--- /dev/null
@@ -0,0 +1,633 @@
+/* GStreamer LADSPA source category
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
+ *               2003 Andy Wingo <wingo at pobox.com>
+ * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net> (audiotestsrc)
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstladspasource.h"
+#include "gstladspa.h"
+#include "gstladspautils.h"
+#include <gst/base/gstbasetransform.h>
+
+GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
+#define GST_CAT_DEFAULT ladspa_debug
+
+#define GST_LADSPA_SOURCE_CLASS_TAGS                   "Source/Audio/LADSPA"
+#define GST_LADSPA_SOURCE_DEFAULT_SAMPLES_PER_BUFFER   1024
+#define GST_LADSPA_SOURCE_DEFAULT_IS_LIVE              FALSE
+#define GST_LADSPA_SOURCE_DEFAULT_TIMESTAMP_OFFSET     G_GINT64_CONSTANT (0)
+#define GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PUSH    TRUE
+#define GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PULL    FALSE
+
+enum
+{
+  GST_LADSPA_SOURCE_PROP_0,
+  GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER,
+  GST_LADSPA_SOURCE_PROP_IS_LIVE,
+  GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET,
+  GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH,
+  GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL,
+  GST_LADSPA_SOURCE_PROP_LAST
+};
+
+static GstLADSPASourceClass *gst_ladspa_source_type_parent_class = NULL;
+
+/*
+ * Boilerplates BaseSrc add pad.
+ */
+void
+gst_my_base_source_class_add_pad_template (GstBaseSrcClass * base_class,
+    GstCaps * srccaps)
+{
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (base_class);
+  GstPadTemplate *pad_template;
+
+  g_return_if_fail (GST_IS_CAPS (srccaps));
+
+  pad_template =
+      gst_pad_template_new (GST_BASE_TRANSFORM_SRC_NAME, GST_PAD_SRC,
+      GST_PAD_ALWAYS, srccaps);
+  gst_element_class_add_pad_template (elem_class, pad_template);
+}
+
+static GstCaps *
+gst_ladspa_source_type_fixate (GstBaseSrc * base, GstCaps * caps)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
+  GstStructure *structure;
+
+  caps = gst_caps_make_writable (caps);
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  GST_DEBUG_OBJECT (ladspa, "fixating samplerate to %d", GST_AUDIO_DEF_RATE);
+
+  gst_structure_fixate_field_nearest_int (structure, "rate",
+      GST_AUDIO_DEF_RATE);
+
+  gst_structure_fixate_field_string (structure, "format", GST_AUDIO_NE (F32));
+
+  gst_structure_fixate_field_nearest_int (structure, "channels",
+      ladspa->ladspa.klass->count.audio.out);
+
+  caps =
+      GST_BASE_SRC_CLASS (gst_ladspa_source_type_parent_class)->fixate
+      (base, caps);
+
+  return caps;
+}
+
+static gboolean
+gst_ladspa_source_type_set_caps (GstBaseSrc * base, GstCaps * caps)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
+  GstAudioInfo info;
+
+  if (!gst_audio_info_from_caps (&info, caps)) {
+    GST_ERROR_OBJECT (base, "received invalid caps");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (ladspa, "negotiated to caps %" GST_PTR_FORMAT, caps);
+
+  ladspa->info = info;
+
+  gst_base_src_set_blocksize (base,
+      GST_AUDIO_INFO_BPF (&info) * ladspa->samples_per_buffer);
+
+  return gst_ladspa_setup (&ladspa->ladspa, GST_AUDIO_INFO_RATE (&info));
+}
+
+static gboolean
+gst_ladspa_source_type_query (GstBaseSrc * base, GstQuery * query)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_CONVERT:
+    {
+      GstFormat src_fmt, dest_fmt;
+      gint64 src_val, dest_val;
+
+      gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
+
+      if (!gst_audio_info_convert (&ladspa->info, src_fmt, src_val, dest_fmt,
+              &dest_val)) {
+        GST_DEBUG_OBJECT (ladspa, "query failed");
+        return FALSE;
+      }
+
+      gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
+      res = TRUE;
+      break;
+    }
+    case GST_QUERY_SCHEDULING:
+    {
+      /* if we can operate in pull mode */
+      gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEEKABLE, 1, -1, 0);
+      gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
+      if (ladspa->can_activate_pull)
+        gst_query_add_scheduling_mode (query, GST_PAD_MODE_PULL);
+
+      res = TRUE;
+      break;
+    }
+    default:
+      res =
+          GST_BASE_SRC_CLASS (gst_ladspa_source_type_parent_class)->query
+          (base, query);
+      break;
+  }
+
+  return res;
+}
+
+static void
+gst_ladspa_source_type_get_times (GstBaseSrc * base, GstBuffer * buffer,
+    GstClockTime * start, GstClockTime * end)
+{
+  /* for live sources, sync on the timestamp of the buffer */
+  if (gst_base_src_is_live (base)) {
+    GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer);
+
+    if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+      /* get duration to calculate end time */
+      GstClockTime duration = GST_BUFFER_DURATION (buffer);
+
+      if (GST_CLOCK_TIME_IS_VALID (duration)) {
+        *end = timestamp + duration;
+      }
+      *start = timestamp;
+    }
+  } else {
+    *start = -1;
+    *end = -1;
+  }
+}
+
+/* seek to time, will be called when we operate in push mode. In pull mode we
+ * get the requested byte offset. */
+static gboolean
+gst_ladspa_source_type_do_seek (GstBaseSrc * base, GstSegment * segment)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
+  GstClockTime time;
+  gint samplerate, bpf;
+  gint64 next_sample;
+
+  GST_DEBUG_OBJECT (ladspa, "seeking %" GST_SEGMENT_FORMAT, segment);
+
+  time = segment->position;
+  ladspa->reverse = (segment->rate < 0.0);
+
+  samplerate = GST_AUDIO_INFO_RATE (&ladspa->info);
+  bpf = GST_AUDIO_INFO_BPF (&ladspa->info);
+
+  /* now move to the time indicated, don't seek to the sample *after* the time */
+  next_sample = gst_util_uint64_scale_int (time, samplerate, GST_SECOND);
+  ladspa->next_byte = next_sample * bpf;
+  if (samplerate == 0)
+    ladspa->next_time = 0;
+  else
+    ladspa->next_time =
+        gst_util_uint64_scale_round (next_sample, GST_SECOND, samplerate);
+
+  GST_DEBUG_OBJECT (ladspa, "seeking next_sample=%" G_GINT64_FORMAT
+      " next_time=%" GST_TIME_FORMAT, next_sample,
+      GST_TIME_ARGS (ladspa->next_time));
+
+  g_assert (ladspa->next_time <= time);
+
+  ladspa->next_sample = next_sample;
+
+  if (!ladspa->reverse) {
+    if (GST_CLOCK_TIME_IS_VALID (segment->start)) {
+      segment->time = segment->start;
+    }
+  } else {
+    if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
+      segment->time = segment->stop;
+    }
+  }
+
+  if (GST_CLOCK_TIME_IS_VALID (segment->stop)) {
+    time = segment->stop;
+    ladspa->sample_stop =
+        gst_util_uint64_scale_round (time, samplerate, GST_SECOND);
+    ladspa->check_seek_stop = TRUE;
+  } else {
+    ladspa->check_seek_stop = FALSE;
+  }
+  ladspa->eos_reached = FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+gst_ladspa_source_type_is_seekable (GstBaseSrc * base)
+{
+  /* we're seekable... */
+  return TRUE;
+}
+
+static GstFlowReturn
+gst_ladspa_source_type_fill (GstBaseSrc * base, guint64 offset,
+    guint length, GstBuffer * buffer)
+{
+  GstLADSPASource *ladspa;
+  GstClockTime next_time;
+  gint64 next_sample, next_byte;
+  gint bytes, samples;
+  GstElementClass *eclass;
+  GstMapInfo map;
+  gint samplerate, bpf;
+
+  ladspa = GST_LADSPA_SOURCE (base);
+
+  /* example for tagging generated data */
+  if (!ladspa->tags_pushed) {
+    GstTagList *taglist;
+
+    taglist = gst_tag_list_new (GST_TAG_DESCRIPTION, "ladspa wave", NULL);
+
+    eclass = GST_ELEMENT_CLASS (gst_ladspa_source_type_parent_class);
+    if (eclass->send_event)
+      eclass->send_event (GST_ELEMENT (base), gst_event_new_tag (taglist));
+    else
+      gst_tag_list_unref (taglist);
+    ladspa->tags_pushed = TRUE;
+  }
+
+  if (ladspa->eos_reached) {
+    GST_INFO_OBJECT (ladspa, "eos");
+    return GST_FLOW_EOS;
+  }
+
+  samplerate = GST_AUDIO_INFO_RATE (&ladspa->info);
+  bpf = GST_AUDIO_INFO_BPF (&ladspa->info);
+
+  /* if no length was given, use our default length in samples otherwise convert
+   * the length in bytes to samples. */
+  if (length == -1)
+    samples = ladspa->samples_per_buffer;
+  else
+    samples = length / bpf;
+
+  /* if no offset was given, use our next logical byte */
+  if (offset == -1)
+    offset = ladspa->next_byte;
+
+  /* now see if we are at the byteoffset we think we are */
+  if (offset != ladspa->next_byte) {
+    GST_DEBUG_OBJECT (ladspa, "seek to new offset %" G_GUINT64_FORMAT, offset);
+    /* we have a discont in the expected sample offset, do a 'seek' */
+    ladspa->next_sample = offset / bpf;
+    ladspa->next_time =
+        gst_util_uint64_scale_int (ladspa->next_sample, GST_SECOND, samplerate);
+    ladspa->next_byte = offset;
+  }
+
+  /* check for eos */
+  if (ladspa->check_seek_stop &&
+      (ladspa->sample_stop > ladspa->next_sample) &&
+      (ladspa->sample_stop < ladspa->next_sample + samples)
+      ) {
+    /* calculate only partial buffer */
+    ladspa->generate_samples_per_buffer =
+        ladspa->sample_stop - ladspa->next_sample;
+    next_sample = ladspa->sample_stop;
+    ladspa->eos_reached = TRUE;
+  } else {
+    /* calculate full buffer */
+    ladspa->generate_samples_per_buffer = samples;
+    next_sample =
+        ladspa->next_sample + (ladspa->reverse ? (-samples) : samples);
+  }
+
+  bytes = ladspa->generate_samples_per_buffer * bpf;
+
+  next_byte = ladspa->next_byte + (ladspa->reverse ? (-bytes) : bytes);
+  next_time = gst_util_uint64_scale_int (next_sample, GST_SECOND, samplerate);
+
+  GST_LOG_OBJECT (ladspa, "samplerate %d", samplerate);
+  GST_LOG_OBJECT (ladspa,
+      "next_sample %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT, next_sample,
+      GST_TIME_ARGS (next_time));
+
+  gst_buffer_set_size (buffer, bytes);
+
+  GST_BUFFER_OFFSET (buffer) = ladspa->next_sample;
+  GST_BUFFER_OFFSET_END (buffer) = next_sample;
+  if (!ladspa->reverse) {
+    GST_BUFFER_TIMESTAMP (buffer) =
+        ladspa->timestamp_offset + ladspa->next_time;
+    GST_BUFFER_DURATION (buffer) = next_time - ladspa->next_time;
+  } else {
+    GST_BUFFER_TIMESTAMP (buffer) = ladspa->timestamp_offset + next_time;
+    GST_BUFFER_DURATION (buffer) = ladspa->next_time - next_time;
+  }
+
+  gst_object_sync_values (GST_OBJECT (ladspa), GST_BUFFER_TIMESTAMP (buffer));
+
+  ladspa->next_time = next_time;
+  ladspa->next_sample = next_sample;
+  ladspa->next_byte = next_byte;
+
+  GST_LOG_OBJECT (ladspa, "generating %u samples at ts %" GST_TIME_FORMAT,
+      ladspa->generate_samples_per_buffer,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+
+  gst_buffer_map (buffer, &map, GST_MAP_WRITE);
+  gst_ladspa_transform (&ladspa->ladspa, map.data,
+      ladspa->generate_samples_per_buffer, NULL);
+  gst_buffer_unmap (buffer, &map);
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_ladspa_source_type_start (GstBaseSrc * base)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
+
+  ladspa->next_sample = 0;
+  ladspa->next_byte = 0;
+  ladspa->next_time = 0;
+  ladspa->check_seek_stop = FALSE;
+  ladspa->eos_reached = FALSE;
+  ladspa->tags_pushed = FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+gst_ladspa_source_type_stop (GstBaseSrc * base)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (base);
+  return gst_ladspa_cleanup (&ladspa->ladspa);
+}
+
+static void
+gst_ladspa_source_type_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
+
+  switch (prop_id) {
+    case GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER:
+      ladspa->samples_per_buffer = g_value_get_int (value);
+      gst_base_src_set_blocksize (GST_BASE_SRC (ladspa),
+          GST_AUDIO_INFO_BPF (&ladspa->info) * ladspa->samples_per_buffer);
+      break;
+    case GST_LADSPA_SOURCE_PROP_IS_LIVE:
+      gst_base_src_set_live (GST_BASE_SRC (ladspa),
+          g_value_get_boolean (value));
+      break;
+    case GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET:
+      ladspa->timestamp_offset = g_value_get_int64 (value);
+      break;
+    case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH:
+      GST_BASE_SRC (ladspa)->can_activate_push = g_value_get_boolean (value);
+      break;
+    case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL:
+      ladspa->can_activate_pull = g_value_get_boolean (value);
+      break;
+    default:
+      gst_ladspa_object_set_property (&ladspa->ladspa, object, prop_id, value,
+          pspec);
+      break;
+  }
+}
+
+static void
+gst_ladspa_source_type_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
+
+  switch (prop_id) {
+    case GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER:
+      g_value_set_int (value, ladspa->samples_per_buffer);
+      break;
+    case GST_LADSPA_SOURCE_PROP_IS_LIVE:
+      g_value_set_boolean (value, gst_base_src_is_live (GST_BASE_SRC (ladspa)));
+      break;
+    case GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET:
+      g_value_set_int64 (value, ladspa->timestamp_offset);
+      break;
+    case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH:
+      g_value_set_boolean (value, GST_BASE_SRC (ladspa)->can_activate_push);
+      break;
+    case GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL:
+      g_value_set_boolean (value, ladspa->can_activate_pull);
+      break;
+    default:
+      gst_ladspa_object_get_property (&ladspa->ladspa, object, prop_id, value,
+          pspec);
+      break;
+  }
+}
+
+static void
+gst_ladspa_source_type_init (GstLADSPASource * ladspa,
+    LADSPA_Descriptor * desc)
+{
+  GstLADSPASourceClass *ladspa_class = GST_LADSPA_SOURCE_GET_CLASS (ladspa);
+
+  gst_ladspa_init (&ladspa->ladspa, &ladspa_class->ladspa);
+
+  /* we operate in time */
+  gst_base_src_set_format (GST_BASE_SRC (ladspa), GST_FORMAT_TIME);
+  gst_base_src_set_live (GST_BASE_SRC (ladspa),
+      GST_LADSPA_SOURCE_DEFAULT_IS_LIVE);
+
+  ladspa->samples_per_buffer = GST_LADSPA_SOURCE_DEFAULT_SAMPLES_PER_BUFFER;
+  ladspa->generate_samples_per_buffer = ladspa->samples_per_buffer;
+  ladspa->timestamp_offset = GST_LADSPA_SOURCE_DEFAULT_TIMESTAMP_OFFSET;
+  ladspa->can_activate_pull = GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PULL;
+
+  gst_base_src_set_blocksize (GST_BASE_SRC (ladspa), -1);
+}
+
+static void
+gst_ladspa_source_type_dispose (GObject * object)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
+
+  gst_ladspa_cleanup (&ladspa->ladspa);
+
+  G_OBJECT_CLASS (gst_ladspa_source_type_parent_class)->dispose (object);
+}
+
+static void
+gst_ladspa_source_type_finalize (GObject * object)
+{
+  GstLADSPASource *ladspa = GST_LADSPA_SOURCE (object);
+
+  gst_ladspa_finalize (&ladspa->ladspa);
+
+  G_OBJECT_CLASS (gst_ladspa_source_type_parent_class)->finalize (object);
+}
+
+/*
+ * It is okay for plugins to 'leak' a one-time allocation. This will be freed when
+ * the application exits. When the plugins are scanned for the first time, this is
+ * done from a separate process to not impose the memory overhead on the calling
+ * application (among other reasons). Hence no need for class_finalize.
+ */
+static void
+gst_ladspa_source_type_base_init (GstLADSPASourceClass * ladspa_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (ladspa_class);
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (ladspa_class);
+  GstBaseSrcClass *base_class = GST_BASE_SRC_CLASS (ladspa_class);
+  LADSPA_Descriptor *desc;
+
+  desc =
+      g_type_get_qdata (G_OBJECT_CLASS_TYPE (object_class), descriptor_quark);
+  g_assert (desc);
+
+  gst_ladspa_class_init (&ladspa_class->ladspa, desc);
+
+  gst_ladspa_element_class_set_metadata (&ladspa_class->ladspa, elem_class,
+      GST_LADSPA_SOURCE_CLASS_TAGS);
+
+  gst_ladspa_source_type_class_add_pad_template (&ladspa_class->ladspa,
+      base_class);
+}
+
+static void
+gst_ladspa_source_type_base_finalize (GstLADSPASourceClass * ladspa_class)
+{
+  gst_ladspa_class_finalize (&ladspa_class->ladspa);
+}
+
+static void
+gst_ladspa_source_type_class_init (GstLADSPASourceClass * ladspa_class,
+    LADSPA_Descriptor * desc)
+{
+  GObjectClass *object_class = (GObjectClass *) ladspa_class;
+  GstBaseSrcClass *base_class = (GstBaseSrcClass *) ladspa_class;
+
+  gst_ladspa_source_type_parent_class =
+      g_type_class_peek_parent (ladspa_class);
+
+  object_class->dispose =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_dispose);
+  object_class->finalize =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_finalize);
+  object_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_set_property);
+  object_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_get_property);
+
+  base_class->set_caps =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_set_caps);
+  base_class->fixate = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_fixate);
+  base_class->is_seekable =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_is_seekable);
+  base_class->do_seek = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_do_seek);
+  base_class->query = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_query);
+  base_class->get_times =
+      GST_DEBUG_FUNCPTR (gst_ladspa_source_type_get_times);
+  base_class->start = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_start);
+  base_class->stop = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_stop);
+  base_class->fill = GST_DEBUG_FUNCPTR (gst_ladspa_source_type_fill);
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SOURCE_PROP_SAMPLES_PER_BUFFER,
+      g_param_spec_int ("samplesperbuffer", "Samples per buffer",
+          "Number of samples in each outgoing buffer", 1, G_MAXINT,
+          GST_LADSPA_SOURCE_DEFAULT_SAMPLES_PER_BUFFER,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class, GST_LADSPA_SOURCE_PROP_IS_LIVE,
+      g_param_spec_boolean ("is-live", "Is Live",
+          "Whether to act as a live source", GST_LADSPA_SOURCE_DEFAULT_IS_LIVE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SOURCE_PROP_TIMESTAMP_OFFSET,
+      g_param_spec_int64 ("timestamp-offset", "Timestamp offset",
+          "An offset added to timestamps set on buffers (in ns)", G_MININT64,
+          G_MAXINT64, GST_LADSPA_SOURCE_DEFAULT_TIMESTAMP_OFFSET,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PUSH,
+      g_param_spec_boolean ("can-activate-push", "Can activate push",
+          "Can activate in push mode",
+          GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PUSH,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (object_class,
+      GST_LADSPA_SOURCE_PROP_CAN_ACTIVATE_PULL,
+      g_param_spec_boolean ("can-activate-pull", "Can activate pull",
+          "Can activate in pull mode",
+          GST_LADSPA_SOURCE_DEFAULT_CAN_ACTIVATE_PULL,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gst_ladspa_object_class_install_properties (&ladspa_class->ladspa,
+      object_class, GST_LADSPA_SOURCE_PROP_LAST);
+}
+
+G_DEFINE_ABSTRACT_TYPE (GstLADSPASource, gst_ladspa_source, GST_TYPE_BASE_SRC);
+
+static void
+gst_ladspa_source_init (GstLADSPASource * ladspa)
+{
+}
+
+static void
+gst_ladspa_source_class_init (GstLADSPASourceClass * ladspa_class)
+{
+}
+
+/*
+ * Construct the type.
+ */
+void
+ladspa_describe_source_plugin (GstPlugin * plugin,
+    const gchar * filename, const LADSPA_Descriptor * desc)
+{
+  GTypeInfo info = {
+    sizeof (GstLADSPASourceClass),
+    (GBaseInitFunc) gst_ladspa_source_type_base_init,
+    (GBaseFinalizeFunc) gst_ladspa_source_type_base_finalize,
+    (GClassInitFunc) gst_ladspa_source_type_class_init,
+    NULL,
+    desc,
+    sizeof (GstLADSPASource),
+    0,
+    (GInstanceInitFunc) gst_ladspa_source_type_init,
+    NULL
+  };
+  gchar *tmp;
+
+  tmp = g_strdup_printf ("ladspasrc-%s-%s", filename, desc->Label);
+  ladspa_register_plugin (plugin, GST_TYPE_LADSPA_SOURCE, tmp, &info,
+      descriptor_quark, filename, desc);
+  g_free (tmp);
+}
diff --git a/ext/ladspa/gstladspasource.h b/ext/ladspa/gstladspasource.h
new file mode 100644 (file)
index 0000000..15df9ba
--- /dev/null
@@ -0,0 +1,90 @@
+/* GStreamer 
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2005 Stefan Kost <ensonic@users.sf.net> (audiotestsrc)
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * gstladspasource.h: Header for LADSPA source 
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_LADSPA_SOURCE_H__
+#define __GST_LADSPA_SOURCE_H__
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesrc.h>
+#include "gstladspautils.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_LADSPA_SOURCE              (gst_ladspa_source_get_type())
+#define GST_LADSPA_SOURCE(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LADSPA_SOURCE,GstLADSPASource))
+#define GST_LADSPA_SOURCE_CAST(obj)         ((GstLADSPASource *) (obj))
+#define GST_LADSPA_SOURCE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LADSPA_SOURCE,GstLADSPASourceClass))
+#define GST_LADSPA_SOURCE_CLASS_CAST(klass) ((GstLADSPASourceClass *) (klass))
+#define GST_LADSPA_SOURCE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_LADSPA_SOURCE,GstLADSPASourceClass))
+#define GST_IS_LADSPA_SOURCE(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LADSPA_SOURCE))
+#define GST_IS_LADSPA_SOURCE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LADSPA_SOURCE))
+
+typedef struct _GstLADSPASource GstLADSPASource;
+
+typedef struct _GstLADSPASourceClass GstLADSPASourceClass;
+
+struct _GstLADSPASource
+{
+  GstBaseSrc parent;
+
+  GstLADSPA ladspa;
+
+  /* audio parameters */
+  GstAudioInfo info;
+  gint samples_per_buffer;
+
+  /*< private > */
+  gboolean tags_pushed;              /* send tags just once ? */
+  GstClockTimeDiff timestamp_offset; /* base offset */
+  GstClockTime next_time;            /* next timestamp */
+  gint64 next_sample;                /* next sample to send */
+  gint64 next_byte;                  /* next byte to send */
+  gint64 sample_stop;
+  gboolean check_seek_stop;
+  gboolean eos_reached;
+  gint generate_samples_per_buffer;  /* used to generate a partial buffer */
+  gboolean can_activate_pull;
+  gboolean reverse;                  /* play backwards */
+};
+
+struct _GstLADSPASourceClass
+{
+  GstBaseSrcClass parent_class;
+
+  GstLADSPAClass ladspa;
+};
+
+GType
+gst_ladspa_source_get_type (void);
+
+void
+ladspa_describe_source_plugin (GstPlugin * plugin,
+    const gchar * filename, const LADSPA_Descriptor * desc);
+
+void
+gst_my_base_source_class_add_pad_template (GstBaseSrcClass * base_class,
+    GstCaps * srccaps);
+
+G_END_DECLS
+
+#endif /* __GST_LADSPA_SOURCE_H__ */
diff --git a/ext/ladspa/gstladspautils.c b/ext/ladspa/gstladspautils.c
new file mode 100644 (file)
index 0000000..e5f8af6
--- /dev/null
@@ -0,0 +1,892 @@
+/* GStreamer LADSPA utils 
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
+ *               2003 Andy Wingo <wingo at pobox.com>
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* 
+ * This module is smartly shared between the source, transform and
+ * sink elements. Handling any specific LADSPA <-> gstreamer interaction.
+ *
+ * FIXME:
+ * Assigning channel orders could be tricky since LADSPA seems to not
+ * specify order of channels in a really nice computer parseable way,
+ * stereo is probably wrong, more than stereo is crazy. LADSPA has
+ * no channel order. All that could be done is to parse the port names
+ * for "(Left)/(Right)", "-L/-R" or ":l/:r" - these are the 3 patterns
+ * seen most of the time.  By now, it just let's them pass in / pass out.
+ * Some nice effort might be done to set channel-masks and/or channel
+ * positions correctly, if this is needed and expected, users will tell.
+ *
+ * This affects mainly interleaving, right now, it just interleaves all
+ * input and output ports. This is the right thing in 90% of the cases,
+ * but will e.g. create a 4 channel out for a plugin that has 2 stereo
+ * 'pairs'.
+ *
+ * Also, gstreamer supports not-interleaved audio, where you just memcpy
+ * each channel after each other: c1...c1c2....c2 and so on. This is not
+ * taken into account, but could be added to the _transform and caps easily
+ * if users demands it.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstladspautils.h"
+#include "gstladspafilter.h"
+#include "gstladspasource.h"
+#include "gstladspasink.h"
+
+#include <math.h>
+
+GST_DEBUG_CATEGORY_EXTERN (ladspa_debug);
+#define GST_CAT_DEFAULT ladspa_debug
+
+/* 
+ * Interleaved buffer: (c1c2c1c2...)
+ * De-interleaved buffer: (c1c1...c2c2...)
+ */
+static inline void
+gst_ladspa_ladspa_deinterleave_data (GstLADSPA * ladspa, LADSPA_Data * outdata,
+    guint samples, guint8 * indata)
+{
+  guint i, j;
+  const guint audio_in = ladspa->klass->count.audio.in;
+
+  for (i = 0; i < audio_in; i++)
+    for (j = 0; j < samples; j++)
+      outdata[i * samples + j] =
+          ((LADSPA_Data *) indata)[j * audio_in + i];
+}
+
+/* 
+ * Interleaved buffer: (c1c2c1c2...)
+ * De-interleaved buffer: (c1c1...c2c2...)
+ */
+static inline void
+gst_ladspa_interleave_ladspa_data (GstLADSPA * ladspa, guint8 * outdata,
+    guint samples, LADSPA_Data * indata)
+{
+  guint i, j;
+  const guint audio_out = ladspa->klass->count.audio.out;
+
+  for (i = 0; i < audio_out; i++)
+    for (j = 0; j < samples; j++)
+      ((LADSPA_Data *) outdata)[j * audio_out + i] =
+          indata[i * samples + j];
+}
+
+/*
+ * Connect the audio in ports.
+ */
+static inline void
+gst_ladspa_connect_audio_in (GstLADSPA * ladspa, guint samples,
+    LADSPA_Data * data)
+{
+  guint i;
+
+  for (i = 0; i < ladspa->klass->count.audio.in; i++) {
+    ladspa->ports.audio.in[i] = data + (i * samples);
+    ladspa->klass->descriptor->connect_port (ladspa->handle,
+        ladspa->klass->map.audio.in[i], ladspa->ports.audio.in[i]);
+  }
+}
+
+/*
+ * Connect the audio out ports.
+ */
+static inline void
+gst_ladspa_connect_audio_out (GstLADSPA * ladspa, guint samples,
+    LADSPA_Data * data)
+{
+  guint i;
+
+  for (i = 0; i < ladspa->klass->count.audio.out; i++) {
+    ladspa->ports.audio.out[i] = data + (i * samples);
+    ladspa->klass->descriptor->connect_port (ladspa->handle,
+        ladspa->klass->map.audio.out[i], ladspa->ports.audio.out[i]);
+  }
+}
+
+/*
+ * Process a block of audio with the ladspa plugin.
+ */
+static inline void
+gst_ladspa_run (GstLADSPA * ladspa, guint nframes)
+{
+  ladspa->klass->descriptor->run (ladspa->handle, nframes);
+}
+
+/*
+ * The data entry/exit point.
+ */
+gboolean
+gst_ladspa_transform (GstLADSPA * ladspa, guint8 * outdata, guint samples,
+    guint8 * indata)
+{
+  LADSPA_Data *in, *out;
+
+  in = g_new0 (LADSPA_Data, samples * ladspa->klass->count.audio.in);
+  out = g_new0 (LADSPA_Data, samples * ladspa->klass->count.audio.out);
+
+  gst_ladspa_ladspa_deinterleave_data (ladspa, in, samples, indata);
+
+  gst_ladspa_connect_audio_in (ladspa, samples, in);
+  gst_ladspa_connect_audio_out (ladspa, samples, out);
+
+  gst_ladspa_run (ladspa, samples);
+
+  gst_ladspa_interleave_ladspa_data (ladspa, outdata, samples, out);
+
+  g_free (out);
+  g_free (in);
+
+  return TRUE;
+}
+
+static gboolean
+gst_ladspa_activate (GstLADSPA * ladspa)
+{
+  g_return_val_if_fail (ladspa->handle != NULL, FALSE);
+  g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
+
+  GST_DEBUG ("activating LADSPA plugin");
+
+  if (ladspa->klass->descriptor->activate)
+    ladspa->klass->descriptor->activate (ladspa->handle);
+
+  ladspa->activated = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+gst_ladspa_deactivate (GstLADSPA * ladspa)
+{
+  g_return_val_if_fail (ladspa->handle != NULL, FALSE);
+  g_return_val_if_fail (ladspa->activated == TRUE, FALSE);
+
+  GST_DEBUG ("LADSPA deactivating plugin");
+
+  if (ladspa->klass->descriptor->deactivate)
+    ladspa->klass->descriptor->deactivate (ladspa->handle);
+
+  ladspa->activated = FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+gst_ladspa_open (GstLADSPA * ladspa, unsigned long rate)
+{
+  guint i;
+
+  GST_DEBUG ("LADSPA instantiating plugin at %lu Hz", rate);
+
+  if (!(ladspa->handle =
+          ladspa->klass->descriptor->instantiate (ladspa->klass->descriptor,
+              rate))) {
+    GST_WARNING ("could not instantiate LADSPA plugin");
+    return FALSE;
+  }
+
+  ladspa->rate = rate;
+
+  /* connect the control ports */
+  for (i = 0; i < ladspa->klass->count.control.in; i++)
+    ladspa->klass->descriptor->connect_port (ladspa->handle,
+        ladspa->klass->map.control.in[i], &(ladspa->ports.control.in[i]));
+  for (i = 0; i < ladspa->klass->count.control.out; i++)
+    ladspa->klass->descriptor->connect_port (ladspa->handle,
+        ladspa->klass->map.control.out[i], &(ladspa->ports.control.out[i]));
+
+  return TRUE;
+}
+
+static void
+gst_ladspa_close (GstLADSPA * ladspa)
+{
+  g_return_if_fail (ladspa->handle != NULL);
+  g_return_if_fail (ladspa->activated == FALSE);
+
+  GST_DEBUG ("LADSPA deinstantiating plugin");
+
+  if (ladspa->klass->descriptor->cleanup)
+    ladspa->klass->descriptor->cleanup (ladspa->handle);
+
+  ladspa->rate = 0;
+  ladspa->handle = NULL;
+}
+
+/*
+ * Safe open.
+ */
+gboolean
+gst_ladspa_setup (GstLADSPA * ladspa, unsigned long rate)
+{
+  gboolean ret = TRUE;
+
+  GST_DEBUG ("LADSPA setting up plugin");
+
+  if (ladspa->handle && ladspa->rate != rate) {
+    if (ladspa->activated)
+      gst_ladspa_deactivate (ladspa);
+
+    gst_ladspa_close (ladspa);
+  }
+
+  if (!ladspa->handle) {
+    gst_ladspa_open (ladspa, rate);
+    if (!(ret = gst_ladspa_activate (ladspa)))
+      gst_ladspa_close (ladspa);
+  }
+
+  return ret;
+}
+
+/*
+ * Safe close.
+ */
+gboolean
+gst_ladspa_cleanup (GstLADSPA * ladspa)
+{
+  gboolean ret = TRUE;
+
+  GST_DEBUG ("LADSPA cleaning up plugin");
+
+  if (ladspa->handle) {
+    if (ladspa->activated)
+      ret = gst_ladspa_deactivate (ladspa);
+    gst_ladspa_close (ladspa);
+  }
+
+  return ret;
+}
+
+static gchar *
+gst_ladspa_object_class_get_param_name (GstLADSPAClass * ladspa_class,
+    GObjectClass * object_class, unsigned long portnum)
+{
+  LADSPA_Descriptor *desc;
+  gchar *name, **namev, **v, *tmp;
+  guint i;
+
+  desc = ladspa_class->descriptor;
+
+  /* beauty in the mess */
+  name = g_strdup ("");
+  namev = g_strsplit_set (desc->PortNames[portnum], "[]()", 0);
+  for (i = 0, v = namev; *v; i++, v++) {
+    if (!(i % 2)) {
+      tmp = name;
+      name = g_strconcat (name, *v, NULL);
+      g_free (tmp);
+    }
+  }
+  g_strfreev (namev);
+  g_strstrip (name);
+  tmp = name;
+  name = g_ascii_strdown (name, -1);
+  g_free (tmp);
+
+  /* this is the same thing that param_spec_* will do */
+  g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
+
+  /* satisfy glib2 (argname[0] must be [A-Za-z]) */
+  if (!((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A'
+              && name[0] <= 'Z'))) {
+    tmp = name;
+    name = g_strconcat ("param-", name, NULL);
+    g_free (tmp);
+  }
+
+  /* check for duplicate property names */
+  if (g_object_class_find_property (G_OBJECT_CLASS (object_class), name)) {
+    gint n = 1;
+    gchar *nprop = g_strdup_printf ("%s-%d", name, n++);
+
+    while (g_object_class_find_property (G_OBJECT_CLASS (object_class), nprop)) {
+      g_free (nprop);
+      nprop = g_strdup_printf ("%s-%d", name, n++);
+    }
+    g_free (name);
+    name = nprop;
+  }
+
+  GST_DEBUG ("LADSPA built property name '%s' from port name '%s'", name,
+      desc->PortNames[portnum]);
+
+  return name;
+}
+
+static GParamSpec *
+gst_ladspa_object_class_get_param_spec (GstLADSPAClass * ladspa_class,
+    GObjectClass * object_class, unsigned long portnum)
+{
+  LADSPA_Descriptor *desc;
+  GParamSpec *ret;
+  gchar *name;
+  gint hintdesc, perms;
+  gfloat lower, upper, def;
+
+  desc = ladspa_class->descriptor;
+
+  name =
+      gst_ladspa_object_class_get_param_name (ladspa_class, object_class,
+      portnum);
+  perms = G_PARAM_READABLE;
+  if (LADSPA_IS_PORT_INPUT (desc->PortDescriptors[portnum]))
+    perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
+  if (LADSPA_IS_PORT_CONTROL (desc->PortDescriptors[portnum]))
+    perms |= GST_PARAM_CONTROLLABLE;
+
+  /* short name for hint descriptor */
+  hintdesc = desc->PortRangeHints[portnum].HintDescriptor;
+
+  if (LADSPA_IS_HINT_TOGGLED (hintdesc)) {
+    ret =
+        g_param_spec_boolean (name, name, desc->PortNames[portnum], FALSE,
+        perms);
+    g_free (name);
+    return ret;
+  }
+
+  if (LADSPA_IS_HINT_BOUNDED_BELOW (hintdesc))
+    lower = desc->PortRangeHints[portnum].LowerBound;
+  else
+    lower = -G_MAXFLOAT;
+
+  if (LADSPA_IS_HINT_BOUNDED_ABOVE (hintdesc))
+    upper = desc->PortRangeHints[portnum].UpperBound;
+  else
+    upper = G_MAXFLOAT;
+
+  if (LADSPA_IS_HINT_SAMPLE_RATE (hintdesc)) {
+    /* FIXME:! (*= ladspa->rate?, *= GST_AUDIO_DEF_RATE?) */
+    lower *= 44100;
+    upper *= 44100;
+  }
+
+  if (LADSPA_IS_HINT_INTEGER (hintdesc)) {
+    lower = CLAMP (lower, G_MININT, G_MAXINT);
+    upper = CLAMP (upper, G_MININT, G_MAXINT);
+  }
+
+  /* default to lower bound */
+  def = lower;
+
+#ifdef LADSPA_IS_HINT_HAS_DEFAULT
+  if (LADSPA_IS_HINT_HAS_DEFAULT (hintdesc)) {
+    if (LADSPA_IS_HINT_DEFAULT_0 (hintdesc))
+      def = 0.0;
+    else if (LADSPA_IS_HINT_DEFAULT_1 (hintdesc))
+      def = 1.0;
+    else if (LADSPA_IS_HINT_DEFAULT_100 (hintdesc))
+      def = 100.0;
+    else if (LADSPA_IS_HINT_DEFAULT_440 (hintdesc))
+      def = 440.0;
+    if (LADSPA_IS_HINT_DEFAULT_MINIMUM (hintdesc))
+      def = lower;
+    else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM (hintdesc))
+      def = upper;
+    else if (LADSPA_IS_HINT_LOGARITHMIC (hintdesc)) {
+      if (LADSPA_IS_HINT_DEFAULT_LOW (hintdesc))
+        def = exp (0.75 * log (lower) + 0.25 * log (upper));
+      else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hintdesc))
+        def = exp (0.5 * log (lower) + 0.5 * log (upper));
+      else if (LADSPA_IS_HINT_DEFAULT_HIGH (hintdesc))
+        def = exp (0.25 * log (lower) + 0.75 * log (upper));
+    } else {
+      if (LADSPA_IS_HINT_DEFAULT_LOW (hintdesc))
+        def = 0.75 * lower + 0.25 * upper;
+      else if (LADSPA_IS_HINT_DEFAULT_MIDDLE (hintdesc))
+        def = 0.5 * lower + 0.5 * upper;
+      else if (LADSPA_IS_HINT_DEFAULT_HIGH (hintdesc))
+        def = 0.25 * lower + 0.75 * upper;
+    }
+  }
+#endif /* LADSPA_IS_HINT_HAS_DEFAULT */
+
+  if (lower > upper) {
+    gfloat tmp;
+
+    /* silently swap */
+    tmp = lower;
+    lower = upper;
+    upper = tmp;
+  }
+
+  def = CLAMP (def, lower, upper);
+
+  if (LADSPA_IS_HINT_INTEGER (hintdesc)) {
+    ret =
+        g_param_spec_int (name, name, desc->PortNames[portnum], lower, upper,
+        def, perms);
+  } else {
+    ret =
+        g_param_spec_float (name, name, desc->PortNames[portnum], lower, upper,
+        def, perms);
+  }
+
+  g_free (name);
+
+  return ret;
+}
+
+void
+gst_ladspa_object_set_property (GstLADSPA * ladspa, GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  /* remember, properties have an offset */
+  prop_id -= ladspa->klass->properties;
+
+  /* only input ports */
+  g_return_if_fail (prop_id < ladspa->klass->count.control.in);
+
+  /* now see what type it is */
+  switch (pspec->value_type) {
+    case G_TYPE_BOOLEAN:
+      ladspa->ports.control.in[prop_id] =
+          g_value_get_boolean (value) ? 1.f : 0.f;
+      break;
+    case G_TYPE_INT:
+      ladspa->ports.control.in[prop_id] = g_value_get_int (value);
+      break;
+    case G_TYPE_FLOAT:
+      ladspa->ports.control.in[prop_id] = g_value_get_float (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+void
+gst_ladspa_object_get_property (GstLADSPA * ladspa, GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  LADSPA_Data *controls;
+
+  /* remember, properties have an offset */
+  prop_id -= ladspa->klass->properties;
+
+  if (prop_id < ladspa->klass->count.control.in) {
+    controls = ladspa->ports.control.in;
+  } else if (prop_id <
+      ladspa->klass->count.control.in + ladspa->klass->count.control.out) {
+    controls = ladspa->ports.control.out;
+    prop_id -= ladspa->klass->count.control.in;
+  } else {
+    g_return_if_reached ();
+  }
+
+  /* now see what type it is */
+  switch (pspec->value_type) {
+    case G_TYPE_BOOLEAN:
+      g_value_set_boolean (value, controls[prop_id] > 0.5);
+      break;
+    case G_TYPE_INT:
+      g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT));
+      break;
+    case G_TYPE_FLOAT:
+      g_value_set_float (value, controls[prop_id]);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+void
+gst_ladspa_object_class_install_properties (GstLADSPAClass * ladspa_class,
+    GObjectClass * object_class, guint offset)
+{
+  GParamSpec *p;
+  gint i, ix;
+
+  ladspa_class->properties = offset;
+
+  /* properties have an offset */
+  ix = ladspa_class->properties;
+
+  /* register properties */
+
+  for (i = 0; i < ladspa_class->count.control.in; i++, ix++) {
+    p = gst_ladspa_object_class_get_param_spec (ladspa_class, object_class,
+        ladspa_class->map.control.in[i]);
+    g_object_class_install_property (object_class, ix, p);
+  }
+  for (i = 0; i < ladspa_class->count.control.out; i++, ix++) {
+    p = gst_ladspa_object_class_get_param_spec (ladspa_class, object_class,
+        ladspa_class->map.control.out[i]);
+    g_object_class_install_property (object_class, ix, p);
+  }
+}
+
+void
+gst_ladspa_element_class_set_metadata (GstLADSPAClass * ladspa_class,
+    GstElementClass * elem_class, const gchar * ladspa_class_tags)
+{
+  LADSPA_Descriptor *desc = ladspa_class->descriptor;
+  gchar *longname, *author, *extra_ladspa_class_tags = NULL, *tmp;
+#ifdef HAVE_LRDF
+  gchar *uri;
+#endif
+
+  longname = g_locale_to_utf8 (desc->Name, -1, NULL, NULL, NULL);
+  if (!longname)
+    longname = g_strdup ("no LADSPA description available");
+
+  /* FIXME: no plugin author field different from element author field */
+  tmp = g_locale_to_utf8 (desc->Maker, -1, NULL, NULL, NULL);
+  if (!tmp)
+    tmp = g_strdup ("no LADSPA author available");
+  author =
+      g_strjoin (", ", tmp,
+      "Juan Manuel Borges Caño <juanmabcmail@gmail.com>",
+      "Andy Wingo <wingo at pobox.com>",
+      "Steve Baker <stevebaker_org@yahoo.co.uk>",
+      "Erik Walthinsen <omega@cse.ogi.edu>",
+      "Stefan Kost <ensonic@users.sf.net>",
+      "Wim Taymans <wim@fluendo.com>", NULL);
+  g_free (tmp);
+
+#ifdef HAVE_LRDF
+  /* libldrf support, we want to get extra klass information here */
+  uri = g_strdup_printf (LADSPA_BASE "%ld", desc->UniqueID);
+  if (uri) {
+    lrdf_statement query = { 0, };
+    lrdf_uris *uris;
+    gchar *str, *base_type = NULL;
+
+    GST_DEBUG ("LADSPA uri (id=%d) : %s", desc->UniqueID, uri);
+
+    /* we can take this directly from 'desc', keep this example for future
+       attributes. 
+
+       if ((str = lrdf_get_setting_metadata (uri, "title"))) {
+       GST_DEBUG ("LADSPA title : %s", str);
+       }
+       if ((str = lrdf_get_setting_metadata (uri, "creator"))) {
+       GST_DEBUG ("LADSPA creator : %s", str);
+       }
+     */
+
+    /* get the rdf:type for this plugin */
+    query.subject = uri;
+    query.predicate = (char *) RDF_BASE "type";
+    query.object = (char *) "?";
+    query.next = NULL;
+    uris = lrdf_match_multi (&query);
+    if (uris) {
+      if (uris->ladspa.count == 1) {
+        base_type = g_strdup (uris->items[0]);
+        GST_DEBUG ("LADSPA base_type :  %s", base_type);
+      }
+      lrdf_free_uris (uris);
+    }
+
+    /* query taxonomy */
+    if (base_type) {
+      uris = lrdf_get_all_superclasses (base_type);
+      if (uris) {
+        guint32 j;
+
+        for (j = 0; j < uris->ladspa.count; j++) {
+          if ((str = lrdf_get_label (uris->items[j]))) {
+            GST_DEBUG ("LADSPA parent_type_label : %s", str);
+            if (extra_ladspa_class_tags) {
+              gchar *old_tags = extra_ladspa_class_tags;
+              extra_ladspa_class_tags =
+                  g_strconcat (extra_ladspa_class_tags, "/", str, NULL);
+              g_free (old_tags);
+            } else {
+              extra_ladspa_class_tags = g_strconcat ("/", str, NULL);
+            }
+          }
+        }
+        lrdf_free_uris (uris);
+      }
+      g_free (base_type);
+    }
+
+    /* we can use this for the presets
+
+       uris = lrdf_get_setting_uris (desc->UniqueID);
+       if (uris) {
+       guint32 j;
+
+       for (j = 0; j < uris->ladspa.count; j++) {
+       GST_INFO ("setting_uri : %s", uris->items[j]);
+       if ((str = lrdf_get_label (uris->items[j]))) {
+       GST_INFO ("setting_label : %s", str);
+       }
+       }
+       lrdf_free_uris (uris);
+       }
+
+     */
+  }
+  g_free (uri);
+
+  if (extra_ladspa_class_tags) {
+    char *s = g_strconcat (ladspa_class_tags, extra_ladspa_class_tags, NULL);
+    g_free (extra_ladspa_class_tags);
+    extra_ladspa_class_tags = s;
+  }
+#endif
+
+  GST_INFO ("tags : %s", ladspa_class_tags);
+  gst_element_class_set_metadata (elem_class, longname,
+      extra_ladspa_class_tags ? extra_ladspa_class_tags : ladspa_class_tags,
+      longname, author);
+
+  g_free (extra_ladspa_class_tags);
+  g_free (author);
+  g_free (longname);
+}
+
+void
+gst_ladspa_filter_type_class_add_pad_templates (GstLADSPAClass *
+    ladspa_class, GstAudioFilterClass * audio_class)
+{
+  GstCaps *srccaps, *sinkcaps;
+
+  srccaps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+      "channels", G_TYPE_INT, ladspa_class->count.audio.out,
+      "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  sinkcaps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+      "channels", G_TYPE_INT, ladspa_class->count.audio.in,
+      "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  gst_my_audio_filter_class_add_pad_templates (audio_class, srccaps, sinkcaps);
+
+  gst_caps_unref (sinkcaps);
+  gst_caps_unref (srccaps);
+}
+
+void
+gst_ladspa_source_type_class_add_pad_template (GstLADSPAClass *
+    ladspa_class, GstBaseSrcClass * base_class)
+{
+  GstCaps *srccaps;
+
+  srccaps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+      "channels", G_TYPE_INT, ladspa_class->count.audio.out,
+      "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  gst_my_base_source_class_add_pad_template (base_class, srccaps);
+
+  gst_caps_unref (srccaps);
+}
+
+void
+gst_ladspa_sink_type_class_add_pad_template (GstLADSPAClass * ladspa_class,
+    GstBaseSinkClass * base_class)
+{
+  GstCaps *sinkcaps;
+
+  sinkcaps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+      "channels", G_TYPE_INT, ladspa_class->count.audio.in,
+      "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  gst_my_base_sink_class_add_pad_template (base_class, sinkcaps);
+
+  gst_caps_unref (sinkcaps);
+}
+
+void
+gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * ladspa_class)
+{
+  GST_DEBUG ("LADSPA initializing component");
+
+  ladspa->klass = ladspa_class;
+
+  ladspa->handle = NULL;
+  ladspa->activated = FALSE;
+  ladspa->rate = 0;
+
+  ladspa->ports.audio.in =
+      g_new0 (LADSPA_Data *, ladspa->klass->count.audio.in);
+  ladspa->ports.audio.out =
+      g_new0 (LADSPA_Data *, ladspa->klass->count.audio.out);
+
+  ladspa->ports.control.in =
+      g_new0 (LADSPA_Data, ladspa->klass->count.control.in);
+  ladspa->ports.control.out =
+      g_new0 (LADSPA_Data, ladspa->klass->count.control.out);
+}
+
+void
+gst_ladspa_finalize (GstLADSPA * ladspa)
+{
+  GST_DEBUG ("LADSPA finalizing component");
+
+  g_free (ladspa->ports.control.out);
+  ladspa->ports.control.out = NULL;
+  g_free (ladspa->ports.control.in);
+  ladspa->ports.control.in = NULL;
+
+  g_free (ladspa->ports.audio.out);
+  ladspa->ports.audio.out = NULL;
+  g_free (ladspa->ports.audio.in);
+  ladspa->ports.audio.in = NULL;
+}
+
+void
+gst_ladspa_class_init (GstLADSPAClass * ladspa_class,
+    LADSPA_Descriptor * descriptor)
+{
+  guint mapper;
+  struct
+  {
+    struct
+    {
+      guint in, out;
+    } control;
+    struct
+    {
+      guint in, out;
+    } audio;
+  } count;
+
+  GST_DEBUG ("LADSPA initializing class");
+
+  ladspa_class->descriptor = descriptor;
+  ladspa_class->properties = 1;
+
+  ladspa_count_ports (ladspa_class->descriptor, &ladspa_class->count.audio.in,
+      &ladspa_class->count.audio.out, &ladspa_class->count.control.in,
+      &ladspa_class->count.control.out);
+
+  ladspa_class->map.audio.in =
+      g_new0 (unsigned long, ladspa_class->count.audio.in);
+  ladspa_class->map.audio.out =
+      g_new0 (unsigned long, ladspa_class->count.audio.out);
+
+  ladspa_class->map.control.in =
+      g_new0 (unsigned long, ladspa_class->count.control.in);
+  ladspa_class->map.control.out =
+      g_new0 (unsigned long, ladspa_class->count.control.out);
+
+  count.audio.in = count.audio.out = count.control.in = count.control.out = 0;
+
+  for (mapper = 0; mapper < ladspa_class->descriptor->PortCount; mapper++) {
+    LADSPA_PortDescriptor p = ladspa_class->descriptor->PortDescriptors[mapper];
+
+    if (LADSPA_IS_PORT_AUDIO (p)) {
+      if (LADSPA_IS_PORT_INPUT (p))
+        ladspa_class->map.audio.in[count.audio.in++] = mapper;
+      else
+        ladspa_class->map.audio.out[count.audio.out++] = mapper;
+    } else if (LADSPA_IS_PORT_CONTROL (p)) {
+      if (LADSPA_IS_PORT_INPUT (p))
+        ladspa_class->map.control.in[count.control.in++] = mapper;
+      else
+        ladspa_class->map.control.out[count.control.out++] = mapper;
+    }
+  }
+
+  g_assert (count.control.out == ladspa_class->count.control.out);
+  g_assert (count.control.in == ladspa_class->count.control.in);
+
+  g_assert (count.audio.out == ladspa_class->count.audio.out);
+  g_assert (count.audio.in == ladspa_class->count.audio.in);
+}
+
+void
+gst_ladspa_class_finalize (GstLADSPAClass * ladspa_class)
+{
+  GST_DEBUG ("LADSPA finalizing class");
+
+  g_free (ladspa_class->map.control.out);
+  ladspa_class->map.control.out = NULL;
+  g_free (ladspa_class->map.control.in);
+  ladspa_class->map.control.in = NULL;
+
+  g_free (ladspa_class->map.audio.out);
+  ladspa_class->map.audio.out = NULL;
+  g_free (ladspa_class->map.audio.in);
+  ladspa_class->map.audio.in = NULL;
+}
+
+void
+ladspa_count_ports (const LADSPA_Descriptor * descriptor,
+    guint * audio_in, guint * audio_out, guint * control_in,
+    guint * control_out)
+{
+  guint i;
+
+  *audio_in = *audio_out = *control_in = *control_out = 0;
+
+  for (i = 0; i < descriptor->PortCount; i++) {
+    LADSPA_PortDescriptor p = descriptor->PortDescriptors[i];
+
+    if (LADSPA_IS_PORT_AUDIO (p)) {
+      if (LADSPA_IS_PORT_INPUT (p))
+        (*audio_in)++;
+      else
+        (*audio_out)++;
+    } else if (LADSPA_IS_PORT_CONTROL (p)) {
+      if (LADSPA_IS_PORT_INPUT (p))
+        (*control_in)++;
+      else
+        (*control_out)++;
+    }
+  }
+}
+
+/* 
+ * Register the type.
+ */
+void
+ladspa_register_plugin (GstPlugin * plugin, GType parent_type,
+    const gchar * tmp, const GTypeInfo * info, GQuark descriptor_quark,
+    const gchar * filename, const LADSPA_Descriptor * desc)
+{
+  gchar *name;
+  GType type;
+
+  name = g_ascii_strdown (tmp, -1);
+  g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-');
+
+  /* if it's not already registered, do it */
+  if (!g_type_from_name (name)) {
+    /* create the type now */
+    type = g_type_register_static (parent_type, name, info, 0);
+
+    /* base init is expected to initialize dynamic data */
+    g_type_set_qdata (type, descriptor_quark, (gpointer) desc);
+
+    /* register the element */
+    gst_element_register (plugin, name, GST_RANK_NONE, type);
+  } else
+    GST_WARNING ("Plugin identifier collision for %s (%s:%lu/%s)", name,
+        filename, desc->UniqueID, desc->Label);
+
+  g_free (name);
+}
diff --git a/ext/ladspa/gstladspautils.h b/ext/ladspa/gstladspautils.h
new file mode 100644 (file)
index 0000000..7ab5e1d
--- /dev/null
@@ -0,0 +1,160 @@
+/* GStreamer
+ * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
+ *
+ * gstladspautils.h: Header for LADSPA plugin utils
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_LADSPA_UTILS_H__
+#define __GST_LADSPA_UTILS_H__
+
+#include <gst/gst.h>
+#include <gst/audio/gstaudiofilter.h>
+#include <gst/base/gstbasesrc.h>
+#include <gst/base/gstbasesink.h>
+
+#include <ladspa.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstLADSPA GstLADSPA;
+
+typedef struct _GstLADSPAClass GstLADSPAClass;
+
+struct _GstLADSPA
+{
+  GstLADSPAClass *klass;
+
+  LADSPA_Handle *handle;
+  gboolean activated;
+  unsigned long rate;
+
+  struct
+  {
+    struct
+    {
+      LADSPA_Data *in;
+      LADSPA_Data *out;
+    } control;
+
+    struct
+    {
+      LADSPA_Data **in;
+      LADSPA_Data **out;
+    } audio;
+  } ports;
+};
+
+struct _GstLADSPAClass
+{
+  guint properties;
+
+  LADSPA_Descriptor *descriptor;
+
+  struct
+  {
+    struct
+    {
+      guint in;
+      guint out;
+    } control;
+
+    struct
+    {
+      guint in;
+      guint out;
+    } audio;
+  } count;
+
+  struct
+  {
+    struct
+    {
+      unsigned long *in;
+      unsigned long *out;
+    } control;
+
+    struct
+    {
+      unsigned long *in;
+      unsigned long *out;
+    } audio;
+  } map;
+};
+
+gboolean
+gst_ladspa_transform (GstLADSPA * ladspa, guint8 * outdata, guint samples,
+    guint8 * indata);
+
+gboolean
+gst_ladspa_setup (GstLADSPA * ladspa, unsigned long rate);
+
+gboolean
+gst_ladspa_cleanup (GstLADSPA * ladspa);
+
+void
+gst_ladspa_object_set_property (GstLADSPA * ladspa, GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+
+void
+gst_ladspa_object_get_property (GstLADSPA * ladspa, GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+void
+gst_ladspa_object_class_install_properties (GstLADSPAClass * ladspa_class,
+    GObjectClass * object_class, guint offset);
+
+void
+gst_ladspa_element_class_set_metadata (GstLADSPAClass * ladspa_class,
+    GstElementClass * elem_class, const gchar * ladspa_class_tags);
+
+void
+gst_ladspa_filter_type_class_add_pad_templates (GstLADSPAClass * ladspa_class,
+    GstAudioFilterClass * audio_class);
+
+void
+gst_ladspa_source_type_class_add_pad_template (GstLADSPAClass * ladspa_class,
+    GstBaseSrcClass * audio_class);
+
+void
+gst_ladspa_sink_type_class_add_pad_template (GstLADSPAClass * ladspa_class,
+    GstBaseSinkClass * base_class);
+
+void
+gst_ladspa_init (GstLADSPA * ladspa, GstLADSPAClass * ladspa_class);
+
+void
+gst_ladspa_finalize (GstLADSPA * ladspa);
+
+void
+gst_ladspa_class_init (GstLADSPAClass * ladspa_class, LADSPA_Descriptor * desc);
+
+void
+gst_ladspa_class_finalize (GstLADSPAClass * ladspa_class);
+
+void
+ladspa_count_ports (const LADSPA_Descriptor * desc, guint * audio_in,
+    guint * audio_out, guint * control_in, guint * control_out);
+
+void
+ladspa_register_plugin (GstPlugin * plugin, GType parent_type,
+    const gchar * tmp, const GTypeInfo * info, GQuark descriptor_quark,
+    const gchar * filename, const LADSPA_Descriptor * desc);
+
+G_END_DECLS
+
+#endif /* __GST_LADSPA_UTILS_H__ */