add missing profile header
[ardour.git] / libs / ardour / route.cc
index 2727f511cb300825c46b6e66f6f3f0ae17788a2a..b27d36dfbb6d261d9c20b251e8aadf1850bcf02d 100644 (file)
@@ -26,6 +26,7 @@
 #include <cassert>
 #include <algorithm>
 
+#include <glibmm.h>
 #include <boost/algorithm/string.hpp>
 
 #include "pbd/xml++.h"
@@ -37,6 +38,7 @@
 
 #include "ardour/amp.h"
 #include "ardour/audio_buffer.h"
+#include "ardour/audio_track.h"
 #include "ardour/audio_port.h"
 #include "ardour/audioengine.h"
 #include "ardour/buffer.h"
@@ -47,6 +49,7 @@
 #include "ardour/internal_return.h"
 #include "ardour/internal_send.h"
 #include "ardour/meter.h"
+#include "ardour/delayline.h"
 #include "ardour/midi_buffer.h"
 #include "ardour/midi_port.h"
 #include "ardour/monitor_processor.h"
@@ -57,6 +60,7 @@
 #include "ardour/port.h"
 #include "ardour/port_insert.h"
 #include "ardour/processor.h"
+#include "ardour/profile.h"
 #include "ardour/route.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
@@ -79,11 +83,15 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , GraphNode (sess._process_graph)
        , _active (true)
        , _signal_latency (0)
+       , _signal_latency_at_amp_position (0)
+       , _signal_latency_at_trim_position (0)
        , _initial_delay (0)
        , _roll_delay (0)
+       , _pending_process_reorder (0)
        , _flags (flg)
        , _pending_declick (true)
        , _meter_point (MeterPostFader)
+       , _pending_meter_point (MeterPostFader)
        , _meter_type (MeterPeak)
        , _self_solo (false)
        , _soloed_by_others_upstream (0)
@@ -100,10 +108,10 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _order_key (0)
        , _has_order_key (false)
        , _remote_control_id (0)
+       , _track_number (0)
        , _in_configure_processors (false)
        , _initial_io_setup (false)
        , _custom_meter_position_noted (false)
