lv2: add support for saving presets
authorStefan Sauer <ensonic@users.sf.net>
Sat, 2 Jul 2016 08:24:51 +0000 (10:24 +0200)
committerStefan Sauer <ensonic@users.sf.net>
Mon, 4 Jul 2016 19:18:28 +0000 (21:18 +0200)
Plugns supporting the state interface can now save their presets under '.lv2'.

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

index bbdd5f6..0ef6ade 100644 (file)
@@ -91,7 +91,9 @@ gst_lv2_filter_load_preset (GstPreset * preset, const gchar * name)
 static gboolean
 gst_lv2_filter_save_preset (GstPreset * preset, const gchar * name)
 {
-  return FALSE;
+  GstLV2Filter *self = (GstLV2Filter *) preset;
+
+  return gst_lv2_save_preset (&self->lv2, (GstObject *) self, name);
 }
 
 static gboolean
@@ -104,7 +106,9 @@ gst_lv2_filter_rename_preset (GstPreset * preset, const gchar * old_name,
 static gboolean
 gst_lv2_filter_delete_preset (GstPreset * preset, const gchar * name)
 {
-  return FALSE;
+  GstLV2Filter *self = (GstLV2Filter *) preset;
+
+  return gst_lv2_delete_preset (&self->lv2, (GstObject *) self, name);
 }
 
 static gboolean
index 92d73e2..56f9911 100644 (file)
@@ -108,7 +108,9 @@ gst_lv2_source_load_preset (GstPreset * preset, const gchar * name)
 static gboolean
 gst_lv2_source_save_preset (GstPreset * preset, const gchar * name)
 {
-  return FALSE;
+  GstLV2Source *self = (GstLV2Source *) preset;
+
+  return gst_lv2_save_preset (&self->lv2, (GstObject *) self, name);
 }
 
 static gboolean
@@ -121,7 +123,9 @@ gst_lv2_source_rename_preset (GstPreset * preset, const gchar * old_name,
 static gboolean
 gst_lv2_source_delete_preset (GstPreset * preset, const gchar * name)
 {
-  return FALSE;
+  GstLV2Source *self = (GstLV2Source *) preset;
+
+  return gst_lv2_delete_preset (&self->lv2, (GstObject *) self, name);
 }
 
 static gboolean
index e5b6e8e..017d449 100644 (file)
@@ -225,7 +225,6 @@ set_port_value (const char *port_symbol, void *data, const void *value,
   g_object_set (obj, prop_name, fvalue, NULL);
 }
 
-
 gboolean
 gst_lv2_load_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
 {
@@ -242,26 +241,120 @@ gst_lv2_load_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
   return FALSE;
 }
 
-#if 0
+static const void *
+get_port_value (const char *port_symbol, void *data, 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);
+  static gfloat fvalue;
+
+  if (!prop_name) {
+    GST_WARNING_OBJECT (obj, "Preset port '%s' is missing", port_symbol);
+    *size = *type = 0;
+    return NULL;
+  }
+
+  *size = sizeof (float);
+  *type = forge.Float;
+  g_object_get (obj, prop_name, &fvalue, NULL);
+  /* FIXME: can we return &lv2->ports.{in,out}[x]; */
+  return &fvalue;
+}
+
 gboolean
 gst_lv2_save_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
 {
-  return FALSE;
+  gchar *filename, *basename, *bundle, *dir, *tmp_dir, *s;
+  gpointer user_data[] = { lv2->klass, obj };
+  GstElementFactory *factory;
+  LilvState *state;
+  LilvNode *bundle_dir;
+  LilvInstance *instance = lv2->instance;
+  gboolean res;
+
+  factory = gst_element_get_factory ((GstElement *) obj);
+  basename = g_strdup (gst_element_factory_get_metadata (factory,
+          GST_ELEMENT_METADATA_LONGNAME));
+  s = basename;
+  while ((s = strchr (s, ' '))) {
+    *s = '_';
+  }
+
+  filename = g_strjoin (NULL, name, ".ttl", NULL);
+  bundle = g_strjoin (NULL, basename, "_", name, ".preset.lv2", NULL);
+  /* dir needs to end on a dir separator for the lilv_new_file_uri() to work */
+  dir =
+      g_build_filename (g_get_home_dir (), ".lv2", bundle, G_DIR_SEPARATOR_S,
+      NULL);
+  tmp_dir = g_dir_make_tmp ("gstlv2-XXXXXX", NULL);
+  g_mkdir_with_parents (dir, 0750);
+
+  if (!instance) {
+    /* instance is NULL until we play!! */
+    instance = lilv_plugin_instantiate (lv2->klass->plugin, GST_AUDIO_DEF_RATE,
+        lv2_features);
+  }
+
+  state = lilv_state_new_from_instance (lv2->klass->plugin, instance, &lv2_map,
+      tmp_dir, dir, dir, dir, get_port_value, user_data,
+      LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, NULL);
+
+  lilv_state_set_label (state, name);
+
+  res = lilv_state_save (world, &lv2_map, &lv2_unmap, state, /*uri */ NULL, dir,
+      filename) != 0;
+
+  /* reload bundle into the world */
+  bundle_dir = lilv_new_file_uri (world, NULL, dir);
+  lilv_world_unload_bundle (world, bundle_dir);
+  lilv_world_load_bundle (world, bundle_dir);
+  lilv_node_free (bundle_dir);
+
+  lilv_world_load_resource (world, lilv_state_get_uri (state));
+  g_hash_table_insert (lv2->presets, g_strdup (name),
+      lilv_node_duplicate (lilv_state_get_uri (state)));
+
+  lilv_state_free (state);
+  if (!lv2->instance) {
+    lilv_instance_free (instance);
+  }
+
+  g_free (tmp_dir);
+  g_free (dir);
+  g_free (bundle);
+  g_free (filename);
+  g_free (basename);
+
+  return res;
 }
 
+#if 0
 gboolean
 gst_lv2_rename_preset (GstLV2 * lv2, GstObject * obj,
     const gchar * old_name, const gchar * new_name)
 {
+  /* need to relabel the preset */
   return FALSE;
 }
+#endif
 
 gboolean
 gst_lv2_delete_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);
+
+  lilv_world_unload_resource (world, lilv_state_get_uri (state));
+  lilv_state_delete (world, state);
+  lilv_state_free (state);
+
+  g_hash_table_remove (lv2->presets, name);
+
   return FALSE;
 }
-#endif
 
 
 /* api helpers */
index 815bb11..b179e36 100644 (file)
@@ -106,6 +106,9 @@ 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);
+gboolean gst_lv2_save_preset (GstLV2 * lv2, GstObject * obj, const gchar * name);
+gboolean gst_lv2_delete_preset (GstLV2 * lv2, GstObject * obj, const gchar * name);
+
 
 void gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class);
 void gst_lv2_finalize (GstLV2 * lv2);