Replace RouteGroup's collection of apply() methods with a single one.
[ardour.git] / libs / ardour / route.cc
index 1bb9c63db56d374f92df8e538aae3ad6a7620710..90867940a14a520cbd318615caa124a7ccd82147 100644 (file)
@@ -59,6 +59,7 @@
 #include "ardour/timestamps.h"
 #include "ardour/utils.h"
 #include "ardour/graph.h"
+#include "ardour/unknown_processor.h"
 
 #include "i18n.h"
 
@@ -107,7 +108,7 @@ Route::init ()
        /* add standard controls */
 
        _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
-       _mute_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle));
+       _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle));
 
        add_control (_solo_control);
        add_control (_mute_control);
@@ -120,6 +121,8 @@ Route::init ()
        _input->changed.connect_same_thread (*this, boost::bind (&Route::input_change_handler, this, _1, _2));
        _output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
 
+       _input->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::input_port_count_changing, this, _1));
+
        /* add amp processor  */
 
        _amp.reset (new Amp (_session));
@@ -350,11 +353,11 @@ Route::set_gain (gain_t val, void *src)
                                }
                        }
 
-                       _route_group->apply (&Route::inc_gain, factor, _route_group);
+                       _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor, _route_group));
 
                } else {
 
-                       _route_group->apply (&Route::set_gain, val, _route_group);
+                       _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, _route_group));
                }
 
                return;
@@ -379,8 +382,9 @@ Route::set_gain (gain_t val, void *src)
  */
 void
 Route::process_output_buffers (BufferSet& bufs,
-                              framepos_t start_frame, framepos_t end_frame, nframes_t nframes,
-                              bool /*with_processors*/, int declick)
+                              framepos_t start_frame, framepos_t end_frame, pframes_t nframes,
+                              bool /*with_processors*/, int declick,
+                               bool gain_automation_ok)
 {
        bool monitor;
 
@@ -400,9 +404,12 @@ Route::process_output_buffers (BufferSet& bufs,
        }
 
        /* figure out if we're going to use gain automation */
-       _amp->setup_gain_automation (start_frame, end_frame, nframes);
-
-
+        if (gain_automation_ok) {
+                _amp->setup_gain_automation (start_frame, end_frame, nframes);
+        } else {
+                _amp->apply_gain_automation (false);
+        }
+        
        /* tell main outs what to do about monitoring */
        _main_outs->no_outs_cuz_we_no_monitor (!monitor);
 
@@ -433,12 +440,12 @@ Route::process_output_buffers (BufferSet& bufs,
                                Sample* const sp = i->data();
 
                                if (_phase_invert[chn]) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx]  = -sp[nx];
                                                sp[nx] += 1.0e-27f;
                                        }
                                } else {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] += 1.0e-27f;
                                        }
                                }
@@ -450,7 +457,7 @@ Route::process_output_buffers (BufferSet& bufs,
                                Sample* const sp = i->data();
 
                                if (_phase_invert[chn]) {
-                                       for (nframes_t nx = 0; nx < nframes; ++nx) {
+                                       for (pframes_t nx = 0; nx < nframes; ++nx) {
                                                sp[nx] = -sp[nx];
                                        }
                                }
@@ -463,7 +470,7 @@ Route::process_output_buffers (BufferSet& bufs,
 
                        for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
                                Sample* const sp = i->data();
-                               for (nframes_t nx = 0; nx < nframes; ++nx) {
+                               for (pframes_t nx = 0; nx < nframes; ++nx) {
                                        sp[nx] += 1.0e-27f;
                                }
                        }
@@ -477,6 +484,10 @@ Route::process_output_buffers (BufferSet& bufs,
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
 
+               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()
@@ -500,7 +511,7 @@ Route::n_process_buffers ()
 }
 
 void
-Route::passthru (framepos_t start_frame, framepos_t end_frame, nframes_t nframes, int declick)
+Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
 {
        BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
 
@@ -536,16 +547,16 @@ Route::passthru (framepos_t start_frame, framepos_t end_frame, nframes_t nframes
        }
 
        write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, true);
 }
 
 void
-Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, nframes_t nframes, int declick)
+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);
+       process_output_buffers (bufs, start_frame, end_frame, nframes, true, declick, false);
 }
 
 void
