Merge branch 'master' into saveas
[ardour.git] / libs / ardour / lv2_plugin.cc
index 4d5e063c7142e22a61bf6ff51ed127dacd9b6e0e..d98f9a9eccf961438d5aa22e002efbe899f24edb 100644 (file)
@@ -66,6 +66,7 @@
 #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"
+#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"
@@ -78,7 +79,7 @@
 #include <suil/suil.h>
 #endif
 
-// Compatibility for lv2-1.0.0
+// Compatibility for old LV2
 #ifndef LV2_ATOM_CONTENTS_CONST
 #define LV2_ATOM_CONTENTS_CONST(type, atom) \
        ((const void*)((const uint8_t*)(atom) + sizeof(type)))
 #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
@@ -97,34 +107,6 @@ 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_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),
-       _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),
-       _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 {
 public:
        LV2World ();
@@ -151,6 +133,9 @@ public:
        LilvNode* lv2_freewheeling;
        LilvNode* lv2_inPlaceBroken;
        LilvNode* lv2_integer;
+       LilvNode* lv2_default;
+       LilvNode* lv2_minimum;
+       LilvNode* lv2_maximum;
        LilvNode* lv2_reportsLatency;
        LilvNode* lv2_sampleRate;
        LilvNode* lv2_toggled;
@@ -163,7 +148,10 @@ public:
        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;
@@ -220,11 +208,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
@@ -278,6 +266,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine,
        , _insert_id("0")
        , _patch_port_in_index((uint32_t)-1)
        , _patch_port_out_index((uint32_t)-1)
+       , _uri_map(URIMap::instance())
 {
        init(c_plugin, rate);
 }
@@ -291,6 +280,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other)
        , _insert_id(other._insert_id)
        , _patch_port_in_index((uint32_t)-1)
        , _patch_port_out_index((uint32_t)-1)
+       , _uri_map(URIMap::instance())
 {
        init(other._impl->plugin, other._sample_rate);
 
@@ -613,6 +603,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                }
        }
 
+       load_supported_properties(_property_descriptors);
        allocate_atom_event_buffers();
        latency_compute_run();
 }
@@ -923,7 +914,7 @@ LV2Plugin::add_state(XMLNode* root) const
 
        XMLNode*    child;
        char        buf[16];
