variable plugin port config.
[ardour.git] / libs / ardour / lv2_plugin.cc
index d98f9a9eccf961438d5aa22e002efbe899f24edb..43a4901ca7d5639a1619f54cda0270bad2f79577 100644 (file)
@@ -25,7 +25,7 @@
 #include <cstdlib>
 #include <cstring>
 
-#include <glib/gstdio.h>
+#include "pbd/gstdio_compat.h"
 #include <glib/gprintf.h>
 #include <glibmm.h>
 
@@ -132,6 +132,7 @@ public:
        LilvNode* lv2_enumeration;
        LilvNode* lv2_freewheeling;
        LilvNode* lv2_inPlaceBroken;
+       LilvNode* lv2_isSideChain;
        LilvNode* lv2_integer;
        LilvNode* lv2_default;
        LilvNode* lv2_minimum;
@@ -155,6 +156,24 @@ public:
        LilvNode* units_midiNote;
        LilvNode* patch_writable;
        LilvNode* patch_Message;
+#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;
@@ -198,6 +217,16 @@ work_respond(LV2_Worker_Respond_Handle handle,
        }
 }
 
+#ifdef LV2_EXTENDED
+/* inline display extension */
+static void
+queue_draw (LV2_Inline_Display_Handle handle)
+{
+       LV2Plugin* plugin = (LV2Plugin*)handle;
+       plugin->QueueDraw(); /* EMIT SIGNAL */
+}
+#endif
+
 /* log extension */
 
 static int
@@ -234,7 +263,17 @@ log_printf(LV2_Log_Handle handle,
 struct LV2Plugin::Impl {
        Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0)
               , work_iface(0)
+#ifdef HAVE_LV2_1_2_0
+              , opts_iface(0)
+#endif
               , state(0)
+              , block_length(0)
+#ifdef HAVE_LV2_1_2_0
+              , options(0)
+#endif
+#ifdef LV2_EXTENDED
+              , queue_draw(0)
+#endif
        {}
 
        /** Find the LV2 input port with the given designation.
@@ -242,16 +281,26 @@ struct LV2Plugin::Impl {
         */
        const LilvPort* designated_input (const char* uri, void** bufptrs[], void** bufptr);
 
-       const LilvPlugin*           plugin;
-       const LilvUI*               ui;
-       const LilvNode*             ui_type;
-       LilvNode*                   name;
-       LilvNode*                   author;
-       LilvInstance*               instance;
-       const LV2_Worker_Interface* work_iface;
-       LilvState*                  state;
-       LV2_Atom_Forge              forge;
-       LV2_Atom_Forge              ui_forge;
+       const LilvPlugin*            plugin;
+       const LilvUI*                ui;
+       const LilvNode*              ui_type;
+       LilvNode*                    name;
+       LilvNode*                    author;
+       LilvInstance*                instance;
+       const LV2_Worker_Interface*  work_iface;
+#ifdef HAVE_LV2_1_2_0
+       const LV2_Options_Interface* opts_iface;
+#endif
+       LilvState*                   state;
+       LV2_Atom_Forge               forge;
+       LV2_Atom_Forge               ui_forge;
+       int32_t                      block_length;
+#ifdef HAVE_LV2_1_2_0
+       LV2_Options_Option*          options;
+#endif
+#ifdef LV2_EXTENDED
+       LV2_Inline_Display*          queue_draw;
+#endif
 };
 
 LV2Plugin::LV2Plugin (AudioEngine& engine,
@@ -267,6 +316,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine,
        , _patch_port_in_index((uint32_t)-1)
        , _patch_port_out_index((uint32_t)-1)
        , _uri_map(URIMap::instance())
+       , _no_sample_accurate_ctrl (false)
 {
        init(c_plugin, rate);
 }
@@ -281,6 +331,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
        , _patch_port_in_index((uint32_t)-1)
        , _patch_port_out_index((uint32_t)-1)
        , _uri_map(URIMap::instance())
+       , _no_sample_accurate_ctrl (false)
 {
        init(other._impl->plugin, other._sample_rate);
 
@@ -309,11 +360,14 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _latency_control_port   = 0;
        _next_cycle_start       = std::numeric_limits<framepos_t>::max();
        _next_cycle_speed       = 1.0;
-       _block_length           = _engine.samples_per_cycle();
        _seq_size               = _engine.raw_buffer_size(DataType::MIDI);
        _state_version          = 0;
        _was_activated          = false;
        _has_state_interface    = false;
+       _can_write_automation   = false;
+       _max_latency            = 0;
+       _current_latency        = 0;
+       _impl->block_length     = _session.get_block_size();
 
        _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access";
        _data_access_feature.URI     = "http://lv2plug.in/ns/ext/data-access";
@@ -336,7 +390,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        lilv_node_free(state_uri);
        lilv_node_free(state_iface_uri);
 
-       _features    = (LV2_Feature**)calloc(11, sizeof(LV2_Feature*));
+       _features    = (LV2_Feature**)calloc(12, sizeof(LV2_Feature*));
        _features[0] = &_instance_access_feature;
        _features[1] = &_data_access_feature;
        _features[2] = &_make_path_feature;
@@ -353,20 +407,45 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        lv2_atom_forge_init(&_impl->forge, _uri_map.urid_map());
        lv2_atom_forge_init(&_impl->ui_forge, _uri_map.urid_map());
 
+#ifdef LV2_EXTENDED
+       _impl->queue_draw = (LV2_Inline_Display*)
+               malloc (sizeof(LV2_Inline_Display));
+       _impl->queue_draw->handle     = this;
+       _impl->queue_draw->queue_draw = queue_draw;
+
+       _queue_draw_feature.URI  = LV2_INLINEDISPLAY__queue_draw;
+       _queue_draw_feature.data = _impl->queue_draw;
+       _features[n_features++]  = &_queue_draw_feature;
+#endif
+
 #ifdef HAVE_LV2_1_2_0
        LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
+       static const int32_t _min_block_length = 1;   // may happen during split-cycles
+       static const int32_t _max_block_length = 8192; // max possible (with all engines and during export)
+       /* Consider updating max-block-size whenever the buffersize changes.
+        * It requires re-instantiating the plugin (which is a non-realtime operation),
+        * so it should be done lightly and only for plugins that require it.
+        *
+        * given that the block-size can change at any time (split-cycles) ardour currently
+        * does not support plugins that require bufz_fixedBlockLength.
+        */
        LV2_Options_Option options[] = {
                { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__minBlockLength),
-                 sizeof(int32_t), atom_Int, &_block_length },
+                 sizeof(int32_t), atom_Int, &_min_block_length },
                { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__maxBlockLength),
-                 sizeof(int32_t), atom_Int, &_block_length },
+                 sizeof(int32_t), atom_Int, &_max_block_length },
                { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__sequenceSize),
                  sizeof(int32_t), atom_Int, &_seq_size },
