+bool
+SlavableAutomationControl::find_next_event_locked (double now, double end, Evoral::ControlEvent& next_event) const
+{
+ if (_masters.empty()) {
+ return false;
+ }
+ bool rv = false;
+ /* iterate over all masters check their automation lists
+ * for any event between "now" and "end" which is earlier than
+ * next_event.when. If found, set next_event.when and return true.
+ * (see also Automatable::find_next_event)
+ */
+ for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
+ boost::shared_ptr<AutomationControl> ac (mr->second.master());
+
+ boost::shared_ptr<SlavableAutomationControl> sc
+ = boost::dynamic_pointer_cast<SlavableAutomationControl>(ac);
+
+ if (sc && sc->find_next_event_locked (now, end, next_event)) {
+ rv = true;
+ }
+
+ Evoral::ControlList::const_iterator i;
+ boost::shared_ptr<const Evoral::ControlList> alist (ac->list());
+ Evoral::ControlEvent cp (now, 0.0f);
+ if (!alist) {
+ continue;
+ }
+
+ for (i = lower_bound (alist->begin(), alist->end(), &cp, Evoral::ControlList::time_comparator);
+ i != alist->end() && (*i)->when < end; ++i) {
+ if ((*i)->when > now) {
+ break;
+ }
+ }
+
+ if (i != alist->end() && (*i)->when < end) {
+ if ((*i)->when < next_event.when) {
+ next_event.when = (*i)->when;
+ rv = true;
+ }
+ }
+ }
+
+ return rv;
+}
+
+bool
+SlavableAutomationControl::handle_master_change (boost::shared_ptr<AutomationControl>)
+{
+ /* Derived classes can implement this for special cases (e.g. mute).
+ * This method is called with a ReaderLock (master_lock) held.
+ *
+ * return true if the changed master value resulted
+ * in a change of the control itself. */
+ return true; // emit Changed
+}
+
+void
+SlavableAutomationControl::automation_run (samplepos_t start, pframes_t nframes)
+{
+ if (!automation_playback ()) {
+ return;
+ }
+
+ assert (_list);
+ bool valid = false;
+ double val = _list->rt_safe_eval (start, valid);
+ if (!valid) {
+ return;
+ }
+ if (toggled ()) {
+ const double thresh = .5 * (_desc.upper - _desc.lower);
+ bool on = (val >= thresh) || (get_masters_value () >= thresh);
+ set_value_unchecked (on ? _desc.upper : _desc.lower);
+ } else {
+ set_value_unchecked (val * get_masters_value ());
+ }
+}
+
+bool
+SlavableAutomationControl::boolean_automation_run_locked (samplepos_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);
+ }
+ }
+ return rv;
+}
+
+bool
+SlavableAutomationControl::boolean_automation_run (samplepos_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;
+}
+