prepare plugin configuration and replacing instruments
[ardour.git] / libs / ardour / route.cc
index f5785c174a6e1fb0039bdeb20e8d7022f1887479..3442d0ec8aca145c56c0bc774ec8f06bd1e4c3b5 100644 (file)
@@ -33,7 +33,6 @@
 #include "pbd/memento_command.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
-#include "pbd/boost_debug.h"
 #include "pbd/unwind.h"
 
 #include "ardour/amp.h"
@@ -41,6 +40,7 @@
 #include "ardour/audio_track.h"
 #include "ardour/audio_port.h"
 #include "ardour/audioengine.h"
+#include "ardour/boost_debug.h"
 #include "ardour/buffer.h"
 #include "ardour/buffer_set.h"
 #include "ardour/capturing_processor.h"
@@ -78,6 +78,7 @@ using namespace PBD;
 
 PBD::Signal0<void> Route::SyncOrderKeys;
 PBD::Signal0<void> Route::RemoteControlIDChange;
+PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Route::PluginSetupOptions > Route::PluginSetup;
 
 /** Base class for all routable/mixable objects (tracks and busses) */
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
@@ -119,6 +120,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _in_sidechain_setup (false)
        , _strict_io (false)
        , _custom_meter_position_noted (false)
+       , _pinmgr_proxy (0)
 {
        processor_max_streams.reset();
 }
@@ -1256,93 +1258,17 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
        DEBUG_TRACE (DEBUG::Processors, string_compose (
                             "%1 adding processor %2\n", name(), processor->name()));
 
-       if (!AudioEngine::instance()->connected() || !processor) {
-               return 1;
-       }
-
-       if (_strict_io) {
-               boost::shared_ptr<PluginInsert> pi;
-               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-                       pi->set_strict_io (true);
-               }
-       }
-
-       {
-               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
-               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               ProcessorState pstate (this);
-
-               boost::shared_ptr<PluginInsert> pi;
-               boost::shared_ptr<PortInsert> porti;
-
-               if (processor == _amp) {
-                       /* Ensure that only one amp is in the list at any time */
-                       ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
-                       if (check != _processors.end()) {
-                               if (before == _amp) {
-                                       /* Already in position; all is well */
-                                       return 0;
-                               } else {
-                                       _processors.erase (check);
-                               }
-                       }
-               }
+       ProcessorList pl;
 
-               assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
+       pl.push_back (processor);
+       int rv = add_processors (pl, before, err);
 
-               ProcessorList::iterator loc;
-               if (before) {
-                       /* inserting before a processor; find it */
-                       loc = find (_processors.begin(), _processors.end(), before);
-                       if (loc == _processors.end ()) {
-                               /* Not found */
-                               return 1;
-                       }
-               } else {
-                       /* inserting at end */
-                       loc = _processors.end ();
-               }
-
-               _processors.insert (loc, processor);
-               processor->set_owner (this);
-
-               // Set up processor list channels.  This will set processor->[input|output]_streams(),
-               // configure redirect ports properly, etc.
-
-               {
-                       if (configure_processors_unlocked (err)) {
-                               pstate.restore ();
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
-                       }
-               }
-
-               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-
-                       if (pi->has_no_inputs ()) {
-                               /* generator plugin */
-                               _have_internal_generator = true;
-                       }
-
-               }
-
-               if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
-                       processor->activate ();
-               }
-
-               processor->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
-
-               _output->set_user_latency (0);
+       if (rv) {
+               return rv;
        }
 
-       reset_instrument_info ();
-       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
-       set_processor_positions ();
-
-       boost::shared_ptr<Send> send;
-       if ((send = boost::dynamic_pointer_cast<Send> (processor))) {
-               send->SelfDestruct.connect_same_thread (*this,
-                               boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+       if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
+               processor->activate ();
        }
 
        return 0;
@@ -1362,7 +1288,7 @@ Route::processor_selfdestruct (boost::weak_ptr<Processor> wp)
 bool
 Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 {
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        try {
                boost::shared_ptr<Processor> processor;
@@ -1444,24 +1370,31 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
        }
 }
 
+
+inline Route::PluginSetupOptions operator|= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+       return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) | static_cast<int> (b));
+}
+
+inline Route::PluginSetupOptions operator&= (Route::PluginSetupOptions& a, const Route::PluginSetupOptions& b) {
+       return a = static_cast<Route::PluginSetupOptions> (static_cast <int>(a) & static_cast<int> (b));
+}
+
 int
 Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
