Tweak Lua GC
[ardour.git] / libs / ardour / session.cc
index 3cf24ba581ca98083899c8eb007394be6b262983..039288952bc0c6f3984fa2d1d4e583518128cdf4 100644 (file)
@@ -51,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"
@@ -66,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"
 #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"
@@ -137,13 +140,13 @@ guint Session::_name_id_counter = 0;
 PBD::Signal1<int,uint32_t> Session::AudioEngineSetupRequired;
 PBD::Signal1<void,std::string> Session::Dialog;
 PBD::Signal0<int> Session::AskAboutPendingState;
-PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
-PBD::Signal2<void, framecnt_t, framecnt_t> Session::NotifyAboutSampleRateMismatch;
+PBD::Signal2<int, samplecnt_t, samplecnt_t> Session::AskAboutSampleRateMismatch;
+PBD::Signal2<void, samplecnt_t, samplecnt_t> Session::NotifyAboutSampleRateMismatch;
 PBD::Signal0<void> Session::SendFeedback;
 PBD::Signal3<int,Session*,std::string,DataType> Session::MissingFile;
 
-PBD::Signal1<void, framepos_t> Session::StartTimeChanged;
-PBD::Signal1<void, framepos_t> Session::EndTimeChanged;
+PBD::Signal1<void, samplepos_t> Session::StartTimeChanged;
+PBD::Signal1<void, samplepos_t> Session::EndTimeChanged;
 PBD::Signal2<void,std::string, std::string> Session::Exported;
 PBD::Signal1<int,boost::shared_ptr<Playlist> > Session::AskAboutPlaylistDeletion;
 PBD::Signal0<void> Session::Quit;
@@ -151,7 +154,7 @@ PBD::Signal0<void> Session::FeedbackDetected;
 PBD::Signal0<void> Session::SuccessfulGraphSort;
 PBD::Signal2<void,std::string,std::string> Session::VersionMismatch;
 
-const framecnt_t Session::bounce_chunk_size = 8192;
+const samplecnt_t Session::bounce_chunk_size = 8192;
 static void clean_up_session_event (SessionEvent* ev) { delete ev; }
 const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
 
