Make sure CapturingProcessors write a type to their state node so that they can be...
[ardour.git] / libs / ardour / route.cc
index 5fe287c2bf521a325cffb9b7ce07ac4e0e1de9fd..4277bea0226a190ac957974e9118acb8416a38a2 100644 (file)
 
 */
 
+#ifdef WAF_BUILD
+#include "libardour-config.h"
+#endif
+
 #include <cmath>
 #include <fstream>
 #include <cassert>
@@ -27,6 +31,7 @@
 #include "pbd/memento_command.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
+#include "pbd/boost_debug.h"
 
 #include "evoral/Curve.hpp"
 
@@ -46,7 +51,9 @@
 #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"
@@ -60,6 +67,7 @@
 #include "ardour/utils.h"
 #include "ardour/graph.h"
 #include "ardour/unknown_processor.h"
+#include "ardour/capturing_processor.h"
 
 #include "i18n.h"
 
@@ -89,8 +97,6 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
         , _recordable (true)
         , _silent (false)
         , _declickable (false)
-       , _solo_control (new SoloControllable (X_("solo"), *this))
-       , _mute_control (new MuteControllable (X_("mute"), *this))
        , _mute_master (new MuteMaster (sess, name))
         , _have_internal_generator (false)
         , _solo_safe (false)
@@ -105,7 +111,10 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type)
 int
 Route::init ()
 {
-       /* add standard controls */
+        /* add standard controls */
+
+       _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ()));
+       _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ()));
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
        _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
@@ -113,6 +122,14 @@ Route::init ()
        add_control (_solo_control);
        add_control (_mute_control);
 
+        /* panning */
+        
+        Pannable* p = new Pannable (_session);
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+       boost_debug_shared_ptr_mark_interesting (p, "Pannable");
+#endif
+        _pannable.reset (p);
+
        /* input and output objects */
 
        _input.reset (new IO (_session, _name, IO::Input, _default_type));
@@ -128,41 +145,33 @@ Route::init ()
        _amp.reset (new Amp (_session));
        add_processor (_amp, PostFader);
 
-       /* add standard processors: meter, main outs, monitor out */
+       /* create standard processors: meter, main outs, monitor out;
+          they will be added to _processors by setup_invisible_processors ()
+       */
 
        _meter.reset (new PeakMeter (_session));
        _meter->set_display_to_user (false);
+       _meter->activate ();
 
-       add_processor (_meter, PostFader);
-
-       _main_outs.reset (new Delivery (_session, _output, _mute_master, _name, Delivery::Main));
-
-        add_processor (_main_outs, PostFader);
+       _main_outs.reset (new Delivery (_session, _output, _pannable, _mute_master, _name, Delivery::Main));
+       _main_outs->activate ();
 
        if (is_monitor()) {
                /* where we listen to tracks */
                _intreturn.reset (new InternalReturn (_session));
-               add_processor (_intreturn, PreFader);
-
-                ProcessorList::iterator i;
-
-                for (i = _processors.begin(); i != _processors.end(); ++i) {
-                        if (*i == _intreturn) {
-                                ++i;
-                                break;
-                        }
-                }
+               _intreturn->activate ();
 
                 /* the thing that provides proper control over a control/monitor/listen bus 
                    (such as per-channel cut, dim, solo, invert, etc).
-                   It always goes right after the internal return;
                  */
                 _monitor_control.reset (new MonitorProcessor (_session));
-                add_processor (_monitor_control, i);
+               _monitor_control->activate ();
 
                 /* no panning on the monitor main outs */
 
+#ifdef PANNER_HACKS
                 _main_outs->panner()->set_bypassed (true);
+#endif
        }
 
         if (is_master() || is_monitor() || is_hidden()) {
@@ -173,6 +182,12 @@ Route::init ()
 
        Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this)));
 
+       {
+               /* run a configure so that the invisible processors get set up */
+               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               configure_processors (0);
+       }
+
         return 0;
 }
 
