From 4aa1c242ab1e1bf4fc3575c5d88a22824976a04f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 8 Apr 2016 16:51:34 -0400 Subject: add new files to source tree --- libs/ardour/ardour/control_group.h | 108 +++++++++++ libs/ardour/ardour/monitor_control.h | 58 ++++++ libs/ardour/ardour/monitorable.h | 35 ++++ libs/ardour/ardour/mute_control.h | 73 ++++++++ libs/ardour/ardour/muteable.h | 51 +++++ libs/ardour/ardour/phase_control.h | 71 +++++++ libs/ardour/ardour/record_enable_control.h | 60 ++++++ libs/ardour/ardour/recordable.h | 35 ++++ libs/ardour/ardour/solo_control.h | 99 ++++++++++ libs/ardour/ardour/solo_isolate_control.h | 90 +++++++++ libs/ardour/ardour/solo_safe_control.h | 53 ++++++ libs/ardour/ardour/soloable.h | 38 ++++ libs/ardour/control_group.cc | 287 +++++++++++++++++++++++++++++ libs/ardour/monitor_control.cc | 80 ++++++++ libs/ardour/mute_control.cc | 115 ++++++++++++ libs/ardour/muteable.cc | 27 +++ libs/ardour/phase_control.cc | 97 ++++++++++ libs/ardour/record_enable_control.cc | 73 ++++++++ libs/ardour/slavable_automation_control.cc | 226 +++++++++++++++++++++++ libs/ardour/solo_control.cc | 264 ++++++++++++++++++++++++++ libs/ardour/solo_isolate_control.cc | 180 ++++++++++++++++++ libs/ardour/solo_safe_control.cc | 86 +++++++++ 22 files changed, 2206 insertions(+) create mode 100644 libs/ardour/ardour/control_group.h create mode 100644 libs/ardour/ardour/monitor_control.h create mode 100644 libs/ardour/ardour/monitorable.h create mode 100644 libs/ardour/ardour/mute_control.h create mode 100644 libs/ardour/ardour/muteable.h create mode 100644 libs/ardour/ardour/phase_control.h create mode 100644 libs/ardour/ardour/record_enable_control.h create mode 100644 libs/ardour/ardour/recordable.h create mode 100644 libs/ardour/ardour/solo_control.h create mode 100644 libs/ardour/ardour/solo_isolate_control.h create mode 100644 libs/ardour/ardour/solo_safe_control.h create mode 100644 libs/ardour/ardour/soloable.h create mode 100644 libs/ardour/control_group.cc create mode 100644 libs/ardour/monitor_control.cc create mode 100644 libs/ardour/mute_control.cc create mode 100644 libs/ardour/muteable.cc create mode 100644 libs/ardour/phase_control.cc create mode 100644 libs/ardour/record_enable_control.cc create mode 100644 libs/ardour/slavable_automation_control.cc create mode 100644 libs/ardour/solo_control.cc create mode 100644 libs/ardour/solo_isolate_control.cc create mode 100644 libs/ardour/solo_safe_control.cc diff --git a/libs/ardour/ardour/control_group.h b/libs/ardour/ardour/control_group.h new file mode 100644 index 0000000000..e1b83bb7b4 --- /dev/null +++ b/libs/ardour/ardour/control_group.h @@ -0,0 +1,108 @@ +/* + 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_control_group_h__ +#define __libardour_control_group_h__ + +#include +#include + +#include +#include + +#include + +#include "pbd/controllable.h" + +#include "evoral/Parameter.hpp" + +#include "ardour/automation_control.h" +#include "ardour/types.h" + +namespace ARDOUR { + +class LIBARDOUR_API ControlGroup : public boost::enable_shared_from_this +{ + public: + ControlGroup (Evoral::Parameter p); + virtual ~ControlGroup (); + + enum Mode { + Relative = 0x1, + Inverted = 0x2, + }; + + int add_control (boost::shared_ptr); + int remove_control (boost::shared_ptr); + + ControlList controls () const; + + void clear (); + + void set_active (bool); + bool active() const { return _active; } + + void set_mode (Mode m); + Mode mode () const { return _mode; } + + Evoral::Parameter parameter() const { return _parameter; } + + virtual void set_group_value (boost::shared_ptr, double val); + + bool use_me (PBD::Controllable::GroupControlDisposition gcd) const { + switch (gcd) { + case PBD::Controllable::ForGroup: + return false; + case PBD::Controllable::NoGroup: + return false; + case PBD::Controllable::InverseGroup: + return !_active; + default: + return _active; + } + } + + protected: + typedef std::map > ControlMap; + Evoral::Parameter _parameter; + mutable Glib::Threads::RWLock controls_lock; + ControlMap _controls; + bool _active; + Mode _mode; + PBD::ScopedConnectionList member_connections; + bool propagating; + + void control_going_away (boost::weak_ptr); +}; + + +class LIBARDOUR_API GainControlGroup : public ControlGroup +{ + public: + GainControlGroup(); + + void set_group_value (boost::shared_ptr, double val); + + private: + gain_t get_max_factor (gain_t); + gain_t get_min_factor (gain_t); +}; + +} /* namespace */ + +#endif /* __libardour_control_group_h__ */ diff --git a/libs/ardour/ardour/monitor_control.h b/libs/ardour/ardour/monitor_control.h new file mode 100644 index 0000000000..871263a0c6 --- /dev/null +++ b/libs/ardour/ardour/monitor_control.h @@ -0,0 +1,58 @@ +/* + 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 __ardour_monitor_control_h__ +#define __ardour_monitor_control_h__ + +#include + +#include +#include + +#include "ardour/automation_control.h" +#include "ardour/monitorable.h" + +#include "ardour/libardour_visibility.h" + +namespace ARDOUR { + +class Session; + +class LIBARDOUR_API MonitorControl : public SlavableAutomationControl +{ + public: + MonitorControl (Session& session, std::string const & name, Monitorable& m); + ~MonitorControl() {} + + MonitorChoice monitoring_choice() const { return static_cast (get_value()); } + MonitorState monitoring_state () const { return _monitorable.monitoring_state(); } + + int set_state (XMLNode const&, int); + XMLNode& get_state (); + + protected: + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); + + private: + Monitorable& _monitorable; + MonitorChoice _monitoring; +}; + +} /* namespace */ + +#endif /* __libardour_monitor_control_h__ */ diff --git a/libs/ardour/ardour/monitorable.h b/libs/ardour/ardour/monitorable.h new file mode 100644 index 0000000000..03968e61a5 --- /dev/null +++ b/libs/ardour/ardour/monitorable.h @@ -0,0 +1,35 @@ +/* + 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 __ardour_monitorable_h__ +#define __ardour_monitorable_h__ + +#include "ardour/types.h" + +namespace ARDOUR { + +class Monitorable { + public: + virtual ~Monitorable() {} + + virtual MonitorState monitoring_state() const = 0; +}; + +} /* namespace */ + +#endif /* __ardour_monitorable_h__ */ diff --git a/libs/ardour/ardour/mute_control.h b/libs/ardour/ardour/mute_control.h new file mode 100644 index 0000000000..f9a55cacfc --- /dev/null +++ b/libs/ardour/ardour/mute_control.h @@ -0,0 +1,73 @@ +/* + 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 __ardour_mute_control_h__ +#define __ardour_mute_control_h__ + +#include + +#include + +#include "ardour/libardour_visibility.h" + +namespace ARDOUR { + +class Session; +class Muteable; + +class LIBARDOUR_API MuteControl : public SlavableAutomationControl +{ + public: + MuteControl (Session& session, std::string const& name, Muteable&); + + double get_value () const; + + /* Export additional API so that objects that only get access + * to a Controllable/AutomationControl can do more fine-grained + * operations with respect to mute. Obviously, they would need + * to dynamic_cast first. + * + * Mute state is not representable by a single scalar value, + * so set_value() and get_value() is not enough. + * + * This means that the Controllable is technically + * asymmetric. It is possible to call ::set_value (0.0) to + * turn off mute, and then call ::get_value() and get a + * return of 1.0 because the control is affected by + * upstream/downstream or a master. + */ + + bool muted () const; + + bool muted_by_others_soloing () const; + bool muted_by_others () const; + + void set_mute_points (MuteMaster::MutePoint); + MuteMaster::MutePoint mute_points () const; + + protected: + void master_changed (bool, PBD::Controllable::GroupControlDisposition); + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); + + private: + Muteable& _muteable; +}; + +} /* namespace */ + +#endif /* __libardour_mute_control_h__ */ diff --git a/libs/ardour/ardour/muteable.h b/libs/ardour/ardour/muteable.h new file mode 100644 index 0000000000..57db66b1ee --- /dev/null +++ b/libs/ardour/ardour/muteable.h @@ -0,0 +1,51 @@ +/* + 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 __ardour_muteable_h__ +#define __ardour_muteable_h__ + +#include + +#include "pbd/signals.h" + +namespace ARDOUR { + +class MuteMaster; +class Session; + +class Muteable { + public: + Muteable (Session&, std::string const &name); + virtual ~Muteable() {} + + virtual bool can_be_muted_by_others () const = 0; + virtual void act_on_mute () {} + + boost::shared_ptr mute_master() const { + return _mute_master; + } + + PBD::Signal0 mute_points_changed; + + protected: + boost::shared_ptr _mute_master; +}; + +} /* namespace */ + +#endif /* __ardour_muteable_h__ */ diff --git a/libs/ardour/ardour/phase_control.h b/libs/ardour/ardour/phase_control.h new file mode 100644 index 0000000000..054ad4a5a4 --- /dev/null +++ b/libs/ardour/ardour/phase_control.h @@ -0,0 +1,71 @@ +/* + 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 __ardour_phase_control_h__ +#define __ardour_phase_control_h__ + +#include + +#include +#include + +#include "ardour/automation_control.h" +#include "ardour/libardour_visibility.h" + +namespace ARDOUR { + +class Session; + +/* Note that PhaseControl is not Slavable. There's no particular reason for + * this, it could be changed at any time. But it seems useless. + */ + +class LIBARDOUR_API PhaseControl : public AutomationControl +{ + public: + PhaseControl (Session& session, std::string const & name); + + /* There are two approaches to designing/using a PhaseControl. One is + * to have one such control for every channel of the control's + * owner. The other is to have a single control which manages all + * channels. For now (Spring 2016) we're using the second design. + */ + + void set_phase_invert (uint32_t, bool yn); + void set_phase_invert (boost::dynamic_bitset<>); + bool inverted (uint32_t chn) const { return _phase_invert[chn]; } + + bool none () const { return !_phase_invert.any(); } + bool any() const { return _phase_invert.any(); } + uint64_t count() const { return _phase_invert.count(); } + uint64_t size() const { return _phase_invert.size(); } + void resize (uint32_t); + + int set_state (XMLNode const&, int); + XMLNode& get_state (); + + protected: + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); + + private: + boost::dynamic_bitset<> _phase_invert; +}; + +} /* namespace */ + +#endif /* __libardour_phase_control_h__ */ diff --git a/libs/ardour/ardour/record_enable_control.h b/libs/ardour/ardour/record_enable_control.h new file mode 100644 index 0000000000..0bb5edac8d --- /dev/null +++ b/libs/ardour/ardour/record_enable_control.h @@ -0,0 +1,60 @@ +/* + 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 __ardour_record_enable_control_h__ +#define __ardour_record_enable_control_h__ + +#include + +#include +#include + +#include "ardour/automation_control.h" +#include "ardour/recordable.h" + +#include "ardour/libardour_visibility.h" + +namespace ARDOUR { + +class Session; + +class LIBARDOUR_API RecordEnableControl : public SlavableAutomationControl +{ + public: + RecordEnableControl (Session& session, std::string const & name, Recordable& m); + ~RecordEnableControl() {} + + /* Most (Slavable)AutomationControls do not override this, but we need + * to in order to prepare the Recordable for a change that will happen + * subsequently, in a realtime context. So the change is divided into + * two parts: the non-RT preparation, executed inside ::set_value(), + * then the second RT part. + */ + + void set_value (double, PBD::Controllable::GroupControlDisposition); + + protected: + void actually_set_value (double val, Controllable::GroupControlDisposition gcd); + + private: + Recordable& _recordable; +}; + +} /* namespace */ + +#endif /* __libardour_record_enable_control_h__ */ diff --git a/libs/ardour/ardour/recordable.h b/libs/ardour/ardour/recordable.h new file mode 100644 index 0000000000..e8568cfd1f --- /dev/null +++ b/libs/ardour/ardour/recordable.h @@ -0,0 +1,35 @@ +/* + 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 __ardour_recordable_h__ +#define __ardour_recordable_h__ + +namespace ARDOUR { + +class Recordable { + public: + virtual ~Recordable() {} + + virtual int prep_record_enabled (bool yn) = 0; + virtual bool can_be_record_enabled() = 0; + virtual bool can_be_record_safe() = 0; +}; + +} /* namespace */ + +#endif /* __ardour_recordable_h__ */ diff --git a/libs/ardour/ardour/solo_control.h b/libs/ardour/ardour/solo_control.h new file mode 100644 index 0000000000..7f7e532669 --- /dev/null +++ b/libs/ardour/ardour/solo_control.h @@ -0,0 +1,99 @@ +/* + 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 __ardour_solo_control_h__ +#define __ardour_solo_control_h__ + +#include + +#include + +#include "ardour/automation_control.h" +#include "ardour/libardour_visibility.h" + +namespace ARDOUR { + +class Session; +class Soloable; +class Muteable; + +class LIBARDOUR_API SoloControl : public SlavableAutomationControl +{ + public: + SoloControl (Session& session, std::string const & name, Soloable& soloable, Muteable& m); + + double get_value () const; + + /* 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 first. + * + * Solo state is not representable by a single scalar value, + * so set_value() and get_value() is not enough. + * + * 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 control is soloed by + * upstream/downstream or a master. + */ + + void mod_solo_by_others_upstream (int32_t delta); + void mod_solo_by_others_downstream (int32_t delta); + + /* API to check different aspects of solo substate + */ + + bool soloed_by_others () const { + return _soloed_by_others_downstream || _soloed_by_others_downstream; + } + uint32_t soloed_by_others_upstream () const { + return _soloed_by_others_upstream; + } + uint32_t soloed_by_others_downstream () const { + return _soloed_by_others_downstream; + } + bool self_soloed () const { + return _self_solo; + } + bool soloed() const { return self_soloed() || soloed_by_others(); } + + void clear_all_solo_state (); + + int set_state (XMLNode const&, int); + XMLNode& get_state (); + + protected: + void master_changed (bool, PBD::Controllable::GroupControlDisposition); + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); + + private: + Soloable& _soloable; + Muteable& _muteable; + bool _self_solo; + uint32_t _soloed_by_others_upstream; + uint32_t _soloed_by_others_downstream; + + void set_self_solo (bool yn); + void set_mute_master_solo (); +}; + +} /* namespace */ + +#endif /* __libardour_solo_control_h__ */ diff --git a/libs/ardour/ardour/solo_isolate_control.h b/libs/ardour/ardour/solo_isolate_control.h new file mode 100644 index 0000000000..cb8ea8b507 --- /dev/null +++ b/libs/ardour/ardour/solo_isolate_control.h @@ -0,0 +1,90 @@ +/* + 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 __ardour_solo_isolate_control_h__ +#define __ardour_solo_isolate_control_h__ + +#include + +#include + +#include "ardour/libardour_visibility.h" + +class XMLNode; + +namespace ARDOUR { + +class Session; +class Soloable; +class Muteable; + +class LIBARDOUR_API SoloIsolateControl : public SlavableAutomationControl +{ + public: + SoloIsolateControl (Session& session, std::string const & name, Soloable& soloable, Muteable& m); + + double get_value () const; + + /* Export additional API so that objects that only get access + * to a Controllable/AutomationControl can do more fine-grained + * operations with respect to solo isolate. Obviously, they would need + * to dynamic_cast first. + * + * Solo Isolate state is not representable by a single scalar value, + * so set_value() and get_value() is not enough. + * + * 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 control is isolated by + * upstream/downstream or a master. + */ + + void mod_solo_isolated_by_upstream (int32_t delta); + + /* API to check different aspects of solo isolate substate + */ + + uint32_t solo_isolated_by_upstream () const { + return _solo_isolated_by_upstream; + } + bool self_solo_isolated () const { + return _solo_isolated; + } + bool solo_isolated() const { return self_solo_isolated() || solo_isolated_by_upstream(); } + + int set_state (XMLNode const&, int); + XMLNode& get_state (); + + protected: + void master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd); + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); + + private: + Soloable& _soloable; + Muteable& _muteable; + bool _solo_isolated; + uint32_t _solo_isolated_by_upstream; + + void set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override); + +}; + +} /* namespace */ + +#endif /* __libardour_solo_isolate_control_h__ */ diff --git a/libs/ardour/ardour/solo_safe_control.h b/libs/ardour/ardour/solo_safe_control.h new file mode 100644 index 0000000000..d230a9c2d9 --- /dev/null +++ b/libs/ardour/ardour/solo_safe_control.h @@ -0,0 +1,53 @@ +/* + 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 __ardour_solo_safe_control_h__ +#define __ardour_solo_safe_control_h__ + +#include + +#include "ardour/libardour_visibility.h" + +class XMLNode; + +namespace ARDOUR { + +class Session; + +class LIBARDOUR_API SoloSafeControl : public SlavableAutomationControl +{ + public: + SoloSafeControl (Session& session, std::string const & name); + + double get_value () const; + + bool solo_safe() const { return _solo_safe; } + + int set_state (XMLNode const&, int); + XMLNode& get_state (); + + protected: + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); + + private: + bool _solo_safe; +}; + +} /* namespace */ + +#endif /* __libardour_solo_safe_control_h__ */ diff --git a/libs/ardour/ardour/soloable.h b/libs/ardour/ardour/soloable.h new file mode 100644 index 0000000000..6dafac7041 --- /dev/null +++ b/libs/ardour/ardour/soloable.h @@ -0,0 +1,38 @@ +/* + 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 __ardour_soloable_h__ +#define __ardour_soloable_h__ + +#include + +namespace ARDOUR { + +class Soloable { + public: + virtual ~Soloable() {} + + virtual void push_solo_upstream (int32_t delta) = 0; + virtual void push_solo_isolate_upstream (int32_t delta) = 0; + virtual bool is_safe () const = 0; + virtual bool can_solo () const = 0; +}; + +} /* namespace */ + +#endif /* __ardour_soloable_h__ */ diff --git a/libs/ardour/control_group.cc b/libs/ardour/control_group.cc new file mode 100644 index 0000000000..6752940d27 --- /dev/null +++ b/libs/ardour/control_group.cc @@ -0,0 +1,287 @@ +/* + 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. +*/ + +#include + +#include "pbd/unwind.h" + +#include "ardour/control_group.h" +#include "ardour/gain_control.h" + +using namespace ARDOUR; +using namespace PBD; + +ControlGroup::ControlGroup (Evoral::Parameter p) + : _parameter (p) + , _active (true) + , _mode (Mode (0)) + , propagating (false) +{ +} + + +ControlGroup::~ControlGroup () +{ + clear (); +} + +void +ControlGroup::set_active (bool yn) +{ + _active = yn; + std::cerr << " CG for " << enum_2_string ((AutomationType) _parameter.type()) << " now active ? " << _active << std::endl; +} + +void +ControlGroup::clear () +{ + /* we're giving up on all members, so we don't care about their + * DropReferences signals anymore + */ + + member_connections.drop_connections (); + + /* make a copy so that when the control calls ::remove_control(), we + * don't deadlock. + */ + + std::vector > controls; + { + Glib::Threads::RWLock::WriterLock lm (controls_lock); + for (ControlMap::const_iterator i = _controls.begin(); i != _controls.end(); ++i) { + controls.push_back (i->second); + } + } + + _controls.clear (); + + for (std::vector >::iterator c = controls.begin(); c != controls.end(); ++c) { + (*c)->set_group (boost::shared_ptr()); + } +} + +ControlList +ControlGroup::controls () const +{ + ControlList c; + + if (_active) { + Glib::Threads::RWLock::WriterLock lm (controls_lock); + for (ControlMap::const_iterator i = _controls.begin(); i != _controls.end(); ++i) { + c.push_back (i->second); + } + } + + return c; +} + +void +ControlGroup::control_going_away (boost::weak_ptr wac) +{ + boost::shared_ptr ac (wac.lock()); + if (!ac) { + return; + } + + remove_control (ac); +} + +int +ControlGroup::remove_control (boost::shared_ptr ac) +{ + Glib::Threads::RWLock::WriterLock lm (controls_lock); + /* return zero if erased, non-zero otherwise */ + return !(_controls.erase (ac->id()) > 0); +} + +int +ControlGroup::add_control (boost::shared_ptr ac) +{ + if (ac->parameter() != _parameter) { + return -1; + } + + std::pair res; + + { + Glib::Threads::RWLock::WriterLock lm (controls_lock); + res = _controls.insert (std::make_pair (ac->id(), ac)); + } + + if (!res.second) { + /* already in ControlMap */ + return -1; + } + + /* Inserted */ + + ac->set_group (shared_from_this()); + + ac->DropReferences.connect_same_thread (member_connections, boost::bind (&ControlGroup::control_going_away, this, boost::weak_ptr(ac))); + + return 0; +} + +void +ControlGroup::set_group_value (boost::shared_ptr control, double val) +{ + double old = control->get_value (); + + /* set the primary control */ + + control->set_value (val, Controllable::ForGroup); + + /* now propagate across the group */ + + Glib::Threads::RWLock::ReaderLock lm (controls_lock); + + if (_mode & Relative) { + + const double factor = old / control->get_value (); + + for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { + if (c->second != control) { + c->second->set_value (factor * c->second->get_value(), Controllable::ForGroup); + } + } + + } else { + + for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { + if (c->second != control) { + c->second->set_value (val, Controllable::ForGroup); + } + } + } +} + +/*---- GAIN CONTROL GROUP -----------*/ + +gain_t +GainControlGroup::get_min_factor (gain_t factor) +{ + /* CALLER MUST HOLD READER LOCK */ + + for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { + gain_t const g = c->second->get_value(); + + if ((g + g * factor) >= 0.0f) { + continue; + } + + if (g <= 0.0000003f) { + return 0.0f; + } + + factor = 0.0000003f / g - 1.0f; + } + + return factor; +} + +gain_t +GainControlGroup::get_max_factor (gain_t factor) +{ + /* CALLER MUST HOLD READER LOCK */ + + for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { + gain_t const g = c->second->get_value(); + + // if the current factor woulnd't raise this route above maximum + if ((g + g * factor) <= 1.99526231f) { + continue; + } + + // if route gain is already at peak, return 0.0f factor + if (g >= 1.99526231f) { + return 0.0f; + } + + // factor is calculated so that it would raise current route to max + factor = 1.99526231f / g - 1.0f; + } + + return factor; +} + +void +GainControlGroup::set_group_value (boost::shared_ptr control, double val) +{ + /* set the primary control */ + + control->set_value (val, Controllable::ForGroup); + + /* now propagate across the group */ + + Glib::Threads::RWLock::ReaderLock lm (controls_lock); + + if (_mode & Relative) { + + gain_t usable_gain = control->get_value(); + + if (usable_gain < 0.000001f) { + usable_gain = 0.000001f; + } + + gain_t delta = val; + if (delta < 0.000001f) { + delta = 0.000001f; + } + + delta -= usable_gain; + + if (delta == 0.0f) + return; + + gain_t factor = delta / usable_gain; + + if (factor > 0.0f) { + factor = get_max_factor (factor); + if (factor == 0.0f) { + control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */ + return; + } + } else { + factor = get_min_factor (factor); + if (factor == 0.0f) { + control->Changed (true, Controllable::ForGroup); /* EMIT SIGNAL */ + return; + } + } + + for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { + if (c->second == control) { + continue; + } + + boost::shared_ptr gc = boost::dynamic_pointer_cast (c->second); + + if (gc) { + gc->inc_gain (factor); + } + } + + } else { + + for (ControlMap::iterator c = _controls.begin(); c != _controls.end(); ++c) { + if (c->second != control) { + c->second->set_value (val, Controllable::ForGroup); + } + } + } +} diff --git a/libs/ardour/monitor_control.cc b/libs/ardour/monitor_control.cc new file mode 100644 index 0000000000..c16de3b7c1 --- /dev/null +++ b/libs/ardour/monitor_control.cc @@ -0,0 +1,80 @@ +/* + 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. +*/ + +#include "ardour/monitor_control.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; + +MonitorControl::MonitorControl (Session& session, std::string const & name, Monitorable& m) + : SlavableAutomationControl (session, MonitoringAutomation, ParameterDescriptor (MonitoringAutomation), + boost::shared_ptr(new AutomationList(Evoral::Parameter(MonitoringAutomation))), + name) + + , _monitorable (m) + , _monitoring (MonitorAuto) +{ + _list->set_interpolation(Evoral::ControlList::Discrete); + /* monitoring changes must be synchronized by the process cycle */ + set_flags (Controllable::Flag (flags() | Controllable::RealTime)); +} + +void +MonitorControl::actually_set_value (double val, Controllable::GroupControlDisposition gcd) +{ + int v = (int) val; + switch (v) { + case MonitorAuto: + case MonitorInput: + case MonitorDisk: + case MonitorCue: + break; + default: + /* illegal value */ + return; + } + + _monitoring = MonitorChoice (v); + AutomationControl::actually_set_value (val, gcd); +} + +XMLNode& +MonitorControl::get_state () +{ + XMLNode& node (SlavableAutomationControl::get_state()); + node.add_property (X_("monitoring"), enum_2_string (_monitoring)); + return node; +} + +int +MonitorControl::set_state (XMLNode const & node, int version) +{ + SlavableAutomationControl::set_state (node, version); + + const XMLProperty* prop; + + if ((prop = node.property (X_("monitoring"))) != 0) { + _monitoring = MonitorChoice (string_2_enum (prop->value(), _monitoring)); + } else { + _monitoring = MonitorAuto; + } + + return 0; +} diff --git a/libs/ardour/mute_control.cc b/libs/ardour/mute_control.cc new file mode 100644 index 0000000000..b0513d1138 --- /dev/null +++ b/libs/ardour/mute_control.cc @@ -0,0 +1,115 @@ +/* + 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. +*/ + +#include "evoral/ControlList.hpp" + +#include "ardour/mute_master.h" +#include "ardour/session.h" +#include "ardour/mute_control.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace std; + + +MuteControl::MuteControl (Session& session, std::string const & name, Muteable& m) + : SlavableAutomationControl (session, MuteAutomation, ParameterDescriptor (MuteAutomation), + boost::shared_ptr (new AutomationList (Evoral::Parameter (MuteAutomation))), + name) + , _muteable (m) +{ + _list->set_interpolation (Evoral::ControlList::Discrete); + /* mute changes must be synchronized by the process cycle */ + set_flags (Controllable::Flag (flags() | Controllable::RealTime)); +} + +void +MuteControl::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 (); + } + + _muteable.mute_master()->mod_muted_by_others (master_muted ? 1 : -1); + + SlavableAutomationControl::master_changed (false, gcd); +} + +void +MuteControl::actually_set_value (double val, Controllable::GroupControlDisposition gcd) +{ + if (muted() != bool (val)) { + _muteable.mute_master()->set_muted_by_self (val); + + /* allow the Muteable to respond to the mute change + before anybody else knows about it. + */ + _muteable.act_on_mute (); + } + + AutomationControl::actually_set_value (val, gcd); +} + +double +MuteControl::get_value () const +{ + if (slaved()) { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return get_masters_value_locked () ? 1.0 : 0.0; + } + + if (_list && boost::dynamic_pointer_cast(_list)->automation_playback()) { + // Playing back automation, get the value from the list + return AutomationControl::get_value(); + } + + return muted() ? 1.0 : 0.0; +} + +void +MuteControl::set_mute_points (MuteMaster::MutePoint mp) +{ + _muteable.mute_master()->set_mute_points (mp); + _muteable.mute_points_changed (); /* EMIT SIGNAL */ + + if (_muteable.mute_master()->muted_by_self()) { + Changed (true, Controllable::UseGroup); /* EMIT SIGNAL */ + } +} + +MuteMaster::MutePoint +MuteControl::mute_points () const +{ + return _muteable.mute_master()->mute_points (); +} + +bool +MuteControl::muted () const +{ + return _muteable.mute_master()->muted_by_self(); +} + +bool +MuteControl::muted_by_others () const +{ + return _muteable.mute_master()->muted_by_others (); +} diff --git a/libs/ardour/muteable.cc b/libs/ardour/muteable.cc new file mode 100644 index 0000000000..9d434888bf --- /dev/null +++ b/libs/ardour/muteable.cc @@ -0,0 +1,27 @@ +/* + 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. +*/ + +#include "ardour/muteable.h" +#include "ardour/mute_master.h" + +using namespace ARDOUR; + +Muteable::Muteable (Session& s, std::string const & name) + : _mute_master (new MuteMaster (s, name)) +{ +} diff --git a/libs/ardour/phase_control.cc b/libs/ardour/phase_control.cc new file mode 100644 index 0000000000..bc237893a0 --- /dev/null +++ b/libs/ardour/phase_control.cc @@ -0,0 +1,97 @@ +/* + 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. +*/ + +#include "ardour/phase_control.h" +#include "ardour/session.h" + +#include "i18n.h" + +using namespace std; +using namespace PBD; +using namespace ARDOUR; + +PhaseControl::PhaseControl (Session& session, std::string const & name) + : AutomationControl (session, PhaseAutomation, ParameterDescriptor (PhaseAutomation), + boost::shared_ptr(new AutomationList(Evoral::Parameter(PhaseAutomation))), + name) +{ +} + +void +PhaseControl::actually_set_value (double val, Controllable::GroupControlDisposition gcd) +{ + _phase_invert = boost::dynamic_bitset<> (std::numeric_limits::digits, (unsigned long) val); + + AutomationControl::actually_set_value (val, gcd); +} + +/** @param c Audio channel index. + * @param yn true to invert phase, otherwise false. + */ +void +PhaseControl::set_phase_invert (uint32_t c, bool yn) +{ + if (_phase_invert[c] != yn) { + _phase_invert[c] = yn; + AutomationControl::actually_set_value (_phase_invert.to_ulong(), Controllable::NoGroup); + _session.set_dirty (); + } +} + +void +PhaseControl::set_phase_invert (boost::dynamic_bitset<> p) +{ + if (_phase_invert != p) { + _phase_invert = p; + AutomationControl::actually_set_value (_phase_invert.to_ulong(), Controllable::NoGroup); + Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ + _session.set_dirty (); + } +} + +void +PhaseControl::resize (uint32_t n) +{ + _phase_invert.resize (n); +} + +XMLNode& +PhaseControl::get_state () +{ + XMLNode& node (AutomationControl::get_state ()); + + string p; + boost::to_string (_phase_invert, p); + node.add_property("phase-invert", p); + + return node; +} + +int +PhaseControl::set_state (XMLNode const & node, int version) +{ + AutomationControl::set_state (node, version); + + const XMLProperty* prop; + + if ((prop = node.property (X_("phase-invert"))) != 0) { + set_phase_invert (boost::dynamic_bitset<> (prop->value ())); + } + + return 0; +} diff --git a/libs/ardour/record_enable_control.cc b/libs/ardour/record_enable_control.cc new file mode 100644 index 0000000000..f71fd5034b --- /dev/null +++ b/libs/ardour/record_enable_control.cc @@ -0,0 +1,73 @@ +/* + 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. +*/ + +#include "ardour/audioengine.h" +#include "ardour/record_enable_control.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; + +RecordEnableControl::RecordEnableControl (Session& session, std::string const & name, Recordable& r) + : SlavableAutomationControl (session, RecEnableAutomation, ParameterDescriptor (RecEnableAutomation), + boost::shared_ptr(new AutomationList(Evoral::Parameter(RecEnableAutomation))), + name) + , _recordable (r) +{ + _list->set_interpolation(Evoral::ControlList::Discrete); + + /* record-enable changes must be synchronized by the process cycle */ + set_flags (Controllable::Flag (flags() | Controllable::RealTime)); +} + +void +RecordEnableControl::set_value (double val, Controllable::GroupControlDisposition gcd) +{ + /* do the non-RT part of rec-enabling first - the RT part will be done + * on the next process cycle. This does mean that theoretically we are + * doing things provisionally on the assumption that the rec-enable + * change will work, but this had better be a solid assumption for + * other reasons. + */ + + if (!AudioEngine::instance()->in_process_thread()) { + if (_recordable.prep_record_enabled (val)) { + /* failed */ + std::cerr << "Prep rec-enable failed\n"; + return; + } + } + + /* Because we are marked as a RealTime control, this will queue + up the control change to be executed in a realtime context. + */ + SlavableAutomationControl::set_value (val, gcd); +} + +void +RecordEnableControl::actually_set_value (double val, Controllable::GroupControlDisposition gcd) +{ + if (val && !_recordable.can_be_record_enabled()) { + std::cerr << "rec-enable not allowed\n"; + return; + } + + SlavableAutomationControl::actually_set_value (val, gcd); +} + diff --git a/libs/ardour/slavable_automation_control.cc b/libs/ardour/slavable_automation_control.cc new file mode 100644 index 0000000000..900e640e55 --- /dev/null +++ b/libs/ardour/slavable_automation_control.cc @@ -0,0 +1,226 @@ +/* + 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_slavable_automation_control_h__ +#define __libardour_slavable_automation_control_h__ + +#include "ardour/automation_control.h" +#include "ardour/session.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +SlavableAutomationControl::SlavableAutomationControl(ARDOUR::Session& s, + const Evoral::Parameter& parameter, + const ParameterDescriptor& desc, + boost::shared_ptr l, + const std::string& name) + : AutomationControl (s, parameter, desc, l, name) +{ +} + +SlavableAutomationControl::~SlavableAutomationControl () +{ + +} + +double +SlavableAutomationControl::get_masters_value_locked () const +{ + gain_t v = 1.0; + + for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { + /* get current master value, scale by our current ratio with that master */ + v *= mr->second.master()->get_value () * mr->second.ratio(); + } + + return min (_desc.upper, v); +} + +double +SlavableAutomationControl::get_value_locked() const +{ + /* read or write masters lock must be held */ + + if (_masters.empty()) { + return Control::get_double (false, _session.transport_frame()); + } + + return get_masters_value_locked (); +} + +/** Get the current effective `user' value based on automation state */ +double +SlavableAutomationControl::get_value() const +{ + bool from_list = _list && ((AutomationList*)_list.get())->automation_playback(); + + if (!from_list) { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return get_value_locked (); + } else { + return Control::get_double (from_list, _session.transport_frame()); + } +} + +void +SlavableAutomationControl::add_master (boost::shared_ptr m) +{ + double current_value; + double new_value; + std::pair res; + + { + Glib::Threads::RWLock::WriterLock lm (master_lock); + current_value = get_value_locked (); + + /* ratio will be recomputed below */ + + res = _masters.insert (make_pair (m->id(), MasterRecord (m, 1.0))); + + if (res.second) { + + recompute_masters_ratios (current_value); + + /* note that we bind @param m as a weak_ptr, thus + avoiding holding a reference to the control in the binding + itself. + */ + + m->DropReferences.connect_same_thread (masters_connections, boost::bind (&SlavableAutomationControl::master_going_away, this, m)); + + /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed + and we no longer hear about changes to the AutomationControl. + + Note that we fix the "from_self" argument that will + be given to our own Changed signal to "false", + because the change came from the master. + */ + + + m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::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 +SlavableAutomationControl::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 +SlavableAutomationControl::master_going_away (boost::weak_ptr wm) +{ + boost::shared_ptr m = wm.lock(); + if (m) { + remove_master (m); + } +} + +void +SlavableAutomationControl::remove_master (boost::shared_ptr m) +{ + double current_value; + double new_value; + Masters::size_type erased = 0; + + { + Glib::Threads::RWLock::WriterLock lm (master_lock); + current_value = get_value_locked (); + erased = _masters.erase (m->id()); + if (erased) { + recompute_masters_ratios (current_value); + } + new_value = get_value_locked (); + } + + if (erased) { + MasterStatusChange (); /* EMIT SIGNAL */ + } + + if (new_value != current_value) { + Changed (false, Controllable::NoGroup); + } +} + +void +SlavableAutomationControl::clear_masters () +{ + double current_value; + double new_value; + bool had_masters = false; + + { + Glib::Threads::RWLock::WriterLock lm (master_lock); + current_value = get_value_locked (); + if (!_masters.empty()) { + had_masters = true; + } + _masters.clear (); + new_value = get_value_locked (); + } + + if (had_masters) { + MasterStatusChange (); /* EMIT SIGNAL */ + } + + if (new_value != current_value) { + Changed (false, Controllable::NoGroup); + } + +} + +bool +SlavableAutomationControl::slaved_to (boost::shared_ptr m) const +{ + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return _masters.find (m->id()) != _masters.end(); +} + +bool +SlavableAutomationControl::slaved () const +{ + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return !_masters.empty(); +} + +#endif /* __libardour_slavable_automation_control_h__ */ diff --git a/libs/ardour/solo_control.cc b/libs/ardour/solo_control.cc new file mode 100644 index 0000000000..45ec5b90ec --- /dev/null +++ b/libs/ardour/solo_control.cc @@ -0,0 +1,264 @@ +/* + 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. +*/ + +#include "ardour/debug.h" +#include "ardour/mute_master.h" +#include "ardour/session.h" +#include "ardour/solo_control.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace std; +using namespace PBD; + +SoloControl::SoloControl (Session& session, std::string const & name, Soloable& s, Muteable& m) + : SlavableAutomationControl (session, SoloAutomation, ParameterDescriptor (SoloAutomation), + boost::shared_ptr(new AutomationList(Evoral::Parameter(SoloAutomation))), + name) + , _soloable (s) + , _muteable (m) + , _self_solo (false) + , _soloed_by_others_upstream (0) + , _soloed_by_others_downstream (0) +{ + _list->set_interpolation (Evoral::ControlList::Discrete); + /* solo changes must be synchronized by the process cycle */ + set_flags (Controllable::Flag (flags() | Controllable::RealTime)); +} + +void +SoloControl::set_self_solo (bool yn) +{ + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn)); + _self_solo = yn; + set_mute_master_solo (); +} + +void +SoloControl::set_mute_master_solo () +{ + _muteable.mute_master()->set_soloed_by_self (self_soloed()); + + if (Config->get_solo_control_is_listen_control()) { + _muteable.mute_master()->set_soloed_by_others (false); + } else { + _muteable.mute_master()->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream()); + } +} + +void +SoloControl::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) +{ + if (_soloable.is_safe() || !_soloable.can_solo()) { + 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. + */ + + mod_solo_by_others_upstream (master_soloed ? 1 : -1); + + /* no need to call AutomationControl::master_changed() since it just + emits Changed() which we already did in mod_solo_by_others_upstream() + */ +} + +void +SoloControl::mod_solo_by_others_downstream (int32_t delta) +{ + if (_soloable.is_safe() || !_soloable.can_solo()) { + return; + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", + name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); + + if (delta < 0) { + if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { + _soloed_by_others_downstream += delta; + } else { + _soloed_by_others_downstream = 0; + } + } else { + _soloed_by_others_downstream += delta; + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); + + set_mute_master_solo (); + Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ +} + +void +SoloControl::mod_solo_by_others_upstream (int32_t delta) +{ + if (_soloable.is_safe() || !_soloable.can_solo()) { + return; + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", + name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); + + uint32_t old_sbu = _soloed_by_others_upstream; + + if (delta < 0) { + if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) { + _soloed_by_others_upstream += delta; + } else { + _soloed_by_others_upstream = 0; + } + } else { + _soloed_by_others_upstream += delta; + } + + DEBUG_TRACE (DEBUG::Solo, string_compose ( + "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n", + name(), delta, _soloed_by_others_upstream, old_sbu, + _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo())); + + + /* push the inverse solo change to everything that feeds us. + + This is important for solo-within-group. When we solo 1 track out of N that + feed a bus, that track will cause mod_solo_by_upstream (+1) to be called + on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all + tracks that feed it. This will silence them if they were audible because + of a bus solo, but the newly soloed track will still be audible (because + it is self-soloed). + + but .. do this only when we are being told to solo-by-upstream (i.e delta = +1), + not in reverse. + */ + + if ((_self_solo || _soloed_by_others_downstream) && + ((old_sbu == 0 && _soloed_by_others_upstream > 0) || + (old_sbu > 0 && _soloed_by_others_upstream == 0))) { + + if (delta > 0 || !Config->get_exclusive_solo()) { + _soloable.push_solo_upstream (delta); + } + } + + set_mute_master_solo (); + Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ +} + +void +SoloControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) +{ + if (_soloable.is_safe() || !_soloable.can_solo()) { + return; + } + + set_self_solo (val == 1.0); + + /* this sets the Evoral::Control::_user_value for us, which will + be retrieved by AutomationControl::get_value (), and emits Changed + */ + + AutomationControl::actually_set_value (val, group_override); + _session.set_dirty (); +} + +double +SoloControl::get_value () const +{ + if (slaved()) { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return get_masters_value_locked () ? 1.0 : 0.0; + } + + std::cerr << "solo control @ " << this << " list = " << _list << " as AL " << boost::dynamic_pointer_cast(_list) << std::endl; + + if (_list && boost::dynamic_pointer_cast(_list)->automation_playback()) { + // Playing back automation, get the value from the list + return AutomationControl::get_value(); + } + + return self_soloed() ? 1.0 : 0.0; +} + +void +SoloControl::clear_all_solo_state () +{ + // ideally this function will never do anything, it only exists to forestall Murphy + +#ifndef NDEBUG + // these are really debug messages, but of possible interest. + if (self_soloed()) { + PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name()); + } + if (_soloed_by_others_upstream || _soloed_by_others_downstream) { + PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"), + name(), _soloed_by_others_upstream, _soloed_by_others_downstream); + } +#endif + + _soloed_by_others_upstream = 0; + _soloed_by_others_downstream = 0; + + set_self_solo (false); + + Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ +} + +int +SoloControl::set_state (XMLNode const & node, int) +{ + XMLProperty const * prop; + + if ((prop = node.property ("self-solo")) != 0) { + set_self_solo (string_is_affirmative (prop->value())); + } + + if ((prop = node.property ("soloed-by-upstream")) != 0) { + _soloed_by_others_upstream = 0; // needed for mod_.... () to work + mod_solo_by_others_upstream (atoi (prop->value())); + } + + if ((prop = node.property ("soloed-by-downstream")) != 0) { + _soloed_by_others_downstream = 0; // needed for mod_.... () to work + mod_solo_by_others_downstream (atoi (prop->value())); + } + + return 0; +} + +XMLNode& +SoloControl::get_state () +{ + XMLNode& node (SlavableAutomationControl::get_state()); + + node.add_property (X_("self-solo"), _self_solo ? X_("yes") : X_("no")); + char buf[32]; + snprintf (buf, sizeof(buf), "%d", _soloed_by_others_upstream); + node.add_property (X_("soloed-by-upstream"), buf); + snprintf (buf, sizeof(buf), "%d", _soloed_by_others_downstream); + node.add_property (X_("soloed-by-downstream"), buf); + + return node; +} diff --git a/libs/ardour/solo_isolate_control.cc b/libs/ardour/solo_isolate_control.cc new file mode 100644 index 0000000000..d85a973ba3 --- /dev/null +++ b/libs/ardour/solo_isolate_control.cc @@ -0,0 +1,180 @@ +/* + 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. +*/ + +#include "ardour/debug.h" +#include "ardour/mute_master.h" +#include "ardour/session.h" +#include "ardour/solo_isolate_control.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace std; +using namespace PBD; + +SoloIsolateControl::SoloIsolateControl (Session& session, std::string const & name, Soloable& s, Muteable& m) + : SlavableAutomationControl (session, SoloAutomation, ParameterDescriptor (SoloIsolateAutomation), + boost::shared_ptr(new AutomationList(Evoral::Parameter(SoloIsolateAutomation))), + name) + , _soloable (s) + , _muteable (m) + , _solo_isolated (false) + , _solo_isolated_by_upstream (0) +{ + _list->set_interpolation (Evoral::ControlList::Discrete); + /* isolate changes must be synchronized by the process cycle */ + set_flags (Controllable::Flag (flags() | Controllable::RealTime)); +} + +void +SoloIsolateControl::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) +{ + if (!_soloable.can_solo()) { + 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. + */ + + mod_solo_isolated_by_upstream (master_soloed ? 1 : -1); + + /* no need to call AutomationControl::master_changed() since it just + emits Changed() which we already did in mod_solo_by_others_upstream() + */ +} + +void +SoloIsolateControl::mod_solo_isolated_by_upstream (int32_t delta) +{ + bool old = solo_isolated (); + DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n", + name(), _solo_isolated_by_upstream, delta)); + + if (delta < 0) { + if (_solo_isolated_by_upstream >= (uint32_t) abs(delta)) { + _solo_isolated_by_upstream += delta; + } else { + _solo_isolated_by_upstream = 0; + } + } else { + _solo_isolated_by_upstream += delta; + } + + if (solo_isolated() != old) { + /* solo isolated status changed */ + _muteable.mute_master()->set_solo_ignore (solo_isolated()); + Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ + } +} + +void +SoloIsolateControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition gcd) +{ + if (!_soloable.can_solo()) { + return; + } + + set_solo_isolated (val == 0.0 ? false : true, gcd); + + /* this sets the Evoral::Control::_user_value for us, which will + be retrieved by AutomationControl::get_value (), and emits Changed + */ + + AutomationControl::actually_set_value (val, gcd); + _session.set_dirty (); +} + +void +SoloIsolateControl::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override) +{ + if (_soloable.can_solo()) { + return; + } + + bool changed = false; + + if (yn) { + if (_solo_isolated == false) { + _muteable.mute_master()->set_solo_ignore (true); + changed = true; + } + _solo_isolated = true; + } else { + if (_solo_isolated == true) { + _solo_isolated = false; + _muteable.mute_master()->set_solo_ignore (false); + changed = true; + } + } + + + if (!changed) { + return; + } + + _soloable.push_solo_isolate_upstream (yn ? 1 : -1); + + /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ + + Changed (true, group_override); /* EMIT SIGNAL */ +} + + +double +SoloIsolateControl::get_value () const +{ + if (slaved()) { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return get_masters_value_locked () ? 1.0 : 0.0; + } + + if (_list && boost::dynamic_pointer_cast(_list)->automation_playback()) { + // Playing back automation, get the value from the list + return AutomationControl::get_value(); + } + + return solo_isolated () ? 1.0 : 0.0; +} + +int +SoloIsolateControl::set_state (XMLNode const & node, int) +{ + XMLProperty const * prop; + + if ((prop = node.property ("solo-isolated")) != 0) { + _solo_isolated = string_is_affirmative (prop->value()); + } + + return 0; +} + +XMLNode& +SoloIsolateControl::get_state () +{ + XMLNode& node (SlavableAutomationControl::get_state()); + node.add_property (X_("solo-isolated"), _solo_isolated ? X_("yes") : X_("no")); + return node; +} diff --git a/libs/ardour/solo_safe_control.cc b/libs/ardour/solo_safe_control.cc new file mode 100644 index 0000000000..040f454ffc --- /dev/null +++ b/libs/ardour/solo_safe_control.cc @@ -0,0 +1,86 @@ +/* + 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. +*/ + +#include "ardour/debug.h" +#include "ardour/mute_master.h" +#include "ardour/session.h" +#include "ardour/solo_isolate_control.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace std; +using namespace PBD; + +SoloSafeControl::SoloSafeControl (Session& session, std::string const & name) + : SlavableAutomationControl (session, SoloAutomation, ParameterDescriptor (SoloSafeAutomation), + boost::shared_ptr(new AutomationList(Evoral::Parameter(SoloSafeAutomation))), + name) + , _solo_safe (false) +{ + _list->set_interpolation(Evoral::ControlList::Discrete); +} + +void +SoloSafeControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition gcd) +{ + _solo_safe = (val ? true : false); + + /* this sets the Evoral::Control::_user_value for us, which will + be retrieved by AutomationControl::get_value (), and emits Changed + */ + + AutomationControl::actually_set_value (val, gcd); + _session.set_dirty (); +} + +double +SoloSafeControl::get_value () const +{ + if (slaved()) { + Glib::Threads::RWLock::ReaderLock lm (master_lock); + return get_masters_value_locked () ? 1.0 : 0.0; + } + + if (_list && boost::dynamic_pointer_cast(_list)->automation_playback()) { + // Playing back automation, get the value from the list + return AutomationControl::get_value(); + } + + return _solo_safe ? 1.0 : 0.0; +} + +int +SoloSafeControl::set_state (XMLNode const & node, int) +{ + XMLProperty const * prop; + + if ((prop = node.property ("solo-safe")) != 0) { + _solo_safe = string_is_affirmative (prop->value()); + } + + return 0; +} + +XMLNode& +SoloSafeControl::get_state () +{ + XMLNode& node (SlavableAutomationControl::get_state()); + node.add_property (X_("solo-safe"), _solo_safe ? X_("yes") : X_("no")); + return node; +} -- cgit v1.2.3