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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user