From a1bf2e17cc2331ff284a89264268f8a5dd074e9a Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Wed, 18 May 2016 21:29:15 -0700 Subject: [PATCH] lv2: support CVPorts CVPorts are ports that take a buffer. For now we just fill the buffers with the control value. --- ext/lv2/README | 3 -- ext/lv2/gstlv2.c | 19 +++++++++ ext/lv2/gstlv2.h | 5 +++ ext/lv2/gstlv2filter.c | 32 +++++++++++---- ext/lv2/gstlv2source.c | 29 +++++++++++--- ext/lv2/gstlv2utils.c | 103 +++++++++++++++++++++++++++++++++++++++---------- ext/lv2/gstlv2utils.h | 15 ++++++- 7 files changed, 169 insertions(+), 37 deletions(-) diff --git a/ext/lv2/README b/ext/lv2/README index 0e456db..98fab55 100644 --- a/ext/lv2/README +++ b/ext/lv2/README @@ -34,9 +34,6 @@ gst-launch-1.0 calf-sourceforge-net-plugins-Organ event-in="C-3" name=s ! interl TODO -* support http://lv2plug.in/ns/lv2core/#CVPort - - these ports need a buffer with the property value - - we should sync, then fill the buffer and connect the port * support presets for pl in $(lv2ls); do if test "$(lv2info "$pl" | grep -A1 "Presets:" | tail -n1)" != ""; then echo "$pl"; fi; done * support more host features diff --git a/ext/lv2/gstlv2.c b/ext/lv2/gstlv2.c index de8f4be..6ad8330 100644 --- a/ext/lv2/gstlv2.c +++ b/ext/lv2/gstlv2.c @@ -42,6 +42,7 @@ #include #include +#include "lv2/lv2plug.in/ns/ext/event/event.h" GST_DEBUG_CATEGORY (lv2_debug); #define GST_CAT_DEFAULT lv2_debug @@ -198,8 +199,19 @@ plugin_init (GstPlugin * plugin) world = lilv_world_new (); lilv_world_load_all (world); +/* have been added after lilv-0.22.0, which is the last release */ +#ifndef LILV_URI_ATOM_PORT +#define LILV_URI_ATOM_PORT "http://lv2plug.in/ns/ext/atom#AtomPort" +#endif +#ifndef LILV_URI_CV_PORT +#define LILV_URI_CV_PORT "http://lv2plug.in/ns/lv2core#CVPort" +#endif + + atom_class = lilv_new_uri (world, LILV_URI_ATOM_PORT); audio_class = lilv_new_uri (world, LILV_URI_AUDIO_PORT); control_class = lilv_new_uri (world, LILV_URI_CONTROL_PORT); + cv_class = lilv_new_uri (world, LILV_URI_CV_PORT); + event_class = lilv_new_uri (world, LILV_URI_EVENT_PORT); input_class = lilv_new_uri (world, LILV_URI_INPUT_PORT); output_class = lilv_new_uri (world, LILV_URI_OUTPUT_PORT); @@ -207,7 +219,9 @@ plugin_init (GstPlugin * plugin) toggled_prop = lilv_new_uri (world, LV2_CORE__toggled); designation_pred = lilv_new_uri (world, LV2_CORE__designation); in_place_broken_pred = lilv_new_uri (world, LV2_CORE__inPlaceBroken); + optional_pred = lilv_new_uri (world, LV2_CORE__optionalFeature); group_pred = lilv_new_uri (world, LV2_PORT_GROUPS__group); + supports_event_pred = lilv_new_uri (world, LV2_EVENT__supportsEvent); center_role = lilv_new_uri (world, LV2_PORT_GROUPS__center); left_role = lilv_new_uri (world, LV2_PORT_GROUPS__left); @@ -275,8 +289,11 @@ __attribute__ ((destructor)) #endif static void plugin_cleanup (GstPlugin * plugin) { + lilv_node_free (atom_class); lilv_node_free (audio_class); lilv_node_free (control_class); + lilv_node_free (cv_class); + lilv_node_free (event_class); lilv_node_free (input_class); lilv_node_free (output_class); @@ -284,7 +301,9 @@ __attribute__ ((destructor)) lilv_node_free (toggled_prop); lilv_node_free (designation_pred); lilv_node_free (in_place_broken_pred); + lilv_node_free (optional_pred); lilv_node_free (group_pred); + lilv_node_free (supports_event_pred); lilv_node_free (center_role); lilv_node_free (left_role); diff --git a/ext/lv2/gstlv2.h b/ext/lv2/gstlv2.h index 340752a..2a98885 100644 --- a/ext/lv2/gstlv2.h +++ b/ext/lv2/gstlv2.h @@ -29,15 +29,20 @@ #include "gstlv2utils.h" LilvWorld *world; +LilvNode *atom_class; LilvNode *audio_class; LilvNode *control_class; +LilvNode *cv_class; +LilvNode *event_class; LilvNode *input_class; LilvNode *output_class; LilvNode *integer_prop; LilvNode *toggled_prop; LilvNode *designation_pred; LilvNode *in_place_broken_pred; +LilvNode *optional_pred; LilvNode *group_pred; +LilvNode *supports_event_pred; LilvNode *center_role; LilvNode *left_role; diff --git a/ext/lv2/gstlv2filter.c b/ext/lv2/gstlv2filter.c index 0f98463..dc7ea90 100644 --- a/ext/lv2/gstlv2filter.c +++ b/ext/lv2/gstlv2filter.c @@ -305,18 +305,19 @@ static GstFlowReturn gst_lv2_filter_transform_data (GstLV2Filter * self, GstMapInfo * in_map, GstMapInfo * out_map) { - GstLV2FilterClass *lv2_class; + GstLV2FilterClass *klass = + (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (self); + GstLV2Class *lv2_class = &klass->lv2; GstLV2Group *lv2_group; GstLV2Port *lv2_port; - guint j, nframes, samples, out_samples; - gfloat *in = NULL, *out = NULL; + guint j, k, l, nframes, samples, out_samples; + gfloat *in = NULL, *out = NULL, *cv = NULL, *mem; + gfloat val; nframes = in_map->size / sizeof (float); - lv2_class = (GstLV2FilterClass *) GST_AUDIO_FILTER_GET_CLASS (self); - /* multi channel inputs */ - lv2_group = &lv2_class->lv2.in_group; + lv2_group = &lv2_class->in_group; samples = nframes / lv2_group->ports->len; in = g_new0 (gfloat, nframes); GST_LOG_OBJECT (self, "in : samples=%u, nframes=%u, ports=%d", samples, @@ -333,7 +334,7 @@ gst_lv2_filter_transform_data (GstLV2Filter * self, } /* multi channel outputs */ - lv2_group = &lv2_class->lv2.out_group; + lv2_group = &lv2_class->out_group; out_samples = nframes / lv2_group->ports->len; out = g_new0 (gfloat, samples * lv2_group->ports->len); GST_LOG_OBJECT (self, "out: samples=%u, nframes=%u, ports=%d", out_samples, @@ -344,6 +345,22 @@ gst_lv2_filter_transform_data (GstLV2Filter * self, out + (j * out_samples)); } + /* cv ports */ + cv = g_new (gfloat, samples * lv2_class->num_cv_in); + for (j = k = 0; j < lv2_class->control_in_ports->len; j++) { + lv2_port = &g_array_index (lv2_class->control_in_ports, GstLV2Port, j); + if (lv2_port->type != GST_LV2_PORT_CV) + continue; + + mem = cv + (k * samples); + val = self->lv2.ports.control.in[j]; + /* FIXME: use gst_control_binding_get_value_array */ + for (l = 0; l < samples; l++) + mem[l] = val; + lilv_instance_connect_port (self->lv2.instance, lv2_port->index, mem); + k++; + } + lilv_instance_run (self->lv2.instance, samples); if (lv2_group->ports->len > 1) @@ -351,6 +368,7 @@ gst_lv2_filter_transform_data (GstLV2Filter * self, (gfloat *) out_map->data, out_samples, out); g_free (out); g_free (in); + g_free (cv); return GST_FLOW_OK; } diff --git a/ext/lv2/gstlv2source.c b/ext/lv2/gstlv2source.c index 23317a2..3d41c04 100644 --- a/ext/lv2/gstlv2source.c +++ b/ext/lv2/gstlv2source.c @@ -292,8 +292,8 @@ gst_lv2_source_fill (GstBaseSrc * base, guint64 offset, guint length, GstBuffer * buffer) { GstLV2Source *lv2 = (GstLV2Source *) base; - GstLV2SourceClass *lv2_class = - (GstLV2SourceClass *) GST_BASE_SRC_GET_CLASS (lv2); + GstLV2SourceClass *klass = (GstLV2SourceClass *) GST_BASE_SRC_GET_CLASS (lv2); + GstLV2Class *lv2_class = &klass->lv2; GstLV2Group *lv2_group; GstLV2Port *lv2_port; GstClockTime next_time; @@ -302,8 +302,9 @@ gst_lv2_source_fill (GstBaseSrc * base, guint64 offset, GstElementClass *eclass; GstMapInfo map; gint samplerate, bpf; - guint j; - gfloat *out = NULL; + guint j, k, l; + gfloat *out = NULL, *cv = NULL, *mem; + gfloat val; /* example for tagging generated data */ if (!lv2->tags_pushed) { @@ -399,7 +400,7 @@ gst_lv2_source_fill (GstBaseSrc * base, guint64 offset, gst_buffer_map (buffer, &map, GST_MAP_WRITE); /* multi channel outputs */ - lv2_group = &lv2_class->lv2.out_group; + lv2_group = &lv2_class->out_group; if (lv2_group->ports->len > 1) { out = g_new0 (gfloat, samples * lv2_group->ports->len); for (j = 0; j < lv2_group->ports->len; ++j) { @@ -415,6 +416,22 @@ gst_lv2_source_fill (GstBaseSrc * base, guint64 offset, GST_LOG_OBJECT (lv2, "connected port 0"); } + /* cv ports */ + cv = g_new (gfloat, samples * lv2_class->num_cv_in); + for (j = k = 0; j < lv2_class->control_in_ports->len; j++) { + lv2_port = &g_array_index (lv2_class->control_in_ports, GstLV2Port, j); + if (lv2_port->type != GST_LV2_PORT_CV) + continue; + + mem = cv + (k * samples); + val = lv2->lv2.ports.control.in[j]; + /* FIXME: use gst_control_binding_get_value_array */ + for (l = 0; l < samples; l++) + mem[l] = val; + lilv_instance_connect_port (lv2->lv2.instance, lv2_port->index, mem); + k++; + } + lilv_instance_run (lv2->lv2.instance, samples); if (lv2_group->ports->len > 1) { @@ -423,6 +440,8 @@ gst_lv2_source_fill (GstBaseSrc * base, guint64 offset, g_free (out); } + g_free (cv); + gst_buffer_unmap (buffer, &map); return GST_FLOW_OK; diff --git a/ext/lv2/gstlv2utils.c b/ext/lv2/gstlv2utils.c index cedd6bc..08d9d94 100644 --- a/ext/lv2/gstlv2utils.c +++ b/ext/lv2/gstlv2utils.c @@ -158,6 +158,8 @@ gboolean gst_lv2_setup (GstLV2 * lv2, unsigned long rate) { GstLV2Class *lv2_class = lv2->klass; + GstLV2Port *port; + GArray *ports; gint i; if (lv2->instance) @@ -168,15 +170,22 @@ gst_lv2_setup (GstLV2 * lv2, unsigned long rate) 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, + ports = lv2_class->control_in_ports; + for (i = 0; i < ports->len; i++) { + port = &g_array_index (ports, GstLV2Port, i); + if (port->type != GST_LV2_PORT_CONTROL) + continue; + lilv_instance_connect_port (lv2->instance, port->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, + } + ports = lv2_class->control_out_ports; + for (i = 0; i < ports->len; i++) { + port = &g_array_index (ports, GstLV2Port, i); + if (port->type != GST_LV2_PORT_CONTROL) + continue; + lilv_instance_connect_port (lv2->instance, port->index, &(lv2->ports.control.out[i])); + } lilv_instance_activate (lv2->instance); lv2->activated = TRUE; @@ -365,7 +374,8 @@ gst_lv2_class_get_param_spec (GstLV2Class * klass, GObjectClass * object_class, 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)) + if (lilv_port_is_a (lv2plugin, port, control_class) || + lilv_port_is_a (lv2plugin, port, cv_class)) perms |= GST_PARAM_CONTROLLABLE; if (lilv_port_has_property (lv2plugin, port, toggled_prop)) { @@ -547,8 +557,6 @@ gst_lv2_class_init (GstLV2Class * lv2_class, GType type) gst_structure_get_value (lv2_meta_all, g_type_name (type)); GstStructure *lv2_meta = g_value_get_boxed (value); const LilvPlugin *lv2plugin; - /* FIXME Handle channels positionning - * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */ guint j, in_pad_index = 0, out_pad_index = 0; const LilvPlugins *plugins = lilv_world_get_all_plugins (world); LilvNode *plugin_uri; @@ -573,8 +581,12 @@ gst_lv2_class_init (GstLV2Class * lv2_class, GType type) 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, }; + const gboolean is_optional = lilv_port_has_property (lv2plugin, port, + optional_pred); + GstLV2Port desc = { j, GST_LV2_PORT_AUDIO, -1, }; LilvNodes *lv2group = lilv_port_get (lv2plugin, port, group_pred); + /* FIXME Handle channels positionning + * GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID; */ if (lv2group) { /* port is part of a group */ @@ -590,7 +602,7 @@ gst_lv2_class_init (GstLV2Class * lv2_class, GType type) /* FIXME Handle channels positionning position = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; - sub_values = lilv_port_get_value (lv2plugin, port, has_role_pred); + sub_values = lilv_port_get_value (lv2plugin, port, designation_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); @@ -606,21 +618,70 @@ gst_lv2_class_init (GstLV2Class * lv2_class, GType type) /* 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) + if (is_input) { + desc.pad = in_pad_index++; g_array_append_val (lv2_class->in_group.ports, desc); - else + } else { + desc.pad = out_pad_index++; g_array_append_val (lv2_class->out_group.ports, desc); + } } else if (lilv_port_is_a (lv2plugin, port, control_class)) { - if (is_input) + desc.type = GST_LV2_PORT_CONTROL; + if (is_input) { + lv2_class->num_control_in++; + g_array_append_val (lv2_class->control_in_ports, desc); + } else { + lv2_class->num_control_out++; + g_array_append_val (lv2_class->control_out_ports, desc); + } + } else if (lilv_port_is_a (lv2plugin, port, cv_class)) { + desc.type = GST_LV2_PORT_CV; + if (is_input) { + lv2_class->num_cv_in++; g_array_append_val (lv2_class->control_in_ports, desc); - else + } else { + lv2_class->num_cv_out++; g_array_append_val (lv2_class->control_out_ports, desc); + } + } else if (lilv_port_is_a (lv2plugin, port, event_class)) { + LilvNodes *supported = lilv_port_get_value (lv2plugin, port, + supports_event_pred); + + GST_INFO ("%s: unhandled event port %d: %s, optional=%d, input=%d", + element_uri, j, + lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)), + is_optional, is_input); + + if (lilv_nodes_size (supported) > 0) { + LilvIter *i; + + for (i = lilv_nodes_begin (supported); + !lilv_nodes_is_end (supported, i); + i = lilv_nodes_next (supported, i)) { + const LilvNode *value = lilv_nodes_get (supported, i); + GST_INFO (" type = %s", lilv_node_as_uri (value)); + } + } + lilv_nodes_free (supported); + // FIXME: handle them } else { - /* unknown port type */ - GST_INFO ("unhandled port %d: %s", j, - lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port))); - continue; + /* unhandled port type */ + const LilvNodes *classes = lilv_port_get_classes (lv2plugin, port); + GST_INFO ("%s: unhandled port %d: %s, optional=%d, input=%d", + element_uri, j, + lilv_node_as_string (lilv_port_get_symbol (lv2plugin, port)), + is_optional, is_input); + if (classes && lilv_nodes_size (classes) > 0) { + LilvIter *i; + + // FIXME: we getting the same classe multiple times + for (i = lilv_nodes_begin (classes); + !lilv_nodes_is_end (classes, i); + i = lilv_nodes_next (classes, i)) { + const LilvNode *value = lilv_nodes_get (classes, i); + GST_INFO (" class = %s", lilv_node_as_uri (value)); + } + } } } } diff --git a/ext/lv2/gstlv2utils.h b/ext/lv2/gstlv2utils.h index 6ff6482..e5213b2 100644 --- a/ext/lv2/gstlv2utils.h +++ b/ext/lv2/gstlv2utils.h @@ -43,13 +43,23 @@ struct _GstLV2Group guint pad; /**< Gst pad index */ gchar *symbol; /**< Gst pad name / LV2 group symbol */ GArray *ports; /**< Array of GstLV2Port */ + /* FIXME: not set as of now */ gboolean has_roles; /**< TRUE iff all ports have a known role */ }; +typedef enum { + GST_LV2_PORT_AUDIO = 0, + GST_LV2_PORT_CONTROL, + GST_LV2_PORT_CV +} GstLV2PortType; + struct _GstLV2Port { gint index; /**< LV2 port index (on LV2 plugin) */ - gint pad; /**< Gst pad index (iff not part of a group) */ + GstLV2PortType type; /**< Port type */ + /**< Gst pad index (iff not part of a group), only for audio ports */ + gint pad; + /* FIXME: not set as of now */ LilvNode *role; /**< Channel position / port role */ GstAudioChannelPosition position; /**< Channel position */ }; @@ -79,6 +89,9 @@ struct _GstLV2Class const LilvPlugin *plugin; + gint num_control_in, num_control_out; + gint num_cv_in, num_cv_out; + GstLV2Group in_group; /**< Array of GstLV2Group */ GstLV2Group out_group; /**< Array of GstLV2Group */ GArray *control_in_ports; /**< Array of GstLV2Port */ -- 2.7.4