lv2: Port filters to GStreamer 1.0 and use lilv instead of slv2
authorThibault Saunier <tsaunier@gnome.org>
Tue, 23 Feb 2016 14:11:36 +0000 (15:11 +0100)
committerThibault Saunier <tsaunier@gnome.org>
Sun, 27 Mar 2016 17:28:00 +0000 (19:28 +0200)
Properly separate files as we will not have only one single base class
for all elements as we used to with 0.10, but the same way it is done
with ladspa, we subclass GstAudioFilter, GstBaseSource etc...

https://bugzilla.gnome.org/show_bug.cgi?id=678207

configure.ac
ext/lv2/Makefile.am
ext/lv2/gstlv2.c
ext/lv2/gstlv2.h
ext/lv2/gstlv2filter.c [new file with mode: 0644]

index eac6852..78bb9d0 100644 (file)
@@ -453,7 +453,8 @@ GST_PLUGINS_NONPORTED=" cdxaparse \
  apexsink dc1394 \
  musepack nas sdl timidity \
  wininet \
- xvid lv2 sndio libvisual"
+ xvid sndio libvisual"
+
 AC_SUBST(GST_PLUGINS_NONPORTED)
 
 dnl these are all the gst plug-ins, compilable without additional libs
@@ -2324,9 +2325,9 @@ AG_GST_CHECK_FEATURE(LADSPA, [ladspa], ladspa, [
 dnl *** LV2 ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_LV2, true)
 AG_GST_CHECK_FEATURE(LV2, [lv2], lv2, [
-  PKG_CHECK_MODULES(SLV2, slv2 >= 0.6.6, HAVE_LV2="yes", HAVE_LV2="no")
-  AC_SUBST(SLV2_CFLAGS)
-  AC_SUBST(SLV2_LIBS)
+  PKG_CHECK_MODULES(LILV, lilv-0 >= 0.6.6, HAVE_LV2="yes", HAVE_LV2="no")
+  AC_SUBST(LILV_CFLAGS)
+  AC_SUBST(LILV_LIBS)
 ])
 
 dnl *** libde265 ***
index ea4517f..0c320e3 100644 (file)
@@ -1,14 +1,17 @@
 plugin_LTLIBRARIES = libgstlv2.la
 
-libgstlv2_la_SOURCES = gstlv2.c
+libgstlv2_la_SOURCES = gstlv2.c gstlv2filter.c
 libgstlv2_la_CFLAGS = \
        -I$(top_srcdir)/gst-libs \
+       $(GST_AUDIO_CFLAGS) \
+       $(GST_BASE_CFLAGS) \
        $(GST_PLUGINS_BASE_CFLAGS) \
-       $(GST_CFLAGS) $(SLV2_CFLAGS)
+       $(GST_CFLAGS) $(LILV_CFLAGS)
 libgstlv2_la_LIBADD = \
-       $(top_builddir)/gst-libs/gst/signalprocessor/libgstsignalprocessor-@GST_API_VERSION@.la \
+       $(GST_AUDIO_LIBS) \
+       $(GST_BASE_LIBS) \
        $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \
-       $(LIBM) $(SLV2_LIBS)
+       $(LIBM) $(LILV_LIBS)
 libgstlv2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstlv2_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
 
index 41fcc21..4d5aeff 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
  *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
  *               2003 Andy Wingo <wingo at pobox.com>
+ *               2016 Thibault Saunier <thibault.saunier@collabora.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
-#include <string.h>
-#include <math.h>
-#include <glib.h>
-#include <gst/audio/audio.h>
-#include <gst/audio/multichannel.h>
 
+#include <string.h>
 #include "gstlv2.h"
-#include <slv2/slv2.h>
+
+#include <gst/audio/audio-channels.h>
+#include <lv2/lv2plug.in/ns/ext/port-groups/port-groups.h>
+
+GST_DEBUG_CATEGORY (lv2_debug);
+#define GST_CAT_DEFAULT lv2_debug
 
 #define GST_LV2_DEFAULT_PATH \
   "/usr/lib/lv2" G_SEARCHPATH_SEPARATOR_S \
   "/usr/local/lib/lv2" G_SEARCHPATH_SEPARATOR_S \
   LIBDIR "/lv2"
 
-static void gst_lv2_set_property (GObject * object,
-    guint prop_id, const GValue * value, GParamSpec * pspec);
-
-static void gst_lv2_get_property (GObject * object,
-    guint prop_id, GValue * value, GParamSpec * pspec);
-
-static gboolean gst_lv2_setup (GstSignalProcessor * sigproc, GstCaps * caps);
-static gboolean gst_lv2_start (GstSignalProcessor * sigproc);
-static void gst_lv2_stop (GstSignalProcessor * sigproc);
-static void gst_lv2_cleanup (GstSignalProcessor * sigproc);
-static void gst_lv2_process (GstSignalProcessor * sigproc, guint nframes);
-
-static SLV2World world;
-static SLV2Value audio_class;
-static SLV2Value control_class;
-static SLV2Value input_class;
-static SLV2Value output_class;
-static SLV2Value integer_prop;
-static SLV2Value toggled_prop;
-static SLV2Value in_place_broken_pred;
-static SLV2Value in_group_pred;
-static SLV2Value has_role_pred;
-static SLV2Value lv2_symbol_pred;
-
-static SLV2Value center_role;
-static SLV2Value left_role;
-static SLV2Value right_role;
-static SLV2Value rear_center_role;
-static SLV2Value rear_left_role;
-static SLV2Value rear_right_role;
-static SLV2Value lfe_role;
-static SLV2Value center_left_role;
-static SLV2Value center_right_role;
-static SLV2Value side_left_role;
-static SLV2Value side_right_role;
-
-static GstSignalProcessorClass *parent_class;
-
-static GstPlugin *gst_lv2_plugin;
-
-GST_DEBUG_CATEGORY_STATIC (lv2_debug);
-#define GST_CAT_DEFAULT lv2_debug
-
-static GQuark descriptor_quark = 0;
-
-
-/* Convert an LV2 port role to a Gst channel positon
- * WARNING: If the group has only a single port,
- * GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER will be returned for pg:centerRole
- * (which is used by LV2 for mono groups), but this is not correct.  In this
- * case the value must be changed to GST_AUDIO_CHANNEL_POSITION_FRONT_MONO
- * (this can't be done by this function because this information isn't known
- * at the time it is used).
- */
-static GstAudioChannelPosition
-gst_lv2_role_to_position (SLV2Value role)
-{
-  /* Front.  Mono and left/right are mututally exclusive */
-  if (slv2_value_equals (role, center_role)) {
-
-    return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
-  } else if (slv2_value_equals (role, left_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
-  } else if (slv2_value_equals (role, right_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
-
-    /* Rear. Left/right and center are mututally exclusive */
-  } else if (slv2_value_equals (role, rear_center_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
-  } else if (slv2_value_equals (role, rear_left_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
-  } else if (slv2_value_equals (role, rear_right_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
-
-    /* Subwoofer/low-frequency-effects */
-  } else if (slv2_value_equals (role, lfe_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_LFE;
-
-    /* Center front speakers. Center and left/right_of_center
-     * are mutually exclusive */
-  } else if (slv2_value_equals (role, center_left_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
-  } else if (slv2_value_equals (role, center_right_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
-
-    /* sides */
-  } else if (slv2_value_equals (role, side_left_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
-  } else if (slv2_value_equals (role, side_right_role)) {
-    return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
-  }
-
-  return GST_AUDIO_CHANNEL_POSITION_INVALID;
-}
-
-/* Find and return the group @a uri in @a groups, or NULL if not found */
-static GstLV2Group *
-gst_lv2_class_find_group (GArray * groups, SLV2Value uri)
-{
-  int i = 0;
-  for (; i < groups->len; ++i)
-    if (slv2_value_equals (g_array_index (groups, GstLV2Group, i).uri, uri))
-      return &g_array_index (groups, GstLV2Group, i);
-  return NULL;
-}
-
-static GstAudioChannelPosition *
-gst_lv2_build_positions (GstLV2Group * group)
-{
-  GstAudioChannelPosition *positions = NULL;
-
-  /* don't do anything for mono */
-  if (group->ports->len > 1) {
-    gint i;
-
-    positions = g_new (GstAudioChannelPosition, group->ports->len);
-    for (i = 0; i < group->ports->len; ++i)
-      positions[i] = g_array_index (group->ports, GstLV2Port, i).position;
-  }
-  return positions;
-}
-
-static void
-gst_lv2_base_init (gpointer g_class)
-{
-  GstLV2Class *klass = (GstLV2Class *) g_class;
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-  GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class);
-  SLV2Plugin lv2plugin;
-  SLV2Value val;
-  SLV2Values values, sub_values;
-  GstLV2Group *group = NULL;
-  GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID;
-  guint j, in_pad_index = 0, out_pad_index = 0;
-  const gchar *klass_tags;
-  gchar *longname, *author;
-
-  lv2plugin = (SLV2Plugin) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
-      descriptor_quark);
-
-  g_assert (lv2plugin);
-
-  GST_DEBUG ("base_init %p, plugin %s", g_class,
-      slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)));
-
-  gsp_class->num_group_in = 0;
-  gsp_class->num_group_out = 0;
-  gsp_class->num_audio_in = 0;
-  gsp_class->num_audio_out = 0;
-  gsp_class->num_control_in = 0;
-  gsp_class->num_control_out = 0;
-
-  klass->in_groups = g_array_new (FALSE, TRUE, sizeof (GstLV2Group));
-  klass->out_groups = g_array_new (FALSE, TRUE, sizeof (GstLV2Group));
-  klass->audio_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
-  klass->audio_out_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
-  klass->control_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
-  klass->control_out_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
-
-  /* find ports and groups */
-  for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) {
-    const SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j);
-    const gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class);
-    gboolean in_group = FALSE;
-    struct _GstLV2Port desc = { j, 0, };
-    values = slv2_port_get_value (lv2plugin, port, in_group_pred);
-
-    if (slv2_values_size (values) > 0) {
-      /* port is part of a group */
-      SLV2Value group_uri = slv2_values_get_at (values, 0);
-      GArray *groups = is_input ? klass->in_groups : klass->out_groups;
-      GstLV2Group *group = gst_lv2_class_find_group (groups, group_uri);
-      in_group = TRUE;
-      if (group == NULL) {
-        GstLV2Group g;
-        g.uri = slv2_value_duplicate (group_uri);
-        g.pad = is_input ? in_pad_index++ : out_pad_index++;
-        g.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
-        g.has_roles = TRUE;
-        g.symbol = NULL;
-        sub_values = slv2_plugin_get_value_for_subject (lv2plugin, group_uri,
-            lv2_symbol_pred);
-        /* symbol is mandatory */
-        if (slv2_values_size (sub_values) > 0) {
-          g.symbol = slv2_value_duplicate (slv2_values_get_at (sub_values, 0));
-          if (!gst_element_class_get_pad_template (element_class,
-                  slv2_value_as_string (g.symbol))) {
-            g_array_append_val (groups, g);
-            group = &g_array_index (groups, GstLV2Group, groups->len - 1);
-            assert (group);
-            assert (slv2_value_equals (group->uri, group_uri));
-          } else {
-            GST_WARNING ("plugin %s has duplicate group symbol '%s'",
-                slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)),
-                slv2_value_as_string (g.symbol));
-            in_group = FALSE;
-          }
-        } else {
-          GST_WARNING ("plugin %s has illegal group with no symbol",
-              slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)));
-          in_group = FALSE;
-        }
-      }
-
-      if (in_group) {
-        position = GST_AUDIO_CHANNEL_POSITION_INVALID;
-        sub_values = slv2_port_get_value (lv2plugin, port, has_role_pred);
-        if (slv2_values_size (sub_values) > 0) {
-          SLV2Value role = slv2_values_get_at (sub_values, 0);
-          position = gst_lv2_role_to_position (role);
-        }
-        slv2_values_free (sub_values);
-        if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
-          desc.position = position;
-          g_array_append_val (group->ports, desc);
-        } else {
-          in_group = FALSE;
-        }
-      }
-    }
-
-    if (!in_group) {
-      /* port is not part of a group, or it is part of a group but that group
-       * is illegal so we just ignore it */
-      if (slv2_port_is_a (lv2plugin, port, audio_class)) {
-        desc.pad = is_input ? in_pad_index++ : out_pad_index++;
-        if (is_input)
-          g_array_append_val (klass->audio_in_ports, desc);
-        else
-          g_array_append_val (klass->audio_out_ports, desc);
-      } else if (slv2_port_is_a (lv2plugin, port, control_class)) {
-        if (is_input)
-          g_array_append_val (klass->control_in_ports, desc);
-        else
-          g_array_append_val (klass->control_out_ports, desc);
-      } else {
-        /* unknown port type */
-        GST_INFO ("unhandled port %d", j);
-        continue;
-      }
-    }
-    slv2_values_free (values);
-  }
-
-  gsp_class->num_group_in = klass->in_groups->len;
-  gsp_class->num_group_out = klass->out_groups->len;
-  gsp_class->num_audio_in = klass->audio_in_ports->len;
-  gsp_class->num_audio_out = klass->audio_out_ports->len;
-  gsp_class->num_control_in = klass->control_in_ports->len;
-  gsp_class->num_control_out = klass->control_out_ports->len;
-
-  /* add input group pad templates */
-  for (j = 0; j < gsp_class->num_group_in; ++j) {
-    group = &g_array_index (klass->in_groups, GstLV2Group, j);
-
-    gst_signal_processor_class_add_pad_template (gsp_class,
-        slv2_value_as_string (group->symbol), GST_PAD_SINK, j,
-        group->ports->len);
-  }
-
-  /* add output group pad templates */
-  for (j = 0; j < gsp_class->num_group_out; ++j) {
-    group = &g_array_index (klass->out_groups, GstLV2Group, j);
-
-    gst_signal_processor_class_add_pad_template (gsp_class,
-        slv2_value_as_string (group->symbol), GST_PAD_SRC, j,
-        group->ports->len);
-  }
-
-  /* add non-grouped input port pad templates */
-  for (j = 0; j < gsp_class->num_audio_in; ++j) {
-    struct _GstLV2Port *desc =
-        &g_array_index (klass->audio_in_ports, GstLV2Port, j);
-    SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, desc->index);
-    const gchar *name =
-        slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
-    gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
-        j, 1);
-  }
-
-  /* add non-grouped output port pad templates */
-  for (j = 0; j < gsp_class->num_audio_out; ++j) {
-    struct _GstLV2Port *desc =
-        &g_array_index (klass->audio_out_ports, GstLV2Port, j);
-    SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, desc->index);
-    const gchar *name =
-        slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
-    gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SRC,
-        j, 1);
-  }
-
-  val = slv2_plugin_get_name (lv2plugin);
-  if (val) {
-    longname = g_strdup (slv2_value_as_string (val));
-    slv2_value_free (val);
-  } else {
-    longname = g_strdup ("no description available");
-  }
-  val = slv2_plugin_get_author_name (lv2plugin);
-  if (val) {
-    author = g_strdup (slv2_value_as_string (val));
-    slv2_value_free (val);
-  } else {
-    author = g_strdup ("no author available");
-  }
-
-  if (gsp_class->num_audio_in == 0)
-    klass_tags = "Source/Audio/LV2";
-  else if (gsp_class->num_audio_out == 0) {
-    if (gsp_class->num_control_out == 0)
-      klass_tags = "Sink/Audio/LV2";
-    else
-      klass_tags = "Sink/Analyzer/Audio/LV2";
-  } else
-    klass_tags = "Filter/Effect/Audio/LV2";
-
-  GST_INFO ("tags : %s", klass_tags);
-  gst_element_class_set_metadata (element_class, longname,
-      klass_tags, longname, author);
-  g_free (longname);
-  g_free (author);
-
-  if (!slv2_plugin_has_feature (lv2plugin, in_place_broken_pred))
-    GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass);
-
-  klass->plugin = lv2plugin;
-}
-
-static gchar *
-gst_lv2_class_get_param_name (GstLV2Class * klass, SLV2Port port)
-{
-  SLV2Plugin lv2plugin = klass->plugin;
-  gchar *ret;
-
-  ret = g_strdup (slv2_value_as_string (slv2_port_get_symbol (lv2plugin,
-              port)));
-
-  /* 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,
-      slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port)));
-
-  return ret;
-}
-
-static gchar *
-gst_lv2_class_get_param_nick (GstLV2Class * klass, SLV2Port port)
-{
-  SLV2Plugin lv2plugin = klass->plugin;
-
-  return g_strdup (slv2_value_as_string (slv2_port_get_name (lv2plugin, port)));
-}
-
-static GParamSpec *
-gst_lv2_class_get_param_spec (GstLV2Class * klass, gint portnum)
-{
-  SLV2Plugin lv2plugin = klass->plugin;
-  SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, portnum);
-  SLV2Value lv2def, lv2min, lv2max;
-  GParamSpec *ret;
-  gchar *name, *nick;
-  gint perms;
-  gfloat lower = 0.0f, upper = 1.0f, def = 0.0f;
-
-  nick = gst_lv2_class_get_param_nick (klass, port);
-  name = gst_lv2_class_get_param_name (klass, port);
-
-  GST_DEBUG ("%s trying port %s : %s",
-      slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), name, nick);
-
-  perms = G_PARAM_READABLE;
-  if (slv2_port_is_a (lv2plugin, port, input_class))
-    perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
-  if (slv2_port_is_a (lv2plugin, port, control_class))
-    perms |= GST_PARAM_CONTROLLABLE;
-
-  if (slv2_port_has_property (lv2plugin, port, toggled_prop)) {
-    ret = g_param_spec_boolean (name, nick, nick, FALSE, perms);
-    goto done;
-  }
-
-  slv2_port_get_range (lv2plugin, port, &lv2def, &lv2min, &lv2max);
-
-  if (lv2def)
-    def = slv2_value_as_float (lv2def);
-  if (lv2min)
-    lower = slv2_value_as_float (lv2min);
-  if (lv2max)
-    upper = slv2_value_as_float (lv2max);
-
-  slv2_value_free (lv2def);
-  slv2_value_free (lv2min);
-  slv2_value_free (lv2max);
-
-  if (def < lower) {
-    GST_WARNING ("%s has lower bound %f > default %f",
-        slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), lower, def);
-    lower = def;
-  }
-
-  if (def > upper) {
-    GST_WARNING ("%s has upper bound %f < default %f",
-        slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), upper, def);
-    upper = def;
-  }
-
-  if (slv2_port_has_property (lv2plugin, port, integer_prop))
-    ret = g_param_spec_int (name, nick, nick, lower, upper, def, perms);
-  else
-    ret = g_param_spec_float (name, nick, nick, lower, upper, def, perms);
-
-done:
-  g_free (name);
-  g_free (nick);
-
-  return ret;
-}
-
-static void
-gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
-{
-  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_lv2_set_property;
-  gobject_class->get_property = gst_lv2_get_property;
-
-  gsp_class = GST_SIGNAL_PROCESSOR_CLASS (klass);
-  gsp_class->setup = gst_lv2_setup;
-  gsp_class->start = gst_lv2_start;
-  gsp_class->stop = gst_lv2_stop;
-  gsp_class->cleanup = gst_lv2_cleanup;
-  gsp_class->process = gst_lv2_process;
-
-  klass->plugin = lv2plugin;
-
-  /* properties have an offset of 1 */
-  ix = 1;
-
-  /* register properties */
-
-  for (i = 0; i < gsp_class->num_control_in; i++, ix++) {
-    p = gst_lv2_class_get_param_spec (klass,
-        g_array_index (klass->control_in_ports, GstLV2Port, i).index);
-
-    g_object_class_install_property (gobject_class, ix, p);
-  }
-
-  for (i = 0; i < gsp_class->num_control_out; i++, ix++) {
-    p = gst_lv2_class_get_param_spec (klass,
-        g_array_index (klass->control_out_ports, GstLV2Port, i).index);
-
-    g_object_class_install_property (gobject_class, ix, p);
-  }
-}
-
-static void
-gst_lv2_init (GstLV2 * lv2, GstLV2Class * klass)
-{
-  lv2->plugin = klass->plugin;
-  lv2->instance = NULL;
-  lv2->activated = FALSE;
-}
-
-static void
-gst_lv2_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) ? 0.0f : 1.0f;
-      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_lv2_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.0f);
-      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_lv2_setup (GstSignalProcessor * gsp, GstCaps * caps)
-{
-  GstLV2 *lv2;
-  GstLV2Class *oclass;
-  GstSignalProcessorClass *gsp_class;
-  GstStructure *s;
-  gint i;
-  GstLV2Group *group = NULL;
-  GstAudioChannelPosition *positions = NULL;
-  GstPad *pad;
-
-  gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
-  lv2 = (GstLV2 *) gsp;
-  oclass = (GstLV2Class *) gsp_class;
-
-  g_return_val_if_fail (lv2->activated == FALSE, FALSE);
-
-  GST_DEBUG_OBJECT (lv2, "instantiating the plugin at %d Hz", gsp->sample_rate);
-
-  if (!(lv2->instance =
-          slv2_plugin_instantiate (oclass->plugin, gsp->sample_rate, NULL)))
-    goto no_instance;
-
-  /* connect the control ports */
-  for (i = 0; i < gsp_class->num_control_in; i++)
-    slv2_instance_connect_port (lv2->instance,
-        g_array_index (oclass->control_in_ports, GstLV2Port, i).index,
-        &(gsp->control_in[i]));
-  for (i = 0; i < gsp_class->num_control_out; i++)
-    slv2_instance_connect_port (lv2->instance,
-        g_array_index (oclass->control_out_ports, GstLV2Port, i).index,
-        &(gsp->control_out[i]));
-
-  /* set input group pad audio channel position */
-  for (i = 0; i < gsp_class->num_group_in; ++i) {
-    group = &g_array_index (oclass->in_groups, GstLV2Group, i);
-    if (group->has_roles) {
-      if ((positions = gst_lv2_build_positions (group))) {
-        if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp),
-                    slv2_value_as_string (group->symbol)))) {
-          GST_INFO_OBJECT (lv2, "set audio channel positions on sink pad %s",
-              slv2_value_as_string (group->symbol));
-          s = gst_caps_get_structure (caps, 0);
-          gst_audio_set_channel_positions (s, positions);
-          gst_object_unref (pad);
-        }
-        g_free (positions);
-        positions = NULL;
-      }
-    }
-  }
-  /* set output group pad audio channel position */
-  for (i = 0; i < gsp_class->num_group_out; ++i) {
-    group = &g_array_index (oclass->out_groups, GstLV2Group, i);
-    if (group->has_roles) {
-      if ((positions = gst_lv2_build_positions (group))) {
-        if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp),
-                    slv2_value_as_string (group->symbol)))) {
-          GST_INFO_OBJECT (lv2, "set audio channel positions on src pad %s",
-              slv2_value_as_string (group->symbol));
-          s = gst_caps_get_structure (caps, 0);
-          gst_audio_set_channel_positions (s, positions);
-          gst_object_unref (pad);
-        }
-        g_free (positions);
-        positions = NULL;
-      }
-    }
-  }
-  return TRUE;
-
-no_instance:
-  {
-    GST_WARNING_OBJECT (gsp, "could not create instance");
-    return FALSE;
-  }
-}
-
-static gboolean
-gst_lv2_start (GstSignalProcessor * gsp)
-{
-  GstLV2 *lv2 = (GstLV2 *) gsp;
-
-  g_return_val_if_fail (lv2->activated == FALSE, FALSE);
-  g_return_val_if_fail (lv2->instance != NULL, FALSE);
-
-  GST_DEBUG_OBJECT (lv2, "activating");
-
-  slv2_instance_activate (lv2->instance);
-
-  lv2->activated = TRUE;
-
-  return TRUE;
-}
-
-static void
-gst_lv2_stop (GstSignalProcessor * gsp)
-{
-  GstLV2 *lv2 = (GstLV2 *) gsp;
-
-  g_return_if_fail (lv2->activated == TRUE);
-  g_return_if_fail (lv2->instance != NULL);
-
-  GST_DEBUG_OBJECT (lv2, "deactivating");
-
-  slv2_instance_deactivate (lv2->instance);
-
-  lv2->activated = FALSE;
-}
-
-static void
-gst_lv2_cleanup (GstSignalProcessor * gsp)
-{
-  GstLV2 *lv2 = (GstLV2 *) gsp;
-
-  g_return_if_fail (lv2->activated == FALSE);
-  g_return_if_fail (lv2->instance != NULL);
-
-  GST_DEBUG_OBJECT (lv2, "cleaning up");
-
-  slv2_instance_free (lv2->instance);
-
-  lv2->instance = NULL;
-}
-
-static void
-gst_lv2_process (GstSignalProcessor * gsp, guint nframes)
-{
-  GstSignalProcessorClass *gsp_class;
-  GstLV2 *lv2;
-  GstLV2Class *lv2_class;
-  GstLV2Group *lv2_group;
-  GstLV2Port *lv2_port;
-  GstSignalProcessorGroup *gst_group;
-  guint i, j;
-
-  gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
-  lv2 = (GstLV2 *) gsp;
-  lv2_class = (GstLV2Class *) gsp_class;
-
-  /* multi channel inputs */
-  for (i = 0; i < gsp_class->num_group_in; i++) {
-    lv2_group = &g_array_index (lv2_class->in_groups, GstLV2Group, i);
-    gst_group = &gsp->group_in[i];
-    for (j = 0; j < lv2_group->ports->len; ++j) {
-      lv2_port = &g_array_index (lv2_group->ports, GstLV2Port, j);
-      slv2_instance_connect_port (lv2->instance, lv2_port->index,
-          gst_group->buffer + (j * nframes));
-    }
-  }
-  /* mono inputs */
-  for (i = 0; i < gsp_class->num_audio_in; i++) {
-    lv2_port = &g_array_index (lv2_class->audio_in_ports, GstLV2Port, i);
-    slv2_instance_connect_port (lv2->instance, lv2_port->index,
-        gsp->audio_in[i]);
-  }
-  /* multi channel outputs */
-  for (i = 0; i < gsp_class->num_group_out; i++) {
-    lv2_group = &g_array_index (lv2_class->out_groups, GstLV2Group, i);
-    gst_group = &gsp->group_out[i];
-    for (j = 0; j < lv2_group->ports->len; ++j) {
-      lv2_port = &g_array_index (lv2_group->ports, GstLV2Port, j);
-      slv2_instance_connect_port (lv2->instance, lv2_port->index,
-          gst_group->buffer + (j * nframes));
-    }
-  }
-  /* mono outputs */
-  for (i = 0; i < gsp_class->num_audio_out; i++) {
-    lv2_port = &g_array_index (lv2_class->audio_out_ports, GstLV2Port, i);
-    slv2_instance_connect_port (lv2->instance, lv2_port->index,
-        gsp->audio_out[i]);
-  }
-
-  slv2_instance_run (lv2->instance, nframes);
-}
-
 /* search the plugin path
  */
 static gboolean
