From 102ba23f77650bbeaceca98c3a6539f1e637b4f7 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Fri, 16 Dec 2011 20:15:21 +0000 Subject: Support presets in Gtk UI. git-svn-id: http://svn.drobilla.net/lad/trunk/jalv@3878 a436a847-0d15-0410-975c-d299462d15a1 --- src/jalv.c | 24 +++++++++----- src/jalv_gtk2.c | 54 ++++++++++++++++++++++++++++-- src/jalv_internal.h | 23 ++++++++++++- src/presets.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 184 insertions(+), 11 deletions(-) create mode 100644 src/presets.c (limited to 'src') diff --git a/src/jalv.c b/src/jalv.c index f1fb6c2..de40077 100644 --- a/src/jalv.c +++ b/src/jalv.c @@ -46,6 +46,7 @@ #define NS_ATOM "http://lv2plug.in/ns/ext/atom#" #define NS_MIDI "http://lv2plug.in/ns/ext/midi#" +#define NS_PSET "http://lv2plug.in/ns/ext/presets#" sem_t exit_sem; /**< Exit semaphore */ @@ -144,8 +145,9 @@ create_port(Jalv* host, port->lilv_port = lilv_plugin_get_port_by_index(host->plugin, port_index); port->jack_port = NULL; - port->control = 0.0f; port->evbuf = NULL; + port->index = port_index; + port->control = 0.0f; port->flow = FLOW_UNKNOWN; /* Get the port symbol for console printing */ @@ -446,12 +448,12 @@ jack_session_cb(jack_session_event_t* event, void* arg) } #endif /* JALV_JACK_SESSION */ -static void -lv2_ui_write(SuilController controller, - uint32_t port_index, - uint32_t buffer_size, - uint32_t protocol, - const void* buffer) +void +jalv_ui_write(SuilController controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t protocol, + const void* buffer) { Jalv* host = (Jalv*)controller; @@ -477,11 +479,13 @@ lv2_ui_write(SuilController controller, ev->protocol = protocol; ev->size = buffer_size; memcpy(ev->body, buffer, buffer_size); + #if 0 printf("WRITE: "); for (uint32_t i = 0; i < sizeof(buf); ++i) { printf("%c", buf[i]); } printf("\n"); + #endif jack_ringbuffer_write(host->ui_events, buf, sizeof(buf)); } @@ -558,6 +562,8 @@ main(int argc, char** argv) host.event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); host.aevent_class = lilv_new_uri(world, NS_ATOM "EventPort"); host.midi_class = lilv_new_uri(world, LILV_URI_MIDI_EVENT); + host.preset_class = lilv_new_uri(world, NS_PSET "Preset"); + host.label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); host.optional = lilv_new_uri(world, LILV_NS_LV2 "connectionOptional"); @@ -694,7 +700,7 @@ main(int argc, char** argv) SuilHost* ui_host = NULL; if (host.ui) { /* Instantiate UI */ - ui_host = suil_host_new(lv2_ui_write, NULL, NULL, NULL); + ui_host = suil_host_new(jalv_ui_write, NULL, NULL, NULL); host.ui_instance = suil_instance_new( ui_host, @@ -755,6 +761,8 @@ main(int argc, char** argv) lilv_node_free(host.audio_class); lilv_node_free(host.event_class); lilv_node_free(host.midi_class); + lilv_node_free(host.preset_class); + lilv_node_free(host.label_pred); lilv_node_free(host.optional); symap_free(host.symap); suil_instance_free(host.ui_instance); diff --git a/src/jalv_gtk2.c b/src/jalv_gtk2.c index fae3f6a..c56bab1 100644 --- a/src/jalv_gtk2.c +++ b/src/jalv_gtk2.c @@ -79,13 +79,55 @@ on_save_activate(GtkWidget* widget, void* ptr) } static void -on_quit_activate(GtkWidget* widget, - gpointer data) +on_quit_activate(GtkWidget* widget, gpointer data) { GtkWidget* window = (GtkWidget*)data; gtk_widget_destroy(window); } +typedef struct { + Jalv* jalv; + LilvNode* preset; +} PresetRecord; + +static void +on_preset_activate(GtkWidget* widget, gpointer data) +{ + PresetRecord* record = (PresetRecord*)data; + jalv_apply_preset(record->jalv, record->preset); +} + +static void +on_preset_destroy(gpointer data, GClosure* closure) +{ + PresetRecord* record = (PresetRecord*)data; + lilv_node_free(record->preset); + free(record); +} + +static int +add_preset_to_menu(Jalv* jalv, + const LilvNode* node, + const LilvNode* title, + void* data) +{ + GtkWidget* presets_menu = GTK_WIDGET(data); + const char* label = lilv_node_as_string(title); + GtkWidget* item = gtk_menu_item_new_with_label(label); + + PresetRecord* record = (PresetRecord*)malloc(sizeof(PresetRecord)); + record->jalv = jalv; + record->preset = lilv_node_duplicate(node); + + g_signal_connect_data(G_OBJECT(item), "activate", + G_CALLBACK(on_preset_activate), + record, on_preset_destroy, + 0); + + gtk_menu_shell_append(GTK_MENU_SHELL(presets_menu), item); + return 0; +} + int jalv_ui_resize(Jalv* jalv, int width, int height) { @@ -124,6 +166,14 @@ jalv_open_ui(Jalv* jalv, gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save); gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit); gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file); + + GtkWidget* presets = gtk_menu_item_new_with_mnemonic("_Presets"); + GtkWidget* presets_menu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(presets), presets_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), presets); + + jalv_load_presets(jalv, add_preset_to_menu, presets_menu); + gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(quit), "activate", diff --git a/src/jalv_internal.h b/src/jalv_internal.h index 8ecb44c..d44b5c5 100644 --- a/src/jalv_internal.h +++ b/src/jalv_internal.h @@ -55,8 +55,9 @@ struct Port { enum PortType type; enum PortFlow flow; jack_port_t* jack_port; /**< For audio/MIDI ports, otherwise NULL */ - float control; /**< For control ports, otherwise 0.0f */ LV2_Evbuf* evbuf; /**< For MIDI ports, otherwise NULL */ + uint32_t index; /**< Port index */ + float control; /**< For control ports, otherwise 0.0f */ bool old_api; /**< True for event, false for atom */ }; @@ -105,6 +106,8 @@ typedef struct { LilvNode* event_class; /**< Event port class (URI) */ LilvNode* aevent_class; /**< Atom event port class (URI) */ LilvNode* midi_class; /**< MIDI event class (URI) */ + LilvNode* preset_class; /**< Preset class (URI) */ + LilvNode* label_pred; /**< rdfs:label */ LilvNode* optional; /**< lv2:connectionOptional port property */ uint32_t midi_event_id; /**< MIDI event class ID */ uint32_t atom_prot_id; /**< Atom protocol ID */ @@ -128,12 +131,30 @@ int jalv_open_ui(Jalv* jalv, SuilInstance* instance); +void +jalv_ui_write(SuilController controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t protocol, + const void* buffer); + bool jalv_emit_ui_events(Jalv* jalv); int jalv_ui_resize(Jalv* jalv, int width, int height); +typedef int (*PresetSink)(Jalv* jalv, + const LilvNode* node, + const LilvNode* title, + void* data); + +int +jalv_load_presets(Jalv* jalv, PresetSink sink, void* data); + +int +jalv_apply_preset(Jalv* jalv, LilvNode* preset); + void jalv_save(Jalv* jalv, const char* dir); diff --git a/src/presets.c b/src/presets.c new file mode 100644 index 0000000..bf7fd78 --- /dev/null +++ b/src/presets.c @@ -0,0 +1,94 @@ +/* + Copyright 2007-2011 David Robillard + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include "jalv-config.h" +#include "jalv_internal.h" + +#define NS_LV2 "http://lv2plug.in/ns/lv2core#" +#define NS_PSET "http://lv2plug.in/ns/ext/presets#" + +#define USTR(s) ((const uint8_t*)s) + +int +jalv_load_presets(Jalv* jalv, PresetSink sink, void* data) +{ + LilvNodes* presets = lilv_plugin_get_related(jalv->plugin, + jalv->preset_class); + LILV_FOREACH(nodes, i, presets) { + const LilvNode* preset = lilv_nodes_get(presets, i); + lilv_world_load_resource(jalv->world, preset); + LilvNodes* titles = lilv_world_find_nodes( + jalv->world, preset, jalv->label_pred, NULL); + if (titles) { + const LilvNode* title = lilv_nodes_get_first(titles); + sink(jalv, preset, title, data); + lilv_nodes_free(titles); + } else { + fprintf(stderr, "Preset <%s> has no rdfs:label\n", + lilv_node_as_string(lilv_nodes_get(presets, i))); + } + } + lilv_nodes_free(presets); + + return 0; +} + +static inline const LilvNode* +get_value(LilvWorld* world, const LilvNode* subject, const LilvNode* predicate) +{ + LilvNodes* vs = lilv_world_find_nodes(world, subject, predicate, NULL); + return vs ? lilv_nodes_get_first(vs) : NULL; +} + +int +jalv_apply_preset(Jalv* jalv, LilvNode* preset) +{ + LilvNode* lv2_port = lilv_new_uri(jalv->world, NS_LV2 "port"); + LilvNode* lv2_symbol = lilv_new_uri(jalv->world, NS_LV2 "symbol"); + LilvNode* pset_value = lilv_new_uri(jalv->world, NS_PSET "value"); + + LilvNodes* ports = lilv_world_find_nodes( + jalv->world, preset, lv2_port, NULL); + LILV_FOREACH(nodes, i, ports) { + const LilvNode* port = lilv_nodes_get(ports, i); + const LilvNode* symbol = get_value(jalv->world, port, lv2_symbol); + const LilvNode* value = get_value(jalv->world, port, pset_value); + if (!symbol) { + fprintf(stderr, "error: Preset port missing symbol.\n"); + } else if (!value) { + fprintf(stderr, "error: Preset port missing value.\n"); + } else if (!lilv_node_is_float(value) && !lilv_node_is_int(value)) { + fprintf(stderr, "error: Preset port value is not a number.\n"); + } else { + const char* sym = lilv_node_as_string(symbol); + struct Port* p = jalv_port_by_symbol(jalv, sym); + if (p) { + const float fvalue = lilv_node_as_float(value); + jalv_ui_write(jalv, p->index, sizeof(float), 0, &fvalue); + } else { + fprintf(stderr, "error: Preset port `%s' is missing\n", sym); + } + } + } + lilv_nodes_free(ports); + + lilv_node_free(pset_value); + lilv_node_free(preset); + lilv_node_free(lv2_symbol); + lilv_node_free(lv2_port); + + return 0; +} -- cgit v1.2.1