@@ -487,14 +502,18 @@ Route::process_output_buffers (BufferSet& bufs,
                if (boost::dynamic_pointer_cast<UnknownProcessor> (*i)) {
                        break;
                }
-               
-               if (bufs.count() != (*i)->input_streams()) {
-                       cerr << _name << " bufs = " << bufs.count()
-                            << " input for " << (*i)->name() << " = " << (*i)->input_streams()
-                            << endl;
-               }
-               assert (bufs.count() == (*i)->input_streams());
-                
+
+#ifndef NDEBUG
+                /* if it has any inputs, make sure they match */
+                if ((*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 ();
+                        }
+                }
+#endif                
                 /* should we NOT run plugins here if the route is inactive?
                    do we catch route != active somewhere higher?
                 */
@@ -582,7 +601,7 @@ Route::set_listen (bool yn, void* src)
 }
 
 bool
-Route::listening () const
+Route::listening_via_monitor () const
 {
        if (_monitor_send) {
                return _monitor_send->active ();
@@ -844,13 +863,16 @@ Route::add_processor (boost::shared_ptr<Processor> processor, Placement placemen
 
 
 /** Add a processor to the route.
- * @a iter must point to an iterator in _processors and the new
- * processor will be inserted immediately before this location.  Otherwise,
- * @a position is used.
+ *  @param iter an iterator in _processors; the new processor will be inserted immediately before this location.
  */
 int
 Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::iterator iter, ProcessorStreams* err, bool activation_allowed)
 {
+       assert (processor != _meter);
+       assert (processor != _main_outs);
+
+        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1 adding processor %2\n", name(), processor->name()));
+       
        ChanCount old_pms = processor_max_streams;
 
        if (!_session.engine().connected() || !processor) {
@@ -859,14 +881,15 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
 
                boost::shared_ptr<PluginInsert> pi;
                boost::shared_ptr<PortInsert> porti;
 
                ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), processor);
 
-               if (processor == _amp || processor == _meter || processor == _main_outs) {
-                       // Ensure only one of these are in the list at any time
+               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
                                        return 0;
@@ -893,11 +916,8 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
                        if (configure_processors_unlocked (err)) {
-                               ProcessorList::iterator ploc = loc;
-                               --ploc;
-                               _processors.erase(ploc);
+                               pstate.restore ();
                                configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               cerr << "configure failed\n";
                                return -1;
                        }
                }
@@ -911,15 +931,7 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
 
                }
 
-                /* is this the monitor send ? if so, make sure we keep track of it */
-
-                boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
-
-                if (isend && _session.monitor_out() && (isend->target_id() == _session.monitor_out()->id())) {
-                        _monitor_send = isend;
-                }
-
-               if (activation_allowed && (processor != _monitor_send)) {
+               if (activation_allowed) {
                        processor->activate ();
                }
 
@@ -974,14 +986,14 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 
                                } else {
 
-                                       processor.reset (new PortInsert (_session, _mute_master));
+                                       processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                }
 
                        }
 
                } else if (node.name() == "Send") {
 
-                       processor.reset (new Send (_session, _mute_master));
+                       processor.reset (new Send (_session, _pannable, _mute_master));
 
                } else {
 
@@ -1005,26 +1017,20 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version)
 int
 Route::add_processors (const ProcessorList& others, boost::shared_ptr<Processor> before, ProcessorStreams* err)
 {
+       /* NOTE: this is intended to be used ONLY when copying
+          processors from another Route. Hence the subtle
+          differences between this and ::add_processor()
+       */
+
        ProcessorList::iterator loc;
 
        if (before) {
                loc = find(_processors.begin(), _processors.end(), before);
        } else {
-               /* nothing specified - at end but before main outs */
-               loc = find (_processors.begin(), _processors.end(), _main_outs);
+               /* nothing specified - at end */
+               loc = _processors.end ();
        }
 
-       return add_processors (others, loc, err);
-}
-
-int
-Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter, ProcessorStreams* err)
-{
-       /* NOTE: this is intended to be used ONLY when copying
-          processors from another Route. Hence the subtle
-          differences between this and ::add_processor()
-       */
-
        ChanCount old_pms = processor_max_streams;
 
        if (!_session.engine().connected()) {
@@ -1037,32 +1043,21 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-
-               ChanCount potential_max_streams = ChanCount::max (_input->n_ports(), _output->n_ports());
+               ProcessorState pstate (this);
 
                for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
 
-                       // Ensure meter only appears in the list once
                        if (*i == _meter) {
-                               ProcessorList::iterator m = find(_processors.begin(), _processors.end(), *i);
-                               if (m != _processors.end()) {
-                                       _processors.erase(m);
-                               }
+                               continue;
                        }
 
                        boost::shared_ptr<PluginInsert> pi;
 
                        if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
                                pi->set_count (1);
-
-                               ChanCount m = max (pi->input_streams(), pi->output_streams());
-
-                               if (m > potential_max_streams) {
-                                       potential_max_streams = m;
-                               }
                        }
 
-                       ProcessorList::iterator inserted = _processors.insert (iter, *i);
+                       ProcessorList::iterator inserted = _processors.insert (loc, *i);
 
                        if ((*i)->active()) {
                                (*i)->activate ();
@@ -1071,7 +1066,7 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                        {
                                Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                if (configure_processors_unlocked (err)) {
-                                       _processors.erase (inserted);
+                                       pstate.restore ();
                                        configure_processors_unlocked (0); // it worked before we tried to add it ...
                                        return -1;
                                }
@@ -1315,6 +1310,8 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+               
                ProcessorList::iterator i;
                bool removed = false;
 
@@ -1361,8 +1358,7 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                
                        if (configure_processors_unlocked (err)) {
-                               /* get back to where we where */
-                               _processors.insert (i, processor);
+                               pstate.restore ();
                                /* we know this will work, because it worked before :) */
                                configure_processors_unlocked (0);
                                return -1;
@@ -1403,11 +1399,11 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
+               ProcessorState pstate (this);
+               
                ProcessorList::iterator i;
                boost::shared_ptr<Processor> processor;
 
-               ProcessorList as_we_were = _processors;
-
                for (i = _processors.begin(); i != _processors.end(); ) {
 
                        processor = *i;
@@ -1452,8 +1448,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                
                        if (configure_processors_unlocked (err)) {
-                               /* get back to where we where */
-                               _processors = as_we_were;
+                               pstate.restore ();
                                /* we know this will work, because it worked before :) */
                                configure_processors_unlocked (0);
                                return -1;
@@ -1496,6 +1491,7 @@ Route::configure_processors (ProcessorStreams* err)
                Glib::RWLock::WriterLock lm (_processor_lock);
                return configure_processors_unlocked (err);
        }
+       
        return 0;
 }
 
@@ -1565,6 +1561,9 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                return 0;
        }
 
+       /* put invisible processors where they should be */
+       setup_invisible_processors ();
+
        _in_configure_processors = true;
 
        list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
@@ -1694,10 +1693,10 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ChanCount old_pms = processor_max_streams;
+               ProcessorState pstate (this);
+               
                ProcessorList::iterator oiter;
                ProcessorList::const_iterator niter;
-               ProcessorList as_it_was_before = _processors;
                ProcessorList as_it_will_be;
 
                oiter = _processors.begin();
@@ -1755,17 +1754,14 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                
                        if (configure_processors_unlocked (err)) {
-                               _processors = as_it_was_before;
-                               processor_max_streams = old_pms;
+                               pstate.restore ();
                                return -1;
                        }
                }
        }
 
-        if (true) {
-                processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
-               set_processor_positions ();
-        }
+       processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */
+       set_processor_positions ();
 
        return 0;
 }
