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