summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/lv2/Makefile.am4
-rw-r--r--ext/lv2/gstlv2filter.c487
-rw-r--r--ext/lv2/gstlv2utils.c427
-rw-r--r--ext/lv2/gstlv2utils.h111
4 files changed, 608 insertions, 421 deletions
diff --git a/ext/lv2/Makefile.am b/ext/lv2/Makefile.am
index 0c320e3cd..3903c8d0f 100644
--- a/ext/lv2/Makefile.am
+++ b/ext/lv2/Makefile.am
@@ -1,6 +1,6 @@
plugin_LTLIBRARIES = libgstlv2.la
-libgstlv2_la_SOURCES = gstlv2.c gstlv2filter.c
+libgstlv2_la_SOURCES = gstlv2.c gstlv2utils.c gstlv2filter.c
libgstlv2_la_CFLAGS = \
-I$(top_srcdir)/gst-libs \
$(GST_AUDIO_CFLAGS) \
@@ -15,4 +15,4 @@ libgstlv2_la_LIBADD = \
libgstlv2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstlv2_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
-noinst_HEADERS = gstlv2.h
+noinst_HEADERS = gstlv2.h gstlv2utils.h
diff --git a/ext/lv2/gstlv2filter.c b/ext/lv2/gstlv2filter.c
index 5e8c1128c..4dcc7ad28 100644
--- a/ext/lv2/gstlv2filter.c
+++ b/ext/lv2/gstlv2filter.c
@@ -24,6 +24,7 @@
#include "config.h"
#endif
#include "gstlv2.h"
+#include "gstlv2utils.h"
#include <string.h>
#include <math.h>
@@ -51,58 +52,20 @@ typedef struct _lv2_control_info
typedef struct _GstLV2Filter GstLV2Filter;
typedef struct _GstLV2FilterClass GstLV2FilterClass;
-typedef struct _GstLV2FilterGroup GstLV2FilterGroup;
-typedef struct _GstLV2FilterPort GstLV2FilterPort;
struct _GstLV2Filter
{
GstAudioFilter parent;
- LilvPlugin *plugin;
- LilvInstance *instance;
-
- gboolean activated;
-
- /* TODO refactor in the same way as LADSPA plugin */
- struct
- {
- struct
- {
- gfloat *in;
- gfloat *out;
- } control;
- } ports;
-};
-
-struct _GstLV2FilterGroup
-{
- gchar *uri; /**< RDF resource (URI or blank node) */
- guint pad; /**< Gst pad index */
- gchar *symbol; /**< Gst pad name / LV2 group symbol */
- GArray *ports; /**< Array of GstLV2FilterPort */
- gboolean has_roles; /**< TRUE iff all ports have a known role */
-};
-
-struct _GstLV2FilterPort
-{
- gint index; /**< LV2 port index (on LV2 plugin) */
- gint pad; /**< Gst pad index (iff not part of a group) */
- LilvNode *role; /**< Channel position / port role */
- GstAudioChannelPosition position; /**< Channel position */
+ GstLV2 lv2;
};
struct _GstLV2FilterClass
{
GstAudioFilterClass parent_class;
- LilvPlugin *plugin;
-
- GstLV2FilterGroup in_group; /**< Array of GstLV2FilterGroup */
- GstLV2FilterGroup out_group; /**< Array of GstLV2FilterGroup */
- GArray *control_in_ports; /**< Array of GstLV2FilterPort */
- GArray *control_out_ports; /**< Array of GstLV2FilterPort */
-
+ GstLV2Class lv2;
};
static GstAudioFilter *parent_class = NULL;
@@ -112,70 +75,28 @@ static void
gst_lv2_filter_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
- GstLV2Filter *self = (GstLV2Filter *) (object);
- GstLV2FilterClass *klass =
- (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (object);
-
- /* remember, properties have an offset of 1 */
- prop_id--;
-
- /* only input ports */
- g_return_if_fail (prop_id < klass->control_in_ports->len);
-
- /* now see what type it is */
- switch (pspec->value_type) {
- case G_TYPE_BOOLEAN:
- self->ports.control.in[prop_id] =
- g_value_get_boolean (value) ? 0.0f : 1.0f;
- break;
- case G_TYPE_INT:
- self->ports.control.in[prop_id] = g_value_get_int (value);
- break;
- case G_TYPE_FLOAT:
- self->ports.control.in[prop_id] = g_value_get_float (value);
- break;
- default:
- g_assert_not_reached ();
- }
+ GstLV2Filter *self = (GstLV2Filter *) object;
+
+ gst_lv2_object_set_property (&self->lv2, object, prop_id, value, pspec);
}
static void
gst_lv2_filter_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
- GstLV2Filter *self = (GstLV2Filter *) (object);
- GstLV2FilterClass *klass =
- (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (object);
-
- gfloat *controls;
-
- /* remember, properties have an offset of 1 */
- prop_id--;
-
- if (prop_id < klass->control_in_ports->len) {
- controls = self->ports.control.in;
- } else if (prop_id < klass->control_in_ports->len +
- klass->control_out_ports->len) {
- controls = self->ports.control.out;
- prop_id -= klass->control_in_ports->len;
- } else {
- g_return_if_reached ();
- }
+ GstLV2Filter *self = (GstLV2Filter *) object;
- /* 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:
- g_return_if_reached ();
- }
+ gst_lv2_object_get_property (&self->lv2, object, prop_id, value, pspec);
+}
+
+static void
+gst_lv2_filter_finalize (GObject * object)
+{
+ GstLV2Filter *self = (GstLV2Filter *) object;
+
+ gst_lv2_finalize (&self->lv2);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
#if 0
@@ -229,7 +150,7 @@ gst_lv2_filter_role_to_position (LilvNode * role)
}
static GstAudioChannelPosition *
-gst_lv2_filter_build_positions (GstLV2FilterGroup * group)
+gst_lv2_filter_build_positions (GstLV2Group * group)
{
GstAudioChannelPosition *positions = NULL;
@@ -239,7 +160,7 @@ gst_lv2_filter_build_positions (GstLV2FilterGroup * group)
positions = g_new (GstAudioChannelPosition, group->ports->len);
for (i = 0; i < group->ports->len; ++i)
- positions[i] = g_array_index (group->ports, GstLV2FilterPort, i).position;
+ positions[i] = g_array_index (group->ports, GstLV2Port, i).position;
}
return positions;
}
@@ -255,9 +176,9 @@ gst_lv2_filter_type_class_add_pad_templates (GstLV2FilterClass * klass)
gint in_channels = 1, out_channels = 1;
- in_channels = klass->in_group.ports->len;
+ in_channels = klass->lv2.in_group.ports->len;
- out_channels = klass->out_group.ports->len;
+ out_channels = klass->lv2.out_group.ports->len;
/* FIXME Implement deintereleaved audio support */
sinkcaps = gst_caps_new_simple ("audio/x-raw",
@@ -289,44 +210,22 @@ gst_lv2_filter_type_class_add_pad_templates (GstLV2FilterClass * klass)
static gboolean
gst_lv2_filter_setup (GstAudioFilter * gsp, const GstAudioInfo * info)
{
- GstLV2Filter *self;
- GstLV2FilterClass *oclass;
- GstAudioFilterClass *audiofilter_class;
- gint i;
-
- audiofilter_class = GST_AUDIO_FILTER_GET_CLASS (gsp);
- self = (GstLV2Filter *) gsp;
- oclass = (GstLV2FilterClass *) audiofilter_class;
+ GstLV2Filter *self = (GstLV2Filter *) gsp;
- g_return_val_if_fail (self->activated == FALSE, FALSE);
+ g_return_val_if_fail (self->lv2.activated == FALSE, FALSE);
GST_DEBUG_OBJECT (self, "instantiating the plugin at %d Hz",
GST_AUDIO_INFO_RATE (info));
- if (self->instance)
- lilv_instance_free (self->instance);
-
- if (!(self->instance =
- lilv_plugin_instantiate (oclass->plugin, GST_AUDIO_INFO_RATE (info),
- NULL)))
+ if (!gst_lv2_setup (&self->lv2, GST_AUDIO_INFO_RATE (info)))
goto no_instance;
- /* connect the control ports */
- for (i = 0; i < oclass->control_in_ports->len; i++)
- lilv_instance_connect_port (self->instance,
- g_array_index (oclass->control_in_ports, GstLV2FilterPort, i).index,
- &(self->ports.control.in[i]));
-
- for (i = 0; i < oclass->control_out_ports->len; i++)
- lilv_instance_connect_port (self->instance,
- g_array_index (oclass->control_out_ports, GstLV2FilterPort, i).index,
- &(self->ports.control.out[i]));
-
/* FIXME Handle audio channel positionning while negotiating CAPS */
#if 0
+ gint i;
/* set input group pad audio channel position */
- for (i = 0; i < oclass->in_groups->len; ++i) {
- group = &g_array_index (oclass->in_groups, GstLV2FilterGroup, i);
+ for (i = 0; i < oclass->lv2.in_groups->len; ++i) {
+ group = &g_array_index (oclass->lv2.in_groups, GstLV2Group, i);
if (group->has_roles) {
if ((positions = gst_lv2_filter_build_positions (group))) {
if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp),
@@ -343,8 +242,8 @@ gst_lv2_filter_setup (GstAudioFilter * gsp, const GstAudioInfo * info)
}
}
/* set output group pad audio channel position */
- for (i = 0; i < oclass->out_groups->len; ++i) {
- group = &g_array_index (oclass->out_groups, GstLV2FilterGroup, i);
+ for (i = 0; i < oclass->lv2.out_groups->len; ++i) {
+ group = &g_array_index (oclass->lv2.out_groups, GstLV2Group, i);
if (group->has_roles) {
if ((positions = gst_lv2_filter_build_positions (group))) {
if ((pad = gst_element_get_static_pad (GST_ELEMENT (gsp),
@@ -362,9 +261,6 @@ gst_lv2_filter_setup (GstAudioFilter * gsp, const GstAudioInfo * info)
}
#endif
- lilv_instance_activate (self->instance);
- self->activated = TRUE;
-
return TRUE;
no_instance:
@@ -379,28 +275,7 @@ gst_lv2_filter_stop (GstBaseTransform * transform)
{
GstLV2Filter *lv2 = (GstLV2Filter *) transform;
- if (lv2->activated == FALSE) {
- GST_ERROR_OBJECT (transform, "Deactivating but LV2 plugin not activated");
-
- return TRUE;
- }
-
- if (lv2->instance == NULL) {
- GST_ERROR_OBJECT (transform, "Deactivating but no LV2 plugin set");
-
- return TRUE;
- }
-
- GST_DEBUG_OBJECT (lv2, "deactivating");
-
- lilv_instance_deactivate (lv2->instance);
-
- lv2->activated = FALSE;
-
- lilv_instance_free (lv2->instance);
- lv2->instance = NULL;
-
- return TRUE;
+ return gst_lv2_cleanup (&lv2->lv2, (GstObject *) lv2);
}
static inline void
@@ -430,51 +305,50 @@ static GstFlowReturn
gst_lv2_filter_transform_data (GstLV2Filter * self,
GstMapInfo * in_map, GstMapInfo * out_map)
{
- GstAudioFilterClass *audiofilter_class;
GstLV2FilterClass *lv2_class;
- GstLV2FilterGroup *lv2_group;
- GstLV2FilterPort *lv2_port;
+ GstLV2Group *lv2_group;
+ GstLV2Port *lv2_port;
guint j, nframes, samples, out_samples;
-
gfloat *in = NULL, *out = NULL;
nframes = in_map->size / sizeof (float);
- audiofilter_class = GST_AUDIO_FILTER_GET_CLASS (self);
- lv2_class = (GstLV2FilterClass *) audiofilter_class;
-
- samples = nframes / lv2_class->in_group.ports->len;
+ lv2_class = (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (self);
/* multi channel inputs */
- lv2_group = &lv2_class->in_group;
-
+ lv2_group = &lv2_class->lv2.in_group;
+ samples = nframes / lv2_group->ports->len;
in = g_new0 (gfloat, nframes);
+ GST_INFO ("in : samples=%u, nframes=%u, ports=%d", samples, nframes,
+ lv2_group->ports->len);
if (lv2_group->ports->len > 1)
- gst_lv2_filter_deinterleave_data (lv2_group->ports->len, in,
- samples, (gfloat *) in_map->data);
+ gst_lv2_filter_deinterleave_data (lv2_group->ports->len, in,
+ samples, (gfloat *) in_map->data);
for (j = 0; j < lv2_group->ports->len; ++j) {
- lv2_port = &g_array_index (lv2_group->ports, GstLV2FilterPort, j);
-
- lilv_instance_connect_port (self->instance, lv2_port->index,
+ lv2_port = &g_array_index (lv2_group->ports, GstLV2Port, j);
+ lilv_instance_connect_port (self->lv2.instance, lv2_port->index,
in + (j * samples));
}
- lv2_group = &lv2_class->out_group;
+ /* multi channel outputs */
+ lv2_group = &lv2_class->lv2.out_group;
out_samples = nframes / lv2_group->ports->len;
out = g_new0 (gfloat, samples * lv2_group->ports->len);
+ GST_INFO ("out: samples=%u, nframes=%u, ports=%d", out_samples, nframes,
+ lv2_group->ports->len);
for (j = 0; j < lv2_group->ports->len; ++j) {
- lv2_port = &g_array_index (lv2_group->ports, GstLV2FilterPort, j);
- lilv_instance_connect_port (self->instance, lv2_port->index,
+ lv2_port = &g_array_index (lv2_group->ports, GstLV2Port, j);
+ lilv_instance_connect_port (self->lv2.instance, lv2_port->index,
out + (j * out_samples));
}
- lilv_instance_run (self->instance, samples);
+ lilv_instance_run (self->lv2.instance, samples);
if (lv2_group->ports->len > 1)
- gst_lv2_filter_interleave_data (lv2_group->ports->len,
- (gfloat *) out_map->data, out_samples, out);
+ gst_lv2_filter_interleave_data (lv2_group->ports->len,
+ (gfloat *) out_map->data, out_samples, out);
g_free (out);
g_free (in);
@@ -520,276 +394,51 @@ gst_lv2_filter_base_init (gpointer g_class)
{
GstLV2FilterClass *klass = (GstLV2FilterClass *) g_class;
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- LilvPlugin *lv2plugin;
- LilvNode *val;
- /* FIXME Handle channels positionning
- * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */
- guint j, in_pad_index = 0, out_pad_index = 0;
- gchar *longname, *author;
-
- lv2plugin = (LilvPlugin *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass),
- descriptor_quark);
-
- g_assert (lv2plugin);
-
- GST_INFO ("base_init %p, plugin %s", g_class,
- lilv_node_get_turtle_token (lilv_plugin_get_uri (lv2plugin)));
-
- klass->in_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
- klass->out_group.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
- klass->control_in_ports =
- g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
- klass->control_out_ports =
- g_array_new (FALSE, TRUE, sizeof (GstLV2FilterPort));
-
- /* 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 _GstLV2FilterPort 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);
- GstLV2FilterGroup *group =
- is_input ? &klass->in_group : &klass->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 (GstLV2FilterPort));
- }
-
- /* 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 (klass->in_group.ports, desc);
- else
- g_array_append_val (klass->out_group.ports, desc);
- } else if (lilv_port_is_a (lv2plugin, port, control_class)) {
- if (is_input)
- g_array_append_val (klass->control_in_ports, desc);
- else
- g_array_append_val (klass->control_out_ports, desc);
- } else {
- /* unknown port type */
- GST_INFO ("unhandled port %d", j);
- continue;
- }
- }
- }
- gst_lv2_filter_type_class_add_pad_templates (klass);
-
- 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 (element_class, longname,
- "Filter/Effect/Audio/LV2", longname, author);
- g_free (longname);
- g_free (author);
-
- klass->plugin = lv2plugin;
-}
-
-static gchar *
-gst_lv2_filter_class_get_param_name (GstLV2FilterClass * klass,
- 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;
+ gst_lv2_class_init (&klass->lv2, G_TYPE_FROM_CLASS (klass));
- ret = g_strconcat ("param-", ret, NULL);
- g_free (tempstr);
- }
-
- /* check for duplicate property names */
- if (g_object_class_find_property (G_OBJECT_CLASS (klass), ret)) {
- gint n = 1;
- gchar *nret = g_strdup_printf ("%s-%d", ret, n++);
-
- while (g_object_class_find_property (G_OBJECT_CLASS (klass), 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_filter_class_get_param_nick (GstLV2FilterClass * klass,
- const LilvPort * port)
-{
- LilvPlugin *lv2plugin = klass->plugin;
+ gst_lv2_element_class_set_metadata (&klass->lv2, element_class,
+ "Filter/Effect/Audio/LV2");
- return g_strdup (lilv_node_as_string (lilv_port_get_name (lv2plugin, port)));
+ gst_lv2_filter_type_class_add_pad_templates (klass);
}
-static GParamSpec *
-gst_lv2_filter_class_get_param_spec (GstLV2FilterClass * klass, gint portnum)
+static void
+gst_lv2_filter_base_finalize (GstLV2FilterClass * lv2_class)
{
- 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_filter_class_get_param_nick (klass, port);
- name = gst_lv2_filter_class_get_param_name (klass, 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;
+ gst_lv2_class_finalize (&lv2_class->lv2);
}
static void
gst_lv2_filter_class_init (GstLV2FilterClass * klass, LilvPlugin * lv2plugin)
{
- GObjectClass *gobject_class;
- GstBaseTransformClass *transform_class;
- GstAudioFilterClass *audiofilter_class;
- GParamSpec *p;
- gint i, ix;
+ GObjectClass *gobject_class = (GObjectClass *) klass;
+ GstBaseTransformClass *transform_class = GST_BASE_TRANSFORM_CLASS (klass);
+ GstAudioFilterClass *audiofilter_class = GST_AUDIO_FILTER_CLASS (klass);
GST_DEBUG ("class_init %p", klass);
- gobject_class = (GObjectClass *) klass;
gobject_class->set_property = gst_lv2_filter_set_property;
gobject_class->get_property = gst_lv2_filter_get_property;
+ gobject_class->finalize = gst_lv2_filter_finalize;
- audiofilter_class = GST_AUDIO_FILTER_CLASS (klass);
audiofilter_class->setup = gst_lv2_filter_setup;
- transform_class = GST_BASE_TRANSFORM_CLASS (klass);
transform_class->stop = gst_lv2_filter_stop;
transform_class->transform = gst_lv2_filter_transform;
transform_class->transform_ip = gst_lv2_filter_transform_ip;
- klass->plugin = lv2plugin;
-
- /* properties have an offset of 1 */
- ix = 1;
+ klass->lv2.plugin = lv2plugin;
- /* register properties */
-
- for (i = 0; i < klass->control_in_ports->len; i++, ix++) {
- p = gst_lv2_filter_class_get_param_spec (klass,
- g_array_index (klass->control_in_ports, GstLV2FilterPort, i).index);
-
- g_object_class_install_property (gobject_class, ix, p);
- }
-
- for (i = 0; i < klass->control_out_ports->len; i++, ix++) {
- p = gst_lv2_filter_class_get_param_spec (klass,
- g_array_index (klass->control_out_ports, GstLV2FilterPort, i).index);
-
- g_object_class_install_property (gobject_class, ix, p);
- }
+ gst_lv2_class_install_properties (&klass->lv2, gobject_class, 1);
}
static void
gst_lv2_filter_init (GstLV2Filter * self, GstLV2FilterClass * klass)
{
- self->plugin = klass->plugin;
- self->instance = NULL;
- self->activated = FALSE;
+ gst_lv2_init (&self->lv2, &klass->lv2);
- self->ports.control.in = g_new0 (gfloat, klass->control_in_ports->len);
- self->ports.control.out = g_new0 (gfloat, klass->control_out_ports->len);
-
- if (!lilv_plugin_has_feature (self->plugin, in_place_broken_pred))
+ if (!lilv_plugin_has_feature (klass->lv2.plugin, in_place_broken_pred))
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (self), TRUE);
}
@@ -801,7 +450,7 @@ gst_lv2_filter_register_element (GstPlugin * plugin, const gchar * type_name,
GTypeInfo typeinfo = {
sizeof (GstLV2FilterClass),
(GBaseInitFunc) gst_lv2_filter_base_init,
- NULL,
+ (GBaseFinalizeFunc) gst_lv2_filter_base_finalize,
(GClassInitFunc) gst_lv2_filter_class_init,
NULL,
lv2plugin,
@@ -819,7 +468,7 @@ gst_lv2_filter_register_element (GstPlugin * plugin, const gchar * type_name,
/* FIXME: not needed anymore when we can add pad templates, etc in class_init
- * as class_data contains the LADSPA_Descriptor too */
+ * as class_data contains the Descriptor too */
g_type_set_qdata (type, descriptor_quark, lv2plugin);
return gst_element_register (plugin, type_name, GST_RANK_NONE, type);
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;
+}
diff --git a/ext/lv2/gstlv2utils.h b/ext/lv2/gstlv2utils.h
new file mode 100644
index 000000000..c23e115bc
--- /dev/null
+++ b/ext/lv2/gstlv2utils.h
@@ -0,0 +1,111 @@
+/* GStreamer
+ * Copyright (C) 2016 Thibault Saunier <thibault.saunier@collabora.com>
+ * 2016 Stefan Sauer <ensonic@users.sf.net>
+ *
+ * gstlv2utils.h: Header for LV2 plugin utils
+ *
+ * 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.
+ */
+
+#ifndef __GST_LV2_UTILS_H__
+#define __GST_LV2_UTILS_H__
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/audio/audio-channels.h>
+
+#include <lilv/lilv.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstLV2Group GstLV2Group;
+typedef struct _GstLV2Port GstLV2Port;
+
+typedef struct _GstLV2 GstLV2;
+typedef struct _GstLV2Class GstLV2Class;
+
+struct _GstLV2Group
+{
+ gchar *uri; /**< RDF resource (URI or blank node) */
+ guint pad; /**< Gst pad index */
+ gchar *symbol; /**< Gst pad name / LV2 group symbol */
+ GArray *ports; /**< Array of GstLV2Port */
+ gboolean has_roles; /**< TRUE iff all ports have a known role */
+};
+
+struct _GstLV2Port
+{
+ gint index; /**< LV2 port index (on LV2 plugin) */
+ gint pad; /**< Gst pad index (iff not part of a group) */
+ LilvNode *role; /**< Channel position / port role */
+ GstAudioChannelPosition position; /**< Channel position */
+};
+
+struct _GstLV2
+{
+ GstLV2Class *klass;
+
+ LilvInstance *instance;
+
+ gboolean activated;
+ unsigned long rate;
+
+ struct
+ {
+ struct
+ {
+ gfloat *in;
+ gfloat *out;
+ } control;
+ } ports;
+};
+
+struct _GstLV2Class
+{
+ guint properties;
+
+ LilvPlugin *plugin;
+
+ GstLV2Group in_group; /**< Array of GstLV2Group */
+ GstLV2Group out_group; /**< Array of GstLV2Group */
+ GArray *control_in_ports; /**< Array of GstLV2Port */
+ GArray *control_out_ports; /**< Array of GstLV2Port */
+};
+
+
+void gst_lv2_init (GstLV2 * lv2, GstLV2Class * lv2_class);
+void gst_lv2_finalize (GstLV2 * lv2);
+
+gboolean gst_lv2_setup (GstLV2 * lv2, unsigned long rate);
+gboolean gst_lv2_cleanup (GstLV2 * lv2, GstObject *obj);
+
+
+void gst_lv2_object_set_property (GstLV2 * lv2, GObject * object,
+ guint prop_id, const GValue * value, GParamSpec * pspec);
+void gst_lv2_object_get_property (GstLV2 * lv2, GObject * object,
+ guint prop_id, GValue * value, GParamSpec * pspec);
+
+void gst_lv2_class_install_properties (GstLV2Class * lv2_class,
+ GObjectClass * object_class, guint offset);
+void gst_lv2_element_class_set_metadata (GstLV2Class * lv2_class,
+ GstElementClass * elem_class, const gchar * lv2_class_tags);
+
+void gst_lv2_class_init (GstLV2Class * lv2_class, GType type);
+void gst_lv2_class_finalize (GstLV2Class * lv2_class);
+
+G_END_DECLS
+
+#endif /* __GST_LV2_UTILS_H__ */