-       , _last_custom_meter_was_at_end (false)
 {
        if (is_master()) {
                _meter_type = MeterK20;
@@ -142,11 +150,33 @@ Route::init ()
        _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
        _output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
 
+#if 0 // not used - just yet
+       if (!is_master() && !is_monitor() && !is_auditioner()) {
+               _delayline.reset (new DelayLine (_session, _name));
+               add_processor (_delayline, PreFader);
+       }
+#endif
+
        /* add amp processor  */
 
        _amp.reset (new Amp (_session));
        add_processor (_amp, PostFader);
 
+       /* and input trim */
+       _trim.reset (new Amp (_session, "trim"));
+       _trim->set_display_to_user (false);
+
+       if (dynamic_cast<AudioTrack*>(this)) {
+               /* we can't do this in the AudioTrack's constructor
+                * because _trim does not exit then
+                */
+               _trim->activate();
+       }
+       else if (!dynamic_cast<Track*>(this) && ! ( is_monitor() || is_auditioner() )) {
+               /* regular bus */
+               _trim->activate();
+       }
+
        /* create standard processors: meter, main outs, monitor out;
           they will be added to _processors by setup_invisible_processors ()
        */
@@ -177,8 +207,6 @@ Route::init ()
 
        /* now that we have _meter, its safe to connect to this */
 
-       Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
-
        {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                configure_processors (0);
@@ -334,7 +362,6 @@ Route::ensure_track_or_route_name(string name, Session &session)
        return newname;
 }
 
-
 void
 Route::inc_gain (gain_t fraction, void *src)
 {
@@ -396,6 +423,19 @@ Route::set_gain (gain_t val, void *src)
        _amp->set_gain (val, src);
 }
 
+void
+Route::inc_trim (gain_t fraction, void *src)
+{
+       _trim->inc_gain (fraction, src);
+}
+
+void
+Route::set_trim (gain_t val, void * /* src */)
+{
+       // TODO route group, see set_gain()
+       _trim->set_gain (val, 0);
+}
+
 void
 Route::maybe_declick (BufferSet&, framecnt_t, int)
 {
@@ -423,22 +463,42 @@ Route::process_output_buffers (BufferSet& bufs,
        assert (!AudioEngine::instance()->process_lock().trylock());
 
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
-       assert(lm.locked());
+       if (!lm.locked()) {
+               // can this actually happen? functions calling process_output_buffers()
+               // already take a reader-lock.
+               bufs.silence (nframes, 0);
+               return;
+       }
 
        /* figure out if we're going to use gain automation */
        if (gain_automation_ok) {
                _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
-               _amp->setup_gain_automation (start_frame, end_frame, nframes);
+               _amp->setup_gain_automation (
+                               start_frame + _signal_latency_at_amp_position,
+                               end_frame + _signal_latency_at_amp_position,
+                               nframes);
+
+               _trim->set_gain_automation_buffer (_session.trim_automation_buffer ());
+               _trim->setup_gain_automation (
+                               start_frame + _signal_latency_at_trim_position,
+                               end_frame + _signal_latency_at_trim_position,
+                               nframes);
        } else {
                _amp->apply_gain_automation (false);
+               _trim->apply_gain_automation (false);
        }
 
        /* Tell main outs what to do about monitoring.  We do this so that
           on a transition between monitoring states we get a de-clicking gain
           change in the _main_outs delivery.
        */
+       bool silence = monitoring_state () == MonitoringSilence;
+
+       //but we override this in the case where we have an internal generator
+       if ( _have_internal_generator )
+               silence = false;
 
-       _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence);
+       _main_outs->no_outs_cuz_we_no_monitor (silence);
 
        /* -------------------------------------------------------------------------------------------
           GLOBAL DECLICK (for transport changes etc.)
@@ -506,7 +566,9 @@ Route::process_output_buffers (BufferSet& bufs,
        /* set this to be true if the meter will already have been ::run() earlier */
        bool const meter_already_run = metering_state() == MeteringInput;
 
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+       framecnt_t latency = 0;
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
                        /* don't ::run() the meter, otherwise it will have its previous peak corrupted */
@@ -531,20 +593,38 @@ Route::process_output_buffers (BufferSet& bufs,
                   do we catch route != active somewhere higher?
                */
 
-               (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+               if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
+                       boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
+               }
+
+               (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
                bufs.set_count ((*i)->output_streams());
+
+               if ((*i)->active ()) {
+                       latency += (*i)->signal_latency ();
+               }
        }
 }
 
 void
 Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
-                         boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export)
+               boost::shared_ptr<Processor> endpoint,
+               bool include_endpoint, bool for_export, bool for_freeze)
 {
        /* If no processing is required, there's no need to go any further. */
        if (!endpoint && !include_endpoint) {
                return;
        }
 
+       framecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze);
+       _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
+       _amp->setup_gain_automation (start - latency, start - latency + nframes, nframes);
+
+       /* trim is always at the top, for bounce no latency compensation is needed */
+       _trim->set_gain_automation_buffer (_session.trim_automation_buffer ());
+       _trim->setup_gain_automation (start, start + nframes, nframes);
+
+       latency = 0;
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
                if (!include_endpoint && (*i) == endpoint) {
@@ -552,8 +632,10 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                }
 
                /* if we're not exporting, stop processing if we come across a routing processor. */
-
-               if (!for_export && (*i)->does_routing()) {
+               if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+                       break;
+               }
+               if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
                        break;
                }
 
@@ -561,11 +643,11 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
                 * oh, and don't bother with the peak meter either.
                 */
                if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
-                       (*i)->run (buffers, start, start+nframes, nframes, true);
+                       (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+                       buffers.set_count ((*i)->output_streams());
+                       latency += (*i)->signal_latency ();
                }
 
-               //buffers.set_count ((*i)->output_streams());
-
                if ((*i) == endpoint) {
                        break;
                }
@@ -573,7 +655,8 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
 }
 
 framecnt_t
-Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export) const
+Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint,
+               bool include_endpoint, bool for_export, bool for_freeze) const
 {
        framecnt_t latency = 0;
        if (!endpoint && !include_endpoint) {
@@ -581,11 +664,13 @@ Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint, bool include_e
        }
 
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
                if (!include_endpoint && (*i) == endpoint) {
                        break;
                }
-               if (!for_export && (*i)->does_routing()) {
+               if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+                       break;
+               }
+               if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
                        break;
                }
                if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
@@ -598,6 +683,34 @@ Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint, bool include_e
        return latency;
 }
 
+ChanCount
+Route::bounce_get_output_streams (ChanCount &cc, boost::shared_ptr<Processor> endpoint,
+               bool include_endpoint, bool for_export, bool for_freeze) const
+{
+       if (!endpoint && !include_endpoint) {
+               return cc;
+       }
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (!include_endpoint && (*i) == endpoint) {
+                       break;
+               }
+               if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+                       break;
+               }
+               if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+                       break;
+               }
+               if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+                       cc = (*i)->output_streams();
+               }
+               if ((*i) == endpoint) {
+                       break;
+               }
+       }
+       return cc;
+}
+
 ChanCount
 Route::n_process_buffers ()
 {
@@ -702,6 +815,11 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
+       if (is_master() || is_monitor() || is_auditioner()) {
+               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name()));
+               return;
+       }
+
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
                _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
                return;