-       /* NOTE: this is intended to be used ONLY when copying
-          processors from another Route. Hence the subtle
-          differences between this and ::add_processor()
-       */
-
        ProcessorList::iterator loc;
 
        if (before) {
                loc = find(_processors.begin(), _processors.end(), before);
+               if (loc == _processors.end ()) {
+                       return 1;
+               }
        } else {
                /* nothing specified - at end */
                loc = _processors.end ();
        }
 
-       if (!_session.engine().connected()) {
+       if (!AudioEngine::instance()->connected()) {
                return 1;
        }
 
@@ -1469,6 +1402,59 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                return 0;
        }
 
+       ProcessorList to_skip;
+
+       // check if there's an instrument to replace or configure
+       for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
+               boost::shared_ptr<PluginInsert> pi;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) == 0) {
+                       continue;
+               }
+               if (!pi->plugin ()->get_info ()->is_instrument ()) {
+                       continue;
+               }
+               boost::shared_ptr<Processor> instrument = the_instrument ();
+               ChanCount in (DataType::MIDI, 1);
+               ChanCount out (DataType::AUDIO, 2); // XXX route's out?!
+
+               PluginSetupOptions flags = None;
+               if (instrument) {
+                       flags |= CanReplace;
+                       in = instrument->input_streams ();
+                       out = instrument->output_streams ();
+               }
+               if (pi->has_output_presets (in, out)) {
+                       flags |= MultiOut;
+               }
+
+               pi->set_strict_io (_strict_io);
+
+               PluginSetupOptions mask = None;
+               if (Config->get_ask_replace_instrument ()) {
+                       mask |= CanReplace;
+               }
+               if (Config->get_ask_setup_instrument ()) {
+                       mask |= MultiOut;
+               }
+
+               flags &= mask;
+
+               if (flags != None) {
+                       boost::optional<int> rv = PluginSetup (shared_from_this (), pi, flags);  /* EMIT SIGNAL */
+                       switch (rv.get_value_or (0)) {
+                               case 1:
+                                       to_skip.push_back (*i); // don't add this one;
+                                       break;
+                               case 2:
+                                       replace_processor (instrument, *i, err);
+                                       to_skip.push_back (*i);
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
@@ -1479,6 +1465,10 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                        if (*i == _meter) {
                                continue;
                        }
+                       ProcessorList::iterator check = find (to_skip.begin(), to_skip.end(), *i);
+                       if (check != to_skip.end()) {
+                               continue;
+                       }
 
                        boost::shared_ptr<PluginInsert> pi;
 
@@ -1486,23 +1476,43 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                                pi->set_strict_io (_strict_io);
                        }
 
+                       if (*i == _amp) {
+                               /* Ensure that only one amp is in the list at any time */
+                               ProcessorList::iterator check = find (_processors.begin(), _processors.end(), *i);
+                               if (check != _processors.end()) {
+                                       if (before == _amp) {
+                                               /* Already in position; all is well */
+                                               continue;
+                                       } else {
+                                               _processors.erase (check);
+                                       }
+                               }
+                       }
+
+                       assert (find (_processors.begin(), _processors.end(), *i) == _processors.end ());
+
                        _processors.insert (loc, *i);
                        (*i)->set_owner (this);
 
-                       if ((*i)->active()) {
-                               (*i)->activate ();
-                       }
-
-                       /* Think: does this really need to be called for every processor in the loop? */
                        {
-                               if (configure_processors_unlocked (err)) {
+                               if (configure_processors_unlocked (err, &lm)) {
                                        pstate.restore ();
-                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                                        return -1;
                                }
                        }
 
+                       if ((*i)->active()) {
+                               (*i)->activate ();
+                       }
+
                        (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false));
+
+                       boost::shared_ptr<Send> send;
+                       if ((send = boost::dynamic_pointer_cast<Send> (*i))) {
+                               send->SelfDestruct.connect_same_thread (*this,
+                                               boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (*i)));
+                       }
                }
 
                for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -1715,7 +1725,7 @@ Route::clear_processors (Placement p)
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+               configure_processors_unlocked (&err, &lm); // this can't fail
        }
 
        processor_max_streams.reset();
@@ -1815,10 +1825,10 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
 
@@ -1912,9 +1922,9 @@ Route::replace_processor (boost::shared_ptr<Processor> old, boost::shared_ptr<Pr
                        }
                }
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
 