+               { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"),
+                 sizeof(int32_t), atom_Int, &_impl->block_length },
                { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL }
        };
 
+       _impl->options = (LV2_Options_Option*) malloc (sizeof (options));
+       memcpy ((void*) _impl->options, (void*) options, sizeof (options));
+
        _options_feature.URI    = LV2_OPTIONS__options;
-       _options_feature.data   = options;
+       _options_feature.data   = _impl->options;
        _features[n_features++] = &_options_feature;
 #endif
 
@@ -415,15 +494,57 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        }
        lilv_node_free(worker_iface_uri);
 
+
+#ifdef HAVE_LV2_1_2_0
+       LilvNode* options_iface_uri = lilv_new_uri(_world.world, LV2_OPTIONS__interface);
+       if (lilv_plugin_has_extension_data(plugin, options_iface_uri)) {
+               _impl->opts_iface = (const LV2_Options_Interface*)extension_data(
+                       LV2_OPTIONS__interface);
+       }
+       lilv_node_free(options_iface_uri);
+#endif
+
+#ifdef LV2_EXTENDED
+       _display_interface = (const LV2_Inline_Display_Interface*)
+               extension_data (LV2_INLINEDISPLAY__interface);
+#endif
+
        if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) {
                error << string_compose(
-                   _("LV2: \"%1\" cannot be used, since it cannot do inplace processing"),
+                   _("LV2: \"%1\" cannot be used, since it cannot do inplace processing."),
                    lilv_node_as_string(_impl->name)) << endmsg;
                lilv_node_free(_impl->name);
                lilv_node_free(_impl->author);
                throw failed_constructor();
        }
 