@@ -716,6 +834,14 @@ Route::set_solo (bool yn, void *src)
                solo_changed (true, src); /* EMIT SIGNAL */
                _solo_control->Changed (); /* EMIT SIGNAL */
        }
+
+       /* XXX TRACKS DEVELOPERS: THIS LOGIC SUGGESTS THAT YOU ARE NOT AWARE OF
+          Config->get_solo_mute_overrride().
+       */
+
+       if (yn && Profile->get_trx()) {
+               set_mute (false, src);
+       }
 }
 
 void
@@ -916,6 +1042,17 @@ Route::muted () const
        return _mute_master->muted_by_self();
 }
 
+bool
+Route::muted_by_others () const
+{
+       //master is never muted by others
+       if (is_master())
+               return false;
+               
+       //now check to see if something is soloed (and I am not)
+       return (_session.soloing() && !self_soloed() && !solo_isolated());
+}
+
 #if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
@@ -1147,8 +1284,18 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                if (processor->set_state (node, version)) {
                        return false;
                }
+               
+               //A2 uses the "active" flag in the toplevel redirect node, not in the child plugin/IO
+               if (i != children.end()) {
+                       if ((prop = (*i)->property (X_("active"))) != 0) {
+                               if ( string_is_affirmative (prop->value()) && !_session.get_disable_all_loaded_plugins() )
+                                       processor->activate();
+                               else
+                                       processor->deactivate();
+                       }
+               }
 
-               return (add_processor (processor, placement) == 0);
+               return (add_processor (processor, placement, 0, false) == 0);
        }
 
        catch (failed_constructor &err) {
@@ -1395,7 +1542,7 @@ Route::clear_processors (Placement p)
                                seen_amp = true;
                        }
 
-                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline || (*i) == _trim) {
 
                                /* you can't remove these */
 
@@ -1462,7 +1609,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
        /* these can never be removed */
 
-       if (processor == _amp || processor == _meter || processor == _main_outs) {
+       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline || processor == _trim) {
                return 0;
        }
 
@@ -1579,7 +1726,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                        /* these can never be removed */
 
-                       if (processor == _amp || processor == _meter || processor == _main_outs) {
+                       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline || processor == _trim) {
                                ++i;
                                continue;
                        }
@@ -1769,6 +1916,14 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                (*p)->configure_io(c->first, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
+
+               boost::shared_ptr<PluginInsert> pi;
+               if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*p)) != 0) {
+                       /* plugins connected via Split Match may have more channels.
+                        * route/scratch buffers are needed for all of them*/
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->input_streams());
+                       processor_max_streams = ChanCount::max(processor_max_streams, pi->natural_input_streams());
+               }
                out = c->second;
 
                if (boost::dynamic_pointer_cast<Delivery> (*p)
@@ -1786,7 +1941,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
 
        if (_meter) {
-               _meter->reset_max_channels (processor_max_streams);
+               _meter->set_max_channels (processor_max_streams);
        }
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
@@ -1827,86 +1982,175 @@ Route::all_visible_processors_active (bool state)
        _session.set_dirty ();
 }
 
-int
-Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
+bool
+Route::processors_reorder_needs_configure (const ProcessorList& new_order)
+{
+       /* check if re-order requires re-configuration of any processors
+        * -> compare channel configuration for all processors
+        */
+       Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
+       ChanCount c = input_streams ();
+
+       for (ProcessorList::const_iterator j = new_order.begin(); j != new_order.end(); ++j) {
+               bool found = false;
+               if (c != (*j)->input_streams()) {
+                       return true;
+               }
+               for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+                       if (*i == *j) {
+                               found = true;
+                               if ((*i)->input_streams() != c) {
+                                       return true;
+                               }
+                               c = (*i)->output_streams();
+                               break;
+                       }
+               }
+               if (!found) {
+                       return true;
+               }
+       }
+       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.
-       */
+        * 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.
+        */
 
