new API for route solo/mute state mgmt
[ardour.git] / libs / ardour / route.cc
index 79799ce807b9b48b88b2739539055f923da03d10..569f00fbc770c020e8d2798f0994dbfd3695491f 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"
@@ -69,6 +69,7 @@
 #include "ardour/session.h"
 #include "ardour/unknown_processor.h"
 #include "ardour/utils.h"
+#include "ardour/vca.h"
 
 #include "i18n.h"
 
@@ -78,10 +79,11 @@ 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)
-       : SessionObject (sess, name)
+       : Stripable (sess, name)
        , Automatable (sess)
        , GraphNode (sess._process_graph)
        , _active (true)
@@ -119,6 +121,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();
 }
@@ -170,14 +173,9 @@ Route::init ()
        _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
        _output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
 
-#if 0 // not used - just yet
-       if (!is_master() && !is_monitor() && !is_auditioner()) {
-               _delayline.reset (new DelayLine (_session, _name));
-               add_processor (_delayline, PreFader);
-       }
-#endif
-
-       /* add amp processor  */
+       /* add the amp/fader processor.
+        * it should be the first processor to be added on every route.
+        */
 
        _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ()));
        add_control (_gain_control);
@@ -189,6 +187,13 @@ Route::init ()
                _amp->set_display_name (_("Monitor"));
        }
 
+#if 0 // not used - just yet
+       if (!is_master() && !is_monitor() && !is_auditioner()) {
+               _delayline.reset (new DelayLine (_session, _name));
+               add_processor (_delayline, PreFader);
+       }
+#endif
+
        /* and input trim */
 
        _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ()));
@@ -825,7 +830,8 @@ Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override
                        }
                        _mute_master->set_soloed_by_others (false);
 
-                       listen_changed (group_override); /* EMIT SIGNAL */
+                       _session.listen_changed (group_override, shared_from_this());
+                       _solo_control->Changed(); /* EMIT SIGNAL */
                }
        }
 }
@@ -845,7 +851,6 @@ Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition /* group_ov
 {
        if (_solo_safe != yn) {
                _solo_safe = yn;
-               solo_safe_changed (); /* EMIT SIGNAL */
                _solo_safe_control->Changed(); /* EMIT SIGNAL */
        }
 }
@@ -888,7 +893,8 @@ Route::clear_all_solo_state ()
 
        if (emit_changed) {
                set_mute_master_solo ();
-               solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
+               _session.solo_changed (false, Controllable::UseGroup, shared_from_this());
+               _solo_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -915,7 +921,7 @@ Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
 
        if (self_soloed() != yn) {
                set_self_solo (yn);
-               solo_changed (true, group_override); /* EMIT SIGNAL */
+               _session.solo_changed (true, group_override, shared_from_this());
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
 
@@ -993,7 +999,8 @@ Route::mod_solo_by_others_upstream (int32_t delta)
        }
 
        set_mute_master_solo ();
-       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
+       _session.solo_changed (false, Controllable::UseGroup, shared_from_this());
+       _solo_control->Changed (); /* EMIT SIGNAL */
 }
 
 void
@@ -1015,7 +1022,8 @@ Route::mod_solo_by_others_downstream (int32_t delta)
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream));
 
        set_mute_master_solo ();
-       solo_changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
+       _session.solo_changed (false, Controllable::UseGroup, shared_from_this());
+       _solo_control->Changed (); /* EMIT SIGNAL */
 }
 
 void
@@ -1045,7 +1053,8 @@ Route::mod_solo_isolated_by_upstream (bool yn)
        if (solo_isolated() != old) {
                /* solo isolated status changed */
                _mute_master->set_solo_ignore (solo_isolated());
-               solo_isolated_changed (); /* EMIT SIGNAL */
+               _session.solo_isolated_changed (shared_from_this());
+               _solo_isolate_control->Changed(); /* EMIT SIGNAL */
        }
 }
 
@@ -1101,7 +1110,7 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-       solo_isolated_changed (); /* EMIT SIGNAL */
+       _session.solo_isolated_changed (shared_from_this());
        _solo_isolate_control->Changed(); /* EMIT SIGNAL */
 }
 
@@ -1118,7 +1127,7 @@ Route::set_mute_points (MuteMaster::MutePoint mp)
        mute_points_changed (); /* EMIT SIGNAL */
 
        if (_mute_master->muted_by_self()) {
-               mute_changed (); /* EMIT SIGNAL */
+               _session.mute_changed ();
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
@@ -1138,7 +1147,7 @@ Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override)
                */
                act_on_mute ();
                /* tell everyone else */
-               mute_changed (); /* EMIT SIGNAL */
+               _session.mute_changed ();
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
@@ -1256,93 +1265,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;
-       }
+       ProcessorList pl;
 
-       if (_strict_io) {
-               boost::shared_ptr<PluginInsert> pi;
-               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
-                       pi->set_strict_io (true);
-               }
-       }
+       pl.push_back (processor);
+       int rv = add_processors (pl, before, err);
 
