2 * Copyright (C) 2016 Thibault Saunier <thibault.saunier@collabora.com>
3 * 2016 Stefan Sauer <ensonic@users.sf.net>
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.
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.
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.
29 #include "gstlv2utils.h"
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>
37 GST_DEBUG_CATEGORY_EXTERN (lv2_debug);
38 #define GST_CAT_DEFAULT lv2_debug
44 #ifndef GST_DISABLE_GST_DEBUG
46 lv2_log_printf (LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...)
51 gst_debug_log_valist (lv2_debug, GST_LEVEL_INFO, "", "", 0, NULL, fmt, ap);
57 lv2_log_vprintf (LV2_Log_Handle handle, LV2_URID type,
58 const char *fmt, va_list ap)
60 gst_debug_log_valist (lv2_debug, GST_LEVEL_INFO, "", "", 0, NULL, fmt, ap);
64 static LV2_Log_Log lv2_log = {
65 /* handle = */ NULL, lv2_log_printf, lv2_log_vprintf
69 static const LV2_Feature lv2_log_feature = { LV2_LOG__log, &lv2_log };
72 /* - urid map/unmap extension */
75 lv2_urid_map (LV2_URID_Map_Handle handle, const char *uri)
77 return (LV2_URID) g_quark_from_string (uri);
81 lv2_urid_unmap (LV2_URID_Unmap_Handle handle, LV2_URID urid)
83 return g_quark_to_string ((GQuark) urid);
86 static LV2_URID_Map lv2_map = {
87 /* handle = */ NULL, lv2_urid_map
90 static LV2_URID_Unmap lv2_unmap = {
91 /* handle = */ NULL, lv2_urid_unmap
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 };
99 static const LV2_Feature *lv2_features[] = {
100 #ifndef GST_DISABLE_GST_DEBUG
109 gst_lv2_check_required_features (const LilvPlugin * lv2plugin)
111 LilvNodes *required_features = lilv_plugin_get_required_features (lv2plugin);
112 if (required_features) {
115 gboolean missing = FALSE;
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);
124 for (j = 0; lv2_features[j]; j++) {
125 if (!strcmp (lv2_features[j]->URI, required_feature_uri)) {
131 GST_FIXME ("lv2 plugin %s needs host feature: %s",
132 lilv_node_as_uri (lilv_plugin_get_uri (lv2plugin)),
133 required_feature_uri);
137 lilv_nodes_free (required_features);
143 static LV2_Atom_Forge forge;
146 gst_lv2_host_init (void)
148 lv2_atom_forge_init (&forge, &lv2_map);
151 /* preset interface */
154 make_bundle_name (GstObject * obj, const gchar * name)
156 GstElementFactory *factory;
157 gchar *basename, *s, *bundle;
159 factory = gst_element_get_factory ((GstElement *) obj);
160 basename = g_strdup (gst_element_factory_get_metadata (factory,
161 GST_ELEMENT_METADATA_LONGNAME));
163 while ((s = strchr (s, ' '))) {
166 bundle = g_strjoin (NULL, basename, "_", name, ".preset.lv2", NULL);
174 gst_lv2_get_preset_names (GstLV2 * lv2, GstObject * obj)
176 /* lazily scan for presets when first called */
180 if ((presets = lilv_plugin_get_related (lv2->klass->plugin, preset_class))) {
183 lv2->presets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
184 (GDestroyNotify) lilv_node_free);
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);
191 lilv_world_load_resource (world, preset);
192 titles = lilv_world_find_nodes (world, preset, label_pred, NULL);
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);
200 GST_WARNING_OBJECT (obj, "plugin has preset '%s' without rdfs:label",
201 lilv_node_as_string (preset));
204 lilv_nodes_free (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);
212 for (node = keys; node; node = g_list_next (node)) {
213 names[i++] = g_strdup (node->data);
222 set_port_value (const char *port_symbol, void *data, const void *value,
223 uint32_t size, uint32_t type)
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);
232 GST_WARNING_OBJECT (obj, "Preset port '%s' is missing", port_symbol);
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;
245 GST_WARNING_OBJECT (obj, "Preset '%s' value has bad type '%s'",
246 port_symbol, lv2_unmap.unmap (lv2_unmap.handle, type));
249 g_object_set (obj, prop_name, fvalue, NULL);
253 gst_lv2_load_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
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 };
259 GST_INFO_OBJECT (obj, "loading preset <%s>", lilv_node_as_string (preset));
261 lilv_state_restore (state, lv2->instance, set_port_value,
262 (gpointer) user_data, 0, NULL);
264 lilv_state_free (state);
269 get_port_value (const char *port_symbol, void *data, uint32_t * size,
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;
279 GST_WARNING_OBJECT (obj, "Preset port '%s' is missing", port_symbol);
284 *size = sizeof (float);
286 g_object_get (obj, prop_name, &fvalue, NULL);
287 /* FIXME: can we return &lv2->ports.{in,out}[x]; */
292 gst_lv2_save_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
294 gchar *filename, *bundle, *dir, *tmp_dir;
295 gpointer user_data[] = { lv2->klass, obj };
297 LilvNode *bundle_dir;
298 const LilvNode *state_uri;
299 LilvInstance *instance = lv2->instance;
301 #ifndef HAVE_LILV_0_22
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 */
309 g_build_filename (g_get_home_dir (), ".lv2", bundle, G_DIR_SEPARATOR_S,
311 tmp_dir = g_dir_make_tmp ("gstlv2-XXXXXX", NULL);
312 g_mkdir_with_parents (dir, 0750);
315 /* instance is NULL until we play!! */
316 instance = lilv_plugin_instantiate (lv2->klass->plugin, GST_AUDIO_DEF_RATE,
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);
324 lilv_state_set_label (state, name);
326 res = lilv_state_save (world, &lv2_map, &lv2_unmap, state, /*uri */ NULL, dir,
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);
335 #ifdef HAVE_LILV_0_22
336 state_uri = lilv_state_get_uri (state);
338 filepath = g_build_filename (dir, filename, NULL);
339 state_uri = lilv_new_uri (world, filepath);
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);
349 lilv_state_free (state);
350 if (!lv2->instance) {
351 lilv_instance_free (instance);
364 gst_lv2_rename_preset (GstLV2 * lv2, GstObject * obj,
365 const gchar * old_name, const gchar * new_name)
367 /* need to relabel the preset */
373 gst_lv2_delete_preset (GstLV2 * lv2, GstObject * obj, const gchar * name)
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);
379 lilv_world_unload_resource (world, lilv_state_get_uri (state));
380 lilv_state_delete (world, state);
381 lilv_state_free (state);
383 g_hash_table_remove (lv2->presets, name);
391 gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class)
393 lv2->klass = lv2_class;
395 lv2->instance = NULL;
396 lv2->activated = FALSE;
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);
403 gst_lv2_finalize (GstLV2 * lv2)
406 g_hash_table_destroy (lv2->presets);
408 g_free (lv2->ports.control.in);
409 g_free (lv2->ports.control.out);
413 gst_lv2_setup (GstLV2 * lv2, unsigned long rate)
415 GstLV2Class *lv2_class = lv2->klass;
421 lilv_instance_free (lv2->instance);
423 if (!(lv2->instance =
424 lilv_plugin_instantiate (lv2_class->plugin, rate, lv2_features)))
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)
433 lilv_instance_connect_port (lv2->instance, port->index,
434 &(lv2->ports.control.in[i]));
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)
441 lilv_instance_connect_port (lv2->instance, port->index,
442 &(lv2->ports.control.out[i]));
445 lilv_instance_activate (lv2->instance);
446 lv2->activated = TRUE;
452 gst_lv2_cleanup (GstLV2 * lv2, GstObject * obj)
454 if (lv2->activated == FALSE) {
455 GST_ERROR_OBJECT (obj, "Deactivating but LV2 plugin not activated");
459 if (lv2->instance == NULL) {
460 GST_ERROR_OBJECT (obj, "Deactivating but no LV2 plugin set");
464 GST_DEBUG_OBJECT (obj, "deactivating");
466 lilv_instance_deactivate (lv2->instance);
468 lv2->activated = FALSE;
470 lilv_instance_free (lv2->instance);
471 lv2->instance = NULL;
477 gst_lv2_object_set_property (GstLV2 * lv2, GObject * object,
478 guint prop_id, const GValue * value, GParamSpec * pspec)
480 GType base, type = pspec->value_type;
481 /* remember, properties have an offset */
482 prop_id -= lv2->klass->properties;
484 /* only input ports */
485 g_return_if_fail (prop_id < lv2->klass->control_in_ports->len);
487 while ((base = g_type_parent (type)))
490 /* now see what type it is */
493 lv2->ports.control.in[prop_id] =
494 g_value_get_boolean (value) ? 1.0f : 0.0f;
497 lv2->ports.control.in[prop_id] = g_value_get_int (value);
500 lv2->ports.control.in[prop_id] = g_value_get_float (value);
503 lv2->ports.control.in[prop_id] = g_value_get_enum (value);
506 GST_WARNING_OBJECT (object, "unhandled type: %s",
507 g_type_name (pspec->value_type));
508 g_assert_not_reached ();
513 gst_lv2_object_get_property (GstLV2 * lv2, GObject * object,
514 guint prop_id, GValue * value, GParamSpec * pspec)
516 GType base, type = pspec->value_type;
519 /* remember, properties have an offset */
520 prop_id -= lv2->klass->properties;
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;
529 g_return_if_reached ();
532 while ((base = g_type_parent (type)))
535 /* now see what type it is */
538 g_value_set_boolean (value, controls[prop_id] > 0.0f);
541 g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT));
544 g_value_set_float (value, controls[prop_id]);
547 g_value_set_enum (value, (gint) controls[prop_id]);
550 GST_WARNING_OBJECT (object, "unhandled type: %s",
551 g_type_name (pspec->value_type));
552 g_return_if_reached ();
558 gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
559 const gchar * port_symbol)
561 gchar *ret = g_strdup (port_symbol);
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;
569 ret = g_strconcat ("param-", ret, NULL);
573 /* check for duplicate property names */
574 if (g_object_class_find_property (object_class, ret)) {
576 gchar *nret = g_strdup_printf ("%s-%d", ret, n++);
578 while (g_object_class_find_property (object_class, nret)) {
580 nret = g_strdup_printf ("%s-%d", ret, n++);
586 GST_DEBUG ("built property name '%s' from port name '%s'", ret, port_symbol);
591 gst_lv2_class_get_param_nick (GstLV2Class * klass, const LilvPort * port)
593 const LilvPlugin *lv2plugin = klass->plugin;
595 return g_strdup (lilv_node_as_string (lilv_port_get_name (lv2plugin, port)));
599 enum_val_cmp (GEnumValue * p1, GEnumValue * p2)
601 return p1->value - p2->value;
605 gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class,
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;
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));
620 nick = gst_lv2_class_get_param_nick (klass, port);
621 name = gst_lv2_class_get_param_name (klass, object_class, port_symbol);
623 GST_DEBUG ("%s trying port %s : %s",
624 lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, nick);
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;
633 if (lilv_port_has_property (lv2plugin, port, toggled_prop)) {
634 ret = g_param_spec_boolean (name, nick, nick, FALSE, perms);
638 lilv_port_get_range (lv2plugin, port, &lv2def, &lv2min, &lv2max);
641 def = lilv_node_as_float (lv2def);
643 lower = lilv_node_as_float (lv2min);
645 upper = lilv_node_as_float (lv2max);
647 lilv_node_free (lv2def);
648 lilv_node_free (lv2min);
649 lilv_node_free (lv2max);
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,
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,
669 if ((points = lilv_port_get_scale_points (lv2plugin, port))) {
672 gint j = 0, n, def_ix = -1;
674 n = lilv_scale_points_size (points);
675 enums = g_new (GEnumValue, n + 1);
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));
684 /* check if value can be safely converted to int */
686 GST_INFO ("%s:%s non integer scale point %lf, %s",
687 lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, v, l);
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);
702 /* scalepoints are not sorted */
703 qsort (enums, n, sizeof (GEnumValue),
704 (int (*)(const void *, const void *)) enum_val_cmp);
708 GST_WARNING ("%s:%s has default %f outside of scalepoints",
709 lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, def);
711 def = enums[0].value;
715 enums[j].value_name = enums[j].value_nick = NULL;
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);
724 lilv_scale_points_free (points);
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);
732 ret = g_param_spec_float (name, nick, nick, lower, upper, def, perms);
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);
746 gst_lv2_class_install_properties (GstLV2Class * lv2_class,
747 GObjectClass * object_class, guint offset)
752 lv2_class->properties = offset;
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);
758 g_object_class_install_property (object_class, offset, p);
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);
765 g_object_class_install_property (object_class, offset, p);
770 gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
771 GstElementClass * elem_class, const gchar * lv2_class_tags)
773 const LilvPlugin *lv2plugin = lv2_class->plugin;
775 const LilvPluginClass *lv2plugin_class;
776 const LilvNode *cval;
777 gchar *longname, *author, *class_tags = NULL;
779 val = lilv_plugin_get_name (lv2plugin);
781 longname = g_strdup (lilv_node_as_string (val));
782 lilv_node_free (val);
784 longname = g_strdup ("no description available");
786 val = lilv_plugin_get_author_name (lv2plugin);
788 // TODO: check lilv_plugin_get_author_email(lv2plugin);
789 author = g_strdup (lilv_node_as_string (val));
790 lilv_node_free (val);
792 author = g_strdup ("no author available");
795 // TODO: better description from:
796 // lilv_plugin_get_author_homepage() and lilv_plugin_get_project()
798 lv2plugin_class = lilv_plugin_get_class (lv2plugin);
799 cval = lilv_plugin_class_get_label (lv2plugin_class);
801 class_tags = g_strconcat (lv2_class_tags, "/", lilv_node_as_string (cval),
805 gst_element_class_set_metadata (elem_class, longname,
806 (class_tags ? class_tags : lv2_class_tags), longname, author);
814 gst_lv2_class_init (GstLV2Class * lv2_class, GType type)
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;
825 GST_DEBUG ("LV2 initializing class");
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);
835 lv2_class->sym_to_name = g_hash_table_new (g_str_hash, g_str_equal);
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));
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,
848 GstLV2Port desc = { j, GST_LV2_PORT_AUDIO, -1, };
849 LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
850 /* FIXME Handle channels positionning
851 * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */
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;
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));
865 /* FIXME Handle channels positionning
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);
872 lilv_nodes_free (sub_values);
874 if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
875 desc.position = position;
878 g_array_append_val (group->ports, desc);
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)) {
884 desc.pad = in_pad_index++;
885 g_array_append_val (lv2_class->in_group.ports, desc);
887 desc.pad = out_pad_index++;
888 g_array_append_val (lv2_class->out_group.ports, desc);
890 } else if (lilv_port_is_a (lv2plugin, port, control_class)) {
891 desc.type = GST_LV2_PORT_CONTROL;
893 lv2_class->num_control_in++;
894 g_array_append_val (lv2_class->control_in_ports, desc);
896 lv2_class->num_control_out++;
897 g_array_append_val (lv2_class->control_out_ports, desc);
899 } else if (lilv_port_is_a (lv2plugin, port, cv_class)) {
900 desc.type = GST_LV2_PORT_CV;
902 lv2_class->num_cv_in++;
903 g_array_append_val (lv2_class->control_in_ports, desc);
905 lv2_class->num_cv_out++;
906 g_array_append_val (lv2_class->control_out_ports, desc);
908 } else if (lilv_port_is_a (lv2plugin, port, event_class)) {
909 LilvNodes *supported = lilv_port_get_value (lv2plugin, port,
910 supports_event_pred);
912 GST_INFO ("%s: unhandled event port %d: %s, optional=%d, input=%d",
914 lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)),
915 is_optional, is_input);
917 if (lilv_nodes_size (supported) > 0) {
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));
927 lilv_nodes_free (supported);
928 // FIXME: handle them
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",
934 lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)),
935 is_optional, is_input);
936 if (classes && lilv_nodes_size (classes) > 0) {
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));
953 gst_lv2_class_finalize (GstLV2Class * lv2_class)
955 GST_DEBUG ("LV2 finalizing class");
957 g_hash_table_destroy (lv2_class->sym_to_name);
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;