diff options
author | David Robillard <d@drobilla.net> | 2014-11-01 23:29:10 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-11-02 02:10:24 -0500 |
commit | 8a128b33d38172ae525ac798c53bc105bc4e2c64 (patch) | |
tree | 226459f2fec72a9717d12f190d354f72175607dc /libs/ardour | |
parent | 6dfb11c2d08201f1a27818955707590b762f5a40 (diff) |
Automation of LV2 plugin properties.
Work towards ParameterDescriptor being used more universally to describe control characteristics.
Diffstat (limited to 'libs/ardour')
30 files changed, 640 insertions, 228 deletions
diff --git a/libs/ardour/ardour/amp.h b/libs/ardour/ardour/amp.h index c0e9dbc5b5..b70cd0095c 100644 --- a/libs/ardour/ardour/amp.h +++ b/libs/ardour/ardour/amp.h @@ -79,7 +79,7 @@ public: struct GainControl : public AutomationControl { GainControl (std::string name, Session& session, Amp* a, const Evoral::Parameter ¶m, 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); diff --git a/libs/ardour/ardour/automatable_sequence.h b/libs/ardour/ardour/automatable_sequence.h index b0003189a4..0d3871eb17 100644 --- a/libs/ardour/ardour/automatable_sequence.h +++ b/libs/ardour/ardour/automatable_sequence.h @@ -22,6 +22,7 @@ #include "evoral/Sequence.hpp" #include "ardour/automatable.h" +#include "ardour/event_type_map.h" namespace ARDOUR { diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 3603ea2e72..639c5b5287 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -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; }; diff --git a/libs/ardour/ardour/event_type_map.h b/libs/ardour/ardour/event_type_map.h index fbfd9ec73c..f69d20b773 100644 --- a/libs/ardour/ardour/event_type_map.h +++ b/libs/ardour/ardour/event_type_map.h @@ -29,11 +29,15 @@ 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 diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index 82912a947d..9696784f01 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -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); diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h index f0c76ca86c..2923b784a9 100644 --- a/libs/ardour/ardour/midi_buffer.h +++ b/libs/ardour/ardour/midi_buffer.h @@ -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); } diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index cea3af5aed..3db16937fc 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -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) {} diff --git a/libs/ardour/ardour/pan_controllable.h b/libs/ardour/ardour/pan_controllable.h index 9abbec42ab..63ee9d3b8c 100644 --- a/libs/ardour/ardour/pan_controllable.h +++ b/libs/ardour/ardour/pan_controllable.h @@ -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) {} diff --git a/libs/ardour/ardour/parameter_descriptor.h b/libs/ardour/ardour/parameter_descriptor.h index a6315ae429..8916f081a3 100644 --- a/libs/ardour/ardour/parameter_descriptor.h +++ b/libs/ardour/ardour/parameter_descriptor.h @@ -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 index 0000000000..8442d1f1bf --- /dev/null +++ b/libs/ardour/ardour/parameter_types.h @@ -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__ */ + diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index dc7dbf68e1..be109885d7 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -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. * diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index f1c03a79d2..6571732bb9 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -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 ¶m, - 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 { diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index af56e12a5b..7444a54a7c 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -127,6 +127,7 @@ namespace ARDOUR { PanFrontBackAutomation, PanLFEAutomation, PluginAutomation, + PluginPropertyAutomation, SoloAutomation, MuteAutomation, MidiCCAutomation, diff --git a/libs/ardour/ardour/uri_map.h b/libs/ardour/ardour/uri_map.h index a948ea4002..d745ad58e7 100644 --- a/libs/ardour/ardour/uri_map.h +++ b/libs/ardour/ardour/uri_map.h @@ -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 diff --git a/libs/ardour/ardour/variant.h b/libs/ardour/ardour/variant.h index 1e9dda179a..cc483e3cdf 100644 --- a/libs/ardour/ardour/variant.h +++ b/libs/ardour/ardour/variant.h @@ -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[] = { diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 8629722889..466899ce48 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -31,8 +31,10 @@ #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); diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index f1305609ef..d209e733d5 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -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) { } diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index 9db99d20ce..f64eec3191 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -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); } diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 3f2f1470c2..c1eaa00977 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -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); diff --git a/libs/ardour/event_type_map.cc b/libs/ardour/event_type_map.cc index 29a363c78b..ee6270356c 100644 --- a/libs/ardour/event_type_map.cc +++ b/libs/ardour/event_type_map.cc @@ -18,9 +18,12 @@ */ +#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) { diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 48abda8cfc..44367b8cf3 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -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" @@ -100,6 +101,7 @@ #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; diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 4f41b51d1b..cf33c22424 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -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); diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index 9c1e338b6c..0261f06ceb 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -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 */ diff --git a/libs/ardour/midi_state_tracker.cc b/libs/ardour/midi_state_tracker.cc index 73b6fb639e..466fc20b63 100644 --- a/libs/ardour/midi_state_tracker.cc +++ b/libs/ardour/midi_state_tracker.cc @@ -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)); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index cf04094015..5e26251d84 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -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); } diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 4ad3123e07..3bc2d18b7b 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -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 ¶m, 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() { diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 519279bb1f..f78cd1e2f1 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -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))); diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index d09f4d561e..c7cd7160f3 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -37,10 +37,10 @@ #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; diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 189b315e20..03d75d9d79 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -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))); diff --git a/libs/ardour/uri_map.cc b/libs/ardour/uri_map.cc index 163a460624..6fac103715 100644 --- a/libs/ardour/uri_map.cc +++ b/libs/ardour/uri_map.cc @@ -30,6 +30,47 @@ 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; } |