more solo propagation fixes.
[ardour.git] / libs / ardour / route.cc
index c2a7c62f6d891290b45ae3bfea6e435c4ff60761..5393bcc6820dbf5c03e583421338e51b0aaf4887 100644 (file)
 #endif
 
 #include <cmath>
-#include <fstream>
 #include <cassert>
 #include <algorithm>
 
+#include <glibmm.h>
 #include <boost/algorithm/string.hpp>
 
 #include "pbd/xml++.h"
@@ -37,6 +37,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"
@@ -58,6 +59,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"
@@ -81,16 +83,21 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _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)
+       , _pending_signals (0)
        , _flags (flg)
        , _pending_declick (true)
        , _meter_point (MeterPostFader)
+       , _pending_meter_point (MeterPostFader)
        , _meter_type (MeterPeak)
        , _self_solo (false)
        , _soloed_by_others_upstream (0)
        , _soloed_by_others_downstream (0)
-       , _solo_isolated (0)
+       , _solo_isolated (false)
+       , _solo_isolated_by_upstream (0)
        , _denormal_protection (false)
        , _recordable (true)
        , _silent (false)
@@ -106,17 +113,23 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _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;
-       }
        processor_max_streams.reset();
 }
 
 int
 Route::init ()
 {
+       /* set default meter type */
+       if (is_master()) {
+               _meter_type = Config->get_meter_type_master ();
+       }
+       else if (dynamic_cast<Track*>(this)) {
+               _meter_type = Config->get_meter_type_track ();
+       } else {
+               _meter_type = Config->get_meter_type_bus ();
+       }
+
        /* add standard controls */
 
        _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
@@ -157,6 +170,21 @@ Route::init ()
        _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 ()
        */
@@ -187,8 +215,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);
@@ -231,7 +257,7 @@ Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 void
 Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 {
-       /* force IDs for master/monitor busses and prevent 
+       /* force IDs for master/monitor busses and prevent
           any other route from accidentally getting these IDs
           (i.e. legacy sessions)
        */
@@ -250,7 +276,7 @@ Route::set_remote_control_id_internal (uint32_t id, bool notify_class_listeners)
 
        /* don't allow it to collide */
 
-       if (!is_master () && !is_monitor() && 
+       if (!is_master () && !is_monitor() &&
            (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
                id += MonitorBusRemoteControlID;
        }
@@ -270,7 +296,7 @@ Route::remote_control_id() const
 {
        if (is_master()) {
                return MasterBusRemoteControlID;
-       } 
+       }
 
        if (is_monitor()) {
                return MonitorBusRemoteControlID;
@@ -338,13 +364,12 @@ Route::ensure_track_or_route_name(string name, Session &session)
        string newname = name;
 
        while (!session.io_name_is_legal (newname)) {
-               newname = bump_name_once (newname, '.');
+               newname = bump_name_once (newname, ' ');
        }
 
        return newname;
 }
 
-
 void
 Route::inc_gain (gain_t fraction, void *src)
 {
@@ -406,6 +431,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)
 {
@@ -434,6 +472,8 @@ Route::process_output_buffers (BufferSet& bufs,
 
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
        if (!lm.locked()) {
+               // can this actually happen? functions calling process_output_buffers()
+               // already take a reader-lock.
                bufs.silence (nframes, 0);
                return;
        }
@@ -445,19 +485,25 @@ Route::process_output_buffers (BufferSet& bufs,
                                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;
+          change in the _main_outs delivery, if config.get_use_monitor_fades()
+          is true.
 
-       //but we override this in the case where we have an internal generator
-       if ( _have_internal_generator )
-               silence = false;
+          We override this in the case where we have an internal generator.
+       */
+       bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
 
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
@@ -529,7 +575,7 @@ Route::process_output_buffers (BufferSet& bufs,
 
        framecnt_t latency = 0;
 
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+       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 */
@@ -581,6 +627,10 @@ Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
        _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) {
 
@@ -728,11 +778,12 @@ Route::set_listen (bool yn, void* src)
                if (yn != _monitor_send->active()) {
                        if (yn) {
                                _monitor_send->activate ();
-                               _mute_master->set_soloed (true);
+                               _mute_master->set_soloed_by_self (true);
                        } else {
                                _monitor_send->deactivate ();
-                               _mute_master->set_soloed (false);
+                               _mute_master->set_soloed_by_self (false);
                        }
+                       _mute_master->set_soloed_by_others (false);
 
                        listen_changed (src); /* EMIT SIGNAL */
                }
@@ -782,7 +833,7 @@ Route::set_solo (bool yn, void *src)
                return;
        }
 
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n", 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, src: %3 grp ? %4 currently self-soloed ? %5\n",
                                                  name(), yn, src, (src == _route_group), self_soloed()));
 
        if (self_soloed() != yn) {
@@ -791,6 +842,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
@@ -803,12 +862,7 @@ Route::set_self_solo (bool yn)
 void
 Route::mod_solo_by_others_upstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-upstream due to solo-safe\n", name()));
-               return;
-       }
-
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
        uint32_t old_sbu = _soloed_by_others_upstream;
@@ -846,8 +900,11 @@ Route::mod_solo_by_others_upstream (int32_t delta)
             (old_sbu > 0 && _soloed_by_others_upstream == 0))) {
 
                if (delta > 0 || !Config->get_exclusive_solo()) {
-                       DEBUG_TRACE (DEBUG::Solo, "\t ... INVERT push\n");
+                       DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name));
                        for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) {
+                               if (i->sends_only) {
+                                       continue;
+                               }
                                boost::shared_ptr<Route> sr = i->r.lock();
                                if (sr) {
                                        sr->mod_solo_by_others_downstream (-delta);
@@ -863,12 +920,7 @@ Route::mod_solo_by_others_upstream (int32_t delta)
 void
 Route::mod_solo_by_others_downstream (int32_t delta)
 {
-       if (_solo_safe) {
-               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo-by-downstream due to solo safe\n", name()));
-               return;
-       }
-
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n",
                                                  name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream));
 
        if (delta < 0) {
@@ -890,7 +942,30 @@ Route::mod_solo_by_others_downstream (int32_t delta)
 void
 Route::set_mute_master_solo ()
 {
-       _mute_master->set_soloed (self_soloed() || soloed_by_others_downstream() || soloed_by_others_upstream());
+       _mute_master->set_soloed_by_self (self_soloed());
+       _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream());
+}
+
+void
+Route::mod_solo_isolated_by_upstream (bool yn, void* src)
+{
+       bool old = solo_isolated ();
+
+       if (!yn) {
+               if (_solo_isolated_by_upstream >= 1) {
+                       _solo_isolated_by_upstream--;
+               } else {
+                       _solo_isolated_by_upstream = 0;
+               }
+       } else {
+               _solo_isolated_by_upstream++;
+       }
+
+       if (solo_isolated() != old) {
+               /* solo isolated status changed */
+               _mute_master->set_solo_ignore (yn);
+               solo_isolated_changed (src);
+       }
 }
 
 void
@@ -905,6 +980,27 @@ Route::set_solo_isolated (bool yn, void *src)
                return;
        }
 
+       bool changed = false;
+
+       if (yn) {
+               if (_solo_isolated == false) {
+                       _mute_master->set_solo_ignore (true);
+                       changed = true;
+               }
+               _solo_isolated = true;
+       } else {
+               if (_solo_isolated == true) {
+                       _solo_isolated = false;
+            _mute_master->set_solo_ignore (false);
+                       changed = true;
+               }
+       }
+
+
+       if (!changed) {
+               return;
+       }
+
        /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */
 
        boost::shared_ptr<RouteList> routes = _session.get_routes ();
@@ -915,42 +1011,22 @@ Route::set_solo_isolated (bool yn, void *src)
                }
 
                bool sends_only;