-lv2_plugin_discover (void)
+lv2_plugin_discover (GstPlugin * plugin)
 {
-  guint i, j;
-  SLV2Plugins plugins = slv2_world_get_all_plugins (world);
+  guint j, num_sink_pads, num_src_pads;
+  LilvIter *i;
+  const LilvPlugins *plugins = lilv_world_get_all_plugins (world);
 
-  for (i = 0; i < slv2_plugins_size (plugins); ++i) {
-    SLV2Plugin lv2plugin = slv2_plugins_get_at (plugins, i);
-    gint num_audio_ports = 0;
+  for (i = lilv_plugins_begin (plugins); !lilv_plugins_is_end (plugins, i);
+      i = lilv_plugins_next (plugins, i)) {
+    const LilvPlugin *lv2plugin = lilv_plugins_get (plugins, i);
     const gchar *plugin_uri, *p;
     gchar *type_name;
-    GTypeInfo typeinfo = {
-      sizeof (GstLV2Class),
-      (GBaseInitFunc) gst_lv2_base_init,
-      NULL,
-      (GClassInitFunc) gst_lv2_class_init,
-      NULL,
-      lv2plugin,
-      sizeof (GstLV2),
-      0,
-      (GInstanceInitFunc) gst_lv2_init,
-    };
-    GType type;
-
-    plugin_uri = slv2_value_as_uri (slv2_plugin_get_uri (lv2plugin));
+    GHashTable *port_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+        g_free, NULL);
+
+    plugin_uri = lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin));
     /* construct the type name from plugin URI */
     if ((p = strstr (plugin_uri, "://"))) {
       /* cut off the protocol (e.g. http://) */
@@ -828,32 +83,44 @@ lv2_plugin_discover (void)
       goto next;
 
     /* check if this has any audio ports */
-    for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) {
-      const SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j);
-      if (slv2_port_is_a (lv2plugin, port, audio_class)) {
-        num_audio_ports++;
+    num_sink_pads = num_src_pads = 0;
+    for (j = 0; j < lilv_plugin_get_num_ports (lv2plugin); j++) {
+      const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, j);
+      const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
+
+      if (lilv_port_is_a (lv2plugin, port, audio_class)) {
+        LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
+        if (lv2group) {
+          const gchar *uri = lilv_node_as_uri (lv2group);
+
+          if (g_hash_table_contains (port_groups, uri))
+            continue;
+
+          g_hash_table_add (port_groups, g_strdup (uri));
+          lilv_node_free (lv2group);
+        }
+
+
+        if (is_input)
+          num_sink_pads++;
+        else
+          num_src_pads++;
       }
+
     }
-    if (!num_audio_ports) {
-      GST_INFO ("plugin %s has no audio ports", type_name);
+    if (num_sink_pads != 1 || num_src_pads != 1) {
+      GST_FIXME ("plugin %s is not a GstAudioFilter (num_sink_pads: %d"
+          " num_src_pads: %d)", type_name, num_sink_pads, num_src_pads);
       goto next;
     }
 
-    /* create the type */
-    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) lv2plugin);
-
-    if (!gst_element_register (gst_lv2_plugin, type_name, GST_RANK_NONE, type))
-      goto next;
+    gst_lv2_filter_register_element (plugin, type_name, (gpointer) lv2plugin);
 
   next:
     g_free (type_name);
+    g_hash_table_unref (port_groups);
   }
+
   return TRUE;
 }
 
@@ -863,52 +130,53 @@ plugin_init (GstPlugin * plugin)
   GST_DEBUG_CATEGORY_INIT (lv2_debug, "lv2",
       GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LV2");
 
-  world = slv2_world_new ();
-  slv2_world_load_all (world);
+  world = lilv_world_new ();
+  lilv_world_load_all (world);
 
-  audio_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_AUDIO);
-  control_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_CONTROL);
-  input_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_INPUT);
-  output_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_OUTPUT);
+  audio_class = lilv_new_uri (world, LILV_URI_AUDIO_PORT);
+  control_class = lilv_new_uri (world, LILV_URI_CONTROL_PORT);
+  input_class = lilv_new_uri (world, LILV_URI_INPUT_PORT);
+  output_class = lilv_new_uri (world, LILV_URI_OUTPUT_PORT);
 
 #define NS_LV2 "http://lv2plug.in/ns/lv2core#"
