deactivate plugin if connect_and_run returns an error
[ardour.git] / libs / ardour / plugin_insert.cc
index b98706a3d08d31947b4d24ce2e574853b722f499..96b8570d8c1c10f32a589d829f88c6660a23290c 100644 (file)
@@ -167,7 +167,7 @@ PluginInsert::input_streams() const
        }
 
        DEBUG_TRACE (DEBUG::Processors, string_compose ("Plugin insert, input streams = %1, match using %2\n", in, _match.method));
-       
+
        if (_match.method == Split) {
 
                /* we are splitting 1 processor input to multiple plugin inputs,
@@ -188,7 +188,7 @@ PluginInsert::input_streams() const
                return in;
 
        } else {
-               
+
                for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                        in.set (*t, in.get (*t) * _plugins.size ());
                }
@@ -240,46 +240,77 @@ 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)));
+                       }
                }
        }
 }
-
+/** Called when something outside of this host has modified a plugin
+ * parameter. Responsible for propagating the change to two places:
+ *
+ *   1) anything listening to the Control itself
+ *   2) any replicated plugins that make up this PluginInsert.
+ *
+ * The PluginInsert is connected to the ParameterChangedExternally signal for
+ * the first (primary) plugin, and here broadcasts that change to any others.
+ *
+ * XXX We should probably drop this whole replication idea (Paul, October 2015)
+ * since it isn't used by sensible plugin APIs (AU, LV2).
+ */
 void
-PluginInsert::parameter_changed (uint32_t which, float val)
+PluginInsert::parameter_changed_externally (uint32_t which, float val)
 {
        boost::shared_ptr<AutomationControl> ac = automation_control (Evoral::Parameter (PluginAutomation, 0, which));
 
-       if (ac) {
-               ac->set_value (val);
-                
-                Plugins::iterator i = _plugins.begin();
-                
-                /* don't set the first plugin, just all the slaves */
-                
-                if (i != _plugins.end()) {
-                        ++i;
-                        for (; i != _plugins.end(); ++i) {
-                                (*i)->set_parameter (which, val);
-                        }
-                }
-        }
+       /* First propagation: alter the underlying value of the control,
+        * without telling the plugin(s) that own/use it to set it.
+        */
+
+       if (!ac) {
+               return;
+       }
+
+       boost::shared_ptr<PluginControl> pc = boost::dynamic_pointer_cast<PluginControl> (ac);
+
+       if (pc) {
+               pc->catch_up_with_external_value (val);
+       }
+
+       /* Second propagation: tell all plugins except the first to
+          update the value of this parameter. For sane plugin APIs,
+          there are no other plugins, so this is a no-op in those
+          cases.
+       */
+
+       Plugins::iterator i = _plugins.begin();
+
+       /* don't set the first plugin, just all the slaves */
+
+       if (i != _plugins.end()) {
+               ++i;
+               for (; i != _plugins.end(); ++i) {
+                       (*i)->set_parameter (which, val);
+               }
+       }
 }
 
 int
@@ -368,13 +399,22 @@ 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);
 
                                if (valid) {
-                                       c->set_value(val);
+                                       /* This is the ONLY place where we are
+                                        *  allowed to call
+                                        *  AutomationControl::set_value_unchecked(). We
+                                        *  know that the control is in
+                                        *  automation playback mode, so no
+                                        *  check on writable() is required
+                                        *  (which must be done in AutomationControl::set_value()
+                                        *
+                                        */
+                                       c->set_value_unchecked(val);
                                }
 
                        }
@@ -399,7 +439,9 @@ PluginInsert::connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t of
        }
 
        for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
-               (*i)->connect_and_run(bufs, in_map, out_map, nframes, offset);
+               if ((*i)->connect_and_run(bufs, in_map, out_map, nframes, offset)) {
+                       deactivate ();
+               }
                for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                        in_map.offset_to(*t, natural_input_streams().get(*t));
                        out_map.offset_to(*t, natural_output_streams().get(*t));