@@ -2009,10 +2019,10 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                _output->set_user_latency (0);
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                        return -1;
                }
                //lx.unlock();
@@ -2063,7 +2073,7 @@ Route::configure_processors (ProcessorStreams* err)
 
        if (!_in_configure_processors) {
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               return configure_processors_unlocked (err);
+               return configure_processors_unlocked (err, &lm);
        }
 
        return 0;
@@ -2179,7 +2189,7 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
  *  Return 0 on success, otherwise configuration is impossible.
  */
 int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+Route::configure_processors_unlocked (ProcessorStreams* err, Glib::Threads::RWLock::WriterLock* lm)
 {
 #ifndef PLATFORM_WINDOWS
        assert (!AudioEngine::instance()->process_lock().trylock());
@@ -2206,12 +2216,37 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        processor_out_streams = _input->n_ports();
        processor_max_streams.reset();
 
+       /* processor configure_io() may result in adding ports
+        * e.g. Delivery::configure_io -> ARDOUR::IO::ensure_io ()
+        *
+        * with jack2 adding ports results in a graph-order callback,
+        * which calls Session::resort_routes() and eventually
+        * Route::direct_feeds_according_to_reality()
+        * which takes a ReaderLock (_processor_lock).
+        *
+        * so we can't hold a WriterLock here until jack2 threading
+        * is fixed.
+        *
+        * NB. we still hold the process lock
+        *
+        * (ardour's own engines do call graph-order from the
+        * process-thread and hence do not have this issue; besides
+        * merely adding ports won't trigger a graph-order, only
+        * making connections does)
+        */
+       lm->release ();
+
+       // TODO check for a potential ReaderLock after ReaderLock ??
+       Glib::Threads::RWLock::ReaderLock lr (_processor_lock);
+
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
 
                if (!(*p)->configure_io(c->first, c->second)) {
                        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
                        _in_configure_processors = false;
+                       lr.release ();
+                       lm->acquire ();
                        return -1;
                }
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
@@ -2245,6 +2280,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                }
        }
 
+       lr.release ();
+       lm->acquire ();
+
 
        if (_meter) {
                _meter->set_max_channels (processor_max_streams);
@@ -2438,7 +2476,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                apply_processor_order (new_order);
 
-               if (configure_processors_unlocked (err)) {
+               if (configure_processors_unlocked (err, &lm)) {
                        pstate.restore ();
                        return -1;
                }
@@ -2511,7 +2549,7 @@ Route::add_remove_sidechain (boost::shared_ptr<Processor> proc, bool add)
                        return false;
                }
                lx.acquire ();
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lm);
        }
 
        if (pi->has_sidechain ()) {
@@ -2554,7 +2592,7 @@ Route::plugin_preset_output (boost::shared_ptr<Processor> proc, ChanCount outs)
                        pi->set_preset_out (old);
                        return false;
                }
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lm);
        }
 
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
@@ -2566,11 +2604,11 @@ bool
 Route::reset_plugin_insert (boost::shared_ptr<Processor> proc)
 {
        ChanCount unused;
-       return customize_plugin_insert (proc, 0, unused);
+       return customize_plugin_insert (proc, 0, unused, unused);
 }
 
 bool
-Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs)
+Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t count, ChanCount outs, ChanCount sinks)
 {
        boost::shared_ptr<PluginInsert> pi;
        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(proc)) == 0) {
@@ -2589,9 +2627,10 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
 
-               bool      old_cust = pi->custom_cfg ();
-               uint32_t  old_cnt  = pi->get_count ();
-               ChanCount old_chan = pi->output_streams ();
+               bool      old_cust  = pi->custom_cfg ();
+               uint32_t  old_cnt   = pi->get_count ();
+               ChanCount old_chan  = pi->output_streams ();
+               ChanCount old_sinks = pi->natural_input_streams ();
 
                if (count == 0) {
                        pi->set_custom_cfg (false);
@@ -2599,6 +2638,7 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                        pi->set_custom_cfg (true);
                        pi->set_count (count);
                        pi->set_outputs (outs);
+                       pi->set_sinks (sinks);
                }
 
                list<pair<ChanCount, ChanCount> > c = try_configure_processors_unlocked (n_inputs (), 0);
@@ -2606,12 +2646,13 @@ Route::customize_plugin_insert (boost::shared_ptr<Processor> proc, uint32_t coun
                        /* not possible */
 
                        pi->set_count (old_cnt);
+                       pi->set_sinks (old_sinks);
                        pi->set_outputs (old_chan);
                        pi->set_custom_cfg (old_cust);
 
                        return false;
                }
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lm);
        }
 
        processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
