perhaps, just possibly, a working solo model. needs to be fixed so that connections...
authorPaul Davis <paul@linuxaudiosystems.com>
Sat, 1 May 2010 15:09:19 +0000 (15:09 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Sat, 1 May 2010 15:09:19 +0000 (15:09 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@7033 d708f5d6-7413-0410-9779-e7cbd77b26cf

13 files changed:
gtk2_ardour/route_ui.cc
libs/ardour/ardour/io.h
libs/ardour/ardour/mute_master.h
libs/ardour/ardour/port.h
libs/ardour/ardour/route.h
libs/ardour/ardour/session.h
libs/ardour/ardour/types.h
libs/ardour/delivery.cc
libs/ardour/io.cc
libs/ardour/mute_master.cc
libs/ardour/port.cc
libs/ardour/route.cc
libs/ardour/session.cc

index 010f0714b90333e4b32724f823264a842498ae4d..e6c8fbf57430cf883a30abdfc4481f73e3888fa0 100644 (file)
@@ -838,7 +838,7 @@ RouteUI::mute_visual_state (Session* s, boost::shared_ptr<Route> r)
                if (r->self_muted ()) {
                        /* full mute */
                        return 2;
-               } else if (r->muted_by_others()) {
+               } else if (r->muted_by_others() || r->path_muted_by_others()) {
                        return 1;
                } else {
                        /* no mute at all */
index a7ba3a23a9c3acd30b9becf166306bcb43b4cd29..72815c17ccc756eb5fb5e1777d0c187010b58d78 100644 (file)
@@ -108,7 +108,8 @@ class IO : public SessionObject, public Latent
        int disconnect (void *src);
        bool connected_to (boost::shared_ptr<const IO>) const;
         bool connected () const;
-
+        bool physically_connected () const;
+        
        nframes_t signal_latency() const { return _own_latency; }
        nframes_t latency() const;
        void      set_port_latency (nframes_t);
index 4ddb7075dab00aed9ccda6098860c1bd790a0477..a0207f9817e83180354b6b28afd7f8ee659500bc 100644 (file)
 #include "pbd/stateful.h"
 #include <string>
 
+#include "ardour/session_handle.h"
+
 namespace ARDOUR {
 
 class Session;
 
-class MuteMaster : public PBD::Stateful
+class MuteMaster : public SessionHandleRef, public PBD::Stateful
 {
   public:
        enum MutePoint {
@@ -69,7 +71,7 @@ class MuteMaster : public PBD::Stateful
         void set_mute_points (MutePoint);
         MutePoint mute_points() const { return _mute_point; }
 
-        void set_solo_level (int32_t);
+        void set_solo_level (SoloLevel);
 
        PBD::Signal0<void> MutePointChanged;
 
@@ -80,7 +82,7 @@ class MuteMaster : public PBD::Stateful
        volatile MutePoint _mute_point;
         volatile bool      _self_muted;
         volatile uint32_t  _muted_by_others;
-        volatile int32_t   _solo_level;
+        volatile SoloLevel _solo_level;
 };
 
 } // namespace ARDOUR
index ce4b88f8f2c37f9a451fa3780b70ef9b447a2636..cc37bf082139dacb8c483a0f229b4c02a7250b4f 100644 (file)
@@ -116,6 +116,8 @@ public:
        }
        virtual void transport_stopped () {}
 
+        bool physically_connected () const;
+
        static void set_engine (AudioEngine *);
 
        PBD::Signal1<void,bool> MonitorInputChanged;
@@ -128,7 +130,7 @@ protected:
 
        static nframes_t _port_offset;
        static nframes_t _buffer_size;
-
+        
        static AudioEngine* _engine; ///< the AudioEngine
 
 private:
index 5efb9c111bc08f4a513eb969436f9c1466b0b767..b4a392c23b4ee22d78ec3b60856f1232bf113c75 100644 (file)
@@ -129,6 +129,9 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
        bool self_muted () const;
        bool muted_by_others () const;
 
+        bool path_muted_by_others() const { return _path_muted_by_others > 0; }
+        void mod_path_muted_by_others (int delta);
+
        void set_mute (bool yn, void* src);
         void mod_muted_by_others (int delta);
 
@@ -138,7 +141,9 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
        void set_solo (bool yn, void *src);
        bool soloed () const { return self_soloed () || soloed_by_others (); }
 
-       bool soloed_by_others () const { return !_solo_isolated && _soloed_by_others; }
+        bool soloed_by_others () const { return _soloed_by_others_upstream||_soloed_by_others_downstream; }
+       bool soloed_by_others_upstream () const { return _soloed_by_others_upstream; }
+       bool soloed_by_others_downstream () const { return _soloed_by_others_downstream; }
        bool self_soloed () const { return _self_solo; }
        
        void set_solo_isolated (bool yn, void *src);
@@ -372,8 +377,15 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
   protected:
        friend class Session;
 
+        void set_graph_level (int32_t);
+        int32_t graph_level() const { return _graph_level; } 
+        void check_physical_connections ();
+        // this functions may ONLY be called during a route resort
+        bool physically_connected () const { return _physically_connected; }
+
        void catch_up_on_solo_mute_override ();
-       void mod_solo_by_others (int32_t);
+       void mod_solo_by_others_upstream (int32_t);
+       void mod_solo_by_others_downstream (int32_t);
        bool has_external_redirects() const;
        void curve_reallocate ();
        void just_meter_input (sframes_t start_frame, sframes_t end_frame, nframes_t nframes);
@@ -411,7 +423,8 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
        MeterPoint     _meter_point;
        uint32_t       _phase_invert;
        bool           _self_solo;
-       uint32_t       _soloed_by_others;
+       uint32_t       _soloed_by_others_upstream;
+       uint32_t       _soloed_by_others_downstream;
        uint32_t       _solo_isolated;
 
        bool           _denormal_protection;
@@ -424,9 +437,12 @@ class Route : public SessionObject, public AutomatableControls, public RouteGrou
        boost::shared_ptr<MuteControllable> _mute_control;
        boost::shared_ptr<MuteMaster> _mute_master;
        MuteMaster::MutePoint _mute_points;
-
+        volatile uint32_t     _path_muted_by_others;
+    
        std::string    _comment;
        bool           _have_internal_generator;
+        bool           _physically_connected; // valid ONLY during a route resort
+        int32_t        _graph_level;
        bool           _solo_safe;
        DataType       _default_type;
         FedBy          _fed_by;
index 014b49b318a224725047cb3285474a76ba536414..b3997451875d6e33bdc5c5578dc49517d9f68671 100644 (file)
@@ -415,6 +415,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
        void   remove_route (boost::shared_ptr<Route>);
        void   resort_routes ();
        void   resort_routes_using (boost::shared_ptr<RouteList>);
+        void   find_route_levels (boost::shared_ptr<RouteList>);
 
        void   set_remote_control_ids();
 
index 25a6ea4c98d7a29b9e4962b5b8bee09cb777fe6f..2bd08cbcdfb27f2900f46dc25aa72cd12b6ac4a1 100644 (file)
@@ -306,6 +306,13 @@ namespace ARDOUR {
                PreFaderListen
        };
 
+        enum SoloLevel {
+                NotSoloed,
+                DownstreamSoloed,
+                UpstreamSoloed,
+                SelfSoloed
+        };
+
        enum AutoConnectOption {
                ManualConnect = 0x0,
                AutoConnectPhysical = 0x1,
index e2928c1f5be67dfb79bc92e5b7a95e72db30aca3..5861e5ea2c70d3c0858b05c0e609bda92b45da7c 100644 (file)
@@ -500,6 +500,7 @@ Delivery::target_gain ()
                 break;
         }
 
+        // cerr << name() << ' ';
         desired_gain = _mute_master->mute_gain_at (mp);
         
         if (_role == Listen && _session.monitor_out() && !_session.listening()) {
index a41003cf8901ba8e285463466ef83b75d2fa121e..8b52bfaea5efcf6e8beb017745c3890f7484e9e5 100644 (file)
@@ -1553,3 +1553,15 @@ IO::port_by_name (const std::string& str) const
 
        return 0;
 }
+
+bool
+IO::physically_connected () const
+{
+       for (PortSet::const_iterator i = _ports.begin(); i != _ports.end(); ++i) {
+                if (i->physically_connected()) {
+                        return true;
+                }
+        }
+
+        return false;
+}
index 0182e205d03588d5c742d8e6c6b86d70af6adfab..df9a66ef7f60d9642c1eac0f114e1c4c8c08ac26 100644 (file)
@@ -23,7 +23,7 @@
 
 #include "ardour/types.h"
 #include "ardour/mute_master.h"
-#include "ardour/rc_configuration.h"
+#include "ardour/session.h"
 
 #include "i18n.h"
 
@@ -35,8 +35,9 @@ const MuteMaster::MutePoint MuteMaster::AllPoints = MutePoint (MuteMaster::PreFa
                                                               MuteMaster::Listen|
                                                               MuteMaster::Main);
 
-MuteMaster::MuteMaster (Session&, const std::string&)
-       : _mute_point (AllPoints)
+MuteMaster::MuteMaster (Session& s, const std::string&)
+       : SessionHandleRef (s) 
+        , _mute_point (AllPoints)
         , _self_muted (false)
         , _muted_by_others (0)
 {
@@ -83,7 +84,7 @@ MuteMaster::mod_muted_by_others (int32_t delta)
 }
 
 void
-MuteMaster::set_solo_level (int32_t l)
+MuteMaster::set_solo_level (SoloLevel l)
 {
         _solo_level = l;
 }
@@ -92,14 +93,16 @@ gain_t
 MuteMaster::mute_gain_at (MutePoint mp) const
 {
         gain_t gain;
-        int32_t l = _solo_level;
+        const SoloLevel l = _solo_level;
 
+        // cerr << "solo level = " << _solo_level << " selfmuted " <<  self_muted_at (mp) << " omute " << muted_by_others_at (mp) << endl;
+        
         if (Config->get_solo_mute_override()) {
-                if (l == 2) { // self-soloed
+                if ((l == SelfSoloed) || (l == DownstreamSoloed)) { 
                         gain = 1.0;
                 } else if (self_muted_at (mp)) { // self-muted 
                         gain = Config->get_solo_mute_gain ();
-                } else if (l == 1) { // soloed by others
+                } else if (l == UpstreamSoloed) {
                         gain = 1.0;
                 } else if (muted_by_others_at (mp)) { // muted by others
                         gain = Config->get_solo_mute_gain ();
@@ -109,17 +112,19 @@ MuteMaster::mute_gain_at (MutePoint mp) const
         } else {
                 if (self_muted_at (mp)) { // self-muted 
                         gain = Config->get_solo_mute_gain ();
-                } else if (l == 2) { // self-soloed
+                } else if ((l == SelfSoloed) || (l == DownstreamSoloed)) {
                         gain = 1.0;
                 } else if (muted_by_others_at (mp)) { // muted by others
                         gain = Config->get_solo_mute_gain ();
-                } else if (l == 1) { // soloed by others
+                } else if (l == UpstreamSoloed) { // soloed by others
                         gain = 1.0;
                 } else {
                         gain = 1.0;
                 }
         }
 
+        // cerr << "\tgain = " << gain << endl;
+        
         return gain;
 }
 
@@ -130,7 +135,7 @@ MuteMaster::set_mute_points (const std::string& mute_point)
 
        _mute_point = (MutePoint) string_2_enum (mute_point, _mute_point);
         cerr << "Mute point set from string, now " << _mute_point << endl;
-
+        
         if (old != _mute_point) {
                 MutePointChanged(); /* EMIT SIGNAL */
         }
@@ -152,7 +157,7 @@ MuteMaster::set_state (const XMLNode& node, int /*version*/)
        const XMLProperty* prop;
 
        if ((prop = node.property ("mute-point")) != 0) {
-               //_mute_point = (MutePoint) string_2_enum (prop->value(), _mute_point);
+               _mute_point = (MutePoint) string_2_enum (prop->value(), _mute_point);
                 cerr << "Mute point set from STATE string, now " << _mute_point << endl;
        }
 
index 5897015b273baed20c172da4b1bb1decbf92933d..9cbdd14172d48519776b3efe2b2d6bc22c5abfe8 100644 (file)
@@ -99,7 +99,8 @@ Port::get_connections (std::vector<std::string> & c) const
                        c.push_back (jc[i]);
                        ++n;
                }
-               free (jc);
+
+               jack_free (jc);
        }
 
        return n;
@@ -148,7 +149,7 @@ Port::disconnect (std::string const & other)
                _connections.erase (other);
        }
 
-return r;
+        return r;
 }
 
 
@@ -290,3 +291,24 @@ Port::set_latency (nframes_t n)
        jack_port_set_latency (_jack_port, n);
 }
 
