variable plugin port config.
[ardour.git] / libs / ardour / lv2_plugin.cc
index 54a8b03848e8241d076f768edd60fcea203f53e2..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>
 
@@ -66,7 +66,9 @@
 #include "lv2/lv2plug.in/ns/ext/worker/worker.h"
 #include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h"
 #include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
-#ifdef HAVE_NEW_LV2
+#include "lv2/lv2plug.in/ns/extensions/units/units.h"
+#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
+#ifdef HAVE_LV2_1_2_0
 #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
 #include "lv2/lv2plug.in/ns/ext/options/options.h"
 #endif
 #include <suil/suil.h>
 #endif
 
+// Compatibility for old LV2
+#ifndef LV2_ATOM_CONTENTS_CONST
+#define LV2_ATOM_CONTENTS_CONST(type, atom) \
+       ((const void*)((const uint8_t*)(atom) + sizeof(type)))
+#endif
+#ifndef LV2_ATOM_BODY_CONST
+#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(LV2_Atom, atom)
+#endif
+#ifndef LV2_PATCH__property
+#define LV2_PATCH__property LV2_PATCH_PREFIX "property"
+#endif
+#ifndef LV2_PATCH__value
+#define LV2_PATCH__value LV2_PATCH_PREFIX "value"
+#endif
+#ifndef LV2_PATCH__writable
+#define LV2_PATCH__writable LV2_PATCH_PREFIX "writable"
+#endif
+
 /** The number of MIDI buffers that will fit in a UI/worker comm buffer.
     This needs to be roughly the number of cycles the UI will get around to
     actually processing the traffic.  Lower values are flakier but save memory.
@@ -87,33 +107,12 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-URIMap LV2Plugin::_uri_map;
-
-LV2Plugin::URIDs LV2Plugin::urids = {
-       _uri_map.uri_to_id(LV2_ATOM__Chunk),
-       _uri_map.uri_to_id(LV2_ATOM__Path),
-       _uri_map.uri_to_id(LV2_ATOM__Sequence),
-       _uri_map.uri_to_id(LV2_ATOM__eventTransfer),
-       _uri_map.uri_to_id(LV2_LOG__Error),
-       _uri_map.uri_to_id(LV2_LOG__Note),
-       _uri_map.uri_to_id(LV2_LOG__Warning),
-       _uri_map.uri_to_id(LV2_MIDI__MidiEvent),
-       _uri_map.uri_to_id(LV2_TIME__Position),
-       _uri_map.uri_to_id(LV2_TIME__bar),
-       _uri_map.uri_to_id(LV2_TIME__barBeat),
-       _uri_map.uri_to_id(LV2_TIME__beatUnit),
-       _uri_map.uri_to_id(LV2_TIME__beatsPerBar),
-       _uri_map.uri_to_id(LV2_TIME__beatsPerMinute),
-       _uri_map.uri_to_id(LV2_TIME__frame),
-       _uri_map.uri_to_id(LV2_TIME__speed)
-};
-
 class LV2World : boost::noncopyable {
 public:
        LV2World ();
        ~LV2World ();
 
-       void load_bundled_plugins();
+       void load_bundled_plugins(bool verbose=false);
 
        LilvWorld* world;
 
@@ -133,19 +132,48 @@ public:
        LilvNode* lv2_enumeration;
        LilvNode* lv2_freewheeling;
        LilvNode* lv2_inPlaceBroken;
+       LilvNode* lv2_isSideChain;
        LilvNode* lv2_integer;
+       LilvNode* lv2_default;
+       LilvNode* lv2_minimum;
+       LilvNode* lv2_maximum;
        LilvNode* lv2_reportsLatency;
        LilvNode* lv2_sampleRate;
        LilvNode* lv2_toggled;
        LilvNode* midi_MidiEvent;
        LilvNode* rdfs_comment;
+       LilvNode* rdfs_label;
+       LilvNode* rdfs_range;
        LilvNode* rsz_minimumSize;
        LilvNode* time_Position;
        LilvNode* ui_GtkUI;
        LilvNode* ui_external;
        LilvNode* ui_externalkx;
+       LilvNode* units_hz;
+       LilvNode* units_db;
        LilvNode* units_unit;
+       LilvNode* units_render;
        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;
@@ -189,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
@@ -199,11 +237,11 @@ log_vprintf(LV2_Log_Handle /*handle*/,
 {
        char* str = NULL;
        const int ret = g_vasprintf(&str, fmt, args);
-       if (type == LV2Plugin::urids.log_Error) {
+       if (type == URIMap::instance().urids.log_Error) {
                error << str << endmsg;
-       } else if (type == LV2Plugin::urids.log_Warning) {
+       } else if (type == URIMap::instance().urids.log_Warning) {
                warning << str << endmsg;
-       } else if (type == LV2Plugin::urids.log_Note) {
+       } else if (type == URIMap::instance().urids.log_Note) {
                info << str << endmsg;
        }
        // TODO: Toggleable log:Trace message support
@@ -225,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.
@@ -233,15 +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;
+       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,
@@ -254,6 +313,10 @@ LV2Plugin::LV2Plugin (AudioEngine& engine,
        , _features(NULL)
        , _worker(NULL)
        , _insert_id("0")
+       , _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);
 }
@@ -265,6 +328,10 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
        , _features(NULL)
        , _worker(NULL)
        , _insert_id(other._insert_id)
+       , _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);
 
