remove InputConfigurationChange from session events, since it is a no-op in this...
[ardour.git] / libs / ardour / session.cc
index fc66dfe602ff38f512dfacba71436eceaa7ad4c7..b658487969c3827479096ab426e66c538c20892d 100644 (file)
@@ -37,7 +37,6 @@
 
 #include "pbd/basename.h"
 #include "pbd/convert.h"
-#include "pbd/convert.h"
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
 #include "pbd/md5.h"
@@ -52,7 +51,6 @@
 #include "ardour/analyser.h"
 #include "ardour/async_midi_port.h"
 #include "ardour/audio_buffer.h"
-#include "ardour/audio_diskstream.h"
 #include "ardour/audio_port.h"
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
@@ -67,6 +65,7 @@
 #include "ardour/control_protocol_manager.h"
 #include "ardour/data_type.h"
 #include "ardour/debug.h"
+#include "ardour/disk_reader.h"
 #include "ardour/directory_names.h"
 #ifdef USE_TRACKS_CODE_FEATURES
 #include "ardour/engine_state_controller.h"
@@ -82,6 +81,7 @@
 #include "ardour/midi_ui.h"
 #include "ardour/operations.h"
 #include "ardour/playlist.h"
+#include "ardour/playlist_factory.h"
 #include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/process_thread.h"
 #include "ardour/route_graph.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
+#include "ardour/selection.h"
 #include "ardour/session.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_playlists.h"
+#include "ardour/slave.h"
 #include "ardour/smf_source.h"
 #include "ardour/solo_isolate_control.h"
 #include "ardour/source_factory.h"
 #include "ardour/tempo.h"
 #include "ardour/ticker.h"
 #include "ardour/track.h"
+#include "ardour/types_convert.h"
 #include "ardour/user_bundle.h"
 #include "ardour/utils.h"
 #include "ardour/vca_manager.h"
