diff options
author | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2016-06-07 10:58:52 +0200 |
---|---|---|
committer | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2016-06-07 10:58:52 +0200 |
commit | f42d2337cfba49be6868905d57ad28af4a6a2053 (patch) | |
tree | 7ab71fe36ccc093161505e89fe186640f37fd3b7 | |
parent | 12e61f66451039eab6580d562b3e8e87fb5e26d5 (diff) | |
parent | 28e396adb8d82630fc7f6b5b141b47f2c4deab41 (diff) | |
download | sherlock.lv2-f42d2337cfba49be6868905d57ad28af4a6a2053.tar.xz |
Add 'props.lv2/' from commit '28e396adb8d82630fc7f6b5b141b47f2c4deab41'
git-subtree-dir: props.lv2
git-subtree-mainline: 12e61f66451039eab6580d562b3e8e87fb5e26d5
git-subtree-split: 28e396adb8d82630fc7f6b5b141b47f2c4deab41
-rw-r--r-- | props.lv2/CMakeLists.txt | 41 | ||||
-rw-r--r-- | props.lv2/COPYING | 201 | ||||
-rw-r--r-- | props.lv2/README.md | 20 | ||||
-rw-r--r-- | props.lv2/props.h | 1325 | ||||
-rw-r--r-- | props.lv2/test/chunk.bin | bin | 0 -> 16 bytes | |||
-rw-r--r-- | props.lv2/test/manifest.ttl.in | 28 | ||||
-rw-r--r-- | props.lv2/test/props.c | 580 | ||||
-rw-r--r-- | props.lv2/test/props.ttl | 152 |
8 files changed, 2347 insertions, 0 deletions
diff --git a/props.lv2/CMakeLists.txt b/props.lv2/CMakeLists.txt new file mode 100644 index 0000000..2fff6f0 --- /dev/null +++ b/props.lv2/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 2.8) + +project(props.lv2) + +include_directories(${PROJECT_SOURCE_DIR}) +include_directories(${PROJECT_BINARY_DIR}) + +set(CMAKE_C_FLAGS "-std=gnu99 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}") +set(CMAKE_MODULE_LINKER_FLAGS "-Wl,-z,nodelete ${CMAKE_MODULE_LINKER_FLAGS}") +add_definitions("-D_GNU_SOURCE=1") # asprintf + +set(PROPS_MAJOR_VERSION 0) +set(PROPS_MINOR_VERSION 1) +set(PROPS_MICRO_VERSION 1) +set(PROPS_VERSION "${PROPS_MAJOR_VERSION}.${PROPS_MINOR_VERSION}.${PROPS_MICRO_VERSION}") +add_definitions("-DPROPS_VERSION=\"${PROPS_VERSION}\"") + +set(DEST lib/lv2/props.lv2) +if(WIN32) + set(LIB_EXT ".dll") +elseif(APPLE) + set(LIB_EXT ".so") +else() + set(LIB_EXT ".so") +endif() + +find_package(PkgConfig) # ${PKG_CONFIG_FOUND} + +# props +add_library(props MODULE + test/props.c) +target_link_libraries(props ${LIBS}) +set_target_properties(props PROPERTIES PREFIX "") +install(TARGETS props DESTINATION ${DEST}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test/props.ttl DESTINATION ${DEST}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test/chunk.bin DESTINATION ${DEST}) + +# manifest +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/manifest.ttl.in ${PROJECT_BINARY_DIR}/manifest.ttl) +install(FILES ${PROJECT_BINARY_DIR}/manifest.ttl DESTINATION ${DEST}) diff --git a/props.lv2/COPYING b/props.lv2/COPYING new file mode 100644 index 0000000..ddb9a46 --- /dev/null +++ b/props.lv2/COPYING @@ -0,0 +1,201 @@ + The Artistic License 2.0 + + Copyright (c) 2000-2006, The Perl Foundation. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/props.lv2/README.md b/props.lv2/README.md new file mode 100644 index 0000000..08466d2 --- /dev/null +++ b/props.lv2/README.md @@ -0,0 +1,20 @@ +# Props.lv2 + +## Utility header for property based LV2 plugins + +### License + +Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) + +This is free software: you can redistribute it and/or modify +it under the terms of the Artistic License 2.0 as published by +The Perl Foundation. + +This source is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +Artistic License 2.0 for more details. + +You should have received a copy of the Artistic License 2.0 +along the source as a COPYING file. If not, obtain it from +<http://www.perlfoundation.org/artistic_license_2_0>. diff --git a/props.lv2/props.h b/props.lv2/props.h new file mode 100644 index 0000000..ebbc3ec --- /dev/null +++ b/props.lv2/props.h @@ -0,0 +1,1325 @@ +/* + * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef _LV2_PROPS_H_ +#define _LV2_PROPS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdatomic.h> + +#include <lv2/lv2plug.in/ns/lv2core/lv2.h> +#include <lv2/lv2plug.in/ns/ext/urid/urid.h> +#include <lv2/lv2plug.in/ns/ext/atom/atom.h> +#include <lv2/lv2plug.in/ns/ext/atom/forge.h> +#include <lv2/lv2plug.in/ns/ext/patch/patch.h> +#include <lv2/lv2plug.in/ns/ext/state/state.h> +#include <lv2/lv2plug.in/ns/extensions/units/units.h> + +/***************************************************************************** + * API START + *****************************************************************************/ + +// definitions +#define PROPS_TYPE_N 10 + +// unions +typedef union _props_raw_t props_raw_t; + +// enumerations +typedef enum _props_mode_t props_mode_t; +typedef enum _props_event_t props_event_t; + +// structures +typedef struct _props_scale_point_t props_scale_point_t; +typedef struct _props_def_t props_def_t; +typedef struct _props_type_t props_type_t; +typedef struct _props_impl_t props_impl_t; +typedef struct _props_t props_t; + +// function callbacks +typedef void (*props_event_cb_t)( + void *data, + LV2_Atom_Forge *forge, + int64_t frames, + props_event_t event, + props_impl_t *impl); + +typedef uint32_t (*props_type_size_cb_t)( + const void *value); + +typedef LV2_Atom_Forge_Ref (*props_type_get_cb_t)( + LV2_Atom_Forge *forge, + const void *value); + +typedef void (*props_type_set_cb_t)( + props_impl_t *impl, + void *value, + LV2_URID new_type, + uint32_t sz, + const void *new_value); + +union _props_raw_t { + const int32_t i; // Int + const int64_t h; // Long + const float f; // Float + const double d; // Double + const int32_t b; // Bool + const uint32_t u; // URID + //TODO more types +}; + +enum _props_mode_t { + PROP_MODE_STATIC = 0, + PROP_MODE_DYNAMIC = 1 +}; + +enum _props_event_t { + PROP_EVENT_GET = (1 << 0), + PROP_EVENT_SET = (1 << 1), + PROP_EVENT_SAVE = (1 << 2), + PROP_EVENT_RESTORE = (1 << 3), + PROP_EVENT_REGISTER = (1 << 4) +}; + +#define PROP_EVENT_NONE (0) +#define PROP_EVENT_READ (PROP_EVENT_GET | PROP_EVENT_SAVE) +#define PROP_EVENT_WRITE (PROP_EVENT_SET | PROP_EVENT_RESTORE) +#define PROP_EVENT_RW (PROP_EVENT_READ | PROP_EVENT_WRITE) +#define PROP_EVENT_ALL (PROP_EVENT_RW | PROP_EVENT_REGISTER) + +struct _props_scale_point_t { + const char *label; + props_raw_t value; +}; + +struct _props_def_t { + const char *property; + const char *type; + const char *access; + const char *unit; + props_mode_t mode; + props_event_t event_mask; + props_event_cb_t event_cb; + uint32_t max_size; + + const char *label; + const char *comment; + props_raw_t minimum; + props_raw_t maximum; + const props_scale_point_t *scale_points; +}; + +struct _props_type_t { + LV2_URID urid; + uint32_t size; + props_type_size_cb_t size_cb; + props_type_get_cb_t get_cb; + props_type_set_cb_t set_cb; +}; + +struct _props_impl_t { + LV2_URID property; + LV2_URID access; + LV2_URID unit; + const props_t *props; + const props_type_t *type; + const props_def_t *def; + void *value; + void *stash; + atomic_flag lock; + bool stashing; +}; + +struct _props_t { + struct { + LV2_URID subject; + + LV2_URID patch_get; + LV2_URID patch_set; + LV2_URID patch_put; + LV2_URID patch_patch; + LV2_URID patch_wildcard; + LV2_URID patch_add; + LV2_URID patch_remove; + LV2_URID patch_subject; + LV2_URID patch_body; + LV2_URID patch_property; + LV2_URID patch_value; + LV2_URID patch_writable; + LV2_URID patch_readable; + + LV2_URID rdf_value; + + LV2_URID rdfs_label; + LV2_URID rdfs_range; + LV2_URID rdfs_comment; + + LV2_URID lv2_minimum; + LV2_URID lv2_maximum; + LV2_URID lv2_scale_point; + + LV2_URID atom_int; + LV2_URID atom_long; + LV2_URID atom_float; + LV2_URID atom_double; + LV2_URID atom_bool; + LV2_URID atom_urid; + LV2_URID atom_string; + LV2_URID atom_path; + LV2_URID atom_uri; + LV2_URID atom_chunk; + + LV2_URID units_unit; + } urid; + + LV2_URID_Map *map; + void *data; + + props_type_t types [PROPS_TYPE_N]; + + bool stashing; + + unsigned max_size; + unsigned max_nimpls; + unsigned nimpls; + props_impl_t impls [0]; +}; + +#define PROPS_T(PROPS, MAX_NIMPLS) \ + props_t (PROPS); \ + props_impl_t _impls [(MAX_NIMPLS)]; + +// rt-safe +static inline int +props_init(props_t *props, const size_t max_nimpls, const char *subject, + LV2_URID_Map *map, void *data); + +// rt-safe +static inline LV2_URID +props_register(props_t *props, const props_def_t *def, void *value, void *stash); + +// rt-safe +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); + +// rt-safe +static inline void +props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames, LV2_URID property, + LV2_Atom_Forge_Ref *ref); + +// rt-safe +static inline void +props_stash(props_t *props, LV2_URID property); + +// non-rt +static inline LV2_State_Status +props_save(props_t *props, LV2_Atom_Forge *forge, LV2_State_Store_Function store, + LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features); + +// non-rt +static inline LV2_State_Status +props_restore(props_t *props, LV2_Atom_Forge *forge, LV2_State_Retrieve_Function retrieve, + LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features); + +/***************************************************************************** + * API END + *****************************************************************************/ + +static inline void +_impl_spin_lock(props_impl_t *impl) +{ + while(atomic_flag_test_and_set_explicit(&impl->lock, memory_order_acquire)) + { + // spin + } +} + +static inline bool +_impl_try_lock(props_impl_t *impl) +{ + return atomic_flag_test_and_set_explicit(&impl->lock, memory_order_acquire) == false; +} + +static inline void +_impl_unlock(props_impl_t *impl) +{ + atomic_flag_clear_explicit(&impl->lock, memory_order_release); +} + +static inline uint32_t +_impl_size_get(props_impl_t *impl) +{ + return impl->type->size_cb + ? impl->type->size_cb(impl->value) + : impl->type->size; +} + +static LV2_Atom_Forge_Ref +_props_int_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_int(forge, *(const int32_t *)value); +} +static LV2_Atom_Forge_Ref +_props_bool_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_bool(forge, *(const int32_t *)value); +} +static void +_props_int_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + int32_t *ref = value; + + if(new_type == props->urid.atom_int) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_bool) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_urid) + *ref = *(const uint32_t *)new_value; + else if(new_type == props->urid.atom_long) + *ref = *(const int64_t *)new_value; + + else if(new_type == props->urid.atom_float) + *ref = *(const float *)new_value; + else if(new_type == props->urid.atom_double) + *ref = *(const double *)new_value; +} + +static LV2_Atom_Forge_Ref +_props_long_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_long(forge, *(const int64_t *)value); +} +static void +_props_long_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + int64_t *ref = value; + + if(new_type == props->urid.atom_long) + *ref = *(const int64_t *)new_value; + else if(new_type == props->urid.atom_int) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_bool) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_urid) + *ref = *(const uint32_t *)new_value; + + else if(new_type == props->urid.atom_float) + *ref = *(const float *)new_value; + else if(new_type == props->urid.atom_double) + *ref = *(const double *)new_value; +} + +static LV2_Atom_Forge_Ref +_props_float_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_float(forge, *(const float *)value); +} +static void +_props_float_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + float *ref = value; + + if(new_type == props->urid.atom_float) + *ref = *(const float *)new_value; + else if(new_type == props->urid.atom_double) + *ref = *(const double *)new_value; + + else if(new_type == props->urid.atom_int) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_bool) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_urid) + *ref = *(const uint32_t *)new_value; + else if(new_type == props->urid.atom_long) + *ref = *(const int64_t *)new_value; +} + +static LV2_Atom_Forge_Ref +_props_double_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_double(forge, *(const double *)value); +} +static void +_props_double_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + double *ref = value; + + if(new_type == props->urid.atom_double) + *ref = *(const double *)new_value; + else if(new_type == props->urid.atom_float) + *ref = *(const float *)new_value; + + else if(new_type == props->urid.atom_int) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_bool) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_urid) + *ref = *(const uint32_t *)new_value; + else if(new_type == props->urid.atom_long) + *ref = *(const int64_t *)new_value; +} + +static LV2_Atom_Forge_Ref +_props_urid_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_urid(forge, *(const uint32_t *)value); +} +static void +_props_urid_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + uint32_t *ref = value; + + if(new_type == props->urid.atom_urid) + *ref = *(const uint32_t *)new_value; + + else if(new_type == props->urid.atom_int) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_long) + *ref = *(const int64_t *)new_value; + else if(new_type == props->urid.atom_bool) + *ref = *(const int32_t *)new_value; + else if(new_type == props->urid.atom_float) + *ref = *(const float *)new_value; + else if(new_type == props->urid.atom_double) + *ref = *(const double *)new_value; +} + +static uint32_t +_props_string_size_cb(const void *value) +{ + return strlen((const char *)value) + 1; +} +static LV2_Atom_Forge_Ref +_props_string_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_string(forge, (const char *)value, strlen((const char *)value)); +} +static LV2_Atom_Forge_Ref +_props_path_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_path(forge, (const char *)value, strlen((const char *)value)); +} +static LV2_Atom_Forge_Ref +_props_uri_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + return lv2_atom_forge_uri(forge, (const char *)value, strlen((const char *)value)); +} +static void +_props_string_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + + if( (new_type == props->urid.atom_string) + || (new_type == props->urid.atom_path) + || (new_type == props->urid.atom_uri) ) + strncpy((char *)value, (const char *)new_value, impl->def->max_size); +} + +static uint32_t +_props_chunk_size_cb(const void *value) +{ + const uint32_t sz = *(uint32_t *)value; + return sz; +} +static LV2_Atom_Forge_Ref +_props_chunk_get_cb(LV2_Atom_Forge *forge, const void *value) +{ + const uint32_t sz = *(uint32_t *)value; + const uint8_t *src = value + sizeof(uint32_t); + LV2_Atom_Forge_Ref ref; + + return (ref = lv2_atom_forge_atom(forge, sz, forge->Chunk)) + && (ref = lv2_atom_forge_write(forge, src, sz)); +} +static void +_props_chunk_set_cb(props_impl_t *impl, void *value, + LV2_URID new_type, uint32_t sz, const void *new_value) +{ + const props_t *props = impl->props; + + if(new_type == props->urid.atom_chunk) + { + *(uint32_t *)value = sz; // set chunk size + uint8_t *dst = value + sizeof(uint32_t); + const uint32_t msz = sz < impl->def->max_size - sizeof(uint32_t) + ? sz + : impl->def->max_size - sizeof(uint32_t); + memcpy(dst, new_value, msz); + } +} + +static inline void +_type_qsort(props_type_t *a, unsigned n) +{ + if(n < 2) + return; + + const props_type_t *p = &a[n/2]; + + unsigned i, j; + for(i=0, j=n-1; ; i++, j--) + { + while(a[i].urid < p->urid) + i++; + + while(p->urid < a[j].urid) + j--; + + if(i >= j) + break; + + const props_type_t t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + _type_qsort(a, i); + _type_qsort(&a[i], n - i); +} + +static inline props_type_t * +_type_bsearch(LV2_URID p, props_type_t *a, unsigned n) +{ + unsigned start = 0; + unsigned end = n; + + while(start < end) + { + const unsigned mid = start + (end - start)/2; + props_type_t *dst = &a[mid]; + + if(p < dst->urid) + end = mid; + else if(p > dst->urid) + start = mid + 1; + else + return dst; + } + + return NULL; +} + +static inline void +_impl_qsort(props_impl_t *a, unsigned n) +{ + if(n < 2) + return; + + const props_impl_t *p = &a[n/2]; + + unsigned i, j; + for(i=0, j=n-1; ; i++, j--) + { + while(a[i].property < p->property) + i++; + + while(p->property < a[j].property) + j--; + + if(i >= j) + break; + + const props_impl_t t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + _impl_qsort(a, i); + _impl_qsort(&a[i], n - i); +} + +static inline props_impl_t * +_impl_bsearch(LV2_URID p, props_impl_t *a, unsigned n) +{ + unsigned start = 0; + unsigned end = n; + + while(start < end) + { + const unsigned mid = start + (end - start)/2; + props_impl_t *dst = &a[mid]; + + if(p < dst->property) + end = mid; + else if(p > dst->property) + start = mid + 1; + else + return dst; + } + + return NULL; +} + +static inline props_impl_t * +_props_impl_search(props_t *props, LV2_URID property) +{ + return _impl_bsearch(property, props->impls, props->nimpls); +} + +static inline LV2_Atom_Forge_Ref +_props_get(props_t *props, LV2_Atom_Forge *forge, uint32_t frames, props_impl_t *impl) +{ + LV2_Atom_Forge_Frame obj_frame; + + LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames); + + if(ref) + ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_set); + { + if(props->urid.subject) // is optional + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_subject); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.subject); + } + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_property); + if(ref) + ref = lv2_atom_forge_urid(forge, impl->property); + + if(ref) + lv2_atom_forge_key(forge, props->urid.patch_value); + if(ref) + ref = impl->type->get_cb(forge, impl->value); + } + if(ref) + lv2_atom_forge_pop(forge, &obj_frame); + + return ref; +} + +static inline void +_props_stash(props_t *props, props_impl_t *impl) +{ + if(_impl_try_lock(impl)) + { + const uint32_t size = _impl_size_get(impl); + memcpy(impl->stash, impl->value, size); + + _impl_unlock(impl); + } + else + { + impl->stashing = true; + props->stashing= true; + } +} + +static inline void +_props_set(props_t *props, props_impl_t *impl, LV2_URID type, uint32_t sz, const void *value) +{ + impl->type->set_cb(impl, impl->value, type, sz, value); + _props_stash(props, impl); +} + +static inline LV2_Atom_Forge_Ref +_props_reg(props_t *props, LV2_Atom_Forge *forge, uint32_t frames, props_impl_t *impl) +{ + const props_def_t *def = impl->def; + LV2_Atom_Forge_Frame obj_frame; + LV2_Atom_Forge_Frame add_frame; + LV2_Atom_Forge_Frame remove_frame; + + LV2_Atom_Forge_Ref ref = lv2_atom_forge_frame_time(forge, frames); + if(ref) + ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_patch); + { + if(props->urid.subject) // is optional + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_subject); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.subject); + } + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_remove); + if(ref) + ref = lv2_atom_forge_object(forge, &remove_frame, 0, 0); + { + if(ref) + ref = lv2_atom_forge_key(forge, impl->access); + if(ref) + ref = lv2_atom_forge_urid(forge, impl->property); + } + if(ref) + lv2_atom_forge_pop(forge, &remove_frame); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_add); + if(ref) + ref = lv2_atom_forge_object(forge, &add_frame, 0, 0); + { + if(ref) + ref = lv2_atom_forge_key(forge, impl->access); + if(ref) + ref = lv2_atom_forge_urid(forge, impl->property); + } + if(ref) + lv2_atom_forge_pop(forge, &add_frame); + } + if(ref) + lv2_atom_forge_pop(forge, &obj_frame); + + if(ref) + ref = lv2_atom_forge_frame_time(forge, frames); + if(ref) + ref = lv2_atom_forge_object(forge, &obj_frame, 0, props->urid.patch_patch); + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_subject); + if(ref) + ref = lv2_atom_forge_urid(forge, impl->property); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_remove); + if(ref) + ref = lv2_atom_forge_object(forge, &remove_frame, 0, 0); + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_range); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_label); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_comment); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.lv2_minimum); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.lv2_maximum); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.units_unit); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.lv2_scale_point); + if(ref) + ref = lv2_atom_forge_urid(forge, props->urid.patch_wildcard); + } + if(ref) + lv2_atom_forge_pop(forge, &remove_frame); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.patch_add); + if(ref) + ref = lv2_atom_forge_object(forge, &add_frame, 0, 0); + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_range); + if(ref) + ref = lv2_atom_forge_urid(forge, impl->type->urid); + + if(def->label) + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_label); + if(ref) + ref = lv2_atom_forge_string(forge, def->label, strlen(def->label)); + } + + if(def->comment) + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_comment); + if(ref) + ref = lv2_atom_forge_string(forge, def->comment, strlen(def->comment)); + } + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.lv2_minimum); + if(ref) + ref = impl->type->get_cb(forge, &def->minimum); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.lv2_maximum); + if(ref) + ref = impl->type->get_cb(forge, &def->maximum); + + if(props->urid.units_unit) + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.units_unit); + if(ref) + ref = lv2_atom_forge_urid(forge, impl->unit); + } + + if(def->scale_points) + { + for(const props_scale_point_t *sp = def->scale_points; sp->label; sp++) + { + LV2_Atom_Forge_Frame scale_point_frame; + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.lv2_scale_point); + if(ref) + ref = lv2_atom_forge_object(forge, &scale_point_frame, 0, 0); + { + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdfs_label); + if(ref) + ref = lv2_atom_forge_string(forge, sp->label, strlen(sp->label)); + + if(ref) + ref = lv2_atom_forge_key(forge, props->urid.rdf_value); + if(ref) + ref = impl->type->get_cb(forge, &sp->value); + } + if(ref) + lv2_atom_forge_pop(forge, &scale_point_frame); + } + } + } + if(ref) + lv2_atom_forge_pop(forge, &add_frame); + } + if(ref) + lv2_atom_forge_pop(forge, &obj_frame); + + return ref; +} + +static inline int +props_init(props_t *props, const size_t max_nimpls, const char *subject, + LV2_URID_Map *map, void *data) +{ + if(!map) + return 0; + + props->nimpls = 0; + props->max_nimpls = max_nimpls; + props->map = map; + props->data = data; + + props->urid.subject = subject ? map->map(map->handle, subject) : 0; + + props->urid.patch_get = map->map(map->handle, LV2_PATCH__Get); + props->urid.patch_set = map->map(map->handle, LV2_PATCH__Set); + props->urid.patch_put = map->map(map->handle, LV2_PATCH__Put); + props->urid.patch_patch = map->map(map->handle, LV2_PATCH__Patch); + props->urid.patch_wildcard = map->map(map->handle, LV2_PATCH__wildcard); + props->urid.patch_add = map->map(map->handle, LV2_PATCH__add); + props->urid.patch_remove = map->map(map->handle, LV2_PATCH__remove); + props->urid.patch_subject = map->map(map->handle, LV2_PATCH__subject); + props->urid.patch_body = map->map(map->handle, LV2_PATCH__body); + props->urid.patch_property = map->map(map->handle, LV2_PATCH__property); + props->urid.patch_value = map->map(map->handle, LV2_PATCH__value); + props->urid.patch_writable = map->map(map->handle, LV2_PATCH__writable); + props->urid.patch_readable = map->map(map->handle, LV2_PATCH__readable); + + props->urid.rdf_value = map->map(map->handle, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#value"); + + props->urid.rdfs_label = map->map(map->handle, + "http://www.w3.org/2000/01/rdf-schema#label"); + props->urid.rdfs_range = map->map(map->handle, + "http://www.w3.org/2000/01/rdf-schema#range"); + props->urid.rdfs_comment = map->map(map->handle, + "http://www.w3.org/2000/01/rdf-schema#comment"); + + props->urid.lv2_minimum = map->map(map->handle, LV2_CORE__minimum); + props->urid.lv2_maximum = map->map(map->handle, LV2_CORE__maximum); + props->urid.lv2_scale_point = map->map(map->handle, LV2_CORE__scalePoint); + + props->urid.atom_int = map->map(map->handle, LV2_ATOM__Int); + props->urid.atom_long = map->map(map->handle, LV2_ATOM__Long); + props->urid.atom_float = map->map(map->handle, LV2_ATOM__Float); + props->urid.atom_double = map->map(map->handle, LV2_ATOM__Double); + props->urid.atom_bool = map->map(map->handle, LV2_ATOM__Bool); + props->urid.atom_urid = map->map(map->handle, LV2_ATOM__URID); + props->urid.atom_string = map->map(map->handle, LV2_ATOM__String); + props->urid.atom_path = map->map(map->handle, LV2_ATOM__Path); + props->urid.atom_uri = map->map(map->handle, LV2_ATOM__URI); + props->urid.atom_chunk = map->map(map->handle, LV2_ATOM__Chunk); + + props->urid.units_unit = map->map(map->handle, LV2_UNITS__unit); + + // Int + unsigned ptr = 0; + props->types[ptr].urid = props->urid.atom_int; + props->types[ptr].size = sizeof(int32_t); + props->types[ptr].size_cb = NULL; + props->types[ptr].get_cb = _props_int_get_cb; + props->types[ptr].set_cb = _props_int_set_cb; + ptr++; + + // Long + props->types[ptr].urid = props->urid.atom_long; + props->types[ptr].size = sizeof(int64_t); + props->types[ptr].size_cb = NULL; + props->types[ptr].get_cb = _props_long_get_cb; + props->types[ptr].set_cb = _props_long_set_cb; + ptr++; + + // Float + props->types[ptr].urid = props->urid.atom_float; + props->types[ptr].size = sizeof(float); + props->types[ptr].size_cb = NULL; + props->types[ptr].get_cb = _props_float_get_cb; + props->types[ptr].set_cb = _props_float_set_cb; + ptr++; + + // double + props->types[ptr].urid = props->urid.atom_double; + props->types[ptr].size = sizeof(double); + props->types[ptr].size_cb = NULL; + props->types[ptr].get_cb = _props_double_get_cb; + props->types[ptr].set_cb = _props_double_set_cb; + ptr++; + + // Bool + props->types[ptr].urid = props->urid.atom_bool; + props->types[ptr].size = sizeof(int32_t); + props->types[ptr].size_cb = NULL; + props->types[ptr].get_cb = _props_bool_get_cb; + props->types[ptr].set_cb = _props_int_set_cb; + ptr++; + + // URID + props->types[ptr].urid = props->urid.atom_urid; + props->types[ptr].size = sizeof(uint32_t); + props->types[ptr].size_cb = NULL; + props->types[ptr].get_cb = _props_urid_get_cb; + props->types[ptr].set_cb = _props_urid_set_cb; + ptr++; + + // String + props->types[ptr].urid = props->urid.atom_string; + props->types[ptr].size = 0; + props->types[ptr].size_cb = _props_string_size_cb; + props->types[ptr].get_cb = _props_string_get_cb; + props->types[ptr].set_cb = _props_string_set_cb; + ptr++; + + // Path + props->types[ptr].urid = props->urid.atom_path; + props->types[ptr].size = 0; + props->types[ptr].size_cb = _props_string_size_cb; + props->types[ptr].get_cb = _props_path_get_cb; + props->types[ptr].set_cb = _props_string_set_cb; + ptr++; + + // URI + props->types[ptr].urid = props->urid.atom_uri; + props->types[ptr].size = 0; + props->types[ptr].size_cb = _props_string_size_cb; + props->types[ptr].get_cb = _props_uri_get_cb; + props->types[ptr].set_cb = _props_string_set_cb; + ptr++; + + // URI + props->types[ptr].urid = props->urid.atom_chunk; + props->types[ptr].size = 0; + props->types[ptr].size_cb = _props_chunk_size_cb; + props->types[ptr].get_cb = _props_chunk_get_cb; + props->types[ptr].set_cb = _props_chunk_set_cb; + ptr++; + + assert(ptr == PROPS_TYPE_N); + _type_qsort(props->types, PROPS_TYPE_N); + + return 1; +} + +static inline LV2_URID +props_register(props_t *props, const props_def_t *def, void *value, void *stash) +{ + if(props->nimpls >= props->max_nimpls) + return 0; + + if(!def || !def->property || !def->access || !def->type || !value || !stash) + return 0; + + const LV2_URID type = props->map->map(props->map->handle, def->type); + const props_type_t *props_type = _type_bsearch(type, props->types, PROPS_TYPE_N); + const LV2_URID property = props->map->map(props->map->handle, def->property); + const LV2_URID access = props->map->map(props->map->handle, def->access); + + if(!props_type || !property || !access) + return 0; + + props_impl_t *impl = &props->impls[props->nimpls++]; + + impl->props = props; + impl->property = property; + impl->access = access; + impl->unit = def->unit ? props->map->map(props->map->handle, def->unit) : 0; + impl->type = props_type; + impl->def = def; + impl->value = value; + impl->stash = stash; + atomic_flag_clear_explicit(&impl->lock, memory_order_relaxed); + + // update maximal value size + if(props_type->size && (props_type->size > props->max_size) ) + props->max_size = props_type->size; + else if(def->max_size && (def->max_size > props->max_size) ) + props->max_size = def->max_size; + + _impl_qsort(props->impls, props->nimpls); + + //TODO register? + + return property; +} + +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) +{ + if(props->stashing) + { + props->stashing = false; + + for(unsigned i = 0; i < props->nimpls; i++) + { + props_impl_t *impl = &props->impls[i]; + + if(impl->stashing) + { + impl->stashing= false; + _props_stash(props, impl); + } + } + } + + if(!lv2_atom_forge_is_object_type(forge, obj->atom.type)) + return 0; + + if(obj->body.otype == props->urid.patch_get) + { + const LV2_Atom_URID *subject = NULL; + const LV2_Atom_URID *property = NULL; + + LV2_Atom_Object_Query q [] = { + { props->urid.patch_subject, (const LV2_Atom **)&subject }, + { props->urid.patch_property, (const LV2_Atom **)&property }, + LV2_ATOM_OBJECT_QUERY_END + }; + lv2_atom_object_query(obj, q); + + // check for a matching optional subject + if( (subject && props->urid.subject) + && ( (subject->atom.type != props->urid.atom_urid) + || (subject->body != props->urid.subject) ) ) + { + return 0; + } + + if(!property) + { + for(unsigned i = 0; i < props->nimpls; i++) + { + props_impl_t *impl = &props->impls[i]; + const props_def_t *def = impl->def; + + if(impl->def->mode == PROP_MODE_DYNAMIC) + { + if(*ref) + *ref = _props_reg(props, forge, frames, impl); + if(def->event_cb && (def->event_mask & PROP_EVENT_REGISTER) ) + def->event_cb(props->data, forge, frames, PROP_EVENT_REGISTER, impl); + } + + if(*ref) + *ref = _props_get(props, forge, frames, impl); + if(def->event_cb && (def->event_mask & PROP_EVENT_GET) ) + def->event_cb(props->data, forge, frames, PROP_EVENT_GET, impl); + } + return 1; + } + else if(property->atom.type == props->urid.atom_urid) + { + props_impl_t *impl = _props_impl_search(props, property->body); + + if(impl) + { + *ref = _props_get(props, forge, frames, impl); + + const props_def_t *def = impl->def; + if(def->event_cb && (def->event_mask & PROP_EVENT_GET) ) + def->event_cb(props->data, forge, frames, PROP_EVENT_GET, impl); + + return 1; + } + } + } + else if(obj->body.otype == props->urid.patch_set) + { + const LV2_Atom_URID *subject = NULL; + const LV2_Atom_URID *property = NULL; + const LV2_Atom *value = NULL; + + LV2_Atom_Object_Query q [] = { + { props->urid.patch_subject, (const LV2_Atom **)&subject }, + { props->urid.patch_property, (const LV2_Atom **)&property }, + { props->urid.patch_value, &value }, + LV2_ATOM_OBJECT_QUERY_END + }; + lv2_atom_object_query(obj, q); + + if(!property || (property->atom.type != props->urid.atom_urid) || !value) + { + return 0; + } + + // check for a matching optional subject + if( (subject && props->urid.subject) + && ( (subject->atom.type != props->urid.atom_urid) + || (subject->body != props->urid.subject) ) ) + { + return 0; + } + + props_impl_t *impl = _props_impl_search(props, property->body); + if(impl && (impl->access == props->urid.patch_writable) ) + { + _props_set(props, impl, value->type, value->size, LV2_ATOM_BODY_CONST(value)); + + const props_def_t *def = impl->def; + if(def->event_cb && (def->event_mask & PROP_EVENT_SET) ) + def->event_cb(props->data, forge, frames, PROP_EVENT_SET, impl); + + return 1; + } + } + else if(obj->body.otype == props->urid.patch_put) + { + const LV2_Atom_URID *subject = NULL; + const LV2_Atom_Object *body = NULL; + + LV2_Atom_Object_Query q [] = { + { props->urid.patch_subject, (const LV2_Atom **)&subject }, + { props->urid.patch_body, (const LV2_Atom **)&body }, + LV2_ATOM_OBJECT_QUERY_END + }; + lv2_atom_object_query(obj, q); + + if(!body || !lv2_atom_forge_is_object_type(forge, body->atom.type)) + { + return 0; + } + + // check for a matching optional subject + if( (subject && props->urid.subject) + && ( (subject->atom.type != props->urid.atom_urid) + || (subject->body != props->urid.subject) ) ) + { + return 0; + } + + LV2_ATOM_OBJECT_FOREACH(body, prop) + { + const LV2_URID property = prop->key; + const LV2_Atom *value = &prop->value; + + props_impl_t *impl = _props_impl_search(props, property); + if(impl && (impl->access == props->urid.patch_writable) ) + { + _props_set(props, impl, value->type, value->size, LV2_ATOM_BODY_CONST(value)); + + const props_def_t *def = impl->def; + if(def->event_cb && (def->event_mask & PROP_EVENT_SET) ) + def->event_cb(props->data, forge, frames, PROP_EVENT_SET, impl); + } + } + return 1; + } + + return 0; // did not handle a patch event +} + +static inline void +props_set(props_t *props, LV2_Atom_Forge *forge, uint32_t frames, LV2_URID property, + LV2_Atom_Forge_Ref *ref) +{ + props_impl_t *impl = _props_impl_search(props, property); + + if(impl) + { + _props_stash(props, impl); + if(*ref) + *ref = _props_get(props, forge, frames, impl); + } +} + +static inline void +props_stash(props_t *props, LV2_URID property) +{ + props_impl_t *impl = _props_impl_search(props, property); + + if(impl) + _props_stash(props, impl); +} + +static inline LV2_State_Status +props_save(props_t *props, LV2_Atom_Forge *forge, LV2_State_Store_Function store, + LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features) +{ + const LV2_State_Map_Path *map_path = NULL; + + // set POD flag if not already set by host + flags |= LV2_STATE_IS_POD; + + for(unsigned i = 0; features[i]; i++) + { + if(!strcmp(features[i]->URI, LV2_STATE__mapPath)) + { + map_path = features[i]->data; + break; + } + } + + void *value = malloc(props->max_size); // create memory to store widest value + if(value) + { + for(unsigned i = 0; i < props->nimpls; i++) + { + props_impl_t *impl = &props->impls[i]; + + if(impl->access == props->urid.patch_readable) + continue; // skip read-only, as it makes no sense to restore them + + // create lockfree copy of value, store() may well be blocking + _impl_spin_lock(impl); + + const uint32_t size = _impl_size_get(impl); + memcpy(value, impl->stash, size); + + _impl_unlock(impl); + + if( map_path && (impl->type->urid == forge->Path) ) + { + const char *path = strstr(value, "file://") + ? value + 7 // skip "file://" + : value; + char *abstract = map_path->abstract_path(map_path->handle, path); + if(abstract && strcmp(abstract, path)) + { + store(state, impl->property, abstract, strlen(abstract) + 1, impl->type->urid, flags); + free(abstract); + } + } + else // !Path + { + store(state, impl->property, value, size, impl->type->urid, flags); + } + + const props_def_t *def = impl->def; + if(def->event_cb && (def->event_mask & PROP_EVENT_SAVE) ) + def->event_cb(props->data, forge, 0, PROP_EVENT_SAVE, impl); + } + + free(value); + } + + return LV2_STATE_SUCCESS; +} + +static inline LV2_State_Status +props_restore(props_t *props, LV2_Atom_Forge *forge, LV2_State_Retrieve_Function retrieve, + LV2_State_Handle state, uint32_t flags, const LV2_Feature *const *features) +{ + const LV2_State_Map_Path *map_path = NULL; + + for(unsigned i = 0; features[i]; i++) + { + if(!strcmp(features[i]->URI, LV2_STATE__mapPath)) + map_path = features[i]->data; + } + + for(unsigned i = 0; i < props->nimpls; i++) + { + props_impl_t *impl = &props->impls[i]; + + if(impl->access == props->urid.patch_readable) + continue; // skip read-only, as it makes no sense to restore them + + size_t size; + uint32_t type; + uint32_t _flags; + const void *value = retrieve(state, impl->property, &size, &type, &_flags); + + if(value) + { + if( map_path && (impl->type->urid == forge->Path) ) + { + char *absolute = map_path->absolute_path(map_path->handle, value); + if(absolute) + { + _props_set(props, impl, type, strlen(absolute) + 1, absolute); + free(absolute); + } + } + else // !Path + { + _props_set(props, impl, type, size, value); + } + + const props_def_t *def = impl->def; + if(def->event_cb && (def->event_mask & PROP_EVENT_RESTORE) ) + def->event_cb(props->data, forge, 0, PROP_EVENT_RESTORE, impl); + } + else + { + fprintf(stderr, "props_restore: no property '%s'.\n", impl->def->property); + } + } + + return LV2_STATE_SUCCESS; +} + +// undefinitions +#undef PROPS_TYPE_N + +#ifdef __cplusplus +} +#endif + +#endif // _LV2_PROPS_H_ diff --git a/props.lv2/test/chunk.bin b/props.lv2/test/chunk.bin Binary files differnew file mode 100644 index 0000000..b66efb8 --- /dev/null +++ b/props.lv2/test/chunk.bin diff --git a/props.lv2/test/manifest.ttl.in b/props.lv2/test/manifest.ttl.in new file mode 100644 index 0000000..c477aeb --- /dev/null +++ b/props.lv2/test/manifest.ttl.in @@ -0,0 +1,28 @@ +# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the Artistic License 2.0 as published by +# The Perl Foundation. +# +# This source is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Artistic License 2.0 for more details. +# +# You should have received a copy of the Artistic License 2.0 +# along the source as a COPYING file. If not, obtain it from +# http://www.perlfoundation.org/artistic_license_2_0. + +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix owl: <http://www.w3.org/2002/07/owl#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +@prefix props: <http://open-music-kontrollers.ch/lv2/props#> . + +# Orbit Looper +props:test + a lv2:Plugin ; + lv2:minorVersion @PROPS_MINOR_VERSION@ ; + lv2:microVersion @PROPS_MICRO_VERSION@ ; + lv2:binary <props@LIB_EXT@> ; + rdfs:seeAlso <props.ttl> . diff --git a/props.lv2/test/props.c b/props.lv2/test/props.c new file mode 100644 index 0000000..516d48c --- /dev/null +++ b/props.lv2/test/props.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#include <stdio.h> + +#include <props.h> + +#include <lv2/lv2plug.in/ns/ext/log/log.h> + +#define PROPS_PREFIX "http://open-music-kontrollers.ch/lv2/props#" +#define PROPS_TEST_URI PROPS_PREFIX"test" + +#define MAX_NPROPS 33 +#define MAX_STRLEN 256 + +typedef struct _plugstate0_t plugstate0_t; +typedef struct _plugstate1_t plugstate1_t; +typedef struct _plughandle_t plughandle_t; + +struct _plugstate0_t { + int32_t val1; + int64_t val2; + float val3; + double val4; + int32_t val5; + int32_t val6; + char val7 [MAX_STRLEN]; +}; + +struct _plugstate1_t { + int32_t val1; + int64_t val2; + float val3; + double val4; + char val5 [MAX_STRLEN]; + char val6 [MAX_STRLEN]; + uint8_t val7 [MAX_STRLEN]; +}; + +struct _plughandle_t { + LV2_URID_Map *map; + LV2_Log_Log *log; + LV2_Atom_Forge forge; + LV2_Atom_Forge_Ref ref; + + LV2_URID log_trace; + LV2_URID log_note; + + PROPS_T(props, MAX_NPROPS); + + plugstate0_t dyn; + plugstate0_t _dyn; + + plugstate1_t stat; + plugstate1_t _stat; + + struct { + LV2_URID stat2; + LV2_URID stat4; + LV2_URID dyn2; + LV2_URID dyn4; + } urid; + + const LV2_Atom_Sequence *event_in; + LV2_Atom_Sequence *event_out; +}; + +static int +_log_vprintf(plughandle_t *handle, LV2_URID type, const char *fmt, va_list args) +{ + return handle->log->vprintf(handle->log->handle, type, fmt, args); +} + +// non-rt || rt with LV2_LOG__Trace +static int +_log_printf(plughandle_t *handle, LV2_URID type, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start (args, fmt); + ret = _log_vprintf(handle, type, fmt, args); + va_end(args); + + return ret; +} + +static void +_intercept(void *data, LV2_Atom_Forge *forge, int64_t frames, + props_event_t event, props_impl_t *impl) +{ + plughandle_t *handle = data; + + switch(event) + { + case PROP_EVENT_GET: + { + _log_printf(handle, handle->log_trace, "GET : %s", impl->def->label); + break; + } + case PROP_EVENT_SET: + { + _log_printf(handle, handle->log_trace, "SET : %s", impl->def->label); + break; + } + case PROP_EVENT_REGISTER: + { + _log_printf(handle, handle->log_trace, "REGISTER: %s", impl->def->label); + break; + } + case PROP_EVENT_SAVE: + { + _log_printf(handle, handle->log_note, "SAVE : %s", impl->def->label); + break; + } + case PROP_EVENT_RESTORE: + { + _log_printf(handle, handle->log_note, "RESTORE : %s", impl->def->label); + break; + } + } +} + +static void +_intercept_dyn1(void *data, LV2_Atom_Forge *forge, int64_t frames, + props_event_t event, props_impl_t *impl) +{ + plughandle_t *handle = data; + + _intercept(data, forge, frames, event, impl); + + if(event & PROP_EVENT_WRITE) + { + handle->dyn.val2 = handle->dyn.val1 * 2; + + props_set(&handle->props, forge, frames, handle->urid.dyn2, &handle->ref); + } +} + +static void +_intercept_dyn3(void *data, LV2_Atom_Forge *forge, int64_t frames, + props_event_t event, props_impl_t *impl) +{ + plughandle_t *handle = data; + + _intercept(data, forge, frames, event, impl); + + if(event & PROP_EVENT_WRITE) + { + handle->dyn.val4 = handle->dyn.val3 * 2; + + props_set(&handle->props, forge, frames, handle->urid.dyn4, &handle->ref); + } +} + +static void +_intercept_stat1(void *data, LV2_Atom_Forge *forge, int64_t frames, + props_event_t event, props_impl_t *impl) +{ + plughandle_t *handle = data; + + _intercept(data, forge, frames, event, impl); + + if(event & PROP_EVENT_WRITE) + { + handle->stat.val2 = handle->stat.val1 * 2; + + props_set(&handle->props, forge, frames, handle->urid.stat2, &handle->ref); + } +} + +static void +_intercept_stat3(void *data, LV2_Atom_Forge *forge, int64_t frames, + props_event_t event, props_impl_t *impl) +{ + plughandle_t *handle = data; + + _intercept(data, forge, frames, event, impl); + + if(event & PROP_EVENT_WRITE) + { + handle->stat.val4 = handle->stat.val3 * 2; + + props_set(&handle->props, forge, frames, handle->urid.stat4, &handle->ref); + } +} + +static void +_intercept_stat6(void *data, LV2_Atom_Forge *forge, int64_t frames, + props_event_t event, props_impl_t *impl) +{ + plughandle_t *handle = data; + + _intercept(data, forge, frames, event, impl); + + if(event & PROP_EVENT_WRITE) + { + const char *path = strstr(handle->stat.val6, "file://") + ? handle->stat.val6 + 7 // skip "file://" + : handle->stat.val6; + FILE *f = fopen(path, "wb"); // create empty file + if(f) + fclose(f); + } +} + +static const props_def_t dyn1 = { + .label = "Int", + .comment = "This is a 32-bit integer", + .property = PROPS_PREFIX"Int", + .access = LV2_PATCH__writable, + .unit = LV2_UNITS__hz, + .type = LV2_ATOM__Int, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept_dyn1, + .minimum.i = 0, + .maximum.i = 10 +}; + +static const props_def_t dyn2 = { + .label = "Long", + .comment = "This is a 64-bit integer", + .property = PROPS_PREFIX"Long", + .access = LV2_PATCH__readable, + .unit = LV2_UNITS__khz, + .type = LV2_ATOM__Long, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .minimum.h = 0, + .maximum.h = 20 +}; + +static const props_def_t dyn3 = { + .label = "Float", + .comment = "This is a 32-bit floating point", + .property = PROPS_PREFIX"Float", + .access = LV2_PATCH__writable, + .unit = LV2_UNITS__mhz, + .type = LV2_ATOM__Float, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept_dyn3, + .minimum.f = -0.5f, + .maximum.f = 0.5f +}; + +static const props_def_t dyn4 = { + .label = "Double", + .comment = "This is a 64-bit floating point", + .property = PROPS_PREFIX"Double", + .access = LV2_PATCH__readable, + .unit = LV2_UNITS__db, + .type = LV2_ATOM__Double, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .minimum.d = -1.0, + .maximum.d = 1.0 +}; + +static const props_scale_point_t scale_points5 [] = { + {.label = "One", .value.i = 0}, + {.label = "Two", .value.i = 1}, + {.label = "Three", .value.i = 2}, + {.label = "Four", .value.i = 3}, + {.label = NULL } // sentinel +}; + +static const props_def_t dyn5 = { + .label = "scaleInt", + .comment = "This is a 32-bit integer enumeration", + .property = PROPS_PREFIX"scaleInt", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__Int, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .minimum.i = 0, + .maximum.i = 3, + .scale_points = scale_points5 +}; + +static const props_def_t dyn6 = { + .label = "Bool", + .comment = "This is a boolean", + .property = PROPS_PREFIX"Bool", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__Bool, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .minimum.d = 0, + .maximum.d = 1 +}; + +static const props_def_t dyn7 = { + .label = "String", + .comment = "This is a string", + .property = PROPS_PREFIX"String", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__String, + .mode = PROP_MODE_DYNAMIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .max_size = MAX_STRLEN // strlen +}; + +static const props_def_t stat1 = { + .label = "statInt", + .property = PROPS_PREFIX"statInt", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__Int, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept_stat1, +}; + +static const props_def_t stat2 = { + .label = "statLong", + .property = PROPS_PREFIX"statLong", + .access = LV2_PATCH__readable, + .type = LV2_ATOM__Long, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, +}; + +static const props_def_t stat3 = { + .label = "statFloat", + .property = PROPS_PREFIX"statFloat", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__Float, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept_stat3, +}; + +static const props_def_t stat4 = { + .label = "statDouble", + .property = PROPS_PREFIX"statDouble", + .access = LV2_PATCH__readable, + .type = LV2_ATOM__Double, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, +}; + +static const props_def_t stat5 = { + .label = "statString", + .property = PROPS_PREFIX"statString", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__String, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .max_size = MAX_STRLEN // strlen +}; + +static const props_def_t stat6 = { + .label = "statPath", + .property = PROPS_PREFIX"statPath", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__Path, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept_stat6, + .max_size = MAX_STRLEN // strlen +}; + +static const props_def_t stat7 = { + .label = "statChunk", + .property = PROPS_PREFIX"statChunk", + .access = LV2_PATCH__writable, + .type = LV2_ATOM__Chunk, + .mode = PROP_MODE_STATIC, + .event_mask = PROP_EVENT_ALL, + .event_cb = _intercept, + .max_size = MAX_STRLEN // strlen +}; + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, double rate, + const char *bundle_path, const LV2_Feature *const *features) +{ + plughandle_t *handle = calloc(1, sizeof(plughandle_t)); + if(!handle) + return NULL; + + for(unsigned i=0; features[i]; i++) + { + if(!strcmp(features[i]->URI, LV2_URID__map)) + handle->map = features[i]->data; + else if(!strcmp(features[i]->URI, LV2_LOG__log)) + handle->log = features[i]->data; + } + + if(!handle->map) + { + fprintf(stderr, + "%s: Host does not support urid:map\n", descriptor->URI); + free(handle); + return NULL; + } + if(!handle->log) + { + fprintf(stderr, + "%s: Host does not support log:log\n", descriptor->URI); + free(handle); + return NULL; + } + + handle->log_trace = handle->map->map(handle->map->handle, LV2_LOG__Trace); + handle->log_note = handle->map->map(handle->map->handle, LV2_LOG__Note); + + lv2_atom_forge_init(&handle->forge, handle->map); + if(!props_init(&handle->props, MAX_NPROPS, descriptor->URI, handle->map, handle)) + { + fprintf(stderr, "failed to initialize property structure\n"); + free(handle); + return NULL; + } + + plugstate0_t *dyn = &handle->dyn; + plugstate0_t *_dyn = &handle->_dyn; + plugstate1_t *stat = &handle->stat; + plugstate1_t *_stat = &handle->_stat; + + if( !props_register(&handle->props, &dyn1, &dyn->val1, &_dyn->val1) + || !(handle->urid.dyn2 = + props_register(&handle->props, &dyn2, &dyn->val2, &_dyn->val2)) + || !props_register(&handle->props, &dyn3, &dyn->val3, &_dyn->val3) + || !(handle->urid.dyn4 = + props_register(&handle->props, &dyn4, &dyn->val4, &_dyn->val4)) + || !props_register(&handle->props, &dyn5, &dyn->val5, &_dyn->val5) + || !props_register(&handle->props, &dyn6, &dyn->val6, &_dyn->val6) + || !props_register(&handle->props, &dyn7, &dyn->val7, &_dyn->val7) + + || !props_register(&handle->props, &stat1, &stat->val1, &_stat->val1) + || !(handle->urid.stat2 = + props_register(&handle->props, &stat2, &stat->val2, &_stat->val2)) + || !props_register(&handle->props, &stat3, &stat->val3, &_stat->val3) + || !(handle->urid.stat4 = + props_register(&handle->props, &stat4, &stat->val4, &_stat->val4)) + || !props_register(&handle->props, &stat5, &stat->val5, &_stat->val5) + || !props_register(&handle->props, &stat6, &stat->val6, &_stat->val6) + || !props_register(&handle->props, &stat7, &stat->val7, &_stat->val7) ) + { + _log_printf(handle, handle->log_trace, "ERR : registering"); + free(handle); + return NULL; + } + + return handle; +} + +static void +connect_port(LV2_Handle instance, uint32_t port, void *data) +{ + plughandle_t *handle = (plughandle_t *)instance; + + switch(port) + { + case 0: + handle->event_in = (const LV2_Atom_Sequence *)data; + break; + case 1: + handle->event_out = (LV2_Atom_Sequence *)data; + break; + default: + break; + } +} + +static void +run(LV2_Handle instance, uint32_t nsamples) +{ + plughandle_t *handle = instance; + + uint32_t capacity = handle->event_out->atom.size; + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity); + handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0); + + LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev) + { + const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body; + + if(handle->ref) + props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref); //TODO handle return + } + if(handle->ref) + lv2_atom_forge_pop(&handle->forge, &frame); + else + lv2_atom_sequence_clear(handle->event_out); +} + +static void +cleanup(LV2_Handle instance) +{ + plughandle_t *handle = instance; + + free(handle); +} + +static LV2_State_Status +_state_save(LV2_Handle instance, LV2_State_Store_Function store, + LV2_State_Handle state, uint32_t flags, + const LV2_Feature *const *features) +{ + plughandle_t *handle = (plughandle_t *)instance; + + return props_save(&handle->props, &handle->forge, store, state, flags, features); +} + +static LV2_State_Status +_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, + LV2_State_Handle state, uint32_t flags, + const LV2_Feature *const *features) +{ + plughandle_t *handle = (plughandle_t *)instance; + + return props_restore(&handle->props, &handle->forge, retrieve, state, flags, features); +} + +LV2_State_Interface state_iface = { + .save = _state_save, + .restore = _state_restore +}; + +static const void * +extension_data(const char *uri) +{ + if(!strcmp(uri, LV2_STATE__interface)) + return &state_iface; + return NULL; +} + +const LV2_Descriptor props_test = { + .URI = PROPS_TEST_URI, + .instantiate = instantiate, + .connect_port = connect_port, + .activate = NULL, + .run = run, + .deactivate = NULL, + .cleanup = cleanup, + .extension_data = extension_data +}; + +#ifdef _WIN32 +__declspec(dllexport) +#else +__attribute__((visibility("default"))) +#endif +const LV2_Descriptor* +lv2_descriptor(uint32_t index) +{ + switch(index) + { + case 0: + return &props_test; + default: + return NULL; + } +} diff --git a/props.lv2/test/props.ttl b/props.lv2/test/props.ttl new file mode 100644 index 0000000..17a8041 --- /dev/null +++ b/props.lv2/test/props.ttl @@ -0,0 +1,152 @@ +# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) +# +# This is free software: you can redistribute it and/or modify +# it under the terms of the Artistic License 2.0 as published by +# The Perl Foundation. +# +# This source is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Artistic License 2.0 for more details. +# +# You should have received a copy of the Artistic License 2.0 +# along the source as a COPYING file. If not, obtain it from +# http://www.perlfoundation.org/artistic_license_2_0. + +@prefix owl: <http://www.w3.org/2002/07/owl#> . +@prefix foaf: <http://xmlns.com/foaf/0.1/> . +@prefix doap: <http://usefulinc.com/ns/doap#> . +@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix atom: <http://lv2plug.in/ns/ext/atom#> . +@prefix urid: <http://lv2plug.in/ns/ext/urid#> . +@prefix state: <http://lv2plug.in/ns/ext/state#> . +@prefix patch: <http://lv2plug.in/ns/ext/patch#> . +@prefix log: <http://lv2plug.in/ns/ext/log#> . +@prefix units: <http://lv2plug.in/ns/extensions/units#> . +@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . + +@prefix lic: <http://opensource.org/licenses/> . +@prefix omk: <http://open-music-kontrollers.ch/ventosus#> . +@prefix proj: <http://open-music-kontrollers.ch/lv2/> . +@prefix props: <http://open-music-kontrollers.ch/lv2/props#> . + +# Maintainer +omk:me + a foaf:Person ; + foaf:name "Hanspeter Portner" ; + foaf:mbox <mailto:dev@open-music-kontrollers.ch> ; + foaf:homepage <http://open-music-kontrollers.ch> . + +# Project +proj:props + a doap:Project ; + doap:maintainer omk:me ; + doap:name "Props Bundle" . + +props:statInt + a lv2:Parameter ; + rdfs:range atom:Int ; + rdfs:label "statInt" ; + rdfs:comment "This is a 32-bit integer" ; + units:unit units:hz ; + lv2:minimum 0 ; + lv2:maximum 10 . + +props:statLong + a lv2:Parameter ; + rdfs:range atom:Long ; + rdfs:label "statLong" ; + rdfs:comment "This is a 64-bit integer" ; + units:unit units:khz ; + lv2:minimum 0 ; + lv2:maximum 20 . + +props:statFloat + a lv2:Parameter ; + rdfs:range atom:Float ; + rdfs:label "statFloat" ; + rdfs:comment "This is a 32-bit float" ; + units:unit units:mhz ; + lv2:minimum -0.5 ; + lv2:maximum 0.5 . + +props:statDouble + a lv2:Parameter ; + rdfs:range atom:Double ; + rdfs:label "statDouble" ; + rdfs:comment "This is a 64-bit double" ; + units:unit units:db ; + lv2:minimum -1.0 ; + lv2:maximum 1.0 . + +props:statString + a lv2:Parameter ; + rdfs:range atom:String ; + rdfs:label "statString" ; + rdfs:comment "This is a string" . + +props:statPath + a lv2:Parameter ; + rdfs:range atom:Path ; + rdfs:label "statPath" ; + rdfs:comment "This is a path" . + +props:statChunk + a lv2:Parameter ; + rdfs:range atom:Chunk; + rdfs:label "statChunk" ; + rdfs:comment "This is a chunk" . + +# Looper Test +props:test + a lv2:Plugin , + lv2:ConverterPlugin ; + doap:name "Props Test" ; + doap:license lic:Artistic-2.0 ; + lv2:project proj:props ; + lv2:extensionData state:interface ; + lv2:requiredFeature urid:map, log:log, state:loadDefaultState ; + lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ; + + lv2:port [ + # sink event port + a lv2:InputPort , + atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message ; + lv2:index 0 ; + lv2:symbol "event_in" ; + lv2:name "Event Input" ; + lv2:designation lv2:control ; + ] , [ + # source event port + a lv2:OutputPort , + atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports patch:Message ; + lv2:index 1 ; + lv2:symbol "event_out" ; + lv2:name "Event Output" ; + lv2:designation lv2:control ; + ] ; + + patch:writable + props:statInt , + props:statFloat , + props:statString , + props:statPath , + props:statChunk ; + + patch:readable + props:statLong , + props:statDouble ; + + state:state [ + props:statInt 4 ; + props:statFloat "0.4"^^xsd:float ; + props:statString "Hello world" ; + props:statPath <manifest.ttl> ; + props:statChunk "Q"^^xsd:base64Binary ; + ] . |