move ever close to working master/slave logic, this time with audio testing
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 21 Apr 2016 03:22:29 +0000 (23:22 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 31 May 2016 19:30:41 +0000 (15:30 -0400)
18 files changed:
gtk2_ardour/route_ui.cc
gtk2_ardour/vca_master_strip.cc
libs/ardour/ardour/mute_control.h
libs/ardour/ardour/mute_master.h
libs/ardour/ardour/route.h
libs/ardour/ardour/slavable_automation_control.h
libs/ardour/ardour/solo_control.h
libs/ardour/ardour/stripable.h
libs/ardour/ardour/vca.h
libs/ardour/midi_track.cc
libs/ardour/mute_control.cc
libs/ardour/mute_master.cc
libs/ardour/route.cc
libs/ardour/session.cc
libs/ardour/slavable_automation_control.cc
libs/ardour/solo_control.cc
libs/ardour/vca.cc
libs/surfaces/faderport/faderport.cc

index 67de68aa698f4c048b23a165d5d72b7cb815fc03..bfd3ed10067c1dda7dd0531c725f7bed72314c58 100644 (file)
@@ -1228,7 +1228,7 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr<Route> r)
                if (r->mute_control()->muted_by_self ()) {
                        /* full mute */
                        return Gtkmm2ext::ExplicitActive;
-               } else if (r->muted_by_others_soloing () || r->muted_by_others()) {
+               } else if (r->muted_by_others_soloing () || r->muted_by_masters ()) {
                        /* this will reflect both solo mutes AND master mutes */
                        return Gtkmm2ext::ImplicitActive;
                } else {
@@ -1241,7 +1241,7 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr<Route> r)
                if (r->mute_control()->muted_by_self()) {
                        /* full mute */
                        return Gtkmm2ext::ExplicitActive;
-               } else if (r->muted_by_others()) {
+               } else if (r->muted_by_masters ()) {
                        /* this shows only master mutes, not mute-by-others-soloing */
                        return Gtkmm2ext::ImplicitActive;
                } else {
index 6f43228424b360191393f3f4d932bb7e0e2df13b..c1615edab787b53a55055da82d8001a03304a946 100644 (file)
@@ -237,10 +237,9 @@ VCAMasterStrip::set_solo_text ()
 void
 VCAMasterStrip::mute_changed ()
 {
-       std::cerr << "Mute changed for " << _vca->number() << std::endl;
        if (_vca->mute_control()->muted_by_self()) {
                mute_button.set_active_state (ExplicitActive);
-       } else if (_vca->mute_control()->muted_by_others()) {
+       } else if (_vca->mute_control()->muted_by_masters ()) {
                mute_button.set_active_state (ImplicitActive);
        } else {
                mute_button.set_active_state (Gtkmm2ext::Off);
index 431236692efb066808bfbc5c06868a80a26c6842..5332fd4fa714a2cf4b05cdb5c0c202fca755c24e 100644 (file)
@@ -56,9 +56,12 @@ class LIBARDOUR_API MuteControl : public SlavableAutomationControl
 
        bool muted () const;
        bool muted_by_self () const;
+       bool muted_by_masters () const;
+       bool muted_by_self_or_masters () const {
+               return muted_by_self() || muted_by_masters ();
+       }
 
        bool muted_by_others_soloing () const;
-       bool muted_by_others () const;
 
        void set_mute_points (MuteMaster::MutePoint);
        MuteMaster::MutePoint mute_points () const;
index 7a72e79607e25344e2046c463ba3b618e898d057..6f5999efb44c8b704a440061900400fe15d78b12 100644 (file)
@@ -52,7 +52,9 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful
 
        bool muted_by_self () const { return _muted_by_self && (_mute_point != MutePoint (0)); }
        bool muted_by_self_at (MutePoint mp) const { return _muted_by_self && (_mute_point & mp); }
-       bool muted_by_others_at (MutePoint mp) const;
+       bool muted_by_others_soloing_at (MutePoint mp) const;
+       bool muted_by_masters () const { return _muted_by_masters && (_mute_point != MutePoint (0)); }
+       bool muted_by_masters_at (MutePoint mp) const { return _muted_by_masters && (_mute_point & mp); }
 
        gain_t mute_gain_at (MutePoint) const;
 
@@ -69,7 +71,7 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful
        void set_soloed_by_others (bool yn) { _soloed_by_others = yn; }
        void set_solo_ignore (bool yn) { _solo_ignore = yn; }
 
-       void set_muted_by_others (bool);
+       void set_muted_by_masters (bool);
 
        PBD::Signal0<void> MutePointChanged;
 
@@ -83,7 +85,7 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful
        bool      _soloed_by_self;
        bool      _soloed_by_others;
        bool      _solo_ignore;
-       bool      _muted_by_others;
+       bool      _muted_by_masters;
 };
 
 } // namespace ARDOUR
index 9f66b95b6cb6574ec8f7fd08e6072b20f8f98b82..d93883b600361090fdb8fd570f6e74e68f2f168b 100644 (file)
@@ -460,7 +460,7 @@ public:
 
        bool can_be_muted_by_others () const { return !is_master(); }
        bool muted () const { return _mute_control->muted(); }
-       bool muted_by_others () const { return _mute_control->muted_by_others(); }
+       bool muted_by_masters () const { return _mute_control->muted_by_masters(); }
        bool muted_by_self () const { return _mute_control->muted_by_self(); }
        bool muted_by_others_soloing () const;
 
index 5e95cba22b64ec21f4124b0a683c82ea722ed994..98745e025d091a317d3461db5a5cc957e6800739 100644 (file)
@@ -45,6 +45,11 @@ class SlavableAutomationControl : public AutomationControl
                return get_masters_value_locked ();
        }
 