-       LocaleGuard lg(X_("POSIX"));
+       LocaleGuard lg(X_("C"));
 
        for (uint32_t i = 0; i < parameter_count(); ++i) {
                if (parameter_is_input(i) && parameter_is_control(i)) {
@@ -975,11 +966,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
@@ -993,12 +990,13 @@ 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);
                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_free(name);
                } else {
                        warning << string_compose(
                            _("Plugin \"%1\" preset \"%2\" is missing a label\n"),
@@ -1021,7 +1019,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
        }
 
@@ -1076,11 +1074,13 @@ 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"));
 
        LilvState* state = lilv_state_new_from_instance(
                _impl->plugin,
@@ -1112,7 +1112,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
@@ -1120,20 +1120,41 @@ 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;
+       }
+
+       /* 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
@@ -1224,6 +1245,12 @@ 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;
@@ -1286,6 +1313,9 @@ 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
@@ -1297,15 +1327,15 @@ LV2Plugin::set_property(uint32_t key, const Variant& value)
 
        // 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_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, LV2Plugin::urids.patch_value);
+       lv2_atom_forge_key(forge, _uri_map.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_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, LV2Plugin::urids.patch_value, 0);
+       lv2_atom_forge_property_head(forge, _uri_map.urids.patch_value, 0);
 #endif
 
        forge_variant(forge, value);
@@ -1313,13 +1343,80 @@ LV2Plugin::set_property(uint32_t key, const Variant& 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,
+                     _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::get_supported_properties(std::vector<ParameterDescriptor>& descs)
+LV2Plugin::load_supported_properties(PropertyDescriptors& descs)
 {
        LilvWorld*       lworld     = _world.world;
        const LilvNode*  subject    = lilv_plugin_get_uri(_impl->plugin);
@@ -1328,27 +1425,28 @@ LV2Plugin::get_supported_properties(std::vector<ParameterDescriptor>& descs)
        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);
+               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: unknown variant datatype \"%1\""),
-                                               lilv_node_as_uri(range));
+                       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.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);
+               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);
@@ -1370,15 +1468,15 @@ LV2Plugin::announce_property_values()
 
        // 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);
+       lv2_atom_forge_object(forge, &frame, 1, _uri_map.urids.patch_Get);
 #else
-       lv2_atom_forge_blank(forge, &frame, 1, LV2Plugin::urids.patch_Get);
+       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,
-                     LV2Plugin::urids.atom_eventTransfer,
+                     _uri_map.urids.atom_eventTransfer,
                      lv2_atom_total_size(atom),
                      (const uint8_t*)atom);
 }
@@ -1439,9 +1537,9 @@ LV2Plugin::work_response(uint32_t size, const void* data)
 }
 
 void
-LV2Plugin::set_insert_info(const PluginInsert* insert)
+LV2Plugin::set_insert_id(PBD::ID id)
 {
-       _insert_id = insert->id();
+       _insert_id = id;
 }
 
 int
@@ -1454,7 +1552,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;
@@ -1527,20 +1625,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 ();
@@ -1550,18 +1656,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);
@@ -1622,6 +1720,11 @@ 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;
 }
 
@@ -1710,7 +1813,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;
@@ -1728,42 +1831,44 @@ 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;
 #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_object(forge, &frame, 1, urids.time_Position);
+       lv2_atom_forge_key(forge, urids.time_frame);
        lv2_atom_forge_long(forge, position);
-       lv2_atom_forge_key(forge, LV2Plugin::urids.time_speed);
+       lv2_atom_forge_key(forge, urids.time_speed);
        lv2_atom_forge_float(forge, speed);
-       lv2_atom_forge_key(forge, LV2Plugin::urids.time_barBeat);
+       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_key(forge, LV2Plugin::urids.time_bar);
+       lv2_atom_forge_key(forge, urids.time_bar);
        lv2_atom_forge_long(forge, bbt.bars - 1);
-       lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatUnit);
+       lv2_atom_forge_key(forge, urids.time_beatUnit);
        lv2_atom_forge_int(forge, t.meter().note_divisor());
-       lv2_atom_forge_key(forge, LV2Plugin::urids.time_beatsPerBar);
+       lv2_atom_forge_key(forge, 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_key(forge, 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_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, LV2Plugin::urids.time_speed, 0);
+       lv2_atom_forge_property_head(forge, urids.time_speed, 0);
        lv2_atom_forge_float(forge, speed);
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_barBeat, 0);
+       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, LV2Plugin::urids.time_bar, 0);
+       lv2_atom_forge_property_head(forge, urids.time_bar, 0);
        lv2_atom_forge_long(forge, bbt.bars - 1);
-       lv2_atom_forge_property_head(forge, LV2Plugin::urids.time_beatUnit, 0);
+       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, LV2Plugin::urids.time_beatsPerBar, 0);
+       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, LV2Plugin::urids.time_beatsPerMinute, 0);
+       lv2_atom_forge_property_head(forge, urids.time_beatsPerMinute, 0);
        lv2_atom_forge_float(forge, t.tempo().beats_per_minute());
 #endif
 
@@ -1874,7 +1979,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() &&
@@ -1926,11 +2031,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";
                                }
@@ -1992,20 +2097,20 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                // 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) {
+                                       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 == LV2Plugin::urids.patch_Set) {
+                                               if (obj->body.otype == _uri_map.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,
+                                                                           _uri_map.urids.patch_property, &property,
+                                                                           _uri_map.urids.patch_value,    &value,
                                                                            0);
 
                                                        if (!property || !value ||
-                                                           property->type != LV2Plugin::urids.atom_URID ||
-                                                           value->type != LV2Plugin::urids.atom_Path) {
+                                                           property->type != _uri_map.urids.atom_URID ||
+                                                           value->type    != _uri_map.urids.atom_Path) {
                                                                std::cerr << "warning: patch:Set for unknown property" << std::endl;
                                                                continue;
                                                        }
@@ -2014,13 +2119,14 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
                                                        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));
                                                }
                                        }
                                }
 
                                if (!_to_ui) continue;
-                               write_to_ui(port_index, urids.atom_eventTransfer,
+                               write_to_ui(port_index, URIMap::instance().urids.atom_eventTransfer,
                                            size + sizeof(LV2_Atom),
                                            data - sizeof(LV2_Atom));
                        }
@@ -2084,18 +2190,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);
@@ -2204,8 +2310,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);
@@ -2221,6 +2325,9 @@ LV2World::LV2World()
        lv2_OutputPort     = lilv_new_uri(world, LILV_URI_OUTPUT_PORT);
        lv2_inPlaceBroken  = lilv_new_uri(world, LV2_CORE__inPlaceBroken);
        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);
@@ -2235,8 +2342,11 @@ LV2World::LV2World()
        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);
 }
@@ -2245,8 +2355,11 @@ LV2World::~LV2World()
 {
        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);
@@ -2300,6 +2413,7 @@ LV2World::load_bundled_plugins(bool verbose)
                        lilv_node_free(node);
                }
 
+               lilv_world_load_all(world);
                _bundle_checked = true;
        }
 }
@@ -2347,10 +2461,6 @@ LV2PluginInfo::discover()
        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);