+#ifdef HAVE_LV2_1_2_0
+       LilvNodes *required_features = lilv_plugin_get_required_features (plugin);
+       if (lilv_nodes_contains (required_features, _world.bufz_powerOf2BlockLength) ||
+                       lilv_nodes_contains (required_features, _world.bufz_fixedBlockLength)
+          ) {
+               error << string_compose(
+                   _("LV2: \"%1\" buffer-size requirements cannot be satisfied."),
+                   lilv_node_as_string(_impl->name)) << endmsg;
+               lilv_node_free(_impl->name);
+               lilv_node_free(_impl->author);
+               lilv_nodes_free(required_features);
+               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
+
 #ifdef HAVE_LILV_0_16_0
        // Load default state
        LilvState* state = lilv_state_new_from_world(
@@ -431,6 +552,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        if (state && _has_state_interface) {
                lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL);
        }
+       lilv_state_free(state);
 #endif
 
        _sample_rate = rate;
@@ -473,6 +595,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) {
@@ -497,6 +624,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);
        }
@@ -540,6 +675,9 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                        lilv_instance_connect_port(_impl->instance, i, &_control_data[i]);
 
                        if (latent && i == latency_index) {
+                               LilvNode *max;
+                               lilv_port_get_range(_impl->plugin, port, NULL, NULL, &max);
+                               _max_latency = max ? lilv_node_as_float(max) : .02 * _sample_rate;
                                _latency_control_port  = &_control_data[i];
                                *_latency_control_port = 0;
                        }
@@ -608,6 +746,49 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        latency_compute_run();
 }
 
+int
+LV2Plugin::set_block_size (pframes_t nframes)
+{
+#ifdef HAVE_LV2_1_2_0
+       if (_impl->opts_iface) {
+               LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
+               _impl->block_length = nframes;
+               LV2_Options_Option block_size_option = {
+                       LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id ("http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"),
+                       sizeof(int32_t), atom_Int, (void*)&_impl->block_length
+               };
+               _impl->opts_iface->set (_impl->instance->lv2_handle, &block_size_option);
+       }
+#endif
+       return 0;
+}
+
+bool
+LV2Plugin::requires_fixed_sized_buffers () const
+{
+       /* This controls if Ardour will split the plugin's run()
+        * on automation events in order to pass sample-accurate automation
+        * via standard control-ports.
+        *
+        * When returning true Ardour will *not* sub-divide the process-cycle.
+        * Automation events that happen between cycle-start and cycle-end will be
+        * ignored (ctrl values are interpolated to cycle-start).
+        * NB. Atom Sequences are still sample accurate.
+        *
+        * Note: This does not guarantee a fixed block-size.
+        * e.g The process cycle may be split when looping, also
+        * the period-size may change any time: see set_block_size()
+        */
+       if (get_info()->n_inputs.n_midi() > 0) {
+               /* we don't yet implement midi buffer offsets (for split cycles).
+                * Also connect_and_run() also uses _session.transport_frame() directly
+                * (for BBT) which is not offset for plugin cycle split.
+                */
+               return true;
+       }
+       return _no_sample_accurate_ctrl;
+}
+
 LV2Plugin::~LV2Plugin ()
 {
        DEBUG_TRACE(DEBUG::LV2, string_compose("%1 destroy\n", name()));
@@ -616,8 +797,15 @@ LV2Plugin::~LV2Plugin ()
        cleanup();
 
        lilv_instance_free(_impl->instance);
+       lilv_state_free(_impl->state);
        lilv_node_free(_impl->name);
        lilv_node_free(_impl->author);
+#ifdef HAVE_LV2_1_2_0
+       free(_impl->options);
+#endif
+#ifdef LV2_EXTENDED
+       free(_impl->queue_draw);
+#endif
 
        free(_features);
        free(_make_path_feature.data);
@@ -638,6 +826,7 @@ LV2Plugin::~LV2Plugin ()
 
        delete [] _control_data;
        delete [] _shadow_data;
+       delete [] _defaults;
        delete [] _ev_buffers;
 }
 
@@ -679,6 +868,23 @@ LV2Plugin::ui_is_resizable () const
        return !fs_matches && !nrs_matches;
 }
 
