13
0

redesign control slave/master system, move code from GainControl to AutomationControl

This commit is contained in:
Paul Davis 2016-03-08 23:29:17 -05:00
parent 74f8a81276
commit c660703f95
5 changed files with 186 additions and 198 deletions

View File

@ -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;
};

View File

@ -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 &param,
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);
};

View File

@ -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();
}

View File

@ -43,32 +43,6 @@ GainControl::GainControl (Session& session, const Evoral::Parameter &param, 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());
}
}

View File

@ -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());
}