Add Trim to Mackie controls.
[ardour.git] / libs / ardour / route.cc
index eaf7dfa31bd2b19b60a9f65c4908bc35ea5e5157..c3b09df2d36cf25b00857ad8f058d52f0a73c306 100644 (file)
@@ -34,6 +34,7 @@
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
 #include "pbd/boost_debug.h"
+#include "pbd/unwind.h"
 
 #include "ardour/amp.h"
 #include "ardour/audio_buffer.h"
@@ -763,14 +764,19 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t
 }
 
 void
-Route::set_listen (bool yn, void* src)
+Route::set_listen (bool yn, void* src, bool group_override)
 {
        if (_solo_safe) {
                return;
        }
 
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group));
+       bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
+       if (group_override && _route_group) {
+               group_active = !group_active;
+       }
+
+       if (_route_group && src != _route_group && group_active) {
+               _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, _route_group, group_override));
                return;
        }
 
@@ -785,7 +791,7 @@ Route::set_listen (bool yn, void* src)
                        }
                        _mute_master->set_soloed_by_others (false);
 
-                       listen_changed (src); /* EMIT SIGNAL */
+                       listen_changed (src, group_override); /* EMIT SIGNAL */
                }
        }
 }
@@ -816,7 +822,43 @@ Route::solo_safe() const
 }
 
 void
-Route::set_solo (bool yn, void *src)
+Route::clear_all_solo_state ()
+{
+       // ideally this function will never do anything, it only exists to forestall Murphy
+       bool emit_changed = false;
+
+#ifndef NDEBUG
+       // these are really debug messages, but of possible interest.
+       if (_self_solo) {
+               PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
+       }
+       if (_soloed_by_others_upstream || _soloed_by_others_downstream) {
+               PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"),
+                               name(), _soloed_by_others_upstream, _soloed_by_others_downstream);
+       }
+#endif
+
+       if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) {
+               // if self-soled, set_solo() will do signal emission
+               emit_changed = true;
+       }
+
+       _soloed_by_others_upstream = 0;
+       _soloed_by_others_downstream = 0;
+
+       {
+               PBD::Unwinder<bool> uw (_solo_safe, false);
+               set_solo (false, this);
+       }
+
+       if (emit_changed) {
+               set_mute_master_solo ();
+               solo_changed (false, this, false); /* EMIT SIGNAL */
+       }
+}
+
+void
+Route::set_solo (bool yn, void *src, bool group_override)
 {
        if (_solo_safe) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name()));
@@ -828,8 +870,12 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
+       bool group_active = _route_group && _route_group->is_active() && _route_group->is_solo();
+       if (group_override && _route_group) {
+               group_active = !group_active;
+       }
+       if (_route_group && src != _route_group && group_active) {
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group, group_override));
                return;
        }
 
@@ -839,10 +885,12 @@ Route::set_solo (bool yn, void *src)
        if (self_soloed() != yn) {
                set_self_solo (yn);
                set_mute_master_solo ();
-               solo_changed (true, src); /* EMIT SIGNAL */
+               solo_changed (true, src, group_override); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
 
+       assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
+
        /* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
           Config->get_solo_mute_overrride().
        */
@@ -862,11 +910,6 @@ Route::set_self_solo (bool yn)
 void
 Route::mod_solo_by_others_upstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name()));
-               return;
-       }
-
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
@@ -905,8 +948,11 @@ Route::mod_solo_by_others_upstream (int32_t delta)
             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
 
                if (delta > 0 || !Config->get_exclusive_solo()) {
-                       DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                       DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                               if (i->sends_only) {
+                                       continue;
+                               }
                                boost::shared_ptr<Route> sr = i->r.lock();
                                if (sr) {
                                        sr->mod_solo_by_others_downstream (-delta);
@@ -916,17 +962,12 @@ Route::mod_solo_by_others_upstream (int32_t delta)
        }
 
        set_mute_master_solo ();
-       solo_changed (false, this);
+       solo_changed (false, this, false); /* EMIT SIGNAL */
 }
 
 void
 Route::mod_solo_by_others_downstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name()));
-               return;
-       }
-
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
@@ -943,7 +984,7 @@ 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, this);
+       solo_changed (false, this, false); /* EMIT SIGNAL */
 }
 
 void
@@ -957,6 +998,8 @@ void
 Route::mod_solo_isolated_by_upstream (bool yn, void* src)
 {
        bool old = solo_isolated ();
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n",
+                               name(), _solo_isolated_by_upstream, yn ? "+1" : "-1"));
 
        if (!yn) {
                if (_solo_isolated_by_upstream >= 1) {
@@ -970,8 +1013,8 @@ Route::mod_solo_isolated_by_upstream (bool yn, void* src)
 
        if (solo_isolated() != old) {
                /* solo isolated status changed */
-               _mute_master->set_solo_ignore (yn);
-               solo_isolated_changed (src);
+               _mute_master->set_solo_ignore (solo_isolated());
+               solo_isolated_changed (src); /* EMIT SIGNAL */
        }
 }
 
@@ -998,7 +1041,7 @@ Route::set_solo_isolated (bool yn, void *src)
        } else {
                if (_solo_isolated == true) {
                        _solo_isolated = false;
-            _mute_master->set_solo_ignore (false);
+                       _mute_master->set_solo_ignore (false);
                        changed = true;
                }
        }