@@ -1851,6 +1847,8 @@ Route::state(bool full_state)
                cmt->add_content (_comment);
        }
 
+        node->add_child_nocopy (_pannable->state (full_state));
+
        for (i = _processors.begin(); i != _processors.end(); ++i) {
                node->add_child_nocopy((*i)->state (full_state));
        }
@@ -1927,6 +1925,11 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
                if (child->name() == X_("Processor")) {
                        processor_state.add_child_copy (*child);
                }
+
+
+                if (child->name() == X_("Pannable")) {
+                        _pannable->set_state (*child, version);
+                }
        }
 
        set_processor_state (processor_state);
@@ -2232,7 +2235,7 @@ Route::_set_state_2X (const XMLNode& node, int version)
                                io_child = *io_niter;
                                
                                if (io_child->name() == X_("Panner")) {
-                                       _main_outs->panner()->set_state(*io_child, version);
+                                       _main_outs->panner_shell()->set_state(*io_child, version);
                                } else if (io_child->name() == X_("Automation")) {
                                        /* IO's automation is for the fader */
                                        _amp->set_automation_xml_state (*io_child, Evoral::Parameter (GainAutomation));
@@ -2331,24 +2334,22 @@ 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);
-                        new_order.push_back (_main_outs);
                 } else if (prop->value() == "intreturn") {
                         if (!_intreturn) {
                                 _intreturn.reset (new InternalReturn (_session));
                                 must_configure = true;
                         }
                         _intreturn->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_intreturn);
                 } else if (is_monitor() && prop->value() == "monitor") {
                         if (!_monitor_control) {
                                 _monitor_control.reset (new MonitorProcessor (_session));
                                 must_configure = true;
                         }
                         _monitor_control->set_state (**niter, Stateful::current_state_version);
-                        new_order.push_back (_monitor_control);
+               } else if (prop->value() == "capture") {
+                       _capturing_processor.reset (new CapturingProcessor (_session));
                 } else {
                         ProcessorList::iterator o;
 
@@ -2369,8 +2370,7 @@ Route::set_processor_state (const XMLNode& node)
 
                                 if (prop->value() == "intsend") {
                                         
-                                        processor.reset (new InternalSend (_session, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
-                                        
+                                        processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Role (0)));
                                 } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
                                            prop->value() == "lv2" ||
                                            prop->value() == "vst" ||
@@ -2380,11 +2380,11 @@ Route::set_processor_state (const XMLNode& node)
                                         
                                 } else if (prop->value() == "port") {
                                         
-                                        processor.reset (new PortInsert (_session, _mute_master));
+                                        processor.reset (new PortInsert (_session, _pannable, _mute_master));
                                         
                                 } else if (prop->value() == "send") {
                                         
-                                        processor.reset (new Send (_session, _mute_master));
+                                        processor.reset (new Send (_session, _pannable, _mute_master));
                                         
                                 } else {
                                         error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
@@ -2395,7 +2395,19 @@ Route::set_processor_state (const XMLNode& node)
                                        /* This processor could not be configured.  Turn it into a UnknownProcessor */
                                        processor.reset (new UnknownProcessor (_session, **niter));
                                }
