aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--VERSION2
-rw-r--r--manifest.ttl.in8
-rw-r--r--meson.build7
-rw-r--r--orbit.c2
-rw-r--r--orbit.h4
-rw-r--r--orbit.ttl111
-rw-r--r--orbit_monitor.c331
8 files changed, 465 insertions, 5 deletions
diff --git a/README.md b/README.md
index 225b233..92a0fb3 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,11 @@ Quantizes incoming events to whole beats.
Subdivide or multiply incoming time signals by whole fractions, e.g. to
speed up time x2, x3, ... or slow it down to x1/2, x1/3, ...
+#### Monitor
+
+Break out transport events to parameters, e.g. to get BPM, etc. and automate
+other plugins with those values.
+
#### Timecapsule
Record/Playback of arbitrary LV2 atoms to/from memory. Record all incoming atom
diff --git a/VERSION b/VERSION
index a7083df..2390f59 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.599
+0.1.603
diff --git a/manifest.ttl.in b/manifest.ttl.in
index e1597d5..5152a67 100644
--- a/manifest.ttl.in
+++ b/manifest.ttl.in
@@ -67,6 +67,14 @@ orbit:subspace
lv2:binary <orbit@MODULE_SUFFIX@> ;
rdfs:seeAlso <orbit.ttl> .
+# Orbit Monitor
+orbit:monitor
+ a lv2:Plugin ;
+ lv2:minorVersion @MINOR_VERSION@ ;
+ lv2:microVersion @MICRO_VERSION@ ;
+ lv2:binary <orbit@MODULE_SUFFIX@> ;
+ rdfs:seeAlso <orbit.ttl> .
+
# Orbit Quantum
orbit:quantum
a lv2:Plugin ;
diff --git a/meson.build b/meson.build
index f9449ab..985a0f3 100644
--- a/meson.build
+++ b/meson.build
@@ -5,6 +5,9 @@ project('orbit.lv2', 'c', default_options : [
'b_lto=false',
'c_std=c11'])
+source_root = meson.source_root()
+build_root = meson.build_root()
+
add_project_arguments('-D_GNU_SOURCE', language : 'c')
conf_data = configuration_data()
@@ -37,6 +40,7 @@ dsp_srcs = ['orbit.c',
'orbit_pacemaker.c',
'orbit_quantum.c',
'orbit_subspace.c',
+ 'orbit_monitor.c',
'orbit_timecapsule.c']
c_args = ['-fvisibility=hidden',
@@ -85,12 +89,13 @@ endif
if lv2lint.found()
test('LV2 lint', lv2lint,
- args : ['-Swarn', # -Ewarn fails due to linking against libz
+ args : ['-I', join_paths(build_root, ''),
'http://open-music-kontrollers.ch/lv2/orbit#beatbox',
'http://open-music-kontrollers.ch/lv2/orbit#click',
'http://open-music-kontrollers.ch/lv2/orbit#looper',
'http://open-music-kontrollers.ch/lv2/orbit#pacemaker',
'http://open-music-kontrollers.ch/lv2/orbit#quantum',
'http://open-music-kontrollers.ch/lv2/orbit#subspace',
+ 'http://open-music-kontrollers.ch/lv2/orbit#monitor',
'http://open-music-kontrollers.ch/lv2/orbit#timecapsule'])
endif
diff --git a/orbit.c b/orbit.c
index ef7b031..9fad60f 100644
--- a/orbit.c
+++ b/orbit.c
@@ -36,6 +36,8 @@ lv2_descriptor(uint32_t index)
return &orbit_timecapsule;
case 6:
return &orbit_quantum;
+ case 7:
+ return &orbit_monitor;
default:
return NULL;
}
diff --git a/orbit.h b/orbit.h
index c7eb384..abe1120 100644
--- a/orbit.h
+++ b/orbit.h
@@ -47,7 +47,7 @@
#define ORBIT_PACEMAKER_URI ORBIT_URI"#pacemaker"
#define ORBIT_BEATBOX_URI ORBIT_URI"#beatbox"
#define ORBIT_SUBSPACE_URI ORBIT_URI"#subspace"
-#define ORBIT_CARGOSHIP_URI ORBIT_URI"#cargoship"
+#define ORBIT_MONITOR_URI ORBIT_URI"#monitor"
#define ORBIT_TIMECAPSULE_URI ORBIT_URI"#timecapsule"
#define ORBIT_QUANTUM_URI ORBIT_URI"#quantum"
@@ -56,7 +56,7 @@ extern const LV2_Descriptor orbit_click;
extern const LV2_Descriptor orbit_pacemaker;
extern const LV2_Descriptor orbit_beatbox;
extern const LV2_Descriptor orbit_subspace;
-extern const LV2_Descriptor orbit_cargoship;
+extern const LV2_Descriptor orbit_monitor;
extern const LV2_Descriptor orbit_timecapsule;
extern const LV2_Descriptor orbit_quantum;
diff --git a/orbit.ttl b/orbit.ttl
index a1f8beb..c1dd385 100644
--- a/orbit.ttl
+++ b/orbit.ttl
@@ -508,7 +508,7 @@ orbit:beatbox
orbit:beatbox_beat_channel 0 ;
] .
-# Click Subspace
+# Orbit Subspace
orbit:subspace_mode
a lv2:Parameter ;
rdfs:range atom:Int ;
@@ -569,6 +569,115 @@ orbit:subspace
orbit:subspace_factor 1;
] .
+# Orbit Monitor
+orbit:monitor_barBeat
+ a lv2:Parameter ;
+ rdfs:label "barBeat" ;
+ rdfs:comment "get bar beat of transport" ;
+ lv2:minimum 0.0 ;
+ lv2:maximum 128.0 ;
+ units:unit units:beat ;
+ rdfs:range atom:Float .
+orbit:monitor_bar
+ a lv2:Parameter ;
+ rdfs:label "bar" ;
+ rdfs:comment "get bar of transport" ;
+ lv2:minimum 0 ;
+ lv2:maximum 2147483647 ;
+ units:unit units:bar ;
+ rdfs:range atom:Long .
+orbit:monitor_beatUnit
+ a lv2:Parameter ;
+ rdfs:label "beatUnit" ;
+ rdfs:comment "get beat unit of transport" ;
+ lv2:minimum 1 ;
+ lv2:maximum 128 ;
+ rdfs:range atom:Int .
+orbit:monitor_beatsPerBar
+ a lv2:Parameter ;
+ rdfs:label "beatsPerBar" ;
+ rdfs:comment "get beats per bar of transport" ;
+ lv2:minimum 1.0 ;
+ lv2:maximum 128.0 ;
+ units:unit units:beat ;
+ rdfs:range atom:Float .
+orbit:monitor_beatsPerMinute
+ a lv2:Parameter ;
+ rdfs:label "beatsPerMinute" ;
+ rdfs:comment "get beats per minute of transport" ;
+ lv2:minimum 1.0 ;
+ lv2:maximum 800.0 ;
+ units:unit units:bpm ;
+ rdfs:range atom:Float .
+orbit:monitor_frame
+ a lv2:Parameter ;
+ rdfs:label "frame" ;
+ rdfs:comment "get frame of transport" ;
+ lv2:minimum 1 ;
+ lv2:maximum 2147483647 ;
+ rdfs:range atom:Long .
+orbit:monitor_framesPerSecond
+ a lv2:Parameter ;
+ rdfs:label "framesPerSecond" ;
+ rdfs:comment "get frames per second of transport" ;
+ lv2:minimum 1.0 ;
+ lv2:maximum 192000.0 ;
+ units:unit units:hz ;
+ rdfs:range atom:Float .
+orbit:monitor_speed
+ a lv2:Parameter ;
+ rdfs:label "speed" ;
+ rdfs:comment "get speed of transport" ;
+ lv2:minimum -1.0 ;
+ lv2:maximum 1.0 ;
+ rdfs:range atom:Float .
+
+orbit:monitor
+ a lv2:Plugin ,
+ lv2:ConverterPlugin ;
+ doap:name "Orbit Monitor" ;
+ doap:license lic:Artistic-2.0 ;
+ lv2:project proj:orbit ;
+ lv2:requiredFeature urid:map, state:loadDefaultState ;
+ lv2:optionalFeature lv2:isLive, lv2:hardRTCapable, state:threadSafeRestore, log:log ;
+ lv2:extensionData state:interface ;
+
+ lv2:port [
+ # sink event port
+ a lv2:InputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports time:Position ;
+ atom:supports patch:Message;
+ lv2:index 0 ;
+ lv2:symbol "event_in" ;
+ lv2:name "Time Input" ;
+ lv2:designation lv2:control ;
+ ] , [
+ # source event port
+ a lv2:OutputPort ,
+ atom:AtomPort ;
+ atom:bufferType atom:Sequence ;
+ atom:supports patch:Message;
+ lv2:index 1 ;
+ lv2:symbol "notify" ;
+ lv2:name "Notify" ;
+ lv2:designation lv2:control ;
+ ] ;
+
+ patch:readable
+ orbit:monitor_barBeat ,
+ orbit:monitor_bar ,
+ orbit:monitor_beatUnit ,
+ orbit:monitor_beatsPerBar ,
+ orbit:monitor_beatsPerMinute ,
+ orbit:monitor_frame ,
+ orbit:monitor_framesPerSecond ,
+ orbit:monitor_speed ;
+
+ state:state [
+ ] .
+
# TimeCapsule Plugin
orbit:timecapsule_mute
a lv2:Parameter ;
diff --git a/orbit_monitor.c b/orbit_monitor.c
new file mode 100644
index 0000000..9544202
--- /dev/null
+++ b/orbit_monitor.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <orbit.h>
+#include <timely.h>
+#include <props.h>
+
+#define MAX_NPROPS 8
+
+typedef struct _plugstate_t plugstate_t;
+typedef struct _plughandle_t plughandle_t;
+
+struct _plugstate_t {
+ 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;
+};
+
+enum {
+ MODE_MULTIPLY = 0,
+ MODE_DIVIDE = 1
+};
+
+struct _plughandle_t {
+ LV2_URID_Map *map;
+ LV2_Atom_Forge forge;
+ LV2_Atom_Forge_Ref ref;
+
+ LV2_Log_Log *log;
+ LV2_Log_Logger logger;
+
+ 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;
+
+ timely_t timely;
+
+ const LV2_Atom_Sequence *event_in;
+ LV2_Atom_Sequence *event_out;
+
+ plugstate_t state;
+ plugstate_t stash;
+
+ PROPS_T(props, MAX_NPROPS);
+};
+
+static const props_def_t defs [MAX_NPROPS] = {
+ {
+ .property = ORBIT_URI"#monitor_barBeat",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, bar_beat),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = ORBIT_URI"#monitor_bar",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, bar),
+ .type = LV2_ATOM__Long
+ },
+ {
+ .property = ORBIT_URI"#monitor_beatUnit",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, beat_unit),
+ .type = LV2_ATOM__Int
+ },
+ {
+ .property = ORBIT_URI"#monitor_beatsPerBar",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, beats_per_bar),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = ORBIT_URI"#monitor_beatsPerMinute",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, beats_per_minute),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = ORBIT_URI"#monitor_frame",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, frame),
+ .type = LV2_ATOM__Long
+ },
+ {
+ .property = ORBIT_URI"#monitor_framesPerSecond",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, frames_per_second),
+ .type = LV2_ATOM__Float
+ },
+ {
+ .property = ORBIT_URI"#monitor_speed",
+ .access = LV2_PATCH__readable,
+ .offset = offsetof(plugstate_t, speed),
+ .type = LV2_ATOM__Float
+ },
+};
+
+static void
+_cb(timely_t *timely, int64_t frames, LV2_URID type, void *data)
+{
+ plughandle_t *handle = data;
+
+ // do nothing
+}
+
+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;
+ mlock(handle, sizeof(plughandle_t));
+
+ 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)
+ lv2_log_logger_init(&handle->logger, handle->map, handle->log);
+
+ const timely_mask_t mask = 0;
+ timely_init(&handle->timely, handle->map, rate, mask, _cb, handle);
+ lv2_atom_forge_init(&handle->forge, handle->map);
+
+ if(!props_init(&handle->props, descriptor->URI,
+ defs, MAX_NPROPS, &handle->state, &handle->stash,
+ handle->map, handle))
+ {
+ fprintf(stderr, "failed to initialize property structure\n");
+ free(handle);
+ return NULL;
+ }
+
+ handle->time_barBeat = props_map(&handle->props, ORBIT_URI"#monitor_barBeat");
+ handle->time_bar = props_map(&handle->props, ORBIT_URI"#monitor_bar");
+ handle->time_beatUnit = props_map(&handle->props, ORBIT_URI"#monitor_beatUnit");
+ handle->time_beatsPerBar = props_map(&handle->props, ORBIT_URI"#monitor_beatsPerBar");
+ handle->time_beatsPerMinute = props_map(&handle->props, ORBIT_URI"#monitor_beatsPerMinute");
+ handle->time_frame = props_map(&handle->props, ORBIT_URI"#monitor_frame");
+ handle->time_framesPerSecond = props_map(&handle->props, ORBIT_URI"#monitor_framesPerSecond");
+ handle->time_speed = props_map(&handle->props, ORBIT_URI"#monitor_speed");
+
+ return handle;
+}
+
+static void
+connect_port(LV2_Handle instance, uint32_t port, void *data)
+{
+ plughandle_t *handle = (plughandle_t *)instance;
+
+ switch(port)
+ {
+ case 0:
+ handle->event_in = (const LV2_Atom_Sequence *)data;
+ break;
+ case 1:
+ handle->event_out = (LV2_Atom_Sequence *)data;
+ break;
+ default:
+ break;
+ }
+}
+
+static inline void
+_position_atomize(plughandle_t *handle, LV2_Atom_Forge *forge, int64_t frames,
+ LV2_Atom_Forge_Ref *ref)
+{
+ timely_t *timely = &handle->timely;
+
+ handle->state.bar_beat = TIMELY_BAR_BEAT(timely);
+ handle->state.bar = TIMELY_BAR(timely);
+ handle->state.beat_unit = TIMELY_BEAT_UNIT(timely);
+ handle->state.beats_per_bar = TIMELY_BEATS_PER_BAR(timely);
+ handle->state.beats_per_minute = TIMELY_BEATS_PER_MINUTE(timely);
+ handle->state.frame = TIMELY_FRAME(timely);
+ handle->state.frames_per_second = TIMELY_FRAMES_PER_SECOND(timely);
+ handle->state.speed = TIMELY_SPEED(timely);
+
+ props_set(&handle->props, forge, frames, handle->time_barBeat, ref);
+ props_set(&handle->props, forge, frames, handle->time_bar, ref);
+ props_set(&handle->props, forge, frames, handle->time_beatUnit, ref);
+ props_set(&handle->props, forge, frames, handle->time_beatsPerBar, ref);
+ props_set(&handle->props, forge, frames, handle->time_beatsPerMinute, ref);
+ props_set(&handle->props, forge, frames, handle->time_frame, ref);
+ props_set(&handle->props, forge, frames, handle->time_framesPerSecond, ref);
+ props_set(&handle->props, forge, frames, handle->time_speed, ref);
+}
+
+static void
+run(LV2_Handle instance, uint32_t nsamples)
+{
+ plughandle_t *handle = instance;
+ uint32_t last_t = 0;
+
+ const uint32_t capacity = handle->event_out->atom.size;
+ LV2_Atom_Forge_Frame frame;
+ lv2_atom_forge_set_buffer(&handle->forge, (uint8_t *)handle->event_out, capacity);
+ handle->ref = lv2_atom_forge_sequence_head(&handle->forge, &frame, 0);
+
+ props_idle(&handle->props, &handle->forge, 0, &handle->ref);
+
+ LV2_ATOM_SEQUENCE_FOREACH(handle->event_in, ev)
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)&ev->body;
+
+ if(timely_advance(&handle->timely, obj, last_t, ev->time.frames))
+ {
+ // nothing to do
+ }
+ else
+ {
+ props_advance(&handle->props, &handle->forge, ev->time.frames, obj, &handle->ref);
+ }
+
+ last_t = ev->time.frames;
+ }
+
+ timely_advance(&handle->timely, NULL, last_t, nsamples);
+
+ _position_atomize(handle, &handle->forge, nsamples - 1, &handle->ref);
+
+ if(handle->ref)
+ {
+ lv2_atom_forge_pop(&handle->forge, &frame);
+ }
+ else
+ {
+ lv2_atom_sequence_clear(handle->event_out);
+
+ if(handle->log)
+ lv2_log_trace(&handle->logger, "forge buffer overflow\n");
+ }
+}
+
+static void
+cleanup(LV2_Handle instance)
+{
+ plughandle_t *handle = instance;
+
+ munlock(handle, sizeof(plughandle_t));
+ free(handle);
+}
+
+static LV2_State_Status
+_state_save(LV2_Handle instance, LV2_State_Store_Function store,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_save(&handle->props, store, state, flags, features);
+}
+
+static LV2_State_Status
+_state_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve,
+ LV2_State_Handle state, uint32_t flags,
+ const LV2_Feature *const *features)
+{
+ plughandle_t *handle = instance;
+
+ return props_restore(&handle->props, retrieve, state, flags, features);
+}
+
+static const LV2_State_Interface state_iface = {
+ .save = _state_save,
+ .restore = _state_restore
+};
+
+static const void*
+extension_data(const char* uri)
+{
+ if(!strcmp(uri, LV2_STATE__interface))
+ return &state_iface;
+
+ return NULL;
+}
+
+const LV2_Descriptor orbit_monitor = {
+ .URI = ORBIT_MONITOR_URI,
+ .instantiate = instantiate,
+ .connect_port = connect_port,
+ .activate = NULL,
+ .run = run,
+ .deactivate = NULL,
+ .cleanup = cleanup,
+ .extension_data = extension_data
+};