summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2016-03-13 23:20:45 +0100
committerRobin Gareus <robin@gareus.org>2016-03-14 00:17:14 +0100
commit58469214befaa714c856790b78da58c4593b2b54 (patch)
treeea1bde9d92a9ef964072d3801d4c16a555c3bd9a /libs
parent5fa4cf996bc9aad51d97a825f07cc23397437024 (diff)
prototype online self-automating LV2 plugin interface
goes along with https://github.com/x42/automate.lv2
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/lv2_plugin.h29
-rw-r--r--libs/ardour/ardour/plugin.h3
-rw-r--r--libs/ardour/ardour/uri_map.h9
-rw-r--r--libs/ardour/lv2_plugin.cc146
-rw-r--r--libs/ardour/plugin_insert.cc4
-rw-r--r--libs/ardour/uri_map.cc10
6 files changed, 195 insertions, 6 deletions
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h
index a4cdfcd036..aacef82e5b 100644
--- a/libs/ardour/ardour/lv2_plugin.h
+++ b/libs/ardour/ardour/lv2_plugin.h
@@ -30,6 +30,10 @@
#include "ardour/worker.h"
#include "pbd/ringbuffer.h"
+#ifdef LV2_EXTENDED // -> needs to eventually go upstream to lv2plug.in
+#include "ardour/lv2_extensions.h"
+#endif
+
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
@@ -91,6 +95,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
const LV2_Feature* const* features () { return _features; }
std::set<Evoral::Parameter> automatable () const;
+ virtual void set_automation_control (uint32_t, boost::shared_ptr<AutomationControl>);
void activate ();
void deactivate ();
@@ -182,6 +187,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
uint32_t _patch_port_out_index;
URIMap& _uri_map;
bool _no_sample_accurate_ctrl;
+ bool _can_write_automation;
friend const void* lv2plugin_get_port_value(const char* port_symbol,
void* user_data,
@@ -197,7 +203,9 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
PORT_SEQUENCE = 1 << 5, ///< New atom API event port
PORT_MIDI = 1 << 6, ///< Event port understands MIDI
PORT_POSITION = 1 << 7, ///< Event port understands position
- PORT_PATCHMSG = 1 << 8 ///< Event port supports patch:Message
+ PORT_PATCHMSG = 1 << 8, ///< Event port supports patch:Message
+ PORT_AUTOCTRL = 1 << 9, ///< Event port supports auto:AutomationControl
+ PORT_CTRLED = 1 << 10 ///< Port prop auto:AutomationControlled (can be self controlled)
} PortFlag;
typedef unsigned PortFlags;
@@ -208,6 +216,25 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
PropertyDescriptors _property_descriptors;
+ struct AutomationCtrl {
+ AutomationCtrl (const AutomationCtrl &other)
+ : ac (other.ac)
+ , guard (other.guard)
+ { }
+
+ AutomationCtrl (boost::shared_ptr<ARDOUR::AutomationControl> c)
+ : ac (c)
+ , guard (false)
+ { }
+ boost::shared_ptr<ARDOUR::AutomationControl> ac;
+ bool guard;
+ };
+
+ typedef boost::shared_ptr<AutomationCtrl> AutomationCtrlPtr;
+ typedef std::map<uint32_t, AutomationCtrlPtr> AutomationCtrlMap;
+ AutomationCtrlMap _ctrl_map;
+ AutomationCtrlPtr get_automation_control (uint32_t);
+
/// Message send to/from UI via ports
struct UIMessage {
uint32_t index;
diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h
index 0ce5522c7c..0bc766c462 100644
--- a/libs/ardour/ardour/plugin.h
+++ b/libs/ardour/ardour/plugin.h
@@ -49,6 +49,7 @@ class BufferSet;
class PluginInsert;
class Plugin;
class PluginInfo;
+class AutomationControl;
typedef boost::shared_ptr<Plugin> PluginPtr;
typedef boost::shared_ptr<PluginInfo> PluginInfoPtr;
@@ -100,6 +101,8 @@ class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public Latent
virtual bool parameter_is_input(uint32_t) const = 0;
virtual bool parameter_is_output(uint32_t) const = 0;
+ virtual void set_automation_control (uint32_t /*port_index*/, boost::shared_ptr<ARDOUR::AutomationControl>) { }
+
virtual boost::shared_ptr<ScalePoints> get_scale_points(uint32_t /*port_index*/) const {
return boost::shared_ptr<ScalePoints>();
}
diff --git a/libs/ardour/ardour/uri_map.h b/libs/ardour/ardour/uri_map.h
index d745ad58e7..523eb18e91 100644
--- a/libs/ardour/ardour/uri_map.h
+++ b/libs/ardour/ardour/uri_map.h
@@ -83,6 +83,15 @@ public:
uint32_t patch_Set;
uint32_t patch_property;
uint32_t patch_value;
+#ifdef LV2_EXTENDED
+ uint32_t auto_event;
+ uint32_t auto_setup;
+ uint32_t auto_finalize;
+ uint32_t auto_start;
+ uint32_t auto_end;
+ uint32_t auto_parameter;
+ uint32_t auto_value;
+#endif
};
URIDs urids;
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
index 8dde93f3b3..4af6748c41 100644
--- a/libs/ardour/lv2_plugin.cc
+++ b/libs/ardour/lv2_plugin.cc
@@ -155,13 +155,25 @@ public:
LilvNode* units_midiNote;
LilvNode* patch_writable;
LilvNode* patch_Message;
- LilvNode* lv2_noSampleAccurateCtrl;
#ifdef HAVE_LV2_1_2_0
LilvNode* bufz_powerOf2BlockLength;
LilvNode* bufz_fixedBlockLength;
LilvNode* bufz_nominalBlockLength;
#endif
+#ifdef HAVE_LV2_1_10_0
+ LilvNode* atom_int;
+ LilvNode* atom_float;
+ LilvNode* atom_object; // new in 1.8
+ LilvNode* atom_vector;
+#endif
+#ifdef LV2_EXTENDED
+ LilvNode* lv2_noSampleAccurateCtrl;
+ LilvNode* auto_can_write_automatation; // lv2:optionalFeature
+ LilvNode* auto_automation_control; // atom:supports
+ LilvNode* auto_automation_controlled; // lv2:portProperty
+#endif
+
private:
bool _bundle_checked;
};
@@ -335,6 +347,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
_state_version = 0;
_was_activated = false;
_has_state_interface = false;
+ _can_write_automation = false;
_impl->block_length = _session.get_block_size();
_instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access";
@@ -484,11 +497,16 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
throw failed_constructor();
}
lilv_nodes_free(required_features);
+#endif
+#ifdef LV2_EXTENDED
LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin);
if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) {
_no_sample_accurate_ctrl = true;
}
+ if (lilv_nodes_contains (optional_features, _world.auto_can_write_automatation)) {
+ _can_write_automation = true;
+ }
lilv_nodes_free(optional_features);
#endif
@@ -542,6 +560,11 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
if (lilv_nodes_contains(atom_supports, _world.time_Position)) {
flags |= PORT_POSITION;
}
+#ifdef LV2_EXTENDED
+ if (lilv_nodes_contains(atom_supports, _world.auto_automation_control)) {
+ flags |= PORT_AUTOCTRL;
+ }
+#endif
if (lilv_nodes_contains(atom_supports, _world.patch_Message)) {
flags |= PORT_PATCHMSG;
if (flags & PORT_INPUT) {
@@ -566,6 +589,14 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
throw failed_constructor();
}
+#ifdef LV2_EXTENDED
+ if (lilv_port_has_property(_impl->plugin, port, _world.auto_automation_controlled)) {
+ if ((flags & PORT_INPUT) && (flags & PORT_CONTROL)) {
+ flags |= PORT_CTRLED;
+ }
+ }
+#endif
+
_port_flags.push_back(flags);
_port_minimumSize.push_back(minimumSize);
}
@@ -1904,6 +1935,23 @@ LV2Plugin::automatable() const
}
void
+LV2Plugin::set_automation_control (uint32_t i, boost::shared_ptr<AutomationControl> c)
+{
+ if ((_port_flags[i] & PORT_CTRLED)) {
+ _ctrl_map [i] = AutomationCtrlPtr (new AutomationCtrl(c));
+ }
+}
+
+LV2Plugin::AutomationCtrlPtr
+LV2Plugin::get_automation_control (uint32_t i)
+{
+ if (_ctrl_map.find (i) == _ctrl_map.end()) {
+ return AutomationCtrlPtr ();
+ }
+ return _ctrl_map[i];
+}
+
+void
LV2Plugin::activate()
{
DEBUG_TRACE(DEBUG::LV2, string_compose("%1 activate\n", name()));
@@ -2074,6 +2122,15 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
*_bpm_control_port = tmetric.tempo().beats_per_minute();
}
+#ifdef LV2_EXTENDED
+ if (_can_write_automation && _session.transport_frame() != _next_cycle_start) {
+ // add guard-points after locating
+ for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) {
+ i->second->guard = true;
+ }
+ }
+#endif
+
ChanCount bufs_count;
bufs_count.set(DataType::AUDIO, 1);
bufs_count.set(DataType::MIDI, 1);
@@ -2258,9 +2315,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
}
}
-
// Write messages to UI
- if ((_to_ui || _patch_port_out_index != (uint32_t)-1) &&
+ if ((_to_ui || _can_write_automation || _patch_port_out_index != (uint32_t)-1) &&
(flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
LV2_Evbuf* buf = _ev_buffers[port_index];
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf);
@@ -2270,6 +2326,78 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
uint8_t* data;
lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
+#ifdef LV2_EXTENDED
+ // Intercept Automation Write Events
+ if ((flags & PORT_AUTOCTRL)) {
+ LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
+ if (atom->type == _uri_map.urids.atom_Blank ||
+ atom->type == _uri_map.urids.atom_Object) {
+ LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
+ if (obj->body.otype == _uri_map.urids.auto_event) {
+ // only if transport_rolling ??
+ const LV2_Atom* parameter = NULL;
+ const LV2_Atom* value = NULL;
+ lv2_atom_object_get(obj,
+ _uri_map.urids.auto_parameter, &parameter,
+ _uri_map.urids.auto_value, &value,
+ 0);
+ if (parameter && value) {
+ const uint32_t p = ((const LV2_Atom_Int*)parameter)->body;
+ const float v = ((const LV2_Atom_Float*)value)->body;
+ // -> add automation event..
+ AutomationCtrlPtr c = get_automation_control (p);
+ if (c && c->ac->automation_state() == Touch) {
+ if (c->guard) {
+ c->guard = false;
+ c->ac->list()->add (_session.transport_frame() + frames, v, true, true);
+ } else {
+ c->ac->set_double (v, _session.transport_frame() + frames, true);
+ }
+ }
+ }
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_setup) {
+ // TODO optional arguments, for now we assume the plugin
+ // writes automation for its own inputs
+ // -> put them in "touch" mode (preferably "exclusive plugin touch(TM)"
+ for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) {
+ i->second->ac->set_automation_state (Touch);
+ }
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_finalize) {
+ // set [touched] parameters to "play" ??
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_start) {
+ const LV2_Atom* parameter = NULL;
+ lv2_atom_object_get(obj,
+ _uri_map.urids.auto_parameter, &parameter,
+ 0);
+ if (parameter) {
+ const uint32_t p = ((const LV2_Atom_Int*)parameter)->body;
+ AutomationCtrlPtr c = get_automation_control (p);
+ if (c) {
+ c->ac->start_touch (_session.transport_frame());
+ c->guard = true;
+ }
+ }
+ }
+ else if (obj->body.otype == _uri_map.urids.auto_end) {
+ const LV2_Atom* parameter = NULL;
+ lv2_atom_object_get(obj,
+ _uri_map.urids.auto_parameter, &parameter,
+ 0);
+ if (parameter) {
+ const uint32_t p = ((const LV2_Atom_Int*)parameter)->body;
+ AutomationCtrlPtr c = get_automation_control (p);
+ if (c) {
+ c->ac->stop_touch (true, _session.transport_frame());
+ }
+ }
+ }
+ }
+ }
+#endif
+
// Intercept patch change messages to emit PropertyChanged signal
if ((flags & PORT_PATCHMSG)) {
LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
@@ -2525,7 +2653,12 @@ LV2World::LV2World()
units_db = lilv_new_uri(world, LV2_UNITS__db);
patch_writable = lilv_new_uri(world, LV2_PATCH__writable);
patch_Message = lilv_new_uri(world, LV2_PATCH__Message);
- lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "noSampleAccurateControls");
+#ifdef LV2_EXTENDED
+ lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "noSampleAccurateControls");
+ auto_can_write_automatation = lilv_new_uri(world, LV2_AUTOMATE_URI__can_write);
+ auto_automation_control = lilv_new_uri(world, LV2_AUTOMATE_URI__control);
+ auto_automation_controlled = lilv_new_uri(world, LV2_AUTOMATE_URI__controlled);
+#endif
#ifdef HAVE_LV2_1_2_0
bufz_powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength);
bufz_fixedBlockLength = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength);
@@ -2544,7 +2677,12 @@ LV2World::~LV2World()
lilv_node_free(bufz_fixedBlockLength);
lilv_node_free(bufz_powerOf2BlockLength);
#endif
+#ifdef LV2_EXTENDED
lilv_node_free(lv2_noSampleAccurateCtrl);
+ lilv_node_free(auto_can_write_automatation);
+ lilv_node_free(auto_automation_control);
+ lilv_node_free(auto_automation_controlled);
+#endif
lilv_node_free(patch_Message);
lilv_node_free(patch_writable);
lilv_node_free(units_hz);
diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc
index 30fe1c005d..c085f1f9cb 100644
--- a/libs/ardour/plugin_insert.cc
+++ b/libs/ardour/plugin_insert.cc
@@ -251,7 +251,9 @@ PluginInsert::create_automatable_parameters ()
can_automate (param);
boost::shared_ptr<AutomationList> list(new AutomationList(param, desc));
- add_control (boost::shared_ptr<AutomationControl> (new PluginControl(this, param, desc, list)));
+ boost::shared_ptr<AutomationControl> c (new PluginControl(this, param, desc, list));
+ add_control (c);
+ _plugins.front()->set_automation_control (i->id(), c);
} else if (i->type() == PluginPropertyAutomation) {
Evoral::Parameter param(*i);
const ParameterDescriptor& desc = _plugins.front()->get_property_descriptor(param.id());
diff --git a/libs/ardour/uri_map.cc b/libs/ardour/uri_map.cc
index 6fac103715..0bf6796547 100644
--- a/libs/ardour/uri_map.cc
+++ b/libs/ardour/uri_map.cc
@@ -27,6 +27,7 @@
#include "pbd/error.h"
#include "ardour/uri_map.h"
+#include "ardour/lv2_extensions.h"
namespace ARDOUR {
@@ -60,6 +61,15 @@ URIMap::URIDs::init(URIMap& uri_map)
patch_Set = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#Set");
patch_property = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#property");
patch_value = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#value");
+#ifdef LV2_EXTENDED
+ auto_event = uri_map.uri_to_id(LV2_AUTOMATE_URI__event);
+ auto_setup = uri_map.uri_to_id(LV2_AUTOMATE_URI__setup);
+ auto_finalize = uri_map.uri_to_id(LV2_AUTOMATE_URI__finalize);
+ auto_start = uri_map.uri_to_id(LV2_AUTOMATE_URI__start);
+ auto_end = uri_map.uri_to_id(LV2_AUTOMATE_URI__end);
+ auto_parameter = uri_map.uri_to_id(LV2_AUTOMATE_URI__parameter);
+ auto_value = uri_map.uri_to_id(LV2_AUTOMATE_URI__value);
+#endif
}
URIMap&