@@ -293,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";
@@ -320,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;
@@ -330,26 +400,52 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
        _features[6] = &_log_feature;
 
        unsigned n_features = 7;
-#ifdef HAVE_NEW_LV2
+#ifdef HAVE_LV2_1_2_0
        _features[n_features++] = &_def_state_feature;
 #endif
 
        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;
 
-#ifdef HAVE_NEW_LV2
+       _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
 
@@ -398,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(
@@ -414,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;
@@ -456,6 +595,19 @@ 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) {
+                                               _patch_port_in_index = i;
+                                       } else {
+                                               _patch_port_out_index = i;
+                                       }
+                               }
                        }
                        LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize);
                        LilvNode* min_size = min_size_v ? lilv_nodes_get_first(min_size_v) : NULL;
@@ -472,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);
        }
@@ -515,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;
                        }
@@ -578,10 +741,54 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                }
        }
 
+       load_supported_properties(_property_descriptors);
        allocate_atom_event_buffers();
        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()));
@@ -590,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);
@@ -612,6 +826,7 @@ LV2Plugin::~LV2Plugin ()
 
        delete [] _control_data;
        delete [] _shadow_data;
+       delete [] _defaults;
        delete [] _ev_buffers;
 }
 
@@ -653,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
 {
@@ -832,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). */
@@ -887,8 +1123,8 @@ LV2Plugin::add_state(XMLNode* root) const
        assert(_insert_id != PBD::ID("0"));
 
        XMLNode*    child;
-       char        buf[16];
-       LocaleGuard lg(X_("POSIX"));
+       char        buf[32];
+       LocaleGuard lg(X_("C"));
 
        for (uint32_t i = 0; i < parameter_count(); ++i) {
                if (parameter_is_input(i) && parameter_is_control(i)) {
@@ -900,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);
@@ -918,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(),
@@ -927,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);
@@ -940,11 +1188,17 @@ LV2Plugin::add_state(XMLNode* root) const
        }
 }
 
-static inline const LilvNode*
+// TODO: Once we can rely on lilv 0.16.0, lilv_world_get can replace this
+static LilvNode*
 get_value(LilvWorld* world, const LilvNode* subject, const LilvNode* predicate)
 {
        LilvNodes* vs = lilv_world_find_nodes(world, subject, predicate, NULL);
-       return vs ? lilv_nodes_get_first(vs) : NULL;
+       if (vs) {
+               LilvNode* node = lilv_node_duplicate(lilv_nodes_get_first(vs));
+               lilv_nodes_free(vs);
+               return node;
+       }
+       return NULL;
 }
 
 void