@@ -184,6 +187,7 @@ Session::Session (AudioEngine &eng,
        , _transport_speed (0)
        , _default_transport_speed (1.0)
        , _last_transport_speed (0)
+       , _signalled_varispeed (0)
        , _target_transport_speed (0.0)
        , auto_play_legal (false)
        , _last_slave_transport_frame (0)
@@ -214,6 +218,7 @@ Session::Session (AudioEngine &eng,
        , _exporting (false)
        , _export_rolling (false)
        , _realtime_export (false)
+       , _region_export (false)
        , _export_preroll (0)
        , _export_latency (0)
        , _pre_export_mmc_enabled (false)
@@ -221,7 +226,6 @@ Session::Session (AudioEngine &eng,
        , _is_new (true)
        , _send_qf_mtc (false)
        , _pframes_since_last_mtc (0)
-       , session_midi_feedback (0)
        , play_loop (false)
        , loop_changing (false)
        , last_loopend (0)
@@ -240,7 +244,7 @@ Session::Session (AudioEngine &eng,
        , pending_locate_flush (false)
        , pending_abort (false)
        , pending_auto_loop (false)
-       , _mempool ("Session", 2097152)
+       , _mempool ("Session", 3145728)
        , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
        , _n_lua_scripts (0)
        , _butler (new Butler (*this))
@@ -251,12 +255,12 @@ Session::Session (AudioEngine &eng,
        , _ignore_skips_updates (false)
        , _rt_thread_active (false)
        , _rt_emit_pending (false)
-       , _ac_thread_active (false)
+       , _ac_thread_active (0)
        , _latency_recompute_pending (0)
        , step_speed (0)
        , outbound_mtc_timecode_frame (0)
        , next_quarter_frame_to_send (-1)
-       , _frames_per_timecode_frame (0)
+       , _samples_per_timecode_frame (0)
        , _frames_per_hour (0)
        , _timecode_frames_per_hour (0)
        , last_timecode_valid (false)
@@ -295,14 +299,19 @@ Session::Session (AudioEngine &eng,
        , _bundle_xml_node (0)
        , _current_trans (0)
        , _clicking (false)
+       , _click_rec_only (false)
        , click_data (0)
        , click_emphasis_data (0)
        , click_length (0)
        , click_emphasis_length (0)
        , _clicks_cleared (0)
+       , _count_in_samples (0)
        , _play_range (false)
        , _range_selection (-1,-1)
        , _object_selection (-1,-1)
+       , _preroll_record_punch_pos (-1)
+       , _preroll_record_trim_len (0)
+       , _count_in_once (false)
        , main_outs (0)
        , first_file_data_format_reset (true)
        , first_file_header_format_reset (true)
@@ -312,12 +321,13 @@ Session::Session (AudioEngine &eng,
        , _step_editors (0)
        , _suspend_timecode_transmission (0)
        ,  _speakers (new Speakers)
-       , ignore_route_processor_changes (false)
+       , _ignore_route_processor_changes (0)
        , midi_clock (0)
        , _scene_changer (0)
        , _midi_ports (0)
        , _mmc (0)
        , _vca_manager (new VCAManager (*this))
+       , _selection (new CoreSelection (*this))
 {
        uint32_t sr = 0;
 
@@ -332,7 +342,7 @@ Session::Session (AudioEngine &eng,
        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);
+       pre_engine_init (fullpath); // sets _is_new
 
        setup_lua ();
 
@@ -370,8 +380,12 @@ Session::Session (AudioEngine &eng,
                 */
 
                if (!mix_template.empty()) {
-                       if (load_state (_current_snapshot_name)) {
-                               throw SessionException (_("Failed to load template/snapshot state"));
+                       try {
+                               if (load_state (_current_snapshot_name)) {
+                                       throw SessionException (_("Failed to load template/snapshot state"));
+                               }
+                       } catch (PBD::unknown_enumeration& e) {
+                               throw SessionException (_("Failed to parse template/snapshot state"));
                        }
                        store_recent_templates (mix_template);
                }
@@ -404,9 +418,27 @@ Session::Session (AudioEngine &eng,
                }
        }
 
-       if (post_engine_init ()) {
+       int err = post_engine_init ();
+       if (err) {
                destroy ();
-               throw SessionException (_("Cannot configure audio/midi engine with session parameters"));
+               switch (err) {
+                       case -1:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Failed to create background threads.")));
+                               break;
+                       case -2:
+                       case -3:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid TempoMap in session-file.")));
+                               break;
+                       case -4:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid or corrupt session state.")));
+                               break;
+                       case -5:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Port registration failed.")));
+                               break;
+                       default:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Unexpected exception during session setup, possibly invalid audio/midi engine parameters. Please see stdout/stderr for details")));
+                               break;
+               }
        }
 
        store_recent_sessions (_name, _path);
@@ -415,6 +447,8 @@ Session::Session (AudioEngine &eng,
 
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
 
+       PresentationInfo::Change.connect_same_thread (*this, boost::bind (&Session::notify_presentation_info_change, this));
+
        Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false));
        config.ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, true));
 
@@ -478,8 +512,18 @@ Session::Session (AudioEngine &eng,
        }
 #endif
 
-       _is_new = false;
+       ensure_subdirs (); // archived or zipped sessions may lack peaks/ analysis/ etc
+
+       if (!mix_template.empty ()) {
+               /* ::create() unsets _is_new after creating the session.
+                * But for templated sessions, the sample-rate is initially unset
+                * (not read from template), so we need to save it (again).
+                */
+               _is_new = true;
+       }
+
        session_loaded ();
+       _is_new = false;
 
        BootMessage (_("Session loading complete"));
 }
@@ -571,7 +615,6 @@ Session::immediately_post_engine ()
        }
 
        try {
-               LocaleGuard lg;
                BootMessage (_("Set up LTC"));
                setup_ltc ();
                BootMessage (_("Set up Click"));
@@ -606,8 +649,12 @@ Session::destroy ()
 
        _state_of_the_state = StateOfTheState (CannotSave|Deletion);
 
-       /* stop autoconnecting */
-       auto_connect_thread_terminate ();
+       {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               ltc_tx_cleanup();
+               delete _slave;
+               _slave = 0;
+       }
 
        /* disconnect from any and all signals that we are connected to */
 
@@ -615,11 +662,19 @@ Session::destroy ()
        drop_connections ();
 
        /* shutdown control surface protocols while we still have ports
-          and the engine to move data to any devices.
-       */
+        * and the engine to move data to any devices.
+        */
+
+       /* remove I/O objects before unsetting the engine session */
+       _click_io.reset ();
+       _ltc_input.reset ();
+       _ltc_output.reset ();
 
        ControlProtocolManager::instance().drop_protocols ();
 
+       /* stop auto dis/connecting */
+       auto_connect_thread_terminate ();
+
        MIDI::Name::MidiPatchManager::instance().remove_search_path(session_directory().midi_patch_path());
 
        _engine.remove_session ();
@@ -634,8 +689,6 @@ Session::destroy ()
 
        Port::PortDrop (); /* EMIT SIGNAL */
 
-       ltc_tx_cleanup();
-
        /* clear history so that no references to objects are held any more */
 
        _history.clear ();
@@ -645,17 +698,20 @@ Session::destroy ()
        delete state_tree;
        state_tree = 0;
 
-       // 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 ();
+       {
+               /* unregister all lua functions, drop held references (if any) */
+               Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
+               (*_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;
@@ -683,6 +739,7 @@ Session::destroy ()
 
        /* need to remove auditioner before monitoring section
         * otherwise it is re-connected */
+       auditioner->drop_references ();
        auditioner.reset ();
 
        /* drop references to routes held by the monitoring section
@@ -694,7 +751,7 @@ Session::destroy ()
        routes.flush ();
        _bundles.flush ();
 
-       AudioDiskstream::free_working_buffers();
+       DiskReader::free_working_buffers();
 
        /* tell everyone who is still standing that we're about to die */
        drop_references ();
@@ -778,6 +835,7 @@ Session::destroy ()
                        case SessionEvent::Skip:
                        case SessionEvent::PunchIn:
                        case SessionEvent::PunchOut:
+                       case SessionEvent::RecordStart:
                        case SessionEvent::StopOnce:
                        case SessionEvent::RangeStop:
                        case SessionEvent::RangeLocate:
@@ -798,6 +856,18 @@ Session::destroy ()
                }
        }
 
+       {
+               /* unregister all dropped ports, process pending port deletion. */
+               // this may call ARDOUR::Port::drop ... jack_port_unregister ()
+               // jack1 cannot cope with removing ports while processing
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               AudioEngine::instance()->clear_pending_port_deletions ();
+       }
+
+       DEBUG_TRACE (DEBUG::Destruction, "delete selection\n");
+       delete _selection;
+       _selection = 0;
+
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
        BOOST_SHOW_POINTERS ();
@@ -817,8 +887,10 @@ Session::setup_ltc ()
                {
                        Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                        _ltc_input->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+                       // TODO use auto-connect thread somehow (needs a route currently)
+                       // see note in Session::auto_connect_thread_run() why process lock is needed.
+                       reconnect_ltc_input ();
                }
-               reconnect_ltc_input ();
        }
 
        if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) {
@@ -827,8 +899,9 @@ Session::setup_ltc ()
                {
                        Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                        _ltc_output->ensure_io (ChanCount (DataType::AUDIO, 1), true, this);
+                       // TODO use auto-connect thread
+                       reconnect_ltc_output ();
                }
-               reconnect_ltc_output ();
        }
 
        /* fix up names of LTC ports because we don't want the normal
@@ -911,6 +984,14 @@ Session::setup_click_state (const XMLNode* node)
        }
 }
 
+void
+Session::get_physical_ports (vector<string>& inputs, vector<string>& outputs, DataType type,
+                             MidiPortFlags include, MidiPortFlags exclude)
+{
+       _engine.get_physical_inputs (type, inputs, include, exclude);
+       _engine.get_physical_outputs (type, outputs, include, exclude);
+}
+
 void
 Session::setup_bundles ()
 {
@@ -929,9 +1010,12 @@ Session::setup_bundles ()
 
        vector<string> inputs[DataType::num_types];
        vector<string> outputs[DataType::num_types];
+
        for (uint32_t i = 0; i < DataType::num_types; ++i) {
-               _engine.get_physical_inputs (DataType (DataType::Symbol (i)), inputs[i]);
-               _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]);
+               get_physical_ports (inputs[i], outputs[i], DataType (DataType::Symbol (i)),
+                                   MidiPortFlags (0), /* no specific inclusions */
+                                   MidiPortFlags (MidiPortControl|MidiPortVirtual) /* exclude control & virtual ports */
+                       );
        }
 
        /* Create a set of Bundle objects that map
@@ -1015,6 +1099,7 @@ Session::setup_bundles ()
 
        for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) {
                string n = inputs[DataType::MIDI][np];
+
                std::string pn = _engine.get_pretty_name_by_name (n);
                if (!pn.empty()) {
                        n = pn;
@@ -1117,7 +1202,7 @@ Session::remove_monitor_section ()
 
 
                boost::shared_ptr<RouteList> r = routes.reader ();
-               PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+               ProcessorChangeBlocker  pcb (this, false);
 
                for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
 
@@ -1277,7 +1362,7 @@ Session::add_monitor_section ()
 
        boost::shared_ptr<RouteList> rls = routes.reader ();
 
-       PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+       ProcessorChangeBlocker  pcb (this, false /* XXX */);
 
        for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) {
 
@@ -1401,7 +1486,7 @@ Session::reset_monitor_section ()
 
        boost::shared_ptr<RouteList> rls = routes.reader ();
 
-       PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+       ProcessorChangeBlocker pcb (this, false);
 
        for (RouteList::iterator x = rls->begin(); x != rls->end(); ++x) {
 
@@ -1415,6 +1500,33 @@ Session::reset_monitor_section ()
        }
 }
 
+int
+Session::add_master_bus (ChanCount const& count)
+{
+       if (master_out ()) {
+               return -1;
+       }
+
+       RouteList rl;
+
+       boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
+       if (r->init ()) {
+               return -1;
+       }
+
+       BOOST_MARK_ROUTE(r);
+
+       {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               r->input()->ensure_io (count, false, this);
+               r->output()->ensure_io (count, false, this);
+       }
+
+       rl.push_back (r);
+       add_routes (rl, false, false, false, PresentationInfo::max_order);
+       return 0;
+}
+
 void
 Session::hookup_io ()
 {
@@ -1435,7 +1547,6 @@ Session::hookup_io ()
                        if (a->init()) {
                                throw failed_constructor ();
                        }
-                       a->use_new_diskstream ();
                        auditioner = a;
                }
 
