// Copyright 2016 David Robillard // SPDX-License-Identifier: ISC #include "lv2/atom/atom.h" #include "lv2/urid/urid.h" #include #include #include /** 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; } 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; // NOLINT(cppcoreguidelines-init-variables) va_start(args, handle); for (const char* uri = NULL; (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); }