+bool
+Port::physically_connected () const
+{
+       const char** jc = jack_port_get_connections (_jack_port);
+
+       if (jc) {
+               for (int i = 0; jc[i]; ++i) {
+
+                        jack_port_t* port = jack_port_by_name (_engine->jack(), jc[i]);
+                        
+                        if (port && (jack_port_flags (port) & JackPortIsPhysical)) {
+                                jack_free (jc);
+                                return true;
+                        }
+               }
+                
+               jack_free (jc);
+       }
+
+        return false;
+}
index 392ac7b0ca2eeef3868bbabb81843badae6ef641..ddb227c6bbbee511f7595c25dd474d60607e0bec 100644 (file)
@@ -80,7 +80,8 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
         , _meter_point (MeterPostFader)
         , _phase_invert (0)
         , _self_solo (false)
-        , _soloed_by_others (0)
+        , _soloed_by_others_upstream (0)
+        , _soloed_by_others_downstream (0)
         , _solo_isolated (0)
         , _denormal_protection (false)
         , _recordable (true)
@@ -90,7 +91,10 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, name))
         , _mute_points (MuteMaster::AllPoints)
+        , _path_muted_by_others (false)
         , _have_internal_generator (false)
+        , _physically_connected (false)
+        , _graph_level (-1)
         , _solo_safe (false)
        , _default_type (default_type)
         , _remote_control_id (0)