-#define NS_PG  "http://lv2plug.in/ns/ext/port-groups"
-
-  integer_prop = slv2_value_new_uri (world, NS_LV2 "integer");
-  toggled_prop = slv2_value_new_uri (world, NS_LV2 "toggled");
-  in_place_broken_pred = slv2_value_new_uri (world, NS_LV2 "inPlaceBroken");
-  in_group_pred = slv2_value_new_uri (world, NS_PG "inGroup");
-  has_role_pred = slv2_value_new_uri (world, NS_PG "role");
-  lv2_symbol_pred = slv2_value_new_string (world, NS_LV2 "symbol");
-
-  center_role = slv2_value_new_uri (world, NS_PG "centerChannel");
-  left_role = slv2_value_new_uri (world, NS_PG "leftChannel");
-  right_role = slv2_value_new_uri (world, NS_PG "rightChannel");
-  rear_center_role = slv2_value_new_uri (world, NS_PG "rearCenterChannel");
-  rear_left_role = slv2_value_new_uri (world, NS_PG "rearLeftChannel");
-  rear_right_role = slv2_value_new_uri (world, NS_PG "rearRightChannel");
-  lfe_role = slv2_value_new_uri (world, NS_PG "lfeChannel");
-  center_left_role = slv2_value_new_uri (world, NS_PG "centerLeftChannel");
-  center_right_role = slv2_value_new_uri (world, NS_PG "centerRightChannel");
-  side_left_role = slv2_value_new_uri (world, NS_PG "sideLeftChannel");
-  side_right_role = slv2_value_new_uri (world, NS_PG "sideRightChannel");
+#define NS_PG  "http://lv2plug.in/ns/ext/port-groups#"
+
+  integer_prop = lilv_new_uri (world, NS_LV2 "integer");
+  toggled_prop = lilv_new_uri (world, NS_LV2 "toggled");
+  in_place_broken_pred = lilv_new_uri (world, NS_LV2 "inPlaceBroken");
+  group_pred = lilv_new_uri (world, LV2_PORT_GROUPS__group);
+  has_role_pred = lilv_new_uri (world, NS_PG "role");
+
+  /* FIXME Verify what should be used here */
+  lv2_symbol_pred = lilv_new_uri (world, LILV_NS_LV2 "symbol");
+
+  center_role = lilv_new_uri (world, LV2_PORT_GROUPS__center);
+  left_role = lilv_new_uri (world, LV2_PORT_GROUPS__left);
+  right_role = lilv_new_uri (world, LV2_PORT_GROUPS__right);
+  rear_center_role = lilv_new_uri (world, LV2_PORT_GROUPS__rearCenter);
+  rear_left_role = lilv_new_uri (world, LV2_PORT_GROUPS__rearLeft);
+  rear_right_role = lilv_new_uri (world, LV2_PORT_GROUPS__rearLeft);
+  lfe_role = lilv_new_uri (world, LV2_PORT_GROUPS__lowFrequencyEffects);
+  center_left_role = lilv_new_uri (world, LV2_PORT_GROUPS__centerLeft);
+  center_right_role = lilv_new_uri (world, LV2_PORT_GROUPS__centerRight);
+  side_left_role = lilv_new_uri (world, LV2_PORT_GROUPS__sideLeft);
+  side_right_role = lilv_new_uri (world, LV2_PORT_GROUPS__sideRight);
 
   gst_plugin_add_dependency_simple (plugin,
       "LV2_PATH", GST_LV2_DEFAULT_PATH, NULL, GST_PLUGIN_DEPENDENCY_FLAG_NONE);
 