+#ifdef LV2_EXTENDED
+bool
+LV2Plugin::has_inline_display () {
+       return _display_interface ? true : false;
+}
+
+Plugin::Display_Image_Surface*
+LV2Plugin::render_inline_display (uint32_t w, uint32_t h) {
+       if (_display_interface) {
+               /* Plugin::Display_Image_Surface is identical to
+                * LV2_Inline_Display_Image_Surface */
+               return (Plugin::Display_Image_Surface*) _display_interface->render ((void*)_impl->instance->lv2_handle, w, h);
+       }
+       return NULL;
+}
+#endif
+
 string
 LV2Plugin::unique_id() const
 {
@@ -858,7 +1064,11 @@ LV2Plugin::c_ui_type()
 const std::string
 LV2Plugin::plugin_dir() const
 {
-       return Glib::build_filename(_session.plugins_dir(), _insert_id.to_s());
+       if (!_plugin_state_dir.empty ()){
+               return Glib::build_filename(_plugin_state_dir, _insert_id.to_s());
+       } else {
+               return Glib::build_filename(_session.plugins_dir(), _insert_id.to_s());
+       }
 }
 
 /** Directory for files created by the plugin (except during save). */
@@ -913,7 +1123,7 @@ LV2Plugin::add_state(XMLNode* root) const
        assert(_insert_id != PBD::ID("0"));
 
        XMLNode*    child;
-       char        buf[16];
+       char        buf[32];
        LocaleGuard lg(X_("C"));
 
        for (uint32_t i = 0; i < parameter_count(); ++i) {
@@ -926,6 +1136,10 @@ LV2Plugin::add_state(XMLNode* root) const
                }
        }
 
+       if (!_plugin_state_dir.empty()) {
+               root->add_property("template-dir", _plugin_state_dir);
+       }
+
        if (_has_state_interface) {
                // Provisionally increment state version and create directory
                const std::string new_dir = state_dir(++_state_version);
@@ -944,7 +1158,9 @@ LV2Plugin::add_state(XMLNode* root) const
                        0,
                        NULL);
 
-               if (!_impl->state || !lilv_state_equals(state, _impl->state)) {
+               if (!_plugin_state_dir.empty()
+                   || !_impl->state
+                   || !lilv_state_equals(state, _impl->state)) {
                        lilv_state_save(_world.world,
                                        _uri_map.urid_map(),
                                        _uri_map.urid_unmap(),
@@ -953,8 +1169,14 @@ LV2Plugin::add_state(XMLNode* root) const
                                        new_dir.c_str(),
                                        "state.ttl");
 
-                       lilv_state_free(_impl->state);
-                       _impl->state = state;
+                       if (_plugin_state_dir.empty()) {
+                               // normal session save
+                               lilv_state_free(_impl->state);
+                               _impl->state = state;
+                       } else {
+                               // template save (dedicated state-dir)
+                               lilv_state_free(state);
+                       }
                } else {
                        // State is identical, decrement version and nuke directory
                        lilv_state_free(state);
@@ -991,11 +1213,13 @@ LV2Plugin::find_presets()
                const LilvNode* preset = lilv_nodes_get(presets, i);
                lilv_world_load_resource(_world.world, preset);
                LilvNode* name = get_value(_world.world, preset, rdfs_label);
+               bool userpreset = true; // TODO
                if (name) {
                        _presets.insert(std::make_pair(lilv_node_as_string(preset),
                                                       Plugin::PresetRecord(
                                                               lilv_node_as_string(preset),
-                                                              lilv_node_as_string(name))));
+                                                              lilv_node_as_string(name),
+                                                              userpreset)));
                        lilv_node_free(name);
                } else {
                        warning << string_compose(
@@ -1082,6 +1306,18 @@ LV2Plugin::do_save_preset(string name)
                Glib::get_home_dir(),
                Glib::build_filename(".lv2", prefix + "_" + base_name + ".lv2"));
 
+#ifdef HAVE_LILV_0_21_3
+       /* delete reference to old preset (if any) */
+       const PresetRecord* r = preset_by_label(name);
+       if (r) {
+               LilvNode*  pset  = lilv_new_uri (_world.world, r->uri.c_str());
+               if (pset) {
+                       lilv_world_unload_resource (_world.world, pset);
+                       lilv_node_free(pset);
+               }
+       }
+#endif
+
        LilvState* state = lilv_state_new_from_instance(
                _impl->plugin,
                _impl->instance,
@@ -1143,6 +1379,9 @@ LV2Plugin::do_remove_preset(string name)
                return;
        }
 
+       /* Unload preset from world. */
+       lilv_world_unload_resource(world, pset);
+
        /* Delete it from the file system.  This will remove the preset file and the entry
           from the manifest.  If this results in an empty manifest (i.e. the
           preset is the only thing in the bundle), then the bundle is removed. */
@@ -1539,7 +1778,19 @@ LV2Plugin::work_response(uint32_t size, const void* data)
 void
 LV2Plugin::set_insert_id(PBD::ID id)
 {
-       _insert_id = id;
+       if (_insert_id == "0") {
+               _insert_id = id;
+       } else if (_insert_id != id) {
+               lilv_state_free(_impl->state);
+               _impl->state = NULL;
+               _insert_id   = id;
+       }
+}
+
+void
+LV2Plugin::set_state_dir (const std::string& d)
+{
+       _plugin_state_dir = d;
 }
 
 int
@@ -1597,6 +1848,10 @@ LV2Plugin::set_state(const XMLNode& node, int version)
                set_parameter(port_id, atof(value));
        }
 