@@ -447,6 +451,10 @@ Route::process_output_buffers (BufferSet& bufs,
        Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
 
        if (rm.locked()) {
+                //cerr << name() << " upstream solo " << _soloed_by_others_upstream
+                // << " downstream solo " << _soloed_by_others_downstream
+                // << " self " << _self_solo
+                //<< endl;
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                        if (bufs.count() != (*i)->input_streams()) {
@@ -589,20 +597,41 @@ Route::set_self_solo (bool yn)
 }
 
 void
-Route::mod_solo_by_others (int32_t delta)
+Route::mod_solo_by_others_upstream (int32_t delta)
 {
         if (_solo_safe) {
                 return;
         }
 
        if (delta < 0) {
-               if (_soloed_by_others >= (uint32_t) abs (delta)) {
-                       _soloed_by_others += delta;
+               if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) {
+                       _soloed_by_others_upstream += delta;
                } else {
-                       _soloed_by_others = 0;
+                       _soloed_by_others_upstream = 0;
                }
        } else {
-               _soloed_by_others += delta;
+               _soloed_by_others_upstream += delta;
+       }
+
+        set_mute_master_solo ();
+        solo_changed (false, this);
+}
+
+void
+Route::mod_solo_by_others_downstream (int32_t delta)
+{
+        if (_solo_safe) {
+                return;
+        }
+
+       if (delta < 0) {
+               if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) {
+                       _soloed_by_others_downstream += delta;
+               } else {
+                       _soloed_by_others_downstream = 0;
+               }
+       } else {
+               _soloed_by_others_downstream += delta;
        }
 
         set_mute_master_solo ();
@@ -612,14 +641,16 @@ Route::mod_solo_by_others (int32_t delta)
 void
 Route::set_mute_master_solo ()
 {
-        int32_t level;
+        SoloLevel level;
 
         if (self_soloed()) {
-                level = 2;
-        } else if (soloed_by_others()) {
-                level = 1;
+                level = SelfSoloed;
+        } else if (soloed_by_others_upstream()) {
+                level = UpstreamSoloed;
+        } else if (soloed_by_others_downstream()) {
+                level = DownstreamSoloed;
         } else {
-                level = 0;
+                level = NotSoloed;
         }
 
         _mute_master->set_solo_level (level);
@@ -722,19 +753,31 @@ Route::muted_by_others() const
 void
 Route::mod_muted_by_others (int delta)
 {
-        bool old = muted ();
-
         if (_solo_isolated) {
                 return;
         }
 
+        bool old = muted ();
         _mute_master->mod_muted_by_others (delta);
-
         if (old != muted()) {
                 mute_changed (this);
         }
 }
 
+void
+Route::mod_path_muted_by_others (int32_t delta)
+{
+       if (delta < 0) {
+               if (_path_muted_by_others >= (uint32_t) abs (delta)) {
+                       _path_muted_by_others += delta;
+               } else {
+                       _path_muted_by_others = 0;
+               }
+       } else {
+               _path_muted_by_others += delta;
+       }
+}
+
 #if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
@@ -1683,8 +1726,10 @@ Route::state(bool full_state)
        }
        node->add_property ("order-keys", order_string);
        node->add_property ("self-solo", (_self_solo ? "yes" : "no"));
-       snprintf (buf, sizeof (buf), "%d", _soloed_by_others);
-       node->add_property ("soloed-by-others", buf);
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream);
+       node->add_property ("soloed-by-upstream", buf);
+       snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream);
+       node->add_property ("soloed-by-downstream", buf);
 
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
@@ -1782,9 +1827,14 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                set_self_solo (string_is_affirmative (prop->value()));
        }
 