@@ -1027,7 +1070,7 @@ Route::set_solo_isolated (bool yn, void *src)
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-       solo_isolated_changed (src);
+       solo_isolated_changed (src); /* EMIT SIGNAL */
 }
 
 bool
@@ -1077,12 +1120,16 @@ Route::muted () const
 bool
 Route::muted_by_others () const
 {
+       // This method is only used by route_ui for display state.
+       // The real thing is MuteMaster::muted_by_others_at()
+
        //master is never muted by others
        if (is_master())
                return false;
 
        //now check to see if something is soloed (and I am not)
-       return (_session.soloing() && !self_soloed() && !solo_isolated());
+       //see also MuteMaster::mute_gain_at()
+       return (_session.soloing() && !soloed() && !solo_isolated());
 }
 
 #if 0
@@ -3255,56 +3302,87 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool /*did_
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
-       bool need_to_queue_solo_change = true;
-
        if ((change.type & IOChange::ConfigurationChanged)) {
                /* This is called with the process lock held if change
                   contains ConfigurationChanged
                */
-               need_to_queue_solo_change = false;
                configure_processors (0);
                _phase_invert.resize (_input->n_ports().n_audio ());
                io_changed (); /* EMIT SIGNAL */
        }
 
-       if (!_input->connected() && _soloed_by_others_upstream) {
-               if (need_to_queue_solo_change) {
-                       _session.cancel_solo_after_disconnect (shared_from_this(), true);
-               } else {
-                       cancel_solo_after_disconnect (true);
-               }
-#if 1
-       } else if (_soloed_by_others_upstream) {
-               bool cancel_solo = true;
+       if (_soloed_by_others_upstream || _solo_isolated_by_upstream) {
+               int sbou = 0;
+               int ibou = 0;
                boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               if (_input->connected()) {
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+                               if (does_feed && !sends_only) {
+                                       if ((*i)->soloed()) {
+                                               ++sbou;
+                                       }
+                                       if ((*i)->solo_isolated()) {
+                                               ++ibou;
+                                       }
+                               }
+                       }
+               }
+
+               int delta  = sbou - _soloed_by_others_upstream;
+               int idelta = ibou - _solo_isolated_by_upstream;
+
+               if (idelta < -1) {
+                       PBD::warning << string_compose (
+                                       _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"),
+                                       _name, ibou, _solo_isolated_by_upstream, idelta)
+                                    << endmsg;
+
+               }
+
+               if (_soloed_by_others_upstream) {
+                       // ignore new connections (they're not propagated)
+                       if (delta <= 0) {
+                               mod_solo_by_others_upstream (delta);
+                       }
+               }
+
+               if (_solo_isolated_by_upstream) {
+                       // solo-isolate currently only propagates downstream
+                       if (idelta < 0) {
+                               mod_solo_isolated_by_upstream (false, this);
+                       }
+                       // TODO think: mod_solo_isolated_by_upstream() does not take delta arg,
+                       // but idelta can't be smaller than -1, can it?
+                       //_solo_isolated_by_upstream = ibou;
+               }
+
+               // Session::route_solo_changed  does not propagate indirect solo-changes
+               // propagate downstream to tracks
                for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
                        if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
                                continue;
                        }
                        bool sends_only;
-                       bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
-                       if (does_feed && !sends_only) {
-                               if ((*i)->soloed()) {
-                                       cancel_solo = false;
-                                       break;
-                               }
+                       bool does_feed = feeds (*i, &sends_only);
+                       if (delta <= 0 && does_feed && !sends_only) {
+                               (*i)->mod_solo_by_others_upstream (delta);
+                       }
+
+                       if (idelta < 0 && does_feed && !sends_only) {
+                               (*i)->mod_solo_isolated_by_upstream (false, this);
                        }
                }
-               if (cancel_solo) {
-                       cancel_solo_after_disconnect (true);
-               }
-#else
-       } else if (self_soloed()) {
-#endif
-               // TODO propagate upstream
-               // see commment in output_change_handler() below
        }
 }
 
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
-       bool need_to_queue_solo_change = true;
        if (_initial_io_setup) {
                return;
        }
@@ -3313,7 +3391,6 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                /* This is called with the process lock held if change
                   contains ConfigurationChanged
                */
-               need_to_queue_solo_change = false;
                configure_processors (0);
 
                if (is_master()) {
@@ -3323,56 +3400,48 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                io_changed (); /* EMIT SIGNAL */
        }
 
