diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 8de0ec6ec0..80cbf1c2f5 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -26,7 +26,6 @@ #include #include -#include #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 , public ControlGroupMember , public SessionHandleRef { diff --git a/libs/ardour/ardour/monitor_processor.h b/libs/ardour/ardour/monitor_processor.h index 63e153e48e..5f233d9ad0 100644 --- a/libs/ardour/ardour/monitor_processor.h +++ b/libs/ardour/ardour/monitor_processor.h @@ -192,6 +192,7 @@ private: MPControl& soloed; ChannelRecord (uint32_t); + ~ChannelRecord (); }; std::vector _channels; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index ab7b59c5c6..265e197d00 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1079,7 +1079,6 @@ public: boost::shared_ptr automation_control_by_id (const PBD::ID&); void add_controllable (boost::shared_ptr); - void remove_controllable (PBD::Controllable*); boost::shared_ptr solo_cut_control() const; diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index 5755e04aac..a194e2858a 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -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(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(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(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(shared_from_this())); } } else { - AutomationWatch::instance().remove_automation_watch (shared_from_this()); + AutomationWatch::instance().remove_automation_watch (boost::dynamic_pointer_cast(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(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(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(shared_from_this()), val, gcd); return true; } diff --git a/libs/ardour/monitor_processor.cc b/libs/ardour/monitor_processor.cc index 56d8f879d6..42a23a7b4d 100644 --- a/libs/ardour/monitor_processor.cc +++ b/libs/ardour/monitor_processor.cc @@ -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 (false, _("monitor dim"), Controllable::Toggle)) - , _cut_all_ptr (new MPControl (false, _("monitor cut"), Controllable::Toggle)) - , _mono_ptr (new MPControl (false, _("monitor mono"), Controllable::Toggle)) - , _dim_level_ptr (new MPControl - /* 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 - /* 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 (false, _("monitor dim"), Controllable::Toggle)) + , _cut_all_ptr (new MPControl (false, _("monitor cut"), Controllable::Toggle)) + , _mono_ptr (new MPControl (false, _("monitor mono"), Controllable::Toggle)) + , _dim_level_ptr (new MPControl + /* 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 + /* 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::const_iterator x = _channels.begin(); x != _channels.end(); ++x) { - if ((*x)->soloed) { - solo_cnt++; - } - } + for (vector::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::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 MonitorProcessor::channel_cut_control (uint32_t chn) const { - if (chn < _channels.size()) { - return _channels[chn]->cut_control; - } - return boost::shared_ptr(); + if (chn < _channels.size()) { + return _channels[chn]->cut_control; + } + return boost::shared_ptr(); } boost::shared_ptr MonitorProcessor::channel_dim_control (uint32_t chn) const { - if (chn < _channels.size()) { - return _channels[chn]->dim_control; - } - return boost::shared_ptr(); + if (chn < _channels.size()) { + return _channels[chn]->dim_control; + } + return boost::shared_ptr(); } boost::shared_ptr MonitorProcessor::channel_polarity_control (uint32_t chn) const { - if (chn < _channels.size()) { - return _channels[chn]->polarity_control; - } - return boost::shared_ptr(); + if (chn < _channels.size()) { + return _channels[chn]->polarity_control; + } + return boost::shared_ptr(); } boost::shared_ptr MonitorProcessor::channel_solo_control (uint32_t chn) const { - if (chn < _channels.size()) { - return _channels[chn]->soloed_control; - } - return boost::shared_ptr(); + if (chn < _channels.size()) { + return _channels[chn]->soloed_control; + } + return boost::shared_ptr(); } 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 */ } diff --git a/libs/ardour/pan_controllable.cc b/libs/ardour/pan_controllable.cc index 35354102e9..9f558898b4 100644 --- a/libs/ardour/pan_controllable.cc +++ b/libs/ardour/pan_controllable.cc @@ -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(shared_from_this())); } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 00a966aa2c..ac66e4feda 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -4224,6 +4224,9 @@ Route::shift (samplepos_t pos, samplecnt_t samples) boost::shared_ptr ac = (*i)->automation_control (*p); if (ac) { boost::shared_ptr al = ac->alist(); + if (al->empty ()) { + continue; + } XMLNode &before = al->get_state (); al->shift (pos, samples); XMLNode &after = al->get_state (); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 2c00fbbd80..d6753c50d9 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -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 (); } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 6e791b4d8a..9ed5a27269 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -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 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(c, null_deleter())); - - if (x != controllables.end()) { - controllables.erase (x); - } -} - boost::shared_ptr Session::controllable_by_id (const PBD::ID& id) { diff --git a/libs/pbd/controllable.cc b/libs/pbd/controllable.cc index c289ee0287..9572bade6e 100644 --- a/libs/pbd/controllable.cc +++ b/libs/pbd/controllable.cc @@ -29,16 +29,14 @@ using namespace PBD; using namespace std; -PBD::Signal1 Controllable::Destroyed; -PBD::Signal1 Controllable::StartLearning; -PBD::Signal1 Controllable::StopLearning; -PBD::Signal3 Controllable::CreateBinding; -PBD::Signal1 Controllable::DeleteBinding; +PBD::Signal1 > Controllable::StartLearning; +PBD::Signal1 > Controllable::StopLearning; PBD::Signal1 > 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::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(); +} + +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"; +} diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index abaa933348..92743c4f66 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -22,12 +22,13 @@ #include #include -#include #include "pbd/libpbd_visibility.h" #include "pbd/signals.h" #include +#include + #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 +{ 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 LearningFinished; - static PBD::Signal3 CreateBinding; - static PBD::Signal1 DeleteBinding; - static PBD::Signal1 StartLearning; - static PBD::Signal1 StopLearning; - - static PBD::Signal1 Destroyed; + static PBD::Signal1 > StartLearning; + static PBD::Signal1 > StopLearning; static PBD::Signal1 > 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 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 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 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 diff --git a/libs/pbd/pbd/playback_buffer.h b/libs/pbd/pbd/playback_buffer.h index e5a6857b1f..d41adea2e2 100644 --- a/libs/pbd/pbd/playback_buffer.h +++ b/libs/pbd/pbd/playback_buffer.h @@ -21,6 +21,7 @@ #define playback_buffer_h #include +#include #include #include "pbd/libpbd_visibility.h" diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 214c9fede7..3c0507abec 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -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 wc) { - if (c == 0) { + boost::shared_ptr 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 wc) { + boost::shared_ptr 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 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) { - AutomationControl *actl = dynamic_cast (controllable); + boost::shared_ptr actl = boost::dynamic_pointer_cast (controllable); if (actl) { actl->start_touch (session->audible_sample ()); } diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.h b/libs/surfaces/generic_midi/generic_midi_control_protocol.h index da4bb619a3..bd7e646282 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.h +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.h @@ -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 lookup_controllable (std::string const &) const; - void maybe_start_touch (PBD::Controllable*); + void maybe_start_touch (boost::shared_ptr); XMLNode& get_state (); int set_state (const XMLNode&, int version); @@ -110,7 +110,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol { PBD::Signal0 ConnectionChange; - private: +private: boost::shared_ptr _input_bundle; boost::shared_ptr _output_bundle; boost::shared_ptr _input_port; @@ -144,17 +144,14 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol { }; typedef std::list 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); + void stop_learning (boost::weak_ptr); 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&); diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index cbf525bd1b..ee08bd70a9 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -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 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 +MIDIControllable::get_controllable () const { - if (c == controllable) { + return _controllable; +} + +void +MIDIControllable::set_controllable (boost::shared_ptr 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 (controllable); + boost::shared_ptr actl = boost::dynamic_pointer_cast (_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 (controllable); + boost::shared_ptr actl = boost::dynamic_pointer_cast (_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()); } 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); diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h index b795067a61..47cb4f0572 100644 --- a/libs/surfaces/generic_midi/midicontrollable.h +++ b/libs/surfaces/generic_midi/midicontrollable.h @@ -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, 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); + boost::shared_ptr 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 _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__ - diff --git a/libs/widgets/binding_proxy.cc b/libs/widgets/binding_proxy.cc index 85119ab1c0..98f4443ad2 100644 --- a/libs/widgets/binding_proxy.cc +++ b/libs/widgets/binding_proxy.cc @@ -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; }