Automation of LV2 plugin properties.
authorDavid Robillard <d@drobilla.net>
Sun, 2 Nov 2014 03:29:10 +0000 (23:29 -0400)
committerDavid Robillard <d@drobilla.net>
Sun, 2 Nov 2014 07:10:24 +0000 (02:10 -0500)
Work towards ParameterDescriptor being used more universally to describe control characteristics.

39 files changed:
gtk2_ardour/automation_controller.cc
gtk2_ardour/automation_controller.h
gtk2_ardour/automation_line.cc
gtk2_ardour/automation_line.h
gtk2_ardour/automation_time_axis.cc
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/lv2_plugin_ui.cc
gtk2_ardour/plugin_ui.h
gtk2_ardour/route_time_axis.cc
libs/ardour/ardour/amp.h
libs/ardour/ardour/automatable_sequence.h
libs/ardour/ardour/automation_control.h
libs/ardour/ardour/event_type_map.h
libs/ardour/ardour/lv2_plugin.h
libs/ardour/ardour/midi_buffer.h
libs/ardour/ardour/midi_track.h
libs/ardour/ardour/pan_controllable.h
libs/ardour/ardour/parameter_descriptor.h
libs/ardour/ardour/parameter_types.h [new file with mode: 0644]
libs/ardour/ardour/plugin.h
libs/ardour/ardour/plugin_insert.h
libs/ardour/ardour/types.h
libs/ardour/ardour/uri_map.h
libs/ardour/ardour/variant.h
libs/ardour/automatable.cc
libs/ardour/automation_control.cc
libs/ardour/buffer_set.cc
libs/ardour/enums.cc
libs/ardour/event_type_map.cc
libs/ardour/globals.cc
libs/ardour/lv2_plugin.cc
libs/ardour/midi_playlist.cc
libs/ardour/midi_state_tracker.cc
libs/ardour/midi_track.cc
libs/ardour/plugin_insert.cc
libs/ardour/route.cc
libs/ardour/smf_source.cc
libs/ardour/track.cc
libs/ardour/uri_map.cc

index 564387a55e6e54d5b2cf8410cde667b22cc89ae1..c4cc93386f6f98f9982add6d17b7d51502fc33f4 100644 (file)
@@ -36,7 +36,9 @@
 using namespace ARDOUR;
 using namespace Gtk;
 
-AutomationController::AutomationController(boost::shared_ptr<Automatable> printer, boost::shared_ptr<AutomationControl> ac, Adjustment* adj)
+AutomationController::AutomationController(boost::shared_ptr<Automatable>       printer,
+                                           boost::shared_ptr<AutomationControl> ac,
+                                           Adjustment*                          adj)
        : BarController (*adj, ac)
        , _ignore_change(false)
        , _printer (printer)
@@ -64,21 +66,27 @@ AutomationController::~AutomationController()
 }
 
 boost::shared_ptr<AutomationController>
-AutomationController::create(
-               boost::shared_ptr<Automatable> printer,
-               const Evoral::Parameter& param,
-               boost::shared_ptr<AutomationControl> ac)
+AutomationController::create(boost::shared_ptr<Automatable>       printer,
+                             const Evoral::Parameter&             param,
+                             const ParameterDescriptor&           desc,
+                             boost::shared_ptr<AutomationControl> ac)
 {
-       double const lo = ac->internal_to_interface(param.min());
-       double const up = ac->internal_to_interface(param.max());
+       const double lo        = ac->internal_to_interface(desc.lower);
+       const double up        = ac->internal_to_interface(desc.upper);
+       const double normal    = ac->internal_to_interface(desc.normal);
+       double       smallstep = desc.smallstep;
+       double       largestep = desc.largestep;
+       if (smallstep == 0.0) {
+               smallstep = (up - lo) / 100;
+       }
+       if (largestep == 0.0) {
+               largestep = (up - lo) / 10;
+       }
+       smallstep = ac->internal_to_interface(smallstep);
+       largestep = ac->internal_to_interface(largestep);
+
        Gtk::Adjustment* adjustment = manage (
-                       new Gtk::Adjustment (
-                               ac->internal_to_interface(param.normal()),
-                               lo, up,
-                               // TODO we should use explicit step-sizes if provided by Plugin::ParameterDescriptor
-                               (up - lo) / 100, (up - lo) / 10
-                               )
-                       );
+               new Gtk::Adjustment (normal, lo, up, smallstep, largestep));
 
        assert (ac);
        assert(ac->parameter() == param);
