PT import: Add new import_pt.cc session methods
[ardour.git] / libs / ardour / solo_control.cc
index 45ec5b90ec8bace7c72308e71aa0c4b02351e938..9d898493dc0b1b8e198785437543023cb12389c6 100644 (file)
@@ -21,7 +21,7 @@
 #include "ardour/session.h"
 #include "ardour/solo_control.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace std;
@@ -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,45 +49,32 @@ 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());
        }
 }
 
-void
-SoloControl::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
-{
-       if (_soloable.is_safe() || !_soloable.can_solo()) {
-               return;
-       }
-
-       bool master_soloed;
-
-       {
-               Glib::Threads::RWLock::ReaderLock lm (master_lock);
-               master_soloed = (bool) get_masters_value_locked ();
-       }
-
-       /* Master is considered equivalent to an upstream solo control, not
-        * direct control over self-soloed.
-        */
-
-       mod_solo_by_others_upstream (master_soloed ? 1 : -1);
-
-       /* no need to call AutomationControl::master_changed() since it just
-          emits Changed() which we already did in mod_solo_by_others_upstream()
-       */
-}
-
 void
 SoloControl::mod_solo_by_others_downstream (int32_t delta)
 {
@@ -110,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 */
 }
 
@@ -164,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 */
 }
 
@@ -180,69 +170,77 @@ SoloControl::actually_set_value (double val, PBD::Controllable::GroupControlDisp
           be retrieved by AutomationControl::get_value (), and emits Changed
        */
 
-       AutomationControl::actually_set_value (val, group_override);
-       _session.set_dirty ();
+       SlavableAutomationControl::actually_set_value (val, group_override);
 }
 
 double
 SoloControl::get_value () const
 {
        if (slaved()) {
-               Glib::Threads::RWLock::ReaderLock lm (master_lock);
-               return get_masters_value_locked () ? 1.0 : 0.0;
+               return self_soloed() || get_masters_value ();
        }
 
-       std::cerr << "solo control @ " << this << " list = " << _list << " as AL " << boost::dynamic_pointer_cast<AutomationList>(_list) << std::endl;
-
        if (_list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback()) {
                // Playing back automation, get the value from the list
                return AutomationControl::get_value();
        }
 
-       return self_soloed() ? 1.0 : 0.0;
+       return soloed();
 }
 
 void
 SoloControl::clear_all_solo_state ()
 {
-       // ideally this function will never do anything, it only exists to forestall Murphy
+       bool change = false;
 
-#ifndef NDEBUG
-       // these are really debug messages, but of possible interest.
        if (self_soloed()) {
-               PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name());
+               PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name()) << endmsg;
+               actually_set_value (0.0, Controllable::NoGroup);
+               change = true;
        }
-       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);
+
+       if (_soloed_by_others_upstream) {
+               PBD::info << string_compose (_("Cleared upstream solo: %1 up:%2\n"), name(), _soloed_by_others_upstream)
+                         << endmsg;
+               _soloed_by_others_upstream = 0;
+               change = true;
        }
-#endif
 
-       _soloed_by_others_upstream = 0;
-       _soloed_by_others_downstream = 0;
+       if (_soloed_by_others_downstream) {
+               PBD::info << string_compose (_("Cleared downstream solo: %1 down:%2\n"), name(), _soloed_by_others_downstream)
+                         << endmsg;
+               _soloed_by_others_downstream = 0;
+               change = true;
+       }
 
-       set_self_solo (false);
+       _transition_into_solo = 0; /* Session does not need to propagate */
 
-       Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
+       if (change) {
+               Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
+       }
 }
 
 int