@@ -958,12 +1212,15 @@ LV2Plugin::find_presets()
        LILV_FOREACH(nodes, i, presets) {
                const LilvNode* preset = lilv_nodes_get(presets, i);
                lilv_world_load_resource(_world.world, preset);
-               const LilvNode* name = get_value(_world.world, preset, rdfs_label);
+               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(
                            _("Plugin \"%1\" preset \"%2\" is missing a label\n"),
@@ -986,7 +1243,7 @@ set_port_value(const char* port_symbol,
                uint32_t    type)
 {
        LV2Plugin* self = (LV2Plugin*)user_data;
-       if (type != 0 && type != self->_uri_map.uri_to_id(LV2_ATOM__Float)) {
+       if (type != 0 && type != URIMap::instance().urids.atom_Float) {
                return;  // TODO: Support non-float ports
        }
 
@@ -1041,11 +1298,25 @@ ARDOUR::lv2plugin_get_port_value(const char* port_symbol,
 std::string
 LV2Plugin::do_save_preset(string name)
 {
+       LilvNode*    plug_name = lilv_plugin_get_name(_impl->plugin);
+       const string prefix    = legalize_for_uri(lilv_node_as_string(plug_name));
        const string base_name = legalize_for_uri(name);
        const string file_name = base_name + ".ttl";
        const string bundle    = Glib::build_filename(
                Glib::get_home_dir(),
-               Glib::build_filename(".lv2", base_name + ".lv2"));
+               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,
@@ -1077,7 +1348,7 @@ LV2Plugin::do_save_preset(string name)
        std::string uri = Glib::filename_to_uri(Glib::build_filename(bundle, file_name));
        LilvNode *node_bundle = lilv_new_uri(_world.world, Glib::filename_to_uri(Glib::build_filename(bundle, "/")).c_str());
        LilvNode *node_preset = lilv_new_uri(_world.world, uri.c_str());
-#ifdef HAVE_LILV_0_19_2
+#ifdef HAVE_LILV_0_21_3
        lilv_world_unload_resource(_world.world, node_preset);
        lilv_world_unload_bundle(_world.world, node_bundle);
 #endif
@@ -1085,20 +1356,44 @@ LV2Plugin::do_save_preset(string name)
        lilv_world_load_resource(_world.world, node_preset);
        lilv_node_free(node_bundle);
        lilv_node_free(node_preset);
+       lilv_node_free(plug_name);
        return uri;
 }
 
 void
 LV2Plugin::do_remove_preset(string name)
 {
-       string preset_file = Glib::build_filename(
-               Glib::get_home_dir(),
-               Glib::build_filename(
-                       Glib::build_filename(".lv2", "presets"),
-                       name + ".ttl"
-               )
-       );
-       ::g_unlink(preset_file.c_str());
+#ifdef HAVE_LILV_0_21_3
+       /* Look up preset record by label (FIXME: ick, label as ID) */
+       const PresetRecord* r = preset_by_label(name);
+       if (!r) {
+               return;
+       }
+
+       /* Load a LilvState for the preset. */
+       LilvWorld* world = _world.world;
+       LilvNode*  pset  = lilv_new_uri(world, r->uri.c_str());
+       LilvState* state = lilv_state_new_from_world(world, _uri_map.urid_map(), pset);
+       if (!state) {
+               lilv_node_free(pset);
+               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. */
+       lilv_state_delete(world, state);
+
+       lilv_state_free(state);
+       lilv_node_free(pset);
+#endif
+       /* Without lilv_state_delete(), we could delete the preset file, but this
+          would leave a broken bundle/manifest around, so the preset would still
+          be visible, but broken.  Naively deleting a bundle is too dangerous, so
+          we simply do not support preset deletion with older Lilv */
 }
 
 bool
@@ -1185,8 +1480,248 @@ LV2Plugin::write_to_ui(uint32_t       index,
        return true;
 }
 
+static void
+forge_variant(LV2_Atom_Forge* forge, const Variant& value)
+{
+       switch (value.type()) {
+       case Variant::NOTHING:
+               break;
+       case Variant::BEATS:
+               // No atom type for this, just forge a double
+               lv2_atom_forge_double(forge, value.get_beats().to_double());
+               break;
+       case Variant::BOOL:
+               lv2_atom_forge_bool(forge, value.get_bool());
+               break;
+       case Variant::DOUBLE:
+               lv2_atom_forge_double(forge, value.get_double());
+               break;
+       case Variant::FLOAT:
+               lv2_atom_forge_float(forge, value.get_float());
+               break;
+       case Variant::INT:
+               lv2_atom_forge_int(forge, value.get_int());
+               break;
+       case Variant::LONG:
+               lv2_atom_forge_long(forge, value.get_long());
+               break;
+       case Variant::PATH:
+               lv2_atom_forge_path(
+                       forge, value.get_path().c_str(), value.get_path().size());
+               break;
+       case Variant::STRING:
+               lv2_atom_forge_string(
+                       forge, value.get_string().c_str(), value.get_string().size());
+               break;
+       case Variant::URI:
+               lv2_atom_forge_uri(
+                       forge, value.get_uri().c_str(), value.get_uri().size());
+               break;
+       }
+}
+
+/** Get a variant type from a URI, return false iff no match found. */
+static bool
+uri_to_variant_type(const std::string& uri, Variant::Type& type)
+{
+       if (uri == LV2_ATOM__Bool) {
+               type = Variant::BOOL;
+       } else if (uri == LV2_ATOM__Double) {
+               type = Variant::DOUBLE;
+       } else if (uri == LV2_ATOM__Float) {
+               type = Variant::FLOAT;
+       } else if (uri == LV2_ATOM__Int) {
+               type = Variant::INT;
+       } else if (uri == LV2_ATOM__Long) {
+               type = Variant::LONG;
+       } else if (uri == LV2_ATOM__Path) {
+               type = Variant::PATH;
+       } else if (uri == LV2_ATOM__String) {
+               type = Variant::STRING;
+       } else if (uri == LV2_ATOM__URI) {
+               type = Variant::URI;
+       } else {
+               return false;
+       }
+       return true;
+}
+
+void
+LV2Plugin::set_property(uint32_t key, const Variant& value)
+{
+       if (_patch_port_in_index == (uint32_t)-1) {
+               error << "LV2: set_property called with unset patch_port_in_index" << endmsg;
+               return;
+       } else if (value.type() == Variant::NOTHING) {
+               error << "LV2: set_property called with void value" << endmsg;
+               return;
+       }
+
+       // Set up forge to write to temporary buffer on the stack
+       LV2_Atom_Forge*      forge = &_impl->ui_forge;
+       LV2_Atom_Forge_Frame frame;
+       uint8_t              buf[PATH_MAX];  // Ought to be enough for anyone...
+
+       lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
+
+       // Serialize patch:Set message to set property
+#ifdef HAVE_LV2_1_10_0
+       lv2_atom_forge_object(forge, &frame, 1, _uri_map.urids.patch_Set);
+       lv2_atom_forge_key(forge, _uri_map.urids.patch_property);
+       lv2_atom_forge_urid(forge, key);
+       lv2_atom_forge_key(forge, _uri_map.urids.patch_value);
+#else
+       lv2_atom_forge_blank(forge, &frame, 1, _uri_map.urids.patch_Set);
+       lv2_atom_forge_property_head(forge, _uri_map.urids.patch_property, 0);
+       lv2_atom_forge_urid(forge, key);
+       lv2_atom_forge_property_head(forge, _uri_map.urids.patch_value, 0);
+#endif
+
+       forge_variant(forge, value);
+
+       // Write message to UI=>Plugin ring
+       const LV2_Atom* const atom = (const LV2_Atom*)buf;
+       write_from_ui(_patch_port_in_index,
+                     _uri_map.urids.atom_eventTransfer,
+                     lv2_atom_total_size(atom),
+                     (const uint8_t*)atom);
+}
+
+const ParameterDescriptor&
+LV2Plugin::get_property_descriptor(uint32_t id) const
+{
+       PropertyDescriptors::const_iterator p = _property_descriptors.find(id);
+       if (p != _property_descriptors.end()) {
+               return p->second;
+       }
+       return Plugin::get_property_descriptor(id);
+}
+
+static void
+load_parameter_descriptor_units(LilvWorld* lworld, ParameterDescriptor& desc, const LilvNodes* units)
+{
+       if (lilv_nodes_contains(units, _world.units_midiNote)) {
+               desc.unit = ParameterDescriptor::MIDI_NOTE;
+       } else if (lilv_nodes_contains(units, _world.units_db)) {
+               desc.unit = ParameterDescriptor::DB;
+       } else if (lilv_nodes_contains(units, _world.units_hz)) {
+               desc.unit = ParameterDescriptor::HZ;
+       }
+       if (lilv_nodes_size(units) > 0) {
+               const LilvNode* unit = lilv_nodes_get_first(units);
+               LilvNode* render = get_value(lworld, unit, _world.units_render);
+               if (render) {
+                       desc.print_fmt = lilv_node_as_string(render);
+                       lilv_node_free(render);
+               }
+       }
+}
+
+static void
+load_parameter_descriptor(LV2World&            world,
+                          ParameterDescriptor& desc,
+                          Variant::Type        datatype,
+                          const LilvNode*      subject)
+{
+       LilvWorld* lworld  = _world.world;
+       LilvNode*  label   = get_value(lworld, subject, _world.rdfs_label);
+       LilvNode*  def     = get_value(lworld, subject, _world.lv2_default);
+       LilvNode*  minimum = get_value(lworld, subject, _world.lv2_minimum);
+       LilvNode*  maximum = get_value(lworld, subject, _world.lv2_maximum);
+       LilvNodes* units   = lilv_world_find_nodes(lworld, subject, _world.units_unit, NULL);
+       if (label) {
+               desc.label = lilv_node_as_string(label);
+       }
+       if (def && lilv_node_is_float(def)) {
+               desc.normal = lilv_node_as_float(def);
+       }
+       if (minimum && lilv_node_is_float(minimum)) {
+               desc.lower = lilv_node_as_float(minimum);
+       }
+       if (maximum && lilv_node_is_float(maximum)) {
+               desc.upper = lilv_node_as_float(maximum);
+       }
+       load_parameter_descriptor_units(lworld, desc, units);
+       desc.datatype      = datatype;
+       desc.toggled      |= datatype == Variant::BOOL;
+       desc.integer_step |= datatype == Variant::INT || datatype == Variant::LONG;
+       desc.update_steps();
+
+       lilv_nodes_free(units);
+       lilv_node_free(label);
+       lilv_node_free(def);
+       lilv_node_free(minimum);
+       lilv_node_free(maximum);
+}
+
+void
+LV2Plugin::load_supported_properties(PropertyDescriptors& descs)
+{
+       LilvWorld*       lworld     = _world.world;
+       const LilvNode*  subject    = lilv_plugin_get_uri(_impl->plugin);
+       LilvNodes*       properties = lilv_world_find_nodes(
+               lworld, subject, _world.patch_writable, NULL);
+       LILV_FOREACH(nodes, p, properties) {
+               // Get label and range
+               const LilvNode* prop  = lilv_nodes_get(properties, p);
+               LilvNode*       range = get_value(lworld, prop, _world.rdfs_range);
+               if (!range) {
+                       warning << string_compose(_("LV2: property <%1> has no range datatype, ignoring"),
+                                                 lilv_node_as_uri(prop)) << endmsg;
+                       continue;
+               }
+
+               // Convert range to variant type (TODO: support for multiple range types)
+               Variant::Type datatype;
+               if (!uri_to_variant_type(lilv_node_as_uri(range), datatype)) {
+                       error << string_compose(_("LV2: property <%1> has unsupported datatype <%1>"),
+                                               lilv_node_as_uri(prop), lilv_node_as_uri(range)) << endmsg;
+                       continue;
+               }
+
+               // Add description to result
+               ParameterDescriptor desc;
+               desc.key      = _uri_map.uri_to_id(lilv_node_as_uri(prop));
+               desc.datatype = datatype;
+               load_parameter_descriptor(_world, desc, datatype, prop);
+               descs.insert(std::make_pair(desc.key, desc));
+
+               lilv_node_free(range);
+       }
+       lilv_nodes_free(properties);
+}
+
+void
+LV2Plugin::announce_property_values()
+{
+       if (_patch_port_in_index == (uint32_t)-1) {
+               return;
+       }
+
+       // Set up forge to write to temporary buffer on the stack
+       LV2_Atom_Forge*      forge = &_impl->ui_forge;
+       LV2_Atom_Forge_Frame frame;
+       uint8_t              buf[PATH_MAX];  // Ought to be enough for anyone...
+
+       lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
+
+       // Serialize patch:Get message with no subject (implicitly plugin instance)
+#ifdef HAVE_LV2_1_10_0
+       lv2_atom_forge_object(forge, &frame, 1, _uri_map.urids.patch_Get);
+#else
+       lv2_atom_forge_blank(forge, &frame, 1, _uri_map.urids.patch_Get);
+#endif
+
+       // Write message to UI=>Plugin ring
+       const LV2_Atom* const atom = (const LV2_Atom*)buf;
+       write_from_ui(_patch_port_in_index,
+                     _uri_map.urids.atom_eventTransfer,
+                     lv2_atom_total_size(atom),
+                     (const uint8_t*)atom);
+}
+
 void
-LV2Plugin::enable_ui_emmission()
+LV2Plugin::enable_ui_emission()
 {
        if (!_to_ui) {
                /* see note in LV2Plugin::write_from_ui() */
@@ -1241,9 +1776,21 @@ LV2Plugin::work_response(uint32_t size, const void* data)
 }
 
 void
-LV2Plugin::set_insert_info(const PluginInsert* insert)
+LV2Plugin::set_insert_id(PBD::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)
 {
-       _insert_id = insert->id();
+       _plugin_state_dir = d;
 }
 
 int
@@ -1256,7 +1803,7 @@ LV2Plugin::set_state(const XMLNode& node, int version)
        const char*          sym;
        const char*          value;
        uint32_t             port_id;
-       LocaleGuard          lg(X_("POSIX"));
+       LocaleGuard          lg(X_("C"));
 
        if (node.name() != state_node_name()) {
                error << _("Bad node sent to LV2Plugin::set_state") << endmsg;
@@ -1301,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) {
@@ -1317,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();
@@ -1329,20 +1889,28 @@ int
 LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) const
 {
        const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, which);
+       if (!port) {
+               error << string_compose("LV2: get descriptor of non-existent port %1", which)
+                     << endmsg;
+               return 1;
+       }
 
        LilvNodes* portunits;
        LilvNode *def, *min, *max;
        lilv_port_get_range(_impl->plugin, port, &def, &min, &max);
        portunits = lilv_port_get_value(_impl->plugin, port, _world.units_unit);
 
+       // TODO: Once we can rely on lilv 0.18.0 being present,
+       // load_parameter_descriptor() can be used for ports as well
        desc.integer_step = lilv_port_has_property(_impl->plugin, port, _world.lv2_integer);
        desc.toggled      = lilv_port_has_property(_impl->plugin, port, _world.lv2_toggled);
        desc.logarithmic  = lilv_port_has_property(_impl->plugin, port, _world.ext_logarithmic);
        desc.sr_dependent = lilv_port_has_property(_impl->plugin, port, _world.lv2_sampleRate);
        desc.label        = lilv_node_as_string(lilv_port_get_name(_impl->plugin, port));
+       desc.normal       = def ? lilv_node_as_float(def) : 0.0f;
        desc.lower        = min ? lilv_node_as_float(min) : 0.0f;
        desc.upper        = max ? lilv_node_as_float(max) : 1.0f;
-       desc.midinote     = lilv_nodes_contains(portunits, _world.units_midiNote);
+       load_parameter_descriptor_units(_world.world, desc, portunits);
 
        if (desc.sr_dependent) {
                desc.lower *= _session.frame_rate ();
@@ -1352,18 +1920,10 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c
        desc.min_unbound  = false; // TODO: LV2 extension required
        desc.max_unbound  = false; // TODO: LV2 extension required
 
-       if (desc.integer_step) {
-               desc.step      = 1.0;
-               desc.smallstep = 0.1;
-               desc.largestep = 10.0;
-       } else {
-               const float delta = desc.upper - desc.lower;
-               desc.step      = delta / 1000.0f;
-               desc.smallstep = delta / 10000.0f;
-               desc.largestep = delta / 10.0f;
-       }
-
        desc.enumeration = lilv_port_has_property(_impl->plugin, port, _world.lv2_enumeration);
+       desc.scale_points = get_scale_points(which);
+
+       desc.update_steps();
 
        lilv_node_free(def);
        lilv_node_free(min);
@@ -1373,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)
 {
@@ -1403,6 +2013,12 @@ LV2Plugin::describe_parameter(Evoral::Parameter which)
        }
 }
 
+framecnt_t
+LV2Plugin::max_latency () const
+{
+       return _max_latency;
+}
+
 framecnt_t
 LV2Plugin::signal_latency() const
 {
@@ -1424,9 +2040,31 @@ LV2Plugin::automatable() const
                }
        }
 
+       for (PropertyDescriptors::const_iterator p = _property_descriptors.begin();
+            p != _property_descriptors.end();
+            ++p) {
+               ret.insert(ret.end(), Evoral::Parameter(PluginPropertyAutomation, 0, p->first));
+       }
        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()
 {
@@ -1454,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;
@@ -1512,7 +2149,7 @@ LV2Plugin::allocate_atom_event_buffers()
        _atom_ev_buffers = (LV2_Evbuf**) malloc((total_atom_buffers + 1) * sizeof(LV2_Evbuf*));
        for (int i = 0; i < total_atom_buffers; ++i ) {
                _atom_ev_buffers[i] = lv2_evbuf_new(minimumSize, LV2_EVBUF_ATOM,
-                               LV2Plugin::urids.atom_Chunk, LV2Plugin::urids.atom_Sequence);
+                               _uri_map.urids.atom_Chunk, _uri_map.urids.atom_Sequence);
        }
        _atom_ev_buffers[total_atom_buffers] = 0;
        return;
@@ -1530,25 +2167,46 @@ write_position(LV2_Atom_Forge*     forge,
                framepos_t          position,
                framecnt_t          offset)
 {
+       const URIMap::URIDs& urids = URIMap::instance().urids;
+
        uint8_t pos_buf[256];
        lv2_atom_forge_set_buffer(forge, pos_buf, sizeof(pos_buf));
        LV2_Atom_Forge_Frame frame;
-       lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.time_Position);
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_frame, 0);
+#ifdef HAVE_LV2_1_10_0
+       lv2_atom_forge_object(forge, &frame, 1, urids.time_Position);
+       lv2_atom_forge_key(forge, urids.time_frame);
        lv2_atom_forge_long(forge, position);
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_speed, 0);
+       lv2_atom_forge_key(forge, urids.time_speed);
        lv2_atom_forge_float(forge, speed);
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_barBeat, 0);
+       lv2_atom_forge_key(forge, urids.time_barBeat);
        lv2_atom_forge_float(forge, bbt.beats - 1 +
                             (bbt.ticks / Timecode::BBT_Time::ticks_per_beat));
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_bar, 0);
+       lv2_atom_forge_key(forge, urids.time_bar);
        lv2_atom_forge_long(forge, bbt.bars - 1);
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatUnit, 0);
+       lv2_atom_forge_key(forge, urids.time_beatUnit);
        lv2_atom_forge_int(forge, t.meter().note_divisor());
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerBar, 0);
+       lv2_atom_forge_key(forge, urids.time_beatsPerBar);
        lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatsPerMinute, 0);
