13
0

save/restore VCA master state inside slaves, so that a reloaded session ends up back in the same state

This commit is contained in:
Paul Davis 2017-02-06 16:18:09 +01:00
parent 4161a60244
commit edd1061c3d
10 changed files with 169 additions and 69 deletions

View File

@ -128,7 +128,7 @@ ControlSlaveUI::vca_menu_toggle (Gtk::CheckMenuItem* menuitem, uint32_t n)
if (!menuitem->get_active()) {
sl->unassign (vca);
} else {
sl->assign (vca);
sl->assign (vca, false);
}
}

View File

@ -500,7 +500,7 @@ GroupTabs::assign_some_to_master (uint32_t which, RouteList rl)
}
for (RouteList::iterator r = rl.begin(); r != rl.end(); ++r) {
(*r)->assign (master);
(*r)->assign (master, false);
}
}

View File

@ -431,7 +431,7 @@ MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
{
boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
if (sl)
sl->assign(vca);
sl->assign(vca, false);
}
void

View File

@ -48,9 +48,6 @@ class LIBARDOUR_API GainControl : public SlavableAutomationControl {
double lower_db;
double range_db;
int set_state (XMLNode const&, int);
XMLNode& get_state();
void inc_gain (gain_t);
private:

View File

@ -49,7 +49,7 @@ class LIBARDOUR_API Slavable
XMLNode& get_state () const;
int set_state (XMLNode const&, int);
void assign (boost::shared_ptr<VCA>);
void assign (boost::shared_ptr<VCA>, bool loading);
void unassign (boost::shared_ptr<VCA>);
PBD::Signal2<void,boost::shared_ptr<VCA>,bool> AssignmentChange;
@ -62,7 +62,7 @@ class LIBARDOUR_API Slavable
static PBD::Signal1<void,VCAManager*> Assign;
protected:
virtual int assign_controls (boost::shared_ptr<VCA>);
virtual int assign_controls (boost::shared_ptr<VCA>, bool loading);
virtual int unassign_controls (boost::shared_ptr<VCA>);
private:

View File

@ -36,9 +36,11 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
PBD::Controllable::Flag flags=PBD::Controllable::Flag (0)
);
~SlavableAutomationControl ();
double get_value () const;
void add_master (boost::shared_ptr<AutomationControl>);
void add_master (boost::shared_ptr<AutomationControl>, bool loading);
void remove_master (boost::shared_ptr<AutomationControl>);
void clear_masters ();
bool slaved_to (boost::shared_ptr<AutomationControl>) const;
@ -57,6 +59,11 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
PBD::Signal0<void> MasterStatusChange;
void use_saved_master_ratios ();
int set_state (XMLNode const&, int);
XMLNode& get_state();
protected:
class MasterRecord {
@ -111,7 +118,7 @@ class LIBARDOUR_API SlavableAutomationControl : public AutomationControl
virtual void pre_remove_master (boost::shared_ptr<AutomationControl>) {}
virtual void post_add_master (boost::shared_ptr<AutomationControl>) {}
XMLNode* _masters_node; /* used to store master ratios in ::set_state() for later use */
};
} // namespace ARDOUR

View File

@ -133,7 +133,6 @@ GainControl::recompute_masters_ratios (double val)
Mr(n) is the new ratio number for the slaves
*/
const double nmasters = _masters.size();
double masters_total_gain_coefficient = 1.0;
@ -148,37 +147,3 @@ GainControl::recompute_masters_ratios (double val)
}
}
XMLNode&
GainControl::get_state ()
{
XMLNode& node (AutomationControl::get_state());
#if 0
/* store VCA master IDs */
string str;
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
if (!str.empty()) {
str += ',';
}
str += PBD::to_string (mr->first, std::dec);
}
}
if (!str.empty()) {
node.add_property (X_("masters"), str);
}
#endif
return node;
}
int
GainControl::set_state (XMLNode const& node, int version)
{
return AutomationControl::set_state (node, version);
}

View File

@ -180,7 +180,7 @@ RouteGroup::add (boost::shared_ptr<Route> r)
boost::shared_ptr<VCA> vca (group_master.lock());
if (vca) {
r->assign (vca);
r->assign (vca, false);
}
_session.set_dirty ();
@ -623,7 +623,7 @@ RouteGroup::assign_master (boost::shared_ptr<VCA> master)
}
for (RouteList::iterator r = routes->begin(); r != routes->end(); ++r) {
(*r)->assign (master);
(*r)->assign (master, false);
}
group_master = master;

View File

