lv2: support for loading presets
authorStefan Sauer <ensonic@users.sf.net>
Wed, 15 Jun 2016 20:22:58 +0000 (22:22 +0200)
committerStefan Sauer <ensonic@users.sf.net>
Mon, 4 Jul 2016 19:18:28 +0000 (21:18 +0200)
Detect if plugins can do presets. Lazily read a list of presets and add support
for loading.

ext/lv2/README
ext/lv2/gstlv2.c
ext/lv2/gstlv2.h
ext/lv2/gstlv2filter.c
ext/lv2/gstlv2source.c
ext/lv2/gstlv2utils.c
ext/lv2/gstlv2utils.h

index 98fab55..289086d 100644 (file)
@@ -34,8 +34,7 @@ gst-launch-1.0 calf-sourceforge-net-plugins-Organ event-in="C-3" name=s ! interl
 
 
 TODO
-* support presets
-  for pl in $(lv2ls); do if test "$(lv2info "$pl" | grep -A1 "Presets:" | tail -n1)" != ""; then echo "$pl"; fi; done
+* make filters gap-aware
 * support more host features
   GST_DEBUG="lv2:4" GST_DEBUG_FILE=/tmp/gst.log gst-inspect lv2
   grep -o "needs host feature: .*$" /tmp/gst.log  | sort | uniq -c | sort -n
index 6ad8330..acad443 100644 (file)
@@ -43,6 +43,8 @@
 #include <gst/audio/audio-channels.h>
 #include <lv2/lv2plug.in/ns/ext/port-groups/port-groups.h>
 #include "lv2/lv2plug.in/ns/ext/event/event.h"
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
 
 GST_DEBUG_CATEGORY (lv2_debug);
 #define GST_CAT_DEFAULT lv2_debug
@@ -71,13 +73,13 @@ lv2_plugin_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
 
 static void
 lv2_count_ports (const LilvPlugin * lv2plugin, guint * audio_in,
-    guint * audio_out)
+    guint * audio_out, guint * control)
 {
   GHashTable *port_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
       g_free, NULL);
   guint i;
 
-  *audio_in = *audio_out = 0;
+  *audio_in = *audio_out = *control = 0;
   for (i = 0; i < lilv_plugin_get_num_ports (lv2plugin); i++) {
     const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, i);
 
@@ -99,6 +101,9 @@ lv2_count_ports (const LilvPlugin * lv2plugin, guint * audio_in,
         (*audio_in)++;
       else
         (*audio_out)++;
+    } else if (lilv_port_is_a (lv2plugin, port, control_class) ||
+        lilv_port_is_a (lv2plugin, port, cv_class)) {
+      (*control)++;
     }
   }
   g_hash_table_unref (port_groups);
@@ -108,7 +113,7 @@ lv2_count_ports (const LilvPlugin * lv2plugin, guint * audio_in,
 static gboolean
 lv2_plugin_discover (GstPlugin * plugin)
 {
-  guint audio_in, audio_out;
+  guint audio_in, audio_out, control;
   LilvIter *i;
   const LilvPlugins *plugins = lilv_world_get_all_plugins (world);
 
@@ -119,6 +124,7 @@ lv2_plugin_discover (GstPlugin * plugin)
     const LilvPlugin *lv2plugin = lilv_plugins_get (plugins, i);
     const gchar *plugin_uri, *p;
     gchar *type_name;
+    gboolean can_do_presets;
 
     plugin_uri = lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin));
 
@@ -142,7 +148,7 @@ lv2_plugin_discover (GstPlugin * plugin)
       goto next;
 
     /* check if this has any audio ports */
-    lv2_count_ports (lv2plugin, &audio_in, &audio_out);
+    lv2_count_ports (lv2plugin, &audio_in, &audio_out, &control);
 
     if (audio_in == 0 && audio_out == 0) {
       GST_FIXME ("plugin %s has no audio pads", type_name);
@@ -165,12 +171,19 @@ lv2_plugin_discover (GstPlugin * plugin)
       }
     }
 
