redesign control slave/master system, move code from GainControl to AutomationControl
This commit is contained in:
parent
74f8a81276
commit
c660703f95
@ -21,10 +21,16 @@
|
||||
#ifndef __ardour_automation_control_h__
|
||||
#define __ardour_automation_control_h__
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
|
||||
#include "pbd/controllable.h"
|
||||
|
||||
#include "evoral/types.hpp"
|
||||
#include "evoral/Control.hpp"
|
||||
|
||||
#include "ardour/libardour_visibility.h"
|
||||
@ -111,10 +117,48 @@ public:
|
||||
const ARDOUR::Session& session() const { return _session; }
|
||||
void commit_transaction (bool did_write);
|
||||
|
||||
protected:
|
||||
void add_master (boost::shared_ptr<AutomationControl>);
|
||||
void remove_master (boost::shared_ptr<AutomationControl>);
|
||||
void clear_masters ();
|
||||
bool slaved_to (boost::shared_ptr<AutomationControl>) const;
|
||||
bool slaved () const;
|
||||
std::vector<PBD::ID> masters () const;
|
||||
|
||||
PBD::Signal0<void> MasterStatusChange;
|
||||
|
||||
protected:
|
||||
ARDOUR::Session& _session;
|
||||
|
||||
const ParameterDescriptor _desc;
|
||||
|
||||
|
||||
class MasterRecord {
|
||||
public:
|
||||
MasterRecord (boost::shared_ptr<AutomationControl> gc, double r)
|
||||
: _master (gc)
|
||||
, _ratio (r)
|
||||
{}
|
||||
|
||||
boost::shared_ptr<AutomationControl> master() const { return _master; }
|
||||
double ratio () const { return _ratio; }
|
||||
void reset_ratio (double r) { _ratio = r; }
|
||||
|
||||
PBD::ScopedConnection connection;
|
||||
|
||||
private:
|
||||
boost::shared_ptr<AutomationControl> _master;
|
||||
double _ratio;
|
||||
|
||||
};
|
||||
|
||||
mutable Glib::Threads::RWLock master_lock;
|
||||
typedef std::map<PBD::ID,MasterRecord> Masters;
|
||||
Masters _masters;
|
||||
PBD::ScopedConnectionList masters_connections;
|
||||
|
||||
void master_going_away (boost::weak_ptr<AutomationControl>);
|
||||
virtual void recompute_masters_ratios (double val) { /* do nothing by default */}
|
||||
double get_value_locked() const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -20,10 +20,8 @@
|
||||
#define __ardour_gain_control_h__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include "pbd/controllable.h"
|
||||
|
||||
@ -41,7 +39,6 @@ class LIBARDOUR_API GainControl : public AutomationControl {
|
||||
GainControl (Session& session, const Evoral::Parameter ¶m,
|
||||
boost::shared_ptr<AutomationList> al = boost::shared_ptr<AutomationList>());
|
||||
|
||||
double get_value () const;
|
||||
void set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
|
||||
void set_value_unchecked (double);
|
||||
|
||||
@ -54,51 +51,15 @@ class LIBARDOUR_API GainControl : public AutomationControl {
|
||||
double lower_db;
|
||||
double range_db;
|
||||
|
||||
gain_t get_master_gain () const;
|
||||
void add_master (boost::shared_ptr<VCA>);
|
||||
void remove_master (boost::shared_ptr<VCA>);
|
||||
void clear_masters ();
|
||||
bool slaved_to (boost::shared_ptr<VCA>) const;
|
||||
bool slaved () const;
|
||||
std::vector<uint32_t> masters () const;
|
||||
|
||||
PBD::Signal0<void> VCAStatusChange;
|
||||
|
||||
int set_state (XMLNode const&, int);
|
||||
XMLNode& get_state();
|
||||
|
||||
private:
|
||||
class MasterRecord {
|
||||
public:
|
||||
MasterRecord (boost::shared_ptr<AutomationControl> gc, double r)
|
||||
: _master (gc)
|
||||
, _ratio (r)
|
||||
{}
|
||||
|
||||
boost::shared_ptr<AutomationControl> master() const { return _master; }
|
||||
double ratio () const { return _ratio; }
|
||||
void reset_ratio (double r) { _ratio = r; }
|
||||
|
||||
PBD::ScopedConnection connection;
|
||||
|
||||
private:
|
||||
boost::shared_ptr<AutomationControl> _master;
|
||||
double _ratio;
|
||||
|
||||
};
|
||||
|
||||
mutable Glib::Threads::RWLock master_lock;
|
||||
typedef std::map<uint32_t,MasterRecord> Masters;
|
||||
Masters _masters;
|
||||
PBD::ScopedConnectionList masters_connections;
|
||||
std::string masters_string;
|
||||
PBD::ScopedConnection vca_loaded_connection;
|
||||
|
||||
gain_t get_value_locked () const;
|
||||
gain_t get_master_gain_locked () const;
|
||||
void master_going_away (boost::weak_ptr<VCA>);
|
||||
void recompute_masters_ratios (double val);
|
||||
void vcas_loaded();
|
||||
void recompute_masters_ratios (double val);
|
||||
|
||||
void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override);
|
||||
};
|
||||
|
@ -56,6 +56,7 @@ AutomationControl::AutomationControl(ARDOUR::Session& s
|
||||
|
||||
AutomationControl::~AutomationControl ()
|
||||
{
|
||||
DropReferences (); /* EMIT SIGNAL */
|
||||
}
|
||||
|
||||
bool
|
||||
@ -73,9 +74,36 @@ double
|
||||
AutomationControl::get_value() const
|
||||
{
|
||||
bool from_list = _list && ((AutomationList*)_list.get())->automation_playback();
|
||||
return Control::get_double (from_list, _session.transport_frame());
|
||||
|
||||
if (!from_list) {
|
||||
Glib::Threads::RWLock::ReaderLock lm (master_lock);
|
||||
return get_value_locked ();
|
||||
} else {
|
||||
return Control::get_double (from_list, _session.transport_frame());
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
AutomationControl::get_value_locked() const
|
||||
{
|
||||
/* read or write masters lock must be held */
|
||||
|
||||
if (_masters.empty()) {
|
||||
return Control::get_double (false, _session.transport_frame());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Set the value and do the right thing based on automation state
|
||||
* (e.g. record if necessary, etc.)
|
||||
* @param value `user' value
|
||||
@ -232,3 +260,102 @@ AutomationControl::interface_to_internal (double val) const
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
AutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
|
||||
{
|
||||
double current_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.
|
||||
*/
|
||||
|
||||
m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
|
||||
}
|
||||
}
|
||||
|
||||
if (res.second) {
|
||||
MasterStatusChange (); /* 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (erased) {
|
||||
MasterStatusChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AutomationControl::clear_masters ()
|
||||
{
|
||||
bool had_masters = false;
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (master_lock);
|
||||
if (!_masters.empty()) {
|
||||
had_masters = true;
|
||||
}
|
||||
_masters.clear ();
|
||||
}
|
||||
|
||||
if (had_masters) {
|
||||
MasterStatusChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AutomationControl::slaved_to (boost::shared_ptr<AutomationControl> m) const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (master_lock);
|
||||
return _masters.find (m->id()) != _masters.end();
|
||||
}
|
||||
|
||||
bool
|
||||
AutomationControl::slaved () const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (master_lock);
|
||||
return !_masters.empty();
|
||||
}
|
||||
|
||||
|
@ -43,32 +43,6 @@ GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, boos
|
||||
range_db = accurate_coefficient_to_dB (_desc.upper) - lower_db;
|
||||
}
|
||||
|
||||
gain_t
|
||||
GainControl::get_value_locked () const {
|
||||
|
||||
/* read or write masters lock must be held */
|
||||
|
||||
if (_masters.empty()) {
|
||||
return AutomationControl::get_value();
|
||||
}
|
||||
|
||||
gain_t g = 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 */
|
||||
g *= mr->second.master()->get_value () * mr->second.ratio();
|
||||
}
|
||||
|
||||
return min (Config->get_max_gain(), g);
|
||||
}
|
||||
|
||||
double
|
||||
GainControl::get_value () const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (master_lock);
|
||||
return get_value_locked ();
|
||||
}
|
||||
|
||||
void
|
||||
GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override)
|
||||
{
|
||||
@ -97,6 +71,10 @@ GainControl::_set_value (double val, Controllable::GroupControlDisposition group
|
||||
}
|
||||
}
|
||||
|
||||
/* this sets the Evoral::Control::_user_value for us, which will
|
||||
be retrieved by AutomationControl::get_value ()
|
||||
*/
|
||||
|
||||
AutomationControl::set_value (val, group_override);
|
||||
|
||||
_session.set_dirty ();
|
||||
@ -141,118 +119,6 @@ GainControl::get_user_string () const
|
||||
return std::string(theBuf);
|
||||
}
|
||||
|
||||
gain_t
|
||||
GainControl::get_master_gain () const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock sm (master_lock, Glib::Threads::TRY_LOCK);
|
||||
|
||||
if (sm.locked()) {
|
||||
return get_master_gain_locked ();
|
||||
}
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
gain_t
|
||||
GainControl::get_master_gain_locked () const
|
||||
{
|
||||
/* Master lock MUST be held (read or write lock is acceptable) */
|
||||
|
||||
gain_t g = 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 */
|
||||
g *= mr->second.master()->get_value () * mr->second.ratio();
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
void
|
||||
GainControl::add_master (boost::shared_ptr<VCA> vca)
|
||||
{
|
||||
gain_t current_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<uint32_t,MasterRecord> (vca->number(), MasterRecord (vca->gain_control(), 0.0)));
|
||||
|
||||
if (res.second) {
|
||||
|
||||
recompute_masters_ratios (current_value);
|
||||
|
||||
/* note that we bind @param m as a weak_ptr<GainControl>, thus
|
||||
avoiding holding a reference to the control in the binding
|
||||
itself.
|
||||
*/
|
||||
|
||||
vca->DropReferences.connect_same_thread (masters_connections, boost::bind (&GainControl::master_going_away, this, vca));
|
||||
|
||||
/* 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 VCA.
|
||||
*/
|
||||
|
||||
vca->gain_control()->Changed.connect_same_thread (res.first->second.connection, boost::bind (&PBD::Signal0<void>::operator(), &Changed));
|
||||
}
|
||||
}
|
||||
|
||||
if (res.second) {
|
||||
VCAStatusChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GainControl::master_going_away (boost::weak_ptr<VCA> wv)
|
||||
{
|
||||
boost::shared_ptr<VCA> v = wv.lock();
|
||||
if (v) {
|
||||
remove_master (v);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GainControl::remove_master (boost::shared_ptr<VCA> vca)
|
||||
{
|
||||
gain_t current_value;
|
||||
Masters::size_type erased = 0;
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (master_lock);
|
||||
current_value = get_value_locked ();
|
||||
erased = _masters.erase (vca->number());
|
||||
if (erased) {
|
||||
recompute_masters_ratios (current_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (erased) {
|
||||
VCAStatusChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GainControl::clear_masters ()
|
||||
{
|
||||
bool had_masters = false;
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (master_lock);
|
||||
if (!_masters.empty()) {
|
||||
had_masters = true;
|
||||
}
|
||||
_masters.clear ();
|
||||
}
|
||||
|
||||
if (had_masters) {
|
||||
VCAStatusChange (); /* EMIT SIGNAL */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GainControl::recompute_masters_ratios (double val)
|
||||
{
|
||||
@ -301,25 +167,12 @@ GainControl::recompute_masters_ratios (double val)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GainControl::slaved_to (boost::shared_ptr<VCA> vca) const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (master_lock);
|
||||
return _masters.find (vca->number()) != _masters.end();
|
||||
}
|
||||
|
||||
bool
|
||||
GainControl::slaved () const
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (master_lock);
|
||||
return !_masters.empty();
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
GainControl::get_state ()
|
||||
{
|
||||
XMLNode& node (AutomationControl::get_state());
|
||||
|
||||
#if 0
|
||||
/* store VCA master IDs */
|
||||
|
||||
string str;
|
||||
@ -337,6 +190,7 @@ GainControl::get_state ()
|
||||
if (!str.empty()) {
|
||||
node.add_property (X_("masters"), str);
|
||||
}
|
||||
#endif
|
||||
|
||||
return node;
|
||||
}
|
||||
@ -346,6 +200,7 @@ GainControl::set_state (XMLNode const& node, int version)
|
||||
{
|
||||
AutomationControl::set_state (node, version);
|
||||
|
||||
#if 0
|
||||
XMLProperty const* prop = node.property (X_("masters"));
|
||||
|
||||
/* Problem here if we allow VCA's to be slaved to other VCA's .. we
|
||||
@ -362,6 +217,7 @@ GainControl::set_state (XMLNode const& node, int version)
|
||||
_session.vca_manager().VCAsLoaded.connect_same_thread (vca_loaded_connection, boost::bind (&GainControl::vcas_loaded, this));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -379,7 +235,7 @@ GainControl::vcas_loaded ()
|
||||
for (vector<string>::const_iterator m = masters.begin(); m != masters.end(); ++m) {
|
||||
boost::shared_ptr<VCA> vca = _session.vca_manager().vca_by_number (PBD::atoi (*m));
|
||||
if (vca) {
|
||||
add_master (vca);
|
||||
add_master (vca->gain_control());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5892,13 +5892,13 @@ Route::slaved_to (boost::shared_ptr<VCA> vca) const
|
||||
return false;
|
||||
}
|
||||
|
||||
return _gain_control->slaved_to (vca);
|
||||
return _gain_control->slaved_to (vca->gain_control());
|
||||
}
|
||||
|
||||
void
|
||||
Route::vca_assign (boost::shared_ptr<VCA> vca)
|
||||
{
|
||||
_gain_control->add_master (vca);
|
||||
_gain_control->add_master (vca->gain_control());
|
||||
vca->add_solo_target (shared_from_this());
|
||||
vca->add_mute_target (shared_from_this());
|
||||
}
|
||||
@ -5911,7 +5911,7 @@ Route::vca_unassign (boost::shared_ptr<VCA> vca)
|
||||
_gain_control->clear_masters ();
|
||||
/* XXXX need to remove from solo/mute target lists */
|
||||
} else {
|
||||
_gain_control->remove_master (vca);
|
||||
_gain_control->remove_master (vca->gain_control());
|
||||
vca->remove_solo_target (shared_from_this());
|
||||
vca->remove_mute_target (shared_from_this());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user