-       {
-               Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
-               Glib::Threads::RWLock::WriterLock lm (_processor_lock);
-               ProcessorState pstate (this);
+       /* "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::iterator oiter;
-               ProcessorList::const_iterator niter;
-               ProcessorList as_it_will_be;
+       ProcessorList as_it_will_be;
+       ProcessorList::iterator oiter;
+       ProcessorList::const_iterator niter;
 
-               oiter = _processors.begin();
-               niter = new_order.begin();
+       oiter = _processors.begin();
+       niter = new_order.begin();
 
-               while (niter !=  new_order.end()) {
+       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.
+               /* 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.
-                       */
+                  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()) {
+               if (oiter == _processors.end()) {
 
-                               /* no more elements in the old list, so just stick the rest of
-                                  the new order onto the temp list.
-                               */
+                       /* 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;
+                       as_it_will_be.insert (as_it_will_be.end(), niter, new_order.end());
+                       while (niter != new_order.end()) {
+                               ++niter;
+                       }
+                       break;
 
-                       } else {
+               } else {
 
-                               if (!(*oiter)->display_to_user()) {
+                       if (!(*oiter)->display_to_user()) {
 
-                                       as_it_will_be.push_back (*oiter);
+                               as_it_will_be.push_back (*oiter);
 
-                               } else {
+                       } else {
 
-                                       /* visible processor: check that its in the new order */
+                               /* 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;
-                                       }
+                               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);
                        }
 
+                       /* 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());
+       }
+       _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 ();
+       /* 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)
+{
+       // 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);
+               }
+       }
+
+       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);
+
+               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;
 }
@@ -1968,6 +2212,10 @@ Route::state(bool full_state)
        node->add_child_nocopy (_mute_control->get_state ());
        node->add_child_nocopy (_mute_master->get_state ());
 
+       if (full_state) {
+               node->add_child_nocopy (Automatable::get_automation_xml_state ());
+       }
+
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
        snprintf (buf, sizeof (buf), "%d", _remote_control_id);
        remote_control_node->add_property (X_("id"), buf);
@@ -2014,8 +2262,6 @@ Route::state(bool full_state)
                        after->id().print (buf, sizeof (buf));
                        node->add_property (X_("processor-after-last-custom-meter"), buf);
                }
-
-               node->add_property (X_("last-custom-meter-was-at-end"), _last_custom_meter_was_at_end ? "yes" : "no");
        }
 
        return *node;
@@ -2208,10 +2454,6 @@ Route::set_state (const XMLNode& node, int version)
                }
        }
 
-       if ((prop = node.property (X_("last-custom-meter-was-at-end"))) != 0) {
-               _last_custom_meter_was_at_end = string_is_affirmative (prop->value ());
-       }
-
        for (niter = nlist.begin(); niter != nlist.end(); ++niter){
                child = *niter;
 
@@ -2238,6 +2480,9 @@ Route::set_state (const XMLNode& node, int version)
 
                } else if (child->name() == X_("MuteMaster")) {
                        _mute_master->set_state (*child, version);
+
+               } else if (child->name() == Automatable::xml_node_name) {
+                       set_automation_xml_state (*child, Evoral::Parameter(NullAutomation));
                }
        }
 
@@ -2247,6 +2492,7 @@ Route::set_state (const XMLNode& node, int version)
 int
 Route::set_state_2X (const XMLNode& node, int version)
 {
+       LocaleGuard lg (X_("C"));
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
        XMLNode *child;
@@ -2541,9 +2787,17 @@ Route::set_processor_state (const XMLNode& node)
                if (prop->value() == "amp") {
                        _amp->set_state (**niter, Stateful::current_state_version);
                        new_order.push_back (_amp);
+               } else if (prop->value() == "trim") {
+                       _trim->set_state (**niter, Stateful::current_state_version);
+                       new_order.push_back (_trim);
                } else if (prop->value() == "meter") {
                        _meter->set_state (**niter, Stateful::current_state_version);
                        new_order.push_back (_meter);
+               } else if (prop->value() == "delay") {
+                       if (_delayline) {
+                               _delayline->set_state (**niter, Stateful::current_state_version);
+                               new_order.push_back (_delayline);
+                       }
                } else if (prop->value() == "main-outs") {
                        _main_outs->set_state (**niter, Stateful::current_state_version);
                } else if (prop->value() == "intreturn") {
@@ -2581,7 +2835,7 @@ Route::set_processor_state (const XMLNode& node)
 
                                if (prop->value() == "intsend") {
 
-                                       processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Aux, true));
+                                       processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
 
                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                           prop->value() == "lv2" ||
@@ -2653,7 +2907,7 @@ Route::set_processor_state (const XMLNode& node)
        }
 
        reset_instrument_info ();
-       processors_changed (RouteProcessorChange ());
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 }
 
@@ -2750,7 +3004,7 @@ Route::enable_monitor_send ()
 
        /* make sure we have one */
        if (!_monitor_send) {
-               _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+               _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), _session.monitor_out(), Delivery::Listen));
                _monitor_send->set_display_to_user (false);
        }
 
