Move ParameterDescriptor from Plugin to its own header.
[ardour.git] / libs / ardour / lv2_plugin.cc
index a33b01cbd939a43a55284b51f21f4e719cc1e015..4f41b51d1b11ac414f738d71e1a81e30f65b98f6 100644 (file)
@@ -31,7 +31,6 @@
 
 #include <boost/utility.hpp>
 
-#include "pbd/clear_dir.h"
 #include "pbd/file_utils.h"
 #include "pbd/stl_delete.h"
 #include "pbd/compose.h"
@@ -67,7 +66,8 @@
 #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/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 lv2-1.0.0
+#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
+
 /** 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.
@@ -95,6 +104,9 @@ LV2Plugin::URIDs LV2Plugin::urids = {
        _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_ATOM__URID),
+       _uri_map.uri_to_id(LV2_ATOM__Blank),
+       _uri_map.uri_to_id(LV2_ATOM__Object),
        _uri_map.uri_to_id(LV2_LOG__Error),
        _uri_map.uri_to_id(LV2_LOG__Note),
        _uri_map.uri_to_id(LV2_LOG__Warning),
@@ -106,7 +118,11 @@ LV2Plugin::URIDs LV2Plugin::urids = {
        _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)
+       _uri_map.uri_to_id(LV2_TIME__speed),
+       _uri_map.uri_to_id(LV2_PATCH__Get),
+       _uri_map.uri_to_id(LV2_PATCH__Set),
+       _uri_map.uri_to_id(LV2_PATCH__property),
+       _uri_map.uri_to_id(LV2_PATCH__value)
 };
 
 class LV2World : boost::noncopyable {
@@ -114,7 +130,7 @@ public:
        LV2World ();
        ~LV2World ();
 
-       void load_bundled_plugins();
+       void load_bundled_plugins(bool verbose=false);
 
        LilvWorld* world;
 
@@ -140,6 +156,8 @@ public:
        LilvNode* lv2_toggled;
        LilvNode* midi_MidiEvent;
        LilvNode* rdfs_comment;
+       LilvNode* rdfs_label;
+       LilvNode* rdfs_range;
        LilvNode* rsz_minimumSize;
        LilvNode* time_Position;
        LilvNode* ui_GtkUI;
@@ -147,6 +165,8 @@ public:
        LilvNode* ui_externalkx;
        LilvNode* units_unit;
        LilvNode* units_midiNote;
+       LilvNode* patch_writable;
+       LilvNode* patch_Message;
 
 private:
        bool _bundle_checked;
@@ -243,6 +263,7 @@ struct LV2Plugin::Impl {
        const LV2_Worker_Interface* work_iface;
        LilvState*                  state;
        LV2_Atom_Forge              forge;
+       LV2_Atom_Forge              ui_forge;
 };
 
 LV2Plugin::LV2Plugin (AudioEngine& engine,
@@ -255,6 +276,8 @@ 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)
 {
        init(c_plugin, rate);
 }
@@ -266,6 +289,8 @@ 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)
 {
        init(other._impl->plugin, other._sample_rate);
 
@@ -331,13 +356,14 @@ 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 HAVE_NEW_LV2
+#ifdef HAVE_LV2_1_2_0
        LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int);
        LV2_Options_Option options[] = {
                { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__minBlockLength),
@@ -408,7 +434,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                throw failed_constructor();
        }
 
-#ifdef HAVE_NEW_LILV
+#ifdef HAVE_LILV_0_16_0
        // Load default state
        LilvState* state = lilv_state_new_from_world(
                _world.world, _uri_map.urid_map(), lilv_plugin_get_uri(_impl->plugin));
@@ -457,6 +483,14 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                                if (lilv_nodes_contains(atom_supports, _world.time_Position)) {
                                        flags |= PORT_POSITION;
                                }
+                               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;
@@ -1078,6 +1112,10 @@ 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
+       lilv_world_unload_resource(_world.world, node_preset);
+       lilv_world_unload_bundle(_world.world, node_bundle);
+#endif
        lilv_world_load_bundle(_world.world, node_bundle);
        lilv_world_load_resource(_world.world, node_preset);
        lilv_node_free(node_bundle);
@@ -1182,8 +1220,176 @@ 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::VOID:
+               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::VOID) {
+               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, LV2Plugin::urids.patch_Set);
+       lv2_atom_forge_key(forge, LV2Plugin::urids.patch_property);
+       lv2_atom_forge_urid(forge, key);
+       lv2_atom_forge_key(forge, LV2Plugin::urids.patch_value);
+#else
+       lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.patch_Set);
+       lv2_atom_forge_property_head(forge, LV2Plugin::urids.patch_property, 0);
+       lv2_atom_forge_urid(forge, key);
+       lv2_atom_forge_property_head(forge, LV2Plugin::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,
+                     LV2Plugin::urids.atom_eventTransfer,
+                     lv2_atom_total_size(atom),
+                     (const uint8_t*)atom);
+}
+
+void
+LV2Plugin::get_supported_properties(std::vector<ParameterDescriptor>& 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*       label = lilv_world_get(lworld, prop, _world.rdfs_label, NULL);
+               LilvNode*       range = lilv_world_get(lworld, prop, _world.rdfs_range, NULL);
+
+               // 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: unknown variant datatype \"%1\""),
+                                               lilv_node_as_uri(range));
+                       continue;
+               }
+
+               // Add description to result
+               ParameterDescriptor desc;
+               desc.key          = _uri_map.uri_to_id(lilv_node_as_uri(prop));
+               desc.label        = lilv_node_as_string(label);
+               desc.datatype     = datatype;
+               desc.toggled      = datatype == Variant::BOOL;
+               desc.integer_step = datatype == Variant::INT || datatype == Variant::LONG;
+               descs.push_back(desc);
+
+               lilv_node_free(label);
+               lilv_node_free(range);
+       }
+       lilv_nodes_free(properties);
+}
+
 void
-LV2Plugin::enable_ui_emmission()
+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, LV2Plugin::urids.patch_Get);
+#else
+       lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::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,
+                     LV2Plugin::urids.atom_eventTransfer,
+                     lv2_atom_total_size(atom),
+                     (const uint8_t*)atom);
+}
+
+void
+LV2Plugin::enable_ui_emission()
 {
        if (!_to_ui) {
                /* see note in LV2Plugin::write_from_ui() */
