a-EQ: Remove LPF/HPF completely from this plugin
[ardour.git] / libs / ardour / lv2_plugin.cc
index cceb6020e9c330d1cd384d7e4db74b7c5b48a4d2..c850cb6fe7de6d64129437eab8c824c280ecf0f7 100644 (file)
@@ -17,6 +17,7 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <cctype>
 #include <string>
 #include <vector>
 #include <limits>
@@ -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
 {
@@ -1127,7 +1203,7 @@ LV2Plugin::add_state(XMLNode* root) const
 
        XMLNode*    child;
        char        buf[32];
-       LocaleGuard lg ();
+       LocaleGuard lg;
 
        for (uint32_t i = 0; i < parameter_count(); ++i) {
                if (parameter_is_input(i) && parameter_is_control(i)) {
@@ -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);
                }
        }
@@ -1806,7 +1883,7 @@ LV2Plugin::set_state(const XMLNode& node, int version)
        const char*          sym;
        const char*          value;
        uint32_t             port_id;
-       LocaleGuard          lg ();
+       LocaleGuard          lg;
 
        if (node.name() != state_node_name()) {
                error << _("Bad node sent to LV2Plugin::set_state") << endmsg;
@@ -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<Evoral::Parameter> 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<AutomationControl> 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, &parameter,
+                                                                           _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);