add implicit mute state to MuteMaster and use when a master of a mute control is enabled/disabled. Add AutomationControl::master_changed() as a virtual method to handle ... master value changes

This commit is contained in:
Paul Davis
2016-03-12 22:58:00 -05:00
parent 5531c83496
commit 194b213456
11 changed files with 150 additions and 178 deletions

View File

@@ -156,6 +156,7 @@ public:
Masters _masters;
PBD::ScopedConnectionList masters_connections;
virtual void master_changed (bool from_self, GroupControlDisposition gcd);
void master_going_away (boost::weak_ptr<AutomationControl>);
virtual void recompute_masters_ratios (double val) { /* do nothing by default */}
virtual double get_masters_value_locked () const;

View File

@@ -66,17 +66,21 @@ 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 mod_muted_by_others (int32_t delta);
bool muted_by_others () const { return _muted_by_others; }
PBD::Signal0<void> MutePointChanged;
XMLNode& get_state();
int set_state(const XMLNode&, int version);
private:
volatile MutePoint _mute_point;
volatile bool _muted_by_self;
volatile bool _soloed_by_self;
volatile bool _soloed_by_others;
volatile bool _solo_ignore;
MutePoint _mute_point;
bool _muted_by_self;
bool _soloed_by_self;
bool _soloed_by_others;
bool _solo_ignore;
int32_t _muted_by_others;
};
} // namespace ARDOUR

View File

@@ -160,7 +160,8 @@ public:
bool muted () const;
void set_mute (bool yn, PBD::Controllable::GroupControlDisposition);
bool muted_by_others() const;
bool muted_by_others_soloing () const;
bool muted_by_others () const;
/* controls use set_solo() to modify this route's solo state
*/
@@ -508,17 +509,57 @@ public:
};
class SoloControllable : public BooleanRouteAutomationControl {
public:
public:
SoloControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
double get_value () const;
private:
/* Export additional API so that objects that only get access
* to a Controllable/AutomationControl can do more fine-grained
* operations with respect to solo. Obviously, they would need
* to dynamic_cast<Route::SoloControllable> first.
*
* Solo state is not representable by a single scalar value,
* so this AutomationControl maps set_value() and get_value()
* to r->set_self_solo() and r->soloed() respectively. This
* means that the Controllable is technically asymmetric. It is
* possible to call ::set_value (0.0) to disable (self)solo,
* and then call ::get_value() and get a return of 1.0 because
* the Route owner is soloed by upstream/downstream.
*/
void set_self_solo (bool yn) {
boost::shared_ptr<Route> r(_route.lock()); if (r) r->set_self_solo (yn);
}
void mod_solo_by_others_upstream (int32_t delta) {
boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_upstream (delta);
}
void mod_solo_by_others_downstream (int32_t delta) {
boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_downstream (delta);
}
bool soloed_by_others () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others(); else return false;
}
bool soloed_by_others_upstream () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_upstream(); else return false;
}
bool soloed_by_others_downstream () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_downstream(); else return false;
}
bool self_soloed () const {
boost::shared_ptr<Route> r(_route.lock()); if (r) return r->self_soloed(); else return false;
}
protected:
void master_changed (bool, PBD::Controllable::GroupControlDisposition);
private:
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};
struct MuteControllable : public BooleanRouteAutomationControl {
public:
public:
MuteControllable (std::string name, boost::shared_ptr<Route>);
void set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void set_value_unchecked (double);
@@ -526,7 +567,10 @@ public:
/* Pretend to change value, but do not affect actual route mute. */
void set_superficial_value(bool muted);
private:
protected:
void master_changed (bool, PBD::Controllable::GroupControlDisposition);
private:
boost::weak_ptr<Route> _route;
void _set_value (double, PBD::Controllable::GroupControlDisposition group_override);
};

View File

@@ -1,53 +0,0 @@
/*
Copyright (C) 2016 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __libardour_session_solo_notifications_h__
#define __libardour_session_solo_notifications_h__
#include <boost/shared_ptr.hpp>
#include "pbd/controllable.h"
namespace ARDOUR {
class Route;
template<typename T>
class SessionSoloNotifications
{
public:
void solo_changed (bool self_solo_change, PBD::Controllable::GroupControlDisposition gcd, boost::shared_ptr<Route> route) {
static_cast<T*>(this)->_route_solo_changed (self_solo_change, gcd, route);
}
void listen_changed (PBD::Controllable::GroupControlDisposition gcd, boost::shared_ptr<Route> route) {
static_cast<T*>(this)->_route_listen_changed (gcd, route);
}
void mute_changed () {
static_cast<T*>(this)->_route_mute_changed ();
}
void solo_isolated_changed (boost::shared_ptr<Route> route) {
static_cast<T*>(this)->_route_solo_isolated_changed (route);
}
};
} /* namespace */
#endif /* __libardour_session_solo_notifications_h__ */

