click-less processor re-ordering.

This commit is contained in:
Robin Gareus 2015-04-27 04:52:14 +02:00
parent 1fdb3560e8
commit b7a711e385
3 changed files with 151 additions and 87 deletions

View File

@ -182,7 +182,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
bool denormal_protection() const;
void set_meter_point (MeterPoint, bool force = false);
void apply_meter_change_rt ();
void apply_processor_changes_rt ();
MeterPoint meter_point() const { return _pending_meter_point; }
void meter ();
@ -522,6 +522,9 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
boost::shared_ptr<MonitorProcessor> _monitor_control;
boost::shared_ptr<Pannable> _pannable;
ProcessorList _pending_processor_order;
gint _pending_process_reorder; // atomic
Flag _flags;
int _pending_declick;
MeterPoint _meter_point;
@ -602,6 +605,7 @@ class LIBARDOUR_API Route : public SessionObject, public Automatable, public Rou
int configure_processors_unlocked (ProcessorStreams*);
void set_meter_point_unlocked ();
void apply_processor_order (const ProcessorList& new_order);
std::list<std::pair<ChanCount, ChanCount> > try_configure_processors (ChanCount, ProcessorStreams *);
std::list<std::pair<ChanCount, ChanCount> > try_configure_processors_unlocked (ChanCount, ProcessorStreams *);

View File