@@ -1361,6 +1567,7 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c
        }
 
        desc.enumeration = lilv_port_has_property(_impl->plugin, port, _world.lv2_enumeration);
+       desc.scale_points = get_scale_points(which);
 
        lilv_node_free(def);
        lilv_node_free(min);
@@ -1530,6 +1737,24 @@ write_position(LV2_Atom_Forge*     forge,
        uint8_t pos_buf[256];
        lv2_atom_forge_set_buffer(forge, pos_buf, sizeof(pos_buf));
        LV2_Atom_Forge_Frame frame;
+#ifdef HAVE_LV2_1_10_0
+       lv2_atom_forge_object(forge, &frame, 1, LV2Plugin::urids.time_Position);
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_frame);
+       lv2_atom_forge_long(forge, position);
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_speed);
+       lv2_atom_forge_float(forge, speed);
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_barBeat);
+       lv2_atom_forge_float(forge, bbt.beats - 1 +
+                            (bbt.ticks / Timecode::BBT_Time::ticks_per_beat));
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_bar);
+       lv2_atom_forge_long(forge, bbt.bars - 1);
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatUnit);
+       lv2_atom_forge_int(forge, t.meter().note_divisor());
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerBar);
+       lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
+       lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerMinute);
+       lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
+#else
        lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.time_Position);
        lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_frame, 0);
        lv2_atom_forge_long(forge, position);
@@ -1546,6 +1771,7 @@ write_position(LV2_Atom_Forge*     forge,
        lv2_atom_forge_float(forge, t.meter().divisions_per_bar());
        lv2_atom_forge_property_head(forge, LV2Plugin::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;
@@ -1684,6 +1910,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
@@ -1756,8 +1983,10 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                        }
                }
 
+
                // Write messages to UI