@@ -173,28 +176,32 @@ Session::Session (AudioEngine &eng,
        , process_function (&Session::process_with_events)
        , _bounce_processing_active (false)
        , waiting_for_sync_offset (false)
-       , _base_frame_rate (0)
-       , _nominal_frame_rate (0)
-       , _current_frame_rate (0)
+       , _base_sample_rate (0)
+       , _nominal_sample_rate (0)
+       , _current_sample_rate (0)
        , transport_sub_state (0)
        , _record_status (Disabled)
-       , _transport_frame (0)
+       , _transport_sample (0)
+       , _seek_counter (0)
        , _session_range_location (0)
        , _session_range_end_is_free (true)
        , _slave (0)
        , _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_frame (0)
-       , maximum_output_latency (0)
-       , _requested_return_frame (-1)
+       , _last_slave_transport_sample (0)
+       , _requested_return_sample (-1)
        , current_block_size (0)
        , _worst_output_latency (0)
        , _worst_input_latency (0)
-       , _worst_track_latency (0)
+       , _worst_route_latency (0)
+       , _send_latency_changes (0)
        , _have_captured (false)
        , _non_soloed_outs_muted (false)
        , _listening (false)
@@ -218,7 +225,6 @@ Session::Session (AudioEngine &eng,
        , _realtime_export (false)
        , _region_export (false)
        , _export_preroll (0)
-       , _export_latency (0)
        , _pre_export_mmc_enabled (false)
        , _name (snapshot_name)
        , _is_new (true)
@@ -238,7 +244,7 @@ Session::Session (AudioEngine &eng,
        , _last_roll_or_reversal_location (0)
        , _last_record_location (0)
        , pending_locate_roll (false)
-       , pending_locate_frame (0)
+       , pending_locate_sample (0)
        , pending_locate_flush (false)
        , pending_abort (false)
        , pending_auto_loop (false)
@@ -307,7 +313,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)
@@ -326,6 +331,7 @@ Session::Session (AudioEngine &eng,
        , _mmc (0)
        , _vca_manager (new VCAManager (*this))
        , _selection (new CoreSelection (*this))
+       , _global_locate_pending (false)
 {
        uint32_t sr = 0;
 
@@ -359,7 +365,7 @@ Session::Session (AudioEngine &eng,
                // set samplerate for plugins added early
                // e.g from templates or MB channelstrip
                set_block_size (_engine.samples_per_cycle());
-               set_frame_rate (_engine.sample_rate());
+               set_sample_rate (_engine.sample_rate());
 
                if (create (mix_template, bus_profile)) {
                        destroy ();
@@ -378,8 +384,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);
                }
@@ -412,9 +422,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);
@@ -435,6 +463,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 ();
 
@@ -490,8 +520,16 @@ Session::Session (AudioEngine &eng,
 
        ensure_subdirs (); // archived or zipped sessions may lack peaks/ analysis/ etc
 
-       _is_new = false;
+       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"));
 }
@@ -563,6 +601,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.
@@ -579,11 +619,10 @@ Session::immediately_post_engine ()
        }
 
        if (config.get_jack_time_master()) {
-               _engine.transport_locate (_transport_frame);
+               _engine.transport_locate (_transport_sample);
        }
 
        try {
-               LocaleGuard lg;
                BootMessage (_("Set up LTC"));
                setup_ltc ();
                BootMessage (_("Set up Click"));
@@ -618,14 +657,26 @@ Session::destroy ()
 
        _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 */
 
        Port::PortSignalDrop (); /* EMIT SIGNAL */
        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 ();
 
@@ -640,14 +691,18 @@ 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 */
 
-       ltc_tx_cleanup();
-
        /* clear history so that no references to objects are held any more */
 
        _history.clear ();
@@ -657,17 +712,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;
@@ -694,7 +752,13 @@ Session::destroy ()
        clear_clicks ();
 
        /* need to remove auditioner before monitoring section
-        * otherwise it is re-connected */
+        * otherwise it is re-connected.
+        * Note: If a session was never successfully loaded, there
+        * may not yet be an auditioner.
+        */
+       if (auditioner) {
+               auditioner->drop_references ();
+       }
        auditioner.reset ();
 
        /* drop references to routes held by the monitoring section
@@ -706,7 +770,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 ();
@@ -790,7 +854,6 @@ Session::destroy ()
                        case SessionEvent::Skip:
                        case SessionEvent::PunchIn:
                        case SessionEvent::PunchOut:
-                       case SessionEvent::RecordStart:
                        case SessionEvent::StopOnce:
                        case SessionEvent::RangeStop:
                        case SessionEvent::RangeLocate:
@@ -819,6 +882,10 @@ Session::destroy ()
                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 ();
@@ -1451,6 +1518,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 ()
 {
@@ -1471,7 +1565,6 @@ Session::hookup_io ()
                        if (a->init()) {
                                throw failed_constructor ();
                        }
-                       a->use_new_diskstream ();
                        auditioner = a;
                }
 
@@ -1566,7 +1659,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);
        }
@@ -1575,27 +1668,22 @@ Session::auto_punch_start_changed (Location* location)
 void
 Session::auto_punch_end_changed (Location* location)
 {
-       framepos_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)
 {
-       framepos_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.
- *  @param pos Filled in with the start time of the required fade-out (in session frames).
+ *  @param pos Filled in with the start time of the required fade-out (in session samples).
  *  @param length Filled in with the length of the required fade-out.
  */
 void
-Session::auto_loop_declick_range (Location* loc, framepos_t & pos, framepos_t & length)
+Session::auto_loop_declick_range (Location* loc, samplepos_t & pos, samplepos_t & length)
 {
        pos = max (loc->start(), loc->end() - 64);
        length = loc->end() - pos;
@@ -1605,17 +1693,17 @@ void
 Session::auto_loop_changed (Location* location)
 {
        replace_event (SessionEvent::AutoLoop, location->end(), location->start());
-       framepos_t dcp;
-       framecnt_t dcl;
+       samplepos_t dcp;
+       samplecnt_t dcl;
        auto_loop_declick_range (location, dcp, dcl);
 
        if (transport_rolling() && play_loop) {
 
                replace_event (SessionEvent::AutoLoopDeclick, dcp, dcl);
 
-               // if (_transport_frame > location->end()) {
+               // if (_transport_sample > location->end()) {
 
-               if (_transport_frame < location->start() || _transport_frame > location->end()) {
+               if (_transport_sample < location->start() || _transport_sample > location->end()) {
                        // relocate to beginning of loop
                        clear_events (SessionEvent::LocateRoll);
 
@@ -1624,7 +1712,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;
 
@@ -1644,7 +1732,7 @@ Session::auto_loop_changed (Location* location)
           to the loop start on stop if that is appropriate.
         */
 
-       framepos_t pos;
+       samplepos_t pos;
 
        if (!transport_rolling() && select_playhead_priority_target (pos)) {
                if (pos == location->start()) {
@@ -1665,7 +1753,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);
        }
@@ -1695,7 +1783,7 @@ Session::set_auto_punch_location (Location* location)
 }
 
 void
-Session::set_session_extents (framepos_t start, framepos_t end)
+Session::set_session_extents (samplepos_t start, samplepos_t end)
 {
        Location* existing;
        if ((existing = _locations->session_range_location()) == 0) {
@@ -1722,8 +1810,8 @@ Session::set_auto_loop_location (Location* location)
                loop_connections.drop_connections ();
                existing->set_auto_loop (false, this);
                remove_event (existing->end(), SessionEvent::AutoLoop);
-               framepos_t dcp;
-               framecnt_t dcl;
+               samplepos_t dcp;
+               samplecnt_t dcl;
                auto_loop_declick_range (existing, dcp, dcl);
                remove_event (dcp, SessionEvent::AutoLoopDeclick);
                auto_loop_location_changed (0);
@@ -1756,7 +1844,7 @@ Session::set_auto_loop_location (Location* location)
                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 && !tr->hidden()) {
+                       if (tr && !tr->is_private_route()) {
                                tr->set_loop (location);
                        }
                }
@@ -1984,7 +2072,7 @@ Session::enable_record ()
 
                if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) {
 
-                       _last_record_location = _transport_frame;
+                       _last_record_location = _transport_sample;
                        send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
 
                        if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
@@ -2029,7 +2117,6 @@ Session::disable_record (bool rt_context, bool force)
                if (!rt_context) {
                        remove_pending_capture_state ();
                }
-               unset_preroll_record_punch ();
        }
 }
 
@@ -2067,7 +2154,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 {
@@ -2078,107 +2165,85 @@ Session::maybe_enable_record (bool rt_context)
        set_dirty();
 }
 
-framepos_t
-Session::audible_frame (bool* latent_locate) const
+samplepos_t
+Session::audible_sample (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;
        }
 
+       samplepos_t ret;
+
        if (synced_to_engine()) {
                /* Note: this is basically just sync-to-JACK */
-               ret = _engine.transport_frame();
+               ret = _engine.transport_sample();
        } else {
-               ret = _transport_frame;
+               ret = _transport_sample;
        }
 
-       if (transport_rolling()) {
-               ret -= offset;
+       assert (ret >= 0);
 
-               /* Check to see if we have passed the first guaranteed
-                * audible frame past our last start position. if not,
-                * return that last start point because in terms
-                * of audible frames, we have not moved yet.
-                *
-                * `Start position' in this context means the time we last
-                * either started, located, or changed transport direction.
-                */
-
-               if (_transport_speed > 0.0f) {
+       if (!transport_rolling()) {
+               return ret;
+       }
 
-                       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 {
-                               /* 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;
-                                       }
+#if 0 // TODO looping
+       if (_transport_speed > 0.0f) {
+               if (play_loop && have_looped) {
+                       /* 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();
+                       sampleoffset_t lo = location->start() - ret;
+                       if (lo > 0) {
+                               ret = location->end () - lo;
+                               if (latent_locate) {
+                                       *latent_locate = true;
                                }
                        }
-
-               } else if (_transport_speed < 0.0f) {
-
-                       /* XXX wot? no backward looping? */
-
-                       if (ret > _last_roll_or_reversal_location) {
-                               return _last_roll_or_reversal_location;
-                       }
                }
+       } else if (_transport_speed < 0.0f) {
+               /* XXX wot? no backward looping? */
        }
+#endif
 
-       return std::max ((framepos_t)0, ret);
+       return std::max ((samplepos_t)0, ret);
 }
 
-
-framecnt_t
-Session::preroll_samples (framepos_t pos) const
+samplecnt_t
+Session::preroll_samples (samplepos_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;
+               const Tempo& tempo = _tempo_map->tempo_at_sample (pos);
+               const Meter& meter = _tempo_map->meter_at_sample (pos);
+               return meter.samples_per_bar (tempo, sample_rate()) * -pr;
        }
        if (pr < 0) {
                return 0;
        }
-       return pr * frame_rate();
+       return pr * sample_rate();
 }
 
 void
-Session::set_frame_rate (framecnt_t frames_per_second)
+Session::set_sample_rate (samplecnt_t frames_per_second)
 {
-       /** \fn void Session::set_frame_size(framecnt_t)
+       /** \fn void Session::set_sample_size(samplecnt_t)
                the AudioEngine object that calls this guarantees
                that it will not be called while we are also in
                ::process(). Its fine to do things that block
                here.
        */
 
-       if (_base_frame_rate == 0) {
-               _base_frame_rate = frames_per_second;
+       if (_base_sample_rate == 0) {
+               _base_sample_rate = frames_per_second;
        }
-       else if (_base_frame_rate != frames_per_second && frames_per_second != _nominal_frame_rate) {
-               NotifyAboutSampleRateMismatch (_base_frame_rate, frames_per_second);
+       else if (_base_sample_rate != frames_per_second && frames_per_second != _nominal_sample_rate) {
+               NotifyAboutSampleRateMismatch (_base_sample_rate, frames_per_second);
        }
-       _nominal_frame_rate = frames_per_second;
+       _nominal_sample_rate = frames_per_second;
 
        sync_time_vars();
 
@@ -2482,11 +2547,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();
        }
 }
 
@@ -2548,8 +2615,6 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s
                                track->set_strict_io (true);
                        }
 
-                       track->use_new_diskstream();
-
                        BOOST_MARK_TRACK (track);
 
                        {
@@ -2565,14 +2630,10 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s
                                }
                        }
 
-                       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);
                }
@@ -3037,9 +3098,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;
@@ -3054,7 +3142,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;
                }
 
@@ -3115,8 +3207,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                                }
                        }
 