-    lv2_meta = gst_structure_new_empty ("lv2");
-    gst_structure_set (lv2_meta,
+    /* check supported extensions */
+    can_do_presets = lilv_plugin_has_extension_data (lv2plugin, state_iface)
+        || lilv_plugin_has_feature (lv2plugin, state_uri)
+        || (control > 0);
+    GST_INFO ("plugin %s can%s do presets", type_name,
+        (can_do_presets ? "" : "'t"));
+
+    lv2_meta = gst_structure_new ("lv2",
         "element-uri", G_TYPE_STRING, plugin_uri,
         "element-type-name", G_TYPE_STRING, type_name,
         "audio-in", G_TYPE_UINT, audio_in,
-        "audio-out", G_TYPE_UINT, audio_out, NULL);
+        "audio-out", G_TYPE_UINT, audio_out,
+        "can-do-presets", G_TYPE_BOOLEAN, can_do_presets, NULL);
 
     g_value_init (&value, GST_TYPE_STRUCTURE);
     g_value_set_boxed (&value, lv2_meta);
@@ -198,6 +211,7 @@ plugin_init (GstPlugin * plugin)
 
   world = lilv_world_new ();
   lilv_world_load_all (world);
+  gst_lv2_host_init ();
 
 /* have been added after lilv-0.22.0, which is the last release */
 #ifndef LILV_URI_ATOM_PORT
@@ -214,6 +228,9 @@ plugin_init (GstPlugin * plugin)
   event_class = lilv_new_uri (world, LILV_URI_EVENT_PORT);
   input_class = lilv_new_uri (world, LILV_URI_INPUT_PORT);
   output_class = lilv_new_uri (world, LILV_URI_OUTPUT_PORT);
+  preset_class = lilv_new_uri (world, LV2_PRESETS__Preset);
+  state_iface = lilv_new_uri (world, LV2_STATE__interface);
+  state_uri = lilv_new_uri (world, LV2_STATE_URI);
 
   integer_prop = lilv_new_uri (world, LV2_CORE__integer);
   toggled_prop = lilv_new_uri (world, LV2_CORE__toggled);
@@ -222,6 +239,7 @@ plugin_init (GstPlugin * plugin)
   optional_pred = lilv_new_uri (world, LV2_CORE__optionalFeature);
   group_pred = lilv_new_uri (world, LV2_PORT_GROUPS__group);
   supports_event_pred = lilv_new_uri (world, LV2_EVENT__supportsEvent);
+  label_pred = lilv_new_uri (world, LILV_NS_RDFS "label");
 
   center_role = lilv_new_uri (world, LV2_PORT_GROUPS__center);
   left_role = lilv_new_uri (world, LV2_PORT_GROUPS__left);
@@ -296,6 +314,9 @@ __attribute__ ((destructor))
   lilv_node_free (event_class);
   lilv_node_free (input_class);
   lilv_node_free (output_class);
+  lilv_node_free (preset_class);
+  lilv_node_free (state_iface);
+  lilv_node_free (state_uri);
 
   lilv_node_free (integer_prop);
   lilv_node_free (toggled_prop);
@@ -304,6 +325,7 @@ __attribute__ ((destructor))
   lilv_node_free (optional_pred);
   lilv_node_free (group_pred);
   lilv_node_free (supports_event_pred);
+  lilv_node_free (label_pred);
 
   lilv_node_free (center_role);
   lilv_node_free (left_role);
index 2a98885..177f9b2 100644 (file)
@@ -36,6 +36,10 @@ LilvNode *cv_class;
 LilvNode *event_class;
 LilvNode *input_class;
 LilvNode *output_class;
+LilvNode *preset_class;
+LilvNode *state_iface;
+LilvNode *state_uri;
+
 LilvNode *integer_prop;
 LilvNode *toggled_prop;
 LilvNode *designation_pred;
@@ -43,6 +47,7 @@ LilvNode *in_place_broken_pred;
 LilvNode *optional_pred;
 LilvNode *group_pred;
 LilvNode *supports_event_pred;
+LilvNode *label_pred;
 
 LilvNode *center_role;
 LilvNode *left_role;