+       lv2_atom_forge_key(forge, urids.time_beatsPerMinute);
        lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
+#else
+       lv2_atom_forge_blank(forge, &frame, 1, urids.time_Position);
+       lv2_atom_forge_property_head(forge, urids.time_frame, 0);
+       lv2_atom_forge_long(forge, position);
+       lv2_atom_forge_property_head(forge, urids.time_speed, 0);
+       lv2_atom_forge_float(forge, speed);
+       lv2_atom_forge_property_head(forge, urids.time_barBeat, 0);
+       lv2_atom_forge_float(forge, bbt.beats - 1 +
+                            (bbt.ticks / Timecode::BBT_Time::ticks_per_beat));
+       lv2_atom_forge_property_head(forge, urids.time_bar, 0);
+       lv2_atom_forge_long(forge, bbt.bars - 1);
+       lv2_atom_forge_property_head(forge, urids.time_beatUnit, 0);
+       lv2_atom_forge_int(forge, t.meter().note_divisor());
+       lv2_atom_forge_property_head(forge, urids.time_beatsPerBar, 0);
+       lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
+       lv2_atom_forge_property_head(forge, urids.time_beatsPerMinute, 0);
+       lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
+#endif
 
        LV2_Evbuf_Iterator    end  = lv2_evbuf_end(buf);
        const LV2_Atom* const atom = (const LV2_Atom*)pos_buf;