@@ -1588,7 +1699,7 @@ Session::auto_loop_changed (Location* location)
                }
                else if (Config->get_seamless_loop() && !loop_changing) {
 
-                       // schedule a locate-roll to refill the diskstreams at the
+                       // schedule a locate-roll to refill the disk readers at the
                        // previous loop end
                        loop_changing = true;
 
@@ -1664,7 +1775,7 @@ Session::set_session_extents (framepos_t start, framepos_t end)
        Location* existing;
        if ((existing = _locations->session_range_location()) == 0) {
                //if there is no existing session, we need to make a new session location  (should never happen)
-               existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange);
+               existing = new Location (*this, 0, 0, _("session"), Location::IsSessionRange, 0);
        }
 
        if (end <= start) {
@@ -1853,6 +1964,17 @@ Session::location_added (Location *location)
                 location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
                 location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
                 location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+        }
+
+       if (location->is_range_marker()) {
+                /* listen for per-location signals that require us to do any * global updates for marks */
+
+                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+                location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+                location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+                location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
+               location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
         }
 
         if (location->is_skip()) {
@@ -1862,6 +1984,7 @@ Session::location_added (Location *location)
                 location->EndChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
                 location->Changed.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
                 location->FlagsChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, false));
+               location->PositionLockStyleChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_marks, this, location));
 
                 update_skips (location, true);
         }
@@ -1981,6 +2104,7 @@ Session::disable_record (bool rt_context, bool force)
                if (!rt_context) {
                        remove_pending_capture_state ();
                }
+               unset_preroll_record_punch ();
        }
 }
 
@@ -1998,7 +2122,7 @@ Session::step_back_from_record ()
 }
 
 void
-Session::maybe_enable_record ()
+Session::maybe_enable_record (bool rt_context)
 {
        if (_step_editors > 0) {
                return;
@@ -2007,15 +2131,18 @@ Session::maybe_enable_record ()
        g_atomic_int_set (&_record_status, Enabled);
 
        /* This function is currently called from somewhere other than an RT thread.
-          This save_state() call therefore doesn't impact anything.  Doing it here
-          means that we save pending state of which sources the next record will use,
-          which gives us some chance of recovering from a crash during the record.
-       */
+        * (except maybe lua scripts, which can use rt_context = true)
+        * This save_state() call therefore doesn't impact anything.  Doing it here
+        * means that we save pending state of which sources the next record will use,
+        * which gives us some chance of recovering from a crash during the record.
+        */
 
-       save_state ("", true);
+       if (!rt_context) {
+               save_state ("", true);
+       }
 
        if (_transport_speed) {
-               if (!config.get_punch_in()) {
+               if (!config.get_punch_in() && !preroll_record_punch_enabled ()) {
                        enable_record ();
                }
        } else {
@@ -2027,12 +2154,15 @@ Session::maybe_enable_record ()
 }
 
 framepos_t
-Session::audible_frame () const
+Session::audible_frame (bool* latent_locate) const
 {
        framepos_t ret;
 
        frameoffset_t offset = worst_playback_latency (); // - _engine.samples_since_cycle_start ();
        offset *= transport_speed ();
+       if (latent_locate) {
+               *latent_locate = false;
+       }
 
        if (synced_to_engine()) {
                /* Note: this is basically just sync-to-JACK */
@@ -2057,14 +2187,24 @@ Session::audible_frame () const
 
                        if (!play_loop || !have_looped) {
                                if (ret < _last_roll_or_reversal_location) {
+                                       if (latent_locate) {
+                                               *latent_locate = true;
+                                       }
                                        return _last_roll_or_reversal_location;
                                }
                        } else {
-                               // latent loops
+                               /* the play-position wrapped at the loop-point
+                                * ardour is already playing the beginning of the loop,
+                                * but due to playback latency, the "audible frame"
+                                * is still at the end of the loop.
+                                */
                                Location *location = _locations->auto_loop_location();
                                frameoffset_t lo = location->start() - ret;
                                if (lo > 0) {
                                        ret = location->end () - lo;
+                                       if (latent_locate) {
+                                               *latent_locate = true;
+                                       }
                                }
                        }
 
@@ -2081,6 +2221,22 @@ Session::audible_frame () const
        return std::max ((framepos_t)0, ret);
 }
 
+
+framecnt_t
+Session::preroll_samples (framepos_t pos) const
+{
+       const float pr = Config->get_preroll_seconds();
+       if (pos >= 0 && pr < 0) {
+               const Tempo& tempo = _tempo_map->tempo_at_frame (pos);
+               const Meter& meter = _tempo_map->meter_at_frame (pos);
+               return meter.frames_per_bar (tempo, frame_rate()) * -pr;
+       }
+       if (pr < 0) {
+               return 0;
+       }
+       return pr * frame_rate();
+}
+
 void
 Session::set_frame_rate (framecnt_t frames_per_second)
 {
@@ -2353,12 +2509,12 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool
           before anything else.
        */
 
-       for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
-               if (base == *reserved) {
+       for (map<string,bool>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
+               if (base == reserved->first) {
                        /* Check if this reserved name already exists, and if
                           so, disallow it without a numeric suffix.
                        */
-                       if (route_by_name (*reserved)) {
+                       if (!reserved->second || route_by_name (reserved->first)) {
                                definitely_add_number = true;
                                if (id < 1) {
                                        id = 1;
@@ -2368,8 +2524,11 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool
                }
        }
 
-       if (!definitely_add_number && route_by_name (base) == 0) {
-               /* juse use the base */
+       /* if we have "base 1" already, it doesn't make sense to add "base"
+        * if "base 1" has been deleted, adding "base" is no worse than "base 1"
+        */
+       if (!definitely_add_number && route_by_name (base) == 0 && (route_by_name (string_compose("%1 1", base)) == 0)) {
+               /* just use the base */
                name = base;
                return true;
        }
@@ -2398,11 +2557,13 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out)
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr && !tr->is_auditioner()) {
-                       in  += tr->n_inputs();
-                       out += tr->n_outputs();
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+               if (!tr) {
+                       continue;
                }
+               assert (!tr->is_auditioner()); // XXX remove me
+               in  += tr->n_inputs();
+               out += tr->n_outputs();
        }
 }
 
@@ -2430,9 +2591,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,
+Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool strict_io,
                          boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
-                         RouteGroup* route_group, uint32_t how_many, string name_template, PresentationInfo::order_t order,
+                         RouteGroup* route_group, uint32_t how_many,
+                         string name_template, PresentationInfo::order_t order,
                          TrackMode mode)
 {
        string track_name;
@@ -2459,12 +2621,10 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output,
                                goto failed;
                        }
 
-                       if (Profile->get_mixbus ()) {
+                       if (strict_io) {
                                track->set_strict_io (true);
                        }
 
-                       track->use_new_diskstream();
-
                        BOOST_MARK_TRACK (track);
 
                        {
@@ -2480,14 +2640,10 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output,
                                }
                        }
 
-                       track->non_realtime_input_change();
-
                        if (route_group) {
                                route_group->add (track);
                        }
 
-                       track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-
                        new_routes.push_back (track);
                        ret.push_back (track);
                }
@@ -2518,12 +2674,23 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output,
                if (instrument) {
                        for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) {
                                PluginPtr plugin = instrument->load (*this);
+                               if (!plugin) {
+                                       warning << "Failed to add Synth Plugin to newly created track." << endmsg;
+                                       continue;
+                               }
                                if (pset) {
                                        plugin->load_preset (*pset);
                                }
-                               boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
-                               (*r)->add_processor (p, PreFader);
+                               boost::shared_ptr<PluginInsert> pi (new PluginInsert (*this, plugin));
+                               if (strict_io) {
+                                       pi->set_strict_io (true);
+                               }
 
+                               (*r)->add_processor (pi, PreFader);
+
+                               if (Profile->get_mixbus () && pi->configured () && pi->output_streams().n_audio() > 2) {
+                                       (*r)->move_instrument_down (false);
+                               }
                        }
                }
        }
@@ -2532,7 +2699,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output,
 }
 
 RouteList
-Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, bool strict_io,
+                         boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset,
                          PresentationInfo::Flag flag, PresentationInfo::order_t order)
 {
        string bus_name;
@@ -2555,7 +2723,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                                goto failure;
                        }
 
-                       if (Profile->get_mixbus ()) {
+                       if (strict_io) {
                                bus->set_strict_io (true);
                        }
 
@@ -2606,11 +2774,23 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                if (instrument) {
                        for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
                                PluginPtr plugin = instrument->load (*this);
+                               if (!plugin) {
+                                       warning << "Failed to add Synth Plugin to newly created track." << endmsg;
+                                       continue;
+                               }
                                if (pset) {
                                        plugin->load_preset (*pset);
                                }
-                               boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
-                               (*r)->add_processor (p, PreFader);
+                               boost::shared_ptr<PluginInsert> pi (new PluginInsert (*this, plugin));
+                               if (strict_io) {
+                                       pi->set_strict_io (true);
+                               }
+
+                               (*r)->add_processor (pi, PreFader);
+
+                               if (Profile->get_mixbus () && pi->configured () && pi->output_streams().n_audio() > 2) {
+                                       (*r)->move_instrument_down (false);
+                               }
                        }
                }
        }
