diff options
Diffstat (limited to 'ext/lv2/gstlv2.c')
-rw-r--r-- | ext/lv2/gstlv2.c | 702 |
1 files changed, 549 insertions, 153 deletions
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", |