Fix periodic backup saves
[ardour.git] / libs / ardour / session.cc
index df8ae9fc51f355969c1b459e394141115fc930d8..d61a53cd523dd4913fd16f03319d6852438a655f 100644 (file)
@@ -1,21 +1,31 @@
 /*
-    Copyright (C) 1999-2010 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 1999-2019 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2006-2007 Jesse Chappell <jesse@essej.net>
+ * Copyright (C) 2006-2009 Sampo Savolainen <v2@iki.fi>
+ * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2006-2016 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2008-2009 Hans Baier <hansfbaier@googlemail.com>
+ * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
+ * Copyright (C) 2015 GZharun <grygoriiz@wavesglobal.com>
+ * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 #include <stdint.h>
 
@@ -45,6 +55,7 @@
 #include "pbd/stacktrace.h"
 #include "pbd/stl_delete.h"
 #include "pbd/replace_all.h"
+#include "pbd/types_convert.h"
 #include "pbd/unwind.h"
 
 #include "ardour/amp.h"
 #include "ardour/revision.h"
 #include "ardour/route_graph.h"
 #include "ardour/route_group.h"
+#include "ardour/rt_tasklist.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/slave.h"
 #include "ardour/solo_isolate_control.h"
 #include "ardour/source_factory.h"
 #include "ardour/speakers.h"
 #include "ardour/tempo.h"
 #include "ardour/ticker.h"
+#include "ardour/transport_master.h"
 #include "ardour/track.h"
 #include "ardour/types_convert.h"
 #include "ardour/user_bundle.h"
@@ -170,7 +181,7 @@ Session::Session (AudioEngine &eng,
                   const string& snapshot_name,
                   BusProfile* bus_profile,
                   string mix_template)
-       : playlists (new SessionPlaylists)
+       : _playlists (new SessionPlaylists)
        , _engine (eng)
        , process_function (&Session::process_with_events)
        , _bounce_processing_active (false)
@@ -183,24 +194,22 @@ Session::Session (AudioEngine &eng,
        , _transport_sample (0)
        , _seek_counter (0)
        , _session_range_location (0)
-       , _session_range_end_is_free (true)
-       , _slave (0)
+       , _session_range_is_free (true)
        , _silent (false)
        , _remaining_latency_preroll (0)
+       , _engine_speed (1.0)
        , _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_sample (0)
-       , maximum_output_latency (0)
        , _requested_return_sample (-1)
        , current_block_size (0)
        , _worst_output_latency (0)
        , _worst_input_latency (0)
-       , _worst_track_latency (0)
-       , _worst_track_out_latency (0)
+       , _worst_route_latency (0)
+       , _send_latency_changes (0)
        , _have_captured (false)
        , _non_soloed_outs_muted (false)
        , _listening (false)
@@ -210,13 +219,8 @@ Session::Session (AudioEngine &eng,
        , _was_seamless (Config->get_seamless_loop ())
        , _under_nsm_control (false)
        , _xrun_count (0)
-       , delta_accumulator_cnt (0)
-       , average_slave_delta (1800) // !!! why 1800 ???
-       , average_dir (0)
-       , have_first_delta_accumulator (false)
-       , _slave_state (Stopped)
-       , _mtc_active (false)
-       , _ltc_active (false)
+       , transport_master_tracking_state (Stopped)
+       , master_wait_end (0)
        , post_export_sync (false)
        , post_export_position (0)
        , _exporting (false)
@@ -236,24 +240,18 @@ Session::Session (AudioEngine &eng,
        , _current_snapshot_name (snapshot_name)
        , state_tree (0)
        , state_was_pending (false)
-       , _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
+       , _state_of_the_state (StateOfTheState (CannotSave | InitialConnecting | Loading))
        , _suspend_save (0)
        , _save_queued (false)
        , _last_roll_location (0)
        , _last_roll_or_reversal_location (0)
        , _last_record_location (0)
-       , pending_locate_roll (false)
-       , pending_locate_sample (0)
-       , pending_locate_flush (false)
-       , pending_abort (false)
        , pending_auto_loop (false)
        , _mempool ("Session", 3145728)
        , lua (lua_newstate (&PBD::ReallocPool::lalloc, &_mempool))
        , _n_lua_scripts (0)
        , _butler (new Butler (*this))
        , _post_transport_work (0)
-       ,  cumulative_rf_motion (0)
-       , rf_scale (1.0)
        , _locations (new Locations (*this))
        , _ignore_skips_updates (false)
        , _rt_thread_active (false)
@@ -312,7 +310,6 @@ Session::Session (AudioEngine &eng,
        , _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)
@@ -448,8 +445,7 @@ Session::Session (AudioEngine &eng,
        store_recent_sessions (_name, _path);
 
        bool was_dirty = dirty();
-
-       _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+       unset_dirty ();
 
        PresentationInfo::Change.connect_same_thread (*this, boost::bind (&Session::notify_presentation_info_change, this));
 
@@ -463,6 +459,8 @@ Session::Session (AudioEngine &eng,
        StartTimeChanged.connect_same_thread (*this, boost::bind (&Session::start_time_changed, this, _1));
        EndTimeChanged.connect_same_thread (*this, boost::bind (&Session::end_time_changed, this, _1));
 
+       Send::ChangedLatency.connect_same_thread (*this, boost::bind (&Session::send_latency_compensation_change, this));
+
        emit_thread_start ();
        auto_connect_thread_start ();
 
@@ -599,6 +597,8 @@ Session::immediately_post_engine ()
         * session or set state for an existing one.
         */
 
+       _rt_tasklist.reset (new RTTaskList ());
+
        if (how_many_dsp_threads () > 1) {
                /* For now, only create the graph if we are using >1 DSP threads, as
                   it is a bit slower than the old code with 1 thread.
@@ -651,13 +651,11 @@ Session::destroy ()
 
        Analyser::flush ();
 
-       _state_of_the_state = StateOfTheState (CannotSave|Deletion);
+       _state_of_the_state = StateOfTheState (CannotSave | Deletion);
 
        {
                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 */
@@ -671,7 +669,6 @@ Session::destroy ()
 
        /* remove I/O objects before unsetting the engine session */
        _click_io.reset ();
-       _ltc_input.reset ();
        _ltc_output.reset ();
 
        ControlProtocolManager::instance().drop_protocols ();
@@ -687,18 +684,20 @@ Session::destroy ()
        EngineStateController::instance()->remove_session();
 #endif
 
-       /* drop slave, if any. We don't use use_sync_source (0) because
-        * there's no reason to do all the other stuff that may happen
-        * when calling that method.
-        */
-       delete _slave;
-
        /* deregister all ports - there will be no process or any other
         * callbacks from the engine any more.
         */
 
        Port::PortDrop (); /* EMIT SIGNAL */
 
+       {
+               Glib::Threads::Mutex::Lock lm (controllables_lock);
+               for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
+                       (*i)->DropReferences (); /* EMIT SIGNAL */
+               }
+               controllables.clear ();
+       }
+
        /* clear history so that no references to objects are held any more */
 
        _history.clear ();