index dc7ea90..bbdd5f6 100644 (file)
@@ -70,6 +70,73 @@ struct _GstLV2FilterClass
 
 static GstAudioFilter *parent_class = NULL;
 
+/* preset interface */
+
+static gchar **
+gst_lv2_filter_get_preset_names (GstPreset * preset)
+{
+  GstLV2Filter *self = (GstLV2Filter *) preset;
+
+  return gst_lv2_get_preset_names (&self->lv2, (GstObject *) self);
+}
+
+static gboolean
+gst_lv2_filter_load_preset (GstPreset * preset, const gchar * name)
+{
+  GstLV2Filter *self = (GstLV2Filter *) preset;
+
+  return gst_lv2_load_preset (&self->lv2, (GstObject *) self, name);
+}
+
+static gboolean
+gst_lv2_filter_save_preset (GstPreset * preset, const gchar * name)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_filter_rename_preset (GstPreset * preset, const gchar * old_name,
+    const gchar * new_name)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_filter_delete_preset (GstPreset * preset, const gchar * name)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_filter_set_meta (GstPreset * preset, const gchar * name,
+    const gchar * tag, const gchar * value)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_filter_get_meta (GstPreset * preset, const gchar * name,
+    const gchar * tag, gchar ** value)
+{
+  *value = NULL;
+  return FALSE;
+}
+
+static void
+gst_lv2_filter_preset_interface_init (gpointer g_iface, gpointer iface_data)
+{
+  GstPresetInterface *iface = g_iface;
+
+  iface->get_preset_names = gst_lv2_filter_get_preset_names;
+  iface->load_preset = gst_lv2_filter_load_preset;
+  iface->save_preset = gst_lv2_filter_save_preset;
+  iface->rename_preset = gst_lv2_filter_rename_preset;
+  iface->delete_preset = gst_lv2_filter_delete_preset;
+  iface->set_meta = gst_lv2_filter_set_meta;
+  iface->get_meta = gst_lv2_filter_get_meta;
+}
+
+
 /* GObject vmethods implementation */
 static void
 gst_lv2_filter_set_property (GObject * object, guint prop_id,
@@ -472,9 +539,25 @@ gst_lv2_filter_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
     0,
     (GInstanceInitFunc) gst_lv2_filter_init,
   };
+  const gchar *type_name =
+      gst_structure_get_string (lv2_meta, "element-type-name");
+  GType element_type =
+      g_type_register_static (GST_TYPE_AUDIO_FILTER, type_name, &info, 0);
+  gboolean can_do_presets;
+
+  /* register interfaces */
+  gst_structure_get_boolean (lv2_meta, "can-do-presets", &can_do_presets);
+  if (can_do_presets) {
+    const GInterfaceInfo preset_interface_info = {
+      (GInterfaceInitFunc) gst_lv2_filter_preset_interface_init,
+      NULL,
+      NULL
+    };
+    g_type_add_interface_static (element_type, GST_TYPE_PRESET,
+        &preset_interface_info);
+  }
 
-  /* create the type */
-  gst_lv2_register_element (plugin, GST_TYPE_AUDIO_FILTER, &info, lv2_meta);
+  gst_element_register (plugin, type_name, GST_RANK_NONE, element_type);
 
   if (!parent_class)
     parent_class = g_type_class_ref (GST_TYPE_AUDIO_FILTER);
index 3d41c04..92d73e2 100644 (file)
@@ -87,6 +87,73 @@ enum
 
 static GstBaseSrc *parent_class = NULL;
 
