diff options
author | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2017-03-21 13:25:58 +0100 |
---|---|---|
committer | Hanspeter Portner <dev@open-music-kontrollers.ch> | 2017-03-21 13:25:58 +0100 |
commit | 11088137929d6cff5e53daf76b16cbdc0471a729 (patch) | |
tree | d28ba4fed8fdcf9d874efb8bd5076f8acf2b2613 | |
parent | fb5967921e71ddd7ac122f752de3bfd07a1adb60 (diff) | |
parent | e995aff05b6d89a92caa91af9df3c78ae07d7f99 (diff) | |
download | vm.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.txt | 33 | ||||
-rw-r--r-- | timely.lv2/COPYING | 201 | ||||
-rw-r--r-- | timely.lv2/README.md | 20 | ||||
-rw-r--r-- | timely.lv2/test/manifest.ttl.in | 28 | ||||
-rw-r--r-- | timely.lv2/test/timely.c | 205 | ||||
-rw-r--r-- | timely.lv2/test/timely.ttl | 65 | ||||
-rw-r--r-- | timely.lv2/timely.h | 375 |
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_ |