@ -84,6 +84,17 @@ Slavable::set_state (XMLNode const& node, int version)
}
/* Gain, solo & mute are currently the only controls that are
* automatically slaved to the master's own equivalent controls.
*/
static AutomationType auto_slave_types[] = {
GainAutomation,
SoloAutomation,
MuteAutomation,
NullAutomation
};
int
Slavable::do_assign (VCAManager* manager)
{
@ -104,8 +115,22 @@ Slavable::do_assign (VCAManager* manager)
/* now that we've released the lock, we can do the assignments */
for (std::vector<boost::shared_ptr<VCA> >::iterator v = vcas.begin(); v != vcas.end(); ++v) {
assign (*v);
if (!vcas.empty()) {
for (std::vector<boost::shared_ptr<VCA> >::iterator v = vcas.begin(); v != vcas.end(); ++v) {
assign (*v, true);
}
for (uint32_t n = 0; auto_slave_types[n] != NullAutomation; ++n) {
boost::shared_ptr<SlavableAutomationControl> slave;
slave = boost::dynamic_pointer_cast<SlavableAutomationControl> (automation_control (auto_slave_types[n]));
if (slave) {
slave->use_saved_master_ratios ();
}
}
}
assign_connection.disconnect ();
@ -114,12 +139,12 @@ Slavable::do_assign (VCAManager* manager)
}
void
Slavable::assign (boost::shared_ptr<VCA> v)
Slavable::assign (boost::shared_ptr<VCA> v, bool loading)
{
assert (v);
{
Glib::Threads::RWLock::WriterLock lm (master_lock);
if (assign_controls (v) == 0) {
if (assign_controls (v, loading) == 0) {
_masters.insert (v->number());
}
@ -161,19 +186,8 @@ Slavable::unassign (boost::shared_ptr<VCA> v)
AssignmentChange (v, false);
}
/* Gain, solo & mute are currently the only controls that are
* automatically slaved to the master's own equivalent controls.
*/
static AutomationType auto_slave_types[] = {
GainAutomation,
SoloAutomation,
MuteAutomation,
NullAutomation
};
int
Slavable::assign_controls (boost::shared_ptr<VCA> vca)
Slavable::assign_controls (boost::shared_ptr<VCA> vca, bool loading)
{
boost::shared_ptr<SlavableAutomationControl> slave;
boost::shared_ptr<AutomationControl> master;
@ -184,7 +198,7 @@ Slavable::assign_controls (boost::shared_ptr<VCA> vca)
master = vca->automation_control (auto_slave_types[n]);
if (slave && master) {
slave->add_master (master);
slave->add_master (master, loading);
}
}

View File

@ -20,6 +20,8 @@
#define __libardour_slavable_automation_control_h__
#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "pbd/i18n.h"
#include "ardour/slavable_automation_control.h"
#include "ardour/session.h"
@ -35,9 +37,18 @@ SlavableAutomationControl::SlavableAutomationControl(ARDOUR::Session& s,
const std::string& name,
Controllable::Flag flags)
: AutomationControl (s, parameter, desc, l, name, flags)
, _masters_node (0)
{
}
SlavableAutomationControl::~SlavableAutomationControl ()
{
if (_masters_node) {
delete _masters_node;
_masters_node = 0;
}
}
double
SlavableAutomationControl::get_masters_value_locked () const
{
@ -116,7 +127,7 @@ SlavableAutomationControl::actually_set_value (double val, Controllable::GroupCo
}
void
SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m, bool loading)
{
std::pair<Masters::iterator,bool> res;
@ -131,7 +142,9 @@ SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
if (res.second) {
recompute_masters_ratios (current_value);
if (!loading) {
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
@ -143,11 +156,11 @@ SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> 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.
AutomationControl.
Note that this also makes it safe to store a
boost::shared_ptr<AutomationControl> in the functor,
since we know we will destroy the functor when the
since we know we will destroy the functor when the
connection is destroyed, which happens when we
disconnect from the master (for any reason).
@ -157,7 +170,6 @@ SlavableAutomationControl::add_master (boost::shared_ptr<AutomationControl> m)
*/
m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&SlavableAutomationControl::master_changed, this, _1, _2, m));
cerr << this << enum_2_string ((AutomationType) _parameter.type()) << " now listening to Changed from " << m << endl;
}
}
@ -330,4 +342,109 @@ SlavableAutomationControl::slaved () const
return !_masters.empty();
}
void
SlavableAutomationControl::use_saved_master_ratios ()
{
if (!_masters_node) {
return;
}
Glib::Threads::RWLock::ReaderLock lm (master_lock);
/* use stored state, do not recompute */
if (_desc.toggled) {
XMLNodeList nlist = _masters_node->children();
XMLNodeIterator niter;
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
XMLProperty const * id_prop = (*niter)->property (X_("id"));
if (!id_prop) {
continue;
}
XMLProperty const * yn_prop = (*niter)->property (X_("yn"));
if (!yn_prop) {
continue;
}
Masters::iterator mi = _masters.find (ID (id_prop->value()));
if (mi != _masters.end()) {
mi->second.set_yn (string_is_affirmative (yn_prop->value()));
}
}
} else {
XMLProperty const * prop = _masters_node->property (X_("ratio"));
if (prop) {
gain_t ratio;
sscanf (prop->value().c_str(), "%g", &ratio);
for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
mr->second.reset_ratio (ratio);
}
} else {
PBD::error << string_compose (_("programming error: %1"), X_("missing ratio information for control slave"))<< endmsg;
}
}
delete _masters_node;
_masters_node = 0;
return;
}
XMLNode&
SlavableAutomationControl::get_state ()
{
XMLNode& node (AutomationControl::get_state());
/* store VCA master ratios */
{
Glib::Threads::RWLock::ReaderLock lm (master_lock);
if (!_masters.empty()) {
XMLNode* masters_node = new XMLNode (X_("masters"));
if (_desc.toggled) {
for (Masters::iterator mr = _masters.begin(); mr != _masters.end(); ++mr) {
XMLNode* mnode = new XMLNode (X_("master"));
mnode->add_property (X_("id"), mr->second.master()->id().to_s());
mnode->add_property (X_("yn"), mr->second.yn());
masters_node->add_child_nocopy (*mnode);
}
} else {
XMLNode* masters_node = new XMLNode (X_("masters"));
/* ratio is the same for all masters, so just store one */
masters_node->add_property (X_("ratio"), PBD::to_string (_masters.begin()->second.ratio(), std::dec));
}
node.add_child_nocopy (*masters_node);
}
}
return node;
}
int
SlavableAutomationControl::set_state (XMLNode const& node, int version)
{
XMLNodeList nlist = node.children();
XMLNodeIterator niter;
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
if ((*niter)->name() == X_("masters")) {
_masters_node = new XMLNode (**niter);
}
}
return AutomationControl::set_state (node, version);
}
#endif /* __libardour_slavable_automation_control_h__ */