X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=cd1c190c383b0810fb55a49f66222834d4a5f5f4;hb=26fdf99a92abfd3e84ae5fb768cb73f99563c156;hp=5a97bfa41f6b26713e06dea858b7ed979a4c933b;hpb=c11a7a1bd7ae2d8ec0862eaa61022567dfb8a673;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 5a97bfa41f..cd1c190c38 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -155,6 +155,11 @@ public: LilvNode* units_midiNote; LilvNode* patch_writable; LilvNode* patch_Message; +#ifdef HAVE_LV2_1_2_0 + LilvNode* bufz_powerOf2BlockLength; + LilvNode* bufz_fixedBlockLength; + LilvNode* bufz_nominalBlockLength; +#endif private: bool _bundle_checked; @@ -234,7 +239,10 @@ 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) + , opts_iface(0) , state(0) + , block_length(0) + , options(0) {} /** Find the LV2 input port with the given designation. @@ -242,16 +250,19 @@ 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; + const LV2_Options_Interface* opts_iface; + LilvState* state; + LV2_Atom_Forge forge; + LV2_Atom_Forge ui_forge; + int32_t block_length; + LV2_Options_Option* options; }; LV2Plugin::LV2Plugin (AudioEngine& engine, @@ -309,11 +320,11 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) _latency_control_port = 0; _next_cycle_start = std::numeric_limits::max(); _next_cycle_speed = 1.0; - _block_length = _engine.samples_per_cycle(); _seq_size = _engine.raw_buffer_size(DataType::MIDI); _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"; @@ -355,18 +366,32 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) #ifdef HAVE_LV2_1_2_0 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) + /* 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), - sizeof(int32_t), atom_Int, &_block_length }, + sizeof(int32_t), atom_Int, &_min_block_length }, { LV2_OPTIONS_INSTANCE, 0, _uri_map.uri_to_id(LV2_BUF_SIZE__maxBlockLength), - sizeof(int32_t), atom_Int, &_block_length }, + 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 @@ -415,14 +440,38 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) } lilv_node_free(worker_iface_uri); + + 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); + if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) { error << string_compose( - _("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), + _("LV2: \"%1\" cannot be used, since it cannot do inplace processing."), + lilv_node_as_string(_impl->name)) << endmsg; + lilv_node_free(_impl->name); + lilv_node_free(_impl->author); + throw failed_constructor(); + } + +#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); +#endif #ifdef HAVE_LILV_0_16_0 // Load default state @@ -608,6 +657,23 @@ 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; +} + LV2Plugin::~LV2Plugin () { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 destroy\n", name())); @@ -618,6 +684,7 @@ LV2Plugin::~LV2Plugin () lilv_instance_free(_impl->instance); lilv_node_free(_impl->name); lilv_node_free(_impl->author); + free(_impl->options); free(_features); free(_make_path_feature.data); @@ -638,6 +705,7 @@ LV2Plugin::~LV2Plugin () delete [] _control_data; delete [] _shadow_data; + delete [] _defaults; delete [] _ev_buffers; } @@ -1074,11 +1142,25 @@ ARDOUR::lv2plugin_get_port_value(const char* port_symbol, std::string LV2Plugin::do_save_preset(string name) { + LilvNode* plug_name = lilv_plugin_get_name(_impl->plugin); + const string prefix = legalize_for_uri(lilv_node_as_string(plug_name)); const string base_name = legalize_for_uri(name); const string file_name = base_name + ".ttl"; const string bundle = Glib::build_filename( Glib::get_home_dir(), - Glib::build_filename(".lv2", base_name + ".lv2")); + Glib::build_filename(".lv2", prefix + "_" + base_name + ".lv2")); + +#ifdef HAVE_LILV_0_21_3 + /* delete reference to old preset (if any) */ + const PresetRecord* r = preset_by_label(name); + if (r) { + LilvNode* pset = lilv_new_uri (_world.world, r->uri.c_str()); + if (pset) { + lilv_world_unload_resource (_world.world, pset); + lilv_node_free(pset); + } + } +#endif LilvState* state = lilv_state_new_from_instance( _impl->plugin, @@ -1110,7 +1192,7 @@ LV2Plugin::do_save_preset(string name) std::string uri = Glib::filename_to_uri(Glib::build_filename(bundle, file_name)); LilvNode *node_bundle = lilv_new_uri(_world.world, Glib::filename_to_uri(Glib::build_filename(bundle, "/")).c_str()); LilvNode *node_preset = lilv_new_uri(_world.world, uri.c_str()); -#ifdef HAVE_LILV_0_19_2 +#ifdef HAVE_LILV_0_21_3 lilv_world_unload_resource(_world.world, node_preset); lilv_world_unload_bundle(_world.world, node_bundle); #endif @@ -1118,20 +1200,44 @@ LV2Plugin::do_save_preset(string name) lilv_world_load_resource(_world.world, node_preset); lilv_node_free(node_bundle); lilv_node_free(node_preset); + lilv_node_free(plug_name); return uri; } void LV2Plugin::do_remove_preset(string name) { - string preset_file = Glib::build_filename( - Glib::get_home_dir(), - Glib::build_filename( - Glib::build_filename(".lv2", "presets"), - name + ".ttl" - ) - ); - ::g_unlink(preset_file.c_str()); +#ifdef HAVE_LILV_0_21_3 + /* Look up preset record by label (FIXME: ick, label as ID) */ + const PresetRecord* r = preset_by_label(name); + if (!r) { + return; + } + + /* Load a LilvState for the preset. */ + LilvWorld* world = _world.world; + LilvNode* pset = lilv_new_uri(world, r->uri.c_str()); + LilvState* state = lilv_state_new_from_world(world, _uri_map.urid_map(), pset); + if (!state) { + lilv_node_free(pset); + return; + } + + /* Unload preset from world. */ + lilv_world_unload_resource(world, pset); + + /* Delete it from the file system. This will remove the preset file and the entry + from the manifest. If this results in an empty manifest (i.e. the + preset is the only thing in the bundle), then the bundle is removed. */ + lilv_state_delete(world, state); + + lilv_state_free(state); + lilv_node_free(pset); +#endif + /* Without lilv_state_delete(), we could delete the preset file, but this + would leave a broken bundle/manifest around, so the preset would still + be visible, but broken. Naively deleting a bundle is too dangerous, so + we simply do not support preset deletion with older Lilv */ } bool @@ -1870,7 +1976,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, TempoMetric tmetric = tmap.metric_at(_session.transport_frame(), &metric_i); if (_freewheel_control_port) { - *_freewheel_control_port = _session.engine().freewheeling(); + *_freewheel_control_port = _session.engine().freewheeling() ? 1.f : 0.f; } if (_bpm_control_port) { @@ -2012,7 +2118,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, LV2_Evbuf* buf = _ev_buffers[msg.index]; LV2_Evbuf_Iterator i = lv2_evbuf_end(buf); const LV2_Atom* const atom = (const LV2_Atom*)&body[0]; - if (!lv2_evbuf_write(&i, nframes, 0, atom->type, atom->size, + if (!lv2_evbuf_write(&i, nframes - 1, 0, atom->type, atom->size, (const uint8_t*)(atom + 1))) { error << "Failed to write data to LV2 event buffer\n"; } @@ -2326,10 +2432,21 @@ 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); +#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() { +#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(patch_Message); lilv_node_free(patch_writable); lilv_node_free(units_hz); @@ -2452,6 +2569,29 @@ LV2PluginInfo::discover() continue; } + if (lilv_plugin_has_feature(p, world.lv2_inPlaceBroken)) { + warning << string_compose( + _("Ignoring LV2 plugin \"%1\" since it cannot do inplace processing."), + lilv_node_as_string(name)) << endmsg; + lilv_node_free(name); + continue; + } + +#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; info->name = string(lilv_node_as_string(name));