@@ -2788,7 +3042,7 @@ Route::add_aux_send (boost::shared_ptr<Route> route, boost::shared_ptr<Processor
                {
                        Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                        boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
-                       listener.reset (new InternalSend (_session, sendpan, _mute_master, route, Delivery::Aux));
+                       listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
                }
 
                add_processor (listener, before);
@@ -2998,6 +3252,11 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                */
                need_to_queue_solo_change = false;
                configure_processors (0);
+
+               if (is_master()) {
+                       _session.reset_monitor_section();
+               }
+
                io_changed (); /* EMIT SIGNAL */
        }
 
@@ -3074,6 +3333,7 @@ Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
        }
 
        _amp->apply_gain_automation (false);
+       _trim->apply_gain_automation (false);
        passthru (bufs, start_frame, end_frame, nframes, 0);
 
        return 0;
@@ -3138,78 +3398,164 @@ Route::flush_processors ()
        }
 }
 
+#ifdef __clang__
+__attribute__((annotate("realtime")))
+#endif
+bool
+Route::apply_processor_changes_rt ()
+{
+       int emissions = EmitNone;
+
+       if (_pending_meter_point != _meter_point) {
+               Glib::Threads::RWLock::WriterLock pwl (_processor_lock, Glib::Threads::TRY_LOCK);
+               if (pwl.locked()) {
+                       /* meters always have buffers for 'processor_max_streams'
+                        * they can be re-positioned without re-allocation */
+                       if (set_meter_point_unlocked()) {
+                               emissions |= EmitMeterChanged | EmitMeterVisibilityChange;;
+                       } else {
+                               emissions |= EmitMeterChanged;
+                       }
+               }
+       }
+
+       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);
+                       emissions |= EmitRtProcessorChange;
+               }
+       }
+       if (changed) {
+               set_processor_positions ();
+       }
+       if (emissions != 0) {
+               g_atomic_int_set (&_pending_signals, emissions);
+               return true;
+       }
+       return false;
+}
+
+void
+Route::emit_pending_signals ()
+{
+
+       int sig = g_atomic_int_and (&_pending_signals, 0);
+       if (sig & EmitMeterChanged) {
+               _meter->emit_configuration_changed();
+               meter_change (); /* EMIT SIGNAL */
+               if (sig & EmitMeterVisibilityChange) {
+               processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, true)); /* EMIT SIGNAL */
+               } else {
+               processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, false)); /* EMIT SIGNAL */
+               }
+       }
+       if (sig & EmitRtProcessorChange) {
+               processors_changed (RouteProcessorChange (RouteProcessorChange::RealTimeChange)); /* EMIT SIGNAL */
+       }
+}
+
 void
 Route::set_meter_point (MeterPoint p, bool force)
 {
-       if (_meter_point == p && !force) {
+       if (_pending_meter_point == p && !force) {
                return;
        }
 
-       bool meter_was_visible_to_user = _meter->display_to_user ();
-
-       {
+       if (force || !AudioEngine::instance()->running()) {
                Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::WriterLock lm (_processor_lock);
+               _pending_meter_point = p;
+               _meter->emit_configuration_changed();
+               meter_change (); /* EMIT SIGNAL */
+               if (set_meter_point_unlocked()) {
+                       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, true)); /* EMIT SIGNAL */
+               } else {
+                       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, false)); /* EMIT SIGNAL */
+               }
+       } else {
+               _pending_meter_point = p;
+       }
+}
 
-               maybe_note_meter_position ();
 
