X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=36b77e6d59fb8a8f287defe69dceac24f960f34b;hb=d89573f8e73ee7f0c28a6b9a8b8ba0f8e78c69aa;hp=0389a33c696d2666ace3c4dbfb8821624315f3bb;hpb=db3961323894d93d7159f6bc254aefe7a7ac1f99;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 0389a33c69..36b77e6d59 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -31,6 +31,7 @@ #include +#include "pbd/pathscanner.h" #include "pbd/compose.h" #include "pbd/error.h" #include "pbd/xml++.h" @@ -46,6 +47,7 @@ #include "ardour/types.h" #include "ardour/utils.h" #include "ardour/worker.h" +#include "ardour/lv2_bundled_search_path.h" #include "i18n.h" #include @@ -61,6 +63,7 @@ #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/time/time.h" #include "lv2/lv2plug.in/ns/ext/worker/worker.h" +#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #ifdef HAVE_NEW_LV2 #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" @@ -109,6 +112,8 @@ public: LV2World (); ~LV2World (); + void load_bundled_plugins(); + LilvWorld* world; LilvNode* atom_AtomPort; @@ -119,20 +124,30 @@ public: LilvNode* atom_supports; LilvNode* ev_EventPort; LilvNode* ext_logarithmic; + LilvNode* ext_notOnGUI; LilvNode* lv2_AudioPort; LilvNode* lv2_ControlPort; LilvNode* lv2_InputPort; LilvNode* lv2_OutputPort; LilvNode* lv2_enumeration; + LilvNode* lv2_freewheeling; LilvNode* lv2_inPlaceBroken; LilvNode* lv2_integer; + LilvNode* lv2_reportsLatency; LilvNode* lv2_sampleRate; LilvNode* lv2_toggled; LilvNode* midi_MidiEvent; LilvNode* rdfs_comment; + LilvNode* rsz_minimumSize; LilvNode* time_Position; LilvNode* ui_GtkUI; LilvNode* ui_external; + LilvNode* ui_externalkx; + LilvNode* units_unit; + LilvNode* units_midiNote; + +private: + bool _bundle_checked; }; static LV2World _world; @@ -277,7 +292,7 @@ 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.frames_per_cycle(); + _block_length = _engine.samples_per_cycle(); _seq_size = _engine.raw_buffer_size(DataType::MIDI); _state_version = 0; _was_activated = false; @@ -406,6 +421,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) for (uint32_t i = 0; i < num_ports; ++i) { const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, i); PortFlags flags = 0; + size_t minimumSize = 0; if (lilv_port_is_a(_impl->plugin, port, _world.lv2_OutputPort)) { flags |= PORT_OUTPUT; @@ -440,6 +456,12 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) flags |= PORT_POSITION; } } + LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize); + LilvNode* min_size = min_size_v ? lilv_nodes_get_first(min_size_v) : NULL; + if (min_size && lilv_node_is_int(min_size)) { + minimumSize = lilv_node_as_int(min_size); + } + lilv_nodes_free(min_size_v); lilv_nodes_free(buffer_types); lilv_nodes_free(atom_supports); } else { @@ -450,6 +472,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) } _port_flags.push_back(flags); + _port_minimumSize.push_back(minimumSize); } _control_data = new float[num_ports]; @@ -541,11 +564,15 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) if (!_impl->ui) { LILV_FOREACH(uis, i, uis) { const LilvUI* ui = lilv_uis_get(uis, i); - if (lilv_ui_is_a(ui, _world.ui_external)) { + if (lilv_ui_is_a(ui, _world.ui_externalkx)) { _impl->ui = ui; _impl->ui_type = _world.ui_external; break; } + if (lilv_ui_is_a(ui, _world.ui_external)) { + _impl->ui = ui; + _impl->ui_type = _world.ui_external; + } } } } @@ -593,7 +620,16 @@ LV2Plugin::is_external_ui() const if (!_impl->ui) { return false; } - return lilv_ui_is_a(_impl->ui, _world.ui_external); + return lilv_ui_is_a(_impl->ui, _world.ui_external) || lilv_ui_is_a(_impl->ui, _world.ui_externalkx); +} + +bool +LV2Plugin::is_external_kx() const +{ + if (!_impl->ui) { + return false; + } + return lilv_ui_is_a(_impl->ui, _world.ui_externalkx); } bool @@ -950,7 +986,7 @@ LV2Plugin::find_presets() lilv_node_as_string(name)))); } else { warning << string_compose( - _("Plugin \"%1\% preset \"%2%\" is missing a label\n"), + _("Plugin \"%1\" preset \"%2\" is missing a label\n"), lilv_node_as_string(lilv_plugin_get_uri(_impl->plugin)), lilv_node_as_string(preset)) << endmsg; } @@ -986,8 +1022,10 @@ LV2Plugin::load_preset(PresetRecord r) 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); + printf("LV2Plugin::load_preset %s\n", r.uri.c_str()); if (state) { + printf("found state\n"); lilv_state_restore(state, _impl->instance, set_port_value, this, 0, NULL); lilv_state_free(state); } @@ -1057,7 +1095,13 @@ LV2Plugin::do_save_preset(string name) lilv_state_free(state); - return Glib::filename_to_uri(Glib::build_filename(bundle, file_name)); + std::string uri = Glib::filename_to_uri(Glib::build_filename(bundle, file_name)); + LilvNode *node = lilv_new_uri(_world.world, uri.c_str()); + lilv_world_load_bundle(_world.world, node); + lilv_node_free(node); + printf("LV2Plugin::do_save_preset %s\n", uri.c_str()); + + return uri; } void @@ -1117,8 +1161,24 @@ LV2Plugin::write_from_ui(uint32_t index, const uint8_t* body) { if (!_from_ui) { - _from_ui = new RingBuffer( - _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS); + size_t rbs = _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS; + /* buffer data communication from plugin UI to plugin instance. + * this buffer needs to potentially hold + * (port's minimumSize) * (audio-periods) / (UI-periods) + * bytes. + * + * e.g 48kSPS / 128fpp -> audio-periods = 375 Hz + * ui-periods = 25 Hz (SuperRapidScreenUpdate) + * default minimumSize = 32K (see LV2Plugin::allocate_atom_event_buffers() + * + * it is NOT safe to overflow (msg.size will be misinterpreted) + */ + uint32_t bufsiz = 32768; + if (_atom_ev_buffers && _atom_ev_buffers[0]) { + bufsiz = lv2_evbuf_get_capacity(_atom_ev_buffers[0]); + } + rbs = max((size_t) bufsiz * 8, rbs); + _from_ui = new RingBuffer(rbs); } if (!write_to(_from_ui, index, protocol, size, body)) { @@ -1145,8 +1205,14 @@ void LV2Plugin::enable_ui_emmission() { if (!_to_ui) { - _to_ui = new RingBuffer( - _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS); + /* see note in LV2Plugin::write_from_ui() */ + uint32_t bufsiz = 32768; + if (_atom_ev_buffers && _atom_ev_buffers[0]) { + bufsiz = lv2_evbuf_get_capacity(_atom_ev_buffers[0]); + } + size_t rbs = _session.engine().raw_buffer_size(DataType::MIDI) * NBUFS; + rbs = max((size_t) bufsiz * 8, rbs); + _to_ui = new RingBuffer(rbs); } } @@ -1213,6 +1279,8 @@ LV2Plugin::set_state(const XMLNode& node, int version) return -1; } +#ifndef NO_PLUGIN_STATE + if (version < 3000) { nodes = node.children("port"); } else { @@ -1268,6 +1336,7 @@ LV2Plugin::set_state(const XMLNode& node, int version) } latency_compute_run(); +#endif return Plugin::set_state(node, version); } @@ -1277,8 +1346,10 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c { const LilvPort* port = lilv_plugin_get_port_by_index(_impl->plugin, which); + LilvNodes* portunits; LilvNode *def, *min, *max; lilv_port_get_range(_impl->plugin, port, &def, &min, &max); + portunits = lilv_port_get_value(_impl->plugin, port, _world.units_unit); desc.integer_step = lilv_port_has_property(_impl->plugin, port, _world.lv2_integer); desc.toggled = lilv_port_has_property(_impl->plugin, port, _world.lv2_toggled); @@ -1287,6 +1358,8 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c desc.label = lilv_node_as_string(lilv_port_get_name(_impl->plugin, port)); desc.lower = min ? lilv_node_as_float(min) : 0.0f; desc.upper = max ? lilv_node_as_float(max) : 1.0f; + desc.midinote = lilv_nodes_contains(portunits, _world.units_midiNote); + if (desc.sr_dependent) { desc.lower *= _session.frame_rate (); desc.upper *= _session.frame_rate (); @@ -1311,6 +1384,7 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c lilv_node_free(def); lilv_node_free(min); lilv_node_free(max); + lilv_nodes_free(portunits); return 0; } @@ -1319,6 +1393,22 @@ string 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)) { + return X_("hidden"); + } + + if (lilv_port_has_property(_impl->plugin, + lilv_plugin_get_port_by_index(_impl->plugin, which.id()), _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)) { + return X_("latency"); + } + LilvNode* name = lilv_port_get_name(_impl->plugin, lilv_plugin_get_port_by_index(_impl->plugin, which.id())); string ret(lilv_node_as_string(name)); @@ -1397,6 +1487,7 @@ LV2Plugin::allocate_atom_event_buffers() */ int count_atom_out = 0; int count_atom_in = 0; + int minimumSize = 32768; // TODO use a per-port minimum-size for (uint32_t i = 0; i < lilv_plugin_get_num_ports(p); ++i) { const LilvPort* port = lilv_plugin_get_port_by_index(p, i); if (lilv_port_is_a(p, port, _world.atom_AtomPort)) { @@ -1413,6 +1504,12 @@ LV2Plugin::allocate_atom_event_buffers() if (lilv_port_is_a(p, port, _world.lv2_OutputPort)) { count_atom_out++; } + LilvNodes* min_size_v = lilv_port_get_value(_impl->plugin, port, _world.rsz_minimumSize); + LilvNode* min_size = min_size_v ? lilv_nodes_get_first(min_size_v) : NULL; + if (min_size && lilv_node_is_int(min_size)) { + minimumSize = std::max(minimumSize, lilv_node_as_int(min_size)); + } + lilv_nodes_free(min_size_v); } lilv_nodes_free(buffer_types); lilv_nodes_free(atom_supports); @@ -1427,10 +1524,10 @@ LV2Plugin::allocate_atom_event_buffers() return; } - DEBUG_TRACE(DEBUG::LV2, string_compose("allocate %1 atom_ev_buffers\n", total_atom_buffers)); + DEBUG_TRACE(DEBUG::LV2, string_compose("allocate %1 atom_ev_buffers of %d bytes\n", total_atom_buffers, minimumSize)); _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(32768, LV2_EVBUF_ATOM, + _atom_ev_buffers[i] = lv2_evbuf_new(minimumSize, LV2_EVBUF_ATOM, LV2Plugin::urids.atom_Chunk, LV2Plugin::urids.atom_Sequence); } _atom_ev_buffers[total_atom_buffers] = 0; @@ -1539,6 +1636,12 @@ LV2Plugin::connect_and_run(BufferSet& bufs, index = out_map.get(DataType::MIDI, midi_out_index++, &valid); } if (valid && bufs.count().n_midi() > index) { + /* Note, ensure_lv2_bufsize() is not RT safe! + * However free()/alloc() is only called if a + * plugin requires a rsz:minimumSize buffersize + * and the existing buffer if smaller. + */ + bufs.ensure_lv2_bufsize((flags & PORT_INPUT), index, _port_minimumSize[port_index]); _ev_buffers[port_index] = bufs.get_lv2_midi( (flags & PORT_INPUT), index, (flags & PORT_EVENT)); } @@ -1594,7 +1697,10 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } } else if (!valid) { // Nothing we understand or care about, connect to scratch - _ev_buffers[port_index] = silent_bufs.get_lv2_midi( + // see note for midi-buffer size above + scratch_bufs.ensure_lv2_bufsize((flags & PORT_INPUT), + 0, _port_minimumSize[port_index]); + _ev_buffers[port_index] = scratch_bufs.get_lv2_midi( (flags & PORT_INPUT), 0, (flags & PORT_EVENT)); } buf = lv2_evbuf_get_buffer(_ev_buffers[port_index]); @@ -1844,10 +1950,20 @@ LV2Plugin::Impl::designated_input (const char* uri, void** bufptrs[], void** buf return port; } +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)); +} + + LV2World::LV2World() : world(lilv_world_new()) + , _bundle_checked(false) { lilv_world_load_all(world); + atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); atom_Chunk = lilv_new_uri(world, LV2_ATOM__Chunk); atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence); @@ -1856,37 +1972,55 @@ LV2World::LV2World() atom_eventTransfer = lilv_new_uri(world, LV2_ATOM__eventTransfer); ev_EventPort = lilv_new_uri(world, LILV_URI_EVENT_PORT); ext_logarithmic = lilv_new_uri(world, LV2_PORT_PROPS__logarithmic); + ext_notOnGUI = lilv_new_uri(world, LV2_PORT_PROPS__notOnGUI); lv2_AudioPort = lilv_new_uri(world, LILV_URI_AUDIO_PORT); lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT); lv2_InputPort = lilv_new_uri(world, LILV_URI_INPUT_PORT); 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_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); lv2_enumeration = lilv_new_uri(world, LV2_CORE__enumeration); + lv2_freewheeling = lilv_new_uri(world, LV2_CORE__freeWheeling); midi_MidiEvent = lilv_new_uri(world, LILV_URI_MIDI_EVENT); rdfs_comment = lilv_new_uri(world, LILV_NS_RDFS "comment"); + rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize); time_Position = lilv_new_uri(world, LV2_TIME__Position); ui_GtkUI = lilv_new_uri(world, LV2_UI__GtkUI); ui_external = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/ui#external"); + ui_externalkx = lilv_new_uri(world, "http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget"); + units_unit = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/units#unit"); + units_midiNote = lilv_new_uri(world, "http://lv2plug.in/ns/extensions/units#midiNote"); } LV2World::~LV2World() { + lilv_node_free(units_midiNote); + lilv_node_free(units_unit); + lilv_node_free(ui_externalkx); lilv_node_free(ui_external); lilv_node_free(ui_GtkUI); + lilv_node_free(time_Position); + lilv_node_free(rsz_minimumSize); + lilv_node_free(rdfs_comment); lilv_node_free(midi_MidiEvent); + lilv_node_free(lv2_enumeration); + lilv_node_free(lv2_freewheeling); lilv_node_free(lv2_toggled); lilv_node_free(lv2_sampleRate); + lilv_node_free(lv2_reportsLatency); lilv_node_free(lv2_integer); lilv_node_free(lv2_inPlaceBroken); lilv_node_free(lv2_OutputPort); lilv_node_free(lv2_InputPort); lilv_node_free(lv2_ControlPort); lilv_node_free(lv2_AudioPort); + lilv_node_free(ext_notOnGUI); lilv_node_free(ext_logarithmic); lilv_node_free(ev_EventPort); + lilv_node_free(atom_supports); lilv_node_free(atom_eventTransfer); lilv_node_free(atom_bufferType); lilv_node_free(atom_Sequence); @@ -1894,6 +2028,31 @@ LV2World::~LV2World() lilv_node_free(atom_AtomPort); } +void +LV2World::load_bundled_plugins() +{ + if (!_bundle_checked) { + cout << "Scanning folders for bundled LV2s: " << ARDOUR::lv2_bundled_search_path().to_string() << endl; + PathScanner scanner; + vector *plugin_objects = scanner (ARDOUR::lv2_bundled_search_path().to_string(), lv2_filter, 0, true, true); + if (plugin_objects) { + for ( vector::iterator x = plugin_objects->begin(); x != plugin_objects->end (); ++x) { +#ifdef WINDOWS + string uri = "file:///" + **x + "/"; +#else + string uri = "file://" + **x + "/"; +#endif + LilvNode *node = lilv_new_uri(world, uri.c_str()); + lilv_world_load_bundle(world, node); + lilv_node_free(node); + } + } + delete (plugin_objects); + + _bundle_checked = true; + } +} + LV2PluginInfo::LV2PluginInfo (const void* c_plugin) : _c_plugin(c_plugin) { @@ -1925,6 +2084,8 @@ LV2PluginInfo::load(Session& session) PluginInfoList* LV2PluginInfo::discover() { + _world.load_bundled_plugins(); + PluginInfoList* plugs = new PluginInfoList; const LilvPlugins* plugins = lilv_world_get_all_plugins(_world.world);