-                       track->use_new_diskstream();
-
                        BOOST_MARK_TRACK (track);
 
                        {
@@ -3143,10 +3233,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);
                }
@@ -3326,7 +3412,7 @@ 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 */
-
+#warning fixme -- no more Diskstream
                        if (pd == CopyPlaylist) {
                                XMLNode* ds_node = find_named_node (node_copy, "Diskstream");
                                if (ds_node) {
@@ -3401,6 +3487,10 @@ Session::new_route_from_template (uint32_t how_many, PresentationInfo::order_t i
                                }
                        }
 
+                       /* 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) {
@@ -3466,8 +3556,8 @@ Session::add_routes (RouteList& new_routes, bool input_auto_connect, bool output
 
        graph_reordered ();
 
-       update_latency (true);
        update_latency (false);
+       update_latency (true);
 
        set_dirty();
 
@@ -3493,8 +3583,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
@@ -3507,21 +3597,15 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                }
        }
 
-       /* auditioner and monitor routes are not part of the order */
-       if (auditioner) {
-               assert (n_routes > 0);
-               --n_routes;
-       }
+       /* monitor is not part of the order */
        if (_monitor_out) {
                assert (n_routes > 0);
                --n_routes;
        }
 
-       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());
-
        {
                PresentationInfo::ChangeSuspender cs;
+               ensure_route_presentation_info_gap (order, new_routes.size());
 
                for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x, ++added) {
 
@@ -3558,25 +3642,13 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                                }
                        }
 