-               _meter_point = p;
+#ifdef __clang__
+__attribute__((annotate("realtime")))
+#endif
+bool
+Route::set_meter_point_unlocked ()
+{
+#ifndef NDEBUG
+       /* Caller must hold process and processor write lock */
+       assert (!AudioEngine::instance()->process_lock().trylock());
+       Glib::Threads::RWLock::WriterLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
+       assert (!lm.locked ());
+#endif
+
+       _meter_point = _pending_meter_point;
 
-               if (_meter_point != MeterCustom) {
+       bool meter_was_visible_to_user = _meter->display_to_user ();
 
-                       _meter->set_display_to_user (false);
+       if (!_custom_meter_position_noted) {
+               maybe_note_meter_position ();
+       }
 
-                       setup_invisible_processors ();
+       if (_meter_point != MeterCustom) {
 
-               } else {
+               _meter->set_display_to_user (false);
 
-                       _meter->set_display_to_user (true);
+               setup_invisible_processors ();
 
-                       /* If we have a previous position for the custom meter, try to put it there */
-                       if (_custom_meter_position_noted) {
-                               boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
-                               
-                               if (after) {
-                                       ProcessorList::iterator i = find (_processors.begin(), _processors.end(), after);
-                                       if (i != _processors.end ()) {
-                                               _processors.remove (_meter);
-                                               _processors.insert (i, _meter);
-                                       }
-                               } else if (_last_custom_meter_was_at_end) {
-                                       _processors.remove (_meter);
-                                       _processors.push_back (_meter);
-                               }
+       } else {
+               _meter->set_display_to_user (true);
+
+               /* If we have a previous position for the custom meter, try to put it there */
+               boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
+               if (after) {
+                       ProcessorList::iterator i = find (_processors.begin(), _processors.end(), after);
+                       if (i != _processors.end ()) {
+                               _processors.remove (_meter);
+                               _processors.insert (i, _meter);
                        }
+               } else {// at end, right before the mains_out/panner
+                       _processors.remove (_meter);
+                       ProcessorList::iterator main = _processors.end();
+                       _processors.insert (--main, _meter);
                }
+       }
 
-               /* Set up the meter for its new position */
+       /* Set up the meter for its new position */
 
-               ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-               
-               ChanCount m_in;
-               
-               if (loc == _processors.begin()) {
-                       m_in = _input->n_ports();
-               } else {
-                       ProcessorList::iterator before = loc;
-                       --before;
-                       m_in = (*before)->output_streams ();
-               }
-               
-               _meter->reflect_inputs (m_in);
-               
-               /* we do not need to reconfigure the processors, because the meter
-                  (a) is always ready to handle processor_max_streams
-                  (b) is always an N-in/N-out processor, and thus moving
-                  it doesn't require any changes to the other processors.
-               */
+       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+       
+       ChanCount m_in;
+       
+       if (loc == _processors.begin()) {
+               m_in = _input->n_ports();
+       } else {
+               ProcessorList::iterator before = loc;
+               --before;
+               m_in = (*before)->output_streams ();
        }
 
-       meter_change (); /* EMIT SIGNAL */
+       _meter->reflect_inputs (m_in);
 
-       bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
+       /* we do not need to reconfigure the processors, because the meter
+          (a) is always ready to handle processor_max_streams
+          (b) is always an N-in/N-out processor, and thus moving
+          it doesn't require any changes to the other processors.
+       */
 
-       processors_changed (RouteProcessorChange (RouteProcessorChange::MeterPointChange, meter_visibly_changed)); /* EMIT SIGNAL */
+       /* these should really be done after releasing the lock
+        * but all those signals are subscribed to with gui_thread()
+        * so we're safe.
+        */
+        return (_meter->display_to_user() != meter_was_visible_to_user);
 }
 
 void
@@ -3254,15 +3600,35 @@ framecnt_t
 Route::update_signal_latency ()
 {
        framecnt_t l = _output->user_latency();
+       framecnt_t lamp = 0;
+       bool before_amp = true;
+       framecnt_t ltrim = 0;
+       bool before_trim = true;
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if ((*i)->active ()) {
                        l += (*i)->signal_latency ();
                }
+               if ((*i) == _amp) {
+                       before_amp = false;
+               }
+               if ((*i) == _trim) {
+                       before_amp = false;
+               }
+               if (before_amp) {
+                       lamp = l;
+               }
+               if (before_trim) {
+                       lamp = l;
+               }
        }
 
        DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
 
+       // TODO: (lamp - _signal_latency) to sync to output (read-ahed),  currently _roll_delay shifts this around
+       _signal_latency_at_amp_position = lamp;
+       _signal_latency_at_trim_position = ltrim;
+
        if (_signal_latency != l) {
                _signal_latency = l;
                signal_latency_changed (); /* EMIT SIGNAL */
@@ -3304,18 +3670,21 @@ Route::set_latency_compensation (framecnt_t longest_session_latency)
 }
 
 Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
-                            boost::shared_ptr<AutomationList>(), name)
+       : AutomationControl (r->session(),
+                            Evoral::Parameter (SoloAutomation),
+                            ParameterDescriptor(Evoral::Parameter (SoloAutomation)),
+                            boost::shared_ptr<AutomationList>(), name)
        , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
+       gl->set_interpolation(Evoral::ControlList::Discrete);
        set_list (gl);
 }
 
 void
 Route::SoloControllable::set_value (double val)
 {
-       bool bval = ((val >= 0.5f) ? true: false);
+       const bool bval = ((val >= 0.5) ? true : false);
 
        boost::shared_ptr<RouteList> rl (new RouteList);
 
@@ -3342,46 +3711,71 @@ Route::SoloControllable::get_value () const
        }
 
        if (Config->get_solo_control_is_listen_control()) {
-               return r->listening_via_monitor() ? 1.0f : 0.0f;
+               return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
        } else {
-               return r->self_soloed() ? 1.0f : 0.0f;
+               return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
        }
 }
 
 Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
