mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
lv2: support CVPorts
CVPorts are ports that take a buffer. For now we just fill the buffers with the control value.
This commit is contained in:
parent
c66d3906a4
commit
a1bf2e17cc
7 changed files with 169 additions and 37 deletions
|
@ -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
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <gst/audio/audio-channels.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/port-groups/port-groups.h>
|
||||
#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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
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;
|
||||
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)) {
|
||||
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 {
|
||||
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 {
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue