move ever close to working master/slave logic, this time with audio testing

This commit is contained in:
Paul Davis 2016-04-20 23:22:29 -04:00
parent 202ddba9e2
commit 69250b64ea
18 changed files with 130 additions and 69 deletions

View 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 {

View 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);

View 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;

View 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

View 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;

View 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 */}

View 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 ();

View 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;
};

View 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 ();

View 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.
*/

View 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 ();
}

View 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;
}

View 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();
}

View 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;
}
}

View 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).

View 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;
}
}

View File

@ -146,5 +146,3 @@ VCA::set_state (XMLNode const& node, int version)
return 0;
}

View 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);