-  parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR);
-
-  gst_lv2_plugin = plugin;
-  descriptor_quark = g_quark_from_static_string ("slv2-plugin");
+  descriptor_quark = g_quark_from_static_string ("lilv-plugin");
 
   /* ensure GstAudioChannelPosition type is registered */
   if (!gst_audio_channel_position_get_type ())
     return FALSE;
 
-  if (!lv2_plugin_discover ()) {
+  if (!lv2_plugin_discover (plugin)) {
     GST_WARNING ("no lv2 plugins found, check LV2_PATH");
   }
 
+
+
   /* we don't want to fail, even if there are no elements registered */
   return TRUE;
 }
@@ -918,31 +186,31 @@ __attribute__ ((destructor))
 #endif
      static void plugin_cleanup (GstPlugin * plugin)
 {
-  slv2_value_free (audio_class);
-  slv2_value_free (control_class);
-  slv2_value_free (input_class);
-  slv2_value_free (output_class);
-
-  slv2_value_free (integer_prop);
-  slv2_value_free (toggled_prop);
-  slv2_value_free (in_place_broken_pred);
-  slv2_value_free (in_group_pred);
-  slv2_value_free (has_role_pred);
-  slv2_value_free (lv2_symbol_pred);
-
-  slv2_value_free (center_role);
-  slv2_value_free (left_role);
-  slv2_value_free (right_role);
-  slv2_value_free (rear_center_role);
-  slv2_value_free (rear_left_role);
-  slv2_value_free (rear_right_role);
-  slv2_value_free (lfe_role);
-  slv2_value_free (center_left_role);
-  slv2_value_free (center_right_role);
-  slv2_value_free (side_left_role);
-  slv2_value_free (side_right_role);
-
-  slv2_world_free (world);
+  lilv_node_free (audio_class);
+  lilv_node_free (control_class);
+  lilv_node_free (input_class);
+  lilv_node_free (output_class);
+
+  lilv_node_free (integer_prop);
+  lilv_node_free (toggled_prop);
+  lilv_node_free (in_place_broken_pred);
+  lilv_node_free (group_pred);
+  lilv_node_free (has_role_pred);
+  lilv_node_free (lv2_symbol_pred);
+
+  lilv_node_free (center_role);
+  lilv_node_free (left_role);
+  lilv_node_free (right_role);
+  lilv_node_free (rear_center_role);
+  lilv_node_free (rear_left_role);
+  lilv_node_free (rear_right_role);
+  lilv_node_free (lfe_role);
+  lilv_node_free (center_left_role);
+  lilv_node_free (center_right_role);
+  lilv_node_free (side_left_role);
+  lilv_node_free (side_right_role);
+
+  lilv_world_free (world);
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
index 6f0b466..537ff47 100644 (file)
@@ -1,7 +1,8 @@
 /* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
- *
- * gstladspa.h: Header for LV2 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>
+ *               2016 Thibault Saunier <thibault.saunier@collabora.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * Boston, MA 02110-1301, USA.
  */
 
-
 #ifndef __GST_LV2_H__
 #define __GST_LV2_H__
 
-
-#include <slv2/slv2.h>
-
+#include <lilv/lilv.h>
 #include <gst/gst.h>
 
-#include <gst/signalprocessor/gstsignalprocessor.h>
-
-
-G_BEGIN_DECLS
-
-
-typedef struct _lv2_control_info {
-  gchar *name;
-  gchar *param_name;
-  gfloat lowerbound, upperbound;
-  gfloat def;
-  gboolean lower, upper, samplerate;
-  gboolean toggled, logarithmic, integer, writable;
-} lv2_control_info;
-
-
-typedef struct _GstLV2 GstLV2;
-typedef struct _GstLV2Class GstLV2Class;
-typedef struct _GstLV2Group GstLV2Group;
-typedef struct _GstLV2Port GstLV2Port;
-
-
-struct _GstLV2 {
-  GstSignalProcessor parent;
-
-  SLV2Plugin plugin;
-  SLV2Instance instance;
-
-  gboolean activated;
-};
-
-struct _GstLV2Group {
-  SLV2Value uri; /**< RDF resource (URI or blank node) */
-  guint pad; /**< Gst pad index */
-  SLV2Value symbol; /**< Gst pad name / LV2 group symbol */
-  GArray *ports; /**< Array of GstLV2Port */
-  gboolean has_roles; /**< TRUE iff all ports have a known role */
-};
-
-struct _GstLV2Port {
-  gint index; /**< LV2 port index (on LV2 plugin) */
-  gint pad; /**< Gst pad index (iff not part of a group) */
-  SLV2Value role; /**< Channel position / port role */
-  GstAudioChannelPosition position; /**< Channel position */
-};
-
-struct _GstLV2Class {
-  GstSignalProcessorClass parent_class;
-
-  SLV2Plugin plugin;
-
-  GArray *in_groups; /**< Array of GstLV2Group */
-  GArray *out_groups; /**< Array of GstLV2Group */
-  GArray *audio_in_ports; /**< Array of GstLV2Port */
-  GArray *audio_out_ports; /**< Array of GstLV2Port */
-  GArray *control_in_ports; /**< Array of GstLV2Port */
-  GArray *control_out_ports; /**< Array of GstLV2Port */
-
-};
-
-
-G_END_DECLS
-
-
+LilvWorld *world;
+LilvNode *audio_class;
+LilvNode *control_class;
+LilvNode *input_class;
+LilvNode *output_class;
+LilvNode *integer_prop;
+LilvNode *toggled_prop;
+LilvNode *in_place_broken_pred;
+LilvNode *group_pred;
+LilvNode *has_role_pred;
+LilvNode *lv2_symbol_pred;
+
+LilvNode *center_role;
+LilvNode *left_role;
+LilvNode *right_role;
+LilvNode *rear_center_role;
+LilvNode *rear_left_role;
+LilvNode *rear_right_role;
+LilvNode *lfe_role;
+LilvNode *center_left_role;
+LilvNode *center_right_role;
+LilvNode *side_left_role;
+LilvNode *side_right_role;
+
+GQuark descriptor_quark;
+
+gboolean gst_lv2_filter_register_element (GstPlugin *plugin,
+                                          const gchar *type_name,
+                                          gpointer *lv2plugin);
 #endif /* __GST_LV2_H__ */
diff --git a/ext/lv2/gstlv2filter.c b/ext/lv2/gstlv2filter.c
new file mode 100644 (file)
index 0000000..d4082de
--- /dev/null
@@ -0,0 +1,843 @@
+/* GStreamer
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ *               2001 Steve Baker <stevebaker_org@yahoo.co.uk>
+ *               2003 Andy Wingo <wingo at pobox.com>
+ *               2016 Thibault Saunier <thibault.saunier@collabora.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.
+ */
+
+/**
+ * SECTION:element-lv2
+ * @short_description: bridge for LV2.
+ *
+ * LV2 is a standard for plugins and matching host applications,
+ * mainly targeted at audio processing and generation.  It is intended as
+ * a successor to LADSPA (Linux Audio Developer's Simple Plugin API).
+ *
+ * The LV2 element is a bridge for plugins using the
+ * <ulink url="http://www.lv2plug.in/">LV2</ulink> API.  It scans all
+ * installed LV2 plugins and registers them as gstreamer elements.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "gstlv2.h"
+
+#include <string.h>
+#include <math.h>
+#include <glib.h>
+
+#include <lilv/lilv.h>
+
+#include <gst/audio/audio.h>
+#include <gst/audio/gstaudiofilter.h>
+#include <gst/audio/audio-channels.h>
+
+GST_DEBUG_CATEGORY_EXTERN (lv2_debug);
+#define GST_CAT_DEFAULT lv2_debug
+
+typedef struct _lv2_control_info
+{
+  gchar *name;
+  gchar *param_name;
+  gfloat lowerbound, upperbound;
+  gfloat def;
+  gboolean lower, upper, samplerate;
+  gboolean toggled, logarithmic, integer, writable;
+} lv2_control_info;
+
+
+typedef struct _GstLV2Filter GstLV2Filter;
+typedef struct _GstLV2FilterClass GstLV2FilterClass;
+typedef struct _GstLV2FilterGroup GstLV2FilterGroup;
+typedef struct _GstLV2FilterPort GstLV2FilterPort;
+
+
+struct _GstLV2Filter
+{
+  GstAudioFilter parent;
+
+  LilvPlugin *plugin;
+  LilvInstance *instance;
+
+  gboolean activated;
+
+  /* TODO refactor in the same way as LADSPA plugin */
+  struct
+  {
+    struct
+    {
+      gfloat *in;
+      gfloat *out;
+    } control;
+  } ports;
+};
+
+struct _GstLV2FilterGroup
+{
+  gchar *uri; /**< RDF resource (URI or blank node) */
+  guint pad; /**< Gst pad index */
+  gchar *symbol; /**< Gst pad name / LV2 group symbol */
+  GArray *ports; /**< Array of GstLV2FilterPort */
+  gboolean has_roles; /**< TRUE iff all ports have a known role */
+};
+
+struct _GstLV2FilterPort
+{
+  gint index; /**< LV2 port index (on LV2 plugin) */
+  gint pad; /**< Gst pad index (iff not part of a group) */
+  LilvNode *role; /**< Channel position / port role */
+  GstAudioChannelPosition position; /**< Channel position */
+};
+
+struct _GstLV2FilterClass
+{
+  GstAudioFilterClass parent_class;
+
+  LilvPlugin *plugin;
+
+  GstLV2FilterGroup in_group; /**< Array of GstLV2FilterGroup */
+  GstLV2FilterGroup out_group; /**< Array of GstLV2FilterGroup */
+  GArray *control_in_ports; /**< Array of GstLV2FilterPort */
+  GArray *control_out_ports; /**< Array of GstLV2FilterPort */
+
+};
+
+static GstAudioFilter *parent_class = NULL;
+
+/* GObject vmethods implementation */
+static void
+gst_lv2_filter_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstLV2Filter *self = (GstLV2Filter *) (object);
+  GstLV2FilterClass *klass =
+      (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (object);
+
+  /* remember, properties have an offset of 1 */
+  prop_id--;
+
+  /* only input ports */
+  g_return_if_fail (prop_id < klass->control_in_ports->len);
+
+  /* now see what type it is */
+  switch (pspec->value_type) {
+    case G_TYPE_BOOLEAN:
+      self->ports.control.in[prop_id] =
+          g_value_get_boolean (value) ? 0.0f : 1.0f;
+      break;
+    case G_TYPE_INT:
+      self->ports.control.in[prop_id] = g_value_get_int (value);
+      break;
+    case G_TYPE_FLOAT:
+      self->ports.control.in[prop_id] = g_value_get_float (value);
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+static void
+gst_lv2_filter_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstLV2Filter *self = (GstLV2Filter *) (object);
+  GstLV2FilterClass *klass =
+      (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (object);
+
+  gfloat *controls;
+
+  /* remember, properties have an offset of 1 */
+  prop_id--;
+
+  if (prop_id < klass->control_in_ports->len) {
+    controls = self->ports.control.in;
+  } else if (prop_id < klass->control_in_ports->len +
+      klass->control_out_ports->len) {
+    controls = self->ports.control.out;
+    prop_id -= klass->control_in_ports->len;
+  } 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.0f);
+      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 ();
+  }
+}
+
+#if 0
+/* Convert an LV2 port role to a Gst channel positon
+ * WARNING: If the group has only a single port,
+ * GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER will be returned for pg:centerRole
+ * (which is used by LV2 for mono groups), but this is not correct.  In this
+ * case the value must be changed to GST_AUDIO_CHANNEL_POSITION_FRONT_MONO
+ * (this can't be done by this function because this information isn't known
+ * at the time it is used).
+ */
+static GstAudioChannelPosition
+gst_lv2_filter_role_to_position (LilvNode * role)
+{
+  /* Front.  Mono and left/right are mututally exclusive */
+  if (lilv_node_equals (role, center_role)) {
+
+    return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+  } else if (lilv_node_equals (role, left_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+  } else if (lilv_node_equals (role, right_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+
+    /* Rear. Left/right and center are mututally exclusive */
+  } else if (lilv_node_equals (role, rear_center_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
+  } else if (lilv_node_equals (role, rear_left_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+  } else if (lilv_node_equals (role, rear_right_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+
+    /* Subwoofer/low-frequency-effects */
+  } else if (lilv_node_equals (role, lfe_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_LFE1;
+
+    /* Center front speakers. Center and left/right_of_center
+     * are mutually exclusive */
+  } else if (lilv_node_equals (role, center_left_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+  } else if (lilv_node_equals (role, center_right_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+
+    /* sides */
+  } else if (lilv_node_equals (role, side_left_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
+  } else if (lilv_node_equals (role, side_right_role)) {
+    return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
+  }
+
+  return GST_AUDIO_CHANNEL_POSITION_INVALID;
+}
+
+static GstAudioChannelPosition *
+gst_lv2_filter_build_positions (GstLV2FilterGroup * group)
+{
+  GstAudioChannelPosition *positions = NULL;
+
+  /* don't do anything for mono */
+  if (group->ports->len > 1) {
+    gint i;
+
+    positions = g_new (GstAudioChannelPosition, group->ports->len);
+    for (i = 0; i < group->ports->len; ++i)
+      positions[i] = g_array_index (group->ports, GstLV2FilterPort, i).position;
+  }
+  return positions;
+}
+#endif
+
+/* Find and return the group @a uri in @a groups, or NULL if not found */
+static void
+gst_lv2_filter_type_class_add_pad_templates (GstLV2FilterClass * klass)
+{
+  GstCaps *srccaps, *sinkcaps;
+  GstPadTemplate *pad_template;
+  GstElementClass *elem_class = GST_ELEMENT_CLASS (klass);
+
+  gint in_channels = 1, out_channels = 1;
+
+  in_channels = klass->in_group.ports->len;
+
+  out_channels = klass->out_group.ports->len;
+
+  /* FIXME Implement deintereleaved audio support */
+  sinkcaps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+      "channels", G_TYPE_INT, in_channels,
+      "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  srccaps = gst_caps_new_simple ("audio/x-raw",
+      "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+      "channels", G_TYPE_INT, out_channels,
+      "rate", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+      "layout", G_TYPE_STRING, "interleaved", NULL);
+
+  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);
+
+  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);
+
+  gst_caps_unref (sinkcaps);
+  gst_caps_unref (srccaps);
+}
+
+static gboolean
+gst_lv2_filter_setup (GstAudioFilter * gsp, const GstAudioInfo * info)
+{
+  GstLV2Filter *self;
+  GstLV2FilterClass *oclass;
+  GstAudioFilterClass *audiofilter_class;
+  gint i;
+
+  audiofilter_class = GST_AUDIO_FILTER_GET_CLASS (gsp);
+  self = (GstLV2Filter *) gsp;
+  oclass = (GstLV2FilterClass *) audiofilter_class;
+
+  g_return_val_if_fail (self->activated == FALSE, FALSE);
+
+  GST_DEBUG_OBJECT (self, "instantiating the plugin at %d Hz",
+      GST_AUDIO_INFO_RATE (info));
+
+  if (self->instance)
+    lilv_instance_free (self->instance);
+
+  if (!(self->instance =
+          lilv_plugin_instantiate (oclass->plugin, GST_AUDIO_INFO_RATE (info),
+              NULL)))
+    goto no_instance;
+
+  /* connect the control ports */
+  for (i = 0; i < oclass->control_in_ports->len; i++)
+    lilv_instance_connect_port (self->instance,
+        g_array_index (oclass->control_in_ports, GstLV2FilterPort, i).index,
+        &(self->ports.control.in[i]));
+
+  for (i = 0; i < oclass->control_out_ports->len; i++)
+    lilv_instance_connect_port (self->instance,
+        g_array_index (oclass->control_out_ports, GstLV2FilterPort, i).index,
+        &(self->ports.control.out[i]));
+
+  /* FIXME Handle audio channel positionning while negotiating CAPS */
+#if 0
+  /* set input group pad audio channel position */
+  for (i = 0; i < oclass->in_groups->len; ++i) {
+    group = &g_array_index (oclass->in_groups, GstLV2FilterGroup, i);
+    if (group->has_roles) {
+      if ((positions = gst_lv2_filter_build_positions (group))) {
+        if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp),
+                    lilv_node_as_string (group->symbol)))) {
+          GST_INFO_OBJECT (self, "set audio channel positions on sink pad %s",
+              lilv_node_as_string (group->symbol));
+          s = gst_caps_get_structure (caps, 0);
+          gst_audio_set_channel_positions (s, positions);
+          gst_object_unref (pad);
+        }
+        g_free (positions);
+        positions = NULL;
+      }
+    }
+  }
+  /* set output group pad audio channel position */
+  for (i = 0; i < oclass->out_groups->len; ++i) {
+    group = &g_array_index (oclass->out_groups, GstLV2FilterGroup, i);
+    if (group->has_roles) {
+      if ((positions = gst_lv2_filter_build_positions (group))) {
+        if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp),
+                    lilv_node_as_string (group->symbol)))) {
+          GST_INFO_OBJECT (self, "set audio channel positions on src pad %s",
+              lilv_node_as_string (group->symbol));
+          s = gst_caps_get_structure (caps, 0);
+          gst_audio_set_channel_positions (s, positions);
+          gst_object_unref (pad);
+        }
+        g_free (positions);
+        positions = NULL;
+      }
+    }
+  }
+#endif
+
+  lilv_instance_activate (self->instance);
+  self->activated = TRUE;
+
+  return TRUE;
+
+no_instance:
+  {
+    GST_ERROR_OBJECT (gsp, "could not create instance");
+    return FALSE;
+  }
+}
+
+static gboolean
+gst_lv2_filter_stop (GstBaseTransform * transform)
+{
+  GstLV2Filter *lv2 = (GstLV2Filter *) transform;
+
+  if (lv2->activated == FALSE) {
+      GST_ERROR_OBJECT (transform, "Deactivating but LV2 plugin not activated");
+
+      return TRUE;
+  }
+
+  if (lv2->instance == NULL) {
+      GST_ERROR_OBJECT (transform, "Deactivating but no LV2 plugin set");
+
+      return TRUE;
+  }
+
+  GST_DEBUG_OBJECT (lv2, "deactivating");
+
+  lilv_instance_deactivate (lv2->instance);
+
+  lv2->activated = FALSE;
+
+  lilv_instance_free (lv2->instance);
+  lv2->instance = NULL;
+
+  return TRUE;
+}
+
+static inline void
+gst_lv2_filter_deinterleave_data (guint n_channels, gfloat * outdata,
+    guint samples, gfloat * indata)
+{
+  guint i, j;
+
+  for (i = 0; i < n_channels; i++)
+    for (j = 0; j < samples; j++)
+      outdata[i * samples + j] = indata[j * n_channels + i];
+}
+
+static inline void
+gst_lv2_filter_interleave_data (guint n_channels, gfloat * outdata,
+    guint samples, gfloat * indata)
+{
+  guint i, j;
+
+  for (i = 0; i < n_channels; i++)
+    for (j = 0; j < samples; j++) {
+      outdata[j * n_channels + i] = indata[i * samples + j];
+    }
+}
+
+static GstFlowReturn
+gst_lv2_filter_transform_data (GstLV2Filter * self,
+    GstMapInfo * in_map, GstMapInfo * out_map)
+{
+  GstAudioFilterClass *audiofilter_class;
+  GstLV2FilterClass *lv2_class;
+  GstLV2FilterGroup *lv2_group;
+  GstLV2FilterPort *lv2_port;
+  guint j, nframes, samples, out_samples;
+
+  gfloat *in = NULL, *out = NULL;
+
+  nframes = in_map->size / sizeof (float);
+
+  audiofilter_class = GST_AUDIO_FILTER_GET_CLASS (self);
+  lv2_class = (GstLV2FilterClass *) audiofilter_class;
+
+  samples = nframes / lv2_class->in_group.ports->len;
+
+  /* multi channel inputs */
+  lv2_group = &lv2_class->in_group;
+
+  in = g_new0 (gfloat, nframes);
+
+  if (lv2_group->ports->len > 1)
+      gst_lv2_filter_deinterleave_data (lv2_group->ports->len, in,
+          samples, (gfloat *) in_map->data);
+
+  for (j = 0; j < lv2_group->ports->len; ++j) {
+    lv2_port = &g_array_index (lv2_group->ports, GstLV2FilterPort, j);
+
+    lilv_instance_connect_port (self->instance, lv2_port->index,
+        in + (j * samples));
+  }
+
+  lv2_group = &lv2_class->out_group;
+  out_samples = nframes / lv2_group->ports->len;
+  out = g_new0 (gfloat, samples * lv2_group->ports->len);
+  for (j = 0; j < lv2_group->ports->len; ++j) {
+    lv2_port = &g_array_index (lv2_group->ports, GstLV2FilterPort, j);
+    lilv_instance_connect_port (self->instance, lv2_port->index,
+        out + (j * out_samples));
+  }
+
+  lilv_instance_run (self->instance, samples);
+
+  if (lv2_group->ports->len > 1)
+      gst_lv2_filter_interleave_data (lv2_group->ports->len,
+          (gfloat *) out_map->data, out_samples, out);
+  g_free (out);
+  g_free (in);
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_lv2_filter_transform_ip (GstBaseTransform * transform, GstBuffer * buf)
+{
+  GstFlowReturn ret;
+  GstMapInfo map;
+
+  gst_buffer_map (buf, &map, GST_MAP_READWRITE);
+
+  ret = gst_lv2_filter_transform_data ((GstLV2Filter *) transform, &map, &map);
+
+  gst_buffer_unmap (buf, &map);
+
+  return ret;
+}
+
+static GstFlowReturn
+gst_lv2_filter_transform (GstBaseTransform * transform,
+    GstBuffer * inbuf, GstBuffer * outbuf)
+{
+  GstMapInfo in_map, out_map;
+  GstFlowReturn ret;
+
+  gst_buffer_map (inbuf, &in_map, GST_MAP_READ);
+  gst_buffer_map (outbuf, &out_map, GST_MAP_WRITE);
+
+  ret = gst_lv2_filter_transform_data ((GstLV2Filter *) transform,
+      &in_map, &out_map);
+
+  gst_buffer_unmap (inbuf, &in_map);
+  gst_buffer_unmap (outbuf, &out_map);
+
+  return ret;
+}
+
+static void
+gst_lv2_filter_base_init (gpointer g_class)
+{
+  GstLV2FilterClass *klass = (GstLV2FilterClass *) g_class;
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+  LilvPlugin *lv2plugin;
+  LilvNode *val;
+  /* FIXME Handle channels positionning
+   * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */
+  guint j, in_pad_index = 0, out_pad_index = 0;
+  const gchar *klass_tags;
+  gchar *longname, *author;
+
+  lv2plugin = (LilvPlugin *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
+      descriptor_quark);
+
+  g_assert (lv2plugin);
+
+  GST_INFO ("base_init %p, plugin %s", g_class,
+      lilv_node_get_turtle_token (lilv_plugin_get_uri (lv2plugin)));
+
+  klass->in_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
+  klass->out_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
+  klass->control_in_ports =
+      g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
+  klass->control_out_ports =
+      g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
+
+  /* find ports and groups */
+  for (j = 0; j < lilv_plugin_get_num_ports (lv2plugin); j++) {
+    const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, j);
+    const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
+    struct _GstLV2FilterPort desc = { j, 0, };
+    LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
+
+    if (lv2group) {
+      /* port is part of a group */
+      const gchar *group_uri = lilv_node_as_uri (lv2group);
+      GstLV2FilterGroup *group =
+          is_input ? &klass->in_group : &klass->out_group;
+
+      if (group->uri == NULL) {
+        group->uri = g_strdup (group_uri);
+        group->pad = is_input ? in_pad_index++ : out_pad_index++;
+        group->ports = g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
+      }
+
+      /* FIXME Handle channels positionning
+         position = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+         sub_values = lilv_port_get_value (lv2plugin, port, has_role_pred);
+         if (lilv_nodes_size (sub_values) > 0) {
+         LilvNode *role = lilv_nodes_get_at (sub_values, 0);
+         position = gst_lv2_filter_role_to_position (role);
+         }
+         lilv_nodes_free (sub_values);
+
+         if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
+         desc.position = position;
+         } */
+
+      g_array_append_val (group->ports, desc);
+    } else {
+      /* port is not part of a group, or it is part of a group but that group
+       * is illegal so we just ignore it */
+      if (lilv_port_is_a (lv2plugin, port, audio_class)) {
+
+        desc.pad = is_input ? in_pad_index++ : out_pad_index++;
+        if (is_input)
+          g_array_append_val (klass->in_group.ports, desc);
+        else
+          g_array_append_val (klass->out_group.ports, desc);
+      } else if (lilv_port_is_a (lv2plugin, port, control_class)) {
+        if (is_input)
+          g_array_append_val (klass->control_in_ports, desc);
+        else
+          g_array_append_val (klass->control_out_ports, desc);
+      } else {
+        /* unknown port type */
+        GST_INFO ("unhandled port %d", j);
+        continue;
+      }
+    }
+  }
+  gst_lv2_filter_type_class_add_pad_templates (klass);
+
+  val = lilv_plugin_get_name (lv2plugin);
+  if (val) {
+    longname = g_strdup (lilv_node_as_string (val));
+    lilv_node_free (val);
+  } else {
+    longname = g_strdup ("no description available");
+  }
+  val = lilv_plugin_get_author_name (lv2plugin);
+  if (val) {
+    author = g_strdup (lilv_node_as_string (val));
+    lilv_node_free (val);
+  } else {
+    author = g_strdup ("no author available");
+  }
+
+  klass_tags = "Filter/Effect/Audio/LV2";
+
+  GST_INFO ("tags : %s", klass_tags);
+  gst_element_class_set_metadata (element_class, longname,
+      klass_tags, longname, author);
+  g_free (longname);
+  g_free (author);
+
+  klass->plugin = lv2plugin;
+}
+
+static gchar *
+gst_lv2_filter_class_get_param_name (GstLV2FilterClass * klass,
+    const LilvPort * port)
+{
+  LilvPlugin *lv2plugin = klass->plugin;
+  gchar *ret;
+
+  ret = g_strdup (lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
+
+  /* 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,
+      lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
+
+  return ret;
+}
+
+static gchar *
+gst_lv2_filter_class_get_param_nick (GstLV2FilterClass * klass,
+    const LilvPort * port)
+{
+  LilvPlugin *lv2plugin = klass->plugin;
+
+  return g_strdup (lilv_node_as_string (lilv_port_get_name (lv2plugin, port)));
+}
+
+static GParamSpec *
+gst_lv2_filter_class_get_param_spec (GstLV2FilterClass * klass, gint portnum)
+{
+  LilvPlugin *lv2plugin = klass->plugin;
+  const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, portnum);
+  LilvNode *lv2def, *lv2min, *lv2max;
+  GParamSpec *ret;
+  gchar *name, *nick;
+  gint perms;
+  gfloat lower = 0.0f, upper = 1.0f, def = 0.0f;
+
+  nick = gst_lv2_filter_class_get_param_nick (klass, port);
+  name = gst_lv2_filter_class_get_param_name (klass, port);
+
+  GST_DEBUG ("%s trying port %s : %s",
+      lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, nick);
+
+  perms = G_PARAM_READABLE;
+  if (lilv_port_is_a (lv2plugin, port, input_class))
+    perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
+  if (lilv_port_is_a (lv2plugin, port, control_class))
+    perms |= GST_PARAM_CONTROLLABLE;
+
+  if (lilv_port_has_property (lv2plugin, port, toggled_prop)) {
+    ret = g_param_spec_boolean (name, nick, nick, FALSE, perms);
+    goto done;
+  }
+
+  lilv_port_get_range (lv2plugin, port, &lv2def, &lv2min, &lv2max);
+
+  if (lv2def)
+    def = lilv_node_as_float (lv2def);
+  if (lv2min)
+    lower = lilv_node_as_float (lv2min);
+  if (lv2max)
+    upper = lilv_node_as_float (lv2max);
+
+  lilv_node_free (lv2def);
+  lilv_node_free (lv2min);
+  lilv_node_free (lv2max);
+
+  if (def < lower) {
+    GST_WARNING ("%s has lower bound %f > default %f",
+        lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), lower, def);
+    lower = def;
+  }
+
+  if (def > upper) {
+    GST_WARNING ("%s has upper bound %f < default %f",
+        lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), upper, def);
+    upper = def;
+  }
+
+  if (lilv_port_has_property (lv2plugin, port, integer_prop))
+    ret = g_param_spec_int (name, nick, nick, lower, upper, def, perms);
+  else
+    ret = g_param_spec_float (name, nick, nick, lower, upper, def, perms);
+
+done:
+  g_free (name);
+  g_free (nick);
+
+  return ret;
+}
+
+static void
+gst_lv2_filter_class_init (GstLV2FilterClass * klass, LilvPlugin * lv2plugin)
+{
+  GObjectClass *gobject_class;
+  GstBaseTransformClass *transform_class;
+  GstAudioFilterClass *audiofilter_class;
+  GParamSpec *p;
+  gint i, ix;
+
+  GST_DEBUG ("class_init %p", klass);
+
+  gobject_class = (GObjectClass *) klass;
+  gobject_class->set_property = gst_lv2_filter_set_property;
+  gobject_class->get_property = gst_lv2_filter_get_property;
+
+  audiofilter_class = GST_AUDIO_FILTER_CLASS (klass);
+  audiofilter_class->setup = gst_lv2_filter_setup;
+
+  transform_class = GST_BASE_TRANSFORM_CLASS (klass);
+  transform_class->stop = gst_lv2_filter_stop;
+  transform_class->transform = gst_lv2_filter_transform;
+  transform_class->transform_ip = gst_lv2_filter_transform_ip;
+
+  klass->plugin = lv2plugin;
+
+  /* properties have an offset of 1 */
+  ix = 1;
+
+  /* register properties */
+
+  for (i = 0; i < klass->control_in_ports->len; i++, ix++) {
+    p = gst_lv2_filter_class_get_param_spec (klass,
+        g_array_index (klass->control_in_ports, GstLV2FilterPort, i).index);
+
+    g_object_class_install_property (gobject_class, ix, p);
+  }
+
+  for (i = 0; i < klass->control_out_ports->len; i++, ix++) {
+    p = gst_lv2_filter_class_get_param_spec (klass,
+        g_array_index (klass->control_out_ports, GstLV2FilterPort, i).index);
+
+    g_object_class_install_property (gobject_class, ix, p);
+  }
+}
+
+static void
+gst_lv2_filter_init (GstLV2Filter * self, GstLV2FilterClass * klass)
+{
+  self->plugin = klass->plugin;
+  self->instance = NULL;
+  self->activated = FALSE;
+
+  self->ports.control.in = g_new0 (gfloat, klass->control_in_ports->len);
+  self->ports.control.out = g_new0 (gfloat, klass->control_out_ports->len);
+
+  if (!lilv_plugin_has_feature (self->plugin, in_place_broken_pred))
+    gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
+}
+
+gboolean
+gst_lv2_filter_register_element (GstPlugin * plugin, const gchar * type_name,
+    gpointer * lv2plugin)
+{
+  GType type;
+  GTypeInfo typeinfo = {
+    sizeof (GstLV2FilterClass),
+    (GBaseInitFunc) gst_lv2_filter_base_init,
+    NULL,
+    (GClassInitFunc) gst_lv2_filter_class_init,
+    NULL,
+    lv2plugin,
+    sizeof (GstLV2Filter),
+    0,
+    (GInstanceInitFunc) gst_lv2_filter_init,
+  };
+
+  /* create the type */
+  type =
+      g_type_register_static (GST_TYPE_AUDIO_FILTER, type_name, &typeinfo, 0);
+
+  if (!parent_class)
+    parent_class = g_type_class_ref (GST_TYPE_AUDIO_FILTER);
+
+
+  /* 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, lv2plugin);
+
+  return gst_element_register (plugin, type_name, GST_RANK_NONE, type);
+}