diff options
Diffstat (limited to 'libs')
44 files changed, 715 insertions, 2301 deletions
diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 455e8891c1..d84f0a091f 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -41,7 +41,7 @@ namespace ARDOUR { class Session; class Automatable; - +class ControlGroup; /** A PBD::Controllable with associated automation data (AutomationList) */ @@ -50,7 +50,7 @@ class LIBARDOUR_API AutomationControl , public Evoral::Control , public boost::enable_shared_from_this<AutomationControl> { -public: + public: AutomationControl(ARDOUR::Session&, const Evoral::Parameter& parameter, const ParameterDescriptor& desc, @@ -87,8 +87,6 @@ public: void stop_touch(bool mark, double when); /* inherited from PBD::Controllable. - * Derived classes MUST call ::writable() to verify - * that writing to the parameter is legal at that time. */ double get_value () const; /* inherited from PBD::Controllable. @@ -99,10 +97,11 @@ public: /* automation related value setting */ virtual bool writable () const; /* Call to ::set_value() with no test for writable() because - * this is only used by automation playback. We would like - * to make it pure virtual + * this is only used by automation playback. */ - virtual void set_value_unchecked (double val) {} + void set_value_unchecked (double val) { + actually_set_value (val, PBD::Controllable::NoGroup); + } double lower() const { return _desc.lower; } double upper() const { return _desc.upper; } @@ -117,6 +116,37 @@ public: const ARDOUR::Session& session() const { return _session; } void commit_transaction (bool did_write); + void set_group (boost::shared_ptr<ControlGroup>); + + protected: + ARDOUR::Session& _session; + boost::shared_ptr<ControlGroup> _group; + + const ParameterDescriptor _desc; + + bool check_rt (double val, Controllable::GroupControlDisposition gcd); + + /* derived classes may reimplement this, but should either + call this explicitly inside their version OR make sure that the + Controllable::Changed signal is emitted when necessary. + */ + + virtual void actually_set_value (double value, PBD::Controllable::GroupControlDisposition); +}; + +class SlavableAutomationControl : public AutomationControl +{ + public: + SlavableAutomationControl(ARDOUR::Session&, + const Evoral::Parameter& parameter, + const ParameterDescriptor& desc, + boost::shared_ptr<ARDOUR::AutomationList> l=boost::shared_ptr<ARDOUR::AutomationList>(), + const std::string& name=""); + + ~SlavableAutomationControl (); + + double get_value () const; + void add_master (boost::shared_ptr<AutomationControl>); void remove_master (boost::shared_ptr<AutomationControl>); void clear_masters (); @@ -126,11 +156,7 @@ public: PBD::Signal0<void> MasterStatusChange; - protected: - ARDOUR::Session& _session; - - const ParameterDescriptor _desc; - + protected: class MasterRecord { public: @@ -155,12 +181,12 @@ public: typedef std::map<PBD::ID,MasterRecord> Masters; Masters _masters; PBD::ScopedConnectionList masters_connections; - virtual void master_changed (bool from_self, GroupControlDisposition gcd); void master_going_away (boost::weak_ptr<AutomationControl>); virtual void recompute_masters_ratios (double val) { /* do nothing by default */} virtual double get_masters_value_locked () const; double get_value_locked() const; + }; diff --git a/libs/ardour/ardour/gain_control.h b/libs/ardour/ardour/gain_control.h index 4ec538e698..f68ec00452 100644 --- a/libs/ardour/ardour/gain_control.h +++ b/libs/ardour/ardour/gain_control.h @@ -34,14 +34,11 @@ namespace ARDOUR { class Session; -class LIBARDOUR_API GainControl : public AutomationControl { +class LIBARDOUR_API GainControl : public SlavableAutomationControl { public: GainControl (Session& session, const Evoral::Parameter ¶m, boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>()); - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double internal_to_interface (double) const; double interface_to_internal (double) const; double internal_to_user (double) const; @@ -54,6 +51,8 @@ class LIBARDOUR_API GainControl : public AutomationControl { int set_state (XMLNode const&, int); XMLNode& get_state(); + void inc_gain (gain_t); + private: std::string masters_string; PBD::ScopedConnection vca_loaded_connection; @@ -61,7 +60,7 @@ class LIBARDOUR_API GainControl : public AutomationControl { void vcas_loaded(); void recompute_masters_ratios (double val); - void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override); }; } /* namespace */ diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index ed13832360..69f4981047 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -50,8 +50,9 @@ public: boost::shared_ptr<Diskstream> create_diskstream (); void set_diskstream (boost::shared_ptr<Diskstream>); - void set_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition); - void set_record_safe (bool yn, PBD::Controllable::GroupControlDisposition); + + bool can_be_record_enabled (); + bool can_be_record_safe (); DataType data_type () const { return DataType::MIDI; @@ -89,14 +90,12 @@ public: , _route (route) {} - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); bool writable() const { return true; } MidiTrack* _route; private: - void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override); }; virtual void set_parameter_automation_state (Evoral::Parameter param, AutoState); @@ -133,7 +132,6 @@ public: PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded; boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const; - void set_monitoring (MonitorChoice, PBD::Controllable::GroupControlDisposition); MonitorState monitoring_state () const; void set_input_active (bool); @@ -144,6 +142,7 @@ protected: XMLNode& state (bool full); void act_on_mute (); + void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition); private: MidiRingBuffer<framepos_t> _immediate_events; diff --git a/libs/ardour/ardour/mute_master.h b/libs/ardour/ardour/mute_master.h index 14597cb56b..d147a58771 100644 --- a/libs/ardour/ardour/mute_master.h +++ b/libs/ardour/ardour/mute_master.h @@ -20,12 +20,15 @@ #ifndef __ardour_mute_master_h__ #define __ardour_mute_master_h__ -#include "evoral/Parameter.hpp" +#include <string> + #include "pbd/signals.h" #include "pbd/stateful.h" -#include <string> + +#include "evoral/Parameter.hpp" #include "ardour/session_handle.h" +#include "ardour/types.h" namespace ARDOUR { @@ -73,6 +76,7 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful XMLNode& get_state(); int set_state(const XMLNode&, int version); + static const std::string xml_node_name; private: MutePoint _mute_point; diff --git a/libs/ardour/ardour/pan_controllable.h b/libs/ardour/ardour/pan_controllable.h index 85a4efe2fc..26f0707ff7 100644 --- a/libs/ardour/ardour/pan_controllable.h +++ b/libs/ardour/ardour/pan_controllable.h @@ -47,12 +47,10 @@ class LIBARDOUR_API PanControllable : public AutomationControl {} double lower () const; - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); private: Pannable* owner; - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); }; } // namespace diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index 4b5f38964a..70bbd76a71 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -185,15 +185,13 @@ class LIBARDOUR_API PluginInsert : public Processor const ParameterDescriptor& desc, boost::shared_ptr<AutomationList> list=boost::shared_ptr<AutomationList>()); - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); double get_value (void) const; void catch_up_with_external_value (double val); XMLNode& get_state(); private: PluginInsert* _plugin; - void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override); }; /** A control that manipulates a plugin property (message). */ @@ -204,10 +202,10 @@ class LIBARDOUR_API PluginInsert : public Processor const ParameterDescriptor& desc, boost::shared_ptr<AutomationList> list=boost::shared_ptr<AutomationList>()); - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); double get_value (void) const; XMLNode& get_state(); + protected: + void actually_set_value (double value, PBD::Controllable::GroupControlDisposition); private: PluginInsert* _plugin; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 9abe56afcb..6c0380963d 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -28,7 +28,6 @@ #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> -#include <boost/dynamic_bitset.hpp> #include <boost/enable_shared_from_this.hpp> #include <glibmm/threads.h> @@ -46,12 +45,18 @@ #include "ardour/io_vector.h" #include "ardour/libardour_visibility.h" #include "ardour/types.h" +#include "ardour/monitorable.h" +#include "ardour/muteable.h" #include "ardour/mute_master.h" +#include "ardour/mute_control.h" #include "ardour/route_group_member.h" #include "ardour/stripable.h" #include "ardour/graphnode.h" #include "ardour/automatable.h" #include "ardour/unknown_processor.h" +#include "ardour/soloable.h" +#include "ardour/solo_control.h" +#include "ardour/solo_safe_control.h" class RoutePinWindowProxy; @@ -74,8 +79,18 @@ class Pannable; class CapturingProcessor; class InternalSend; class VCA; - -class LIBARDOUR_API Route : public Stripable, public Automatable, public RouteGroupMember, public GraphNode, public boost::enable_shared_from_this<Route> +class SoloIsolateControl; +class PhaseControl; +class MonitorControl; + +class LIBARDOUR_API Route : public Stripable, + public Soloable, + public Muteable, + public Monitorable, + public Automatable, + public RouteGroupMember, + public GraphNode, + public boost::enable_shared_from_this<Route> { public: @@ -119,7 +134,7 @@ public: bool is_master() const { return _flags & MasterOut; } bool is_monitor() const { return _flags & MonitorOut; } - virtual MonitorState monitoring_state () const; + MonitorState monitoring_state () const; virtual MeterState metering_state () const; /* these are the core of the API of a Route. see the protected sections as well */ @@ -135,10 +150,6 @@ public: virtual bool can_record() { return false; } - virtual void set_record_enabled (bool /*yn*/, PBD::Controllable::GroupControlDisposition) {} - virtual bool record_enabled() const { return false; } - virtual void set_record_safe (bool /*yn*/, PBD::Controllable::GroupControlDisposition) {} - virtual bool record_safe () const {return false; } virtual void nonrealtime_handle_transport_stopped (bool abort, bool did_locate, bool flush_processors); virtual void realtime_handle_transport_stopped () {} virtual void realtime_locate () {} @@ -149,47 +160,31 @@ public: void shift (framepos_t, framecnt_t); - void set_gain (gain_t val, PBD::Controllable::GroupControlDisposition); - void inc_gain (gain_t delta); - void set_trim (gain_t val, PBD::Controllable::GroupControlDisposition); - void set_mute_points (MuteMaster::MutePoint); - MuteMaster::MutePoint mute_points () const; - - bool muted () const; - void set_mute (bool yn, PBD::Controllable::GroupControlDisposition); - - bool muted_by_others_soloing () const; - bool muted_by_others () const; - /* controls use set_solo() to modify this route's solo state */ - void set_solo (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - bool soloed () const { return self_soloed () || soloed_by_others (); } void clear_all_solo_state (); - bool soloed_by_others () const { return _soloed_by_others_upstream||_soloed_by_others_downstream; } - bool soloed_by_others_upstream () const { return _soloed_by_others_upstream; } - bool soloed_by_others_downstream () const { return _soloed_by_others_downstream; } - bool self_soloed () const { return _self_solo; } - - void set_solo_isolated (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - bool solo_isolated() const; + bool soloed_by_others () const { return _solo_control->soloed_by_others(); } + bool soloed_by_others_upstream () const { return _solo_control->soloed_by_others_upstream(); } + bool soloed_by_others_downstream () const { return _solo_control->soloed_by_others_downstream(); } + bool self_soloed () const { return _solo_control->self_soloed(); } + bool soloed () const { return self_soloed () || soloed_by_others (); } - void set_solo_safe (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - bool solo_safe() const; + void push_solo_upstream (int32_t delta); + void push_solo_isolate_upstream (int32_t delta); + bool can_solo () const { + return !(is_master() || is_monitor() || is_auditioner()); + } + bool is_safe () const { + return _solo_safe_control->get_value(); + } - void set_listen (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); bool listening_via_monitor () const; void enable_monitor_send (); - void set_phase_invert (uint32_t, bool yn); - void set_phase_invert (boost::dynamic_bitset<>); - bool phase_invert (uint32_t) const; - boost::dynamic_bitset<> phase_invert () const; - void set_denormal_protection (bool yn); bool denormal_protection() const; @@ -353,7 +348,6 @@ public: PBD::Signal0<void> active_changed; PBD::Signal0<void> denormal_protection_changed; PBD::Signal0<void> comment_changed; - PBD::Signal0<void> mute_points_changed; /** track numbers - assigned by session * nubers > 0 indicate tracks (audio+midi) @@ -456,185 +450,32 @@ public: boost::shared_ptr<AutomationControl> get_control (const Evoral::Parameter& param); - class RouteAutomationControl : public AutomationControl { - public: - RouteAutomationControl (const std::string& name, - AutomationType atype, - boost::shared_ptr<AutomationList> alist, - boost::shared_ptr<Route> route); - protected: - friend class Route; - - void route_set_value (double val) { - AutomationControl::set_value (val, Controllable::NoGroup); - } - - boost::weak_ptr<Route> _route; - }; - - class BooleanRouteAutomationControl : public RouteAutomationControl { - public: - BooleanRouteAutomationControl (const std::string& name, - AutomationType atype, - boost::shared_ptr<AutomationList> alist, - boost::shared_ptr<Route> route) - : RouteAutomationControl (name, atype, alist, route) {} - protected: - double get_masters_value_locked() const; - - }; - - class GainControllable : public GainControl { - public: - GainControllable (Session& session, - AutomationType type, - boost::shared_ptr<Route> route); - - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override) { - boost::shared_ptr<Route> r = _route.lock(); - if (r) { - /* Route must mediate group control */ - r->set_control ((AutomationType) parameter().type(), val, group_override); - } - } - - protected: - friend class Route; - - void route_set_value (double val) { - GainControl::set_value (val, Controllable::NoGroup); - } - - boost::weak_ptr<Route> _route; - }; - - class SoloControllable : public BooleanRouteAutomationControl { - public: - SoloControllable (std::string name, boost::shared_ptr<Route>); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - - /* Export additional API so that objects that only get access - * to a Controllable/AutomationControl can do more fine-grained - * operations with respect to solo. Obviously, they would need - * to dynamic_cast<Route::SoloControllable> first. - * - * Solo state is not representable by a single scalar value, - * so this AutomationControl maps set_value() and get_value() - * to r->set_self_solo() and r->soloed() respectively. This - * means that the Controllable is technically asymmetric. It is - * possible to call ::set_value (0.0) to disable (self)solo, - * and then call ::get_value() and get a return of 1.0 because - * the Route owner is soloed by upstream/downstream. - */ - - void set_self_solo (bool yn) { - boost::shared_ptr<Route> r(_route.lock()); if (r) r->set_self_solo (yn); - } - void mod_solo_by_others_upstream (int32_t delta) { - boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_upstream (delta); - } - void mod_solo_by_others_downstream (int32_t delta) { - boost::shared_ptr<Route> r(_route.lock()); if (r) r->mod_solo_by_others_downstream (delta); - } - bool soloed_by_others () const { - boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others(); else return false; - } - bool soloed_by_others_upstream () const { - boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_upstream(); else return false; - } - bool soloed_by_others_downstream () const { - boost::shared_ptr<Route> r(_route.lock()); if (r) return r->soloed_by_others_downstream(); else return false; - } - bool self_soloed () const { - boost::shared_ptr<Route> r(_route.lock()); if (r) return r->self_soloed(); else return false; - } - - protected: - void master_changed (bool, PBD::Controllable::GroupControlDisposition); - - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - struct MuteControllable : public BooleanRouteAutomationControl { - public: - MuteControllable (std::string name, boost::shared_ptr<Route>); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - - /* Pretend to change value, but do not affect actual route mute. */ - void set_superficial_value(bool muted); - protected: - void master_changed (bool, PBD::Controllable::GroupControlDisposition); - - private: - boost::weak_ptr<Route> _route; - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - class LIBARDOUR_API PhaseControllable : public BooleanRouteAutomationControl { - public: - PhaseControllable (std::string name, boost::shared_ptr<Route>); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - void set_channel (uint32_t); - double get_value () const; - uint32_t channel() const; - private: - uint32_t _current_phase; - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - class LIBARDOUR_API SoloIsolateControllable : public BooleanRouteAutomationControl { - public: - SoloIsolateControllable (std::string name, boost::shared_ptr<Route>); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - class LIBARDOUR_API SoloSafeControllable : public BooleanRouteAutomationControl { - public: - SoloSafeControllable (std::string name, boost::shared_ptr<Route>); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - void set_control (AutomationType, double val, PBD::Controllable::GroupControlDisposition group_override); - - boost::shared_ptr<AutomationControl> solo_control() const { + boost::shared_ptr<SoloControl> solo_control() const { return _solo_control; } - boost::shared_ptr<AutomationControl> mute_control() const { + boost::shared_ptr<MuteControl> mute_control() const { return _mute_control; } - boost::shared_ptr<MuteMaster> mute_master() const { - return _mute_master; - } + bool can_be_muted_by_others () const { return !is_master(); } + bool muted () const { return _mute_control->muted(); } + bool muted_by_others_soloing () const; + bool muted_by_others () const; - boost::shared_ptr<AutomationControl> solo_isolate_control() const { + boost::shared_ptr<SoloIsolateControl> solo_isolate_control() const { return _solo_isolate_control; } - boost::shared_ptr<AutomationControl> solo_safe_control() const { + boost::shared_ptr<SoloSafeControl> solo_safe_control() const { return _solo_safe_control; } - boost::shared_ptr<AutomationControl> monitoring_control() const { + boost::shared_ptr<MonitorControl> monitoring_control() const { /* tracks override this to provide actual monitoring control; busses have no possible choices except input monitoring. */ - return boost::shared_ptr<AutomationControl> (); + return boost::shared_ptr<MonitorControl> (); } /* Route doesn't own these items, but sub-objects that it does own have them @@ -647,8 +488,8 @@ public: boost::shared_ptr<Pannable> pannable() const; boost::shared_ptr<GainControl> gain_control() const; - boost::shared_ptr<AutomationControl> trim_control() const; - boost::shared_ptr<AutomationControl> phase_control() const; + boost::shared_ptr<GainControl> trim_control() const; + boost::shared_ptr<PhaseControl> phase_control() const; /** Return the first processor that accepts has at least one MIDI input @@ -766,8 +607,8 @@ public: friend class Session; void catch_up_on_solo_mute_override (); - void mod_solo_by_others_upstream (int32_t); - void mod_solo_by_others_downstream (int32_t); + void set_listen (bool); + void curve_reallocate (); virtual void set_block_size (pframes_t nframes); @@ -829,14 +670,6 @@ protected: MeterPoint _meter_point; MeterPoint _pending_meter_point; MeterType _meter_type; - boost::dynamic_bitset<> _phase_invert; - bool _self_solo; - uint32_t _soloed_by_others_upstream; - uint32_t _soloed_by_others_downstream; - bool _solo_isolated; - uint32_t _solo_isolated_by_upstream; - - void mod_solo_isolated_by_upstream (bool); bool _denormal_protection; @@ -844,18 +677,14 @@ protected: bool _silent : 1; bool _declickable : 1; - boost::shared_ptr<SoloControllable> _solo_control; - boost::shared_ptr<MuteControllable> _mute_control; - boost::shared_ptr<MuteMaster> _mute_master; - boost::shared_ptr<PhaseControllable> _phase_control; - boost::shared_ptr<SoloIsolateControllable> _solo_isolate_control; - boost::shared_ptr<SoloSafeControllable> _solo_safe_control; - - virtual void act_on_mute () {} + boost::shared_ptr<SoloControl> _solo_control; + boost::shared_ptr<MuteControl> _mute_control; + boost::shared_ptr<PhaseControl> _phase_control; + boost::shared_ptr<SoloIsolateControl> _solo_isolate_control; + boost::shared_ptr<SoloSafeControl> _solo_safe_control; std::string _comment; bool _have_internal_generator; - bool _solo_safe; DataType _default_type; FedBy _fed_by; @@ -882,9 +711,9 @@ protected: virtual void maybe_declick (BufferSet&, framecnt_t, int); - boost::shared_ptr<GainControllable> _gain_control; + boost::shared_ptr<GainControl> _gain_control; boost::shared_ptr<Amp> _amp; - boost::shared_ptr<GainControllable> _trim_control; + boost::shared_ptr<GainControl> _trim_control; boost::shared_ptr<Amp> _trim; boost::shared_ptr<PeakMeter> _meter; boost::shared_ptr<DelayLine> _delayline; @@ -928,7 +757,6 @@ private: void placement_range (Placement p, ProcessorList::iterator& start, ProcessorList::iterator& end); void set_self_solo (bool yn); - void set_mute_master_solo (); void set_processor_positions (); framecnt_t update_port_latencies (PortSet& ports, PortSet& feeders, bool playback, framecnt_t) const; @@ -985,6 +813,7 @@ private: void reset_instrument_info (); void set_remote_control_id_internal (uint32_t id, bool notify_class_listeners = true); + void solo_control_changed (bool self, PBD::Controllable::GroupControlDisposition); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/route_group.h b/libs/ardour/ardour/route_group.h index 95affaeaf5..feeac6a467 100644 --- a/libs/ardour/ardour/route_group.h +++ b/libs/ardour/ardour/route_group.h @@ -29,10 +29,12 @@ #include "pbd/signals.h" #include "pbd/stateful.h" -#include "ardour/libardour_visibility.h" +#include "ardour/control_group.h" #include "ardour/types.h" #include "ardour/session_object.h" +#include "ardour/libardour_visibility.h" + namespace ARDOUR { namespace Properties { @@ -157,8 +159,17 @@ class LIBARDOUR_API RouteGroup : public SessionObject PBD::Property<bool> _color; PBD::Property<bool> _monitoring; + boost::shared_ptr<ControlGroup> _solo_group; + boost::shared_ptr<ControlGroup> _mute_group; + boost::shared_ptr<ControlGroup> _rec_enable_group; + boost::shared_ptr<ControlGroup> _gain_group; + boost::shared_ptr<ControlGroup> _monitoring_group; + void remove_when_going_away (boost::weak_ptr<Route>); int set_state_2X (const XMLNode&, int); + + void post_set (PBD::PropertyChange const &); + void push_to_groups (); }; } /* namespace */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 18507ee5d9..786751dea5 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -791,16 +791,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop static const SessionEvent::RTeventCallback rt_cleanup; - void set_solo (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_implicit_solo (boost::shared_ptr<RouteList>, int delta, bool up_or_downstream, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); void clear_all_solo_state (boost::shared_ptr<RouteList>); - void set_just_one_solo (boost::shared_ptr<Route>, bool, SessionEvent::RTeventCallback after = rt_cleanup); - void set_mute (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_listen (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_record_enabled (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_record_safe (boost::shared_ptr<RouteList>, bool yn, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_solo_isolated (boost::shared_ptr<RouteList>, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_monitoring (boost::shared_ptr<RouteList>, MonitorChoice, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); + + /* Control-based methods */ + + void set_controls (boost::shared_ptr<ControlList>, double val, PBD::Controllable::GroupControlDisposition); + void set_control (boost::shared_ptr<AutomationControl>, double val, PBD::Controllable::GroupControlDisposition); + void set_exclusive_input_active (boost::shared_ptr<RouteList> rt, bool onoff, bool flip_others = false); PBD::Signal1<void,bool> SoloActive; @@ -1936,16 +1933,18 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop return ev; } - void rt_set_solo (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_implicit_solo (boost::shared_ptr<RouteList>, int delta, bool up_or_downstream, PBD::Controllable::GroupControlDisposition); + /* specialized version realtime "apply to set of controls" operations */ + SessionEvent* get_rt_event (boost::shared_ptr<ControlList> cl, double arg, PBD::Controllable::GroupControlDisposition group_override) { + SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); + ev->rt_slot = boost::bind (&Session::rt_set_controls, this, cl, arg, group_override); + ev->rt_return = Session::rt_cleanup; + ev->event_loop = PBD::EventLoop::get_event_loop_for_thread (); + + return ev; + } + + void rt_set_controls (boost::shared_ptr<ControlList>, double val, PBD::Controllable::GroupControlDisposition group_override); void rt_clear_all_solo_state (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_just_one_solo (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition /* ignored*/ ); - void rt_set_mute (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_listen (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_solo_isolated (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_record_enabled (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_record_safe (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_monitoring (boost::shared_ptr<RouteList>, MonitorChoice, PBD::Controllable::GroupControlDisposition group_override); /** temporary list of Diskstreams used only during load of 2.X sessions */ std::list<boost::shared_ptr<Diskstream> > _diskstreams_2X; diff --git a/libs/ardour/ardour/session_event.h b/libs/ardour/ardour/session_event.h index cca7f1274e..8bb160b90e 100644 --- a/libs/ardour/ardour/session_event.h +++ b/libs/ardour/ardour/session_event.h @@ -84,26 +84,28 @@ public: union { void* ptr; bool yes_or_no; - framepos_t target2_frame; + framepos_t target2_frame; Slave* slave; Route* route; }; union { bool second_yes_or_no; + double control_value; }; union { bool third_yes_or_no; }; - /* 4 members to handle a multi-group event handled in RT context */ + /* 5 members to handle a multi-group event handled in RT context */ typedef boost::function<void (SessionEvent*)> RTeventCallback; - boost::shared_ptr<RouteList> routes; /* apply to */ - boost::function<void (void)> rt_slot; /* what to call in RT context */ - RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */ + boost::shared_ptr<ControlList> controls; /* apply to */ + boost::shared_ptr<RouteList> routes; /* apply to */ + boost::function<void (void)> rt_slot; /* what to call in RT context */ + RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */ PBD::EventLoop* event_loop; std::list<AudioRange> audio_range; diff --git a/libs/ardour/ardour/stripable.h b/libs/ardour/ardour/stripable.h index e97fe79103..f68cb07b91 100644 --- a/libs/ardour/ardour/stripable.h +++ b/libs/ardour/ardour/stripable.h @@ -26,13 +26,19 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "ardour/gain_control.h" #include "ardour/session_object.h" namespace ARDOUR { class AutomationControl; +class GainControl; class PeakMeter; +class SoloControl; +class MuteControl; +class PhaseControl; +class SoloIsolateControl; +class SoloSafeControl; +class MonitorControl; /* This is a virtual base class for any object that needs to be potentially * represented by a control-centric user interface using the general model of a @@ -58,12 +64,13 @@ class Stripable : public SessionObject { virtual boost::shared_ptr<GainControl> gain_control() const = 0; - virtual boost::shared_ptr<AutomationControl> solo_control() const = 0; - virtual boost::shared_ptr<AutomationControl> mute_control() const = 0; - virtual boost::shared_ptr<AutomationControl> phase_control() const = 0; - virtual boost::shared_ptr<AutomationControl> trim_control() const = 0; + virtual boost::shared_ptr<SoloControl> solo_control() const = 0; + virtual boost::shared_ptr<MuteControl> mute_control() const = 0; - virtual boost::shared_ptr<AutomationControl> monitoring_control() const = 0; + virtual boost::shared_ptr<PhaseControl> phase_control() const = 0; + virtual boost::shared_ptr<GainControl> trim_control() const = 0; + + virtual boost::shared_ptr<MonitorControl> monitoring_control() const = 0; virtual boost::shared_ptr<AutomationControl> recenable_control() const { return boost::shared_ptr<AutomationControl>(); } /* "well-known" controls for panning. Any or all of these may return @@ -131,6 +138,9 @@ class Stripable : public SessionObject { * the route. */ virtual boost::shared_ptr<AutomationControl> master_send_enable_controllable () const = 0; + + virtual bool muted_by_others_soloing () const = 0; + virtual bool muted_by_others () const = 0; }; diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index b378021bce..bb955265c8 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -22,6 +22,7 @@ #include <boost/shared_ptr.hpp> #include "ardour/interthread_info.h" +#include "ardour/recordable.h" #include "ardour/route.h" #include "ardour/public_diskstream.h" @@ -34,13 +35,14 @@ class Source; class Region; class Diskstream; class IO; +class MonitorControl; /** A track is an route (bus) with a recordable diskstream and * related objects relevant to tracking, playback and editing. * * Specifically a track has regions and playlist objects. */ -class LIBARDOUR_API Track : public Route, public PublicDiskstream +class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskstream { public: Track (Session&, std::string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal, DataType default_type = DataType::AUDIO); @@ -56,23 +58,9 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream virtual bool can_use_mode (TrackMode /*m*/, bool& /*bounce_required*/) { return false; } PBD::Signal0<void> TrackModeChanged; - class LIBARDOUR_API MonitoringControllable : public RouteAutomationControl { - public: - MonitoringControllable (std::string name, boost::shared_ptr<Track>); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - void set_monitoring (MonitorChoice, PBD::Controllable::GroupControlDisposition group_override); - MonitorChoice monitoring_choice() const { return _monitoring; } - MonitorState monitoring_state () const; - PBD::Signal0<void> MonitoringChanged; - - boost::shared_ptr<AutomationControl> monitoring_control() const { return _monitoring_control; } + boost::shared_ptr<MonitorControl> monitoring_control() const { return _monitoring_control; } + MonitorState monitoring_state () const; MeterState metering_state () const; virtual int no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, @@ -142,13 +130,12 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream virtual int set_state (const XMLNode&, int version); static void zero_diskstream_id_in_xml (XMLNode&); - boost::shared_ptr<AutomationControl> rec_enable_control() { return _rec_enable_control; } + boost::shared_ptr<AutomationControl> rec_enable_control() const { return _record_enable_control; } + boost::shared_ptr<AutomationControl> rec_safe_control() const { return _record_safe_control; } - bool record_enabled() const; - bool record_safe () const; - void set_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition); - void set_record_safe (bool yn, PBD::Controllable::GroupControlDisposition); - void prep_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition); + int prep_record_enabled (bool); + bool can_be_record_enabled (); + bool can_be_record_safe (); bool using_diskstream_id (PBD::ID) const; @@ -204,8 +191,6 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream PBD::Signal0<void> FreezeChange; /* Emitted when our diskstream is set to use a different playlist */ PBD::Signal0<void> PlaylistChanged; - PBD::Signal0<void> RecordEnableChanged; - PBD::Signal0<void> RecordSafeChanged; PBD::Signal0<void> SpeedChanged; PBD::Signal0<void> AlignmentStyleChanged; @@ -216,8 +201,7 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream MeterPoint _saved_meter_point; TrackMode _mode; bool _needs_butler; - MonitorChoice _monitoring; - boost::shared_ptr<MonitoringControllable> _monitoring_control; + boost::shared_ptr<MonitorControl> _monitoring_control; //private: (FIXME) struct FreezeRecordProcessorInfo { @@ -242,20 +226,6 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream FreezeState state; }; - class RecEnableControl : public AutomationControl { - public: - RecEnableControl (boost::shared_ptr<Track> t); - - void set_value (double, PBD::Controllable::GroupControlDisposition); - void set_value_unchecked (double); - double get_value (void) const; - - boost::weak_ptr<Track> track; - - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition); - }; - virtual void set_state_part_two () = 0; FreezeRecord _freeze_record; @@ -264,17 +234,20 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream void maybe_declick (BufferSet&, framecnt_t, int); - boost::shared_ptr<RecEnableControl> _rec_enable_control; + boost::shared_ptr<AutomationControl> _record_enable_control; + boost::shared_ptr<AutomationControl> _record_safe_control; + + virtual void record_enable_changed (bool, PBD::Controllable::GroupControlDisposition); + virtual void record_safe_changed (bool, PBD::Controllable::GroupControlDisposition); framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&); + virtual void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition); private: virtual boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &) = 0; void diskstream_playlist_changed (); - void diskstream_record_enable_changed (); - void diskstream_record_safe_changed (); void diskstream_speed_changed (); void diskstream_alignment_style_changed (); void parameter_changed (std::string const & p); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 626109557f..bd866141d5 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -53,6 +53,7 @@ namespace ARDOUR { class Route; class Region; class VCA; + class AutomationControl; typedef float Sample; typedef float pan_t; @@ -149,6 +150,7 @@ namespace ARDOUR { FadeOutAutomation, EnvelopeAutomation, RecEnableAutomation, + RecSafeAutomation, TrimAutomation, PhaseAutomation, MonitoringAutomation, @@ -565,6 +567,7 @@ namespace ARDOUR { typedef std::list<boost::shared_ptr<Route> > RouteList; typedef std::list<boost::weak_ptr <Route> > WeakRouteList; + typedef std::list<boost::shared_ptr<AutomationControl> > ControlList; typedef std::list<boost::shared_ptr<VCA> > VCAList; diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index f3767d471a..1dc7c2baf4 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -28,19 +28,26 @@ #include <string> #include <cmath> +#include "boost/shared_ptr.hpp" + #if __APPLE__ #include <CoreFoundation/CoreFoundation.h> #endif /* __APPLE__ */ -#include "ardour/libardour_visibility.h" #include "ardour/ardour.h" #include "ardour/data_type.h" #include "ardour/dB.h" +#include "ardour/types.h" + +#include "ardour/libardour_visibility.h" class XMLNode; namespace ARDOUR { +class Route; +class Track; + LIBARDOUR_API std::string legalize_for_path (const std::string& str); LIBARDOUR_API std::string legalize_for_universal_path (const std::string& str); LIBARDOUR_API std::string legalize_for_uri (const std::string& str); @@ -169,6 +176,29 @@ LIBARDOUR_API bool matching_unsuffixed_filename_exists_in (const std::string& di LIBARDOUR_API uint32_t how_many_dsp_threads (); +template<typename T> boost::shared_ptr<ControlList> route_list_to_control_list (boost::shared_ptr<RouteList> rl, boost::shared_ptr<T> (Route::*get_control)() const) { + boost::shared_ptr<ControlList> cl (new ControlList); + for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { + boost::shared_ptr<AutomationControl> ac = ((*r).get()->*get_control)(); + if (ac) { + cl->push_back (ac); + } + } + return cl; +} + +template<typename T> boost::shared_ptr<ControlList> route_list_to_control_list (boost::shared_ptr<RouteList> rl, boost::shared_ptr<T> (Track::*get_control)() const) { + boost::shared_ptr<ControlList> cl (new ControlList); + for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { + boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*r); + boost::shared_ptr<AutomationControl> ac = (t.get()->*get_control)(); + if (ac) { + cl->push_back (ac); + } + } + return cl; +} + #if __APPLE__ LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef); #endif // __APPLE__ @@ -176,4 +206,3 @@ LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef); } //namespave #endif /* __ardour_utils_h__ */ - diff --git a/libs/ardour/ardour/vca.h b/libs/ardour/ardour/vca.h index dc48ffa480..1c247c0e92 100644 --- a/libs/ardour/ardour/vca.h +++ b/libs/ardour/ardour/vca.h @@ -27,14 +27,19 @@ #include "pbd/statefuldestructible.h" #include "ardour/automatable.h" +#include "ardour/muteable.h" +#include "ardour/soloable.h" #include "ardour/stripable.h" namespace ARDOUR { -class GainControl; class Route; +class GainControl; +class SoloControl; +class MuteControl; +class MonitorControl; -class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::enable_shared_from_this<VCA> { +class LIBARDOUR_API VCA : public Stripable, public Soloable, public Muteable, public Automatable, public boost::enable_shared_from_this<VCA> { public: VCA (Session& session, uint32_t num, const std::string& name); ~VCA(); @@ -47,7 +52,15 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en int set_state (XMLNode const&, int version); bool soloed () const; + void push_solo_upstream (int32_t) {} + void push_solo_isolate_upstream (int32_t) {} + bool can_solo() const { return true; } + bool is_safe () const { return false; } + bool muted () const; + bool can_be_muted_by_others () const { return true; } + bool muted_by_others_soloing() const { return false; } + bool muted_by_others() const { return false; } static std::string default_name_template (); static int next_vca_number (); @@ -58,16 +71,16 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en static void set_next_vca_number (uint32_t); virtual boost::shared_ptr<GainControl> gain_control() const { return _gain_control; } - virtual boost::shared_ptr<AutomationControl> solo_control() const { return _solo_control; } - virtual boost::shared_ptr<AutomationControl> mute_control() const { return _mute_control; } + virtual boost::shared_ptr<SoloControl> solo_control() const { return _solo_control; } + virtual boost::shared_ptr<MuteControl> mute_control() const { return _mute_control; } /* null Stripable API, because VCAs don't have any of this */ - virtual boost::shared_ptr<PeakMeter> peak_meter() { return boost::shared_ptr<PeakMeter>(); } - virtual boost::shared_ptr<const PeakMeter> peak_meter() const { return boost::shared_ptr<PeakMeter>(); } - virtual boost::shared_ptr<AutomationControl> phase_control() const { return boost::shared_ptr<AutomationControl>(); } - virtual boost::shared_ptr<AutomationControl> trim_control() const { return boost::shared_ptr<AutomationControl>(); } - virtual boost::shared_ptr<AutomationControl> monitoring_control() const { return boost::shared_ptr<AutomationControl>(); } + virtual boost::shared_ptr<PeakMeter> peak_meter() { return boost::shared_ptr<PeakMeter>(); } + virtual boost::shared_ptr<const PeakMeter> peak_meter() const { return boost::shared_ptr<PeakMeter>(); } + virtual boost::shared_ptr<PhaseControl> phase_control() const { return boost::shared_ptr<PhaseControl>(); } + virtual boost::shared_ptr<GainControl> trim_control() const { return boost::shared_ptr<GainControl>(); } + virtual boost::shared_ptr<MonitorControl> monitoring_control() const { return boost::shared_ptr<MonitorControl>(); } virtual boost::shared_ptr<AutomationControl> recenable_control() const { return boost::shared_ptr<AutomationControl>(); } virtual boost::shared_ptr<AutomationControl> pan_azimuth_control() const { return boost::shared_ptr<AutomationControl>(); } virtual boost::shared_ptr<AutomationControl> pan_elevation_control() const { return boost::shared_ptr<AutomationControl>(); } @@ -96,36 +109,12 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en virtual boost::shared_ptr<AutomationControl> master_send_enable_controllable () const { return boost::shared_ptr<AutomationControl>(); } private: - class VCASoloControllable : public AutomationControl { - public: - VCASoloControllable (std::string const & name, boost::shared_ptr<VCA> vca); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - boost::weak_ptr<VCA> _vca; - }; - - class VCAMuteControllable : public AutomationControl { - public: - VCAMuteControllable (std::string const & name, boost::shared_ptr<VCA> vca); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - boost::weak_ptr<VCA> _vca; - }; - - friend class VCASoloControllable; - friend class VCAMuteControllable; - uint32_t _number; boost::shared_ptr<GainControl> _gain_control; - boost::shared_ptr<VCASoloControllable> _solo_control; - boost::shared_ptr<VCAMuteControllable> _mute_control; + boost::shared_ptr<SoloControl> _solo_control; + boost::shared_ptr<MuteControl> _mute_control; + bool _solo_requested; bool _mute_requested; diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 579b0064bc..2ed200d6f6 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -33,6 +33,7 @@ #include "ardour/buffer_set.h" #include "ardour/delivery.h" #include "ardour/meter.h" +#include "ardour/monitor_control.h" #include "ardour/playlist_factory.h" #include "ardour/processor.h" #include "ardour/profile.h" @@ -349,7 +350,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram if (!_active) { silence (nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->reset(); } return 0; @@ -391,7 +392,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram fill_buffers_with_input (bufs, _input, nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->run (bufs, start_frame, end_frame, nframes, true); } @@ -629,8 +630,9 @@ AudioTrack::freeze_me (InterThreadInfo& itt) /* reset stuff that has already been accounted for in the freeze process */ - set_gain (GAIN_COEFF_UNITY, Controllable::NoGroup); - _amp->gain_control()->set_automation_state (Off); + gain_control()->set_value (GAIN_COEFF_UNITY, Controllable::NoGroup); + gain_control()->set_automation_state (Off); + /* XXX need to use _main_outs _panner->set_automation_state (Off); */ _freeze_record.state = Frozen; diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index b00c615625..7efaa07f23 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -20,14 +20,17 @@ #include <math.h> #include <iostream> + +#include "pbd/memento_command.h" +#include "pbd/stacktrace.h" + +#include "ardour/audioengine.h" #include "ardour/automation_control.h" #include "ardour/automation_watch.h" +#include "ardour/control_group.h" #include "ardour/event_type_map.h" #include "ardour/session.h" -#include "pbd/memento_command.h" -#include "pbd/stacktrace.h" - #include "i18n.h" #ifdef COMPILER_MSVC @@ -69,42 +72,38 @@ AutomationControl::writable() const return true; } +/** Get the current effective `user' value based on automation state */ double -AutomationControl::get_masters_value_locked () const +AutomationControl::get_value() 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); + bool from_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_playback(); + return Control::get_double (from_list, _session.transport_frame()); } -double -AutomationControl::get_value_locked() const +void +AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) { - /* read or write masters lock must be held */ - - if (_masters.empty()) { - return Control::get_double (false, _session.transport_frame()); + if (!writable()) { + return; } - return get_masters_value_locked (); -} + /* enforce strict double/boolean value mapping */ -/** Get the current effective `user' value based on automation state */ -double -AutomationControl::get_value() const -{ - bool from_list = _list && ((AutomationList*)_list.get())->automation_playback(); + if (_desc.toggled) { + if (val != 0.0) { + val = 1.0; + } + } + + if (check_rt (val, gcd)) { + /* change has been queued to take place in an RT context */ + return; + } - if (!from_list) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_value_locked (); + if (_group && _group->use_me (gcd)) { + _group->set_group_value (shared_from_this(), val); } else { - return Control::get_double (from_list, _session.transport_frame()); + actually_set_value (val, gcd); } } @@ -113,12 +112,15 @@ AutomationControl::get_value() const * @param value `user' value */ void -AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition gcd) +AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd) { - bool to_list = _list && ((AutomationList*)_list.get())->automation_write(); + bool to_list = _list && boost::dynamic_pointer_cast<AutomationList>(_list)->automation_write(); Control::set_double (value, _session.transport_frame(), to_list); + AutomationType at = (AutomationType) _parameter.type(); + + std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value << " @ " << this << std::endl; Changed (true, gcd); } @@ -263,147 +265,25 @@ AutomationControl::interface_to_internal (double val) const return val; } - -void -AutomationControl::add_master (boost::shared_ptr<AutomationControl> m) -{ - double current_value; - double new_value; - std::pair<Masters::iterator,bool> res; - - { - Glib::Threads::RWLock::WriterLock lm (master_lock); - current_value = get_value_locked (); - - /* ratio will be recomputed below */ - - res = _masters.insert (make_pair<PBD::ID,MasterRecord> (m->id(), MasterRecord (m, 1.0))); - - if (res.second) { - - recompute_masters_ratios (current_value); - - /* note that we bind @param m as a weak_ptr<AutomationControl>, thus - avoiding holding a reference to the control in the binding - itself. - */ - - m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::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 (&AutomationControl::master_changed, this, _1, _2)); - } - - new_value = get_value_locked (); - } - - if (res.second) { - /* this will notify everyone that we're now slaved to the master */ - MasterStatusChange (); /* EMIT SIGNAL */ - } - - if (new_value != current_value) { - /* force a call to to ::master_changed() to carry the - * consequences that would occur if the master assumed - * its current value WHILE we were slaved. - */ - master_changed (false, Controllable::NoGroup); - /* effective value changed by master */ - Changed (false, Controllable::NoGroup); - } - -} - -void -AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd) -{ - /* our value has (likely) changed, but not because we were - * modified. Just the master. - */ - - Changed (false, gcd); /* EMIT SIGNAL */ -} - -void -AutomationControl::master_going_away (boost::weak_ptr<AutomationControl> wm) -{ - boost::shared_ptr<AutomationControl> m = wm.lock(); - if (m) { - remove_master (m); - } -} - -void -AutomationControl::remove_master (boost::shared_ptr<AutomationControl> 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 -AutomationControl::clear_masters () +AutomationControl::set_group (boost::shared_ptr<ControlGroup> cg) { - 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); + if (_group) { + _group->remove_control (shared_from_this()); } + _group = cg; } bool -AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const +AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return _masters.find (m->id()) != _masters.end(); -} + if ((flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) { + /* queue change in RT context */ + std::cerr << "::set_value (" << val << ", " << enum_2_string (gcd) << ") called for " << enum_2_string ((AutomationType) _parameter.type()) << ", queueing in RT context\n"; + _session.set_control (shared_from_this(), val, gcd); + return true; + } -bool -AutomationControl::slaved () const -{ - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return !_masters.empty(); + return false; } diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index b7e77952cb..85634640b9 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -140,21 +140,30 @@ setup_enum_writer () #define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e) #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e) + REGISTER_ENUM (NullAutomation); REGISTER_ENUM (GainAutomation); - REGISTER_ENUM (TrimAutomation); REGISTER_ENUM (PanAzimuthAutomation); REGISTER_ENUM (PanElevationAutomation); REGISTER_ENUM (PanWidthAutomation); + REGISTER_ENUM (PanFrontBackAutomation); + REGISTER_ENUM (PanLFEAutomation); REGISTER_ENUM (PluginAutomation); REGISTER_ENUM (PluginPropertyAutomation); REGISTER_ENUM (SoloAutomation); + REGISTER_ENUM (SoloIsolateAutomation); + REGISTER_ENUM (SoloSafeAutomation); REGISTER_ENUM (MuteAutomation); REGISTER_ENUM (MidiCCAutomation); + REGISTER_ENUM (MidiPgmChangeAutomation); + REGISTER_ENUM (MidiPitchBenderAutomation); + REGISTER_ENUM (MidiChannelPressureAutomation); + REGISTER_ENUM (MidiSystemExclusiveAutomation); REGISTER_ENUM (FadeInAutomation); REGISTER_ENUM (FadeOutAutomation); REGISTER_ENUM (EnvelopeAutomation); - REGISTER_ENUM (SoloIsolateAutomation); - REGISTER_ENUM (SoloSafeAutomation); + REGISTER_ENUM (RecEnableAutomation); + REGISTER_ENUM (RecSafeAutomation); + REGISTER_ENUM (TrimAutomation); REGISTER_ENUM (PhaseAutomation); REGISTER_ENUM (MonitoringAutomation); REGISTER_ENUM (EQGain); diff --git a/libs/ardour/gain_control.cc b/libs/ardour/gain_control.cc index 456fd9b248..3cb8230198 100644 --- a/libs/ardour/gain_control.cc +++ b/libs/ardour/gain_control.cc @@ -33,9 +33,9 @@ using namespace ARDOUR; using namespace std; GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, boost::shared_ptr<AutomationList> al) - : AutomationControl (session, param, ParameterDescriptor(param), - al ? al : boost::shared_ptr<AutomationList> (new AutomationList (param)), - param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) { + : SlavableAutomationControl (session, param, ParameterDescriptor(param), + al ? al : boost::shared_ptr<AutomationList> (new AutomationList (param)), + param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) { alist()->reset_default (1.0); @@ -44,22 +44,7 @@ GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, boos } void -GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -GainControl::set_value_unchecked (double val) -{ - /* used only automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override) +GainControl::actually_set_value (double val, Controllable::GroupControlDisposition group_override) { val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower); @@ -75,7 +60,7 @@ GainControl::_set_value (double val, Controllable::GroupControlDisposition group be retrieved by AutomationControl::get_value () */ - AutomationControl::set_value (val, group_override); + AutomationControl::actually_set_value (val, group_override); _session.set_dirty (); } @@ -120,6 +105,23 @@ GainControl::get_user_string () const } void +GainControl::inc_gain (gain_t factor) +{ + /* To be used ONLY when doing group-relative gain adjustment, from + * ControlGroup::set_group_values(). + */ + + const float desired_gain = user_double(); + + if (fabsf (desired_gain) < GAIN_COEFF_SMALL) { + // really?! what's the idea here? + actually_set_value (0.000001f + (0.000001f * factor), Controllable::ForGroup); + } else { + actually_set_value (desired_gain + (desired_gain * factor), Controllable::ForGroup); + } +} + +void GainControl::recompute_masters_ratios (double val) { /* Master WRITE lock must be held */ diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 23dc0d26f0..28214b7e62 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -583,10 +583,10 @@ LuaBindings::common (lua_State* L) .addCast<MidiTrack> ("to_midi_track") .addFunction ("set_name", &Track::set_name) .addFunction ("can_record", &Track::can_record) - .addFunction ("record_enabled", &Track::record_enabled) - .addFunction ("record_safe", &Track::record_safe) - .addFunction ("set_record_enabled", &Track::set_record_enabled) - .addFunction ("set_record_safe", &Track::set_record_safe) + //.addFunction ("record_enabled", &Track::record_enabled) + //.addFunction ("record_safe", &Track::record_safe) + //.addFunction ("set_record_enabled", &Track::set_record_enabled) + //.addFunction ("set_record_safe", &Track::set_record_safe) .addFunction ("bounceable", &Track::bounceable) .addFunction ("bounce", &Track::bounce) .addFunction ("bounce_range", &Track::bounce_range) diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 9a38f9f05a..7c1b3a9399 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -44,6 +44,7 @@ #include "ardour/midi_port.h" #include "ardour/midi_region.h" #include "ardour/midi_track.h" +#include "ardour/monitor_control.h" #include "ardour/parameter_types.h" #include "ardour/port.h" #include "ardour/processor.h" @@ -103,24 +104,24 @@ MidiTrack::create_diskstream () } -void -MidiTrack::set_record_enabled (bool yn, Controllable::GroupControlDisposition group_override) +bool +MidiTrack::can_be_record_safe () { if (_step_editing) { - return; + return false; } - Track::set_record_enabled (yn, group_override); + return Track::can_be_record_safe (); } -void -MidiTrack::set_record_safe (bool yn, Controllable::GroupControlDisposition group_override) +bool +MidiTrack::can_be_record_enabled () { - if (_step_editing) { /* REQUIRES REVIEW */ - return; + if (_step_editing) { + return false; } - Track::set_record_safe (yn, group_override); + return Track::can_be_record_enabled (); } void @@ -372,7 +373,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame if (!_active) { silence (nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->reset(); } return 0; @@ -412,7 +413,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame /* filter captured data before meter sees it */ _capture_filter.filter (bufs); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->run (bufs, start_frame, end_frame, nframes, true); } @@ -726,22 +727,7 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st } void -MidiTrack::MidiControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -MidiTrack::MidiControl::set_value_unchecked (double val) -{ - /* used only by automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) +MidiTrack::MidiControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) { const Evoral::Parameter ¶meter = _list ? _list->parameter() : Control::parameter(); const Evoral::ParameterDescriptor &desc = EventTypeMap::instance().descriptor(parameter); @@ -798,7 +784,7 @@ MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlD _route->write_immediate_event(size, ev); } - AutomationControl::set_value(val, group_override); + AutomationControl::actually_set_value(val, group_override); } void @@ -958,35 +944,27 @@ MidiTrack::act_on_mute () } void -MidiTrack::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd) +MidiTrack::monitoring_changed (bool self, Controllable::GroupControlDisposition gcd) { - if (use_group (gcd, &RouteGroup::is_monitoring)) { - _route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup); - return; - } - - if (mc != _monitoring) { + Track::monitoring_changed (self, gcd); + + /* monitoring state changed, so flush out any on notes at the + * port level. + */ - Track::set_monitoring (mc, gcd); + PortSet& ports (_output->ports()); - /* monitoring state changed, so flush out any on notes at the - * port level. - */ - - PortSet& ports (_output->ports()); - - for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) { - boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (*p); - if (mp) { - mp->require_resolve (); - } + for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) { + boost::shared_ptr<MidiPort> mp = boost::dynamic_pointer_cast<MidiPort> (*p); + if (mp) { + mp->require_resolve (); } + } - boost::shared_ptr<MidiDiskstream> md (midi_diskstream()); + boost::shared_ptr<MidiDiskstream> md (midi_diskstream()); - if (md) { - md->reset_tracker (); - } + if (md) { + md->reset_tracker (); } } diff --git a/libs/ardour/mute_master.cc b/libs/ardour/mute_master.cc index dc40d1fd6b..6817374dc7 100644 --- a/libs/ardour/mute_master.cc +++ b/libs/ardour/mute_master.cc @@ -31,6 +31,8 @@ using namespace ARDOUR; using namespace std; +const string MuteMaster::xml_node_name (X_("MuteMaster")); + const MuteMaster::MutePoint MuteMaster::AllPoints = MuteMaster::MutePoint( PreFader|PostFader|Listen|Main); @@ -155,7 +157,7 @@ MuteMaster::set_state (const XMLNode& node, int /*version*/) XMLNode& MuteMaster::get_state() { - XMLNode* node = new XMLNode (X_("MuteMaster")); + XMLNode* node = new XMLNode (xml_node_name); node->add_property ("mute-point", enum_2_string (_mute_point)); node->add_property ("muted", (_muted_by_self ? X_("yes") : X_("no"))); return *node; diff --git a/libs/ardour/pan_controllable.cc b/libs/ardour/pan_controllable.cc index a6a96787a2..624f397b77 100644 --- a/libs/ardour/pan_controllable.cc +++ b/libs/ardour/pan_controllable.cc @@ -35,27 +35,13 @@ PanControllable::lower () const } void -PanControllable::set_value (double v, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (v, group_override); - } -} -void -PanControllable::set_value_unchecked (double v) -{ - /* used only automation playback */ - _set_value (v, Controllable::NoGroup); -} - -void -PanControllable::_set_value (double v, Controllable::GroupControlDisposition group_override) +PanControllable::actually_set_value (double v, Controllable::GroupControlDisposition group_override) { boost::shared_ptr<Panner> p = owner->panner(); if (!p) { /* no panner: just do it */ - AutomationControl::set_value (v, group_override); + AutomationControl::actually_set_value (v, group_override); return; } @@ -76,7 +62,7 @@ PanControllable::_set_value (double v, Controllable::GroupControlDisposition gro } if (can_set) { - AutomationControl::set_value (v, group_override); + AutomationControl::actually_set_value (v, group_override); } } diff --git a/libs/ardour/parameter_descriptor.cc b/libs/ardour/parameter_descriptor.cc index 9fb6b89a0c..e28dbecd35 100644 --- a/libs/ardour/parameter_descriptor.cc +++ b/libs/ardour/parameter_descriptor.cc @@ -75,6 +75,7 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter) normal = 0.0f; break; case RecEnableAutomation: + case RecSafeAutomation: lower = 0.0; upper = 1.0; toggled = true; diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 451f801f1d..b0d4d2f9ac 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -2614,22 +2614,9 @@ PluginInsert::PluginControl::PluginControl (PluginInsert* p, } /** @param val `user' value */ -void -PluginInsert::PluginControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (user_val, group_override); - } -} -void -PluginInsert::PluginControl::set_value_unchecked (double user_val) -{ - /* used only by automation playback */ - _set_value (user_val, Controllable::NoGroup); -} void -PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override) +PluginInsert::PluginControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override) { /* FIXME: probably should be taking out some lock here.. */ @@ -2642,13 +2629,13 @@ PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::Gro iasp->set_parameter (_list->parameter().id(), user_val); } - AutomationControl::set_value (user_val, group_override); + AutomationControl::actually_set_value (user_val, group_override); } void PluginInsert::PluginControl::catch_up_with_external_value (double user_val) { - AutomationControl::set_value (user_val, Controllable::NoGroup); + AutomationControl::actually_set_value (user_val, Controllable::NoGroup); } XMLNode& @@ -2700,15 +2687,7 @@ PluginInsert::PluginPropertyControl::PluginPropertyControl (PluginInsert* } void -PluginInsert::PluginPropertyControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition /* group_override*/) -{ - if (writable()) { - set_value_unchecked (user_val); - } -} - -void -PluginInsert::PluginPropertyControl::set_value_unchecked (double user_val) +PluginInsert::PluginPropertyControl::actually_set_value (double user_val, Controllable::GroupControlDisposition gcd) { /* Old numeric set_value(), coerce to appropriate datatype if possible. This is lossy, but better than nothing until Ardour's automation system @@ -2724,7 +2703,8 @@ PluginInsert::PluginPropertyControl::set_value_unchecked (double user_val) } _value = value; - AutomationControl::set_value (user_val, Controllable::NoGroup); + + AutomationControl::actually_set_value (user_val, gcd); } XMLNode& diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 8207176729..7a3d37d2a7 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -58,6 +58,7 @@ #include "ardour/panner.h" #include "ardour/panner_shell.h" #include "ardour/parameter_descriptor.h" +#include "ardour/phase_control.h" #include "ardour/plugin_insert.h" #include "ardour/port.h" #include "ardour/port_insert.h" @@ -67,6 +68,8 @@ #include "ardour/route_group.h" #include "ardour/send.h" #include "ardour/session.h" +#include "ardour/solo_control.h" +#include "ardour/solo_isolate_control.h" #include "ardour/unknown_processor.h" #include "ardour/utils.h" #include "ardour/vca.h" @@ -84,6 +87,7 @@ PBD::Signal3<int,boost::shared_ptr<Route>, boost::shared_ptr<PluginInsert>, Rout /** Base class for all routable/mixable objects (tracks and busses) */ Route::Route (Session& sess, string name, Flag flg, DataType default_type) : Stripable (sess, name) + , Muteable (sess, name) , Automatable (sess) , GraphNode (sess._process_graph) , _active (true) @@ -99,18 +103,11 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _meter_point (MeterPostFader) , _pending_meter_point (MeterPostFader) , _meter_type (MeterPeak) - , _self_solo (false) - , _soloed_by_others_upstream (0) - , _soloed_by_others_downstream (0) - , _solo_isolated (false) - , _solo_isolated_by_upstream (0) , _denormal_protection (false) , _recordable (true) , _silent (false) , _declickable (false) - , _mute_master (new MuteMaster (sess, name)) , _have_internal_generator (false) - , _solo_safe (false) , _default_type (default_type) , _order_key (0) , _has_order_key (false) @@ -141,21 +138,30 @@ Route::init () /* add standard controls */ - _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ())); - _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ())); - _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ())); + _gain_control.reset (new GainControl (_session, GainAutomation)); + add_control (_gain_control); - _solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ())); - _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ())); + _trim_control.reset (new GainControl (_session, TrimAutomation)); + add_control (_trim_control); + _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this)); _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); - _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); - _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle)); - add_control (_solo_control); + _solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2)); + + _mute_control.reset (new MuteControl (_session, X_("mute"), *this)); + _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); add_control (_mute_control); + + _phase_control.reset (new PhaseControl (_session, X_("phase"))); add_control (_phase_control); + _solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this)); + add_control (_solo_isolate_control); + + _solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe"))); + add_control (_solo_safe_control); + /* panning */ if (!(_flags & Route::MonitorOut)) { @@ -177,9 +183,6 @@ Route::init () * it should be the first processor to be added on every route. */ - _gain_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, GainAutomation, shared_from_this ())); - add_control (_gain_control); - _amp.reset (new Amp (_session, X_("Fader"), _gain_control, true)); add_processor (_amp, PostFader); @@ -196,9 +199,6 @@ Route::init () /* and input trim */ - _trim_control = boost::shared_ptr<GainControllable> (new GainControllable (_session, TrimAutomation, shared_from_this ())); - add_control (_trim_control); - _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false)); _trim->set_display_to_user (false); @@ -399,82 +399,10 @@ Route::ensure_track_or_route_name(string name, Session &session) } void -Route::inc_gain (gain_t factor) -{ - /* To be used ONLY when doing group-relative gain adjustment, from - * ::set_gain() - */ - - float desired_gain = _gain_control->user_double(); - - if (fabsf (desired_gain) < GAIN_COEFF_SMALL) { - // really?! what's the idea here? - _gain_control->route_set_value (0.000001f + (0.000001f * factor)); - } else { - _gain_control->route_set_value (desired_gain + (desired_gain * factor)); - } -} - -void -Route::set_gain (gain_t val, Controllable::GroupControlDisposition gcd) -{ - if (use_group (gcd, &RouteGroup::is_gain)) { - - if (_route_group->is_relative()) { - - gain_t usable_gain = _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 = _route_group->get_max_factor(factor); - if (factor == 0.0f) { - _amp->gain_control()->Changed (true, gcd); /* EMIT SIGNAL */ - return; - } - } else { - factor = _route_group->get_min_factor(factor); - if (factor == 0.0f) { - _amp->gain_control()->Changed (true, gcd); /* EMIT SIGNAL */ - return; - } - } - - _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor)); - - } else { - - _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup)); - } - - return; - } - - if (val == _gain_control->get_value()) { - return; - } - - _gain_control->route_set_value (val); -} - -void Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */) { // TODO route group, see set_gain() - _trim_control->route_set_value (val); + // _trim_control->route_set_value (val); } void @@ -551,7 +479,7 @@ Route::process_output_buffers (BufferSet& bufs, DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ - if (_phase_invert.any ()) { + if (!_phase_control->none()) { int chn = 0; @@ -560,7 +488,7 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert[chn]) { + if (_phase_control->inverted (chn)) { for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; sp[nx] += 1.0e-27f; @@ -577,7 +505,7 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert[chn]) { + if (_phase_control->inverted (chn)) { for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; } @@ -808,283 +736,46 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t } void -Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override) -{ - if (_solo_safe) { - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup)); - return; - } - - if (_monitor_send) { - if (yn != _monitor_send->active()) { - if (yn) { - _monitor_send->activate (); - _mute_master->set_soloed_by_self (true); - } else { - _monitor_send->deactivate (); - _mute_master->set_soloed_by_self (false); - } - _mute_master->set_soloed_by_others (false); - - /* first argument won't matter because solo <=> listen right now */ - _solo_control->Changed (false, group_override); /* EMIT SIGNAL */ - } - } -} - -bool -Route::listening_via_monitor () const +Route::set_listen (bool yn) { - if (_monitor_send) { - return _monitor_send->active (); + if (yn) { + _monitor_send->activate (); } else { - return false; + _monitor_send->deactivate (); } } void -Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition gcd) +Route::solo_control_changed (bool, Controllable::GroupControlDisposition) { - if (_solo_safe != yn) { - _solo_safe = yn; - _solo_safe_control->Changed (true, gcd); /* EMIT SIGNAL */ - } -} - -bool -Route::solo_safe() const -{ - return _solo_safe; -} - -void -Route::clear_all_solo_state () -{ - // ideally this function will never do anything, it only exists to forestall Murphy - bool emit_changed = false; - -#ifndef NDEBUG - // these are really debug messages, but of possible interest. - if (_self_solo) { - 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 - - if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) { - // if self-soled, set_solo() will do signal emission - emit_changed = true; - } - - _soloed_by_others_upstream = 0; - _soloed_by_others_downstream = 0; - - { - PBD::Unwinder<bool> uw (_solo_safe, false); - set_solo (false, Controllable::NoGroup); - } - - if (emit_changed) { - set_mute_master_solo (); - _solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ - } -} - -void -Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override) -{ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n", - name(), yn, enum_2_string(group_override), self_soloed())); - - if (_solo_safe) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); - return; - } - - if (is_master() || is_monitor() || is_auditioner()) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name())); - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup)); - return; - } - - if (self_soloed() != yn) { - set_self_solo (yn); - _solo_control->Changed (true, group_override); /* EMIT SIGNAL */ - } - - assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active()); -} - -void -Route::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 -Route::mod_solo_by_others_upstream (int32_t delta) -{ - 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. + /* nothing to do if we're not using AFL/PFL. But if we are, we need + to alter the active state of the monitor send. */ - 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()) { - DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name)); - for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { - if (i->sends_only) { - continue; - } - boost::shared_ptr<Route> sr = i->r.lock(); - if (sr) { - sr->mod_solo_by_others_downstream (-delta); - } - } - } - } - - set_mute_master_solo (); - cerr << name() << " SC->Changed (false, UseGroup)\n"; - _solo_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ -} - -void -Route::mod_solo_by_others_downstream (int32_t delta) -{ - 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; + if (Config->get_solo_control_is_listen_control ()) { + set_listen (_solo_control->self_soloed()); } - - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); - - set_mute_master_solo (); - _solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ -} - -void -Route::set_mute_master_solo () -{ - _mute_master->set_soloed_by_self (self_soloed()); - _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream()); } -void -Route::mod_solo_isolated_by_upstream (bool yn) +bool +Route::listening_via_monitor () const { - 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, yn ? "+1" : "-1")); - - if (!yn) { - if (_solo_isolated_by_upstream >= 1) { - _solo_isolated_by_upstream--; - } else { - _solo_isolated_by_upstream = 0; - } + if (_monitor_send) { + return _monitor_send->active (); } else { - _solo_isolated_by_upstream++; - } - - if (solo_isolated() != old) { - /* solo isolated status changed */ - _mute_master->set_solo_ignore (solo_isolated()); - _solo_isolate_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ + return false; } } void -Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override) +Route::push_solo_isolate_upstream (int32_t delta) { - if (is_master() || is_monitor() || is_auditioner()) { - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup)); - return; - } - - bool changed = false; - - if (yn) { - if (_solo_isolated == false) { - _mute_master->set_solo_ignore (true); - changed = true; - } - _solo_isolated = true; - } else { - if (_solo_isolated == true) { - _solo_isolated = false; - _mute_master->set_solo_ignore (false); - changed = true; - } - } - - - if (!changed) { - return; - } - /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */ boost::shared_ptr<RouteList> routes = _session.get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i).get() == this || !(*i)->can_solo()) { continue; } @@ -1092,87 +783,26 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o bool does_feed = feeds (*i, &sends_only); if (does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (yn); + (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta); } } - - /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ - - _solo_isolate_control->Changed (true, group_override); /* EMIT SIGNAL */ -} - -bool -Route::solo_isolated () const -{ - return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0); -} - -void -Route::set_mute_points (MuteMaster::MutePoint mp) -{ - _mute_master->set_mute_points (mp); - mute_points_changed (); /* EMIT SIGNAL */ - - if (_mute_master->muted_by_self()) { - _mute_control->Changed (true, Controllable::UseGroup); /* EMIT SIGNAL */ - } } void -Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override) +Route::push_solo_upstream (int delta) { - if (use_group (group_override, &RouteGroup::is_mute)) { - _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup)); - return; - } - - if (muted() != yn) { - _mute_master->set_muted_by_self (yn); - /* allow any derived classes to respond to the mute change - before anybody else knows about it. - */ - act_on_mute (); - /* tell everyone else */ - _mute_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ + DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name)); + for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { + if (i->sends_only) { + continue; + } + boost::shared_ptr<Route> sr (i->r.lock()); + if (sr) { + sr->solo_control()->mod_solo_by_others_downstream (-delta); + } } } -bool -Route::muted () const -{ - return _mute_master->muted_by_self(); -} - -bool -Route::muted_by_others_soloing () const -{ - // This method is only used by route_ui for display state. - // The real thing is MuteMaster::muted_by_others_at() - - //master is never muted by others - if (is_master()) - return false; - - //now check to see if something is soloed (and I am not) - //see also MuteMaster::mute_gain_at() - return _session.soloing() && !soloed() && !solo_isolated(); -} - -bool -Route::muted_by_others () const -{ - // This method is only used by route_ui for display state. - // The real thing is MuteMaster::muted_by_others_at() - - //master is never muted by others - if (is_master()) - return false; - - //now check to see if something is soloed (and I am not) - //see also MuteMaster::mute_gain_at() - return _mute_master->muted_by_others() || (_session.soloing() && !soloed() && !solo_isolated()); -} - #if 0 static void dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs) @@ -2743,8 +2373,6 @@ Route::state(bool full_state) node->add_property("active", _active?"yes":"no"); string p; - boost::to_string (_phase_invert, p); - node->add_property("phase-invert", p); node->add_property("denormal-protection", _denormal_protection?"yes":"no"); node->add_property("meter-point", enum_2_string (_meter_point)); @@ -2756,18 +2384,9 @@ Route::state(bool full_state) snprintf (buf, sizeof (buf), "%d", _order_key); node->add_property ("order-key", buf); - node->add_property ("self-solo", (_self_solo ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream); - node->add_property ("soloed-by-upstream", buf); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream); - node->add_property ("soloed-by-downstream", buf); - node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no"); - node->add_property ("solo-safe", _solo_safe ? "yes" : "no"); node->add_child_nocopy (_input->state (full_state)); node->add_child_nocopy (_output->state (full_state)); - node->add_child_nocopy (_solo_control->get_state ()); - node->add_child_nocopy (_mute_control->get_state ()); node->add_child_nocopy (_mute_master->get_state ()); if (full_state) { @@ -2866,7 +2485,7 @@ Route::set_state (const XMLNode& node, int version) _strict_io = string_is_affirmative (prop->value()); } - if (is_master() || is_monitor() || is_auditioner()) { + if (!can_solo()) { _mute_master->set_solo_ignore (true); } @@ -2933,31 +2552,10 @@ Route::set_state (const XMLNode& node, int version) // this looks up the internal instrument in processors reset_instrument_info(); - 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())); - } - - if ((prop = node.property ("solo-isolated")) != 0) { - set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup); - } - - if ((prop = node.property ("solo-safe")) != 0) { - set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup); - } - - if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (boost::dynamic_bitset<> (prop->value ())); - } + _solo_control->set_state (node, version); + _solo_safe_control->set_state (node, version); + _solo_isolate_control->set_state (node, version); + _mute_control->set_state (node, version); if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); @@ -3046,7 +2644,7 @@ Route::set_state (const XMLNode& node, int version) set_remote_control_id_internal (x); } - } else if (child->name() == X_("MuteMaster")) { + } else if (child->name() == MuteMaster::xml_node_name) { _mute_master->set_state (*child, version); } else if (child->name() == Automatable::xml_node_name) { @@ -3089,26 +2687,10 @@ Route::set_state_2X (const XMLNode& node, int version) _mute_master->set_solo_ignore (true); } - if ((prop = node.property (X_("phase-invert"))) != 0) { - boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); - if (string_is_affirmative (prop->value ())) { - p.set (); - } - set_phase_invert (p); - } - if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); } - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - - /* XXX force reset of solo status */ - - set_solo (yn); - } - if ((prop = node.property (X_("muted"))) != 0) { bool first = true; @@ -3870,11 +3452,11 @@ Route::input_change_handler (IOChange change, void * /*src*/) contains ConfigurationChanged */ configure_processors (0); - _phase_invert.resize (_input->n_ports().n_audio ()); + _phase_control->resize (_input->n_ports().n_audio ()); io_changed (); /* EMIT SIGNAL */ } - if (_soloed_by_others_upstream || _solo_isolated_by_upstream) { + if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) { int sbou = 0; int ibou = 0; boost::shared_ptr<RouteList> routes = _session.get_routes (); @@ -3889,38 +3471,36 @@ Route::input_change_handler (IOChange change, void * /*src*/) if ((*i)->soloed()) { ++sbou; } - if ((*i)->solo_isolated()) { + if ((*i)->solo_isolate_control()->solo_isolated()) { ++ibou; } } } } - int delta = sbou - _soloed_by_others_upstream; - int idelta = ibou - _solo_isolated_by_upstream; + int delta = sbou - _solo_control->soloed_by_others_upstream(); + int idelta = ibou - _solo_isolate_control->solo_isolated_by_upstream(); if (idelta < -1) { PBD::warning << string_compose ( _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"), - _name, ibou, _solo_isolated_by_upstream, idelta) + _name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta) << endmsg; } - if (_soloed_by_others_upstream) { + if (_solo_control->soloed_by_others_upstream()) { // ignore new connections (they're not propagated) if (delta <= 0) { - mod_solo_by_others_upstream (delta); + _solo_control->mod_solo_by_others_upstream (delta); } } - if (_solo_isolated_by_upstream) { + if (_solo_isolate_control->solo_isolated_by_upstream()) { // solo-isolate currently only propagates downstream if (idelta < 0) { - mod_solo_isolated_by_upstream (false); + _solo_isolate_control->mod_solo_isolated_by_upstream (1); } - // TODO think: mod_solo_isolated_by_upstream() does not take delta arg, - // but idelta can't be smaller than -1, can it? //_solo_isolated_by_upstream = ibou; } @@ -3933,11 +3513,11 @@ Route::input_change_handler (IOChange change, void * /*src*/) bool sends_only; bool does_feed = feeds (*i, &sends_only); if (delta <= 0 && does_feed && !sends_only) { - (*i)->mod_solo_by_others_upstream (delta); + (*i)->solo_control()->mod_solo_by_others_upstream (delta); } if (idelta < 0 && does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (false); + (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1); } } } @@ -3963,7 +3543,7 @@ Route::output_change_handler (IOChange change, void * /*src*/) io_changed (); /* EMIT SIGNAL */ } - if (_soloed_by_others_downstream) { + if (_solo_control->soloed_by_others_downstream()) { int sbod = 0; /* checking all all downstream routes for * explicit of implict solo is a rather drastic measure, @@ -3986,20 +3566,20 @@ Route::output_change_handler (IOChange change, void * /*src*/) } } } - int delta = sbod - _soloed_by_others_downstream; + int delta = sbod - _solo_control->soloed_by_others_downstream(); if (delta <= 0) { // do not allow new connections to change implicit solo (no propagation) - mod_solo_by_others_downstream (delta); + _solo_control->mod_solo_by_others_downstream (delta); // Session::route_solo_changed() does not propagate indirect solo-changes // propagate upstream to tracks for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i).get() == this || !can_solo()) { continue; } bool sends_only; bool does_feed = (*i)->feeds (shared_from_this(), &sends_only); if (delta != 0 && does_feed && !sends_only) { - (*i)->mod_solo_by_others_downstream (delta); + (*i)->solo_control()->mod_solo_by_others_downstream (delta); } } @@ -4639,41 +4219,6 @@ Route::internal_send_for (boost::shared_ptr<const Route> target) const return boost::shared_ptr<Send>(); } -/** @param c Audio channel index. - * @param yn true to invert phase, otherwise false. - */ -void -Route::set_phase_invert (uint32_t c, bool yn) -{ - if (_phase_invert[c] != yn) { - _phase_invert[c] = yn; - _phase_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ - _session.set_dirty (); - } -} - -void -Route::set_phase_invert (boost::dynamic_bitset<> p) -{ - if (_phase_invert != p) { - _phase_invert = p; - _phase_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ - _session.set_dirty (); - } -} - -bool -Route::phase_invert (uint32_t c) const -{ - return _phase_invert[c]; -} - -boost::dynamic_bitset<> -Route::phase_invert () const -{ - return _phase_invert; -} - void Route::set_denormal_protection (bool yn) { @@ -4735,20 +4280,16 @@ Route::gain_control() const return _gain_control; } -boost::shared_ptr<AutomationControl> +boost::shared_ptr<GainControl> Route::trim_control() const { return _trim_control; } -boost::shared_ptr<AutomationControl> +boost::shared_ptr<PhaseControl> Route::phase_control() const { - if (phase_invert().size()) { - return _phase_control; - } else { - return boost::shared_ptr<PhaseControllable>(); - } + return _phase_control; } boost::shared_ptr<AutomationControl> @@ -4841,12 +4382,6 @@ Route::has_io_processor_named (const string& name) return false; } -MuteMaster::MutePoint -Route::mute_points () const -{ - return _mute_master->mute_points (); -} - void Route::set_processor_positions () { @@ -5903,7 +5438,7 @@ Route::vca_assign (boost::shared_ptr<VCA> vca) { _gain_control->add_master (vca->gain_control()); _solo_control->add_master (vca->solo_control()); - _mute_control->add_master (vca->mute_control()); + // _mute_control->add_master (vca->mute_control()); } void @@ -5913,10 +5448,48 @@ Route::vca_unassign (boost::shared_ptr<VCA> vca) /* unassign from all */ _gain_control->clear_masters (); _solo_control->clear_masters (); - _mute_control->clear_masters (); + //_mute_control->clear_masters (); } else { _gain_control->remove_master (vca->gain_control()); _solo_control->remove_master (vca->solo_control()); - _mute_control->remove_master (vca->mute_control()); + //_mute_control->remove_master (vca->mute_control()); + } +} + +bool +Route::muted_by_others_soloing () const +{ + // This method is only used by route_ui for display state. + // The DSP version is MuteMaster::muted_by_others_at() + + if (!can_be_muted_by_others ()) { + return false; + } + + return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); +} + +bool +Route::muted_by_others () const +{ + // This method is only used by route_ui for display state. + // The DSP version is MuteMaster::muted_by_others_at() + + if (!can_be_muted_by_others()) { + return false; + } + + return _mute_master->muted_by_others(); +} + +void +Route::clear_all_solo_state () +{ + double v = _solo_safe_control->get_value (); + + _solo_control->clear_all_solo_state (); + + if (v != 0.0) { + _solo_safe_control->set_value (v, Controllable::NoGroup); } } diff --git a/libs/ardour/route_controls.cc b/libs/ardour/route_controls.cc deleted file mode 100644 index ad5408d06d..0000000000 --- a/libs/ardour/route_controls.cc +++ /dev/null @@ -1,431 +0,0 @@ -/* - Copyright (C) 2000 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. - -*/ - -#ifdef WAF_BUILD -#include "libardour-config.h" -#endif - -#include "ardour/automation_control.h" -#include "ardour/parameter_descriptor.h" -#include "ardour/route.h" -#include "ardour/session.h" - -#include "i18n.h" - -using namespace std; -using namespace ARDOUR; -using namespace PBD; - -void -Route::set_control (AutomationType type, double val, PBD::Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr<RouteList> rl; - - switch (type) { - case GainAutomation: - /* route must mediate group control */ - set_gain (val, group_override); - break; - - case TrimAutomation: - /* route must mediate group control */ - set_trim (val, group_override); - break; - - case RecEnableAutomation: - /* session must mediate group control */ - rl.reset (new RouteList); - rl->push_back (shared_from_this()); - _session.set_record_enabled (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - break; - - case SoloAutomation: - /* session must mediate group control */ - rl.reset (new RouteList); - rl->push_back (shared_from_this()); - if (Config->get_solo_control_is_listen_control()) { - _session.set_listen (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - } else { - _session.set_solo (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - } - break; - - case MuteAutomation: - /* session must mediate group control */ - rl.reset (new RouteList); - rl->push_back (shared_from_this()); - _session.set_mute (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - return; - break; - - default: - /* Not a route automation control */ - fatal << string_compose (_("programming error: %1%2\n"), X_("illegal type of route automation control passed to Route::set_control(): "), enum_2_string(type)) << endmsg; - /*NOTREACHED*/ - return; - } -} - - -Route::RouteAutomationControl::RouteAutomationControl (const std::string& name, - AutomationType atype, - boost::shared_ptr<AutomationList> alist, - boost::shared_ptr<Route> r) - : AutomationControl (r->session(), Evoral::Parameter (atype), - ParameterDescriptor (Evoral::Parameter (atype)), - alist, name) - , _route (r) -{ -} - -double -Route::BooleanRouteAutomationControl::get_masters_value_locked () const -{ - /* masters (read/write) lock must be held */ - - /* if any master is enabled (val > 0.0) then we consider the master - value to be 1.0 - */ - - for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { - if (mr->second.master()->get_value()) { - return 1.0; - } - } - - return 0.0; -} - - - -Route::GainControllable::GainControllable (Session& s, AutomationType atype, boost::shared_ptr<Route> r) - : GainControl (s, Evoral::Parameter(atype)) - , _route (r) -{ - -} - -Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r) - : BooleanRouteAutomationControl (name, SoloAutomation, boost::shared_ptr<AutomationList>(), r) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::SoloControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr<Route> r = _route.lock (); - - if (!r) { - return; - } - - bool master_soloed; - - { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - master_soloed = (bool) get_masters_value_locked (); - } - - /* Master is considered equivalent to an upstream solo control, not - * direct control over self-soloed. - */ - - r->mod_solo_by_others_upstream (master_soloed ? 1 : -1); - - AutomationControl::master_changed (false, gcd); -} - -void -Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -Route::SoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr<Route> r = _route.lock (); - if (!r) { - return; - } - r->set_control (SoloAutomation, val, group_override); -} - -void -Route::SoloControllable::set_value_unchecked (double val) -{ - /* Used only by automation playback */ - - _set_value (val, Controllable::NoGroup); -} - -double -Route::SoloControllable::get_value () const -{ - if (slaved()) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Playing back automation, get the value from the list - return AutomationControl::get_value(); - } - - boost::shared_ptr<Route> r = _route.lock (); - - if (!r) { - return 0; - } - - if (Config->get_solo_control_is_listen_control()) { - return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - } else { - return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - } -} - -Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r) - : BooleanRouteAutomationControl (name, MuteAutomation, boost::shared_ptr<AutomationList>(), r) - , _route (r) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::MuteControllable::set_superficial_value(bool muted) -{ - /* Note we can not use AutomationControl::set_value here since it will emit - Changed(), but the value will not be correct to the observer. */ - - const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write (); - const double where = _session.audible_frame (); - - if (to_list) { - /* Note that we really need this: - * if (as == Touch && _list->in_new_write_pass ()) { - * alist->start_write_pass (_session.audible_frame ()); - * } - * here in the case of the user calling from a GUI or whatever. - * Without the ability to distinguish between user and - * automation-initiated changes, we lose the "touch mute" - * behaviour we have in AutomationController::toggled (). - */ - _list->set_in_write_pass (true, false, where); - } - - Control::set_double (muted, where, to_list); -} - -void -Route::MuteControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) -{ - bool master_muted; - - { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - master_muted = (bool) get_masters_value_locked (); - } - - boost::shared_ptr<Route> r (_route.lock()); - if (r) { - r->mute_master()->mod_muted_by_others (master_muted ? 1 : -1); - } - - AutomationControl::master_changed (false, gcd); -} - -void -Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -Route::MuteControllable::set_value_unchecked (double val) -{ - /* used only automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -Route::MuteControllable::_set_value (double val, Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr<Route> r = _route.lock (); - - if (!r) { - return; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Set superficial/automation value to drive controller (and possibly record) - const bool bval = ((val >= 0.5) ? true : false); - set_superficial_value (bval); - // Playing back automation, set route mute directly - r->set_mute (bval, Controllable::NoGroup); - } else { - r->set_control (MuteAutomation, val, group_override); - } -} - -double -Route::MuteControllable::get_value () const -{ - if (slaved()) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_masters_value_locked () ? 1.0 : 0.0; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Playing back automation, get the value from the list - return AutomationControl::get_value(); - } - - // Not playing back automation, get the actual route mute value - boost::shared_ptr<Route> r = _route.lock (); - return (r && r->muted()) ? 1.0 : 0.0; -} - -Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr<Route> r) - : BooleanRouteAutomationControl (name, PhaseAutomation, boost::shared_ptr<AutomationList>(), r) - , _current_phase (0) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(PhaseAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::PhaseControllable::set_value (double v, PBD::Controllable::GroupControlDisposition /* group_override */) -{ - boost::shared_ptr<Route> r = _route.lock (); - if (r->phase_invert().size()) { - if (v == 0 || (v < 1 && v > 0.9) ) { - r->set_phase_invert (_current_phase, false); - } else { - r->set_phase_invert (_current_phase, true); - } - } -} - -double -Route::PhaseControllable::get_value () const -{ - boost::shared_ptr<Route> r = _route.lock (); - if (!r) { - return 0.0; - } - return (double) r->phase_invert (_current_phase); -} - -void -Route::PhaseControllable::set_channel (uint32_t c) -{ - _current_phase = c; -} - -uint32_t -Route::PhaseControllable::channel () const -{ - return _current_phase; -} - -Route::SoloIsolateControllable::SoloIsolateControllable (std::string name, boost::shared_ptr<Route> r) - : BooleanRouteAutomationControl (name, SoloIsolateAutomation, boost::shared_ptr<AutomationList>(), r) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloIsolateAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - - -double -Route::SoloIsolateControllable::get_value () const -{ - boost::shared_ptr<Route> r = _route.lock (); - if (!r) { - return 0.0; /* "false" */ - } - - return r->solo_isolated() ? 1.0 : 0.0; -} - -void -Route::SoloIsolateControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - _set_value (val, gcd); -} - -void -Route::SoloIsolateControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition) -{ - boost::shared_ptr<Route> r = _route.lock (); - if (!r) { - return; - } - - /* no group semantics yet */ - r->set_solo_isolated (val >= 0.5 ? true : false); -} - -Route::SoloSafeControllable::SoloSafeControllable (std::string name, boost::shared_ptr<Route> r) - : BooleanRouteAutomationControl (name, SoloSafeAutomation, boost::shared_ptr<AutomationList>(), r) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloSafeAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::SoloSafeControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - _set_value (val, gcd); -} - -void -Route::SoloSafeControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition) -{ - boost::shared_ptr<Route> r = _route.lock (); - if (!r) { - return; - } - - /* no group semantics yet */ - r->set_solo_safe (val >= 0.5 ? true : false); -} - -double -Route::SoloSafeControllable::get_value () const -{ - boost::shared_ptr<Route> r = _route.lock (); - if (!r) { - return 0.0; /* "false" */ - } - - return r->solo_safe() ? 1.0 : 0.0; -} diff --git a/libs/ardour/route_graph.cc b/libs/ardour/route_graph.cc index 910141a440..70b9b48d6f 100644 --- a/libs/ardour/route_graph.cc +++ b/libs/ardour/route_graph.cc @@ -20,6 +20,7 @@ #include "ardour/route.h" #include "ardour/route_graph.h" +#include "ardour/track.h" #include "i18n.h" @@ -195,21 +196,39 @@ struct RouteRecEnabledComparator { bool operator () (GraphVertex r1, GraphVertex r2) const { - if (r1->record_enabled()) { - if (r2->record_enabled()) { - /* both rec-enabled, just use signal order */ + boost::shared_ptr<Track> t1 (boost::dynamic_pointer_cast<Track>(r1)); + boost::shared_ptr<Track> t2 (boost::dynamic_pointer_cast<Track>(r2)); + + if (!t1) { + if (!t2) { + /* makes no difference which is first, use signal order */ return r1->order_key () < r2->order_key (); } else { - /* r1 rec-enabled, r2 not rec-enabled, run r2 early */ + /* r1 is not a track, r2 is, run it early */ + return false; + } + } + + if (!t2) { + /* we already tested !t1, so just use signal order */ + return r1->order_key () < r2->order_key (); + } + + if (t1->rec_enable_control()->get_value()) { + if (t2->rec_enable_control()->get_value()) { + /* both rec-enabled, just use signal order */ + return t1->order_key () < t2->order_key (); + } else { + /* t1 rec-enabled, t2 not rec-enabled, run t2 early */ return false; } } else { - if (r2->record_enabled()) { - /* r2 rec-enabled, r1 not rec-enabled, run r1 early */ + if (t2->rec_enable_control()->get_value()) { + /* t2 rec-enabled, t1 not rec-enabled, run t1 early */ return true; } else { /* neither rec-enabled, use signal order */ - return r1->order_key () < r2->order_key (); + return t1->order_key () < t2->order_key (); } } } diff --git a/libs/ardour/route_group.cc b/libs/ardour/route_group.cc index b482bb85d3..137e2c4734 100644 --- a/libs/ardour/route_group.cc +++ b/libs/ardour/route_group.cc @@ -28,6 +28,7 @@ #include "ardour/amp.h" #include "ardour/audio_track.h" +#include "ardour/monitor_control.h" #include "ardour/route.h" #include "ardour/route_group.h" #include "ardour/session.h" @@ -57,21 +58,21 @@ void RouteGroup::make_property_quarks () { Properties::relative.property_id = g_quark_from_static_string (X_("relative")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id)); Properties::active.property_id = g_quark_from_static_string (X_("active")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id)); Properties::hidden.property_id = g_quark_from_static_string (X_("hidden")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); Properties::gain.property_id = g_quark_from_static_string (X_("gain")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id)); Properties::mute.property_id = g_quark_from_static_string (X_("mute")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id)); Properties::solo.property_id = g_quark_from_static_string (X_("solo")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id)); Properties::recenable.property_id = g_quark_from_static_string (X_("recenable")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id)); Properties::select.property_id = g_quark_from_static_string (X_("select")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id)); Properties::route_active.property_id = g_quark_from_static_string (X_("route-active")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for route-active = %1\n", Properties::route_active.property_id)); Properties::color.property_id = g_quark_from_static_string (X_("color")); @@ -96,6 +97,11 @@ RouteGroup::RouteGroup (Session& s, const string &n) : SessionObject (s, n) , routes (new RouteList) , ROUTE_GROUP_DEFAULT_PROPERTIES + , _solo_group (new ControlGroup (SoloAutomation)) + , _mute_group (new ControlGroup (MuteAutomation)) + , _rec_enable_group (new ControlGroup (RecEnableAutomation)) + , _gain_group (new ControlGroup (GainAutomation)) + , _monitoring_group (new ControlGroup (MonitoringAutomation)) { _xml_node_name = X_("RouteGroup"); @@ -114,6 +120,12 @@ RouteGroup::RouteGroup (Session& s, const string &n) RouteGroup::~RouteGroup () { + _solo_group->clear (); + _mute_group->clear (); + _gain_group->clear (); + _rec_enable_group->clear (); + _monitoring_group->clear (); + for (RouteList::iterator i = routes->begin(); i != routes->end();) { RouteList::iterator tmp = i; ++tmp; @@ -140,6 +152,15 @@ RouteGroup::add (boost::shared_ptr<Route> r) routes->push_back (r); + _solo_group->add_control (r->solo_control()); + _mute_group->add_control (r->mute_control()); + _gain_group->add_control (r->gain_control()); + boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r); + if (trk) { + _rec_enable_group->add_control (trk->rec_enable_control()); + _monitoring_group->add_control (trk->monitoring_control()); + } + r->set_route_group (this); r->DropReferences.connect_same_thread (*this, boost::bind (&RouteGroup::remove_when_going_away, this, boost::weak_ptr<Route> (r))); @@ -165,6 +186,14 @@ RouteGroup::remove (boost::shared_ptr<Route> r) if ((i = find (routes->begin(), routes->end(), r)) != routes->end()) { r->set_route_group (0); + _solo_group->remove_control (r->solo_control()); + _mute_group->remove_control (r->mute_control()); + _gain_group->remove_control (r->gain_control()); + boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r); + if (trk) { + _rec_enable_group->remove_control (trk->rec_enable_control()); + _monitoring_group->remove_control (trk->monitoring_control()); + } routes->erase (i); _session.set_dirty (); RouteRemoved (this, boost::weak_ptr<Route> (r)); /* EMIT SIGNAL */ @@ -175,49 +204,6 @@ RouteGroup::remove (boost::shared_ptr<Route> r) } -gain_t -RouteGroup::get_min_factor (gain_t factor) -{ - for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - gain_t const g = (*i)->gain_control()->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 -RouteGroup::get_max_factor (gain_t factor) -{ - for (RouteList::iterator i = routes->begin(); i != routes->end(); i++) { - gain_t const g = (*i)->gain_control()->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; -} - XMLNode& RouteGroup::get_state () { @@ -269,6 +255,8 @@ RouteGroup::set_state (const XMLNode& node, int version) } } + push_to_groups (); + return 0; } @@ -293,6 +281,8 @@ RouteGroup::set_state_2X (const XMLNode& node, int /*version*/) _color = false; } + push_to_groups (); + return 0; } @@ -303,6 +293,8 @@ RouteGroup::set_gain (bool yn) return; } _gain = yn; + _gain_group->set_active (yn); + send_change (PropertyChange (Properties::gain)); } @@ -313,6 +305,7 @@ RouteGroup::set_mute (bool yn) return; } _mute = yn; + _mute_group->set_active (yn); send_change (PropertyChange (Properties::mute)); } @@ -323,6 +316,7 @@ RouteGroup::set_solo (bool yn) return; } _solo = yn; + _solo_group->set_active (yn); send_change (PropertyChange (Properties::solo)); } @@ -333,6 +327,7 @@ RouteGroup::set_recenable (bool yn) return; } _recenable = yn; + _rec_enable_group->set_active (yn); send_change (PropertyChange (Properties::recenable)); } @@ -384,6 +379,8 @@ RouteGroup::set_monitoring (bool yn) } _monitoring = yn; + _monitoring_group->set_active (yn); + send_change (PropertyChange (Properties::monitoring)); _session.set_dirty (); @@ -531,3 +528,19 @@ RouteGroup::enabled_property (PBD::PropertyID prop) return dynamic_cast<const PropertyTemplate<bool>* > (i->second)->val (); } + +void +RouteGroup::post_set (PBD::PropertyChange const &) +{ + push_to_groups (); +} + +void +RouteGroup::push_to_groups () +{ + _gain_group->set_active (_gain); + _solo_group->set_active (_solo); + _mute_group->set_active (_mute); + _rec_enable_group->set_active (_recenable); + _monitoring_group->set_active (_monitoring); +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ee70d2a40f..7658ff270d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -98,6 +98,7 @@ #include "ardour/session_directory.h" #include "ardour/session_playlists.h" #include "ardour/smf_source.h" +#include "ardour/solo_isolate_control.h" #include "ardour/source_factory.h" #include "ardour/speakers.h" #include "ardour/tempo.h" @@ -1506,7 +1507,7 @@ Session::set_track_monitor_input_status (bool yn) boost::shared_ptr<RouteList> rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; tr->request_input_monitoring (yn); } @@ -1941,15 +1942,9 @@ void Session::set_all_tracks_record_enabled (bool enable ) { boost::shared_ptr<RouteList> rl = routes.reader(); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (tr) { - tr->set_record_enabled (enable, Controllable::NoGroup); - } - } + set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), enable, Controllable::NoGroup); } - void Session::disable_record (bool rt_context, bool force) { @@ -2981,7 +2976,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod // 0 for Stereo Out mode // 0 Multi Out mode if (Config->get_output_auto_connect() & AutoConnectMaster) { - track->set_gain (dB_to_coefficient (0), Controllable::NoGroup); + track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup); } } @@ -3430,7 +3425,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool if (tr) { tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr<Track> (tr))); track_playlist_changed (boost::weak_ptr<Track> (tr)); - tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); + tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr); if (mt) { @@ -3580,7 +3575,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove) continue; } - (*iter)->set_solo (false, Controllable::NoGroup); + (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup); rs->remove (*iter); @@ -3721,7 +3716,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3734,7 +3729,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr */ continue; } - (*i)->set_listen (false, Controllable::NoGroup); + (*i)->solo_control()->set_value (0.0, Controllable::NoGroup); } } @@ -3759,7 +3754,7 @@ Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr) bool send_changed = false; - if (route->solo_isolated()) { + if (route->solo_isolate_control()->solo_isolated()) { if (_solo_isolated_cnt == 0) { send_changed = true; } @@ -3838,7 +3833,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3852,7 +3847,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - (*i)->set_solo (false, group_override); + (*i)->solo_control()->set_value (0.0, group_override); } } @@ -3871,7 +3866,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3893,7 +3888,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); if (!via_sends_only) { if (!route->soloed_by_others_upstream()) { - (*i)->mod_solo_by_others_downstream (delta); + (*i)->solo_control()->mod_solo_by_others_downstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n"); } @@ -3922,7 +3917,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi if (!via_sends_only) { //NB. Triggers Invert Push, which handles soloed by downstream DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); - (*i)->mod_solo_by_others_upstream (delta); + (*i)->solo_control()->mod_solo_by_others_upstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); } @@ -3969,7 +3964,7 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r) } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) { + if ((*i)->can_solo() && (*i)->self_soloed()) { something_soloed = true; } @@ -3978,11 +3973,11 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r) listeners++; something_listening = true; } else { - (*i)->set_listen (false, Controllable::NoGroup); + (*i)->set_listen (false); } } - if ((*i)->solo_isolated()) { + if ((*i)->solo_isolate_control()->solo_isolated()) { isolated++; } } @@ -6078,7 +6073,7 @@ Session::update_route_record_state () while (i != rl->end ()) { boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { break; } @@ -6095,7 +6090,7 @@ Session::update_route_record_state () for (i = rl->begin(); i != rl->end (); ++i) { boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (tr && !tr->record_enabled ()) { + if (tr && !tr->rec_enable_control()->get_value()) { break; } } @@ -6124,12 +6119,7 @@ void Session::solo_control_mode_changed () { /* cancel all solo or all listen when solo control mode changes */ - - if (soloing()) { - set_solo (get_routes(), false); - } else if (listening()) { - set_listen (get_routes(), false); - } + clear_all_solo_state (get_routes()); } /** Called when a property of one of our route groups changes */ diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index c2010fd592..390f1de32f 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -350,7 +350,7 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled) if ((at = dynamic_cast<AudioTrack*>((*i).get())) != 0) { if (trk == at->remote_control_id()) { - at->set_record_enabled (enabled, Controllable::UseGroup); + at->rec_enable_control()->set_value (enabled, Controllable::UseGroup); break; } } diff --git a/libs/ardour/session_rtevents.cc b/libs/ardour/session_rtevents.cc index 6b807fbf52..67249d48b9 100644 --- a/libs/ardour/session_rtevents.cc +++ b/libs/ardour/session_rtevents.cc @@ -21,8 +21,9 @@ #include "pbd/error.h" #include "pbd/compose.h" -#include "ardour/session.h" +#include "ardour/monitor_control.h" #include "ardour/route.h" +#include "ardour/session.h" #include "ardour/track.h" #include "i18n.h" @@ -33,26 +34,26 @@ using namespace ARDOUR; using namespace Glib; void -Session::set_monitoring (boost::shared_ptr<RouteList> rl, MonitorChoice mc, - SessionEvent::RTeventCallback after, - Controllable::GroupControlDisposition group_override) +Session::set_controls (boost::shared_ptr<ControlList> cl, double val, Controllable::GroupControlDisposition gcd) { - queue_event (get_rt_event (rl, mc, after, group_override, &Session::rt_set_monitoring)); + std::cerr << "Session::set_controls called on " << cl->size() << " controls, group = " << enum_2_string (gcd) << std::endl; + queue_event (get_rt_event (cl, val, gcd)); } void -Session::rt_set_monitoring (boost::shared_ptr<RouteList> rl, MonitorChoice mc, Controllable::GroupControlDisposition group_override) +Session::set_control (boost::shared_ptr<AutomationControl> ac, double val, Controllable::GroupControlDisposition gcd) { - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i); - if (t) { - t->set_monitoring (mc, group_override); - } - } - } + boost::shared_ptr<ControlList> cl (new ControlList); + cl->push_back (ac); + set_controls (cl, val, gcd); +} - set_dirty(); +void +Session::rt_set_controls (boost::shared_ptr<ControlList> cl, double val, Controllable::GroupControlDisposition gcd) +{ + for (ControlList::iterator c = cl->begin(); c != cl->end(); ++c) { + (*c)->set_value (val, gcd); + } } void @@ -74,224 +75,6 @@ Session::rt_clear_all_solo_state (boost::shared_ptr<RouteList> rl, bool /* yn */ } void -Session::set_solo (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, - Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo)); -} - -void -Session::rt_set_solo (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - (*i)->set_solo (yn, group_override); - } - } - - set_dirty(); - - /* XXX boost::shared_ptr<RouteList> goes out of scope here and is likley free()ed in RT context - * because boost's shared_ptr does reference counting and free/delete in the dtor. - * (this also applies to other rt_ methods here) - */ -} - -void -Session::set_implicit_solo (boost::shared_ptr<RouteList> rl, int delta, bool upstream, SessionEvent::RTeventCallback after, - Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, delta, upstream, after, group_override, &Session::rt_set_implicit_solo)); -} - -void -Session::rt_set_implicit_solo (boost::shared_ptr<RouteList> rl, int delta, bool upstream, PBD::Controllable::GroupControlDisposition) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - if (upstream) { - std::cerr << "Changing " << (*i)->name() << " upstream by " << delta << std::endl; - (*i)->mod_solo_by_others_upstream (delta); - } else { - std::cerr << "Changing " << (*i)->name() << " downstream by " << delta << std::endl; - (*i)->mod_solo_by_others_downstream (delta); - } - } - } - - set_dirty(); - - /* XXX boost::shared_ptr<RouteList> goes out of scope here and is likley free()ed in RT context - * because boost's shared_ptr does reference counting and free/delete in the dtor. - * (this also applies to other rt_ methods here) - */ -} - -void -Session::set_just_one_solo (boost::shared_ptr<Route> r, bool yn, SessionEvent::RTeventCallback after) -{ - /* its a bit silly to have to do this, but it keeps the API for this public method sane (we're - only going to solo one route) and keeps our ability to use get_rt_event() for the internal - private method. - */ - - boost::shared_ptr<RouteList> rl (new RouteList); - rl->push_back (r); - - queue_event (get_rt_event (rl, yn, after, Controllable::NoGroup, &Session::rt_set_just_one_solo)); -} - -void -Session::rt_set_just_one_solo (boost::shared_ptr<RouteList> just_one, bool yn, Controllable::GroupControlDisposition /*ignored*/) -{ - boost::shared_ptr<RouteList> rl = routes.reader (); - boost::shared_ptr<Route> r = just_one->front(); - - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner() && r != *i) { - (*i)->set_solo (!yn, Controllable::NoGroup); - } - } - - r->set_solo (yn, Controllable::NoGroup); - - set_dirty(); -} - -void -Session::set_listen (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_listen)); -} - -void -Session::rt_set_listen (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - (*i)->set_listen (yn, group_override); - } - } - - set_dirty(); -} - -void -Session::set_mute (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - /* Set superficial value of mute controls for automation. */ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr<Route::MuteControllable> mc = boost::dynamic_pointer_cast<Route::MuteControllable> ((*i)->mute_control()); - mc->set_superficial_value(yn); - } - - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_mute)); -} - -void -Session::rt_set_mute (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_monitor() && !(*i)->is_auditioner()) { - (*i)->set_mute (yn, group_override); - } - } - - set_dirty(); -} - -void -Session::set_solo_isolated (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo_isolated)); -} - -void -Session::rt_set_solo_isolated (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) { - (*i)->set_solo_isolated (yn, group_override); - } - } - - set_dirty(); -} - -void -Session::set_record_enabled (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - if (!writable()) { - return; - } - - /* 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. - */ - - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if ((*i)->is_auditioner() || (*i)->record_safe ()) { - continue; - } - - boost::shared_ptr<Track> t; - - if ((t = boost::dynamic_pointer_cast<Track>(*i)) != 0) { - t->prep_record_enabled (yn, group_override); - } - } - - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_enabled)); -} - -void -Session::rt_set_record_enabled (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if ((*i)->is_auditioner() || (*i)->record_safe ()) { - continue; - } - - boost::shared_ptr<Track> t; - - if ((t = boost::dynamic_pointer_cast<Track>(*i)) != 0) { - t->set_record_enabled (yn, group_override); - } - } - - set_dirty (); -} - - -void -Session::set_record_safe (boost::shared_ptr<RouteList> rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - set_record_enabled (rl, false, after, group_override); - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_safe)); -} - -void -Session::rt_set_record_safe (boost::shared_ptr<RouteList> rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) { - if ((*i)->is_auditioner ()) { // REQUIRES REVIEW Can audiotioner be in Record Safe mode? - continue; - } - - boost::shared_ptr<Track> t; - - if ((t = boost::dynamic_pointer_cast<Track>(*i)) != 0) { - t->set_record_safe (yn, group_override); - } - } - - set_dirty (); -} - -void Session::process_rtop (SessionEvent* ev) { ev->rt_slot (); diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 4e59da7137..47c7885b85 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1265,7 +1265,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { // tell it we've looped, so it can deal with the record state tr->transport_looped (_transport_frame); } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 8aebf09259..84a6d71349 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -23,9 +23,11 @@ #include "ardour/diskstream.h" #include "ardour/io_processor.h" #include "ardour/meter.h" +#include "ardour/monitor_control.h" #include "ardour/playlist.h" #include "ardour/port.h" #include "ardour/processor.h" +#include "ardour/record_enable_control.h" #include "ardour/route_group_specialized.h" #include "ardour/session.h" #include "ardour/session_playlists.h" @@ -42,7 +44,6 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data : Route (sess, name, flag, default_type) , _saved_meter_point (_meter_point) , _mode (mode) - , _monitoring (MonitorAuto) { _freeze_record.state = NoFreeze; _declickable = true; @@ -62,17 +63,24 @@ Track::init () boost::shared_ptr<Route> rp (shared_from_this()); boost::shared_ptr<Track> rt = boost::dynamic_pointer_cast<Track> (rp); - _rec_enable_control = boost::shared_ptr<RecEnableControl> (new RecEnableControl(rt)); - _rec_enable_control->set_flags (Controllable::Toggle); - _monitoring_control.reset (new MonitoringControllable (X_("monitoring"), rt)); - /* don't add rec_enable_control to controls because we don't want it to - * appear as an automatable parameter - */ + _record_enable_control.reset (new RecordEnableControl (_session, X_("recenable"), *this)); + _record_enable_control->set_flags (Controllable::Toggle); + + _monitoring_control.reset (new MonitorControl (_session, X_("monitoring"), *this)); + + _record_safe_control.reset (new AutomationControl (_session, RecSafeAutomation, ParameterDescriptor (RecSafeAutomation), + boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (RecSafeAutomation))), + X_("recsafe"))); + track_number_changed.connect_same_thread (*this, boost::bind (&Track::resync_track_name, this)); _session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&Track::parameter_changed, this, _1)); - return 0; + _monitoring_control->Changed.connect_same_thread (*this, boost::bind (&Track::monitoring_changed, this, _1, _2)); + _record_safe_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_safe_changed, this, _1, _2)); + _record_enable_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_enable_changed, this, _1, _2)); + + return 0; } void @@ -97,9 +105,7 @@ XMLNode& Track::state (bool full) { XMLNode& root (Route::state (full)); - root.add_property (X_("monitoring"), enum_2_string (_monitoring)); root.add_property (X_("saved-meter-point"), enum_2_string (_saved_meter_point)); - root.add_child_nocopy (_rec_enable_control->get_state()); root.add_child_nocopy (_diskstream->get_state ()); return root; @@ -137,19 +143,13 @@ Track::set_state (const XMLNode& node, int version) XMLProperty const * prop; if (child->name() == Controllable::xml_node_name && (prop = child->property ("name")) != 0) { if (prop->value() == X_("recenable")) { - _rec_enable_control->set_state (*child, version); + _record_enable_control->set_state (*child, version); } } } XMLProperty const * prop; - if ((prop = node.property (X_("monitoring"))) != 0) { - _monitoring = MonitorChoice (string_2_enum (prop->value(), _monitoring)); - } else { - _monitoring = MonitorAuto; - } - if ((prop = node.property (X_("saved-meter-point"))) != 0) { _saved_meter_point = MeterPoint (string_2_enum (prop->value(), _saved_meter_point)); } else { @@ -178,62 +178,6 @@ Track::freeze_state() const return _freeze_record.state; } -Track::RecEnableControl::RecEnableControl (boost::shared_ptr<Track> t) - : AutomationControl (t->session(), - RecEnableAutomation, - ParameterDescriptor(Evoral::Parameter(RecEnableAutomation)), - boost::shared_ptr<AutomationList>(), - X_("recenable")) - , track (t) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(RecEnableAutomation))); - set_list (gl); -} - -void -Track::RecEnableControl::set_value (double val, Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -Track::RecEnableControl::set_value_unchecked (double val) -{ - if (writable()) { - _set_value (val, Controllable::NoGroup); - } -} - -void -Track::RecEnableControl::_set_value (double val, Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr<Track> t = track.lock (); - if (!t) { - return; - } - - t->set_record_enabled (val >= 0.5 ? true : false, group_override); -} - -double -Track::RecEnableControl::get_value () const -{ - boost::shared_ptr<Track> t = track.lock (); - if (!t) { - return 0; - } - - return (t->record_enabled() ? 1.0 : 0.0); -} - -bool -Track::record_enabled () const -{ - return _diskstream && _diskstream->record_enabled (); -} - bool Track::can_record() { @@ -246,24 +190,15 @@ Track::can_record() return will_record; } -void -Track::prep_record_enabled (bool yn, Controllable::GroupControlDisposition group_override) +int +Track::prep_record_enabled (bool yn) { - if (yn && record_safe ()) { - return; - } - - if (!_session.writable()) { - return; - } - - if (_freeze_record.state == Frozen) { - return; + if (yn && _record_safe_control->get_value()) { + return -1; } - if (use_group (group_override, &RouteGroup::is_recenable)) { - _route_group->apply (&Track::prep_record_enabled, yn, Controllable::NoGroup); - return; + if (!can_be_record_enabled()) { + return -1; } /* keep track of the meter point as it was before we rec-enabled */ @@ -288,31 +223,20 @@ Track::prep_record_enabled (bool yn, Controllable::GroupControlDisposition group set_meter_point (_saved_meter_point); } } + + return 0; } void -Track::set_record_enabled (bool yn, Controllable::GroupControlDisposition gcd) +Track::record_enable_changed (bool, Controllable::GroupControlDisposition) { - if (_diskstream->record_safe ()) { - return; - } - - if (!_session.writable()) { - return; - } - - if (_freeze_record.state == Frozen) { - return; - } - - if (use_group (gcd, &RouteGroup::is_recenable)) { - _route_group->apply (&Track::set_record_enabled, yn, Controllable::NoGroup); - return; - } - - _diskstream->set_record_enabled (yn); + _diskstream->set_record_enabled (_record_enable_control->get_value()); +} - _rec_enable_control->Changed (true, gcd); +void +Track::record_safe_changed (bool, Controllable::GroupControlDisposition) +{ + _diskstream->set_record_safe (_record_safe_control->get_value()); } bool @@ -336,12 +260,7 @@ Track::set_record_safe (bool yn, Controllable::GroupControlDisposition group_ove return; } - if (use_group (group_override, &RouteGroup::is_recenable)) { - _route_group->apply (&Track::set_record_safe, yn, Controllable::NoGroup); - return; - } - - _diskstream->set_record_safe (yn); + _rec_safe_control->set_value (yn, group_override); } void @@ -371,7 +290,7 @@ Track::set_name (const string& str) { bool ret; - if (record_enabled() && _session.actively_recording()) { + if (_record_enable_control->get_value() && _session.actively_recording()) { /* this messes things up if done while recording */ return false; } @@ -452,7 +371,7 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, if (!_active) { silence (nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->reset(); } return 0; @@ -611,8 +530,6 @@ Track::set_diskstream (boost::shared_ptr<Diskstream> ds) ds->PlaylistChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_playlist_changed, this)); diskstream_playlist_changed (); - ds->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_record_enable_changed, this)); - ds->RecordSafeChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_record_safe_changed, this)); ds->SpeedChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_speed_changed, this)); ds->AlignmentStyleChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_alignment_style_changed, this)); } @@ -624,18 +541,6 @@ Track::diskstream_playlist_changed () } void -Track::diskstream_record_enable_changed () -{ - RecordEnableChanged (); /* EMIT SIGNAL */ -} - -void -Track::diskstream_record_safe_changed () -{ - RecordSafeChanged (); /* EMIT SIGNAL */ -} - -void Track::diskstream_speed_changed () { SpeedChanged (); /* EMIT SIGNAL */ @@ -1014,12 +919,13 @@ MonitorState Track::monitoring_state () const { /* Explicit requests */ + MonitorChoice m (_monitoring_control->monitoring_choice()); - if (_monitoring & MonitorInput) { + if (m & MonitorInput) { return MonitoringInput; } - if (_monitoring & MonitorDisk) { + if (m & MonitorDisk) { return MonitoringDisk; } @@ -1086,7 +992,7 @@ Track::maybe_declick (BufferSet& bufs, framecnt_t nframes, int declick) ditto if we are monitoring inputs. */ - if (_have_internal_generator || monitoring_choice() == MonitorInput) { + if (_have_internal_generator || (_monitoring_control->monitoring_choice() == MonitorInput)) { return; } @@ -1136,22 +1042,10 @@ Track::check_initial_delay (framecnt_t nframes, framepos_t& transport_frame) } void -Track::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd) +Track::monitoring_changed (bool, Controllable::GroupControlDisposition) { - if (use_group (gcd, &RouteGroup::is_monitoring)) { - _route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup); - return; - } - - if (mc != _monitoring) { - _monitoring = mc; - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->monitoring_changed (); - } - - MonitoringChanged (); /* EMIT SIGNAL */ - _monitoring_control->Changed (true, gcd); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->monitoring_changed (); } } @@ -1161,64 +1055,10 @@ Track::metering_state () const bool rv; if (_session.transport_rolling ()) { // audio_track.cc || midi_track.cc roll() runs meter IFF: - rv = _meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled()); + rv = _meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled()); } else { // track no_roll() always metering if rv = _meter_point == MeterInput; } return rv ? MeteringInput : MeteringRoute; } - -Track::MonitoringControllable::MonitoringControllable (std::string name, boost::shared_ptr<Track> r) - : RouteAutomationControl (name, MonitoringAutomation, boost::shared_ptr<AutomationList>(), r) -{ - boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MonitoringAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Track::MonitoringControllable::set_value (double val, Controllable::GroupControlDisposition gcd) -{ - _set_value (val, gcd); -} - -void -Track::MonitoringControllable::_set_value (double val, Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr<Route> r = _route.lock(); - if (!r) { - return; - } - - boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (r); - if (!t) { - return; - } - - int mc = (int) val; - - if (mc < MonitorAuto || mc > MonitorDisk) { - return; - } - - /* no group effect at present */ - - t->set_monitoring ((MonitorChoice) mc, gcd); -} - -double -Track::MonitoringControllable::get_value () const -{ - boost::shared_ptr<Route> r = _route.lock(); - if (!r) { - return 0.0; - } - - boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (r); - if (!t) { - return 0.0; - } - - return t->monitoring_choice(); -} diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 08bb61065b..0ae1c569c8 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -754,3 +754,4 @@ ARDOUR::slider_position_to_gain_with_max (double g, double max_gain) extern "C" { void c_stacktrace() { stacktrace (cerr); } } + diff --git a/libs/ardour/vca.cc b/libs/ardour/vca.cc index d92fa67c03..39303fc8ca 100644 --- a/libs/ardour/vca.cc +++ b/libs/ardour/vca.cc @@ -63,6 +63,7 @@ VCA::get_next_vca_number () VCA::VCA (Session& s, uint32_t num, const string& name) : Stripable (s, name) + , Muteable (s, name) , Automatable (s) , _number (num) , _gain_control (new GainControl (s, Evoral::Parameter (GainAutomation), boost::shared_ptr<AutomationList> ())) @@ -74,8 +75,8 @@ VCA::VCA (Session& s, uint32_t num, const string& name) int VCA::init () { - _solo_control.reset (new VCASoloControllable (X_("solo"), shared_from_this())); - _mute_control.reset (new VCAMuteControllable (X_("mute"), shared_from_this())); + _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this)); + _mute_control.reset (new MuteControl (_session, X_("mute"), *this)); add_control (_gain_control); add_control (_solo_control); @@ -159,98 +160,3 @@ VCA::muted () const { return _mute_requested; } - -VCA::VCASoloControllable::VCASoloControllable (string const & name, boost::shared_ptr<VCA> vca) - : AutomationControl (vca->session(), Evoral::Parameter (SoloAutomation), ParameterDescriptor (Evoral::Parameter (SoloAutomation)), - boost::shared_ptr<AutomationList>(), name) - , _vca (vca) -{ -} - -void -VCA::VCASoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - if (writable()) { - _set_value (val, gcd); - } -} - -void -VCA::VCASoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr<VCA> vca = _vca.lock(); - if (!vca) { - return; - } - - vca->set_solo (val >= 0.5); - - AutomationControl::set_value (val, gcd); -} - -void -VCA::VCASoloControllable::set_value_unchecked (double val) -{ - /* used only by automation playback */ - _set_value (val, Controllable::NoGroup); -} - -double -VCA::VCASoloControllable::get_value() const -{ - boost::shared_ptr<VCA> vca = _vca.lock(); - if (!vca) { - return 0.0; - } - - return vca->soloed() ? 1.0 : 0.0; -} - -/*----*/ - -VCA::VCAMuteControllable::VCAMuteControllable (string const & name, boost::shared_ptr<VCA> vca) - : AutomationControl (vca->session(), Evoral::Parameter (MuteAutomation), ParameterDescriptor (Evoral::Parameter (MuteAutomation)), - boost::shared_ptr<AutomationList>(), name) - , _vca (vca) -{ -} - -void -VCA::VCAMuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - if (writable()) { - _set_value (val, gcd); - } -} - -void -VCA::VCAMuteControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr<VCA> vca = _vca.lock(); - - if (!vca) { - return; - } - - vca->set_mute (val >= 0.5); - - AutomationControl::set_value (val, gcd); -} - -void -VCA::VCAMuteControllable::set_value_unchecked (double val) -{ - /* used only by automation playback */ - _set_value (val, Controllable::NoGroup); -} - -double -VCA::VCAMuteControllable::get_value() const -{ - boost::shared_ptr<VCA> vca = _vca.lock(); - if (!vca) { - return 0.0; - } - - return vca->muted() ? 1.0 : 0.0; -} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 625bac8742..17876f889b 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -57,6 +57,7 @@ libardour_sources = [ 'chan_count.cc', 'chan_mapping.cc', 'config_text.cc', + 'control_group.cc', 'control_protocol_manager.cc', 'cycle_timer.cc', 'data_type.cc', @@ -137,12 +138,15 @@ libardour_sources = [ 'midi_stretch.cc', 'midi_track.cc', 'midi_ui.cc', + 'mididm.cc', 'midiport_manager.cc', 'mix.cc', + 'monitor_control.cc', 'monitor_processor.cc', 'mtc_slave.cc', - 'mididm.cc', 'mtdm.cc', + 'muteable.cc', + 'mute_control.cc', 'mute_master.cc', 'note_fixer.cc', 'onset_detector.cc', @@ -154,6 +158,7 @@ libardour_sources = [ 'panner_shell.cc', 'parameter_descriptor.cc', 'pcm_utils.cc', + 'phase_control.cc', 'playlist.cc', 'playlist_factory.cc', 'playlist_source.cc', @@ -170,13 +175,13 @@ libardour_sources = [ 'quantize.cc', 'rc_configuration.cc', 'recent_sessions.cc', + 'record_enable_control.cc', 'region_factory.cc', 'resampled_source.cc', 'region.cc', 'return.cc', 'reverse.cc', 'route.cc', - 'route_controls.cc', 'route_graph.cc', 'route_group.cc', 'route_group_member.cc', @@ -206,10 +211,14 @@ libardour_sources = [ 'session_transport.cc', 'sidechain.cc', 'slave.cc', + 'slavable_automation_control.cc', 'smf_source.cc', 'sndfile_helpers.cc', 'sndfileimportable.cc', 'sndfilesource.cc', + 'solo_control.cc', + 'solo_isolate_control.cc', + 'solo_safe_control.cc', 'soundcloud_upload.cc', 'source.cc', 'source_factory.cc', diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index 078671c91b..11ba979198 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -54,6 +54,7 @@ class LIBPBD_API Controllable : public PBD::StatefulDestructible { enum Flag { Toggle = 0x1, GainLike = 0x2, + RealTime = 0x4 }; Controllable (const std::string& name, Flag f = Flag (0)); diff --git a/libs/surfaces/control_protocol/control_protocol.cc b/libs/surfaces/control_protocol/control_protocol.cc index 5470232ad7..f413ad82d6 100644 --- a/libs/surfaces/control_protocol/control_protocol.cc +++ b/libs/surfaces/control_protocol/control_protocol.cc @@ -199,7 +199,7 @@ ControlProtocol::route_set_rec_enable (uint32_t table_index, bool yn) boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(r); if (at) { - at->set_record_enabled (yn, Controllable::NoGroup); + at->rec_enable_control()->set_value (1.0, Controllable::UseGroup); } } @@ -215,7 +215,7 @@ ControlProtocol::route_get_rec_enable (uint32_t table_index) boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(r); if (at) { - return at->record_enabled (); + return at->rec_enable_control()->get_value(); } return false; @@ -248,7 +248,7 @@ ControlProtocol::route_set_gain (uint32_t table_index, float gain) boost::shared_ptr<Route> r = route_table[table_index]; if (r != 0) { - r->set_gain (gain, Controllable::UseGroup); + r->gain_control()->set_value (gain, Controllable::UseGroup); } } @@ -298,7 +298,7 @@ ControlProtocol::route_get_muted (uint32_t table_index) return false; } - return r->muted (); + return r->mute_control()->muted (); } void @@ -311,7 +311,7 @@ ControlProtocol::route_set_muted (uint32_t table_index, bool yn) boost::shared_ptr<Route> r = route_table[table_index]; if (r != 0) { - r->set_mute (yn, Controllable::UseGroup); + r->mute_control()->set_value (yn ? 1.0 : 0.0, Controllable::UseGroup); } } @@ -342,7 +342,7 @@ ControlProtocol::route_set_soloed (uint32_t table_index, bool yn) boost::shared_ptr<Route> r = route_table[table_index]; if (r != 0) { - r->set_solo (yn, Controllable::UseGroup); + r->solo_control()->set_value (yn ? 1.0 : 0.0, Controllable::UseGroup); } } diff --git a/libs/surfaces/faderport/faderport.cc b/libs/surfaces/faderport/faderport.cc index d08264a42d..1df2fc4865 100644 --- a/libs/surfaces/faderport/faderport.cc +++ b/libs/surfaces/faderport/faderport.cc @@ -507,7 +507,7 @@ FaderPort::fader_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb) single route at a time, allow the fader to modify the group, if appropriate. */ - _current_route->set_gain (val, Controllable::UseGroup); + _current_route->gain_control()->set_value (val, Controllable::UseGroup); } } } @@ -1147,7 +1147,7 @@ FaderPort::set_current_route (boost::shared_ptr<Route> r) boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_route); if (t) { - t->RecordEnableChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this); + t->rec_enable_control()->Changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this); } boost::shared_ptr<AutomationControl> control = _current_route->gain_control (); @@ -1227,7 +1227,7 @@ FaderPort::map_mute () if (_current_route->muted()) { stop_blinking (Mute); get_button (Mute).set_led_state (_output_port, true); - } else if (_current_route->muted_by_others()) { + } else if (_current_route->mute_control()->muted_by_others()) { start_blinking (Mute); } else { stop_blinking (Mute); @@ -1252,7 +1252,7 @@ FaderPort::map_recenable () { boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_route); if (t) { - get_button (Rec).set_led_state (_output_port, t->record_enabled()); + get_button (Rec).set_led_state (_output_port, t->rec_enable_control()->get_value()); } else { get_button (Rec).set_led_state (_output_port, false); } diff --git a/libs/surfaces/faderport/operations.cc b/libs/surfaces/faderport/operations.cc index bb72794504..592b26da23 100644 --- a/libs/surfaces/faderport/operations.cc +++ b/libs/surfaces/faderport/operations.cc @@ -129,9 +129,9 @@ FaderPort::mute () return; } - boost::shared_ptr<RouteList> rl (new RouteList); - rl->push_back (_current_route); - session->set_mute (rl, !_current_route->muted()); + boost::shared_ptr<ControlList> cl (new ControlList); + cl->push_back (_current_route->mute_control()); + session->set_controls (cl, !_current_route->muted(), PBD::Controllable::UseGroup); } void @@ -141,14 +141,15 @@ FaderPort::solo () return; } - boost::shared_ptr<RouteList> rl (new RouteList); - rl->push_back (_current_route); + bool yn; if (Config->get_solo_control_is_listen_control()) { - session->set_listen (rl, !_current_route->listening_via_monitor()); + yn = !_current_route->listening_via_monitor(); } else { - session->set_solo (rl, !_current_route->soloed()); + yn = !_current_route->soloed(); } + + _current_route->solo_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::UseGroup); } void @@ -164,10 +165,7 @@ FaderPort::rec_enable () return; } - boost::shared_ptr<RouteList> rl (new RouteList); - rl->push_back (_current_route); - - session->set_record_enabled (rl, !t->record_enabled()); + t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup); } void diff --git a/libs/surfaces/mackie/mcp_buttons.cc b/libs/surfaces/mackie/mcp_buttons.cc index 1869d5e231..025d56d25a 100644 --- a/libs/surfaces/mackie/mcp_buttons.cc +++ b/libs/surfaces/mackie/mcp_buttons.cc @@ -383,7 +383,7 @@ MackieControlProtocol::save_press (Button &) } else { save_state (); } - + return none; } @@ -886,15 +886,9 @@ MackieControlProtocol::clearsolo_press (Mackie::Button&) access_action ("Editor/set-session-from-edit-range"); return none; } - - if (session) { - if (session->soloing()) { - session->set_solo (session->get_routes(), false); - } else if (session->listening()) { - session->set_listen (session->get_routes(), false); - } - session->clear_all_solo_state (session->get_routes()); // safeguard, ideally this won't do anything, check the log-window + if (session) { + session->clear_all_solo_state (session->get_routes()); } return none; } @@ -1063,7 +1057,7 @@ MackieControlProtocol::nudge_release (Mackie::Button&) _modifier_state &= ~MODIFIER_NUDGE; /* XXX these action names are stupid, because the action can affect - * regions, markers or the playhead depending on selection state. + * regions, markers or the playhead depending on selection state. */ if (main_modifier_state() & MODIFIER_SHIFT) { diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc index caa3a3bc66..0062c3b4fb 100644 --- a/libs/surfaces/mackie/strip.cc +++ b/libs/surfaces/mackie/strip.cc @@ -37,14 +37,17 @@ #include "ardour/debug.h" #include "ardour/midi_ui.h" #include "ardour/meter.h" +#include "ardour/monitor_control.h" #include "ardour/plugin_insert.h" #include "ardour/pannable.h" #include "ardour/panner.h" #include "ardour/panner_shell.h" +#include "ardour/phase_control.h" #include "ardour/rc_configuration.h" #include "ardour/route.h" #include "ardour/session.h" #include "ardour/send.h" +#include "ardour/solo_isolate_control.h" #include "ardour/track.h" #include "ardour/midi_track.h" #include "ardour/user_bundle.h" @@ -302,7 +305,10 @@ void Strip::notify_record_enable_changed () { if (_route && _recenable) { - _surface->write (_recenable->set_state (_route->record_enabled() ? on : off)); + boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (_route); + if (trk) { + _surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off)); + } } } diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc index b26c2d8597..d758e29f03 100644 --- a/libs/surfaces/osc/osc.cc +++ b/libs/surfaces/osc/osc.cc @@ -974,7 +974,7 @@ OSC::routes_list (lo_message msg) || boost::dynamic_pointer_cast<MidiTrack>(r)) { boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r); - lo_message_add_int32 (reply, t->record_enabled()); + lo_message_add_int32 (reply, (int32_t) t->rec_enable_control()->get_value()); } //Automatically listen to routes listed @@ -1054,7 +1054,7 @@ OSC::route_mute (int rid, int yn) boost::shared_ptr<Route> r = session->route_by_remote_id (rid); if (r) { - r->set_mute (yn, PBD::Controllable::NoGroup); + r->mute_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::NoGroup); } return 0; @@ -1068,7 +1068,7 @@ OSC::route_solo (int rid, int yn) boost::shared_ptr<Route> r = session->route_by_remote_id (rid); if (r) { - r->solo_control()->set_value(yn, PBD::Controllable::NoGroup); + r->solo_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::NoGroup); } return 0; @@ -1082,7 +1082,10 @@ OSC::route_recenable (int rid, int yn) boost::shared_ptr<Route> r = session->route_by_remote_id (rid); if (r) { - r->set_record_enabled (yn, PBD::Controllable::NoGroup); + boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (r); + if (trk) { + trk->rec_enable_control()->set_value (yn, PBD::Controllable::UseGroup); + } } return 0; @@ -1096,7 +1099,7 @@ OSC::route_set_gain_abs (int rid, float level) boost::shared_ptr<Route> r = session->route_by_remote_id (rid); if (r) { - r->set_gain (level, PBD::Controllable::NoGroup); + r->gain_control()->set_value (level, PBD::Controllable::NoGroup); } return 0; |