@@ -2928,9 +3108,36 @@ Session::reconnect_mmc_ports(bool inputs)
 
 #endif
 
+bool
+Session::ensure_stripable_sort_order ()
+{
+       StripableList sl;
+       get_stripables (sl);
+       sl.sort (Stripable::Sorter ());
+
+       bool change = false;
+       PresentationInfo::order_t order = 0;
+
+       for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
+               boost::shared_ptr<Stripable> s (*si);
+               assert (!s->is_auditioner ()); // XXX remove me
+               if (s->is_monitor ()) {
+                       continue;
+               }
+               if (order != s->presentation_info().order()) {
+                       s->set_presentation_order (order);
+                       change = true;
+               }
+               ++order;
+       }
+       return change;
+}
+
 void
 Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new_order, uint32_t how_many)
 {
+       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("ensure order gap starting at %1 for %2\n", first_new_order, how_many));
+
        if (first_new_order == PresentationInfo::max_order) {
                /* adding at end, no worries */
                return;
@@ -2945,7 +3152,11 @@ Session::ensure_route_presentation_info_gap (PresentationInfo::order_t first_new
        for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
                boost::shared_ptr<Stripable> s (*si);
 
-               if (s->is_monitor() || s->is_auditioner()) {
+               if (s->presentation_info().special (false)) {
+                       continue;
+               }
+
+               if (!s->presentation_info().order_set()) {
                        continue;
                }
 
@@ -3006,8 +3217,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                                }
                        }
 
-                       track->use_new_diskstream();
-
                        BOOST_MARK_TRACK (track);
 
                        {
@@ -3034,10 +3243,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                                route_group->add (track);
                        }
 
-                       track->non_realtime_input_change();
-
-                       track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-
                        new_routes.push_back (track);
                        ret.push_back (track);
                }
@@ -3218,17 +3423,27 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
 
                        /* set this name in the XML description that we are about to use */
 
-                       bool rename_playlist;
-                       switch (pd) {
-                       case NewPlaylist:
-                               rename_playlist = true;
-                               break;
-                       default:
-                       case CopyPlaylist:
-                       case SharePlaylist:
-                               rename_playlist = false;
+                       if (pd == CopyPlaylist) {
+                               XMLNode* ds_node = find_named_node (node_copy, "Diskstream");
+                               if (ds_node) {
+                                       const std::string playlist_name = ds_node->property (X_("playlist"))->value ();
+                                       boost::shared_ptr<Playlist> playlist = playlists->by_name (playlist_name);
+                                       // Use same name as Route::set_name_in_state so playlist copy
+                                       // is picked up when creating the Route in XMLRouteFactory below
+                                       playlist = PlaylistFactory::create (playlist, string_compose ("%1.1", name));
+                                       playlist->reset_shares ();
+                               }
+                       } else if (pd == SharePlaylist) {
+                               XMLNode* ds_node = find_named_node (node_copy, "Diskstream");
+                               if (ds_node) {
+                                       const std::string playlist_name = ds_node->property (X_("playlist"))->value ();
+                                       boost::shared_ptr<Playlist> playlist = playlists->by_name (playlist_name);
+                                       playlist->share_with ((node_copy.property (X_("id")))->value());
+                               }
                        }
 
+                       bool rename_playlist = (pd == CopyPlaylist || pd == NewPlaylist);
+
                        Route::set_name_in_state (node_copy, name, rename_playlist);
 
                        /* trim bitslots from listen sends so that new ones are used */
@@ -3244,12 +3459,12 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                                 */
                                                XMLProperty const * target = (*i)->property (X_("target"));
                                                if (!target) {
-                                                       (*i)->add_property ("type", "dangling-aux-send");
+                                                       (*i)->set_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");
+                                                       (*i)->set_property ("type", "dangling-aux-send");
                                                        continue;
                                                }
                                        }
@@ -3257,20 +3472,18 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                                (*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);
+                                               (*i)->set_property ("bitslot", bitslot);
+                                               (*i)->set_property ("name", name);
                                        }
                                        else if (type && type->value() == X_("intreturn")) {
                                                (*i)->remove_property (X_("bitslot"));
-                                               (*i)->add_property ("ignore-bitslot", "1");
+                                               (*i)->set_property ("ignore-bitslot", "1");
                                        }
                                        else if (type && type->value() == X_("return")) {
                                                // Return::set_state() generates a new one
@@ -3279,11 +3492,15 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                        else if (type && type->value() == X_("port")) {
                                                // PortInsert::set_state() handles the bitslot
                                                (*i)->remove_property (X_("bitslot"));
-                                               (*i)->add_property ("ignore-name", "1");
+                                               (*i)->set_property ("ignore-name", "1");
                                        }
                                }
                        }
 
+                       /* new routes start off unsoloed to avoid issues related to
+                          upstream / downstream buses. */
+                       node_copy.remove_node_and_delete (X_("Controllable"), X_("name"), X_("solo"));
+
                        boost::shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
 
                        if (route == 0) {
@@ -3306,21 +3523,6 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                route->output()->changed (change, this);
                        }
 