@@ -814,7 +813,7 @@ Session::destroy ()
        }
 
        /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
-       playlists.reset ();
+       _playlists.reset ();
 
        emit_thread_terminate ();
 
@@ -846,11 +845,9 @@ Session::destroy ()
                bool del = true;
                switch (ev->type) {
                        case SessionEvent::AutoLoop:
-                       case SessionEvent::AutoLoopDeclick:
                        case SessionEvent::Skip:
                        case SessionEvent::PunchIn:
                        case SessionEvent::PunchOut:
-                       case SessionEvent::RecordStart:
                        case SessionEvent::StopOnce:
                        case SessionEvent::RangeStop:
                        case SessionEvent::RangeLocate:
@@ -885,6 +882,10 @@ Session::destroy ()
 
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
+#ifndef NDEBUG
+       Controllable::dump_registry ();
+#endif
+
        BOOST_SHOW_POINTERS ();
 }
 
@@ -893,21 +894,8 @@ Session::setup_ltc ()
 {
        XMLNode* child = 0;
 
-       _ltc_input.reset (new IO (*this, X_("LTC In"), IO::Input));
        _ltc_output.reset (new IO (*this, X_("LTC Out"), IO::Output));
 
-       if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC In"))) != 0) {
-               _ltc_input->set_state (*(child->children().front()), Stateful::loading_state_version);
-       } else {
-               {
-                       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 ();
-               }
-       }
-
        if (state_tree && (child = find_named_node (*state_tree->root(), X_("LTC Out"))) != 0) {
                _ltc_output->set_state (*(child->children().front()), Stateful::loading_state_version);
        } else {
@@ -923,7 +911,6 @@ Session::setup_ltc ()
         * IO style of NAME/TYPE-{in,out}N
         */
 
-       _ltc_input->nth (0)->set_name (X_("LTC-in"));
        _ltc_output->nth (0)->set_name (X_("LTC-out"));
 }
 
@@ -1065,7 +1052,7 @@ Session::setup_bundles ()
                if (np + 1 < outputs[DataType::AUDIO].size()) {
                        char buf[32];
                        snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2);
-                        boost::shared_ptr<Bundle> c (new Bundle (buf, true));
+                       boost::shared_ptr<Bundle> c (new Bundle (buf, true));
                        c->add_channel (_("L"), DataType::AUDIO);
                        c->set_port (0, outputs[DataType::AUDIO][np]);
                        c->add_channel (_("R"), DataType::AUDIO);
@@ -1194,6 +1181,12 @@ Session::remove_monitor_section ()
                return;
        }
 
+       /* allow deletion when session is unloaded */
+       if (!_engine.running() && !deletion_in_progress ()) {
+               error << _("Cannot remove monitor section while the engine is offline.") << endmsg;
+               return;
+       }
+
        /* force reversion to Solo-In-Place */
        Config->set_solo_control_is_listen_control (false);
 
@@ -1203,7 +1196,7 @@ Session::remove_monitor_section ()
        */
        cancel_audition ();
 
-       {
+       if (!deletion_in_progress ()) {
                /* Hold process lock while doing this so that we don't hear bits and
                 * pieces of audio as we work on each route.
                 */
@@ -1232,7 +1225,7 @@ Session::remove_monitor_section ()
        }
 
        remove_route (_monitor_out);
-       if (_state_of_the_state & Deletion) {
+       if (deletion_in_progress ()) {
                return;
        }
 
@@ -1242,7 +1235,7 @@ Session::remove_monitor_section ()
                auditioner->connect ();
        }
 
-       Config->ParameterChanged ("use-monitor-bus");
+       MonitorBusAddedOrRemoved (); /* EMIT SIGNAL */
 }
 
 void
@@ -1250,6 +1243,11 @@ Session::add_monitor_section ()
 {
        RouteList rl;
 
+       if (!_engine.running()) {
+               error << _("Cannot create monitor section while the engine is offline.") << endmsg;
+               return;
+       }
+
        if (_monitor_out || !_master_out || Profile->get_trx()) {
                return;
        }
@@ -1393,7 +1391,8 @@ Session::add_monitor_section ()
        if (auditioner) {
                auditioner->connect ();
        }
-       Config->ParameterChanged ("use-monitor-bus");
+
+       MonitorBusAddedOrRemoved (); /* EMIT SIGNAL */
 }
 
 void