-                       if (!r->presentation_info().special()) {
+                       if (!r->presentation_info().special (false)) {
 
                                DEBUG_TRACE (DEBUG::OrderKeys, string_compose ("checking PI state for %1\n", r->name()));
 
                                /* presentation info order may already have been set from XML */
 
                                if (!r->presentation_info().order_set()) {
-                                       /* this is only useful for headless sessions,
-                                        * Editor::add_routes() and Mixer_UI::add_routes() will
-                                        * override it following the RouteAdded signal.
-                                        *
-                                        * Also routes should be sorted before VCAs (like the GUI does).
-                                        * Session::ensure_route_presentation_info_gap() does not special case VCAs either.
-                                        *
-                                        * ... but not to worry, the GUI's
-                                        * gtk2_ardour/route_sorter.h and various ::sync_presentation_info_from_treeview()
-                                        * handle this :)
-                                        */
-
                                        if (order == PresentationInfo::max_order) {
                                                /* just add to the end */
                                                r->set_presentation_order (n_routes + added);
@@ -3610,6 +3682,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
 
                        ARDOUR::GUIIdle ();
                }
+               ensure_stripable_sort_order ();
        }
 
        if (_monitor_out && IO::connecting_legal) {
@@ -3719,6 +3792,7 @@ 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);
@@ -3727,6 +3801,10 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 
                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;
                        }
@@ -3820,23 +3898,34 @@ 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
         */
 
        for (RouteList::iterator iter = routes_to_remove->begin(); iter != routes_to_remove->end(); ++iter) {
-               cerr << "Drop references to " << (*iter)->name() << endl;
                (*iter)->drop_references ();
        }
 
-       if (_state_of_the_state & Deletion) {
+       if (deletion_in_progress()) {
                return;
        }
 
-       PropertyChange so;
-       so.add (Properties::selected);
-       so.add (Properties::order);
-       PresentationInfo::Change (PropertyChange (so));
+       PropertyChange pc;
+       pc.add (Properties::order);
+       PresentationInfo::Change (pc);
 
        /* save the new state of the world */
 
@@ -4186,7 +4275,8 @@ Session::muted () const
        StripableList all;
        get_stripables (all);
        for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
-               if ((*i)->is_auditioner() || (*i)->is_monitor()) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->is_monitor()) {
                        continue;
                }
                boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
@@ -4210,7 +4300,8 @@ Session::cancel_all_mute ()
        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) {
-               if ((*i)->is_auditioner() || (*i)->is_monitor()) {
+               assert (!(*i)->is_auditioner());
+               if ((*i)->is_monitor()) {
                        continue;
                }
                boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*i);
@@ -4239,6 +4330,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
 {
@@ -4261,8 +4361,8 @@ Session::io_name_is_legal (const std::string& name) const
        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 */
-                               return true;
+                               /* 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;
@@ -4421,21 +4521,6 @@ Session::processor_by_id (PBD::ID id) const
        return boost::shared_ptr<Processor> ();
 }
 
-boost::shared_ptr<Track>
-Session::track_by_diskstream_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;
-               }
-       }
-
-       return boost::shared_ptr<Track> ();
-}
-
 boost::shared_ptr<Route>
 Session::get_remote_nth_route (PresentationInfo::order_t n) const
 {
@@ -4449,7 +4534,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) {
 
@@ -4481,26 +4566,11 @@ Session::get_remote_nth_stripable (PresentationInfo::order_t n, PresentationInfo
        return boost::shared_ptr<Stripable>();
 }
 
-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();
-               }
-       }
-};
-
 boost::shared_ptr<Route>
 Session::route_by_selected_count (uint32_t id) const
 {
        RouteList r (*(routes.reader ()));
-       PresentationOrderSorter sorter;
-       r.sort (sorter);
+       r.sort (Stripable::Sorter());
 
        RouteList::iterator i;
 
@@ -4522,16 +4592,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);
                }
        }
@@ -4598,7 +4668,7 @@ Session::playlist_region_added (boost::weak_ptr<Region> w)
 
        /* If so, update the session range markers */
        if (!in.empty ()) {
-               maybe_update_session_range (r->position (), r->last_frame ());
+               maybe_update_session_range (r->position (), r->last_sample ());
        }
 }
 
@@ -4606,13 +4676,13 @@ Session::playlist_region_added (boost::weak_ptr<Region> w)
  *  b is after the current end.
  */
 void
-Session::maybe_update_session_range (framepos_t a, framepos_t b)
+Session::maybe_update_session_range (samplepos_t a, samplepos_t b)
 {
        if (_state_of_the_state & Loading) {
                return;
        }
 
-       framepos_t session_end_marker_shift_samples = session_end_shift * _nominal_frame_rate;
+       samplepos_t session_end_marker_shift_samples = session_end_shift * _nominal_sample_rate;
 
        if (_session_range_location == 0) {
 
@@ -4637,17 +4707,17 @@ Session::set_end_is_free (bool yn)
 }
 
 void
-Session::playlist_ranges_moved (list<Evoral::RangeMove<framepos_t> > const & ranges)
+Session::playlist_ranges_moved (list<Evoral::RangeMove<samplepos_t> > const & ranges)
 {
-       for (list<Evoral::RangeMove<framepos_t> >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) {
+       for (list<Evoral::RangeMove<samplepos_t> >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) {
                maybe_update_session_range (i->to, i->to + i->length);
        }
 }
 
 void
-Session::playlist_regions_extended (list<Evoral::Range<framepos_t> > const & ranges)
+Session::playlist_regions_extended (list<Evoral::Range<samplepos_t> > const & ranges)
 {
-       for (list<Evoral::Range<framepos_t> >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) {
+       for (list<Evoral::Range<samplepos_t> >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) {
                maybe_update_session_range (i->from, i->to);
        }
 }
@@ -4858,13 +4928,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
@@ -5165,17 +5238,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);
@@ -5190,38 +5258,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
@@ -5240,7 +5306,7 @@ Session::create_audio_source_for_session (size_t n_chans, string const & base, u
 
        if (!path.empty()) {
                return boost::dynamic_pointer_cast<AudioFileSource> (
-                       SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate(), true, true));
+                       SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, sample_rate(), true, true));
        } else {
                throw failed_constructor ();
        }
@@ -5255,7 +5321,7 @@ Session::create_midi_source_for_session (string const & basic_name)
        if (!path.empty()) {
                return boost::dynamic_pointer_cast<SMFSource> (
                        SourceFactory::createWritable (
-                               DataType::MIDI, *this, path, false, frame_rate()));
+                               DataType::MIDI, *this, path, false, sample_rate()));
        } else {
                throw failed_constructor ();
        }
