Merge branch 'master' into mixer-snapshots

This commit is contained in:
Nikolaus Gullotta 2019-03-26 10:29:21 -05:00
commit efdb93eddb
17 changed files with 472 additions and 516 deletions

View file

@ -26,7 +26,6 @@
#include <glibmm/threads.h>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "pbd/controllable.h"
@ -51,7 +50,6 @@ class ControlGroup;
class LIBARDOUR_API AutomationControl
: public PBD::Controllable
, public Evoral::Control
, public boost::enable_shared_from_this<AutomationControl>
, public ControlGroupMember
, public SessionHandleRef
{

View file

@ -192,6 +192,7 @@ private:
MPControl<bool>& soloed;
ChannelRecord (uint32_t);
~ChannelRecord ();
};
std::vector<ChannelRecord*> _channels;

View file

@ -1079,7 +1079,6 @@ public:
boost::shared_ptr<AutomationControl> automation_control_by_id (const PBD::ID&);
void add_controllable (boost::shared_ptr<PBD::Controllable>);
void remove_controllable (PBD::Controllable*);
boost::shared_ptr<PBD::Controllable> solo_cut_control() const;

View file

@ -141,7 +141,7 @@ AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposi
}
if (_group && _group->use_me (gcd)) {
_group->set_group_value (shared_from_this(), val);
_group->set_group_value (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()), val);
} else {
actually_set_value (val, gcd);
}
@ -252,7 +252,7 @@ AutomationControl::set_automation_state (AutoState as)
}
if (as == Write) {
AutomationWatch::instance().add_automation_watch (shared_from_this());
AutomationWatch::instance().add_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
} else if (as & (Touch | Latch)) {
if (alist()->empty()) {
Control::set_double (val, _session.current_start_sample (), true);
@ -260,17 +260,17 @@ AutomationControl::set_automation_state (AutoState as)
Changed (true, Controllable::NoGroup);
}
if (!touching()) {
AutomationWatch::instance().remove_automation_watch (shared_from_this());
AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
} else {
/* this seems unlikely, but the combination of
* a control surface and the mouse could make
* it possible to put the control into Touch
* mode *while* touching it.
*/
AutomationWatch::instance().add_automation_watch (shared_from_this());
AutomationWatch::instance().add_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
}
} else {
AutomationWatch::instance().remove_automation_watch (shared_from_this());
AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
Changed (false, Controllable::NoGroup);
}
}
@ -293,7 +293,7 @@ AutomationControl::start_touch (double when)
AutomationControl::actually_set_value (get_value (), Controllable::NoGroup);
alist()->start_touch (when);
if (!_desc.toggled) {
AutomationWatch::instance().add_automation_watch (shared_from_this());
AutomationWatch::instance().add_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
}
set_touching (true);
}
@ -315,7 +315,7 @@ AutomationControl::stop_touch (double when)
if (alist()->automation_state() & (Touch | Latch)) {
alist()->stop_touch (when);
if (!_desc.toggled) {
AutomationWatch::instance().remove_automation_watch (shared_from_this());
AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()));
}
}
}
@ -378,7 +378,7 @@ AutomationControl::check_rt (double val, Controllable::GroupControlDisposition g
{
if (!_session.loading() && (flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) {
/* queue change in RT context */
_session.set_control (shared_from_this(), val, gcd);
_session.set_control (boost::dynamic_pointer_cast<AutomationControl>(shared_from_this()), val, gcd);
return true;
}

View file

@ -45,173 +45,180 @@ namespace ARDOUR {
}
MonitorProcessor::MonitorProcessor (Session& s)
: Processor (s, X_("MonitorOut"))
, solo_cnt (0)
, _monitor_active (false)
: Processor (s, X_("MonitorOut"))
, solo_cnt (0)
, _monitor_active (false)
, _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
, _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
, _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
, _dim_level_ptr (new MPControl<volatile gain_t>
/* default is -12dB, range is -20dB to 0dB */
(dB_to_coefficient(-12.0), _("monitor dim level"), Controllable::Flag (0),
dB_to_coefficient(-20.0), dB_to_coefficient (0.0)))
, _solo_boost_level_ptr (new MPControl<volatile gain_t>
/* default is 0dB, range is 0dB to +20dB */
(dB_to_coefficient(0.0), _("monitor solo boost level"), Controllable::Flag (0),
dB_to_coefficient(0.0), dB_to_coefficient(10.0)))
, _dim_all_control (_dim_all_ptr)
, _cut_all_control (_cut_all_ptr)
, _mono_control (_mono_ptr)
, _dim_level_control (_dim_level_ptr)
, _solo_boost_level_control (_solo_boost_level_ptr)
, _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
, _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
, _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
, _dim_level_ptr (new MPControl<volatile gain_t>
/* default is -12dB, range is -20dB to 0dB */
(dB_to_coefficient(-12.0), _("monitor dim level"), Controllable::Flag (0),
dB_to_coefficient(-20.0), dB_to_coefficient (0.0)))
, _solo_boost_level_ptr (new MPControl<volatile gain_t>
/* default is 0dB, range is 0dB to +20dB */
(dB_to_coefficient(0.0), _("monitor solo boost level"), Controllable::Flag (0),
dB_to_coefficient(0.0), dB_to_coefficient(10.0)))
, _dim_all_control (_dim_all_ptr)
, _cut_all_control (_cut_all_ptr)
, _mono_control (_mono_ptr)
, _dim_level_control (_dim_level_ptr)
, _solo_boost_level_control (_solo_boost_level_ptr)
, _dim_all (*_dim_all_ptr)
, _cut_all (*_cut_all_ptr)
, _mono (*_mono_ptr)
, _dim_level (*_dim_level_ptr)
, _solo_boost_level (*_solo_boost_level_ptr)
, _dim_all (*_dim_all_ptr)
, _cut_all (*_cut_all_ptr)
, _mono (*_mono_ptr)
, _dim_level (*_dim_level_ptr)
, _solo_boost_level (*_solo_boost_level_ptr)
{
}
MonitorProcessor::~MonitorProcessor ()
{
allocate_channels (0);
allocate_channels (0);
/* special case for MPControl */
_dim_all_control->DropReferences (); /* EMIT SIGNAL */
_cut_all_control->DropReferences (); /* EMIT SIGNAL */
_mono_control->DropReferences (); /* EMIT SIGNAL */
_dim_level_control->DropReferences (); /* EMIT SIGNAL */
_solo_boost_level_control->DropReferences (); /* EMIT SIGNAL */
}
void
MonitorProcessor::allocate_channels (uint32_t size)
{
while (_channels.size() > size) {
if (_channels.back()->soloed) {
if (solo_cnt > 0) {
--solo_cnt;
}
}
ChannelRecord* cr = _channels.back();
_channels.pop_back();
delete cr;
}
while (_channels.size() > size) {
if (_channels.back()->soloed) {
if (solo_cnt > 0) {
--solo_cnt;
}
}
ChannelRecord* cr = _channels.back();
_channels.pop_back();
delete cr;
}
uint32_t n = _channels.size() + 1;
uint32_t n = _channels.size() + 1;
while (_channels.size() < size) {
_channels.push_back (new ChannelRecord (n));
}
while (_channels.size() < size) {
_channels.push_back (new ChannelRecord (n));
}
}
int
MonitorProcessor::set_state (const XMLNode& node, int version)
{
int ret = Processor::set_state (node, version);
int ret = Processor::set_state (node, version);
if (ret != 0) {
return ret;
}
if (ret != 0) {
return ret;
}
std::string type_name;
if (!node.get_property (X_("type"), type_name)) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings have no type information"))
<< endmsg;
return -1;
}
std::string type_name;
if (!node.get_property (X_("type"), type_name)) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings have no type information"))
<< endmsg;
return -1;
}
if (type_name != X_("monitor")) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor given unknown XML settings"))
<< endmsg;
return -1;
}
if (type_name != X_("monitor")) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor given unknown XML settings"))
<< endmsg;
return -1;
}
uint32_t channels = 0;
if (!node.get_property (X_("channels"), channels)) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing a channel cnt"))
<< endmsg;
return -1;
}
uint32_t channels = 0;
if (!node.get_property (X_("channels"), channels)) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing a channel cnt"))
<< endmsg;
return -1;
}
allocate_channels (channels);
allocate_channels (channels);
// need to check that these conversions are working as expected
gain_t val;
if (node.get_property (X_("dim-level"), val)) {
_dim_level = val;
}
// need to check that these conversions are working as expected
gain_t val;
if (node.get_property (X_("dim-level"), val)) {
_dim_level = val;
}
if (node.get_property (X_("solo-boost-level"), val)) {
_solo_boost_level = val;
}
if (node.get_property (X_("solo-boost-level"), val)) {
_solo_boost_level = val;
}
bool bool_val;
if (node.get_property (X_("cut-all"), bool_val)) {
_cut_all = bool_val;
}
bool bool_val;
if (node.get_property (X_("cut-all"), bool_val)) {
_cut_all = bool_val;
}
if (node.get_property (X_("dim-all"), bool_val)) {
_dim_all = bool_val;
}
if (node.get_property (X_("dim-all"), bool_val)) {
_dim_all = bool_val;
}
if (node.get_property (X_("mono"), bool_val)) {
_mono = bool_val;
}
if (node.get_property (X_("mono"), bool_val)) {
_mono = bool_val;
}
for (XMLNodeList::const_iterator i = node.children().begin(); i != node.children().end(); ++i) {
for (XMLNodeList::const_iterator i = node.children().begin(); i != node.children().end(); ++i) {
if ((*i)->name() == X_("Channel")) {
if ((*i)->name() == X_("Channel")) {
uint32_t chn;
if (!(*i)->get_property (X_("id"), chn)) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing an ID"))
<< endmsg;
return -1;
}
uint32_t chn;
if (!(*i)->get_property (X_("id"), chn)) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings are missing an ID"))
<< endmsg;
return -1;
}
if (chn >= _channels.size()) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an illegal channel count"))
<< endmsg;
return -1;
}
ChannelRecord& cr (*_channels[chn]);
if (chn >= _channels.size()) {
error << string_compose (X_("programming error: %1"), X_("MonitorProcessor XML settings has an illegal channel count"))
<< endmsg;
return -1;
}
ChannelRecord& cr (*_channels[chn]);
bool gain_coeff_zero;
if ((*i)->get_property ("cut", gain_coeff_zero)) {
if (gain_coeff_zero) {
cr.cut = GAIN_COEFF_ZERO;
} else {
cr.cut = GAIN_COEFF_UNITY;
}
}
bool gain_coeff_zero;
if ((*i)->get_property ("cut", gain_coeff_zero)) {
if (gain_coeff_zero) {
cr.cut = GAIN_COEFF_ZERO;
} else {
cr.cut = GAIN_COEFF_UNITY;
}
}
bool dim;
if ((*i)->get_property ("dim", dim)) {
cr.dim = dim;
}
bool dim;
if ((*i)->get_property ("dim", dim)) {
cr.dim = dim;
}
bool invert_polarity;
if ((*i)->get_property ("invert", invert_polarity)) {
if (invert_polarity) {
cr.polarity = -1.0f;
} else {
cr.polarity = 1.0f;
}
}
bool invert_polarity;
if ((*i)->get_property ("invert", invert_polarity)) {
if (invert_polarity) {
cr.polarity = -1.0f;
} else {
cr.polarity = 1.0f;
}
}
bool soloed;
if ((*i)->get_property ("solo", soloed)) {
cr.soloed = soloed;
}
}
}
bool soloed;
if ((*i)->get_property ("solo", soloed)) {
cr.soloed = soloed;
}
}
}
/* reset solo cnt */
/* reset solo cnt */
solo_cnt = 0;
solo_cnt = 0;
for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
if ((*x)->soloed) {
solo_cnt++;
}
}
for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
if ((*x)->soloed) {
solo_cnt++;
}
}
update_monitor_state ();
return 0;
@ -239,7 +246,7 @@ MonitorProcessor::state ()
uint32_t chn = 0;
for (vector<ChannelRecord*>::const_iterator x = _channels.begin (); x != _channels.end ();
++x, ++chn) {
++x, ++chn) {
chn_node = new XMLNode (X_("Channel"));
chn_node->set_property ("id", chn);
@ -259,87 +266,87 @@ MonitorProcessor::state ()
void
MonitorProcessor::run (BufferSet& bufs, samplepos_t /*start_sample*/, samplepos_t /*end_sample*/, double /*speed*/, pframes_t nframes, bool /*result_required*/)
{
uint32_t chn = 0;
gain_t target_gain;
gain_t dim_level_this_time = _dim_level;
gain_t global_cut = (_cut_all ? GAIN_COEFF_ZERO : GAIN_COEFF_UNITY);
gain_t global_dim = (_dim_all ? dim_level_this_time : GAIN_COEFF_UNITY);
gain_t solo_boost;
uint32_t chn = 0;
gain_t target_gain;
gain_t dim_level_this_time = _dim_level;
gain_t global_cut = (_cut_all ? GAIN_COEFF_ZERO : GAIN_COEFF_UNITY);
gain_t global_dim = (_dim_all ? dim_level_this_time : GAIN_COEFF_UNITY);
gain_t solo_boost;
if (_session.listening() || _session.soloing()) {
solo_boost = _solo_boost_level;
} else {
solo_boost = GAIN_COEFF_UNITY;
}
if (_session.listening() || _session.soloing()) {
solo_boost = _solo_boost_level;
} else {
solo_boost = GAIN_COEFF_UNITY;
}
for (BufferSet::audio_iterator b = bufs.audio_begin(); b != bufs.audio_end(); ++b) {
for (BufferSet::audio_iterator b = bufs.audio_begin(); b != bufs.audio_end(); ++b) {
/* don't double-scale by both track dim and global dim coefficients */
/* don't double-scale by both track dim and global dim coefficients */
gain_t dim_level = (global_dim == GAIN_COEFF_UNITY ? (_channels[chn]->dim ? dim_level_this_time : GAIN_COEFF_UNITY) : GAIN_COEFF_UNITY);
gain_t dim_level = (global_dim == GAIN_COEFF_UNITY ? (_channels[chn]->dim ? dim_level_this_time : GAIN_COEFF_UNITY) : GAIN_COEFF_UNITY);
if (_channels[chn]->soloed) {
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
} else {
if (solo_cnt == 0) {
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
} else {
target_gain = GAIN_COEFF_ZERO;
}
}
if (_channels[chn]->soloed) {
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
} else {
if (solo_cnt == 0) {
target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
} else {
target_gain = GAIN_COEFF_ZERO;
}
}
if (target_gain != _channels[chn]->current_gain || target_gain != GAIN_COEFF_UNITY) {
if (target_gain != _channels[chn]->current_gain || target_gain != GAIN_COEFF_UNITY) {
_channels[chn]->current_gain = Amp::apply_gain (*b, _session.nominal_sample_rate(), nframes, _channels[chn]->current_gain, target_gain);
}
_channels[chn]->current_gain = Amp::apply_gain (*b, _session.nominal_sample_rate(), nframes, _channels[chn]->current_gain, target_gain);
}
++chn;
}
++chn;
}
if (_mono) {
DEBUG_TRACE (DEBUG::Monitor, "mono-izing\n");
if (_mono) {
DEBUG_TRACE (DEBUG::Monitor, "mono-izing\n");
/* chn is now the number of channels, use as a scaling factor when mixing
*/
gain_t scale = 1.f / (float)chn;
BufferSet::audio_iterator b = bufs.audio_begin();
AudioBuffer& ab (*b);
Sample* buf = ab.data();
/* chn is now the number of channels, use as a scaling factor when mixing
*/
gain_t scale = 1.f / (float)chn;
BufferSet::audio_iterator b = bufs.audio_begin();
AudioBuffer& ab (*b);
Sample* buf = ab.data();
/* scale the first channel */
/* scale the first channel */
for (pframes_t n = 0; n < nframes; ++n) {
buf[n] *= scale;
}
for (pframes_t n = 0; n < nframes; ++n) {
buf[n] *= scale;
}
/* add every other channel into the first channel's buffer */
/* add every other channel into the first channel's buffer */
++b;
for (; b != bufs.audio_end(); ++b) {
AudioBuffer& ob (*b);
Sample* obuf = ob.data ();
for (pframes_t n = 0; n < nframes; ++n) {
buf[n] += obuf[n] * scale;
}
}
++b;
for (; b != bufs.audio_end(); ++b) {
AudioBuffer& ob (*b);
Sample* obuf = ob.data ();
for (pframes_t n = 0; n < nframes; ++n) {
buf[n] += obuf[n] * scale;
}
}
/* copy the first channel to every other channel's buffer */
/* copy the first channel to every other channel's buffer */
b = bufs.audio_begin();
++b;
for (; b != bufs.audio_end(); ++b) {
AudioBuffer& ob (*b);
Sample* obuf = ob.data ();
memcpy (obuf, buf, sizeof (Sample) * nframes);
}
}
b = bufs.audio_begin();
++b;
for (; b != bufs.audio_end(); ++b) {
AudioBuffer& ob (*b);
Sample* obuf = ob.data ();
memcpy (obuf, buf, sizeof (Sample) * nframes);
}
}
}
bool
MonitorProcessor::configure_io (ChanCount in, ChanCount out)
{
allocate_channels (in.n_audio());
return Processor::configure_io (in, out);
allocate_channels (in.n_audio());
return Processor::configure_io (in, out);
}
bool
@ -419,51 +426,49 @@ MonitorProcessor::set_dim_all (bool yn)
bool
MonitorProcessor::display_to_user () const
{
return false;
return false;
}
bool
MonitorProcessor::soloed (uint32_t chn) const
{
return _channels[chn]->soloed;
return _channels[chn]->soloed;
}
bool
MonitorProcessor::inverted (uint32_t chn) const
{
return _channels[chn]->polarity < 0.0f;
return _channels[chn]->polarity < 0.0f;
}
bool
MonitorProcessor::cut (uint32_t chn) const
{
return _channels[chn]->cut == GAIN_COEFF_ZERO;
return _channels[chn]->cut == GAIN_COEFF_ZERO;
}
bool
MonitorProcessor::dimmed (uint32_t chn) const
{
return _channels[chn]->dim;
return _channels[chn]->dim;
}
bool
MonitorProcessor::mono () const
{
return _mono;
return _mono;
}
bool
MonitorProcessor::dim_all () const
{
return _dim_all;
return _dim_all;
}
bool
MonitorProcessor::cut_all () const
{
return _cut_all;
return _cut_all;
}
void
@ -492,37 +497,37 @@ MonitorProcessor::update_monitor_state ()
boost::shared_ptr<Controllable>
MonitorProcessor::channel_cut_control (uint32_t chn) const
{
if (chn < _channels.size()) {
return _channels[chn]->cut_control;
}
return boost::shared_ptr<Controllable>();
if (chn < _channels.size()) {
return _channels[chn]->cut_control;
}
return boost::shared_ptr<Controllable>();
}
boost::shared_ptr<Controllable>
MonitorProcessor::channel_dim_control (uint32_t chn) const
{
if (chn < _channels.size()) {
return _channels[chn]->dim_control;
}
return boost::shared_ptr<Controllable>();
if (chn < _channels.size()) {
return _channels[chn]->dim_control;
}
return boost::shared_ptr<Controllable>();
}
boost::shared_ptr<Controllable>
MonitorProcessor::channel_polarity_control (uint32_t chn) const
{
if (chn < _channels.size()) {
return _channels[chn]->polarity_control;
}
return boost::shared_ptr<Controllable>();
if (chn < _channels.size()) {
return _channels[chn]->polarity_control;
}
return boost::shared_ptr<Controllable>();
}
boost::shared_ptr<Controllable>
MonitorProcessor::channel_solo_control (uint32_t chn) const
{
if (chn < _channels.size()) {
return _channels[chn]->soloed_control;
}
return boost::shared_ptr<Controllable>();
if (chn < _channels.size()) {
return _channels[chn]->soloed_control;
}
return boost::shared_ptr<Controllable>();
}
MonitorProcessor::ChannelRecord::ChannelRecord (uint32_t chn)
@ -542,5 +547,13 @@ MonitorProcessor::ChannelRecord::ChannelRecord (uint32_t chn)
, polarity (*polarity_ptr)
, soloed (*soloed_ptr)
{
}
MonitorProcessor::ChannelRecord::~ChannelRecord ()
{
/* special case for MPControl */
cut_control->DropReferences(); /* EMIT SIGNAL */
dim_control->DropReferences(); /* EMIT SIGNAL */
polarity_control->DropReferences(); /* EMIT SIGNAL */
soloed_control->DropReferences(); /* EMIT SIGNAL */
}

View file

@ -57,5 +57,5 @@ PanControllable::actually_set_value (double v, Controllable::GroupControlDisposi
std::string
PanControllable::get_user_string () const
{
return owner->value_as_string (shared_from_this());
return owner->value_as_string (boost::dynamic_pointer_cast<const AutomationControl>(shared_from_this()));
}

View file

@ -4224,6 +4224,9 @@ Route::shift (samplepos_t pos, samplecnt_t samples)
boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
if (ac) {
boost::shared_ptr<AutomationList> al = ac->alist();
if (al->empty ()) {
continue;
}
XMLNode &before = al->get_state ();
al->shift (pos, samples);
XMLNode &after = al->get_state ();

View file

@ -680,6 +680,14 @@ Session::destroy ()
Port::PortDrop (); /* EMIT SIGNAL */
{
Glib::Threads::Mutex::Lock lm (controllables_lock);
for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
(*i)->DropReferences (); /* EMIT SIGNAL */
}
controllables.clear ();
}
/* clear history so that no references to objects are held any more */
_history.clear ();
@ -864,6 +872,10 @@ Session::destroy ()
DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
#ifndef NDEBUG
Controllable::dump_registry ();
#endif
BOOST_SHOW_POINTERS ();
}

View file

@ -208,7 +208,6 @@ Session::pre_engine_init (string fullpath)
SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
PlaylistFactory::PlaylistCreated.connect_same_thread (*this, boost::bind (&Session::add_playlist, this, _1, _2));
AutomationList::AutomationListCreated.connect_same_thread (*this, boost::bind (&Session::add_automation_list, this, _1));
Controllable::Destroyed.connect_same_thread (*this, boost::bind (&Session::remove_controllable, this, _1));
IO::PortCountChanged.connect_same_thread (*this, boost::bind (&Session::ensure_buffers, this, _1));
/* stop IO objects from doing stuff until we're ready for them */
@ -3739,24 +3738,6 @@ Session::add_controllable (boost::shared_ptr<Controllable> c)
controllables.insert (c);
}
struct null_deleter { void operator()(void const *) const {} };
void
Session::remove_controllable (Controllable* c)
{
if (deletion_in_progress()) {
return;
}
Glib::Threads::Mutex::Lock lm (controllables_lock);
Controllables::iterator x = controllables.find (boost::shared_ptr<Controllable>(c, null_deleter()));
if (x != controllables.end()) {
controllables.erase (x);
}
}
boost::shared_ptr<Controllable>
Session::controllable_by_id (const PBD::ID& id)
{

View file

@ -29,16 +29,14 @@
using namespace PBD;
using namespace std;
PBD::Signal1<void,Controllable*> Controllable::Destroyed;
PBD::Signal1<bool,Controllable*> Controllable::StartLearning;
PBD::Signal1<void,Controllable*> Controllable::StopLearning;
PBD::Signal3<void,Controllable*,int,int> Controllable::CreateBinding;
PBD::Signal1<void,Controllable*> Controllable::DeleteBinding;
PBD::Signal1<bool, boost::weak_ptr<PBD::Controllable> > Controllable::StartLearning;
PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > Controllable::StopLearning;
PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > Controllable::GUIFocusChanged;
Glib::Threads::RWLock Controllable::registry_lock;
Controllable::Controllables Controllable::registry;
PBD::ScopedConnectionList* registry_connections = 0;
PBD::ScopedConnectionList Controllable::registry_connections;
const std::string Controllable::xml_node_name = X_("Controllable");
Controllable::Controllable (const string& name, Flag f)
@ -49,62 +47,6 @@ Controllable::Controllable (const string& name, Flag f)
add (*this);
}
void
Controllable::add (Controllable& ctl)
{
using namespace boost;
Glib::Threads::RWLock::WriterLock lm (registry_lock);
registry.insert (&ctl);
if (!registry_connections) {
registry_connections = new ScopedConnectionList;
}
/* Controllable::remove() is static - no need to manage this connection */
ctl.DropReferences.connect_same_thread (*registry_connections, boost::bind (&Controllable::remove, &ctl));
}
void
Controllable::remove (Controllable* ctl)
{
Glib::Threads::RWLock::WriterLock lm (registry_lock);
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
if ((*i) == ctl) {
registry.erase (i);
break;
}
}
}
Controllable*
Controllable::by_id (const ID& id)
{
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
if ((*i)->id() == id) {
return (*i);
}
}
return 0;
}
Controllable*
Controllable::by_name (const string& str)
{
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
if ((*i)->_name == str) {
return (*i);
}
}
return 0;
}
XMLNode&
Controllable::get_state ()
{
@ -154,3 +96,47 @@ Controllable::set_flags (Flag f)
{
_flags = f;
}
void
Controllable::add (Controllable& ctl)
{
Glib::Threads::RWLock::WriterLock lm (registry_lock);
registry.insert (&ctl);
ctl.DropReferences.connect_same_thread (registry_connections, boost::bind (&Controllable::remove, &ctl));
ctl.Destroyed.connect_same_thread (registry_connections, boost::bind (&Controllable::remove, &ctl));
}
void
Controllable::remove (Controllable* ctl)
{
Glib::Threads::RWLock::WriterLock lm (registry_lock);
Controllables::iterator i = std::find (registry.begin(), registry.end(), ctl);
if (i != registry.end()) {
registry.erase (i);
}
}
boost::shared_ptr<Controllable>
Controllable::by_id (const ID& id)
{
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i) {
if ((*i)->id() == id) {
return (*i)->shared_from_this ();
}
}
return boost::shared_ptr<Controllable>();
}
void
Controllable::dump_registry ()
{
Glib::Threads::RWLock::ReaderLock lm (registry_lock);
unsigned int cnt = 0;
cout << "-- List Of Registered Controllables\n";
for (Controllables::iterator i = registry.begin(); i != registry.end(); ++i, ++cnt) {
cout << "CTRL: " << (*i)->name() << "\n";
}
cout << "Total number of registered sontrollables: " << cnt << "\n";
}