-               bool does_feed = direct_feeds_according_to_graph (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
+               bool does_feed = feeds (*i, &sends_only);
 
                if (does_feed && !sends_only) {
-                       (*i)->set_solo_isolated (yn, (*i)->route_group());
+                       (*i)->mod_solo_isolated_by_upstream (yn, src);
                }
        }
 
        /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */
 
-       bool changed = false;
-
-       if (yn) {
-               if (_solo_isolated == 0) {
-                       _mute_master->set_solo_ignore (true);
-                       changed = true;
-               }
-               _solo_isolated++;
-       } else {
-               if (_solo_isolated > 0) {
-                       _solo_isolated--;
-                       if (_solo_isolated == 0) {
-                               _mute_master->set_solo_ignore (false);
-                               changed = true;
-                       }
-               }
-       }
-
-       if (changed) {
-               solo_isolated_changed (src);
-       }
+       solo_isolated_changed (src);
 }
 
 bool
 Route::solo_isolated () const
 {
-       return _solo_isolated > 0;
+       return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0);
 }
 
 void
@@ -991,6 +1067,21 @@ Route::muted () const
        return _mute_master->muted_by_self();
 }
 
+bool
+Route::muted_by_others () const
+{
+       // This method is only used by route_ui for display state.
+       // The real thing is MuteMaster::muted_by_others_at()
+
+       //master is never muted by others
+       if (is_master())
+               return false;
+
+       //now check to see if something is soloed (and I am not)
+       //see also MuteMaster::mute_gain_at()
+       return (_session.soloing() && !soloed() && !solo_isolated());
+}
+
 #if 0
 static void
 dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& procs)
