X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=507e546adc7239362c84712e4ae80afbf0c8b5c4;hb=22ea6423cd274bf57ea687ddbd738ae734fc4e5c;hp=a3283e8d36942e1a0ba4763ce5b5a7ce8d2cf42c;hpb=e72c0b81a7a1fe569f698e2e7a21ce91f42e8f14;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index a3283e8d36..507e546adc 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -25,7 +25,7 @@ #include #include -#include +#include "pbd/gstdio_compat.h" #include #include @@ -155,8 +155,12 @@ public: LilvNode* units_midiNote; LilvNode* patch_writable; LilvNode* patch_Message; + LilvNode* lv2_noSampleAccurateCtrl; +#ifdef HAVE_LV2_1_2_0 LilvNode* bufz_powerOf2BlockLength; LilvNode* bufz_fixedBlockLength; + LilvNode* bufz_nominalBlockLength; +#endif private: bool _bundle_checked; @@ -236,7 +240,14 @@ log_printf(LV2_Log_Handle handle, struct LV2Plugin::Impl { Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0) , work_iface(0) +#ifdef HAVE_LV2_1_2_0 + , opts_iface(0) +#endif , state(0) + , block_length(0) +#ifdef HAVE_LV2_1_2_0 + , options(0) +#endif {} /** Find the LV2 input port with the given designation. @@ -244,16 +255,23 @@ struct LV2Plugin::Impl { */ const LilvPort* designated_input (const char* uri, void** bufptrs[], void** bufptr); - const LilvPlugin* plugin; - const LilvUI* ui; - const LilvNode* ui_type; - LilvNode* name; - LilvNode* author; - LilvInstance* instance; - const LV2_Worker_Interface* work_iface; - LilvState* state; - LV2_Atom_Forge forge; - LV2_Atom_Forge ui_forge; + const LilvPlugin* plugin; + const LilvUI* ui; + const LilvNode* ui_type; + LilvNode* name; + LilvNode* author; + LilvInstance* instance; + const LV2_Worker_Interface* work_iface; +#ifdef HAVE_LV2_1_2_0 + const LV2_Options_Interface* opts_iface; +#endif + LilvState* state; + LV2_Atom_Forge forge; + LV2_Atom_Forge ui_forge; + int32_t block_length; +#ifdef HAVE_LV2_1_2_0 + LV2_Options_Option* options; +#endif }; LV2Plugin::LV2Plugin (AudioEngine& engine, @@ -269,6 +287,7 @@ LV2Plugin::LV2Plugin (AudioEngine& engine, , _patch_port_in_index((uint32_t)-1) , _patch_port_out_index((uint32_t)-1) , _uri_map(URIMap::instance()) + , _no_sample_accurate_ctrl (false) { init(c_plugin, rate); } @@ -283,6 +302,7 @@ LV2Plugin::LV2Plugin (const LV2Plugin& other) , _patch_port_in_index((uint32_t)-1) , _patch_port_out_index((uint32_t)-1) , _uri_map(URIMap::instance()) + , _no_sample_accurate_ctrl (false) { init(other._impl->plugin, other._sample_rate); @@ -315,6 +335,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) _state_version = 0; _was_activated = false; _has_state_interface = false; + _impl->block_length = _session.get_block_size(); _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access"; _data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access"; @@ -358,9 +379,12 @@ LV2Plugin::init(const void* c_plugin, framecnt_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) - /* @drobilla: Does it make sense to re-instantiate all plugins - * whenever the buffersize changes? - * Is there a way to find plugins that require the min/max blocksize option? + /* 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. + * + * given that the block-size can change at any time (split-cycles) ardour currently + * does not support plugins that require bufz_fixedBlockLength. */ LV2_Options_Option options[] = { { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__minBlockLength), @@ -369,11 +393,16 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) sizeof(int32_t), atom_Int, &_max_block_length }, { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__sequenceSize), 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, 0, 0, 0, NULL } }; + _impl->options = (LV2_Options_Option*) malloc (sizeof (options)); + memcpy ((void*) _impl->options, (void*) options, sizeof (options)); + _options_feature.URI = LV2_OPTIONS__options; - _options_feature.data = options; + _options_feature.data = _impl->options; _features[n_features++] = &_options_feature; #endif @@ -422,6 +451,16 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) } lilv_node_free(worker_iface_uri); + +#ifdef HAVE_LV2_1_2_0 + LilvNode* options_iface_uri = lilv_new_uri(_world.world, LV2_OPTIONS__interface); + if (lilv_plugin_has_extension_data(plugin, options_iface_uri)) { + _impl->opts_iface = (const LV2_Options_Interface*)extension_data( + LV2_OPTIONS__interface); + } + lilv_node_free(options_iface_uri); +#endif + if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) { error << string_compose( _("LV2: \"%1\" cannot be used, since it cannot do inplace processing."), @@ -430,16 +469,28 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) lilv_node_free(_impl->author); throw failed_constructor(); } - if (lilv_plugin_has_feature(plugin, _world.bufz_powerOf2BlockLength) || - lilv_plugin_has_feature(plugin, _world.bufz_fixedBlockLength) + +#ifdef HAVE_LV2_1_2_0 + LilvNodes *required_features = lilv_plugin_get_required_features (plugin); + if (lilv_nodes_contains (required_features, _world.bufz_powerOf2BlockLength) || + lilv_nodes_contains (required_features, _world.bufz_fixedBlockLength) ) { error << string_compose( _("LV2: \"%1\" buffer-size requirements cannot be satisfied."), lilv_node_as_string(_impl->name)) << endmsg; lilv_node_free(_impl->name); lilv_node_free(_impl->author); + lilv_nodes_free(required_features); throw failed_constructor(); } + lilv_nodes_free(required_features); + + LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin); + if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) { + _no_sample_accurate_ctrl = true; + } + lilv_nodes_free(optional_features); +#endif #ifdef HAVE_LILV_0_16_0 // Load default state @@ -448,6 +499,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) if (state && _has_state_interface) { lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL); } + lilv_state_free(state); #endif _sample_rate = rate; @@ -625,6 +677,42 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) latency_compute_run(); } +int +LV2Plugin::set_block_size (pframes_t nframes) +{ +#ifdef HAVE_LV2_1_2_0 + if (_impl->opts_iface) { + LV2_URID atom_Int = _uri_map.uri_to_id(LV2_ATOM__Int); + _impl->block_length = nframes; + LV2_Options_Option block_size_option = { + LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id ("http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"), + sizeof(int32_t), atom_Int, (void*)&_impl->block_length + }; + _impl->opts_iface->set (_impl->instance->lv2_handle, &block_size_option); + } +#endif + return 0; +} + +bool +LV2Plugin::requires_fixed_sized_buffers () const +{ + /* This controls if Ardour will split the plugin's run() + * on automation events in order to pass sample-accurate automation + * via standard control-ports. + * + * When returning true Ardour will *not* sub-divide the process-cycle. + * Automation events that happen between cycle-start and cycle-end will be + * ignored (ctrl values are interpolated to cycle-start). + * NB. Atom Sequences are still sample accurate. + * + * Note: This does not guarantee a fixed block-size. + * e.g The process cycle may be split when looping, also + * the period-size may change any time: see set_block_size() + */ + return _no_sample_accurate_ctrl; +} + LV2Plugin::~LV2Plugin () { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 destroy\n", name())); @@ -633,8 +721,12 @@ LV2Plugin::~LV2Plugin () cleanup(); lilv_instance_free(_impl->instance); + lilv_state_free(_impl->state); lilv_node_free(_impl->name); lilv_node_free(_impl->author); +#ifdef HAVE_LV2_1_2_0 + free(_impl->options); +#endif free(_features); free(_make_path_feature.data); @@ -655,6 +747,7 @@ LV2Plugin::~LV2Plugin () delete [] _control_data; delete [] _shadow_data; + delete [] _defaults; delete [] _ev_buffers; } @@ -875,7 +968,11 @@ LV2Plugin::c_ui_type() const std::string LV2Plugin::plugin_dir() const { - return Glib::build_filename(_session.plugins_dir(), _insert_id.to_s()); + if (!_plugin_state_dir.empty ()){ + return Glib::build_filename(_plugin_state_dir, _insert_id.to_s()); + } else { + return Glib::build_filename(_session.plugins_dir(), _insert_id.to_s()); + } } /** Directory for files created by the plugin (except during save). */ @@ -930,7 +1027,7 @@ LV2Plugin::add_state(XMLNode* root) const assert(_insert_id != PBD::ID("0")); XMLNode* child; - char buf[16]; + char buf[32]; LocaleGuard lg(X_("C")); for (uint32_t i = 0; i < parameter_count(); ++i) { @@ -943,6 +1040,10 @@ LV2Plugin::add_state(XMLNode* root) const } } + if (!_plugin_state_dir.empty()) { + root->add_property("template-dir", _plugin_state_dir); + } + if (_has_state_interface) { // Provisionally increment state version and create directory const std::string new_dir = state_dir(++_state_version); @@ -961,7 +1062,9 @@ LV2Plugin::add_state(XMLNode* root) const 0, NULL); - if (!_impl->state || !lilv_state_equals(state, _impl->state)) { + if (!_plugin_state_dir.empty() + || !_impl->state + || !lilv_state_equals(state, _impl->state)) { lilv_state_save(_world.world, _uri_map.urid_map(), _uri_map.urid_unmap(), @@ -970,8 +1073,14 @@ LV2Plugin::add_state(XMLNode* root) const new_dir.c_str(), "state.ttl"); - lilv_state_free(_impl->state); - _impl->state = state; + if (_plugin_state_dir.empty()) { + // normal session save + lilv_state_free(_impl->state); + _impl->state = state; + } else { + // template save (dedicated state-dir) + lilv_state_free(state); + } } else { // State is identical, decrement version and nuke directory lilv_state_free(state); @@ -1008,11 +1117,13 @@ LV2Plugin::find_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 if (name) { _presets.insert(std::make_pair(lilv_node_as_string(preset), Plugin::PresetRecord( lilv_node_as_string(preset), - lilv_node_as_string(name)))); + lilv_node_as_string(name), + userpreset))); lilv_node_free(name); } else { warning << string_compose( @@ -1571,7 +1682,19 @@ LV2Plugin::work_response(uint32_t size, const void* data) void LV2Plugin::set_insert_id(PBD::ID id) { - _insert_id = id; + if (_insert_id == "0") { + _insert_id = id; + } else if (_insert_id != id) { + lilv_state_free(_impl->state); + _impl->state = NULL; + _insert_id = id; + } +} + +void +LV2Plugin::set_state_dir (const std::string& d) +{ + _plugin_state_dir = d; } int @@ -1629,6 +1752,10 @@ LV2Plugin::set_state(const XMLNode& node, int version) set_parameter(port_id, atof(value)); } + if ((prop = node.property("template-dir")) != 0) { + set_state_dir (prop->value ()); + } + _state_version = 0; if ((prop = node.property("state-dir")) != 0) { if (sscanf(prop->value().c_str(), "state%u", &_state_version) != 1) { @@ -1645,6 +1772,15 @@ LV2Plugin::set_state(const XMLNode& node, int version) _world.world, _uri_map.urid_map(), NULL, state_file.c_str()); lilv_state_restore(state, _impl->instance, NULL, NULL, 0, NULL); + lilv_state_free(_impl->state); + _impl->state = state; + } + + if (!_plugin_state_dir.empty ()) { + // force save with session, next time (increment counter) + lilv_state_free (_impl->state); + _impl->state = NULL; + set_state_dir (""); } latency_compute_run(); @@ -1787,7 +1923,6 @@ LV2Plugin::cleanup() { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 cleanup\n", name())); - activate(); deactivate(); lilv_instance_free(_impl->instance); _impl->instance = NULL; @@ -2333,7 +2468,7 @@ LV2Plugin::Impl::designated_input (const char* uri, void** bufptrs[], void** buf static bool lv2_filter (const string& str, void* /*arg*/) { /* Not a dotfile, has a prefix before a period, suffix is "lv2" */ - + return str[0] != '.' && (str.length() > 3 && str.find (".lv2") == (str.length() - 4)); } @@ -2381,15 +2516,26 @@ LV2World::LV2World() units_db = lilv_new_uri(world, LV2_UNITS__db); patch_writable = lilv_new_uri(world, LV2_PATCH__writable); patch_Message = lilv_new_uri(world, LV2_PATCH__Message); + lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "noSampleAccurateControls"); +#ifdef HAVE_LV2_1_2_0 bufz_powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength); bufz_fixedBlockLength = lilv_new_uri(world, LV2_BUF_SIZE__fixedBlockLength); + bufz_nominalBlockLength = lilv_new_uri(world, "http://lv2plug.in/ns/ext/buf-size#nominalBlockLength"); +#endif } LV2World::~LV2World() { + if (!world) { + return; + } +#ifdef HAVE_LV2_1_2_0 + lilv_node_free(bufz_nominalBlockLength); lilv_node_free(bufz_fixedBlockLength); lilv_node_free(bufz_powerOf2BlockLength); +#endif + lilv_node_free(lv2_noSampleAccurateCtrl); lilv_node_free(patch_Message); lilv_node_free(patch_writable); lilv_node_free(units_hz); @@ -2427,6 +2573,7 @@ LV2World::~LV2World() lilv_node_free(atom_Chunk); lilv_node_free(atom_AtomPort); lilv_world_free(world); + world = NULL; } void @@ -2488,6 +2635,76 @@ LV2PluginInfo::load(Session& session) return PluginPtr(); } +std::vector +LV2PluginInfo::get_presets (bool /*user_only*/) const +{ + std::vector p; +#ifndef NO_PLUGIN_STATE + const LilvPlugin* lp = NULL; + try { + PluginPtr plugin; + const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world); + LilvNode* uri = lilv_new_uri(_world.world, _plugin_uri); + if (!uri) { throw failed_constructor(); } + lp = lilv_plugins_get_by_uri(plugins, uri); + if (!lp) { throw failed_constructor(); } + lilv_node_free(uri); + } catch (failed_constructor& err) { + return p; + } + assert (lp); + // see LV2Plugin::find_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"); + + 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 + if (name) { + p.push_back (Plugin::PresetRecord (lilv_node_as_string(preset), lilv_node_as_string(name), userpreset)); + lilv_node_free(name); + } + } + lilv_nodes_free(presets); + lilv_node_free(rdfs_label); + lilv_node_free(pset_Preset); + lilv_node_free(lv2_appliesTo); +#endif + return p; +} + +bool +LV2PluginInfo::in_category (const std::string &c) const +{ + // TODO use untranslated lilv_plugin_get_class() + // match gtk2_ardour/plugin_selector.cc + if (category == c) { + return true; + } + return false; +} + +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() { @@ -2520,15 +2737,20 @@ LV2PluginInfo::discover() continue; } - if (lilv_plugin_has_feature(p, _world.bufz_powerOf2BlockLength) || - lilv_plugin_has_feature(p, world.bufz_fixedBlockLength) +#ifdef HAVE_LV2_1_2_0 + LilvNodes *required_features = lilv_plugin_get_required_features (p); + if (lilv_nodes_contains (required_features, world.bufz_powerOf2BlockLength) || + lilv_nodes_contains (required_features, world.bufz_fixedBlockLength) ) { warning << string_compose( _("Ignoring LV2 plugin \"%1\" because its buffer-size requirements cannot be satisfied."), lilv_node_as_string(name)) << endmsg; + lilv_nodes_free(required_features); lilv_node_free(name); continue; } + lilv_nodes_free(required_features); +#endif info->type = LV2;