+/* preset interface */
+
+static gchar **
+gst_lv2_source_get_preset_names (GstPreset * preset)
+{
+  GstLV2Source *self = (GstLV2Source *) preset;
+
+  return gst_lv2_get_preset_names (&self->lv2, (GstObject *) self);
+}
+
+static gboolean
+gst_lv2_source_load_preset (GstPreset * preset, const gchar * name)
+{
+  GstLV2Source *self = (GstLV2Source *) preset;
+
+  return gst_lv2_load_preset (&self->lv2, (GstObject *) self, name);
+}
+
+static gboolean
+gst_lv2_source_save_preset (GstPreset * preset, const gchar * name)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_source_rename_preset (GstPreset * preset, const gchar * old_name,
+    const gchar * new_name)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_source_delete_preset (GstPreset * preset, const gchar * name)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_source_set_meta (GstPreset * preset, const gchar * name,
+    const gchar * tag, const gchar * value)
+{
+  return FALSE;
+}
+
+static gboolean
+gst_lv2_source_get_meta (GstPreset * preset, const gchar * name,
+    const gchar * tag, gchar ** value)
+{
+  *value = NULL;
+  return FALSE;
+}
+
+static void
+gst_lv2_source_preset_interface_init (gpointer g_iface, gpointer iface_data)
+{
+  GstPresetInterface *iface = g_iface;
+
+  iface->get_preset_names = gst_lv2_source_get_preset_names;
+  iface->load_preset = gst_lv2_source_load_preset;
+  iface->save_preset = gst_lv2_source_save_preset;
+  iface->rename_preset = gst_lv2_source_rename_preset;
+  iface->delete_preset = gst_lv2_source_delete_preset;
+  iface->set_meta = gst_lv2_source_set_meta;
+  iface->get_meta = gst_lv2_source_get_meta;
+}
+
+
 /* GstBasesrc vmethods implementation */
 
 static gboolean
@@ -658,8 +725,26 @@ gst_lv2_source_register_element (GstPlugin * plugin, GstStructure * lv2_meta)
     0,
     (GInstanceInitFunc) gst_lv2_source_init,
   };
+  const gchar *type_name =
+      gst_structure_get_string (lv2_meta, "element-type-name");
+  GType element_type =
+      g_type_register_static (GST_TYPE_BASE_SRC, type_name, &info, 0);
+  gboolean can_do_presets;
+
+  /* register interfaces */
+  gst_structure_get_boolean (lv2_meta, "can-do-presets", &can_do_presets);
+  if (can_do_presets) {
+    const GInterfaceInfo preset_interface_info = {
+      (GInterfaceInitFunc) gst_lv2_source_preset_interface_init,
+      NULL,
+      NULL
+    };
+
+    g_type_add_interface_static (element_type, GST_TYPE_PRESET,
+        &preset_interface_info);
+  }
 
-  gst_lv2_register_element (plugin, GST_TYPE_BASE_SRC, &info, lv2_meta);
+  gst_element_register (plugin, type_name, GST_RANK_NONE, element_type);
 
   if (!parent_class)
     parent_class = g_type_class_ref (GST_TYPE_BASE_SRC);
index 08d9d94..e5b6e8e 100644 (file)
 #include "gstlv2.h"
 #include "gstlv2utils.h"
 
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
 #include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/state/state.h>
 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
 
 GST_DEBUG_CATEGORY_EXTERN (lv2_debug);
@@ -133,6 +136,134 @@ gst_lv2_check_required_features (const LilvPlugin * lv2plugin)
   return TRUE;
 }
 
