try not thinning when loading old-school automation lists
[ardour.git] / libs / ardour / route.cc
index a7fb05ec15340bc5b763777fc8c63730d5fd3817..1948ba14aa63aa265f1f4e1d7cfaf1e403137fc6 100644 (file)
 #include "pbd/convert.h"
 #include "pbd/boost_debug.h"
 
-#include "evoral/Curve.hpp"
-
 #include "ardour/amp.h"
-#include "ardour/audio_port.h"
+#include "ardour/audio_buffer.h"
 #include "ardour/audioengine.h"
 #include "ardour/buffer.h"
 #include "ardour/buffer_set.h"
-#include "ardour/configuration.h"
-#include "ardour/cycle_timer.h"
+#include "ardour/capturing_processor.h"
 #include "ardour/debug.h"
 #include "ardour/delivery.h"
-#include "ardour/dB.h"
-#include "ardour/internal_send.h"
 #include "ardour/internal_return.h"
-#include "ardour/ladspa_plugin.h"
+#include "ardour/internal_send.h"
 #include "ardour/meter.h"
-#include "ardour/mix.h"
 #include "ardour/monitor_processor.h"
 #include "ardour/pannable.h"
-#include "ardour/panner.h"
 #include "ardour/panner_shell.h"
 #include "ardour/plugin_insert.h"
 #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"
 #include "ardour/session.h"
-#include "ardour/timestamps.h"
-#include "ardour/utils.h"
-#include "ardour/graph.h"
 #include "ardour/unknown_processor.h"
-#include "ardour/capturing_processor.h"
+#include "ardour/utils.h"
 
 #include "i18n.h"
 
@@ -82,7 +71,7 @@ PBD::Signal0<void> Route::RemoteControlIDChange;
 Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        : SessionObject (sess, name)
        , Automatable (sess)
-       , GraphNode( sess.route_graph )
+       , GraphNode (sess._process_graph)
        , _active (true)
        , _signal_latency (0)
        , _initial_delay (0)
@@ -104,9 +93,18 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
        , _default_type (default_type)
        , _remote_control_id (0)
        , _in_configure_processors (false)
+       , _custom_meter_position_noted (false)
+       , _last_custom_meter_was_at_end (false)
 {
        processor_max_streams.reset();
        order_keys[N_("signal")] = order_key_cnt++;
+
+       if (is_master()) {
+               set_remote_control_id (MasterBusRemoteControlID);
+       } else if (is_monitor()) {
+               set_remote_control_id (MonitorBusRemoteControlID);
+       }
+               
 }
 
 int
@@ -126,11 +124,7 @@ Route::init ()
        /* panning */
 
        if (!(_flags & Route::MonitorOut)) {
-               Pannable* p = new Pannable (_session);
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-               boost_debug_shared_ptr_mark_interesting (p, "Pannable");
-#endif
-               _pannable.reset (p);
+               _pannable.reset (new Pannable (_session));
        }
 
        /* input and output objects */
@@ -141,6 +135,8 @@ Route::init ()
        _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
        _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
 
+       _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
+
        /* add amp processor  */
 
        _amp.reset (new Amp (_session));