@@ -1013,7 +1104,7 @@ Route::before_processor_for_placement (Placement p)
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
 
        ProcessorList::iterator loc;
-       
+
        if (p == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
                loc = find (_processors.begin(), _processors.end(), _amp);
@@ -1036,14 +1127,14 @@ Route::before_processor_for_index (int index)
        }
 
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-       
+
        ProcessorList::iterator i = _processors.begin ();
        int j = 0;
        while (i != _processors.end() && j < index) {
                if ((*i)->display_to_user()) {
                        ++j;
                }
-               
+
                ++i;
        }
 
@@ -1146,7 +1237,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<
 
                }
 
-               if (activation_allowed && !_session.get_disable_all_loaded_plugins()) {
+               if (activation_allowed && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user ())) {
                        processor->activate ();
                }
 
@@ -1199,7 +1290,11 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
                                                prop->value() == "lxvst" ||
                                                prop->value() == "audiounit") {
 
-                                       processor.reset (new PluginInsert (_session));
+                                       if (_session.get_disable_all_loaded_plugins ()) {
+                                               processor.reset (new UnknownProcessor (_session, node));
+                                       } else {
+                                               processor.reset (new PluginInsert (_session));
+                                       }
 
                                } else {
 
@@ -1222,11 +1317,11 @@ 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() )
+                               if ( string_is_affirmative (prop->value()) && (!_session.get_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
                                        processor->activate();
                                else
                                        processor->deactivate();
@@ -1480,7 +1575,7 @@ Route::clear_processors (Placement p)
                                seen_amp = true;
                        }
 
-                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline) {
+                       if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline || (*i) == _trim) {
 
                                /* you can't remove these */
 
@@ -1547,7 +1642,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
        /* these can never be removed */
 
-       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
+       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline || processor == _trim) {
                return 0;
        }
 
@@ -1605,7 +1700,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (!removed) {
                        /* what? */
                        return 1;
-               } 
+               }
 
                if (configure_processors_unlocked (err)) {
                        pstate.restore ();
@@ -1664,7 +1759,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                        /* these can never be removed */
 
-                       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
+                       if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline || processor == _trim) {
                                ++i;
                                continue;
                        }
@@ -1785,12 +1880,6 @@ Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
 
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
-               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
-                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
-                       DEBUG_TRACE (DEBUG::Processors, "}\n");
-                       return list<pair<ChanCount, ChanCount> > ();
-               }
-
                if ((*p)->can_support_io_configuration(in, out)) {
                        DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
@@ -1847,13 +1936,19 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
 
-               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
-                       break;
+               if (!(*p)->configure_io(c->first, c->second)) {
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration failed\n", _name));
                }
-
-               (*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)
@@ -1871,11 +1966,11 @@ 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
-          configuration 
+          configuration
        */
        _session.ensure_buffers (n_process_buffers ());
 
@@ -1896,12 +1991,12 @@ Route::all_visible_processors_active (bool state)
        if (_processors.empty()) {
                return;
        }
-       
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (!(*i)->display_to_user() || boost::dynamic_pointer_cast<Amp> (*i)) {
                        continue;
                }
-               
+
                if (state) {
                        (*i)->activate ();
                } else {
@@ -1912,86 +2007,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;
 }
@@ -2073,7 +2257,7 @@ Route::state(bool full_state)
 
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                if (!full_state) {
-                       /* template save: do not include internal sends functioning as 
+                       /* template save: do not include internal sends functioning as
                           aux sends because the chance of the target ID
                           in the session where this template is used
                           is not very likely.
@@ -2103,8 +2287,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;
@@ -2297,10 +2479,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;
 
@@ -2634,6 +2812,9 @@ 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);
@@ -2687,8 +2868,11 @@ Route::set_processor_state (const XMLNode& node)
                                           prop->value() == "lxvst" ||
                                           prop->value() == "audiounit") {
 
-                                       processor.reset (new PluginInsert(_session));
-
+                                       if (_session.get_disable_all_loaded_plugins ()) {
+                                               processor.reset (new UnknownProcessor (_session, **niter));
+                                       } else {
+                                               processor.reset (new PluginInsert (_session));
+                                       }
                                } else if (prop->value() == "port") {
 
                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
@@ -2751,7 +2935,7 @@ Route::set_processor_state (const XMLNode& node)
        }
 
        reset_instrument_info ();
-       processors_changed (RouteProcessorChange ());
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 }
 
@@ -2917,19 +3101,26 @@ Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
 
          again:
                for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
-                       
+
                        boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-                       
+
                        if (d && d->target_route() == route) {
                                rl.release ();
-                               remove_processor (*x, &err, false);
+                               if (remove_processor (*x, &err, false) > 0) {
+                                       rl.acquire ();
+                                       continue;
+                               }
                                rl.acquire ();
 
                                /* list could have been demolished while we dropped the lock
                                   so start over.
                                */
-                               
-                               goto again;
+                               if (_session.engine().connected()) {
+                                       /* i/o processors cannot be removed if the engine is not running
+                                        * so don't live-loop in case the engine is N/A or dies
+                                        */
+                                       goto again;
+                               }
                        }
                }
        }
@@ -3064,8 +3255,8 @@ Route::input_change_handler (IOChange change, void * /*src*/)
        bool need_to_queue_solo_change = true;
 
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
                need_to_queue_solo_change = false;
                configure_processors (0);
@@ -3079,6 +3270,31 @@ Route::input_change_handler (IOChange change, void * /*src*/)
                } else {
                        cancel_solo_after_disconnect (true);
                }
+#if 1
+       } else if (_soloed_by_others_upstream) {
+               bool cancel_solo = true;
+               boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                       if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                               continue;
+                       }
+                       bool sends_only;
+                       bool does_feed = (*i)->direct_feeds_according_to_reality (shared_from_this(), &sends_only);
+                       if (does_feed && !sends_only) {
+                               if ((*i)->soloed()) {
+                                       cancel_solo = false;
+                                       break;
+                               }
+                       }
+               }
+               if (cancel_solo) {
+                       cancel_solo_after_disconnect (true);
+               }
+#else
+       } else if (self_soloed()) {
+#endif
+               // TODO propagate upstream
+               // see commment in output_change_handler() below
        }
 }
 
@@ -3091,8 +3307,8 @@ Route::output_change_handler (IOChange change, void * /*src*/)
        }
 
        if ((change.type & IOChange::ConfigurationChanged)) {
-               /* This is called with the process lock held if change 
-                  contains ConfigurationChanged 
+               /* This is called with the process lock held if change
+                  contains ConfigurationChanged
                */
                need_to_queue_solo_change = false;
                configure_processors (0);
@@ -3110,6 +3326,37 @@ Route::output_change_handler (IOChange change, void * /*src*/)
                } else {
                        cancel_solo_after_disconnect (false);
                }
+#if 1
+       } else if (_soloed_by_others_downstream) {
+               bool cancel_solo = true;
+               /* checking all all downstream routes for
+                * explicit of implict solo is a rather drastic measure,
+                * ideally the input_change_handler() of the other route
+                * would propagate the change to us.
+                */
+               boost::shared_ptr<RouteList> routes = _session.get_routes ();
+               for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                       if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                               continue;
+                       }
+                       bool sends_only;
+                       bool does_feed = direct_feeds_according_to_reality (*i, &sends_only);
+                       if (does_feed && !sends_only) {
+                               if ((*i)->soloed()) {
+                                       cancel_solo = false;
+                                       break;
+                               }
+                       }
+               }
+               if (cancel_solo) {
+                       cancel_solo_after_disconnect (false);
+               }
+#else
+       } else if (self_soloed()) {
+               // TODO propagate change downstream to the disconnected routes
+               // Q: how to get the routes that were just disconnected. ?
+               // A: /maybe/ by diff feeds() aka fed_by() vs direct_feeds_according_to_reality() ?!?
+#endif
        }
 }
 
@@ -3177,6 +3424,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;
@@ -3241,78 +3489,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);
-
-                       /* 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);
-                               }
+               setup_invisible_processors ();
+
+       } 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
@@ -3359,6 +3693,8 @@ 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 ()) {
@@ -3367,14 +3703,23 @@ Route::update_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 */
@@ -3577,6 +3922,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());
@@ -3671,7 +4028,7 @@ Route::set_name_in_state (XMLNode& node, string const & name)
 
        XMLNodeList children = node.children();
        for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