+       /* for toggled/boolean controls, returns a count of the number of
+          masters currently enabled. For other controls, returns zero.
+       */
+       int32_t   get_boolean_masters () const;
+
        std::vector<PBD::ID> masters () const;
 
        PBD::Signal0<void> MasterStatusChange;
@@ -96,7 +101,6 @@ class SlavableAutomationControl : public AutomationControl
        double get_value_locked() const;
        void   actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
        void   update_boolean_masters_records (boost::shared_ptr<AutomationControl>);
-       int32_t   get_boolean_masters () const;
 
        virtual void   master_changed (bool from_self, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl>);
        virtual void   recompute_masters_ratios (double val) { /* do nothing by default */}
index 78ce7c56be5c25c72c568177f0559c0573d5fd75..bcb01344a4f2c86bebbf2278509a148e7bfafe81 100644 (file)
@@ -74,6 +74,14 @@ class LIBARDOUR_API SoloControl : public SlavableAutomationControl
        }
        bool soloed() const { return self_soloed() || soloed_by_others(); }
 
+       /* The session object needs to respond to solo
+          changes, but to do so accurately it needs to know if we transition
+          into or out of solo. The normal Changed signal doesn't make that
+          possible.
+       */
+
+       int32_t transitioned_into_solo () const { return _transition_into_solo; }
+
        void clear_all_solo_state ();
 
        int set_state (XMLNode const&, int);
@@ -86,11 +94,12 @@ class LIBARDOUR_API SoloControl : public SlavableAutomationControl
        void post_add_master (boost::shared_ptr<AutomationControl>);
 
   private:
-       Soloable&      _soloable;
-       Muteable&      _muteable;
-       bool           _self_solo;
-       uint32_t       _soloed_by_others_upstream;
-       uint32_t       _soloed_by_others_downstream;
+       Soloable& _soloable;
+       Muteable& _muteable;
+       bool      _self_solo;
+       uint32_t  _soloed_by_others_upstream;
+       uint32_t  _soloed_by_others_downstream;
+       int32_t   _transition_into_solo;
 
        void set_self_solo (bool yn);
        void set_mute_master_solo ();
index f68cb07b91d967a40636b024221c328d8ea1e2e3..cee6075a51d03f1cb8e1e7bf51b4bebdbdd92e06 100644 (file)
@@ -140,7 +140,6 @@ class Stripable : public SessionObject {
        virtual boost::shared_ptr<AutomationControl> master_send_enable_controllable () const = 0;
 
        virtual bool muted_by_others_soloing () const = 0;
-       virtual bool muted_by_others () const = 0;
 };
 
 
