From c95f59cd2dc129899b37fecaa52c89db3d8a5ef8 Mon Sep 17 00:00:00 2001 From: Dave Robillard Date: Mon, 17 Aug 2009 17:06:40 -0400 Subject: [PATCH] LV2 preset support. --- ext/lv2/gstlv2.c | 169 ++++++++++++++++++++++++++++++++++++++++++----- ext/lv2/gstlv2.h | 18 ++++- 2 files changed, 171 insertions(+), 16 deletions(-) diff --git a/ext/lv2/gstlv2.c b/ext/lv2/gstlv2.c index ac4c903828..4b99f93961 100644 --- a/ext/lv2/gstlv2.c +++ b/ext/lv2/gstlv2.c @@ -167,6 +167,13 @@ gst_lv2_build_positions (GstLV2Group * group) 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) { @@ -176,13 +183,16 @@ gst_lv2_base_init (gpointer g_class) GstElementDetails *details; SLV2Plugin lv2plugin; SLV2Value val; - SLV2Values values, sub_values; + 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; + 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); @@ -210,8 +220,17 @@ gst_lv2_base_init (gpointer g_class) 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 = { j, 0 }; - values = slv2_port_get_value (lv2plugin, port, in_group_pred); + 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 */ @@ -272,15 +291,22 @@ gst_lv2_base_init (gpointer g_class) * 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) + if (is_input) { g_array_append_val (klass->audio_in_ports, desc); - else + } else { g_array_append_val (klass->audio_out_ports, desc); + } } else if (slv2_port_is_a (lv2plugin, port, control_class)) { - if (is_input) + 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 + } else { g_array_append_val (klass->control_out_ports, desc); + } } else { /* unknown port type */ continue; @@ -352,6 +378,59 @@ gst_lv2_base_init (gpointer g_class) j, 1, &mono_position); } + /* find presets */ + values = slv2_plugin_query_sparql (lv2plugin, + "PREFIX pset: \n" + "PREFIX dc: \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); @@ -466,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); @@ -483,7 +563,22 @@ 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; @@ -491,8 +586,8 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin) 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++) { @@ -501,9 +596,9 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin) 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); } } @@ -513,6 +608,7 @@ gst_lv2_init (GstLV2 * lv2, GstLV2Class * klass) lv2->plugin = klass->plugin; lv2->instance = NULL; lv2->activated = FALSE; + lv2->current_preset = 0; } static void @@ -521,13 +617,44 @@ gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value, { 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); @@ -553,13 +680,25 @@ gst_lv2_get_property (GObject * object, guint prop_id, GValue * value, { 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; diff --git a/ext/lv2/gstlv2.h b/ext/lv2/gstlv2.h index 85e4532e75..ab9c8ae85e 100644 --- a/ext/lv2/gstlv2.h +++ b/ext/lv2/gstlv2.h @@ -48,6 +48,8 @@ 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 { @@ -57,6 +59,7 @@ struct _GstLV2 { SLV2Instance instance; gboolean activated; + gint current_preset; }; struct _GstLV2Group { @@ -69,11 +72,23 @@ struct _GstLV2Group { struct _GstLV2Port { gint index; /**< LV2 port index (on LV2 plugin) */ - gint pad; /**< Gst pad index (iff not part of a group) */ + 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 { GstSignalProcessorClass parent_class; @@ -86,6 +101,7 @@ struct _GstLV2Class { GArray *control_in_ports; /**< Array of GstLV2Port */ GArray *control_out_ports; /**< Array of GstLV2Port */ + GArray *presets; /**< Array of GstLV2Preset */ };