diff options
-rw-r--r-- | plugins/eg-params.lv2/params.c | 354 | ||||
-rw-r--r-- | plugins/eg-params.lv2/state_map.h | 110 |
2 files changed, 237 insertions, 227 deletions
diff --git a/plugins/eg-params.lv2/params.c b/plugins/eg-params.lv2/params.c index b36e1be..3df6652 100644 --- a/plugins/eg-params.lv2/params.c +++ b/plugins/eg-params.lv2/params.c @@ -33,10 +33,13 @@ #include "lv2/lv2plug.in/ns/lv2core/lv2.h" #include "lv2/lv2plug.in/ns/lv2core/lv2_util.h" +#include "state_map.h" + #define MAX_STRING 1024 #define EG_PARAMS_URI "http://lv2plug.in/plugins/eg-params" -#define EG_PARAMS__float EG_PARAMS_URI "#float" + +#define N_PROPS 9 typedef struct { LV2_URID plugin; @@ -44,14 +47,6 @@ typedef struct { LV2_URID atom_Sequence; LV2_URID atom_URID; LV2_URID atom_eventTransfer; - LV2_URID eg_int; - LV2_URID eg_long; - LV2_URID eg_float; - LV2_URID eg_double; - LV2_URID eg_bool; - LV2_URID eg_string; - LV2_URID eg_path; - LV2_URID eg_lfo; LV2_URID eg_spring; LV2_URID midi_Event; LV2_URID patch_Get; @@ -86,14 +81,6 @@ map_uris(LV2_URID_Map* map, URIs* uris) uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); - uris->eg_int = map->map(map->handle, EG_PARAMS_URI "#int"); - uris->eg_long = map->map(map->handle, EG_PARAMS_URI "#long"); - uris->eg_float = map->map(map->handle, EG_PARAMS_URI "#float"); - uris->eg_double = map->map(map->handle, EG_PARAMS_URI "#double"); - uris->eg_bool = map->map(map->handle, EG_PARAMS_URI "#bool"); - uris->eg_string = map->map(map->handle, EG_PARAMS_URI "#string"); - uris->eg_path = map->map(map->handle, EG_PARAMS_URI "#path"); - uris->eg_lfo = map->map(map->handle, EG_PARAMS_URI "#lfo"); uris->eg_spring = map->map(map->handle, EG_PARAMS_URI "#spring"); uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); uris->patch_Get = map->map(map->handle, LV2_PATCH__Get); @@ -127,10 +114,11 @@ typedef struct { URIs uris; // Plugin state - State state; + StateMapItem props[N_PROPS]; + State state; - float spring; - float lfo; + // Buffer for making strings from URIDs if unmap is not provided + char urid_buf[12]; } Params; static void @@ -151,11 +139,6 @@ connect_port(LV2_Handle instance, } } -#define INIT_PARAM(atype, param) { \ - (param)->atom.type = (atype); \ - (param)->atom.size = sizeof((param)->body); \ -} - static LV2_Handle instantiate(const LV2_Descriptor* descriptor, double rate, @@ -186,18 +169,20 @@ instantiate(const LV2_Descriptor* descriptor, map_uris(self->map, &self->uris); lv2_atom_forge_init(&self->forge, self->map); - // Initialize state - INIT_PARAM(self->forge.Int, &self->state.aint); - INIT_PARAM(self->forge.Long, &self->state.along); - INIT_PARAM(self->forge.Float, &self->state.afloat); - INIT_PARAM(self->forge.Double, &self->state.adouble); - INIT_PARAM(self->forge.Bool, &self->state.abool); - INIT_PARAM(self->forge.Float, &self->state.spring); - INIT_PARAM(self->forge.Float, &self->state.lfo); - self->state.astring.type = self->forge.String; - self->state.astring.size = 0; - self->state.apath.type = self->forge.Path; - self->state.apath.size = 0; + // Initialise state dictionary + State* state = &self->state; + state_map_init( + self->props, self->map, self->map->handle, + EG_PARAMS_URI "#int", STATE_MAP_INIT(Int, &state->aint), + EG_PARAMS_URI "#long", STATE_MAP_INIT(Long, &state->along), + EG_PARAMS_URI "#float", STATE_MAP_INIT(Float, &state->afloat), + EG_PARAMS_URI "#double", STATE_MAP_INIT(Double, &state->adouble), + EG_PARAMS_URI "#bool", STATE_MAP_INIT(Bool, &state->abool), + EG_PARAMS_URI "#string", STATE_MAP_INIT(String, &state->astring), + EG_PARAMS_URI "#path", STATE_MAP_INIT(Path, &state->apath), + EG_PARAMS_URI "#lfo", STATE_MAP_INIT(Float, &state->lfo), + EG_PARAMS_URI "#spring", STATE_MAP_INIT(Float, &state->spring), + NULL); return (LV2_Handle)self; } @@ -208,29 +193,33 @@ cleanup(LV2_Handle instance) free(instance); } +/** Helper function to unmap a URID if possible. */ +static const char* +unmap(Params* self, LV2_URID urid) +{ + if (self->unmap) { + return self->unmap->unmap(self->unmap->handle, urid); + } else { + snprintf(self->urid_buf, sizeof(self->urid_buf), "%u", urid); + return self->urid_buf; + } +} + static LV2_State_Status -check_param(Params* self, - LV2_URID key, - LV2_URID required_key, - LV2_URID type, - LV2_URID required_type) +check_type(Params* self, + LV2_URID key, + LV2_URID type, + LV2_URID required_type) { - if (key == required_key) { - if (type == required_type) { - return LV2_STATE_SUCCESS; - } else if (self->unmap) { - lv2_log_trace( - &self->log, "Bad type <%s> for <%s> (needs <%s>)\n", - self->unmap->unmap(self->unmap->handle, type), - self->unmap->unmap(self->unmap->handle, key), - self->unmap->unmap(self->unmap->handle, required_type)); - } else { - lv2_log_trace(&self->log, "Bad type for parameter %d\n", key); - } + if (type != required_type) { + lv2_log_trace( + &self->log, "Bad type <%s> for <%s> (needs <%s>)\n", + unmap(self, type), + unmap(self, key), + unmap(self, required_type)); return LV2_STATE_ERR_BAD_TYPE; } - - return LV2_STATE_ERR_NO_PROPERTY; + return LV2_STATE_SUCCESS; } static LV2_State_Status @@ -241,105 +230,35 @@ set_parameter(Params* self, const void* body, bool from_state) { - const URIs* uris = &self->uris; - const LV2_Atom_Forge* forge = &self->forge; - LV2_State_Status st = LV2_STATE_SUCCESS; - - if (!(st = check_param(self, key, uris->eg_int, type, forge->Int))) { - self->state.aint.body = *(const int32_t*)body; - lv2_log_trace(&self->log, "Set int %d\n", self->state.aint.body); - } else if (!(st = check_param(self, key, uris->eg_long, type, forge->Long))) { - self->state.along.body = *(const int64_t*)body; - lv2_log_trace(&self->log, "Set long %ld\n", self->state.along.body); - } else if (!(st = check_param(self, key, uris->eg_float, type, forge->Float))) { - self->state.afloat.body = *(const float*)body; - lv2_log_trace(&self->log, "Set float %f\n", self->state.afloat.body); - } else if (!(st = check_param(self, key, uris->eg_double, type, forge->Double))) { - self->state.adouble.body = *(const double*)body; - lv2_log_trace(&self->log, "Set double %f\n", self->state.adouble.body); - } else if (!(st = check_param(self, key, uris->eg_bool, type, forge->Bool))) { - self->state.abool.body = *(const int32_t*)body; - lv2_log_trace(&self->log, "Set bool %d\n", self->state.abool.body); - } else if (!(st = check_param(self, key, uris->eg_string, type, forge->String))) { - if (size <= MAX_STRING) { - memcpy(self->state.string, body, size); - self->state.astring.size = size; - lv2_log_trace(&self->log, "Set string %s\n", self->state.string); - } else { - lv2_log_error(&self->log, "Insufficient space for string\n"); - return LV2_STATE_ERR_NO_SPACE; - } - } else if (!(st = check_param(self, key, uris->eg_path, type, forge->Path))) { - if (size <= MAX_STRING) { - memcpy(self->state.path, body, size); - self->state.apath.size = size; - lv2_log_trace(&self->log, "Set path %s\n", self->state.path); - } else { - lv2_log_error(&self->log, "Insufficient space for path\n"); - return LV2_STATE_ERR_NO_SPACE; - } - } else if (!(st = check_param(self, key, uris->eg_spring, type, forge->Float))) { - self->spring = *(const float*)body; - lv2_log_trace(&self->log, "Set spring %f\n", *(const float*)body); - } else if (key == uris->eg_lfo) { - if (!from_state) { - st = LV2_STATE_ERR_UNKNOWN; - lv2_log_error(&self->log, "Attempt to set non-writable LFO\n"); - } else { - self->state.lfo.body = *(const float*)body; - lv2_log_trace(&self->log, "Set LFO %f\n", self->state.lfo.body); - } - } else if (self->unmap) { - st = LV2_STATE_ERR_NO_PROPERTY; - lv2_log_trace(&self->log, "Unknown parameter <%s>\n", - self->unmap->unmap(self->unmap->handle, key)); - } else { - st = LV2_STATE_ERR_NO_PROPERTY; - lv2_log_trace(&self->log, "Unknown parameter %d\n", key); + // Look up property in state dictionary + const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); + if (!entry) { + lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); + return LV2_STATE_ERR_NO_PROPERTY; } - return st; + // Ensure given type matches property's type + if (check_type(self, key, type, entry->value->type)) { + return LV2_STATE_ERR_BAD_TYPE; + } + + // Set property value in state dictionary + lv2_log_trace(&self->log, "Set <%s>\n", entry->uri); + memcpy(entry->value + 1, body, size); + entry->value->size = size; + return LV2_STATE_SUCCESS; } static const LV2_Atom* get_parameter(Params* self, LV2_URID key) { - const URIs* uris = &self->uris; - - if (key == uris->eg_int) { - lv2_log_trace(&self->log, "Get int %d\n", self->state.aint.body); - return &self->state.aint.atom; - } else if (key == uris->eg_long) { - lv2_log_trace(&self->log, "Get long %ld\n", self->state.along.body); - return &self->state.along.atom; - } else if (key == uris->eg_float) { - lv2_log_trace(&self->log, "Get float %f\n", self->state.afloat.body); - return &self->state.afloat.atom; - } else if (key == uris->eg_double) { - lv2_log_trace(&self->log, "Get double %f\n", self->state.adouble.body); - return &self->state.adouble.atom; - } else if (key == uris->eg_bool) { - lv2_log_trace(&self->log, "Get bool %d\n", self->state.abool.body); - return &self->state.abool.atom; - } else if (key == uris->eg_string) { - lv2_log_trace(&self->log, "Get string %s\n", self->state.string); - return &self->state.astring; - } else if (key == uris->eg_path) { - lv2_log_trace(&self->log, "Get path %s\n", self->state.path); - return &self->state.apath; - } else if (key == uris->eg_spring) { - lv2_log_trace(&self->log, "Get spring %f\n", self->state.spring.body); - return &self->state.spring.atom; - } else if (key == uris->eg_lfo) { - lv2_log_trace(&self->log, "Get LFO %f\n", self->state.lfo.body); - return &self->state.lfo.atom; - } else if (self->unmap) { - lv2_log_trace(&self->log, "Unknown parameter <%s>\n", - self->unmap->unmap(self->unmap->handle, key)); - } else { - lv2_log_trace(&self->log, "Unknown parameter %d\n", key); + const StateMapItem* entry = state_map_find(self->props, N_PROPS, key); + if (entry) { + lv2_log_trace(&self->log, "Get <%s>\n", entry->uri); + return entry->value; } + lv2_log_trace(&self->log, "Unknown parameter <%s>\n", unmap(self, key)); return NULL; } @@ -363,63 +282,48 @@ write_param_to_forge(LV2_State_Handle handle, } static void -store_param(Params* self, - LV2_State_Status* save_status, - LV2_State_Store_Function store, - LV2_State_Handle handle, - LV2_URID key, - const LV2_Atom* value) -{ - const LV2_State_Status st = store(handle, - key, - value + 1, - value->size, - value->type, - LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); - - if (!*save_status) { - *save_status = st; - } -} - -static LV2_State_Status -store_state(Params* self, - LV2_State_Store_Function store, - LV2_State_Handle handle, - uint32_t flags, - LV2_State_Map_Path* map_path) +store_prop(Params* self, + LV2_State_Map_Path* map_path, + LV2_State_Status* save_status, + LV2_State_Store_Function store, + LV2_State_Handle handle, + LV2_URID key, + const LV2_Atom* value) { - const URIs* uris = &self->uris; - const State* state = &self->state; - LV2_State_Status st = LV2_STATE_SUCCESS; - - // Store simple properties - store_param(self, &st, store, handle, uris->eg_int, &state->aint.atom); - store_param(self, &st, store, handle, uris->eg_long, &state->along.atom); - store_param(self, &st, store, handle, uris->eg_float, &state->afloat.atom); - store_param(self, &st, store, handle, uris->eg_double, &state->adouble.atom); - store_param(self, &st, store, handle, uris->eg_bool, &state->abool.atom); - store_param(self, &st, store, handle, uris->eg_string, &state->astring); - store_param(self, &st, store, handle, uris->eg_spring, &state->spring.atom); - store_param(self, &st, store, handle, uris->eg_lfo, &state->lfo.atom); - - if (map_path) { + LV2_State_Status st; + if (map_path && value->type == self->uris.atom_Path) { // Map path to abstract path for portable storage - char* apath = map_path->abstract_path(map_path->handle, state->path); + const char* path = (const char*)(value + 1); + char* apath = map_path->abstract_path(map_path->handle, path); st = store(handle, - self->uris.eg_path, + key, apath, strlen(apath) + 1, self->uris.atom_Path, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); free(apath); } else { - store_param(self, &st, store, handle, uris->eg_path, &state->apath); + // Store simple property + st = store(handle, + key, + value + 1, + value->size, + value->type, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE); } - return st; + if (save_status && !*save_status) { + *save_status = st; + } } +/** + State save method. + + This is used in the usual way when called by the host to save plugin state, + but also internally for writing messages in the audio thread by passing a + "store" function which actually writes the description to the forge. +*/ static LV2_State_Status save(LV2_Handle instance, LV2_State_Store_Function store, @@ -431,15 +335,21 @@ save(LV2_Handle instance, LV2_State_Map_Path* map_path = (LV2_State_Map_Path*)lv2_features_data( features, LV2_STATE__mapPath); - return store_state(self, store, handle, flags, map_path); + LV2_State_Status st = LV2_STATE_SUCCESS; + for (unsigned i = 0; i < N_PROPS; ++i) { + StateMapItem* prop = &self->props[i]; + store_prop(self, map_path, &st, store, handle, prop->urid, prop->value); + } + + return st; } static void -retrieve_param(Params* self, - LV2_State_Status* restore_status, - LV2_State_Retrieve_Function retrieve, - LV2_State_Handle handle, - LV2_URID key) +retrieve_prop(Params* self, + LV2_State_Status* restore_status, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + LV2_URID key) { // Retrieve value from saved state size_t vsize; @@ -457,6 +367,7 @@ retrieve_param(Params* self, } } +/** State restore method. */ static LV2_State_Status restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, @@ -467,16 +378,9 @@ restore(LV2_Handle instance, Params* self = (Params*)instance; LV2_State_Status st = LV2_STATE_SUCCESS; - // Retrieve simple properties - retrieve_param(self, &st, retrieve, handle, self->uris.eg_int); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_long); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_float); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_double); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_bool); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_string); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_path); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_spring); - retrieve_param(self, &st, retrieve, handle, self->uris.eg_lfo); + for (unsigned i = 0; i < N_PROPS; ++i) { + retrieve_prop(self, &st, retrieve, handle, self->props[i].urid); + } return st; } @@ -502,7 +406,7 @@ run(LV2_Handle instance, uint32_t sample_count) (uint8_t*)self->out_port, out_capacity); - // Start a sequence in the output port. + // Start a sequence in the output port LV2_Atom_Forge_Frame out_frame; lv2_atom_forge_sequence_head(&self->forge, &out_frame, 0); @@ -526,11 +430,12 @@ run(LV2_Handle instance, uint32_t sample_count) } else if (property->atom.type != uris->atom_URID) { lv2_log_error(&self->log, "Set property is not a URID\n"); } else { + // Set property to the given value const LV2_URID key = property->body; set_parameter(self, key, value->size, value->type, value + 1, false); } } else if (obj->body.otype == uris->patch_Get) { - // Get the property and value of the get message + // Get the property of the get message const LV2_Atom_URID* subject = NULL; const LV2_Atom_URID* property = NULL; lv2_atom_object_get(obj, @@ -540,7 +445,7 @@ run(LV2_Handle instance, uint32_t sample_count) if (!subject_is_plugin(self, subject)) { lv2_log_error(&self->log, "Get with unknown subject\n"); } else if (!property) { - // Received a get message, emit our full state (probably to UI) + // Get with no property, emit complete state lv2_atom_forge_frame_time(&self->forge, ev->time.frames); LV2_Atom_Forge_Frame pframe; lv2_atom_forge_object(&self->forge, &pframe, 0, uris->patch_Put); @@ -548,41 +453,36 @@ run(LV2_Handle instance, uint32_t sample_count) LV2_Atom_Forge_Frame bframe; lv2_atom_forge_object(&self->forge, &bframe, 0, 0); - store_state(self, write_param_to_forge, &self->forge, 0, NULL); + save(self, write_param_to_forge, &self->forge, 0, NULL); lv2_atom_forge_pop(&self->forge, &bframe); lv2_atom_forge_pop(&self->forge, &pframe); } else if (property->atom.type != uris->atom_URID) { lv2_log_error(&self->log, "Get property is not a URID\n"); } else { - // Received a get message, emit single property state (probably to UI) - const LV2_URID key = property->body; - const LV2_Atom* atom = get_parameter(self, key); - if (atom) { + // Get for a specific property + const LV2_URID key = property->body; + const LV2_Atom* value = get_parameter(self, key); + if (value) { lv2_atom_forge_frame_time(&self->forge, ev->time.frames); - LV2_State_Status st = LV2_STATE_SUCCESS; LV2_Atom_Forge_Frame frame; lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); lv2_atom_forge_key(&self->forge, uris->patch_property); lv2_atom_forge_urid(&self->forge, property->body); - store_param(self, &st, write_param_to_forge, &self->forge, - uris->patch_value, atom); + store_prop(self, NULL, NULL, write_param_to_forge, &self->forge, + uris->patch_value, value); lv2_atom_forge_pop(&self->forge, &frame); } } } else { - if (self->unmap) { - lv2_log_trace(&self->log, "Unknown object type <%s>\n", - self->unmap->unmap(self->unmap->handle, obj->body.otype)); - } else { - lv2_log_trace(&self->log, "Unknown object type %d\n", - obj->body.otype); - } + lv2_log_trace(&self->log, "Unknown object type <%s>\n", + unmap(self, obj->body.otype)); } } - if (self->spring > 0.0f) { - self->spring = (self->spring >= 0.001) ? self->spring - 0.001 : 0.0; + if (self->state.spring.body > 0.0f) { + const float spring = self->state.spring.body; + self->state.spring.body = (spring >= 0.001) ? spring - 0.001 : 0.0; lv2_atom_forge_frame_time(&self->forge, 0); LV2_Atom_Forge_Frame frame; lv2_atom_forge_object(&self->forge, &frame, 0, uris->patch_Set); @@ -590,7 +490,7 @@ run(LV2_Handle instance, uint32_t sample_count) lv2_atom_forge_key(&self->forge, uris->patch_property); lv2_atom_forge_urid(&self->forge, uris->eg_spring); lv2_atom_forge_key(&self->forge, uris->patch_value); - lv2_atom_forge_float(&self->forge, self->spring); + lv2_atom_forge_float(&self->forge, self->state.spring.body); lv2_atom_forge_pop(&self->forge, &frame); } diff --git a/plugins/eg-params.lv2/state_map.h b/plugins/eg-params.lv2/state_map.h new file mode 100644 index 0000000..ccd9ca6 --- /dev/null +++ b/plugins/eg-params.lv2/state_map.h @@ -0,0 +1,110 @@ +/* + LV2 State Map + Copyright 2016 David Robillard <d@drobilla.net> + + 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 <stdlib.h> + +/** Entry in an array that serves as a dictionary of properties. */ +typedef struct { + const char* uri; + LV2_URID urid; + LV2_Atom* value; +} StateMapItem; + +/** Comparator for StateMapItems sorted by URID. */ +static int +state_map_cmp(const void* a, const void* b) +{ + const StateMapItem* ka = (const StateMapItem*)a; + const StateMapItem* kb = (const StateMapItem*)b; + if (ka->urid < kb->urid) { + return -1; + } else if (kb->urid < ka->urid) { + return 1; + } + return 0; +} + +/** Helper macro for terse state map initialisation. */ +#define STATE_MAP_INIT(type, ptr) \ + (LV2_ATOM__ ## type), \ + (sizeof(*ptr) - sizeof(LV2_Atom)), \ + (ptr) + +/** + Initialise a state map. + + The variable parameters list must be NULL terminated, and is a sequence of + const char* uri, const char* type, uint32_t size, LV2_Atom* value. The + value must point to a valid atom that resides elsewhere, the state map is + only an index and does not contain actual state values. The macro + STATE_MAP_INIT can be used to make simpler code when state is composed of + standard atom types, for example: + + struct Plugin { + LV2_URID_Map* map; + StateMapItem props[3]; + // ... + }; + + state_map_init( + self->props, self->map, self->map->handle, + PLUG_URI "#gain", STATE_MAP_INIT(Float, &state->gain), + PLUG_URI "#offset", STATE_MAP_INIT(Int, &state->offset), + PLUG_URI "#file", STATE_MAP_INIT(Path, &state->file), + NULL); +*/ +static void +state_map_init(StateMapItem dict[], + LV2_URID_Map* map, + LV2_URID_Map_Handle* handle, + /* const char* uri, const char* type, uint32_t size, LV2_Atom* value */ ...) +{ + // Set dict entries from parameters + unsigned i = 0; + va_list args; + va_start(args, handle); + for (const char* uri; (uri = va_arg(args, const char*)); ++i) { + const char* type = va_arg(args, const char*); + const uint32_t size = va_arg(args, uint32_t); + LV2_Atom* const value = va_arg(args, LV2_Atom*); + dict[i].uri = uri; + dict[i].urid = map->map(map->handle, uri); + dict[i].value = value; + dict[i].value->size = size; + dict[i].value->type = map->map(map->handle, type); + } + va_end(args); + + // Sort for fast lookup by URID by state_map_find() + qsort(dict, i, sizeof(StateMapItem), state_map_cmp); +} + +/** + Retrieve an item from a state map by URID. + + This takes O(lg(n)) time, and is useful for implementing generic property + access with little code, for example to respond to patch:Get messages for a + specific property. +*/ +static StateMapItem* +state_map_find(StateMapItem dict[], uint32_t n_entries, LV2_URID urid) +{ + const StateMapItem key = { NULL, urid, NULL }; + return (StateMapItem*)bsearch( + &key, dict, n_entries, sizeof(StateMapItem), state_map_cmp); +} + |