-                                       
+
+                               /* we have to note the monitor send here, otherwise a new one will be created
+                                  and the state of this one will be lost.
+                               */
+                               boost::shared_ptr<InternalSend> isend = boost::dynamic_pointer_cast<InternalSend> (processor);
+                               if (isend && isend->role() == Delivery::Listen) {
+                                       _monitor_send = isend;
+                               }
+
+                               /* it doesn't matter if invisible processors are added here, as they
+                                  will be sorted out by setup_invisible_processors () shortly.
+                               */
+
                                new_order.push_back (processor);
                                must_configure = true;
                         }
@@ -2468,8 +2480,8 @@ Route::add_internal_return ()
        }
 }
 
-BufferSet*
-Route::get_return_buffer () const
+void
+Route::add_send_to_internal_return (InternalSend* send)
 {
        Glib::RWLock::ReaderLock rm (_processor_lock);
 
@@ -2477,16 +2489,13 @@ Route::get_return_buffer () const
                boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
 
                if (d) {
-                       BufferSet* bs = d->get_buffers ();
-                       return bs;
+                       return d->add_send (send);
                }
        }
-
-       return 0;
 }
 
 void
-Route::release_return_buffer () const
+Route::remove_send_from_internal_return (InternalSend* send)
 {
        Glib::RWLock::ReaderLock rm (_processor_lock);
 
@@ -2494,83 +2503,62 @@ Route::release_return_buffer () const
                boost::shared_ptr<InternalReturn> d = boost::dynamic_pointer_cast<InternalReturn>(*x);
 
                if (d) {
-                       return d->release_buffers ();
+                       return d->remove_send (send);
                }
        }
 }
 
+/** Add a monitor send (if we don't already have one) but don't activate it */
 int
-Route::listen_via (boost::shared_ptr<Route> route, Placement placement, bool /*active*/, bool aux)
+Route::listen_via_monitor ()
 {
-       vector<string> ports;
-       vector<string>::const_iterator i;
+       /* master never sends to control outs */
+       assert (!is_master ());
+       
+       /* make sure we have one */
+       if (!_monitor_send) {
+               _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+               _monitor_send->set_display_to_user (false);
+       }
+       
+       /* set it up */
+       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+       configure_processors (0);
+
+       return 0;
+}
 
+/** Add an internal send to a route.  
+ *  @param route route to send to.
+ *  @param placement placement for the send.
+ */
+int
+Route::listen_via (boost::shared_ptr<Route> route, Placement placement)
+{
+       assert (route != _session.monitor_out ());
+       
        {
                Glib::RWLock::ReaderLock rm (_processor_lock);
 
                for (ProcessorList::iterator x = _processors.begin(); x != _processors.end(); ++x) {
 
-                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend>(*x);
+                       boost::shared_ptr<InternalSend> d = boost::dynamic_pointer_cast<InternalSend> (*x);
 
                        if (d && d->target_route() == route) {
-
-                               /* if the target is the control outs, then make sure
-                                  we take note of which i-send is doing that.
-                               */
-
-                               if (route == _session.monitor_out()) {
-                                       _monitor_send = boost::dynamic_pointer_cast<Delivery>(d);
-                               }
-
                                /* already listening via the specified IO: do nothing */
-
                                return 0;
                        }
                }
        }
 
-       boost::shared_ptr<InternalSend> listener;
-
        try {
-
-                if (is_master()) {
-                        
-                        if (route == _session.monitor_out()) {
-                                /* master never sends to control outs */
-                                return 0;
-                        } else {
-                                listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
-                        }
-
-                } else {
-                        listener.reset (new InternalSend (_session, _mute_master, route, (aux ? Delivery::Aux : Delivery::Listen)));
-                }
+               boost::shared_ptr<InternalSend> listener (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux));
+               add_processor (listener, placement);
 
        } catch (failed_constructor& err) {
                return -1;
        }
 
-       if (route == _session.monitor_out()) {
-               _monitor_send = listener;
-       }
-
-
-       if (aux) {
-
-               add_processor (listener, placement);
-
-       } else {
-               
-               if (placement == PostFader) {
-                       /* put it *really* at the end, not just after the panner (main outs)
-                        */
-                       add_processor (listener, _processors.end());
-               } else {
-                       add_processor (listener, PreFader);
-               }
-               
-       }
-
        return 0;
 }
 
@@ -2954,29 +2942,21 @@ 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);
        
-               if (p != MeterCustom) {
-                       // Move meter in the processors list to reflect the new position
-                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
-                       _processors.erase(loc);
-                       switch (p) {
-                       case MeterInput:
-                               loc = _processors.begin();
-                               break;
-                       case MeterPreFader:
-                               loc = find (_processors.begin(), _processors.end(), _amp);
-                               break;
-                       case MeterPostFader:
-                               loc = _processors.end();
-                               break;
-                       default:
-                               break;
-                       }
+               if (_meter_point != MeterCustom) {
+
+                       _meter->set_display_to_user (false);
+                       
+                       setup_invisible_processors ();
                        
+                       ProcessorList::iterator loc = find (_processors.begin(), _processors.end(), _meter);
+
                        ChanCount m_in;
                        
                        if (loc == _processors.begin()) {
@@ -2989,16 +2969,12 @@ Route::set_meter_point (MeterPoint p, bool force)
                        
                        _meter->reflect_inputs (m_in);
                        
-                       _processors.insert (loc, _meter);
-
                        /* 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->set_display_to_user (false);
-                       
                } else {
                        
                        // just make it visible and let the user move it
@@ -3007,7 +2983,6 @@ Route::set_meter_point (MeterPoint p, bool force)
                }
        }
 
-       _meter_point = p;
        meter_change (); /* EMIT SIGNAL */
 
        bool const meter_visibly_changed = (_meter->display_to_user() != meter_was_visible_to_user);
@@ -3016,37 +2991,17 @@ Route::set_meter_point (MeterPoint p, bool force)
 }
 
 void
-Route::put_monitor_send_at (Placement p)
+Route::listen_position_changed ()
 {
-       if (!_monitor_send) {
-               return;
-       }
-
        {
                Glib::RWLock::WriterLock lm (_processor_lock);
-               ProcessorList as_it_was (_processors);
-               ProcessorList::iterator loc = find(_processors.begin(), _processors.end(), _monitor_send);
-               _processors.erase(loc);
-               
-               switch (p) {
-               case PreFader:
-                       loc = find(_processors.begin(), _processors.end(), _amp);
-                       if (loc != _processors.begin()) {
-                               --loc;
-                       }
-                       break;
-               case PostFader:
-                       loc = _processors.end();
-                       break;
-               }
-               
-               _processors.insert (loc, _monitor_send);
+               ProcessorState pstate (this);
 
                {
                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                        
                        if (configure_processors_unlocked (0)) {
-                               _processors = as_it_was;
+                               pstate.restore ();
                                configure_processors_unlocked (0); // it worked before we tried to add it ...
                                return;
                        }
@@ -3057,6 +3012,24 @@ Route::put_monitor_send_at (Placement p)
        _session.set_dirty ();
 }
 
+boost::shared_ptr<CapturingProcessor>
+Route::add_export_point()
+{
+       if (!_capturing_processor) {
+               
+               _capturing_processor.reset (new CapturingProcessor (_session));
+               _capturing_processor->activate ();
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors (0);
+               }
+
+       }
+       
+       return _capturing_processor;
+}
+
 framecnt_t
 Route::update_total_latency ()
 {
@@ -3125,17 +3098,16 @@ Route::set_latency_delay (framecnt_t longest_session_latency)
 void
 Route::automation_snapshot (framepos_t now, bool force)
 {
-       panner()->automation_snapshot (now, force);
-       
+        _pannable->automation_snapshot (now, force);
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->automation_snapshot (now, force);
        }
 }
 
-Route::SoloControllable::SoloControllable (std::string name, Route& r)
-       : AutomationControl (r.session(), Evoral::Parameter (SoloAutomation),
+Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr<Route> r)
+       : AutomationControl (r->session(), Evoral::Parameter (SoloAutomation),
                             boost::shared_ptr<AutomationList>(), name)
-       , route (r)
+       , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(SoloAutomation)));
        set_list (gl);
@@ -3145,36 +3117,42 @@ void
 Route::SoloControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-# if 0
-       this is how it should be done 
 
        boost::shared_ptr<RouteList> rl (new RouteList);
-       rl->push_back (route);
+
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return;
+       }
+       
+       rl->push_back (r);
 
        if (Config->get_solo_control_is_listen_control()) {
                _session.set_listen (rl, bval);
        } else {
                _session.set_solo (rl, bval);
        }
-#else
-       route.set_solo (bval, this);
-#endif
 }
 
 double
-Route::SoloControllable::get_value (void) const
+Route::SoloControllable::get_value () const
 {
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return 0;
+       }
+       
        if (Config->get_solo_control_is_listen_control()) {
-               return route.listening() ? 1.0f : 0.0f;
+               return r->listening_via_monitor() ? 1.0f : 0.0f;
        } else {
-               return route.self_soloed() ? 1.0f : 0.0f;
+               return r->self_soloed() ? 1.0f : 0.0f;
        }
 }
 
-Route::MuteControllable::MuteControllable (std::string name, Route& r)
-       : AutomationControl (r.session(), Evoral::Parameter (MuteAutomation),
+Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr<Route> r)
+       : AutomationControl (r->session(), Evoral::Parameter (MuteAutomation),
                             boost::shared_ptr<AutomationList>(), name)
-       , route (r)
+       , _route (r)
 {
        boost::shared_ptr<AutomationList> gl(new AutomationList(Evoral::Parameter(MuteAutomation)));
        set_list (gl);
@@ -3184,21 +3162,27 @@ void
 Route::MuteControllable::set_value (double val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
-# if 0
-       this is how it should be done 
 
        boost::shared_ptr<RouteList> rl (new RouteList);
-       rl->push_back (route);
+
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return;
+       }
+       
+       rl->push_back (r);
        _session.set_mute (rl, bval);
-#else
-       route.set_mute (bval, this);
-#endif
 }
 
 double
-Route::MuteControllable::get_value (void) const
+Route::MuteControllable::get_value () const
 {
-       return route.muted() ? 1.0f : 0.0f;
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return 0;
+       }
+       
+       return r->muted() ? 1.0f : 0.0f;
 }
 
 void
@@ -3255,11 +3239,10 @@ Route::shift (framepos_t pos, framecnt_t frames)
 
        /* pan automation */
         {
-                boost::shared_ptr<AutomationControl> pc;
-                uint32_t npans = _main_outs->panner()->npanners();
-
-                for (uint32_t p = 0; p < npans; ++p) {
-                        pc = _main_outs->panner()->pan_control (0, p);
+                ControlSet::Controls& c (_pannable->controls());
+                
+                for (ControlSet::Controls::const_iterator ci = c.begin(); ci != c.end(); ++ci) {
+                        boost::shared_ptr<AutomationControl> pc = boost::dynamic_pointer_cast<AutomationControl> (ci->second);
                         if (pc) {
                                 boost::shared_ptr<AutomationList> al = pc->alist();
                                 XMLNode& before = al->get_state ();
@@ -3445,10 +3428,23 @@ Route::meter ()
        }
 }
 
+boost::shared_ptr<Pannable>
+Route::pannable() const
+{
+       return _pannable;
+}
+
 boost::shared_ptr<Panner>
 Route::panner() const
 {
-       return _main_outs->panner();
+        /* may be null ! */
+       return _main_outs->panner_shell()->panner();
+}
+
+boost::shared_ptr<PannerShell>
+Route::panner_shell() const
+{
+       return _main_outs->panner_shell();
 }
 
 boost::shared_ptr<AutomationControl>
@@ -3589,3 +3585,228 @@ Route::unknown_processors () const
 
        return p;
 }
+
+void
+Route::set_latency_ranges (bool playback) const
+{
+       framecnt_t own_latency = 0;
+
+        /* Processor list not protected by lock: MUST BE CALLED FROM PROCESS THREAD OR
+           LATENCY CALLBACK
+        */
+
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->active ()) {
+                       own_latency += (*i)->signal_latency ();
+               }
+       }
+
+        if (playback) {
+                update_port_latencies (_input->ports (), _output->ports (), true, own_latency);
+        } else {
+                update_port_latencies (_output->ports (), _input->ports (), false, own_latency);
+        }
+}
+
+void
+Route::update_port_latencies (const PortSet& operands, const PortSet& feeders, bool playback, framecnt_t our_latency) const
+{
+#ifdef HAVE_JACK_NEW_LATENCY
+
+        /* we assume that all our input ports feed all our output ports. its not
+           universally true, but the alternative is way too corner-case to worry about.
+        */
+        
+        jack_latency_range_t all_connections;
+        
+        all_connections.min = ~((jack_nframes_t) 0);
+        all_connections.max = 0;
+        
+        /* iterate over all feeder ports and determine their relevant latency, taking
+           the maximum and minimum across all of them.
+        */
+        
+        for (PortSet::const_iterator p = feeders.begin(); p != feeders.end(); ++p) {
+                
+                jack_latency_range_t range;
+                
+                p->get_connected_latency_range (range, playback);
+                
+                all_connections.min = min (all_connections.min, range.min);
+                all_connections.max = max (all_connections.max, range.max);
+        }
+        
+        all_connections.min += our_latency;
+        all_connections.max += our_latency;
+
+        for (PortSet::const_iterator p = operands.begin(); p != operands.end(); ++p) {
+                
+                p->set_latency_range (all_connections, playback);
+                
+                DEBUG_TRACE (DEBUG::Latency, string_compose ("Port %1 %5 latency range %2 .. %3 (including route latency of %4)\n",
+                                                             p->name(),
+                                                             all_connections.min,
+                                                             all_connections.max,
+                                                             our_latency,
+                                                             (playback ? "PLAYBACK" : "CAPTURE")));
+        }
+#endif
+}
+
+
+/** Put the invisible processors in the right place in _processors.
+ *  Must be called with a writer lock on _processor_lock held.
+ */
+void
+Route::setup_invisible_processors ()
+{
+#ifndef NDEBUG
+       Glib::RWLock::WriterLock lm (_processor_lock, Glib::TRY_LOCK);
+       assert (!lm.locked ());
+#endif
+
+        if (!_main_outs) {
+                /* too early to be doing this stuff */
+                return;
+        }
+
+       /* we'll build this new list here and then use it */
+       
+       ProcessorList new_processors;
+
+       /* find visible processors */
+       
+       for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if ((*i)->display_to_user ()) {
+                       new_processors.push_back (*i);
+               }
+       }
+
+       /* find the amp */
+
+       ProcessorList::iterator amp = new_processors.begin ();
+       while (amp != new_processors.end() && boost::dynamic_pointer_cast<Amp> (*amp) == 0) {
+               ++amp;
+       }
+
+       assert (amp != _processors.end ());
+
+       /* and the processor after the amp */
+
+       ProcessorList::iterator after_amp = amp;
+       ++after_amp;
+
+       /* METER */
+
+       if (_meter) {
+               switch (_meter_point) {
+               case MeterInput:
+                       assert (!_meter->display_to_user ());
+                       new_processors.push_front (_meter);
+                       break;
+               case MeterPreFader:
+                       assert (!_meter->display_to_user ());
+                       new_processors.insert (amp, _meter);
+                       break;
+               case MeterPostFader:
+                        /* do nothing here */
+                       break;
+               case MeterOutput:
+                        /* do nothing here */
+                       break;
+               case MeterCustom:
+                       /* the meter is visible, so we don't touch it here */
+                       break;
+               }
+       }
+
+       /* MAIN OUTS */
+
+        assert (_main_outs);
+        assert (!_main_outs->display_to_user ());
+        new_processors.push_back (_main_outs);
+
+        /* iterator for the main outs */
+        
+        ProcessorList::iterator main = new_processors.end();
+        --main;
+
+        /* OUTPUT METERING */
+
+        if (_meter && (_meter_point == MeterOutput || _meter_point == MeterPostFader)) {
+                assert (!_meter->display_to_user ());
+
+                /* add the processor just before or just after the main outs */
+                
+                ProcessorList::iterator meter_point = main;
+
+                if (_meter_point == MeterOutput) {
+                        ++meter_point;
+                }
+                new_processors.insert (meter_point, _meter);                        
+        }
+
+       /* MONITOR SEND */
+
+       if (_monitor_send && !is_monitor ()) {
+               assert (!_monitor_send->display_to_user ());
+                if (Config->get_solo_control_is_listen_control()) {
+                        switch (Config->get_listen_position ()) {
+                        case PreFaderListen:
+                                switch (Config->get_pfl_position ()) {
+                                case PFLFromBeforeProcessors:
+                                        new_processors.push_front (_monitor_send);
+                                        break;
+                                case PFLFromAfterProcessors:
+                                        new_processors.insert (amp, _monitor_send);
+                                        break;
+                                }
+                                break;
+                        case AfterFaderListen:
+                                switch (Config->get_afl_position ()) {
+                                case AFLFromBeforeProcessors:
+                                        new_processors.insert (after_amp, _monitor_send);
+                                        break;
+                                case AFLFromAfterProcessors:
+                                        new_processors.insert (new_processors.end(), _monitor_send);
+                                        break;
+                                }
+                                break;
+                        }
+                }  else {
+                        new_processors.insert (new_processors.end(), _monitor_send);
+                }
+       }
+
+       /* MONITOR CONTROL */
+
+       if (_monitor_control && is_monitor ()) {
+               assert (!_monitor_control->display_to_user ());
+               new_processors.push_front (_monitor_control);
+       }
+       
+       /* INTERNAL RETURN */
+
+       /* doing this here means that any monitor control will come just after
+          the return.
+       */
+
+       if (_intreturn) {
+               assert (!_intreturn->display_to_user ());
+               new_processors.push_front (_intreturn);
+       }
+
+       /* EXPORT PROCESSOR */
+       
+       if (_capturing_processor) {
+               assert (!_capturing_processor->display_to_user ());
+               new_processors.push_front (_capturing_processor);
+       }
+
+       _processors = new_processors;
+
+       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 ()));
+       }
+}