From ad6a1ddba3029405dacc93fb773817316c8ee3b3 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 10 Jan 2013 06:25:29 +0000 Subject: Support event-based file parameters in built-in UI. git-svn-id: http://svn.drobilla.net/lad/trunk/jalv@4913 a436a847-0d15-0410-975c-d299462d15a1 --- src/jalv.c | 16 +++++-- src/jalv_gtk.c | 118 +++++++++++++++++++++++++++++++++++++++++++--------- src/jalv_internal.h | 5 +++ wscript | 2 +- 4 files changed, 117 insertions(+), 24 deletions(-) diff --git a/src/jalv.c b/src/jalv.c index dcc1425..7d29777 100644 --- a/src/jalv.c +++ b/src/jalv.c @@ -50,6 +50,7 @@ #include "lv2/lv2plug.in/ns/ext/event/event.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" +#include "lv2/lv2plug.in/ns/ext/patch/patch.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/time/time.h" #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" @@ -194,9 +195,8 @@ create_port(Jalv* jalv, const LilvNode* symbol = lilv_port_get_symbol(jalv->plugin, port->lilv_port); - const bool optional = lilv_port_has_property(jalv->plugin, - port->lilv_port, - jalv->nodes.lv2_connectionOptional); + const bool optional = lilv_port_has_property( + jalv->plugin, port->lilv_port, jalv->nodes.lv2_connectionOptional); /* Set the port flow (input or output) */ if (lilv_port_is_a(jalv->plugin, port->lilv_port, jalv->nodes.lv2_InputPort)) { @@ -249,6 +249,12 @@ jalv_create_ports(Jalv* jalv) create_port(jalv, i, default_values[i]); } + const LilvPort* control_input = lilv_plugin_get_port_by_designation( + jalv->plugin, jalv->nodes.lv2_InputPort, jalv->nodes.lv2_control); + if (control_input) { + jalv->control_in = lilv_port_get_index(jalv->plugin, control_input); + } + free(default_values); } @@ -805,6 +811,9 @@ main(int argc, char** argv) jalv.urids.log_Trace = symap_map(jalv.symap, LV2_LOG__Trace); jalv.urids.midi_MidiEvent = symap_map(jalv.symap, LV2_MIDI__MidiEvent); jalv.urids.param_sampleRate = symap_map(jalv.symap, LV2_PARAMETERS__sampleRate); + jalv.urids.patch_Set = symap_map(jalv.symap, LV2_PATCH__Set); + jalv.urids.patch_property = symap_map(jalv.symap, LV2_PATCH__property); + jalv.urids.patch_value = symap_map(jalv.symap, LV2_PATCH__value); jalv.urids.time_Position = symap_map(jalv.symap, LV2_TIME__Position); jalv.urids.time_bar = symap_map(jalv.symap, LV2_TIME__bar); jalv.urids.time_barBeat = symap_map(jalv.symap, LV2_TIME__barBeat); @@ -857,6 +866,7 @@ main(int argc, char** argv) jalv.nodes.lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort); jalv.nodes.lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort); jalv.nodes.lv2_connectionOptional = lilv_new_uri(world, LV2_CORE__connectionOptional); + jalv.nodes.lv2_control = lilv_new_uri(world, LV2_CORE__control); jalv.nodes.midi_MidiEvent = lilv_new_uri(world, LV2_MIDI__MidiEvent); jalv.nodes.pset_Preset = lilv_new_uri(world, LV2_PRESETS__Preset); jalv.nodes.rdfs_label = lilv_new_uri(world, LILV_NS_RDFS "label"); diff --git a/src/jalv_gtk.c b/src/jalv_gtk.c index 25de946..5c126cc 100644 --- a/src/jalv_gtk.c +++ b/src/jalv_gtk.c @@ -18,10 +18,16 @@ #include +#include "lv2/lv2plug.in/ns/ext/patch/patch.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #include "jalv_internal.h" +typedef struct { + Jalv* jalv; + LilvNode* property; +} PropertyRecord; + static GtkWidget* new_box(gboolean horizontal, gint spacing) { @@ -322,6 +328,36 @@ toggle_changed(GtkToggleButton* button, gpointer data) return FALSE; } +static void +file_changed(GtkFileChooserButton* widget, + gpointer data) +{ + PropertyRecord* record = (PropertyRecord*)data; + Jalv* jalv = record->jalv; + const char* property = lilv_node_as_uri(record->property); + const char* filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(widget)); + + // Copy forge since it is used by process thread + LV2_Atom_Forge forge = jalv->forge; + LV2_Atom_Forge_Frame frame; + uint8_t buf[1024]; + lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf)); + + lv2_atom_forge_blank(&forge, &frame, 1, jalv->urids.patch_Set); + lv2_atom_forge_property_head(&forge, jalv->urids.patch_property, 0); + lv2_atom_forge_urid(&forge, jalv->map.map(jalv, property)); + lv2_atom_forge_property_head(&forge, jalv->urids.patch_value, 0); + lv2_atom_forge_path(&forge, filename, strlen(filename)); + + const LV2_Atom* atom = lv2_atom_forge_deref(&forge, frame.ref); + jalv_ui_write(jalv, + jalv->control_in, + lv2_atom_total_size(atom), + jalv->urids.atom_eventTransfer, + atom); +} + static gchar* scale_format(GtkScale* scale, gdouble value, gpointer user_data) { @@ -421,14 +457,49 @@ make_toggle(struct Port* port) return check; } +static GtkWidget* +make_file_chooser(Jalv* jalv, const LilvNode* property) +{ + GtkWidget* button = gtk_file_chooser_button_new( + lilv_node_as_uri(property), GTK_FILE_CHOOSER_ACTION_OPEN); + PropertyRecord* record = (PropertyRecord*)malloc(sizeof(PropertyRecord)); + record->jalv = jalv; + record->property = lilv_node_duplicate(property); + g_signal_connect( + G_OBJECT(button), "file-set", G_CALLBACK(file_changed), record); + return button; +} + +static void +add_control_row(GtkWidget* table, + int row, + const char* name, + GtkWidget* control) +{ + GtkWidget* label = gtk_label_new(NULL); + gchar* markup = g_markup_printf_escaped( + "%s:", name); + gtk_label_set_markup(GTK_LABEL(label), markup); + g_free(markup); + gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); + gtk_table_attach(GTK_TABLE(table), + label, + 0, 1, row, row + 1, + GTK_FILL, GTK_FILL | GTK_EXPAND, 15, 15); + gtk_table_attach(GTK_TABLE(table), control, + 1, 2, row, row + 1, + GTK_FILL | GTK_EXPAND, 0, 0, 0); +} + static GtkWidget* build_control_widget(Jalv* jalv, GtkWidget* window) { - LilvNode* lv2_sampleRate = lilv_new_uri(jalv->world, LV2_CORE__sampleRate); - LilvNode* lv2_integer = lilv_new_uri(jalv->world, LV2_CORE__integer); - LilvNode* lv2_toggled = lilv_new_uri(jalv->world, LV2_CORE__toggled); LilvNode* lv2_enum = lilv_new_uri(jalv->world, LV2_CORE__enumeration); + LilvNode* lv2_integer = lilv_new_uri(jalv->world, LV2_CORE__integer); LilvNode* lv2_log = lilv_new_uri(jalv->world, LV2_PORT_PROPS__logarithmic); + LilvNode* lv2_sampleRate = lilv_new_uri(jalv->world, LV2_CORE__sampleRate); + LilvNode* lv2_toggled = lilv_new_uri(jalv->world, LV2_CORE__toggled); + LilvNode* patch_writable = lilv_new_uri(jalv->world, LV2_PATCH__writable); LilvNode* rdfs_comment = lilv_new_uri(jalv->world, LILV_NS_RDFS "comment"); GtkWidget* port_table = gtk_table_new(jalv->num_ports, 2, false); float* mins = (float*)calloc(jalv->num_ports, sizeof(float)); @@ -497,31 +568,38 @@ build_control_widget(Jalv* jalv, GtkWidget* window) } lilv_nodes_free(comments); - /* Add control table row */ - GtkWidget* label = gtk_label_new(NULL); - gchar* markup = g_markup_printf_escaped( - "%s:", - lilv_node_as_string(name)); - gtk_label_set_markup(GTK_LABEL(label), markup); - g_free(markup); - gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5); - gtk_table_attach(GTK_TABLE(port_table), - label, - 0, 1, i, i + 1, - GTK_FILL, GTK_FILL | GTK_EXPAND, 15, 15); - gtk_table_attach(GTK_TABLE(port_table), control, - 1, 2, i, i + 1, - GTK_FILL | GTK_EXPAND, 0, 0, 0); + add_control_row(port_table, i, lilv_node_as_string(name), control); + lilv_node_free(name); } + LilvNodes* properties = lilv_world_find_nodes( + jalv->world, + lilv_plugin_get_uri(jalv->plugin), + patch_writable, + NULL); + LILV_FOREACH(nodes, p, properties) { + const LilvNode* property = lilv_nodes_get(properties, p); + LilvNode* label = lilv_nodes_get_first( + lilv_world_find_nodes( + jalv->world, property, jalv->nodes.rdfs_label, NULL)); + + GtkWidget* control = make_file_chooser(jalv, property); + add_control_row( + port_table, num_controls++, + label ? lilv_node_as_string(label) : lilv_node_as_uri(property), + control); + } + lilv_nodes_free(properties); + free(mins); free(maxs); + lilv_node_free(rdfs_comment); + lilv_node_free(patch_writable); + lilv_node_free(lv2_toggled); lilv_node_free(lv2_sampleRate); lilv_node_free(lv2_integer); - lilv_node_free(lv2_toggled); lilv_node_free(lv2_enum); - lilv_node_free(rdfs_comment); if (num_controls > 0) { gtk_window_set_resizable(GTK_WINDOW(window), TRUE); diff --git a/src/jalv_internal.h b/src/jalv_internal.h index 76de642..64c4373 100644 --- a/src/jalv_internal.h +++ b/src/jalv_internal.h @@ -100,6 +100,9 @@ typedef struct { LV2_URID log_Trace; LV2_URID midi_MidiEvent; LV2_URID param_sampleRate; + LV2_URID patch_Set; + LV2_URID patch_property; + LV2_URID patch_value; LV2_URID time_Position; LV2_URID time_bar; LV2_URID time_barBeat; @@ -120,6 +123,7 @@ typedef struct { LilvNode* lv2_InputPort; LilvNode* lv2_OutputPort; LilvNode* lv2_connectionOptional; + LilvNode* lv2_control; LilvNode* midi_MidiEvent; LilvNode* pset_Preset; LilvNode* rdfs_label; @@ -175,6 +179,7 @@ typedef struct { struct Port* ports; ///< Port array of size num_ports uint32_t block_length; ///< Jack buffer size (block length) size_t midi_buf_size; ///< Size of MIDI port buffers + uint32_t control_in; ///< Index of control input port uint32_t num_ports; ///< Size of the two following arrays: uint32_t longest_sym; ///< Longest port symbol uint32_t ui_update_hz; ///< Frequency of UI updates diff --git a/wscript b/wscript index 4d26809..e76a850 100644 --- a/wscript +++ b/wscript @@ -30,7 +30,7 @@ def configure(conf): autowaf.set_c99_mode(conf) autowaf.display_header('Jalv Configuration') - autowaf.check_pkg(conf, 'lv2', atleast_version='1.0.15', uselib_store='LV2') + autowaf.check_pkg(conf, 'lv2', atleast_version='1.3.0', uselib_store='LV2') autowaf.check_pkg(conf, 'lilv-0', uselib_store='LILV', atleast_version='0.14.0', mandatory=True) autowaf.check_pkg(conf, 'serd-0', uselib_store='SERD', -- cgit v1.2.1