aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYING201
-rw-r--r--README.md20
-rw-r--r--timely.h294
3 files changed, 515 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..ddb9a46
--- /dev/null
+++ b/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/README.md b/README.md
new file mode 100644
index 0000000..c422cf9
--- /dev/null
+++ b/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.h b/timely.h
new file mode 100644
index 0000000..5a83911
--- /dev/null
+++ b/timely.h
@@ -0,0 +1,294 @@
+/*
+ * 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 <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 void (*timely_cb_t)(int64_t frame, LV2_URID type, const LV2_Atom *atom, void *data);
+typedef struct _timely_t timely_t;
+
+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)
+};
+
+struct _timely_t {
+ struct {
+ LV2_URID atom_object;
+ 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;
+
+ uint32_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;
+
+ struct {
+ LV2_Atom_Long bar;
+ LV2_Atom_Float bar_beat;
+ } atom;
+
+ timely_mask_t mask;
+ timely_cb_t cb;
+ void *data;
+};
+
+static inline void
+_timely_deatomize(timely_t *timely, int64_t frames, const LV2_Atom_Object *obj)
+{
+ 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_Query q [] = {
+ { timely->urid.time_barBeat, (const LV2_Atom **)&bar_beat },
+ { timely->urid.time_bar, (const LV2_Atom **)&bar },
+ { timely->urid.time_beatUnit, (const LV2_Atom **)&beat_unit },
+ { timely->urid.time_beatsPerBar, (const LV2_Atom **)&beats_per_bar },
+ { timely->urid.time_beatsPerMinute, (const LV2_Atom **)&beats_per_minute },
+ { timely->urid.time_frame, (const LV2_Atom **)&frame },
+ { timely->urid.time_framesPerSecond, (const LV2_Atom **)&frames_per_second },
+ { timely->urid.time_speed, (const LV2_Atom **)&speed },
+ LV2_ATOM_OBJECT_QUERY_END
+ };
+
+ lv2_atom_object_query(obj, q);
+
+ if(beat_unit && (beat_unit->body != timely->pos.beat_unit) )
+ {
+ timely->pos.beat_unit = beat_unit->body;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BEAT_UNIT) )
+ timely->cb(frames, timely->urid.time_beatUnit, (const LV2_Atom *)beat_unit, 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->cb && (timely->mask & TIMELY_MASK_BEATS_PER_BAR) )
+ timely->cb(frames, timely->urid.time_beatsPerBar, (const LV2_Atom *)beats_per_bar, 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->cb && (timely->mask & TIMELY_MASK_BEATS_PER_MINUTE) )
+ timely->cb(frames, timely->urid.time_beatsPerMinute, (const LV2_Atom *)beats_per_minute, timely->data);
+ }
+
+ if(frame && (frame->body != timely->pos.frame) )
+ {
+ timely->pos.frame = frame->body;
+ if(timely->cb && (timely->mask & TIMELY_MASK_FRAME) )
+ timely->cb(frames, timely->urid.time_frame, (const LV2_Atom *)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->cb && (timely->mask & TIMELY_MASK_FRAMES_PER_SECOND) )
+ timely->cb(frames, timely->urid.time_framesPerSecond, (const LV2_Atom *)frames_per_second, timely->data);
+ }
+
+ if(speed && (speed->body != timely->pos.speed) )
+ {
+ timely->pos.speed = speed->body;
+ if(timely->cb && (timely->mask & TIMELY_MASK_SPEED) )
+ timely->cb(frames, timely->urid.time_speed, (const LV2_Atom *)speed, timely->data);
+ }
+
+ if(bar_beat && (bar_beat->body != timely->pos.bar_beat) )
+ {
+ timely->pos.bar_beat = bar_beat->body;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BAR_BEAT) )
+ timely->cb(frames, timely->urid.time_barBeat, (const LV2_Atom *)bar_beat, timely->data);
+ }
+ else if(0.f != timely->pos.bar_beat) // calculate
+ {
+ timely->pos.bar_beat = 0.f;
+ timely->atom.bar_beat.body = timely->pos.bar_beat;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BAR_BEAT) )
+ timely->cb(frames, timely->urid.time_barBeat, (const LV2_Atom *)&timely->atom.bar_beat, timely->data);
+ }
+
+ if(bar && (bar->body != timely->pos.bar) )
+ {
+ timely->pos.bar = bar->body;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BAR) )
+ timely->cb(frames, timely->urid.time_bar, (const LV2_Atom *)bar, timely->data);
+ }
+ else if(0 != timely->pos.bar) // calculate
+ {
+ timely->pos.bar = 0;
+ timely->atom.bar.body = timely->pos.bar;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BAR) )
+ timely->cb(frames, timely->urid.time_bar, (const LV2_Atom *)&timely->atom.bar, timely->data);
+ }
+}
+
+static inline void
+_timely_refresh(timely_t *timely)
+{
+ timely->frames_per_beat = 240.f / (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 void
+timely_init(timely_t *timely, LV2_URID_Map *map, double rate,
+ timely_mask_t mask, timely_cb_t cb, void *data)
+{
+ timely->mask = mask;
+ timely->cb = cb;
+ timely->data = data;
+
+ timely->urid.atom_object = map->map(map->handle, LV2_ATOM__Object);
+ 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->atom.bar.atom.size = sizeof(int64_t);
+ timely->atom.bar.atom.type = map->map(map->handle, LV2_ATOM__Int);
+
+ timely->atom.bar_beat.atom.size = sizeof(float);
+ timely->atom.bar_beat.atom.type = map->map(map->handle, LV2_ATOM__Float);
+
+ 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.0f;
+ timely->pos.frame = 0;
+ timely->pos.frames_per_second = rate;
+ timely->pos.speed = 0.f;
+ //TODO send updates for initial values?
+
+ _timely_refresh(timely);
+}
+
+static void
+timely_advance(timely_t *timely, const LV2_Atom_Object *obj,
+ uint32_t from, uint32_t to)
+{
+ if(timely->pos.speed > 0.f)
+ {
+ for(unsigned i=from; i<to; i++)
+ {
+ timely->offset.bar += 1;
+ timely->offset.beat += 1;
+
+ if(timely->offset.bar >= timely->window.bar)
+ {
+ timely->pos.bar += 1;
+ timely->offset.bar -= timely->window.bar;
+
+ timely->atom.bar.body = timely->pos.bar;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BAR) )
+ timely->cb(i, timely->urid.time_bar, (const LV2_Atom *)&timely->atom.bar, timely->data);
+ }
+
+ if(timely->offset.beat >= timely->window.beat)
+ {
+ 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 = 0;
+
+ timely->atom.bar_beat.body = timely->pos.bar_beat;
+ if(timely->cb && (timely->mask & TIMELY_MASK_BAR_BEAT) )
+ timely->cb(i, timely->urid.time_barBeat, (const LV2_Atom *)&timely->atom.bar_beat, timely->data);
+ }
+
+ timely->pos.frame += to - from;
+ }
+ }
+
+ if( obj
+ && (obj->atom.type == timely->urid.atom_object)
+ && (obj->body.otype == timely->urid.time_position) )
+ {
+ _timely_deatomize(timely, to, obj);
+ _timely_refresh(timely);
+ }
+}
+
+#endif // _LV2_TIMELY_H_