@@ -603,7 +614,7 @@ Route::set_solo (bool yn, void *src)
        }
 
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->apply (&Route::set_solo, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, _route_group));
                return;
        }
 
@@ -713,7 +724,7 @@ Route::set_solo_isolated (bool yn, void *src)
        }
 
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_solo()) {
-               _route_group->apply (&Route::set_solo_isolated, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, _route_group));
                return;
        }
        
@@ -773,6 +784,7 @@ Route::set_mute_points (MuteMaster::MutePoint mp)
         
         if (_mute_master->muted_by_self()) {
                 mute_changed (this); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
         }
 }
 
@@ -780,13 +792,14 @@ void
 Route::set_mute (bool yn, void *src)
 {
        if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_mute()) {
-               _route_group->apply (&Route::set_mute, yn, _route_group);
+               _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, _route_group));
                return;
        }
 
        if (muted() != yn) {
                 _mute_master->set_muted_by_self (yn);
                mute_changed (src); /* EMIT SIGNAL */
+               _mute_control->Changed (); /* EMIT SIGNAL */
        }
 }
 
@@ -876,13 +889,17 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorList::ite
                // Set up processor list channels.  This will set processor->[input|output]_streams(),
                // configure redirect ports properly, etc.
 
-               if (configure_processors_unlocked (err)) {
-                       ProcessorList::iterator ploc = loc;
-                       --ploc;
-                       _processors.erase(ploc);
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       cerr << "configure failed\n";
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       if (configure_processors_unlocked (err)) {
+                               ProcessorList::iterator ploc = loc;
+                               --ploc;
+                               _processors.erase(ploc);
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               cerr << "configure failed\n";
+                               return -1;
+                       }
                }
 
                if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
@@ -1051,10 +1068,13 @@ Route::add_processors (const ProcessorList& others, ProcessorList::iterator iter
                                (*i)->activate ();
                        }
 
-                       if (configure_processors_unlocked (err)) {
-                               _processors.erase (inserted);
-                               configure_processors_unlocked (0); // it worked before we tried to add it ...
-                               return -1;
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               if (configure_processors_unlocked (err)) {
+                                       _processors.erase (inserted);
+                                       configure_processors_unlocked (0); // it worked before we tried to add it ...
+                                       return -1;
+                               }
                        }
 
                        (*i)->ActiveChanged.connect_same_thread (*this, boost::bind (&Session::update_latency_compensation, &_session, false, false));
@@ -1259,7 +1279,11 @@ Route::clear_processors (Placement p)
                }
 
                _processors = new_list;
-               configure_processors_unlocked (&err); // this can't fail
+
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       configure_processors_unlocked (&err); // this can't fail
+               }
        }
 
        processor_max_streams.reset();
@@ -1333,12 +1357,16 @@ Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStream
                        return 1;
                }
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors.insert (i, processor);
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors.insert (i, processor);
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
@@ -1420,12 +1448,16 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
 
                _output->set_user_latency (0);
 
-               if (configure_processors_unlocked (err)) {
-                       /* get back to where we where */
-                       _processors = as_we_were;
-                       /* we know this will work, because it worked before :) */
-                       configure_processors_unlocked (0);
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               /* get back to where we where */
+                               _processors = as_we_were;
+                               /* we know this will work, because it worked before :) */
+                               configure_processors_unlocked (0);
+                               return -1;
+                       }
                }
 
                _have_internal_generator = false;
@@ -1454,10 +1486,12 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams*
        return 0;
 }
 
