aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2016-03-14 21:15:00 +0100
committerHanspeter Portner <dev@open-music-kontrollers.ch>2016-03-14 21:15:00 +0100
commit35f02dcb83d59e550eba20900123082ea03bd528 (patch)
treed9c7c0645f7b9e977c5e704bc58349f6f8e4aa6a
parentcca617911ec9ea33e92d8ff43c891261aa103689 (diff)
parent2a2bd7d2896e5ca4aac80dd52a56a9910081aa5c (diff)
downloadmoony.lv2-35f02dcb83d59e550eba20900123082ea03bd528.tar.xz
Add 'xpress.lv2/' from commit '2a2bd7d2896e5ca4aac80dd52a56a9910081aa5c'
git-subtree-dir: xpress.lv2 git-subtree-mainline: cca617911ec9ea33e92d8ff43c891261aa103689 git-subtree-split: 2a2bd7d2896e5ca4aac80dd52a56a9910081aa5c
-rw-r--r--xpress.lv2/CMakeLists.txt40
-rw-r--r--xpress.lv2/COPYING201
-rw-r--r--xpress.lv2/README.md20
-rw-r--r--xpress.lv2/test/manifest.ttl.in28
-rw-r--r--xpress.lv2/test/xpress.c268
-rw-r--r--xpress.lv2/test/xpress.ttl82
-rw-r--r--xpress.lv2/xpress.h559
7 files changed, 1198 insertions, 0 deletions
diff --git a/xpress.lv2/CMakeLists.txt b/xpress.lv2/CMakeLists.txt
new file mode 100644
index 0000000..a5cdd78
--- /dev/null
+++ b/xpress.lv2/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(xpress.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(XPRESS_MAJOR_VERSION 0)
+set(XPRESS_MINOR_VERSION 1)
+set(XPRESS_MICRO_VERSION 1)
+set(XPRESS_VERSION "${XPRESS_MAJOR_VERSION}.${XPRESS_MINOR_VERSION}.${XPRESS_MICRO_VERSION}")
+add_definitions("-DXPRESS_VERSION=\"${XPRESS_VERSION}\"")
+
+set(DEST lib/lv2/xpress.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}
+
+# xpress
+add_library(xpress MODULE
+ test/xpress.c)
+target_link_libraries(xpress ${LIBS})
+set_target_properties(xpress PROPERTIES PREFIX "")
+install(TARGETS xpress DESTINATION ${DEST})
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test/xpress.ttl 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/xpress.lv2/COPYING b/xpress.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/xpress.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/xpress.lv2/README.md b/xpress.lv2/README.md
new file mode 100644
index 0000000..4a24722
--- /dev/null
+++ b/xpress.lv2/README.md
@@ -0,0 +1,20 @@
+# xpress.lv2
+
+## Utility header for expression based LV2 plugins
+
+### License
+
+Copyright (c) 2016 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/xpress.lv2/test/manifest.ttl.in b/xpress.lv2/test/manifest.ttl.in
new file mode 100644
index 0000000..1f816ac
--- /dev/null
+++ b/xpress.lv2/test/manifest.ttl.in
@@ -0,0 +1,28 @@
+# Copyright (c) 2016 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 xpress: <http://open-music-kontrollers.ch/lv2/xpress#> .
+
+# Orbit Looper
+xpress:test
+ a lv2:Plugin ;
+ lv2:minorVersion @XPRESS_MINOR_VERSION@ ;
+ lv2:microVersion @XPRESS_MICRO_VERSION@ ;
+ lv2:binary <xpress@LIB_EXT@> ;
+ rdfs:seeAlso <xpress.ttl> .
diff --git a/xpress.lv2/test/xpress.c b/xpress.lv2/test/xpress.c
new file mode 100644
index 0000000..311d034
--- /dev/null
+++ b/xpress.lv2/test/xpress.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2016 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 voiceied 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 <inttypes.h>
+#include <stdatomic.h>
+
+#include <xpress.h>
+
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+#include <lv2/lv2plug.in/ns/ext/log/logger.h>
+
+#define XPRESS_TEST_URI XPRESS_PREFIX"test"
+
+#define MAX_NVOICES 32
+
+typedef struct _target_t target_t;
+typedef struct _plughandle_t plughandle_t;
+
+struct _target_t {
+ xpress_uuid_t uuid;
+};
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Ref ref;
+
+ XPRESS_T(xpress, MAX_NVOICES);
+ target_t target [MAX_NVOICES];
+
+ const LV2_Atom_Sequence *event_in;
+ LV2_Atom_Sequence *event_out;
+};
+
+static _Atomic xpress_uuid_t voice_uuid = ATOMIC_VAR_INIT(0);
+
+static xpress_uuid_t
+_voice_map_new_uuid(void *handle)
+{
+ _Atomic xpress_uuid_t *uuid = handle;
+ return atomic_fetch_add_explicit(uuid, 1, memory_order_relaxed);
+}
+
+static xpress_map_t voice_map_fallback = {
+ .handle = &voice_uuid,
+ .new_uuid = _voice_map_new_uuid
+};
+
+static void
+_dump(plughandle_t *handle)
+{
+ XPRESS_VOICE_FOREACH(&handle->xpress, voice)
+ {
+ lv2_log_trace(&handle->logger, "%"PRIi64, voice->uuid);
+ }
+ lv2_log_trace(&handle->logger, "");
+}
+
+static void
+_add(void *data, int64_t frames, const xpress_state_t *state,
+ xpress_uuid_t uuid, void *target)
+{
+ plughandle_t *handle = data;
+ LV2_Atom_Forge *forge = &handle->forge;
+ target_t *src = target;
+
+ lv2_log_trace(&handle->logger, "ADD: %"PRIi64, uuid);
+
+ src->uuid = xpress_map(&handle->xpress);
+
+ xpress_state_t new_state;
+ memcpy(&new_state, state, sizeof(xpress_state_t));
+ new_state.position[0] *= 2;
+
+ if(handle->ref)
+ handle->ref = xpress_put(&handle->xpress, forge, frames, src->uuid, &new_state);
+
+ _dump(handle);
+}
+
+static void
+_put(void *data, int64_t frames, const xpress_state_t *state,
+ xpress_uuid_t uuid, void *target)
+{
+ plughandle_t *handle = data;
+ LV2_Atom_Forge *forge = &handle->forge;
+ target_t *src = target;
+
+ lv2_log_trace(&handle->logger, "PUT: %"PRIi64, uuid);
+
+ xpress_state_t new_state;
+ memcpy(&new_state, state, sizeof(xpress_state_t));
+ new_state.position[0] *= 2;
+
+ if(handle->ref)
+ handle->ref = xpress_put(&handle->xpress, forge, frames, src->uuid, &new_state);
+
+ _dump(handle);
+}
+
+static void
+_del(void *data, int64_t frames, const xpress_state_t *state,
+ xpress_uuid_t uuid, void *target)
+{
+ plughandle_t *handle = data;
+ LV2_Atom_Forge *forge = &handle->forge;
+ target_t *src = target;
+
+ lv2_log_trace(&handle->logger, "DEL: %"PRIi64, uuid);
+
+ if(handle->ref)
+ handle->ref = xpress_del(&handle->xpress, forge, frames, src->uuid);
+
+ _dump(handle);
+}
+
+static const xpress_iface_t iface = {
+ .size = sizeof(target_t),
+ .add = _add,
+ .put = _put,
+ .del = _del
+};
+
+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;
+
+ xpress_map_t *voice_map = 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;
+ else if(!strcmp(features[i]->URI, XPRESS_VOICE_MAP))
+ voice_map = 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;
+ }
+ if(!voice_map)
+ voice_map = &voice_map_fallback;
+
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ if(!xpress_init(&handle->xpress, MAX_NVOICES, handle->map, voice_map,
+ XPRESS_EVENT_ALL, &iface, handle->target, handle) )
+ {
+ fprintf(stderr, "failed to initialize xpress structure\n");
+ 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)
+ xpress_advance(&handle->xpress, &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);
+}
+
+const LV2_Descriptor xpress_test = {
+ .URI = XPRESS_TEST_URI,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = NULL
+};
+
+#ifdef _WIN32
+__declspec(dllexport)
+#else
+__attribute__((visibility("default")))
+#endif
+const LV2_Descriptor*
+lv2_descriptor(uint32_t index)
+{
+ switch(index)
+ {
+ case 0:
+ return &xpress_test;
+ default:
+ return NULL;
+ }
+}
diff --git a/xpress.lv2/test/xpress.ttl b/xpress.lv2/test/xpress.ttl
new file mode 100644
index 0000000..ced9ec7
--- /dev/null
+++ b/xpress.lv2/test/xpress.ttl
@@ -0,0 +1,82 @@
+# Copyright (c) 2016 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 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 log: <http://lv2plug.in/ns/ext/log#> .
+
+@prefix lic: <http://opensource.org/licenses/> .
+@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
+@prefix proj: <http://open-music-kontrollers.ch/lv2/> .
+@prefix xpress: <http://open-music-kontrollers.ch/lv2/xpress#> .
+
+# Features
+xpress:voiceMap
+ a lv2:Feature .
+
+xpress:Message
+ a rdfs:Class ,
+ rdfs:Datatype ;
+ rdfs:subClassOf atom:Atom .
+
+# 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:xpress
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Xpress Bundle" .
+
+# Test Plugin
+xpress:test
+ a lv2:Plugin ,
+ lv2:ConverterPlugin ;
+ doap:name "Xpress Test" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:xpress ;
+ lv2:requiredFeature urid:map, log:log ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, xpress:voiceMap ;
+
+ lv2:port [
+ # sink event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports xpress: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 xpress:Message ;
+ lv2:index 1 ;
+ lv2:symbol "event_out" ;
+ lv2:name "Event Output" ;
+ lv2:designation lv2:control ;
+ ] .
diff --git a/xpress.lv2/xpress.h b/xpress.lv2/xpress.h
new file mode 100644
index 0000000..3234a93
--- /dev/null
+++ b/xpress.lv2/xpress.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2016 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 voiceied 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_XPRESS_H_
+#define _LV2_XPRESS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.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>
+
+/*****************************************************************************
+ * API START
+ *****************************************************************************/
+
+#define XPRESS_PREFIX "http://open-music-kontrollers.ch/lv2/xpress#"
+
+// Features
+#define XPRESS_VOICE_MAP XPRESS_PREFIX"voiceMap"
+
+// Message types
+#define XPRESS_PUT XPRESS_PREFIX"Put"
+#define XPRESS_DELETE XPRESS_PREFIX"Delete"
+#define XPRESS_CLEAR XPRESS_PREFIX"Clear"
+
+// Properties
+#define XPRESS_UUID XPRESS_PREFIX"uuid"
+#define XPRESS_ZONE XPRESS_PREFIX"zone"
+#define XPRESS_POSITION XPRESS_PREFIX"position"
+#define XPRESS_VELOCITY XPRESS_PREFIX"velocity"
+#define XPRESS_ACCELERATION XPRESS_PREFIX"acceleration"
+
+// types
+typedef int64_t xpress_uuid_t;
+
+// enumerations
+typedef enum _xpress_event_t xpress_event_t;
+
+// structures
+typedef struct _xpress_map_t xpress_map_t;
+typedef struct _xpress_state_t xpress_state_t;
+typedef struct _xpress_voice_t xpress_voice_t;
+typedef struct _xpress_iface_t xpress_iface_t;
+typedef struct _xpress_t xpress_t;
+
+// function callbacks
+typedef xpress_uuid_t (*xpress_map_new_uuid_t)(void *handle);
+
+typedef void (*xpress_state_cb_t)(void *data,
+ int64_t frames, const xpress_state_t *state,
+ xpress_uuid_t uuid, void *target);
+
+enum _xpress_event_t {
+ XPRESS_EVENT_ADD = (1 << 0),
+ XPRESS_EVENT_DEL = (1 << 1),
+ XPRESS_EVENT_PUT = (1 << 2)
+};
+
+#define XPRESS_EVENT_NONE (0)
+#define XPRESS_EVENT_ALL (XPRESS_EVENT_ADD | XPRESS_EVENT_DEL | XPRESS_EVENT_PUT)
+
+#define XPRESS_MAX_NDIMS 3
+
+struct _xpress_state_t {
+ int32_t zone;
+
+ float position [XPRESS_MAX_NDIMS];
+ float velocity [XPRESS_MAX_NDIMS];
+ float acceleration [XPRESS_MAX_NDIMS];
+};
+
+struct _xpress_iface_t {
+ size_t size;
+
+ xpress_state_cb_t add;
+ xpress_state_cb_t put;
+ xpress_state_cb_t del;
+};
+
+struct _xpress_voice_t {
+ xpress_uuid_t uuid;
+ void *target;
+};
+
+struct _xpress_map_t {
+ void *handle;
+ xpress_map_new_uuid_t new_uuid;
+};
+
+struct _xpress_t {
+ struct {
+
+ LV2_URID atom_int;
+ LV2_URID atom_long;
+ LV2_URID atom_float;
+ LV2_URID atom_vector;
+
+ LV2_URID xpress_Put;
+ LV2_URID xpress_Delete;
+ LV2_URID xpress_Clear;
+
+ LV2_URID xpress_uuid;
+ LV2_URID xpress_zone;
+ LV2_URID xpress_position;
+ LV2_URID xpress_velocity;
+ LV2_URID xpress_acceleration;
+ } urid;
+
+ LV2_URID_Map *map;
+ xpress_map_t *voice_map;
+
+ xpress_event_t event_mask;
+ const xpress_iface_t *iface;
+ void *data;
+
+ unsigned max_nvoices;
+ unsigned nvoices;
+ xpress_voice_t voices [0];
+};
+
+#define XPRESS_CONCAT_IMPL(X, Y) X##Y
+#define XPRESS_CONCAT(X, Y) XPRESS_CONCAT_IMPL(X, Y)
+
+#define XPRESS_T(XPRESS, MAX_NVOICES) \
+ xpress_t (XPRESS); \
+ xpress_voice_t XPRESS_CONCAT(_voices, __COUNTER__) [(MAX_NVOICES)];
+
+#define XPRESS_VOICE_FOREACH(XPRESS, VOICE) \
+ for(xpress_voice_t *VOICE = &(XPRESS)->voices[(int)(XPRESS)->nvoices - 1]; \
+ VOICE >= (XPRESS)->voices; \
+ VOICE--)
+
+#define XPRESS_VOICE_FREE(XPRESS, VOICE) \
+ for(xpress_voice_t *VOICE = &(XPRESS)->voices[(int)(XPRESS)->nvoices - 1]; \
+ VOICE >= (XPRESS)->voices; \
+ VOICE->uuid = 0, VOICE--, (XPRESS)->nvoices--)
+
+// non rt-safe
+static inline int
+xpress_init(xpress_t *xpress, const size_t max_nvoices, LV2_URID_Map *map,
+ xpress_map_t *voice_map, xpress_event_t event_mask, const xpress_iface_t *iface,
+ void *target, void *data);
+
+// rt-safe
+static inline void *
+xpress_get(xpress_t *xpress, xpress_uuid_t uuid);
+
+// rt-safe
+static inline int
+xpress_advance(xpress_t *xpress, LV2_Atom_Forge *forge, uint32_t frames,
+ const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref);
+
+// rt-safe
+static inline void *
+xpress_add(xpress_t *xpress, xpress_uuid_t uuid);
+
+// rt-safe
+static inline void *
+xpress_create(xpress_t *xpress, xpress_uuid_t *uuid);
+
+// rt-safe
+static inline int
+xpress_free(xpress_t *xpress, xpress_uuid_t uuid);
+
+// rt-safe
+static inline LV2_Atom_Forge_Ref
+xpress_del(xpress_t *xpress, LV2_Atom_Forge *forge, uint32_t frames,
+ xpress_uuid_t uuid);
+
+// rt-safe
+static inline LV2_Atom_Forge_Ref
+xpress_put(xpress_t *xpress, LV2_Atom_Forge *forge, uint32_t frames,
+ xpress_uuid_t uuid, const xpress_state_t *state);
+
+// rt-safe
+static inline int32_t
+xpress_map(xpress_t *xpress);
+
+/*****************************************************************************
+ * API END
+ *****************************************************************************/
+
+static inline void
+_xpress_float_vec(xpress_t *xpress, float *dst, const LV2_Atom_Vector *vec)
+{
+ const float *src = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, &vec->atom);
+ unsigned nelements = (vec->atom.size - sizeof(LV2_Atom_Vector_Body)) / sizeof(float);
+
+ // only copy as many floats as present on either side
+ if(nelements > XPRESS_MAX_NDIMS)
+ nelements = XPRESS_MAX_NDIMS;
+
+ memcpy(dst, src, nelements * sizeof(float));
+}
+
+static inline int
+_xpress_signum(xpress_uuid_t urid1, xpress_uuid_t urid2)
+{
+ if(urid1 < urid2)
+ return 1;
+ else if(urid1 > urid2)
+ return -1;
+
+ return 0;
+}
+
+static int
+_xpress_voice_sort(const void *itm1, const void *itm2)
+{
+ const xpress_voice_t *voice1 = itm1;
+ const xpress_voice_t *voice2 = itm2;
+
+ return _xpress_signum(voice1->uuid, voice2->uuid);
+}
+
+static inline void
+_xpress_sort(xpress_t *xpress)
+{
+ qsort(xpress->voices, xpress->nvoices, sizeof(xpress_voice_t), _xpress_voice_sort);
+}
+
+static inline xpress_voice_t *
+_xpress_voice_get(xpress_t *xpress, xpress_uuid_t uuid)
+{
+ const xpress_voice_t voice = {
+ .uuid = uuid,
+ .target = NULL
+ };
+
+ return bsearch(&voice, xpress->voices, xpress->nvoices, sizeof(xpress_voice_t),
+ _xpress_voice_sort);
+}
+
+static inline void *
+_xpress_voice_add(xpress_t *xpress, xpress_uuid_t uuid)
+{
+ if(xpress->nvoices >= xpress->max_nvoices)
+ return NULL; // failed
+
+ xpress_voice_t *voice = &xpress->voices[xpress->nvoices++];
+ voice->uuid = uuid;
+ void *target = voice->target;
+
+ _xpress_sort(xpress);
+
+ return target;
+}
+
+static inline void
+_xpress_voice_free(xpress_t *xpress, xpress_voice_t *voice)
+{
+ voice->uuid = 0;
+
+ _xpress_sort(xpress);
+
+ xpress->nvoices--;
+}
+
+static inline int
+xpress_init(xpress_t *xpress, const size_t max_nvoices, LV2_URID_Map *map,
+ xpress_map_t *voice_map, xpress_event_t event_mask, const xpress_iface_t *iface,
+ void *target, void *data)
+{
+ if(!map || ( (event_mask != XPRESS_EVENT_NONE) && !iface))
+ return 0;
+
+ xpress->nvoices = 0;
+ xpress->max_nvoices = max_nvoices;
+ xpress->map = map;
+ xpress->voice_map = voice_map;
+ xpress->event_mask = event_mask;
+ xpress->iface = iface;
+ xpress->data = data;
+
+ xpress->urid.atom_int = map->map(map->handle, LV2_ATOM__Int);
+ xpress->urid.atom_long = map->map(map->handle, LV2_ATOM__Long);
+ xpress->urid.atom_float = map->map(map->handle, LV2_ATOM__Float);
+ xpress->urid.atom_vector = map->map(map->handle, LV2_ATOM__Vector);
+
+ xpress->urid.xpress_Put = map->map(map->handle, XPRESS_PUT);
+ xpress->urid.xpress_Delete = map->map(map->handle, XPRESS_DELETE);
+ xpress->urid.xpress_Clear = map->map(map->handle, XPRESS_CLEAR);
+
+ xpress->urid.xpress_uuid = map->map(map->handle, XPRESS_UUID);
+ xpress->urid.xpress_zone = map->map(map->handle, XPRESS_ZONE);
+ xpress->urid.xpress_position = map->map(map->handle, XPRESS_POSITION);
+ xpress->urid.xpress_velocity = map->map(map->handle, XPRESS_VELOCITY);
+ xpress->urid.xpress_acceleration = map->map(map->handle, XPRESS_ACCELERATION);
+
+ for(unsigned i = 0; i < xpress->max_nvoices; i++)
+ {
+ xpress_voice_t *voice = &xpress->voices[i];
+
+ voice->uuid = 0;
+ voice->target = target && iface
+ ? target + i*iface->size
+ : NULL;
+ }
+
+ return 1;
+}
+
+static inline void *
+xpress_get(xpress_t *xpress, xpress_uuid_t uuid)
+{
+ xpress_voice_t *voice = _xpress_voice_get(xpress, uuid);
+ if(voice)
+ return voice->target;
+
+ return NULL;
+}
+
+static inline int
+xpress_advance(xpress_t *xpress, LV2_Atom_Forge *forge, uint32_t frames,
+ const LV2_Atom_Object *obj, LV2_Atom_Forge_Ref *ref)
+{
+ if(!lv2_atom_forge_is_object_type(forge, obj->atom.type))
+ return 0;
+
+ if(obj->body.otype == xpress->urid.xpress_Put)
+ {
+ const LV2_Atom_Long *uuid = NULL;
+ const LV2_Atom_Int *zone = NULL;
+ const LV2_Atom_Vector *pos = NULL;
+ const LV2_Atom_Vector *vel = NULL;
+ const LV2_Atom_Vector *acc = NULL;
+
+ LV2_Atom_Object_Query q [] = {
+ { xpress->urid.xpress_uuid, (const LV2_Atom **)&uuid },
+ { xpress->urid.xpress_zone, (const LV2_Atom **)&zone },
+ { xpress->urid.xpress_position, (const LV2_Atom **)&pos },
+ { xpress->urid.xpress_velocity, (const LV2_Atom **)&vel },
+ { xpress->urid.xpress_acceleration, (const LV2_Atom **)&acc },
+ { 0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if( !uuid || (uuid->atom.type != xpress->urid.atom_long)
+ || !zone || (zone->atom.type != xpress->urid.atom_int)
+ || !pos || (pos->atom.type != xpress->urid.atom_vector)
+ || (pos->body.child_type != xpress->urid.atom_float)
+ || (pos->body.child_size != sizeof(float))
+ || !vel || (vel->atom.type != xpress->urid.atom_vector)
+ || (vel->body.child_type != xpress->urid.atom_float)
+ || (vel->body.child_size != sizeof(float))
+ || !acc || (acc->atom.type != xpress->urid.atom_vector)
+ || (acc->body.child_type != xpress->urid.atom_float)
+ || (acc->body.child_size != sizeof(float)) )
+ {
+ return 0;
+ }
+
+ bool added;
+ xpress_voice_t *voice = _xpress_voice_get(xpress, uuid->body);
+ void *target;
+ if(voice)
+ {
+ target = voice->target;
+
+ added = false;
+ }
+ else
+ {
+ if(!(target = _xpress_voice_add(xpress, uuid->body)))
+ return 0;
+
+ added = true;
+ }
+
+ xpress_state_t state;
+ memset(&state, 0x0, sizeof(xpress_state_t));
+ state.zone = zone->body;
+ _xpress_float_vec(xpress, state.position, pos);
+ _xpress_float_vec(xpress, state.velocity, vel);
+ _xpress_float_vec(xpress, state.acceleration, acc);
+
+ if(added)
+ {
+ if( (xpress->event_mask & XPRESS_EVENT_ADD) && xpress->iface->add)
+ xpress->iface->add(xpress->data, frames, &state, uuid->body, target);
+ }
+ else
+ {
+ if( (xpress->event_mask & XPRESS_EVENT_PUT) && xpress->iface->put)
+ xpress->iface->put(xpress->data, frames, &state, uuid->body, target);
+ }
+
+ return 1;
+ }
+ else if(obj->body.otype == xpress->urid.xpress_Delete)
+ {
+ const LV2_Atom_Long *uuid = NULL;
+
+ LV2_Atom_Object_Query q [] = {
+ { xpress->urid.xpress_uuid, (const LV2_Atom **)&uuid },
+ { 0, NULL}
+ };
+ lv2_atom_object_query(obj, q);
+
+ if(!uuid || (uuid->atom.type != xpress->urid.atom_long))
+ return 0;
+
+ xpress_voice_t *voice = _xpress_voice_get(xpress, uuid->body);
+ if(!voice)
+ return 0;
+
+ const LV2_URID voice_uuid = voice->uuid;
+ void *voice_target = voice->target;
+
+ _xpress_voice_free(xpress, voice);
+
+ if( (xpress->event_mask & XPRESS_EVENT_DEL) && xpress->iface->del)
+ xpress->iface->del(xpress->data, frames, NULL, voice_uuid, voice_target);
+
+ return 1;
+ }
+ else if(obj->body.otype == xpress->urid.xpress_Clear)
+ {
+ XPRESS_VOICE_FREE(xpress, voice)
+ {
+ const LV2_URID voice_uuid = voice->uuid;
+ void *voice_target = voice->target;
+
+ if( (xpress->event_mask & XPRESS_EVENT_DEL) && xpress->iface->del)
+ xpress->iface->del(xpress->data, frames, NULL, voice_uuid, voice_target);
+ }
+
+ return 1;
+ }
+
+ return 0; // did not handle a patch event
+}
+
+static inline void *
+xpress_add(xpress_t *xpress, xpress_uuid_t uuid)
+{
+ return _xpress_voice_add(xpress, uuid);
+}
+
+static inline void *
+xpress_create(xpress_t *xpress, xpress_uuid_t *uuid)
+{
+ *uuid = xpress->voice_map->new_uuid(xpress->voice_map->handle);
+
+ return xpress_add(xpress, *uuid);
+}
+
+static inline int
+xpress_free(xpress_t *xpress, xpress_uuid_t uuid)
+{
+ xpress_voice_t *voice = _xpress_voice_get(xpress, uuid);
+ if(!voice)
+ return 0; // failed
+
+ _xpress_voice_free(xpress, voice);
+
+ return 1;
+}
+
+static inline LV2_Atom_Forge_Ref
+xpress_del(xpress_t *xpress, LV2_Atom_Forge *forge, uint32_t frames,
+ xpress_uuid_t uuid)
+{
+ 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, xpress->urid.xpress_Delete);
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, xpress->urid.xpress_uuid);
+ if(ref)
+ ref = lv2_atom_forge_long(forge, uuid);
+ }
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+xpress_put(xpress_t *xpress, LV2_Atom_Forge *forge, uint32_t frames,
+ xpress_uuid_t uuid, const xpress_state_t *state)
+{
+ 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, xpress->urid.xpress_Put);
+ {
+ if(ref)
+ ref = lv2_atom_forge_key(forge, xpress->urid.xpress_uuid);
+ if(ref)
+ ref = lv2_atom_forge_long(forge, uuid);
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, xpress->urid.xpress_zone);
+ if(ref)
+ ref = lv2_atom_forge_int(forge, state->zone);
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, xpress->urid.xpress_position);
+ if(ref)
+ ref = lv2_atom_forge_vector(forge, sizeof(float), xpress->urid.atom_float,
+ XPRESS_MAX_NDIMS, state->position);
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, xpress->urid.xpress_velocity);
+ if(ref)
+ ref = lv2_atom_forge_vector(forge, sizeof(float), xpress->urid.atom_float,
+ XPRESS_MAX_NDIMS, state->velocity);
+
+ if(ref)
+ ref = lv2_atom_forge_key(forge, xpress->urid.xpress_acceleration);
+ if(ref)
+ ref = lv2_atom_forge_vector(forge, sizeof(float), xpress->urid.atom_float,
+ XPRESS_MAX_NDIMS, state->acceleration);
+ }
+ if(ref)
+ lv2_atom_forge_pop(forge, &obj_frame);
+
+ return ref;
+}
+
+static inline int32_t
+xpress_map(xpress_t *xpress)
+{
+ return xpress->voice_map->new_uuid(xpress->voice_map->handle);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_XPRESS_H_