-                       boost::shared_ptr<Track> track;
-
-                       if ((track = boost::dynamic_pointer_cast<Track> (route))) {
-                               switch (pd) {
-                               case NewPlaylist:
-                                       track->use_new_playlist ();
-                                       break;
-                               case CopyPlaylist:
-                                       track->use_copy_playlist ();
-                                       break;
-                               case SharePlaylist:
-                                       break;
-                               }
-                       };
-
                        ret.push_back (route);
                }
 
@@ -3391,8 +3593,8 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
        {
                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();
+               r->insert (r->end(), new_routes.begin(), new_routes.end());
 
                /* 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
@@ -3405,83 +3607,92 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                }
        }
 
-       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());
+       /* monitor is not part of the order */
+       if (_monitor_out) {
+               assert (n_routes > 0);
+               --n_routes;
+       }
 
-       for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
+       {
+               PresentationInfo::ChangeSuspender cs;
+               ensure_route_presentation_info_gap (order, new_routes.size());
 
-               boost::weak_ptr<Route> wpr (*x);
-               boost::shared_ptr<Route> r (*x);
+               for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
 
-               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));
+                       boost::weak_ptr<Route> wpr (*x);
+                       boost::shared_ptr<Route> r (*x);
 
-               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));
+                       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));
 
-               if (r->is_master()) {
-                       _master_out = r;
-               }
+                       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_monitor()) {
-                       _monitor_out = r;
-               }
+                       if (r->is_master()) {
+                               _master_out = r;
+                       }
 
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (r);
-               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->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)));
+                       if (r->is_monitor()) {
+                               _monitor_out = r;
                        }
-               }
 
-               if (!r->presentation_info().special()) {
+                       boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (r);
+                       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->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->presentation_info().PropertyChanged.connect_same_thread (*this, boost::bind (&Session::midi_track_presentation_info_changed, this, _1, boost::weak_ptr<MidiTrack>(mt)));
+                               }
+                       }
 
-                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
+                       if (!r->presentation_info().special (false)) {
 
-                       /* presentation info order may already have been set from XML */
+                               DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
 
-                       if (!r->presentation_info().order_set()) {
+                               /* presentation info order may already have been set from XML */
 
-                               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));
+                               if (!r->presentation_info().order_set()) {
+                                       if (order == PresentationInfo::max_order) {
+                                               /* just add to the end */
+                                               r->set_presentation_order (n_routes + added);
+                                               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 {
-                                       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));
+                                       DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("group order already set to %1\n", r->presentation_info().order()));
                                }
-                       } else {
-                               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()));
+                       /* 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();
-               }
+                       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 ();
+                       ARDOUR::GUIIdle ();
+               }
+               ensure_stripable_sort_order ();
        }
 
        if (_monitor_out && IO::connecting_legal) {
@@ -3590,19 +3801,32 @@ Session::add_internal_send (boost::shared_ptr<Route> dest, boost::shared_ptr<Pro
 void
 Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 {
+       bool mute_changed = false;
+       bool send_selected = false;
+
        { // RCU Writer scope
                PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> rs = writer.get_copy ();
 
-
                for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
 
+                       if (_selection->selected (*iter)) {
+                               send_selected = true;
+                       }
+
                        if (*iter == _master_out) {
                                continue;
                        }
 
-                       (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
+                       /* speed up session deletion, don't do the solo dance */
+                       if (0 == (_state_of_the_state & Deletion)) {
+                               (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
+                       }
+
+                       if ((*iter)->mute_control()->muted ()) {
+                               mute_changed = true;
+                       }
 
                        rs->remove (*iter);
 
@@ -3639,7 +3863,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                        /* if the monitoring section had a pointer to this route, remove it */
                        if (_monitor_out && !(*iter)->is_master() && !(*iter)->is_monitor()) {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
-                               PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+                               ProcessorChangeBlocker pcb (this, false);
                                (*iter)->remove_aux_or_listen (_monitor_out);
                        }
 
@@ -3655,6 +3879,10 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 
        } // end of RCU Writer scope
 
+       if (mute_changed) {
+               MuteChanged (); /* EMIT SIGNAL */
+       }
+
        update_route_solo_state ();
        update_latency_compensation ();
        set_dirty();
@@ -3680,6 +3908,19 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 
        routes.flush ();
 
+       /* remove these routes from the selection if appropriate, and signal
+        * the change *before* we call DropReferences for them.
+        */
+
+       if (send_selected && !deletion_in_progress()) {
+               for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
+                       _selection->remove_stripable_by_id ((*iter)->id());
+               }
+               PropertyChange pc;
+               pc.add (Properties::selected);
+               PresentationInfo::Change (pc);
+       }
+
        /* try to cause everyone to drop their references
         * and unregister ports from the backend
         */
@@ -3688,11 +3929,13 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                (*iter)->drop_references ();
        }
 
-       if (_state_of_the_state & Deletion) {
+       if (deletion_in_progress()) {
                return;
        }
 
-       PresentationInfo::Change(); /* EMIT SIGNAL */
+       PropertyChange pc;
+       pc.add (Properties::order);
+       PresentationInfo::Change (pc);
 
        /* save the new state of the world */
 
@@ -3714,6 +3957,7 @@ Session::remove_route (boost::shared_ptr<Route> route)
 void
 Session::route_mute_changed ()
 {
+       MuteChanged (); /* EMIT SIGNAL */
        set_dirty ();
 }
 
@@ -3766,8 +4010,6 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr
 
                _listen_cnt--;
        }
-
-       update_route_solo_state ();
 }
 
 void
@@ -3801,7 +4043,7 @@ Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
 void
 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_changed));
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1, update\n", self_solo_changed));
 
        boost::shared_ptr<Route> route (wpr.lock());
 
@@ -3817,21 +4059,13 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
        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->solo_control()->transitioned_into_solo() == 0) {
-               /* route solo changed by upstream/downstream; not interesting
+               /* route solo changed by upstream/downstream or clear all solo state; 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;
        }
 
-       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 ();
 
@@ -3854,6 +4088,8 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
        RouteGroup* rg = route->route_group ();
        const bool group_already_accounted_for = (group_override == Controllable::ForGroup);
 
+       DEBUG_TRACE (DEBUG::Solo, string_compose ("propagate to session, group accounted for ? %1\n", group_already_accounted_for));
+
        if (delta == 1 && Config->get_exclusive_solo()) {
 
                /* new solo: disable all other solos, but not the group if its solo-enabled */
@@ -3967,8 +4203,6 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
 
        DEBUG_TRACE (DEBUG::Solo, "propagation complete\n");
 
-       update_route_solo_state (r);
-
        /* now notify that the mute state of the routes not involved in the signal
           pathway of the just-solo-changed route may have altered.
        */
@@ -3976,11 +4210,10 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
        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_control()->Changed (false, Controllable::NoGroup);
+               /* Session will emit SoloChanged() after all solo changes are
+                * complete, which should be used by UIs to update mute status
+                */
        }
-
-       SoloChanged (); /* EMIT SIGNAL */
-       set_dirty();
 }
 
 void