@@ -210,6 +206,26 @@ Route::~Route ()
 void
 Route::set_remote_control_id (uint32_t id, bool notify_class_listeners)
 {
+       /* force IDs for master/monitor busses and prevent 
+          any other route from accidentally getting these IDs
+          (i.e. legacy sessions)
+       */
+
+       if (is_master() && id != MasterBusRemoteControlID) {
+               id = MasterBusRemoteControlID;
+       }
+
+       if (is_monitor() && id != MonitorBusRemoteControlID) {
+               id = MonitorBusRemoteControlID;
+       }
+
+       /* don't allow it to collide */
+
+       if (!is_master () && !is_monitor() && 
+           (id == MasterBusRemoteControlID || id == MonitorBusRemoteControlID)) {
+               id += MonitorBusRemoteControlID;
+       }
+
        if (id != _remote_control_id) {
                _remote_control_id = id;
                RemoteControlIDChanged ();
@@ -401,12 +417,9 @@ Route::maybe_declick (BufferSet&, framecnt_t, int)
 void
 Route::process_output_buffers (BufferSet& bufs,
                               framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
-                              bool /*with_processors*/, int declick,
-                               bool gain_automation_ok)
+                              int declick, bool gain_automation_ok)
 {
-       bool monitor = should_monitor ();
-
-       bufs.is_silent (false);
+       bufs.set_is_silent (false);
 
        /* figure out if we're going to use gain automation */
        if (gain_automation_ok) {
@@ -415,8 +428,11 @@ Route::process_output_buffers (BufferSet& bufs,
                _amp->apply_gain_automation (false);
        }
 
-       /* tell main outs what to do about monitoring */
-       _main_outs->no_outs_cuz_we_no_monitor (!monitor);
+       /* 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.
+       */
+       _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence);
 
 
        /* -------------------------------------------------------------------------------------------
@@ -482,23 +498,31 @@ Route::process_output_buffers (BufferSet& bufs,
           and go ....
           ----------------------------------------------------------------------------------------- */
 
+       /* set this to be true if the meter will already have been ::run() earlier */
+       bool const meter_already_run = metering_state() == MeteringInput;
+
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
-               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
-                       break;
+               if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
+                       /* don't ::run() the meter, otherwise it will have its previous peak corrupted */
+                       continue;
                }
 
 #ifndef NDEBUG
                /* if it has any inputs, make sure they match */
-               if ((*i)->input_streams() != ChanCount::ZERO) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*i) == 0 && (*i)->input_streams() != ChanCount::ZERO) {
                        if (bufs.count() != (*i)->input_streams()) {
-                               cerr << _name << " bufs = " << bufs.count()
-                                    << " input for " << (*i)->name() << " = " << (*i)->input_streams()
-                                    << endl;
-                               abort ();
+                               DEBUG_TRACE (
+                                       DEBUG::Processors, string_compose (
+                                               "%1 bufs = %2 input for %3 = %4\n",
+                                               _name, bufs.count(), (*i)->name(), (*i)->input_streams()
+                                               )
+                                       );
+                               continue;
                        }
                }
 #endif
+
                /* should we NOT run plugins here if the route is inactive?
                   do we catch route != active somewhere higher?
                */
@@ -551,16 +575,17 @@ Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes
        }
 
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, declick, true);
 }
 
 void
 Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
        BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
+
        bufs.set_count (_input->n_ports());
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, declick, false);
 }
 
 void
@@ -748,7 +773,7 @@ Route::set_solo_isolated (bool yn, void *src)
                }
 
                bool sends_only;
