aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2017-03-21 13:25:58 +0100
committerHanspeter Portner <dev@open-music-kontrollers.ch>2017-03-21 13:25:58 +0100
commit11088137929d6cff5e53daf76b16cbdc0471a729 (patch)
treed28ba4fed8fdcf9d874efb8bd5076f8acf2b2613
parentfb5967921e71ddd7ac122f752de3bfd07a1adb60 (diff)
parente995aff05b6d89a92caa91af9df3c78ae07d7f99 (diff)
downloadvm.lv2-11088137929d6cff5e53daf76b16cbdc0471a729.tar.xz
Add 'timely.lv2/' from commit 'e995aff05b6d89a92caa91af9df3c78ae07d7f99'
git-subtree-dir: timely.lv2 git-subtree-mainline: fb5967921e71ddd7ac122f752de3bfd07a1adb60 git-subtree-split: e995aff05b6d89a92caa91af9df3c78ae07d7f99
-rw-r--r--timely.lv2/CMakeLists.txt33
-rw-r--r--timely.lv2/COPYING201
-rw-r--r--timely.lv2/README.md20
-rw-r--r--timely.lv2/test/manifest.ttl.in28
-rw-r--r--timely.lv2/test/timely.c205
-rw-r--r--timely.lv2/test/timely.ttl65
-rw-r--r--timely.lv2/timely.h375
7 files changed, 927 insertions, 0 deletions
diff --git a/timely.lv2/CMakeLists.txt b/timely.lv2/CMakeLists.txt
new file mode 100644
index 0000000..4180e7b
--- /dev/null
+++ b/timely.lv2/CMakeLists.txt
@@ -0,0 +1,33 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(timely.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 -Wl,-z,defs ${CMAKE_MODULE_LINKER_FLAGS}")
+add_definitions("-D_GNU_SOURCE=1") # asprintf
+
+set(TIMELY_MAJOR_VERSION 0)
+set(TIMELY_MINOR_VERSION 1)
+set(TIMELY_MICRO_VERSION 1)
+set(TIMELY_VERSION "${TIMELY_MAJOR_VERSION}.${TIMELY_MINOR_VERSION}.${TIMELY_MICRO_VERSION}")
+add_definitions("-DTIMELY_VERSION=\"${TIMELY_VERSION}\"")
+
+set(DEST lib/lv2/timely.lv2)
+
+find_package(PkgConfig) # ${PKG_CONFIG_FOUND}
+
+# timely
+add_library(timely MODULE
+ test/timely.c)
+target_link_libraries(timely m)
+set_target_properties(timely PROPERTIES PREFIX "")
+install(TARGETS timely DESTINATION ${DEST})
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/test/timely.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/timely.lv2/COPYING b/timely.lv2/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/timely.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/timely.lv2/README.md b/timely.lv2/README.md
new file mode 100644
index 0000000..c422cf9
--- /dev/null
+++ b/timely.lv2/README.md
@@ -0,0 +1,20 @@
+# Timely.lv2
+
+## Utility header for time-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/timely.lv2/test/manifest.ttl.in b/timely.lv2/test/manifest.ttl.in
new file mode 100644
index 0000000..3605c1c
--- /dev/null
+++ b/timely.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 timely: <http://open-music-kontrollers.ch/lv2/timely#> .
+
+# Orbit Looper
+timely:test
+ a lv2:Plugin ;
+ lv2:minorVersion @TIMELY_MINOR_VERSION@ ;
+ lv2:microVersion @TIMELY_MICRO_VERSION@ ;
+ lv2:binary <timely@CMAKE_SHARED_MODULE_SUFFIX@> ;
+ rdfs:seeAlso <timely.ttl> .
diff --git a/timely.lv2/test/timely.c b/timely.lv2/test/timely.c
new file mode 100644
index 0000000..171fc9d
--- /dev/null
+++ b/timely.lv2/test/timely.c
@@ -0,0 +1,205 @@
+/*
+ * 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 <stdlib.h>
+#include <inttypes.h>
+
+#include <timely.h>
+
+#include <lv2/lv2plug.in/ns/ext/log/log.h>
+
+#define TIMELY_PREFIX "http://open-music-kontrollers.ch/lv2/timely#"
+#define TIMELY_TEST_URI TIMELY_PREFIX"test"
+
+typedef struct _plughandle_t plughandle_t;
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Log_Log *log;
+ timely_t timely;
+
+ LV2_URID log_trace;
+
+ const LV2_Atom_Sequence *event_in;
+};
+
+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
+_timely_cb(timely_t *timely, int64_t frames, LV2_URID type, void *data)
+{
+ plughandle_t *handle = data;
+
+ const char *uri = NULL;
+
+ if(type == TIMELY_URI_BAR_BEAT(timely))
+ uri = LV2_TIME__barBeat;
+ else if(type == TIMELY_URI_BAR(timely))
+ uri = LV2_TIME__bar;
+ else if(type == TIMELY_URI_BEAT_UNIT(timely))
+ uri = LV2_TIME__beatUnit;
+ else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
+ uri = LV2_TIME__beatsPerBar;
+ else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
+ uri = LV2_TIME__beatsPerMinute;
+ else if(type == TIMELY_URI_FRAME(timely))
+ uri = LV2_TIME__frame;
+ else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
+ uri = LV2_TIME__framesPerSecond;
+ else if(type == TIMELY_URI_SPEED(timely))
+ uri = LV2_TIME__speed;
+
+ const int64_t frame = TIMELY_FRAME(timely);
+ _log_printf(data, handle->log_trace, "0x%08"PRIx64" %4"PRIi64" %s (%i)", frame, frames, uri, type);
+}
+
+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);
+
+ timely_mask_t mask = TIMELY_MASK_BAR_BEAT
+ | TIMELY_MASK_BAR
+ | TIMELY_MASK_BEAT_UNIT
+ | TIMELY_MASK_BEATS_PER_BAR
+ | TIMELY_MASK_BEATS_PER_MINUTE
+ | TIMELY_MASK_FRAME
+ | TIMELY_MASK_FRAMES_PER_SECOND
+ | TIMELY_MASK_SPEED
+ | TIMELY_MASK_BAR_BEAT_WHOLE
+ | TIMELY_MASK_BAR_WHOLE;
+ timely_init(&handle->timely, handle->map, rate, mask, _timely_cb, handle);
+
+ 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;
+ default:
+ break;
+ }
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+ int64_t from = 0;
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
+ {
+ const int64_t to = ev->time.frames;
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ const int handled = timely_advance(&handle->timely, obj, from, to);
+ (void)handled;
+ from = to;
+ }
+
+ timely_advance(&handle->timely, NULL, from, nsamples);
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ free(handle);
+}
+
+const LV2_Descriptor timely_test = {
+ .URI = TIMELY_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 &timely_test;
+ default:
+ return NULL;
+ }
+}
diff --git a/timely.lv2/test/timely.ttl b/timely.lv2/test/timely.ttl
new file mode 100644
index 0000000..6064c9c
--- /dev/null
+++ b/timely.lv2/test/timely.ttl
@@ -0,0 +1,65 @@
+# 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 time: <http://lv2plug.in/ns/ext/time#> .
+@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 timely: <http://open-music-kontrollers.ch/lv2/timely#> .
+
+# 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:timely
+ a doap:Project ;
+ doap:maintainer omk:me ;
+ doap:name "Timely Bundle" .
+
+# Looper Test
+timely:test
+ a lv2:Plugin ,
+ lv2:ConverterPlugin ;
+ doap:name "Timely Test" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:timely ;
+ lv2:requiredFeature urid:map, log:log ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable ;
+
+ lv2:port [
+ # sink event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports time:Position ;
+ lv2:index 0 ;
+ lv2:symbol "event_in" ;
+ lv2:name "Event Input" ;
+ lv2:designation lv2:control ;
+ ] .
diff --git a/timely.lv2/timely.h b/timely.lv2/timely.h
new file mode 100644
index 0000000..0cdf679
--- /dev/null
+++ b/timely.lv2/timely.h
@@ -0,0 +1,375 @@
+/*
+ * 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_TIMELY_H_
+#define _LV2_TIMELY_H_
+
+#include <math.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/time/time.h>
+
+typedef enum _timely_mask_t timely_mask_t;
+typedef struct _timely_t timely_t;
+typedef void (*timely_cb_t)(timely_t *timely, int64_t frames, LV2_URID type,
+ void *data);
+
+enum _timely_mask_t {
+ TIMELY_MASK_BAR_BEAT = (1 << 0),
+ TIMELY_MASK_BAR = (1 << 1),
+ TIMELY_MASK_BEAT_UNIT = (1 << 2),
+ TIMELY_MASK_BEATS_PER_BAR = (1 << 3),
+ TIMELY_MASK_BEATS_PER_MINUTE = (1 << 4),
+ TIMELY_MASK_FRAME = (1 << 5),
+ TIMELY_MASK_FRAMES_PER_SECOND = (1 << 6),
+ TIMELY_MASK_SPEED = (1 << 7),
+ TIMELY_MASK_BAR_BEAT_WHOLE = (1 << 8),
+ TIMELY_MASK_BAR_WHOLE = (1 << 9)
+};
+
+struct _timely_t {
+ struct {
+ LV2_URID atom_object;
+ LV2_URID atom_blank;
+ LV2_URID atom_resource;
+
+ LV2_URID time_position;
+ LV2_URID time_barBeat;
+ LV2_URID time_bar;
+ LV2_URID time_beatUnit;
+ LV2_URID time_beatsPerBar;
+ LV2_URID time_beatsPerMinute;
+ LV2_URID time_frame;
+ LV2_URID time_framesPerSecond;
+ LV2_URID time_speed;
+ } urid;
+
+ struct {
+ float bar_beat;
+ int64_t bar;
+
+ int32_t beat_unit;
+ float beats_per_bar;
+ float beats_per_minute;
+
+ int64_t frame;
+ float frames_per_second;
+
+ float speed;
+ } pos;
+
+ double frames_per_beat;
+ double frames_per_bar;
+
+ struct {
+ double beat;
+ double bar;
+ } offset;
+
+ struct {
+ double beat;
+ double bar;
+ } window;
+
+ bool first;
+ timely_mask_t mask;
+ timely_cb_t cb;
+ void *data;
+};
+
+#define TIMELY_URI_BAR_BEAT(timely) ((timely)->urid.time_barBeat)
+#define TIMELY_URI_BAR(timely) ((timely)->urid.time_bar)
+#define TIMELY_URI_BEAT_UNIT(timely) ((timely)->urid.time_beatUnit)
+#define TIMELY_URI_BEATS_PER_BAR(timely) ((timely)->urid.time_beatsPerBar)
+#define TIMELY_URI_BEATS_PER_MINUTE(timely) ((timely)->urid.time_beatsPerMinute)
+#define TIMELY_URI_FRAME(timely) ((timely)->urid.time_frame)
+#define TIMELY_URI_FRAMES_PER_SECOND(timely) ((timely)->urid.time_framesPerSecond)
+#define TIMELY_URI_SPEED(timely) ((timely)->urid.time_speed)
+
+#define TIMELY_BAR_BEAT_RAW(timely) ((timely)->pos.bar_beat)
+#define TIMELY_BAR_BEAT(timely) (floor((timely)->pos.bar_beat) + (timely)->offset.beat / (timely)->frames_per_beat)
+#define TIMELY_BAR(timely) ((timely)->pos.bar)
+#define TIMELY_BEAT_UNIT(timely) ((timely)->pos.beat_unit)
+#define TIMELY_BEATS_PER_BAR(timely) ((timely)->pos.beats_per_bar)
+#define TIMELY_BEATS_PER_MINUTE(timely) ((timely)->pos.beats_per_minute)
+#define TIMELY_FRAME(timely) ((timely)->pos.frame)
+#define TIMELY_FRAMES_PER_SECOND(timely) ((timely)->pos.frames_per_second)
+#define TIMELY_SPEED(timely) ((timely)->pos.speed)
+
+#define TIMELY_FRAMES_PER_BEAT(timely) ((timely)->frames_per_beat)
+#define TIMELY_FRAMES_PER_BAR(timely) ((timely)->frames_per_bar)
+
+static inline void
+_timely_deatomize_body(timely_t *timely, int64_t frames, uint32_t size,
+ const LV2_Atom_Object_Body *body)
+{
+ const LV2_Atom_Float *bar_beat = NULL;
+ const LV2_Atom_Long *bar = NULL;
+ const LV2_Atom_Int *beat_unit = NULL;
+ const LV2_Atom_Float *beats_per_bar = NULL;
+ const LV2_Atom_Float *beats_per_minute = NULL;
+ const LV2_Atom_Long *frame = NULL;
+ const LV2_Atom_Float *frames_per_second = NULL;
+ const LV2_Atom_Float *speed = NULL;
+
+ lv2_atom_object_body_get(size, body,
+ timely->urid.time_barBeat, &bar_beat,
+ timely->urid.time_bar, &bar,
+ timely->urid.time_beatUnit, &beat_unit,
+ timely->urid.time_beatsPerBar, &beats_per_bar,
+ timely->urid.time_beatsPerMinute, &beats_per_minute,
+ timely->urid.time_frame, &frame,
+ timely->urid.time_framesPerSecond, &frames_per_second,
+ timely->urid.time_speed, &speed,
+ 0);
+
+ // send speed first upon transport stop
+ if(speed && (speed->body != timely->pos.speed) && (speed->body == 0.f) )
+ {
+ timely->pos.speed = speed->body;
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, frames, timely->urid.time_speed, timely->data);
+ }
+
+ if(beat_unit && (beat_unit->body != timely->pos.beat_unit) )
+ {
+ timely->pos.beat_unit = beat_unit->body;
+ if(timely->mask & TIMELY_MASK_BEAT_UNIT)
+ timely->cb(timely, frames, timely->urid.time_beatUnit, timely->data);
+ }
+
+ if(beats_per_bar && (beats_per_bar->body != timely->pos.beats_per_bar) )
+ {
+ timely->pos.beats_per_bar = beats_per_bar->body;
+ if(timely->mask & TIMELY_MASK_BEATS_PER_BAR)
+ timely->cb(timely, frames, timely->urid.time_beatsPerBar, timely->data);
+ }
+
+ if(beats_per_minute && (beats_per_minute->body != timely->pos.beats_per_minute) )
+ {
+ timely->pos.beats_per_minute = beats_per_minute->body;
+ if(timely->mask & TIMELY_MASK_BEATS_PER_MINUTE)
+ timely->cb(timely, frames, timely->urid.time_beatsPerMinute, timely->data);
+ }
+
+ if(frame && (frame->body != timely->pos.frame) )
+ {
+ timely->pos.frame = frame->body;
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, frames, timely->urid.time_frame, timely->data);
+ }
+
+ if(frames_per_second && (frames_per_second->body != timely->pos.frames_per_second) )
+ {
+ timely->pos.frames_per_second = frames_per_second->body;
+ if(timely->mask & TIMELY_MASK_FRAMES_PER_SECOND)
+ timely->cb(timely, frames, timely->urid.time_framesPerSecond, timely->data);
+ }
+
+ if(bar && (bar->body != timely->pos.bar) )
+ {
+ timely->pos.bar = bar->body;
+ if(timely->mask & TIMELY_MASK_BAR)
+ timely->cb(timely, frames, timely->urid.time_bar, timely->data);
+ }
+
+ if(bar_beat && (bar_beat->body != timely->pos.bar_beat) )
+ {
+ timely->pos.bar_beat = bar_beat->body;
+ if(timely->mask & TIMELY_MASK_BAR_BEAT)
+ timely->cb(timely, frames, timely->urid.time_barBeat, timely->data);
+ }
+
+ // send speed last upon transport start
+ if(speed && (speed->body != timely->pos.speed) && (speed->body != 0.f) )
+ {
+ timely->pos.speed = speed->body;
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, frames, timely->urid.time_speed, timely->data);
+ }
+}
+
+static inline void
+_timely_refresh(timely_t *timely)
+{
+ timely->frames_per_beat = 240.0 / (timely->pos.beats_per_minute * timely->pos.beat_unit)
+ * timely->pos.frames_per_second;
+ timely->frames_per_bar = timely->frames_per_beat * timely->pos.beats_per_bar;
+
+ // bar
+ timely->window.bar = timely->frames_per_bar;
+ timely->offset.bar = timely->pos.bar_beat * timely->frames_per_beat;
+
+ // beat
+ timely->window.beat = timely->frames_per_beat;
+ double integral;
+ double beat_beat = modf(timely->pos.bar_beat, &integral);
+ (void)integral;
+ timely->offset.beat = beat_beat * timely->frames_per_beat;
+}
+
+static inline void
+timely_init(timely_t *timely, LV2_URID_Map *map, double rate,
+ timely_mask_t mask, timely_cb_t cb, void *data)
+{
+ assert(cb != NULL);
+
+ timely->mask = mask;
+ timely->cb = cb;
+ timely->data = data;
+
+ timely->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
+ timely->urid.atom_blank = map->map(map->handle, LV2_ATOM__Blank);
+ timely->urid.atom_resource = map->map(map->handle, LV2_ATOM__Resource);
+ timely->urid.time_position = map->map(map->handle, LV2_TIME__Position);
+ timely->urid.time_barBeat = map->map(map->handle, LV2_TIME__barBeat);
+ timely->urid.time_bar = map->map(map->handle, LV2_TIME__bar);
+ timely->urid.time_beatUnit = map->map(map->handle, LV2_TIME__beatUnit);
+ timely->urid.time_beatsPerBar = map->map(map->handle, LV2_TIME__beatsPerBar);
+ timely->urid.time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);
+ timely->urid.time_frame = map->map(map->handle, LV2_TIME__frame);
+ timely->urid.time_framesPerSecond = map->map(map->handle, LV2_TIME__framesPerSecond);
+ timely->urid.time_speed = map->map(map->handle, LV2_TIME__speed);
+
+ timely->pos.speed = 0.f;
+ timely->pos.bar_beat = 0.f;
+ timely->pos.bar = 0;
+ timely->pos.beat_unit = 4;
+ timely->pos.beats_per_bar = 4.f;
+ timely->pos.beats_per_minute = 120.f;
+ timely->pos.frame = 0;
+ timely->pos.frames_per_second = rate;
+
+ _timely_refresh(timely);
+
+ timely->first = true;
+}
+
+static inline int
+timely_advance_body(timely_t *timely, uint32_t size, uint32_t type,
+ const LV2_Atom_Object_Body *body, uint32_t from, uint32_t to)
+{
+ if(timely->first)
+ {
+ timely->first = false;
+
+ // send initial values
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, 0, timely->urid.time_speed, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BEAT_UNIT)
+ timely->cb(timely, 0, timely->urid.time_beatUnit, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BEATS_PER_BAR)
+ timely->cb(timely, 0, timely->urid.time_beatsPerBar, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BEATS_PER_MINUTE)
+ timely->cb(timely, 0, timely->urid.time_beatsPerMinute, timely->data);
+
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, 0, timely->urid.time_frame, timely->data);
+
+ if(timely->mask & TIMELY_MASK_FRAMES_PER_SECOND)
+ timely->cb(timely, 0, timely->urid.time_framesPerSecond, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR)
+ timely->cb(timely, 0, timely->urid.time_bar, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR_BEAT)
+ timely->cb(timely, 0, timely->urid.time_barBeat, timely->data);
+ }
+
+ // are we rolling?
+ if(timely->pos.speed > 0.f)
+ {
+ if( (timely->offset.bar == 0) && (timely->pos.bar == 0) )
+ {
+ if(timely->mask & (TIMELY_MASK_BAR | TIMELY_MASK_BAR_WHOLE) )
+ timely->cb(timely, from, timely->urid.time_bar, timely->data);
+ }
+
+ if( (timely->offset.beat == 0) && (timely->pos.bar_beat == 0) )
+ {
+ if(timely->mask & (TIMELY_MASK_BAR_BEAT | TIMELY_MASK_BAR_BEAT_WHOLE) )
+ timely->cb(timely, from, timely->urid.time_barBeat, timely->data);
+ }
+
+ unsigned update_frame = to;
+ for(unsigned i=from; i<to; i++)
+ {
+ if(timely->offset.bar >= timely->window.bar)
+ {
+ timely->pos.bar += 1;
+ timely->offset.bar -= timely->window.bar;
+
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, (update_frame = i), timely->urid.time_frame, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR_WHOLE)
+ timely->cb(timely, i, timely->urid.time_bar, timely->data);
+ }
+
+ if( (timely->offset.beat >= timely->window.beat) )
+ {
+ timely->pos.bar_beat = floor(timely->pos.bar_beat) + 1;
+ timely->offset.beat -= timely->window.beat;
+
+ if(timely->pos.bar_beat >= timely->pos.beats_per_bar)
+ timely->pos.bar_beat -= timely->pos.beats_per_bar;
+
+ if( (timely->mask & TIMELY_MASK_FRAME) && (update_frame != i) )
+ timely->cb(timely, (update_frame = i), timely->urid.time_frame, timely->data);
+
+ if(timely->mask & TIMELY_MASK_BAR_BEAT_WHOLE)
+ timely->cb(timely, i, timely->urid.time_barBeat, timely->data);
+ }
+
+ timely->offset.bar += 1;
+ timely->offset.beat += 1;
+ timely->pos.frame += 1;
+ }
+ }
+
+ // is this a time position event?
+ if( ( (type == timely->urid.atom_object)
+ || (type == timely->urid.atom_blank)
+ || (type == timely->urid.atom_resource) )
+ && body && (body->otype == timely->urid.time_position) )
+ {
+ _timely_deatomize_body(timely, to, size, body);
+ _timely_refresh(timely);
+
+ return 1; // handled a time position event
+ }
+
+ return 0; // did not handle a time position event
+}
+
+static inline int
+timely_advance(timely_t *timely, const LV2_Atom_Object *obj,
+ uint32_t from, uint32_t to)
+{
+ if(obj)
+ return timely_advance_body(timely, obj->atom.size, obj->atom.type, &obj->body, from, to);
+
+ return timely_advance_body(timely, 0, 0, NULL, from, to);
+}
+
+#endif // _LV2_TIMELY_H_