-       : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation),
-                            boost::shared_ptr<AutomationList>(), name)
+       : AutomationControl (r->session(),
+                            Evoral::Parameter (MuteAutomation),
+                            ParameterDescriptor (Evoral::Parameter (MuteAutomation)),
+                            boost::shared_ptr<AutomationList>(),
+                            name)
        , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
+       gl->set_interpolation(Evoral::ControlList::Discrete);
        set_list (gl);
 }
 
 void
-Route::MuteControllable::set_value (double val)
+Route::MuteControllable::set_superficial_value(bool muted)
 {
-       bool bval = ((val >= 0.5f) ? true: false);
+       /* Note we can not use AutomationControl::set_value here since it will emit
+          Changed(), but the value will not be correct to the observer. */
 
-       boost::shared_ptr<RouteList> rl (new RouteList);
+       bool to_list = _list && ((AutomationList*)_list.get())->automation_write();
+
+       Control::set_double (muted, _session.transport_frame(), to_list);
+}
+
+void
+Route::MuteControllable::set_value (double val)
+{
+       const bool bval = ((val >= 0.5) ? true : false);
 
        boost::shared_ptr<Route> r = _route.lock ();
        if (!r) {
                return;
        }
 
-       rl->push_back (r);
-       _session.set_mute (rl, bval);
+       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
+               // Playing back automation, set route mute directly
+               r->set_mute (bval, this);
+       } else {
+               // Set from user, queue mute event
+               boost::shared_ptr<RouteList> rl (new RouteList);
+               rl->push_back (r);
+               _session.set_mute (rl, bval, Session::rt_cleanup);
+       }
+
+       // Set superficial/automation value to drive controller (and possibly record)
+       set_superficial_value(bval);
 }
 
 double
 Route::MuteControllable::get_value () const
 {
-       boost::shared_ptr<Route> r = _route.lock ();
-       if (!r) {
-               return 0;
+       if (_list && ((AutomationList*)_list.get())->automation_playback()) {
+               // Playing back automation, get the value from the list
+               return AutomationControl::get_value();
        }
 
-       return r->muted() ? 1.0f : 0.0f;
+       // Not playing back automation, get the actual route mute value
+       boost::shared_ptr<Route> r = _route.lock ();
+       return (r && r->muted()) ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO;
 }
 
 void
@@ -3437,6 +3831,18 @@ Route::shift (framepos_t pos, framecnt_t frames)
                _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
        }
 