View File

@@ -46,11 +46,6 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en
XMLNode& get_state();
int set_state (XMLNode const&, int version);
void add_solo_target (boost::shared_ptr<Route>);
void remove_solo_target (boost::shared_ptr<Route>);
void add_mute_target (boost::shared_ptr<Route>);
void remove_mute_target (boost::shared_ptr<Route>);
bool soloed () const;
bool muted () const;
@@ -128,14 +123,6 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en
uint32_t _number;
RouteList solo_targets;
PBD::ScopedConnectionList solo_connections;
mutable Glib::Threads::RWLock solo_lock;
RouteList mute_targets;
PBD::ScopedConnectionList mute_connections;
mutable Glib::Threads::RWLock mute_lock;
boost::shared_ptr<GainControl> _gain_control;
boost::shared_ptr<VCASoloControllable> _solo_control;
boost::shared_ptr<VCAMuteControllable> _mute_control;

View File

@@ -119,8 +119,6 @@ AutomationControl::set_value (double value, PBD::Controllable::GroupControlDispo
Control::set_double (value, _session.transport_frame(), to_list);
cerr << "AC was set to " << value << endl;
Changed (true, gcd);
}
@@ -301,23 +299,39 @@ AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
*/
m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal2<void,bool,Controllable::GroupControlDisposition>::operator(), &Changed, false, _2));
m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&AutomationControl::master_changed, this, _1, _2));
}
new_value = get_value_locked ();
}
if (res.second) {
/* this will notify everyone that we're now slaved to the master */
MasterStatusChange (); /* EMIT SIGNAL */
}
if (new_value != current_value) {
/* force a call to to ::master_changed() to carry the
* consequences that would occur if the master assumed
* its current value WHILE we were slaved.
*/
master_changed (false, Controllable::NoGroup);
/* effective value changed by master */
Changed (false, Controllable::NoGroup);
}
}
void
AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd)
{
/* our value has (likely) changed, but not because we were
* modified. Just the master.
*/
Changed (false, gcd); /* EMIT SIGNAL */
}
void
AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm)
{

View File

@@ -41,6 +41,7 @@ MuteMaster::MuteMaster (Session& s, const std::string&)
, _soloed_by_self (false)
, _soloed_by_others (false)
, _solo_ignore (false)
, _muted_by_others (0)
{
if (Config->get_mute_affects_pre_fader ()) {
@@ -163,6 +164,11 @@ MuteMaster::get_state()
bool
MuteMaster::muted_by_others_at (MutePoint mp) const
{
return (!_solo_ignore && _session.soloing() && (_mute_point & mp));
return (!_solo_ignore && (_muted_by_others || _session.soloing()) && (_mute_point & mp));
}
void
MuteMaster::mod_muted_by_others (int32_t delta)
{
_muted_by_others = max (0, _muted_by_others + delta);
}

View File

@@ -924,14 +924,6 @@ Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override)
}
assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active());
/* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
Config->get_solo_mute_overrride().
*/
if (yn && Profile->get_trx()) {
set_mute (false, Controllable::UseGroup);
}
}
void
@@ -997,7 +989,8 @@ Route::mod_solo_by_others_upstream (int32_t delta)
}
set_mute_master_solo ();
_solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */
cerr << name() << " SC->Changed (false, UseGroup)\n";
_solo_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
void
@@ -1150,6 +1143,21 @@ Route::muted () const
return _mute_master->muted_by_self();
}
bool
Route::muted_by_others_soloing () const
{
// This method is only used by route_ui for display state.
// The real thing is MuteMaster::muted_by_others_at()
//master is never muted by others
if (is_master())
return false;
//now check to see if something is soloed (and I am not)
//see also MuteMaster::mute_gain_at()
return _session.soloing() && !soloed() && !solo_isolated();
}
bool
Route::muted_by_others () const
{
@@ -1162,7 +1170,7 @@ Route::muted_by_others () const
//now check to see if something is soloed (and I am not)
//see also MuteMaster::mute_gain_at()
return (_session.soloing() && !soloed() && !solo_isolated());
return _mute_master->muted_by_others() || (_session.soloing() && !soloed() && !solo_isolated());
}
#if 0
@@ -5910,6 +5918,5 @@ Route::vca_unassign (boost::shared_ptr<VCA> vca)
_gain_control->remove_master (vca->gain_control());
_solo_control->remove_master (vca->solo_control());
_mute_control->remove_master (vca->mute_control());
}
}

View File

