X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Flv2_plugin.cc;h=c850cb6fe7de6d64129437eab8c824c280ecf0f7;hb=fdfff818509cb8e7b7753cf40336cb3d112b4e7c;hp=580c1b60db69d588a5a7f61271ed0b208aa62b28;hpb=2066afe28c9708b8739918f82d26a732684ea4a3;p=ardour.git diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index 580c1b60db..c850cb6fe7 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -17,6 +17,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include #include #include #include @@ -35,6 +36,7 @@ #include "pbd/stl_delete.h" #include "pbd/compose.h" #include "pbd/error.h" +#include "pbd/replace_all.h" #include "pbd/xml++.h" #include "libardour-config.h" @@ -125,6 +127,10 @@ public: LilvNode* ev_EventPort; LilvNode* ext_logarithmic; LilvNode* ext_notOnGUI; + LilvNode* ext_expensive; + LilvNode* ext_causesArtifacts; + LilvNode* ext_notAutomatic; + LilvNode* ext_rangeSteps; LilvNode* lv2_AudioPort; LilvNode* lv2_ControlPort; LilvNode* lv2_InputPort; @@ -173,6 +179,7 @@ public: LilvNode* auto_can_write_automatation; // lv2:optionalFeature LilvNode* auto_automation_control; // atom:supports LilvNode* auto_automation_controlled; // lv2:portProperty + LilvNode* auto_automation_controller; // lv2:portProperty #endif private: @@ -237,6 +244,14 @@ log_vprintf(LV2_Log_Handle /*handle*/, { char* str = NULL; const int ret = g_vasprintf(&str, fmt, args); + /* strip trailing whitespace */ + while (strlen (str) > 0 && isspace (str[strlen (str) - 1])) { + str[strlen (str) - 1] = '\0'; + } + if (strlen (str) == 0) { + return 0; + } + if (type == URIMap::instance().urids.log_Error) { error << str << endmsg; } else if (type == URIMap::instance().urids.log_Warning) { @@ -624,12 +639,28 @@ LV2Plugin::init(const void* c_plugin, framecnt_t rate) throw failed_constructor(); } + if ((flags & PORT_INPUT) && (flags & PORT_CONTROL)) { + if (lilv_port_has_property(_impl->plugin, port, _world.ext_causesArtifacts)) { + flags |= PORT_NOAUTO; + } + if (lilv_port_has_property(_impl->plugin, port, _world.ext_notAutomatic)) { + flags |= PORT_NOAUTO; + } + if (lilv_port_has_property(_impl->plugin, port, _world.ext_expensive)) { + flags |= PORT_NOAUTO; + } + } #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; } } + if (lilv_port_has_property(_impl->plugin, port, _world.auto_automation_controller)) { + if ((flags & PORT_INPUT) && (flags & PORT_CONTROL)) { + flags |= PORT_CTRLER; + } + } #endif _port_flags.push_back(flags); @@ -1023,6 +1054,51 @@ LV2Plugin::get_parameter_docs(uint32_t which) const return ""; } +bool +LV2Plugin::get_layout (uint32_t which, UILayoutHint& h) const +{ + /// TODO lookup port-properties + if (unique_id () != "urn:ardour:a-eq") { + return false; + } + h.knob = true; + switch (which) { + case 0: h.x0 = 0; h.x1 = 1; h.y0 = 2; h.y1 = 3; break; // Frequency L + case 1: h.x0 = 0; h.x1 = 1; h.y0 = 0; h.y1 = 1; break; // Gain L + case 17: h.x0 = 0; h.x1 = 1; h.y0 = 5; h.y1 = 6; break; // enable L + + case 2: h.x0 = 1; h.x1 = 3; h.y0 = 2; h.y1 = 3; break; // Frequency 1 + case 3: h.x0 = 1; h.x1 = 3; h.y0 = 0; h.y1 = 1; break; // Gain 1 + case 4: h.x0 = 2; h.x1 = 4; h.y0 = 1; h.y1 = 2; break; // Bandwidth 1 + case 18: h.x0 = 1; h.x1 = 4; h.y0 = 5; h.y1 = 6; break; // enable 1 + + case 5: h.x0 = 4; h.x1 = 6; h.y0 = 2; h.y1 = 3; break; // Frequency 2 + case 6: h.x0 = 4; h.x1 = 6; h.y0 = 0; h.y1 = 1; break; // Gain 2 + case 7: h.x0 = 5; h.x1 = 7; h.y0 = 1; h.y1 = 2; break; // Bandwidth 2 + case 19: h.x0 = 4; h.x1 = 7; h.y0 = 5; h.y1 = 6; break; // enable 2 + + case 8: h.x0 = 7; h.x1 = 9; h.y0 = 2; h.y1 = 3; break; // Frequency 3 + case 9: h.x0 = 7; h.x1 = 9; h.y0 = 0; h.y1 = 1; break; // Gain 3 + case 10: h.x0 = 8; h.x1 = 10; h.y0 = 1; h.y1 = 2; break; // Bandwidth 3 + case 20: h.x0 = 7; h.x1 = 10; h.y0 = 5; h.y1 = 6; break; // enable 3 + + case 11: h.x0 = 10; h.x1 = 12; h.y0 = 2; h.y1 = 3; break; // Frequency 4 + case 12: h.x0 = 10; h.x1 = 12; h.y0 = 0; h.y1 = 1; break; // Gain 4 + case 13: h.x0 = 11; h.x1 = 13; h.y0 = 1; h.y1 = 2; break; // Bandwidth 4 + case 21: h.x0 = 10; h.x1 = 13; h.y0 = 5; h.y1 = 6; break; // enable 4 + + case 14: h.x0 = 13; h.x1 = 14; h.y0 = 2; h.y1 = 3; break; // Frequency H + case 15: h.x0 = 13; h.x1 = 14; h.y0 = 0; h.y1 = 1; break; // Gain H + case 22: h.x0 = 13; h.x1 = 14; h.y0 = 5; h.y1 = 6; break; // enable H + + + case 16: h.x0 = 14; h.x1 = 15; h.y0 = 4; h.y1 = 6; break; // Master Gain + default: + return false; + } + return true; +} + uint32_t LV2Plugin::nth_parameter(uint32_t n, bool& ok) const { @@ -1615,6 +1691,7 @@ load_parameter_descriptor_units(LilvWorld* lworld, ParameterDescriptor& desc, co LilvNode* render = get_value(lworld, unit, _world.units_render); if (render) { desc.print_fmt = lilv_node_as_string(render); + replace_all (desc.print_fmt, "%f", "%.2f"); lilv_node_free(render); } } @@ -1903,6 +1980,8 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c lilv_port_get_range(_impl->plugin, port, &def, &min, &max); portunits = lilv_port_get_value(_impl->plugin, port, _world.units_unit); + LilvNode* steps = lilv_port_get(_impl->plugin, port, _world.ext_rangeSteps); + // TODO: Once we can rely on lilv 0.18.0 being present, // load_parameter_descriptor() can be used for ports as well desc.integer_step = lilv_port_has_property(_impl->plugin, port, _world.lv2_integer); @@ -1928,9 +2007,34 @@ LV2Plugin::get_parameter_descriptor(uint32_t which, ParameterDescriptor& desc) c desc.update_steps(); + if (steps) { + //override auto-calculated steps in update_steps() + float s = lilv_node_as_float (steps); + const float delta = desc.upper - desc.lower; + + desc.step = desc.smallstep = (delta / s); + desc.largestep = std::min ((delta / 5.0f), 10.f * desc.smallstep); + + if (desc.logarithmic) { + // TODO marry AutomationControl::internal_to_interface () with + // http://lv2plug.in/ns/ext/port-props/#rangeSteps + desc.smallstep = desc.smallstep / logf(s); + desc.step = desc.step / logf(s); + desc.largestep = desc.largestep / logf(s); + } else if (desc.integer_step) { + desc.smallstep = 1.0; + desc.step = std::max(1.f, rintf (desc.step)); + desc.largestep = std::max(1.f, rintf (desc.largestep)); + } + DEBUG_TRACE(DEBUG::LV2, string_compose("parameter %1 small: %2, step: %3 largestep: %4\n", + which, desc.smallstep, desc.step, desc.largestep)); + } + + lilv_node_free(def); lilv_node_free(min); lilv_node_free(max); + lilv_node_free(steps); lilv_nodes_free(portunits); return 0; @@ -2038,7 +2142,7 @@ LV2Plugin::automatable() const set ret; for (uint32_t i = 0; i < parameter_count(); ++i) { - if (parameter_is_input(i) && parameter_is_control(i)) { + if (parameter_is_input(i) && parameter_is_control(i) && !(_port_flags[i] & PORT_NOAUTO)) { ret.insert(ret.end(), Evoral::Parameter(PluginAutomation, 0, i)); } } @@ -2054,7 +2158,8 @@ LV2Plugin::automatable() const void LV2Plugin::set_automation_control (uint32_t i, boost::shared_ptr c) { - if ((_port_flags[i] & PORT_CTRLED)) { + if ((_port_flags[i] & (PORT_CTRLED | PORT_CTRLER))) { + DEBUG_TRACE(DEBUG::LV2Automate, string_compose ("Ctrl Port %1\n", i)); _ctrl_map [i] = AutomationCtrlPtr (new AutomationCtrl(c)); } } @@ -2218,17 +2323,18 @@ write_position(LV2_Atom_Forge* forge, int LV2Plugin::connect_and_run(BufferSet& bufs, - ChanMapping in_map, ChanMapping out_map, - pframes_t nframes, framecnt_t offset) + framepos_t start, framepos_t end, double speed, + ChanMapping in_map, ChanMapping out_map, + pframes_t nframes, framecnt_t offset) { DEBUG_TRACE(DEBUG::LV2, string_compose("%1 run %2 offset %3\n", name(), nframes, offset)); - Plugin::connect_and_run(bufs, in_map, out_map, nframes, offset); + Plugin::connect_and_run(bufs, start, end, speed, in_map, out_map, nframes, offset); cycles_t then = get_cycles(); TempoMap& tmap = _session.tempo_map(); Metrics::const_iterator metric_i = tmap.metrics_end(); - TempoMetric tmetric = tmap.metric_at(_session.transport_frame(), &metric_i); + TempoMetric tmetric = tmap.metric_at(start, &metric_i); if (_freewheel_control_port) { *_freewheel_control_port = _session.engine().freewheeling() ? 1.f : 0.f; @@ -2239,7 +2345,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, } #ifdef LV2_EXTENDED - if (_can_write_automation && _session.transport_frame() != _next_cycle_start) { + if (_can_write_automation && start != _next_cycle_start) { // add guard-points after locating for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) { i->second->guard = true; @@ -2307,13 +2413,12 @@ LV2Plugin::connect_and_run(BufferSet& bufs, if (valid && (flags & PORT_INPUT)) { Timecode::BBT_Time bbt; if ((flags & PORT_POSITION)) { - if (_session.transport_frame() != _next_cycle_start || - _session.transport_speed() != _next_cycle_speed) { + if (start != _next_cycle_start || + speed != _next_cycle_speed) { // Transport has changed, write position at cycle start - tmap.bbt_time(_session.transport_frame(), bbt); + bbt = tmap.bbt_at_frame (start); write_position(&_impl->forge, _ev_buffers[port_index], - tmetric, bbt, _session.transport_speed(), - _session.transport_frame(), 0); + tmetric, bbt, speed, start, 0); } } @@ -2327,7 +2432,7 @@ LV2Plugin::connect_and_run(BufferSet& bufs, // Now merge MIDI and any transport events into the buffer const uint32_t type = _uri_map.urids.midi_MidiEvent; - const framepos_t tend = _session.transport_frame() + nframes; + const framepos_t tend = end; ++metric_i; while (m != m_end || (metric_i != tmap.metrics_end() && (*metric_i)->frame() < tend)) { @@ -2342,11 +2447,11 @@ LV2Plugin::connect_and_run(BufferSet& bufs, ++m; } else { tmetric.set_metric(metric); - bbt = metric->start(); + bbt = tmap.bbt_at_pulse (metric->pulse()); write_position(&_impl->forge, _ev_buffers[port_index], - tmetric, bbt, _session.transport_speed(), + tmetric, bbt, speed, metric->frame(), - metric->frame() - _session.transport_frame()); + metric->frame() - start); ++metric_i; } } @@ -2461,13 +2566,19 @@ LV2Plugin::connect_and_run(BufferSet& bufs, const uint32_t p = ((const LV2_Atom_Int*)parameter)->body; const float v = ((const LV2_Atom_Float*)value)->body; // -> add automation event.. + DEBUG_TRACE(DEBUG::LV2Automate, + string_compose ("Event p: %1 t: %2 v: %3\n", p, frames, v)); AutomationCtrlPtr c = get_automation_control (p); - if (c && c->ac->automation_state() == Touch) { + if (c && + (c->ac->automation_state() == Touch || c->ac->automation_state() == Write) + ) { + framepos_t when = std::max ((framepos_t) 0, start + frames - _current_latency); + assert (start + frames - _current_latency >= 0); if (c->guard) { c->guard = false; - c->ac->list()->add (_session.transport_frame() + frames, v, true, true); + c->ac->list()->add (when, v, true, true); } else { - c->ac->set_double (v, _session.transport_frame() + frames, true); + c->ac->set_double (v, when, true); } } } @@ -2477,11 +2588,42 @@ LV2Plugin::connect_and_run(BufferSet& bufs, // 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); + if (_port_flags[i->first] & PORT_CTRLED) { + DEBUG_TRACE(DEBUG::LV2Automate, + string_compose ("Setup p: %1\n", i->first)); + i->second->ac->set_automation_state (Touch); + } } } else if (obj->body.otype == _uri_map.urids.auto_finalize) { // set [touched] parameters to "play" ?? + // allow plugin to change its mode (from analyze to apply) + 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; + AutomationCtrlPtr c = get_automation_control (p); + DEBUG_TRACE(DEBUG::LV2Automate, + string_compose ("Finalize p: %1 v: %2\n", p, v)); + if (c && _port_flags[p] & PORT_CTRLER) { + c->ac->set_value(v, Controllable::NoGroup); + } + } else { + DEBUG_TRACE(DEBUG::LV2Automate, "Finalize\n"); + } + for (AutomationCtrlMap::iterator i = _ctrl_map.begin(); i != _ctrl_map.end(); ++i) { + // guard will be false if an event was written + if ((_port_flags[i->first] & PORT_CTRLED) && !i->second->guard) { + DEBUG_TRACE(DEBUG::LV2Automate, + string_compose ("Thin p: %1\n", i->first)); + i->second->ac->alist ()->thin (20); + } + } } else if (obj->body.otype == _uri_map.urids.auto_start) { const LV2_Atom* parameter = NULL; @@ -2491,8 +2633,9 @@ LV2Plugin::connect_and_run(BufferSet& bufs, if (parameter) { const uint32_t p = ((const LV2_Atom_Int*)parameter)->body; AutomationCtrlPtr c = get_automation_control (p); + DEBUG_TRACE(DEBUG::LV2Automate, string_compose ("Start Touch p: %1\n", p)); if (c) { - c->ac->start_touch (_session.transport_frame()); + c->ac->start_touch (std::max ((framepos_t)0, start - _current_latency)); c->guard = true; } } @@ -2505,8 +2648,9 @@ LV2Plugin::connect_and_run(BufferSet& bufs, if (parameter) { const uint32_t p = ((const LV2_Atom_Int*)parameter)->body; AutomationCtrlPtr c = get_automation_control (p); + DEBUG_TRACE(DEBUG::LV2Automate, string_compose ("End Touch p: %1\n", p)); if (c) { - c->ac->stop_touch (true, _session.transport_frame()); + c->ac->stop_touch (true, std::max ((framepos_t)0, start - _current_latency)); } } } @@ -2556,14 +2700,11 @@ LV2Plugin::connect_and_run(BufferSet& bufs, set_cycles((uint32_t)(now - then)); // Update expected transport information for next cycle so we can detect changes - _next_cycle_speed = _session.transport_speed(); - _next_cycle_start = _session.transport_frame() + (nframes * _next_cycle_speed); + _next_cycle_speed = speed; + _next_cycle_start = end; if (_latency_control_port) { framecnt_t new_latency = signal_latency (); - if (_current_latency != new_latency) { - LatencyChanged (_current_latency, new_latency); /* EMIT SIGNAL */ - } _current_latency = new_latency; } return 0; @@ -2604,6 +2745,22 @@ LV2Plugin::parameter_is_input(uint32_t param) const return _port_flags[param] & PORT_INPUT; } +uint32_t +LV2Plugin::designated_bypass_port () +{ +#ifdef LV2_EXTENDED + const LilvPort* port = NULL; + LilvNode* designation = lilv_new_uri (_world.world, LV2_PROCESSING_URI__enable); + port = lilv_plugin_get_port_by_designation ( + _impl->plugin, _world.lv2_InputPort, designation); + lilv_node_free(designation); + if (port) { + return lilv_port_get_index (_impl->plugin, port); + } +#endif + return UINT32_MAX; +} + void LV2Plugin::print_parameter(uint32_t param, char* buf, uint32_t len) const { @@ -2745,6 +2902,10 @@ LV2World::LV2World() 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); + ext_expensive = lilv_new_uri(world, LV2_PORT_PROPS__expensive); + 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); 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); @@ -2781,6 +2942,7 @@ LV2World::LV2World() 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); + auto_automation_controller = lilv_new_uri(world, LV2_AUTOMATE_URI__controller); #endif #ifdef HAVE_LV2_1_2_0 bufz_powerOf2BlockLength = lilv_new_uri(world, LV2_BUF_SIZE__powerOf2BlockLength); @@ -2805,6 +2967,7 @@ LV2World::~LV2World() lilv_node_free(auto_can_write_automatation); lilv_node_free(auto_automation_control); lilv_node_free(auto_automation_controlled); + lilv_node_free(auto_automation_controller); #endif lilv_node_free(patch_Message); lilv_node_free(patch_writable); @@ -2834,6 +2997,10 @@ LV2World::~LV2World() lilv_node_free(lv2_InputPort); lilv_node_free(lv2_ControlPort); lilv_node_free(lv2_AudioPort); + lilv_node_free(ext_rangeSteps); + lilv_node_free(ext_notAutomatic); + lilv_node_free(ext_causesArtifacts); + lilv_node_free(ext_expensive); lilv_node_free(ext_notOnGUI); lilv_node_free(ext_logarithmic); lilv_node_free(ev_EventPort);