summaryrefslogtreecommitdiffstats
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/ladspa/gstladspa.c7
-rw-r--r--ext/lv2/calf-lv2-port-groups.patch71
-rw-r--r--ext/lv2/gstlv2.c487
-rw-r--r--ext/lv2/gstlv2.h33
-rw-r--r--ext/lv2/swh-lv2-port-groups.patch502
5 files changed, 950 insertions, 150 deletions
diff --git a/ext/ladspa/gstladspa.c b/ext/ladspa/gstladspa.c
index b62d4c99..52adcc37 100644
--- a/ext/ladspa/gstladspa.c
+++ b/ext/ladspa/gstladspa.c
@@ -24,7 +24,7 @@
*
* The ladspa (Linux Audio Developer's Simple Plugin API) element is a bridge
* for plugins using the <ulink url="http://www.ladspa.org/">ladspa</ulink> API.
- * It scans all installed ladspa plugins and registeres them as gstreamer
+ * It scans all installed ladspa plugins and registers them as gstreamer
* elements. If available it can also parse lrdf files and use the metadata for
* element classification.
*/
@@ -82,6 +82,7 @@ gst_ladspa_base_init (gpointer g_class)
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class);
GstElementDetails *details;
+ GstAudioChannelPosition mono_position = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
LADSPA_Descriptor *desc;
guint j, audio_in_count, audio_out_count, control_in_count, control_out_count;
gchar *klass_tags;
@@ -125,10 +126,10 @@ gst_ladspa_base_init (gpointer g_class)
if (LADSPA_IS_PORT_INPUT (p))
gst_signal_processor_class_add_pad_template (gsp_class, name,
- GST_PAD_SINK, gsp_class->num_audio_in++);
+ GST_PAD_SINK, gsp_class->num_audio_in++, 1, &mono_position);
else
gst_signal_processor_class_add_pad_template (gsp_class, name,
- GST_PAD_SRC, gsp_class->num_audio_out++);
+ GST_PAD_SRC, gsp_class->num_audio_out++, 1, &mono_position);
g_free (name);
} else if (LADSPA_IS_PORT_CONTROL (p)) {
diff --git a/ext/lv2/calf-lv2-port-groups.patch b/ext/lv2/calf-lv2-port-groups.patch
new file mode 100644
index 00000000..232e3ccb
--- /dev/null
+++ b/ext/lv2/calf-lv2-port-groups.patch
@@ -0,0 +1,71 @@
+diff --git a/src/makerdf.cpp b/src/makerdf.cpp
+index 430fb91..ee82152 100644
+--- a/src/makerdf.cpp
++++ b/src/makerdf.cpp
+@@ -168,16 +168,16 @@ static void add_port(string &ports, const char *symbol, const char *name, const
+ ss << ind << "lv2ev:supportsTimestamp <lv2ev:TimeStamp> ;\n";
+ }
+ if (!strcmp(symbol, "in_l"))
+- ss << ind << "pg:membership [ pg:group <#stereoIn>; pg:role pg:leftChannel ] ;" << endl;
++ ss << ind << "pg:inGroup <#stereoIn> ; pg:role pg:leftChannel ;" << endl;
+ else
+ if (!strcmp(symbol, "in_r"))
+- ss << ind << "pg:membership [ pg:group <#stereoIn>; pg:role pg:rightChannel ] ;" << endl;
++ ss << ind << "pg:inGroup <#stereoIn> ; pg:role pg:rightChannel ;" << endl;
+ else
+ if (!strcmp(symbol, "out_l"))
+- ss << ind << "pg:membership [ pg:group <#stereoOut>; pg:role pg:leftChannel ] ;" << endl;
++ ss << ind << "pg:inGroup <#stereoOut> ; pg:role pg:leftChannel ;" << endl;
+ else
+ if (!strcmp(symbol, "out_r"))
+- ss << ind << "pg:membership [ pg:group <#stereoOut>; pg:role pg:rightChannel ] ;" << endl;
++ ss << ind << "pg:inGroup <#stereoOut> ; pg:role pg:rightChannel ;" << endl;
+ ss << " ]";
+ ports += ss.str();
+ }
+@@ -469,7 +469,7 @@ void make_ttl(string path_prefix)
+ "@prefix lv2midi: <http://lv2plug.in/ns/ext/midi#> .\n"
+ "@prefix lv2ctx: <http://lv2plug.in/ns/dev/contexts#> .\n"
+ "@prefix strport: <http://lv2plug.in/ns/dev/string-port#> .\n"
+- "@prefix pg: <http://ll-plugins.nongnu.org/lv2/ext/portgroups#> .\n"
++ "@prefix pg: <http://lv2plug.in/ns/dev/port-groups#> .\n"
+ "@prefix ue: <http://lv2plug.in/ns/extensions/units#> .\n"
+ "@prefix epp: <http://lv2plug.in/ns/dev/extportinfo#> .\n"
+ "@prefix kf: <http://foltman.com/ns/> .\n"
+@@ -538,7 +538,17 @@ void make_ttl(string path_prefix)
+ ttl += gui_uri + " uiext:portNotification [\n uiext:plugin " + uri + " ;\n uiext:portIndex " + i2s(j) + "\n] .\n\n";
+ }
+ #endif
+-
++
++ if (pi->get_input_count() == 2)
++ ttl += "<#stereoIn> a pg:StereoGroup ;\n lv2:symbol \"in\";\n lv2:name \"Input\" .\n\n";
++
++ if (pi->get_output_count() == 2) {
++ ttl += "<#stereoOut> a pg:StereoGroup ;\n lv2:symbol \"out\" ;\n lv2:name \"Output\"";
++ if (pi->get_input_count() == 2)
++ ttl += " ;\n pg:source <#stereoIn>";
++ ttl += " .\n\n";
++ }
++
+ ttl += uri + " a lv2:Plugin ;\n";
+
+ if (classes.count(lpi.plugin_type))
+@@ -583,12 +593,13 @@ void make_ttl(string path_prefix)
+
+ string ports = "";
+ int pn = 0;
+- const char *in_names[] = { "in_l", "in_r" };
+- const char *out_names[] = { "out_l", "out_r" };
++ const char *in_syms[] = { "in_l", "in_r" };
++ const char *out_syms[] = { "out_l", "out_r" };
++ const char *names[] = { "Left", "Right" };
+ for (int i = 0; i < pi->get_input_count(); i++)
+- add_port(ports, in_names[i], in_names[i], "Input", pn++);
++ add_port(ports, in_syms[i], names[i], "Input", pn++);
+ for (int i = 0; i < pi->get_output_count(); i++)
+- add_port(ports, out_names[i], out_names[i], "Output", pn++);
++ add_port(ports, out_syms[i], names[i], "Output", pn++);
+ for (int i = 0; i < pi->get_param_count(); i++)
+ add_ctl_port(ports, *pi->get_param_props(i), pn++, pi, i);
+ if (pi->get_midi()) {
diff --git a/ext/lv2/gstlv2.c b/ext/lv2/gstlv2.c
index a7617071..7c6e1c3b 100644
--- a/ext/lv2/gstlv2.c
+++ b/ext/lv2/gstlv2.c
@@ -19,13 +19,28 @@
* Boston, MA 02111-1307, USA.
*/
+/**
+ * SECTION:element-lv2
+ * @short_description: bridge for LV2.
+ *
+ * LV2 is a standard for plugins and matching host applications,
+ * mainly targeted at audio processing and generation. It is intended as
+ * a successor to LADSPA (Linux Audio Developer's Simple Plugin API).
+ *
+ * The LV2 element is a bridge for plugins using the
+ * <ulink url="http://www.lv2plug.in/">LV2</ulink> API. It scans all
+ * installed LV2 plugins and registers them as gstreamer elements.
+ */
+
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <math.h>
+#include <glib.h>
#include <gst/audio/audio.h>
#include <gst/controller/gstcontroller.h>
+#include <gst/audio/multichannel.h>
#include "gstlv2.h"
#include <slv2/slv2.h>
@@ -51,6 +66,22 @@ SLV2Value input_class;
SLV2Value output_class;
SLV2Value integer_prop;
SLV2Value toggled_prop;
+SLV2Value in_place_broken_pred;
+SLV2Value in_group_pred;
+SLV2Value has_role_pred;
+SLV2Value lv2_symbol_pred;
+
+SLV2Value center_role;
+SLV2Value left_role;
+SLV2Value right_role;
+SLV2Value rear_center_role;
+SLV2Value rear_left_role;
+SLV2Value rear_right_role;
+SLV2Value lfe_role;
+SLV2Value center_left_role;
+SLV2Value center_right_role;
+SLV2Value side_left_role;
+SLV2Value side_right_role;
static GstSignalProcessorClass *parent_class;
@@ -59,6 +90,82 @@ static GstPlugin *gst_lv2_plugin;
GST_DEBUG_CATEGORY_STATIC (lv2_debug);
#define GST_CAT_DEFAULT lv2_debug
+/** Convert an LV2 port role to a Gst channel positon
+ * WARNING: If the group has only a single port,
+ * GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER will be returned for pg:centerRole
+ * (which is used by LV2 for mono groups), but this is not correct. In this
+ * case the value must be changed to GST_AUDIO_CHANNEL_POSITION_FRONT_MONO
+ * (this can't be done by this function because this information isn't known
+ * at the time it is used).
+ */
+static GstAudioChannelPosition
+gst_lv2_role_to_position (SLV2Value role)
+{
+ /* Front. Mono and left/right are mututally exclusive */
+ if (slv2_value_equals (role, center_role)) {
+
+ return GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+ } else if (slv2_value_equals (role, left_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ } else if (slv2_value_equals (role, right_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+
+ /* Rear. Left/right and center are mututally exclusive */
+ } else if (slv2_value_equals (role, rear_center_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_REAR_CENTER;
+ } else if (slv2_value_equals (role, rear_left_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_REAR_LEFT;
+ } else if (slv2_value_equals (role, rear_right_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT;
+
+ /* Subwoofer/low-frequency-effects */
+ } else if (slv2_value_equals (role, lfe_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_LFE;
+
+ /* Center front speakers. Center and left/right_of_center
+ * are mutually exclusive */
+ } else if (slv2_value_equals (role, center_left_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
+ } else if (slv2_value_equals (role, center_right_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+
+ /* sides */
+ } else if (slv2_value_equals (role, side_left_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT;
+ } else if (slv2_value_equals (role, side_right_role)) {
+ return GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT;
+ }
+
+ return GST_AUDIO_CHANNEL_POSITION_INVALID;
+}
+
+/** Find and return the group @a uri in @a groups, or NULL if not found */
+static GstLV2Group *
+gst_lv2_class_find_group (GArray * groups, SLV2Value uri)
+{
+ int i = 0;
+ for (; i < groups->len; ++i)
+ if (slv2_value_equals (g_array_index (groups, GstLV2Group, i).uri, uri))
+ return &g_array_index (groups, GstLV2Group, i);
+ return NULL;
+}
+
+static GstAudioChannelPosition *
+gst_lv2_build_positions (GstLV2Group * group)
+{
+ int i;
+ GstAudioChannelPosition *positions = NULL;
+ if (group->has_roles) {
+ positions = malloc (group->ports->len * sizeof (GstAudioChannelPosition));
+ for (i = 0; i < group->ports->len; ++i)
+ positions[i] = g_array_index (group->ports, GstLV2Port, i).position;
+
+ // Fix up mono groups (see WARNING above)
+ if (group->ports->len == 1)
+ positions[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
+ }
+ return positions;
+}
static void
gst_lv2_base_init (gpointer g_class)
@@ -69,7 +176,12 @@ gst_lv2_base_init (gpointer g_class)
GstElementDetails *details;
SLV2Plugin lv2plugin;
SLV2Value val;
- guint j, audio_in_count, audio_out_count, control_in_count, control_out_count;
+ SLV2Values values, sub_values;
+ GstLV2Group *group = NULL;
+ GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID;
+ GstAudioChannelPosition mono_position = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
+ GstAudioChannelPosition *positions = NULL;
+ guint j, in_pad_index = 0, out_pad_index = 0;
gchar *klass_tags;
GST_DEBUG ("base_init %p", g_class);
@@ -79,39 +191,147 @@ gst_lv2_base_init (gpointer g_class)
g_assert (lv2plugin);
- /* pad templates */
+ gsp_class->num_group_in = 0;
+ gsp_class->num_group_out = 0;
gsp_class->num_audio_in = 0;
gsp_class->num_audio_out = 0;
- /* properties */
gsp_class->num_control_in = 0;
gsp_class->num_control_out = 0;
+ klass->in_groups = g_array_new (FALSE, TRUE, sizeof (GstLV2Group));
+ klass->out_groups = g_array_new (FALSE, TRUE, sizeof (GstLV2Group));
+ klass->audio_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ klass->audio_out_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ klass->control_in_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ klass->control_out_ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+
+ /* find ports and groups */
for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) {
- SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j);
- if (slv2_port_is_a (lv2plugin, port, audio_class)) {
- gchar *name =
- g_strdup (slv2_value_as_string (slv2_port_get_symbol (lv2plugin,
- port)));
-
- GST_DEBUG ("LV2 port name: \"%s\"", name);
-
- if (slv2_port_is_a (lv2plugin, port, input_class))
- gst_signal_processor_class_add_pad_template (gsp_class, name,
- GST_PAD_SINK, gsp_class->num_audio_in++);
- else if (slv2_port_is_a (lv2plugin, port, output_class))
- gst_signal_processor_class_add_pad_template (gsp_class, name,
- GST_PAD_SRC, gsp_class->num_audio_out++);
- /* TODO: else ignore plugin */
-
- g_free (name);
- } else if (slv2_port_is_a (lv2plugin, port, control_class)) {
- if (slv2_port_is_a (lv2plugin, port, input_class))
- gsp_class->num_control_in++;
- else if (slv2_port_is_a (lv2plugin, port, output_class))
- gsp_class->num_control_out++;
- /* TODO: else ignore plugin */
+ const SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j);
+ const gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class);
+ struct _GstLV2Port desc = { j, 0 };
+ values = slv2_port_get_value (lv2plugin, port, in_group_pred);
+
+ if (slv2_values_size (values) > 0) {
+ /* port is part of a group */
+ SLV2Value group_uri = slv2_values_get_at (values, 0);
+ GArray *groups = is_input ? klass->in_groups : klass->out_groups;
+ GstLV2Group *group = gst_lv2_class_find_group (groups, group_uri);
+ if (group == NULL) {
+ GstLV2Group g;
+ g.uri = slv2_value_duplicate (group_uri);
+ g.pad = is_input ? in_pad_index++ : out_pad_index++;
+ g.ports = g_array_new (FALSE, TRUE, sizeof (GstLV2Port));
+ g.has_roles = TRUE;
+ sub_values = slv2_plugin_get_value_for_subject (lv2plugin, group_uri,
+ lv2_symbol_pred);
+ if (slv2_values_size (sub_values) > 0)
+ g.symbol = slv2_value_duplicate (slv2_values_get_at (sub_values, 0));
+ else
+ g.symbol = NULL;
+ slv2_values_free (sub_values);
+
+ g_array_append_val (groups, g);
+ group = &g_array_index (groups, GstLV2Group, groups->len - 1);
+ }
+
+ position = GST_AUDIO_CHANNEL_POSITION_INVALID;
+ sub_values = slv2_port_get_value (lv2plugin, port, has_role_pred);
+ if (slv2_values_size (sub_values) > 0) {
+ SLV2Value role = slv2_values_get_at (sub_values, 0);
+ position = gst_lv2_role_to_position (role);
+ slv2_values_free (sub_values);
+ }
+ if (position != GST_AUDIO_CHANNEL_POSITION_INVALID) {
+ desc.position = position;
+ } else {
+ group->has_roles = FALSE;
+ }
+
+ g_array_append_val (group->ports, desc);
+
+ } else {
+ /* port is not part of a group */
+ if (slv2_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->audio_in_ports, desc);
+ else
+ g_array_append_val (klass->audio_out_ports, desc);
+ } else if (slv2_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 */
+ continue;
+ }
}
- /* TODO: else ignore plugin */
+ slv2_values_free (values);
+ }
+
+ gsp_class->num_group_in = klass->in_groups->len;
+ gsp_class->num_group_out = klass->out_groups->len;
+ gsp_class->num_audio_in = klass->audio_in_ports->len;
+ gsp_class->num_audio_out = klass->audio_out_ports->len;
+ gsp_class->num_control_in = klass->control_in_ports->len;
+ gsp_class->num_control_out = klass->control_out_ports->len;
+
+ /* add input group pad templates */
+ for (j = 0; j < gsp_class->num_group_in; ++j) {
+ group = &g_array_index (klass->in_groups, GstLV2Group, j);
+ if (group->has_roles) {
+ positions = gst_lv2_build_positions (group);
+ }
+
+ gst_signal_processor_class_add_pad_template (gsp_class,
+ slv2_value_as_string (group->symbol),
+ GST_PAD_SINK, j, group->ports->len, positions);
+
+ if (group->has_roles) {
+ free (positions);
+ positions = NULL;
+ }
+ }
+
+ /* add output group pad templates */
+ for (j = 0; j < gsp_class->num_group_out; ++j) {
+ group = &g_array_index (klass->out_groups, GstLV2Group, j);
+ if (group->has_roles) {
+ positions = gst_lv2_build_positions (group);
+ }
+
+ gst_signal_processor_class_add_pad_template (gsp_class,
+ slv2_value_as_string (group->symbol),
+ GST_PAD_SRC, j, group->ports->len, positions);
+
+ if (group->has_roles) {
+ free (positions);
+ positions = NULL;
+ }
+ }
+
+ /* add non-grouped input port pad templates */
+ for (j = 0; j < gsp_class->num_audio_in; ++j) {
+ struct _GstLV2Port *desc =
+ &g_array_index (klass->audio_in_ports, GstLV2Port, j);
+ SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, desc->index);
+ const gchar *name =
+ slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
+ gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
+ j, 1, &mono_position);
+ }
+
+ /* add non-grouped output port pad templates */
+ for (j = 0; j < gsp_class->num_audio_out; ++j) {
+ struct _GstLV2Port *desc =
+ &g_array_index (klass->audio_out_ports, GstLV2Port, j);
+ SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, desc->index);
+ const gchar *name =
+ slv2_value_as_string (slv2_port_get_symbol (lv2plugin, port));
+ gst_signal_processor_class_add_pad_template (gsp_class, name, GST_PAD_SINK,
+ j, 1, &mono_position);
}
/* construct the element details struct */
@@ -149,36 +369,8 @@ gst_lv2_base_init (gpointer g_class)
g_free (details->author);
g_free (details);
- klass->audio_in_portnums = g_new0 (gint, gsp_class->num_audio_in);
- klass->audio_out_portnums = g_new0 (gint, gsp_class->num_audio_out);
- klass->control_in_portnums = g_new0 (gint, gsp_class->num_control_in);
- klass->control_out_portnums = g_new0 (gint, gsp_class->num_control_out);
-
- audio_in_count = audio_out_count = control_in_count = control_out_count = 0;
-
- for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) {
- SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j);
- gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class);
- if (slv2_port_is_a (lv2plugin, port, audio_class)) {
- if (is_input)
- klass->audio_in_portnums[audio_in_count++] = j;
- else
- klass->audio_out_portnums[audio_out_count++] = j;
- } else if (slv2_port_is_a (lv2plugin, port, control_class)) {
- if (is_input)
- klass->control_in_portnums[control_in_count++] = j;
- else
- klass->control_out_portnums[control_out_count++] = j;
- }
- }
-
- g_assert (audio_in_count == gsp_class->num_audio_in);
- g_assert (audio_out_count == gsp_class->num_audio_out);
- g_assert (control_in_count == gsp_class->num_control_in);
- g_assert (control_out_count == gsp_class->num_control_out);
-
- /*if (!LV2_IS_INPLACE_BROKEN (desc->Properties))
- GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass); */
+ if (!slv2_plugin_has_feature (lv2plugin, in_place_broken_pred))
+ GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass);
klass->plugin = lv2plugin;
}
@@ -225,6 +417,10 @@ gst_lv2_class_get_param_spec (GstLV2Class * klass, gint portnum)
if (lv2max)
upper = slv2_value_as_float (lv2max);
+ slv2_value_free (lv2def);
+ slv2_value_free (lv2min);
+ slv2_value_free (lv2max);
+
if (def < lower) {
GST_WARNING ("%s has lower bound %f > default %f\n",
slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), lower, def);
@@ -274,7 +470,8 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
for (i = 0; i < gsp_class->num_control_in; i++) {
GParamSpec *p;
- p = gst_lv2_class_get_param_spec (klass, klass->control_in_portnums[i]);
+ p = gst_lv2_class_get_param_spec (klass,
+ g_array_index (klass->control_in_ports, GstLV2Port, i).index);
/* properties have an offset of 1 */
g_object_class_install_property (G_OBJECT_CLASS (klass), i + 1, p);
@@ -283,7 +480,8 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
for (i = 0; i < gsp_class->num_control_out; i++) {
GParamSpec *p;
- p = gst_lv2_class_get_param_spec (klass, klass->control_out_portnums[i]);
+ p = gst_lv2_class_get_param_spec (klass,
+ g_array_index (klass->control_out_ports, GstLV2Port, i).index);
/* properties have an offset of 1, and we already added num_control_in */
g_object_class_install_property (G_OBJECT_CLASS (klass),
@@ -294,19 +492,15 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
static void
gst_lv2_init (GstLV2 * lv2, GstLV2Class * klass)
{
-#if 0
lv2->plugin = klass->plugin;
lv2->instance = NULL;
lv2->activated = FALSE;
- lv2->inplace_broken = LV2_IS_INPLACE_BROKEN (lv2->descriptor->Properties);
-#endif
}
static void
gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
-#if 0
GstSignalProcessor *gsp;
GstSignalProcessorClass *gsp_class;
@@ -322,7 +516,7 @@ gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value,
/* now see what type it is */
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:
- gsp->control_in[prop_id] = g_value_get_boolean (value) ? 1.f : 0.f;
+ gsp->control_in[prop_id] = g_value_get_boolean (value) ? 0.0f : 1.0f;
break;
case G_TYPE_INT:
gsp->control_in[prop_id] = g_value_get_int (value);
@@ -333,14 +527,12 @@ gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value,
default:
g_assert_not_reached ();
}
-#endif
}
static void
gst_lv2_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
-#if 0
GstSignalProcessor *gsp;
GstSignalProcessorClass *gsp_class;
gfloat *controls;
@@ -363,7 +555,7 @@ gst_lv2_get_property (GObject * object, guint prop_id, GValue * value,
/* now see what type it is */
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:
- g_value_set_boolean (value, controls[prop_id] > 0.5);
+ 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));
@@ -374,64 +566,54 @@ gst_lv2_get_property (GObject * object, guint prop_id, GValue * value,
default:
g_return_if_reached ();
}
-#endif
}
static gboolean
gst_lv2_setup (GstSignalProcessor * gsp, guint sample_rate)
{
-#if 0
- GstLV2 *ladspa;
+ GstLV2 *lv2;
GstLV2Class *oclass;
GstSignalProcessorClass *gsp_class;
- LV2_Descriptor *desc;
- int i;
+ gint i;
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
- ladspa = (GstLV2 *) gsp;
+ lv2 = (GstLV2 *) gsp;
oclass = (GstLV2Class *) gsp_class;
- desc = ladspa->descriptor;
- g_return_val_if_fail (ladspa->handle == NULL, FALSE);
- g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
+ g_return_val_if_fail (lv2->activated == FALSE, FALSE);
- GST_DEBUG_OBJECT (ladspa, "instantiating the plugin at %d Hz", sample_rate);
+ GST_DEBUG_OBJECT (lv2, "instantiating the plugin at %d Hz", sample_rate);
- ladspa->handle = desc->instantiate (desc, sample_rate);
+ lv2->instance = slv2_plugin_instantiate (oclass->plugin, sample_rate, NULL);
- g_return_val_if_fail (ladspa->handle != NULL, FALSE);
+ g_return_val_if_fail (lv2->instance != NULL, FALSE);
/* connect the control ports */
for (i = 0; i < gsp_class->num_control_in; i++)
- desc->connect_port (ladspa->handle,
- oclass->control_in_portnums[i], &(gsp->control_in[i]));
+ slv2_instance_connect_port (lv2->instance,
+ g_array_index (oclass->control_in_ports, GstLV2Port, i).index,
+ &(gsp->control_in[i]));
for (i = 0; i < gsp_class->num_control_out; i++)
- desc->connect_port (ladspa->handle,
- oclass->control_out_portnums[i], &(gsp->control_out[i]));
-#endif
+ slv2_instance_connect_port (lv2->instance,
+ g_array_index (oclass->control_out_ports, GstLV2Port, i).index,
+ &(gsp->control_out[i]));
+
return TRUE;
}
static gboolean
gst_lv2_start (GstSignalProcessor * gsp)
{
-#if 0
- GstLV2 *ladspa;
- LV2_Descriptor *desc;
+ GstLV2 *lv2 = (GstLV2 *) gsp;
- ladspa = (GstLV2 *) gsp;
- desc = ladspa->descriptor;
+ g_return_val_if_fail (lv2->activated == FALSE, FALSE);
+ g_return_val_if_fail (lv2->instance != NULL, FALSE);
- g_return_val_if_fail (ladspa->activated == FALSE, FALSE);
- g_return_val_if_fail (ladspa->handle != NULL, FALSE);
+ GST_DEBUG_OBJECT (lv2, "activating");
- GST_DEBUG_OBJECT (ladspa, "activating");
+ slv2_instance_activate (lv2->instance);
- if (desc->activate)
- desc->activate (ladspa->handle);
-
- ladspa->activated = TRUE;
-#endif
+ lv2->activated = TRUE;
return TRUE;
}
@@ -439,71 +621,78 @@ gst_lv2_start (GstSignalProcessor * gsp)
static void
gst_lv2_stop (GstSignalProcessor * gsp)
{
-#if 0
- GstLV2 *ladspa;
- LV2_Descriptor *desc;
+ GstLV2 *lv2 = (GstLV2 *) gsp;
- ladspa = (GstLV2 *) gsp;
- desc = ladspa->descriptor;
+ g_return_if_fail (lv2->activated == TRUE);
+ g_return_if_fail (lv2->instance != NULL);
- g_return_if_fail (ladspa->activated == TRUE);
- g_return_if_fail (ladspa->handle != NULL);
+ GST_DEBUG_OBJECT (lv2, "deactivating");
- GST_DEBUG_OBJECT (ladspa, "deactivating");
+ slv2_instance_deactivate (lv2->instance);
- if (desc->activate)
- desc->activate (ladspa->handle);
-
- ladspa->activated = FALSE;
-#endif
+ lv2->activated = FALSE;
}
static void
gst_lv2_cleanup (GstSignalProcessor * gsp)
{
-#if 0
- GstLV2 *ladspa;
- LV2_Descriptor *desc;
-
- ladspa = (GstLV2 *) gsp;
- desc = ladspa->descriptor;
+ GstLV2 *lv2 = (GstLV2 *) gsp;
- g_return_if_fail (ladspa->activated == FALSE);
- g_return_if_fail (ladspa->handle != NULL);
+ g_return_if_fail (lv2->activated == FALSE);
+ g_return_if_fail (lv2->instance != NULL);
- GST_DEBUG_OBJECT (ladspa, "cleaning up");
+ GST_DEBUG_OBJECT (lv2, "cleaning up");
- if (desc->cleanup)
- desc->cleanup (ladspa->handle);
+ slv2_instance_free (lv2->instance);
- ladspa->handle = NULL;
-#endif
+ lv2->instance = NULL;
}
static void
gst_lv2_process (GstSignalProcessor * gsp, guint nframes)
{
-#if 0
GstSignalProcessorClass *gsp_class;
- GstLV2 *ladspa;
+ GstLV2 *lv2;
GstLV2Class *oclass;
- LV2_Descriptor *desc;
- guint i;
+ GstLV2Group *lv2_group;
+ GstLV2Port *lv2_port;
+ GstSignalProcessorGroup *gst_group;
+ guint i, j;
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp);
- ladspa = (GstLV2 *) gsp;
+ lv2 = (GstLV2 *) gsp;
oclass = (GstLV2Class *) gsp_class;
- desc = ladspa->descriptor;
- for (i = 0; i < gsp_class->num_audio_in; i++)
- desc->connect_port (ladspa->handle, oclass->audio_in_portnums[i],
+ for (i = 0; i < gsp_class->num_group_in; i++) {
+ lv2_group = &g_array_index (oclass->in_groups, GstLV2Group, i);
+ gst_group = &gsp->group_in[i];
+ for (j = 0; j < lv2_group->ports->len; ++j) {
+ lv2_port = &g_array_index (lv2_group->ports, GstLV2Port, j);
+ slv2_instance_connect_port (lv2->instance, lv2_port->index,
+ gst_group->buffer + (j * nframes));
+ }
+ }
+ for (i = 0; i < gsp_class->num_audio_in; i++) {
+ lv2_port = &g_array_index (oclass->audio_in_ports, GstLV2Port, i);
+ slv2_instance_connect_port (lv2->instance, lv2_port->index,
gsp->audio_in[i]);
- for (i = 0; i < gsp_class->num_audio_out; i++)
- desc->connect_port (ladspa->handle, oclass->audio_out_portnums[i],
+ }
+ for (i = 0; i < gsp_class->num_group_out; i++) {
+ lv2_group = &g_array_index (oclass->out_groups, GstLV2Group, i);
+ gst_group = &gsp->group_out[i];
+ for (j = 0; j < lv2_group->ports->len; ++j) {
+ lv2_port = &g_array_index (lv2_group->ports, GstLV2Port, j);
+ slv2_instance_connect_port (lv2->instance, lv2_port->index,
+ gst_group->buffer + (j * nframes));
+ }
+ }
+ for (i = 0; i < gsp_class->num_audio_out; i++) {
+ lv2_port = &g_array_index (oclass->audio_out_ports, GstLV2Port, i);
+ slv2_instance_connect_port (lv2->instance, lv2_port->index,
gsp->audio_out[i]);
+ }
- desc->run (ladspa->handle, nframes);
-#endif
+ slv2_instance_run (lv2->instance, nframes);
}
/* search the plugin path
@@ -511,7 +700,7 @@ gst_lv2_process (GstSignalProcessor * gsp, guint nframes)
static gboolean
lv2_plugin_discover (void)
{
- unsigned i;
+ guint i;
SLV2Plugins plugins = slv2_world_get_all_plugins (world);
for (i = 0; i < slv2_plugins_size (plugins); ++i) {
SLV2Plugin lv2plugin = slv2_plugins_get_at (plugins, i);
@@ -569,10 +758,28 @@ plugin_init (GstPlugin * plugin)
control_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_CONTROL);
input_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_INPUT);
output_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_OUTPUT);
- integer_prop =
- slv2_value_new_uri (world, "http://lv2plug.in/ns/lv2core#integer");
- toggled_prop =
- slv2_value_new_uri (world, "http://lv2plug.in/ns/lv2core#toggled");
+
+#define NS_LV2 "http://lv2plug.in/ns/lv2core#"
+#define NS_PG "http://lv2plug.in/ns/dev/port-groups#"
+
+ integer_prop = slv2_value_new_uri (world, NS_LV2 "integer");
+ toggled_prop = slv2_value_new_uri (world, NS_LV2 "toggled");
+ in_place_broken_pred = slv2_value_new_uri (world, NS_LV2 "inPlaceBroken");
+ in_group_pred = slv2_value_new_uri (world, NS_PG "inGroup");
+ has_role_pred = slv2_value_new_uri (world, NS_PG "role");
+ lv2_symbol_pred = slv2_value_new_string (world, NS_LV2 "symbol");
+
+ center_role = slv2_value_new_uri (world, NS_PG "centerChannel");
+ left_role = slv2_value_new_uri (world, NS_PG "leftChannel");
+ right_role = slv2_value_new_uri (world, NS_PG "rightChannel");
+ rear_center_role = slv2_value_new_uri (world, NS_PG "rearCenterChannel");
+ rear_left_role = slv2_value_new_uri (world, NS_PG "rearLeftChannel");
+ rear_right_role = slv2_value_new_uri (world, NS_PG "rearRightChannel");
+ lfe_role = slv2_value_new_uri (world, NS_PG "lfeChannel");
+ center_left_role = slv2_value_new_uri (world, NS_PG "centerLeftChannel");
+ center_right_role = slv2_value_new_uri (world, NS_PG "centerRightChannel");
+ side_left_role = slv2_value_new_uri (world, NS_PG "sideLeftChannel");
+ side_right_role = slv2_value_new_uri (world, NS_PG "sideRightChannel");
parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR);
diff --git a/ext/lv2/gstlv2.h b/ext/lv2/gstlv2.h
index 389c03d1..85e4532e 100644
--- a/ext/lv2/gstlv2.h
+++ b/ext/lv2/gstlv2.h
@@ -46,16 +46,32 @@ typedef struct _lv2_control_info {
typedef struct _GstLV2 GstLV2;
typedef struct _GstLV2Class GstLV2Class;
+typedef struct _GstLV2Group GstLV2Group;
+typedef struct _GstLV2Port GstLV2Port;
struct _GstLV2 {
GstSignalProcessor parent;
- SLV2Plugin *plugin;
- SLV2Instance *instance;
+ SLV2Plugin plugin;
+ SLV2Instance instance;
gboolean activated;
- gboolean inplace_broken;
+};
+
+struct _GstLV2Group {
+ SLV2Value uri; /**< RDF resource (URI or blank node) */
+ guint pad; /**< Gst pad index */
+ SLV2Value 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) */
+ SLV2Value role; /**< Channel position / port role */
+ GstAudioChannelPosition position; /**< Channel position */
};
struct _GstLV2Class {
@@ -63,10 +79,13 @@ struct _GstLV2Class {
SLV2Plugin plugin;
- gint *audio_in_portnums;
- gint *audio_out_portnums;
- gint *control_in_portnums;
- gint *control_out_portnums;
+ GArray *in_groups; /**< Array of GstLV2Group */
+ GArray *out_groups; /**< Array of GstLV2Group */
+ GArray *audio_in_ports; /**< Array of GstLV2Port */
+ GArray *audio_out_ports; /**< Array of GstLV2Port */
+ GArray *control_in_ports; /**< Array of GstLV2Port */
+ GArray *control_out_ports; /**< Array of GstLV2Port */
+
};
diff --git a/ext/lv2/swh-lv2-port-groups.patch b/ext/lv2/swh-lv2-port-groups.patch
new file mode 100644
index 00000000..f841b3bb
--- /dev/null
+++ b/ext/lv2/swh-lv2-port-groups.patch
@@ -0,0 +1,502 @@
+diff -ur swh-lv2-1.0.15/plugins/dj_eq-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/dj_eq-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/dj_eq-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/dj_eq-swh.lv2/plugin.xml 2009-07-03 16:55:25.000000000 -0400
+@@ -140,6 +140,9 @@
+ *(plugin_data->latency) = 3; //XXX is this right?
+ ]]></callback>
+
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
++
+ <port label="lo" dir="input" type="control" hint="default_0">
+ <name>Lo gain (dB)</name>
+ <p>Controls the gain of the low (100Hz) peak/dip band</p>
+@@ -158,18 +161,18 @@
+ <range min="-70" max="+6"/>
+ </port>
+
+- <port label="left_input" dir="input" type="audio">
++ <port label="left_input" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>Input L</name>
+ </port>
+- <port label="right_input" dir="input" type="audio">
++ <port label="right_input" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>Input R</name>
+ </port>
+
+- <port label="left_output" dir="output" type="audio">
++ <port label="left_output" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>Output L</name>
+ </port>
+
+- <port label="right_output" dir="output" type="audio">
++ <port label="right_output" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>Output R</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/gverb-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/gverb-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/gverb-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/gverb-swh.lv2/plugin.xml 2009-06-14 22:05:18.000000000 -0400
+@@ -119,16 +119,20 @@
+ <p>The level of the classic reverb tail reflections.</p>
+ <range min="-70" max="0"/>
+ </port>
++
++ <group label="in" type="MonoGroup"/>
+
+- <port label="input" dir="input" type="audio">
++ <port label="input" dir="input" type="audio" group="in" role="centerChannel">
+ <name>Input</name>
+ </port>
++
++ <group label="out" type="StereoGroup" source="in"/>
+
+- <port label="outl" dir="output" type="audio">
++ <port label="outl" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Left output</name>
+ </port>
+
+- <port label="outr" dir="output" type="audio">
++ <port label="outr" dir="output" type="audio" group="out" role="rightChannel">
+ <name>Right output</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/karaoke-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/karaoke-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/karaoke-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/karaoke-swh.lv2/plugin.xml 2009-07-03 16:55:05.000000000 -0400
+@@ -33,19 +33,22 @@
+ <range min="-70" max="0"/>
+ </port>
+
+- <port label="lin" dir="input" type="audio">
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
++
++ <port label="lin" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>Left in</name>
+ </port>
+
+- <port label="rin" dir="input" type="audio">
++ <port label="rin" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>Right in</name>
+ </port>
+
+- <port label="lout" dir="output" type="audio">
++ <port label="lout" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>Left out</name>
+ </port>
+
+- <port label="rout" dir="output" type="audio">
++ <port label="rout" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>Right out</name>
+ </port>
+ </plugin>
+diff -ur swh-lv2-1.0.15/plugins/lcr_delay-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/lcr_delay-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/lcr_delay-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/lcr_delay-swh.lv2/plugin.xml 2009-07-03 16:56:09.000000000 -0400
+@@ -197,20 +197,23 @@
+ <p>The ammounts of the input and effect mixed to produce the output.</p>
+ <range min="0" max="1"/>
+ </port>
++
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
+
+- <port label="in_l" dir="input" type="audio">
++ <port label="in_l" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>L input</name>
+ </port>
+
+- <port label="in_r" dir="input" type="audio">
++ <port label="in_r" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>R input</name>
+ </port>
+
+- <port label="out_l" dir="output" type="audio">
++ <port label="out_l" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>L output</name>
+ </port>
+
+- <port label="out_r" dir="output" type="audio">
++ <port label="out_r" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>R output</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/matrix_ms_st-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/matrix_ms_st-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/matrix_ms_st-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/matrix_ms_st-swh.lv2/plugin.xml 2009-06-14 22:05:13.000000000 -0400
+@@ -27,17 +27,21 @@
+ <range min="0" max="2" />
+ </port>
+
+- <port label="mid" dir="input" type="audio">
++ <group label="in" type="MidSideGroup"/>
++
++ <port label="mid" dir="input" type="audio" group="in" role="centerChannel">
+ <name>Mid</name>
+ </port>
+- <port label="side" dir="input" type="audio">
++ <port label="side" dir="input" type="audio" group="in" role="sideChannel">
+ <name>Side</name>
+ </port>
++
++ <group label="out" type="StereoGroup" source="in"/>
+
+- <port label="left" dir="output" type="audio">
++ <port label="left" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Left</name>
+ </port>
+- <port label="right" dir="output" type="audio">
++ <port label="right" dir="output" type="audio" group="out" role="rightChannel">
+ <name>Right</name>
+ </port>
+ </plugin>
+diff -ur swh-lv2-1.0.15/plugins/matrix_spatialiser-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/matrix_spatialiser-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/matrix_spatialiser-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/matrix_spatialiser-swh.lv2/plugin.xml 2009-07-03 16:56:24.000000000 -0400
+@@ -142,10 +142,13 @@
+ plugin_data->current_s_gain = current_s_gain;
+ ]]></callback>
+
+- <port label="i_left" dir="input" type="audio">
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
++
++ <port label="i_left" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>Input L</name>
+ </port>
+- <port label="i_right" dir="input" type="audio">
++ <port label="i_right" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>Input R</name>
+ </port>
+ <port label="width" dir="input" type="control" hint="integer,default_0">
+@@ -158,10 +161,12 @@
+ ]]></p>
+ </port>
+
+- <port label="o_left" dir="output" type="audio">
++
++
++ <port label="o_left" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>Output L</name>
+ </port>
+- <port label="o_right" dir="output" type="audio">
++ <port label="o_right" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>Output R</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/matrix_st_ms-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/matrix_st_ms-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/matrix_st_ms-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/matrix_st_ms-swh.lv2/plugin.xml 2009-06-14 22:05:10.000000000 -0400
+@@ -21,17 +21,21 @@
+ }
+ ]]></callback>
+
+- <port label="left" dir="input" type="audio">
++ <group label="in" type="StereoGroup"/>
++
++ <group label="out" type="MidSideGroup" source="in"/>
++
++ <port label="left" dir="input" type="audio" group="in" role="leftChannel">
+ <name>Left</name>
+ </port>
+- <port label="right" dir="input" type="audio">
++ <port label="right" dir="input" type="audio" group="in" role="rightChannel">
+ <name>Right</name>
+ </port>
+
+- <port label="mid" dir="output" type="audio">
++ <port label="mid" dir="output" type="audio" group="out" role="centerChannel">
+ <name>Mid</name>
+ </port>
+- <port label="side" dir="output" type="audio">
++ <port label="side" dir="output" type="audio" group="out" role="sideChannel">
+ <name>Side</name>
+ </port>
+ </plugin>
+diff -ur swh-lv2-1.0.15/plugins/plate-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/plate-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/plate-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/plate-swh.lv2/plugin.xml 2009-06-14 22:05:16.000000000 -0400
+@@ -109,15 +109,19 @@
+ <range min="0" max="1"/>
+ </port>
+
+- <port label="input" dir="input" type="audio">
++ <group label="in" type="MonoGroup"/>
++
++ <group label="out" type="StereoGroup" source="in"/>
++
++ <port label="input" dir="input" type="audio" group="in" role="centerChannel">
+ <name>Input</name>
+ </port>
+
+- <port label="outputl" dir="output" type="audio">
++ <port label="outputl" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Left output</name>
+ </port>
+
+- <port label="outputr" dir="output" type="audio">
++ <port label="outputr" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Right output</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/sc3-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/sc3-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/sc3-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/sc3-swh.lv2/plugin.xml 2009-07-03 16:56:34.000000000 -0400
+@@ -137,23 +137,28 @@
+ <range min="0" max="1"/>
+ </port>
+
+- <port label="sidechain" dir="input" type="audio">
++ <group label="sidechain" type="MonoGroup" sidechain-of="main_in"/>
++
++ <port label="sidechain" dir="input" type="audio" group="sidechain" role="centerChannel">
+ <name>Sidechain</name>
+ </port>
++
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
+
+- <port label="left_in" dir="input" type="audio">
++ <port label="left_in" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>Left input</name>
+ </port>
+
+- <port label="right_in" dir="input" type="audio">
++ <port label="right_in" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>Right input</name>
+ </port>
+
+- <port label="left_out" dir="output" type="audio">
++ <port label="left_out" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>Left output</name>
+ </port>
+
+- <port label="right_out" dir="output" type="audio">
++ <port label="right_out" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>Right output</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/sc4-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/sc4-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/sc4-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/sc4-swh.lv2/plugin.xml 2009-07-03 16:54:48.000000000 -0400
+@@ -168,19 +168,22 @@
+ <range min="-24" max="0"/>
+ </port>
+
+- <port label="left_in" dir="input" type="audio">
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
++
++ <port label="left_in" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>Left input</name>
+ </port>
+
+- <port label="right_in" dir="input" type="audio">
++ <port label="right_in" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>Right input</name>
+ </port>
+
+- <port label="left_out" dir="output" type="audio">
++ <port label="left_out" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>Left output</name>
+ </port>
+
+- <port label="right_out" dir="output" type="audio">
++ <port label="right_out" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>Right output</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/se4-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/se4-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/se4-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/se4-swh.lv2/plugin.xml 2009-07-03 16:55:59.000000000 -0400
+@@ -164,20 +164,23 @@
+ <p>The degree of gain expansion applied to the input signal, in decibels.</p>
+ <range min="0" max="+24"/>
+ </port>
++
++ <group label="main_in" type="StereoGroup"/>
++ <group label="main_out" type="StereoGroup"/>
+
+- <port label="left_in" dir="input" type="audio">
++ <port label="left_in" dir="input" type="audio" group="main_in" role="leftChannel">
+ <name>Left input</name>
+ </port>
+
+- <port label="right_in" dir="input" type="audio">
++ <port label="right_in" dir="input" type="audio" group="main_in" role="rightChannel">
+ <name>Right input</name>
+ </port>
+
+- <port label="left_out" dir="output" type="audio">
++ <port label="left_out" dir="output" type="audio" group="main_out" role="leftChannel">
+ <name>Left output</name>
+ </port>
+
+- <port label="right_out" dir="output" type="audio">
++ <port label="right_out" dir="output" type="audio" group="main_out" role="rightChannel">
+ <name>Right output</name>
+ </port>
+
+Only in swh-lv2-port-groups/plugins/single_para-swh.lv2: .plugin.xml.swp
+diff -ur swh-lv2-1.0.15/plugins/split-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/split-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/split-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/split-swh.lv2/plugin.xml 2009-06-14 22:05:51.000000000 -0400
+@@ -23,17 +23,21 @@
+ }
+ </callback>
+
+- <port label="input" dir="input" type="audio">
++ <group label="in" type="MonoGroup"/>
++
++ <group label="out" type="StereoGroup" source="in"/>
++
++ <port label="input" dir="input" type="audio" group="in" role="centerChannel">
+ <name>Input</name>
+ <range min="-1" max="+1"/>
+ </port>
+
+- <port label="out2" dir="output" type="audio">
++ <port label="out2" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Output 1</name>
+ <range min="-1" max="+1"/>
+ </port>
+
+- <port label="out1" dir="output" type="audio">
++ <port label="out1" dir="output" type="audio" group="out" role="rightChannel">
+ <name>Output 2</name>
+ <range min="-1" max="+1"/>
+ </port>
+diff -ur swh-lv2-1.0.15/plugins/surround_encoder-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/surround_encoder-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/surround_encoder-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/surround_encoder-swh.lv2/plugin.xml 2009-06-14 22:05:23.000000000 -0400
+@@ -111,33 +111,37 @@
+ plugin_data->buffer_pos = buffer_pos;
+ ]]></callback>
+
+- <port label="l" dir="input" type="audio">
++ <port label="l" dir="input" type="audio" group="in" role="leftChannel">
+ <name>L</name>
+ <p>Left channel input. Can be treated as per normal stereo recoding, except that the speaker should be at -22.5$^\circ$, rather than the normal stereo -30$^\circ$.</p>
+ </port>
+
+- <port label="r" dir="input" type="audio">
++ <group label="in" type="FourPointZeroGroup"/>
++
++ <port label="r" dir="input" type="audio" group="in" role="rightChannel">
+ <name>R</name>
+ <p>Right channel input. As per left channel.</p>
+ </port>
+
+- <port label="c" dir="input" type="audio">
++ <port label="c" dir="input" type="audio" group="in" role="centerChannel">
+ <name>C</name>
+ <p>Center channel input. Will be directly in front of the listener, stereo and mono compatible.</p>
+ </port>
+
+- <port label="s" dir="input" type="audio">
++ <port label="s" dir="input" type="audio" group="in" role="surroundChannel">
+ <name>S</name>
+ <p>Surround channel. Should sound from the rear speakers, may also leak into the left and right. Has slight delay and bandwidth reduction (cut below 100 Hz, and above 7 KHz) for leakage and noise reduction and enhanced psychoacoustic effects.</p>
+
+ <p>Not mono compatible.</p>
+ </port>
++
++ <group label="out" type="StereoGroup" encoded-type="FourPointZeroGroup" source="in"/>
+
+- <port label="lt" dir="output" type="audio">
++ <port label="lt" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Lt</name>
+ </port>
+
+- <port label="rt" dir="output" type="audio">
++ <port label="rt" dir="output" type="audio" group="out" role="rightChannel">
+ <name>Rt</name>
+ </port>
+
+diff -ur swh-lv2-1.0.15/plugins/xfade-swh.lv2/plugin.xml swh-lv2-port-groups/plugins/xfade-swh.lv2/plugin.xml
+--- swh-lv2-1.0.15/plugins/xfade-swh.lv2/plugin.xml 2008-01-09 16:02:08.000000000 -0500
++++ swh-lv2-port-groups/plugins/xfade-swh.lv2/plugin.xml 2009-06-14 22:02:49.000000000 -0400
+@@ -34,24 +34,30 @@
+ <range min="-1" max="1"/>
+ </port>
+
+- <port label="inputLA" dir="input" type="audio">
++ <group label="in1" type="StereoGroup"/>
++
++ <group label="in2" type="StereoGroup"/>
++
++ <port label="inputLA" dir="input" type="audio" group="in1" role="leftChannel">
+ <name>Input A left</name>
+ </port>
+- <port label="inputRA" dir="input" type="audio">
++ <port label="inputRA" dir="input" type="audio" group="in1" role="rightChannel">
+ <name>Input A right</name>
+ </port>
+
+- <port label="inputLB" dir="input" type="audio">
++ <port label="inputLB" dir="input" type="audio" group="in2" role="leftChannel">
+ <name>Input B left</name>
+ </port>
+- <port label="inputRB" dir="input" type="audio">
++ <port label="inputRB" dir="input" type="audio" group="in2" role="rightChannel">
+ <name>Input B right</name>
+ </port>
++
++ <group label="out" type="StereoGroup"/>
+
+- <port label="outputL" dir="output" type="audio">
++ <port label="outputL" dir="output" type="audio" group="out" role="leftChannel">
+ <name>Output left</name>
+ </port>
+- <port label="outputR" dir="output" type="audio">
++ <port label="outputR" dir="output" type="audio" group="out" role="rightChannel">
+ <name>Output right</name>
+ </port>
+ </plugin>
+Only in swh-lv2-port-groups: swh-lv2-port-groups.patch
+diff -ur swh-lv2-1.0.15/xslt/turtle.xsl swh-lv2-port-groups/xslt/turtle.xsl
+--- swh-lv2-1.0.15/xslt/turtle.xsl 2008-01-09 16:02:07.000000000 -0500
++++ swh-lv2-port-groups/xslt/turtle.xsl 2009-06-16 23:33:26.000000000 -0400
+@@ -6,8 +6,20 @@
+ @prefix foaf: &lt;http://xmlns.com/foaf/0.1/&gt; .
+ @prefix doap: &lt;http://usefulinc.com/ns/doap#&gt; .
+ @prefix swhext: &lt;http://plugin.org.uk/extensions#&gt; .
++@prefix pg: &lt;http://lv2plug.in/ns/dev/port-groups#&gt; .
+ <xsl:for-each select="ladspa/plugin">
+-swh:<xsl:value-of select="@label"/> a :Plugin ;
++ <xsl:variable name="pluglabel" select="@label"/>
++ <xsl:for-each select="group">
++ <xsl:variable name="grouplabel" select="@label"/>
++ <xsl:variable name="groupuri">
++ <xsl:value-of select="$pluglabel"/>-<xsl:value-of select="$grouplabel"/>
++ </xsl:variable>
++swh:<xsl:value-of select="$groupuri"/> a pg:Group ;
++ a pg:<xsl:value-of select="@type"/> ;
++ :symbol "<xsl:value-of select="$grouplabel"/>"<xsl:if test="@source"> ;
++ pg:source swh:<xsl:value-of select="$pluglabel"/>-<xsl:value-of select="@source"/></xsl:if> .
++ </xsl:for-each>
++swh:<xsl:value-of select="$pluglabel"/> a :Plugin ;
+ <xsl:call-template name="csl2type">
+ <xsl:with-param name="in" select="@class"/>
+ </xsl:call-template>
+@@ -22,7 +34,8 @@
+ <!-- <xsl:if test="p">swhext:documentation """<xsl:value-of select="p"/>""" ;
+ </xsl:if>-->
+ <xsl:for-each select="/ladspa/global/meta">
+- <xsl:if test="@name = 'properties' and @value = 'HARD_RT_CAPABLE'">:pluginProperty :hardRtCapable ;
++ <xsl:if test="@name = 'properties' and @value = 'HARD_RT_CAPABLE'">
++ :pluginProperty :hardRtCapable ;
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="port">
+@@ -64,6 +77,9 @@
+ :portProperty :sampleRate ;</xsl:if>
+ <xsl:if test="contains(@hint, 'toggled')">
+ :portProperty :toggled ;</xsl:if>
++ <xsl:if test="@group">
++ pg:inGroup swh:<xsl:value-of select="$pluglabel"/>-<xsl:value-of select="@group"/> ;
++ pg:role pg:<xsl:value-of select="@role"/> ;</xsl:if>
+ <!-- <xsl:if test="p">
+ swhext:documentation """<xsl:value-of select="p"/>""" ;</xsl:if>-->
+ ] ;