X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=4af6748c41ded1195ca0dfd6ad2c6c974faf701f;hb=58469214befaa714c856790b78da58c4593b2b54;hp=9cac6492b4ed0ea2e732e3529c3955c5b62099fc;hpb=7b6ef41f0caca083441748a4ef5d836df2be243a;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 9cac6492b4..4af6748c41 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -155,13 +155,25 @@ 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 +#ifdef HAVE_LV2_1_10_0 + LilvNode* atom_int; + LilvNode* atom_float; + LilvNode* atom_object; // new in 1.8 + LilvNode* atom_vector; +#endif +#ifdef LV2_EXTENDED + LilvNode* lv2_noSampleAccurateCtrl; + LilvNode* auto_can_write_automatation; // lv2:optionalFeature + LilvNode* auto_automation_control; // atom:supports + LilvNode* auto_automation_controlled; // lv2:portProperty +#endif + private: bool _bundle_checked; }; @@ -335,6 +347,7 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) _state_version = 0; _was_activated = false; _has_state_interface = false; + _can_write_automation = false; _impl->block_length = _session.get_block_size(); _instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access"; @@ -484,11 +497,16 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) throw failed_constructor(); } lilv_nodes_free(required_features); +#endif +#ifdef LV2_EXTENDED LilvNodes* optional_features = lilv_plugin_get_optional_features (plugin); if (lilv_nodes_contains (optional_features, _world.lv2_noSampleAccurateCtrl)) { _no_sample_accurate_ctrl = true; } + if (lilv_nodes_contains (optional_features, _world.auto_can_write_automatation)) { + _can_write_automation = true; + } lilv_nodes_free(optional_features); #endif @@ -542,6 +560,11 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) if (lilv_nodes_contains(atom_supports, _world.time_Position)) { flags |= PORT_POSITION; } +#ifdef LV2_EXTENDED + if (lilv_nodes_contains(atom_supports, _world.auto_automation_control)) { + flags |= PORT_AUTOCTRL; + } +#endif if (lilv_nodes_contains(atom_supports, _world.patch_Message)) { flags |= PORT_PATCHMSG; if (flags & PORT_INPUT) { @@ -566,6 +589,14 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) throw failed_constructor(); } +#ifdef LV2_EXTENDED + if (lilv_port_has_property(_impl->plugin, port, _world.auto_automation_controlled)) { + if ((flags & PORT_INPUT) && (flags & PORT_CONTROL)) { + flags |= PORT_CTRLED; + } + } +#endif + _port_flags.push_back(flags); _port_minimumSize.push_back(minimumSize); } @@ -710,6 +741,13 @@ LV2Plugin::requires_fixed_sized_buffers () const * e.g The process cycle may be split when looping, also * the period-size may change any time: see set_block_size() */ + if (get_info()->n_inputs.n_midi() > 0) { + /* we don't yet implement midi buffer offsets (for split cycles). + * Also connect_and_run() also uses _session.transport_frame() directly + * (for BBT) which is not offset for plugin cycle split. + */ + return true; + } return _no_sample_accurate_ctrl; } @@ -968,7 +1006,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). */ @@ -1036,6 +1078,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); @@ -1054,7 +1100,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(), @@ -1063,8 +1111,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); @@ -1101,11 +1155,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( @@ -1673,6 +1729,12 @@ LV2Plugin::set_insert_id(PBD::ID id) } } +void +LV2Plugin::set_state_dir (const std::string& d) +{ + _plugin_state_dir = d; +} + int LV2Plugin::set_state(const XMLNode& node, int version) { @@ -1728,6 +1790,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) { @@ -1736,8 +1802,6 @@ LV2Plugin::set_state(const XMLNode& node, int version) prop->value()) << endmsg; } - // TODO: special case track-templates - // (state must be saved with the template) std::string state_file = Glib::build_filename( plugin_dir(), Glib::build_filename(prop->value(), "state.ttl")); @@ -1750,6 +1814,13 @@ LV2Plugin::set_state(const XMLNode& node, int version) _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(); #endif @@ -1863,6 +1934,23 @@ LV2Plugin::automatable() const return ret; } +void +LV2Plugin::set_automation_control (uint32_t i, boost::shared_ptr c) +{ + if ((_port_flags[i] & PORT_CTRLED)) { + _ctrl_map [i] = AutomationCtrlPtr (new AutomationCtrl(c)); + } +} + +LV2Plugin::AutomationCtrlPtr +LV2Plugin::get_automation_control (uint32_t i) +{ + if (_ctrl_map.find (i) == _ctrl_map.end()) { + return AutomationCtrlPtr (); + } + return _ctrl_map[i]; +} + void LV2Plugin::activate() { @@ -1890,7 +1978,6 @@ LV2Plugin::cleanup() { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 cleanup\n", name())); - activate(); deactivate(); lilv_instance_free(_impl->instance); _impl->instance = NULL; @@ -2035,6 +2122,15 @@ LV2Plugin::connect_and_run(BufferSet& bufs, *_bpm_control_port = tmetric.tempo().beats_per_minute(); } +#ifdef LV2_EXTENDED + if (_can_write_automation && _session.transport_frame() != _next_cycle_start) { + // add guard-points after locating + for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) { + i->second->guard = true; + } + } +#endif + ChanCount bufs_count; bufs_count.set(DataType::AUDIO, 1); bufs_count.set(DataType::MIDI, 1); @@ -2123,8 +2219,10 @@ LV2Plugin::connect_and_run(BufferSet& bufs, ? *metric_i : NULL; if (m != m_end && (!metric || metric->frame() > (*m).time())) { const Evoral::MIDIEvent ev(*m, false); - LV2_Evbuf_Iterator eend = lv2_evbuf_end(_ev_buffers[port_index]); - lv2_evbuf_write(&eend, ev.time(), 0, type, ev.size(), ev.buffer()); + 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()); + } ++m; } else { tmetric.set_metric(metric); @@ -2217,9 +2315,8 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } } - // Write messages to UI - if ((_to_ui || _patch_port_out_index != (uint32_t)-1) && + if ((_to_ui || _can_write_automation || _patch_port_out_index != (uint32_t)-1) && (flags & PORT_OUTPUT) && (flags & (PORT_EVENT|PORT_SEQUENCE))) { LV2_Evbuf* buf = _ev_buffers[port_index]; for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(buf); @@ -2229,6 +2326,78 @@ LV2Plugin::connect_and_run(BufferSet& bufs, uint8_t* data; lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data); +#ifdef LV2_EXTENDED + // Intercept Automation Write Events + if ((flags & PORT_AUTOCTRL)) { + 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.auto_event) { + // only if transport_rolling ?? + const LV2_Atom* parameter = NULL; + const LV2_Atom* value = NULL; + lv2_atom_object_get(obj, + _uri_map.urids.auto_parameter, ¶meter, + _uri_map.urids.auto_value, &value, + 0); + if (parameter && value) { + const uint32_t p = ((const LV2_Atom_Int*)parameter)->body; + const float v = ((const LV2_Atom_Float*)value)->body; + // -> add automation event.. + AutomationCtrlPtr c = get_automation_control (p); + if (c && c->ac->automation_state() == Touch) { + if (c->guard) { + c->guard = false; + c->ac->list()->add (_session.transport_frame() + frames, v, true, true); + } else { + c->ac->set_double (v, _session.transport_frame() + frames, true); + } + } + } + } + else if (obj->body.otype == _uri_map.urids.auto_setup) { + // TODO optional arguments, for now we assume the plugin + // writes automation for its own inputs + // -> put them in "touch" mode (preferably "exclusive plugin touch(TM)" + for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) { + i->second->ac->set_automation_state (Touch); + } + } + else if (obj->body.otype == _uri_map.urids.auto_finalize) { + // set [touched] parameters to "play" ?? + } + else if (obj->body.otype == _uri_map.urids.auto_start) { + const LV2_Atom* parameter = NULL; + lv2_atom_object_get(obj, + _uri_map.urids.auto_parameter, ¶meter, + 0); + if (parameter) { + const uint32_t p = ((const LV2_Atom_Int*)parameter)->body; + AutomationCtrlPtr c = get_automation_control (p); + if (c) { + c->ac->start_touch (_session.transport_frame()); + c->guard = true; + } + } + } + else if (obj->body.otype == _uri_map.urids.auto_end) { + const LV2_Atom* parameter = NULL; + lv2_atom_object_get(obj, + _uri_map.urids.auto_parameter, ¶meter, + 0); + if (parameter) { + const uint32_t p = ((const LV2_Atom_Int*)parameter)->body; + AutomationCtrlPtr c = get_automation_control (p); + if (c) { + c->ac->stop_touch (true, _session.transport_frame()); + } + } + } + } + } +#endif + // Intercept patch change messages to emit PropertyChanged signal if ((flags & PORT_PATCHMSG)) { LV2_Atom* atom = (LV2_Atom*)(data - sizeof(LV2_Atom)); @@ -2484,7 +2653,12 @@ 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 LV2_EXTENDED + lv2_noSampleAccurateCtrl = lilv_new_uri(world, LV2_CORE_PREFIX "noSampleAccurateControls"); + auto_can_write_automatation = lilv_new_uri(world, LV2_AUTOMATE_URI__can_write); + auto_automation_control = lilv_new_uri(world, LV2_AUTOMATE_URI__control); + auto_automation_controlled = lilv_new_uri(world, LV2_AUTOMATE_URI__controlled); +#endif #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); @@ -2495,12 +2669,20 @@ LV2World::LV2World() 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 +#ifdef LV2_EXTENDED lilv_node_free(lv2_noSampleAccurateCtrl); + lilv_node_free(auto_can_write_automatation); + lilv_node_free(auto_automation_control); + lilv_node_free(auto_automation_controlled); +#endif lilv_node_free(patch_Message); lilv_node_free(patch_writable); lilv_node_free(units_hz); @@ -2538,6 +2720,7 @@ LV2World::~LV2World() lilv_node_free(atom_Chunk); lilv_node_free(atom_AtomPort); lilv_world_free(world); + world = NULL; } void @@ -2599,6 +2782,48 @@ 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 {