fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / session.cc
index c20a7709ce88b636de17335cf9490962abf08150..61bfce14b6bfc995f0203b44e42bdf17652e60d8 100644 (file)
 #include <boost/algorithm/string/erase.hpp>
 
 #include "pbd/basename.h"
-#include "pbd/boost_debug.h"
 #include "pbd/convert.h"
 #include "pbd/convert.h"
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
 #include "pbd/md5.h"
+#include "pbd/pthread_utils.h"
 #include "pbd/search_path.h"
 #include "pbd/stacktrace.h"
 #include "pbd/stl_delete.h"
@@ -58,6 +58,7 @@
 #include "ardour/audioengine.h"
 #include "ardour/audiofilesource.h"
 #include "ardour/auditioner.h"
+#include "ardour/boost_debug.h"
 #include "ardour/buffer_manager.h"
 #include "ardour/buffer_set.h"
 #include "ardour/bundle.h"
@@ -73,6 +74,7 @@
 #include "ardour/filename_extensions.h"
 #include "ardour/gain_control.h"
 #include "ardour/graph.h"
+#include "ardour/luabindings.h"
 #include "ardour/midiport_manager.h"
 #include "ardour/scene_changer.h"
 #include "ardour/midi_patch_manager.h"
 #include "ardour/recent_sessions.h"
 #include "ardour/region.h"
 #include "ardour/region_factory.h"
+#include "ardour/revision.h"
 #include "ardour/route_graph.h"
 #include "ardour/route_group.h"
-#include "ardour/route_sorters.h"
 #include "ardour/send.h"
 #include "ardour/session.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_playlists.h"
 #include "ardour/smf_source.h"
+#include "ardour/solo_isolate_control.h"
 #include "ardour/source_factory.h"
 #include "ardour/speakers.h"
 #include "ardour/tempo.h"
+#include "ardour/ticker.h"
 #include "ardour/track.h"
 #include "ardour/user_bundle.h"
 #include "ardour/utils.h"
+#include "ardour/vca_manager.h"
+#include "ardour/vca.h"
 
 #include "midi++/port.h"
 #include "midi++/mmc.h"
 
-#include "i18n.h"
+#include "LuaBridge/LuaBridge.h"
+
+#include "pbd/i18n.h"
 
 #include <glibmm/checksum.h>
 
@@ -122,11 +130,13 @@ using namespace PBD;
 
 bool Session::_disable_all_loaded_plugins = false;
 bool Session::_bypass_all_loaded_plugins = false;
+guint Session::_name_id_counter = 0;
 
 PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
 PBD::Signal1<void,std::string> Session::Dialog;
 PBD::Signal0<int> Session::AskAboutPendingState;
 PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
+PBD::Signal2<void, framecnt_t, framecnt_t> Session::NotifyAboutSampleRateMismatch;
 PBD::Signal0<void> Session::SendFeedback;
 PBD::Signal3<int,Session*,std::string,DataType> Session::MissingFile;
 