-       {
-               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);
-                               }
-                       }
-               }
-
-               assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
-
-               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, &lm)) {
-                               pstate.restore ();
-                               configure_processors_unlocked (0, &lm); // 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;
@@ -1444,24 +1377,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 +1409,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 +1472,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,14 +1483,24 @@ 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, &lm)) {
                                        pstate.restore ();
@@ -1502,7 +1509,17 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor>
                                }
                        }
 
+                       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) {
@@ -2771,26 +2788,29 @@ Route::state(bool full_state)
                node->add_child_nocopy (_pannable->state (full_state));
        }
 
-       for (i = _processors.begin(); i != _processors.end(); ++i) {
-               if (!full_state) {
-                       /* template save: do not include internal sends functioning as
-                          aux sends because the chance of the target ID
-                          in the session where this template is used
-                          is not very likely.
-
-                          similarly, do not save listen sends which connect to
-                          the monitor section, because these will always be
-                          added if necessary.
-                       */
-                       boost::shared_ptr<InternalSend> is;
+       {
+               Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+               for (i = _processors.begin(); i != _processors.end(); ++i) {
+                       if (!full_state) {
+                               /* template save: do not include internal sends functioning as
+                                        aux sends because the chance of the target ID
+                                        in the session where this template is used
+                                        is not very likely.
+
+                                        similarly, do not save listen sends which connect to
+                                        the monitor section, because these will always be
+                                        added if necessary.
+                                        */
+                               boost::shared_ptr<InternalSend> is;
 
-                       if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
-                               if (is->role() == Delivery::Listen) {
-                                       continue;
+                               if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+                                       if (is->role() == Delivery::Listen) {
+                                               continue;
+                                       }
                                }
                        }
+                       node->add_child_nocopy((*i)->state (full_state));
                }
-               node->add_child_nocopy((*i)->state (full_state));
        }
 
        if (_extra_xml) {
@@ -3330,7 +3350,7 @@ Route::set_processor_state (const XMLNode& node)
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-               XMLProperty const * prop = (*niter)->property ("type");
+               XMLProperty* prop = (*niter)->property ("type");
 
                if (prop->value() == "amp") {
                        _amp->set_state (**niter, Stateful::current_state_version);
@@ -4627,7 +4647,6 @@ Route::set_phase_invert (uint32_t c, bool yn)
 {
        if (_phase_invert[c] != yn) {
                _phase_invert[c] = yn;
-               phase_invert_changed (); /* EMIT SIGNAL */
                _phase_control->Changed(); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
@@ -4638,7 +4657,7 @@ Route::set_phase_invert (boost::dynamic_bitset<> p)
 {
        if (_phase_invert != p) {
                _phase_invert = p;
-               phase_invert_changed (); /* EMIT SIGNAL */
+               _phase_control->Changed (); /* EMIT SIGNAL */
                _session.set_dirty ();
        }
 }
@@ -4716,13 +4735,13 @@ Route::gain_control() const
        return _gain_control;
 }
 
-boost::shared_ptr<GainControl>
+boost::shared_ptr<AutomationControl>
 Route::trim_control() const
 {
        return _trim_control;
 }
 
-boost::shared_ptr<Route::PhaseControllable>
+boost::shared_ptr<AutomationControl>
 Route::phase_control() const
 {
        if (phase_invert().size()) {
@@ -5038,12 +5057,13 @@ Route::setup_invisible_processors ()
 
        /* find the amp */
 
-       ProcessorList::iterator amp = new_processors.begin ();
-       while (amp != new_processors.end() && *amp != _amp) {
-               ++amp;
-       }
+       ProcessorList::iterator amp = find (new_processors.begin(), new_processors.end(), _amp);
 
-       assert (amp != new_processors.end ());
+       if (amp == new_processors.end ()) {
+               error << string_compose (_("Amp/Fader on Route '%1' went AWOL. Re-added."), name()) << endmsg;
+               new_processors.push_front (_amp);
+               amp = find (new_processors.begin(), new_processors.end(), _amp);
+       }
 
        /* and the processor after the amp */
 
@@ -5867,3 +5887,37 @@ Route::master_send_enable_controllable () const
        return boost::shared_ptr<AutomationControl>();
 #endif
 }
+
+bool
+Route::slaved_to (boost::shared_ptr<VCA> vca) const
+{
+       if (!vca || !_gain_control) {
+               return false;
+       }
+
+       return _gain_control->slaved_to (vca->gain_control());
+}
+
+void
+Route::vca_assign (boost::shared_ptr<VCA> vca)
+{
+       _gain_control->add_master (vca->gain_control());
+       _solo_control->add_master (vca->solo_control());
+       _mute_control->add_master (vca->mute_control());
+}
+
+void
+Route::vca_unassign (boost::shared_ptr<VCA> vca)
+{
+       if (!vca) {
+               /* unassign from all */
+               _gain_control->clear_masters ();
+               _solo_control->clear_masters ();
+               _mute_control->clear_masters ();
+       } else {
+               _gain_control->remove_master (vca->gain_control());
+               _solo_control->remove_master (vca->solo_control());
+               _mute_control->remove_master (vca->mute_control());
+
+       }
+}