From bb090c0012340b637508e0930376b3d5afba5f5c Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 17 Apr 2016 14:01:09 +0200 Subject: add self-removing Sends (remove on disconnect) The idea is to dynamically add/remove sends for feeding a sidechain and re-use all existing "External Send" infrastructure in particular latency compensation. --- libs/ardour/ardour/route.h | 4 ++++ libs/ardour/ardour/send.h | 6 ++++++ libs/ardour/route.cc | 41 +++++++++++++++++++++++++++++++++++++++-- libs/ardour/send.cc | 21 +++++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index c8f6005872..b8060f059d 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -836,6 +836,10 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou void output_change_handler (IOChange, void *src); void sidechain_change_handler (IOChange, void *src); + void processor_selfdestruct (boost::weak_ptr); + std::vector > selfdestruct_sequence; + Glib::Threads::Mutex selfdestruct_lock; + bool input_port_count_changing (ChanCount); bool output_port_count_changing (ChanCount); diff --git a/libs/ardour/ardour/send.h b/libs/ardour/ardour/send.h index 3b0e8a4309..967957d854 100644 --- a/libs/ardour/ardour/send.h +++ b/libs/ardour/ardour/send.h @@ -55,6 +55,10 @@ class LIBARDOUR_API Send : public Delivery XMLNode& get_state (); int set_state(const XMLNode&, int version); + PBD::Signal0 SelfDestruct; + void set_remove_on_disconnect (bool b) { _remove_on_disconnect = b; } + bool remove_on_disconnect () const { return _remove_on_disconnect; } + uint32_t pans_required() const { return _configured_input.n_audio(); } void run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool); @@ -89,6 +93,7 @@ class LIBARDOUR_API Send : public Delivery /* disallow copy construction */ Send (const Send&); void panshell_changed (); + void snd_output_changed (IOChange, void*); int set_state_2X (XMLNode const &, int); @@ -96,6 +101,7 @@ class LIBARDOUR_API Send : public Delivery framecnt_t _delay_in; framecnt_t _delay_out; + bool _remove_on_disconnect; }; } // namespace ARDOUR diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 42807bcf03..edb9542d1b 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1339,9 +1339,26 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ set_processor_positions (); + boost::shared_ptr send; + if ((send = boost::dynamic_pointer_cast (processor))) { + send->SelfDestruct.connect_same_thread (*this, + boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr (processor))); + } + return 0; } +void +Route::processor_selfdestruct (boost::weak_ptr wp) +{ + /* We cannot destruct the processor here (usually RT-thread + * with various locks held - in case of sends also io_locks). + * Queue for deletion in low-priority thread. + */ + Glib::Threads::Mutex::Lock lx (selfdestruct_lock); + selfdestruct_sequence.push_back (wp); +} + bool Route::add_processor_from_xml_2X (const XMLNode& node, int version) { @@ -3362,6 +3379,9 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "send") { processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true)); + boost::shared_ptr send = boost::dynamic_pointer_cast (processor); + send->SelfDestruct.connect_same_thread (*this, + boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr (processor))); } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; @@ -4108,13 +4128,12 @@ Route::apply_processor_changes_rt () g_atomic_int_set (&_pending_signals, emissions); return true; } - return false; + return (!selfdestruct_sequence.empty ()); } void Route::emit_pending_signals () { - int sig = g_atomic_int_and (&_pending_signals, 0); if (sig & EmitMeterChanged) { _meter->emit_configuration_changed(); @@ -4128,6 +4147,24 @@ Route::emit_pending_signals () if (sig & EmitRtProcessorChange) { processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */ } + + /* this would be a job for the butler. + * Conceptually we should not take processe/processor locks here. + * OTOH its more efficient (less overhead for summoning the butler and + * telling her what do do) and signal emission is called + * directly after the process callback, which decreases the chance + * of x-runs when taking the locks. + */ + while (!selfdestruct_sequence.empty ()) { + Glib::Threads::Mutex::Lock lx (selfdestruct_lock); + if (selfdestruct_sequence.empty ()) { break; } // re-check with lock + boost::shared_ptr proc = selfdestruct_sequence.back ().lock (); + selfdestruct_sequence.pop_back (); + lx.release (); + if (proc) { + remove_processor (proc); + } + } } void diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index 23ae860f47..ebe8c46c8f 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -77,6 +77,7 @@ Send::Send (Session& s, boost::shared_ptr p, boost::shared_ptr p, boost::shared_ptrChanged.connect_same_thread (*this, boost::bind (&Send::panshell_changed, this)); } + if (_output) { + _output->changed.connect_same_thread (*this, boost::bind (&Send::snd_output_changed, this, _1, _2)); + } } Send::~Send () @@ -221,6 +225,8 @@ Send::state (bool full) node.add_property ("bitslot", buf); } + node.add_property("selfdestruct", _remove_on_disconnect ? "yes" : "no"); + node.add_child_nocopy (_amp->state (full)); return node; @@ -268,6 +274,10 @@ Send::set_state (const XMLNode& node, int version) } } + if ((prop = node.property (X_("selfdestruct"))) != 0) { + _remove_on_disconnect = string_is_affirmative (prop->value()); + } + XMLNodeList nlist = node.children(); for (XMLNodeIterator i = nlist.begin(); i != nlist.end(); ++i) { if ((*i)->name() == X_("Processor")) { @@ -403,3 +413,14 @@ Send::value_as_string (boost::shared_ptr ac) const { return _amp->value_as_string (ac); } + +void +Send::snd_output_changed (IOChange change, void* /*src*/) +{ + if (change.type & IOChange::ConnectionsChanged) { + if (!_output->connected() && _remove_on_disconnect) { + _remove_on_disconnect = false; + SelfDestruct (); /* EMIT SIGNAL */ + } + } +} -- cgit v1.2.3