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 {
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 {
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);
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;
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;
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;
bool _soloed_by_self;
bool _soloed_by_others;
bool _solo_ignore;
- bool _muted_by_others;
+ bool _muted_by_masters;
};
} // namespace ARDOUR
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;
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;
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 */}
}
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);
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 ();
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;
};
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 ();
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();
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.
*/
*/
if (!muted_by_self() && !get_boolean_masters()) {
+ _muteable.mute_master()->set_muted_by_masters (true);
Changed (false, Controllable::NoGroup);
}
}
{
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;
}
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;
}
}
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()) {
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
}
bool
-MuteControl::muted_by_others () const
+MuteControl::muted_by_masters () const
{
return get_masters_value ();
}
+
, _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 ()) {
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;
}
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;
}
return false;
}
- /* XXX something needed here re: mute-overrides-solo */
-
return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated();
}
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));
}
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());
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
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));
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;
}
}
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).
, _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 */
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());
}
}
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 */
}
}
set_mute_master_solo ();
+ _transition_into_solo = 0;
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
_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 */
}
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;
}
}
update_boolean_masters_records (m);
if (send_signal) {
- Changed (false, Controllable::NoGroup);
+ set_mute_master_solo ();
+ Changed (false, Controllable::UseGroup);
}
+
}
void
*/
if (!self_soloed() && !get_boolean_masters()) {
+ _transition_into_solo = 1;
Changed (false, Controllable::NoGroup);
}
}
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;
}
}
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);