index 1b7aa8fed8d7b02019bc6c3806a8334d92c2a554..38fba07bc7da8068398a254f829ed0b0912d14ed 100644 (file)
@@ -57,10 +57,8 @@ class LIBARDOUR_API VCA : public Stripable, public Soloable, public Muteable, pu
        bool can_solo() const { return true; }
        bool is_safe () const { return false; }
 
-       bool muted () const;
        bool can_be_muted_by_others () const { return true; }
        bool muted_by_others_soloing() const { return false; }
-       bool muted_by_others() const { return false; }
 
        static std::string default_name_template ();
        static int next_vca_number ();
index 7c1b3a9399cc99a0b0be44a54b20a3ddafef7a79..f078f1afc2a64430036a6fde60a5266a0846ec6a 100644 (file)
@@ -920,7 +920,7 @@ MidiTrack::act_on_mute ()
                return;
        }
 
-       if (muted() || _mute_master->muted_by_others_at(MuteMaster::AllPoints)) {
+       if (muted() || _mute_master->muted_by_others_soloing_at (MuteMaster::AllPoints)) {
                /* only send messages for channels we are using */
 
                uint16_t mask = _playback_filter.get_channel_mask();
@@ -947,7 +947,7 @@ void
 MidiTrack::monitoring_changed (bool self, Controllable::GroupControlDisposition gcd)
 {
        Track::monitoring_changed (self, gcd);
-       
+
        /* monitoring state changed, so flush out any on notes at the
         * port level.
         */
index 26280d5cd5c9ebc0787ccc247a06cd72f48f52d3..d44189e36b9223b4ef8f757f669959bb1be12b69 100644 (file)
@@ -51,6 +51,7 @@ MuteControl::post_add_master (boost::shared_ptr<AutomationControl> m)
                 */
 
                if (!muted_by_self() && !get_boolean_masters()) {
+                       _muteable.mute_master()->set_muted_by_masters (true);
                        Changed (false, Controllable::NoGroup);
                }
        }
@@ -61,7 +62,7 @@ MuteControl::pre_remove_master (boost::shared_ptr<AutomationControl> m)
 {
        if (!m) {
                /* null control ptr means we're removing all masters */
-               _muteable.mute_master()->set_muted_by_others (false);
+               _muteable.mute_master()->set_muted_by_masters (false);
                /* Changed will be emitted in SlavableAutomationControl::clear_masters() */
                return;
        }
@@ -92,16 +93,18 @@ void
 MuteControl::master_changed (bool self_change, Controllable::GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
 {
        bool send_signal = false;
-       const double changed_master_value = m->get_value();
        boost::shared_ptr<MuteControl> mc = boost::dynamic_pointer_cast<MuteControl> (m);
 
-       if (changed_master_value) {
+       if (m->get_value()) {
                /* this master is now enabled */
                if (!muted_by_self() && get_boolean_masters() == 0) {
+                       _muteable.mute_master()->set_muted_by_masters (true);
                        send_signal = true;
                }
        } else {
+               /* this master is disabled and there was only 1 enabled before */
                if (!muted_by_self() && get_boolean_masters() == 1) {
+                       _muteable.mute_master()->set_muted_by_masters (false);
                        send_signal = true;
                }
        }
@@ -117,8 +120,7 @@ double
 MuteControl::get_value () const
 {
        if (slaved ()) {
-               Glib::Threads::RWLock::ReaderLock lm (master_lock);
-               return get_masters_value_locked ();
+               return get_masters_value ();
        }
 
        if (_list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback()) {
@@ -149,7 +151,11 @@ MuteControl::mute_points () const
 bool
 MuteControl::muted () const
 {
-       return muted_by_self() || muted_by_others();
+       /* have to get (self-muted) value from somewhere. could be our own
+          Control, or the Muteable that we sort-of proxy for. Since this
+          method is called by ::get_value(), use the latter to avoid recursion.
+       */
+       return _muteable.mute_master()->muted_by_self() || get_masters_value ();
 }
 
 bool
@@ -159,7 +165,8 @@ MuteControl::muted_by_self () const
 }
 
 bool
-MuteControl::muted_by_others () const
+MuteControl::muted_by_masters () const
 {
        return get_masters_value ();
 }
+
index 89691fc058da14e34d6eb98704634c4503b08bec..32f50bd573ffed4e33f8e117e130c0c00f575cad 100644 (file)
@@ -43,7 +43,7 @@ MuteMaster::MuteMaster (Session& s, const std::string&)
         , _soloed_by_self (false)
         , _soloed_by_others (false)
         , _solo_ignore (false)
-       , _muted_by_others (0)
+       , _muted_by_masters (0)
 {
 
        if (Config->get_mute_affects_pre_fader ()) {
@@ -89,22 +89,22 @@ MuteMaster::mute_gain_at (MutePoint mp) const
         if (Config->get_solo_mute_override()) {
                 if (_soloed_by_self) {
                         gain = GAIN_COEFF_UNITY;
-                } else if (muted_by_self_at (mp)) {
+                } else if (muted_by_self_at (mp) || muted_by_masters_at (mp)) {
                         gain = GAIN_COEFF_ZERO;
                 } else {
-                        if (muted_by_others_at (mp) && !_soloed_by_others) {
+                       if (!_soloed_by_others && muted_by_others_soloing_at (mp)) {
                                 gain = Config->get_solo_mute_gain ();
                         } else {
                                 gain = GAIN_COEFF_UNITY;
                         }
                 }
         } else {
-                if (muted_by_self_at (mp)) {
+               if (muted_by_self_at (mp) || muted_by_masters_at (mp)) {
                         gain = GAIN_COEFF_ZERO;
                 } else if (_soloed_by_self || _soloed_by_others) {
                         gain = GAIN_COEFF_UNITY;
                 } else {
-                        if (muted_by_others_at (mp)) {
+                        if (muted_by_others_soloing_at (mp)) {
                                 gain = Config->get_solo_mute_gain ();
                         } else {
                                 gain = GAIN_COEFF_UNITY;
@@ -164,14 +164,16 @@ MuteMaster::get_state()
 }
 
 bool
-MuteMaster::muted_by_others_at (MutePoint mp) const
+MuteMaster::muted_by_others_soloing_at (MutePoint mp) const
 {
-       return (!_solo_ignore && (_muted_by_others || _session.soloing()) && (_mute_point & mp));
+       /* note: this is currently called with the assumption that the owner is
+          not soloed. it does not test for this condition.
+       */
+       return (!_solo_ignore && _session.soloing()) && (_mute_point & mp);
 }
 
 void
-MuteMaster::set_muted_by_others (bool yn)
+MuteMaster::set_muted_by_masters (bool yn)
 {
-       _muted_by_others = yn;
-       std::cerr << this << " set muted by others to " << yn << std::endl;
+       _muted_by_masters = yn;
 }
index 7da7e42abf6953ea43df120a2a489f2a14b8055b..06053789591615aec4f261055d8b53706e2cc925 100644 (file)
@@ -5485,8 +5485,6 @@ Route::muted_by_others_soloing () const
                return false;
        }
 
-       /* XXX something needed here re: mute-overrides-solo */
-
        return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
 }
 
index e42efae882b49245c53a0c5299321db61436fb69..6c0e341a304571611b377cd480e35d592a7f9c2f 100644 (file)
@@ -3406,7 +3406,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                boost::weak_ptr<Route> wpr (*x);
                boost::shared_ptr<Route> r (*x);
 
-               r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
+               r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2,wpr));
                r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
                r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
 
@@ -3772,9 +3772,9 @@ Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
 }
 
 void
-Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDisposition group_override,  boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlDisposition group_override,  boost::weak_ptr<Route> wpr)
 {
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_changed));
 
        boost::shared_ptr<Route> route (wpr.lock());
 
@@ -3787,20 +3787,27 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
                return;
        }
 
-       if (!self_solo_change) {
-               // session doesn't care about changes to soloed-by-others
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: self %2 masters %3 transition %4\n", route->name(), route->self_soloed(), route->solo_control()->get_masters_value(), route->solo_control()->transitioned_into_solo()));
+
+       if (route->solo_control()->transitioned_into_solo() == 0) {
+               /* route solo changed by upstream/downstream; not interesting
+                  to Session.
+               */
+               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 not self-soloed nor soloed by master (%2), ignoring\n", route->name(), route->solo_control()->get_masters_value()));
                return;
        }
 
-       boost::shared_ptr<RouteList> r = routes.reader ();
-       int32_t delta;
-
-       if (route->self_soloed()) {
-               delta = 1;
-       } else {
-               delta = -1;
+       if (route->solo_control()->transitioned_into_solo() == 0) {
+               /* reason for being soloed changed (e.g. master went away, we
+                * took over the master state), but actual status did
+                * not. nothing to do.
+                */
+               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: solo change was change in reason, not status\n", route->name()));
        }
 
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       int32_t delta = route->solo_control()->transitioned_into_solo ();
+
        /* the route may be a member of a group that has shared-solo
         * semantics. If so, then all members of that group should follow the
         * solo of the changed route. But ... this is optional, controlled by a
@@ -3907,11 +3914,11 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
                           sends are involved.
                        */
                        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 feeds %2 via sends only %3 sboD %4 sboU %5\n",
-                                                                 route->name(),
-                                                                 (*i)->name(),
-                                                                 via_sends_only,
-                                                                 route->soloed_by_others_downstream(),
-                                                                 route->soloed_by_others_upstream()));
+                                                                 route->name(),
+                                                                 (*i)->name(),
+                                                                 via_sends_only,
+                                                                 route->soloed_by_others_downstream(),
+                                                                 route->soloed_by_others_upstream()));
                        if (!via_sends_only) {
                                //NB. Triggers Invert Push, which handles soloed by downstream
                                DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta));
@@ -3964,13 +3971,13 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->can_solo()) {
                        if (Config->get_solo_control_is_listen_control()) {
-                               if ((*i)->self_soloed()) {
+                               if ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value()) {
                                        listeners++;
                                        something_listening = true;
                                }
                        } else {
                                (*i)->set_listen (false);
-                               if ((*i)->can_solo() && (*i)->self_soloed()) {
+                               if ((*i)->can_solo() && ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value())) {
                                        something_soloed = true;
                                }
                        }
index 78a1cc192d87ca11b619cf8485fa930f2dca6ae4..03662c1a26767fecfae7310ab2f123dd0de8dbed 100644 (file)
@@ -199,7 +199,7 @@ SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<Aut
                           really) which have more than a simple scalar
                           value. For example, the master may be a mute control
                           which can be muted_by_self() and/or
-                          muted_by_others(). When either of those two
+                          muted_by_masters(). When either of those two
                           conditions changes, Changed() will be emitted, even
                           though ::get_value() will return the same value each
                           time (1.0 if either are true, 0.0 if neither is).
index b9346406a5e4a63655d0c9b5458952e4ed818cc9..8514da36ecc1ac31f654764d87b6bc3ca9c754c0 100644 (file)
@@ -36,6 +36,7 @@ SoloControl::SoloControl (Session& session, std::string const & name, Soloable&
        , _self_solo (false)
        , _soloed_by_others_upstream (0)
        , _soloed_by_others_downstream (0)
+       , _transition_into_solo (false)
 {
        _list->set_interpolation (Evoral::ControlList::Discrete);
        /* solo changes must be synchronized by the process cycle */
@@ -48,17 +49,29 @@ SoloControl::set_self_solo (bool yn)
        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn));
        _self_solo = yn;
        set_mute_master_solo ();
+
+       _transition_into_solo = 0;
+
+       if (yn) {
+               if (get_masters_value() == 0) {
+                       _transition_into_solo = 1;
+               }
+       } else {
+               if (get_masters_value() == 0) {
+                       _transition_into_solo = -1;
+               }
+       }
 }
 
 void
 SoloControl::set_mute_master_solo ()
 {
-       _muteable.mute_master()->set_soloed_by_self (self_soloed());
+       _muteable.mute_master()->set_soloed_by_self (self_soloed() || get_masters_value());
 
        if (Config->get_solo_control_is_listen_control()) {
                _muteable.mute_master()->set_soloed_by_others (false);
        } else {
-               _muteable.mute_master()->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
+               _muteable.mute_master()->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream() || get_masters_value());
        }
 }
 