index 29635c848869d2bed9ae98f5f3c955a78c61c541..e5799cc519d612cc95efa6e0ca5e0c4b2bf26e29 100644 (file)
@@ -46,9 +46,10 @@ namespace ARDOUR {
 class AutomationController : public Gtkmm2ext::BarController {
 public:
        static boost::shared_ptr<AutomationController> create(
-                       boost::shared_ptr<ARDOUR::Automatable> parent,
-                       const Evoral::Parameter& param,
-                       boost::shared_ptr<ARDOUR::AutomationControl> ac);
+               boost::shared_ptr<ARDOUR::Automatable>       parent,
+               const Evoral::Parameter&                     param,
+               const ARDOUR::ParameterDescriptor&           desc,
+               boost::shared_ptr<ARDOUR::AutomationControl> ac);
 
        ~AutomationController();
 
@@ -62,7 +63,9 @@ public:
        void stop_updating ();
 
 private:
-       AutomationController (boost::shared_ptr<ARDOUR::Automatable> printer, boost::shared_ptr<ARDOUR::AutomationControl> ac, Gtk::Adjustment* adj);
+       AutomationController (boost::shared_ptr<ARDOUR::Automatable>       printer,
+                             boost::shared_ptr<ARDOUR::AutomationControl> ac,
+                             Gtk::Adjustment*                             adj);
        std::string get_label (double&);
 
        void start_touch();
index b22681f1f825691b25abaa5d53a0cd3e7b120aa3..c1549ecab3f6ae26b565d0e168a5102e2201f7ae 100644 (file)
@@ -69,8 +69,10 @@ using namespace Editing;
 /** @param converter A TimeConverter whose origin_b is the start time of the AutomationList in session frames.
  *  This will not be deleted by AutomationLine.
  */
-AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Item& parent,
-                                boost::shared_ptr<AutomationList> al,
+AutomationLine::AutomationLine (const string&                              name,
+                                TimeAxisView&                              tv,
+                                ArdourCanvas::Item&                        parent,
+                                boost::shared_ptr<AutomationList>          al,
                                 Evoral::TimeConverter<double, framepos_t>* converter)
        : trackview (tv)
        , _name (name)
@@ -1160,7 +1162,7 @@ AutomationLine::view_to_model_coord (double& x, double& y) const
 void
 AutomationLine::view_to_model_coord_y (double& y) const
 {
-       /* TODO: This should be more generic ... */
+       /* TODO: This should be more generic (use ParameterDescriptor) */
        if (alist->parameter().type() == GainAutomation ||
            alist->parameter().type() == EnvelopeAutomation) {
                y = slider_position_to_gain_with_max (y, Config->get_max_gain());
@@ -1171,17 +1173,15 @@ AutomationLine::view_to_model_coord_y (double& y) const
                y = 1.0 - y;
        } else if (alist->parameter().type() == PanWidthAutomation) {
                y = 2.0 * y - 1.0;
-       } else if (alist->parameter().type() == PluginAutomation) {
-               y = y * (double)(alist->get_max_y()- alist->get_min_y()) + alist->get_min_y();
        } else {
-               y = rint (y * alist->parameter().max());
+               y = y * (double)(alist->get_max_y() - alist->get_min_y()) + alist->get_min_y();
        }
 }
 
 void
 AutomationLine::model_to_view_coord (double& x, double& y) const
 {
-       /* TODO: This should be more generic ... */
+       /* TODO: This should be more generic (use ParameterDescriptor) */
        if (alist->parameter().type() == GainAutomation ||
            alist->parameter().type() == EnvelopeAutomation) {
                y = gain_to_slider_position_with_max (y, Config->get_max_gain());
@@ -1190,10 +1190,8 @@ AutomationLine::model_to_view_coord (double& x, double& y) const
                y = 1.0 - y;
        } else if (alist->parameter().type() == PanWidthAutomation) {
                y = .5 + y * .5;
-       } else if (alist->parameter().type() == PluginAutomation) {
-               y = (y - alist->get_min_y()) / (double)(alist->get_max_y()- alist->get_min_y());
        } else {
-               y = y / (double)alist->parameter().max(); /* ... like this */
+               y = (y - alist->get_min_y()) / (double)(alist->get_max_y() - alist->get_min_y());
        }
 
        x = _time_converter->to (x) - _offset;
index 663310dc46c9d339e6433c219606c2e05c13847f..02c67d0dcf4abd630dffb58952c9f5453d3a1d1d 100644 (file)
@@ -60,9 +60,12 @@ public:
                SelectedControlPoints = 0x4
        };
        
-       AutomationLine (const std::string& name, TimeAxisView&, ArdourCanvas::Item&,
-                       boost::shared_ptr<ARDOUR::AutomationList>,
+       AutomationLine (const std::string&                                 name,
+                       TimeAxisView&                                      tv,
+                       ArdourCanvas::Item&                                parent,
+                       boost::shared_ptr<ARDOUR::AutomationList>          al,
                        Evoral::TimeConverter<double, ARDOUR::framepos_t>* converter = 0);
+
        virtual ~AutomationLine ();
 
        void queue_reset ();
index 87f2a2e26a3c5fc56a57741206594f7e14ab3fae..a49bcf00868f9ee333abf1ba82e1771038f18de1 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "ardour/automation_control.h"
 #include "ardour/event_type_map.h"
+#include "ardour/parameter_types.h"
 #include "ardour/profile.h"
 #include "ardour/route.h"
 #include "ardour/session.h"
@@ -103,7 +104,7 @@ AutomationTimeAxisView::AutomationTimeAxisView (
        }
 
        if (_automatable && _control) {
-               _controller = AutomationController::create (_automatable, _control->parameter(), _control);
+               _controller = AutomationController::create (_automatable, _control->parameter(), _control->desc(), _control);
        }
 
        automation_menu = 0;
@@ -559,7 +560,7 @@ AutomationTimeAxisView::build_display_menu ()
        /* current interpolation state */
        AutomationList::InterpolationStyle const s = _view ? _view->interpolation() : _control->list()->interpolation ();
 
-       if (EventTypeMap::instance().is_midi_parameter(_parameter)) {
+       if (ARDOUR::parameter_is_midi((AutomationType)_parameter.type())) {
 
                Menu* auto_mode_menu = manage (new Menu);
                auto_mode_menu->set_name ("ArdourContextMenu");
@@ -838,7 +839,7 @@ AutomationTimeAxisView::what_has_visible_automation (const boost::shared_ptr<Aut
                
                boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (i->second);
 
-               if (ac) {
+               if (ac && ac->alist()) {
                        
                        const XMLNode* gui_node = ac->extra_xml ("GUI");
                        
index 459c8d54d9a7e76f2be8b991f1f7532b6c9f922a..fb35882b348e41a8e9a0990d43de00746594b94e 100644 (file)
@@ -239,9 +239,9 @@ GenericPluginUI::build ()
        frame->add (*box);
        hpacker.pack_start(*frame, true, true);
 
-       /* find all ports. build control elements for all appropriate control ports */
-       std::vector<ControlUI *> cui_controls_list;
+       std::vector<ControlUI *> control_uis;
 
+       // Build a ControlUI for each control port
        for (i = 0; i < plugin->parameter_count(); ++i) {
 
                if (plugin->parameter_is_control (i)) {
@@ -274,77 +274,73 @@ GenericPluginUI::build ()
                                ARDOUR_UI::instance()->set_tip(cui, param_docs.c_str());
                        }
 
-                       if (cui->controller || cui->clickbox || cui->combo) {
-                               // Get all of the controls into a list, so that
-                               // we can lay them out a bit more nicely later.
-                               cui_controls_list.push_back(cui);
-                       } else if (cui->button) {
-
-                               if (!is_scrollable && button_row == button_rows) {
-                                       button_row = 0;
-                                       if (++button_col == button_cols) {
-                                               button_cols += 2;
-                                               button_table.resize (button_rows, button_cols);
-                                       }
-                               }
+                       control_uis.push_back(cui);
+               }
+       }
+
+       // Build a ControlUI for each property
+       const Plugin::PropertyDescriptors& descs = plugin->get_supported_properties();
+       for (Plugin::PropertyDescriptors::const_iterator d = descs.begin(); d != descs.end(); ++d) {
+               const ParameterDescriptor& desc = d->second;
 
-                               button_table.attach (*cui, button_col, button_col + 1, button_row, button_row+1,
-                                                    FILL|EXPAND, FILL);
-                               button_row++;
+               boost::shared_ptr<ARDOUR::AutomationControl> c
+                       = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
+                               insert->control(Evoral::Parameter(PluginPropertyAutomation, 0, desc.key)));
 
-                       } else if (cui->display) {
+               if (!c) {
+                       error << string_compose(_("Plugin Editor: no control for property %1"), desc.key) << endmsg;
+                       continue;
+               }
 
-                               output_table.attach (*cui, output_col, output_col + 1, output_row, output_row+1,
-                                                    FILL|EXPAND, FILL);
+               ControlUI* cui = build_control_ui(desc, c, true);
+               if (!cui) {
+                       error << string_compose(_("Plugin Editor: could not build control element for property %1"),
+                                               desc.key) << endmsg;
+                       continue;
+               }
 
-                               // TODO: The meters should be divided into multiple rows
+               control_uis.push_back(cui);
+       }
+       if (!descs.empty()) {
+               plugin->announce_property_values();
+       }
 
-                               if (++output_col == output_cols) {
-                                       output_cols ++;
-                                       output_table.resize (output_rows, output_cols);
+       // Add special controls to UI, and build list of normal controls to be layed out later
+       std::vector<ControlUI *> cui_controls_list;
+       for (i = 0; i < control_uis.size(); ++i) {
+               ControlUI* cui = control_uis[i];
+
+               if (cui->controller || cui->clickbox || cui->combo) {
+                       // Get all of the controls into a list, so that
+                       // we can lay them out a bit more nicely later.
+                       cui_controls_list.push_back(cui);
+               } else if (cui->button || cui->file_button) {
+
+                       if (!is_scrollable && button_row == button_rows) {
+                               button_row = 0;
+                               if (++button_col == button_cols) {
+                                       button_cols += 2;
+                                       button_table.resize (button_rows, button_cols);
                                }
                        }
-               } 
-       }
-
-       // Add property controls (currently file chooser button for paths only)
-       typedef std::vector<ParameterDescriptor> Descs;
-       Descs descs;
-       plugin->get_supported_properties(descs);
-       for (Descs::const_iterator d = descs.begin(); d != descs.end(); ++d) {
-               if (d->datatype == Variant::PATH) {
-                       // Create/add label
-                       Gtk::Label* label = manage(new Label(d->label));
-                       button_table.attach(*label,
-                                           0, button_cols, button_row, button_row + 1,
-                                           FILL|EXPAND, FILL);
-                       ++button_row;
 
-                       // Create/add controller
-                       Gtk::FileChooserButton* widget = manage(
-                               new Gtk::FileChooserButton(Gtk::FILE_CHOOSER_ACTION_OPEN));
-                       widget->set_title(d->label);
-                       _property_controls.insert(std::make_pair(d->key, widget));
-                       button_table.attach(*widget,
-                                           0, button_cols, button_row, button_row + 1,
-                                           FILL|EXPAND, FILL);
-                       ++button_row;
-
-                       // Connect signals
-                       widget->signal_file_set().connect(
-                               sigc::bind(sigc::mem_fun(*this, &GenericPluginUI::set_property), *d, widget));
-                       plugin->PropertyChanged.connect(*this, invalidator(*this),
-                                                       boost::bind(&GenericPluginUI::property_changed, this, _1, _2),
-                                                       gui_context());
-               } else {
-                       // TODO: widgets for other datatypes, use ControlUI?
-                       std::cerr << "warning: unsupported property " << d->key
-                                 << " type " << d->datatype << std::endl;
+                       button_table.attach (*cui, button_col, button_col + 1, button_row, button_row+1,
+                                            FILL|EXPAND, FILL);
+                       button_row++;
+
+               } else if (cui->display) {
+
+                       output_table.attach (*cui, output_col, output_col + 1, output_row, output_row+1,
+                                            FILL|EXPAND, FILL);
+
+                       // TODO: The meters should be divided into multiple rows
+
+                       if (++output_col == output_cols) {
+                               output_cols ++;
+                               output_table.resize (output_rows, output_cols);
+                       }
                }
        }
-       if (!descs.empty()) {
-               plugin->announce_property_values();
-       }
 
        // Iterate over the list of controls to find which adjacent controls
        // are similar enough to be grouped together.
@@ -455,6 +451,7 @@ GenericPluginUI::build ()
 
 GenericPluginUI::ControlUI::ControlUI ()
        : automate_button (X_("")) // force creation of a label
+       , file_button(NULL)
 {
        automate_button.set_name ("PluginAutomateButton");
        ARDOUR_UI::instance()->set_tip (automate_button, _("Automation control"));
@@ -652,10 +649,35 @@ GenericPluginUI::build_control_ui (const ParameterDescriptor&           desc,
                        return control_ui;
                }
 
+               if (desc.datatype == Variant::PATH) {
+
+                       /* Build a file selector button */
+
+                       // Create/add controller
+                       control_ui->file_button = manage(new Gtk::FileChooserButton(Gtk::FILE_CHOOSER_ACTION_OPEN));
+                       control_ui->file_button->set_title(desc.label);
+
+                       control_ui->pack_start (control_ui->label, true, true);
+                       control_ui->pack_start (*control_ui->file_button, true, true);
+
+                       // Connect signals (TODO: do this via the Control)
+                       control_ui->file_button->signal_file_set().connect(
+                               sigc::bind(sigc::mem_fun(*this, &GenericPluginUI::set_property),
+                                          desc, control_ui->file_button));
+                       plugin->PropertyChanged.connect(*this, invalidator(*this),
+                                                       boost::bind(&GenericPluginUI::property_changed, this, _1, _2),
+                                                       gui_context());
+
+                       _property_controls.insert(std::make_pair(desc.key, control_ui->file_button));
+                       control_ui->file_button = control_ui->file_button;
+
+                       return control_ui;
+               }
+
                /* create the controller */
 
                if (mcontrol) {
-                       control_ui->controller = AutomationController::create(insert, mcontrol->parameter(), mcontrol);
+                       control_ui->controller = AutomationController::create(insert, mcontrol->parameter(), desc, mcontrol);
                }
 
                /* XXX this code is not right yet, because it doesn't handle
@@ -663,7 +685,6 @@ GenericPluginUI::build_control_ui (const ParameterDescriptor&           desc,
                */
 
                Adjustment* adj = control_ui->controller->adjustment();
-               boost::shared_ptr<PluginInsert::PluginControl> pc = boost::dynamic_pointer_cast<PluginInsert::PluginControl> (control_ui->control);
 
                if (desc.integer_step) {
                        control_ui->clickbox = new ClickBox (adj, "PluginUIClickBox");
index 51ab79287e79eba56e8a3add71b70777c106e790..9f6f650f56fabc93b50fcf1aaa95968fa3f50530 100644 (file)
@@ -56,7 +56,7 @@ LV2PluginUI::write_from_ui(void*       controller,
                if (ac) {
                        ac->set_value(*(const float*)buffer);
                }
-       } else if (format == me->_lv2->urids.atom_eventTransfer) {
+       } else if (format == URIMap::instance().urids.atom_eventTransfer) {
 
                const int cnt = me->_pi->get_count();
                for (int i=0; i < cnt; i++ ) {
index 9ebd56436e2dea9c26135d7d9f5de4c8e7fb09d9..9f4e2effcfc836d5ff06ac9321b89aa397d9f7a1 100644 (file)
@@ -241,6 +241,7 @@ class GenericPluginUI : public PlugUIBase, public Gtk::VBox
                bool                                    update_pending;
                char                                    ignore_change;
                Gtk::Button                             automate_button;
+               Gtk::FileChooserButton*                 file_button;
 
                /* output */
 
index b68e814fba131e48d508f6f13b5eeca0e45f903f..31c2eb171229879b7a02dc681e20efd56026bdfb 100644 (file)
@@ -2103,7 +2103,7 @@ RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_
                request_redraw ();
        }
 
-       if (!EventTypeMap::instance().is_midi_parameter(param)) {
+       if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
                /* MIDI-related parameters are always in the menu, there's no
                   reason to rebuild the menu just because we added a automation
                   lane for one of them. But if we add a non-MIDI automation
index c0e9dbc5b5b5065c25069e6adec092c8065c9286..b70cd0095ce088f098622bb6f45b22f6ad48ed6b 100644 (file)
@@ -79,7 +79,7 @@ public:
        struct GainControl : public AutomationControl {
                GainControl (std::string name, Session& session, Amp* a, const Evoral::Parameter &param,
                                boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>() )
-                       : AutomationControl (session, param, al, name)
+                       : AutomationControl (session, param, ParameterDescriptor(param), al, name)
                        , _amp (a) {
                        set_flags (Controllable::Flag (flags() | Controllable::GainLike));
                        alist()->reset_default (1.0);
index b0003189a441dadabbe872389213c0694fd377f6..0d3871eb17879f13cb8d0f796cbc9edebfd6c7d3 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "evoral/Sequence.hpp"
 #include "ardour/automatable.h"
+#include "ardour/event_type_map.h"
 
 namespace ARDOUR {
 
index 3603ea2e72e367a98d3d38e54b1e7c12c83538ef..639c5b528787fac816201e91fb6e9f02ae10447c 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "ardour/libardour_visibility.h"
 #include "ardour/automation_list.h"
+#include "ardour/parameter_descriptor.h"
 
 namespace ARDOUR {
 
@@ -42,11 +43,12 @@ class LIBARDOUR_API AutomationControl : public PBD::Controllable, public Evoral:
 {
 public:
        AutomationControl(ARDOUR::Session&,
-                         const Evoral::Parameter& parameter,
-                         boost::shared_ptr<ARDOUR::AutomationList> l=boost::shared_ptr<ARDOUR::AutomationList>(),
-                         const std::string& name="");
+                         const Evoral::Parameter&                  parameter,
+                         const ParameterDescriptor&                desc,
+                         boost::shared_ptr<ARDOUR::AutomationList> l=boost::shared_ptr<ARDOUR::AutomationList>(),
+                         const std::string&                        name="");
 
-        ~AutomationControl ();
+       ~AutomationControl ();
 
        boost::shared_ptr<AutomationList> alist() const {
                return boost::dynamic_pointer_cast<AutomationList>(_list);
@@ -78,16 +80,20 @@ public:
        void set_value (double);
        double get_value () const;
 
-       double lower() const { return parameter().min(); }
-       double upper() const { return parameter().max(); }
-       double normal() const { return parameter().normal(); }
-       bool toggled() const { return parameter().toggled(); }
+       double lower()   const { return _desc.lower; }
+       double upper()   const { return _desc.upper; }
+       double normal()  const { return _desc.normal; }
+       bool   toggled() const { return _desc.toggled; }
+
+       const ParameterDescriptor& desc() const { return _desc; }
 
        const ARDOUR::Session& session() const { return _session; }
 
 protected:
 
        ARDOUR::Session& _session;
+
+       const ParameterDescriptor _desc;
 };
 
 
index fbfd9ec73c5be8cc192ec0f35c6e4e61462d84b0..f69d20b773e6b0ec0a2009b119ec3d7c9f186316 100644 (file)
 
 namespace ARDOUR {
 
+class URIMap;
+
 /** This is the interface Ardour provides to Evoral about what
  * parameter and event types/ranges/names etc. to use.
  */
 class LIBARDOUR_API EventTypeMap : public Evoral::TypeMap {
 public:
+       static EventTypeMap& instance();
+
        bool     type_is_midi(uint32_t type) const;
        uint8_t  parameter_midi_type(const Evoral::Parameter& param) const;
        uint32_t midi_event_type(uint8_t status) const;
@@ -46,10 +50,14 @@ public:
 
        bool                 is_midi_parameter(const Evoral::Parameter& param);
 
-       static EventTypeMap& instance() { return event_type_map; }
+       URIMap& uri_map() { return _uri_map; }
 
 private:
-       static EventTypeMap event_type_map;
+       EventTypeMap(URIMap& uri_map) : _uri_map(uri_map) {}
+
+       URIMap& _uri_map;
+
+       static EventTypeMap* event_type_map;
 };
 
 } // namespace ARDOUR
index 82912a947d3fdaab22e066f429e05a78eab41961..9696784f010a83d679a835e62dabf2091ddd208d 100644 (file)
@@ -146,42 +146,16 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
 
        Worker* worker() { return _worker; }
 
+       URIMap&       uri_map()       { return _uri_map; }
+       const URIMap& uri_map() const { return _uri_map; }
+
        int work(uint32_t size, const void* data);
        int work_response(uint32_t size, const void* data);
 
-       void set_property(uint32_t key, const Variant& value);
-       void get_supported_properties(std::vector<ParameterDescriptor>& descs);
-       void announce_property_values();
-
-       static URIMap _uri_map;
-
-       struct URIDs {
-               uint32_t atom_Chunk;
-               uint32_t atom_Path;
-               uint32_t atom_Sequence;
-               uint32_t atom_eventTransfer;
-               uint32_t atom_URID;
-               uint32_t atom_Blank;
-               uint32_t atom_Object;
-               uint32_t log_Error;
-               uint32_t log_Note;
-               uint32_t log_Warning;
-               uint32_t midi_MidiEvent;
-               uint32_t time_Position;
-               uint32_t time_bar;
-               uint32_t time_barBeat;
-               uint32_t time_beatUnit;
-               uint32_t time_beatsPerBar;
-               uint32_t time_beatsPerMinute;
-               uint32_t time_frame;
-               uint32_t time_speed;
-               uint32_t patch_Get;
-               uint32_t patch_Set;
-               uint32_t patch_property;
-               uint32_t patch_value;
-       };
-
-       static URIDs urids;
+       void                       set_property(uint32_t key, const Variant& value);
+       const PropertyDescriptors& get_supported_properties() const { return _property_descriptors; }
+       const ParameterDescriptor& get_property_descriptor(uint32_t id) const;
+       void                       announce_property_values();
 
   private:
        struct Impl;
@@ -203,6 +177,7 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
        PBD::ID       _insert_id;
        uint32_t      _patch_port_in_index;
        uint32_t      _patch_port_out_index;
+       URIMap&       _uri_map;
 
        friend const void* lv2plugin_get_port_value(const char* port_symbol,
                                                    void*       user_data,
@@ -227,6 +202,8 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
        std::vector<size_t>            _port_minimumSize;
        std::map<std::string,uint32_t> _port_indices;
 
+       PropertyDescriptors _property_descriptors;
+
        /// Message send to/from UI via ports
        struct UIMessage {
                uint32_t index;
@@ -283,6 +260,8 @@ class LIBARDOUR_API LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
        void allocate_atom_event_buffers ();
        void run (pframes_t nsamples);
 
+       void load_supported_properties(PropertyDescriptors& descs);
+
        void latency_compute_run ();
        std::string do_save_preset (std::string);
        void do_remove_preset (std::string);
index f0c76ca86c96e527c3ebdabc1e62a4393e5d7a08..2923b784a99baa4e4e6aab358936288eddf56b6e 100644 (file)
@@ -23,7 +23,7 @@
 #include "evoral/midi_util.h"
 #include "midi++/event.h"
 #include "ardour/buffer.h"
-#include "ardour/event_type_map.h"
+#include "ardour/parameter_types.h"
 
 namespace ARDOUR {
 
@@ -77,7 +77,7 @@ public:
                        uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
                        int event_size = Evoral::midi_event_size(ev_start);
                        assert(event_size >= 0);
-                       return EventType(EventTypeMap::instance().midi_event_type(*ev_start),
+                       return EventType(midi_parameter_type(*ev_start),
                                        *((TimeType*)(buffer->_data + offset)),
                                        event_size, ev_start);
                }
@@ -86,7 +86,7 @@ public:
                        uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
                        int event_size = Evoral::midi_event_size(ev_start);
                        assert(event_size >= 0);
-                       return EventType(EventTypeMap::instance().midi_event_type(*ev_start),
+                       return EventType(midi_parameter_type(*ev_start),
                                        *(reinterpret_cast<TimeType*>((uintptr_t)(buffer->_data + offset))),
                                        event_size, ev_start);
                }
index cea3af5aeda5f6254aefe9af5774c99d0b63430d..3db16937fc8bd932c70801258b2dfacdba21bbf3 100644 (file)
@@ -84,7 +84,7 @@ public:
        struct MidiControl : public AutomationControl {
                MidiControl(MidiTrack* route, const Evoral::Parameter& param,
                            boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>())
-                       : AutomationControl (route->session(), param, al)
+                       : AutomationControl (route->session(), param, ParameterDescriptor(param), al)
                        , _route (route)
                {}
 
index 9abbec42ab82277fe9af408549410d514390a245..63ee9d3b8cbf64016f9b1d16f244a344864f14c5 100644 (file)
@@ -38,7 +38,11 @@ class LIBARDOUR_API PanControllable : public AutomationControl
 {
 public:
        PanControllable (Session& s, std::string name, Pannable* o, Evoral::Parameter param)
-               : AutomationControl (s, param, boost::shared_ptr<AutomationList>(new AutomationList(param)), name)
+               : AutomationControl (s,
+                                    param,
+                                    ParameterDescriptor(param),
+                                    boost::shared_ptr<AutomationList>(new AutomationList(param)),
+                                    name)
                , owner (o)
        {}
 
index a6315ae429ac1c6fc6d9593c64e161a0007cd021..8916f081a38be3c4cc3f9fc1683e4c6b62aee271 100644 (file)
@@ -21,6 +21,7 @@
 #define __ardour_parameter_descriptor_h__
 
 #include "ardour/variant.h"
+#include "evoral/Parameter.hpp"
 
 namespace ARDOUR {
 
@@ -32,9 +33,29 @@ typedef std::map<const std::string, const float> ScalePoints;
  */
 struct ParameterDescriptor
 {
+       ParameterDescriptor(const Evoral::Parameter& parameter)
+               : key((uint32_t)-1)
+               , datatype(Variant::VOID)
+               , normal(parameter.normal())
+               , lower(parameter.min())
+               , upper(parameter.max())
+               , step(0)
+               , smallstep((upper - lower) / 100.0)
+               , largestep((upper - lower) / 10.0)
+               , integer_step(false)
+               , toggled(parameter.toggled())
+               , logarithmic(false)
+               , sr_dependent(false)
+               , min_unbound(0)
+               , max_unbound(0)
+               , enumeration(false)
+               , midinote(false)
+       {}
+
        ParameterDescriptor()
                : key((uint32_t)-1)
                , datatype(Variant::VOID)
+               , normal(0)
                , lower(0)
                , upper(0)
                , step(0)
@@ -54,6 +75,7 @@ struct ParameterDescriptor
        boost::shared_ptr<ScalePoints> scale_points;
        uint32_t                       key;  ///< for properties
        Variant::Type                  datatype;  ///< for properties
+       float                          normal;
        float                          lower;  ///< for frequencies, this is in Hz (not a fraction of the sample rate)
        float                          upper;  ///< for frequencies, this is in Hz (not a fraction of the sample rate)
        float                          step;
diff --git a/libs/ardour/ardour/parameter_types.h b/libs/ardour/ardour/parameter_types.h
new file mode 100644 (file)
index 0000000..8442d1f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+    Copyright (C) 2014 Paul Davis
+    Author: David Robillard
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_parameter_types_h__
+#define __ardour_parameter_types_h__
+
+#include <stdint.h>
+
+#include "ardour/types.h"
+#include "evoral/midi_events.h"
+
+namespace ARDOUR {
+
+inline uint8_t
+parameter_midi_type(AutomationType type)
+{
+       switch (type) {
+       case MidiCCAutomation:              return MIDI_CMD_CONTROL;          break;
+       case MidiPgmChangeAutomation:       return MIDI_CMD_PGM_CHANGE;       break;
+       case MidiChannelPressureAutomation: return MIDI_CMD_CHANNEL_PRESSURE; break;
+       case MidiPitchBenderAutomation:     return MIDI_CMD_BENDER;           break;
+       case MidiSystemExclusiveAutomation: return MIDI_CMD_COMMON_SYSEX;     break;
+       default: return 0;
+       }
+}
+
+inline AutomationType
+midi_parameter_type(uint8_t status)
+{
+       switch (status & 0xF0) {
+       case MIDI_CMD_CONTROL:          return MidiCCAutomation;              break;
+       case MIDI_CMD_PGM_CHANGE:       return MidiPgmChangeAutomation;       break;
+       case MIDI_CMD_CHANNEL_PRESSURE: return MidiChannelPressureAutomation; break;
+       case MIDI_CMD_BENDER:           return MidiPitchBenderAutomation;     break;
+       case MIDI_CMD_COMMON_SYSEX:     return MidiSystemExclusiveAutomation; break;
+       default: return NullAutomation;
+       }
+}
+
+inline bool
+parameter_is_midi(AutomationType type)
+{
+       return (type >= MidiCCAutomation) && (type <= MidiChannelPressureAutomation);
+}
+
+}  // namespace ARDOUR
+
+#endif /* __ardour_parameter_types_h__ */
+
index dc7dbf68e1a8badbceaacfb67fc14a960f501cb4..be109885d7c5912fa993fe36bd9e8e113cfc0f76 100644 (file)
@@ -231,6 +231,8 @@ class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public Latent
        void set_cycles (uint32_t c) { _cycles = c; }
        cycles_t cycles() const { return _cycles; }
 
+       typedef std::map<uint32_t, ParameterDescriptor> PropertyDescriptors;
+
        /** Get a descrption of all properties supported by this plugin.
         *
         * Properties are distinct from parameters in that they are potentially
@@ -239,7 +241,15 @@ class LIBARDOUR_API Plugin : public PBD::StatefulDestructible, public Latent
         * For LV2 plugins, properties are implemented by sending/receiving set/get
         * messages to/from the plugin via event ports.
         */
-       virtual void get_supported_properties(std::vector<ParameterDescriptor>& descs) {}
+       virtual const PropertyDescriptors& get_supported_properties() const {
+               static const PropertyDescriptors nothing;
+               return nothing;
+       }
+
+       virtual const ParameterDescriptor& get_property_descriptor(uint32_t id) const {
+               static const ParameterDescriptor nothing;
+               return nothing;
+       }
 
        /** Set a property from the UI.
         *
index f1c03a79d268b182efeed5ebe681b0fdc0778485..6571732bb9b0705587fb73b87af312cb9b93676d 100644 (file)
@@ -28,6 +28,7 @@
 #include "ardour/ardour.h"
 #include "ardour/libardour_visibility.h"
 #include "ardour/types.h"
+#include "ardour/parameter_descriptor.h"
 #include "ardour/processor.h"
 #include "ardour/automation_control.h"
 
@@ -81,10 +82,13 @@ class LIBARDOUR_API PluginInsert : public Processor
        void realtime_locate ();
        void monitoring_changed ();
 
+       /** A control that manipulates a plugin parameter (control port). */
        struct PluginControl : public AutomationControl
        {
-               PluginControl (PluginInsert* p, const Evoral::Parameter &param,
-                               boost::shared_ptr<AutomationList> list = boost::shared_ptr<AutomationList>());
+               PluginControl (PluginInsert*                     p,
+                              const Evoral::Parameter&          param,
+                              const ParameterDescriptor&        desc,
+                              boost::shared_ptr<AutomationList> list=boost::shared_ptr<AutomationList>());
 
                void set_value (double val);
                double get_value (void) const;
@@ -95,9 +99,24 @@ class LIBARDOUR_API PluginInsert : public Processor
 
        private:
                PluginInsert* _plugin;
-               bool _logarithmic;
-               bool _sr_dependent;
-               bool _toggled;
+       };
+
+       /** A control that manipulates a plugin property (message). */
+       struct PluginPropertyControl : public AutomationControl
+       {
+               PluginPropertyControl (PluginInsert*                     p,
+                                      const Evoral::Parameter&          param,
+                                      const ParameterDescriptor&        desc,
+                                      boost::shared_ptr<AutomationList> list=boost::shared_ptr<AutomationList>());
+
+               void set_value (const Variant& val);
+               void set_value (double val);
+               double get_value (void) const;
+               XMLNode& get_state();
+
+       private:
+               PluginInsert* _plugin;
+               Variant       _value;
        };
 
        boost::shared_ptr<Plugin> plugin(uint32_t num=0) const {
index af56e12a5be7838eff553b9821d3b22313a82bbe..7444a54a7c7822c4d834c4421d3128f7ada26051 100644 (file)
@@ -127,6 +127,7 @@ namespace ARDOUR {
                PanFrontBackAutomation,
                PanLFEAutomation,
                PluginAutomation,
+               PluginPropertyAutomation,
                SoloAutomation,
                MuteAutomation,
                MidiCCAutomation,
index a948ea40022d26e3d453338e5c66377ef0f6d85f..d745ad58e775434a9cec2b661cf5a9286c3d4a58 100644 (file)
@@ -24,6 +24,8 @@
 
 #include <boost/utility.hpp>
 
+#include <glibmm/threads.h>
+
 #include "lv2.h"
 #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h"
 #include "lv2/lv2plug.in/ns/ext/urid/urid.h"
@@ -39,6 +41,8 @@ namespace ARDOUR {
  */
 class LIBARDOUR_API URIMap : public boost::noncopyable {
 public:
+       static URIMap& instance();
+
        URIMap();
 
        LV2_Feature* uri_map_feature()    { return &_uri_map_feature; }
@@ -51,6 +55,38 @@ public:
        uint32_t    uri_to_id(const char* uri);
        const char* id_to_uri(uint32_t id) const;
 
+       // Cached URIDs for use in real-time code
+       struct URIDs {
+               void init(URIMap& uri_map);
+
+               uint32_t atom_Chunk;
+               uint32_t atom_Path;
+               uint32_t atom_Sequence;
+               uint32_t atom_eventTransfer;
+               uint32_t atom_URID;
+               uint32_t atom_Blank;
+               uint32_t atom_Object;
+               uint32_t atom_Float;
+               uint32_t log_Error;
+               uint32_t log_Note;
+               uint32_t log_Warning;
+               uint32_t midi_MidiEvent;
+               uint32_t time_Position;
+               uint32_t time_bar;
+               uint32_t time_barBeat;
+               uint32_t time_beatUnit;
+               uint32_t time_beatsPerBar;
+               uint32_t time_beatsPerMinute;
+               uint32_t time_frame;
+               uint32_t time_speed;
+               uint32_t patch_Get;
+               uint32_t patch_Set;
+               uint32_t patch_property;
+               uint32_t patch_value;
+       };
+
+       URIDs urids;
+
 private:
        typedef std::map<const std::string, uint32_t> Map;
        typedef std::map<uint32_t, const std::string> Unmap;
@@ -64,6 +100,10 @@ private:
        LV2_URID_Map        _urid_map_feature_data;
        LV2_Feature         _urid_unmap_feature;
        LV2_URID_Unmap      _urid_unmap_feature_data;
+
+       mutable Glib::Threads::Mutex _lock;
+
+       static URIMap* uri_map;
 };
 
 } // namespace ARDOUR
index 1e9dda179a49bcd6e7dbcc684ab68e68dad6e2cc..cc483e3cdfe4988800acf9e5c0819b2653b095fa 100644 (file)
@@ -21,7 +21,9 @@
 #define __ardour_variant_h__
 
 #include <stdint.h>
+#include <limits.h>
 
+#include <algorithm>
 #include <stdexcept>
 
 #include "ardour/libardour_visibility.h"
@@ -45,17 +47,62 @@ public:
                URI      ///< URI string
        };
 
-       explicit Variant(bool value)   : _type(BOOL)   { _bool   = value; }
-       explicit Variant(double value) : _type(DOUBLE) { _double = value; }
-       explicit Variant(float value)  : _type(FLOAT)  { _float  = value; }
-       explicit Variant(int value)    : _type(INT)    { _int    = value; }
-       explicit Variant(long value)   : _type(LONG)   { _long   = value; }
+       explicit Variant()              : _type(VOID)   { _long   = 0;     }
+       explicit Variant(bool    value) : _type(BOOL)   { _bool   = value; }
+       explicit Variant(double  value) : _type(DOUBLE) { _double = value; }
+       explicit Variant(float   value) : _type(FLOAT)  { _float  = value; }
+       explicit Variant(int32_t value) : _type(INT)    { _int    = value; }
+       explicit Variant(int64_t value) : _type(LONG)   { _long   = value; }
 
+       /** Make a variant of a specific string type (string types only) */
        Variant(Type type, const std::string& value)
                : _type(type)
                , _string(value)
        {}
 
+       /** Make a numeric variant from a double (numeric types only).
+        *
+        * If conversion is impossible, the variant will have type VOID.
+        */
+       Variant(Type type, double value)
+               : _type(type)
+       {
+               switch (type) {
+               case BOOL:
+                       _bool = value != 0.0;
+                       break;
+               case DOUBLE:
+                       _double = (double)value;
+                       break;
+               case FLOAT:
+                       _float = (float)value;
+                       break;
+               case INT:
+                       _int = (int32_t)lrint(std::max((double)INT32_MIN,
+                                                      std::min(value, (double)INT32_MAX)));
+                       break;
+               case LONG:
+                       _long = (int64_t)lrint(std::max((double)INT64_MIN,
+                                                       std::min(value, (double)INT64_MAX)));
+                       break;
+               default:
+                       _type = VOID;
+                       _long = 0;
+               }
+       }
+
+       /** Convert a numeric variant to a double. */
+       double to_double() const {
+               switch (_type) {
+               case BOOL:   return _bool;
+               case DOUBLE: return _double;
+               case FLOAT:  return _float;
+               case INT:    return _int;
+               case LONG:   return _long;
+               default:     return 0.0;
+               }
+       }
+
        bool   get_bool()   const { ensure_type(BOOL);   return _bool;   }
        double get_double() const { ensure_type(DOUBLE); return _double; }
        float  get_float()  const { ensure_type(FLOAT);  return _float;  }
@@ -68,6 +115,15 @@ public:
 
        Type type() const { return _type; }
 
+       static bool type_is_numeric(Type type) {
+               switch (type) {
+               case BOOL: case DOUBLE: case FLOAT: case INT: case LONG:
+                       return true;
+               default:
+                       return false;
+               }
+       }
+
 private:
        static const char* type_name(const Type type) {
                static const char* names[] = {
index 8629722889de2fe5d86a65140893146237addcbe..466899ce48e97d83a0da361ba2fd1bdde5d58d2e 100644 (file)
 #include "ardour/midi_track.h"
 #include "ardour/pan_controllable.h"
 #include "ardour/pannable.h"
+#include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/session.h"
+#include "ardour/uri_map.h"
 
 #include "i18n.h"
 
@@ -146,9 +148,9 @@ Automatable::add_control(boost::shared_ptr<Evoral::Control> ac)
        }
 
        ControlSet::add_control (ac);
-       _can_automate_list.insert (param);
 
        if (al) {
+               _can_automate_list.insert (param);
                automation_list_automation_state_changed (param, al->automation_state ()); // sync everything up
        }
 }
@@ -170,6 +172,8 @@ Automatable::describe_parameter (Evoral::Parameter param)
                return string_compose("Bender [%1]", int(param.channel()) + 1);
        } else if (param.type() == MidiChannelPressureAutomation) {
                return string_compose("Pressure [%1]", int(param.channel()) + 1);
+       } else if (param.type() == PluginPropertyAutomation) {
+               return string_compose("Property %1", URIMap::instance().id_to_uri(param.id()));
        } else {
                return EventTypeMap::instance().to_symbol(param);
        }
@@ -251,7 +255,7 @@ Automatable::get_automation_xml_state ()
 
        for (Controls::iterator li = controls().begin(); li != controls().end(); ++li) {
                boost::shared_ptr<AutomationList> l = boost::dynamic_pointer_cast<AutomationList>(li->second->list());
-               if (!l->empty()) {
+               if (l && !l->empty()) {
                        node->add_child_nocopy (l->get_state ());
                }
        }
@@ -394,6 +398,7 @@ Automatable::control_factory(const Evoral::Parameter& param)
 {
        boost::shared_ptr<AutomationList> list(new AutomationList(param));
        Evoral::Control* control = NULL;
+       ParameterDescriptor desc(param);
        if (param.type() >= MidiCCAutomation && param.type() <= MidiChannelPressureAutomation) {
                MidiTrack* mt = dynamic_cast<MidiTrack*>(this);
                if (mt) {
@@ -405,10 +410,24 @@ Automatable::control_factory(const Evoral::Parameter& param)
        } else if (param.type() == PluginAutomation) {
                PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
                if (pi) {
-                       control = new PluginInsert::PluginControl(pi, param);
+                       pi->plugin(0)->get_parameter_descriptor(param.id(), desc);
+                       control = new PluginInsert::PluginControl(pi, param, desc);
                } else {
                        warning << "PluginAutomation for non-Plugin" << endl;
                }
+       } else if (param.type() == PluginPropertyAutomation) {
+               PluginInsert* pi = dynamic_cast<PluginInsert*>(this);
+               if (pi) {
+                       desc = pi->plugin(0)->get_property_descriptor(param.id());
+                       if (desc.datatype != Variant::VOID) {
+                               if (!Variant::type_is_numeric(desc.datatype)) {
+                                       list.reset();  // Can't automate non-numeric data yet
+                               }
+                               control = new PluginInsert::PluginPropertyControl(pi, param, desc, list);
+                       }
+               } else {
+                       warning << "PluginPropertyAutomation for non-Plugin" << endl;
+               }
        } else if (param.type() == GainAutomation) {
                Amp* amp = dynamic_cast<Amp*>(this);
                if (amp) {
@@ -426,7 +445,7 @@ Automatable::control_factory(const Evoral::Parameter& param)
        }
 
        if (!control) {
-               control = new AutomationControl(_a_session, param);
+               control = new AutomationControl(_a_session, param, desc);
        }
 
        control->set_list(list);
index f1305609ef009862bff6f827d5d8348091a31957..d209e733d555725aec85fe1d019d76906d97f792 100644 (file)
@@ -29,14 +29,15 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-AutomationControl::AutomationControl(
-               ARDOUR::Session& session,
-               const Evoral::Parameter& parameter,
-               boost::shared_ptr<ARDOUR::AutomationList> list,
-               const string& name)
+AutomationControl::AutomationControl(ARDOUR::Session&                          session,
+                                     const Evoral::Parameter&                  parameter,
+                                     const ParameterDescriptor&                desc,
+                                     boost::shared_ptr<ARDOUR::AutomationList> list,
+                                     const string&                             name)
        : Controllable (name.empty() ? EventTypeMap::instance().to_symbol(parameter) : name)
        , Evoral::Control(parameter, list)
        , _session(session)
+       , _desc(desc)
 {
 }
 
index 9db99d20ce520eeced0825e2614b40a73ac1fe12..f64eec3191634b5d18d5a9020f5411493d8a6926 100644 (file)
@@ -34,6 +34,7 @@
 #include "ardour/midi_buffer.h"
 #include "ardour/port.h"
 #include "ardour/port_set.h"
+#include "ardour/uri_map.h"
 #ifdef LV2_SUPPORT
 #include "ardour/lv2_plugin.h"
 #include "lv2_evbuf.h"
@@ -192,8 +193,8 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac
                        _lv2_buffers.push_back(
                                std::make_pair(false, lv2_evbuf_new(buffer_capacity,
                                                                    LV2_EVBUF_EVENT,
-                                                                   LV2Plugin::urids.atom_Chunk,
-                                                                   LV2Plugin::urids.atom_Sequence)));
+                                                                   URIMap::instance().urids.atom_Chunk,
+                                                                   URIMap::instance().urids.atom_Sequence)));
                }
        }
 #endif
@@ -267,8 +268,8 @@ BufferSet::ensure_lv2_bufsize(bool input, size_t i, size_t buffer_capacity)
                std::make_pair(false, lv2_evbuf_new(
                                        buffer_capacity,
                                        LV2_EVBUF_EVENT,
-                                       LV2Plugin::urids.atom_Chunk,
-                                       LV2Plugin::urids.atom_Sequence));
+                                       URIMap::instance().urids.atom_Chunk,
+                                       URIMap::instance().urids.atom_Sequence));
 }
 
 LV2_Evbuf*
@@ -297,7 +298,7 @@ BufferSet::forward_lv2_midi(LV2_Evbuf* buf, size_t i, bool purge_ardour_buffer)
                uint32_t frames, subframes, type, size;
                uint8_t* data;
                lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
-               if (type == LV2Plugin::urids.midi_MidiEvent) {
+               if (type == URIMap::instance().urids.midi_MidiEvent) {
                        mbuf.push_back(frames, size, data);
                }
        }
@@ -326,7 +327,7 @@ BufferSet::flush_lv2_midi(bool input, size_t i)
                        DEBUG_TRACE (PBD::DEBUG::LV2, string_compose ("\tByte[%1] = %2\n", x, (int) data[x]));
                }
 #endif
-               if (type == LV2Plugin::urids.midi_MidiEvent) {
+               if (type == URIMap::instance().urids.midi_MidiEvent) {
                        // TODO: Make Ardour event buffers generic so plugins can communicate
                        mbuf.push_back(frames, size, data);
                }
index 3f2f1470c2e3ff4d570440ece4a1925c064c243e..c1eaa00977b8ac0735dff60c6f165a7b001e5dbe 100644 (file)
@@ -137,6 +137,7 @@ setup_enum_writer ()
        REGISTER_ENUM (PanElevationAutomation);
        REGISTER_ENUM (PanWidthAutomation);
        REGISTER_ENUM (PluginAutomation);
+       REGISTER_ENUM (PluginPropertyAutomation);
        REGISTER_ENUM (SoloAutomation);
        REGISTER_ENUM (MuteAutomation);
        REGISTER_ENUM (MidiCCAutomation);
index 29a363c78b42952220af25b8b162ff5b59855a8a..ee6270356c8750c06b27beff54be3e87959033c2 100644 (file)
 
 */
 
+#include <ctype.h>
 #include <cstdio>
 #include "ardour/types.h"
 #include "ardour/event_type_map.h"
+#include "ardour/parameter_types.h"
+#include "ardour/uri_map.h"
 #include "evoral/Parameter.hpp"
 #include "evoral/midi_events.h"
 #include "evoral/MIDIParameters.hpp"
@@ -31,44 +34,58 @@ using namespace std;
 
 namespace ARDOUR {
 
-EventTypeMap EventTypeMap::event_type_map;
+EventTypeMap* EventTypeMap::event_type_map;
+
+EventTypeMap&
+EventTypeMap::instance()
+{
+       if (!EventTypeMap::event_type_map) {
+               EventTypeMap::event_type_map = new EventTypeMap(URIMap::instance());
+
+               // Initialize parameter metadata
+               EventTypeMap::event_type_map->new_parameter(NullAutomation);
+               EventTypeMap::event_type_map->new_parameter(GainAutomation);
+               EventTypeMap::event_type_map->new_parameter(PanAzimuthAutomation);
+               EventTypeMap::event_type_map->new_parameter(PanElevationAutomation);
+               EventTypeMap::event_type_map->new_parameter(PanWidthAutomation);
+               EventTypeMap::event_type_map->new_parameter(PluginAutomation);
+               EventTypeMap::event_type_map->new_parameter(PluginPropertyAutomation);
+               EventTypeMap::event_type_map->new_parameter(SoloAutomation);
+               EventTypeMap::event_type_map->new_parameter(MuteAutomation);
+               EventTypeMap::event_type_map->new_parameter(MidiCCAutomation);
+               EventTypeMap::event_type_map->new_parameter(MidiPgmChangeAutomation);
+               EventTypeMap::event_type_map->new_parameter(MidiPitchBenderAutomation);
+               EventTypeMap::event_type_map->new_parameter(MidiChannelPressureAutomation);
+               EventTypeMap::event_type_map->new_parameter(FadeInAutomation);
+               EventTypeMap::event_type_map->new_parameter(FadeOutAutomation);
+               EventTypeMap::event_type_map->new_parameter(EnvelopeAutomation);
+               EventTypeMap::event_type_map->new_parameter(MidiCCAutomation);
+       }
+       return *EventTypeMap::event_type_map;
+}
 
 bool
 EventTypeMap::type_is_midi(uint32_t type) const
 {
-       return (type >= MidiCCAutomation) && (type <= MidiChannelPressureAutomation);
+       return ARDOUR::parameter_is_midi((AutomationType)type);
 }
 
 bool
 EventTypeMap::is_midi_parameter(const Evoral::Parameter& param)
 {
-               return type_is_midi(param.type());
+       return type_is_midi(param.type());
 }
 
 uint8_t
 EventTypeMap::parameter_midi_type(const Evoral::Parameter& param) const
 {
-       switch (param.type()) {
-       case MidiCCAutomation:              return MIDI_CMD_CONTROL; break;
-       case MidiPgmChangeAutomation:       return MIDI_CMD_PGM_CHANGE; break;
-       case MidiChannelPressureAutomation: return MIDI_CMD_CHANNEL_PRESSURE; break;
-       case MidiPitchBenderAutomation:     return MIDI_CMD_BENDER; break;
-       case MidiSystemExclusiveAutomation: return MIDI_CMD_COMMON_SYSEX; break;
-       default: return 0;
-       }
+       return ARDOUR::parameter_midi_type((AutomationType)param.type());
 }
 
 uint32_t
 EventTypeMap::midi_event_type(uint8_t status) const
 {
-       switch (status & 0xF0) {
-       case MIDI_CMD_CONTROL:          return MidiCCAutomation; break;
-       case MIDI_CMD_PGM_CHANGE:       return MidiPgmChangeAutomation; break;
-       case MIDI_CMD_CHANNEL_PRESSURE: return MidiChannelPressureAutomation; break;
-       case MIDI_CMD_BENDER:           return MidiPitchBenderAutomation; break;
-       case MIDI_CMD_COMMON_SYSEX:     return MidiSystemExclusiveAutomation; break;
-       default: return 0;
-       }
+       return (uint32_t)ARDOUR::midi_parameter_type(status);
 }
 
 bool
@@ -182,6 +199,8 @@ EventTypeMap::new_parameter(uint32_t type, uint8_t channel, uint32_t id) const
                Evoral::MIDI::bender_range(min, max, normal); break;
        case MidiSystemExclusiveAutomation:
                return p;
+       case PluginPropertyAutomation:
+               return p;
        }
 
        p.set_range(type, min, max, normal, false);
@@ -220,6 +239,14 @@ EventTypeMap::new_parameter(const string& str) const
        } else if (str.length() > 10 && str.substr(0, 10) == "parameter-") {
                p_type = PluginAutomation;
                p_id = atoi(str.c_str()+10);
+       } else if (str.length() > 9 && str.substr(0, 9) == "property-") {
+               p_type = PluginPropertyAutomation;
+               const char* name = str.c_str() + 9;
+               if (isdigit(str.c_str()[0])) {
+                       p_id = atoi(name);
+               } else {
+                       p_id = _uri_map.uri_to_id(name);
+               }
        } else if (str.length() > 7 && str.substr(0, 7) == "midicc-") {
                p_type = MidiCCAutomation;
                uint32_t channel = 0;
@@ -286,6 +313,13 @@ EventTypeMap::to_symbol(const Evoral::Parameter& param) const
                return "envelope";
        } else if (t == PluginAutomation) {
                return string_compose("parameter-%1", param.id());
+       } else if (t == PluginPropertyAutomation) {
+               const char* uri = _uri_map.id_to_uri(param.id());
+               if (uri) {
+                       return string_compose("property-%1", uri);
+               } else {
+                       return string_compose("property-%1", param.id());
+               }
        } else if (t == MidiCCAutomation) {
                return string_compose("midicc-%1-%2", int(param.channel()), param.id());
        } else if (t == MidiPgmChangeAutomation) {
index 48abda8cfcb8367a18bdfa6962823aff2cad581b..44367b8cf32cf055109e6de84cd21008e1ad6ddd 100644 (file)
@@ -85,6 +85,7 @@
 #include "ardour/audioregion.h"
 #include "ardour/buffer_manager.h"
 #include "ardour/control_protocol_manager.h"
+#include "ardour/event_type_map.h"
 #include "ardour/filesystem_paths.h"
 #include "ardour/midi_region.h"
 #include "ardour/midiport_manager.h"
 #include "ardour/runtime_functions.h"
 #include "ardour/session_event.h"
 #include "ardour/source_factory.h"
+#include "ardour/uri_map.h"
 
 #include "audiographer/routines.h"
 
@@ -319,8 +321,10 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
        SourceFactory::init ();
        Analyser::init ();
 
-       /* singleton - first object is "it" */
+       /* singletons - first object is "it" */
        (void) PluginManager::instance();
+       (void) URIMap::instance();
+       (void) EventTypeMap::instance();
 
         ProcessThread::init ();
        /* the + 4 is a bit of a handwave. i don't actually know
@@ -331,24 +335,6 @@ ARDOUR::init (bool use_windows_vst, bool try_optimization, const char* localedir
 
         PannerManager::instance().discover_panners();
 
-       // Initialize parameter metadata
-       EventTypeMap::instance().new_parameter(NullAutomation);
-       EventTypeMap::instance().new_parameter(GainAutomation);
-       EventTypeMap::instance().new_parameter(PanAzimuthAutomation);
-       EventTypeMap::instance().new_parameter(PanElevationAutomation);
-       EventTypeMap::instance().new_parameter(PanWidthAutomation);
-       EventTypeMap::instance().new_parameter(PluginAutomation);
-       EventTypeMap::instance().new_parameter(SoloAutomation);
-       EventTypeMap::instance().new_parameter(MuteAutomation);
-       EventTypeMap::instance().new_parameter(MidiCCAutomation);
-       EventTypeMap::instance().new_parameter(MidiPgmChangeAutomation);
-       EventTypeMap::instance().new_parameter(MidiPitchBenderAutomation);
-       EventTypeMap::instance().new_parameter(MidiChannelPressureAutomation);
-       EventTypeMap::instance().new_parameter(FadeInAutomation);
-       EventTypeMap::instance().new_parameter(FadeOutAutomation);
-       EventTypeMap::instance().new_parameter(EnvelopeAutomation);
-       EventTypeMap::instance().new_parameter(MidiCCAutomation);
-
        ARDOUR::AudioEngine::create ();
 
        libardour_initialized = true;
index 4f41b51d1b11ac414f738d71e1a81e30f65b98f6..cf33c2242492ce975e37a6492fd6bfd59ac50f83 100644 (file)
@@ -97,34 +97,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 +123,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;
@@ -220,11 +195,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 +253,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 +267,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 +590,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate)
                }
        }
 
+       load_supported_properties(_property_descriptors);
        allocate_atom_event_buffers();
        latency_compute_run();
 }
@@ -1021,7 +999,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
        }
 
@@ -1302,15 +1280,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);
@@ -1318,13 +1296,51 @@ 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
+set_parameter_descriptor(LV2World&            world,
+                         ParameterDescriptor& desc,
+                         Variant::Type        datatype,
+                         const LilvNode*      subject)
+{
+       LilvWorld* lworld  = _world.world;
+       LilvNode*  label   = lilv_world_get(lworld, subject, _world.rdfs_label, NULL);
+       LilvNode*  def     = lilv_world_get(lworld, subject, _world.lv2_default, NULL);
+       LilvNode*  minimum = lilv_world_get(lworld, subject, _world.lv2_minimum, NULL);
+       LilvNode*  maximum = lilv_world_get(lworld, subject, _world.lv2_maximum, 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);
+       }
+       desc.datatype      = datatype;
+       desc.toggled      |= datatype == Variant::BOOL;
+       desc.integer_step |= datatype == Variant::INT || datatype == Variant::LONG;
+}
+
 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);
@@ -1333,27 +1349,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);
+               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;
+               set_parameter_descriptor(_world, desc, datatype, prop);
+               descs.insert(std::make_pair(desc.key, desc));
+
                lilv_node_free(range);
        }
        lilv_nodes_free(properties);
@@ -1375,15 +1392,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);
 }
@@ -1532,6 +1549,11 @@ 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;
@@ -1628,6 +1650,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;
 }
 
@@ -1716,7 +1743,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;
@@ -1734,42 +1761,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
 
@@ -1880,7 +1909,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() &&
@@ -1932,7 +1961,7 @@ 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];
@@ -1998,20 +2027,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;
                                                        }
@@ -2020,13 +2049,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));
                        }
@@ -2227,6 +2257,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);
index 9c1e338b6c320e9ce9981df9e9e6c9695d1c3677..0261f06ceb9d515179a3309de165ca42ad274797 100644 (file)
@@ -84,7 +84,8 @@ template<typename Time>
 struct EventsSortByTimeAndType {
     bool operator() (Evoral::Event<Time>* a, Evoral::Event<Time>* b) {
            if (a->time() == b->time()) {
-                   if (EventTypeMap::instance().type_is_midi (a->event_type()) && EventTypeMap::instance().type_is_midi (b->event_type())) {
+                   if (parameter_is_midi ((AutomationType)a->event_type()) &&
+                       parameter_is_midi ((AutomationType)b->event_type())) {
                            /* negate return value since we must return whether
                             * or not a should sort before b, not b before a
                             */
index 73b6fb639e1ea0e1db4823fcdc92449e5863ea44..466fc20b63445acf011652a75ecf0dad045c0640 100644 (file)
@@ -25,9 +25,9 @@
 #include "evoral/EventSink.hpp"
 
 #include "ardour/debug.h"
-#include "ardour/event_type_map.h"
 #include "ardour/midi_source.h"
 #include "ardour/midi_state_tracker.h"
+#include "ardour/parameter_types.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -162,7 +162,7 @@ MidiStateTracker::resolve_notes (Evoral::EventSink<framepos_t> &dst, framepos_t
                                /* note that we do not care about failure from
                                   write() ... should we warn someone ?
                                */
-                               dst.write (time, EventTypeMap::instance().midi_event_type (buf[0]), 3, buf);
+                               dst.write (time, midi_parameter_type (buf[0]), 3, buf);
                                _active_notes[note + 128 * channel]--;
                                DEBUG_TRACE (PBD::DEBUG::MidiTrackers, string_compose ("%1: EVS-resolved note %2/%3 at %4\n", 
                                                                                       this, (int) note, (int) channel, time));
index cf04094015b57b24d6b5ffbf78eb4f9ed8f3b632..5e26251d8401330a615a10cbc12fad5c8c981c3c 100644 (file)
@@ -30,6 +30,7 @@
 #include "ardour/midi_playlist.h"
 #include "ardour/midi_port.h"
 #include "ardour/midi_track.h"
+#include "ardour/parameter_types.h"
 #include "ardour/port.h"
 #include "ardour/processor.h"
 #include "ardour/session.h"
@@ -618,7 +619,7 @@ MidiTrack::write_immediate_event(size_t size, const uint8_t* buf)
                cerr << "WARNING: Ignoring illegal immediate MIDI event" << endl;
                return false;
        }
-       const uint32_t type = EventTypeMap::instance().midi_event_type(buf[0]);
+       const uint32_t type = midi_parameter_type(buf[0]);
        return (_immediate_events.write (0, type, size, buf) == size);
 }
 
index 4ad3123e077215854e05a1d9eb7e01f38bbf0f2a..3bc2d18b7b708785fc625c3c3e9dbf1de0c016ee 100644 (file)
@@ -256,7 +256,17 @@ PluginInsert::create_automatable_parameters ()
                        param.set_range (desc.lower, desc.upper, _plugins.front()->default_value(i->id()), desc.toggled);
                        can_automate (param);
                        boost::shared_ptr<AutomationList> list(new AutomationList(param));
-                       add_control (boost::shared_ptr<AutomationControl> (new PluginControl(this, param, list)));
+                       add_control (boost::shared_ptr<AutomationControl> (new PluginControl(this, param, desc, list)));
+               } else if (i->type() == PluginPropertyAutomation) {
+                       Evoral::Parameter param(*i);
+                       const ParameterDescriptor& desc = _plugins.front()->get_property_descriptor(param.id());
+                       if (desc.datatype != Variant::VOID) {
+                               boost::shared_ptr<AutomationList> list;
+                               if (Variant::type_is_numeric(desc.datatype)) {
+                                       list = boost::shared_ptr<AutomationList>(new AutomationList(param));
+                               }
+                               add_control (boost::shared_ptr<AutomationControl> (new PluginPropertyControl(this, param, desc, list)));
+                       }
                }
        }
 }
@@ -368,7 +378,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                        boost::shared_ptr<AutomationControl> c
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
 
-                       if (c->parameter().type() == PluginAutomation && c->automation_playback()) {
+                       if (c->list() && c->automation_playback()) {
                                bool valid;
 
                                const float val = c->list()->rt_safe_eval (now, valid);
@@ -1167,11 +1177,15 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version)
 string
 PluginInsert::describe_parameter (Evoral::Parameter param)
 {
-       if (param.type() != PluginAutomation) {
-               return Automatable::describe_parameter(param);
+       if (param.type() == PluginAutomation) {
+               return _plugins[0]->describe_parameter (param);
+       } else if (param.type() == PluginPropertyAutomation) {
+               boost::shared_ptr<AutomationControl> c(automation_control(param));
+               if (c && !c->desc().label.empty()) {
+                       return c->desc().label;
+               }
        }
-
-       return _plugins[0]->describe_parameter (param);
+       return Automatable::describe_parameter(param);
 }
 
 ARDOUR::framecnt_t
@@ -1190,19 +1204,16 @@ PluginInsert::type ()
        return plugin()->get_info()->type;
 }
 
-PluginInsert::PluginControl::PluginControl (PluginInsert* p, const Evoral::Parameter &param, boost::shared_ptr<AutomationList> list)
-       : AutomationControl (p->session(), param, list, p->describe_parameter(param))
+PluginInsert::PluginControl::PluginControl (PluginInsert*                     p,
+                                            const Evoral::Parameter&          param,
+                                            const ParameterDescriptor&        desc,
+                                            boost::shared_ptr<AutomationList> list)
+       : AutomationControl (p->session(), param, desc, list, p->describe_parameter(param))
        , _plugin (p)
 {
-       ParameterDescriptor desc;
-       boost::shared_ptr<Plugin> plugin = p->plugin (0);
-       
-       alist()->reset_default (plugin->default_value (param.id()));
-
-       plugin->get_parameter_descriptor (param.id(), desc);
-       _logarithmic = desc.logarithmic;
-       _sr_dependent = desc.sr_dependent;
-       _toggled = desc.toggled;
+       if (alist()) {
+               alist()->reset_default (desc.normal);
+       }
 
        if (desc.toggled) {
                set_flags(Controllable::Toggle);
@@ -1232,7 +1243,7 @@ PluginInsert::PluginControl::internal_to_interface (double val) const
 {
        val = Controllable::internal_to_interface(val);
        
-       if (_logarithmic) {
+       if (_desc.logarithmic) {
                if (val > 0) {
                        val = pow (val, 1/1.5);
                } else {
@@ -1246,9 +1257,9 @@ PluginInsert::PluginControl::internal_to_interface (double val) const
 double
 PluginInsert::PluginControl::interface_to_internal (double val) const
 {
-       if (_logarithmic) {
+       if (_desc.logarithmic) {
                if (val <= 0) {
-                       val= 0;
+                       val = 0;
                } else {
                        val = pow (val, 1.5);
                }
@@ -1279,6 +1290,62 @@ PluginInsert::PluginControl::get_value () const
        return _plugin->get_parameter (_list->parameter());
 }
 
+PluginInsert::PluginPropertyControl::PluginPropertyControl (PluginInsert*                     p,
+                                                            const Evoral::Parameter&          param,
+                                                            const ParameterDescriptor&        desc,
+                                                            boost::shared_ptr<AutomationList> list)
+       : AutomationControl (p->session(), param, desc, list)
+       , _plugin (p)
+{
+       if (alist()) {
+               alist()->set_yrange (desc.lower, desc.upper);
+               alist()->reset_default (desc.normal);
+       }
+
+       if (desc.toggled) {
+               set_flags(Controllable::Toggle);
+       }
+}
+
+void
+PluginInsert::PluginPropertyControl::set_value (double user_val)
+{
+       /* Old numeric set_value(), coerce to appropriate datatype if possible.
+          This is lossy, but better than nothing until Ardour's automation system
+          can handle various datatypes all the way down. */
+       const Variant value(_desc.datatype, user_val);
+       if (value.type() == Variant::VOID) {
+               error << "set_value(double) called for non-numeric property" << endmsg;
+               return;
+       }
+
+       for (Plugins::iterator i = _plugin->_plugins.begin(); i != _plugin->_plugins.end(); ++i) {
+               (*i)->set_property(_list->parameter().id(), value);
+       }
+
+       _value = value;
+       AutomationControl::set_value(user_val);
+}
+
+XMLNode&
+PluginInsert::PluginPropertyControl::get_state ()
+{
+       stringstream ss;
+
+       XMLNode& node (AutomationControl::get_state());
+       ss << parameter().id();
+       node.add_property (X_("property"), ss.str());
+       node.remove_property (X_("value"));
+
+       return node;
+}
+
+double
+PluginInsert::PluginPropertyControl::get_value () const
+{
+       return _value.to_double();
+}
+
 boost::shared_ptr<Plugin>
 PluginInsert::get_impulse_analysis_plugin()
 {
index 519279bb1f1896b46af80c96e3bf4715caa5bb93..f78cd1e2f11981f6015d2cd6b9e7f2a06597e468 100644 (file)
@@ -3385,8 +3385,10 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
 }
 
 Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
-                            boost::shared_ptr<AutomationList>(), name)
+       : AutomationControl (r->session(),
+                            Evoral::Parameter (SoloAutomation),
+                            ParameterDescriptor(Evoral::Parameter (SoloAutomation)),
+                            boost::shared_ptr<AutomationList>(), name)
        , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
@@ -3430,8 +3432,11 @@ Route::SoloControllable::get_value () const
 }
 
 Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation),
-                            boost::shared_ptr<AutomationList>(), name)
+       : AutomationControl (r->session(),
+                            Evoral::Parameter (MuteAutomation),
+                            ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
+                            boost::shared_ptr<AutomationList>(),
+                            name)
        , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
index d09f4d561effc32f6d81a2fbc5c68fa05c0d4e0f..c7cd7160f3e64b56fbf2640130a039dd0dabb025 100644 (file)
 #include "evoral/Control.hpp"
 #include "evoral/SMF.hpp"
 
-#include "ardour/event_type_map.h"
 #include "ardour/midi_model.h"
 #include "ardour/midi_ring_buffer.h"
 #include "ardour/midi_state_tracker.h"
+#include "ardour/parameter_types.h"
 #include "ardour/session.h"
 #include "ardour/smf_source.h"
 #include "ardour/debug.h"
@@ -263,7 +263,7 @@ SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
                        continue;
                }
 
-               ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
+               ev_type = midi_parameter_type(ev_buffer[0]);
 
                DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3, type %4\n",
                                                                  ev_delta_t, time, ev_buffer[0], ev_type));
@@ -361,7 +361,7 @@ SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
                time -= position;
                        
                ev.set(buf, size, time);
-               ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0]));
+               ev.set_event_type(midi_parameter_type(ev.buffer()[0]));
                ev.set_id(Evoral::next_event_id());
 
                if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
@@ -645,7 +645,7 @@ SMFSource::load_model (bool lock, bool force_reload)
                                if (!have_event_id) {
                                        event_id = Evoral::next_event_id();
                                }
-                               uint32_t event_type = EventTypeMap::instance().midi_event_type(buf[0]);
+                               uint32_t event_type = midi_parameter_type(buf[0]);
                                double   event_time = time / (double) ppqn();
 #ifndef NDEBUG
                                std::string ss;
index 189b315e201b08150313c09efc1b4f26c805c6b1..03d75d9d79e6be33e7635d40bd9e52de5abd0263 100644 (file)
@@ -178,7 +178,11 @@ Track::freeze_state() const
 }
 
 Track::RecEnableControl::RecEnableControl (boost::shared_ptr<Track> t)
-       : AutomationControl (t->session(), RecEnableAutomation, boost::shared_ptr<AutomationList>(), X_("recenable"))
+       : AutomationControl (t->session(),
+                            RecEnableAutomation,
+                            ParameterDescriptor(Evoral::Parameter(RecEnableAutomation)),
+                            boost::shared_ptr<AutomationList>(),
+                            X_("recenable"))
        , track (t)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(RecEnableAutomation)));
index 163a460624fe8e544d3a2b6ad5a02963a2c33d9f..6fac103715441d26be9e17bcdb99f5383d929c70 100644 (file)
 
 namespace ARDOUR {
 
+URIMap* URIMap::uri_map;
+
+void
+URIMap::URIDs::init(URIMap& uri_map)
+{
+       // Use string literals here instead of LV2 defines to avoid LV2 dependency
+       atom_Chunk          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#Chunk");
+       atom_Path           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#Path");
+       atom_Sequence       = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#Sequence");
+       atom_eventTransfer  = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#eventTransfer");
+       atom_URID           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#URID");
+       atom_Blank          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#Blank");
+       atom_Object         = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#Object");
+       atom_Float          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/atom#Float");
+       log_Error           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/log#Error");
+       log_Note            = uri_map.uri_to_id("http://lv2plug.in/ns/ext/log#Note");
+       log_Warning         = uri_map.uri_to_id("http://lv2plug.in/ns/ext/log#Warning");
+       midi_MidiEvent      = uri_map.uri_to_id("http://lv2plug.in/ns/ext/midi#MidiEvent");
+       time_Position       = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#Position");
+       time_bar            = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#bar");
+       time_barBeat        = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#barBeat");
+       time_beatUnit       = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#beatUnit");
+       time_beatsPerBar    = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#beatsPerBar");
+       time_beatsPerMinute = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#beatsPerMinute");
+       time_frame          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#frame");
+       time_speed          = uri_map.uri_to_id("http://lv2plug.in/ns/ext/time#speed");
+       patch_Get           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#Get");
+       patch_Set           = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#Set");
+       patch_property      = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#property");
+       patch_value         = uri_map.uri_to_id("http://lv2plug.in/ns/ext/patch#value");
+}
+
+URIMap&
+URIMap::instance()
+{
+       if (!URIMap::uri_map) {
+               URIMap::uri_map = new URIMap();
+       }
+       return *URIMap::uri_map;
+}
+
 static uint32_t
 c_uri_map_uri_to_id(LV2_URI_Map_Callback_Data callback_data,
                     const char*               map,
@@ -62,7 +103,7 @@ c_urid_map(LV2_URID_Map_Handle handle,
 
 static const char*
 c_urid_unmap(LV2_URID_Unmap_Handle handle,
-           LV2_URID              urid)
+             LV2_URID              urid)
 {
        URIMap* const me = (URIMap*)handle;
        return me->id_to_uri(urid);
@@ -84,11 +125,15 @@ URIMap::URIMap()
        _urid_unmap_feature_data.handle = this;
        _urid_unmap_feature.URI         = LV2_URID_UNMAP_URI;
        _urid_unmap_feature.data        = &_urid_unmap_feature_data;
+
+       urids.init(*this);
 }
 
 uint32_t
 URIMap::uri_to_id(const char* uri)
 {
+       Glib::Threads::Mutex::Lock lm (_lock);
+
        const std::string urimm(uri);
        const Map::const_iterator i = _map.find(urimm);
        if (i != _map.end()) {
@@ -103,6 +148,8 @@ URIMap::uri_to_id(const char* uri)
 const char*
 URIMap::id_to_uri(const uint32_t id) const
 {
+       Glib::Threads::Mutex::Lock lm (_lock);
+
        const Unmap::const_iterator i = _unmap.find(id);
        return (i != _unmap.end()) ? i->second.c_str() : NULL;
 }