+       if ((prop = node.property("template-dir")) != 0) {
+               set_state_dir (prop->value ());
+       }
+
        _state_version = 0;
        if ((prop = node.property("state-dir")) != 0) {
                if (sscanf(prop->value().c_str(), "state%u", &_state_version) != 1) {
@@ -1613,6 +1868,15 @@ LV2Plugin::set_state(const XMLNode& node, int version)
                        _world.world, _uri_map.urid_map(), NULL, state_file.c_str());
 
                lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL);
+               lilv_state_free(_impl->state);
+               _impl->state = state;
+       }
+
+       if (!_plugin_state_dir.empty ()) {
+               // force save with session, next time (increment counter)
+               lilv_state_free (_impl->state);
+               _impl->state = NULL;
+               set_state_dir ("");
        }
 
        latency_compute_run();
@@ -1669,6 +1933,56 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c
        return 0;
 }
 
+Plugin::IOPortDescription
+LV2Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id) const
+{
+       PortFlags match = 0;
+       switch (dt) {
+               case DataType::AUDIO:
+                       match = PORT_AUDIO;
+                       break;
+               case DataType::MIDI:
+                       match = PORT_SEQUENCE | PORT_MIDI; // ignore old PORT_EVENT
+                       break;
+               default:
+                       return Plugin::IOPortDescription ("?");
+                       break;
+       }
+       if (input) {
+               match |= PORT_INPUT;
+       } else {
+               match |= PORT_OUTPUT;
+       }
+
+       uint32_t p = 0;
+       uint32_t idx = UINT32_MAX;
+
+       uint32_t const num_ports = parameter_count();
+       for (uint32_t port_index = 0; port_index < num_ports; ++port_index) {
+               PortFlags flags = _port_flags[port_index];
+               if ((flags & match) == match) {
+                       if (p == id) {
+                               idx = port_index;
+                       }
+                       ++p;
+               }
+       }
+       if (idx == UINT32_MAX) {
+               return Plugin::IOPortDescription ("?");
+       }
+
+       LilvNode* name = lilv_port_get_name(_impl->plugin,
+                       lilv_plugin_get_port_by_index(_impl->plugin, idx));
+       Plugin::IOPortDescription iod (lilv_node_as_string (name));
+       lilv_node_free(name);
+
+       if (lilv_port_has_property(_impl->plugin,
+                               lilv_plugin_get_port_by_index(_impl->plugin, idx), _world.lv2_isSideChain)) {
+               iod.is_sidechain = true;
+       }
+       return iod;
+}
+
 string
 LV2Plugin::describe_parameter(Evoral::Parameter which)
 {
@@ -1699,6 +2013,12 @@ LV2Plugin::describe_parameter(Evoral::Parameter which)
        }
 }
 
+framecnt_t
+LV2Plugin::max_latency () const
+{
+       return _max_latency;
+}
+
 framecnt_t
 LV2Plugin::signal_latency() const
 {
@@ -1728,6 +2048,23 @@ LV2Plugin::automatable() const
        return ret;
 }
 