-               bool does_feed = direct_feeds (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
+               bool does_feed = direct_feeds_according_to_graph (*i, &sends_only); // we will recurse anyway, so don't use ::feeds()
 
                if (does_feed && !sends_only) {
                        (*i)->set_solo_isolated (yn, (*i)->route_group());
@@ -808,6 +833,11 @@ Route::set_mute (bool yn, void *src)
 
        if (muted() != yn) {
                _mute_master->set_muted_by_self (yn);
+               /* allow any derived classes to respond to the mute change
+                  before anybody else knows about it.
+               */
+               act_on_mute ();
+               /* tell everyone else */
                mute_changed (src); /* EMIT SIGNAL */
                _mute_control->Changed (); /* EMIT SIGNAL */
        }
@@ -832,16 +862,17 @@ dump_processors(const string& name, const list<boost::shared_ptr<Processor> >& p
 }
 #endif
 
-int
-Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
+/** Supposing that we want to insert a Processor at a given Placement, return
+ *  the processor to add the new one before (or 0 to add at the end).
+ */
+boost::shared_ptr<Processor>
+Route::before_processor_for_placement (Placement p)
 {
-       ProcessorList::iterator loc;
-
-       /* XXX this is not thread safe - we don't hold the lock across determining the iter
-          to add before and actually doing the insertion. dammit.
-       */
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
-       if (placement == PreFader) {
+       ProcessorList::iterator loc;
+       
+       if (p == PreFader) {
                /* generic pre-fader: insert immediately before the amp */
                loc = find (_processors.begin(), _processors.end(), _amp);
        } else {
@@ -849,24 +880,20 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
                loc = find (_processors.begin(), _processors.end(), _main_outs);
        }
 
-       return add_processor (processor, loc, err, activation_allowed);
+       return loc != _processors.end() ? *loc : boost::shared_ptr<Processor> ();
 }
 
-
-/** Add a processor to a route such that it ends up with a given index into the visible processors.
- *  @param index Index to add the processor at, or -1 to add at the end of the list.
+/** Supposing that we want to insert a Processor at a given index, return
+ *  the processor to add the new one before (or 0 to add at the end).
  */
-
-int
-Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index, ProcessorStreams* err, bool activation_allowed)
+boost::shared_ptr<Processor>
+Route::before_processor_for_index (int index)
 {
-       /* XXX this is not thread safe - we don't hold the lock across determining the iter
-          to add before and actually doing the insertion. dammit.
-       */
-
        if (index == -1) {
-               return add_processor (processor, _processors.end(), err, activation_allowed);
+               return boost::shared_ptr<Processor> ();
        }
+
+       Glib::RWLock::ReaderLock lm (_processor_lock);
        
        ProcessorList::iterator i = _processors.begin ();
        int j = 0;
@@ -877,15 +904,36 @@ Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index
                
                ++i;
        }
-       
-       return add_processor (processor, i, err, activation_allowed);
+
+       return (i != _processors.end() ? *i : boost::shared_ptr<Processor> ());
+}
+
+/** Add a processor either pre- or post-fader
+ *  @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, Placement placement, ProcessorStreams* err, bool activation_allowed)
+{
+       return add_processor (processor, before_processor_for_placement (placement), err, activation_allowed);
+}
+
+
+/** Add a processor to a route such that it ends up with a given index into the visible processors.
+ *  @param index Index to add the processor at, or -1 to add at the end of the list.
+ *  @return 0 on success, non-0 on failure.
+ */
+int
+Route::add_processor_by_index (boost::shared_ptr<Processor> processor, int index, ProcessorStreams* err, bool activation_allowed)
+{
+       return add_processor (processor, before_processor_for_index (index), err, activation_allowed);
 }
 
 /** Add a processor to the route.
- *  @param iter an iterator in _processors; the new processor will be inserted immediately before this location.
+ *  @param before An existing processor in the list, or 0; the new processor will be inserted immediately before it (or at the end).
+ *  @return 0 on success, non-0 on failure.
  */
 int
-Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
+Route::add_processor (boost::shared_ptr<Processor> processor, boost::shared_ptr<Processor> before, ProcessorStreams* err, bool activation_allowed)
 {
        assert (processor != _meter);
        assert (processor != _main_outs);
@@ -904,25 +952,32 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                boost::shared_ptr<PluginInsert> pi;
                boost::shared_ptr<PortInsert> porti;
 
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
-
                if (processor == _amp) {
-                       // Ensure only one amp is in the list at any time
-                       if (loc != _processors.end()) {
-                               if (iter == loc) { // Already in place, do nothing
+                       /* Ensure that only one amp is in the list at any time */
+                       ProcessorList::iterator check = find (_processors.begin(), _processors.end(), processor);
+                       if (check != _processors.end()) {
+                               if (before == _amp) {
+                                       /* Already in position; all is well */
                                        return 0;
-                               } else { // New position given, relocate
-                                       _processors.erase (loc);
+                               } else {
+                                       _processors.erase (check);
                                }
                        }
+               }
 
-               } else {
-                       if (loc != _processors.end()) {
-                               cerr << "ERROR: Processor added to route twice!" << endl;
+               assert (find (_processors.begin(), _processors.end(), processor) == _processors.end ());
+
+               ProcessorList::iterator loc;
+               if (before) {
+                       /* inserting before a processor; find it */
+                       loc = find (_processors.begin(), _processors.end(), before);
+                       if (loc == _processors.end ()) {
+                               /* Not found */
                                return 1;
                        }
-
-                       loc = iter;
+               } else {
+                       /* inserting at end */
+                       loc = _processors.end ();
                }
 
                _processors.insert (loc, processor);
@@ -1318,7 +1373,7 @@ Route::clear_processors (Placement p)
 }
 
 int
-Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
+Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err, bool need_process_lock)
 {
        /* these can never be removed */
 
@@ -1376,11 +1431,18 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                if (!removed) {
                        /* what? */
                        return 1;
-               }
+               } 
 
-               {
+               if (need_process_lock) {
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
+                       if (configure_processors_unlocked (err)) {
+                               pstate.restore ();
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
+               } else {
                        if (configure_processors_unlocked (err)) {
                                pstate.restore ();
                                /* we know this will work, because it worked before :) */
@@ -1627,34 +1689,11 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
        return 0;
 }
 
-void
-Route::all_processors_flip ()
-{
-       Glib::RWLock::ReaderLock lm (_processor_lock);
-
-       if (_processors.empty()) {
-               return;
-       }
-
-       bool first_is_on = _processors.front()->active();
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-               if (first_is_on) {
-                       (*i)->deactivate ();
-               } else {
-                       (*i)->activate ();
-               }
-       }
-
-       _session.set_dirty ();
-}
-
-/** Set all processors with a given placement to a given active state.
- * @param p Placement of processors to change.
- * @param state New active state for those processors.
+/** Set all visible processors to a given active state (except Fader, whose state is not changed)
+ *  @param state New active state for those processors.
  */
 void
-Route::all_processors_active (Placement p, bool state)
+Route::all_visible_processors_active (bool state)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock);
 
@@ -1662,10 +1701,11 @@ Route::all_processors_active (Placement p, bool state)
                return;
        }
        
-       ProcessorList::iterator start, end;
-       placement_range(p, start, end);
-
-       for (ProcessorList::iterator i = start; i != end; ++i) {
+       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 {
@@ -1676,31 +1716,6 @@ Route::all_processors_active (Placement p, bool state)
        _session.set_dirty ();
 }
 
-bool
-Route::processor_is_prefader (boost::shared_ptr<Processor> p)
-{
-       bool pre_fader = true;
-       Glib::RWLock::ReaderLock lm (_processor_lock);
-
-       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-               /* semantic note: if p == amp, we want to return true, so test
-                  for equality before checking if this is the amp
-               */
-
-               if ((*i) == p) {
-                       break;
-               }
-
-               if ((*i) == _amp) {
-                       pre_fader = false;
-                       break;
-               }
-       }
-
-       return pre_fader;
-}
-
 int
 Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err)
 {
@@ -1769,6 +1784,9 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                _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 ();
+
                {
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
@@ -1871,27 +1889,49 @@ 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 
+                          aux sends because the chance of the target ID
+                          in the session where this template is used
+                          is not very likely.
+
+                          similarly, do not save listen sends which connect to
+                          the monitor section, because these will always be
+                          added if necessary.
+                       */
+                       boost::shared_ptr<InternalSend> is;
+
+                       if ((is = boost::dynamic_pointer_cast<InternalSend> (*i)) != 0) {
+                               if (is->role() == Delivery::Aux || is->role() == Delivery::Listen) {
+                                       continue;
+                               }
+                       }
+               }
                node->add_child_nocopy((*i)->state (full_state));
        }
 
-       if (_extra_xml){
+       if (_extra_xml) {
                node->add_child_copy (*_extra_xml);
        }
 
+       if (_custom_meter_position_noted) {
+               boost::shared_ptr<Processor> after = _processor_after_last_custom_meter.lock ();
+               if (after) {
+                       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;
 }
 
 int
 Route::set_state (const XMLNode& node, int version)
-{
-       return _set_state (node, version);
-}
-
-int
-Route::_set_state (const XMLNode& node, int version)
 {
        if (version < 3000) {
-               return _set_state_2X (node, version);
+               return set_state_2X (node, version);
        }
 
        XMLNodeList nlist;
@@ -1965,6 +2005,14 @@ Route::_set_state (const XMLNode& node, int version)
                }
        }
 
+       if ((prop = node.property (X_("meter-point"))) != 0) {
+               MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
+               set_meter_point (mp, true);
+               if (_meter) {
+                       _meter->set_display_to_user (_meter_point == MeterCustom);
+               }
+       }
+
        set_processor_state (processor_state);
 
        if ((prop = node.property ("self-solo")) != 0) {
@@ -2003,14 +2051,6 @@ Route::_set_state (const XMLNode& node, int version)
                set_active (yn, this);
        }
 
-       if ((prop = node.property (X_("meter-point"))) != 0) {
-               MeterPoint mp = MeterPoint (string_2_enum (prop->value (), _meter_point));
-               set_meter_point (mp, true);
-               if (_meter) {
-                       _meter->set_display_to_user (_meter_point == MeterCustom);
-               }
-       }
-
        if ((prop = node.property (X_("order-keys"))) != 0) {
 
                int32_t n;
@@ -2042,6 +2082,24 @@ Route::_set_state (const XMLNode& node, int version)
                }
        }
 
+       if ((prop = node.property (X_("processor-after-last-custom-meter"))) != 0) {
+               PBD::ID id (prop->value ());
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               ProcessorList::const_iterator i = _processors.begin ();
+               while (i != _processors.end() && (*i)->id() != id) {
+                       ++i;
+               }
+
+               if (i != _processors.end ()) {
+                       _processor_after_last_custom_meter = *i;
+                       _custom_meter_position_noted = true;
+               }
+       }
+
+       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;
 
@@ -2073,7 +2131,7 @@ Route::_set_state (const XMLNode& node, int version)
 }
 
 int
-Route::_set_state_2X (const XMLNode& node, int version)
+Route::set_state_2X (const XMLNode& node, int version)
 {
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
@@ -2360,6 +2418,7 @@ Route::set_processor_state (const XMLNode& node)
                        new_order.push_back (_amp);
                } else if (prop->value() == "meter") {
                        _meter->set_state (**niter, Stateful::current_state_version);
+                       new_order.push_back (_meter);
                } else if (prop->value() == "main-outs") {
                        _main_outs->set_state (**niter, Stateful::current_state_version);
                } else if (prop->value() == "intreturn") {
@@ -2401,7 +2460,7 @@ Route::set_processor_state (const XMLNode& node)
                                } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                           prop->value() == "lv2" ||
                                           prop->value() == "vst" ||
-                                                  prop->value() == "lxvst" ||
+                                          prop->value() == "lxvst" ||
                                           prop->value() == "audiounit") {
 
                                        processor.reset (new PluginInsert(_session));
@@ -2551,11 +2610,13 @@ Route::remove_send_from_internal_return (InternalSend* send)
        }
 }
 
