From 1715d437e9e532e49175e58b187aa44c8f958507 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Sun, 25 Mar 2012 18:10:49 +0200 Subject: [PATCH] Add state support and startup state polling for the ui --- drmr.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++-- drmr.h | 9 +++++ drmr.ttl | 1 + drmr_hydrogen.c | 4 +- drmr_ui.c | 42 +++++++++++++++++-- 5 files changed, 153 insertions(+), 8 deletions(-) diff --git a/drmr.c b/drmr.c index 39915bc..ecaa302 100644 --- a/drmr.c +++ b/drmr.c @@ -159,12 +159,26 @@ connect_port(LV2_Handle instance, } } -static inline LV2_Atom* build_update_message(DrMr *drmr, const char* kit) { +static inline LV2_Atom *build_update_message(DrMr *drmr) { LV2_Atom_Forge_Frame set_frame; LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_resource (&drmr->forge, &set_frame, 1, drmr->uris.ui_msg); - lv2_atom_forge_property_head(&drmr->forge, drmr->uris.kit_path,0); - lv2_atom_forge_string(&drmr->forge, kit, strlen(kit)); + if (drmr->current_path) { + lv2_atom_forge_property_head(&drmr->forge, drmr->uris.kit_path,0); + lv2_atom_forge_string(&drmr->forge, drmr->current_path, strlen(drmr->current_path)); + } + lv2_atom_forge_pop(&drmr->forge,&set_frame); + return msg; +} + +static inline LV2_Atom *build_state_message(DrMr *drmr) { + LV2_Atom_Forge_Frame set_frame; + LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_resource + (&drmr->forge, &set_frame, 1, drmr->uris.get_state); + if (drmr->current_path) { + lv2_atom_forge_property_head(&drmr->forge, drmr->uris.kit_path,0); + lv2_atom_forge_string(&drmr->forge, drmr->current_path, strlen(drmr->current_path)); + } lv2_atom_forge_pop(&drmr->forge,&set_frame); return msg; } @@ -257,6 +271,10 @@ static void run(LV2_Handle instance, uint32_t n_samples) { drmr->curReq = reqPos; if (tmp) free(tmp); } + } else if (obj->body.otype == drmr->uris.get_state) { + lv2_atom_forge_frame_time(&drmr->forge, 0); + build_state_message(drmr); + printf("Sent state message: %s\n",drmr->current_path); } } else printf("unrecognized event\n"); @@ -272,7 +290,8 @@ static void run(LV2_Handle instance, uint32_t n_samples) { if (current_kit_changed) { current_kit_changed = 0; lv2_atom_forge_frame_time(&drmr->forge, 0); - build_update_message(drmr,drmr->current_path); + build_update_message(drmr); + printf("Sent current path message\n"); } lv2_atom_forge_pop(&drmr->forge, &seq_frame); @@ -330,7 +349,85 @@ static void cleanup(LV2_Handle instance) { free(instance); } +void save_state(LV2_Handle instance, + LV2_State_Store_Function store, + void* handle, + uint32_t flags, + const LV2_Feature *const * features) { + DrMr *drmr = (DrMr*)instance; + LV2_State_Map_Path* map_path = NULL; + while(*features) { + if (!strcmp((*features)->URI, LV2_STATE__mapPath)) + map_path = (LV2_State_Map_Path*)((*features)->data); + features++; + } + + if (map_path == NULL) { + fprintf(stderr,"Host does not support map_path, cannot save state\n"); + return; + } + + char* mapped_path = map_path->abstract_path(map_path->handle, + drmr->current_path); + + if (store(handle, + drmr->uris.kit_path, + mapped_path, + strlen(mapped_path) + 1, + drmr->uris.string_urid, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) { + printf("Store failed\n"); + } +} + +void restore_state(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + void* handle, + uint32_t flags, + const LV2_Feature *const * features) { + DrMr* drmr = (DrMr*)instance; + size_t size; + uint32_t type; + uint32_t fgs; + + LV2_State_Map_Path* map_path = NULL; + while(*features) { + if (!strcmp((*features)->URI, LV2_STATE__mapPath)) + map_path = (LV2_State_Map_Path*)((*features)->data); + features++; + } + + if (map_path == NULL) { + fprintf(stderr,"Host does not support map_path, cannot restore state\n"); + return; + } + + + const char* abstract_path = + retrieve(handle, drmr->uris.kit_path, &size, &type, &fgs); + + if (!abstract_path) { + fprintf(stderr,"Found no path in state, not restoring\n"); + return; + } + + char *kit_path = map_path->absolute_path(map_path->handle,abstract_path); + + if (kit_path) { // safe as we're in "Instantiation" threading class + int reqPos = (drmr->curReq+1)%REQ_BUF_SIZE; + char *tmp = NULL; + if (reqPos >= 0 && drmr->request_buf[reqPos]) + tmp = drmr->request_buf[reqPos]; + drmr->request_buf[reqPos] = strdup(kit_path); + drmr->curReq = reqPos; + if (tmp) free(tmp); + } +} + + static const void* extension_data(const char* uri) { + static const LV2_State_Interface state_iface = { save_state, restore_state }; + if (!strcmp(uri, LV2_STATE_URI "#Interface")) return &state_iface; return NULL; } diff --git a/drmr.h b/drmr.h index d06e79d..f2c8633 100644 --- a/drmr.h +++ b/drmr.h @@ -25,6 +25,7 @@ #include "lv2/lv2plug.in/ns/ext/atom/util.h" #include "lv2/lv2plug.in/ns/lv2core/lv2.h" #include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/ext/state/state.h" // drumkit scanned from a hydrogen xml file typedef struct { @@ -147,6 +148,8 @@ typedef struct { LV2_URID kit_path; LV2_URID atom_eventTransfer; LV2_URID atom_resource; + LV2_URID string_urid; + LV2_URID get_state; } drmr_uris; typedef struct { @@ -190,12 +193,18 @@ void map_drmr_uris(LV2_URID_Map *map, uris->midi_event = map->map(map->handle, "http://lv2plug.in/ns/ext/midi#MidiEvent"); + uris->string_urid = + map->map(map->handle, + "http://lv2plug.in/ns/ext/atom#String"); uris->ui_msg = map->map(map->handle, DRMR_URI "#uimsg"); uris->kit_path = map->map(map->handle, DRMR_URI "#kitpath"); + uris->kit_path = + map->map(map->handle, + DRMR_URI "#getstate"); uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); uris->atom_resource = diff --git a/drmr.ttl b/drmr.ttl index c151c2c..da7f30a 100644 --- a/drmr.ttl +++ b/drmr.ttl @@ -19,6 +19,7 @@ doap:license ; lv2:requiredFeature urid:map ; ui:ui ; + lv2:extensionData ; lv2:port [ a lv2:InputPort , atom:MessagePort; atom:bufferType atom:Sequence ; diff --git a/drmr_hydrogen.c b/drmr_hydrogen.c index bca3720..fe0f730 100644 --- a/drmr_hydrogen.c +++ b/drmr_hydrogen.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -307,7 +309,7 @@ kits* scan_kits() { } snprintf(buf,BUFSIZ,"%s/%s/",cur_path,ep->d_name); - kit->path = strdup(buf); + kit->path = realpath(buf,NULL); // realpath will malloc for us node->skit = kit; struct kit_list * cur_k = scanned_kits; if (cur_k) { diff --git a/drmr_ui.c b/drmr_ui.c index ce1deba..afb39b2 100644 --- a/drmr_ui.c +++ b/drmr_ui.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include @@ -295,6 +296,14 @@ static LV2_Atom* build_path_message(DrMrUi *ui, const char* path) { return msg; } +static LV2_Atom* build_get_state_message(DrMrUi *ui) { + LV2_Atom_Forge_Frame set_frame; + LV2_Atom* msg = (LV2_Atom*)lv2_atom_forge_resource + (&ui->forge, &set_frame, 1, ui->uris.get_state); + lv2_atom_forge_pop(&ui->forge,&set_frame); + return msg; +} + static void kit_combobox_changed(GtkComboBox* box, gpointer data) { DrMrUi* ui = (DrMrUi*)data; gint new_kit = gtk_combo_box_get_active (GTK_COMBO_BOX(box)); @@ -357,6 +366,20 @@ static GtkWidget *create_position_combo(void) return combo; } +static gulong expose_id; +static gboolean expose_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data) { + DrMrUi* ui = (DrMrUi*)data; + uint8_t msg_buf[1024]; + lv2_atom_forge_set_buffer(&ui->forge, msg_buf, 1024); + LV2_Atom *msg = build_get_state_message(ui); + ui->write(ui->controller,DRMR_CONTROL, + lv2_atom_total_size(msg), + ui->uris.atom_eventTransfer, + msg); + g_signal_handler_disconnect(widget,expose_id); + return FALSE; +} + static void build_drmr_ui(DrMrUi* ui) { GtkWidget *drmr_ui_widget; GtkWidget *opts_hbox1, *opts_hbox2, @@ -369,6 +392,7 @@ static void build_drmr_ui(DrMrUi* ui) { PangoAttribute *attr; drmr_ui_widget = gtk_vbox_new(false,0); + expose_id = g_signal_connect (drmr_ui_widget, "expose-event", G_CALLBACK (expose_callback), ui); g_object_set(drmr_ui_widget,"border-width",6,NULL); ui->kit_store = gtk_list_store_new(1,G_TYPE_STRING); @@ -503,7 +527,6 @@ instantiate(const LV2UI_Descriptor* descriptor, ui->startSamp = 0; #endif - *widget = ui->drmr_widget; return ui; @@ -548,20 +571,33 @@ port_event(LV2UI_Handle handle, LV2_Atom* atom = (LV2_Atom*)buffer; if (atom->type == ui->uris.atom_resource) { LV2_Atom_Object* obj = (LV2_Atom_Object*)atom; + if (obj->body.otype != ui->uris.get_state && + obj->body.otype != ui->uris.ui_msg) { + // both state and ui_msg are the same at the moment + fprintf(stderr,"Invalid message type to ui\n"); + return; + } const LV2_Atom* path = NULL; lv2_object_get(obj, ui->uris.kit_path, &path, 0); if (!path) fprintf(stderr,"Got UI message without kit_path, ignoring\n"); else { char *kitpath = LV2_ATOM_BODY(path); + char *realp = realpath(kitpath,NULL); + if (!realp) { + fprintf(stderr,"Passed a path I can't resolve, bailing out\n"); + return; + } int i; for(i = 0;i < ui->kits->num_kits;i++) - if (!strcmp(ui->kits->kits[i].path,kitpath)) + if (!strcmp(ui->kits->kits[i].path,realp)) break; if (i < ui->kits->num_kits) { ui->kitReq = i; g_idle_add(kit_callback,ui); - } + } else + fprintf(stderr,"Couldn't find kit %s\n",realp); + free(realp); } } else fprintf(stderr, "Unknown message type.\n");