@@ -1627,11 +1626,6 @@ Session::track_playlist_changed (boost::weak_ptr<Track> wp)
 bool
 Session::record_enabling_legal () const
 {
-       /* this used to be in here, but survey says.... we don't need to restrict it */
-       // if (record_status() == Recording) {
-       //      return false;
-       // }
-
        if (Config->get_all_safe()) {
                return false;
        }
@@ -1656,7 +1650,7 @@ Session::auto_punch_start_changed (Location* location)
 {
        replace_event (SessionEvent::PunchIn, location->start());
 
-       if (get_record_enabled() && config.get_punch_in()) {
+       if (get_record_enabled() && config.get_punch_in() && !actively_recording ()) {
                /* capture start has been changed, so save new pending state */
                save_state ("", true);
        }
@@ -1665,19 +1659,14 @@ Session::auto_punch_start_changed (Location* location)
 void
 Session::auto_punch_end_changed (Location* location)
 {
-       samplepos_t when_to_stop = location->end();
-       // when_to_stop += _worst_output_latency + _worst_input_latency;
-       replace_event (SessionEvent::PunchOut, when_to_stop);
+       replace_event (SessionEvent::PunchOut, location->end());
 }
 
 void
 Session::auto_punch_changed (Location* location)
 {
-       samplepos_t when_to_stop = location->end();
-
-       replace_event (SessionEvent::PunchIn, location->start());
-       //when_to_stop += _worst_output_latency + _worst_input_latency;
-       replace_event (SessionEvent::PunchOut, when_to_stop);
+       auto_punch_start_changed (location);
+       auto_punch_end_changed (location);
 }
 
 /** @param loc A loop location.
@@ -1699,34 +1688,17 @@ Session::auto_loop_changed (Location* location)
        samplecnt_t dcl;
        auto_loop_declick_range (location, dcp, dcl);
 
-       if (transport_rolling() && play_loop) {
+       bool rolling = transport_rolling ();
 
-               replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl);
-
-               // if (_transport_sample > location->end()) {
+       if (rolling && play_loop) {
 
                if (_transport_sample < location->start() || _transport_sample > location->end()) {
                        // relocate to beginning of loop
                        clear_events (SessionEvent::LocateRoll);
-
                        request_locate (location->start(), true);
 
                }
-               else if (Config->get_seamless_loop() && !loop_changing) {
-
-                       // schedule a locate-roll to refill the disk readers at the
-                       // previous loop end
-                       loop_changing = true;
-
-                       if (location->end() > last_loopend) {
-                               clear_events (SessionEvent::LocateRoll);
-                               SessionEvent *ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, last_loopend, last_loopend, 0, true);
-                               queue_event (ev);
-                       }
-
-               }
        } else {
-               clear_events (SessionEvent::AutoLoopDeclick);
                clear_events (SessionEvent::AutoLoop);
        }
 
@@ -1736,13 +1708,12 @@ Session::auto_loop_changed (Location* location)
 
        samplepos_t pos;
 
-       if (!transport_rolling() && select_playhead_priority_target (pos)) {
+       if (!rolling && select_playhead_priority_target (pos)) {
                if (pos == location->start()) {
                        request_locate (pos);
                }
        }
 
-
        last_loopend = location->end();
        set_dirty ();
 }
@@ -1755,7 +1726,7 @@ Session::set_auto_punch_location (Location* location)
        if ((existing = _locations->auto_punch_location()) != 0 && existing != location) {
                punch_connections.drop_connections();
                existing->set_auto_punch (false, this);
-               remove_event (existing->start(), SessionEvent::PunchIn);
+               clear_events (SessionEvent::PunchIn);
                clear_events (SessionEvent::PunchOut);
                auto_punch_location_changed (0);
        }
@@ -1815,7 +1786,6 @@ Session::set_auto_loop_location (Location* location)
                samplepos_t dcp;
                samplecnt_t dcl;
                auto_loop_declick_range (existing, dcp, dcl);
-               remove_event (dcp, SessionEvent::AutoLoopDeclick);
                auto_loop_location_changed (0);
        }
 
@@ -1876,10 +1846,10 @@ Session::update_skips (Location* loc, bool consolidate)
 
        Locations::LocationList skips;
 
-        if (consolidate) {
-               PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
-               consolidate_skips (loc);
-        }
+       if (consolidate) {
+               PBD::Unwinder<bool> uw (_ignore_skips_updates, true);
+               consolidate_skips (loc);
+       }
 
        sync_locations_to_skips ();
 
@@ -1889,41 +1859,41 @@ Session::update_skips (Location* loc, bool consolidate)
 void
 Session::consolidate_skips (Location* loc)
 {
-        Locations::LocationList all_locations = _locations->list ();
+       Locations::LocationList all_locations = _locations->list ();
 
-        for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
+       for (Locations::LocationList::iterator l = all_locations.begin(); l != all_locations.end(); ) {
 
-                if (!(*l)->is_skip ()) {
-                        ++l;
-                        continue;
-                }
+               if (!(*l)->is_skip ()) {
+                       ++l;
+                       continue;
+               }
 
-                /* don't test against self */
+               /* don't test against self */
 
-                if (*l == loc) {
-                        ++l;
-                        continue;
-                }
+               if (*l == loc) {
+                       ++l;
+                       continue;
+               }
 
-                switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
-                case Evoral::OverlapInternal:
-                case Evoral::OverlapExternal:
-                case Evoral::OverlapStart:
-                case Evoral::OverlapEnd:
-                        /* adjust new location to cover existing one */
-                        loc->set_start (min (loc->start(), (*l)->start()));
-                        loc->set_end (max (loc->end(), (*l)->end()));
-                        /* we don't need this one any more */
-                        _locations->remove (*l);
-                        /* the location has been deleted, so remove reference to it in our local list */
-                        l = all_locations.erase (l);
-                        break;
+               switch (Evoral::coverage ((*l)->start(), (*l)->end(), loc->start(), loc->end())) {
+                       case Evoral::OverlapInternal:
+                       case Evoral::OverlapExternal:
+                       case Evoral::OverlapStart:
+                       case Evoral::OverlapEnd:
+                               /* adjust new location to cover existing one */
+                               loc->set_start (min (loc->start(), (*l)->start()));
+                               loc->set_end (max (loc->end(), (*l)->end()));
+                               /* we don't need this one any more */
+                               _locations->remove (*l);
+                               /* the location has been deleted, so remove reference to it in our local list */
+                               l = all_locations.erase (l);
+                               break;
 
-                case Evoral::OverlapNone:
-                        ++l;
-                        break;
-                }
-        }
+                       case Evoral::OverlapNone:
+                               ++l;
+                               break;
+               }
+       }
 }
 
 void
@@ -1957,52 +1927,52 @@ Session::_sync_locations_to_skips ()
 void
 Session::location_added (Location *location)
 {
-        if (location->is_auto_punch()) {
-                set_auto_punch_location (location);
-        }
+       if (location->is_auto_punch()) {
+               set_auto_punch_location (location);
+       }
 
-        if (location->is_auto_loop()) {
-                set_auto_loop_location (location);
-        }
+       if (location->is_auto_loop()) {
+               set_auto_loop_location (location);
+       }
 
-        if (location->is_session_range()) {
-                /* no need for any signal handling or event setting with the session range,
-                   because we keep a direct reference to it and use its start/end directly.
-                */
-                _session_range_location = location;
-        }
+       if (location->is_session_range()) {
+               /* no need for any signal handling or event setting with the session range,
+                        because we keep a direct reference to it and use its start/end directly.
+                        */
+               _session_range_location = location;
+       }
 
-        if (location->is_mark()) {
-                /* listen for per-location signals that require us to do any * global updates for marks */
+       if (location->is_mark()) {
+               /* 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->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_range_marker()) {
-                /* listen for per-location signals that require us to do any * global updates for marks */
+               /* 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->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()) {
-                /* listen for per-location signals that require us to update skip-locate events */
+       if (location->is_skip()) {
+               /* listen for per-location signals that require us to update skip-locate events */
 
-                location->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
-                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->StartChanged.connect_same_thread (skip_update_connections, boost::bind (&Session::update_skips, this, location, true));
+               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);
-        }
+               update_skips (location, true);
+       }
 
        set_dirty ();
 }
@@ -2010,24 +1980,27 @@ Session::location_added (Location *location)
 void
 Session::location_removed (Location *location)
 {
-        if (location->is_auto_loop()) {
-               set_auto_loop_location (0);
-               set_track_loop (false);
-        }
+       if (location->is_auto_loop()) {
+               set_auto_loop_location (0);
+               if (!play_loop) {
+                       set_track_loop (false);
+               }
+               unset_play_loop ();
+       }
 
-        if (location->is_auto_punch()) {
-                set_auto_punch_location (0);
-        }
+       if (location->is_auto_punch()) {
+               set_auto_punch_location (0);
+       }
 
-        if (location->is_session_range()) {
-                /* this is never supposed to happen */
-                error << _("programming error: session range removed!") << endl;
-        }
+       if (location->is_session_range()) {
+               /* this is never supposed to happen */
+               error << _("programming error: session range removed!") << endl;
+       }
 
-        if (location->is_skip()) {
+       if (location->is_skip()) {
 
-                update_skips (location, false);
-        }
+               update_skips (location, false);
+       }
 
        set_dirty ();
 }