@@ -5300,7 +5366,7 @@ Session::create_midi_source_by_stealing_name (boost::shared_ptr<Track> track)
 
        return boost::dynamic_pointer_cast<SMFSource> (
                SourceFactory::createWritable (
-                       DataType::MIDI, *this, path, false, frame_rate()));
+                       DataType::MIDI, *this, path, false, sample_rate()));
 }
 
 
@@ -5403,7 +5469,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;
 }
 
@@ -5419,8 +5485,8 @@ Session::try_run_lua (pframes_t nframes)
        if (_n_lua_scripts == 0) return;
        Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
        if (tm.locked ()) {
-               try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { }
-               lua.collect_garbage_step ();
+               try { (*_lua_run)(nframes); } catch (...) { }
+               lua.collect_garbage_step (100 /*kB*/);
        }
 }
 
@@ -5431,6 +5497,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 = {} }"
@@ -5447,7 +5514,6 @@ 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, 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
@@ -5551,7 +5617,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*/
        }
@@ -5577,6 +5648,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;
@@ -5613,18 +5689,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
 {
@@ -5647,37 +5711,24 @@ 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
-          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) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr) {
-                       tr->set_capture_offset ();
-               }
-       }
+        * reflect any changes in latencies within the graph.
+        */
+       update_route_latency (false, true);
 }
 