@@ -502,41 +544,6 @@ PluginInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t /*end_fra
 
 }
 
-void
-PluginInsert::set_parameter (Evoral::Parameter param, float val)
-{
-       if (param.type() != PluginAutomation) {
-               return;
-       }
-
-       /* the others will be set from the event triggered by this */
-
-       _plugins[0]->set_parameter (param.id(), val);
-
-       boost::shared_ptr<AutomationControl> ac
-                       = boost::dynamic_pointer_cast<AutomationControl>(control(param));
-
-       if (ac) {
-               ac->set_value(val);
-       } else {
-               warning << "set_parameter called for nonexistant parameter "
-                       << EventTypeMap::instance().to_symbol(param) << endmsg;
-       }
-
-       _session.set_dirty();
-}
-
-float
-PluginInsert::get_parameter (Evoral::Parameter param)
-{
-       if (param.type() != PluginAutomation) {
-               return 0.0;
-       } else {
-               assert (!_plugins.empty ());
-               return _plugins[0]->get_parameter (param.id());
-       }
-}
-
 void
 PluginInsert::automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes)
 {
@@ -591,12 +598,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, Controllable::NoGroup);
+       }
+       return all;
+}
+
 boost::shared_ptr<Plugin>
 PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
 {
@@ -637,7 +707,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);
 }
 
@@ -648,36 +718,42 @@ PluginInsert::configure_io (ChanCount in, ChanCount out)
        ChanCount old_in = input_streams ();
        ChanCount old_out = output_streams ();
 