+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()
 {
@@ -1755,7 +2092,6 @@ LV2Plugin::cleanup()
 {
        DEBUG_TRACE(DEBUG::LV2, string_compose("%1 cleanup\n", name()));
 
-       activate();
        deactivate();
        lilv_instance_free(_impl->instance);
        _impl->instance = NULL;
@@ -1893,13 +2229,22 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
        TempoMetric             tmetric  = tmap.metric_at(_session.transport_frame(), &metric_i);
 
        if (_freewheel_control_port) {
-               *_freewheel_control_port = _session.engine().freewheeling();
+               *_freewheel_control_port = _session.engine().freewheeling() ? 1.f : 0.f;
        }
 
        if (_bpm_control_port) {
                *_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);
@@ -1988,8 +2333,10 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                                ? *metric_i : NULL;
                                        if (m != m_end && (!metric || metric->frame() > (*m).time())) {
                                                const Evoral::MIDIEvent<framepos_t> ev(*m, false);
-                                               LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]);
-                                               lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer());
+                                               if (ev.time() < nframes) {
+                                                       LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]);
+                                                       lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer());
+                                               }
                                                ++m;
                                        } else {
                                                tmetric.set_metric(metric);
@@ -2082,9 +2429,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);
@@ -2094,6 +2440,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));
@@ -2108,19 +2526,18 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                                                            _uri_map.urids.patch_value,    &value,
                                                                            0);
 
-                                                       if (!property || !value ||
-                                                           property->type != _uri_map.urids.atom_URID ||
-                                                           value->type    != _uri_map.urids.atom_Path) {
+                                                       if (property && value &&
+                                                           property->type == _uri_map.urids.atom_URID &&
+                                                           value->type    == _uri_map.urids.atom_Path) {
+                                                               const uint32_t prop_id = ((const LV2_Atom_URID*)property)->body;
+                                                               const char*    path    = (const char*)LV2_ATOM_BODY_CONST(value);
+
+                                                               // Emit PropertyChanged signal for UI
+                                                               // TODO: This should emit the control's Changed signal
+                                                               PropertyChanged(prop_id, Variant(Variant::PATH, path));
+                                                       } else {
                                                                std::cerr << "warning: patch:Set for unknown property" << std::endl;
-                                                               continue;
                                                        }
-
-                                                       const uint32_t prop_id = ((const LV2_Atom_URID*)property)->body;
-                                                       const char*    path    = (const char*)LV2_ATOM_BODY_CONST(value);
-
-                                                       // Emit PropertyChanged signal for UI
-                                                       // TODO: This should emit the control's Changed signal
-                                                       PropertyChanged(prop_id, Variant(Variant::PATH, path));
                                                }
                                        }
                                }
@@ -2140,6 +2557,13 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
        _next_cycle_speed = _session.transport_speed();
        _next_cycle_start = _session.transport_frame() + (nframes * _next_cycle_speed);
 
+       if (_latency_control_port) {
+               framecnt_t new_latency = signal_latency ();
+               if (_current_latency != new_latency) {
+                       LatencyChanged (_current_latency, new_latency); /* EMIT SIGNAL */
+               }
+               _current_latency = new_latency;
+       }
        return 0;
 }
 
@@ -2301,7 +2725,7 @@ LV2Plugin::Impl::designated_input (const char* uri, void** bufptrs[], void** buf
 static bool lv2_filter (const string& str, void* /*arg*/)
 {
        /* Not a dotfile, has a prefix before a period, suffix is "lv2" */
-       
+
        return str[0] != '.' && (str.length() > 3 && str.find (".lv2") == (str.length() - 4));
 }
 
@@ -2324,6 +2748,7 @@ LV2World::LV2World()
        lv2_InputPort      = lilv_new_uri(world, LILV_URI_INPUT_PORT);
        lv2_OutputPort     = lilv_new_uri(world, LILV_URI_OUTPUT_PORT);
        lv2_inPlaceBroken  = lilv_new_uri(world, LV2_CORE__inPlaceBroken);
+       lv2_isSideChain    = lilv_new_uri(world, LV2_CORE_PREFIX "isSideChain");
        lv2_integer        = lilv_new_uri(world, LV2_CORE__integer);
        lv2_default        = lilv_new_uri(world, LV2_CORE__default);
        lv2_minimum        = lilv_new_uri(world, LV2_CORE__minimum);
@@ -2349,10 +2774,36 @@ 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);
+#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);
+       bufz_nominalBlockLength  = lilv_new_uri(world, "http://lv2plug.in/ns/ext/buf-size#nominalBlockLength");
+#endif
+
 }
 
 LV2World::~LV2World()
 {
+       if (!world) {
+               return;
+       }
+#ifdef HAVE_LV2_1_2_0
+       lilv_node_free(bufz_nominalBlockLength);
+       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);
@@ -2375,6 +2826,7 @@ LV2World::~LV2World()
        lilv_node_free(lv2_sampleRate);
        lilv_node_free(lv2_reportsLatency);
        lilv_node_free(lv2_integer);