@@ -2035,17 +2008,17 @@ Session::location_removed (Location *location)
 void
 Session::locations_changed ()
 {
-        _locations->apply (*this, &Session::_locations_changed);
+       _locations->apply (*this, &Session::_locations_changed);
 }
 
 void
 Session::_locations_changed (const Locations::LocationList& locations)
 {
-        /* There was some mass-change in the Locations object.
-
-           We might be re-adding a location here but it doesn't actually matter
-           for all the locations that the Session takes an interest in.
-        */
+       /* There was some mass-change in the Locations object.
+        * 
+        * We might be re-adding a location here but it doesn't actually matter
+        * for all the locations that the Session takes an interest in.
+        */
 
        {
                PBD::Unwinder<bool> protect_ignore_skip_updates (_ignore_skips_updates, true);
@@ -2115,11 +2088,6 @@ Session::disable_record (bool rt_context, bool force)
                }
 
                RecordStateChanged (); /* emit signal */
-
-               if (!rt_context) {
-                       remove_pending_capture_state ();
-               }
-               unset_preroll_record_punch ();
        }
 }
 
@@ -2157,7 +2125,7 @@ Session::maybe_enable_record (bool rt_context)
        }
 
        if (_transport_speed) {
-               if (!config.get_punch_in() && !preroll_record_punch_enabled ()) {
+               if (!config.get_punch_in()) {
                        enable_record ();
                }
        } else {
@@ -2295,7 +2263,7 @@ Session::set_block_size (pframes_t nframes)
 
 
 static void
-trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
+trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase, bool sends_only)
 {
        boost::shared_ptr<Route> r2;
 
@@ -2322,7 +2290,7 @@ trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
                   base as being fed by r2
                */
 
-               rbase->add_fed_by (r2, i->sends_only);
+               rbase->add_fed_by (r2, i->sends_only || sends_only);
 
                if (r2 != rbase) {
 
@@ -2338,7 +2306,7 @@ trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
                           all routes that feed r2
                        */
 
-                       trace_terminal (r2, rbase);
+                       trace_terminal (r2, rbase, i->sends_only || sends_only);
                }
 
        }
@@ -2352,7 +2320,7 @@ Session::resort_routes ()
           are being destroyed.
        */
 
-       if (_state_of_the_state & (InitialConnecting | Deletion)) {
+       if (inital_connect_or_deletion_in_progress ()) {
                return;
        }
 
@@ -2453,7 +2421,7 @@ Session::resort_routes_using (boost::shared_ptr<RouteList> r)
                   or indirectly feeds them.
                */
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       trace_terminal (*i, *i);
+                       trace_terminal (*i, *i, false);
                }
 
                *r = *sorted_routes;
@@ -2655,7 +2623,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s
                --how_many;
        }
 
-  failed:
+       failed:
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -2759,7 +2727,7 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                --how_many;
        }
 
-  failure:
+       failure:
        if (!ret.empty()) {
                StateProtector sp (this);
                add_routes (ret, false, false, false, order);
@@ -2991,77 +2959,43 @@ Session::reconnect_existing_routes (bool withLock, bool reconnect_master, bool r
 void
 Session::reconnect_midi_scene_ports(bool inputs)
 {
-    if (inputs ) {
-
-        boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
-        if (scene_in_ptr) {
-            scene_in_ptr->disconnect_all ();
-
-            std::vector<EngineStateController::MidiPortState> midi_port_states;
-            EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
-
-            std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
-
-            for (; state_iter != midi_port_states.end(); ++state_iter) {
-                if (state_iter->active && state_iter->available && state_iter->scene_connected) {
-                    scene_in_ptr->connect (state_iter->name);
-                }
-            }
-        }
+       if (inputs ) {
 
-    } else {
+               boost::shared_ptr<MidiPort> scene_in_ptr = scene_in();
+               if (scene_in_ptr) {
+                       scene_in_ptr->disconnect_all ();
 
-        boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
+                       std::vector<EngineStateController::MidiPortState> midi_port_states;
+                       EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
 
-        if (scene_out_ptr ) {
-            scene_out_ptr->disconnect_all ();
+                       std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
 
-            std::vector<EngineStateController::MidiPortState> midi_port_states;
-            EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
-
-            std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
+                       for (; state_iter != midi_port_states.end(); ++state_iter) {
+                               if (state_iter->active && state_iter->available && state_iter->scene_connected) {
+                                       scene_in_ptr->connect (state_iter->name);
+                               }
+                       }
+               }
 
-            for (; state_iter != midi_port_states.end(); ++state_iter) {
-                if (state_iter->active && state_iter->available && state_iter->scene_connected) {
-                    scene_out_ptr->connect (state_iter->name);
-                }
-            }
-        }
-    }
-}
+       } else {
 
-void
-Session::reconnect_mtc_ports ()
-{
-       boost::shared_ptr<MidiPort> mtc_in_ptr = _midi_ports->mtc_input_port();
+               boost::shared_ptr<MidiPort> scene_out_ptr = scene_out();
 
-       if (!mtc_in_ptr) {
-               return;
-       }
+               if (scene_out_ptr ) {
+                       scene_out_ptr->disconnect_all ();
 
-       mtc_in_ptr->disconnect_all ();
+                       std::vector<EngineStateController::MidiPortState> midi_port_states;
+                       EngineStateController::instance()->get_physical_midi_output_states (midi_port_states);
 
-       std::vector<EngineStateController::MidiPortState> midi_port_states;
-       EngineStateController::instance()->get_physical_midi_input_states (midi_port_states);
+                       std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
 
-       std::vector<EngineStateController::MidiPortState>::iterator state_iter = midi_port_states.begin();
-
-       for (; state_iter != midi_port_states.end(); ++state_iter) {
-               if (state_iter->available && state_iter->mtc_in) {
-                       mtc_in_ptr->connect (state_iter->name);
+                       for (; state_iter != midi_port_states.end(); ++state_iter) {
+                               if (state_iter->active && state_iter->available && state_iter->scene_connected) {
+                                       scene_out_ptr->connect (state_iter->name);
+                               }
+                       }
                }
        }
-
-       if (!_midi_ports->mtc_input_port ()->connected () &&
-           config.get_external_sync () &&
-           (Config->get_sync_source () == MTC) ) {
-               config.set_external_sync (false);
-       }
-
-       if ( ARDOUR::Profile->get_trx () ) {
-               // Tracks need this signal to update timecode_source_dropdown
-               MtcOrLtcInputPortChanged (); //emit signal
-       }
 }
 
 void
@@ -3254,7 +3188,7 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                --how_many;
        }
 
-  failed:
+       failed:
        if (!new_routes.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -3341,11 +3275,13 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                --how_many;
        }
 
-  failure:
+       failure:
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
                        add_routes (ret, false, false, false, order);
+               } else if (flags == PresentationInfo::FoldbackBus) {
+                       add_routes (ret, false, false, true, order); // no autoconnect
                } else {
                        add_routes (ret, false, true, true, order); // autoconnect // outputs only
                }
@@ -3387,6 +3323,7 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                 */
 
                XMLNode node_copy (node);
+               std::vector<boost::shared_ptr<Playlist> > shared_playlists;
 
                try {
                        string name;
@@ -3400,7 +3337,7 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
 
                                if (!find_route_name (name_base.c_str(), ++number, name, (being_added > 1))) {
                                        fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
-                                       /*NOTREACHDE*/
+                                       /*NOTREACHED*/
                                }
 
                        } else {
@@ -3414,30 +3351,60 @@ 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 */
+                       /* figure out the appropriate playlist setup. The track
+                        * (if the Route we're creating is a track) will find
+                        * playlists via ID.
+                        */
 
                        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
+
+                               PBD::ID playlist_id;
+
+                               if (node_copy.get_property (X_("audio-playlist"), playlist_id)) {
+                                       boost::shared_ptr<Playlist> playlist = _playlists->by_id (playlist_id);
+                                       playlist = PlaylistFactory::create (playlist, string_compose ("%1.1", name));
+                                       playlist->reset_shares ();
+                                       node_copy.set_property (X_("audio-playlist"), playlist->id());
+                               }
+
+                               if (node_copy.get_property (X_("midi-playlist"), playlist_id)) {
+                                       boost::shared_ptr<Playlist> playlist = _playlists->by_id (playlist_id);
                                        playlist = PlaylistFactory::create (playlist, string_compose ("%1.1", name));
                                        playlist->reset_shares ();
+                                       node_copy.set_property (X_("midi-playlist"), playlist->id());
                                }
+
                        } 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());
+                               PBD::ID playlist_id;
+
+                               if (node_copy.get_property (X_("audio-playlist"), playlist_id)) {
+                                       boost::shared_ptr<Playlist> playlist = _playlists->by_id (playlist_id);
+                                       shared_playlists.push_back (playlist);
+                               }
+
+                               if (node_copy.get_property (X_("midi-playlist"), playlist_id)) {
+                                       boost::shared_ptr<Playlist> playlist = _playlists->by_id (playlist_id);
+                                       shared_playlists.push_back (playlist);
+                               }
+
+                       } else { /* NewPlaylist */
+
+                               PBD::ID pid;
+
+                               if (node_copy.get_property (X_("audio-playlist"), pid)) {
+                                       boost::shared_ptr<Playlist> playlist = PlaylistFactory::create (DataType::AUDIO, *this, name, false);
+                                       node_copy.set_property (X_("audio-playlist"), playlist->id());
+                               }
+
+                               if (node_copy.get_property (X_("midi-playlist"), pid)) {
+                                       boost::shared_ptr<Playlist> playlist = PlaylistFactory::create (DataType::MIDI, *this, name, false);
+                                       node_copy.set_property (X_("midi-playlist"), playlist->id());
                                }
                        }
 