@ -26,6 +26,7 @@
#include <cassert>
#include <algorithm>
#include <glibmm.h>
#include <boost/algorithm/string.hpp>
#include "pbd/xml++.h"
@ -85,6 +86,7 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
, _signal_latency_at_trim_position (0)
, _initial_delay (0)
, _roll_delay (0)
, _pending_process_reorder (0)
, _flags (flg)
, _pending_declick (true)
, _meter_point (MeterPostFader)
@ -1986,103 +1988,144 @@ Route::processors_reorder_needs_configure (const ProcessorList& new_order)
return false;
}
#ifdef __clang__
__attribute__((annotate("realtime")))
#endif
void
Route::apply_processor_order (const ProcessorList& new_order)
{
/* need to hold processor_lock; either read or write lock
* and the engine process_lock.
* Due to r/w lock ambiguity we can only assert the latter
*/
assert (!AudioEngine::instance()->process_lock().trylock());
/* "new_order" is an ordered list of processors to be positioned according to "placement".
* NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional
* processors in the current actual processor list that are hidden. Any visible processors
* in the current list but not in "new_order" will be assumed to be deleted.
*/
/* "as_it_will_be" and "_processors" are lists of shared pointers.
* actual memory usage is small, but insert/erase is not actually rt-safe :(
* (note though that ::processors_reorder_needs_configure() ensured that
* this function will only ever be called from the rt-thread if no processor were removed)
*
* either way, I can't proove it, but an x-run due to re-order here is less likley
* than an x-run-less 'ardour-silent cycle' both of which effectively "click".
*/
ProcessorList as_it_will_be;
ProcessorList::iterator oiter;
ProcessorList::const_iterator niter;
oiter = _processors.begin();
niter = new_order.begin();
while (niter != new_order.end()) {
/* if the next processor in the old list is invisible (i.e. should not be in the new order)
then append it to the temp list.
Otherwise, see if the next processor in the old list is in the new list. if not,
its been deleted. If its there, append it to the temp list.
*/
if (oiter == _processors.end()) {
/* no more elements in the old list, so just stick the rest of
the new order onto the temp list.
*/
as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
while (niter != new_order.end()) {
++niter;
}
break;
} else {
if (!(*oiter)->display_to_user()) {
as_it_will_be.push_back (*oiter);
} else {
/* visible processor: check that its in the new order */
if (find (new_order.begin(), new_order.end(), (*oiter)) == new_order.end()) {
/* deleted: do nothing, shared_ptr<> will clean up */
} else {
/* ignore this one, and add the next item from the new order instead */
as_it_will_be.push_back (*niter);
++niter;
}
}
/* now remove from old order - its taken care of no matter what */
oiter = _processors.erase (oiter);
}
}
_processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
/* If the meter is in a custom position, find it and make a rough note of its position */
maybe_note_meter_position ();
}
int
Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
{
#if 0 // TODO
if (processors_reorder_needs_configure (new_order)) {
printf("REORDER NEEDS CONFIGURE\n");
// tough luck, use existing code belog
} else {
printf("COULD DO IT CLICKLESS\n");
/* TODO: take a reader-lock, prepare the new order when done
* atomically set it as pending state..
* and do the _processors.insert() in the RT-thread
* configure_processors_unlocked() is NOT needed,
* only setup_invisible_processors() needs to be called.
*
* see also apply_meter_change_rt()
*/
// it a change is already queued, wait for it
// (unless engine is stopped. apply immediately and proceed
while (g_atomic_int_get (&_pending_process_reorder)) {
if (!AudioEngine::instance()->running()) {
DEBUG_TRACE (DEBUG::Processors, "offline apply queued processor re-order.\n");
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
apply_processor_order(_pending_processor_order);
setup_invisible_processors ();
g_atomic_int_set (&_pending_process_reorder, 0);
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
} else {
// TODO rather use a semaphore or something.
// but since ::reorder_processors() is called
// from the GUI thread, this is fine..
Glib::usleep(500);
}
}
#endif
/* "new_order" is an ordered list of processors to be positioned according to "placement".
NOTE: all processors in "new_order" MUST be marked as display_to_user(). There maybe additional
processors in the current actual processor list that are hidden. Any visible processors
in the current list but not in "new_order" will be assumed to be deleted.
*/
if (processors_reorder_needs_configure (new_order) || !AudioEngine::instance()->running()) {
{
Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::WriterLock lm (_processor_lock);
ProcessorState pstate (this);
ProcessorList::iterator oiter;
ProcessorList::const_iterator niter;
ProcessorList as_it_will_be;
oiter = _processors.begin();
niter = new_order.begin();
while (niter != new_order.end()) {
/* if the next processor in the old list is invisible (i.e. should not be in the new order)
then append it to the temp list.
Otherwise, see if the next processor in the old list is in the new list. if not,
its been deleted. If its there, append it to the temp list.
*/
if (oiter == _processors.end()) {
/* no more elements in the old list, so just stick the rest of
the new order onto the temp list.
*/
as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
while (niter != new_order.end()) {
++niter;
}
break;
} else {
if (!(*oiter)->display_to_user()) {
as_it_will_be.push_back (*oiter);
} else {
/* visible processor: check that its in the new order */
if (find (new_order.begin(), new_order.end(), (*oiter)) == new_order.end()) {
/* deleted: do nothing, shared_ptr<> will clean up */
} else {
/* ignore this one, and add the next item from the new order instead */
as_it_will_be.push_back (*niter);
++niter;
}
}
/* now remove from old order - its taken care of no matter what */
oiter = _processors.erase (oiter);
}
}
_processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
/* If the meter is in a custom position, find it and make a rough note of its position */
maybe_note_meter_position ();
apply_processor_order (new_order);
if (configure_processors_unlocked (err)) {
pstate.restore ();
return -1;
}
}
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
lm.release();
lx.release();
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
} else {
DEBUG_TRACE (DEBUG::Processors, "Queue clickless processor re-order.\n");
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
// _pending_processor_order is protected by _processor_lock
_pending_processor_order = new_order;
g_atomic_int_set (&_pending_process_reorder, 1);
}
return 0;
}
@ -2845,7 +2888,7 @@ Route::set_processor_state (const XMLNode& node)
}
reset_instrument_info ();
processors_changed (RouteProcessorChange ());
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
}
@ -3340,7 +3383,7 @@ Route::flush_processors ()
__attribute__((annotate("realtime")))
#endif
void
Route::apply_meter_change_rt ()
Route::apply_processor_changes_rt ()
{
if (_pending_meter_point != _meter_point) {
Glib::Threads::RWLock::WriterLock pwl (_processor_lock, Glib::Threads::TRY_LOCK);
@ -3350,6 +3393,23 @@ Route::apply_meter_change_rt ()
set_meter_point_unlocked();
}
}
bool changed = false;
if (g_atomic_int_get (&_pending_process_reorder)) {
Glib::Threads::RWLock::WriterLock pwl (_processor_lock, Glib::Threads::TRY_LOCK);
if (pwl.locked()) {
apply_processor_order (_pending_processor_order);
setup_invisible_processors ();
changed = true;
g_atomic_int_set (&_pending_process_reorder, 0);
}
}
if (changed) {
processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
set_processor_positions ();
}
}
void
@ -3383,7 +3443,7 @@ Route::set_meter_point_unlocked ()
assert (!lm.locked ());
#endif
_meter_point = _pending_meter_point;;
_meter_point = _pending_meter_point;
bool meter_was_visible_to_user = _meter->display_to_user ();

View File

@ -82,8 +82,8 @@ Session::process (pframes_t nframes)
* callig it hold a _processor_lock reader-lock
*/
boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->apply_meter_change_rt();
for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
(*i)->apply_processor_changes_rt();
}
_engine.main_thread()->drop_buffers ();