-/** Add a monitor send (if we don't already have one) but don't activate it */
-int
-Route::listen_via_monitor ()
+void
+Route::enable_monitor_send ()
 {
-       /* master never sends to control outs */
+       /* Caller must hold process lock */
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       /* master never sends to monitor section via the normal mechanism */
        assert (!is_master ());
 
        /* make sure we have one */
@@ -2565,18 +2626,15 @@ Route::listen_via_monitor ()
        }
 
        /* set it up */
-       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
        configure_processors (0);
-
-       return 0;
 }
 
-/** Add an internal send to a route.
+/** Add an aux send to a route.
  *  @param route route to send to.
- *  @param placement placement for the send.
+ *  @param before Processor to insert before, or 0 to insert at the end.
  */
 int
-Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
+Route::add_aux_send (boost::shared_ptr<Route> route, boost::shared_ptr<Processor> before)
 {
        assert (route != _session.monitor_out ());
 
@@ -2595,8 +2653,15 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
        }
 
        try {
-               boost::shared_ptr<InternalSend> listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
-               add_processor (listener, placement);
+
+               boost::shared_ptr<InternalSend> listener;
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+               }
+
+               add_processor (listener, before);
 
        } catch (failed_constructor& err) {
                return -1;
@@ -2606,36 +2671,39 @@ Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
 }
 
 void