-
+/** Caller must hold process lock */
 int
 Route::configure_processors (ProcessorStreams* err)
 {
+       assert (!AudioEngine::instance()->process_lock().trylock());
+       
        if (!_in_configure_processors) {
                Glib::RWLock::WriterLock lm (_processor_lock);
                return configure_processors_unlocked (err);
@@ -1471,35 +1505,34 @@ Route::input_streams () const
         return _input->n_ports ();
 }
 
-/** Configure the input/output configuration of each processor in the processors list.
- * Return 0 on success, otherwise configuration is impossible.
- */
-int
-Route::configure_processors_unlocked (ProcessorStreams* err)
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors (ChanCount in, ProcessorStreams* err)
 {
-       if (_in_configure_processors) {
-          return 0;
-       }
+       Glib::RWLock::ReaderLock lm (_processor_lock);
 
-       _in_configure_processors = true;
+       return try_configure_processors_unlocked (in, err);
+}
 
+list<pair<ChanCount, ChanCount> >
+Route::try_configure_processors_unlocked (ChanCount in, ProcessorStreams* err)
+{
        // Check each processor in order to see if we can configure as requested
-       ChanCount in = input_streams ();
        ChanCount out;
-       list< pair<ChanCount,ChanCount> > configuration;
+       list<pair<ChanCount, ChanCount> > configuration;
        uint32_t index = 0;
 
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configure processors\n", _name));
        DEBUG_TRACE (DEBUG::Processors, "{\n");
-       for (list<boost::shared_ptr<Processor> >::const_iterator p = _processors.begin(); p != _processors.end(); ++p) {
-               DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID = %2\n", (*p)->name(), (*p)->id()));
-       }
-       DEBUG_TRACE (DEBUG::Processors, "}\n");
 
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++index) {
 
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       DEBUG_TRACE (DEBUG::Processors, "--- CONFIGURE ABORTED due to unknown processor.\n");
+                       break;
+               }
+               
                if ((*p)->can_support_io_configuration(in, out)) {
-                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 in = %2 out = %3\n",(*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("\t%1 ID=%2 in=%3 out=%4\n",(*p)->name(), (*p)->id(), in, out));
                        configuration.push_back(make_pair(in, out));
                        in = out;
                } else {
@@ -1507,14 +1540,49 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
                                err->index = index;
                                err->count = in;
                        }
-                       _in_configure_processors = false;
-                       return -1;
+                       DEBUG_TRACE (DEBUG::Processors, "---- CONFIGURATION FAILED.\n");
+                       DEBUG_TRACE (DEBUG::Processors, string_compose ("---- %1 cannot support in=%2 out=%3\n", (*p)->name(), in, out));
+                       DEBUG_TRACE (DEBUG::Processors, "}\n");
+                       return list<pair<ChanCount, ChanCount> > ();
                }
        }
 
-       // We can, so configure everything
+       DEBUG_TRACE (DEBUG::Processors, "}\n");
+       
+       return configuration;
+}
+
+/** Set the input/output configuration of each processor in the processors list.
+ *  Caller must hold process lock.
+ *  Return 0 on success, otherwise configuration is impossible.
+ */
+int
+Route::configure_processors_unlocked (ProcessorStreams* err)
+{
+       assert (!AudioEngine::instance()->process_lock().trylock());
+
+       if (_in_configure_processors) {
+               return 0;
+       }
+
+       _in_configure_processors = true;
+
+       list<pair<ChanCount, ChanCount> > configuration = try_configure_processors_unlocked (input_streams (), err);
+
+       if (configuration.empty ()) {
+               _in_configure_processors = false;
+               return -1;
+       }
+
+       ChanCount out;
+
        list< pair<ChanCount,ChanCount> >::iterator c = configuration.begin();
        for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p, ++c) {
+
+               if (boost::dynamic_pointer_cast<UnknownProcessor> (*p)) {
+                       break;
+               }
+               
                (*p)->configure_io(c->first, c->second);
                processor_max_streams = ChanCount::max(processor_max_streams, c->first);
                processor_max_streams = ChanCount::max(processor_max_streams, c->second);
@@ -1527,10 +1595,7 @@ Route::configure_processors_unlocked (ProcessorStreams* err)
 
        /* make sure we have sufficient scratch buffers to cope with the new processor
           configuration */
-       {
-               Glib::Mutex::Lock em (_session.engine().process_lock ());
-               _session.ensure_buffers (n_process_buffers ());
-       }
+       _session.ensure_buffers (n_process_buffers ());
 
        DEBUG_TRACE (DEBUG::Processors, string_compose ("%1: configuration complete\n", _name));
 
@@ -1686,10 +1751,14 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err
 
                _processors.insert (oiter, as_it_will_be.begin(), as_it_will_be.end());
 
-               if (configure_processors_unlocked (err)) {
-                       _processors = as_it_was_before;
-                       processor_max_streams = old_pms;
-                       return -1;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               
+                       if (configure_processors_unlocked (err)) {
+                               _processors = as_it_was_before;
+                               processor_max_streams = old_pms;
+                               return -1;
+                       }
                }
        }
 
