summaryrefslogtreecommitdiff
path: root/ext/lv2/gstlv2utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/lv2/gstlv2utils.c')
-rw-r--r--ext/lv2/gstlv2utils.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/ext/lv2/gstlv2utils.c b/ext/lv2/gstlv2utils.c
new file mode 100644
index 000000000..fca3a1c59
--- /dev/null
+++ b/ext/lv2/gstlv2utils.c
@@ -0,0 +1,427 @@
+/* GStreamer
+ * Copyright (C) 2016 Thibault Saunier <thibault.saunier@collabora.com>
+ * 2016 Stefan Sauer <ensonic@users.sf.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstlv2.h"
+#include "gstlv2utils.h"
+
+GST_DEBUG_CATEGORY_EXTERN (lv2_debug);
+#define GST_CAT_DEFAULT lv2_debug
+
+void
+gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class)
+{
+ lv2->klass = lv2_class;
+
+ lv2->instance = NULL;
+ lv2->activated = FALSE;
+
+ lv2->ports.control.in = g_new0 (gfloat, lv2_class->control_in_ports->len);
+ lv2->ports.control.out = g_new0 (gfloat, lv2_class->control_out_ports->len);
+}
+
+void
+gst_lv2_finalize (GstLV2 * lv2)
+{
+ g_free (lv2->ports.control.in);
+ g_free (lv2->ports.control.out);
+}
+
+gboolean
+gst_lv2_setup (GstLV2 * lv2, unsigned long rate)
+{
+ GstLV2Class *lv2_class = lv2->klass;
+ gint i;
+
+ if (lv2->instance)
+ lilv_instance_free (lv2->instance);
+
+ if (!(lv2->instance =
+ lilv_plugin_instantiate (lv2_class->plugin, rate, NULL)))
+ return FALSE;
+
+ /* connect the control ports */
+ for (i = 0; i < lv2_class->control_in_ports->len; i++)
+ lilv_instance_connect_port (lv2->instance,
+ g_array_index (lv2_class->control_in_ports, GstLV2Port, i).index,
+ &(lv2->ports.control.in[i]));
+
+ for (i = 0; i < lv2_class->control_out_ports->len; i++)
+ lilv_instance_connect_port (lv2->instance,
+ g_array_index (lv2_class->control_out_ports, GstLV2Port, i).index,
+ &(lv2->ports.control.out[i]));
+
+ lilv_instance_activate (lv2->instance);
+ lv2->activated = TRUE;
+
+ return TRUE;
+}
+
+gboolean
+gst_lv2_cleanup (GstLV2 * lv2, GstObject * obj)
+{
+ if (lv2->activated == FALSE) {
+ GST_ERROR_OBJECT (obj, "Deactivating but LV2 plugin not activated");
+ return TRUE;
+ }
+
+ if (lv2->instance == NULL) {
+ GST_ERROR_OBJECT (obj, "Deactivating but no LV2 plugin set");
+ return TRUE;
+ }
+
+ GST_DEBUG_OBJECT (obj, "deactivating");
+
+ lilv_instance_deactivate (lv2->instance);
+
+ lv2->activated = FALSE;
+
+ lilv_instance_free (lv2->instance);
+ lv2->instance = NULL;
+
+ return TRUE;
+}
+
+void
+gst_lv2_object_set_property (GstLV2 * lv2, GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ /* remember, properties have an offset */
+ prop_id -= lv2->klass->properties;
+
+ /* only input ports */
+ g_return_if_fail (prop_id < lv2->klass->control_in_ports->len);
+
+ /* now see what type it is */
+ switch (pspec->value_type) {
+ case G_TYPE_BOOLEAN:
+ lv2->ports.control.in[prop_id] =
+ g_value_get_boolean (value) ? 0.0f : 1.0f;
+ break;
+ case G_TYPE_INT:
+ lv2->ports.control.in[prop_id] = g_value_get_int (value);
+ break;
+ case G_TYPE_FLOAT:
+ lv2->ports.control.in[prop_id] = g_value_get_float (value);
+ break;
+ default:
+ GST_WARNING_OBJECT (object, "unhandled type: %s",
+ g_type_name (pspec->value_type));
+ g_assert_not_reached ();
+ }
+}
+
+void
+gst_lv2_object_get_property (GstLV2 * lv2, GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ gfloat *controls;
+
+ /* remember, properties have an offset */
+ prop_id -= lv2->klass->properties;
+
+ if (prop_id < lv2->klass->control_in_ports->len) {
+ controls = lv2->ports.control.in;
+ } else if (prop_id < lv2->klass->control_in_ports->len +
+ lv2->klass->control_out_ports->len) {
+ controls = lv2->ports.control.out;
+ prop_id -= lv2->klass->control_in_ports->len;
+ } else {
+ g_return_if_reached ();
+ }
+
+ /* now see what type it is */
+ switch (pspec->value_type) {
+ case G_TYPE_BOOLEAN:
+ g_value_set_boolean (value, controls[prop_id] > 0.0f);
+ break;
+ case G_TYPE_INT:
+ g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT));
+ break;
+ case G_TYPE_FLOAT:
+ g_value_set_float (value, controls[prop_id]);
+ break;
+ default:
+ GST_WARNING_OBJECT (object, "unhandled type: %s",
+ g_type_name (pspec->value_type));
+ g_return_if_reached ();
+ }
+}
+
+
+static gchar *
+gst_lv2_class_get_param_name (GstLV2Class * klass, GObjectClass * object_class,
+ const LilvPort * port)
+{
+ LilvPlugin *lv2plugin = klass->plugin;
+ gchar *ret;
+
+ ret = g_strdup (lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
+
+ /* this is the same thing that param_spec_* will do */
+ g_strcanon (ret, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
+ /* satisfy glib2 (argname[0] must be [A-Za-z]) */
+ if (!((ret[0] >= 'a' && ret[0] <= 'z') || (ret[0] >= 'A' && ret[0] <= 'Z'))) {
+ gchar *tempstr = ret;
+
+ ret = g_strconcat ("param-", ret, NULL);
+ g_free (tempstr);
+ }
+
+ /* check for duplicate property names */
+ if (g_object_class_find_property (object_class, ret)) {
+ gint n = 1;
+ gchar *nret = g_strdup_printf ("%s-%d", ret, n++);
+
+ while (g_object_class_find_property (object_class, nret)) {
+ g_free (nret);
+ nret = g_strdup_printf ("%s-%d", ret, n++);
+ }
+ g_free (ret);
+ ret = nret;
+ }
+
+ GST_DEBUG ("built property name '%s' from port name '%s'", ret,
+ lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
+
+ return ret;
+}
+
+static gchar *
+gst_lv2_class_get_param_nick (GstLV2Class * klass, const LilvPort * port)
+{
+ LilvPlugin *lv2plugin = klass->plugin;
+
+ return g_strdup (lilv_node_as_string (lilv_port_get_name (lv2plugin, port)));
+}
+
+static GParamSpec *
+gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class,
+ gint portnum)
+{
+ LilvPlugin *lv2plugin = klass->plugin;
+ const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, portnum);
+ LilvNode *lv2def, *lv2min, *lv2max;
+ GParamSpec *ret;
+ gchar *name, *nick;
+ gint perms;
+ gfloat lower = 0.0f, upper = 1.0f, def = 0.0f;
+
+ nick = gst_lv2_class_get_param_nick (klass, port);
+ name = gst_lv2_class_get_param_name (klass, object_class, port);
+
+ GST_DEBUG ("%s trying port %s : %s",
+ lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), name, nick);
+
+ perms = G_PARAM_READABLE;
+ if (lilv_port_is_a (lv2plugin, port, input_class))
+ perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT;
+ if (lilv_port_is_a (lv2plugin, port, control_class))
+ perms |= GST_PARAM_CONTROLLABLE;
+
+ if (lilv_port_has_property (lv2plugin, port, toggled_prop)) {
+ ret = g_param_spec_boolean (name, nick, nick, FALSE, perms);
+ goto done;
+ }
+
+ lilv_port_get_range (lv2plugin, port, &lv2def, &lv2min, &lv2max);
+
+ if (lv2def)
+ def = lilv_node_as_float (lv2def);
+ if (lv2min)
+ lower = lilv_node_as_float (lv2min);
+ if (lv2max)
+ upper = lilv_node_as_float (lv2max);
+
+ lilv_node_free (lv2def);
+ lilv_node_free (lv2min);
+ lilv_node_free (lv2max);
+
+ if (def < lower) {
+ GST_WARNING ("%s has lower bound %f > default %f",
+ lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), lower, def);
+ lower = def;
+ }
+
+ if (def > upper) {
+ GST_WARNING ("%s has upper bound %f < default %f",
+ lilv_node_as_string (lilv_plugin_get_uri (lv2plugin)), upper, def);
+ upper = def;
+ }
+
+ if (lilv_port_has_property (lv2plugin, port, integer_prop))
+ ret = g_param_spec_int (name, nick, nick, lower, upper, def, perms);
+ else
+ ret = g_param_spec_float (name, nick, nick, lower, upper, def, perms);
+
+done:
+ g_free (name);
+ g_free (nick);
+
+ return ret;
+}
+
+void
+gst_lv2_class_install_properties (GstLV2Class * lv2_class,
+ GObjectClass * object_class, guint offset)
+{
+ GParamSpec *p;
+ guint i;
+
+ lv2_class->properties = offset;
+
+ for (i = 0; i < lv2_class->control_in_ports->len; i++, offset++) {
+ p = gst_lv2_class_get_param_spec (lv2_class, object_class,
+ g_array_index (lv2_class->control_in_ports, GstLV2Port, i).index);
+
+ g_object_class_install_property (object_class, offset, p);
+ }
+
+ for (i = 0; i < lv2_class->control_out_ports->len; i++, offset++) {
+ p = gst_lv2_class_get_param_spec (lv2_class, object_class,
+ g_array_index (lv2_class->control_out_ports, GstLV2Port, i).index);
+
+ g_object_class_install_property (object_class, offset, p);
+ }
+}
+
+void
+gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
+ GstElementClass * elem_class, const gchar * lv2_class_tags)
+{
+ LilvPlugin *lv2plugin = lv2_class->plugin;
+ LilvNode *val;
+ gchar *longname, *author;
+
+ val = lilv_plugin_get_name (lv2plugin);
+ if (val) {
+ longname = g_strdup (lilv_node_as_string (val));
+ lilv_node_free (val);
+ } else {
+ longname = g_strdup ("no description available");
+ }
+ val = lilv_plugin_get_author_name (lv2plugin);
+ if (val) {
+ author = g_strdup (lilv_node_as_string (val));
+ lilv_node_free (val);
+ } else {
+ author = g_strdup ("no author available");
+ }
+
+ gst_element_class_set_metadata (elem_class, longname, lv2_class_tags,
+ longname, author);
+ g_free (longname);
+ g_free (author);
+}
+
+
+void
+gst_lv2_class_init (GstLV2Class * lv2_class, GType type)
+{
+ LilvPlugin *lv2plugin;
+ /* FIXME Handle channels positionning
+ * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */
+ guint j, in_pad_index = 0, out_pad_index = 0;
+
+ GST_DEBUG ("LV2 initializing class");
+
+ lv2plugin = (LilvPlugin *) g_type_get_qdata (type, descriptor_quark);
+ g_assert (lv2plugin);
+ lv2_class->plugin = lv2plugin;
+
+ lv2_class->in_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ lv2_class->out_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ lv2_class->control_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ lv2_class->control_out_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+
+ /* find ports and groups */
+ for (j = 0; j < lilv_plugin_get_num_ports (lv2plugin); j++) {
+ const LilvPort *port = lilv_plugin_get_port_by_index (lv2plugin, j);
+ const gboolean is_input = lilv_port_is_a (lv2plugin, port, input_class);
+ struct _GstLV2Port desc = { j, 0, };
+ LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred);
+
+ if (lv2group) {
+ /* port is part of a group */
+ const gchar *group_uri = lilv_node_as_uri (lv2group);
+ GstLV2Group *group = is_input
+ ? &lv2_class->in_group : &lv2_class->out_group;
+
+ if (group->uri == NULL) {
+ group->uri = g_strdup (group_uri);
+ group->pad = is_input ? in_pad_index++ : out_pad_index++;
+ group->ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ }
+
+ /* FIXME Handle channels positionning
+ position = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ sub_values = lilv_port_get_value (lv2plugin, port, has_role_pred);
+ if (lilv_nodes_size (sub_values) > 0) {
+ LilvNode *role = lilv_nodes_get_at (sub_values, 0);
+ position = gst_lv2_filter_role_to_position (role);
+ }
+ lilv_nodes_free (sub_values);
+
+ if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
+ desc.position = position;
+ } */
+
+ g_array_append_val (group->ports, desc);
+ } else {
+ /* port is not part of a group, or it is part of a group but that group
+ * is illegal so we just ignore it */
+ if (lilv_port_is_a (lv2plugin, port, audio_class)) {
+ desc.pad = is_input ? in_pad_index++ : out_pad_index++;
+ if (is_input)
+ g_array_append_val (lv2_class->in_group.ports, desc);
+ else
+ g_array_append_val (lv2_class->out_group.ports, desc);
+ } else if (lilv_port_is_a (lv2plugin, port, control_class)) {
+ if (is_input)
+ g_array_append_val (lv2_class->control_in_ports, desc);
+ else
+ g_array_append_val (lv2_class->control_out_ports, desc);
+ } else {
+ /* unknown port type */
+ GST_INFO ("unhandled port %d: %s", j,
+ lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)));
+ continue;
+ }
+ }
+ }
+}
+
+void
+gst_lv2_class_finalize (GstLV2Class * lv2_class)
+{
+ GST_DEBUG ("LV2 finalizing class");
+
+ g_array_free (lv2_class->in_group.ports, TRUE);
+ lv2_class->in_group.ports = NULL;
+ g_array_free (lv2_class->out_group.ports, TRUE);
+ lv2_class->out_group.ports = NULL;
+ g_array_free (lv2_class->control_in_ports, TRUE);
+ lv2_class->control_in_ports = NULL;
+ g_array_free (lv2_class->control_out_ports, TRUE);
+ lv2_class->control_out_ports = NULL;
+}