-Route::drop_listen (boost::shared_ptr<Route> route)
+Route::remove_aux_or_listen (boost::shared_ptr<Route> route)
 {
        ProcessorStreams err;
        ProcessorList::iterator tmp;
 
-       Glib::RWLock::ReaderLock rl(_processor_lock);
-       rl.acquire ();
-
-  again:
-       for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ) {
-
-               boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
-
-               if (d && d->target_route() == route) {
-                       rl.release ();
-                       remove_processor (*x, &err);
-                       rl.acquire ();
+       {
+               Glib::RWLock::ReaderLock rl(_processor_lock);
 
-                       /* list could have been demolished while we dropped the lock
-                          so start over.
-                       */
+               /* have to do this early because otherwise processor reconfig
+                * will put _monitor_send back in the list
+                */
 
-                       goto again;
+               if (route == _session.monitor_out()) {
+                       _monitor_send.reset ();
                }
-       }
 
-       rl.release ();
+         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);
+                               rl.acquire ();
 
-       if (route == _session.monitor_out()) {
-               _monitor_send.reset ();
+                               /* list could have been demolished while we dropped the lock
+                                  so start over.
+                               */
+                               
+                               goto again;
+                       }
+               }
        }
 }
 
@@ -2694,14 +2762,14 @@ Route::feeds (boost::shared_ptr<Route> other, bool* via_sends_only)
 }
 
 bool
-Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
+Route::direct_feeds_according_to_reality (boost::shared_ptr<Route> other, bool* via_send_only)
 {
        DEBUG_TRACE (DEBUG::Graph, string_compose ("Feeds? %1\n", _name));
 
        if (_output->connected_to (other->input())) {
                DEBUG_TRACE (DEBUG::Graph, string_compose ("\tdirect FEEDS %2\n", other->name()));
-               if (only_send) {
-                       *only_send = false;
+               if (via_send_only) {
+                       *via_send_only = false;
                }
 
                return true;
@@ -2715,8 +2783,8 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
                if ((iop = boost::dynamic_pointer_cast<IOProcessor>(*r)) != 0) {
                        if (iop->feeds (other)) {
                                DEBUG_TRACE (DEBUG::Graph,  string_compose ("\tIOP %1 does feed %2\n", iop->name(), other->name()));
-                               if (only_send) {
-                                       *only_send = true;
+                               if (via_send_only) {
+                                       *via_send_only = true;
                                }
                                return true;
                        } else {
@@ -2732,6 +2800,12 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
        return false;
 }
 
+bool
+Route::direct_feeds_according_to_graph (boost::shared_ptr<Route> other, bool* via_send_only)
+{
+       return _session._current_route_graph.has (shared_from_this (), other, via_send_only);
+}
+
 /** Called from the (non-realtime) butler thread when the transport is stopped */
 void
 Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
@@ -2760,15 +2834,61 @@ Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_lo
        _roll_delay = _initial_delay;
 }
 
-/** Called with the process lock held if change contains ConfigurationChanged */
 void
 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 
+               */
+               need_to_queue_solo_change = false;
                configure_processors (0);
                _phase_invert.resize (_input->n_ports().n_audio ());
                io_changed (); /* EMIT SIGNAL */
        }
+
+       if (!_input->connected() && _soloed_by_others_upstream) {
+               if (need_to_queue_solo_change) {
+                       _session.cancel_solo_after_disconnect (shared_from_this(), true);
+               } else {
+                       cancel_solo_after_disconnect (true);
+               }
+       }
+}
+
+void
+Route::output_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 
+               */
+               need_to_queue_solo_change = false;
+       }
+
+       if (!_output->connected() && _soloed_by_others_downstream) {
+               if (need_to_queue_solo_change) {
+                       _session.cancel_solo_after_disconnect (shared_from_this(), false);
+               } else {
+                       cancel_solo_after_disconnect (false);
+               }
+       }
+}
+
+void
+Route::cancel_solo_after_disconnect (bool upstream)
+{
+       if (upstream) {
+               _soloed_by_others_upstream = 0;
+       } else {
+               _soloed_by_others_downstream = 0;
+       }
+       set_mute_master_solo ();
+       solo_changed (false, this);
 }
 
 uint32_t
@@ -2837,7 +2957,7 @@ Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, in
                return 0;
        }
 
-       framecnt_t unused = 0;
+       framepos_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
@@ -2857,42 +2977,6 @@ Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*
        return 0;
 }
 
-void
-Route::toggle_monitor_input ()
-{
-       for (PortSet::iterator i = _input->ports().begin(); i != _input->ports().end(); ++i) {
-               i->ensure_monitor_input( ! i->monitoring_input());
-       }
-}
-
-bool
-Route::has_external_redirects () const
-{
-       // FIXME: what about sends? - they don't return a signal back to ardour?
-
-       boost::shared_ptr<const PortInsert> pi;
-
-       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
-               if ((pi = boost::dynamic_pointer_cast<const PortInsert>(*i)) != 0) {
-
-                       for (PortSet::const_iterator port = pi->output()->ports().begin(); port != pi->output()->ports().end(); ++port) {
-
-                               string port_name = port->name();
-                               string client_name = port_name.substr (0, port_name.find(':'));
-
-                               /* only say "yes" if the redirect is actually in use */
-
-                               if (client_name != "ardour" && pi->active()) {
-                                       return true;
-                               }
-                       }
-               }
-       }
-
-       return false;
-}
-
 void
 Route::flush_processors ()
 {
@@ -2916,45 +3000,63 @@ Route::set_meter_point (MeterPoint p, bool force)
                return;
        }
 