@@ -1769,6 +1838,7 @@ Route::state(bool full_state)
        node->add_child_nocopy (_input->state (full_state));
        node->add_child_nocopy (_output->state (full_state));
        node->add_child_nocopy (_solo_control->get_state ());
+       node->add_child_nocopy (_mute_control->get_state ());
        node->add_child_nocopy (_mute_master->get_state ());
 
        XMLNode* remote_control_node = new XMLNode (X_("RemoteControl"));
@@ -1950,11 +2020,9 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/)
 
                        _extra_xml = new XMLNode (*child);
 
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
                        if (prop->value() == "solo") {
                                _solo_control->set_state (*child, version);
-                               _session.add_controllable (_solo_control);
                        }
 
                } else if (child->name() == X_("RemoteControl")) {
@@ -2201,13 +2269,13 @@ Route::_set_state_2X (const XMLNode& node, int version)
 
                        _extra_xml = new XMLNode (*child);
 
-               } else if (child->name() == X_("Controllable") && (prop = child->property("name")) != 0) {
-
-                       if (prop->value() == "solo") {
+               } else if (child->name() == Controllable::xml_node_name && (prop = child->property("name")) != 0) {
+                       if (prop->value() == X_("solo")) {
                                _solo_control->set_state (*child, version);
-                               _session.add_controllable (_solo_control);
-                       }
-
+                       } else if (prop->value() == X_("mute")) {
+                               _mute_control->set_state (*child, version);
+                        }
+                                
                } else if (child->name() == X_("RemoteControl")) {
                        if ((prop = child->property (X_("id"))) != 0) {
                                int32_t x;
@@ -2323,10 +2391,13 @@ Route::set_processor_state (const XMLNode& node)
                                         continue;
                                 }
 
-                               if (processor->set_state (**niter, Stateful::current_state_version) == 0) {
-                                       new_order.push_back (processor);
-                                       must_configure = true;
+                               if (processor->set_state (**niter, Stateful::current_state_version) != 0) {
+                                       /* This processor could not be configured.  Turn it into a UnknownProcessor */
+                                       processor.reset (new UnknownProcessor (_session, **niter));
                                }
+                                       
+                               new_order.push_back (processor);
+                               must_configure = true;
                         }
                 }
         }
@@ -2335,6 +2406,7 @@ Route::set_processor_state (const XMLNode& node)
                Glib::RWLock::WriterLock lm (_processor_lock);
                 _processors = new_order;
                 if (must_configure) {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                         configure_processors_unlocked (0);
                 }
         }
@@ -2351,7 +2423,7 @@ Route::curve_reallocate ()
 }
 
 void
-Route::silence (nframes_t nframes)
+Route::silence (framecnt_t nframes)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
        if (!lm.locked()) {
@@ -2362,7 +2434,7 @@ Route::silence (nframes_t nframes)
 }
 
 void
-Route::silence_unlocked (nframes_t nframes)
+Route::silence_unlocked (framecnt_t nframes)
 {
        /* Must be called with the processor lock held */
        
@@ -2620,8 +2692,9 @@ Route::direct_feeds (boost::shared_ptr<Route> other, bool* only_send)
        return false;
 }
 
+/** Called from the (non-realtime) butler thread when the transport is stopped */
 void
-Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
+Route::nonrealtime_handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool can_flush_processors)
 {
        framepos_t now = _session.transport_frame();
 
@@ -2647,6 +2720,7 @@ Route::handle_transport_stopped (bool /*abort_ignored*/, bool did_locate, bool c
        _roll_delay = _initial_delay;
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
 Route::input_change_handler (IOChange change, void * /*src*/)
 {
@@ -2657,6 +2731,7 @@ Route::input_change_handler (IOChange change, void * /*src*/)
        }
 }
 
+/** Called with the process lock held if change contains ConfigurationChanged */
 void
 Route::output_change_handler (IOChange change, void * /*src*/)
 {
@@ -2706,7 +2781,7 @@ Route::pans_required () const
 }
 
 int
-Route::no_roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame,
+Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame,
                bool session_state_changing, bool /*can_record*/, bool /*rec_monitors_input*/)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
@@ -2743,8 +2818,8 @@ Route::no_roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame,
        return 0;
 }
 
-nframes_t
-Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
+framecnt_t
+Route::check_initial_delay (framecnt_t nframes, framecnt_t& transport_frame)
 {
        if (_roll_delay > nframes) {
 
@@ -2771,7 +2846,7 @@ Route::check_initial_delay (nframes_t nframes, nframes_t& transport_frame)
 }
 
 int
-Route::roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
+Route::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, int declick,
             bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
@@ -2790,7 +2865,7 @@ Route::roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame, in
                return 0;
        }
 