View file

@ -22,12 +22,13 @@
#include <string>
#include <set>
#include <map>
#include "pbd/libpbd_visibility.h"
#include "pbd/signals.h"
#include <glibmm/threads.h>
#include <boost/enable_shared_from_this.hpp>
#include "pbd/statefuldestructible.h"
using std::min;
@ -49,7 +50,8 @@ namespace PBD {
* as a control whose value can range between 0 and 1.0.
*
*/
class LIBPBD_API Controllable : public PBD::StatefulDestructible {
class LIBPBD_API Controllable : public PBD::StatefulDestructible, public boost::enable_shared_from_this<Controllable>
{
public:
enum Flag {
Toggle = 0x1,
@ -59,7 +61,6 @@ public:
};
Controllable (const std::string& name, Flag f = Flag (0));
virtual ~Controllable() { Destroyed (this); }
/* We express Controllable values in one of three ways:
* 1. `user' --- as presented to the user (e.g. dB, Hz, etc.)
@ -122,13 +123,9 @@ public:
virtual std::string get_user_string() const { return std::string(); }
PBD::Signal0<void> LearningFinished;
static PBD::Signal3<void,PBD::Controllable*,int,int> CreateBinding;
static PBD::Signal1<void,PBD::Controllable*> DeleteBinding;
static PBD::Signal1<bool,PBD::Controllable*> StartLearning;
static PBD::Signal1<void,PBD::Controllable*> StopLearning;
static PBD::Signal1<void,Controllable*> Destroyed;
static PBD::Signal1<bool, boost::weak_ptr<PBD::Controllable> > StartLearning;
static PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > StopLearning;
static PBD::Signal1<void, boost::weak_ptr<PBD::Controllable> > GUIFocusChanged;
@ -137,7 +134,7 @@ public:
int set_state (const XMLNode&, int version);
virtual XMLNode& get_state ();
std::string name() const { return _name; }
std::string name() const { return _name; }
bool touching () const { return _touching; }
PBD::Signal0<void> TouchChanged;
@ -152,8 +149,9 @@ public:
Flag flags() const { return _flags; }
void set_flags (Flag f);
static Controllable* by_id (const PBD::ID&);
static Controllable* by_name (const std::string&);
static boost::shared_ptr<Controllable> by_id (const PBD::ID&);
static void dump_registry ();
static const std::string xml_node_name;
protected:
@ -164,18 +162,19 @@ protected:
}
private:
std::string _name;
std::string _units;
Flag _flags;
bool _touching;
static void add (Controllable&);
static void remove (Controllable*);
typedef std::set<PBD::Controllable*> Controllables;
static ScopedConnectionList registry_connections;
static Glib::Threads::RWLock registry_lock;
static Controllables registry;
static void add (Controllable&);
static void remove (Controllable*);
};
/* a utility class for the occasions when you need but do not have

View file

@ -21,6 +21,7 @@
#define playback_buffer_h
#include <cstring>
#include <stdint.h>
#include <glibmm.h>
#include "pbd/libpbd_visibility.h"

View file

@ -107,9 +107,7 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
*/
Controllable::StartLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::start_learning, this, _1));
Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
Controllable::CreateBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::create_binding, this, _1, _2, _3));
Controllable::DeleteBinding.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::delete_binding, this, _1));
Controllable::StopLearning.connect_same_thread (*this, boost::bind (&GenericMidiControlProtocol::stop_learning, this, _1));
/* this signal is emitted by the process() callback, and if
* send_feedback() is going to do anything, it should do it in the
@ -345,9 +343,10 @@ GenericMidiControlProtocol::_send_feedback ()
}
bool
GenericMidiControlProtocol::start_learning (Controllable* c)
GenericMidiControlProtocol::start_learning (boost::weak_ptr <Controllable> wc)
{
if (c == 0) {
boost::shared_ptr<Controllable> c = wc.lock ();
if (!c) {
return false;
}
@ -401,7 +400,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
}
if (!mc) {
mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
mc = new MIDIControllable (this, *_input_port->parser(), c, false);
own_mc = true;
}
@ -443,8 +442,13 @@ GenericMidiControlProtocol::learning_stopped (MIDIControllable* mc)
}
void
GenericMidiControlProtocol::stop_learning (Controllable* c)
GenericMidiControlProtocol::stop_learning (boost::weak_ptr<PBD::Controllable> wc)
{
boost::shared_ptr<Controllable> c = wc.lock ();
if (!c) {
return;
}
Glib::Threads::Mutex::Lock lm (pending_lock);
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
MIDIControllable* dptr = 0;
@ -468,64 +472,6 @@ GenericMidiControlProtocol::stop_learning (Controllable* c)
delete dptr;
}
void
GenericMidiControlProtocol::delete_binding (PBD::Controllable* control)
{
if (control != 0) {
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
MIDIControllable* existingBinding = (*iter);
if (control == (existingBinding->get_controllable())) {
delete existingBinding;
iter = controllables.erase (iter);
} else {
++iter;
}
}
}
}
// This next function seems unused
void
GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
{
if (control != NULL) {
Glib::Threads::Mutex::Lock lm2 (controllables_lock);
MIDI::channel_t channel = (pos & 0xf);
MIDI::byte value = control_number;
// Create a MIDIControllable
MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *control, false);
// Remove any old binding for this midi channel/type/value pair
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
MIDIControllable* existingBinding = (*iter);
if ((existingBinding->get_control_channel() & 0xf ) == channel &&
existingBinding->get_control_additional() == value &&
(existingBinding->get_control_type() & 0xf0 ) == MIDI::controller) {
delete existingBinding;
iter = controllables.erase (iter);
} else {
++iter;
}
}
// Update the MIDI Controllable based on the the pos param
// Here is where a table lookup for user mappings could go; for now we'll just wing it...
mc->bind_midi(channel, MIDI::controller, value);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Create binding: Channel: %1 Controller: %2 Value: %3 \n", channel, MIDI::controller, value));
controllables.push_back (mc);
}
}
void
GenericMidiControlProtocol::check_used_event (int pos, int control_number)
{
@ -537,7 +483,6 @@ GenericMidiControlProtocol::check_used_event (int pos, int control_number)
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("checking for used event: Channel: %1 Controller: %2 value: %3\n", (int) channel, (pos & 0xf0), (int) value));
// Remove any old binding for this midi channel/type/value pair
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
for (MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end();) {
MIDIControllable* existingBinding = (*iter);
if ( (existingBinding->get_control_type() & 0xf0 ) == (pos & 0xf0) && (existingBinding->get_control_channel() & 0xf ) == channel ) {
@ -686,10 +631,10 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
if ((*niter)->get_property ("id", id)) {
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Relearned binding for session: Control ID: %1\n", id.to_s()));
Controllable* c = Controllable::by_id (id);
boost::shared_ptr<PBD::Controllable> c = Controllable::by_id (id);
if (c) {
MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), *c, false);
MIDIControllable* mc = new MIDIControllable (this, *_input_port->parser(), c, false);
if (mc->set_state (**niter, version) == 0) {
controllables.push_back (mc);
@ -1287,11 +1232,11 @@ GenericMidiControlProtocol::create_function (const XMLNode& node)
ev = MIDI::program;
} else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
if (prop->name() == X_("sysex")) {
ev = MIDI::sysex;
} else {
ev = MIDI::any;
}
if (prop->name() == X_("sysex")) {
ev = MIDI::sysex;
} else {
ev = MIDI::any;
}
int val;
uint32_t cnt;
@ -1387,11 +1332,11 @@ GenericMidiControlProtocol::create_action (const XMLNode& node)
ev = MIDI::program;
} else if ((prop = node.property (X_("sysex"))) != 0 || (prop = node.property (X_("msg"))) != 0) {
if (prop->name() == X_("sysex")) {
ev = MIDI::sysex;
} else {
ev = MIDI::any;
}
if (prop->name() == X_("sysex")) {
ev = MIDI::sysex;
} else {
ev = MIDI::any;
}
int val;
uint32_t cnt;
@ -1562,9 +1507,9 @@ GenericMidiControlProtocol::input_port() const
}
void
GenericMidiControlProtocol::maybe_start_touch (Controllable* controllable)
GenericMidiControlProtocol::maybe_start_touch (boost::shared_ptr<Controllable> controllable)
{
AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (controllable);
if (actl) {
actl->start_touch (session->audible_sample ());
}

View file

@ -39,7 +39,7 @@ namespace ARDOUR {
}
namespace MIDI {
class Port;
class Port;
}
class MIDIControllable;
@ -47,7 +47,7 @@ class MIDIFunction;
class MIDIAction;
class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
public:
public:
GenericMidiControlProtocol (ARDOUR::Session&);
virtual ~GenericMidiControlProtocol();
@ -68,7 +68,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
boost::shared_ptr<PBD::Controllable> lookup_controllable (std::string const &) const;
void maybe_start_touch (PBD::Controllable*);
void maybe_start_touch (boost::shared_ptr<PBD::Controllable>);
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
@ -110,7 +110,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
PBD::Signal0<void> ConnectionChange;
private:
private:
boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
boost::shared_ptr<ARDOUR::AsyncMIDIPort> _input_port;
@ -144,17 +144,14 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
};
typedef std::list<MIDIPendingControllable* > MIDIPendingControllables;
MIDIPendingControllables pending_controllables;
Glib::Threads::Mutex controllables_lock;
Glib::Threads::Mutex pending_lock;
Glib::Threads::Mutex controllables_lock;
Glib::Threads::Mutex pending_lock;
bool start_learning (PBD::Controllable*);
void stop_learning (PBD::Controllable*);
bool start_learning (boost::weak_ptr<PBD::Controllable>);
void stop_learning (boost::weak_ptr<PBD::Controllable>);
void learning_stopped (MIDIControllable*);
void create_binding (PBD::Controllable*, int, int);
void delete_binding (PBD::Controllable*);
MIDIControllable* create_binding (const XMLNode&);
MIDIFunction* create_function (const XMLNode&);
MIDIAction* create_action (const XMLNode&);

View file

@ -47,7 +47,6 @@ using namespace ARDOUR;
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, bool m)
: _surface (s)
, controllable (0)
, _parser (p)
, _momentary (m)
{
@ -65,12 +64,12 @@ MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser&
control_additional = (MIDI::byte) -1;
}
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, Controllable& c, bool m)
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser& p, boost::shared_ptr<PBD::Controllable> c, bool m)
: _surface (s)
, _parser (p)
, _momentary (m)
{
set_controllable (&c);
set_controllable (c);
_learned = true; /* from controllable */
_ctltype = Ctl_Momentary;
@ -119,28 +118,35 @@ MIDIControllable::drop_external_control ()
control_additional = (MIDI::byte) -1;
}
void
MIDIControllable::set_controllable (Controllable* c)
boost::shared_ptr<PBD::Controllable>
MIDIControllable::get_controllable () const
{
if (c == controllable) {
return _controllable;
}
void
MIDIControllable::set_controllable (boost::shared_ptr<PBD::Controllable> c)
{
Glib::Threads::Mutex::Lock lm (controllable_lock);
if (c && c == _controllable) {
return;
}
controllable_death_connection.disconnect ();
controllable_death_connections.drop_connections ();
controllable = c;
if (controllable) {
last_controllable_value = controllable->get_value();
if (c) {
_controllable = c;
last_controllable_value = c->get_value();
} else {
_controllable.reset();
last_controllable_value = 0.0f; // is there a better value?
}
last_incoming = 256;
if (controllable) {
controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR,
boost::bind (&MIDIControllable::drop_controllable, this, _1),
if (c) {
c->DropReferences.connect (controllable_death_connections, MISSING_INVALIDATOR,
boost::bind (&MIDIControllable::drop_controllable, this),
MidiControlUI::instance());
}
}
@ -171,22 +177,26 @@ MIDIControllable::stop_learning ()
int
MIDIControllable::control_to_midi (float val)
{
if (controllable->is_gain_like()) {
return controllable->internal_to_interface (val) * max_value_for_type ();
if (!_controllable) {
return 0;
}
float control_min = controllable->lower ();
float control_max = controllable->upper ();
if (_controllable->is_gain_like()) {
return _controllable->internal_to_interface (val) * max_value_for_type ();
}
float control_min = _controllable->lower ();
float control_max = _controllable->upper ();
float control_range = control_max - control_min;
if (controllable->is_toggle()) {
if (_controllable->is_toggle()) {
if (val >= (control_min + (control_range/2.0f))) {
return max_value_for_type();
} else {
return 0;
}
} else {
AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (_controllable);
if (actl) {
control_min = actl->internal_to_interface(control_min);
control_max = actl->internal_to_interface(control_max);
@ -202,6 +212,9 @@ MIDIControllable::control_to_midi (float val)
float
MIDIControllable::midi_to_control (int val)
{
if (!_controllable) {
return 0;
}
/* fiddle with MIDI value so that we get an odd number of integer steps
and can thus represent "middle" precisely as 0.5. this maps to
the range 0..+1.0 (0 to 126)
@ -209,17 +222,17 @@ MIDIControllable::midi_to_control (int val)
float fv = (val == 0 ? 0 : float (val - 1) / (max_value_for_type() - 1));
if (controllable->is_gain_like()) {
return controllable->interface_to_internal (fv);
if (_controllable->is_gain_like()) {
return _controllable->interface_to_internal (fv);
}
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Raw value %1 float %2\n", val, fv));
float control_min = controllable->lower ();
float control_max = controllable->upper ();
float control_min = _controllable->lower ();
float control_max = _controllable->upper ();
float control_range = control_max - control_min;
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Min %1 Max %2 Range %3\n", control_min, control_max, control_range));
AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
boost::shared_ptr<AutomationControl> actl = boost::dynamic_pointer_cast<AutomationControl> (_controllable);
if (actl) {
if (fv == 0.f) return control_min;
if (fv == 1.f) return control_max;
@ -256,62 +269,62 @@ MIDIControllable::lookup_controllable()
return -1;
}
set_controllable (c.get ());
set_controllable (c);
return 0;
}
void
MIDIControllable::drop_controllable (Controllable* c)
MIDIControllable::drop_controllable ()
{
if (c == controllable) {
set_controllable (0);
}
set_controllable (boost::shared_ptr<PBD::Controllable>());
}
void
MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /*is_on*/)
{
if (!controllable) {
if (!_controllable) {
if (lookup_controllable()) {
return;
}
}
_surface->maybe_start_touch (controllable);
assert (_controllable);
if (!controllable->is_toggle()) {
_surface->maybe_start_touch (_controllable);
if (!_controllable->is_toggle()) {
if (control_additional == msg->note_number) {
controllable->set_value (midi_to_control (msg->velocity), Controllable::UseGroup);
_controllable->set_value (midi_to_control (msg->velocity), Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Note %1 value %2 %3\n", (int) msg->note_number, (float) midi_to_control (msg->velocity), current_uri() ));
}
} else {
if (control_additional == msg->note_number) {
float new_value = controllable->get_value() > 0.5f ? 0.0f : 1.0f;
controllable->set_value (new_value, Controllable::UseGroup);
float new_value = _controllable->get_value() > 0.5f ? 0.0f : 1.0f;
_controllable->set_value (new_value, Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Note %1 Value %2 %3\n", (int) msg->note_number, (float) new_value, current_uri()));
}
}
last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
last_value = (MIDI::byte) (_controllable->get_value() * 127.0); // to prevent feedback fights
}
void
MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
{
if (!controllable) {
if (!_controllable) {
if (lookup_controllable ()) {
return;
}
}
assert (controllable);
assert (_controllable);
_surface->maybe_start_touch (controllable);
_surface->maybe_start_touch (_controllable);
if (control_additional == msg->controller_number) {
if (!controllable->is_toggle()) {
if (!_controllable->is_toggle()) {
if (get_encoder() == No_enc) {
float new_value = msg->value;
float max_value = max(last_controllable_value, new_value);
@ -321,8 +334,8 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
bool const in_sync = (
range < threshold &&
controllable->get_value() <= midi_to_control(max_value) &&
controllable->get_value() >= midi_to_control(min_value)
_controllable->get_value() <= midi_to_control(max_value) &&
_controllable->get_value() >= midi_to_control(min_value)
);
/* If the surface is not motorised, we try to prevent jumps when
@ -331,7 +344,7 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
*/
if (in_sync || _surface->motorised ()) {
controllable->set_value (midi_to_control (new_value), Controllable::UseGroup);
_controllable->set_value (midi_to_control (new_value), Controllable::UseGroup);
}
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI CC %1 value %2 %3\n", (int) msg->controller_number, (float) midi_to_control(new_value), current_uri() ));
@ -341,30 +354,30 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
switch (get_encoder()) {
case Enc_L:
if (msg->value > 0x40) {
controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
} else {
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
}
break;
case Enc_R:
if (msg->value > 0x40) {
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
} else {
controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value - offset + 1), Controllable::UseGroup);
}
break;
case Enc_2:
if (msg->value > 0x40) {
controllable->set_value (midi_to_control (last_value - (0x7f - msg->value) + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value - (0x7f - msg->value) + 1), Controllable::UseGroup);
} else {
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
}
break;
case Enc_B:
if (msg->value > 0x40) {
controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value + offset + 1), Controllable::UseGroup);
} else {
controllable->set_value (midi_to_control (last_value - (0x40 - offset)), Controllable::UseGroup);
_controllable->set_value (midi_to_control (last_value - (0x40 - offset)), Controllable::UseGroup);
}
break;
default:
@ -382,9 +395,9 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
/* relax ... first incoming message */
} else {
if (msg->value > last_incoming) {
controllable->set_value (1.0, Controllable::UseGroup);
_controllable->set_value (1.0, Controllable::UseGroup);
} else {
controllable->set_value (0.0, Controllable::UseGroup);
_controllable->set_value (0.0, Controllable::UseGroup);
}
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("dial Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri()));
}
@ -395,7 +408,7 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
* time they are pressed.
*/
if (msg->value >= 0x40) {
controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
_controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("toggle Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri()));
}
break;
@ -404,70 +417,74 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
maintain state (i.e. they know they were pressed) and then send zero the next time.
*/
if (msg->value >= 0x40) {
controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
_controllable->set_value (_controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
} else {
controllable->set_value (0.0, Controllable::NoGroup);
_controllable->set_value (0.0, Controllable::NoGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 0 %2\n", (int) msg->controller_number, current_uri()));
break;
}
}
}
last_value = (MIDI::byte) (control_to_midi(controllable->get_value())); // to prevent feedback fights
last_value = (MIDI::byte) (control_to_midi(_controllable->get_value())); // to prevent feedback fights
}
}
void
MIDIControllable::midi_sense_program_change (Parser &, MIDI::byte msg)
{
if (!controllable) {
if (!_controllable) {
if (lookup_controllable ()) {
return;
}
}
_surface->maybe_start_touch (controllable);
assert (_controllable);
_surface->maybe_start_touch (_controllable);
if (msg == control_additional) {
if (!controllable->is_toggle()) {
controllable->set_value (1.0, Controllable::UseGroup);
if (!_controllable->is_toggle()) {
_controllable->set_value (1.0, Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value 1.0 %3\n", (int) msg, current_uri() ));
} else {
float new_value = controllable->get_value() > 0.5f ? 0.0f : 1.0f;
controllable->set_value (new_value, Controllable::UseGroup);
float new_value = _controllable->get_value() > 0.5f ? 0.0f : 1.0f;
_controllable->set_value (new_value, Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI program %1 value %2 %3\n", (int) msg, (float) new_value, current_uri()));
}
}
last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
last_value = (MIDI::byte) (_controllable->get_value() * 127.0); // to prevent feedback fights
}
void
MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
{
if (!controllable) {
if (!_controllable) {
if (lookup_controllable ()) {
return;
}
}
_surface->maybe_start_touch (controllable);
assert (_controllable);
if (!controllable->is_toggle()) {
controllable->set_value (midi_to_control (pb), Controllable::UseGroup);
_surface->maybe_start_touch (_controllable);
if (!_controllable->is_toggle()) {
_controllable->set_value (midi_to_control (pb), Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("MIDI pitchbend %1 value %2 %3\n", (int) control_channel, (float) midi_to_control (pb), current_uri() ));
} else {
if (pb > 8065.0f) {
controllable->set_value (1, Controllable::UseGroup);
_controllable->set_value (1, Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi pitchbend %1 value 1 %2\n", (int) control_channel, current_uri()));
} else {
controllable->set_value (0, Controllable::UseGroup);
_controllable->set_value (0, Controllable::UseGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi pitchbend %1 value 0 %2\n", (int) control_channel, current_uri()));
}
}
last_value = control_to_midi (controllable->get_value ());
last_value = control_to_midi (_controllable->get_value ());
}
void
@ -482,8 +499,8 @@ MIDIControllable::midi_receiver (Parser &, MIDI::byte *msg, size_t /*len*/)
_surface->check_used_event(msg[0], msg[1]);
bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
if (controllable) {
controllable->LearningFinished ();
if (_controllable) {
_controllable->LearningFinished ();
}
}
@ -491,8 +508,8 @@ void
MIDIControllable::rpn_value_change (Parser&, uint16_t rpn, float val)
{
if (control_rpn == rpn) {
if (controllable) {
controllable->set_value (val, Controllable::UseGroup);
if (_controllable) {
_controllable->set_value (val, Controllable::UseGroup);
}
}
}
@ -501,8 +518,8 @@ void
MIDIControllable::nrpn_value_change (Parser&, uint16_t nrpn, float val)
{
if (control_nrpn == nrpn) {
if (controllable) {
controllable->set_value (val, Controllable::UseGroup);
if (_controllable) {
_controllable->set_value (val, Controllable::UseGroup);
}
}
}
@ -511,9 +528,9 @@ void
MIDIControllable::rpn_change (Parser&, uint16_t rpn, int dir)
{
if (control_rpn == rpn) {
if (controllable) {
if (_controllable) {
/* XXX how to increment/decrement ? */
// controllable->set_value (val);
// _controllable->set_value (val);
}
}
}
@ -522,9 +539,9 @@ void
MIDIControllable::nrpn_change (Parser&, uint16_t nrpn, int dir)
{
if (control_nrpn == nrpn) {
if (controllable) {
if (_controllable) {
/* XXX how to increment/decrement ? */
// controllable->set_value (val);
// _controllable->set_value (val);
}
}
}
@ -629,11 +646,15 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
MIDI::byte*
MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*force*/)
{
if (!controllable || !_surface->get_feedback ()) {
Glib::Threads::Mutex::Lock lm (controllable_lock, Glib::Threads::TRY_LOCK);
if (!lm.locked ()) {
return buf;
}
if (!_controllable || !_surface->get_feedback ()) {
return buf;
}
float val = controllable->get_value ();
float val = _controllable->get_value ();
/* Note that when sending RPN/NPRN we do two things:
*
@ -704,7 +725,7 @@ MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*forc
return buf;
}
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Feedback: %1 %2\n", control_description(), current_uri()));
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Feedback: %1 %2\n", control_description(), current_uri()));
*buf++ = (0xF0 & control_type) | (0xF & control_channel);
int ev_size = 3;
@ -768,13 +789,13 @@ MIDIControllable::get_state ()
XMLNode* node = new XMLNode ("MIDIControllable");
if (_current_uri.empty()) {
node->set_property ("id", controllable->id ());
if (_current_uri.empty() && _controllable) {
node->set_property ("id", _controllable->id ());
} else {
node->set_property ("uri", _current_uri);
}
if (controllable) {
if (_controllable) {
snprintf (buf, sizeof(buf), "0x%x", (int) control_type);
node->set_property ("event", (const char *)buf);
node->set_property ("channel", (int16_t)control_channel);

View file

@ -43,9 +43,9 @@ namespace ARDOUR {
class MIDIControllable : public PBD::Stateful
{
public:
MIDIControllable (GenericMidiControlProtocol *, MIDI::Parser&, PBD::Controllable&, bool momentary);
MIDIControllable (GenericMidiControlProtocol *, MIDI::Parser&, bool momentary = false);
public:
MIDIControllable (GenericMidiControlProtocol*, MIDI::Parser&, boost::shared_ptr<PBD::Controllable>, bool momentary);
MIDIControllable (GenericMidiControlProtocol*, MIDI::Parser&, bool momentary = false);
virtual ~MIDIControllable ();
int init (const std::string&);
@ -89,8 +89,8 @@ class MIDIControllable : public PBD::Stateful
void set_encoder (Encoder val) { _encoder = val; }
MIDI::Parser& get_parser() { return _parser; }
PBD::Controllable* get_controllable() const { return controllable; }
void set_controllable (PBD::Controllable*);
void set_controllable (boost::shared_ptr<PBD::Controllable>);
boost::shared_ptr<PBD::Controllable> get_controllable () const;
const std::string& current_uri() const { return _current_uri; }
std::string control_description() const { return _control_description; }
@ -108,16 +108,16 @@ class MIDIControllable : public PBD::Stateful
MIDI::eventType get_control_type () { return control_type; }
MIDI::byte get_control_additional () { return control_additional; }
int lookup_controllable();
int lookup_controllable();
private:
private:
int max_value_for_type () const;
GenericMidiControlProtocol* _surface;
PBD::Controllable* controllable;
boost::shared_ptr<PBD::Controllable> _controllable;
std::string _current_uri;
MIDI::Parser& _parser;
MIDI::Parser& _parser;
bool setting;
int last_value;
int last_incoming;
@ -130,7 +130,7 @@ class MIDIControllable : public PBD::Stateful
int midi_msg_id; /* controller ID or note number */
PBD::ScopedConnection midi_sense_connection[2];
PBD::ScopedConnection midi_learn_connection;
PBD::ScopedConnection controllable_death_connection;
PBD::ScopedConnectionList controllable_death_connections;
/** the type of MIDI message that is used for this control */
MIDI::eventType control_type;
MIDI::byte control_additional;
@ -142,7 +142,8 @@ class MIDIControllable : public PBD::Stateful
std::string _what;
bool _bank_relative;
void drop_controllable (PBD::Controllable*);
void drop_controllable ();
Glib::Threads::Mutex controllable_lock;
void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t);
void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on);
@ -159,4 +160,3 @@ class MIDIControllable : public PBD::Stateful
};
#endif // __gm_midicontrollable_h__

View file

@ -91,7 +91,7 @@ bool
BindingProxy::button_press_handler (GdkEventButton *ev)
{
if ( controllable && is_bind_action(ev) ) {
if (Controllable::StartLearning (controllable.get())) {
if (Controllable::StartLearning (controllable)) {
string prompt = _("operate controller now");
if (prompter == 0) {
prompter = new PopUp (Gtk::WIN_POS_MOUSE, 30000, false);
@ -121,7 +121,7 @@ BindingProxy::prompter_hiding (GdkEventAny* /*ev*/)
{
learning_connection.disconnect ();
if (controllable) {
Controllable::StopLearning (controllable.get());
Controllable::StopLearning (controllable);
}
return false;
}