-       _meter_point = p;
-
        bool meter_was_visible_to_user = _meter->display_to_user ();
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
 
+               maybe_note_meter_position ();
+
+               _meter_point = p;
+
                if (_meter_point != MeterCustom) {
 
                        _meter->set_display_to_user (false);
 
                        setup_invisible_processors ();
 
-                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+               } else {
 
-                       ChanCount m_in;
+                       _meter->set_display_to_user (true);
 
-                       if (loc == _processors.begin()) {
-                               m_in = _input->n_ports();
-                       } else {
-                               ProcessorList::iterator before = loc;
-                               --before;
-                               m_in = (*before)->output_streams ();
+                       /* 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->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.
-                       */
+               /* 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 {
-
-                       // just make it visible and let the user move it
-
-                       _meter->set_display_to_user (true);
+                       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.
+               */
        }
 
        meter_change (); /* EMIT SIGNAL */
@@ -3285,6 +3387,38 @@ Route::set_name (const string& str)
        return ret;
 }
 
+/** Set the name of a route in an XML description.
+ *  @param node XML <Route> node to set the name in.
+ *  @param name New name.
+ */
+void
+Route::set_name_in_state (XMLNode& node, string const & name)
+{
+       node.add_property (X_("name"), 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);
+
+               } else if ((*i)->name() == X_("Processor")) {
+
+                       XMLProperty* role = (*i)->property (X_("role"));
+                       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);
+                       
+               }
+       }
+}
+
 boost::shared_ptr<Send>
 Route::internal_send_for (boost::shared_ptr<const Route> target) const
 {
@@ -3817,21 +3951,6 @@ Route::setup_invisible_processors ()
        }
 }
 
-bool
-Route::should_monitor () const
-{
-       switch (Config->get_monitoring_model()) {
-       case HardwareMonitoring:
-       case ExternalMonitoring:
-               return !record_enabled() || (_session.config.get_auto_input() && !_session.actively_recording());
-               break;
-       default:
-               break;
-       }
-
-       return true;
-}
-
 void
 Route::unpan ()
 {
@@ -3847,3 +3966,93 @@ Route::unpan ()
                }
        }
 }
+
+/** If the meter point is `Custom', make a note of where the meter is.
+ *  This is so that if the meter point is subsequently set to something else,
+ *  and then back to custom, we can put the meter back where it was last time
+ *  custom was enabled.
+ *
+ *  Must be called with the _processor_lock held.
+ */
+void
+Route::maybe_note_meter_position ()
+{
+       if (_meter_point != MeterCustom) {
+               return;
+       }
+       
+       _custom_meter_position_noted = true;
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               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;
+                       } else {
+                               _last_custom_meter_was_at_end = true;
+                       }
+               }
+       }
+}
+
+boost::shared_ptr<Processor>
+Route::processor_by_id (PBD::ID id) const
+{
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->id() == id) {
+                       return *i;
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
+/** @return the monitoring state, or in other words what data we are pushing
+ *  into the route (data from the inputs, data from disk or silence)
+ */
+MonitorState
+Route::monitoring_state () const
+{
+       return MonitoringInput;
+}
+
+/** @return what we should be metering; either the data coming from the input
+ *  IO or the data that is flowing through the route.
+ */
+MeterState
+Route::metering_state () const
+{
+       return MeteringRoute;
+}
+
+bool
+Route::has_external_redirects () const
+{
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+               /* 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;;
+               }
+       }
+
+       return false;
+}
+
+boost::shared_ptr<Processor>
+Route::the_instrument () const
+{
+       Glib::RWLock::WriterLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->input_streams().n_midi() > 0 &&
+                   (*i)->output_streams().n_audio() > 0) {
+                       return (*i);
+               }
+       }
+       return boost::shared_ptr<Processor>();
+}