X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=a1a7ce860a4d56ceea7cb754c4b97d8c8d93662e;hb=761747760921b5c6b3f4899ca1c40e9aa27bc4a0;hp=25ae65e17ba174fdc60ddd6fb4aa8bdf060f5662;hpb=e2b06156c9106939fb23d8ff4567cf234f7d3001;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 25ae65e17b..a1a7ce860a 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -45,6 +45,7 @@ #include "ardour/audioengine.h" #include "ardour/debug.h" #include "ardour/lv2_plugin.h" +#include "ardour/midi_patch_manager.h" #include "ardour/session.h" #include "ardour/tempo.h" #include "ardour/types.h" @@ -70,6 +71,7 @@ #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "lv2/lv2plug.in/ns/extensions/units/units.h" #include "lv2/lv2plug.in/ns/ext/patch/patch.h" +#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" #ifdef HAVE_LV2_1_2_0 #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" @@ -109,6 +111,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +bool LV2Plugin::force_state_save = false; + class LV2World : boost::noncopyable { public: LV2World (); @@ -131,14 +135,18 @@ public: LilvNode* ext_causesArtifacts; LilvNode* ext_notAutomatic; LilvNode* ext_rangeSteps; + LilvNode* groups_group; + LilvNode* groups_element; LilvNode* lv2_AudioPort; LilvNode* lv2_ControlPort; LilvNode* lv2_InputPort; LilvNode* lv2_OutputPort; + LilvNode* lv2_designation; LilvNode* lv2_enumeration; LilvNode* lv2_freewheeling; LilvNode* lv2_inPlaceBroken; LilvNode* lv2_isSideChain; + LilvNode* lv2_index; LilvNode* lv2_integer; LilvNode* lv2_default; LilvNode* lv2_minimum; @@ -221,6 +229,13 @@ queue_draw (LV2_Inline_Display_Handle handle) LV2Plugin* plugin = (LV2Plugin*)handle; plugin->QueueDraw(); /* EMIT SIGNAL */ } + +static void +midnam_update (LV2_Midnam_Handle handle) +{ + LV2Plugin* plugin = (LV2Plugin*)handle; + plugin->UpdateMidnam (); /* EMIT SIGNAL */ +} #endif /* log extension */ @@ -278,6 +293,7 @@ struct LV2Plugin::Impl { #endif #ifdef LV2_EXTENDED , queue_draw(0) + , midnam(0) #endif {} @@ -305,6 +321,7 @@ struct LV2Plugin::Impl { #endif #ifdef LV2_EXTENDED LV2_Inline_Display* queue_draw; + LV2_Midnam* midnam; #endif }; @@ -397,7 +414,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) lilv_node_free(state_uri); lilv_node_free(state_iface_uri); - _features = (LV2_Feature**)calloc(12, sizeof(LV2_Feature*)); + _features = (LV2_Feature**)calloc(13, sizeof(LV2_Feature*)); _features[0] = &_instance_access_feature; _features[1] = &_data_access_feature; _features[2] = &_make_path_feature; @@ -423,6 +440,15 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) _queue_draw_feature.URI = LV2_INLINEDISPLAY__queue_draw; _queue_draw_feature.data = _impl->queue_draw; _features[n_features++] = &_queue_draw_feature; + + _impl->midnam = (LV2_Midnam*) + malloc (sizeof(LV2_Midnam)); + _impl->midnam->handle = this; + _impl->midnam->update = midnam_update; + + _midnam_feature.URI = LV2_MIDNAM__update; + _midnam_feature.data = _impl->midnam; + _features[n_features++] = &_midnam_feature; #endif #ifdef HAVE_LV2_1_2_0 @@ -519,6 +545,12 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) #ifdef LV2_EXTENDED _display_interface = (const LV2_Inline_Display_Interface*) extension_data (LV2_INLINEDISPLAY__interface); + + _midname_interface = (const LV2_Midnam_Interface*) + extension_data (LV2_MIDNAM__interface); + if (_midname_interface) { + read_midnam (); + } #endif if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) { @@ -838,6 +870,15 @@ LV2Plugin::~LV2Plugin () deactivate(); cleanup(); +#ifdef LV2_EXTENDED + if (has_midnam ()) { + std::stringstream ss; + ss << (void*)this; + ss << unique_id(); + MIDI::Name::MidiPatchManager::instance().remove_custom_midnam (ss.str()); + } +#endif + lilv_instance_free(_impl->instance); lilv_state_free(_impl->state); lilv_node_free(_impl->name); @@ -847,6 +888,7 @@ LV2Plugin::~LV2Plugin () #endif #ifdef LV2_EXTENDED free(_impl->queue_draw); + free(_impl->midnam); #endif free(_features); @@ -928,6 +970,49 @@ LV2Plugin::render_inline_display (uint32_t w, uint32_t h) { } return NULL; } + +bool +LV2Plugin::has_midnam () { + return _midname_interface ? true : false; +} + +bool +LV2Plugin::read_midnam () { + bool rv = false; + if (!_midname_interface) { + return rv; + } + char* midnam = _midname_interface->midnam ((void*)_impl->instance->lv2_handle); + if (midnam) { + std::stringstream ss; + ss << (void*)this; + ss << unique_id(); + rv = MIDI::Name::MidiPatchManager::instance().update_custom_midnam (ss.str(), midnam); + } +#ifndef NDEBUG + if (rv) { + info << string_compose(_("LV2: update midnam for plugin '%1'"), name ()) << endmsg; + } else { + warning << string_compose(_("LV2: Failed to parse midnam of plugin '%1'"), name ()) << endmsg; + } +#endif + _midname_interface->free (midnam); + return rv; +} + +std::string +LV2Plugin::midnam_model () { + std::string rv; + if (!_midname_interface) { + return rv; + } + char* model = _midname_interface->model ((void*)_impl->instance->lv2_handle); + if (model) { + rv = model; + } + _midname_interface->free (model); + return rv; +} #endif string @@ -1233,6 +1318,8 @@ LV2Plugin::add_state(XMLNode* root) const if (_has_state_interface) { // Provisionally increment state version and create directory const std::string new_dir = state_dir(++_state_version); + // and keep track of it (for templates & archive) + unsigned int saved_state = _state_version;; g_mkdir_with_parents(new_dir.c_str(), 0744); LilvState* state = lilv_state_new_from_instance( @@ -1248,7 +1335,7 @@ LV2Plugin::add_state(XMLNode* root) const 0, NULL); - if (!_plugin_state_dir.empty() + if (!_plugin_state_dir.empty() || force_state_save || !_impl->state || !lilv_state_equals(state, _impl->state)) { lilv_state_save(_world.world, @@ -1259,22 +1346,29 @@ LV2Plugin::add_state(XMLNode* root) const new_dir.c_str(), "state.ttl"); - if (_plugin_state_dir.empty()) { + if (force_state_save) { + // archive or save-as + lilv_state_free(state); + --_state_version; + } + else 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); + --_state_version; } } else { // State is identical, decrement version and nuke directory lilv_state_free(state); PBD::remove_directory(new_dir); --_state_version; + saved_state = _state_version; } - root->add_property("state-dir", string_compose("state%1", _state_version)); + root->add_property("state-dir", string_compose("state%1", saved_state)); } } @@ -1553,7 +1647,8 @@ LV2Plugin::write_from_ui(uint32_t index, if (_atom_ev_buffers && _atom_ev_buffers[0]) { bufsiz = lv2_evbuf_get_capacity(_atom_ev_buffers[0]); } - rbs = max((size_t) bufsiz * 8, rbs); + int fact = ceilf(_session.frame_rate () / 3000.f); + rbs = max((size_t) bufsiz * std::max (8, fact), rbs); _from_ui = new RingBuffer(rbs); } @@ -1663,12 +1758,12 @@ 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, _uri_map.urids.patch_Set); + lv2_atom_forge_object(forge, &frame, 0, _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, _uri_map.urids.patch_value); #else - lv2_atom_forge_blank(forge, &frame, 1, _uri_map.urids.patch_Set); + lv2_atom_forge_blank(forge, &frame, 0, _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, _uri_map.urids.patch_value, 0); @@ -1730,14 +1825,26 @@ load_parameter_descriptor(LV2World& world, if (label) { desc.label = lilv_node_as_string(label); } - if (def && lilv_node_is_float(def)) { - desc.normal = lilv_node_as_float(def); + if (def) { + if (lilv_node_is_float(def)) { + desc.normal = lilv_node_as_float(def); + } else if (lilv_node_is_int(def)) { + desc.normal = lilv_node_as_int(def); + } } - if (minimum && lilv_node_is_float(minimum)) { - desc.lower = lilv_node_as_float(minimum); + if (minimum) { + if (lilv_node_is_float(minimum)) { + desc.lower = lilv_node_as_float(minimum); + } else if (lilv_node_is_int(minimum)) { + desc.lower = lilv_node_as_int(minimum); + } } - if (maximum && lilv_node_is_float(maximum)) { - desc.upper = lilv_node_as_float(maximum); + if (maximum) { + if (lilv_node_is_float(maximum)) { + desc.upper = lilv_node_as_float(maximum); + } else if (lilv_node_is_int(maximum)) { + desc.upper = lilv_node_as_int(maximum); + } } load_parameter_descriptor_units(lworld, desc, units); desc.datatype = datatype; @@ -1805,9 +1912,9 @@ 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, _uri_map.urids.patch_Get); + lv2_atom_forge_object(forge, &frame, 0, _uri_map.urids.patch_Get); #else - lv2_atom_forge_blank(forge, &frame, 1, _uri_map.urids.patch_Get); + lv2_atom_forge_blank(forge, &frame, 0, _uri_map.urids.patch_Get); #endif // Write message to UI=>Plugin ring @@ -2097,13 +2204,55 @@ LV2Plugin::describe_io_port (ARDOUR::DataType dt, bool input, uint32_t id) const return Plugin::IOPortDescription ("?"); } - LilvNode* name = lilv_port_get_name(_impl->plugin, - lilv_plugin_get_port_by_index(_impl->plugin, idx)); + const LilvPort* pport = lilv_plugin_get_port_by_index (_impl->plugin, idx); + + LilvNode* name = lilv_port_get_name(_impl->plugin, pport); Plugin::IOPortDescription iod (lilv_node_as_string (name)); lilv_node_free(name); - if (lilv_port_has_property(_impl->plugin, - lilv_plugin_get_port_by_index(_impl->plugin, idx), _world.lv2_isSideChain)) { + /* get the port's pg:group */ + LilvNodes* groups = lilv_port_get_value (_impl->plugin, pport, _world.groups_group); + if (lilv_nodes_size (groups) > 0) { + const LilvNode* group = lilv_nodes_get_first (groups); + LilvNodes* grouplabel = lilv_world_find_nodes (_world.world, group, _world.rdfs_label, NULL); + + /* get the name of the port-group */ + if (lilv_nodes_size (grouplabel) > 0) { + const LilvNode* grpname = lilv_nodes_get_first (grouplabel); + iod.group_name = lilv_node_as_string (grpname); + } + lilv_nodes_free (grouplabel); + + /* get all port designations. + * we're interested in e.g. lv2:designation pg:right */ + LilvNodes* designations = lilv_port_get_value (_impl->plugin, pport, _world.lv2_designation); + if (lilv_nodes_size (designations) > 0) { + /* get all pg:elements of the pg:group */ + LilvNodes* group_childs = lilv_world_find_nodes (_world.world, group, _world.groups_element, NULL); + if (lilv_nodes_size (group_childs) > 0) { + /* iterate over all port designations .. */ + LILV_FOREACH (nodes, i, designations) { + const LilvNode* designation = lilv_nodes_get (designations, i); + /* match the lv2:designation's element against the port-group's element */ + LILV_FOREACH (nodes, j, group_childs) { + const LilvNode* group_element = lilv_nodes_get (group_childs, j); + LilvNodes* elem = lilv_world_find_nodes (_world.world, group_element, _world.lv2_designation, designation); + /* found it. Now look up the index (channel-number) of the pg:Element */ + if (lilv_nodes_size (elem) > 0) { + LilvNodes* idx = lilv_world_find_nodes (_world.world, lilv_nodes_get_first (elem), _world.lv2_index, NULL); + if (lilv_node_is_int (lilv_nodes_get_first (idx))) { + iod.group_channel = lilv_node_as_int(lilv_nodes_get_first (idx)); + } + } + } + } + } + } + lilv_nodes_free (groups); + lilv_nodes_free (designations); + } + + if (lilv_port_has_property(_impl->plugin, pport, _world.lv2_isSideChain)) { iod.is_sidechain = true; } return iod; @@ -2300,7 +2449,7 @@ write_position(LV2_Atom_Forge* forge, 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, urids.time_Position); + lv2_atom_forge_object(forge, &frame, 0, urids.time_Position); lv2_atom_forge_key(forge, urids.time_frame); lv2_atom_forge_long(forge, position); lv2_atom_forge_key(forge, urids.time_speed); @@ -2361,7 +2510,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } if (_bpm_control_port) { - *_bpm_control_port = tmap.tempo_at_frame (start).beats_per_minute(); + *_bpm_control_port = tmap.tempo_at_frame (start).note_types_per_minute(); } #ifdef LV2_EXTENDED @@ -2433,7 +2582,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, if (valid && (flags & PORT_INPUT)) { if ((flags & PORT_POSITION)) { Timecode::BBT_Time bbt (tmap.bbt_at_frame (start)); - double bpm = tmap.tempo_at_frame (start).beats_per_minute(); + double bpm = tmap.tempo_at_frame (start).note_types_per_minute(); double beatpos = (bbt.bars - 1) * tmetric.meter().divisions_per_bar() + (bbt.beats - 1) + (bbt.ticks / Timecode::BBT_Time::ticks_per_beat); @@ -2465,7 +2614,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, MetricSection* metric = (metric_i != tmap.metrics_end()) ? *metric_i : NULL; if (m != m_end && (!metric || metric->frame() > (*m).time())) { - const Evoral::MIDIEvent ev(*m, false); + const Evoral::Event ev(*m, false); if (ev.time() < nframes) { LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]); lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer()); @@ -2474,8 +2623,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } else { tmetric.set_metric(metric); Timecode::BBT_Time bbt; - bbt = tmap.bbt_at_pulse (metric->pulse()); - double bpm = tmap.tempo_at_frame (start/*XXX*/).beats_per_minute(); + bbt = tmap.bbt_at_frame (metric->frame()); + double bpm = tmap.tempo_at_frame (start/*XXX*/).note_types_per_minute(); write_position(&_impl->forge, _ev_buffers[port_index], tmetric, bbt, speed, bpm, metric->frame(), @@ -2685,6 +2834,17 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } } #endif + // Intercept state dirty message + if (_has_state_interface /* && (flags & PORT_DIRTYMSG)*/) { + LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom)); + 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 == _uri_map.urids.state_StateChanged) { + _session.set_dirty (); + } + } + } // Intercept patch change messages to emit PropertyChanged signal if ((flags & PORT_PATCHMSG)) { @@ -2737,7 +2897,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, * so it needs to be realative to that. */ TempoMetric t = tmap.metric_at(start); - _current_bpm = tmap.tempo_at_frame (start).beats_per_minute(); + _current_bpm = tmap.tempo_at_frame (start).note_types_per_minute(); Timecode::BBT_Time bbt (tmap.bbt_at_frame (start)); double beatpos = (bbt.bars - 1) * t.meter().divisions_per_bar() + (bbt.beats - 1) @@ -2971,12 +3131,15 @@ LV2World::LV2World() ext_causesArtifacts= lilv_new_uri(world, LV2_PORT_PROPS__causesArtifacts); ext_notAutomatic = lilv_new_uri(world, LV2_PORT_PROPS__notAutomatic); ext_rangeSteps = lilv_new_uri(world, LV2_PORT_PROPS__rangeSteps); + groups_group = lilv_new_uri(world, LV2_PORT_GROUPS__group); + groups_element = lilv_new_uri(world, LV2_PORT_GROUPS__element); 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_isSideChain = lilv_new_uri(world, LV2_CORE_PREFIX "isSideChain"); + lv2_index = lilv_new_uri(world, LV2_CORE__index); 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); @@ -2984,6 +3147,7 @@ LV2World::LV2World() 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_designation = lilv_new_uri(world, LV2_CORE__designation); 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); @@ -3052,11 +3216,13 @@ LV2World::~LV2World() lilv_node_free(rdfs_label); lilv_node_free(rdfs_range); lilv_node_free(midi_MidiEvent); + lilv_node_free(lv2_designation); 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_index); lilv_node_free(lv2_integer); lilv_node_free(lv2_isSideChain); lilv_node_free(lv2_inPlaceBroken); @@ -3064,6 +3230,8 @@ LV2World::~LV2World() lilv_node_free(lv2_InputPort); lilv_node_free(lv2_ControlPort); lilv_node_free(lv2_AudioPort); + lilv_node_free(groups_group); + lilv_node_free(groups_element); lilv_node_free(ext_rangeSteps); lilv_node_free(ext_notAutomatic); lilv_node_free(ext_causesArtifacts);