1 /*
2 * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch)
3 *
4 * This is free software: you can redistribute it and/or modify
5 * it under the terms of the Artistic License 2.0 as published by
6 * The Perl Foundation.
7 *
8 * This source is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * Artistic License 2.0 for more details.
12 *
13 * You should have received a copy of the Artistic License 2.0
14 * along the source as a COPYING file. If not, obtain it from
15 * http://www.perlfoundation.org/artistic_license_2_0.
16 */
17
18 #ifndef _LV2_PROPS_H_
19 #define _LV2_PROPS_H_
20
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24
25 #include <stdlib.h>
26 #include <stdatomic.h>
27 #include <stdio.h>
28
29 #include <lv2/lv2plug.in/ns/lv2core/lv2.h>
30 #include <lv2/lv2plug.in/ns/ext/urid/urid.h>
31 #include <lv2/lv2plug.in/ns/ext/atom/atom.h>
32 #include <lv2/lv2plug.in/ns/ext/atom/forge.h>
33 #include <lv2/lv2plug.in/ns/ext/patch/patch.h>
34 #include <lv2/lv2plug.in/ns/ext/state/state.h>
35
36 /*****************************************************************************
37 * API START
38 *****************************************************************************/
39
40 // structures
41 typedef struct _props_def_t props_def_t;
42 typedef struct _props_impl_t props_impl_t;
43 typedef struct _props_t props_t;
44
45 // function callbacks
46 typedef void (*props_event_cb_t)(
47 void *data,
48 int64_t frames,
49 props_impl_t *impl);
50
51 struct _props_def_t {
52 const char *property;
53 const char *type;
54 const char *access;
55 size_t offset;
56
57 uint32_t max_size;
58 props_event_cb_t event_cb;
59 };
60
61 struct _props_impl_t {
62 LV2_URID property;
63 LV2_URID type;
64 LV2_URID access;
65
66 struct {
67 uint32_t size;
68 void *body;
69 } value;
70 struct {
71 uint32_t size;
72 void *body;
73 } stash;
74
75 const props_def_t *def;
76
77 atomic_int state;
78 bool stashing;
79 };
80
81 struct _props_t {
82 struct {
83 LV2_URID subject;
84
85 LV2_URID patch_get;
86 LV2_URID patch_set;
87 LV2_URID patch_put;
88 LV2_URID patch_patch;
89 LV2_URID patch_wildcard;
90 LV2_URID patch_add;
91 LV2_URID patch_remove;
92 LV2_URID patch_subject;
93 LV2_URID patch_body;
94 LV2_URID patch_property;
95 LV2_URID patch_value;
96 LV2_URID patch_writable;
97 LV2_URID patch_readable;
98 LV2_URID patch_sequence;
99 LV2_URID patch_error;
100 LV2_URID patch_ack;
101
102 LV2_URID atom_int;
103 LV2_URID atom_long;
104 LV2_URID atom_float;
105 LV2_URID atom_double;
106 LV2_URID atom_bool;
107 LV2_URID atom_urid;
108 LV2_URID atom_path;
109 LV2_URID atom_literal;
110 LV2_URID atom_vector;
111 LV2_URID atom_object;
112 LV2_URID atom_sequence;
113 } urid;
114
115 void *data;
116
117 bool stashing;
118 atomic_bool restoring;
119
120 uint32_t max_size;
121
122 unsigned nimpls;
123 props_impl_t impls [0];
124 };
125
126 #define PROPS_T(PROPS, MAX_NIMPLS) \
127 props_t (PROPS); \
128 props_impl_t _impls [(MAX_NIMPLS)];
129
130 // rt-safe
131 static inline int
132 props_init(props_t *props, const char *subject,
133 const props_def_t *defs, int nimpls,
134 void *value_base, void *stash_base,
135 LV2_URID_Map *map, void *data);
136
137 // rt-safe
138 static inline void
139 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
140 LV2_Atom_Forge_Ref *ref);
141
142 // rt-safe
143 static inline int
144 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
145 const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref);
146
147 // rt-safe
148 static inline void
149 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
150 LV2_URID property, LV2_Atom_Forge_Ref *ref);
151
152 // rt-safe
153 static inline void
154 props_stash(props_t *props, LV2_URID property);
155
156 // rt-safe
157 static inline LV2_URID
158 props_map(props_t *props, const char *property);
159
160 // rt-safe
161 static inline const char *
162 props_unmap(props_t *props, LV2_URID property);
163
164 // non-rt
165 static inline LV2_State_Status
166 props_save(props_t *props, LV2_State_Store_Function store,
167 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
168
169 // non-rt
170 static inline LV2_State_Status
171 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
172 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features);
173
174 /*****************************************************************************
175 * API END
176 *****************************************************************************/
177
178 // enumerations
179 typedef enum _props_state_t props_state_t;
180
181 enum _props_state_t {
182 PROP_STATE_NONE = 0,
183 PROP_STATE_LOCK = 1,
184 PROP_STATE_RESTORE = 2
185 };
186
187 static inline void
_props_impl_spin_lock(props_impl_t * impl,int from,int to)188 _props_impl_spin_lock(props_impl_t *impl, int from, int to)
189 {
190 int expected = from;
191 const int desired = to;
192
193 while(!atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
194 memory_order_acquire, memory_order_acquire))
195 {
196 // spin
197 }
198 }
199
200 static inline bool
_props_impl_try_lock(props_impl_t * impl,int from,int to)201 _props_impl_try_lock(props_impl_t *impl, int from, int to)
202 {
203 int expected = from;
204 const int desired = to;
205
206 return atomic_compare_exchange_strong_explicit(&impl->state, &expected, desired,
207 memory_order_acquire, memory_order_acquire);
208 }
209
210 static inline void
_props_impl_unlock(props_impl_t * impl,int to)211 _props_impl_unlock(props_impl_t *impl, int to)
212 {
213 atomic_store_explicit(&impl->state, to, memory_order_release);
214 }
215
216 static inline bool
_props_restoring_get(props_t * props)217 _props_restoring_get(props_t *props)
218 {
219 return atomic_exchange_explicit(&props->restoring, false, memory_order_acquire);
220 }
221
222 static inline void
_props_restoring_set(props_t * props)223 _props_restoring_set(props_t *props)
224 {
225 atomic_store_explicit(&props->restoring, true, memory_order_release);
226 }
227
228 static inline void
_props_qsort(props_impl_t * A,int n)229 _props_qsort(props_impl_t *A, int n)
230 {
231 if(n < 2)
232 return;
233
234 const props_impl_t *p = A;
235
236 int i = -1;
237 int j = n;
238
239 while(true)
240 {
241 do {
242 i += 1;
243 } while(A[i].property < p->property);
244
245 do {
246 j -= 1;
247 } while(A[j].property > p->property);
248
249 if(i >= j)
250 break;
251
252 const props_impl_t tmp = A[i];
253 A[i] = A[j];
254 A[j] = tmp;
255 }
256
257 _props_qsort(A, j + 1);
258 _props_qsort(A + j + 1, n - j - 1);
259 }
260
261 static inline props_impl_t *
_props_bsearch(props_t * props,LV2_URID property)262 _props_bsearch(props_t *props, LV2_URID property)
263 {
264 props_impl_t *base = props->impls;
265
266 for(int N = props->nimpls, half; N > 1; N -= half)
267 {
268 half = N/2;
269 props_impl_t *dst = &base[half];
270 base = (dst->property > property) ? base : dst;
271 }
272
273 return (base->property == property) ? base : NULL;
274 }
275
276 static inline LV2_Atom_Forge_Ref
_props_patch_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,int32_t sequence_num)277 _props_patch_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
278 props_impl_t *impl, int32_t sequence_num)
279 {
280 LV2_Atom_Forge_Frame obj_frame;
281
282 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
283
284 if(ref)
285 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set);
286 {
287 if(props->urid.subject) // is optional
288 {
289 if(ref)
290 ref = lv2_atom_forge_key(forge, props->urid.patch_subject);
291 if(ref)
292 ref = lv2_atom_forge_urid(forge, props->urid.subject);
293 }
294
295 if(sequence_num) // is optional
296 {
297 if(ref)
298 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
299 if(ref)
300 ref = lv2_atom_forge_int(forge, sequence_num);
301 }
302
303 if(ref)
304 ref = lv2_atom_forge_key(forge, props->urid.patch_property);
305 if(ref)
306 ref = lv2_atom_forge_urid(forge, impl->property);
307
308 if(ref)
309 lv2_atom_forge_key(forge, props->urid.patch_value);
310 if(ref)
311 ref = lv2_atom_forge_atom(forge, impl->value.size, impl->type);
312 if(ref)
313 ref = lv2_atom_forge_write(forge, impl->value.body, impl->value.size);
314 }
315 if(ref)
316 lv2_atom_forge_pop(forge, &obj_frame);
317
318 return ref;
319 }
320
321 static inline LV2_Atom_Forge_Ref
_props_patch_error(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)322 _props_patch_error(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
323 int32_t sequence_num)
324 {
325 LV2_Atom_Forge_Frame obj_frame;
326
327 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
328
329 if(ref)
330 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_error);
331 {
332 if(ref)
333 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
334 if(ref)
335 ref = lv2_atom_forge_int(forge, sequence_num);
336 }
337 if(ref)
338 lv2_atom_forge_pop(forge, &obj_frame);
339
340 return ref;
341 }
342
343 static inline LV2_Atom_Forge_Ref
_props_patch_ack(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,int32_t sequence_num)344 _props_patch_ack(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
345 int32_t sequence_num)
346 {
347 LV2_Atom_Forge_Frame obj_frame;
348
349 LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames);
350
351 if(ref)
352 ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_ack);
353 {
354 if(ref)
355 ref = lv2_atom_forge_key(forge, props->urid.patch_sequence);
356 if(ref)
357 ref = lv2_atom_forge_int(forge, sequence_num);
358 }
359 if(ref)
360 lv2_atom_forge_pop(forge, &obj_frame);
361
362 return ref;
363 }
364
365 static inline void
_props_impl_stash(props_t * props,props_impl_t * impl)366 _props_impl_stash(props_t *props, props_impl_t *impl)
367 {
368 if(_props_impl_try_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK))
369 {
370 impl->stashing = false;
371 impl->stash.size = impl->value.size;
372 memcpy(impl->stash.body, impl->value.body, impl->value.size);
373
374 _props_impl_unlock(impl, PROP_STATE_NONE);
375 }
376 else
377 {
378 impl->stashing = true; // try again later
379 props->stashing = true;
380 }
381 }
382
383 static inline void
_props_impl_restore(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,props_impl_t * impl,LV2_Atom_Forge_Ref * ref)384 _props_impl_restore(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
385 props_impl_t *impl, LV2_Atom_Forge_Ref *ref)
386 {
387 if(_props_impl_try_lock(impl, PROP_STATE_RESTORE, PROP_STATE_LOCK))
388 {
389 impl->stashing = false; // makes no sense to stash a recently restored value
390 impl->value.size = impl->stash.size;
391 memcpy(impl->value.body, impl->stash.body, impl->stash.size);
392
393 _props_impl_unlock(impl, PROP_STATE_NONE);
394
395 if(*ref)
396 *ref = _props_patch_set(props, forge, frames, impl, 0);
397
398 const props_def_t *def = impl->def;
399 if(def->event_cb)
400 def->event_cb(props->data, 0, impl);
401 }
402 }
403
404 static inline void
_props_impl_set(props_t * props,props_impl_t * impl,LV2_URID type,uint32_t size,const void * body)405 _props_impl_set(props_t *props, props_impl_t *impl, LV2_URID type,
406 uint32_t size, const void *body)
407 {
408 if( (impl->type == type)
409 && ( (impl->def->max_size == 0) || (size <= impl->def->max_size)) )
410 {
411 impl->value.size = size;
412 memcpy(impl->value.body, body, size);
413
414 _props_impl_stash(props, impl);
415 }
416 }
417
418 static inline int
_props_impl_init(props_t * props,props_impl_t * impl,const props_def_t * def,void * value_base,void * stash_base,LV2_URID_Map * map)419 _props_impl_init(props_t *props, props_impl_t *impl, const props_def_t *def,
420 void *value_base, void *stash_base, LV2_URID_Map *map)
421 {
422 if(!def->property || !def->type)
423 return 0;
424
425 const LV2_URID type = map->map(map->handle, def->type);
426 const LV2_URID property = map->map(map->handle, def->property);
427 const LV2_URID access = def->access
428 ? map->map(map->handle, def->access)
429 : map->map(map->handle, LV2_PATCH__writable);
430
431 if(!type || !property || !access)
432 return 0;
433
434 impl->property = property;
435 impl->access = access;
436 impl->def = def;
437 impl->value.body = value_base + def->offset;
438 impl->stash.body = stash_base + def->offset;
439
440 uint32_t size;
441 if( (type == props->urid.atom_int)
442 || (type == props->urid.atom_float)
443 || (type == props->urid.atom_bool)
444 || (type == props->urid.atom_urid) )
445 {
446 size = 4;
447 }
448 else if((type == props->urid.atom_long)
449 || (type == props->urid.atom_double) )
450 {
451 size = 8;
452 }
453 else if(type == props->urid.atom_literal)
454 {
455 size = sizeof(LV2_Atom_Literal_Body);
456 }
457 else if(type == props->urid.atom_vector)
458 {
459 size = sizeof(LV2_Atom_Vector_Body);
460 }
461 else if(type == props->urid.atom_object)
462 {
463 size = sizeof(LV2_Atom_Object_Body);
464 }
465 else if(type == props->urid.atom_sequence)
466 {
467 size = sizeof(LV2_Atom_Sequence_Body);
468 }
469 else
470 {
471 size = 0; // assume everything else as having size 0
472 }
473
474 impl->type = type;
475 impl->value.size = size;
476 impl->stash.size = size;
477
478 atomic_init(&impl->state, PROP_STATE_NONE);
479
480 // update maximal value size
481 const uint32_t max_size = def->max_size
482 ? def->max_size
483 : size;
484
485 if(max_size > props->max_size)
486 {
487 props->max_size = max_size;
488 }
489
490 return 1;
491 }
492
493 static inline int
props_init(props_t * props,const char * subject,const props_def_t * defs,int nimpls,void * value_base,void * stash_base,LV2_URID_Map * map,void * data)494 props_init(props_t *props, const char *subject,
495 const props_def_t *defs, int nimpls,
496 void *value_base, void *stash_base,
497 LV2_URID_Map *map, void *data)
498 {
499 if(!props || !defs || !value_base || !stash_base || !map)
500 return 0;
501
502 props->nimpls = nimpls;
503 props->data = data;
504
505 props->urid.subject = subject ? map->map(map->handle, subject) : 0;
506
507 props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get);
508 props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set);
509 props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put);
510 props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch);
511 props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard);
512 props->urid.patch_add = map->map(map->handle, LV2_PATCH__add);
513 props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove);
514 props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject);
515 props->urid.patch_body = map->map(map->handle, LV2_PATCH__body);
516 props->urid.patch_property = map->map(map->handle, LV2_PATCH__property);
517 props->urid.patch_value = map->map(map->handle, LV2_PATCH__value);
518 props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable);
519 props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable);
520 props->urid.patch_sequence = map->map(map->handle, LV2_PATCH__sequenceNumber);
521 props->urid.patch_ack = map->map(map->handle, LV2_PATCH__Ack);
522 props->urid.patch_error = map->map(map->handle, LV2_PATCH__Error);
523
524 props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
525 props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
526 props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
527 props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double);
528 props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool);
529 props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID);
530 props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path);
531 props->urid.atom_literal = map->map(map->handle, LV2_ATOM__Literal);
532 props->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
533 props->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
534 props->urid.atom_sequence = map->map(map->handle, LV2_ATOM__Sequence);
535
536 atomic_init(&props->restoring, false);
537
538 int status = 1;
539 for(unsigned i = 0; i < props->nimpls; i++)
540 {
541 props_impl_t *impl = &props->impls[i];
542
543 status = status
544 && _props_impl_init(props, impl, &defs[i], value_base, stash_base, map);
545 }
546
547 _props_qsort(props->impls, props->nimpls);
548
549 return status;
550 }
551
552 static inline void
props_idle(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_Atom_Forge_Ref * ref)553 props_idle(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
554 LV2_Atom_Forge_Ref *ref)
555 {
556 if(_props_restoring_get(props))
557 {
558 for(unsigned i = 0; i < props->nimpls; i++)
559 {
560 props_impl_t *impl = &props->impls[i];
561
562 _props_impl_restore(props, forge, frames, impl, ref);
563 }
564 }
565
566 if(props->stashing)
567 {
568 props->stashing = false;
569
570 for(unsigned i = 0; i < props->nimpls; i++)
571 {
572 props_impl_t *impl = &props->impls[i];
573
574 if(impl->stashing)
575 _props_impl_stash(props, impl);
576 }
577 }
578 }
579
580 static inline int
props_advance(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,const LV2_Atom_Object * obj,LV2_Atom_Forge_Ref * ref)581 props_advance(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
582 const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref)
583 {
584 if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
585 {
586 return 0;
587 }
588
589 if(obj->body.otype == props->urid.patch_get)
590 {
591 const LV2_Atom_URID *subject = NULL;
592 const LV2_Atom_URID *property = NULL;
593 const LV2_Atom_Int *sequence = NULL;
594
595 LV2_Atom_Object_Query q [] = {
596 { props->urid.patch_subject, (const LV2_Atom **)&subject },
597 { props->urid.patch_property, (const LV2_Atom **)&property },
598 { props->urid.patch_sequence, (const LV2_Atom **)&sequence },
599 LV2_ATOM_OBJECT_QUERY_END
600 };
601 lv2_atom_object_query(obj, q);
602
603 // check for a matching optional subject
604 if( (subject && props->urid.subject)
605 && ( (subject->atom.type != props->urid.atom_urid)
606 || (subject->body != props->urid.subject) ) )
607 {
608 return 0;
609 }
610
611 int32_t sequence_num = 0;
612 if(sequence && (sequence->atom.type == props->urid.atom_int))
613 {
614 sequence_num = sequence->body;
615 }
616
617 if(!property)
618 {
619 for(unsigned i = 0; i < props->nimpls; i++)
620 {
621 props_impl_t *impl = &props->impls[i];
622 const props_def_t *def = impl->def;
623
624 if(*ref)
625 *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
626 }
627
628 return 1;
629 }
630 else if(property->atom.type == props->urid.atom_urid)
631 {
632 props_impl_t *impl = _props_bsearch(props, property->body);
633
634 if(impl)
635 {
636 *ref = _props_patch_set(props, forge, frames, impl, sequence_num);
637
638 const props_def_t *def = impl->def;
639
640 return 1;
641 }
642 else if(sequence_num)
643 {
644 *ref = _props_patch_error(props, forge, frames, sequence_num);
645 }
646 }
647 else if(sequence_num)
648 {
649 *ref = _props_patch_error(props, forge, frames, sequence_num);
650 }
651 }
652 else if(obj->body.otype == props->urid.patch_set)
653 {
654 const LV2_Atom_URID *subject = NULL;
655 const LV2_Atom_URID *property = NULL;
656 const LV2_Atom_Int *sequence = NULL;
657 const LV2_Atom *value = NULL;
658
659 LV2_Atom_Object_Query q [] = {
660 { props->urid.patch_subject, (const LV2_Atom **)&subject },
661 { props->urid.patch_property, (const LV2_Atom **)&property },
662 { props->urid.patch_sequence, (const LV2_Atom **)&sequence },
663 { props->urid.patch_value, &value },
664 LV2_ATOM_OBJECT_QUERY_END
665 };
666 lv2_atom_object_query(obj, q);
667
668 // check for a matching optional subject
669 if( (subject && props->urid.subject)
670 && ( (subject->atom.type != props->urid.atom_urid)
671 || (subject->body != props->urid.subject) ) )
672 {
673 return 0;
674 }
675
676 int32_t sequence_num = 0;
677 if(sequence && (sequence->atom.type == props->urid.atom_int))
678 {
679 sequence_num = sequence->body;
680 }
681
682 if(!property || (property->atom.type != props->urid.atom_urid) || !value)
683 {
684 if(sequence_num)
685 {
686 *ref = _props_patch_error(props, forge, frames, sequence_num);
687 }
688
689 return 0;
690 }
691
692 props_impl_t *impl = _props_bsearch(props, property->body);
693 if(impl && (impl->access == props->urid.patch_writable) )
694 {
695 _props_impl_set(props, impl, value->type, value->size,
696 LV2_ATOM_BODY_CONST(value));
697
698 const props_def_t *def = impl->def;
699 if(def->event_cb)
700 def->event_cb(props->data, frames, impl);
701
702 if(sequence_num)
703 {
704 *ref = _props_patch_ack(props, forge, frames, sequence_num);
705 }
706
707 return 1;
708 }
709 else if(sequence_num)
710 {
711 *ref = _props_patch_error(props, forge, frames, sequence_num);
712 }
713 }
714 else if(obj->body.otype == props->urid.patch_put)
715 {
716 const LV2_Atom_URID *subject = NULL;
717 const LV2_Atom_Int *sequence = NULL;
718 const LV2_Atom_Object *body = NULL;
719
720 LV2_Atom_Object_Query q [] = {
721 { props->urid.patch_subject, (const LV2_Atom **)&subject },
722 { props->urid.patch_sequence, (const LV2_Atom **)&sequence},
723 { props->urid.patch_body, (const LV2_Atom **)&body },
724 LV2_ATOM_OBJECT_QUERY_END
725 };
726 lv2_atom_object_query(obj, q);
727
728 // check for a matching optional subject
729 if( (subject && props->urid.subject)
730 && ( (subject->atom.type != props->urid.atom_urid)
731 || (subject->body != props->urid.subject) ) )
732 {
733 return 0;
734 }
735
736 int32_t sequence_num = 0;
737 if(sequence && (sequence->atom.type == props->urid.atom_int))
738 {
739 sequence_num = sequence->body;
740 }
741
742 if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type))
743 {
744 if(sequence_num)
745 {
746 *ref = _props_patch_error(props, forge, frames, sequence_num);
747 }
748
749 return 0;
750 }
751
752 LV2_ATOM_OBJECT_FOREACH(body, prop)
753 {
754 const LV2_URID property = prop->key;
755 const LV2_Atom *value = &prop->value;
756
757 props_impl_t *impl = _props_bsearch(props, property);
758 if(impl && (impl->access == props->urid.patch_writable) )
759 {
760 _props_impl_set(props, impl, value->type, value->size,
761 LV2_ATOM_BODY_CONST(value));
762
763 const props_def_t *def = impl->def;
764 if(def->event_cb)
765 def->event_cb(props->data, frames, impl);
766 }
767 }
768
769 if(sequence_num)
770 {
771 *ref = _props_patch_ack(props, forge, frames, sequence_num);
772 }
773
774 return 1;
775 }
776
777 return 0; // did not handle a patch event
778 }
779
780 static inline void
props_set(props_t * props,LV2_Atom_Forge * forge,uint32_t frames,LV2_URID property,LV2_Atom_Forge_Ref * ref)781 props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames,
782 LV2_URID property, LV2_Atom_Forge_Ref *ref)
783 {
784 props_impl_t *impl = _props_bsearch(props, property);
785
786 if(impl)
787 {
788 _props_impl_stash(props, impl);
789
790 if(*ref) //TODO use patch:sequenceNumber
791 *ref = _props_patch_set(props, forge, frames, impl, 0);
792 }
793 }
794
795 static inline void
props_stash(props_t * props,LV2_URID property)796 props_stash(props_t *props, LV2_URID property)
797 {
798 props_impl_t *impl = _props_bsearch(props, property);
799
800 if(impl)
801 _props_impl_stash(props, impl);
802 }
803
804 static inline LV2_URID
props_map(props_t * props,const char * uri)805 props_map(props_t *props, const char *uri)
806 {
807 for(unsigned i = 0; i < props->nimpls; i++)
808 {
809 props_impl_t *impl = &props->impls[i];
810
811 if(!strcmp(impl->def->property, uri))
812 return impl->property;
813 }
814
815 return 0;
816 }
817
818 static inline const char *
props_unmap(props_t * props,LV2_URID property)819 props_unmap(props_t *props, LV2_URID property)
820 {
821 props_impl_t *impl = _props_bsearch(props, property);
822
823 if(impl)
824 return impl->def->property;
825
826 return NULL;
827 }
828
829 static inline LV2_State_Status
props_save(props_t * props,LV2_State_Store_Function store,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)830 props_save(props_t *props, LV2_State_Store_Function store,
831 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
832 {
833 const LV2_State_Map_Path *map_path = NULL;
834
835 // set POD flag if not already set by host
836 flags |= LV2_STATE_IS_POD;
837
838 for(unsigned i = 0; features[i]; i++)
839 {
840 if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
841 {
842 map_path = features[i]->data;
843 break;
844 }
845 }
846
847 void *body = malloc(props->max_size); // create memory to store widest value
848 if(body)
849 {
850 for(unsigned i = 0; i < props->nimpls; i++)
851 {
852 props_impl_t *impl = &props->impls[i];
853
854 if(impl->access == props->urid.patch_readable)
855 continue; // skip read-only, as it makes no sense to restore them
856
857 _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
858
859 // create temporary copy of value, store() may well be blocking
860 const uint32_t size = impl->stash.size;
861 memcpy(body, impl->stash.body, size);
862
863 _props_impl_unlock(impl, PROP_STATE_NONE);
864
865 if( map_path && (impl->type == props->urid.atom_path) )
866 {
867 const char *path = strstr(body, "file://")
868 ? body + 7 // skip "file://"
869 : body;
870 char *abstract = map_path->abstract_path(map_path->handle, path);
871 if(abstract)
872 {
873 const uint32_t sz = strlen(abstract) + 1;
874 store(state, impl->property, abstract, sz, impl->type, flags);
875
876 free(abstract);
877 }
878 }
879 else // !Path
880 {
881 store(state, impl->property, body, size, impl->type, flags);
882 }
883 }
884
885 free(body);
886 }
887
888 return LV2_STATE_SUCCESS;
889 }
890
891 static inline LV2_State_Status
props_restore(props_t * props,LV2_State_Retrieve_Function retrieve,LV2_State_Handle state,uint32_t flags,const LV2_Feature * const * features)892 props_restore(props_t *props, LV2_State_Retrieve_Function retrieve,
893 LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features)
894 {
895 const LV2_State_Map_Path *map_path = NULL;
896
897 for(unsigned i = 0; features[i]; i++)
898 {
899 if(!strcmp(features[i]->URI, LV2_STATE__mapPath))
900 map_path = features[i]->data;
901 }
902
903 for(unsigned i = 0; i < props->nimpls; i++)
904 {
905 props_impl_t *impl = &props->impls[i];
906
907 if(impl->access == props->urid.patch_readable)
908 continue; // skip read-only, as it makes no sense to restore them
909
910 size_t size;
911 uint32_t type;
912 uint32_t _flags;
913 const void *body = retrieve(state, impl->property, &size, &type, &_flags);
914
915 if( body
916 && (type == impl->type)
917 && ( (impl->def->max_size == 0) || (size <= impl->def->max_size) ) )
918 {
919 if(map_path && (type == props->urid.atom_path) )
920 {
921 char *absolute = map_path->absolute_path(map_path->handle, body);
922 if(absolute)
923 {
924 const uint32_t sz = strlen(absolute) + 1;
925
926 _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
927
928 impl->stash.size = sz;
929 memcpy(impl->stash.body, absolute, sz);
930
931 _props_impl_unlock(impl, PROP_STATE_RESTORE);
932
933 free(absolute);
934 }
935 }
936 else // !Path
937 {
938 _props_impl_spin_lock(impl, PROP_STATE_NONE, PROP_STATE_LOCK);
939
940 impl->stash.size = size;
941 memcpy(impl->stash.body, body, size);
942
943 _props_impl_unlock(impl, PROP_STATE_RESTORE);
944 }
945 }
946 }
947
948 _props_restoring_set(props);
949
950 return LV2_STATE_SUCCESS;
951 }
952
953 #ifdef __cplusplus
954 }
955 #endif
956
957 #endif // _LV2_PROPS_H_
958