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