-               if (_to_ui && (flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) {
+               if ((_to_ui || _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);
@@ -1765,6 +1994,38 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                uint32_t frames, subframes, type, size;
                                uint8_t* data;
                                lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
+
+                               // Intercept patch change messages to emit PropertyChanged signal
+                               if ((flags & PORT_PATCHMSG)) {
+                                       LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom));
+                                       if (atom->type == LV2Plugin::urids.atom_Blank ||
+                                           atom->type == LV2Plugin::urids.atom_Object) {
+                                               LV2_Atom_Object* obj = (LV2_Atom_Object*)atom;
+                                               if (obj->body.otype == LV2Plugin::urids.patch_Set) {
+                                                       const LV2_Atom* property = NULL;
+                                                       const LV2_Atom* value    = NULL;
+                                                       lv2_atom_object_get(obj,
+                                                                           LV2Plugin::urids.patch_property, &property,
+                                                                           LV2Plugin::urids.patch_value,    &value,
+                                                                           0);
+
+                                                       if (!property || !value ||
+                                                           property->type != LV2Plugin::urids.atom_URID ||
+                                                           value->type != LV2Plugin::urids.atom_Path) {
+                                                               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
+                                                       PropertyChanged(prop_id, Variant(Variant::PATH, path));
+                                               }
+                                       }
+                               }
+
+                               if (!_to_ui) continue;
                                write_to_ui(port_index, urids.atom_eventTransfer,
                                            size + sizeof(LV2_Atom),
                                            data - sizeof(LV2_Atom));
@@ -1829,18 +2090,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);
@@ -1885,14 +2146,16 @@ LV2Plugin::latency_compute_run()
 
        // Run the plugin so that it can set its latency parameter
 
+       bool was_activated = _was_activated;
        activate();
 
        uint32_t port_index = 0;
        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);
 
@@ -1915,6 +2178,10 @@ LV2Plugin::latency_compute_run()
 
        run(bufsize);
        deactivate();
+       if (was_activated) {
+               activate();
+       }
+       free(buffer);
 }
 
 const LilvPort*
@@ -1967,6 +2234,8 @@ 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);
@@ -1974,10 +2243,14 @@ LV2World::LV2World()
        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");
+       patch_writable     = lilv_new_uri(world, LV2_PATCH__writable);
+       patch_Message      = lilv_new_uri(world, LV2_PATCH__Message);
 }
 
 LV2World::~LV2World()
 {
+       lilv_node_free(patch_Message);
+       lilv_node_free(patch_writable);
        lilv_node_free(units_midiNote);
        lilv_node_free(units_unit);
        lilv_node_free(ui_externalkx);
@@ -1986,6 +2259,8 @@ LV2World::~LV2World()
        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);
@@ -2007,16 +2282,19 @@ LV2World::~LV2World()
        lilv_node_free(atom_Sequence);
        lilv_node_free(atom_Chunk);
        lilv_node_free(atom_AtomPort);
+       lilv_world_free(world);
 }
 
 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_files_matching_filter (plugin_objects, ARDOUR::lv2_bundled_search_path(), lv2_filter, 0, true, true);
+               find_paths_matching_filter (plugin_objects, ARDOUR::lv2_bundled_search_path(), lv2_filter, 0, true, true, true);
                for ( vector<string>::iterator x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
 #ifdef PLATFORM_WINDOWS
                        string uri = "file:///" + *x + "/";
@@ -2032,26 +2310,31 @@ LV2World::load_bundled_plugins()
        }
 }
 
-LV2PluginInfo::LV2PluginInfo (const void* c_plugin)
-       : _c_plugin(c_plugin)
+LV2PluginInfo::LV2PluginInfo (const char* plugin_uri)
 {
        type = ARDOUR::LV2;
+       _plugin_uri = strdup(plugin_uri);
 }
 
 LV2PluginInfo::~LV2PluginInfo()