@@ -1571,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);
@@ -1657,7 +2324,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                        : m;
 
                                // Now merge MIDI and any transport events into the buffer
-                               const uint32_t     type = LV2Plugin::urids.midi_MidiEvent;
+                               const uint32_t     type = _uri_map.urids.midi_MidiEvent;
                                const framepos_t   tend = _session.transport_frame() + nframes;
                                ++metric_i;
                                while (m != m_end || (metric_i != tmap.metrics_end() &&
@@ -1666,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);
@@ -1687,6 +2356,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                _ev_buffers[port_index] = scratch_bufs.get_lv2_midi(
                                        (flags & PORT_INPUT), 0, (flags & PORT_EVENT));
                        }
+
                        buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]);
                } else {
                        continue;  // Control port, leave buffer alone
@@ -1708,11 +2378,11 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                error << "Error reading from UI=>Plugin RingBuffer" << endmsg;
                                break;
                        }
-                       if (msg.protocol == urids.atom_eventTransfer) {
+                       if (msg.protocol == URIMap::instance().urids.atom_eventTransfer) {
                                LV2_Evbuf*            buf  = _ev_buffers[msg.index];
                                LV2_Evbuf_Iterator    i    = lv2_evbuf_end(buf);
                                const LV2_Atom* const atom = (const LV2_Atom*)&body[0];
-                               if (!lv2_evbuf_write(&i, nframes, 0, atom->type, atom->size,
+                               if (!lv2_evbuf_write(&i, nframes - 1, 0, atom->type, atom->size,
                                                (const uint8_t*)(atom + 1))) {
                                        error << "Failed to write data to LV2 event buffer\n";
                                }
@@ -1760,7 +2430,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                }
 
                // Write messages to UI
-               if (_to_ui && (flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
+               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);
                             lv2_evbuf_is_valid(i);
@@ -1768,7 +2439,111 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                uint32_t frames, subframes, type, size;
                                uint8_t* data;
                                lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
-                               write_to_ui(port_index, urids.atom_eventTransfer,
+
+#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));
+                                       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.patch_Set) {
+                                                       const LV2_Atom* property = NULL;
+                                                       const LV2_Atom* value    = NULL;
+                                                       lv2_atom_object_get(obj,
+                                                                           _uri_map.urids.patch_property, &property,
+                                                                           _uri_map.urids.patch_value,    &value,
+                                                                           0);
+
+                                                       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;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               if (!_to_ui) continue;
+                               write_to_ui(port_index, URIMap::instance().urids.atom_eventTransfer,
                                            size + sizeof(LV2_Atom),
                                            data - sizeof(LV2_Atom));
                        }
@@ -1782,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;
 }
 
@@ -1832,18 +2614,18 @@ LV2Plugin::print_parameter(uint32_t param, char* buf, uint32_t len) const
        }
 }
 
-boost::shared_ptr<Plugin::ScalePoints>
+boost::shared_ptr<ScalePoints>
 LV2Plugin::get_scale_points(uint32_t port_index) const
 {
        const LilvPort*  port   = lilv_plugin_get_port_by_index(_impl->plugin, port_index);
        LilvScalePoints* points = lilv_port_get_scale_points(_impl->plugin, port);
 
-       boost::shared_ptr<Plugin::ScalePoints> ret;
+       boost::shared_ptr<ScalePoints> ret;
        if (!points) {
                return ret;
        }
 
-       ret = boost::shared_ptr<Plugin::ScalePoints>(new ScalePoints());
+       ret = boost::shared_ptr<ScalePoints>(new ScalePoints());
 
        LILV_FOREACH(scale_points, i, points) {
                const LilvScalePoint* p     = lilv_scale_points_get(points, i);
@@ -1895,8 +2677,9 @@ LV2Plugin::latency_compute_run()
        uint32_t in_index   = 0;
        uint32_t out_index  = 0;
 
-       const framecnt_t bufsize = 1024;
-       float            buffer[bufsize];
+       // this is done in the main thread. non realtime.
+       const framecnt_t bufsize = _engine.samples_per_cycle();
+       float            *buffer = (float*) malloc(_engine.samples_per_cycle() * sizeof(float));
 
        memset(buffer, 0, sizeof(float) * bufsize);
 
@@ -1922,6 +2705,7 @@ LV2Plugin::latency_compute_run()
        if (was_activated) {
                activate();
        }
+       free(buffer);
 }
 
 const LilvPort*
@@ -1941,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));
 }
 