+       /* gain automation */
+       {
+               boost::shared_ptr<AutomationControl> gc = _trim->gain_control();
+
+               XMLNode &before = gc->alist()->get_state ();
+               gc->alist()->shift (pos, frames);
+               XMLNode &after = gc->alist()->get_state ();
+               _session.add_command (new MementoCommand<AutomationList> (*gc->alist().get(), &before, &after));
+       }
+
+       // TODO mute automation ??
+
        /* pan automation */
        if (_pannable) {
                ControlSet::Controls& c (_pannable->controls());
@@ -3491,14 +3897,14 @@ Route::save_as_template (const string& path, const string& name)
 bool
 Route::set_name (const string& str)
 {
-       bool ret;
-       string ioproc_name;
-       string name;
+       if (str == name()) {
+               return true;
+       }
 
-       name = Route::ensure_track_or_route_name (str, _session);
+       string name = Route::ensure_track_or_route_name (str, _session);
        SessionObject::set_name (name);
 
-       ret = (_input->set_name(name) && _output->set_name(name));
+       bool ret = (_input->set_name(name) && _output->set_name(name));
 
        if (ret) {
                /* rename the main outs. Leave other IO processors
@@ -3623,6 +4029,10 @@ Route::denormal_protection () const
 void
 Route::set_active (bool yn, void* src)
 {
+       if (_session.transport_rolling()) {
+               return;
+       }
+
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) {
                _route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group));
                return;
@@ -3637,28 +4047,6 @@ Route::set_active (bool yn, void* src)
        }
 }
 
-void
-Route::meter ()
-{
-       Glib::Threads::RWLock::ReaderLock rm (_processor_lock, Glib::Threads::TRY_LOCK);
-
-       assert (_meter);
-
-       _meter->meter ();
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-               boost::shared_ptr<Send> s;
-               boost::shared_ptr<Return> r;
-
-               if ((s = boost::dynamic_pointer_cast<Send> (*i)) != 0) {
-                       s->meter()->meter();
-               } else if ((r = boost::dynamic_pointer_cast<Return> (*i)) != 0) {
-                       r->meter()->meter ();
-               }
-       }
-}
-
 boost::shared_ptr<Pannable>
 Route::pannable() const
 {
@@ -3695,7 +4083,7 @@ Route::get_control (const Evoral::Parameter& param)
 
                /* maybe one of our processors does or ... */
 
-               Glib::Threads::RWLock::ReaderLock rm (_processor_lock, Glib::Threads::TRY_LOCK);
+               Glib::Threads::RWLock::ReaderLock rm (_processor_lock);
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        if ((c = boost::dynamic_pointer_cast<AutomationControl>((*i)->control (param))) != 0) {
                                break;
@@ -3778,9 +4166,9 @@ Route::set_processor_positions ()
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        bool had_amp = false;
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_pre_fader (!had_amp);
-               if (boost::dynamic_pointer_cast<Amp> (*i)) {
+               if (*i == _amp) {
                        had_amp = true;
                }
        }
@@ -3941,6 +4329,9 @@ Route::set_public_port_latencies (framecnt_t value, bool playback) const
 /** Put the invisible processors in the right place in _processors.
  *  Must be called with a writer lock on _processor_lock held.
  */
+#ifdef __clang__
+__attribute__((annotate("realtime")))
+#endif
 void
 Route::setup_invisible_processors ()
 {
@@ -3954,7 +4345,10 @@ Route::setup_invisible_processors ()
                return;
        }
 
-       /* we'll build this new list here and then use it */
+       /* we'll build this new list here and then use it
+        *
+        * TODO put the ProcessorList is on the stack for RT-safety. 
+        */
 
        ProcessorList new_processors;
 
@@ -3969,7 +4363,7 @@ Route::setup_invisible_processors ()
        /* find the amp */
 
        ProcessorList::iterator amp = new_processors.begin ();
-       while (amp != new_processors.end() && boost::dynamic_pointer_cast<Amp> (*amp) == 0) {
+       while (amp != new_processors.end() && *amp != _amp) {
                ++amp;
        }
 
@@ -4065,6 +4459,12 @@ Route::setup_invisible_processors ()
                }
        }
 
+#if 0 // not used - just yet
+       if (!is_master() && !is_monitor() && !is_auditioner()) {
+               new_processors.push_front (_delayline);
+       }
+#endif
+
        /* MONITOR CONTROL */
 
        if (_monitor_control && is_monitor ()) {
@@ -4083,6 +4483,10 @@ Route::setup_invisible_processors ()
                new_processors.push_front (_intreturn);
        }
 
+       if (_trim && _trim->active()) {
+               assert (!_trim->display_to_user ());
+               new_processors.push_front (_trim);
+       }
        /* EXPORT PROCESSOR */
 
        if (_capturing_processor) {
@@ -4129,18 +4533,32 @@ Route::maybe_note_meter_position ()
        }
        
        _custom_meter_position_noted = true;
+       /* custom meter points range from after trim to before panner/main_outs
+        * this is a limitation by the current processor UI
+        */
+       bool seen_trim = false;
+       _processor_after_last_custom_meter.reset();
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i) == _trim) {
+                       seen_trim = true;
+               }
+               if ((*i) == _main_outs) {
+                       _processor_after_last_custom_meter = *i;
+                       break;
+               }
                if (boost::dynamic_pointer_cast<PeakMeter> (*i)) {
-                       ProcessorList::iterator j = i;
-                       ++j;
-                       if (j != _processors.end ()) {
-                               _processor_after_last_custom_meter = *j;
-                               _last_custom_meter_was_at_end = false;
+                       if (!seen_trim) {
+                               _processor_after_last_custom_meter = _trim;
                        } else {
-                               _last_custom_meter_was_at_end = true;
+                               ProcessorList::iterator j = i;
+                               ++j;
+                               assert(j != _processors.end ()); // main_outs should be before
+                               _processor_after_last_custom_meter = *j;
                        }
+                       break;
                }
        }
+       assert(_processor_after_last_custom_meter.lock());
 }
 
 boost::shared_ptr<Processor>
@@ -4221,6 +4639,10 @@ Route::non_realtime_locate (framepos_t pos)
                _pannable->transport_located (pos);
        }
 
+       if (_delayline.get()) {
+               _delayline.get()->flush();
+       }
+
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);