@@ -4000,13 +4233,13 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->can_solo()) {
                        if (Config->get_solo_control_is_listen_control()) {
-                               if ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value()) {
+                               if ((*i)->solo_control()->soloed_by_self_or_masters()) {
                                        listeners++;
                                        something_listening = true;
                                }
                        } else {
                                (*i)->set_listen (false);
-                               if ((*i)->can_solo() && ((*i)->self_soloed() || (*i)->solo_control()->get_masters_value())) {
+                               if ((*i)->can_solo() && (*i)->solo_control()->soloed_by_self_or_masters()) {
                                        something_soloed = true;
                                }
                        }
@@ -4036,6 +4269,65 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
 
        DEBUG_TRACE (DEBUG::Solo, string_compose ("solo state updated by session, soloed? %1 listeners %2 isolated %3\n",
                                                  something_soloed, listeners, isolated));
+
+
+       SoloChanged (); /* EMIT SIGNAL */
+       set_dirty();
+}
+
+bool
+Session::muted () const
+{
+       // TODO consider caching the value on every MuteChanged signal,
+       // Note that API users may also subscribe to MuteChanged and hence
+       // this method needs to be called first.
+       bool muted = false;
+       StripableList all;
+       get_stripables (all);
+       for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->is_monitor()) {
+                       continue;
+               }
+               boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
+               if (r && !r->active()) {
+                       continue;
+               }
+               boost::shared_ptr<MuteControl> mc = (*i)->mute_control();
+               if (mc && mc->muted ()) {
+                       muted = true;
+                       break;
+               }
+       }
+       return muted;
+}
+
+std::vector<boost::weak_ptr<AutomationControl> >
+Session::cancel_all_mute ()
+{
+       StripableList all;
+       get_stripables (all);
+       std::vector<boost::weak_ptr<AutomationControl> > muted;
+       boost::shared_ptr<ControlList> cl (new ControlList);
+       for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
+               assert (!(*i)->is_auditioner());
+               if ((*i)->is_monitor()) {
+                       continue;
+               }
+               boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*i);
+               if (r && !r->active()) {
+                       continue;
+               }
+               boost::shared_ptr<AutomationControl> ac = (*i)->mute_control();
+               if (ac && ac->get_value () > 0) {
+                       cl->push_back (ac);
+                       muted.push_back (boost::weak_ptr<AutomationControl>(ac));
+               }
+       }
+       if (!cl->empty ()) {
+               set_controls (cl, 0.0, PBD::Controllable::UseGroup);
+       }
+       return muted;
 }
 
 void
@@ -4048,6 +4340,15 @@ Session::get_stripables (StripableList& sl) const
        sl.insert (sl.end(), v.begin(), v.end());
 }
 
+StripableList
+Session::get_stripables () const
+{
+       StripableList rv;
+       Session::get_stripables (rv);
+       rv.sort (Stripable::Sorter ());
+       return rv;
+}
+
 boost::shared_ptr<RouteList>
 Session::get_routes_with_internal_returns() const
 {
@@ -4067,11 +4368,11 @@ Session::io_name_is_legal (const std::string& name) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
 
-       for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
-               if (name == *reserved) {
-                       if (!route_by_name (*reserved)) {
-                               /* first instance of a reserved name is allowed */
-                               return true;
+       for (map<string,bool>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
+               if (name == reserved->first) {
+                       if (!route_by_name (reserved->first)) {
+                               /* first instance of a reserved name is allowed for some */
+                               return reserved->second;
                        }
                        /* all other instances of a reserved name are not allowed */
                        return false;
@@ -4199,34 +4500,35 @@ Session::route_by_id (PBD::ID id) const
        return boost::shared_ptr<Route> ((Route*) 0);
 }
 
-boost::shared_ptr<Processor>
-Session::processor_by_id (PBD::ID id) const
+
+boost::shared_ptr<Stripable>
+Session::stripable_by_id (PBD::ID id) const
 {
-       boost::shared_ptr<RouteList> r = routes.reader ();
+       StripableList sl;
+       get_stripables (sl);
 
-       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;
+       for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
+               if ((*s)->id() == id) {
+                       return *s;
                }
        }
 
-       return boost::shared_ptr<Processor> ();
+       return boost::shared_ptr<Stripable>();
 }
 
-boost::shared_ptr<Track>
-Session::track_by_diskstream_id (PBD::ID id) const
+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<Track> t = boost::dynamic_pointer_cast<Track> (*i);
-               if (t && t->using_diskstream_id (id)) {
-                       return t;
+               boost::shared_ptr<Processor> p = (*i)->Route::processor_by_id (id);
+               if (p) {
+                       return p;
                }
        }
 
-       return boost::shared_ptr<Track> ();
+       return boost::shared_ptr<Processor> ();
 }
 
 boost::shared_ptr<Route>
@@ -4242,7 +4544,7 @@ Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo
        PresentationInfo::order_t match_cnt = 0;
 
        get_stripables (sl);
-       sl.sort (Stripable::PresentationOrderSorter());
+       sl.sort (Stripable::Sorter());
 
        for (StripableList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
 
@@ -4277,28 +4579,22 @@ Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo
 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) {
-               /* NOT IMPLEMENTED */
-       }
+       RouteList r (*(routes.reader ()));
+       r.sort (Stripable::Sorter());
 
-       return boost::shared_ptr<Route> ((Route*) 0);
-}
+       RouteList::iterator i;
 
-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();
+       for (i = r.begin(); i != r.end(); ++i) {
+               if ((*i)->is_selected()) {
+                       if (id == 0) {
+                               return *i;
+                       }
+                       --id;
                }
        }
-};
+
+       return boost::shared_ptr<Route> ();
+}
 
 void
 Session::reassign_track_numbers ()
@@ -4306,16 +4602,16 @@ Session::reassign_track_numbers ()
        int64_t tn = 0;
        int64_t bn = 0;
        RouteList r (*(routes.reader ()));
-       PresentationOrderSorter sorter;
-       r.sort (sorter);
+       r.sort (Stripable::Sorter());
 
        StateProtector sp (this);
 
        for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+               assert (!(*i)->is_auditioner());
                if (boost::dynamic_pointer_cast<Track> (*i)) {
                        (*i)->set_track_number(++tn);
                }
-               else if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) {
+               else if (!(*i)->is_master() && !(*i)->is_monitor()) {
                        (*i)->set_track_number(--bn);
                }
        }
@@ -4642,13 +4938,16 @@ Session::audio_source_by_path_and_channel (const string& path, uint16_t chn) con
 }
 
 boost::shared_ptr<MidiSource>