-       if (!_output->connected() && _soloed_by_others_downstream) {
-               if (need_to_queue_solo_change) {
-                       _session.cancel_solo_after_disconnect (shared_from_this(), false);
-               } else {
-                       cancel_solo_after_disconnect (false);
-               }
-#if 1
-       } else if (_soloed_by_others_downstream) {
-               bool cancel_solo = true;
+       if (_soloed_by_others_downstream) {
+               int sbod = 0;
                /* checking all all downstream routes for
                 * explicit of implict solo is a rather drastic measure,
                 * ideally the input_change_handler() of the other route
                 * would propagate the change to us.
                 */
                boost::shared_ptr<RouteList> routes = _session.get_routes ();
-               for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
-                       if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
-                               continue;
-                       }
-                       bool sends_only;
-                       bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
-                       if (does_feed && !sends_only) {
-                               if ((*i)->soloed()) {
-                                       cancel_solo = false;
-                                       break;
+               if (_output->connected()) {
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+                               if (does_feed && !sends_only) {
+                                       if ((*i)->soloed()) {
+                                               ++sbod;
+                                               break;
+                                       }
                                }
                        }
                }
-               if (cancel_solo) {
-                       cancel_solo_after_disconnect (false);
-               }
-#else
-       } else if (self_soloed()) {
-               // TODO propagate change downstream to the disconnected routes
-               // Q: how to get the routes that were just disconnected. ?
-               // A: /maybe/ by diff feeds() aka fed_by() vs direct_feeds_according_to_reality() ?!?
-#endif
-       }
-}
+               int delta = sbod - _soloed_by_others_downstream;
+               if (delta <= 0) {
+                       // do not allow new connections to change implicit solo (no propagation)
+                       mod_solo_by_others_downstream (delta);
+                       // Session::route_solo_changed() does not propagate indirect solo-changes
+                       // propagate upstream to tracks
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       continue;
+                               }
+                               bool sends_only;
+                               bool does_feed = (*i)->feeds (shared_from_this(), &sends_only);
+                               if (delta != 0 && does_feed && !sends_only) {
+                                       (*i)->mod_solo_by_others_downstream (delta);
+                               }
+                       }
 
-void
-Route::cancel_solo_after_disconnect (bool upstream)
-{
-       if (upstream) {
-               _soloed_by_others_upstream = 0;
-       } else {
-               _soloed_by_others_downstream = 0;
+               }
        }
-       set_mute_master_solo ();
-       solo_changed (false, this);
 }
 
 uint32_t
@@ -3661,6 +3730,7 @@ Route::listen_position_changed ()
                ProcessorState pstate (this);
 
                if (configure_processors_unlocked (0)) {
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
                        pstate.restore ();
                        configure_processors_unlocked (0); // it worked before we tried to add it ...
                        return;
@@ -4166,6 +4236,12 @@ Route::gain_control() const
        return _amp->gain_control();
 }
 
+boost::shared_ptr<AutomationControl>
+Route::trim_control() const
+{
+       return _trim->gain_control();
+}
+
 boost::shared_ptr<AutomationControl>
 Route::get_control (const Evoral::Parameter& param)
 {
@@ -4527,34 +4603,29 @@ Route::setup_invisible_processors ()
 
        if (_monitor_send && !is_monitor ()) {
                assert (!_monitor_send->display_to_user ());
-               if (Config->get_solo_control_is_listen_control()) {
-                       switch (Config->get_listen_position ()) {
-                       case PreFaderListen:
-                               switch (Config->get_pfl_position ()) {
-                               case PFLFromBeforeProcessors:
-                                       new_processors.push_front (_monitor_send);
-                                       break;
-                               case PFLFromAfterProcessors:
-                                       new_processors.insert (amp, _monitor_send);
-                                       break;
-                               }
-                               _monitor_send->set_can_pan (false);
+               switch (Config->get_listen_position ()) {
+               case PreFaderListen:
+                       switch (Config->get_pfl_position ()) {
+                       case PFLFromBeforeProcessors:
+                               new_processors.push_front (_monitor_send);
                                break;
-                       case AfterFaderListen:
-                               switch (Config->get_afl_position ()) {
-                               case AFLFromBeforeProcessors:
-                                       new_processors.insert (after_amp, _monitor_send);
-                                       break;
-                               case AFLFromAfterProcessors:
-                                       new_processors.insert (new_processors.end(), _monitor_send);
-                                       break;
-                               }
-                               _monitor_send->set_can_pan (true);
+                       case PFLFromAfterProcessors:
+                               new_processors.insert (amp, _monitor_send);
                                break;
                        }
-               }  else {
-                       new_processors.insert (new_processors.end(), _monitor_send);
                        _monitor_send->set_can_pan (false);
+                       break;
+               case AfterFaderListen:
+                       switch (Config->get_afl_position ()) {
+                       case AFLFromBeforeProcessors:
+                               new_processors.insert (after_amp, _monitor_send);
+                               break;
+                       case AFLFromAfterProcessors:
+                               new_processors.insert (new_processors.end(), _monitor_send);
+                               break;
+                       }
+                       _monitor_send->set_can_pan (true);
+                       break;
                }
        }