@@ -2673,6 +2714,7 @@ Route::get_template()
 XMLNode&
 Route::state(bool full_state)
 {
+       LocaleGuard lg;
        if (!_session._template_state_dir.empty()) {
                assert (!full_state); // only for templates
                foreach_processor (sigc::bind (sigc::mem_fun (*this, &Route::set_plugin_state_dir), _session._template_state_dir));
@@ -2790,7 +2832,7 @@ Route::set_state (const XMLNode& node, int version)
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        if (node.name() != "Route"){
                error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
@@ -2913,7 +2955,6 @@ Route::set_state (const XMLNode& node, int version)
 
        if ((prop = node.property (X_("active"))) != 0) {
                bool yn = string_is_affirmative (prop->value());
-               _active = !yn; // force switch
                set_active (yn, this);
        }
 
@@ -3009,11 +3050,11 @@ Route::set_state (const XMLNode& node, int version)
 int
 Route::set_state_2X (const XMLNode& node, int version)
 {
-       LocaleGuard lg (X_("C"));
+       LocaleGuard lg;
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
-       const XMLProperty *prop;
+       XMLProperty const * prop;
 
        /* 2X things which still remain to be handled:
         * default-type
@@ -3299,7 +3340,7 @@ Route::set_processor_state (const XMLNode& node)
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-               XMLProperty* prop = (*niter)->property ("type");
+               XMLProperty const * prop = (*niter)->property ("type");
 
                if (prop->value() == "amp") {
                        _amp->set_state (**niter, Stateful::current_state_version);
@@ -3336,7 +3377,7 @@ Route::set_processor_state (const XMLNode& node)
                        ProcessorList::iterator o;
 
                        for (o = _processors.begin(); o != _processors.end(); ++o) {
-                               XMLProperty* id_prop = (*niter)->property(X_("id"));
+                               XMLProperty const * id_prop = (*niter)->property(X_("id"));
                                if (id_prop && (*o)->id() == id_prop->value()) {
                                        (*o)->set_state (**niter, Stateful::current_state_version);
                                        new_order.push_back (*o);
@@ -3423,7 +3464,7 @@ Route::set_processor_state (const XMLNode& node)
                _processors = new_order;
 
                if (must_configure) {
-                       configure_processors_unlocked (0);
+                       configure_processors_unlocked (0, &lm);
                }
 
                for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
@@ -3743,8 +3784,8 @@ Route::direct_feeds_according_to_reality (boost::shared_ptr<Route> other, bool*
                return true;
        }
 
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
-       Glib::Threads::RWLock::ReaderLock lm (_processor_lock); // XXX
        for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
 
                boost::shared_ptr<IOProcessor> iop = boost::dynamic_pointer_cast<IOProcessor>(*r);
@@ -4268,10 +4309,10 @@ Route::listen_position_changed ()
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
                ProcessorState pstate (this);
 
-               if (configure_processors_unlocked (0)) {
+               if (configure_processors_unlocked (0, &lm)) {
                        DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
                        pstate.restore ();
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                       configure_processors_unlocked (0, &lm); // it worked before we tried to add it ...
                        return;
                }
        }
@@ -4292,7 +4333,7 @@ Route::add_export_point()
                _capturing_processor.reset (new CapturingProcessor (_session));
                _capturing_processor->activate ();
 
-               configure_processors_unlocked (0);
+               configure_processors_unlocked (0, &lw);
 
        }
 
@@ -4554,7 +4595,7 @@ Route::set_name_in_state (XMLNode& node, string const & name, bool rename_playli
 
                } else if ((*i)->name() == X_("Processor")) {
 
-                       XMLProperty* role = (*i)->property (X_("role"));
+                       XMLProperty const * role = (*i)->property (X_("role"));
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
@@ -5266,11 +5307,9 @@ boost::shared_ptr<Processor>
 Route::the_instrument_unlocked () const
 {
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if (boost::dynamic_pointer_cast<PluginInsert>(*i)) {
-                       if ((*i)->input_streams().n_midi() > 0 &&
-                           (*i)->output_streams().n_audio() > 0) {
-                               return (*i);
-                       }
+               boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert>(*i);
+               if (pi && pi->plugin ()->get_info ()->is_instrument ()) {
+                       return (*i);
                }
        }
        return boost::shared_ptr<Processor>();