Implement slaved boolean automation and update mute special-case

This commit is contained in:
Robin Gareus 2017-06-10 14:36:03 +02:00
parent 50c5425004
commit 2bc2aea009
6 changed files with 116 additions and 29 deletions

View file

@ -71,7 +71,7 @@ public:
void automation_run (framepos_t start, pframes_t nframes);
protected:
void master_changed (bool, PBD::Controllable::GroupControlDisposition, boost::shared_ptr<AutomationControl>);
bool handle_master_change (boost::shared_ptr<AutomationControl>);
void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override);
void pre_remove_master (boost::shared_ptr<AutomationControl>);

View file

@ -27,7 +27,7 @@ namespace ARDOUR {
class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
{
public:
public:
SlavableAutomationControl(ARDOUR::Session&,
const Evoral::Parameter& parameter,
const ParameterDescriptor& desc,
@ -80,10 +80,10 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
bool find_next_event_locked (double now, double end, Evoral::ControlEvent& next_event) const;
protected:
protected:
class MasterRecord {
public:
public:
MasterRecord (boost::shared_ptr<AutomationControl> gc, double r)
: _master (gc)
, _yn (false)
@ -101,7 +101,7 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
PBD::ScopedConnection connection;
private:
private:
boost::shared_ptr<AutomationControl> _master;
/* holds most recently seen master value for boolean/toggle controls */
bool _yn;
@ -117,6 +117,10 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
void update_boolean_masters_records (boost::shared_ptr<AutomationControl>);
virtual bool handle_master_change (boost::shared_ptr<AutomationControl>);
virtual bool boolean_automation_run_locked (framepos_t start, pframes_t len);
bool boolean_automation_run (framepos_t start, pframes_t len);
virtual void master_changed (bool from_self, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl>);
virtual double get_masters_value_locked () const;
virtual void pre_remove_master (boost::shared_ptr<AutomationControl>) {}

View file

@ -207,6 +207,7 @@ AutomationControl::set_automation_state (AutoState as)
alist()->set_automation_state (as);
if (_desc.toggled) {
Changed (false, Controllable::NoGroup); // notify slaves, update boolean masters
return; // No watch for boolean automation
}

View file

@ -52,7 +52,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);
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
}
}
@ -67,9 +67,10 @@ MuteControl::pre_remove_master (boost::shared_ptr<AutomationControl> m)
return;
}
if (m->get_value()) {
if (!muted_by_self() && (get_boolean_masters() == 1)) {
Changed (false, Controllable::NoGroup);
if (m->get_value() && get_boolean_masters() == 1) {
_muteable.mute_master()->set_muted_by_masters (false);
if (!muted_by_self()) {
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
}
}
@ -89,31 +90,33 @@ MuteControl::actually_set_value (double val, Controllable::GroupControlDispositi
SlavableAutomationControl::actually_set_value (val, gcd);
}
void
MuteControl::master_changed (bool self_change, Controllable::GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
bool
MuteControl::handle_master_change (boost::shared_ptr<AutomationControl> m)
{
bool send_signal = false;
boost::shared_ptr<MuteControl> mc = boost::dynamic_pointer_cast<MuteControl> (m);
if (!mc) {
return false;
}
if (m->get_value()) {
/* this master is now enabled */
if (!muted_by_self() && get_boolean_masters() == 0) {
if (get_boolean_masters() == 0) {
_muteable.mute_master()->set_muted_by_masters (true);
send_signal = true;
if (!muted_by_self()) {
send_signal = true;
}
}
} else {
/* this master is disabled and there was only 1 enabled before */
if (!muted_by_self() && get_boolean_masters() == 1) {
if (get_boolean_masters() == 1) {
_muteable.mute_master()->set_muted_by_masters (false);
send_signal = true;
if (!muted_by_self()) {
send_signal = true;
}
}
}
update_boolean_masters_records (m);
if (send_signal) {
Changed (false, Controllable::NoGroup);
}
return send_signal;
}
double
@ -177,19 +180,30 @@ MuteControl::muted_by_others_soloing () const
}
void
MuteControl::automation_run (framepos_t start, pframes_t)
MuteControl::automation_run (framepos_t start, pframes_t len)
{
if (!list() || !automation_playback()) {
boolean_automation_run (start, len);
if (muted_by_masters ()) {
// already muted, no need to check further
return;
}
bool valid = false;
const float mute = list()->rt_safe_eval (start, valid);
bool valid = false;
bool mute = false;
if (mute >= 0.5 && !muted()) {
if (list() && automation_playback()) {
mute = list()->rt_safe_eval (start, valid) >= 0.5;
}
if (!valid) {
return;
}
if (mute && !muted()) {
set_value_unchecked (1.0); // mute
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
} else if (mute < 0.5 && muted ()) {
} else if (!mute && muted()) {
set_value_unchecked (0.0); // unmute
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}

View file

@ -312,8 +312,16 @@ SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<Aut
void
SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
{
Glib::Threads::RWLock::ReaderLock lm (master_lock, Glib::Threads::TRY_LOCK);
if (!lm.locked ()) {
/* boolean_automation_run_locked () special case */
return;
}
bool send_signal = handle_master_change (m);
update_boolean_masters_records (m);
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
if (send_signal) {
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
}
void
@ -450,6 +458,66 @@ SlavableAutomationControl::find_next_event_locked (double now, double end, Evora
return rv;
}
bool
SlavableAutomationControl::handle_master_change (boost::shared_ptr<AutomationControl>)
{
return true; // emit Changed
}
bool
SlavableAutomationControl::boolean_automation_run_locked (framepos_t start, pframes_t len)
{
bool rv = false;
if (!_desc.toggled) {
return false;
}
for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
boost::shared_ptr<AutomationControl> ac (mr->second.master());
if (!ac->automation_playback ()) {
continue;
}
if (!ac->toggled ()) {
continue;
}
boost::shared_ptr<SlavableAutomationControl> sc = boost::dynamic_pointer_cast<MuteControl>(ac);
if (sc) {
rv |= sc->boolean_automation_run (start, len);
}
boost::shared_ptr<const Evoral::ControlList> alist (ac->list());
bool valid = false;
const bool yn = alist->rt_safe_eval (start, valid) >= 0.5;
if (!valid) {
continue;
}
/* ideally we'd call just master_changed() which calls update_boolean_masters_records()
* but that takes the master_lock, which is already locked */
if (mr->second.yn() != yn) {
rv |= handle_master_change (ac);
mr->second.set_yn (yn);
/* notify the GUI, without recursion:
* master_changed() above will ignore the change if the lock is held.
*/
ac->set_value_unchecked (yn ? 1. : 0.);
ac->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
}
return rv;
}
bool
SlavableAutomationControl::boolean_automation_run (framepos_t start, pframes_t len)
{
bool change = false;
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
change = boolean_automation_run_locked (start, len);
}
if (change) {
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
}
return change;
}
bool
SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
{

View file

@ -225,7 +225,7 @@ public:
* @param ok boolean reference if returned value is valid
* @returns parameter value
*/
double rt_safe_eval (double where, bool& ok) {
double rt_safe_eval (double where, bool& ok) const {
Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);