-Session::midi_source_by_path (const std::string& path) const
+Session::midi_source_by_path (const std::string& path, bool need_source_lock) const
 {
        /* Restricted to MIDI files because audio sources require a channel
           for unique identification, in addition to a path.
        */
 
-       Glib::Threads::Mutex::Lock lm (source_lock);
+       Glib::Threads::Mutex::Lock lm (source_lock, Glib::Threads::NOT_LOCK);
+       if (need_source_lock) {
+               lm.acquire ();
+       }
 
        for (SourceMap::const_iterator s = sources.begin(); s != sources.end(); ++s) {
                boost::shared_ptr<MidiSource> ms
@@ -4949,17 +5248,12 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha
 
 /** Return a unique name based on `base` for a new internal MIDI source */
 string
-Session::new_midi_source_path (const string& base)
+Session::new_midi_source_path (const string& base, bool need_lock)
 {
-       uint32_t cnt;
-       char buf[PATH_MAX+1];
-       const uint32_t limit = 10000;
-       string legalized;
        string possible_path;
        string possible_name;
 
-       buf[0] = '\0';
-       legalized = legalize_for_path (base);
+       possible_name = legalize_for_path (base);
 
        // Find a "version" of the file name that doesn't exist in any of the possible directories.
        std::vector<string> sdirs = source_search_path(DataType::MIDI);
@@ -4974,38 +5268,36 @@ Session::new_midi_source_path (const string& base)
         */
        std::reverse(sdirs.begin(), sdirs.end());
 
-       for (cnt = 1; cnt <= limit; ++cnt) {
+       while (true) {
+               possible_name = bump_name_once (possible_name, '-');
 
                vector<space_and_path>::iterator i;
                uint32_t existing = 0;
 
                for (vector<string>::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) {
 
-                       snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
-                       possible_name = buf;
-
-                       possible_path = Glib::build_filename (*i, possible_name);
+                       possible_path = Glib::build_filename (*i, possible_name + ".mid");
 
                        if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
                                existing++;
                        }
 
-                       if (midi_source_by_path (possible_path)) {
+                       if (midi_source_by_path (possible_path, need_lock)) {
                                existing++;
                        }
                }
 
-               if (existing == 0) {
-                       break;
-               }
-
-               if (cnt > limit) {
+               if (possible_path.size () >= PATH_MAX) {
                        error << string_compose(
-                                       _("There are already %1 recordings for %2, which I consider too many."),
-                                       limit, base) << endmsg;
+                                       _("There are already many recordings for %1, resulting in a too long file-path %2."),
+                                       base, possible_path) << endmsg;
                        destroy ();
                        return 0;
                }
+
+               if (existing == 0) {
+                       break;
+               }
        }
 
        /* No need to "find best location" for software/app-based RAID, because
@@ -5157,6 +5449,9 @@ Session::register_lua_function (
                tbl_arg[(*i)->name] = (*i)->value;
        }
        (*_lua_add)(name, bytecode, tbl_arg); // throws luabridge::LuaException
+       lm.release();
+
+       LuaScriptsChanged (); /* EMIT SIGNAL */
        set_dirty();
 }
 
@@ -5166,6 +5461,9 @@ Session::unregister_lua_function (const std::string& name)
        Glib::Threads::Mutex::Lock lm (lua_lock);
        (*_lua_del)(name); // throws luabridge::LuaException
        lua.collect_garbage ();
+       lm.release();
+
+       LuaScriptsChanged (); /* EMIT SIGNAL */
        set_dirty();
 }
 
@@ -5181,7 +5479,7 @@ Session::registered_lua_functions ()
                        if (!i.key ().isString ()) { assert(0); continue; }
                        rv.push_back (i.key ().cast<std::string> ());
                }
-       } catch (luabridge::LuaException const& e) { }
+       } catch (...) { }
        return rv;
 }
 
@@ -5197,7 +5495,7 @@ 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) { }
+               try { (*_lua_run)(nframes); } catch (...) { }
                lua.collect_garbage_step ();
        }
 }
@@ -5209,6 +5507,7 @@ Session::setup_lua ()
        lua.Print.connect (&_lua_print);
 #endif
        lua.tweak_rt_gc ();
+       lua.sandbox (true);
        lua.do_command (
                        "function ArdourSession ()"
                        "  local self = { scripts = {}, instances = {} }"
@@ -5225,8 +5524,7 @@ Session::setup_lua ()
                        "   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 }"
+                       "   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, bit32=bit32, 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"
@@ -5329,7 +5627,12 @@ Session::setup_lua ()
                _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"))
+                               std::string ("Failed to setup session Lua interpreter") + e.what ())
+                       << endmsg;
+               abort(); /*NOTREACHED*/
+       } catch (...) {
+               fatal << string_compose (_("programming error: %1"),
+                               X_("Failed to setup session Lua interpreter"))
                        << endmsg;
                abort(); /*NOTREACHED*/
        }
@@ -5355,6 +5658,11 @@ Session::scripts_changed ()
                }
                _n_lua_scripts = cnt;
        } catch (luabridge::LuaException const& e) {
+               fatal << string_compose (_("programming error: %1"),
+                               std::string ("Indexing Lua Session Scripts failed.") + e.what ())
+                       << endmsg;
+               abort(); /*NOTREACHED*/
+       } catch (...) {
                fatal << string_compose (_("programming error: %1"),
                                X_("Indexing Lua Session Scripts failed."))
                        << endmsg;
@@ -5391,18 +5699,6 @@ Session::cancel_audition ()
        }
 }
 
-bool
-Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
-{
-       if (a->is_monitor()) {
-               return true;
-       }
-       if (b->is_monitor()) {
-               return false;
-       }
-       return a->presentation_info().order() < b->presentation_info().order();
-}
-
 bool
 Session::is_auditioning () const
 {
@@ -5425,12 +5721,6 @@ Session::graph_reordered ()
                return;
        }
 
-       /* every track/bus asked for this to be handled but it was deferred because
-          we were connecting. do it now.
-       */
-
-       request_input_change_handling ();
-
        resort_routes ();
 
        /* force all diskstreams to update their capture offset values to
@@ -5441,7 +5731,7 @@ Session::graph_reordered ()
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
                if (tr) {
-                       tr->set_capture_offset ();
+                       tr->update_latency_information ();
                }
        }
 }
@@ -5555,21 +5845,11 @@ 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)
 {
        for (Locations::LocationList::const_iterator i = loc.begin(); i != loc.end(); ++i) {
-               (*i)->recompute_frames_from_bbt ();
+               (*i)->recompute_frames_from_beat ();
        }
 }
 
@@ -6069,6 +6349,12 @@ Session::send_gain_automation_buffer() const
        return ProcessThread::send_gain_automation_buffer ();
 }
 
+gain_t*
+Session::scratch_automation_buffer() const
+{
+       return ProcessThread::scratch_automation_buffer ();
+}
+
 pan_t**
 Session::pan_automation_buffer() const
 {
@@ -6136,6 +6422,23 @@ Session::nbusses () const
        return n;
 }
 
+uint32_t
+Session::nstripables (bool with_monitor) const
+{
+       uint32_t rv = routes.reader()->size ();
+       rv += _vca_manager->vcas ().size ();
+
+       if (with_monitor) {
+               return rv;
+       }
+
+       if (_monitor_out) {
+               assert (rv > 0);
+               --rv;
+       }
+       return rv;
+}
+
 void
 Session::add_automation_list(AutomationList *al)
 {
@@ -6199,8 +6502,8 @@ Session::update_route_record_state ()
 void
 Session::listen_position_changed ()
 {
+       ProcessorChangeBlocker pcb (this);
        boost::shared_ptr<RouteList> r = routes.reader ();
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                (*i)->listen_position_changed ();
        }
@@ -6210,14 +6513,18 @@ void
 Session::solo_control_mode_changed ()
 {
        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);
+               if (loading()) {
+                       /* 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);
+               } else {
+                       clear_all_solo_state (get_routes());
+               }
        }
 }
 
@@ -6241,6 +6548,10 @@ Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr<Route>
 {
        update_route_record_state ();
        RouteRemovedFromRouteGroup (rg, r); /* EMIT SIGNAL */
+
+       if (!rg->has_control_master () && !rg->has_subgroup () && rg->empty()) {
+               remove_route_group (*rg);
+       }
 }
 
 boost::shared_ptr<RouteList>