+       _configured_in = in;
+       _configured_out = out;
+
        /* set the matching method and number of plugins that we will use to meet this configuration */
        _match = private_can_support_io_configuration (in, out);
        if (set_count (_match.plugins) == false) {
-               return false;
-       }
-
-       if (  (old_match.method != _match.method && (old_match.method == Split || _match.method == Split))
-                       || old_in != in
-                       || old_out != out
-                       )
-       {
                PluginIoReConfigure (); /* EMIT SIGNAL */
+               return false;
        }
 
        /* configure plugins */
        switch (_match.method) {
        case Split:
        case Hide:
-               if (_plugins.front()->configure_io (_plugins.front()->get_info()->n_inputs, out)) {
+               if (_plugins.front()->configure_io (_plugins.front()->get_info()->n_inputs, out) == false) {
+                       PluginIoReConfigure (); /* EMIT SIGNAL */
                        return false;
                }
                break;
 
        default:
                if (_plugins.front()->configure_io (in, out) == false) {
+                       PluginIoReConfigure (); /* EMIT SIGNAL */
                        return false;
                }
                break;
        }
 
+       if (  (old_match.method != _match.method && (old_match.method == Split || _match.method == Split))
+                       || old_in != in
+                       || old_out != out
+                       )
+       {
+               PluginIoReConfigure (); /* EMIT SIGNAL */
+       }
+
        // we don't know the analysis window size, so we must work with the
        // current buffer size here. each request for data fills in these
        // buffers and the analyser makes sure it gets enough data for the
@@ -769,7 +845,7 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
           We allow replication only for plugins with either zero or 1 inputs and outputs
           for every valid data type.
        */
-       
+
        uint32_t f             = 0;
        bool     can_replicate = true;
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
@@ -837,7 +913,7 @@ PluginInsert::private_can_support_io_configuration (ChanCount const & inx, ChanC
        bool could_hide = false;
        bool cannot_hide = false;
        ChanCount hide_channels;
-       
+
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                if (inputs.get(*t) > in.get(*t)) {
                        /* there is potential to hide, since the plugin has more inputs of type t than the insert */
@@ -872,6 +948,13 @@ PluginInsert::state (bool full)
        node.add_property("type", _plugins[0]->state_node_name());
        node.add_property("unique-id", _plugins[0]->unique_id());
        node.add_property("count", string_compose("%1", _plugins.size()));
+
+       /* remember actual i/o configuration (for later placeholder
+        * in case the plugin goes missing) */
+       node.add_child_nocopy (* _configured_in.state (X_("ConfiguredInput")));
+       node.add_child_nocopy (* _configured_out.state (X_("ConfiguredOutput")));
+
+       _plugins[0]->set_insert_id(this->id());
        node.add_child_nocopy (_plugins[0]->get_state());
 
        for (Controls::iterator c = controls().begin(); c != controls().end(); ++c) {
@@ -895,8 +978,20 @@ PluginInsert::set_control_ids (const XMLNode& node, int version)
                if ((*iter)->name() == Controllable::xml_node_name) {
                        const XMLProperty* prop;
 
-                       if ((prop = (*iter)->property (X_("parameter"))) != 0) {
-                               uint32_t p = atoi (prop->value());
+                       uint32_t p = (uint32_t)-1;
+#ifdef LV2_SUPPORT
+                       if ((prop = (*iter)->property (X_("symbol"))) != 0) {
+                               boost::shared_ptr<LV2Plugin> lv2plugin = boost::dynamic_pointer_cast<LV2Plugin> (_plugins[0]);
+                               if (lv2plugin) {
+                                       p = lv2plugin->port_index(prop->value().c_str());
+                               }
+                       }
+#endif
+                       if (p == (uint32_t)-1 && (prop = (*iter)->property (X_("parameter"))) != 0) {
+                               p = atoi (prop->value());
+                       }
+
+                       if (p != (uint32_t)-1) {
 
                                /* this may create the new controllable */
 
@@ -1034,6 +1129,13 @@ PluginInsert::set_state(const XMLNode& node, int version)
 
        Processor::set_state (node, version);
 
+       PBD::ID new_id = this->id();
+       PBD::ID old_id = this->id();
+
+       if ((prop = node.property ("id")) != 0) {
+               old_id = prop->value ();
+       }
+
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
                /* find the node with the type-specific node name ("lv2", "ladspa", etc)
@@ -1042,10 +1144,28 @@ 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) {
+                               /* Plugin state can include external files which are named after the ID.
+                                *
+                                * If regenerate_xml_or_string_ids() is set, the ID will already have
+                                * been changed, so we need to use the old ID from the XML to load the
+                                * state and then update the ID.
+                                *
+                                * When copying a plugin-state, route_ui takes care of of updating the ID,
+                                * but we need to call set_insert_id() to clear the cached plugin-state
+                                * and force a change.
+                                */
+                               if (!regenerate_xml_or_string_ids ()) {
+                                       (*i)->set_insert_id (new_id);
+                               } else {
+                                       (*i)->set_insert_id (old_id);
+                               }
+
                                (*i)->set_state (**niter, version);
+
+                               if (regenerate_xml_or_string_ids ()) {
+                                       (*i)->set_insert_id (new_id);
+                               }
                        }
 
                        break;
@@ -1080,6 +1200,22 @@ PluginInsert::set_state(const XMLNode& node, int version)
        return 0;
 }
 
+void
+PluginInsert::update_id (PBD::ID id)
+{
+       set_id (id.to_s());
+       for (Plugins::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+               (*i)->set_insert_id (id);
+       }
+}
+
+void
+PluginInsert::set_state_dir (const std::string& d)
+{
+       // state() only saves the state of the first plugin
+       _plugins[0]->set_state_dir (d);
+}
+
 void
 PluginInsert::set_parameter_state_2X (const XMLNode& node, int version)
 {
@@ -1124,7 +1260,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);
 
@@ -1139,7 +1275,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) {
@@ -1167,11 +1303,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
@@ -1190,19 +1330,19 @@ 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()));
-
-       plugin->get_parameter_descriptor (param.id(), desc);
-       _logarithmic = desc.logarithmic;
-       _sr_dependent = desc.sr_dependent;
-       _toggled = desc.toggled;
+       if (alist()) {
+               alist()->reset_default (desc.normal);
+               if (desc.toggled) {
+                       list->set_interpolation(Evoral::ControlList::Discrete);
+               }
+       }
 
        if (desc.toggled) {
                set_flags(Controllable::Toggle);
@@ -1211,7 +1351,21 @@ PluginInsert::PluginControl::PluginControl (PluginInsert* p, const Evoral::Param
 
 /** @param val `user' value */
 void
-PluginInsert::PluginControl::set_value (double user_val)
+PluginInsert::PluginControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
+{
+       if (writable()) {
+               _set_value (user_val, group_override);
+       }
+}
+void
+PluginInsert::PluginControl::set_value_unchecked (double user_val)
+{
+       /* used only by automation playback */
+       _set_value (user_val, Controllable::NoGroup);
+}
+
+void
+PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override)
 {
        /* FIXME: probably should be taking out some lock here.. */
 
@@ -1224,59 +1378,108 @@ PluginInsert::PluginControl::set_value (double user_val)
                iasp->set_parameter (_list->parameter().id(), user_val);
        }
 
-       AutomationControl::set_value (user_val);
+       AutomationControl::set_value (user_val, group_override);
 }
 
-double
-PluginInsert::PluginControl::internal_to_interface (double val) const
+void
+PluginInsert::PluginControl::catch_up_with_external_value (double user_val)
 {
-       val = Controllable::internal_to_interface(val);
-       
-       if (_logarithmic) {
-               if (val > 0) {
-                       val = pow (val, 1/1.5);
-               } else {
-                       val = 0;
-               }
+       AutomationControl::set_value (user_val, Controllable::NoGroup);
+}
+
+XMLNode&
+PluginInsert::PluginControl::get_state ()
+{
+       stringstream ss;
+
+       XMLNode& node (AutomationControl::get_state());
+       ss << parameter().id();
+       node.add_property (X_("parameter"), ss.str());
+#ifdef LV2_SUPPORT
+       boost::shared_ptr<LV2Plugin> lv2plugin = boost::dynamic_pointer_cast<LV2Plugin> (_plugin->_plugins[0]);
+       if (lv2plugin) {
+               node.add_property (X_("symbol"), lv2plugin->port_symbol (parameter().id()));
        }
+#endif
 
-       return val;
+       return node;
 }
 
+/** @return `user' val */
 double
-PluginInsert::PluginControl::interface_to_internal (double val) const
+PluginInsert::PluginControl::get_value () const
 {
-       if (_logarithmic) {
-               if (val <= 0) {
-                       val= 0;
-               } else {
-                       val = pow (val, 1.5);
-               }
+       boost::shared_ptr<Plugin> plugin = _plugin->plugin (0);
+
+       if (!plugin) {
+               return 0.0;
+       }
+
+       return plugin->get_parameter (_list->parameter().id());
+}
+
+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);
        }
 
-       val = Controllable::interface_to_internal(val);
-       
-       return val;
+       if (desc.toggled) {
+               set_flags(Controllable::Toggle);
+       }
+}
+
+void
+PluginInsert::PluginPropertyControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition /* group_override*/)
+{
+       if (writable()) {
+               set_value_unchecked (user_val);
+       }
+}
+
+void
+PluginInsert::PluginPropertyControl::set_value_unchecked (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::NOTHING) {
+               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, Controllable::NoGroup);
 }
 
 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>
@@ -1285,6 +1488,7 @@ PluginInsert::get_impulse_analysis_plugin()
        boost::shared_ptr<Plugin> ret;
        if (_impulseAnalysisPlugin.expired()) {
                ret = plugin_factory(_plugins[0]);
+               ret->configure_io (input_streams (), output_streams ());
                _impulseAnalysisPlugin = ret;
        } else {
                ret = _impulseAnalysisPlugin.lock();
@@ -1309,13 +1513,13 @@ 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 
+                /* first (and probably only) plugin instance - connect to relevant signals
                  */
 
-               plugin->ParameterChanged.connect_same_thread (*this, boost::bind (&PluginInsert::parameter_changed, this, _1, _2));
+               plugin->ParameterChangedExternally.connect_same_thread (*this, boost::bind (&PluginInsert::parameter_changed_externally, this, _1, _2));
                 plugin->StartTouch.connect_same_thread (*this, boost::bind (&PluginInsert::start_touch, this, _1));
                 plugin->EndTouch.connect_same_thread (*this, boost::bind (&PluginInsert::end_touch, this, _1));
        }