-       if ((prop = node.property ("soloed-by-others")) != 0) {
-               _soloed_by_others = 0; // needed for mod_solo_by_others () to work
-               mod_solo_by_others (atoi (prop->value()));
+       if ((prop = node.property ("soloed-by-upstream")) != 0) {
+               _soloed_by_others_upstream = 0; // needed for mod_.... () to work
+               mod_solo_by_others_upstream (atoi (prop->value()));
+       }
+
+       if ((prop = node.property ("soloed-by-downstream")) != 0) {
+               _soloed_by_others_downstream = 0; // needed for mod_.... () to work
+               mod_solo_by_others_downstream (atoi (prop->value()));
        }
 
        if ((prop = node.property ("solo-isolated")) != 0) {
@@ -2531,6 +2581,12 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
        return false;
 }
 
+void
+Route::check_physical_connections ()
+{
+        _physically_connected = _output->physically_connected ();
+}
+
 void
 Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
@@ -3301,3 +3357,9 @@ Route::has_io_processor_named (const string& name)
         
         return false;
 }
+
+void
+Route::set_graph_level (int32_t l)
+{
+        _graph_level = l;
+}
index 4ba092dd91d724ae4b7774b5372a1ccc7efbb159..2da383b810ae3248bbc6b5dd183e6cddd1a7fb44 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
 #include <algorithm>
 #include <string>
 #include <vector>
