properly handle integer steps in plugin controls
[ardour.git] / libs / ardour / plugin_insert.cc
index 6b6e466e1e17262326b9c6ea2dafdacaf01b0867..8da0abb00fac440f58630e57eb383c7d96342449 100644 (file)
@@ -240,23 +240,27 @@ PluginInsert::create_automatable_parameters ()
 
        set<Evoral::Parameter> a = _plugins.front()->automatable ();
 
-       Plugin::ParameterDescriptor desc;
-
        for (set<Evoral::Parameter>::iterator i = a.begin(); i != a.end(); ++i) {
                if (i->type() == PluginAutomation) {
 
                        Evoral::Parameter param(*i);
 
+                       ParameterDescriptor desc;
                        _plugins.front()->get_parameter_descriptor(i->id(), desc);
 
-                       /* the Parameter belonging to the actual plugin doesn't have its range set
-                          but we want the Controllable related to this Parameter to have those limits.
-                       */
-
-                       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)));
+                       boost::shared_ptr<AutomationList> list(new AutomationList(param, desc));
+                       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::NOTHING) {
+                               boost::shared_ptr<AutomationList> list;
+                               if (Variant::type_is_numeric(desc.datatype)) {
+                                       list = boost::shared_ptr<AutomationList>(new AutomationList(param, desc));
+                               }
+                               add_control (boost::shared_ptr<AutomationControl> (new PluginPropertyControl(this, param, desc, list)));
+                       }
                }
        }
 }
@@ -352,6 +356,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 +372,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,13 +459,13 @@ 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);
                }
@@ -535,10 +542,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;
 
@@ -588,12 +595,75 @@ PluginInsert::default_parameter_value (const Evoral::Parameter& param)
        if (_plugins.empty()) {
                fatal << _("programming error: ") << X_("PluginInsert::default_parameter_value() called with no plugin")
                      << endmsg;
-               /*NOTREACHED*/
+               abort(); /*NOTREACHED*/
        }
 
        return _plugins[0]->default_value (param.id());
 }
 
+
+bool
+PluginInsert::can_reset_all_parameters ()
+{
+       bool all = true;
+       uint32_t params = 0;
+       for (uint32_t par = 0; par < _plugins[0]->parameter_count(); ++par) {
+               bool ok=false;
+               const uint32_t cid = _plugins[0]->nth_parameter (par, ok);
+
+               if (!ok || !_plugins[0]->parameter_is_input(cid)) {
+                       continue;
+               }
+
+               boost::shared_ptr<AutomationControl> ac = automation_control (Evoral::Parameter(PluginAutomation, 0, cid));
+               if (!ac) {
+                       continue;
+               }
+
+               ++params;
+               if (ac->automation_state() & Play) {
+                       all = false;
+                       break;
+               }
+       }
+       return all && (params > 0);
+}
+
+bool
+PluginInsert::reset_parameters_to_default ()
+{
+       bool all = true;
+
+       for (uint32_t par = 0; par < _plugins[0]->parameter_count(); ++par) {
+               bool ok=false;
+               const uint32_t cid = _plugins[0]->nth_parameter (par, ok);
+
+               if (!ok || !_plugins[0]->parameter_is_input(cid)) {
+                       continue;
+               }
+
+               const float dflt = _plugins[0]->default_value (cid);
+               const float curr = _plugins[0]->get_parameter (cid);
+
+               if (dflt == curr) {
+                       continue;
+               }
+
+               boost::shared_ptr<AutomationControl> ac = automation_control (Evoral::Parameter(PluginAutomation, 0, cid));
+               if (!ac) {
+                       continue;
+               }
+
+               if (ac->automation_state() & Play) {
+                       all = false;
+                       continue;
+               }
+
+               ac->set_value (dflt);
+       }
+       return all;
+}
+
 boost::shared_ptr<Plugin>
 PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
 {
@@ -634,7 +704,7 @@ PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
        fatal << string_compose (_("programming error: %1"),
                          X_("unknown plugin type in PluginInsert::plugin_factory"))
              << endmsg;
-       /*NOTREACHED*/
+       abort(); /*NOTREACHED*/
        return boost::shared_ptr<Plugin> ((Plugin*) 0);
 }
 
