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