1 /*
2 LV2 State Map
3 Copyright 2016 David Robillard <d@drobilla.net>
4
5 Permission to use, copy, modify, and/or distribute this software for any
6 purpose with or without fee is hereby granted, provided that the above
7 copyright notice and this permission notice appear in all copies.
8
9 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include "lv2/atom/atom.h"
19 #include "lv2/urid/urid.h"
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23
24 /** Entry in an array that serves as a dictionary of properties. */
25 typedef struct {
26 const char* uri;
27 LV2_URID urid;
28 LV2_Atom* value;
29 } StateMapItem;
30
31 /** Comparator for StateMapItems sorted by URID. */
32 static int
state_map_cmp(const void * a,const void * b)33 state_map_cmp(const void* a, const void* b)
34 {
35 const StateMapItem* ka = (const StateMapItem*)a;
36 const StateMapItem* kb = (const StateMapItem*)b;
37 if (ka->urid < kb->urid) {
38 return -1;
39 } else if (kb->urid < ka->urid) {
40 return 1;
41 }
42 return 0;
43 }
44
45 /** Helper macro for terse state map initialisation. */
46 #define STATE_MAP_INIT(type, ptr) \
47 (LV2_ATOM__ ## type), \
48 (sizeof(*ptr) - sizeof(LV2_Atom)), \
49 (ptr)
50
51 /**
52 Initialise a state map.
53
54 The variable parameters list must be NULL terminated, and is a sequence of
55 const char* uri, const char* type, uint32_t size, LV2_Atom* value. The
56 value must point to a valid atom that resides elsewhere, the state map is
57 only an index and does not contain actual state values. The macro
58 STATE_MAP_INIT can be used to make simpler code when state is composed of
59 standard atom types, for example:
60
61 struct Plugin {
62 LV2_URID_Map* map;
63 StateMapItem props[3];
64 // ...
65 };
66
67 state_map_init(
68 self->props, self->map, self->map->handle,
69 PLUG_URI "#gain", STATE_MAP_INIT(Float, &state->gain),
70 PLUG_URI "#offset", STATE_MAP_INIT(Int, &state->offset),
71 PLUG_URI "#file", STATE_MAP_INIT(Path, &state->file),
72 NULL);
73 */
74 static void
state_map_init(StateMapItem dict[],LV2_URID_Map * map,LV2_URID_Map_Handle handle,...)75 state_map_init(StateMapItem dict[],
76 LV2_URID_Map* map,
77 LV2_URID_Map_Handle handle,
78 /* const char* uri, const char* type, uint32_t size, LV2_Atom* value */ ...)
79 {
80 // Set dict entries from parameters
81 unsigned i = 0;
82 va_list args;
83 va_start(args, handle);
84 for (const char* uri; (uri = va_arg(args, const char*)); ++i) {
85 const char* type = va_arg(args, const char*);
86 const uint32_t size = va_arg(args, uint32_t);
87 LV2_Atom* const value = va_arg(args, LV2_Atom*);
88 dict[i].uri = uri;
89 dict[i].urid = map->map(map->handle, uri);
90 dict[i].value = value;
91 dict[i].value->size = size;
92 dict[i].value->type = map->map(map->handle, type);
93 }
94 va_end(args);
95
96 // Sort for fast lookup by URID by state_map_find()
97 qsort(dict, i, sizeof(StateMapItem), state_map_cmp);
98 }
99
100 /**
101 Retrieve an item from a state map by URID.
102
103 This takes O(lg(n)) time, and is useful for implementing generic property
104 access with little code, for example to respond to patch:Get messages for a
105 specific property.
106 */
107 static StateMapItem*
state_map_find(StateMapItem dict[],uint32_t n_entries,LV2_URID urid)108 state_map_find(StateMapItem dict[], uint32_t n_entries, LV2_URID urid)
109 {
110 const StateMapItem key = { NULL, urid, NULL };
111 return (StateMapItem*)bsearch(
112 &key, dict, n_entries, sizeof(StateMapItem), state_map_cmp);
113 }
114
115