@@ -1950,8 +2734,6 @@ LV2World::LV2World()
        : world(lilv_world_new())
        , _bundle_checked(false)
 {
-       lilv_world_load_all(world);
-
        atom_AtomPort      = lilv_new_uri(world, LV2_ATOM__AtomPort);
        atom_Chunk         = lilv_new_uri(world, LV2_ATOM__Chunk);
        atom_Sequence      = lilv_new_uri(world, LV2_ATOM__Sequence);
@@ -1966,7 +2748,11 @@ 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);
+       lv2_maximum        = lilv_new_uri(world, LV2_CORE__maximum);
        lv2_reportsLatency = lilv_new_uri(world, LV2_CORE__reportsLatency);
        lv2_sampleRate     = lilv_new_uri(world, LV2_CORE__sampleRate);
        lv2_toggled        = lilv_new_uri(world, LV2_CORE__toggled);
@@ -1974,25 +2760,65 @@ LV2World::LV2World()
        lv2_freewheeling   = lilv_new_uri(world, LV2_CORE__freeWheeling);
        midi_MidiEvent     = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
        rdfs_comment       = lilv_new_uri(world, LILV_NS_RDFS "comment");
+       rdfs_label         = lilv_new_uri(world, LILV_NS_RDFS "label");
+       rdfs_range         = lilv_new_uri(world, LILV_NS_RDFS "range");
        rsz_minimumSize    = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
        time_Position      = lilv_new_uri(world, LV2_TIME__Position);
        ui_GtkUI           = lilv_new_uri(world, LV2_UI__GtkUI);
        ui_external        = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external");
        ui_externalkx      = lilv_new_uri(world, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget");
-       units_unit         = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/units#unit");
-       units_midiNote     = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/units#midiNote");
+       units_unit         = lilv_new_uri(world, LV2_UNITS__unit);
+       units_render       = lilv_new_uri(world, LV2_UNITS__render);
+       units_hz           = lilv_new_uri(world, LV2_UNITS__hz);
+       units_midiNote     = lilv_new_uri(world, LV2_UNITS__midiNote);
+       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);
        lilv_node_free(units_midiNote);
