X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=974ed87c2d9f7bd925d028fed43d39d6bd077088;hb=84272b4e27e537bf2c38c9cd25675c61addea40a;hp=46c80d56580c1210e5fa1a2e334046eef807ab47;hpb=30b087ab3d28f1585987fa3f6ae006562ae192e3;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 46c80d5658..974ed87c2d 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -37,6 +37,7 @@ #include "pbd/compose.h" #include "pbd/error.h" #include "pbd/locale_guard.h" +#include "pbd/pthread_utils.h" #include "pbd/replace_all.h" #include "pbd/xml++.h" @@ -45,10 +46,13 @@ #include "pbd/windows_special_dirs.h" #endif +#ifdef WAF_BUILD #include "libardour-config.h" +#endif #include "ardour/audio_buffer.h" #include "ardour/audioengine.h" +#include "ardour/directory_names.h" #include "ardour/debug.h" #include "ardour/lv2_plugin.h" #include "ardour/midi_patch_manager.h" @@ -367,6 +371,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, , _no_sample_accurate_ctrl (false) { init(c_plugin, rate); + latency_compute_run(); } LV2Plugin::LV2Plugin (const LV2Plugin& other) @@ -384,10 +389,16 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other) { init(other._impl->plugin, other._sample_rate); + XMLNode root (other.state_node_name ()); + other.add_state (&root); + set_state (root, Stateful::loading_state_version); + for (uint32_t i = 0; i < parameter_count(); ++i) { _control_data[i] = other._shadow_data[i]; _shadow_data[i] = other._shadow_data[i]; } + + latency_compute_run(); } void @@ -414,7 +425,9 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) _was_activated = false; _has_state_interface = false; _can_write_automation = false; +#ifdef LV2_EXTENDED _inline_display_in_gui = false; +#endif _max_latency = 0; _current_latency = 0; _impl->block_length = _session.get_block_size(); @@ -490,6 +503,8 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int); static const int32_t _min_block_length = 1; // may happen during split-cycles static const int32_t _max_block_length = 8192; // max possible (with all engines and during export) + static const int32_t rt_policy = PBD_SCHED_FIFO; + static const int32_t rt_priority = pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 2); /* Consider updating max-block-size whenever the buffersize changes. * It requires re-instantiating the plugin (which is a non-realtime operation), * so it should be done lightly and only for plugins that require it. @@ -506,6 +521,10 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) sizeof(int32_t), atom_Int, &_seq_size }, { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"), sizeof(int32_t), atom_Int, &_impl->block_length }, + { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#schedPolicy"), + sizeof(int32_t), atom_Int, &rt_policy }, + { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id("http://ardour.org/lv2/threads/#schedPriority"), + sizeof(int32_t), atom_Int, &rt_priority }, { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL } }; @@ -641,7 +660,6 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) lilv_nodes_free(optional_features); #endif -#ifdef HAVE_LILV_0_16_0 // Load default state if (_worker) { /* immediately schedule any work, @@ -656,7 +674,6 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL); } lilv_state_free(state); -#endif _sample_rate = rate; @@ -807,6 +824,8 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) if (params[i]) { *params[i] = (void*)&_shadow_data[i]; } + } else { + _shadow_data[i] = 0; } } else { _defaults[i] = 0.0f; @@ -863,7 +882,6 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate) load_supported_properties(_property_descriptors); allocate_atom_event_buffers(); - latency_compute_run(); } int @@ -1376,13 +1394,20 @@ LV2Plugin::add_state(XMLNode* root) const unsigned int saved_state = _state_version;; g_mkdir_with_parents(new_dir.c_str(), 0744); + std::string xternal_dir = _session.externals_dir (); + + if (!_plugin_state_dir.empty()) { + xternal_dir = Glib::build_filename (_plugin_state_dir, externals_dir_name); + g_mkdir_with_parents(xternal_dir.c_str(), 0744); + } + LilvState* state = lilv_state_new_from_instance( _impl->plugin, _impl->instance, _uri_map.urid_map(), scratch_dir().c_str(), file_dir().c_str(), - _session.externals_dir().c_str(), + xternal_dir.c_str(), new_dir.c_str(), NULL, const_cast(this), @@ -1412,6 +1437,7 @@ LV2Plugin::add_state(XMLNode* root) const } else { // template save (dedicated state-dir) lilv_state_free(state); + g_rmdir (xternal_dir.c_str()); // try remove unused dir --_state_version; } } else { @@ -1426,38 +1452,39 @@ LV2Plugin::add_state(XMLNode* root) const } } -// TODO: Once we can rely on lilv 0.16.0, lilv_world_get can replace this static LilvNode* get_value(LilvWorld* world, const LilvNode* subject, const LilvNode* predicate) { - LilvNodes* vs = lilv_world_find_nodes(world, subject, predicate, NULL); - if (vs) { - LilvNode* node = lilv_node_duplicate(lilv_nodes_get_first(vs)); - lilv_nodes_free(vs); - return node; - } - return NULL; + return lilv_world_get(world, subject, predicate, NULL); } void LV2Plugin::find_presets() { + /* see also LV2PluginInfo::get_presets */ LilvNode* lv2_appliesTo = lilv_new_uri(_world.world, LV2_CORE__appliesTo); LilvNode* pset_Preset = lilv_new_uri(_world.world, LV2_PRESETS__Preset); LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label"); + LilvNode* rdfs_comment = lilv_new_uri(_world.world, LILV_NS_RDFS "comment"); LilvNodes* presets = lilv_plugin_get_related(_impl->plugin, pset_Preset); LILV_FOREACH(nodes, i, presets) { const LilvNode* preset = lilv_nodes_get(presets, i); lilv_world_load_resource(_world.world, preset); LilvNode* name = get_value(_world.world, preset, rdfs_label); - bool userpreset = true; // TODO + LilvNode* comment = get_value(_world.world, preset, rdfs_comment); + /* TODO properly identify user vs factory presets. + * here's an indirect condition: only factory presets can have comments + */ + bool userpreset = comment ? false : true; if (name) { _presets.insert(std::make_pair(lilv_node_as_string(preset), Plugin::PresetRecord( lilv_node_as_string(preset), lilv_node_as_string(name), - userpreset))); + userpreset, + comment ? lilv_node_as_string (comment) : "" + ))); lilv_node_free(name); } else { warning << string_compose( @@ -1465,9 +1492,13 @@ LV2Plugin::find_presets() lilv_node_as_string(lilv_plugin_get_uri(_impl->plugin)), lilv_node_as_string(preset)) << endmsg; } + if (comment) { + lilv_node_free(comment); + } } lilv_nodes_free(presets); + lilv_node_free(rdfs_comment); lilv_node_free(rdfs_label); lilv_node_free(pset_Preset); lilv_node_free(lv2_appliesTo); @@ -1571,7 +1602,6 @@ LV2Plugin::do_save_preset(string name) Glib::build_filename(".lv2", prefix + "_" + base_name + ".lv2")); #endif -#ifdef HAVE_LILV_0_21_3 /* delete reference to old preset (if any) */ const PresetRecord* r = preset_by_label(name); if (r) { @@ -1581,7 +1611,6 @@ LV2Plugin::do_save_preset(string name) lilv_node_free(pset); } } -#endif LilvState* state = lilv_state_new_from_instance( _impl->plugin, @@ -1613,10 +1642,8 @@ LV2Plugin::do_save_preset(string name) std::string uri = Glib::filename_to_uri(Glib::build_filename(bundle, file_name)); LilvNode *node_bundle = lilv_new_uri(_world.world, Glib::filename_to_uri(Glib::build_filename(bundle, "/")).c_str()); LilvNode *node_preset = lilv_new_uri(_world.world, uri.c_str()); -#ifdef HAVE_LILV_0_21_3 lilv_world_unload_resource(_world.world, node_preset); lilv_world_unload_bundle(_world.world, node_bundle); -#endif lilv_world_load_bundle(_world.world, node_bundle); lilv_world_load_resource(_world.world, node_preset); lilv_node_free(node_bundle); @@ -1628,7 +1655,6 @@ LV2Plugin::do_save_preset(string name) void LV2Plugin::do_remove_preset(string name) { -#ifdef HAVE_LILV_0_21_3 /* Look up preset record by label (FIXME: ick, label as ID) */ const PresetRecord* r = preset_by_label(name); if (!r) { @@ -1654,11 +1680,6 @@ LV2Plugin::do_remove_preset(string name) lilv_state_free(state); lilv_node_free(pset); -#endif - /* Without lilv_state_delete(), we could delete the preset file, but this - would leave a broken bundle/manifest around, so the preset would still - be visible, but broken. Naively deleting a bundle is too dangerous, so - we simply do not support preset deletion with older Lilv */ } bool @@ -1979,6 +2000,16 @@ LV2Plugin::load_supported_properties(PropertyDescriptors& descs) lilv_nodes_free(properties); } +Variant +LV2Plugin::get_property_value (uint32_t prop_id) const +{ + std::map::const_iterator it; + if ((it = _property_values.find (prop_id)) == _property_values.end()) { + return Variant(); + } + return it->second; +} + void LV2Plugin::announce_property_values() { @@ -2166,7 +2197,17 @@ LV2Plugin::set_state(const XMLNode& node, int version) set_state_dir (""); } - latency_compute_run(); + /* Do not call latency_compute_run() concurrently with connect_and_run(). + * So far this can only guarnteed when the session is loading, + * and the plugin has not been added to the processor chain. + * + * Ideally this would clso be called when copying a plugin from another track, + * but NOT when copying the state from a plugin to another (active) plugin + * instance. + */ + if (_session.loading ()) { + latency_compute_run(); + } #endif return Plugin::set_state(node, version); @@ -2321,18 +2362,22 @@ LV2Plugin::describe_parameter(Evoral::Parameter which) { if (( which.type() == PluginAutomation) && ( which.id() < parameter_count()) ) { - if (lilv_port_has_property(_impl->plugin, - lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.ext_notOnGUI)) { + const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, which.id()); + + if (lilv_port_has_property(_impl->plugin, port, _world.ext_notOnGUI)) { + return X_("hidden"); + } + + const LilvPort* fwport = lilv_plugin_get_port_by_designation(_impl->plugin, _world.lv2_InputPort, _world.lv2_freewheeling); + if (fwport && fwport == port) { return X_("hidden"); } - if (lilv_port_has_property(_impl->plugin, - lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.lv2_freewheeling)) { + if (lilv_port_has_property(_impl->plugin, port, _world.lv2_freewheeling)) { return X_("hidden"); } - if (lilv_port_has_property(_impl->plugin, - lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _world.lv2_reportsLatency)) { + if (lilv_port_has_property(_impl->plugin, port, _world.lv2_reportsLatency)) { return X_("latency"); } @@ -2353,7 +2398,7 @@ LV2Plugin::max_latency () const } samplecnt_t -LV2Plugin::signal_latency() const +LV2Plugin::plugin_latency() const { if (_latency_control_port) { return (samplecnt_t)floor(*_latency_control_port); @@ -2508,7 +2553,7 @@ write_position(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame sample; #ifdef HAVE_LV2_1_10_0 lv2_atom_forge_object(forge, &sample, 0, urids.time_Position); - lv2_atom_forge_key(forge, urids.time_sample); + lv2_atom_forge_key(forge, urids.time_frame); lv2_atom_forge_long(forge, position); lv2_atom_forge_key(forge, urids.time_speed); lv2_atom_forge_float(forge, speed); @@ -2525,7 +2570,7 @@ write_position(LV2_Atom_Forge* forge, lv2_atom_forge_float(forge, bpm); #else lv2_atom_forge_blank(forge, &sample, 1, urids.time_Position); - lv2_atom_forge_property_head(forge, urids.time_sample, 0); + lv2_atom_forge_property_head(forge, urids.time_frame, 0); lv2_atom_forge_long(forge, position); lv2_atom_forge_property_head(forge, urids.time_speed, 0); lv2_atom_forge_float(forge, speed); @@ -2551,7 +2596,7 @@ write_position(LV2_Atom_Forge* forge, int LV2Plugin::connect_and_run(BufferSet& bufs, samplepos_t start, samplepos_t end, double speed, - ChanMapping in_map, ChanMapping out_map, + ChanMapping const& in_map, ChanMapping const& out_map, pframes_t nframes, samplecnt_t offset) { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 run %2 offset %3\n", name(), nframes, offset)); @@ -2927,6 +2972,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, // Emit PropertyChanged signal for UI // TODO: This should emit the control's Changed signal PropertyChanged(prop_id, Variant(Variant::PATH, path)); + _property_values[prop_id] = Variant(Variant::PATH, path); } else { std::cerr << "warning: patch:Set for unknown property" << std::endl; } @@ -3030,18 +3076,6 @@ LV2Plugin::designated_bypass_port () return UINT32_MAX; } -void -LV2Plugin::print_parameter(uint32_t param, char* buf, uint32_t len) const -{ - if (buf && len) { - if (param < parameter_count()) { - snprintf(buf, len, "%.3f", get_parameter(param)); - } else { - strcat(buf, "0"); - } - } -} - boost::shared_ptr LV2Plugin::get_scale_points(uint32_t port_index) const { @@ -3389,19 +3423,28 @@ LV2PluginInfo::get_presets (bool /*user_only*/) const LilvNode* lv2_appliesTo = lilv_new_uri(_world.world, LV2_CORE__appliesTo); LilvNode* pset_Preset = lilv_new_uri(_world.world, LV2_PRESETS__Preset); LilvNode* rdfs_label = lilv_new_uri(_world.world, LILV_NS_RDFS "label"); + LilvNode* rdfs_comment = lilv_new_uri(_world.world, LILV_NS_RDFS "comment"); LilvNodes* presets = lilv_plugin_get_related(lp, pset_Preset); LILV_FOREACH(nodes, i, presets) { const LilvNode* preset = lilv_nodes_get(presets, i); lilv_world_load_resource(_world.world, preset); LilvNode* name = get_value(_world.world, preset, rdfs_label); - bool userpreset = true; // TODO + LilvNode* comment = get_value(_world.world, preset, rdfs_comment); + /* TODO properly identify user vs factory presets. + * here's an indirect condition: only factory presets can have comments + */ + bool userpreset = comment ? false : true; if (name) { - p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset)); + p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset, comment ? lilv_node_as_string (comment) : "")); lilv_node_free(name); } + if (comment) { + lilv_node_free(comment); + } } lilv_nodes_free(presets); + lilv_node_free(rdfs_comment); lilv_node_free(rdfs_label); lilv_node_free(pset_Preset); lilv_node_free(lv2_appliesTo); @@ -3409,31 +3452,6 @@ LV2PluginInfo::get_presets (bool /*user_only*/) const return p; } -bool -LV2PluginInfo::in_category (const std::string &c) const -{ - // TODO use untranslated lilv_plugin_get_class() - // match gtk2_ardour/plugin_selector.cc - return category == c; -} - -bool -LV2PluginInfo::is_instrument () const -{ - if (category == "Instrument") { - return true; - } -#if 1 - /* until we make sure that category remains untranslated in the lv2.ttl spec - * and until most instruments also classify themselves as such, there's a 2nd check: - */ - if (n_inputs.n_midi() > 0 && n_inputs.n_audio() == 0 && n_outputs.n_audio() > 0) { - return true; - } -#endif - return false; -} - PluginInfoList* LV2PluginInfo::discover() {