-{}
+{
+       free(_plugin_uri);
+       _plugin_uri = NULL;
+}
 
 PluginPtr
 LV2PluginInfo::load(Session& session)
 {
        try {
                PluginPtr plugin;
-
-               plugin.reset(new LV2Plugin(session.engine(), session,
-                                          (const LilvPlugin*)_c_plugin,
-                                          session.frame_rate()));
-
-               plugin->set_info(PluginInfoPtr(new LV2PluginInfo(*this)));
+               const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world);
+               LilvNode* uri = lilv_new_uri(_world.world, _plugin_uri);
+               if (!uri) { throw failed_constructor(); }
+               const LilvPlugin* lp = lilv_plugins_get_by_uri(plugins, uri);
+               if (!lp) { throw failed_constructor(); }
+               plugin.reset(new LV2Plugin(session.engine(), session, lp, session.frame_rate()));
+               lilv_node_free(uri);
+               plugin->set_info(PluginInfoPtr(shared_from_this ()));
                return plugin;
        } catch (failed_constructor& err) {
                return PluginPtr((Plugin*)0);
@@ -2063,10 +2346,12 @@ LV2PluginInfo::load(Session& session)
 PluginInfoList*
 LV2PluginInfo::discover()
 {
-       _world.load_bundled_plugins();
+       LV2World world;
+       world.load_bundled_plugins();
+       _world.load_bundled_plugins(true);
 
        PluginInfoList*    plugs   = new PluginInfoList;
-       const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world);
+       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;
@@ -2074,7 +2359,9 @@ LV2PluginInfo::discover()
 
        LILV_FOREACH(plugins, i, plugins) {
                const LilvPlugin* p = lilv_plugins_get(plugins, i);
-               LV2PluginInfoPtr  info(new LV2PluginInfo((const void*)p));
+               const LilvNode* pun = lilv_plugin_get_uri(p);
+               if (!pun) continue;
+               LV2PluginInfoPtr info(new LV2PluginInfo(lilv_node_as_string(pun)));
 
                LilvNode* name = lilv_plugin_get_name(p);
                if (!name || !lilv_plugin_get_port_by_index(p, 0)) {
@@ -2109,18 +2396,18 @@ LV2PluginInfo::discover()
                int count_midi_in = 0;
                for (uint32_t i = 0; i < lilv_plugin_get_num_ports(p); ++i) {
                        const LilvPort* port  = lilv_plugin_get_port_by_index(p, i);
-                       if (lilv_port_is_a(p, port, _world.atom_AtomPort)) {
+                       if (lilv_port_is_a(p, port, world.atom_AtomPort)) {
                                LilvNodes* buffer_types = lilv_port_get_value(
-                                       p, port, _world.atom_bufferType);
+                                       p, port, world.atom_bufferType);
                                LilvNodes* atom_supports = lilv_port_get_value(
-                                       p, port, _world.atom_supports);
+                                       p, port, world.atom_supports);
 
-                               if (lilv_nodes_contains(buffer_types, _world.atom_Sequence)
-                                               && lilv_nodes_contains(atom_supports, _world.midi_MidiEvent)) {
-                                       if (lilv_port_is_a(p, port, _world.lv2_InputPort)) {
+                               if (lilv_nodes_contains(buffer_types, world.atom_Sequence)
+                                               && lilv_nodes_contains(atom_supports, world.midi_MidiEvent)) {
+                                       if (lilv_port_is_a(p, port, world.lv2_InputPort)) {
                                                count_midi_in++;
                                        }
-                                       if (lilv_port_is_a(p, port, _world.lv2_OutputPort)) {
+                                       if (lilv_port_is_a(p, port, world.lv2_OutputPort)) {
                                                count_midi_out++;
                                        }
                                }
@@ -2131,18 +2418,18 @@ LV2PluginInfo::discover()
 
                info->n_inputs.set_audio(
                        lilv_plugin_get_num_ports_of_class(
-                               p, _world.lv2_InputPort, _world.lv2_AudioPort, NULL));
+                               p, world.lv2_InputPort, world.lv2_AudioPort, NULL));
                info->n_inputs.set_midi(
                        lilv_plugin_get_num_ports_of_class(
-                               p, _world.lv2_InputPort, _world.ev_EventPort, NULL)
+                               p, world.lv2_InputPort, world.ev_EventPort, NULL)
                        + count_midi_in);
 
                info->n_outputs.set_audio(
                        lilv_plugin_get_num_ports_of_class(
-                               p, _world.lv2_OutputPort, _world.lv2_AudioPort, NULL));
+                               p, world.lv2_OutputPort, world.lv2_AudioPort, NULL));
                info->n_outputs.set_midi(
                        lilv_plugin_get_num_ports_of_class(
-                               p, _world.lv2_OutputPort, _world.ev_EventPort, NULL)
+                               p, world.lv2_OutputPort, world.ev_EventPort, NULL)
                        + count_midi_out);
 
                info->unique_id = lilv_node_as_uri(lilv_plugin_get_uri(p));