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