professionalize peak-meters
[ardour.git] / libs / ardour / route.cc
index 3d9785b2fbfcb97e98220fe6428a0fe8fdbeb2d3..4b4e450c34bacd2053a24b45e69893db85edd774 100644 (file)
@@ -26,6 +26,7 @@
 #include <cassert>
 #include <algorithm>
 
+#include <glibmm.h>
 #include <boost/algorithm/string.hpp>
 
 #include "pbd/xml++.h"
@@ -82,11 +83,14 @@ 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)
        , _flags (flg)
        , _pending_declick (true)
        , _meter_point (MeterPostFader)
+       , _pending_meter_point (MeterPostFader)
        , _meter_type (MeterPeak)
        , _self_solo (false)
        , _soloed_by_others_upstream (0)
@@ -107,7 +111,6 @@ 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;
@@ -168,7 +171,7 @@ Route::init ()
                 */
                _trim->activate();
        }
-       else if (!dynamic_cast<Track*>(this) && ! (is_master() || is_monitor() || is_auditioner())) {
+       else if (!dynamic_cast<Track*>(this) && ! ( is_monitor() || is_auditioner() )) {
                /* regular bus */
                _trim->activate();
        }
@@ -203,8 +206,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);
@@ -360,7 +361,6 @@ Route::ensure_track_or_route_name(string name, Session &session)
        return newname;
 }
 
-
 void
 Route::inc_gain (gain_t fraction, void *src)
 {
@@ -463,6 +463,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;
        }
@@ -474,8 +476,15 @@ 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
@@ -558,7 +567,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 */
@@ -610,6 +619,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) {
 
@@ -1509,7 +1522,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 */
 
@@ -1576,7 +1589,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;
        }
 
@@ -1900,7 +1913,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
@@ -1941,86 +1954,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;
 }
@@ -2132,8 +2234,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;
@@ -2326,10 +2426,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;
 
@@ -2783,7 +2879,7 @@ Route::set_processor_state (const XMLNode& node)
        }
 
        reset_instrument_info ();
-       processors_changed (RouteProcessorChange ());
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
        set_processor_positions ();
 }
 
@@ -3209,6 +3305,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;
@@ -3273,78 +3370,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);
+               _meter->set_display_to_user (false);
 
-                       /* 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
@@ -3391,6 +3574,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 ()) {
@@ -3399,14 +3584,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 */
@@ -3609,6 +3803,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());
@@ -3813,28 +4019,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
 {
@@ -3954,7 +4138,7 @@ 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)) {
                        had_amp = true;
@@ -4117,6 +4301,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 ()
 {
@@ -4130,7 +4317,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;
 
@@ -4315,18 +4505,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>