mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
LV2 preset support.
This commit is contained in:
parent
a744f8136b
commit
c95f59cd2d
2 changed files with 171 additions and 16 deletions
169
ext/lv2/gstlv2.c
169
ext/lv2/gstlv2.c
|
@ -167,6 +167,13 @@ gst_lv2_build_positions (GstLV2Group * group)
|
||||||
return positions;
|
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
|
static void
|
||||||
gst_lv2_base_init (gpointer g_class)
|
gst_lv2_base_init (gpointer g_class)
|
||||||
{
|
{
|
||||||
|
@ -176,13 +183,16 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
GstElementDetails *details;
|
GstElementDetails *details;
|
||||||
SLV2Plugin lv2plugin;
|
SLV2Plugin lv2plugin;
|
||||||
SLV2Value val;
|
SLV2Value val;
|
||||||
SLV2Values values, sub_values;
|
SLV2Values values = NULL, sub_values = NULL;
|
||||||
|
SLV2Value prev = NULL;
|
||||||
GstLV2Group *group = NULL;
|
GstLV2Group *group = NULL;
|
||||||
GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID;
|
GstAudioChannelPosition position = GST_AUDIO_CHANNEL_POSITION_INVALID;
|
||||||
GstAudioChannelPosition mono_position = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
|
GstAudioChannelPosition mono_position = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO;
|
||||||
GstAudioChannelPosition *positions = NULL;
|
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;
|
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);
|
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 SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j);
|
||||||
const gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class);
|
const gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class);
|
||||||
gboolean in_group = FALSE;
|
gboolean in_group = FALSE;
|
||||||
struct _GstLV2Port desc = { j, 0 };
|
struct _GstLV2Port desc;
|
||||||
values = slv2_port_get_value (lv2plugin, port, in_group_pred);
|
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) {
|
if (slv2_values_size (values) > 0) {
|
||||||
/* port is part of a group */
|
/* port is part of a group */
|
||||||
|
@ -272,15 +291,22 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
* is illegal so we just ignore it */
|
* is illegal so we just ignore it */
|
||||||
if (slv2_port_is_a (lv2plugin, port, audio_class)) {
|
if (slv2_port_is_a (lv2plugin, port, audio_class)) {
|
||||||
desc.pad = is_input ? in_pad_index++ : out_pad_index++;
|
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);
|
g_array_append_val (klass->audio_in_ports, desc);
|
||||||
else
|
} else {
|
||||||
g_array_append_val (klass->audio_out_ports, desc);
|
g_array_append_val (klass->audio_out_ports, desc);
|
||||||
|
}
|
||||||
} else if (slv2_port_is_a (lv2plugin, port, control_class)) {
|
} 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);
|
g_array_append_val (klass->control_in_ports, desc);
|
||||||
else
|
} else {
|
||||||
g_array_append_val (klass->control_out_ports, desc);
|
g_array_append_val (klass->control_out_ports, desc);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* unknown port type */
|
/* unknown port type */
|
||||||
continue;
|
continue;
|
||||||
|
@ -352,6 +378,59 @@ gst_lv2_base_init (gpointer g_class)
|
||||||
j, 1, &mono_position);
|
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 */
|
/* construct the element details struct */
|
||||||
details = g_new0 (GstElementDetails, 1);
|
details = g_new0 (GstElementDetails, 1);
|
||||||
val = slv2_plugin_get_name (lv2plugin);
|
val = slv2_plugin_get_name (lv2plugin);
|
||||||
|
@ -466,7 +545,8 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
|
||||||
{
|
{
|
||||||
GObjectClass *gobject_class;
|
GObjectClass *gobject_class;
|
||||||
GstSignalProcessorClass *gsp_class;
|
GstSignalProcessorClass *gsp_class;
|
||||||
gint i;
|
GParamSpec *preset_spec;
|
||||||
|
gint i, offset = 1;
|
||||||
|
|
||||||
GST_DEBUG ("class_init %p", klass);
|
GST_DEBUG ("class_init %p", klass);
|
||||||
|
|
||||||
|
@ -483,7 +563,22 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
|
||||||
|
|
||||||
klass->plugin = 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++) {
|
for (i = 0; i < gsp_class->num_control_in; i++) {
|
||||||
GParamSpec *p;
|
GParamSpec *p;
|
||||||
|
@ -491,8 +586,8 @@ gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin)
|
||||||
p = gst_lv2_class_get_param_spec (klass,
|
p = gst_lv2_class_get_param_spec (klass,
|
||||||
g_array_index (klass->control_in_ports, GstLV2Port, i).index);
|
g_array_index (klass->control_in_ports, GstLV2Port, i).index);
|
||||||
|
|
||||||
/* properties have an offset of 1 */
|
/* control properties have an offset of (offset) */
|
||||||
g_object_class_install_property (G_OBJECT_CLASS (klass), i + 1, p);
|
g_object_class_install_property (G_OBJECT_CLASS (klass), i + offset, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < gsp_class->num_control_out; i++) {
|
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,
|
p = gst_lv2_class_get_param_spec (klass,
|
||||||
g_array_index (klass->control_out_ports, GstLV2Port, i).index);
|
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),
|
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->plugin = klass->plugin;
|
||||||
lv2->instance = NULL;
|
lv2->instance = NULL;
|
||||||
lv2->activated = FALSE;
|
lv2->activated = FALSE;
|
||||||
|
lv2->current_preset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -521,13 +617,44 @@ gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value,
|
||||||
{
|
{
|
||||||
GstSignalProcessor *gsp;
|
GstSignalProcessor *gsp;
|
||||||
GstSignalProcessorClass *gsp_class;
|
GstSignalProcessorClass *gsp_class;
|
||||||
|
GstLV2 *lv2;
|
||||||
|
GstLV2Class *lv2_class;
|
||||||
|
gint i;
|
||||||
|
|
||||||
gsp = GST_SIGNAL_PROCESSOR (object);
|
gsp = GST_SIGNAL_PROCESSOR (object);
|
||||||
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (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--;
|
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 */
|
/* only input ports */
|
||||||
g_return_if_fail (prop_id < gsp_class->num_control_in);
|
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;
|
GstSignalProcessor *gsp;
|
||||||
GstSignalProcessorClass *gsp_class;
|
GstSignalProcessorClass *gsp_class;
|
||||||
|
GstLV2 *lv2;
|
||||||
|
GstLV2Class *lv2_class;
|
||||||
gfloat *controls;
|
gfloat *controls;
|
||||||
|
|
||||||
gsp = GST_SIGNAL_PROCESSOR (object);
|
gsp = GST_SIGNAL_PROCESSOR (object);
|
||||||
gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (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 */
|
||||||
prop_id--;
|
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) {
|
if (prop_id < gsp_class->num_control_in) {
|
||||||
controls = gsp->control_in;
|
controls = gsp->control_in;
|
||||||
|
|
|
@ -48,6 +48,8 @@ typedef struct _GstLV2 GstLV2;
|
||||||
typedef struct _GstLV2Class GstLV2Class;
|
typedef struct _GstLV2Class GstLV2Class;
|
||||||
typedef struct _GstLV2Group GstLV2Group;
|
typedef struct _GstLV2Group GstLV2Group;
|
||||||
typedef struct _GstLV2Port GstLV2Port;
|
typedef struct _GstLV2Port GstLV2Port;
|
||||||
|
typedef struct _GstLV2Preset GstLV2Preset;
|
||||||
|
typedef struct _GstLV2PresetValue GstLV2PresetValue;
|
||||||
|
|
||||||
|
|
||||||
struct _GstLV2 {
|
struct _GstLV2 {
|
||||||
|
@ -57,6 +59,7 @@ struct _GstLV2 {
|
||||||
SLV2Instance instance;
|
SLV2Instance instance;
|
||||||
|
|
||||||
gboolean activated;
|
gboolean activated;
|
||||||
|
gint current_preset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstLV2Group {
|
struct _GstLV2Group {
|
||||||
|
@ -69,11 +72,23 @@ struct _GstLV2Group {
|
||||||
|
|
||||||
struct _GstLV2Port {
|
struct _GstLV2Port {
|
||||||
gint index; /**< LV2 port index (on LV2 plugin) */
|
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 */
|
SLV2Value role; /**< Channel position / port role */
|
||||||
GstAudioChannelPosition position; /**< Channel position */
|
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 {
|
struct _GstLV2Class {
|
||||||
GstSignalProcessorClass parent_class;
|
GstSignalProcessorClass parent_class;
|
||||||
|
|
||||||
|
@ -86,6 +101,7 @@ struct _GstLV2Class {
|
||||||
GArray *control_in_ports; /**< Array of GstLV2Port */
|
GArray *control_in_ports; /**< Array of GstLV2Port */
|
||||||
GArray *control_out_ports; /**< Array of GstLV2Port */
|
GArray *control_out_ports; /**< Array of GstLV2Port */
|
||||||
|
|
||||||
|
GArray *presets; /**< Array of GstLV2Preset */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue