diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | ext/ladspa/gstladspa.c | 7 | ||||
-rw-r--r-- | ext/lv2/calf-lv2-port-groups.patch | 71 | ||||
-rw-r--r-- | ext/lv2/gstlv2.c | 702 | ||||
-rw-r--r-- | ext/lv2/gstlv2.h | 49 | ||||
-rw-r--r-- | ext/lv2/swh-lv2-port-groups.patch | 502 | ||||
-rw-r--r-- | gst-libs/gst/signalprocessor/gstsignalprocessor.c | 143 | ||||
-rw-r--r-- | gst-libs/gst/signalprocessor/gstsignalprocessor.h | 31 |
8 files changed, 1317 insertions, 190 deletions
diff --git a/configure.ac b/configure.ac index 45b33ffb..ce6a560a 100644 --- a/configure.ac +++ b/configure.ac @@ -999,7 +999,7 @@ AG_GST_CHECK_FEATURE(LADSPA, [ladspa], ladspa, [ dnl *** LV2 *** translit(dnm, m, l) AM_CONDITIONAL(USE_LV2, true) AG_GST_CHECK_FEATURE(LV2, [lv2], lv2, [ - PKG_CHECK_MODULES(SLV2, slv2 >= 0.6.0, HAVE_LV2="yes", HAVE_LV2="no") + PKG_CHECK_MODULES(SLV2, slv2 >= 0.6.6, HAVE_LV2="yes", HAVE_LV2="no") AC_SUBST(SLV2_CFLAGS) AC_SUBST(SLV2_LIBS) ]) 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..4b99f939 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> @@ -45,12 +60,28 @@ static void gst_lv2_cleanup (GstSignalProcessor * sigproc); static void gst_lv2_process (GstSignalProcessor * sigproc, guint nframes); static SLV2World world; -SLV2Value audio_class; -SLV2Value control_class; -SLV2Value input_class; -SLV2Value output_class; -SLV2Value integer_prop; -SLV2Value toggled_prop; +static SLV2Value audio_class; +static SLV2Value control_class; +static SLV2Value input_class; +static SLV2Value output_class; +static SLV2Value integer_prop; +static SLV2Value toggled_prop; +static SLV2Value in_place_broken_pred; +static SLV2Value in_group_pred; +static SLV2Value has_role_pred; +static SLV2Value lv2_symbol_pred; + +static SLV2Value center_role; +static SLV2Value left_role; +static SLV2Value right_role; +static SLV2Value rear_center_role; +static SLV2Value rear_left_role; +static SLV2Value rear_right_role; +static SLV2Value lfe_role; +static SLV2Value center_left_role; +static SLV2Value center_right_role; +static SLV2Value side_left_role; +static SLV2Value side_right_role; static GstSignalProcessorClass *parent_class; @@ -59,6 +90,89 @@ 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 gint +gst_lv2_compare_symbol (gconstpointer a, gconstpointer b, gpointer data) +{ + return strcmp (slv2_value_as_string ((SLV2Value) a), + slv2_value_as_string ((SLV2Value) b)); +} static void gst_lv2_base_init (gpointer g_class) @@ -69,8 +183,16 @@ 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 = NULL, sub_values = NULL; + SLV2Value prev = NULL; + 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, in_prop_index = 0; gchar *klass_tags; + GTree *control_port_indices = g_tree_new_full (gst_lv2_compare_symbol, + NULL, NULL, free); GST_DEBUG ("base_init %p", g_class); @@ -79,41 +201,236 @@ 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); + gboolean in_group = FALSE; + struct _GstLV2Port desc; + desc.index = j; + desc.pad = 0; + desc.role = NULL; + desc.position = GST_AUDIO_CHANNEL_POSITION_INVALID; + + /* we only care about audio port groups (for multi-channel) */ + if (slv2_port_is_a (lv2plugin, port, audio_class)) + values = slv2_port_get_value (lv2plugin, port, in_group_pred); + else + values = NULL; + + 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); + in_group = TRUE; + 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; + g.symbol = NULL; + sub_values = slv2_plugin_get_value_for_subject (lv2plugin, group_uri, + lv2_symbol_pred); + /* symbol is mandatory */ + if (slv2_values_size (sub_values) > 0) { + g.symbol = slv2_value_duplicate (slv2_values_get_at (sub_values, 0)); + if (!gst_element_class_get_pad_template (element_class, + slv2_value_as_string (g.symbol))) { + g_array_append_val (groups, g); + group = &g_array_index (groups, GstLV2Group, groups->len - 1); + assert (group); + assert (slv2_value_equals (group->uri, group_uri)); + } else { + GST_WARNING ("plugin %s has duplicate group symbol '%s'\n", + slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), + slv2_value_as_string (g.symbol)); + in_group = FALSE; + } + } else { + GST_WARNING ("plugin %s has illegal group with no symbol\n", + slv2_value_as_string (slv2_plugin_get_uri (lv2plugin))); + in_group = FALSE; + } + } + + if (in_group) { + 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; + g_array_append_val (group->ports, desc); + } else { + in_group = FALSE; + } + } + } + + if (!in_group) { + /* 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 (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) { + gint *prop_num = malloc (sizeof (gint)); + desc.pad = in_prop_index++; + *prop_num = desc.pad; + g_tree_insert (control_port_indices, slv2_port_get_symbol (lv2plugin, + port), prop_num); + 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); + } + + /* find presets */ + values = slv2_plugin_query_sparql (lv2plugin, + "PREFIX pset: <http://lv2plug.in/ns/dev/presets#>\n" + "PREFIX dc: <http://dublincore.org/documents/dcmi-namespace/>\n" + "SELECT ?preset ?name ?sym ?val WHERE {\n" + " <> pset:hasPreset ?preset .\n" + " ?preset dc:title ?name ;\n" + " lv2:port ?port .\n" + " ?port lv2:symbol ?sym ;\n" + " pset:value ?val .\n" "}\n"); + for (; !slv2_results_finished (values); slv2_results_next (values)) { + SLV2Value preset = slv2_results_get_binding_value (values, 0); + SLV2Value name = slv2_results_get_binding_value (values, 1); + SLV2Value sym = slv2_results_get_binding_value (values, 2); + SLV2Value val = slv2_results_get_binding_value (values, 3); + GstLV2Preset *p = NULL; + const gint *index = NULL; + GstLV2PresetValue preset_val; + + if (!klass->presets) + klass->presets = g_array_new (FALSE, TRUE, sizeof (GstLV2Preset)); + + if (!slv2_value_equals (prev, preset)) { + struct _GstLV2Preset new_preset; + new_preset.uri = preset; + new_preset.name = name; + new_preset.values = NULL; + g_array_append_val (klass->presets, new_preset); + } + p = &g_array_index (klass->presets, GstLV2Preset, klass->presets->len - 1); + prev = p->uri; + + /* we don't understand non-float port values */ + if (!slv2_value_is_float (val)) { + continue; + } + + /* look up port by symbol (we need the index) */ + index = (gint *) g_tree_lookup (control_port_indices, sym); + if (!index) { + /* we don't know about this port, just ignore it */ + continue; + } + + preset_val.index = *index; + preset_val.value = slv2_value_as_float (val); + if (!p->values) + p->values = g_array_new (FALSE, TRUE, sizeof (GstLV2PresetValue)); + g_array_append_val (p->values, preset_val); } + g_tree_destroy (control_port_indices); + /* construct the element details struct */ details = g_new0 (GstElementDetails, 1); val = slv2_plugin_get_name (lv2plugin); @@ -149,36 +466,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 +514,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); @@ -252,7 +545,8 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin) { GObjectClass *gobject_class; GstSignalProcessorClass *gsp_class; - gint i; + GParamSpec *preset_spec; + gint i, offset = 1; GST_DEBUG ("class_init %p", klass); @@ -269,60 +563,105 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin) klass->plugin = lv2plugin; - /* register properties */ + /* register preset property */ + if (klass->presets) { + gint perms = + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | + GST_PARAM_CONTROLLABLE; + preset_spec = + g_param_spec_int ("preset", "preset", "preset", 0, + klass->presets->len - 1, 0, perms); + g_object_class_install_property (G_OBJECT_CLASS (klass), 1, preset_spec); + + /* if there is a preset property, then port properties are offset by 2 + * relative to lv2 port index (rather than 1) */ + offset = 2; + } + + /* register control port properties */ 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); + /* control properties have an offset of (offset) */ + g_object_class_install_property (G_OBJECT_CLASS (klass), i + offset, p); } 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 */ + /* control properties have an offset of (offset), and we already added num_control_in */ g_object_class_install_property (G_OBJECT_CLASS (klass), - gsp_class->num_control_in + i + 1, p); + gsp_class->num_control_in + i + offset, p); } } 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 + lv2->current_preset = 0; } static void gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { -#if 0 GstSignalProcessor *gsp; GstSignalProcessorClass *gsp_class; + GstLV2 *lv2; + GstLV2Class *lv2_class; + gint i; gsp = GST_SIGNAL_PROCESSOR (object); gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object); + lv2 = (GstLV2 *) gsp; + lv2_class = (GstLV2Class *) gsp_class; - /* remember, properties have an offset of 1 */ + /* remember, properties have an offset of 1, or 2 if there are presets */ prop_id--; + if (lv2_class->presets) { + if (prop_id == 0) { + GstLV2Preset *preset; + gint num = g_value_get_int (value); + if (num > lv2_class->presets->len) { + GST_WARNING ("Preset number out of range\n"); + return; + } + preset = &g_array_index (lv2_class->presets, GstLV2Preset, num); + + /* apply preset controls */ + for (i = 0; i < preset->values->len; ++i) { + GstLV2PresetValue *preset_value = + &g_array_index (preset->values, GstLV2PresetValue, i); + GstLV2Port *desc = + &g_array_index (lv2_class->control_in_ports, GstLV2Port, + preset_value->index); + gsp->control_in[prop_id] = preset_value->value; + } + lv2->current_preset = num; + return; + } else { + prop_id--; + } + } + /* only input ports */ g_return_if_fail (prop_id < gsp_class->num_control_in); /* 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,23 +672,33 @@ 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; + GstLV2 *lv2; + GstLV2Class *lv2_class; gfloat *controls; gsp = GST_SIGNAL_PROCESSOR (object); gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object); + lv2 = (GstLV2 *) gsp; + lv2_class = (GstLV2Class *) gsp_class; /* remember, properties have an offset of 1 */ prop_id--; + if (lv2_class->presets != NULL) { + if (prop_id == 0) { + g_value_set_int (value, lv2->current_preset); + return; + } else { + prop_id--; + } + } if (prop_id < gsp_class->num_control_in) { controls = gsp->control_in; @@ -363,7 +712,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 +723,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; - - ladspa = (GstLV2 *) gsp; - desc = ladspa->descriptor; + GstLV2 *lv2 = (GstLV2 *) gsp; - g_return_val_if_fail (ladspa->activated == FALSE, FALSE); - g_return_val_if_fail (ladspa->handle != NULL, FALSE); + g_return_val_if_fail (lv2->activated == FALSE, FALSE); + g_return_val_if_fail (lv2->instance != NULL, FALSE); - GST_DEBUG_OBJECT (ladspa, "activating"); + GST_DEBUG_OBJECT (lv2, "activating"); - if (desc->activate) - desc->activate (ladspa->handle); + slv2_instance_activate (lv2->instance); - ladspa->activated = TRUE; -#endif + lv2->activated = TRUE; return TRUE; } @@ -439,71 +778,78 @@ gst_lv2_start (GstSignalProcessor * gsp) static void gst_lv2_stop (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 == TRUE); - g_return_if_fail (ladspa->handle != NULL); + g_return_if_fail (lv2->activated == TRUE); + g_return_if_fail (lv2->instance != NULL); - GST_DEBUG_OBJECT (ladspa, "deactivating"); + GST_DEBUG_OBJECT (lv2, "deactivating"); - if (desc->activate) - desc->activate (ladspa->handle); + slv2_instance_deactivate (lv2->instance); - 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 +857,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 +915,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); @@ -581,6 +945,38 @@ plugin_init (GstPlugin * plugin) return lv2_plugin_discover (); } +#ifdef __GNUC__ +__attribute__ ((destructor)) +#endif + static void plugin_cleanup (GstPlugin * plugin) +{ + slv2_value_free (audio_class); + slv2_value_free (control_class); + slv2_value_free (input_class); + slv2_value_free (output_class); + + slv2_value_free (integer_prop); + slv2_value_free (toggled_prop); + slv2_value_free (in_place_broken_pred); + slv2_value_free (in_group_pred); + slv2_value_free (has_role_pred); + slv2_value_free (lv2_symbol_pred); + + slv2_value_free (center_role); + slv2_value_free (left_role); + slv2_value_free (right_role); + slv2_value_free (rear_center_role); + slv2_value_free (rear_left_role); + slv2_value_free (rear_right_role); + slv2_value_free (lfe_role); + slv2_value_free (center_left_role); + slv2_value_free (center_right_role); + slv2_value_free (side_left_role); + slv2_value_free (side_right_role); + + slv2_world_free (world); +} + GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "lv2", diff --git a/ext/lv2/gstlv2.h b/ext/lv2/gstlv2.h index 389c03d1..ab9c8ae8 100644 --- a/ext/lv2/gstlv2.h +++ b/ext/lv2/gstlv2.h @@ -46,16 +46,47 @@ typedef struct _lv2_control_info { typedef struct _GstLV2 GstLV2; typedef struct _GstLV2Class GstLV2Class; +typedef struct _GstLV2Group GstLV2Group; +typedef struct _GstLV2Port GstLV2Port; +typedef struct _GstLV2Preset GstLV2Preset; +typedef struct _GstLV2PresetValue GstLV2PresetValue; struct _GstLV2 { GstSignalProcessor parent; - SLV2Plugin *plugin; - SLV2Instance *instance; + SLV2Plugin plugin; + SLV2Instance instance; gboolean activated; - gboolean inplace_broken; + gint current_preset; +}; + +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 (iff not part of a group) or property index */ + SLV2Value role; /**< Channel position / port role */ + GstAudioChannelPosition position; /**< Channel position */ +}; + + +struct _GstLV2PresetValue { + gint index; /**< LV2 port index */ + gfloat value; /**< Port value */ +}; + +struct _GstLV2Preset { + SLV2Value uri; + SLV2Value name; + GArray *values; /**< Array of GstLV2PresetValue */ }; struct _GstLV2Class { @@ -63,10 +94,14 @@ 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 */ + + GArray *presets; /**< Array of GstLV2Preset */ }; 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: <http://xmlns.com/foaf/0.1/> . + @prefix doap: <http://usefulinc.com/ns/doap#> . + @prefix swhext: <http://plugin.org.uk/extensions#> . ++@prefix pg: <http://lv2plug.in/ns/dev/port-groups#> . + <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>--> + ] ; diff --git a/gst-libs/gst/signalprocessor/gstsignalprocessor.c b/gst-libs/gst/signalprocessor/gstsignalprocessor.c index d057ce85..4cf69e35 100644 --- a/gst-libs/gst/signalprocessor/gstsignalprocessor.c +++ b/gst-libs/gst/signalprocessor/gstsignalprocessor.c @@ -31,13 +31,14 @@ * 1. store each received buffer on the pad and decrement pending_in * 2. when pending_in==0, process as much as we can and push outputs * - * In pull mode (gst_signal_processor_getrange) is operates as follows: + * In pull mode (gst_signal_processor_getrange) it operates as follows: * 1. if there is an output ready, deliver * 2. otherwise pull from each sink-pad, process requested frames and deliver * the buffer */ #include <stdlib.h> +#include <string.h> #ifdef HAVE_CONFIG_H # include "config.h" @@ -50,10 +51,6 @@ GST_DEBUG_CATEGORY_STATIC (gst_signal_processor_debug); #define GST_CAT_DEFAULT gst_signal_processor_debug -/* FIXME: this is mono only */ -static GstStaticCaps template_caps = -GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS); - #define GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE \ (gst_signal_processor_pad_template_get_type ()) #define GST_SIGNAL_PROCESSOR_PAD_TEMPLATE(obj) \ @@ -67,6 +64,7 @@ struct _GstSignalProcessorPadTemplate GstPadTemplate parent; guint index; + guint channels; }; static GType @@ -92,11 +90,14 @@ gst_signal_processor_pad_template_get_type (void) * @name: pad name * @direction: pad direction (src/sink) * @index: index for the pad per direction (starting from 0) + * @channels: number of channels in this pad + * @positions: array of channel positions in order * */ void gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass, - const gchar * name, GstPadDirection direction, guint index) + const gchar * name, GstPadDirection direction, guint index, guint channels, + const GstAudioChannelPosition * pos) { GstPadTemplate *new; GstCaps *caps; @@ -105,15 +106,19 @@ gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass, g_return_if_fail (name != NULL); g_return_if_fail (direction == GST_PAD_SRC || direction == GST_PAD_SINK); - /* FIXME: would be nice to have the template as a parameter, right now this can - * only create mono pads */ - caps = gst_caps_copy (gst_static_caps_get (&template_caps)); + caps = gst_caps_new_simple ("audio/x-raw-float", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "width", G_TYPE_INT, 32, "channels", G_TYPE_INT, channels, NULL); + + if (pos) + gst_audio_set_caps_channel_positions_list (caps, pos, channels); new = g_object_new (gst_signal_processor_pad_template_get_type (), "name", name, "name-template", name, "direction", direction, "presence", GST_PAD_ALWAYS, "caps", caps, NULL); GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (new)->index = index; + GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (new)->channels = channels; gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), new); } @@ -135,6 +140,9 @@ struct _GstSignalProcessorPad /* index for the pad per direction (starting from 0) */ guint index; + /* number of channels for the pad */ + guint channels; + /* these are only used for sink pads */ guint samples_avail; /* available mono sample frames */ gfloat *data; /* data pointer to read from / write to */ @@ -211,6 +219,8 @@ gst_signal_processor_add_pad_from_template (GstSignalProcessor * self, "template", templ, NULL); GST_SIGNAL_PROCESSOR_PAD (new)->index = GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (templ)->index; + GST_SIGNAL_PROCESSOR_PAD (new)->channels = + GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (templ)->channels; gst_pad_set_setcaps_function (new, GST_DEBUG_FUNCPTR (gst_signal_processor_setcaps)); @@ -247,20 +257,21 @@ gst_signal_processor_init (GstSignalProcessor * self, while (templates) { GstPadTemplate *templ = GST_PAD_TEMPLATE (templates->data); - gst_signal_processor_add_pad_from_template (self, templ); templates = templates->next; } self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; + self->group_in = g_new0 (GstSignalProcessorGroup, klass->num_group_in); + self->group_out = g_new0 (GstSignalProcessorGroup, klass->num_group_out); self->audio_in = g_new0 (gfloat *, klass->num_audio_in); - self->control_in = g_new0 (gfloat, klass->num_control_in); self->audio_out = g_new0 (gfloat *, klass->num_audio_out); + self->control_in = g_new0 (gfloat, klass->num_control_in); self->control_out = g_new0 (gfloat, klass->num_control_out); /* init */ - self->pending_in = klass->num_audio_in; + self->pending_in = klass->num_group_in + klass->num_audio_in; self->pending_out = 0; self->sample_rate = 0; @@ -271,12 +282,16 @@ gst_signal_processor_finalize (GObject * object) { GstSignalProcessor *self = GST_SIGNAL_PROCESSOR (object); + g_free (self->group_in); + self->group_in = NULL; + g_free (self->group_out); + self->group_out = NULL; g_free (self->audio_in); self->audio_in = NULL; - g_free (self->control_in); - self->control_in = NULL; g_free (self->audio_out); self->audio_out = NULL; + g_free (self->control_in); + self->control_in = NULL; g_free (self->control_out); self->control_out = NULL; @@ -372,6 +387,7 @@ static void gst_signal_processor_cleanup (GstSignalProcessor * self) { GstSignalProcessorClass *klass; + gint i; klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); @@ -382,6 +398,16 @@ gst_signal_processor_cleanup (GstSignalProcessor * self) if (klass->cleanup) klass->cleanup (self); + for (i = 0; i < klass->num_group_in; ++i) { + g_free (self->group_in[i].buffer); + memset (&self->group_in[i], '\0', sizeof (GstSignalProcessorGroup)); + } + + for (i = 0; i < klass->num_group_out; ++i) { + g_free (self->group_out[i].buffer); + memset (&self->group_in[i], '\0', sizeof (GstSignalProcessorGroup)); + } + self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; } @@ -506,6 +532,36 @@ impossible: } } +/** De-interleave a pad (gstreamer => plugin) */ +static void +gst_signal_processor_deinterleave_group (GstSignalProcessorGroup * group, + guint nframes) +{ + guint i, j; + g_assert (group->nframes == nframes); + g_assert (group->interleaved_buffer); + g_assert (group->buffer); + for (i = 0; i < nframes; ++i) + for (j = 0; j < group->channels; ++j) + group->buffer[(j * nframes) + i] + = group->interleaved_buffer[(i * group->channels) + j]; +} + +/** Interleave a pad (plugin => gstreamer) */ +static void +gst_signal_processor_interleave_group (GstSignalProcessorGroup * group, + guint nframes) +{ + guint i, j; + g_assert (group->nframes == nframes); + g_assert (group->interleaved_buffer); + g_assert (group->buffer); + for (i = 0; i < nframes; ++i) + for (j = 0; j < group->channels; ++j) + group->interleaved_buffer[(i * group->channels) + j] + = group->buffer[(j * nframes) + i]; +} + static gboolean gst_signal_processor_event (GstPad * pad, GstEvent * event) { @@ -546,18 +602,38 @@ gst_signal_processor_prepare (GstSignalProcessor * self, guint nframes) GstSignalProcessorClass *klass; GList *sinks, *srcs; guint samples_avail = nframes; + guint i, in_group_index = 0, out_group_index = 0; klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); /* first, assign audio_in pointers, and determine the number of samples that * we can process */ - for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { + i = 0; + for (sinks = elem->sinkpads; sinks; sinks = sinks->next, ++i) { GstSignalProcessorPad *sinkpad; sinkpad = (GstSignalProcessorPad *) sinks->data; g_assert (sinkpad->samples_avail > 0); samples_avail = MIN (samples_avail, sinkpad->samples_avail); - self->audio_in[sinkpad->index] = sinkpad->data; + if (sinkpad->channels > 1) { + GstSignalProcessorGroup *group = &self->group_in[in_group_index++]; + group->interleaved_buffer = sinkpad->data; + /* allocate buffer for de-interleaving */ + if (!group->buffer || group->channels < sinkpad->channels + || group->nframes < samples_avail) { + group->buffer = + (gfloat *) g_realloc (group->buffer, + samples_avail * sinkpad->channels * sizeof (gfloat)); + memset (group->buffer, '\0', + samples_avail * sinkpad->channels * sizeof (gfloat)); + } + g_assert (group->buffer); + group->channels = sinkpad->channels; + group->nframes = samples_avail; + gst_signal_processor_deinterleave_group (group, samples_avail); + } else { + self->audio_in[sinkpad->index] = sinkpad->data; + } } /* FIXME: return if samples_avail==0 ? */ @@ -574,7 +650,9 @@ gst_signal_processor_prepare (GstSignalProcessor * self, guint nframes) sinkpad = (GstSignalProcessorPad *) sinks->data; srcpad = (GstSignalProcessorPad *) srcs->data; - if (GST_BUFFER_SIZE (sinkpad->pen) == samples_avail * sizeof (gfloat)) { + if (sinkpad->channels == 1 && sinkpad->channels == srcpad->channels + && GST_BUFFER_SIZE (sinkpad->pen) == + samples_avail * sizeof (gfloat)) { /* reusable, yay */ g_assert (sinkpad->samples_avail == samples_avail); srcpad->pen = sinkpad->pen; @@ -600,11 +678,24 @@ gst_signal_processor_prepare (GstSignalProcessor * self, guint nframes) ret = gst_pad_alloc_buffer_and_set_caps (GST_PAD (srcpad), -1, - samples_avail * sizeof (gfloat), self->caps, &srcpad->pen); + samples_avail * srcpad->channels * sizeof (gfloat), self->caps, + &srcpad->pen); if (ret != GST_FLOW_OK) { self->flow_state = ret; return 0; + } else if (srcpad->channels > 1) { + GstSignalProcessorGroup *group = &self->group_out[out_group_index++]; + group->interleaved_buffer = (gfloat *) GST_BUFFER_DATA (srcpad->pen); + if (!group->buffer || group->channels < srcpad->channels + || group->nframes < samples_avail) + group->buffer = + (gfloat *) g_realloc (group->buffer, + samples_avail * srcpad->channels * sizeof (gfloat)); + g_assert (group->buffer); + group->channels = srcpad->channels; + group->nframes = samples_avail; + self->pending_out++; } else { self->audio_out[srcpad->index] = (gfloat *) GST_BUFFER_DATA (srcpad->pen); self->pending_out++; @@ -643,12 +734,22 @@ gst_signal_processor_update_inputs (GstSignalProcessor * self, guint nprocessed) /* advance ->data pointers and decrement ->samples_avail, unreffing buffer if no samples are left */ sinkpad->samples_avail -= nprocessed; - sinkpad->data += nprocessed; /* gfloat* arithmetic */ + sinkpad->data += nprocessed * sinkpad->channels; /* gfloat* arithmetic */ } } } static void +gst_signal_processor_update_outputs (GstSignalProcessor * self, + guint nprocessed) +{ + GstSignalProcessorClass *klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + guint i; + for (i = 0; i < klass->num_group_out; ++i) + gst_signal_processor_interleave_group (&self->group_out[i], nprocessed); +} + +static void gst_signal_processor_process (GstSignalProcessor * self, guint nframes) { GstElement *elem; @@ -672,6 +773,7 @@ gst_signal_processor_process (GstSignalProcessor * self, guint nframes) klass->process (self, nframes); gst_signal_processor_update_inputs (self, nframes); + gst_signal_processor_update_outputs (self, nframes); return; @@ -695,7 +797,8 @@ gst_signal_processor_pen_buffer (GstSignalProcessor * self, GstPad * pad, /* keep the reference */ spad->pen = buffer; spad->data = (gfloat *) GST_BUFFER_DATA (buffer); - spad->samples_avail = GST_BUFFER_SIZE (buffer) / sizeof (float); + spad->samples_avail = + GST_BUFFER_SIZE (buffer) / sizeof (float) / spad->channels; g_assert (self->pending_in != 0); diff --git a/gst-libs/gst/signalprocessor/gstsignalprocessor.h b/gst-libs/gst/signalprocessor/gstsignalprocessor.h index f9fac73f..b7daf7e5 100644 --- a/gst-libs/gst/signalprocessor/gstsignalprocessor.h +++ b/gst-libs/gst/signalprocessor/gstsignalprocessor.h @@ -25,6 +25,7 @@ #define __GST_SIGNAL_PROCESSOR_H__ #include <gst/gst.h> +#include <gst/audio/multichannel.h> G_BEGIN_DECLS @@ -62,10 +63,18 @@ typedef enum #define GST_SIGNAL_PROCESSOR_IS_RUNNING(obj) \ (GST_SIGNAL_PROCESSOR (obj)->state == GST_SIGNAL_PROCESSOR_STATE_RUNNING) +typedef struct _GstSignalProcessorGroup GstSignalProcessorGroup; typedef struct _GstSignalProcessor GstSignalProcessor; typedef struct _GstSignalProcessorClass GstSignalProcessorClass; +struct _GstSignalProcessorGroup { + guint channels; /**< Number of channels in buffers */ + guint nframes; /**< Number of frames currently allocated per channel */ + gfloat *interleaved_buffer; /**< Interleaved buffer (c1c2c1c2...)*/ + gfloat *buffer; /**< De-interleaved buffer (c1c1...c2c2...) */ +}; + struct _GstSignalProcessor { GstElement element; @@ -81,23 +90,32 @@ struct _GstSignalProcessor { /* pending inputs before processing can take place */ guint pending_in; - /* panding outputs to be filled */ + /* pending outputs to be filled */ guint pending_out; - gfloat *control_in; + /* multi-channel signal pads */ + GstSignalProcessorGroup *group_in; + GstSignalProcessorGroup *group_out; + + /* single channel signal pads */ gfloat **audio_in; - gfloat *control_out; gfloat **audio_out; + + /* controls */ + gfloat *control_in; + gfloat *control_out; }; struct _GstSignalProcessorClass { GstElementClass parent_class; /*< public >*/ - guint num_control_in; + guint num_group_in; + guint num_group_out; guint num_audio_in; - guint num_control_out; guint num_audio_out; + guint num_control_in; + guint num_control_out; guint flags; @@ -114,7 +132,8 @@ struct _GstSignalProcessorClass { GType gst_signal_processor_get_type (void); void gst_signal_processor_class_add_pad_template (GstSignalProcessorClass *klass, - const gchar *name, GstPadDirection direction, guint index); + const gchar *name, GstPadDirection direction, guint index, guint channels, + const GstAudioChannelPosition *pos); |