+static LV2_Atom_Forge forge;
+
+void
+gst_lv2_host_init (void)
+{
+  lv2_atom_forge_init (&forge, &lv2_map);
+}
+
+/* preset interface */
+
+gchar **
+gst_lv2_get_preset_names (GstLV2 * lv2, GstObject * obj)
+{
+  /* lazily scan for presets when first called */
+  if (!lv2->presets) {
+    LilvNodes *presets;
+
+    if ((presets = lilv_plugin_get_related (lv2->klass->plugin, preset_class))) {
+      LilvIter *j;
+
+      lv2->presets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+          (GDestroyNotify) lilv_node_free);
+
+      for (j = lilv_nodes_begin (presets);
+          !lilv_nodes_is_end (presets, j); j = lilv_nodes_next (presets, j)) {
+        const LilvNode *preset = lilv_nodes_get (presets, j);
+        LilvNodes *titles;
+
+        lilv_world_load_resource (world, preset);
+        titles = lilv_world_find_nodes (world, preset, label_pred, NULL);
+        if (titles) {
+          const LilvNode *title = lilv_nodes_get_first (titles);
+          g_hash_table_insert (lv2->presets,
+              g_strdup (lilv_node_as_string (title)),
+              lilv_node_duplicate (preset));
+          lilv_nodes_free (titles);
+        } else {
+          GST_WARNING_OBJECT (obj, "plugin has preset '%s' without rdfs:label",
+              lilv_node_as_string (preset));
+        }
+      }
+      lilv_nodes_free (presets);
+    }
+  }
+  if (lv2->presets) {
+    GList *node, *keys = g_hash_table_get_keys (lv2->presets);
+    gchar **names = g_new0 (gchar *, g_hash_table_size (lv2->presets) + 1);
+    gint i = 0;
+
+    for (node = keys; node; node = g_list_next (node)) {
+      names[i++] = g_strdup (node->data);
+    }
+    g_list_free (keys);
+    return names;
+  }
+  return NULL;
+}
+
+static void
+set_port_value (const char *port_symbol, void *data, const void *value,
+    uint32_t size, uint32_t type)
+{
+  gpointer *user_data = (gpointer *) data;
+  GstLV2Class *klass = user_data[0];
+  GstObject *obj = user_data[1];
+  gchar *prop_name = g_hash_table_lookup (klass->sym_to_name, port_symbol);
+  gfloat fvalue;
+
+  if (!prop_name) {
+    GST_WARNING_OBJECT (obj, "Preset port '%s' is missing", port_symbol);
+    return;
+  }
+
+  if (type == forge.Float) {
+    fvalue = *(const gfloat *) value;
+  } else if (type == forge.Double) {
+    fvalue = *(const gdouble *) value;
+  } else if (type == forge.Int) {
+    fvalue = *(const gint32 *) value;
+  } else if (type == forge.Long) {
+    fvalue = *(const gint64 *) value;
+  } else {
+    GST_WARNING_OBJECT (obj, "Preset '%s' value has bad type '%s'",
+        port_symbol, lv2_unmap.unmap (lv2_unmap.handle, type));
+    return;
+  }
+  g_object_set (obj, prop_name, fvalue, NULL);
+}
+
+
+gboolean
+gst_lv2_load_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
+{
+  LilvNode *preset = g_hash_table_lookup (lv2->presets, name);
+  LilvState *state = lilv_state_new_from_world (world, &lv2_map, preset);
+  gpointer user_data[] = { lv2->klass, obj };
+
+  GST_INFO_OBJECT (obj, "loading preset <%s>", lilv_node_as_string (preset));
+
+  lilv_state_restore (state, lv2->instance, set_port_value,
+      (gpointer) user_data, 0, NULL);
+
+  lilv_state_free (state);
+  return FALSE;
+}
+
+#if 0
+gboolean
+gst_lv2_save_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
+{
+  return FALSE;
+}
+
+gboolean
+gst_lv2_rename_preset (GstLV2 * lv2, GstObject * obj,
+    const gchar * old_name, const gchar * new_name)
+{
+  return FALSE;
+}
+
+gboolean
+gst_lv2_delete_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
+{
+  return FALSE;
+}
+#endif
+
+
 /* api helpers */
 
 void
@@ -150,6 +281,9 @@ gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class)
 void
 gst_lv2_finalize (GstLV2 * lv2)
 {
+  if (lv2->presets) {
+    g_hash_table_destroy (lv2->presets);
+  }
   g_free (lv2->ports.control.in);
   g_free (lv2->ports.control.out);
 }