-                       bool rename_playlist = (pd == CopyPlaylist || pd == NewPlaylist);
+                       /* Fix up new name in the XML node */
 
-                       Route::set_name_in_state (node_copy, name, rename_playlist);
+                       Route::set_name_in_state (node_copy, name);
 
                        /* trim bitslots from listen sends so that new ones are used */
                        XMLNodeList children = node_copy.children ();
@@ -3473,6 +3440,14 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                                (*i)->remove_property (X_("name"));
                                                (*i)->set_property ("bitslot", bitslot);
                                                (*i)->set_property ("name", name);
+                                               XMLNodeList io_kids = (*i)->children ();
+                                               for (XMLNodeList::iterator j = io_kids.begin(); j != io_kids.end(); ++j) {
+                                                       if ((*j)->name() != X_("IO")) {
+                                                               continue;
+                                                       }
+                                                       (*j)->remove_property (X_("name"));
+                                                       (*j)->set_property ("name", name);
+                                               }
                                        }
                                        else if (type && type->value() == X_("intreturn")) {
                                                (*i)->remove_property (X_("bitslot"));
@@ -3483,15 +3458,14 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                                (*i)->remove_property (X_("bitslot"));
                                        }
                                        else if (type && type->value() == X_("port")) {
-                                               // PortInsert::set_state() handles the bitslot
-                                               (*i)->remove_property (X_("bitslot"));
-                                               (*i)->set_property ("ignore-name", "1");
+                                               IOProcessor::prepare_for_reset (**i, name);
                                        }
                                }
                        }
 
                        /* new routes start off unsoloed to avoid issues related to
-                          upstream / downstream buses. */
+                          upstream / downstream buses.
+                       */
                        node_copy.remove_node_and_delete (X_("Controllable"), X_("name"), X_("solo"));
 
                        boost::shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
@@ -3501,6 +3475,12 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                goto out;
                        }
 