@@ -129,6 +129,31 @@ Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<R
set_list (gl);
}
void
Route::SoloControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
{
boost::shared_ptr<Route> r = _route.lock ();
if (!r) {
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.
*/
r->mod_solo_by_others_upstream (master_soloed ? 1 : -1);
AutomationControl::master_changed (false, gcd);
}
void
Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
@@ -158,13 +183,9 @@ Route::SoloControllable::set_value_unchecked (double val)
double
Route::SoloControllable::get_value () const
{
std::cerr << "RSC get value\n";
if (slaved()) {
std::cerr << "slaved solo control, get master value ... ";
Glib::Threads::RWLock::ReaderLock lm (master_lock);
double v = get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
std::cerr << v << std::endl;
return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
}
if (_list && ((AutomationList*)_list.get())->automation_playback()) {
@@ -202,6 +223,7 @@ Route::MuteControllable::set_superficial_value(bool muted)
const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write ();
const double where = _session.audible_frame ();
if (to_list) {
/* Note that we really need this:
* if (as == Touch && _list->in_new_write_pass ()) {
@@ -218,6 +240,24 @@ Route::MuteControllable::set_superficial_value(bool muted)
Control::set_double (muted, where, to_list);
}
void
Route::MuteControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd)
{
bool master_muted;
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
master_muted = (bool) get_masters_value_locked ();
}
boost::shared_ptr<Route> r (_route.lock());
if (r) {
r->mute_master()->mod_muted_by_others (master_muted ? 1 : -1);
}
AutomationControl::master_changed (false, gcd);
}
void
Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
{
@@ -258,7 +298,7 @@ Route::MuteControllable::get_value () const
{
if (slaved()) {
Glib::Threads::RWLock::ReaderLock lm (master_lock);
return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
return get_masters_value_locked () ? 1.0 : 0.0;
}
if (_list && ((AutomationList*)_list.get())->automation_playback()) {
@@ -268,7 +308,7 @@ Route::MuteControllable::get_value () const
// Not playing back automation, get the actual route mute value
boost::shared_ptr<Route> r = _route.lock ();
return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
return (r && r->muted()) ? 1.0 : 0.0;
}
Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r)

View File

@@ -110,8 +110,10 @@ Session::rt_set_implicit_solo (boost::shared_ptr<RouteList> rl, int delta, bool
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
if (!(*i)->is_auditioner()) {
if (upstream) {
std::cerr << "Changing " << (*i)->name() << " upstream by " << delta << std::endl;
(*i)->mod_solo_by_others_upstream (delta);
} else {
std::cerr << "Changing " << (*i)->name() << " downstream by " << delta << std::endl;
(*i)->mod_solo_by_others_downstream (delta);
}
}

View File

@@ -136,95 +136,15 @@ VCA::set_state (XMLNode const& node, int version)
return 0;
}
void
VCA::add_solo_target (boost::shared_ptr<Route> r)
{
Glib::Threads::RWLock::WriterLock lm (solo_lock);
solo_targets.push_back (r);
r->DropReferences.connect_same_thread (solo_connections, boost::bind (&VCA::solo_target_going_away, this, boost::weak_ptr<Route> (r)));
}
void
VCA::remove_solo_target (boost::shared_ptr<Route> r)
{
Glib::Threads::RWLock::WriterLock lm (solo_lock);
solo_targets.remove (r);
}
void
VCA::solo_target_going_away (boost::weak_ptr<Route> wr)
{
boost::shared_ptr<Route> r (wr.lock());
if (!r) {
return;
}
remove_solo_target (r);
}
void
VCA::set_solo (bool yn)
{
{
Glib::Threads::RWLock::ReaderLock lm (solo_lock);
if (yn == _solo_requested) {
return;
}
if (solo_targets.empty()) {
return;
}
boost::shared_ptr<RouteList> rl (new RouteList (solo_targets));
if (Config->get_solo_control_is_listen_control()) {
_session.set_listen (rl, yn, Session::rt_cleanup, Controllable::NoGroup);
} else {
_session.set_implicit_solo (rl, (yn ? 1 : -1), true, Session::rt_cleanup, Controllable::NoGroup);
}
}
_solo_requested = yn;
}
void
VCA::add_mute_target (boost::shared_ptr<Route> r)
{
Glib::Threads::RWLock::WriterLock lm (mute_lock);
mute_targets.push_back (r);
r->DropReferences.connect_same_thread (mute_connections, boost::bind (&VCA::mute_target_going_away, this, boost::weak_ptr<Route> (r)));
}
void
VCA::remove_mute_target (boost::shared_ptr<Route> r)
{
Glib::Threads::RWLock::WriterLock lm (mute_lock);
mute_targets.remove (r);
}
void
VCA::mute_target_going_away (boost::weak_ptr<Route> wr)
{
boost::shared_ptr<Route> r (wr.lock());
if (!r) {
return;
}
remove_mute_target (r);
}
void
VCA::set_mute (bool yn)
{
{
Glib::Threads::RWLock::ReaderLock lm (mute_lock);
if (yn == _mute_requested) {
return;
}
boost::shared_ptr<RouteList> rl (new RouteList (mute_targets));
_session.set_mute (rl, yn, Session::rt_cleanup, Controllable::NoGroup);
}
_mute_requested = yn;
}