-/** @return Number of frames that there is disk space available to write,
+/** @return Number of samples that there is disk space available to write,
  *  if known.
  */
-boost::optional<framecnt_t>
+boost::optional<samplecnt_t>
 Session::available_capture_duration ()
 {
        Glib::Threads::Mutex::Lock lm (space_lock);
 
        if (_total_free_4k_blocks_uncertain) {
-               return boost::optional<framecnt_t> ();
+               return boost::optional<samplecnt_t> ();
        }
 
        float sample_bytes_on_disk = 4.0; // keep gcc happy
@@ -5705,11 +5756,11 @@ Session::available_capture_duration ()
 
        double scale = 4096.0 / sample_bytes_on_disk;
 
-       if (_total_free_4k_blocks * scale > (double) max_framecnt) {
-               return max_framecnt;
+       if (_total_free_4k_blocks * scale > (double) max_samplecnt) {
+               return max_samplecnt;
        }
 
-       return (framecnt_t) floor (_total_free_4k_blocks * scale);
+       return (samplecnt_t) floor (_total_free_4k_blocks * scale);
 }
 
 void
@@ -5781,7 +5832,7 @@ 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_beat ();
+               (*i)->recompute_samples_from_beat ();
        }
 }
 
@@ -6029,7 +6080,7 @@ Session::freeze_all (InterThreadInfo& itt)
 }
 
 boost::shared_ptr<Region>
-Session::write_one_track (Track& track, framepos_t start, framepos_t end,
+Session::write_one_track (Track& track, samplepos_t start, samplepos_t end,
                          bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
                          InterThreadInfo& itt,
                          boost::shared_ptr<Processor> endpoint, bool include_endpoint,
@@ -6039,12 +6090,12 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
        boost::shared_ptr<Playlist> playlist;
        boost::shared_ptr<Source> source;
        ChanCount diskstream_channels (track.n_channels());
-       framepos_t position;
-       framecnt_t this_chunk;
-       framepos_t to_do;
-       framepos_t latency_skip;
+       samplepos_t position;
+       samplecnt_t this_chunk;
+       samplepos_t to_do;
+       samplepos_t latency_skip;
        BufferSet buffers;
-       framepos_t len = end - start;
+       samplepos_t len = end - start;
        bool need_block_size_reset = false;
        ChanCount const max_proc = track.max_processor_streams ();
        string legal_playlist_name;
@@ -6098,7 +6149,7 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
                }
 
                try {
-                       source = SourceFactory::createWritable (track.data_type(), *this, path, false, frame_rate());
+                       source = SourceFactory::createWritable (track.data_type(), *this, path, false, sample_rate());
                }
 
                catch (failed_constructor& err) {
@@ -6156,7 +6207,7 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
                        continue;
                }
 
-               const framecnt_t current_chunk = this_chunk - latency_skip;
+               const samplecnt_t current_chunk = this_chunk - latency_skip;
 
                uint32_t n = 0;
                for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
@@ -6172,9 +6223,9 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
 
                                const MidiBuffer& buf = buffers.get_midi(0);
                                for (MidiBuffer::const_iterator i = buf.begin(); i != buf.end(); ++i) {
-                                       Evoral::Event<framepos_t> ev = *i;
+                                       Evoral::Event<samplepos_t> ev = *i;
                                        ev.set_time(ev.time() - position);
-                                       ms->append_event_frames(lock, ev, ms->timeline_position());
+                                       ms->append_event_samples(lock, ev, ms->timeline_position());
                                }
                        }
                }