@@ -1350,6 +1353,10 @@ Session::resort_routes_using (shared_ptr<RouteList> r)
 {
        RouteList::iterator i, j;
 
+       for (i = r->begin(); i != r->end(); ++i) {
+                (*i)->check_physical_connections ();
+        }
+
        for (i = r->begin(); i != r->end(); ++i) {
 
                (*i)->clear_fed_by ();
@@ -1381,15 +1388,102 @@ Session::resort_routes_using (shared_ptr<RouteList> r)
        RouteSorter cmp;
        r->sort (cmp);
 
+        find_route_levels (r);
+
 #ifndef NDEBUG
         DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", (*i)->name(), (*i)->order_key ("signal")));
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2 level %3\n", 
+                                                           (*i)->name(), (*i)->order_key ("signal"),
+                                                           (*i)->graph_level()));
        }
 #endif
 
 }
 
+void
+Session::find_route_levels (shared_ptr<RouteList> rl)
+{
+        uint32_t setcnt = 0;
+        uint32_t limit = rl->size();
+        RouteList last_level;
+        RouteList this_level;
+
+        for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
+                
+                /* find routes with direct physical connections,
+                   or routes with no connections at all. Mark them
+                   with "special" level values, and push them into
+                   the "last_level" set.
+                
+                   All other routes get marked with a graph level
+                   of -1, which indicates that it needs to be set.
+
+                */
+                
+                if ((*r)->physically_connected()) {
+                        last_level.push_back (*r);
+                        (*r)->set_graph_level (0);
+                        setcnt++;
+                } else if (!(*r)->output()->connected()) {
+                        last_level.push_back (*r);
+                        (*r)->set_graph_level (INT32_MAX/2);
+                        setcnt++;
+                } else {
+                        (*r)->set_graph_level (-1);
+                }
+        }
+
+        // until we've set the graph level for every route ... 
+
+        while (setcnt < limit) {
+
+                for (RouteList::reverse_iterator r = rl->rbegin(); r != rl->rend(); ++r) {
+
+                        int32_t l = INT32_MAX;
+                        bool found = false;
+
+                        if ((*r)->graph_level() != -1) {
+                                // we already have the graph level for this route
+                                continue;
+                        }
+
+                        /* check if this route (r) has a direction connection to anything in
+                           the set of routes we processed last time. On the first pass
+                           through this, last_level will contain routes with either
+                           no connections or direct "physical" connections. If there is
+                           at least 1 connection, store the lowest graph level of whatever
+                           r is connected to.
+                        */
+
+                        for (RouteList::iterator o = last_level.begin(); o != last_level.end(); ++o) {
+                                bool sends_only;
+                                if ((*r)->direct_feeds (*o, &sends_only)) {
+                                        if (!sends_only) {
+                                                l = min (l, (*o)->graph_level());
+                                                found = true;
+                                        }
+                                }
+                        }
+
+                        /* if we found any connections, then mark the graph level of r, and push
+                           it into the "this_level" set that will become "last_level" next time
+                           around the while() loop.
+                        */
+
+                        if (found) {
+                                (*r)->set_graph_level (l + 1);
+                                this_level.push_back (*r);
+                                setcnt++;
+                        }
+                }
+
+                last_level = this_level;
+                this_level.clear ();
+        }
+}
+
+
 /** Find the route name starting with \a base with the lowest \a id.
  *
  * Names are constructed like e.g. "Audio 3" for base="Audio" and id=3.
@@ -2185,13 +2279,14 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                bool via_sends_only;
-                bool in_signal_flow;
 
                if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
                        continue;
                } 
 
-                in_signal_flow = false;
+                if ((*i)->graph_level () == route->graph_level()) {
+                        (*i)->mod_muted_by_others (delta);                        
+                }
 
                 /* feed-backwards (other route to solo change route):
 
@@ -2204,8 +2299,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p
 
                 if ((*i)->feeds (route, &via_sends_only)) {
                        if (!via_sends_only) {
-                               (*i)->mod_solo_by_others (delta);
-                                in_signal_flow = true;
+                               (*i)->mod_solo_by_others_upstream (delta);
                        }
                } 
                 
@@ -2218,12 +2312,7 @@ Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_p
                  */
 
                 if (route->feeds (*i, &via_sends_only)) {
-                        (*i)->mod_solo_by_others (delta);
-                        in_signal_flow = true;
-                }
-                
-                if (!in_signal_flow) {
-                        (*i)->mod_muted_by_others (delta);
+                        (*i)->mod_solo_by_others_downstream (delta);
                 }
        }