+       lilv_node_free(units_db);
        lilv_node_free(units_unit);
+       lilv_node_free(units_render);
        lilv_node_free(ui_externalkx);
        lilv_node_free(ui_external);
        lilv_node_free(ui_GtkUI);
        lilv_node_free(time_Position);
        lilv_node_free(rsz_minimumSize);
        lilv_node_free(rdfs_comment);
+       lilv_node_free(rdfs_label);
+       lilv_node_free(rdfs_range);
        lilv_node_free(midi_MidiEvent);
        lilv_node_free(lv2_enumeration);
        lilv_node_free(lv2_freewheeling);
@@ -2000,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);
@@ -2015,13 +2842,16 @@ LV2World::~LV2World()
        lilv_node_free(atom_Chunk);
        lilv_node_free(atom_AtomPort);
        lilv_world_free(world);
+       world = NULL;
 }
 
 void
-LV2World::load_bundled_plugins()
+LV2World::load_bundled_plugins(bool verbose)
 {
        if (!_bundle_checked) {
-               cout << "Scanning folders for bundled LV2s: " << ARDOUR::lv2_bundled_search_path().to_string() << endl;
+               if (verbose) {
+                       cout << "Scanning folders for bundled LV2s: " << ARDOUR::lv2_bundled_search_path().to_string() << endl;
+               }
 
                vector<string> plugin_objects;
                find_paths_matching_filter (plugin_objects, ARDOUR::lv2_bundled_search_path(), lv2_filter, 0, true, true, true);
@@ -2036,6 +2866,7 @@ LV2World::load_bundled_plugins()
                        lilv_node_free(node);
                }
 
+               lilv_world_load_all(world);
                _bundle_checked = true;
        }
 }