@@ -6281,6 +6332,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
 {
@@ -6348,6 +6405,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)
 {
@@ -6471,16 +6545,15 @@ 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;
 }
 
 boost::shared_ptr<RouteList>
-Session::get_routes_with_regions_at (framepos_t const p) const
+Session::get_routes_with_regions_at (samplepos_t const p) const
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
        boost::shared_ptr<RouteList> rl (new RouteList);
@@ -6524,20 +6597,20 @@ Session::goto_start (bool and_roll)
        }
 }
 
-framepos_t
-Session::current_start_frame () const
+samplepos_t
+Session::current_start_sample () const
 {
        return _session_range_location ? _session_range_location->start() : 0;
 }
 
-framepos_t
-Session::current_end_frame () const
+samplepos_t
+Session::current_end_sample () const
 {
        return _session_range_location ? _session_range_location->end() : 0;
 }
 
 void
-Session::set_session_range_location (framepos_t start, framepos_t end)
+Session::set_session_range_location (samplepos_t start, samplepos_t end)
 {
        _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange, 0);
        _locations->add (_session_range_location);
@@ -6570,7 +6643,7 @@ Session::step_edit_status_change (bool yn)
 
 
 void
-Session::start_time_changed (framepos_t old)
+Session::start_time_changed (samplepos_t old)
 {
        /* Update the auto loop range to match the session range
           (unless the auto loop range has been changed by the user)
@@ -6590,7 +6663,7 @@ Session::start_time_changed (framepos_t old)
 }
 
 void
-Session::end_time_changed (framepos_t old)
+Session::end_time_changed (samplepos_t old)
 {
        /* Update the auto loop range to match the session range
           (unless the auto loop range has been changed by the user)
@@ -6753,115 +6826,132 @@ Session::unknown_processors () const
 }
 
 void
-Session::update_latency (bool playback)
+Session::set_worst_io_latencies_x (IOChange, void *)
 {
+               set_worst_io_latencies ();
+}
 
-       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) {
-               return;
-       }
+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 ();
-       framecnt_t max_latency = 0;
 
        if (playback) {
-               /* reverse the list so that we work backwards from the last route to run to the first */
-                RouteList* rl = routes.reader().get();
-                r.reset (new RouteList (*rl));
+               /* 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());
        }
 
-       /* compute actual latency values for the given direction and store them all in per-port
-          structures. this will also publish the same values (to JACK) so that computation of latency
-          for routes can consistently use public latency values.
-       */
+       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) {
-               max_latency = max (max_latency, (*i)->set_private_port_latencies (playback));
+               // 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);
        }
 
-        /* because we latency compensate playback, our published playback latencies should
-           be the same for all output ports - all material played back by ardour has
-           the same latency, whether its caused by plugins or by latency compensation. since
-           these may differ from the values computed above, reset all playback port latencies
-           to the same value.
-        */
-
-        DEBUG_TRACE (DEBUG::Latency, string_compose ("Set public port latencies to %1\n", max_latency));
-
-        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                (*i)->set_public_port_latencies (max_latency, playback);
-        }
-
-       if (playback) {
-
-               post_playback_latency ();
-
-       } else {
-
-               post_capture_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, "JACK latency callback: DONE\n");
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_route_latency, (changed ? "yes" : "no")));
+
+       return changed;
 }
 
 void
