Automation of LV2 plugin properties.
[ardour.git] / libs / ardour / plugin_insert.cc
index c25f8962ace7ca8c752efd34bc7bb860bd8c320d..3bc2d18b7b708785fc625c3c3e9dbf1de0c016ee 100644 (file)
@@ -240,7 +240,7 @@ PluginInsert::create_automatable_parameters ()
 
        set<Evoral::Parameter> a = _plugins.front()->automatable ();
 
-       Plugin::ParameterDescriptor desc;
+       ParameterDescriptor desc;
 
        for (set<Evoral::Parameter>::iterator i = a.begin(); i != a.end(); ++i) {
                if (i->type() == PluginAutomation) {
@@ -256,7 +256,17 @@ PluginInsert::create_automatable_parameters ()
                        param.set_range (desc.lower, desc.upper, _plugins.front()->default_value(i->id()), desc.toggled);
                        can_automate (param);
                        boost::shared_ptr<AutomationList> list(new AutomationList(param));
-                       add_control (boost::shared_ptr<AutomationControl> (new PluginControl(this, param, list)));
+                       add_control (boost::shared_ptr<AutomationControl> (new PluginControl(this, param, desc, list)));
+               } else if (i->type() == PluginPropertyAutomation) {
+                       Evoral::Parameter param(*i);
+                       const ParameterDescriptor& desc = _plugins.front()->get_property_descriptor(param.id());
+                       if (desc.datatype != Variant::VOID) {
+                               boost::shared_ptr<AutomationList> list;
+                               if (Variant::type_is_numeric(desc.datatype)) {
+                                       list = boost::shared_ptr<AutomationList>(new AutomationList(param));
+                               }
+                               add_control (boost::shared_ptr<AutomationControl> (new PluginPropertyControl(this, param, desc, list)));
+                       }
                }
        }
 }
@@ -352,6 +362,9 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                }
        }
 
+       bufs.set_count(ChanCount::max(bufs.count(), in_streams));
+       bufs.set_count(ChanCount::max(bufs.count(), out_streams));
+
        /* Note that we've already required that plugins
           be able to handle in-place processing.
        */
@@ -365,7 +378,7 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
                        boost::shared_ptr<AutomationControl> c
                                = boost::dynamic_pointer_cast<AutomationControl>(li->second);
 
