documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / ext / lv2 / gstlv2utils.c
1 /* GStreamer
2  * Copyright (C) 2016 Thibault Saunier <thibault.saunier@collabora.com>
3  *               2016 Stefan Sauer <ensonic@users.sf.net>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "string.h"
26 #include "stdlib.h"
27
28 #include "gstlv2.h"
29 #include "gstlv2utils.h"
30
31 #include "lv2/lv2plug.in/ns/ext/atom/atom.h"
32 #include "lv2/lv2plug.in/ns/ext/atom/forge.h"
33 #include <lv2/lv2plug.in/ns/ext/log/log.h>
34 #include <lv2/lv2plug.in/ns/ext/state/state.h>
35 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
36
37 GST_DEBUG_CATEGORY_EXTERN (lv2_debug);
38 #define GST_CAT_DEFAULT lv2_debug
39
40 /* host features */
41
42 /* - log extension */
43
44 #ifndef GST_DISABLE_GST_DEBUG
45 static int
46 lv2_log_printf (LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...)
47 {
48   va_list ap;
49
50   va_start (ap, fmt);
51   gst_debug_log_valist (lv2_debug, GST_LEVEL_INFO, "", "", 0, NULL, fmt, ap);
52   va_end (ap);
53   return 1;
54 }
55
56 static int
57 lv2_log_vprintf (LV2_Log_Handle handle, LV2_URID type,
58     const char *fmt, va_list ap)
59 {
60   gst_debug_log_valist (lv2_debug, GST_LEVEL_INFO, "", "", 0, NULL, fmt, ap);
61   return 1;
62 }
63
64 static LV2_Log_Log lv2_log = {
65   /* handle = */ NULL, lv2_log_printf, lv2_log_vprintf
66 };
67
68
69 static const LV2_Feature lv2_log_feature = { LV2_LOG__log, &lv2_log };
70 #endif
71
72 /* - urid map/unmap extension */
73
74 static LV2_URID
75 lv2_urid_map (LV2_URID_Map_Handle handle, const char *uri)
76 {
77   return (LV2_URID) g_quark_from_string (uri);
78 }
79
80 static const char *
81 lv2_urid_unmap (LV2_URID_Unmap_Handle handle, LV2_URID urid)
82 {
83   return g_quark_to_string ((GQuark) urid);
84 }
85
86 static LV2_URID_Map lv2_map = {
87   /* handle = */ NULL, lv2_urid_map
88 };
89
90 static LV2_URID_Unmap lv2_unmap = {
91   /* handle = */ NULL, lv2_urid_unmap
92 };
93
94 static const LV2_Feature lv2_map_feature = { LV2_URID__map, &lv2_map };
95 static const LV2_Feature lv2_unmap_feature = { LV2_URID__unmap, &lv2_unmap };
96
97 /* feature list */
98
99 static const LV2_Feature *lv2_features[] = {
100 #ifndef GST_DISABLE_GST_DEBUG
101   &lv2_log_feature,
102 #endif
103   &lv2_map_feature,
104   &lv2_unmap_feature,
105   NULL
106 };
107
108 gboolean
109 gst_lv2_check_required_features (const LilvPlugin * lv2plugin)
110 {
111   LilvNodes *required_features = lilv_plugin_get_required_features (lv2plugin);
112   if (required_features) {
113     LilvIter *i;
114     gint j;
115     gboolean missing = FALSE;
116
117     for (i = lilv_nodes_begin (required_features);
118         !lilv_nodes_is_end (required_features, i);
119         i = lilv_nodes_next (required_features, i)) {
120       const LilvNode *required_feature = lilv_nodes_get (required_features, i);
121       const char *required_feature_uri = lilv_node_as_uri (required_feature);
122       missing = TRUE;
123
124       for (j = 0; lv2_features[j]; j++) {
125         if (!strcmp (lv2_features[j]->URI, required_feature_uri)) {
126           missing = FALSE;
127           break;
128         }
129       }
130       if (missing) {
131         GST_FIXME ("lv2 plugin %s needs host feature: %s",
132             lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin)),
133             required_feature_uri);
134         break;
135       }
136     }
137     lilv_nodes_free (required_features);
138     return (!missing);
139   }
140   return TRUE;
141 }
142
143 static LV2_Atom_Forge forge;
144
145 void
146 gst_lv2_host_init (void)
147 {
148   lv2_atom_forge_init (&forge, &lv2_map);
149 }
150
151 /* preset interface */
152
153 static char *
154 make_bundle_name (GstObject * obj, const gchar * name)
155 {
156   GstElementFactory *factory;
157   gchar *basename, *s, *bundle;
158
159   factory = gst_element_get_factory ((GstElement *) obj);
160   basename = g_strdup (gst_element_factory_get_metadata (factory,
161           GST_ELEMENT_METADATA_LONGNAME));
162   s = basename;
163   while ((s = strchr (s, ' '))) {
164     *s = '_';
165   }
166   bundle = g_strjoin (NULL, basename, "_", name, ".preset.lv2", NULL);
167
168   g_free (basename);
169
170   return bundle;
171 }
172
173 gchar **
174 gst_lv2_get_preset_names (GstLV2 * lv2, GstObject * obj)
175 {
176   /* lazily scan for presets when first called */
177   if (!lv2->presets) {
178     LilvNodes *presets;
179
180     if ((presets = lilv_plugin_get_related (lv2->klass->plugin, preset_class))) {
181       LilvIter *j;
182
183       lv2->presets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
184           (GDestroyNotify) lilv_node_free);
185
186       for (j = lilv_nodes_begin (presets);
187           !lilv_nodes_is_end (presets, j); j = lilv_nodes_next (presets, j)) {
188         const LilvNode *preset = lilv_nodes_get (presets, j);
189         LilvNodes *titles;
190
191         lilv_world_load_resource (world, preset);
192         titles = lilv_world_find_nodes (world, preset, label_pred, NULL);
193         if (titles) {
194           const LilvNode *title = lilv_nodes_get_first (titles);
195           g_hash_table_insert (lv2->presets,
196               g_strdup (lilv_node_as_string (title)),
197               lilv_node_duplicate (preset));
198           lilv_nodes_free (titles);
199         } else {
200           GST_WARNING_OBJECT (obj, "plugin has preset '%s' without rdfs:label",
201               lilv_node_as_string (preset));
202         }
203       }
204       lilv_nodes_free (presets);
205     }
206   }
207   if (lv2->presets) {
208     GList *node, *keys = g_hash_table_get_keys (lv2->presets);
209     gchar **names = g_new0 (gchar *, g_hash_table_size (lv2->presets) + 1);
210     gint i = 0;
211
212     for (node = keys; node; node = g_list_next (node)) {
213       names[i++] = g_strdup (node->data);
214     }
215     g_list_free (keys);
216     return names;
217   }
218   return NULL;
219 }
220
221 static void
222 set_port_value (const char *port_symbol, void *data, const void *value,
223     uint32_t size, uint32_t type)
224 {
225   gpointer *user_data = (gpointer *) data;
226   GstLV2Class *klass = user_data[0];
227   GstObject *obj = user_data[1];
228   gchar *prop_name = g_hash_table_lookup (klass->sym_to_name, port_symbol);
229   gfloat fvalue;
230
231   if (!prop_name) {
232     GST_WARNING_OBJECT (obj, "Preset port '%s' is missing", port_symbol);
233     return;
234   }
235
236   if (type == forge.Float) {
237     fvalue = *(const gfloat *) value;
238   } else if (type == forge.Double) {
239     fvalue = *(const gdouble *) value;
240   } else if (type == forge.Int) {
241     fvalue = *(const gint32 *) value;
242   } else if (type == forge.Long) {
243     fvalue = *(const gint64 *) value;
244   } else {
245     GST_WARNING_OBJECT (obj, "Preset '%s' value has bad type '%s'",
246         port_symbol, lv2_unmap.unmap (lv2_unmap.handle, type));
247     return;
248   }
249   g_object_set (obj, prop_name, fvalue, NULL);
250 }
251
252 gboolean
253 gst_lv2_load_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
254 {
255   LilvNode *preset = g_hash_table_lookup (lv2->presets, name);
256   LilvState *state = lilv_state_new_from_world (world, &lv2_map, preset);
257   gpointer user_data[] = { lv2->klass, obj };
258
259   GST_INFO_OBJECT (obj, "loading preset <%s>", lilv_node_as_string (preset));
260
261   lilv_state_restore (state, lv2->instance, set_port_value,
262       (gpointer) user_data, 0, NULL);
263
264   lilv_state_free (state);
265   return FALSE;
266 }
267
268 static const void *
269 get_port_value (const char *port_symbol, void *data, uint32_t * size,
270     uint32_t * type)
271 {
272   gpointer *user_data = (gpointer *) data;
273   GstLV2Class *klass = user_data[0];
274   GstObject *obj = user_data[1];
275   gchar *prop_name = g_hash_table_lookup (klass->sym_to_name, port_symbol);
276   static gfloat fvalue;
277
278   if (!prop_name) {
279     GST_WARNING_OBJECT (obj, "Preset port '%s' is missing", port_symbol);
280     *size = *type = 0;
281     return NULL;
282   }
283
284   *size = sizeof (float);
285   *type = forge.Float;
286   g_object_get (obj, prop_name, &fvalue, NULL);
287   /* FIXME: can we return &lv2->ports.{in,out}[x]; */
288   return &fvalue;
289 }
290
291 gboolean
292 gst_lv2_save_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
293 {
294   gchar *filename, *bundle, *dir, *tmp_dir;
295   gpointer user_data[] = { lv2->klass, obj };
296   LilvState *state;
297   LilvNode *bundle_dir;
298   const LilvNode *state_uri;
299   LilvInstance *instance = lv2->instance;
300   gboolean res;
301 #ifndef HAVE_LILV_0_22
302   gchar *filepath;
303 #endif
304
305   filename = g_strjoin (NULL, name, ".ttl", NULL);
306   bundle = make_bundle_name (obj, name);
307   /* dir needs to end on a dir separator for the lilv_new_file_uri() to work */
308   dir =
309       g_build_filename (g_get_home_dir (), ".lv2", bundle, G_DIR_SEPARATOR_S,
310       NULL);
311   tmp_dir = g_dir_make_tmp ("gstlv2-XXXXXX", NULL);
312   g_mkdir_with_parents (dir, 0750);
313
314   if (!instance) {
315     /* instance is NULL until we play!! */
316     instance = lilv_plugin_instantiate (lv2->klass->plugin, GST_AUDIO_DEF_RATE,
317         lv2_features);
318   }
319
320   state = lilv_state_new_from_instance (lv2->klass->plugin, instance, &lv2_map,
321       tmp_dir, dir, dir, dir, get_port_value, user_data,
322       LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, NULL);
323
324   lilv_state_set_label (state, name);
325
326   res = lilv_state_save (world, &lv2_map, &lv2_unmap, state, /*uri */ NULL, dir,
327       filename) != 0;
328
329   /* reload bundle into the world */
330   bundle_dir = lilv_new_file_uri (world, NULL, dir);
331   lilv_world_unload_bundle (world, bundle_dir);
332   lilv_world_load_bundle (world, bundle_dir);
333   lilv_node_free (bundle_dir);
334
335 #ifdef HAVE_LILV_0_22
336   state_uri = lilv_state_get_uri (state);
337 #else
338   filepath = g_build_filename (dir, filename, NULL);
339   state_uri = lilv_new_uri (world, filepath);
340   g_free (filepath);
341 #endif
342   lilv_world_load_resource (world, state_uri);
343   g_hash_table_insert (lv2->presets, g_strdup (name),
344       lilv_node_duplicate (state_uri));
345 #ifndef HAVE_LILV_0_22
346   lilv_node_free ((LilvNode *) state_uri);
347 #endif
348
349   lilv_state_free (state);
350   if (!lv2->instance) {
351     lilv_instance_free (instance);
352   }
353
354   g_free (tmp_dir);
355   g_free (dir);
356   g_free (bundle);
357   g_free (filename);
358
359   return res;
360 }
361
362 #if 0
363 gboolean
364 gst_lv2_rename_preset (GstLV2 * lv2, GstObject * obj,
365     const gchar * old_name, const gchar * new_name)
366 {
367   /* need to relabel the preset */
368   return FALSE;
369 }
370 #endif
371
372 gboolean
373 gst_lv2_delete_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
374 {
375 #ifdef HAVE_LILV_0_22
376   LilvNode *preset = g_hash_table_lookup (lv2->presets, name);
377   LilvState *state = lilv_state_new_from_world (world, &lv2_map, preset);
378
379   lilv_world_unload_resource (world, lilv_state_get_uri (state));
380   lilv_state_delete (world, state);
381   lilv_state_free (state);
382 #endif
383   g_hash_table_remove (lv2->presets, name);
384
385   return FALSE;
386 }
387
388 /* api helpers */
389
390 void
391 gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class)
392 {
393   lv2->klass = lv2_class;
394
395   lv2->instance = NULL;
396   lv2->activated = FALSE;
397
398   lv2->ports.control.in = g_new0 (gfloat, lv2_class->control_in_ports->len);
399   lv2->ports.control.out = g_new0 (gfloat, lv2_class->control_out_ports->len);
400 }
401
402 void
403 gst_lv2_finalize (GstLV2 * lv2)
404 {
405   if (lv2->presets) {
406     g_hash_table_destroy (lv2->presets);
407   }
408   g_free (lv2->ports.control.in);
409   g_free (lv2->ports.control.out);
410 }
411
412 gboolean
413 gst_lv2_setup (GstLV2 * lv2, unsigned long rate)
414 {
415   GstLV2Class *lv2_class = lv2->klass;
416   GstLV2Port *port;
417   GArray *ports;
418   gint i;
419
420   if (lv2->instance)
421     lilv_instance_free (lv2->instance);
422
423   if (!(lv2->instance =
424           lilv_plugin_instantiate (lv2_class->plugin, rate, lv2_features)))
425     return FALSE;
426
427   /* connect the control ports */
428   ports = lv2_class->control_in_ports;
429   for (i = 0; i < ports->len; i++) {
430     port = &g_array_index (ports, GstLV2Port, i);
431     if (port->type != GST_LV2_PORT_CONTROL)
432       continue;
433     lilv_instance_connect_port (lv2->instance, port->index,
434         &(lv2->ports.control.in[i]));
435   }
436   ports = lv2_class->control_out_ports;
437   for (i = 0; i < ports->len; i++) {
438     port = &g_array_index (ports, GstLV2Port, i);
439     if (port->type != GST_LV2_PORT_CONTROL)
440       continue;
441     lilv_instance_connect_port (lv2->instance, port->index,
442         &(lv2->ports.control.out[i]));
443   }
444
445   lilv_instance_activate (lv2->instance);
446   lv2->activated = TRUE;
447
448   return TRUE;
449 }
450
451 gboolean
452 gst_lv2_cleanup (GstLV2 * lv2, GstObject * obj)
453 {
454   if (lv2->activated == FALSE) {
455     GST_ERROR_OBJECT (obj, "Deactivating but LV2 plugin not activated");
456     return TRUE;
457   }
458
459   if (lv2->instance == NULL) {
460     GST_ERROR_OBJECT (obj, "Deactivating but no LV2 plugin set");
461     return TRUE;
462   }
463
464   GST_DEBUG_OBJECT (obj, "deactivating");
465
466   lilv_instance_deactivate (lv2->instance);
467
468   lv2->activated = FALSE;
469
470   lilv_instance_free (lv2->instance);
471   lv2->instance = NULL;
472
473   return TRUE;
474 }
475
476 void
477 gst_lv2_object_set_property (GstLV2 * lv2, GObject * object,
478     guint prop_id, const GValue * value, GParamSpec * pspec)
479 {
480   GType base, type = pspec->value_type;
481   /* remember, properties have an offset */
482   prop_id -= lv2->klass->properties;
483
484   /* only input ports */
485   g_return_if_fail (prop_id < lv2->klass->control_in_ports->len);
486
487   while ((base = g_type_parent (type)))
488     type = base;
489
490   /* now see what type it is */
491   switch (type) {
492     case G_TYPE_BOOLEAN:
493       lv2->ports.control.in[prop_id] =
494           g_value_get_boolean (value) ? 1.0f : 0.0f;
495       break;
496     case G_TYPE_INT:
497       lv2->ports.control.in[prop_id] = g_value_get_int (value);
498       break;
499     case G_TYPE_FLOAT:
500       lv2->ports.control.in[prop_id] = g_value_get_float (value);
501       break;
502     case G_TYPE_ENUM:
503       lv2->ports.control.in[prop_id] = g_value_get_enum (value);
504       break;
505     default:
506       GST_WARNING_OBJECT (object, "unhandled type: %s",
507           g_type_name (pspec->value_type));
508       g_assert_not_reached ();
509   }
510 }
511
512 void
513 gst_lv2_object_get_property (GstLV2 * lv2, GObject * object,
514     guint prop_id, GValue * value, GParamSpec * pspec)
515 {
516   GType base, type = pspec->value_type;
517   gfloat *controls;
518
519   /* remember, properties have an offset */
520   prop_id -= lv2->klass->properties;
521
522   if (prop_id < lv2->klass->control_in_ports->len) {
523     controls = lv2->ports.control.in;
524   } else if (prop_id < lv2->klass->control_in_ports->len +
525       lv2->klass->control_out_ports->len) {
526     controls = lv2->ports.control.out;
527     prop_id -= lv2->klass->control_in_ports->len;
528   } else {
529     g_return_if_reached ();
530   }
531
532   while ((base = g_type_parent (type)))
533     type = base;
534
535   /* now see what type it is */
536   switch (type) {
537     case G_TYPE_BOOLEAN:
538       g_value_set_boolean (value, controls[prop_id] > 0.0f);
539       break;
540     case G_TYPE_INT:
541       g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT));
542       break;
543     case G_TYPE_FLOAT:
544       g_value_set_float (value, controls[prop_id]);
545       break;
546     case G_TYPE_ENUM:
547       g_value_set_enum (value, (gint) controls[prop_id]);
548       break;
549     default:
550       GST_WARNING_OBJECT (object, "unhandled type: %s",
551           g_type_name (pspec->value_type));
552       g_return_if_reached ();
553   }
554 }
555
556
557 static gchar *
558 gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
559     const gchar * port_symbol)
560 {
561   gchar *ret = g_strdup (port_symbol);
562
563   /* this is the same thing that param_spec_* will do */
564   g_strcanon (ret, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
565   /* satisfy glib2 (argname[0] must be [A-Za-z]) */
566   if (!((ret[0] >= 'a' && ret[0] <= 'z') || (ret[0] >= 'A' && ret[0] <= 'Z'))) {
567     gchar *tempstr = ret;
568
569     ret = g_strconcat ("param-", ret, NULL);
570     g_free (tempstr);
571   }
572
573   /* check for duplicate property names */
574   if (g_object_class_find_property (object_class, ret)) {
575     gint n = 1;
576     gchar *nret = g_strdup_printf ("%s-%d", ret, n++);
577
578     while (g_object_class_find_property (object_class, nret)) {
579       g_free (nret);
580       nret = g_strdup_printf ("%s-%d", ret, n++);
581     }
582     g_free (ret);
583     ret = nret;
584   }
585
586   GST_DEBUG ("built property name '%s' from port name '%s'", ret, port_symbol);
587   return ret;
588 }
589
590 static gchar *
591 gst_lv2_class_get_param_nick (GstLV2Class * klass, const LilvPort * port)
592 {
593   const LilvPlugin *lv2plugin = klass->plugin;
594
595   return g_strdup (lilv_node_as_string (lilv_port_get_name (lv2plugin, port)));
596 }
597
598 static int
599 enum_val_cmp (GEnumValue * p1, GEnumValue * p2)
600 {
601   return p1->value - p2->value;
602 }
603
604 static GParamSpec *
605 gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class,
606     gint portnum)
607 {
608   const LilvPlugin *lv2plugin = klass->plugin;
609   const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, portnum);
610   LilvNode *lv2def, *lv2min, *lv2max;
611   LilvScalePoints *points;
612   GParamSpec *ret;
613   gchar *name, *nick;
614   gint perms;
615   gfloat lower = 0.0f, upper = 1.0f, def = 0.0f;
616   GType enum_type = G_TYPE_INVALID;
617   const gchar *port_symbol =
618       lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port));
619
620   nick = gst_lv2_class_get_param_nick (klass, port);
621   name = gst_lv2_class_get_param_name (klass, object_class, port_symbol);
622
623   GST_DEBUG ("%s trying port %s : %s",
624       lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, nick);
625
626   perms = G_PARAM_READABLE;
627   if (lilv_port_is_a (lv2plugin, port, input_class))
628     perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
629   if (lilv_port_is_a (lv2plugin, port, control_class) ||
630       lilv_port_is_a (lv2plugin, port, cv_class))
631     perms |= GST_PARAM_CONTROLLABLE;
632
633   if (lilv_port_has_property (lv2plugin, port, toggled_prop)) {
634     ret = g_param_spec_boolean (name, nick, nick, FALSE, perms);
635     goto done;
636   }
637
638   lilv_port_get_range (lv2plugin, port, &lv2def, &lv2min, &lv2max);
639
640   if (lv2def)
641     def = lilv_node_as_float (lv2def);
642   if (lv2min)
643     lower = lilv_node_as_float (lv2min);
644   if (lv2max)
645     upper = lilv_node_as_float (lv2max);
646
647   lilv_node_free (lv2def);
648   lilv_node_free (lv2min);
649   lilv_node_free (lv2max);
650
651   if (def < lower) {
652     if (lv2def && lv2min) {
653       GST_WARNING ("%s:%s has lower bound %f > default %f",
654           lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, lower,
655           def);
656     }
657     lower = def;
658   }
659
660   if (def > upper) {
661     if (lv2def && lv2max) {
662       GST_WARNING ("%s:%s has upper bound %f < default %f",
663           lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, upper,
664           def);
665     }
666     upper = def;
667   }
668
669   if ((points = lilv_port_get_scale_points (lv2plugin, port))) {
670     GEnumValue *enums;
671     LilvIter *i;
672     gint j = 0, n, def_ix = -1;
673
674     n = lilv_scale_points_size (points);
675     enums = g_new (GEnumValue, n + 1);
676
677     for (i = lilv_scale_points_begin (points);
678         !lilv_scale_points_is_end (points, i);
679         i = lilv_scale_points_next (points, i)) {
680       const LilvScalePoint *point = lilv_scale_points_get (points, i);
681       gfloat v = lilv_node_as_float (lilv_scale_point_get_value (point));
682       const gchar *l = lilv_node_as_string (lilv_scale_point_get_label (point));
683
684       /* check if value can be safely converted to int */
685       if (v != (gint) v) {
686         GST_INFO ("%s:%s non integer scale point %lf, %s",
687             lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, v, l);
688         break;
689       }
690       if (v == def) {
691         def_ix = j;
692       }
693       enums[j].value = (gint) v;
694       enums[j].value_nick = enums[j].value_name = l;
695       GST_LOG ("%s:%s enum: %lf, %s",
696           lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, v, l);
697       j++;
698     }
699     if (j == n) {
700       gchar *type_name;
701
702       /* scalepoints are not sorted */
703       qsort (enums, n, sizeof (GEnumValue),
704           (int (*)(const void *, const void *)) enum_val_cmp);
705
706       if (def_ix == -1) {
707         if (lv2def) {
708           GST_WARNING ("%s:%s has default %f outside of scalepoints",
709               lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, def);
710         }
711         def = enums[0].value;
712       }
713       /* terminator */
714       enums[j].value = 0;
715       enums[j].value_name = enums[j].value_nick = NULL;
716
717       type_name = g_strdup_printf ("%s%s",
718           g_type_name (G_TYPE_FROM_CLASS (object_class)), name);
719       enum_type = g_enum_register_static (type_name, enums);
720       g_free (type_name);
721     } else {
722       g_free (enums);
723     }
724     lilv_scale_points_free (points);
725   }
726
727   if (enum_type != G_TYPE_INVALID) {
728     ret = g_param_spec_enum (name, nick, nick, enum_type, def, perms);
729   } else if (lilv_port_has_property (lv2plugin, port, integer_prop))
730     ret = g_param_spec_int (name, nick, nick, lower, upper, def, perms);
731   else
732     ret = g_param_spec_float (name, nick, nick, lower, upper, def, perms);
733
734 done:
735   // build a map of (port_symbol to ret->name) for extensions
736   g_hash_table_insert (klass->sym_to_name, (gchar *) port_symbol,
737       (gchar *) ret->name);
738
739   g_free (name);
740   g_free (nick);
741
742   return ret;
743 }
744
745 void
746 gst_lv2_class_install_properties (GstLV2Class * lv2_class,
747     GObjectClass * object_class, guint offset)
748 {
749   GParamSpec *p;
750   guint i;
751
752   lv2_class->properties = offset;
753
754   for (i = 0; i < lv2_class->control_in_ports->len; i++, offset++) {
755     p = gst_lv2_class_get_param_spec (lv2_class, object_class,
756         g_array_index (lv2_class->control_in_ports, GstLV2Port, i).index);
757
758     g_object_class_install_property (object_class, offset, p);
759   }
760
761   for (i = 0; i < lv2_class->control_out_ports->len; i++, offset++) {
762     p = gst_lv2_class_get_param_spec (lv2_class, object_class,
763         g_array_index (lv2_class->control_out_ports, GstLV2Port, i).index);
764
765     g_object_class_install_property (object_class, offset, p);
766   }
767 }
768
769 void
770 gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
771     GstElementClass * elem_class, const gchar * lv2_class_tags)
772 {
773   const LilvPlugin *lv2plugin = lv2_class->plugin;
774   LilvNode *val;
775   const LilvPluginClass *lv2plugin_class;
776   const LilvNode *cval;
777   gchar *longname, *author, *class_tags = NULL;
778
779   val = lilv_plugin_get_name (lv2plugin);
780   if (val) {
781     longname = g_strdup (lilv_node_as_string (val));
782     lilv_node_free (val);
783   } else {
784     longname = g_strdup ("no description available");
785   }
786   val = lilv_plugin_get_author_name (lv2plugin);
787   if (val) {
788     // TODO: check lilv_plugin_get_author_email(lv2plugin);
789     author = g_strdup (lilv_node_as_string (val));
790     lilv_node_free (val);
791   } else {
792     author = g_strdup ("no author available");
793   }
794
795   // TODO: better description from:
796   // lilv_plugin_get_author_homepage() and lilv_plugin_get_project()
797
798   lv2plugin_class = lilv_plugin_get_class (lv2plugin);
799   cval = lilv_plugin_class_get_label (lv2plugin_class);
800   if (cval) {
801     class_tags = g_strconcat (lv2_class_tags, "/", lilv_node_as_string (cval),
802         NULL);
803   }
804
805   gst_element_class_set_metadata (elem_class, longname,
806       (class_tags ? class_tags : lv2_class_tags), longname, author);
807   g_free (longname);
808   g_free (author);
809   g_free (class_tags);
810 }
811
812
813 void
814 gst_lv2_class_init (GstLV2Class * lv2_class, GType type)
815 {
816   const GValue *value =
817       gst_structure_get_value (lv2_meta_all, g_type_name (type));
818   GstStructure *lv2_meta = g_value_get_boxed (value);
819   const LilvPlugin *lv2plugin;
820   guint j, in_pad_index = 0, out_pad_index = 0;
821   const LilvPlugins *plugins = lilv_world_get_all_plugins (world);
822   LilvNode *plugin_uri;
823   const gchar *element_uri;
824
825   GST_DEBUG ("LV2 initializing class");
826
827   element_uri = gst_structure_get_string (lv2_meta, "element-uri");
828   plugin_uri = lilv_new_uri (world, element_uri);
829   g_assert (plugin_uri);
830   lv2plugin = lilv_plugins_get_by_uri (plugins, plugin_uri);
831   g_assert (lv2plugin);
832   lv2_class->plugin = lv2plugin;
833   lilv_node_free (plugin_uri);
834
835   lv2_class->sym_to_name = g_hash_table_new (g_str_hash, g_str_equal);
836
837   lv2_class->in_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
838   lv2_class->out_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
839   lv2_class->control_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
840   lv2_class->control_out_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
841
842   /* find ports and groups */
843   for (j = 0; j < lilv_plugin_get_num_ports (lv2plugin); j++) {
844     const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, j);
845     const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
846     const gboolean is_optional = lilv_port_has_property (lv2plugin, port,
847         optional_pred);
848     GstLV2Port desc = { j, GST_LV2_PORT_AUDIO, -1, };
849     LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
850     /* FIXME Handle channels positioning
851      * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */
852
853     if (lv2group) {
854       /* port is part of a group */
855       const gchar *group_uri = lilv_node_as_uri (lv2group);
856       GstLV2Group *group = is_input
857           ? &lv2_class->in_group : &lv2_class->out_group;
858
859       if (group->uri == NULL) {
860         group->uri = g_strdup (group_uri);
861         group->pad = is_input ? in_pad_index++ : out_pad_index++;
862         group->ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
863       }
864
865       /* FIXME Handle channels positioning
866          position = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
867          sub_values = lilv_port_get_value (lv2plugin, port, designation_pred);
868          if (lilv_nodes_size (sub_values) > 0) {
869          LilvNode *role = lilv_nodes_get_at (sub_values, 0);
870          position = gst_lv2_filter_role_to_position (role);
871          }
872          lilv_nodes_free (sub_values);
873
874          if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
875          desc.position = position;
876          } */
877
878       g_array_append_val (group->ports, desc);
879     } else {
880       /* port is not part of a group, or it is part of a group but that group
881        * is illegal so we just ignore it */
882       if (lilv_port_is_a (lv2plugin, port, audio_class)) {
883         if (is_input) {
884           desc.pad = in_pad_index++;
885           g_array_append_val (lv2_class->in_group.ports, desc);
886         } else {
887           desc.pad = out_pad_index++;
888           g_array_append_val (lv2_class->out_group.ports, desc);
889         }
890       } else if (lilv_port_is_a (lv2plugin, port, control_class)) {
891         desc.type = GST_LV2_PORT_CONTROL;
892         if (is_input) {
893           lv2_class->num_control_in++;
894           g_array_append_val (lv2_class->control_in_ports, desc);
895         } else {
896           lv2_class->num_control_out++;
897           g_array_append_val (lv2_class->control_out_ports, desc);
898         }
899       } else if (lilv_port_is_a (lv2plugin, port, cv_class)) {
900         desc.type = GST_LV2_PORT_CV;
901         if (is_input) {
902           lv2_class->num_cv_in++;
903           g_array_append_val (lv2_class->control_in_ports, desc);
904         } else {
905           lv2_class->num_cv_out++;
906           g_array_append_val (lv2_class->control_out_ports, desc);
907         }
908       } else if (lilv_port_is_a (lv2plugin, port, event_class)) {
909         LilvNodes *supported = lilv_port_get_value (lv2plugin, port,
910             supports_event_pred);
911
912         GST_INFO ("%s: unhandled event port %d: %s, optional=%d, input=%d",
913             element_uri, j,
914             lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)),
915             is_optional, is_input);
916
917         if (lilv_nodes_size (supported) > 0) {
918           LilvIter *i;
919
920           for (i = lilv_nodes_begin (supported);
921               !lilv_nodes_is_end (supported, i);
922               i = lilv_nodes_next (supported, i)) {
923             const LilvNode *value = lilv_nodes_get (supported, i);
924             GST_INFO ("  type = %s", lilv_node_as_uri (value));
925           }
926         }
927         lilv_nodes_free (supported);
928         // FIXME: handle them
929       } else {
930         /* unhandled port type */
931         const LilvNodes *classes = lilv_port_get_classes (lv2plugin, port);
932         GST_INFO ("%s: unhandled port %d: %s, optional=%d, input=%d",
933             element_uri, j,
934             lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)),
935             is_optional, is_input);
936         if (classes && lilv_nodes_size (classes) > 0) {
937           LilvIter *i;
938
939           // FIXME: we getting the same classe multiple times
940           for (i = lilv_nodes_begin (classes);
941               !lilv_nodes_is_end (classes, i);
942               i = lilv_nodes_next (classes, i)) {
943             const LilvNode *value = lilv_nodes_get (classes, i);
944             GST_INFO ("  class = %s", lilv_node_as_uri (value));
945           }
946         }
947       }
948     }
949   }
950 }
951
952 void
953 gst_lv2_class_finalize (GstLV2Class * lv2_class)
954 {
955   GST_DEBUG ("LV2 finalizing class");
956
957   g_hash_table_destroy (lv2_class->sym_to_name);
958
959   g_array_free (lv2_class->in_group.ports, TRUE);
960   lv2_class->in_group.ports = NULL;
961   g_array_free (lv2_class->out_group.ports, TRUE);
962   lv2_class->out_group.ports = NULL;
963   g_array_free (lv2_class->control_in_ports, TRUE);
964   lv2_class->control_in_ports = NULL;
965   g_array_free (lv2_class->control_out_ports, TRUE);
966   lv2_class->control_out_ports = NULL;
967 }