13
0

add new files to source tree

This commit is contained in:
Paul Davis 2016-04-08 16:51:34 -04:00
parent 653ae4acd6
commit 4aa1c242ab
22 changed files with 2206 additions and 0 deletions

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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__ */

View 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);
}
}
}
}

View 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
View 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
View 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))
{
}

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

View 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);
}

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

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

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