-SoloControl::set_state (XMLNode const & node, int)
+SoloControl::set_state (XMLNode const & node, int version)
 {
-       XMLProperty const * prop;
+       if (SlavableAutomationControl::set_state(node, version)) {
+               return -1;
+       }
 
-       if ((prop = node.property ("self-solo")) != 0) {
-               set_self_solo (string_is_affirmative (prop->value()));
+       bool yn;
+       if (node.get_property ("self-solo", yn)) {
+               set_self_solo (yn);
        }
 
-       if ((prop = node.property ("soloed-by-upstream")) != 0) {
+       uint32_t val;
+       if (node.get_property ("soloed-by-upstream", val)) {
                _soloed_by_others_upstream = 0; // needed for mod_.... () to work
-               mod_solo_by_others_upstream (atoi (prop->value()));
+               mod_solo_by_others_upstream (val);
        }
 
-       if ((prop = node.property ("soloed-by-downstream")) != 0) {
+       if (node.get_property ("soloed-by-downstream", val)) {
                _soloed_by_others_downstream = 0; // needed for mod_.... () to work
-               mod_solo_by_others_downstream (atoi (prop->value()));
+               mod_solo_by_others_downstream (val);
        }
 
        return 0;
@@ -253,12 +251,104 @@ SoloControl::get_state ()
 {
        XMLNode& node (SlavableAutomationControl::get_state());
 
-       node.add_property (X_("self-solo"), _self_solo ? X_("yes") : X_("no"));
-       char buf[32];
-       snprintf (buf, sizeof(buf), "%d",  _soloed_by_others_upstream);
-       node.add_property (X_("soloed-by-upstream"), buf);
-       snprintf (buf, sizeof(buf), "%d",  _soloed_by_others_downstream);
-       node.add_property (X_("soloed-by-downstream"), buf);
+       node.set_property (X_("self-solo"), _self_solo);
+       node.set_property (X_("soloed-by-upstream"), _soloed_by_others_upstream);
+       node.set_property (X_("soloed-by-downstream"), _soloed_by_others_downstream);
 
        return node;
 }
+
+void
+SoloControl::master_changed (bool /*from self*/, GroupControlDisposition, boost::weak_ptr<AutomationControl> wm)
+{
+       boost::shared_ptr<AutomationControl> m = wm.lock ();
+       assert (m);
+       bool send_signal = false;
+
+       _transition_into_solo = 0;
+
+       /* Notice that we call get_boolean_masters() BEFORE we call
+        * update_boolean_masters_records(), in order to know what
+        * our master state was BEFORE it gets changed.
+        */
+
+
+       if (m->get_value()) {
+               /* this master is now enabled */
+               if (!self_soloed() && get_boolean_masters() == 0) {
+                       /* not self-soloed, wasn't soloed by masters before */
+                       send_signal = true;
+                       _transition_into_solo = 1;
+               }
+       } else {
+               if (!self_soloed() && get_boolean_masters() == 1) {
+                       /* not self-soloed, soloed by just 1 master before */
+                       _transition_into_solo = -1;
+                       send_signal = true;
+               }
+       }
+
+       update_boolean_masters_records (m);
+
+       if (send_signal) {
+               set_mute_master_solo ();
+               Changed (false, Controllable::UseGroup);
+       }
+
+}
+
+void
+SoloControl::post_add_master (boost::shared_ptr<AutomationControl> m)
+{
+       if (m->get_value()) {
+
+               /* boolean masters records are not updated until AFTER
+                * ::post_add_master() is called, so we can use them to check
+                * on whether any master was already enabled before the new
+                * one was added.
+                */
+
+               if (!self_soloed() && !get_boolean_masters()) {
+                       _transition_into_solo = 1;
+                       Changed (false, Controllable::NoGroup);
+               }
+       }
+}
+
+void
+SoloControl::pre_remove_master (boost::shared_ptr<AutomationControl> m)
+{
+       if (!m) {
+               /* null control ptr means we're removing all masters. Nothing
+                * to do. Changed will be emitted in
+                * SlavableAutomationControl::clear_masters()
+                */
+               return;
+       }
+
+       if (m->get_value()) {
+               if (!self_soloed() && (get_boolean_masters() == 1)) {
+                       /* 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;
+       }
+}
+
+bool
+SoloControl::can_solo () const
+{
+       return _soloable.can_solo ();
+}