@@ -301,12 +435,9 @@ gst_lv2_object_get_property (GstLV2 * lv2, GObject * object,
 
 static gchar *
 gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
-    const LilvPort * port)
+    const gchar * port_symbol)
 {
-  const LilvPlugin *lv2plugin = klass->plugin;
-  gchar *ret;
-
-  ret = g_strdup (lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
+  gchar *ret = g_strdup (port_symbol);
 
   /* 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 "-", '-');
@@ -331,9 +462,7 @@ gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
     ret = nret;
   }
 
-  GST_DEBUG ("built property name '%s' from port name '%s'", ret,
-      lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
-
+  GST_DEBUG ("built property name '%s' from port name '%s'", ret, port_symbol);
   return ret;
 }
 
@@ -364,9 +493,11 @@ gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class,
   gint perms;
   gfloat lower = 0.0f, upper = 1.0f, def = 0.0f;
   GType enum_type = G_TYPE_INVALID;
+  const gchar *port_symbol =
+      lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port));
 
   nick = gst_lv2_class_get_param_nick (klass, port);
-  name = gst_lv2_class_get_param_name (klass, object_class, port);
+  name = gst_lv2_class_get_param_name (klass, object_class, port_symbol);
 
   GST_DEBUG ("%s trying port %s : %s",
       lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, nick);
@@ -480,6 +611,10 @@ gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class,
     ret = g_param_spec_float (name, nick, nick, lower, upper, def, perms);
 
 done:
+  // build a map of (port_symbol to ret->name) for extensions
+  g_hash_table_insert (klass->sym_to_name, (gchar *) port_symbol,
+      (gchar *) ret->name);
+
   g_free (name);
   g_free (nick);
 
@@ -572,6 +707,8 @@ gst_lv2_class_init (GstLV2Class * lv2_class, GType type)
   lv2_class->plugin = lv2plugin;
   lilv_node_free (plugin_uri);
 
+  lv2_class->sym_to_name = g_hash_table_new (g_str_hash, g_str_equal);
+
   lv2_class->in_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
   lv2_class->out_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
   lv2_class->control_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
@@ -692,6 +829,8 @@ gst_lv2_class_finalize (GstLV2Class * lv2_class)
 {
   GST_DEBUG ("LV2 finalizing class");
 
+  g_hash_table_destroy (lv2_class->sym_to_name);
+
   g_array_free (lv2_class->in_group.ports, TRUE);
   lv2_class->in_group.ports = NULL;
   g_array_free (lv2_class->out_group.ports, TRUE);
@@ -701,14 +840,3 @@ gst_lv2_class_finalize (GstLV2Class * lv2_class)
   g_array_free (lv2_class->control_out_ports, TRUE);
   lv2_class->control_out_ports = NULL;
 }
-
-void
-gst_lv2_register_element (GstPlugin * plugin, GType parent_type,
-    const GTypeInfo * info, GstStructure * lv2_meta)
-{
-  const gchar *type_name =
-      gst_structure_get_string (lv2_meta, "element-type-name");
-
-  gst_element_register (plugin, type_name, GST_RANK_NONE,
-      g_type_register_static (parent_type, type_name, info, 0));
-}
index e5213b2..815bb11 100644 (file)
@@ -69,6 +69,7 @@ struct _GstLV2
   GstLV2Class *klass;
 
   LilvInstance *instance;
+  GHashTable *presets;
 
   gboolean activated;
   unsigned long rate;
@@ -88,6 +89,7 @@ struct _GstLV2Class
   guint properties;
 
   const LilvPlugin *plugin;
+  GHashTable *sym_to_name;
 
   gint num_control_in, num_control_out;
   gint num_cv_in, num_cv_out;
@@ -98,7 +100,12 @@ struct _GstLV2Class
   GArray *control_out_ports; /**< Array of GstLV2Port */
 };
 
-gboolean gst_lv2_check_required_features (const LilvPlugin *lv2plugin);
+gboolean gst_lv2_check_required_features (const LilvPlugin * lv2plugin);
+
+void gst_lv2_host_init (void);
+
+gchar **gst_lv2_get_preset_names (GstLV2 * lv2, GstObject * obj);
+gboolean gst_lv2_load_preset (GstLV2 * lv2, GstObject * obj, const gchar * name);
 
 void gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class);
 void gst_lv2_finalize (GstLV2 * lv2);
@@ -119,8 +126,6 @@ void gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
 void gst_lv2_class_init (GstLV2Class * lv2_class, GType type);
 void gst_lv2_class_finalize (GstLV2Class * lv2_class);
 
-void gst_lv2_register_element (GstPlugin * plugin, GType parent_type,
-    const GTypeInfo * info, GstStructure * lv2_meta);
 G_END_DECLS
 
 #endif /* __GST_LV2_UTILS_H__ */