-                       if (c->parameter().type() == PluginAutomation && c->automation_playback()) {
+                       if (c->list() && c->automation_playback()) {
                                bool valid;
 
                                const float val = c->list()->rt_safe_eval (now, valid);
@@ -452,51 +465,43 @@ PluginInsert::silence (framecnt_t nframes)
 }
 
 void
-PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, pframes_t nframes, bool)
+PluginInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t /*end_frame*/, pframes_t nframes, bool)
 {
        if (_pending_active) {
                /* run as normal if we are active or moving from inactive to active */
 
-               if (_session.transport_rolling()) {
-                       automation_run (bufs, nframes);
+               if (_session.transport_rolling() || _session.bounce_processing()) {
+                       automation_run (bufs, start_frame, nframes);
                } else {
                        connect_and_run (bufs, nframes, 0, false);
                }
 
        } else {
-               if (has_no_audio_inputs()) {
+               uint32_t in = input_streams ().n_audio ();
+               uint32_t out = output_streams().n_audio ();
+
+               if (has_no_audio_inputs() || in == 0) {
 
                        /* silence all (audio) outputs. Should really declick
                         * at the transitions of "active"
                         */
 
-                       uint32_t out = output_streams().n_audio ();
-
                        for (uint32_t n = 0; n < out; ++n) {
                                bufs.get_audio (n).silence (nframes);
                        }
 
-                       bufs.count().set_audio (out);
-
-               } else {
-
-                       /* does this need to be done with MIDI? it appears not */
+               } else if (out > in) {
 
-                       uint32_t in = input_streams ().n_audio ();
-                       uint32_t out = output_streams().n_audio ();
+                       /* not active, but something has make up for any channel count increase */
 
-                       if (out > in) {
-
-                               /* not active, but something has make up for any channel count increase */
-                               
-                               // TODO: option round-robin (n % in) or silence additional buffers ??
-                               for (uint32_t n = in; n < out; ++n) {
-                                       bufs.get_audio(n).read_from(bufs.get_audio(in - 1), nframes);
-                               }
+                       // TODO: option round-robin (n % in) or silence additional buffers ??
+                       // for now , simply replicate last buffer
+                       for (uint32_t n = in; n < out; ++n) {
+                               bufs.get_audio(n).read_from(bufs.get_audio(in - 1), nframes);
                        }
-
-                       bufs.count().set_audio (out);
                }
+
+               bufs.count().set_audio (out);
        }
 
        _active = _pending_active;
@@ -543,10 +548,10 @@ PluginInsert::get_parameter (Evoral::Parameter param)
 }
 
 void
-PluginInsert::automation_run (BufferSet& bufs, pframes_t nframes)
+PluginInsert::automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes)
 {
        Evoral::ControlEvent next_event (0, 0.0f);
-       framepos_t now = _session.transport_frame ();
+       framepos_t now = start;
        framepos_t end = now + nframes;
        framecnt_t offset = 0;
 
@@ -719,6 +724,10 @@ PluginInsert::can_support_io_configuration (const ChanCount& in, ChanCount& out)
 PluginInsert::Match
 PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanCount& out)
 {
+       if (_plugins.empty()) {
+               return Match();
+       }
+
        PluginInfoPtr info = _plugins.front()->get_info();
        ChanCount in; in += inx;
        midi_bypass.reset();
@@ -977,6 +986,22 @@ PluginInsert::set_state(const XMLNode& node, int version)
 
        boost::shared_ptr<Plugin> plugin = find_plugin (_session, prop->value(), type);
 
+       /* treat linux and windows VST plugins equivalent if they have the same uniqueID
+        * allow to move sessions windows <> linux */
+#ifdef LXVST_SUPPORT
+       if (plugin == 0 && type == ARDOUR::Windows_VST) {
+               type = ARDOUR::LXVST;
+               plugin = find_plugin (_session, prop->value(), type);
+       }
+#endif
+
+#ifdef WINDOWS_VST_SUPPORT
+       if (plugin == 0 && type == ARDOUR::LXVST) {
+               type = ARDOUR::Windows_VST;
+               plugin = find_plugin (_session, prop->value(), type);
+       }
+#endif
+
        if (plugin == 0) {
                error << string_compose(
                        _("Found a reference to a plugin (\"%1\") that is unknown.\n"
@@ -1124,7 +1149,7 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version)
                                        float min_y = c->alist()->get_min_y ();
                                        float max_y = c->alist()->get_max_y ();
 
-                                       Plugin::ParameterDescriptor desc;
+                                       ParameterDescriptor desc;
                                        _plugins.front()->get_parameter_descriptor (port_id, desc);
 
                                        if (min_y == FLT_MIN) {
@@ -1152,11 +1177,15 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version)
 string
 PluginInsert::describe_parameter (Evoral::Parameter param)
 {
-       if (param.type() != PluginAutomation) {
-               return Automatable::describe_parameter(param);
+       if (param.type() == PluginAutomation) {
+               return _plugins[0]->describe_parameter (param);
+       } else if (param.type() == PluginPropertyAutomation) {
+               boost::shared_ptr<AutomationControl> c(automation_control(param));
+               if (c && !c->desc().label.empty()) {
+                       return c->desc().label;
+               }
        }
-
-       return _plugins[0]->describe_parameter (param);
+       return Automatable::describe_parameter(param);
 }
 
 ARDOUR::framecnt_t
@@ -1175,19 +1204,20 @@ PluginInsert::type ()
        return plugin()->get_info()->type;
 }
 
-PluginInsert::PluginControl::PluginControl (PluginInsert* p, const Evoral::Parameter &param, boost::shared_ptr<AutomationList> list)
-       : AutomationControl (p->session(), param, list, p->describe_parameter(param))
+PluginInsert::PluginControl::PluginControl (PluginInsert*                     p,
+                                            const Evoral::Parameter&          param,
+                                            const ParameterDescriptor&        desc,
+                                            boost::shared_ptr<AutomationList> list)
+       : AutomationControl (p->session(), param, desc, list, p->describe_parameter(param))
        , _plugin (p)
 {
-       Plugin::ParameterDescriptor desc;
-       boost::shared_ptr<Plugin> plugin = p->plugin (0);
-       
-       alist()->reset_default (plugin->default_value (param.id()));
+       if (alist()) {
+               alist()->reset_default (desc.normal);
+       }
 
-       plugin->get_parameter_descriptor (param.id(), desc);
-       _logarithmic = desc.logarithmic;
-       _sr_dependent = desc.sr_dependent;
-       _toggled = desc.toggled;
+       if (desc.toggled) {
+               set_flags(Controllable::Toggle);
+       }
 }
 
 /** @param val `user' value */
@@ -1211,9 +1241,11 @@ PluginInsert::PluginControl::set_value (double user_val)
 double
 PluginInsert::PluginControl::internal_to_interface (double val) const
 {
-       if (_logarithmic) {
+       val = Controllable::internal_to_interface(val);
+       
+       if (_desc.logarithmic) {
                if (val > 0) {
-                       val = log (val);
+                       val = pow (val, 1/1.5);
                } else {
                        val = 0;
                }
@@ -1225,10 +1257,16 @@ PluginInsert::PluginControl::internal_to_interface (double val) const
 double
 PluginInsert::PluginControl::interface_to_internal (double val) const
 {
-       if (_logarithmic) {
-               val = exp (val);
+       if (_desc.logarithmic) {
+               if (val <= 0) {
+                       val = 0;
+               } else {
+                       val = pow (val, 1.5);
+               }
        }
 
+       val = Controllable::interface_to_internal(val);
+       
        return val;
 }
 
@@ -1252,6 +1290,62 @@ PluginInsert::PluginControl::get_value () const
        return _plugin->get_parameter (_list->parameter());
 }
 
+PluginInsert::PluginPropertyControl::PluginPropertyControl (PluginInsert*                     p,
+                                                            const Evoral::Parameter&          param,
+                                                            const ParameterDescriptor&        desc,
+                                                            boost::shared_ptr<AutomationList> list)
+       : AutomationControl (p->session(), param, desc, list)
+       , _plugin (p)
+{
+       if (alist()) {
+               alist()->set_yrange (desc.lower, desc.upper);
+               alist()->reset_default (desc.normal);
+       }
+
+       if (desc.toggled) {
+               set_flags(Controllable::Toggle);
+       }
+}
+
+void
+PluginInsert::PluginPropertyControl::set_value (double user_val)
+{
+       /* Old numeric set_value(), coerce to appropriate datatype if possible.
+          This is lossy, but better than nothing until Ardour's automation system
+          can handle various datatypes all the way down. */
+       const Variant value(_desc.datatype, user_val);
+       if (value.type() == Variant::VOID) {
+               error << "set_value(double) called for non-numeric property" << endmsg;
+               return;
+       }
+
+       for (Plugins::iterator i = _plugin->_plugins.begin(); i != _plugin->_plugins.end(); ++i) {
+               (*i)->set_property(_list->parameter().id(), value);
+       }
+
+       _value = value;
+       AutomationControl::set_value(user_val);
+}
+
+XMLNode&
+PluginInsert::PluginPropertyControl::get_state ()
+{
+       stringstream ss;
+
+       XMLNode& node (AutomationControl::get_state());
+       ss << parameter().id();
+       node.add_property (X_("property"), ss.str());
+       node.remove_property (X_("value"));
+
+       return node;
+}
+
+double
+PluginInsert::PluginPropertyControl::get_value () const
+{
+       return _value.to_double();
+}
+
 boost::shared_ptr<Plugin>
 PluginInsert::get_impulse_analysis_plugin()
 {