@@ -85,6 +98,7 @@ SoloControl::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 ();
+       _transition_into_solo = 0;
        Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
@@ -139,6 +153,7 @@ SoloControl::mod_solo_by_others_upstream (int32_t delta)
        }
 
        set_mute_master_solo ();
+       _transition_into_solo = 0;
        Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
 }
 
@@ -194,7 +209,7 @@ SoloControl::clear_all_solo_state ()
        _soloed_by_others_downstream = 0;
 
        set_self_solo (false);
-
+       _transition_into_solo = 0; /* Session does not need to propagate */
        Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
 }
 
@@ -239,15 +254,18 @@ void
 SoloControl::master_changed (bool /*from self*/, GroupControlDisposition, boost::shared_ptr<AutomationControl> m)
 {
        bool send_signal = false;
-       const double changed_master_value = m->get_value();
 
-       if (changed_master_value) {
+       _transition_into_solo = 0;
+
+       if (m->get_value()) {
                /* this master is now enabled */
                if (!self_soloed() && get_boolean_masters() == 0) {
                        send_signal = true;
+                       _transition_into_solo = 1;
                }
        } else {
                if (!self_soloed() && get_boolean_masters() == 1) {
+                       _transition_into_solo = -1;
                        send_signal = true;
                }
        }
@@ -255,8 +273,10 @@ SoloControl::master_changed (bool /*from self*/, GroupControlDisposition, boost:
        update_boolean_masters_records (m);
 
        if (send_signal) {
-               Changed (false, Controllable::NoGroup);
+               set_mute_master_solo ();
+               Changed (false, Controllable::UseGroup);
        }
+
 }
 
 void
@@ -271,6 +291,7 @@ SoloControl::post_add_master (boost::shared_ptr<AutomationControl> m)
                 */
 
                if (!self_soloed() && !get_boolean_masters()) {
+                       _transition_into_solo = 1;
                        Changed (false, Controllable::NoGroup);
                }
        }
@@ -289,7 +310,21 @@ SoloControl::pre_remove_master (boost::shared_ptr<AutomationControl> m)
 
        if (m->get_value()) {
                if (!self_soloed() && (get_boolean_masters() == 1)) {
-                       Changed (false, Controllable::NoGroup);
+                       /* we're not self-soloed, this master is, and we're
+                          removing
+                          it. SlavableAutomationControl::remove_master() will
+                          ensure that we reset our own value after actually
+                          removing the master, so that our state does not
+                          change (this is a precondition of the
+                          SlavableAutomationControl API). This will emit
+                          Changed(), and we need to make sure that any
+                          listener knows that there has been no transition.
+                       */
+                       _transition_into_solo = 0;
+               } else {
+                       _transition_into_solo = 1;
                }
+       } else {
+               _transition_into_solo = 0;
        }
 }
index edad50ebdbc311990681e05e63fd984174d07264..f746da8ff38f297d06ebfd582b419494a3422a80 100644 (file)
@@ -146,5 +146,3 @@ VCA::set_state (XMLNode const& node, int version)
 
        return 0;
 }
-
-
index 1df2fc48651ae2bb56d96f14054bacd2a107ce13..1cc47b52e1ba7c3815f8069b96fb55259131e8ae 100644 (file)
@@ -1227,7 +1227,7 @@ FaderPort::map_mute ()
                if (_current_route->muted()) {
                        stop_blinking (Mute);
                        get_button (Mute).set_led_state (_output_port, true);
-               } else if (_current_route->mute_control()->muted_by_others()) {
+               } else if (_current_route->muted_by_others_soloing () || _current_route->muted_by_masters()) {
                        start_blinking (Mute);
                } else {
                        stop_blinking (Mute);