@@ -2064,7 +2895,7 @@ LV2PluginInfo::load(Session& session)
                if (!lp) { throw failed_constructor(); }
                plugin.reset(new LV2Plugin(session.engine(), session, lp, session.frame_rate()));
                lilv_node_free(uri);
-               plugin->set_info(PluginInfoPtr(new LV2PluginInfo(*this)));
+               plugin->set_info(PluginInfoPtr(shared_from_this ()));
                return plugin;
        } catch (failed_constructor& err) {
                return PluginPtr((Plugin*)0);
@@ -2073,20 +2904,86 @@ 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()
 {
        LV2World world;
        world.load_bundled_plugins();
-       _world.load_bundled_plugins();
+       _world.load_bundled_plugins(true);
 
        PluginInfoList*    plugs   = new PluginInfoList;
        const LilvPlugins* plugins = lilv_world_get_all_plugins(world.world);
 
-       if (!Config->get_show_plugin_scan_window()) {
-               info << "LV2: Discovering " << lilv_plugins_size(plugins) << " plugins" << endmsg;
-       }
-
        LILV_FOREACH(plugins, i, plugins) {
                const LilvPlugin* p = lilv_plugins_get(plugins, i);
                const LilvNode* pun = lilv_plugin_get_uri(p);
@@ -2101,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));