-Session::post_playback_latency ()
+Session::update_latency (bool playback)
 {
-       set_worst_playback_latency ();
+       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) {
+               return;
+       }
+       if (!_engine.running()) {
+               return;
+       }
+
+       /* Note; RouteList is sorted as process-graph */
        boost::shared_ptr<RouteList> r = routes.reader ();
 
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_auditioner() && ((*i)->active())) {
-                       _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
-               }
+       if (playback) {
+               /* reverse the list so that we work backwards from the last route to run to the first */
+               RouteList* rl = routes.reader().get();
+               r.reset (new RouteList (*rl));
+               reverse (r->begin(), r->end());
        }
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               (*i)->set_latency_compensation (_worst_track_latency);
+               samplecnt_t latency = (*i)->set_private_port_latencies (playback);
+               (*i)->set_public_port_latencies (latency, playback);
        }
-}
 
-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) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr) {
-                       tr->set_capture_offset ();
-               }
+       if (playback) {
+               set_worst_output_latency ();
+               update_route_latency (true, true);
+       } else {
+               set_worst_input_latency ();
+               update_route_latency (false, false);
        }
+
+       DEBUG_TRACE (DEBUG::Latency, "JACK latency callback: DONE\n");
 }
 
 void
 Session::initialize_latencies ()
 {
-        {
-                Glib::Threads::Mutex::Lock lm (_engine.process_lock());
-                update_latency (false);
-                update_latency (true);
-        }
+       {
+               Glib::Threads::Mutex::Lock lm (_engine.process_lock());
+               update_latency (false);
+               update_latency (true);
+       }
 
-        set_worst_io_latencies ();
+       set_worst_io_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)) {
                return;
@@ -6879,11 +6969,13 @@ Session::set_worst_playback_latency ()
                _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency());
        }
 
+       _worst_output_latency = max (_worst_output_latency, _click_io->latency());
+
        DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst output latency: %1\n", _worst_output_latency));
 }
 
 void
-Session::set_worst_capture_latency ()
+Session::set_worst_input_latency ()
 {
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                return;
@@ -6901,50 +6993,29 @@ Session::set_worst_capture_latency ()
                _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)
 {
-       bool some_track_latency_changed = false;
-
        if (_state_of_the_state & (InitialConnecting|Deletion)) {
                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) {
-               if (!(*i)->is_auditioner() && ((*i)->active())) {
-                       framecnt_t tl;
-                       if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) {
-                               some_track_latency_changed = true;
-                       }
-                       _worst_track_latency = max (tl, _worst_track_latency);
-               }
-       }
-
-       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) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (!tr) {
-                       continue;
+               /* 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 ();
                }
-               tr->set_capture_offset ();
        }
 }
 
@@ -6989,13 +7060,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
@@ -7034,18 +7106,18 @@ Session::reconnect_ltc_output ()
 }
 
 void
-Session::set_range_selection (framepos_t start, framepos_t end)
+Session::set_range_selection (samplepos_t start, samplepos_t end)
 {
-       _range_selection = Evoral::Range<framepos_t> (start, end);
+       _range_selection = Evoral::Range<samplepos_t> (start, end);
 #ifdef USE_TRACKS_CODE_FEATURES
        follow_playhead_priority ();
 #endif
 }
 
 void
-Session::set_object_selection (framepos_t start, framepos_t end)
+Session::set_object_selection (samplepos_t start, samplepos_t end)
 {
-       _object_selection = Evoral::Range<framepos_t> (start, end);
+       _object_selection = Evoral::Range<samplepos_t> (start, end);
 #ifdef USE_TRACKS_CODE_FEATURES
        follow_playhead_priority ();
 #endif
@@ -7054,7 +7126,7 @@ Session::set_object_selection (framepos_t start, framepos_t end)
 void
 Session::clear_range_selection ()
 {
-       _range_selection = Evoral::Range<framepos_t> (-1,-1);
+       _range_selection = Evoral::Range<samplepos_t> (-1,-1);
 #ifdef USE_TRACKS_CODE_FEATURES
        follow_playhead_priority ();
 #endif
@@ -7063,7 +7135,7 @@ Session::clear_range_selection ()
 void
 Session::clear_object_selection ()
 {
-       _object_selection = Evoral::Range<framepos_t> (-1,-1);
+       _object_selection = Evoral::Range<samplepos_t> (-1,-1);
 #ifdef USE_TRACKS_CODE_FEATURES
        follow_playhead_priority ();
 #endif
@@ -7276,12 +7348,12 @@ 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
-                        * modifies the capture offset... which can be a proplem
-                        * in "prepare_to_stop"
+                        * update_latency_compensation ()
+                        * calls DiskWriter::set_capture_offset () which
+                        * 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 ();
                        }
                }