+                       /* Fix up sharing of playlists with the new Route/Track */
+
+                       for (vector<boost::shared_ptr<Playlist> >::iterator sp = shared_playlists.begin(); sp != shared_playlists.end(); ++sp) {
+                               (*sp)->share_with (route->id());
+                       }
+
                        if (boost::dynamic_pointer_cast<Track>(route)) {
                                /* force input/output change signals so that the new diskstream
                                   picks up the configuration of the route. During session
@@ -3529,10 +3509,15 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                        goto out;
                }
 
+               catch (...) {
+                       IO::enable_connecting ();
+                       throw;
+               }
+
                --how_many;
        }
 
-  out:
+       out:
        if (!ret.empty()) {
                StateProtector sp (this);
                if (Profile->get_trx()) {
@@ -3540,9 +3525,10 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                } else {
                        add_routes (ret, true, true, false, insert_at);
                }
-               IO::enable_connecting ();
        }
 
+       IO::enable_connecting ();
+
        return ret;
 }
 
@@ -3813,7 +3799,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                        }
 
                        /* speed up session deletion, don't do the solo dance */
-                       if (0 == (_state_of_the_state & Deletion)) {
+                       if (!deletion_in_progress ()) {
                                (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
                        }
 
@@ -3842,7 +3828,8 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                        (*iter)->output()->disconnect (0);
 
                        /* if the route had internal sends sending to it, remove them */
-                       if ((*iter)->internal_return()) {
+
+                       if (!deletion_in_progress () && (*iter)->internal_return()) {
 
                                boost::shared_ptr<RouteList> r = routes.reader ();
                                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
@@ -3892,7 +3879,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                resort_routes ();
 #endif
 
-       if (_process_graph && !(_state_of_the_state & Deletion)) {
+       if (_process_graph && !deletion_in_progress() && _engine.running()) {
                _process_graph->clear_other_chain ();
        }
 
@@ -4186,7 +4173,7 @@ Session::route_solo_changed (bool self_solo_changed, Controllable::GroupControlD
                        }
                        in_signal_flow = true;
                } else {
-                       DEBUG_TRACE (DEBUG::Solo, "\tno feed to\n");
+                       DEBUG_TRACE (DEBUG::Solo, string_compose("\tno feed to %1\n", (*i)->name()) );
                }
 
                if (!in_signal_flow) {
@@ -4324,24 +4311,45 @@ Session::cancel_all_mute ()
 }
 
 void
-Session::get_stripables (StripableList& sl) const
+Session::get_stripables (StripableList& sl, PresentationInfo::Flag fl) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
-       sl.insert (sl.end(), r->begin(), r->end());
+       for (RouteList::iterator it = r->begin(); it != r->end(); ++it) {
+               if ((*it)->presentation_info ().flags () & fl) {
+                       sl.push_back (*it);
+               }
+       }
 
-       VCAList v = _vca_manager->vcas ();
-       sl.insert (sl.end(), v.begin(), v.end());
+       if (fl & PresentationInfo::VCA) {
+               VCAList v = _vca_manager->vcas ();
+               sl.insert (sl.end(), v.begin(), v.end());
+       }
 }
 
 StripableList
 Session::get_stripables () const
 {
+       PresentationInfo::Flag fl = PresentationInfo::AllStripables;
        StripableList rv;
-       Session::get_stripables (rv);
+       Session::get_stripables (rv, fl);
        rv.sort (Stripable::Sorter ());
        return rv;
 }
 
+RouteList
+Session::get_routelist (bool mixer_order, PresentationInfo::Flag fl) const
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       RouteList rv;
+       for (RouteList::iterator it = r->begin(); it != r->end(); ++it) {
+               if ((*it)->presentation_info ().flags () & fl) {
+                       rv.push_back (*it);
+               }
+       }
+       rv.sort (Stripable::Sorter (mixer_order));
+       return rv;
+}
+
 boost::shared_ptr<RouteList>
 Session::get_routes_with_internal_returns() const
 {
@@ -4681,7 +4689,7 @@ Session::playlist_region_added (boost::weak_ptr<Region> w)
 void
 Session::maybe_update_session_range (samplepos_t a, samplepos_t b)
 {
-       if (_state_of_the_state & Loading) {
+       if (loading ()) {
                return;
        }
 
@@ -4693,20 +4701,20 @@ Session::maybe_update_session_range (samplepos_t a, samplepos_t b)
 
        } else {
 
-               if (a < _session_range_location->start()) {
+               if (_session_range_is_free && (a < _session_range_location->start())) {
                        _session_range_location->set_start (a);
                }
 
-               if (_session_range_end_is_free && (b > _session_range_location->end())) {
+               if (_session_range_is_free && (b > _session_range_location->end())) {
                        _session_range_location->set_end (b);
                }
        }
 }
 
 void
-Session::set_end_is_free (bool yn)
+Session::set_session_range_is_free (bool yn)
 {
-       _session_range_end_is_free = yn;
+       _session_range_is_free = yn;
 }
 
 void
@@ -4766,7 +4774,7 @@ Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
                tmp = r;
                ++tmp;
 
-               playlists->destroy_region (*r);
+               _playlists->destroy_region (*r);
                RegionFactory::map_remove (*r);
 
                (*r)->drop_sources ();
@@ -4787,6 +4795,7 @@ Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
 
                (*s)->mark_for_remove ();
                (*s)->drop_references ();
+               SourceRemoved(*s);
 
                s = srcs.erase (s);
        }
@@ -4821,6 +4830,25 @@ Session::remove_last_capture ()
        return 0;
 }
 
+void
+Session::get_last_capture_sources (std::list<boost::shared_ptr<Source> >& srcs)
+{
+       boost::shared_ptr<RouteList> rl = routes.reader ();
+       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+               if (!tr) {
+                       continue;
+               }
+
+               list<boost::shared_ptr<Source> >& l = tr->last_capture_sources();
+
+               if (!l.empty()) {
+                       srcs.insert (srcs.end(), l.begin(), l.end());
+                       l.clear ();
+               }
+       }
+}
+
 /* Source Management */
 
 void
@@ -4860,13 +4888,15 @@ Session::add_source (boost::shared_ptr<Source> source)
                }
 
                source->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr<Source> (source)));
+
+               SourceAdded(source);
        }
 }
 
 void
 Session::remove_source (boost::weak_ptr<Source> src)
 {
-       if (_state_of_the_state & Deletion) {
+       if (deletion_in_progress ()) {
                return;
        }
 
@@ -4882,14 +4912,15 @@ Session::remove_source (boost::weak_ptr<Source> src)
 
                if ((i = sources.find (source->id())) != sources.end()) {
                        sources.erase (i);
+                       SourceRemoved(source);
                }
        }
 
-       if (!(_state_of_the_state & StateOfTheState (InCleanup|Loading))) {
+       if (!in_cleanup () && !loading ()) {
 
                /* save state so we don't end up with a session file
-                  referring to non-existent sources.
-               */
+                * referring to non-existent sources.
+                */
 
                save_state (_current_snapshot_name);
        }
@@ -5372,6 +5403,17 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
                        DataType::MIDI, *this, path, false, sample_rate()));
 }
 
+bool
+Session::playlist_is_active (boost::shared_ptr<Playlist> playlist)
+{
+       Glib::Threads::Mutex::Lock lm (_playlists->lock);
+       for (SessionPlaylists::List::iterator i = _playlists->playlists.begin(); i != _playlists->playlists.end(); i++) {
+               if ( (*i) == playlist ) {
+                       return true;
+               }
+       }
+       return false;
+}
 
 void
 Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
@@ -5380,7 +5422,7 @@ Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
                return;
        }
 
-       playlists->add (playlist);
+       _playlists->add (playlist);
 
        if (unused) {
                playlist->release();
@@ -5392,7 +5434,7 @@ Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
 void
 Session::remove_playlist (boost::weak_ptr<Playlist> weak_playlist)
 {
-       if (_state_of_the_state & Deletion) {
+       if (deletion_in_progress ()) {
                return;
        }
 
@@ -5402,7 +5444,7 @@ Session::remove_playlist (boost::weak_ptr<Playlist> weak_playlist)
                return;
        }
 
-       playlists->remove (playlist);
+       _playlists->remove (playlist);
 
        set_dirty();
 }
@@ -5499,7 +5541,6 @@ Session::setup_lua ()
 #ifndef NDEBUG
        lua.Print.connect (&_lua_print);
 #endif
-       lua.tweak_rt_gc ();
        lua.sandbox (true);
        lua.do_command (
                        "function ArdourSession ()"
@@ -5630,9 +5671,11 @@ Session::setup_lua ()
                abort(); /*NOTREACHED*/
        }
 
+       lua_mlock (L, 1);
        LuaBindings::stddef (L);
        LuaBindings::common (L);
        LuaBindings::dsp (L);
+       lua_mlock (L, 0);
        luabridge::push <Session *> (L, this);
        lua_setglobal (L, "Session");
 }
@@ -5710,20 +5753,16 @@ Session::graph_reordered ()
           from a set_state() call or creating new tracks. Ditto for deletion.
        */
 
-       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _reconnecting_routes_in_progress || _route_deletion_in_progress) {
+       if (inital_connect_or_deletion_in_progress () || _adding_routes_in_progress || _reconnecting_routes_in_progress || _route_deletion_in_progress) {
                return;
        }
 
        resort_routes ();
 
        /* force all diskstreams to update their capture offset values to
-          reflect any changes in latencies within the graph.
-       */
-
-       boost::shared_ptr<RouteList> rl = routes.reader ();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               (*i)->update_signal_latency (true); // XXX
-       }
+        * reflect any changes in latencies within the graph.
+        */
+       update_route_latency (false, true);
 }
 
 /** @return Number of samples that there is disk space available to write,
@@ -5828,7 +5867,7 @@ Session::tempo_map_changed (const PropertyChange&)
 {
        clear_clicks ();
 
-       playlists->update_after_tempo_map_change ();
+       _playlists->update_after_tempo_map_change ();
 
        _locations->apply (*this, &Session::update_locations_after_tempo_map_change);
 
@@ -6007,7 +6046,9 @@ Session::unmark_aux_send_id (uint32_t id)
 void
 Session::unmark_return_id (uint32_t id)
 {
-       if (_state_of_the_state & Deletion) { return; }
+       if (deletion_in_progress ()) {
+               return;
+       }
        if (id < return_bitset.size()) {
                return_bitset[id] = false;
        }
@@ -6029,9 +6070,8 @@ Session::reset_native_file_format ()
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
                if (tr) {
-                       /* don't save state as we do this, there's no point
-                        */
-                       _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
+                       /* don't save state as we do this, there's no point */
+                       _state_of_the_state = StateOfTheState (_state_of_the_state | InCleanup);
                        tr->reset_write_sources (false);
                        _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
                }