@@ -162,12 +172,13 @@ Session::Session (AudioEngine &eng,
        , _bounce_processing_active (false)
        , waiting_for_sync_offset (false)
        , _base_frame_rate (0)
-       , _current_frame_rate (0)
        , _nominal_frame_rate (0)
+       , _current_frame_rate (0)
        , transport_sub_state (0)
        , _record_status (Disabled)
        , _transport_frame (0)
        , _session_range_location (0)
+       , _session_range_end_is_free (true)
        , _slave (0)
        , _silent (false)
        , _transport_speed (0)
@@ -201,8 +212,10 @@ Session::Session (AudioEngine &eng,
        , post_export_sync (false)
        , post_export_position (0)
        , _exporting (false)
-       , _export_started (false)
        , _export_rolling (false)
+       , _realtime_export (false)
+       , _export_preroll (0)
+       , _export_latency (0)
        , _pre_export_mmc_enabled (false)
        , _name (snapshot_name)
        , _is_new (true)
@@ -227,6 +240,9 @@ Session::Session (AudioEngine &eng,
        , pending_locate_flush (false)
        , pending_abort (false)
        , pending_auto_loop (false)
+       , _mempool ("Session", 2097152)
+       , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
+       , _n_lua_scripts (0)
        , _butler (new Butler (*this))
        , _post_transport_work (0)
        ,  cumulative_rf_motion (0)
@@ -235,6 +251,8 @@ Session::Session (AudioEngine &eng,
        , _ignore_skips_updates (false)
        , _rt_thread_active (false)
        , _rt_emit_pending (false)
+       , _ac_thread_active (false)
+       , _latency_recompute_pending (0)
        , step_speed (0)
        , outbound_mtc_timecode_frame (0)
        , next_quarter_frame_to_send (-1)
@@ -290,23 +308,34 @@ Session::Session (AudioEngine &eng,
        , first_file_header_format_reset (true)
        , have_looped (false)
        , _have_rec_enabled_track (false)
-    , _have_rec_disabled_track (true)
+       , _have_rec_disabled_track (true)
        , _step_editors (0)
        , _suspend_timecode_transmission (0)
        ,  _speakers (new Speakers)
-       , _order_hint (-1)
        , ignore_route_processor_changes (false)
+       , midi_clock (0)
        , _scene_changer (0)
        , _midi_ports (0)
        , _mmc (0)
+       , _vca_manager (new VCAManager (*this))
 {
        uint32_t sr = 0;
 
+       created_with = string_compose ("%1 %2", PROGRAM_NAME, revision);
+
        pthread_mutex_init (&_rt_emit_mutex, 0);
        pthread_cond_init (&_rt_emit_cond, 0);
 
+       pthread_mutex_init (&_auto_connect_mutex, 0);
+       pthread_cond_init (&_auto_connect_cond, 0);
+
+       init_name_id_counter (1); // reset for new sessions, start at 1
+       VCA::set_next_vca_number (1); // reset for new sessions, start at 1
+
        pre_engine_init (fullpath);
 
+       setup_lua ();
+
        if (_is_new) {
 
                Stateful::loading_state_version = CURRENT_SESSION_FILE_VERSION;
@@ -314,7 +343,7 @@ Session::Session (AudioEngine &eng,
 #ifdef USE_TRACKS_CODE_FEATURES
                sr = EngineStateController::instance()->get_current_sample_rate();
 #endif
-               if (ensure_engine (sr)) {
+               if (ensure_engine (sr, true)) {
                        destroy ();
                        throw SessionException (_("Cannot connect to audio/midi engine"));
                }
@@ -362,13 +391,14 @@ Session::Session (AudioEngine &eng,
                 */
 
                if (state_tree) {
-                       const XMLProperty* prop;
-                       if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) {
+                       XMLProperty const * prop;
+                       XMLNode const * root (state_tree->root());
+                       if ((prop = root->property (X_("sample-rate"))) != 0) {
                                sr = atoi (prop->value());
                        }
                }
 
-               if (ensure_engine (sr)) {
+               if (ensure_engine (sr, false)) {
                        destroy ();
                        throw SessionException (_("Cannot connect to audio/midi engine"));
                }
@@ -396,6 +426,7 @@ Session::Session (AudioEngine &eng,
        EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
 
        emit_thread_start ();
+       auto_connect_thread_start ();
 
        /* hook us up to the engine since we are now completely constructed */
 
@@ -461,8 +492,26 @@ Session::~Session ()
        destroy ();
 }
 
+unsigned int
+Session::next_name_id ()
+{
+       return g_atomic_int_add (&_name_id_counter, 1);
+}
+
+unsigned int
+Session::name_id_counter ()
+{
+       return g_atomic_int_get (&_name_id_counter);
+}
+
+void
+Session::init_name_id_counter (guint n)
+{
+       g_atomic_int_set (&_name_id_counter, n);
+}
+
 int
-Session::ensure_engine (uint32_t desired_sample_rate)
+Session::ensure_engine (uint32_t desired_sample_rate, bool isnew)
 {
        if (_engine.current_backend() == 0) {
                /* backend is unknown ... */
@@ -470,6 +519,8 @@ Session::ensure_engine (uint32_t desired_sample_rate)
                if (r.get_value_or (-1) != 0) {
                        return -1;
                }
+       } else if (!isnew && _engine.running() && _engine.sample_rate () == desired_sample_rate) {
+               /* keep engine */
        } else if (_engine.setup_required()) {
                /* backend is known, but setup is needed */
                boost::optional<int> r = AudioEngineSetupRequired (desired_sample_rate);
@@ -482,8 +533,7 @@ Session::ensure_engine (uint32_t desired_sample_rate)
                }
        }
 
-       /* at this point the engine should be running
-       */
+       /* at this point the engine should be running */
 
        if (!_engine.running()) {
                return -1;
@@ -521,6 +571,7 @@ Session::immediately_post_engine ()
        }
 
        try {
+               LocaleGuard lg;
                BootMessage (_("Set up LTC"));
                setup_ltc ();
                BootMessage (_("Set up Click"));
@@ -555,8 +606,12 @@ Session::destroy ()
 
        _state_of_the_state = StateOfTheState (CannotSave|Deletion);
 
+       /* stop autoconnecting */
+       auto_connect_thread_terminate ();
+
        /* disconnect from any and all signals that we are connected to */
 
+       Port::PortSignalDrop (); /* EMIT SIGNAL */
        drop_connections ();
 
        /* shutdown control surface protocols while we still have ports
@@ -590,8 +645,19 @@ Session::destroy ()
        delete state_tree;
        state_tree = 0;
 
-       /* reset dynamic state version back to default */
+       // unregister all lua functions, drop held references (if any)
+       (*_lua_cleanup)();
+       lua.do_command ("Session = nil");
+       delete _lua_run;
+       delete _lua_add;
+       delete _lua_del;
+       delete _lua_list;
+       delete _lua_save;
+       delete _lua_load;
+       delete _lua_cleanup;
+       lua.collect_garbage ();
 
+       /* reset dynamic state version back to default */
        Stateful::loading_state_version = 0;
 
        _butler->drop_references ();
@@ -638,6 +704,11 @@ Session::destroy ()
        DEBUG_TRACE (DEBUG::Destruction, "delete regions\n");
        RegionFactory::delete_all_regions ();
 
+       /* Do this early so that VCAs no longer hold references to routes */
+
+       DEBUG_TRACE (DEBUG::Destruction, "delete vcas\n");
+       delete _vca_manager;
+
        DEBUG_TRACE (DEBUG::Destruction, "delete routes\n");
 
        /* reset these three references to special routes before we do the usual route delete thing */
@@ -678,6 +749,9 @@ Session::destroy ()
        pthread_cond_destroy (&_rt_emit_cond);
        pthread_mutex_destroy (&_rt_emit_mutex);
 
+       pthread_cond_destroy (&_auto_connect_cond);
+       pthread_mutex_destroy (&_auto_connect_mutex);
+
        delete _scene_changer; _scene_changer = 0;
        delete midi_control_ui; midi_control_ui = 0;
 
@@ -685,13 +759,48 @@ Session::destroy ()
        delete _midi_ports; _midi_ports = 0;
        delete _locations; _locations = 0;
 
+       delete midi_clock;
        delete _tempo_map;
 
+       /* clear event queue, the session is gone, nobody is interested in
+        * those anymore, but they do leak memory if not removed
+        */
+       while (!immediate_events.empty ()) {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               SessionEvent *ev = immediate_events.front ();
+               DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("Drop event: %1\n", enum_2_string (ev->type)));
+               immediate_events.pop_front ();
+               bool remove = true;
+               bool del = true;
+               switch (ev->type) {
+                       case SessionEvent::AutoLoop:
+                       case SessionEvent::AutoLoopDeclick:
+                       case SessionEvent::Skip:
+                       case SessionEvent::PunchIn:
+                       case SessionEvent::PunchOut:
+                       case SessionEvent::StopOnce:
+                       case SessionEvent::RangeStop:
+                       case SessionEvent::RangeLocate:
+                               remove = false;
+                               del = false;
+                               break;
+                       case SessionEvent::RealTimeOperation:
+                               process_rtop (ev);
+                               del = false;
+                       default:
+                               break;
+               }
+               if (remove) {
+                       del = del && !_remove_event (ev);
+               }
+               if (del) {
+                       delete ev;
+               }
+       }
+
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-       boost_debug_list_ptrs ();
-#endif
+       BOOST_SHOW_POINTERS ();
 }
 
 void
@@ -736,7 +845,7 @@ Session::setup_click ()
        _clicking = false;
 
        boost::shared_ptr<AutomationList> gl (new AutomationList (Evoral::Parameter (GainAutomation)));
-       boost::shared_ptr<AutomationControl> gain_control = boost::shared_ptr<GainControl> (new GainControl (*this, Evoral::Parameter(GainAutomation), gl));
+       boost::shared_ptr<GainControl> gain_control = boost::shared_ptr<GainControl> (new GainControl (*this, Evoral::Parameter(GainAutomation), gl));
 
        _click_io.reset (new ClickIO (*this, X_("Click")));
        _click_gain.reset (new Amp (*this, _("Fader"), gain_control, true));
@@ -1023,11 +1132,16 @@ Session::remove_monitor_section ()
        }
 
        remove_route (_monitor_out);
+       if (_state_of_the_state & Deletion) {
+               return;
+       }
+
        auto_connect_master_bus ();
 
        if (auditioner) {
                auditioner->connect ();
        }
+
        Config->ParameterChanged ("use-monitor-bus");
 }
 
@@ -1040,15 +1154,14 @@ Session::add_monitor_section ()
                return;
        }
 
-       boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), Route::MonitorOut, DataType::AUDIO));
+       boost::shared_ptr<Route> r (new Route (*this, _("Monitor"), PresentationInfo::MonitorOut, DataType::AUDIO));
 
        if (r->init ()) {
                return;
        }
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-       // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
+       BOOST_MARK_ROUTE(r);
+
        try {
                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                r->input()->ensure_io (_master_out->output()->n_ports(), false, this);
@@ -1059,7 +1172,7 @@ Session::add_monitor_section ()
        }
 
        rl.push_back (r);
-       add_routes (rl, false, false, false);
+       add_routes (rl, false, false, false, 0);
 
        assert (_monitor_out);
 
@@ -1099,7 +1212,7 @@ Session::add_monitor_section ()
        /* if monitor section is not connected, connect it to physical outs
         */
 
-       if (Config->get_auto_connect_standard_busses() && !_monitor_out->output()->connected ()) {
+       if ((Config->get_auto_connect_standard_busses () || Profile->get_mixbus ()) && !_monitor_out->output()->connected ()) {
 
                if (!Config->get_monitor_bus_preferred_bundle().empty()) {
 
@@ -1205,8 +1318,12 @@ Session::reset_monitor_section ()
        _master_out->output()->disconnect (this);
        _monitor_out->output()->disconnect (this);
 
-       _monitor_out->input()->ensure_io (_master_out->output()->n_ports(), false, this);
-       _monitor_out->output()->ensure_io (_master_out->output()->n_ports(), false, this);
+       // monitor section follow master bus - except midi
+       ChanCount mon_chn (_master_out->output()->n_ports());
+       mon_chn.set_midi (0);
+
+       _monitor_out->input()->ensure_io (mon_chn, false, this);
+       _monitor_out->output()->ensure_io (mon_chn, false, this);
 
        for (uint32_t n = 0; n < limit; ++n) {
                boost::shared_ptr<AudioPort> p = _monitor_out->input()->ports().nth_audio_port (n);
@@ -1401,7 +1518,7 @@ Session::set_track_monitor_input_status (bool yn)
        boost::shared_ptr<RouteList> rl = routes.reader ();
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
-               if (tr && tr->record_enabled ()) {
+               if (tr && tr->rec_enable_control()->get_value()) {
                        //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
                        tr->request_input_monitoring (yn);
                }
@@ -1832,6 +1949,13 @@ Session::enable_record ()
        }
 }
 
+void
+Session::set_all_tracks_record_enabled (bool enable )
+{
+       boost::shared_ptr<RouteList> rl = routes.reader();
+       set_controls (route_list_to_control_list (rl, &Stripable::rec_enable_control), enable, Controllable::NoGroup);
+}
+
 void
 Session::disable_record (bool rt_context, bool force)
 {
@@ -1906,59 +2030,55 @@ framepos_t
 Session::audible_frame () const
 {
        framepos_t ret;
-       framepos_t tf;
-       framecnt_t offset;
 
-       offset = worst_playback_latency ();
+       frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start ();
+       offset *= transport_speed ();
 
        if (synced_to_engine()) {
                /* Note: this is basically just sync-to-JACK */
-               tf = _engine.transport_frame();
+               ret = _engine.transport_frame();
        } else {
-               tf = _transport_frame;
+               ret = _transport_frame;
        }
 
-       ret = tf;
-
-       if (!non_realtime_work_pending()) {
-
-               /* MOVING */
+       if (transport_rolling()) {
+               ret -= offset;
 
                /* Check to see if we have passed the first guaranteed
-                  audible frame past our last start position. if not,
-                  return that last start point because in terms
-                  of audible frames, we have not moved yet.
-
-                  `Start position' in this context means the time we last
-                  either started, located, or changed transport direction.
-               */
+                * audible frame past our last start position. if not,
+                * return that last start point because in terms
+                * of audible frames, we have not moved yet.
+                *
+                * `Start position' in this context means the time we last
+                * either started, located, or changed transport direction.
+                */
 
                if (_transport_speed > 0.0f) {
 
                        if (!play_loop || !have_looped) {
-                               if (tf < _last_roll_or_reversal_location + offset) {
+                               if (ret < _last_roll_or_reversal_location) {
                                        return _last_roll_or_reversal_location;
                                }
+                       } else {
+                               // latent loops
+                               Location *location = _locations->auto_loop_location();
+                               frameoffset_t lo = location->start() - ret;
+                               if (lo > 0) {
+                                       ret = location->end () - lo;
+                               }
                        }
 
-
-                       /* forwards */
-                       ret -= offset;
-
                } else if (_transport_speed < 0.0f) {
 
                        /* XXX wot? no backward looping? */
 
-                       if (tf > _last_roll_or_reversal_location - offset) {
+                       if (ret > _last_roll_or_reversal_location) {
                                return _last_roll_or_reversal_location;
-                       } else {
-                               /* backwards */
-                               ret += offset;
                        }
                }
        }
 
-       return ret;
+       return std::max ((framepos_t)0, ret);
 }
 
 void
@@ -1971,7 +2091,12 @@ Session::set_frame_rate (framecnt_t frames_per_second)
                here.
        */
 
-       _base_frame_rate = frames_per_second;
+       if (_base_frame_rate == 0) {
+               _base_frame_rate = frames_per_second;
+       }
+       else if (_base_frame_rate != frames_per_second && frames_per_second != _nominal_frame_rate) {
+               NotifyAboutSampleRateMismatch (_base_frame_rate, frames_per_second);
+       }
        _nominal_frame_rate = frames_per_second;
 
        sync_time_vars();
@@ -2094,16 +2219,18 @@ Session::resort_routes ()
        }
 
 #ifndef NDEBUG
-       boost::shared_ptr<RouteList> rl = routes.reader ();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
+       if (DEBUG_ENABLED(DEBUG::Graph)) {
+               boost::shared_ptr<RouteList> rl = routes.reader ();
+               for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+                       DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
 
-               const Route::FedBy& fb ((*i)->fed_by());
+                       const Route::FedBy& fb ((*i)->fed_by());
 
-               for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
-                       boost::shared_ptr<Route> sf = f->r.lock();
-                       if (sf) {
-                               DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+                       for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
+                               boost::shared_ptr<Route> sf = f->r.lock();
+                               if (sf) {
+                                       DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+                               }
                        }
                }
        }
@@ -2185,8 +2312,7 @@ Session::resort_routes_using (boost::shared_ptr<RouteList> r)
 #ifndef NDEBUG
                DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n",
-                                                                  (*i)->name(), (*i)->order_key ()));
+                       DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 presentation order %2\n", (*i)->name(), (*i)->presentation_info().order()));
                }
 #endif
 
@@ -2304,8 +2430,10 @@ Session::default_track_name_pattern (DataType t)
  *  @param instrument plugin info for the instrument to insert pre-fader, if any
  */
 list<boost::shared_ptr<MidiTrack> >
-Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr<PluginInfo> instrument,
-                        TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template)
+Session::new_midi_track (const ChanCount& input, const ChanCount& output,
+                         boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+                         RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order,
+                         TrackMode mode)
 {
        string track_name;
        uint32_t track_id = 0;
@@ -2325,17 +2453,20 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
                boost::shared_ptr<MidiTrack> track;
 
                try {
-                       track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode));
+                       track.reset (new MidiTrack (*this, track_name, mode));
 
                        if (track->init ()) {
                                goto failed;
                        }
 
+                       if (Profile->get_mixbus ()) {
+                               track->set_strict_io (true);
+                       }
+
                        track->use_new_diskstream();
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+                       BOOST_MARK_TRACK (track);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                if (track->input()->ensure_io (input, false, this)) {
@@ -2357,14 +2488,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
 
                        track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
 
-                       if (Config->get_remote_model() == UserOrdered) {
-                               track->set_remote_control_id (next_control_id());
-                       }
-
                        new_routes.push_back (track);
                        ret.push_back (track);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
                }
 
                catch (failed_constructor &err) {
@@ -2385,14 +2510,17 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (new_routes, false, false, false);
+                       add_routes (new_routes, false, false, false, order);
                } else {
-                       add_routes (new_routes, true, true, false);
+                       add_routes (new_routes, true, true, false, order);
                }
 
                if (instrument) {
                        for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) {
                                PluginPtr plugin = instrument->load (*this);
+                               if (pset) {
+                                       plugin->load_preset (*pset);
+                               }
                                boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
                                (*r)->add_processor (p, PreFader);
 
@@ -2403,154 +2531,118 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
        return ret;
 }
 
-void
-Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::weak_ptr<Route> wmt)
+RouteList
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+                         PresentationInfo::Flag flag, PresentationInfo::order_t order)
 {
-        boost::shared_ptr<Route> midi_track (wmt.lock());
-
-        if (!midi_track) {
-                return;
-        }
-
-       if ((change.type & IOChange::ConfigurationChanged) && Config->get_output_auto_connect() != ManualConnect) {
-
-                if (change.after.n_audio() <= change.before.n_audio()) {
-                        return;
-                }
-
-                /* new audio ports: make sure the audio goes somewhere useful,
-                   unless the user has no-auto-connect selected.
-
-                   The existing ChanCounts don't matter for this call as they are only
-                   to do with matching input and output indices, and we are only changing
-                   outputs here.
-                */
+       string bus_name;
+       uint32_t bus_id = 0;
+       string port;
+       RouteList ret;
 
-                ChanCount dummy;
+       bool const use_number = (how_many != 1) || name_template.empty () || name_template == _("Midi Bus");
 
-                auto_connect_route (midi_track, dummy, dummy, false, false, ChanCount(), change.before);
-        }
-}
+       while (how_many) {
+               if (!find_route_name (name_template.empty () ? _("Midi Bus") : name_template, ++bus_id, bus_name, use_number)) {
+                       error << "cannot find name for new midi bus" << endmsg;
+                       goto failure;
+               }
 
-/** @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs.
- *  @param input_start Where to start from when auto-connecting inputs; e.g. if this is 0, auto-connect starting from input 0.
- *  @param output_start As \a input_start, but for outputs.
- */
-void
-Session::auto_connect_route (boost::shared_ptr<Route> route, ChanCount& existing_inputs, ChanCount& existing_outputs,
-                             bool with_lock, bool connect_inputs, ChanCount input_start, ChanCount output_start)
-{
-       if (!IO::connecting_legal) {
-               return;
-       }
+               try {
+                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, flag, DataType::AUDIO)); // XXX Editor::add_routes is not ready for ARDOUR::DataType::MIDI
 
-       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK);
+                       if (bus->init ()) {
+                               goto failure;
+                       }
 
-       if (with_lock) {
-               lm.acquire ();
-       }
+                       if (Profile->get_mixbus ()) {
+                               bus->set_strict_io (true);
+                       }
 
-       /* If both inputs and outputs are auto-connected to physical ports,
-          use the max of input and output offsets to ensure auto-connected
-          port numbers always match up (e.g. the first audio input and the
-          first audio output of the route will have the same physical
-          port number).  Otherwise just use the lowest input or output
-          offset possible.
-       */
+                       BOOST_MARK_ROUTE(bus);
 
-       DEBUG_TRACE (DEBUG::Graph,
-                    string_compose("Auto-connect: existing in = %1 out = %2\n",
-                                   existing_inputs, existing_outputs));
+                       {
+                               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
-       const bool in_out_physical =
-               (Config->get_input_auto_connect() & AutoConnectPhysical)
-               && (Config->get_output_auto_connect() & AutoConnectPhysical)
-               && connect_inputs;
+                               if (bus->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+                                       error << _("cannot configure new midi bus input") << endmsg;
+                                       goto failure;
+                               }
 
-       const ChanCount in_offset = in_out_physical
-               ? ChanCount::max(existing_inputs, existing_outputs)
-                : existing_inputs;
 
-       const ChanCount out_offset = in_out_physical
-               ? ChanCount::max(existing_inputs, existing_outputs)
-               : existing_outputs;
+                               if (bus->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+                                       error << _("cannot configure new midi bus output") << endmsg;
+                                       goto failure;
+                               }
+                       }
 
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-               vector<string> physinputs;
-               vector<string> physoutputs;
+                       if (route_group) {
+                               route_group->add (bus);
+                       }
 
-               _engine.get_physical_outputs (*t, physoutputs);
-               _engine.get_physical_inputs (*t, physinputs);
+                       bus->add_internal_return ();
+                       ret.push_back (bus);
+               }
 
-               if (!physinputs.empty() && connect_inputs) {
-                       uint32_t nphysical_in = physinputs.size();
+               catch (failed_constructor &err) {
+                       error << _("Session: could not create new audio route.") << endmsg;
+                       goto failure;
+               }
 
-                       DEBUG_TRACE (DEBUG::Graph,
-                                    string_compose("There are %1 physical inputs of type %2\n",
-                                                   nphysical_in, *t));
+               catch (AudioEngine::PortRegistrationFailure& pfe) {
+                       error << pfe.what() << endmsg;
+                       goto failure;
+               }
 
-                       for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
-                               string port;
 
-                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                       DEBUG_TRACE (DEBUG::Graph,
-                                                    string_compose("Get index %1 + %2 % %3 = %4\n",
-                                                                   in_offset.get(*t), i, nphysical_in,
-                                                                   (in_offset.get(*t) + i) % nphysical_in));
-                                       port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
-                               }
+               --how_many;
+       }
 
-                               DEBUG_TRACE (DEBUG::Graph,
-                                            string_compose("Connect route %1 IN to %2\n",
-                                                           route->name(), port));
+  failure:
+       if (!ret.empty()) {
+               StateProtector sp (this);
+               add_routes (ret, false, false, false, order);
 
-                               if (!port.empty() && route->input()->connect (route->input()->ports().port(*t, i), port, this)) {
-                                       break;
+               if (instrument) {
+                       for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
+                               PluginPtr plugin = instrument->load (*this);
+                               if (pset) {
+                                       plugin->load_preset (*pset);
                                }
-
-                                ChanCount one_added (*t, 1);
-                                existing_inputs += one_added;
+                               boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
+                               (*r)->add_processor (p, PreFader);
                        }
                }
+       }
 
-               if (!physoutputs.empty()) {
-                       uint32_t nphysical_out = physoutputs.size();
-                       for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
-                               string port;
+       return ret;
 
-                               /* Waves Tracks:
-                                * do not create new connections if we reached the limit of physical outputs
-                                * in Multi Out mode
-                                */
+}
 
-                               if (!(Config->get_output_auto_connect() & AutoConnectMaster) &&
-                                   ARDOUR::Profile->get_trx () &&
-                                   existing_outputs.get(*t) == nphysical_out ) {
-                                       break;
-                               }
 
-                               if ((*t) == DataType::MIDI && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
-                                       port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
-                               } else if ((*t) == DataType::AUDIO && (Config->get_output_auto_connect() & AutoConnectMaster)) {
-                                        /* master bus is audio only */
-                                       if (_master_out && _master_out->n_inputs().get(*t) > 0) {
-                                               port = _master_out->input()->ports().port(*t,
-                                                               i % _master_out->input()->n_ports().get(*t))->name();
-                                       }
-                               }
+void
+Session::midi_output_change_handler (IOChange change, void * /*src*/, boost::weak_ptr<Route> wmt)
+{
+       boost::shared_ptr<Route> midi_track (wmt.lock());
 
-                               DEBUG_TRACE (DEBUG::Graph,
-                                            string_compose("Connect route %1 OUT to %2\n",
-                                                           route->name(), port));
+       if (!midi_track) {
+               return;
+       }
 
-                               if (!port.empty() && route->output()->connect (route->output()->ports().port(*t, i), port, this)) {
-                                       break;
-                               }
+       if ((change.type & IOChange::ConfigurationChanged) && Config->get_output_auto_connect() != ManualConnect) {
 
-                                ChanCount one_added (*t, 1);
-                                existing_outputs += one_added;
-                       }
+               if (change.after.n_audio() <= change.before.n_audio()) {
+                       return;
                }
+
+               /* new audio ports: make sure the audio goes somewhere useful,
+                * unless the user has no-auto-connect selected.
+                *
+                * The existing ChanCounts don't matter for this call as they are only
+                * to do with matching input and output indices, and we are only changing
+                * outputs here.
+                */
+               auto_connect_route (midi_track, false, ChanCount(), change.before);
        }
 }
 
@@ -2712,8 +2804,6 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r
                                        }
                                }
                        }
-
-                       //auto_connect_route (*rIter, inputs, outputs, false, reconnectIputs);
                }
 
                _master_out->output()->disconnect (this);
@@ -2838,12 +2928,40 @@ Session::reconnect_mmc_ports(bool inputs)
 
 #endif
 
+void
+Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many)
+{
+       if (first_new_order == PresentationInfo::max_order) {
+               /* adding at end, no worries */
+               return;
+       }
+
+       /* create a gap in the presentation info to accomodate @param how_many
+        * new objects.
+        */
+       StripableList sl;
+       get_stripables (sl);
+
+       for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
+               boost::shared_ptr<Stripable> s (*si);
+
+               if (s->is_monitor() || s->is_auditioner()) {
+                       continue;
+               }
+
+               if (s->presentation_info().order () >= first_new_order) {
+                       s->set_presentation_order (s->presentation_info().order () + how_many);
+               }
+       }
+}
+
 /** Caller must not hold process lock
  *  @param name_template string to use for the start of the name, or "" to use "Audio".
  */
 list< boost::shared_ptr<AudioTrack> >
-Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,
-                         uint32_t how_many, string name_template)
+Session::new_audio_track (int input_channels, int output_channels, RouteGroup* route_group,
+                          uint32_t how_many, string name_template, PresentationInfo::order_t order,
+                          TrackMode mode)
 {
        string track_name;
        uint32_t track_id = 0;
@@ -2864,12 +2982,16 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                boost::shared_ptr<AudioTrack> track;
 
                try {
-                       track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode));
+                       track.reset (new AudioTrack (*this, track_name, mode));
 
                        if (track->init ()) {
                                goto failed;
                        }
 
+                       if (Profile->get_mixbus ()) {
+                               track->set_strict_io (true);
+                       }
+
                        if (ARDOUR::Profile->get_trx ()) {
                                // TRACKS considers it's not a USE CASE, it's
                                // a piece of behavior of the session model:
@@ -2880,15 +3002,14 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                                //  0 for Stereo Out mode
                                //  0 Multi Out mode
                                if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                       track->set_gain (dB_to_coefficient (0), 0);
+                                       track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup);
                                }
                        }
 
                        track->use_new_diskstream();
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+                       BOOST_MARK_TRACK (track);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
@@ -2916,14 +3037,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                        track->non_realtime_input_change();
 
                        track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-                       if (Config->get_remote_model() == UserOrdered) {
-                               track->set_remote_control_id (next_control_id());
-                       }
 
                        new_routes.push_back (track);
                        ret.push_back (track);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
                }
 
                catch (failed_constructor &err) {
@@ -2944,9 +3060,9 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (new_routes, false, false, false);
+                       add_routes (new_routes, false, false, false, order);
                } else {
-                       add_routes (new_routes, true, true, false);
+                       add_routes (new_routes, true, true, false, order);
                }
        }
 
@@ -2957,7 +3073,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
  *  @param name_template string to use for the start of the name, or "" to use "Bus".
  */
 RouteList
-Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template)
+Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template,
+                          PresentationInfo::Flag flags, PresentationInfo::order_t order)
 {
        string bus_name;
        uint32_t bus_id = 0;
@@ -2973,15 +3090,18 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                }
 
                try {
-                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO));
+                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, flags, DataType::AUDIO));
 
                        if (bus->init ()) {
                                goto failure;
                        }
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
-#endif
+                       if (Profile->get_mixbus ()) {
+                               bus->set_strict_io (true);
+                       }
+
+                       BOOST_MARK_ROUTE(bus);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
@@ -3004,20 +3124,11 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                        if (route_group) {
                                route_group->add (bus);
                        }
-                       if (Config->get_remote_model() == UserOrdered) {
-                               bus->set_remote_control_id (next_control_id());
-                       }
 
                        bus->add_internal_return ();
-
                        ret.push_back (bus);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
-
-                       ARDOUR::GUIIdle ();
                }
 
-
                catch (failed_constructor &err) {
                        error << _("Session: could not create new audio route.") << endmsg;
                        goto failure;
@@ -3036,9 +3147,9 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (ret, false, false, false);
+                       add_routes (ret, false, false, false, order);
                } else {
-                       add_routes (ret, false, true, true); // autoconnect // outputs only
+                       add_routes (ret, false, true, true, order); // autoconnect // outputs only
                }
        }
 
@@ -3062,7 +3173,6 @@ RouteList
 Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd)
 {
        RouteList ret;
-       uint32_t control_id;
        uint32_t number = 0;
        const uint32_t being_added = how_many;
        /* This will prevent the use of any existing XML-provided PBD::ID
@@ -3071,8 +3181,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
        Stateful::ForceIDRegeneration force_ids;
        IO::disable_connecting ();
 
-       control_id = next_control_id ();
-
        while (how_many) {
 
                /* We're going to modify the node contents a bit so take a
@@ -3114,6 +3222,7 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                        case NewPlaylist:
                                rename_playlist = true;
                                break;
+                       default:
                        case CopyPlaylist:
                        case SharePlaylist:
                                rename_playlist = false;
@@ -3125,10 +3234,52 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                        XMLNodeList children = node_copy.children ();
                        for (XMLNodeList::iterator i = children.begin(); i != children.end(); ++i) {
                                if ((*i)->name() == X_("Processor")) {
-                                       XMLProperty* role = (*i)->property (X_("role"));
+                                       /* ForceIDRegeneration does not catch the following */
+                                       XMLProperty const * role = (*i)->property (X_("role"));
+                                       XMLProperty const * type = (*i)->property (X_("type"));
+                                       if (role && role->value() == X_("Aux")) {
+                                               /* check if the target bus exists.
+                                                * we should not save aux-sends in templates.
+                                                */
+                                               XMLProperty const * target = (*i)->property (X_("target"));
+                                               if (!target) {
+                                                       (*i)->add_property ("type", "dangling-aux-send");
+                                                       continue;
+                                               }
+                                               boost::shared_ptr<Route> r = route_by_id (target->value());
+                                               if (!r || boost::dynamic_pointer_cast<Track>(r)) {
+                                                       (*i)->add_property ("type", "dangling-aux-send");
+                                                       continue;
+                                               }
+                                       }
                                        if (role && role->value() == X_("Listen")) {
                                                (*i)->remove_property (X_("bitslot"));
                                        }
+                                       else if (role && (role->value() == X_("Send") || role->value() == X_("Aux"))) {
+                                               char buf[32];
+                                               Delivery::Role xrole;
+                                               uint32_t bitslot = 0;
+                                               xrole = Delivery::Role (string_2_enum (role->value(), xrole));
+                                               std::string name = Send::name_and_id_new_send(*this, xrole, bitslot, false);
+                                               snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
+                                               (*i)->remove_property (X_("bitslot"));
+                                               (*i)->remove_property (X_("name"));
+                                               (*i)->add_property ("bitslot", buf);
+                                               (*i)->add_property ("name", name);
+                                       }
+                                       else if (type && type->value() == X_("intreturn")) {
+                                               (*i)->remove_property (X_("bitslot"));
+                                               (*i)->add_property ("ignore-bitslot", "1");
+                                       }
+                                       else if (type && type->value() == X_("return")) {
+                                               // Return::set_state() generates a new one
+                                               (*i)->remove_property (X_("bitslot"));
+                                       }
+                                       else if (type && type->value() == X_("port")) {
+                                               // PortInsert::set_state() handles the bitslot
+                                               (*i)->remove_property (X_("bitslot"));
+                                               (*i)->add_property ("ignore-name", "1");
+                                       }
                                }
                        }
 
@@ -3154,9 +3305,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                                route->output()->changed (change, this);
                        }
 
-                       route->set_remote_control_id (control_id);
-                       ++control_id;
-
                        boost::shared_ptr<Track> track;
 
                        if ((track = boost::dynamic_pointer_cast<Track> (route))) {
@@ -3173,8 +3321,6 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                        };
 
                        ret.push_back (route);
-
-                       RouteAddedOrRemoved (true); /* EMIT SIGNAL */
                }
 
                catch (failed_constructor &err) {
@@ -3194,9 +3340,9 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
-                       add_routes (ret, false, false, false);
+                       add_routes (ret, false, false, false, PresentationInfo::max_order);
                } else {
-                       add_routes (ret, true, true, false);
+                       add_routes (ret, true, true, false, PresentationInfo::max_order);
                }
                IO::enable_connecting ();
        }
@@ -3205,11 +3351,11 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
 }
 
 void
-Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save)
+Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, bool save, PresentationInfo::order_t order)
 {
        try {
                PBD::Unwinder<bool> aip (_adding_routes_in_progress, true);
-               add_routes_inner (new_routes, input_auto_connect, output_auto_connect);
+               add_routes_inner (new_routes, input_auto_connect, output_auto_connect, order);
 
        } catch (...) {
                error << _("Adding new tracks/busses failed") << endmsg;
@@ -3226,54 +3372,53 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
                save_state (_current_snapshot_name);
        }
 
-       reassign_track_numbers();
-
        update_route_record_state ();
 
        RouteAdded (new_routes); /* EMIT SIGNAL */
 }
 
 void
-Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect)
+Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool output_auto_connect, PresentationInfo::order_t order)
 {
-        ChanCount existing_inputs;
-        ChanCount existing_outputs;
-       uint32_t order = next_control_id();
-
-       if (_order_hint > -1) {
-               order = _order_hint;
-               _order_hint = -1;
-       }
+       ChanCount existing_inputs;
+       ChanCount existing_outputs;
+       uint32_t n_routes;
+       uint32_t added = 0;
 
-        count_existing_track_channels (existing_inputs, existing_outputs);
+       count_existing_track_channels (existing_inputs, existing_outputs);
 
        {
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> r = writer.get_copy ();
                r->insert (r->end(), new_routes.begin(), new_routes.end());
+               n_routes = r->size();
 
                /* if there is no control out and we're not in the middle of loading,
-                  resort the graph here. if there is a control out, we will resort
-                  toward the end of this method. if we are in the middle of loading,
-                  we will resort when done.
-               */
+                * resort the graph here. if there is a control out, we will resort
+                * toward the end of this method. if we are in the middle of loading,
+                * we will resort when done.
+                */
 
                if (!_monitor_out && IO::connecting_legal) {
                        resort_routes_using (r);
                }
        }
 
-       for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
+       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", order, new_routes.size()));
+       ensure_route_presentation_info_gap (order, new_routes.size());
+
+       for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
 
                boost::weak_ptr<Route> wpr (*x);
                boost::shared_ptr<Route> r (*x);
 
-               r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _2, wpr));
-               r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _3, wpr));
-               r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, _1, wpr));
-               r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1));
+               r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2,wpr));
+               r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
+               r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+
                r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
                r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
+               r->processor_latency_changed.connect_same_thread (*this, boost::bind (&Session::queue_latency_recompute, this));
 
                if (r->is_master()) {
                        _master_out = r;
@@ -3287,36 +3432,54 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                if (tr) {
                        tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr<Track> (tr)));
                        track_playlist_changed (boost::weak_ptr<Track> (tr));
-                       tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
+                       tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this));
 
                        boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
                        if (mt) {
                                mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1));
-                                mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr<Route>(mt)));
+                               mt->output()->changed.connect_same_thread (*this, boost::bind (&Session::midi_output_change_handler, this, _1, _2, boost::weak_ptr<Route>(mt)));
                        }
                }
 
+               if (!r->presentation_info().special()) {
 
-               if (input_auto_connect || output_auto_connect) {
-                       auto_connect_route (r, existing_inputs, existing_outputs, true, input_auto_connect);
-               }
+                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
 
-               /* order keys are a GUI responsibility but we need to set up
-                  reasonable defaults because they also affect the remote control
-                  ID in most situations.
-               */
+                       /* presentation info order may already have been set from XML */
+
+                       if (!r->presentation_info().order_set()) {
 
-               if (!r->has_order_key ()) {
-                       if (r->is_auditioner()) {
-                               /* use an arbitrarily high value */
-                               r->set_order_key (UINT_MAX);
+                               if (order == PresentationInfo::max_order) {
+                                       /* just add to the end */
+                                       r->set_presentation_order (n_routes + added, false);
+                                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to NR %1 + %2 = %3\n", n_routes, added, n_routes + added));
+                               } else {
+                                       r->set_presentation_order (order + added);
+                                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order not set, set to %1 + %2 = %3\n", order, added, order + added));
+                               }
                        } else {
-                               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("while adding, set %1 to order key %2\n", r->name(), order));
-                               r->set_order_key (order);
-                               order++;
+                               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order()));
                        }
                }
 
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
+               /* clang complains: 'operator<<' should be declared prior to the call site or in an associated namespace of one of its
+                * arguments std::ostream& operator<<(std::ostream& o, ARDOUR::PresentationInfo const& rid)"
+                */
+               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("added route %1, group order %2 type %3 (summary: %4)\n",
+                                                              r->name(),
+                                                              r->presentation_info().order(),
+                                                              enum_2_string (r->presentation_info().flags()),
+                                                              r->presentation_info()));
+#endif
+
+
+               if (input_auto_connect || output_auto_connect) {
+                       auto_connect_route (r, input_auto_connect, ChanCount (), ChanCount (), existing_inputs, existing_outputs);
+                       existing_inputs += r->n_inputs();
+                       existing_outputs += r->n_outputs();
+               }
+
                ARDOUR::GUIIdle ();
        }
 
@@ -3327,12 +3490,14 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                        if ((*x)->is_monitor()) {
                                /* relax */
                        } else if ((*x)->is_master()) {
-                                       /* relax */
+                               /* relax */
                        } else {
                                (*x)->enable_monitor_send ();
                        }
                }
        }
+
+       reassign_track_numbers ();
 }
 
 void
@@ -3421,7 +3586,6 @@ Session::add_internal_send (boost::shared_ptr<Route> dest, boost::shared_ptr<Pro
        graph_reordered ();
 }
 
-
 void
 Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 {
@@ -3437,7 +3601,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                                continue;
                        }
 
-                       (*iter)->set_solo (false, this);
+                       (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
 
                        rs->remove (*iter);
 
@@ -3491,7 +3655,6 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
        } // end of RCU Writer scope
 
        update_route_solo_state ();
-       RouteAddedOrRemoved (false); /* EMIT SIGNAL */
        update_latency_compensation ();
        set_dirty();
 
@@ -3507,7 +3670,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                resort_routes ();
 #endif
 
-       if (_process_graph) {
+       if (_process_graph && !(_state_of_the_state & Deletion)) {
                _process_graph->clear_other_chain ();
        }
 
@@ -3524,7 +3687,11 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                (*iter)->drop_references ();
        }
 
-       Route::RemoteControlIDChange(); /* EMIT SIGNAL */
+       if (_state_of_the_state & Deletion) {
+               return;
+       }
+
+       PresentationInfo::Change(); /* EMIT SIGNAL */
 
        /* save the new state of the world */
 
@@ -3532,7 +3699,6 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                save_history (_current_snapshot_name);
        }
 
-       reassign_track_numbers();
        update_route_record_state ();
 }
 
@@ -3545,35 +3711,51 @@ Session::remove_route (boost::shared_ptr<Route> route)
 }
 
 void
-Session::route_mute_changed (void* /*src*/)
+Session::route_mute_changed ()
 {
        set_dirty ();
 }
 
 void
-Session::route_listen_changed (bool group_override, boost::weak_ptr<Route> wpr)
+Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
 {
-       boost::shared_ptr<Route> route = wpr.lock();
+       boost::shared_ptr<Route> route (wpr.lock());
+
        if (!route) {
-               error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_listen_changed")) << endmsg;
                return;
        }
 
-       if (route->listening_via_monitor ()) {
+       assert (Config->get_solo_control_is_listen_control());
+
+       if (route->solo_control()->soloed_by_self_or_masters()) {
 
                if (Config->get_exclusive_solo()) {
-                       /* new listen: disable all other listen, except solo-grouped channels */
+
                        RouteGroup* rg = route->route_group ();
-                       bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
-                       if (group_override && rg) {
-                               leave_group_alone = !leave_group_alone;
-                       }
+                       const bool group_already_accounted_for = (group_override == Controllable::ForGroup);
+
                        boost::shared_ptr<RouteList> r = routes.reader ();
+
                        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                               if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) {
+                               if ((*i) == route) {
+                                       /* already changed */
+                                       continue;
+                               }
+
+                               if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
+                                       /* route does not get solo propagated to it */
+                                       continue;
+                               }
+
+                               if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+                                       /* this route is a part of the same solo group as the route
+                                        * that was changed. Changing that route did change or will
+                                        * change all group members appropriately, so we can ignore it
+                                        * here
+                                        */
                                        continue;
                                }
-                               (*i)->set_listen (false, this, group_override);
+                               (*i)->solo_control()->set_value (0.0, Controllable::NoGroup);
                        }
                }
 
@@ -3586,20 +3768,19 @@ Session::route_listen_changed (bool group_override, boost::weak_ptr<Route> wpr)
 
        update_route_solo_state ();
 }
+
 void
-Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
 {
-       boost::shared_ptr<Route> route = wpr.lock ();
+       boost::shared_ptr<Route> route (wpr.lock());
 
        if (!route) {
-               /* should not happen */
-               error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg;
                return;
        }
 
        bool send_changed = false;
 
-       if (route->solo_isolated()) {
+       if (route->solo_isolate_control()->solo_isolated()) {
                if (_solo_isolated_cnt == 0) {
                        send_changed = true;
                }
@@ -3617,42 +3798,87 @@ Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
 }
 
 void
-Session::route_solo_changed (bool self_solo_change, bool group_override,  boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlDisposition group_override,  boost::weak_ptr<Route> wpr)
 {
-       DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_changed));
 
-       if (!self_solo_change) {
-               // session doesn't care about changes to soloed-by-others
+       boost::shared_ptr<Route> route (wpr.lock());
+
+       if (!route) {
                return;
        }
 
-       boost::shared_ptr<Route> route = wpr.lock ();
-       assert (route);
+       if (Config->get_solo_control_is_listen_control()) {
+               route_listen_changed (group_override, wpr);
+               return;
+       }
 
-       boost::shared_ptr<RouteList> r = routes.reader ();
-       int32_t delta;
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: self %2 masters %3 transition %4\n", route->name(), route->self_soloed(), route->solo_control()->get_masters_value(), route->solo_control()->transitioned_into_solo()));
 
-       if (route->self_soloed()) {
-               delta = 1;
-       } else {
-               delta = -1;
+       if (route->solo_control()->transitioned_into_solo() == 0) {
+               /* route solo changed by upstream/downstream; not interesting
+                  to Session.
+               */
+               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 not self-soloed nor soloed by master (%2), ignoring\n", route->name(), route->solo_control()->get_masters_value()));
+               return;
        }
 
-       RouteGroup* rg = route->route_group ();
-       bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
-       if (group_override && rg) {
-               leave_group_alone = !leave_group_alone;
+       if (route->solo_control()->transitioned_into_solo() == 0) {
+               /* reason for being soloed changed (e.g. master went away, we
+                * took over the master state), but actual status did
+                * not. nothing to do.
+                */
+               DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: solo change was change in reason, not status\n", route->name()));
        }
+
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       int32_t delta = route->solo_control()->transitioned_into_solo ();
+
+       /* the route may be a member of a group that has shared-solo
+        * semantics. If so, then all members of that group should follow the
+        * solo of the changed route. But ... this is optional, controlled by a
+        * Controllable::GroupControlDisposition.
+        *
+        * The first argument to the signal that this method is connected to is the
+        * GroupControlDisposition value that was used to change solo.
+        *
+        * If the solo change was done with group semantics (either InverseGroup
+        * (force the entire group to change even if the group shared solo is
+        * disabled) or UseGroup (use the group, which may or may not have the
+        * shared solo property enabled)) then as we propagate the change to
+        * the entire session we should IGNORE THE GROUP that the changed route
+        * belongs to.
+        */
+
+       RouteGroup* rg = route->route_group ();
+       const bool group_already_accounted_for = (group_override == Controllable::ForGroup);
+
        if (delta == 1 && Config->get_exclusive_solo()) {
 
                /* new solo: disable all other solos, but not the group if its solo-enabled */
 
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ||
-                           (leave_group_alone && ((*i)->route_group() == rg))) {
+
+                       if ((*i) == route) {
+                               /* already changed */
+                               continue;
+                       }
+
+                       if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
+                               /* route does not get solo propagated to it */
+                               continue;
+                       }
+
+                       if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+                               /* this route is a part of the same solo group as the route
+                                * that was changed. Changing that route did change or will
+                                * change all group members appropriately, so we can ignore it
+                                * here
+                                */
                                continue;
                        }
-                       (*i)->set_solo (false, this, group_override);
+
+                       (*i)->solo_control()->set_value (0.0, group_override);
                }
        }
 
@@ -3666,8 +3892,24 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
                bool via_sends_only;
                bool in_signal_flow;
 
-               if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ||
-                   (leave_group_alone && ((*i)->route_group() == rg))) {
+               if ((*i) == route) {
+                       /* already changed */
+                       continue;
+               }
+
+               if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) {
+                       /* route does not get solo propagated to it */
+                       DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 excluded from solo because iso = %2 can_solo = %3\n", (*i)->name(), (*i)->solo_isolate_control()->solo_isolated(),
+                                                                 (*i)->can_solo()));
+                       continue;
+               }
+
+               if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+                       /* this route is a part of the same solo group as the route
+                        * that was changed. Changing that route did change or will
+                        * change all group members appropriately, so we can ignore it
+                        * here
+                        */
                        continue;
                }
 
@@ -3679,7 +3921,7 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
                        DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name()));
                        if (!via_sends_only) {
                                if (!route->soloed_by_others_upstream()) {
-                                       (*i)->mod_solo_by_others_downstream (delta);
+                                       (*i)->solo_control()->mod_solo_by_others_downstream (delta);
                                } else {
                                        DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n");
                                }
@@ -3700,15 +3942,15 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
                           sends are involved.
                        */
                        DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 feeds %2 via sends only %3 sboD %4 sboU %5\n",
-                                                                 route->name(),
-                                                                 (*i)->name(),
-                                                                 via_sends_only,
-                                                                 route->soloed_by_others_downstream(),
-                                                                 route->soloed_by_others_upstream()));
+                                                                 route->name(),
+                                                                 (*i)->name(),
+                                                                 via_sends_only,
+                                                                 route->soloed_by_others_downstream(),
+                                                                 route->soloed_by_others_upstream()));
                        if (!via_sends_only) {
                                //NB. Triggers Invert Push, which handles soloed by downstream
                                DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta));
-                               (*i)->mod_solo_by_others_upstream (delta);
+                               (*i)->solo_control()->mod_solo_by_others_upstream (delta);
                        } else {
                                DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name()));
                        }
@@ -3733,7 +3975,7 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
        for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name()));
                (*i)->act_on_mute ();
-               (*i)->mute_changed (this);
+               (*i)->mute_control()->Changed (false, Controllable::NoGroup);
        }
 
        SoloChanged (); /* EMIT SIGNAL */
@@ -3755,20 +3997,21 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
        }
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) {
-                       something_soloed = true;
-               }
-
-               if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) {
+               if ((*i)->can_solo()) {
                        if (Config->get_solo_control_is_listen_control()) {
-                               listeners++;
-                               something_listening = true;
+                               if ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value()) {
+                                       listeners++;
+                                       something_listening = true;
+                               }
                        } else {
-                               (*i)->set_listen (false, this);
+                               (*i)->set_listen (false);
+                               if ((*i)->can_solo() && ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value())) {
+                                       something_soloed = true;
+                               }
                        }
                }
 
-               if ((*i)->solo_isolated()) {
+               if ((*i)->solo_isolate_control()->solo_isolated()) {
                        isolated++;
                }
        }
@@ -3794,6 +4037,16 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                                                  something_soloed, listeners, isolated));
 }
 
+void
+Session::get_stripables (StripableList& sl) const
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       sl.insert (sl.end(), r->begin(), r->end());
+
+       VCAList v = _vca_manager->vcas ();
+       sl.insert (sl.end(), v.begin(), v.end());
+}
+
 boost::shared_ptr<RouteList>
 Session::get_routes_with_internal_returns() const
 {
@@ -3809,7 +4062,7 @@ Session::get_routes_with_internal_returns() const
 }
 
 bool
-Session::io_name_is_legal (const std::string& name)
+Session::io_name_is_legal (const std::string& name) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -3918,7 +4171,7 @@ Session::routes_using_input_from (const string& str, RouteList& rl)
 }
 
 boost::shared_ptr<Route>
-Session::route_by_name (string name)
+Session::route_by_name (string name) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -3932,7 +4185,7 @@ Session::route_by_name (string name)
 }
 
 boost::shared_ptr<Route>
-Session::route_by_id (PBD::ID id)
+Session::route_by_id (PBD::ID id) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -3945,8 +4198,23 @@ Session::route_by_id (PBD::ID id)
        return boost::shared_ptr<Route> ((Route*) 0);
 }
 
+boost::shared_ptr<Processor>
+Session::processor_by_id (PBD::ID id) const
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               boost::shared_ptr<Processor> p = (*i)->Route::processor_by_id (id);
+               if (p) {
+                       return p;
+               }
+       }
+
+       return boost::shared_ptr<Processor> ();
+}
+
 boost::shared_ptr<Track>
-Session::track_by_diskstream_id (PBD::ID id)
+Session::track_by_diskstream_id (PBD::ID id) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -3961,19 +4229,75 @@ Session::track_by_diskstream_id (PBD::ID id)
 }
 
 boost::shared_ptr<Route>
-Session::route_by_remote_id (uint32_t id)
+Session::get_remote_nth_route (PresentationInfo::order_t n) const
+{
+       return boost::dynamic_pointer_cast<Route> (get_remote_nth_stripable (n, PresentationInfo::Route));
+}
+
+boost::shared_ptr<Stripable>
+Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo::Flag flags) const
+{
+       StripableList sl;
+       PresentationInfo::order_t match_cnt = 0;
+
+       get_stripables (sl);
+       sl.sort (Stripable::PresentationOrderSorter());
+
+       for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
+
+               if ((*s)->presentation_info().hidden()) {
+                       /* if the caller didn't explicitly ask for hidden
+                          stripables, ignore hidden ones. This matches
+                          the semantics of the pre-PresentationOrder
+                          "get by RID" logic of Ardour 4.x and earlier.
+
+                          XXX at some point we should likely reverse
+                          the logic of the flags, because asking for "the
+                          hidden stripables" is not going to be common,
+                          whereas asking for visible ones is normal.
+                       */
+
+                       if (! (flags & PresentationInfo::Hidden)) {
+                               continue;
+                       }
+               }
+
+               if ((*s)->presentation_info().flag_match (flags)) {
+                       if (match_cnt++ == n) {
+                               return *s;
+                       }
+               }
+       }
+
+       /* there is no nth stripable that matches the given flags */
+       return boost::shared_ptr<Stripable>();
+}
+
+boost::shared_ptr<Route>
+Session::route_by_selected_count (uint32_t id) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if ((*i)->remote_control_id() == id) {
-                       return *i;
-               }
+               /* NOT IMPLEMENTED */
        }
 
        return boost::shared_ptr<Route> ((Route*) 0);
 }
 
+struct PresentationOrderSorter {
+       bool operator() (boost::shared_ptr<Stripable> a, boost::shared_ptr<Stripable> b) {
+               if (a->presentation_info().special() && !b->presentation_info().special()) {
+                       /* a is not ordered, b is; b comes before a */
+                       return false;
+               } else if (!b->presentation_info().order_set() && a->presentation_info().order_set()) {
+                       /* b is not ordered, a is; a comes before b */
+                       return true;
+               } else {
+                       return a->presentation_info().order() < b->presentation_info().order();
+               }
+       }
+};
 
 void
 Session::reassign_track_numbers ()
@@ -3981,7 +4305,7 @@ Session::reassign_track_numbers ()
        int64_t tn = 0;
        int64_t bn = 0;
        RouteList r (*(routes.reader ()));
-       SignalOrderRouteSorter sorter;
+       PresentationOrderSorter sorter;
        r.sort (sorter);
 
        StateProtector sp (this);
@@ -4008,6 +4332,16 @@ Session::reassign_track_numbers ()
                // trigger GUI re-layout
                config.ParameterChanged("track-name-number");
        }
+
+#ifndef NDEBUG
+       if (DEBUG_ENABLED(DEBUG::OrderKeys)) {
+               boost::shared_ptr<RouteList> rl = routes.reader ();
+               for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("%1 numbered %2\n", (*i)->name(), (*i)->track_number()));
+               }
+       }
+#endif /* NDEBUG */
+
 }
 
 void
@@ -4073,12 +4407,18 @@ Session::maybe_update_session_range (framepos_t a, framepos_t b)
                        _session_range_location->set_start (a);
                }
 
-               if (b > _session_range_location->end()) {
+               if (_session_range_end_is_free && (b > _session_range_location->end())) {
                        _session_range_location->set_end (b);
                }
        }
 }
 
+void
+Session::set_end_is_free (bool yn)
+{
+       _session_range_end_is_free = yn;
+}
+
 void
 Session::playlist_ranges_moved (list<Evoral::RangeMove<framepos_t> > const & ranges)
 {
@@ -4797,6 +5137,230 @@ Session::audition_playlist ()
        queue_event (ev);
 }
 
+
+void
+Session::register_lua_function (
+               const std::string& name,
+               const std::string& script,
+               const LuaScriptParamList& args
+               )
+{
+       Glib::Threads::Mutex::Lock lm (lua_lock);
+
+       lua_State* L = lua.getState();
+
+       const std::string& bytecode = LuaScripting::get_factory_bytecode (script);
+       luabridge::LuaRef tbl_arg (luabridge::newTable(L));
+       for (LuaScriptParamList::const_iterator i = args.begin(); i != args.end(); ++i) {
+               if ((*i)->optional && !(*i)->is_set) { continue; }
+               tbl_arg[(*i)->name] = (*i)->value;
+       }
+       (*_lua_add)(name, bytecode, tbl_arg); // throws luabridge::LuaException
+       set_dirty();
+}
+
+void
+Session::unregister_lua_function (const std::string& name)
+{
+       Glib::Threads::Mutex::Lock lm (lua_lock);
+       (*_lua_del)(name); // throws luabridge::LuaException
+       lua.collect_garbage ();
+       set_dirty();
+}
+
+std::vector<std::string>
+Session::registered_lua_functions ()
+{
+       Glib::Threads::Mutex::Lock lm (lua_lock);
+       std::vector<std::string> rv;
+
+       try {
+               luabridge::LuaRef list ((*_lua_list)());
+               for (luabridge::Iterator i (list); !i.isNil (); ++i) {
+                       if (!i.key ().isString ()) { assert(0); continue; }
+                       rv.push_back (i.key ().cast<std::string> ());
+               }
+       } catch (luabridge::LuaException const& e) { }
+       return rv;
+}
+
+#ifndef NDEBUG
+static void _lua_print (std::string s) {
+       std::cout << "SessionLua: " << s << "\n";
+}
+#endif
+
+void
+Session::try_run_lua (pframes_t nframes)
+{
+       if (_n_lua_scripts == 0) return;
+       Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
+       if (tm.locked ()) {
+               try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { }
+               lua.collect_garbage_step ();
+       }
+}
+
+void
+Session::setup_lua ()
+{
+#ifndef NDEBUG
+       lua.Print.connect (&_lua_print);
+#endif
+       lua.tweak_rt_gc ();
+       lua.do_command (
+                       "function ArdourSession ()"
+                       "  local self = { scripts = {}, instances = {} }"
+                       ""
+                       "  local remove = function (n)"
+                       "   self.scripts[n] = nil"
+                       "   self.instances[n] = nil"
+                       "   Session:scripts_changed()" // call back
+                       "  end"
+                       ""
+                       "  local addinternal = function (n, f, a)"
+                       "   assert(type(n) == 'string', 'function-name must be string')"
+                       "   assert(type(f) == 'function', 'Given script is a not a function')"
+                       "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
+                       "   assert(self.scripts[n] == nil, 'Callback \"'.. n ..'\" already exists.')"
+                       "   self.scripts[n] = { ['f'] = f, ['a'] = a }"
+                       "   local env = _ENV;  env.f = nil env.io = nil env.os = nil env.loadfile = nil env.require = nil env.dofile = nil env.package = nil env.debug = nil"
+                       "   local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR }"
+                       "   self.instances[n] = load (string.dump(f, true), nil, nil, env)(a)"
+                       "   Session:scripts_changed()" // call back
+                       "  end"
+                       ""
+                       "  local add = function (n, b, a)"
+                       "   assert(type(b) == 'string', 'ByteCode must be string')"
+                       "   load (b)()" // assigns f
+                       "   assert(type(f) == 'string', 'Assigned ByteCode must be string')"
+                       "   addinternal (n, load(f), a)"
+                       "  end"
+                       ""
+                       "  local run = function (...)"
+                       "   for n, s in pairs (self.instances) do"
+                       "     local status, err = pcall (s, ...)"
+                       "     if not status then"
+                       "       print ('fn \"'.. n .. '\": ', err)"
+                       "       remove (n)"
+                       "      end"
+                       "   end"
+                       "   collectgarbage()"
+                       "  end"
+                       ""
+                       "  local cleanup = function ()"
+                       "   self.scripts = nil"
+                       "   self.instances = nil"
+                       "  end"
+                       ""
+                       "  local list = function ()"
+                       "   local rv = {}"
+                       "   for n, _ in pairs (self.scripts) do"
+                       "     rv[n] = true"
+                       "   end"
+                       "   return rv"
+                       "  end"
+                       ""
+                       "  local function basic_serialize (o)"
+                       "    if type(o) == \"number\" then"
+                       "     return tostring(o)"
+                       "    else"
+                       "     return string.format(\"%q\", o)"
+                       "    end"
+                       "  end"
+                       ""
+                       "  local function serialize (name, value)"
+                       "   local rv = name .. ' = '"
+                       "   collectgarbage()"
+                       "   if type(value) == \"number\" or type(value) == \"string\" or type(value) == \"nil\" then"
+                       "    return rv .. basic_serialize(value) .. ' '"
+                       "   elseif type(value) == \"table\" then"
+                       "    rv = rv .. '{} '"
+                       "    for k,v in pairs(value) do"
+                       "     local fieldname = string.format(\"%s[%s]\", name, basic_serialize(k))"
+                       "     rv = rv .. serialize(fieldname, v) .. ' '"
+                       "     collectgarbage()" // string concatenation allocates a new string :(
+                       "    end"
+                       "    return rv;"
+                       "   elseif type(value) == \"function\" then"
+                       "     return rv .. string.format(\"%q\", string.dump(value, true))"
+                       "   else"
+                       "    error('cannot save a ' .. type(value))"
+                       "   end"
+                       "  end"
+                       ""
+                       ""
+                       "  local save = function ()"
+                       "   return (serialize('scripts', self.scripts))"
+                       "  end"
+                       ""
+                       "  local restore = function (state)"
+                       "   self.scripts = {}"
+                       "   load (state)()"
+                       "   for n, s in pairs (scripts) do"
+                       "    addinternal (n, load(s['f']), s['a'])"
+                       "   end"
+                       "  end"
+                       ""
+                       " return { run = run, add = add, remove = remove,"
+                 "          list = list, restore = restore, save = save, cleanup = cleanup}"
+                       " end"
+                       " "
+                       " sess = ArdourSession ()"
+                       " ArdourSession = nil"
+                       " "
+                       "function ardour () end"
+                       );
+
+       lua_State* L = lua.getState();
+
+       try {
+               luabridge::LuaRef lua_sess = luabridge::getGlobal (L, "sess");
+               lua.do_command ("sess = nil"); // hide it.
+               lua.do_command ("collectgarbage()");
+
+               _lua_run = new luabridge::LuaRef(lua_sess["run"]);
+               _lua_add = new luabridge::LuaRef(lua_sess["add"]);
+               _lua_del = new luabridge::LuaRef(lua_sess["remove"]);
+               _lua_list = new luabridge::LuaRef(lua_sess["list"]);
+               _lua_save = new luabridge::LuaRef(lua_sess["save"]);
+               _lua_load = new luabridge::LuaRef(lua_sess["restore"]);
+               _lua_cleanup = new luabridge::LuaRef(lua_sess["cleanup"]);
+       } catch (luabridge::LuaException const& e) {
+               fatal << string_compose (_("programming error: %1"),
+                               X_("Failed to setup Lua interpreter"))
+                       << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+
+       LuaBindings::stddef (L);
+       LuaBindings::common (L);
+       LuaBindings::dsp (L);
+       luabridge::push <Session *> (L, this);
+       lua_setglobal (L, "Session");
+}
+
+void
+Session::scripts_changed ()
+{
+       assert (!lua_lock.trylock()); // must hold lua_lock
+
+       try {
+               luabridge::LuaRef list ((*_lua_list)());
+               int cnt = 0;
+               for (luabridge::Iterator i (list); !i.isNil (); ++i) {
+                       if (!i.key ().isString ()) { assert(0); continue; }
+                       ++cnt;
+               }
+               _n_lua_scripts = cnt;
+       } catch (luabridge::LuaException const& e) {
+               fatal << string_compose (_("programming error: %1"),
+                               X_("Indexing Lua Session Scripts failed."))
+                       << endmsg;
+               abort(); /*NOTREACHED*/
+       }
+}
+
 void
 Session::non_realtime_set_audition ()
 {
@@ -4835,7 +5399,7 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::
        if (b->is_monitor()) {
                return false;
        }
-       return a->order_key () < b->order_key ();
+       return a->presentation_info().order() < b->presentation_info().order();
 }
 
 bool
@@ -4990,6 +5554,16 @@ Session::tempo_map_changed (const PropertyChange&)
        set_dirty ();
 }
 
+void
+Session::gui_tempo_map_changed ()
+{
+       clear_clicks ();
+
+       playlists->update_after_tempo_map_change ();
+
+       _locations->apply (*this, &Session::update_locations_after_tempo_map_change);
+}
+
 void
 Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc)
 {
@@ -5021,7 +5595,7 @@ Session::next_insert_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < insert_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < insert_bitset.size(); ++n) {
                        if (!insert_bitset[n]) {
                                insert_bitset[n] = true;
                                return n;
@@ -5041,7 +5615,7 @@ Session::next_send_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < send_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < send_bitset.size(); ++n) {
                        if (!send_bitset[n]) {
                                send_bitset[n] = true;
                                return n;
@@ -5061,7 +5635,7 @@ Session::next_aux_send_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < aux_send_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < aux_send_bitset.size(); ++n) {
                        if (!aux_send_bitset[n]) {
                                aux_send_bitset[n] = true;
                                return n;
@@ -5081,7 +5655,7 @@ Session::next_return_id ()
        /* this doesn't really loop forever. just think about it */
 
        while (true) {
-               for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < return_bitset.size(); ++n) {
+               for (boost::dynamic_bitset<uint32_t>::size_type n = 1; n < return_bitset.size(); ++n) {
                        if (!return_bitset[n]) {
                                return_bitset[n] = true;
                                return n;
@@ -5162,6 +5736,7 @@ Session::unmark_aux_send_id (uint32_t id)
 void
 Session::unmark_return_id (uint32_t id)
 {
+       if (_state_of_the_state & Deletion) { return; }
        if (id < return_bitset.size()) {
                return_bitset[id] = false;
        }
@@ -5511,6 +6086,12 @@ Session::get_scratch_buffers (ChanCount count, bool silence)
        return ProcessThread::get_scratch_buffers (count, silence);
 }
 
+BufferSet&
+Session::get_noinplace_buffers (ChanCount count)
+{
+       return ProcessThread::get_noinplace_buffers (count);
+}
+
 BufferSet&
 Session::get_route_buffers (ChanCount count, bool silence)
 {
@@ -5582,7 +6163,7 @@ Session::update_route_record_state ()
        while (i != rl->end ()) {
 
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr && tr->record_enabled ()) {
+                                   if (tr && tr->rec_enable_control()->get_value()) {
                        break;
                }
 
@@ -5599,7 +6180,7 @@ Session::update_route_record_state ()
 
        for (i = rl->begin(); i != rl->end (); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr && !tr->record_enabled ()) {
+               if (tr && !tr->rec_enable_control()->get_value()) {
                        break;
                }
        }
@@ -5627,12 +6208,15 @@ Session::listen_position_changed ()
 void
 Session::solo_control_mode_changed ()
 {
-       /* cancel all solo or all listen when solo control mode changes */
-
-       if (soloing()) {
-               set_solo (get_routes(), false);
-       } else if (listening()) {
-               set_listen (get_routes(), false);
+       if (soloing() || listening()) {
+               /* We can't use ::clear_all_solo_state() here because during
+                  session loading at program startup, that will queue a call
+                  to rt_clear_all_solo_state() that will not execute until
+                  AFTER solo states have been established (thus throwing away
+                  the session's saved solo state). So just explicitly turn
+                  them all off.
+               */
+               set_controls (route_list_to_control_list (get_routes(), &Stripable::solo_control), 0.0, Controllable::NoGroup);
        }
 }
 
@@ -5948,9 +6532,10 @@ Session::unknown_processors () const
 void
 Session::update_latency (bool playback)
 {
+
        DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
 
-       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress) {
+       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
                return;
        }
 
@@ -6154,75 +6739,25 @@ Session::session_name_is_legal (const string& path)
        return 0;
 }
 
-uint32_t
-Session::next_control_id () const
-{
-       int subtract = 0;
-
-       /* the monitor bus remote ID is in a different
-        * "namespace" than regular routes. its existence doesn't
-        * affect normal (low) numbered routes.
-        */
-
-       if (_monitor_out) {
-               subtract++;
-       }
-
-       /* the same about masterbus in Waves Tracks */
-
-       if (Profile->get_trx() && _master_out) {
-               subtract++;
-       }
-
-       return nroutes() - subtract;
-}
-
 void
-Session::notify_remote_id_change ()
+Session::notify_presentation_info_change ()
 {
        if (deletion_in_progress()) {
                return;
        }
 
-       switch (Config->get_remote_model()) {
-       case MixerOrdered:
-               Route::RemoteControlIDChange (); /* EMIT SIGNAL */
-               break;
-       default:
-               break;
-       }
+       PresentationInfo::Change (); /* EMIT SIGNAL */
+       reassign_track_numbers();
 
 #ifdef USE_TRACKS_CODE_FEATURES
-               /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs
-                * if track order has been changed by user
-                */
-               reconnect_existing_routes(true, true);
+       /* Waves Tracks: for Waves Tracks session it's required to reconnect their IOs
+        * if track order has been changed by user
+        */
+       reconnect_existing_routes(true, true);
 #endif
 
 }
 
-void
-Session::sync_order_keys ()
-{
-       if (deletion_in_progress()) {
-               return;
-       }
-
-       /* tell everyone that something has happened to the sort keys
-          and let them sync up with the change(s)
-          this will give objects that manage the sort order keys the
-          opportunity to keep them in sync if they wish to.
-       */
-
-       DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n");
-
-       reassign_track_numbers();
-
-       Route::SyncOrderKeys (); /* EMIT SIGNAL */
-
-       DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n");
-}
-
 bool
 Session::operation_in_progress (GQuark op) const
 {
@@ -6311,3 +6846,226 @@ Session::clear_object_selection ()
        follow_playhead_priority ();
 #endif
 }
+
+void
+Session::auto_connect_route (boost::shared_ptr<Route> route, bool connect_inputs,
+               const ChanCount& input_start,
+               const ChanCount& output_start,
+               const ChanCount& input_offset,
+               const ChanCount& output_offset)
+{
+       Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+       _auto_connect_queue.push (AutoConnectRequest (route, connect_inputs,
+                               input_start, output_start,
+                               input_offset, output_offset));
+
+       if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
+               pthread_cond_signal (&_auto_connect_cond);
+               pthread_mutex_unlock (&_auto_connect_mutex);
+       }
+}
+
+void
+Session::queue_latency_recompute ()
+{
+       g_atomic_int_inc (&_latency_recompute_pending);
+       if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
+               pthread_cond_signal (&_auto_connect_cond);
+               pthread_mutex_unlock (&_auto_connect_mutex);
+       }
+}
+
+void
+Session::auto_connect (const AutoConnectRequest& ar)
+{
+       boost::shared_ptr<Route> route = ar.route.lock();
+
+       if (!route) { return; }
+
+       if (!IO::connecting_legal) {
+               return;
+       }
+
+       /* If both inputs and outputs are auto-connected to physical ports,
+        * use the max of input and output offsets to ensure auto-connected
+        * port numbers always match up (e.g. the first audio input and the
+        * first audio output of the route will have the same physical
+        * port number).  Otherwise just use the lowest input or output
+        * offset possible.
+        */
+
+       const bool in_out_physical =
+               (Config->get_input_auto_connect() & AutoConnectPhysical)
+               && (Config->get_output_auto_connect() & AutoConnectPhysical)
+               && ar.connect_inputs;
+
+       const ChanCount in_offset = in_out_physical
+               ? ChanCount::max(ar.input_offset, ar.output_offset)
+               : ar.input_offset;
+
+       const ChanCount out_offset = in_out_physical
+               ? ChanCount::max(ar.input_offset, ar.output_offset)
+               : ar.output_offset;
+
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               vector<string> physinputs;
+               vector<string> physoutputs;
+
+               _engine.get_physical_outputs (*t, physoutputs);
+               _engine.get_physical_inputs (*t, physinputs);
+
+               if (!physinputs.empty() && ar.connect_inputs) {
+                       uint32_t nphysical_in = physinputs.size();
+
+                       for (uint32_t i = ar.input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
+                               string port;
+
+                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+                                       port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
+                               }
+
+                               if (!port.empty() && route->input()->connect (route->input()->ports().port(*t, i), port, this)) {
+                                       break;
+                               }
+                       }
+               }
+
+               if (!physoutputs.empty()) {
+                       uint32_t nphysical_out = physoutputs.size();
+                       for (uint32_t i = ar.output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
+                               string port;
+
+                               /* Waves Tracks:
+                                * do not create new connections if we reached the limit of physical outputs
+                                * in Multi Out mode
+                                */
+                               if (!(Config->get_output_auto_connect() & AutoConnectMaster) &&
+                                               ARDOUR::Profile->get_trx () &&
+                                               ar.output_offset.get(*t) == nphysical_out ) {
+                                       break;
+                               }
+
+                               if ((*t) == DataType::MIDI && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
+                                       port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
+                               } else if ((*t) == DataType::AUDIO && (Config->get_output_auto_connect() & AutoConnectMaster)) {
+                                       /* master bus is audio only */
+                                       if (_master_out && _master_out->n_inputs().get(*t) > 0) {
+                                               port = _master_out->input()->ports().port(*t,
+                                                               i % _master_out->input()->n_ports().get(*t))->name();
+                                       }
+                               }
+
+                               if (!port.empty() && route->output()->connect (route->output()->ports().port(*t, i), port, this)) {
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
+void
+Session::auto_connect_thread_start ()
+{
+       if (_ac_thread_active) {
+               return;
+       }
+
+       while (!_auto_connect_queue.empty ()) {
+               _auto_connect_queue.pop ();
+       }
+
+       _ac_thread_active = true;
+       if (pthread_create (&_auto_connect_thread, NULL, auto_connect_thread, this)) {
+               _ac_thread_active = false;
+       }
+}
+
+void
+Session::auto_connect_thread_terminate ()
+{
+       if (!_ac_thread_active) {
+               return;
+       }
+       _ac_thread_active = false;
+
+       {
+               Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+               while (!_auto_connect_queue.empty ()) {
+                       _auto_connect_queue.pop ();
+               }
+       }
+
+       if (pthread_mutex_lock (&_auto_connect_mutex) == 0) {
+               pthread_cond_signal (&_auto_connect_cond);
+               pthread_mutex_unlock (&_auto_connect_mutex);
+       }
+
+       void *status;
+       pthread_join (_auto_connect_thread, &status);
+}
+
+void *
+Session::auto_connect_thread (void *arg)
+{
+       Session *s = static_cast<Session *>(arg);
+       s->auto_connect_thread_run ();
+       pthread_exit (0);
+       return 0;
+}
+
+void
+Session::auto_connect_thread_run ()
+{
+       pthread_set_name (X_("autoconnect"));
+       SessionEvent::create_per_thread_pool (X_("autoconnect"), 1024);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 1024);
+       pthread_mutex_lock (&_auto_connect_mutex);
+       while (_ac_thread_active) {
+
+               if (!_auto_connect_queue.empty ()) {
+                       // Why would we need the process lock ??
+                       // A: if ports are added while we're connecting, the backend's iterator may be invalidated:
+                       //   graph_order_callback() -> resort_routes() -> direct_feeds_according_to_reality () -> backend::connected_to()
+                       //   All ardour-internal backends use a std::vector   xxxAudioBackend::find_port()
+                       //   We have control over those, but what does jack do?
+                       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                       Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
+                       while (!_auto_connect_queue.empty ()) {
+                               const AutoConnectRequest ar (_auto_connect_queue.front());
+                               _auto_connect_queue.pop ();
+                               lx.release ();
+                               auto_connect (ar);
+                               lx.acquire ();
+                       }
+               }
+
+               if (!actively_recording ()) { // might not be needed,
+                       /* this is only used for updating plugin latencies, the
+                        * graph does not change. so it's safe in general.
+                        * BUT..
+                        * .. update_latency_compensation () entails set_capture_offset()
+                        * which calls Diskstream::set_capture_offset () which
+                        * modifies the capture offset... which can be a proplem
+                        * in "prepare_to_stop"
+                        */
+                       while (g_atomic_int_and (&_latency_recompute_pending, 0)) {
+                               update_latency_compensation ();
+                       }
+               }
+
+               pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);
+       }
+       pthread_mutex_unlock (&_auto_connect_mutex);
+}
+
+void
+Session::cancel_all_solo ()
+{
+       StripableList sl;
+
+       get_stripables (sl);
+
+       set_controls (stripable_list_to_control_list (sl, &Stripable::solo_control), 0.0, Controllable::NoGroup);
+       clear_all_solo_state (routes.reader());
+}