aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2017-07-05 22:39:17 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2017-07-05 22:39:17 +0200
commitc4737c326c5eac0c1a1a08558cab4099a663bd1e (patch)
tree41ce0d8e50f7e8fdc689a3156588e45b2580d092
parent0f5f290f74de93771ea8e99e4e5c7251352eeb14 (diff)
downloadmoony.lv2-c4737c326c5eac0c1a1a08558cab4099a663bd1e.tar.xz
Squashed 'timely.lv2/' changes from 0f5f290..e42f631
e42f631 prototype timely:multiplier. 43a7d74 remove duplicate parameter. 40852e9 fix last commit. 7de603f take time:speed into account for frames/beat. 54412b1 migrate test to lv2_logger. e995aff make TIMELY_MASK_FRAME less verbose. 81d266b fix upper bound sample error. 30c543a fix timely range upper bound. 4d9c71f include math.h 0438d64 modify API to work with atom object bodies, too. 538f2c1 add TIMELY_BAR_BEAT_RAW macro. ecd1626 add missing floor. e667702 fix TIMELY_BAR_BEAT macro, handle MASK_FRAME cb. 1434882 fix of last fix. 98fccf0 fix missing callbacks signals at sequencer start. 9909bb4 add test plugin, two more mask flags. 8b14e62 simplify API. git-subtree-dir: timely.lv2 git-subtree-split: e42f631d0d111de0e3f5c0bb5ebc9c4a09a16a70
-rw-r--r--CMakeLists.txt33
-rw-r--r--test/manifest.ttl.in28
-rw-r--r--test/timely.c217
-rw-r--r--test/timely.ttl65
-rw-r--r--timely.h332
5 files changed, 527 insertions, 148 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..4180e7b
--- /dev/null
+++ b/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/test/manifest.ttl.in b/test/manifest.ttl.in
new file mode 100644
index 0000000..3605c1c
--- /dev/null
+++ b/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/test/timely.c b/test/timely.c
new file mode 100644
index 0000000..ce442b0
--- /dev/null
+++ b/test/timely.c
@@ -0,0 +1,217 @@
+/*
+ * 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>
+#include <lv2/lv2plug.in/ns/ext/log/logger.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;
+ LV2_Log_Logger logger;
+ timely_t timely;
+
+
+ const LV2_Atom_Sequence *event_in;
+};
+
+static void
+_timely_cb(timely_t *timely, int64_t frames, LV2_URID type, void *data)
+{
+ plughandle_t *handle = data;
+
+ const int64_t frame = TIMELY_FRAME(timely);
+
+ if(type == TIMELY_URI_BAR_BEAT(timely))
+ {
+ const float bar_beat = TIMELY_BAR_BEAT_RAW(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:barBeat %f\n",
+ frame, frames, bar_beat);
+ }
+ else if(type == TIMELY_URI_BAR(timely))
+ {
+ const int64_t bar = TIMELY_BAR(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:bar %"PRIi64"\n",
+ frame, frames, bar);
+ }
+ else if(type == TIMELY_URI_BEAT_UNIT(timely))
+ {
+ const int32_t beat_unit = TIMELY_BEAT_UNIT(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:beatUnit %"PRIi32"\n",
+ frame, frames, beat_unit);
+ }
+ else if(type == TIMELY_URI_BEATS_PER_BAR(timely))
+ {
+ const float bpb = TIMELY_BEATS_PER_BAR(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:beatsPerBar %f\n",
+ frame, frames, bpb);
+ }
+ else if(type == TIMELY_URI_BEATS_PER_MINUTE(timely))
+ {
+ const float bpm = TIMELY_BEATS_PER_MINUTE(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:beatsPerMinute %f\n",
+ frame, frames, bpm);
+ }
+ else if(type == TIMELY_URI_FRAME(timely))
+ {
+ /*
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:frame %"PRIi64"\n",
+ frame, frames, frame);
+ */
+ }
+ else if(type == TIMELY_URI_FRAMES_PER_SECOND(timely))
+ {
+ const float fps = TIMELY_FRAMES_PER_SECOND(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:framesPerSecond %f\n",
+ frame, frames, fps);
+ }
+ else if(type == TIMELY_URI_SPEED(timely))
+ {
+ const float speed = TIMELY_SPEED(timely);
+ lv2_log_trace(&handle->logger, "0x%08"PRIx64" %4"PRIi64" time:speed %f\n",
+ frame, frames, speed);
+ }
+}
+
+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;
+ }
+
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+
+ 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);
+ timely_set_multiplier(&handle->timely, 1.f);
+
+ 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/test/timely.ttl b/test/timely.ttl
new file mode 100644
index 0000000..6064c9c
--- /dev/null
+++ b/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.h b/timely.h
index 4f19015..7dd00ee 100644
--- a/timely.h
+++ b/timely.h
@@ -18,6 +18,8 @@
#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>
@@ -27,7 +29,7 @@
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,
- const LV2_Atom *atom, void *data);
+ void *data);
enum _timely_mask_t {
TIMELY_MASK_BAR_BEAT = (1 << 0),
@@ -37,12 +39,17 @@ enum _timely_mask_t {
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_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;
@@ -68,6 +75,8 @@ struct _timely_t {
float speed;
} pos;
+ float multiplier;
+
double frames_per_beat;
double frames_per_bar;
@@ -76,25 +85,38 @@ struct _timely_t {
double bar;
} offset;
- struct {
- double beat;
- double bar;
- } window;
-
- struct {
- LV2_Atom_Int i;
- LV2_Atom_Long h;
- LV2_Atom_Float f;
- } atom;
-
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(timely_t *timely, int64_t frames, const LV2_Atom_Object *obj)
+_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;
@@ -105,114 +127,129 @@ _timely_deatomize(timely_t *timely, int64_t frames, const LV2_Atom_Object *obj)
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);
+ 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->cb && (timely->mask & TIMELY_MASK_SPEED) )
- timely->cb(timely, frames, timely->urid.time_speed, (const LV2_Atom *)speed, timely->data);
+ 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) )
+ if(beat_unit)
{
- timely->pos.beat_unit = beat_unit->body;
- if(timely->cb && (timely->mask & TIMELY_MASK_BEAT_UNIT) )
- timely->cb(timely, frames, timely->urid.time_beatUnit, (const LV2_Atom *)beat_unit, timely->data);
+ const int32_t _beat_unit = beat_unit->body * timely->multiplier;
+ if(_beat_unit != timely->pos.beat_unit)
+ {
+ timely->pos.beat_unit = _beat_unit;
+ 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) )
+ if(beats_per_bar)
{
- timely->pos.beats_per_bar = beats_per_bar->body;
- if(timely->cb && (timely->mask & TIMELY_MASK_BEATS_PER_BAR) )
- timely->cb(timely, frames, timely->urid.time_beatsPerBar, (const LV2_Atom *)beats_per_bar, timely->data);
+ const float _beats_per_bar = beats_per_bar->body * timely->multiplier;
+ if(_beats_per_bar != timely->pos.beats_per_bar)
+ {
+ timely->pos.beats_per_bar = _beats_per_bar;
+ 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->cb && (timely->mask & TIMELY_MASK_BEATS_PER_MINUTE) )
- timely->cb(timely, frames, timely->urid.time_beatsPerMinute, (const LV2_Atom *)beats_per_minute, timely->data);
+ 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->cb && (timely->mask & TIMELY_MASK_FRAME) )
- timely->cb(timely, frames, timely->urid.time_frame, (const LV2_Atom *)frame, timely->data);
+ 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->cb && (timely->mask & TIMELY_MASK_FRAMES_PER_SECOND) )
- timely->cb(timely, frames, timely->urid.time_framesPerSecond, (const LV2_Atom *)frames_per_second, timely->data);
+ if(timely->mask & TIMELY_MASK_FRAMES_PER_SECOND)
+ timely->cb(timely, frames, timely->urid.time_framesPerSecond, timely->data);
}
- if(bar_beat && (bar_beat->body != timely->pos.bar_beat) )
+ if(bar && (bar->body != timely->pos.bar) )
{
- timely->pos.bar_beat = bar_beat->body;
- if(timely->cb && (timely->mask & TIMELY_MASK_BAR_BEAT) )
- timely->cb(timely, frames, timely->urid.time_barBeat, (const LV2_Atom *)bar_beat, timely->data);
+ timely->pos.bar = bar->body;
+ if(timely->mask & TIMELY_MASK_BAR)
+ timely->cb(timely, frames, timely->urid.time_bar, timely->data);
}
- if(bar && (bar->body != timely->pos.bar) )
+ if(bar_beat)
{
- timely->pos.bar = bar->body;
- if(timely->cb && (timely->mask & TIMELY_MASK_BAR) )
- timely->cb(timely, frames, timely->urid.time_bar, (const LV2_Atom *)bar, timely->data);
+ const float _bar_beat = bar_beat->body * timely->multiplier;
+ if(_bar_beat != timely->pos.bar_beat)
+ {
+ timely->pos.bar_beat = _bar_beat;
+ 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->cb && (timely->mask & TIMELY_MASK_SPEED) )
- timely->cb(timely, frames, timely->urid.time_speed, (const LV2_Atom *)speed, timely->data);
+ 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;
+ const float speed = (timely->pos.speed != 0.f)
+ ? timely->pos.speed
+ : 1.f; // prevent divisions through zero later on
+
+ timely->frames_per_beat = 240.0 * timely->pos.frames_per_second
+ / (timely->pos.beats_per_minute * timely->pos.beat_unit * speed);
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
+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);
@@ -223,14 +260,7 @@ timely_init(timely_t *timely, LV2_URID_Map *map, double rate,
timely->urid.time_framesPerSecond = map->map(map->handle, LV2_TIME__framesPerSecond);
timely->urid.time_speed = map->map(map->handle, LV2_TIME__speed);
- timely->atom.i.atom.size = sizeof(int32_t);
- timely->atom.i.atom.type = map->map(map->handle, LV2_ATOM__Int);
-
- timely->atom.h.atom.size = sizeof(int64_t);
- timely->atom.h.atom.type = map->map(map->handle, LV2_ATOM__Long);
-
- timely->atom.f.atom.size = sizeof(float);
- timely->atom.f.atom.type = map->map(map->handle, LV2_ATOM__Float);
+ timely->multiplier = 1.f;
timely->pos.speed = 0.f;
timely->pos.bar_beat = 0.f;
@@ -240,130 +270,136 @@ timely_init(timely_t *timely, LV2_URID_Map *map, double rate,
timely->pos.beats_per_minute = 120.f;
timely->pos.frame = 0;
timely->pos.frames_per_second = rate;
-
+
_timely_refresh(timely);
timely->first = true;
}
-static void
-timely_advance(timely_t *timely, const LV2_Atom_Object *obj,
- uint32_t from, uint32_t to)
+static inline void
+timely_set_multiplier(timely_t *timely, float multiplier)
+{
+ const float mul = multiplier / timely->multiplier;
+
+ timely->pos.bar_beat *= mul;
+ timely->pos.beat_unit *= mul;
+ timely->pos.beats_per_bar *= mul;
+
+ timely->multiplier = multiplier;
+
+ _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->cb)
- {
- if(timely->mask & TIMELY_MASK_SPEED)
- {
- timely->atom.f.body = timely->pos.speed;
- timely->cb(timely, 0, timely->urid.time_speed, (const LV2_Atom *)&timely->atom.f, timely->data);
- }
+ if(timely->mask & TIMELY_MASK_SPEED)
+ timely->cb(timely, 0, timely->urid.time_speed, timely->data);
- if(timely->mask & TIMELY_MASK_BEAT_UNIT)
- {
- timely->atom.i.body = timely->pos.beat_unit;
- timely->cb(timely, 0, timely->urid.time_beatUnit, (const LV2_Atom *)&timely->atom.i, 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->atom.f.body = timely->pos.beats_per_bar;
- timely->cb(timely, 0, timely->urid.time_beatsPerBar, (const LV2_Atom *)&timely->atom.f, timely->data);
- }
-
- if(timely->mask & TIMELY_MASK_BEATS_PER_MINUTE)
- {
- timely->atom.f.body = timely->pos.beats_per_minute;
- timely->cb(timely, 0, timely->urid.time_beatsPerMinute, (const LV2_Atom *)&timely->atom.f, 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_FRAME)
- {
- timely->atom.h.body = timely->pos.frame;
- timely->cb(timely, 0, timely->urid.time_frame, (const LV2_Atom *)&timely->atom.h, timely->data);
- }
-
- if(timely->mask & TIMELY_MASK_FRAMES_PER_SECOND)
- {
- timely->atom.f.body = timely->pos.frames_per_second;
- timely->cb(timely, 0, timely->urid.time_framesPerSecond, (const LV2_Atom *)&timely->atom.f, timely->data);
- }
-
- if(timely->mask & TIMELY_MASK_BAR_BEAT)
- {
- timely->atom.f.body = timely->pos.bar_beat;
- timely->cb(timely, 0, timely->urid.time_barBeat, (const LV2_Atom *)&timely->atom.f, timely->data);
- }
-
- if(timely->mask & TIMELY_MASK_BAR)
- {
- timely->atom.h.body = timely->pos.bar;
- timely->cb(timely, 0, timely->urid.time_bar, (const LV2_Atom *)&timely->atom.h, 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->pos.speed != 0.f)
{
- if( (timely->offset.beat == 0) && (timely->pos.bar_beat == 0) )
+ if( (timely->offset.bar == 0) && (timely->pos.bar == 0) )
{
- timely->atom.f.body = timely->pos.bar_beat;
- if(timely->cb && (timely->mask & TIMELY_MASK_BAR_BEAT) )
- timely->cb(timely, from, timely->urid.time_barBeat, (const LV2_Atom *)&timely->atom.f, timely->data);
+ if(timely->mask & (TIMELY_MASK_BAR | TIMELY_MASK_BAR_WHOLE) )
+ timely->cb(timely, from, timely->urid.time_bar, timely->data);
}
-
- if( (timely->offset.bar == 0) && (timely->pos.bar == 0) )
+
+ if( (timely->offset.beat == 0) && (timely->pos.bar_beat == 0) )
{
- timely->atom.h.body = timely->pos.bar;
- if(timely->cb && (timely->mask & TIMELY_MASK_BAR) )
- timely->cb(timely, from, timely->urid.time_bar, (const LV2_Atom *)&timely->atom.h, timely->data);
+ 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.beat >= timely->window.beat) )
+ if(timely->offset.bar >= timely->frames_per_bar)
{
- timely->pos.bar_beat = floor(timely->pos.bar_beat) + 1;
- timely->offset.beat -= timely->window.beat;
+ timely->pos.bar += 1;
+ timely->offset.bar -= timely->frames_per_bar;
- if(timely->pos.bar_beat >= timely->pos.beats_per_bar)
- timely->pos.bar_beat = 0;
+ if(timely->mask & TIMELY_MASK_FRAME)
+ timely->cb(timely, (update_frame = i), timely->urid.time_frame, timely->data);
- timely->atom.f.body = timely->pos.bar_beat;
- if(timely->cb && (timely->mask & TIMELY_MASK_BAR_BEAT) )
- timely->cb(timely, i, timely->urid.time_barBeat, (const LV2_Atom *)&timely->atom.f, timely->data);
+ if(timely->mask & TIMELY_MASK_BAR_WHOLE)
+ timely->cb(timely, i, timely->urid.time_bar, timely->data);
}
- if(timely->offset.bar >= timely->window.bar)
+ if( (timely->offset.beat >= timely->frames_per_beat) )
{
- timely->pos.bar += 1;
- timely->offset.bar -= timely->window.bar;
+ timely->pos.bar_beat = floor(timely->pos.bar_beat) + 1;
+ timely->offset.beat -= timely->frames_per_beat;
- timely->atom.h.body = timely->pos.bar;
- if(timely->cb && (timely->mask & TIMELY_MASK_BAR) )
- timely->cb(timely, i, timely->urid.time_bar, (const LV2_Atom *)&timely->atom.h, timely->data);
+ 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;
}
-
- timely->pos.frame += to - from;
}
// is this a time position event?
- if( obj
- && (obj->atom.type == timely->urid.atom_object)
- && (obj->body.otype == timely->urid.time_position) )
+ 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(timely, to, obj);
+ _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_