@@ -6108,6 +6148,8 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
        string legal_playlist_name;
        string possible_path;
 
+       DataType data_type = track.data_type();
+
        if (end <= start) {
                error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
                                         end, start) << endmsg;
@@ -6117,7 +6159,11 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
        diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint,
                        include_endpoint, for_export, for_freeze);
 
-       if (diskstream_channels.n(track.data_type()) < 1) {
+       if (data_type == DataType::MIDI && endpoint && !for_export && !for_freeze && diskstream_channels.n(DataType::AUDIO) > 0) {
+               data_type = DataType::AUDIO;
+       }
+
+       if (diskstream_channels.n(data_type) < 1) {
                error << _("Cannot write a range with no data.") << endmsg;
                return result;
        }
@@ -6142,12 +6188,12 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                goto out;
        }
 
-       legal_playlist_name = legalize_for_path (playlist->name());
+       legal_playlist_name = "(bounce)" + legalize_for_path (playlist->name());
 
-       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(track.data_type()); ++chan_n) {
+       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n(data_type); ++chan_n) {
 
                string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
-               string path = ((track.data_type() == DataType::AUDIO)
+               string path = ((data_type == DataType::AUDIO)
                               ? new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true)
                               : new_midi_source_path (legal_playlist_name));
 
@@ -6156,7 +6202,7 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                }
 
                try {
-                       source = SourceFactory::createWritable (track.data_type(), *this, path, false, sample_rate());
+                       source = SourceFactory::createWritable (data_type, *this, path, false, sample_rate());
                }
 
                catch (failed_constructor& err) {
@@ -6231,8 +6277,10 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                                const MidiBuffer& buf = buffers.get_midi(0);
                                for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
                                        Evoral::Event<samplepos_t> ev = *i;
-                                       ev.set_time(ev.time() - position);
-                                       ms->append_event_samples(lock, ev, ms->timeline_position());
+                                       if (!endpoint || for_export) {
+                                               ev.set_time(ev.time() - position);
+                                       }
+                                       ms->append_event_samples(lock, ev, ms->natural_position());
                                }
                        }
                }
@@ -6286,14 +6334,15 @@ Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                PropertyList plist;
 
                plist.add (Properties::start, 0);
-               plist.add (Properties::length, srcs.front()->length(srcs.front()->timeline_position()));
+               plist.add (Properties::whole_file, true);
+               plist.add (Properties::length, srcs.front()->length(srcs.front()->natural_position()));
                plist.add (Properties::name, region_name_from_path (srcs.front()->name(), true));
 
-               result = RegionFactory::create (srcs, plist);
+               result = RegionFactory::create (srcs, plist, true);
 
        }
 
-  out:
+       out:
        if (!result) {
                for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
                        (*src)->mark_for_remove ();
@@ -6429,6 +6478,11 @@ Session::nstripables (bool with_monitor) const
        return rv;
 }
 
+bool
+Session::plot_process_graph (std::string const& file_name) const {
+       return _process_graph ? _process_graph->plot (file_name) : false;
+}
+
 void
 Session::add_automation_list(AutomationList *al)
 {
@@ -6445,7 +6499,7 @@ Session::have_rec_enabled_track () const
 bool
 Session::have_rec_disabled_track () const
 {
-    return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
+       return g_atomic_int_get (const_cast<gint*>(&_have_rec_disabled_track)) == 1;
 }
 
 /** Update the state of our rec-enabled tracks flag */
@@ -6492,6 +6546,10 @@ Session::update_route_record_state ()
 void
 Session::listen_position_changed ()
 {
+       if (loading ()) {
+               /* skip duing session restore (already taken care of) */
+               return;
+       }
        ProcessorChangeBlocker pcb (this);
        boost::shared_ptr<RouteList> r = routes.reader ();
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
@@ -6544,6 +6602,25 @@ Session::route_removed_from_route_group (RouteGroup* rg, boost::weak_ptr<Route>
        }
 }
 
+boost::shared_ptr<AudioTrack>
+Session::get_nth_audio_track (int nth) const
+{
+       boost::shared_ptr<RouteList> rl = routes.reader ();
+       rl->sort (Stripable::Sorter ());
+
+       for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
+               if (!boost::dynamic_pointer_cast<AudioTrack> (*r)) {
+                       continue;
+               }
+
+               if (--nth > 0) {
+                       continue;
+               }
+               return boost::dynamic_pointer_cast<AudioTrack> (*r);
+       }
+       return boost::shared_ptr<AudioTrack> ();
+}
+
 boost::shared_ptr<RouteList>
 Session::get_tracks () const
 {
@@ -6833,12 +6910,79 @@ Session::unknown_processors () const
 }
 
 void
-Session::update_latency (bool playback)
+Session::set_worst_io_latencies_x (IOChange, void *)
 {
+               set_worst_io_latencies ();
+}
 
+void
+Session::send_latency_compensation_change ()
+{
+       /* As a result of Send::set_output_latency()
+        * or InternalReturn::set_playback_offset ()
+        * the send's own latency can change (source track
+        * is aligned with target bus).
+        *
+        * This can only happen be triggered by
+        * Route::update_signal_latency ()
+        * when updating the processor latency.
+        *
+        * We need to walk the graph again to take those changes into account
+        * (we should probably recurse or process the graph in a 2 step process).
+        */
+       ++_send_latency_changes;
+}
+
+bool
+Session::update_route_latency (bool playback, bool apply_to_delayline)
+{
+       /* Note: RouteList is process-graph sorted */
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       if (playback) {
+               /* reverse the list so that we work backwards from the last route to run to the first,
+                * this is not needed, but can help to reduce the iterations for aux-sends.
+                */
+               RouteList* rl = routes.reader().get();
+               r.reset (new RouteList (*rl));
+               reverse (r->begin(), r->end());
+       }
+
+       bool changed = false;
+       int bailout = 0;
+restart:
+       _send_latency_changes = 0;
+       _worst_route_latency = 0;
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               // if (!(*i)->active()) { continue ; } // TODO
+               samplecnt_t l;
+               if ((*i)->signal_latency () != (l = (*i)->update_signal_latency (apply_to_delayline))) {
+                       changed = true;
+               }
+               _worst_route_latency = std::max (l, _worst_route_latency);
+       }
+
+       if (_send_latency_changes > 0) {
+               // only 1 extra iteration is needed (we allow only 1 level of aux-sends)
+               // BUT..  jack'n'sends'n'bugs
+               if (++bailout < 5) {
+                       cerr << "restarting Session::update_latency. # of send changes: " << _send_latency_changes << " iteration: " << bailout << endl;
+                       goto restart;
+               }
+       }
+
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_route_latency, (changed ? "yes" : "no")));
+
+       return changed;
+}
+
+void
+Session::update_latency (bool playback)
+{
        DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
 
-       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
+       if (inital_connect_or_deletion_in_progress () || _adding_routes_in_progress || _route_deletion_in_progress) {
                return;
        }
        if (!_engine.running()) {
@@ -6861,47 +7005,16 @@ Session::update_latency (bool playback)
        }
 
        if (playback) {
-               post_playback_latency ();
+               set_worst_output_latency ();
+               update_route_latency (true, true);
        } else {
-               post_capture_latency ();
+               set_worst_input_latency ();
+               update_route_latency (false, false);
        }
 
        DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n");
 }
 
-void
-Session::post_playback_latency ()
-{
-       set_worst_playback_latency ();
-
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       _worst_track_out_latency = 0; // XXX remove me
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               assert (!(*i)->is_auditioner()); // XXX remove me
-               _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
-       }
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->active()) { continue ; }
-               (*i)->apply_latency_compensation ();
-       }
-}
-
-void
-Session::post_capture_latency ()
-{
-       set_worst_capture_latency ();
-
-       /* reflect any changes in capture latencies into capture offsets */
-
-       boost::shared_ptr<RouteList> rl = routes.reader();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               (*i)->update_signal_latency ();
-       }
-}
-
 void
 Session::initialize_latencies ()
 {
@@ -6917,20 +7030,20 @@ Session::initialize_latencies ()
 void
 Session::set_worst_io_latencies ()
 {
-       set_worst_playback_latency ();
-       set_worst_capture_latency ();
+       set_worst_output_latency ();
+       set_worst_input_latency ();
 }
 
 void
-Session::set_worst_playback_latency ()
+Session::set_worst_output_latency ()
 {
-       if (_state_of_the_state & (InitialConnecting|Deletion)) {
+       if (inital_connect_or_deletion_in_progress ()) {
                return;
        }
 
        _worst_output_latency = 0;
 
-       if (!_engine.connected()) {
+       if (!_engine.running()) {
                return;
        }
 
@@ -6946,69 +7059,58 @@ Session::set_worst_playback_latency ()
 }
 
 void
-Session::set_worst_capture_latency ()
+Session::set_worst_input_latency ()
 {
-       if (_state_of_the_state & (InitialConnecting|Deletion)) {
+       if (inital_connect_or_deletion_in_progress ()) {
                return;
        }
 
        _worst_input_latency = 0;
 
-       if (!_engine.connected()) {
+       if (!_engine.running()) {
                return;
        }
 
        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) {
-                       continue;
-               }
                _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency());
        }
 
-        DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst input latency: %1\n", _worst_input_latency));
 }
 
 void
 Session::update_latency_compensation (bool force_whole_graph)
 {
-       // TODO: consolidate
-       bool some_track_latency_changed = false;
-
-       if (_state_of_the_state & (InitialConnecting|Deletion)) {
+       if (inital_connect_or_deletion_in_progress ()) {
                return;
        }
-
-       DEBUG_TRACE(DEBUG::Latency, "---------------------------- update latency compensation\n\n");
-
-       _worst_track_latency = 0;
-
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               assert (!(*i)->is_auditioner()); // XXX remove me
-               if ((*i)->active()) {
-                       samplecnt_t tl;
-                       if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency () /* - (*i)->output()->user_latency()*/)) {
-                               some_track_latency_changed = true;
-                       }
-                       _worst_track_latency = max (tl, _worst_track_latency);
-               }
+       /* this lock is not usually contended, but under certain conditions,
+        * update_latency_compensation may be called concurrently.
+        * e.g. drag/drop copy a latent plugin while rolling.
+        * GUI thread (via route_processors_changed) and
+        * auto_connect_thread_run may race.
+        */
+       Glib::Threads::Mutex::Lock lx (_update_latency_lock, Glib::Threads::TRY_LOCK);
+       if (!lx.locked()) {
+               /* no need to do this twice */
+               return;
        }
 
-       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency,
-                                                    (some_track_latency_changed ? "yes" : "no")));
-
-       DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n");
+       bool some_track_latency_changed = update_route_latency (false, false);
 
        if (some_track_latency_changed || force_whole_graph)  {
                _engine.update_latencies ();
-       }
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               (*i)->update_signal_latency (true);
+               /* above call will ask the backend up update its latencies, which
+                * eventually will trigger  AudioEngine::latency_callback () and
+                * call Session::update_latency ()
+                */
+       } else {
+               boost::shared_ptr<RouteList> r = routes.reader ();
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       (*i)->apply_latency_compensation ();
+               }
        }
 }
 
@@ -7050,39 +7152,12 @@ Session::operation_in_progress (GQuark op) const
        return (find (_current_trans_quarks.begin(), _current_trans_quarks.end(), op) != _current_trans_quarks.end());
 }
 
-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 ? _ltc_output->nth (0) : boost::shared_ptr<Port> ();
 }
 
-void
-Session::reconnect_ltc_input ()
-{
-       if (_ltc_input) {
-
-               string src = Config->get_ltc_source_port();
-
-               _ltc_input->disconnect (this);
-
-               if (src != _("None") && !src.empty())  {
-                       _ltc_input->nth (0)->connect (src);
-               }
-
-               if ( ARDOUR::Profile->get_trx () ) {
-                       // Tracks need this signal to update timecode_source_dropdown
-                       MtcOrLtcInputPortChanged (); //emit signal
-               }
-       }
-}
-
 void
 Session::reconnect_ltc_output ()
 {
@@ -7346,6 +7421,7 @@ Session::auto_connect_thread_run ()
                         * modifies the capture-offset, which can be a problem.
                         */
                        while (g_atomic_int_and (&_latency_recompute_pending, 0)) {
+                               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                update_latency_compensation ();
                        }
                }
@@ -7372,3 +7448,16 @@ Session::cancel_all_solo ()
        set_controls (stripable_list_to_control_list (sl, &Stripable::solo_control), 0.0, Controllable::NoGroup);
        clear_all_solo_state (routes.reader());
 }
+
+void
+Session::maybe_update_tempo_from_midiclock_tempo (float bpm)
+{
+       if (_tempo_map->n_tempos() == 1) {
+               TempoSection& ts (_tempo_map->tempo_section_at_sample (0));
+               if (fabs (ts.note_types_per_minute() - bpm) > (0.01 * ts.note_types_per_minute())) {
+                       const Tempo tempo (bpm, 4.0, bpm);
+                       std::cerr << "new tempo " << bpm << " old " << ts.note_types_per_minute() << std::endl;
+                       _tempo_map->replace_tempo (ts, tempo, 0.0, 0.0, AudioTime);
+               }
+       }
+}