summaryrefslogtreecommitdiff
path: root/libs/ardour/route.cc
diff options
context:
space:
mode:
authorRobin Gareus <robin@gareus.org>2016-04-17 14:01:09 +0200
committerRobin Gareus <robin@gareus.org>2016-04-17 14:01:09 +0200
commitbb090c0012340b637508e0930376b3d5afba5f5c (patch)
treed641fb6e6945f51ad39f112a37d4838e5b6d30f0 /libs/ardour/route.cc
parent514765631b67080432faad5850528b720edab2b6 (diff)
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.
Diffstat (limited to 'libs/ardour/route.cc')
-rw-r--r--libs/ardour/route.cc41
1 files changed, 39 insertions, 2 deletions
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> processor, boost::shared_ptr<
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
+ boost::shared_ptr<Send> send;
+ if ((send = boost::dynamic_pointer_cast<Send> (processor))) {
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (processor)));
+ }
+
return 0;
}
+void
+Route::processor_selfdestruct (boost::weak_ptr<Processor> 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> send = boost::dynamic_pointer_cast<Send> (processor);
+ send->SelfDestruct.connect_same_thread (*this,
+ boost::bind (&Route::processor_selfdestruct, this, boost::weak_ptr<Processor> (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<Processor> proc = selfdestruct_sequence.back ().lock ();
+ selfdestruct_sequence.pop_back ();
+ lx.release ();
+ if (proc) {
+ remove_processor (proc);
+ }
+ }
}
void