mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-22 14:46:34 +01:00
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.
This commit is contained in:
parent
514765631b
commit
bb090c0012
4 changed files with 70 additions and 2 deletions
|
|
@ -836,6 +836,10 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
|
||||||
void output_change_handler (IOChange, void *src);
|
void output_change_handler (IOChange, void *src);
|
||||||
void sidechain_change_handler (IOChange, void *src);
|
void sidechain_change_handler (IOChange, void *src);
|
||||||
|
|
||||||
|
void processor_selfdestruct (boost::weak_ptr<Processor>);
|
||||||
|
std::vector<boost::weak_ptr<Processor> > selfdestruct_sequence;
|
||||||
|
Glib::Threads::Mutex selfdestruct_lock;
|
||||||
|
|
||||||
bool input_port_count_changing (ChanCount);
|
bool input_port_count_changing (ChanCount);
|
||||||
bool output_port_count_changing (ChanCount);
|
bool output_port_count_changing (ChanCount);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ class LIBARDOUR_API Send : public Delivery
|
||||||
XMLNode& get_state ();
|
XMLNode& get_state ();
|
||||||
int set_state(const XMLNode&, int version);
|
int set_state(const XMLNode&, int version);
|
||||||
|
|
||||||
|
PBD::Signal0<void> 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(); }
|
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);
|
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 */
|
/* disallow copy construction */
|
||||||
Send (const Send&);
|
Send (const Send&);
|
||||||
void panshell_changed ();
|
void panshell_changed ();
|
||||||
|
void snd_output_changed (IOChange, void*);
|
||||||
|
|
||||||
int set_state_2X (XMLNode const &, int);
|
int set_state_2X (XMLNode const &, int);
|
||||||
|
|
||||||
|
|
@ -96,6 +101,7 @@ class LIBARDOUR_API Send : public Delivery
|
||||||
|
|
||||||
framecnt_t _delay_in;
|
framecnt_t _delay_in;
|
||||||
framecnt_t _delay_out;
|
framecnt_t _delay_out;
|
||||||
|
bool _remove_on_disconnect;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ARDOUR
|
} // namespace ARDOUR
|
||||||
|
|
|
||||||
|
|
@ -1339,9 +1339,26 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
|
||||||
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
|
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
|
||||||
set_processor_positions ();
|
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;
|
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
|
bool
|
||||||
Route::add_processor_from_xml_2X (const XMLNode& node, int version)
|
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") {
|
} else if (prop->value() == "send") {
|
||||||
|
|
||||||
processor.reset (new Send (_session, _pannable, _mute_master, Delivery::Send, true));
|
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 {
|
} else {
|
||||||
error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
|
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);
|
g_atomic_int_set (&_pending_signals, emissions);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return (!selfdestruct_sequence.empty ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Route::emit_pending_signals ()
|
Route::emit_pending_signals ()
|
||||||
{
|
{
|
||||||
|
|
||||||
int sig = g_atomic_int_and (&_pending_signals, 0);
|
int sig = g_atomic_int_and (&_pending_signals, 0);
|
||||||
if (sig & EmitMeterChanged) {
|
if (sig & EmitMeterChanged) {
|
||||||
_meter->emit_configuration_changed();
|
_meter->emit_configuration_changed();
|
||||||
|
|
@ -4128,6 +4147,24 @@ Route::emit_pending_signals ()
|
||||||
if (sig & EmitRtProcessorChange) {
|
if (sig & EmitRtProcessorChange) {
|
||||||
processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
|
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
|
void
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMas
|
||||||
, _metering (false)
|
, _metering (false)
|
||||||
, _delay_in (0)
|
, _delay_in (0)
|
||||||
, _delay_out (0)
|
, _delay_out (0)
|
||||||
|
, _remove_on_disconnect (false)
|
||||||
{
|
{
|
||||||
if (_role == Listen) {
|
if (_role == Listen) {
|
||||||
/* we don't need to do this but it keeps things looking clean
|
/* we don't need to do this but it keeps things looking clean
|
||||||
|
|
@ -99,6 +100,9 @@ Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMas
|
||||||
if (panner_shell()) {
|
if (panner_shell()) {
|
||||||
panner_shell()->Changed.connect_same_thread (*this, boost::bind (&Send::panshell_changed, this));
|
panner_shell()->Changed.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 ()
|
Send::~Send ()
|
||||||
|
|
@ -221,6 +225,8 @@ Send::state (bool full)
|
||||||
node.add_property ("bitslot", buf);
|
node.add_property ("bitslot", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.add_property("selfdestruct", _remove_on_disconnect ? "yes" : "no");
|
||||||
|
|
||||||
node.add_child_nocopy (_amp->state (full));
|
node.add_child_nocopy (_amp->state (full));
|
||||||
|
|
||||||
return node;
|
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();
|
XMLNodeList nlist = node.children();
|
||||||
for (XMLNodeIterator i = nlist.begin(); i != nlist.end(); ++i) {
|
for (XMLNodeIterator i = nlist.begin(); i != nlist.end(); ++i) {
|
||||||
if ((*i)->name() == X_("Processor")) {
|
if ((*i)->name() == X_("Processor")) {
|
||||||
|
|
@ -403,3 +413,14 @@ Send::value_as_string (boost::shared_ptr<AutomationControl> ac) const
|
||||||
{
|
{
|
||||||
return _amp->value_as_string (ac);
|
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 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue