logic rearrangement to avoid unnecessary call to Route::monitoring_state() in instrum...
[ardour.git] / libs / ardour / route.cc
index e6ab6fb6fc544e20e3252d2c235454089c3b9af0..29aa5b8a6ab9a5cd6dfbba55b8961e93d0cbc588 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"
@@ -58,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"
@@ -81,16 +84,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 +114,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 +171,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 +216,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);
@@ -344,7 +371,6 @@ Route::ensure_track_or_route_name(string name, Session &session)
        return newname;
 }
 
-
 void
 Route::inc_gain (gain_t fraction, void *src)
 {
@@ -406,6 +432,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)
 {
@@ -433,7 +472,12 @@ 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) {
@@ -442,20 +486,26 @@ 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.
+          change in the _main_outs delivery, if config.get_use_monitor_fades()
+          is true.
+          
+          We override this in the case where we have an internal generator.
        */
-       bool silence = monitoring_state () == MonitoringSilence;
-
-       //but we override this in the case where we have an internal generator
-       if ( _have_internal_generator )
-               silence = false;
-
+       bool silence = _have_internal_generator ? false : (monitoring_state () == MonitoringSilence);
+       
        _main_outs->no_outs_cuz_we_no_monitor (silence);
 
        /* -------------------------------------------------------------------------------------------
@@ -526,7 +576,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 */
@@ -578,6 +628,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) {
 
@@ -769,6 +823,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;
@@ -783,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
@@ -885,6 +952,28 @@ Route::set_mute_master_solo ()
        _mute_master->set_soloed (self_soloed() || 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
 Route::set_solo_isolated (bool yn, void *src)
 {
@@ -897,52 +986,53 @@ 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 ();
        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_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
@@ -983,6 +1073,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)
@@ -1138,7 +1239,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 ();
                }
 
@@ -1191,7 +1292,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 {
 
@@ -1214,8 +1319,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_bypass_all_loaded_plugins () || !processor->display_to_user () ) )
+                                       processor->activate();
+                               else
+                                       processor->deactivate();
+                       }
+               }
 
-               return (add_processor (processor, placement) == 0);
+               return (add_processor (processor, placement, 0, false) == 0);
        }
 
        catch (failed_constructor &err) {
@@ -1462,7 +1577,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 */
 
@@ -1529,7 +1644,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;
        }
 
@@ -1646,7 +1761,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;
                        }
@@ -1767,12 +1882,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));
@@ -1829,13 +1938,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)
@@ -1853,7 +1968,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
@@ -1894,86 +2009,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;
 }
@@ -2085,8 +2289,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;
@@ -2279,10 +2481,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;
 
@@ -2321,6 +2519,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;
@@ -2615,6 +2814,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);
@@ -2668,8 +2870,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));
@@ -2732,7 +2937,7 @@ Route::set_processor_state (const XMLNode& node)
        }
 
        reset_instrument_info ();
-       processors_changed (RouteProcessorChange ());
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 }
 
@@ -2903,14 +3108,21 @@ Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
                        
                        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;
+                               }
                        }
                }
        }
@@ -3077,6 +3289,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 */
        }
 
@@ -3153,6 +3370,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;
@@ -3217,78 +3435,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
 
-               if (_meter_point != MeterCustom) {
+       _meter_point = _pending_meter_point;
 
-                       _meter->set_display_to_user (false);
+       bool meter_was_visible_to_user = _meter->display_to_user ();
 
-                       setup_invisible_processors ();
+       if (!_custom_meter_position_noted) {
+               maybe_note_meter_position ();
+       }
 
-               } else {
+       if (_meter_point != MeterCustom) {
 
-                       _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);
-                               }
+               _meter->set_display_to_user (false);
+
+               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
@@ -3335,6 +3639,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 ()) {
@@ -3343,14 +3649,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 */
@@ -3406,7 +3721,7 @@ Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<R
 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);
 
@@ -3433,9 +3748,9 @@ 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;
        }
 }
 
@@ -3453,37 +3768,51 @@ Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<R
 }
 
 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;
        }
 
-       /* I don't know why this apparently "should" be done via the RT event
-          system, but doing so causes a ton of annoying errors... */
-       // rl->push_back (r);
-       // _session.set_mute (rl, bval);
-
-       /* ... but this seems to work. */
-       r->set_mute (bval, this);
+       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);
+       }
 
-       AutomationControl::set_value(bval);
+       // 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
@@ -3539,6 +3868,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());
@@ -3743,28 +4084,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
 {
@@ -3884,9 +4203,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;
                }
        }
@@ -3926,6 +4245,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)) {
@@ -4047,6 +4371,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 ()
 {
@@ -4060,7 +4387,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;
 
@@ -4075,7 +4405,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;
        }
 
@@ -4195,6 +4525,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) {
@@ -4204,6 +4538,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)->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 ()));
@@ -4241,18 +4581,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>