@@ -973,7 +1043,7 @@ 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 uniqeID
+       /* 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) {
@@ -1039,8 +1109,6 @@ PluginInsert::set_state(const XMLNode& node, int version)
 
                if ((*niter)->name() == plugin->state_node_name()) {
 
-                       plugin->set_state (**niter, version);
-
                        for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
                                (*i)->set_state (**niter, version);
                        }
@@ -1121,7 +1189,7 @@ PluginInsert::set_parameter_state_2X (const XMLNode& node, int version)
                        boost::shared_ptr<AutomationControl> c = boost::dynamic_pointer_cast<AutomationControl>(
                                        control(Evoral::Parameter(PluginAutomation, 0, port_id), true));
 
-                       if (c) {
+                       if (c && c->alist()) {
                                if (!child->children().empty()) {
                                        c->alist()->set_state (*child->children().front(), version);
 
@@ -1136,7 +1204,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) {
@@ -1164,11 +1232,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
@@ -1187,19 +1259,23 @@ 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);
+               if (desc.toggled) {
+                       list->set_interpolation(Evoral::ControlList::Discrete);
+               }
+       }
 
-       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 */
@@ -1220,66 +1296,80 @@ PluginInsert::PluginControl::set_value (double user_val)
        AutomationControl::set_value (user_val);
 }
 
+XMLNode&
+PluginInsert::PluginControl::get_state ()
+{
+       stringstream ss;
+
+       XMLNode& node (AutomationControl::get_state());
+       ss << parameter().id();
+       node.add_property (X_("parameter"), ss.str());
+
+       return node;
+}
+
+/** @return `user' val */
 double
-PluginInsert::PluginControl::internal_to_interface (double val) const
+PluginInsert::PluginControl::get_value () const
 {
-       if (_logarithmic) {
-               /* some plugins have a log-scale range "0.."
-                * ideally we'd map the range down to infinity somehow :)
-                *
-                * one solution could be to use
-                *   val = exp(lower + log(range) * value);
-                *   (log(val) - lower) / range)
-                * This approach would require access to the actual range (ie
-                * Plugin::ParameterDescriptor) and also require handling
-                * of unbound ranges..
-                *
-                * currently an arbitrarly low number is assumed to represnt
-                * log(0) as hot-fix solution.
-                */
-               if (val > 0) {
-                       val = log (val);
-               } else {
-                       val = -8; // ~ -70dB = 20 * log10(exp(-8))
-               }
+       /* FIXME: probably should be taking out some lock here.. */
+       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);
        }
 
-       return val;
+       if (desc.toggled) {
+               set_flags(Controllable::Toggle);
+       }
 }
 
-double
-PluginInsert::PluginControl::interface_to_internal (double val) const
+void
+PluginInsert::PluginPropertyControl::set_value (double user_val)
 {
-       if (_logarithmic) {
-               if (val <= -8) {
-                       /* see note in PluginInsert::PluginControl::internal_to_interface() */
-                       val= 0;
-               } else {
-                       val = exp (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::NOTHING) {
+               error << "set_value(double) called for non-numeric property" << endmsg;
+               return;
        }
 
-       return val;
+       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::PluginControl::get_state ()
+PluginInsert::PluginPropertyControl::get_state ()
 {
        stringstream ss;
 
        XMLNode& node (AutomationControl::get_state());
        ss << parameter().id();
-       node.add_property (X_("parameter"), ss.str());
+       node.add_property (X_("property"), ss.str());
+       node.remove_property (X_("value"));
 
        return node;
 }
 
-/** @return `user' val */
 double
-PluginInsert::PluginControl::get_value () const
+PluginInsert::PluginPropertyControl::get_value () const
 {
-       /* FIXME: probably should be taking out some lock here.. */
-       return _plugin->get_parameter (_list->parameter());
+       return _value.to_double();
 }
 
 boost::shared_ptr<Plugin>
@@ -1312,7 +1402,7 @@ PluginInsert::collect_signal_for_analysis (framecnt_t nframes)
 void
 PluginInsert::add_plugin (boost::shared_ptr<Plugin> plugin)
 {
-       plugin->set_insert_info (this);
+       plugin->set_insert_id (this->id());
        
        if (_plugins.empty()) {
                 /* first (and probably only) plugin instance - connect to relevant signals