+       lilv_node_free(lv2_isSideChain);
        lilv_node_free(lv2_inPlaceBroken);
        lilv_node_free(lv2_OutputPort);
        lilv_node_free(lv2_InputPort);
@@ -2390,6 +2842,7 @@ LV2World::~LV2World()
        lilv_node_free(atom_Chunk);
        lilv_node_free(atom_AtomPort);
        lilv_world_free(world);
+       world = NULL;
 }
 
 void
@@ -2451,6 +2904,76 @@ LV2PluginInfo::load(Session& session)
        return PluginPtr();
 }
 
+std::vector<Plugin::PresetRecord>
+LV2PluginInfo::get_presets (bool /*user_only*/) const
+{
+       std::vector<Plugin::PresetRecord> p;
+#ifndef NO_PLUGIN_STATE
+       const LilvPlugin* lp = NULL;
+       try {
+               PluginPtr plugin;
+               const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world);
+               LilvNode* uri = lilv_new_uri(_world.world, _plugin_uri);
+               if (!uri) { throw failed_constructor(); }
+               lp = lilv_plugins_get_by_uri(plugins, uri);
+               if (!lp) { throw failed_constructor(); }
+               lilv_node_free(uri);
+       } catch (failed_constructor& err) {
+               return p;
+       }
+       assert (lp);
+       // see LV2Plugin::find_presets
+       LilvNode* lv2_appliesTo = lilv_new_uri(_world.world, LV2_CORE__appliesTo);
+       LilvNode* pset_Preset   = lilv_new_uri(_world.world, LV2_PRESETS__Preset);
+       LilvNode* rdfs_label    = lilv_new_uri(_world.world, LILV_NS_RDFS "label");
+
+       LilvNodes* presets = lilv_plugin_get_related(lp, pset_Preset);
+       LILV_FOREACH(nodes, i, presets) {
+               const LilvNode* preset = lilv_nodes_get(presets, i);
+               lilv_world_load_resource(_world.world, preset);
+               LilvNode* name = get_value(_world.world, preset, rdfs_label);
+               bool userpreset = true; // TODO
+               if (name) {
+                       p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset));
+                       lilv_node_free(name);
+               }
+       }
+       lilv_nodes_free(presets);
+       lilv_node_free(rdfs_label);
+       lilv_node_free(pset_Preset);
+       lilv_node_free(lv2_appliesTo);
+#endif
+       return p;
+}
+
+bool
+LV2PluginInfo::in_category (const std::string &c) const
+{
+       // TODO use untranslated lilv_plugin_get_class()
+       // match gtk2_ardour/plugin_selector.cc
+       if (category == c) {
+               return true;
+       }
+       return false;
+}
+
+bool
+LV2PluginInfo::is_instrument () const
+{
+       if (category == "Instrument") {
+               return true;
+       }
+#if 1
+       /* until we make sure that category remains untranslated in the lv2.ttl spec
+        * and until most instruments also classify themselves as such, there's a 2nd check:
+        */
+       if (n_inputs.n_midi() > 0 && n_inputs.n_audio() == 0 && n_outputs.n_audio() > 0) {
+               return true;
+       }
+#endif
+       return false;
+}
+
 PluginInfoList*
 LV2PluginInfo::discover()
 {
@@ -2475,6 +2998,29 @@ LV2PluginInfo::discover()
                        continue;
                }
 
+               if (lilv_plugin_has_feature(p, world.lv2_inPlaceBroken)) {
+                       warning << string_compose(
+                           _("Ignoring LV2 plugin \"%1\" since it cannot do inplace processing."),
+                           lilv_node_as_string(name)) << endmsg;
+                       lilv_node_free(name);
+                       continue;
+               }
+
+#ifdef HAVE_LV2_1_2_0
+               LilvNodes *required_features = lilv_plugin_get_required_features (p);
+               if (lilv_nodes_contains (required_features, world.bufz_powerOf2BlockLength) ||
+                               lilv_nodes_contains (required_features, world.bufz_fixedBlockLength)
+                  ) {
+                       warning << string_compose(
+                           _("Ignoring LV2 plugin \"%1\" because its buffer-size requirements cannot be satisfied."),
+                           lilv_node_as_string(name)) << endmsg;
+                       lilv_nodes_free(required_features);
+                       lilv_node_free(name);
+                       continue;
+               }
+               lilv_nodes_free(required_features);
+#endif
+
                info->type = LV2;
 
                info->name = string(lilv_node_as_string(name));