-       nframes_t unused = 0;
+       framecnt_t unused = 0;
 
        if ((nframes = check_initial_delay (nframes, unused)) == 0) {
                return 0;
@@ -2804,7 +2879,7 @@ Route::roll (nframes_t nframes, framepos_t start_frame, framepos_t end_frame, in
 }
 
 int
-Route::silent_roll (nframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
+Route::silent_roll (pframes_t nframes, framepos_t /*start_frame*/, framepos_t /*end_frame*/,
                    bool /*can_record*/, bool /*rec_monitors_input*/, bool& /* need_butler */)
 {
        silence (nframes);
@@ -2958,10 +3033,14 @@ Route::put_monitor_send_at (Placement p)
                
                _processors.insert (loc, _monitor_send);
 
-               if (configure_processors_unlocked (0)) {
-                       _processors = as_it_was;
-                       configure_processors_unlocked (0); // it worked before we tried to add it ...
-                       return;
+               {
+                       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       
+                       if (configure_processors_unlocked (0)) {
+                               _processors = as_it_was;
+                               configure_processors_unlocked (0); // it worked before we tried to add it ...
+                               return;
+                       }
                }
        }
 
@@ -2969,11 +3048,11 @@ Route::put_monitor_send_at (Placement p)
        _session.set_dirty ();
 }
 
-nframes_t
+framecnt_t
 Route::update_total_latency ()
 {
-       nframes_t old = _output->effective_latency();
-       nframes_t own_latency = _output->user_latency();
+       framecnt_t old = _output->effective_latency();
+       framecnt_t own_latency = _output->user_latency();
 
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                if ((*i)->active ()) {
@@ -3008,16 +3087,16 @@ Route::update_total_latency ()
 }
 
 void
-Route::set_user_latency (nframes_t nframes)
+Route::set_user_latency (framecnt_t nframes)
 {
        _output->set_user_latency (nframes);
        _session.update_latency_compensation (false, false);
 }
 
 void
-Route::set_latency_delay (nframes_t longest_session_latency)
+Route::set_latency_delay (framecnt_t longest_session_latency)
 {
-       nframes_t old = _initial_delay;
+       framecnt_t old = _initial_delay;
 
        if (_output->effective_latency() < longest_session_latency) {
                _initial_delay = longest_session_latency - _output->effective_latency();
@@ -3035,7 +3114,7 @@ Route::set_latency_delay (nframes_t longest_session_latency)
 }
 
 void
-Route::automation_snapshot (nframes_t now, bool force)
+Route::automation_snapshot (framepos_t now, bool force)
 {
        panner()->automation_snapshot (now, force);
        
@@ -3114,7 +3193,7 @@ Route::MuteControllable::get_value (void) const
 }
 
 void
-Route::set_block_size (nframes_t nframes)
+Route::set_block_size (pframes_t nframes)
 {
        for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
                (*i)->set_block_size (nframes);
@@ -3153,44 +3232,54 @@ Route::set_pending_declick (int declick)
  */
 
 void
-Route::shift (framepos_t /*pos*/, framecnt_t /*frames*/)
+Route::shift (framepos_t pos, framecnt_t frames)
 {
-#ifdef THIS_NEEDS_FIXING_FOR_V3
-
        /* gain automation */
-       XMLNode &before = _gain_control->get_state ();
-       _gain_control->shift (pos, frames);
-       XMLNode &after = _gain_control->get_state ();
-       _session.add_command (new MementoCommand<AutomationList> (_gain_automation_curve, &before, &after));
+        {
+                boost::shared_ptr<AutomationControl> gc = _amp->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));
+        }
 
        /* pan automation */
-       for (std::vector<StreamPanner*>::iterator i = _panner->begin (); i != _panner->end (); ++i) {
-               Curve & c = (*i)->automation ();
-               XMLNode &before = c.get_state ();
-               c.shift (pos, frames);
-               XMLNode &after = c.get_state ();
-               _session.add_command (new MementoCommand<AutomationList> (c, &before, &after));
-       }
+        {
+                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);
+                        if (pc) {
+                                boost::shared_ptr<AutomationList> al = pc->alist();
+                                XMLNode& before = al->get_state ();
+                                al->shift (pos, frames);
+                                XMLNode& after = al->get_state ();
+                                _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                        }
+                }
+        }
 
        /* redirect automation */
        {
-               Glib::RWLock::ReaderLock lm (redirect_lock);
-               for (RedirectList::iterator i = _redirects.begin (); i != _redirects.end (); ++i) {
-
-                       set<uint32_t> a;
-                       (*i)->what_has_automation (a);
-
-                       for (set<uint32_t>::const_iterator j = a.begin (); j != a.end (); ++j) {
-                               AutomationList & al = (*i)->automation_list (*j);
-                               XMLNode &before = al.get_state ();
-                               al.shift (pos, frames);
-                               XMLNode &after = al.get_state ();
-                               _session.add_command (new MementoCommand<AutomationList> (al, &before, &after));
-                       }
-               }
+               Glib::RWLock::ReaderLock lm (_processor_lock);
+               for (ProcessorList::iterator i = _processors.begin (); i != _processors.end (); ++i) {
+
+                       set<Evoral::Parameter> parameters = (*i)->what_can_be_automated();
+
+                       for (set<Evoral::Parameter>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
+                                boost::shared_ptr<AutomationControl> ac = (*i)->automation_control (*p);
+                                if (ac) {
+                                        boost::shared_ptr<AutomationList> al = ac->alist();
+                                        XMLNode &before = al->get_state ();
+                                        al->shift (pos, frames);
+                                        XMLNode &after = al->get_state ();
+                                        _session.add_command (new MementoCommand<AutomationList> (*al.get(), &before, &after));
+                                }
+                        }
+                }
        }
-#endif
-
 }
 
 
@@ -3456,3 +3545,33 @@ Route::set_processor_positions ()
        }
 }
 
+/** Called when there is a proposed change to the input port count */
+bool
+Route::input_port_count_changing (ChanCount to)
+{
+       list<pair<ChanCount, ChanCount> > c = try_configure_processors (to, 0);
+       if (c.empty()) {
+               /* The processors cannot be configured with the new input arrangement, so
+                  block the change.
+               */
+               return true;
+       }
+
+       /* The change is ok */
+       return false;
+}
+
+list<string>
+Route::unknown_processors () const
+{
+       list<string> p;
+       
+       Glib::RWLock::ReaderLock lm (_processor_lock);
+       for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+               if (boost::dynamic_pointer_cast<UnknownProcessor const> (*i)) {
+                       p.push_back ((*i)->name ());
+               }
+       }
+
+       return p;
+}