-               
+
                if ((*i)->name() == X_("IO")) {
 
                        IO::set_name_in_state (**i, name);
@@ -3682,12 +4039,12 @@ Route::set_name_in_state (XMLNode& node, string const & name)
                        if (role && role->value() == X_("Main")) {
                                (*i)->add_property (X_("name"), name);
                        }
-                       
+
                } else if ((*i)->name() == X_("Diskstream")) {
 
                        (*i)->add_property (X_("playlist"), string_compose ("%1.1", name).c_str());
                        (*i)->add_property (X_("name"), name);
-                       
+
                }
        }
 }
@@ -3781,28 +4138,6 @@ Route::set_active (bool yn, void* src)
        }
 }
 
-void
-Route::meter ()
-{
-       Glib::Threads::RWLock::ReaderLock rm (_processor_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
 {
@@ -3922,9 +4257,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;
                }
        }
@@ -3964,6 +4299,11 @@ Route::unknown_processors () const
 {
        list<string> p;
 
+       if (_session.get_disable_all_loaded_plugins ()) {
+               // Do not list "missing plugins" if they are explicitly disabled
+               return p;
+       }
+
        Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
        for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
@@ -3990,17 +4330,17 @@ Route::update_port_latencies (PortSet& from, PortSet& to, bool playback, framecn
        } else {
                all_connections.min = ~((pframes_t) 0);
                all_connections.max = 0;
-               
+
                /* iterate over all "from" ports and determine the latency range for all of their
                   connections to the "outside" (outside of this Route).
                */
-               
+
                for (PortSet::iterator p = from.begin(); p != from.end(); ++p) {
-                       
+
                        LatencyRange range;
-                       
+
                        p->get_connected_latency_range (range, playback);
-                       
+
                        all_connections.min = min (all_connections.min, range.min);
                        all_connections.max = max (all_connections.max, range.max);
                }
@@ -4085,6 +4425,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 ()
 {
@@ -4098,7 +4441,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;
 
@@ -4113,7 +4459,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;
        }
 
@@ -4233,6 +4579,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) {
@@ -4242,6 +4592,12 @@ Route::setup_invisible_processors ()
 
        _processors = new_processors;
 
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (!(*i)->display_to_user () && !(*i)->active () && (*i) != _monitor_send) {
+                       (*i)->activate ();
+               }
+       }
+
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: setup_invisible_processors\n", _name));
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1\n", (*i)->name ()));
@@ -4277,20 +4633,34 @@ Route::maybe_note_meter_position ()
        if (_meter_point != MeterCustom) {
                return;
        }
-       
+
        _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>
@@ -4332,7 +4702,7 @@ Route::has_external_redirects () const
                /* ignore inactive processors and obviously ignore the main
                 * outs since everything has them and we don't care.
                 */
-                
+
                if ((*i)->active() && (*i) != _main_outs && (*i)->does_routing()) {
                        return true;;
                }
@@ -4378,11 +4748,12 @@ Route::non_realtime_locate (framepos_t pos)
        {
                //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
                Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
-               
+
                for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                        (*i)->transport_located (pos);
                }
        }
+       _roll_delay = _initial_delay;
 }
 
 void
@@ -4390,9 +4761,9 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 {
        size_t n_buffers;
        size_t i;
-       
-       /* MIDI 
-        *  
+
+       /* MIDI
+        *
         * We don't currently mix MIDI input together, so we don't need the
         * complex logic of the audio case.
         */
@@ -4403,7 +4774,7 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
 
                boost::shared_ptr<MidiPort> source_port = io->midi (i);
                MidiBuffer& buf (bufs.get_midi (i));
-               
+
                if (source_port) {
                        buf.copy (source_port->get_midi_buffer(nframes));
                } else {
@@ -4421,19 +4792,19 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
        if (n_ports > n_buffers) {
                scaling = ((float) n_buffers) / n_ports;
        }
-       
+
        for (i = 0; i < n_ports; ++i) {
-               
+
                /* if there are more ports than buffers, map them onto buffers
                 * in a round-robin fashion
                 */
 
                boost::shared_ptr<AudioPort> source_port = io->audio (i);
                AudioBuffer& buf (bufs.get_audio (i%n_buffers));
-                       
+
 
                if (i < n_buffers) {
-                       
+
                        /* first time through just copy a channel into
                           the output buffer.
                        */
@@ -4443,11 +4814,11 @@ Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pfram
                        if (scaling != 1.0f) {
                                buf.apply_gain (scaling, nframes);
                        }
-                       
+
                } else {
-                       
+
                        /* on subsequent times around, merge data from
-                        * the port with what is already there 
+                        * the port with what is already there
                         */
 
                        if (scaling != 1.0f) {