@@ -6251,9 +6562,8 @@ Session::get_tracks () const
 
        for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
                if (boost::dynamic_pointer_cast<Track> (*r)) {
-                       if (!(*r)->is_auditioner()) {
-                               tl->push_back (*r);
-                       }
+                       assert (!(*r)->is_auditioner()); // XXX remove me
+                       tl->push_back (*r);
                }
        }
        return tl;
@@ -6295,12 +6605,12 @@ Session::goto_end ()
 }
 
 void
-Session::goto_start ()
+Session::goto_start (bool and_roll)
 {
        if (_session_range_location) {
-               request_locate (_session_range_location->start(), false);
+               request_locate (_session_range_location->start(), and_roll);
        } else {
-               request_locate (0, false);
+               request_locate (0, and_roll);
        }
 }
 
@@ -6319,7 +6629,7 @@ Session::current_end_frame () const
 void
 Session::set_session_range_location (framepos_t start, framepos_t end)
 {
-       _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange);
+       _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange, 0);
        _locations->add (_session_range_location);
 }
 
@@ -6366,6 +6676,7 @@ Session::start_time_changed (framepos_t old)
        if (l && l->start() == old) {
                l->set_start (s->start(), true);
        }
+       set_dirty ();
 }
 
 void
@@ -6385,6 +6696,7 @@ Session::end_time_changed (framepos_t old)
        if (l && l->end() == old) {
                l->set_end (s->end(), true);
        }
+       set_dirty ();
 }
 
 std::vector<std::string>
@@ -6539,6 +6851,9 @@ Session::update_latency (bool playback)
        if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
                return;
        }
+       if (!_engine.running()) {
+               return;
+       }
 
        boost::shared_ptr<RouteList> r = routes.reader ();
        framecnt_t max_latency = 0;
@@ -6592,7 +6907,8 @@ Session::post_playback_latency ()
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_auditioner() && ((*i)->active())) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->active()) {
                        _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
                }
        }
@@ -6614,7 +6930,7 @@ Session::post_capture_latency ()
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
                if (tr) {
-                       tr->set_capture_offset ();
+                       tr->update_latency_information ();
                }
        }
 }
@@ -6698,7 +7014,8 @@ Session::update_latency_compensation (bool force_whole_graph)
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_auditioner() && ((*i)->active())) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->active()) {
                        framecnt_t tl;
                        if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) {
                                some_track_latency_changed = true;
@@ -6722,7 +7039,7 @@ Session::update_latency_compensation (bool force_whole_graph)
                if (!tr) {
                        continue;
                }
-               tr->set_capture_offset ();
+               tr->update_latency_information ();
        }
 }
 
@@ -6747,7 +7064,6 @@ Session::notify_presentation_info_change ()
                return;
        }
 
-       PresentationInfo::Change (); /* EMIT SIGNAL */
        reassign_track_numbers();
 
 #ifdef USE_TRACKS_CODE_FEATURES
@@ -6768,13 +7084,14 @@ Session::operation_in_progress (GQuark op) const
 boost::shared_ptr<Port>
 Session::ltc_input_port () const
 {
+       assert (_ltc_input);
        return _ltc_input->nth (0);
 }
 
 boost::shared_ptr<Port>
 Session::ltc_output_port () const
 {
-       return _ltc_output->nth (0);
+       return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr<Port> ();
 }
 
 void
@@ -6860,6 +7177,12 @@ Session::auto_connect_route (boost::shared_ptr<Route> route, bool connect_inputs
                                input_start, output_start,
                                input_offset, output_offset));
 
+       auto_connect_thread_wakeup ();
+}
+
+void
+Session::auto_connect_thread_wakeup ()
+{
        if (pthread_mutex_trylock (&_auto_connect_mutex) == 0) {
                pthread_cond_signal (&_auto_connect_cond);
                pthread_mutex_unlock (&_auto_connect_mutex);
@@ -6870,10 +7193,7 @@ 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);
-       }
+       auto_connect_thread_wakeup ();
 }
 
 void
@@ -6912,8 +7232,12 @@ Session::auto_connect (const AutoConnectRequest& ar)
                vector<string> physinputs;
                vector<string> physoutputs;
 
-               _engine.get_physical_outputs (*t, physoutputs);
-               _engine.get_physical_inputs (*t, physinputs);
+
+               /* for connecting track inputs we only want MIDI ports marked
+                * for "music".
+                */
+
+               get_physical_ports (physinputs, physoutputs, *t, MidiPortMusic);
 
                if (!physinputs.empty() && ar.connect_inputs) {
                        uint32_t nphysical_in = physinputs.size();
@@ -6967,7 +7291,7 @@ Session::auto_connect (const AutoConnectRequest& ar)
 void
 Session::auto_connect_thread_start ()
 {
-       if (_ac_thread_active) {
+       if (g_atomic_int_get (&_ac_thread_active)) {
                return;
        }
 
@@ -6975,19 +7299,18 @@ Session::auto_connect_thread_start ()
                _auto_connect_queue.pop ();
        }
 
-       _ac_thread_active = true;
+       g_atomic_int_set (&_ac_thread_active, 1);
        if (pthread_create (&_auto_connect_thread, NULL, auto_connect_thread, this)) {
-               _ac_thread_active = false;
+               g_atomic_int_set (&_ac_thread_active, 0);
        }
 }
 
 void
 Session::auto_connect_thread_terminate ()
 {
-       if (!_ac_thread_active) {
+       if (!g_atomic_int_get (&_ac_thread_active)) {
                return;
        }
-       _ac_thread_active = false;
 
        {
                Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
@@ -6996,10 +7319,14 @@ Session::auto_connect_thread_terminate ()
                }
        }
 
-       if (pthread_mutex_lock (&_auto_connect_mutex) == 0) {
-               pthread_cond_signal (&_auto_connect_cond);
-               pthread_mutex_unlock (&_auto_connect_mutex);
-       }
+       /* cannot use auto_connect_thread_wakeup() because that is allowed to
+        * fail to wakeup the thread.
+        */
+
+       pthread_mutex_lock (&_auto_connect_mutex);
+       g_atomic_int_set (&_ac_thread_active, 0);
+       pthread_cond_signal (&_auto_connect_cond);
+       pthread_mutex_unlock (&_auto_connect_mutex);
 
        void *status;
        pthread_join (_auto_connect_thread, &status);
@@ -7021,7 +7348,7 @@ Session::auto_connect_thread_run ()
        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) {
+       while (g_atomic_int_get (&_ac_thread_active)) {
 
                if (!_auto_connect_queue.empty ()) {
                        // Why would we need the process lock ??
@@ -7045,8 +7372,8 @@ Session::auto_connect_thread_run ()
                        /* 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
+                        * .. update_latency_compensation () entails Track::update_latency_information()
+                        * which calls DiskWriter::set_capture_offset () which
                         * modifies the capture offset... which can be a proplem
                         * in "prepare_to_stop"
                         */
@@ -7055,6 +7382,13 @@ Session::auto_connect_thread_run ()
                        }
                }
 
+               {
+                       // this may call ARDOUR::Port::drop ... jack_port_unregister ()
+                       // jack1 cannot cope with removing ports while processing
+                       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                       AudioEngine::instance()->clear_pending_port_deletions ();
+               }
+
                pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);
        }
        pthread_mutex_unlock (&_auto_connect_mutex);