mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-11 17:16:38 +01:00
Implement slaved boolean automation and update mute special-case
This commit is contained in:
parent
50c5425004
commit
2bc2aea009
6 changed files with 116 additions and 29 deletions
|
|
@ -71,7 +71,7 @@ public:
|
||||||
void automation_run (framepos_t start, pframes_t nframes);
|
void automation_run (framepos_t start, pframes_t nframes);
|
||||||
|
|
||||||
protected:
|
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 actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override);
|
||||||
|
|
||||||
void pre_remove_master (boost::shared_ptr<AutomationControl>);
|
void pre_remove_master (boost::shared_ptr<AutomationControl>);
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace ARDOUR {
|
||||||
|
|
||||||
class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
|
class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SlavableAutomationControl(ARDOUR::Session&,
|
SlavableAutomationControl(ARDOUR::Session&,
|
||||||
const Evoral::Parameter& parameter,
|
const Evoral::Parameter& parameter,
|
||||||
const ParameterDescriptor& desc,
|
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;
|
bool find_next_event_locked (double now, double end, Evoral::ControlEvent& next_event) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
class MasterRecord {
|
class MasterRecord {
|
||||||
public:
|
public:
|
||||||
MasterRecord (boost::shared_ptr<AutomationControl> gc, double r)
|
MasterRecord (boost::shared_ptr<AutomationControl> gc, double r)
|
||||||
: _master (gc)
|
: _master (gc)
|
||||||
, _yn (false)
|
, _yn (false)
|
||||||
|
|
@ -101,7 +101,7 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
|
||||||
|
|
||||||
PBD::ScopedConnection connection;
|
PBD::ScopedConnection connection;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::shared_ptr<AutomationControl> _master;
|
boost::shared_ptr<AutomationControl> _master;
|
||||||
/* holds most recently seen master value for boolean/toggle controls */
|
/* holds most recently seen master value for boolean/toggle controls */
|
||||||
bool _yn;
|
bool _yn;
|
||||||
|
|
@ -117,6 +117,10 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
|
||||||
void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
|
void actually_set_value (double value, PBD::Controllable::GroupControlDisposition);
|
||||||
void update_boolean_masters_records (boost::shared_ptr<AutomationControl>);
|
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 void master_changed (bool from_self, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl>);
|
||||||
virtual double get_masters_value_locked () const;
|
virtual double get_masters_value_locked () const;
|
||||||
virtual void pre_remove_master (boost::shared_ptr<AutomationControl>) {}
|
virtual void pre_remove_master (boost::shared_ptr<AutomationControl>) {}
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,7 @@ AutomationControl::set_automation_state (AutoState as)
|
||||||
|
|
||||||
alist()->set_automation_state (as);
|
alist()->set_automation_state (as);
|
||||||
if (_desc.toggled) {
|
if (_desc.toggled) {
|
||||||
|
Changed (false, Controllable::NoGroup); // notify slaves, update boolean masters
|
||||||
return; // No watch for boolean automation
|
return; // No watch for boolean automation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ MuteControl::post_add_master (boost::shared_ptr<AutomationControl> m)
|
||||||
|
|
||||||
if (!muted_by_self() && !get_boolean_masters()) {
|
if (!muted_by_self() && !get_boolean_masters()) {
|
||||||
_muteable.mute_master()->set_muted_by_masters (true);
|
_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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m->get_value()) {
|
if (m->get_value() && get_boolean_masters() == 1) {
|
||||||
if (!muted_by_self() && (get_boolean_masters() == 1)) {
|
_muteable.mute_master()->set_muted_by_masters (false);
|
||||||
Changed (false, Controllable::NoGroup);
|
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);
|
SlavableAutomationControl::actually_set_value (val, gcd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
MuteControl::master_changed (bool self_change, Controllable::GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
|
MuteControl::handle_master_change (boost::shared_ptr<AutomationControl> m)
|
||||||
{
|
{
|
||||||
bool send_signal = false;
|
bool send_signal = false;
|
||||||
boost::shared_ptr<MuteControl> mc = boost::dynamic_pointer_cast<MuteControl> (m);
|
boost::shared_ptr<MuteControl> mc = boost::dynamic_pointer_cast<MuteControl> (m);
|
||||||
|
if (!mc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (m->get_value()) {
|
if (m->get_value()) {
|
||||||
/* this master is now enabled */
|
/* 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);
|
_muteable.mute_master()->set_muted_by_masters (true);
|
||||||
send_signal = true;
|
if (!muted_by_self()) {
|
||||||
|
send_signal = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* this master is disabled and there was only 1 enabled before */
|
/* 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);
|
_muteable.mute_master()->set_muted_by_masters (false);
|
||||||
send_signal = true;
|
if (!muted_by_self()) {
|
||||||
|
send_signal = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return send_signal;
|
||||||
update_boolean_masters_records (m);
|
|
||||||
|
|
||||||
if (send_signal) {
|
|
||||||
Changed (false, Controllable::NoGroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
|
|
@ -177,19 +180,30 @@ MuteControl::muted_by_others_soloing () const
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
const float mute = list()->rt_safe_eval (start, valid);
|
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
|
set_value_unchecked (1.0); // mute
|
||||||
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
||||||
} else if (mute < 0.5 && muted ()) {
|
} else if (!mute && muted()) {
|
||||||
set_value_unchecked (0.0); // unmute
|
set_value_unchecked (0.0); // unmute
|
||||||
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -312,8 +312,16 @@ SlavableAutomationControl::update_boolean_masters_records (boost::shared_ptr<Aut
|
||||||
void
|
void
|
||||||
SlavableAutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd, boost::shared_ptr<AutomationControl> m)
|
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);
|
update_boolean_masters_records (m);
|
||||||
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
if (send_signal) {
|
||||||
|
Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -450,6 +458,66 @@ SlavableAutomationControl::find_next_event_locked (double now, double end, Evora
|
||||||
return rv;
|
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
|
bool
|
||||||
SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
|
SlavableAutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -225,7 +225,7 @@ public:
|
||||||
* @param ok boolean reference if returned value is valid
|
* @param ok boolean reference if returned value is valid
|
||||||
* @returns parameter value
|
* @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);
|
Glib::Threads::RWLock::ReaderLock lm (_lock, Glib::Threads::TRY_LOCK);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue