Replace connection cerr dumping with DEBUG_TRACE.
[ardour.git] / libs / ardour / session.cc
index b2a1aa8da8f2c15026a3a12a2a68f527f99fa49f..19c7f5638f3bf9d76b139316b653c5611b0cc3dc 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 1999-2004 Paul Davis
+    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
@@ -17,6 +17,8 @@
 
 */
 
+#include <stdint.h>
+
 #include <algorithm>
 #include <string>
 #include <vector>
 #include <unistd.h>
 #include <limits.h>
 
-
 #include <glibmm/thread.h>
 #include <glibmm/miscutils.h>
 #include <glibmm/fileutils.h>
 
+#include <boost/algorithm/string/erase.hpp>
+
 #include "pbd/error.h"
-#include <glibmm/thread.h>
 #include "pbd/boost_debug.h"
 #include "pbd/pathscanner.h"
 #include "pbd/stl_delete.h"
 #include "pbd/basename.h"
 #include "pbd/stacktrace.h"
 #include "pbd/file_utils.h"
+#include "pbd/convert.h"
+#include "pbd/strsplit.h"
 
 #include "ardour/amp.h"
 #include "ardour/analyser.h"
@@ -53,6 +57,7 @@
 #include "ardour/audioplaylist.h"
 #include "ardour/audioregion.h"
 #include "ardour/auditioner.h"
+#include "ardour/buffer_manager.h"
 #include "ardour/buffer_set.h"
 #include "ardour/bundle.h"
 #include "ardour/butler.h"
@@ -71,6 +76,7 @@
 #include "ardour/midi_track.h"
 #include "ardour/midi_ui.h"
 #include "ardour/named_selection.h"
+#include "ardour/process_thread.h"
 #include "ardour/playlist.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/port_insert.h"
 #include "ardour/tape_file_matcher.h"
 #include "ardour/tempo.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
+#include "ardour/speakers.h"
+#include "ardour/operations.h"
+
+#include "midi++/port.h"
+#include "midi++/mmc.h"
+#include "midi++/manager.h"
 
 #include "i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
-using boost::shared_ptr;
-using boost::weak_ptr;
 
 bool Session::_disable_all_loaded_plugins = false;
 
 PBD::Signal1<void,std::string> Session::Dialog;
 PBD::Signal0<int> Session::AskAboutPendingState;
-PBD::Signal2<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
+PBD::Signal2<int, framecnt_t, framecnt_t> Session::AskAboutSampleRateMismatch;
 PBD::Signal0<void> Session::SendFeedback;
+PBD::Signal3<int,Session*,std::string,DataType> Session::MissingFile;
 
-PBD::Signal0<void> Session::TimecodeOffsetChanged;
-PBD::Signal0<void> Session::StartTimeChanged;
-PBD::Signal0<void> Session::EndTimeChanged;
+PBD::Signal1<void, framepos_t> Session::StartTimeChanged;
+PBD::Signal1<void, framepos_t> Session::EndTimeChanged;
 PBD::Signal0<void> Session::AutoBindingOn;
 PBD::Signal0<void> Session::AutoBindingOff;
 PBD::Signal2<void,std::string, std::string> Session::Exported;
+PBD::Signal1<int,boost::shared_ptr<Playlist> > Session::AskAboutPlaylistDeletion;
+PBD::Signal0<void> Session::Quit;
 
 static void clean_up_session_event (SessionEvent* ev) { delete ev; }
 const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
 
 Session::Session (AudioEngine &eng,
-                 const string& fullpath,
-                 const string& snapshot_name,
-                 string mix_template)
-
-       : _engine (eng),
-         _target_transport_speed (0.0),
-         _requested_return_frame (-1),
-         _scratch_buffers(new BufferSet()),
-         _silent_buffers(new BufferSet()),
-         _mix_buffers(new BufferSet()),
-         mmc (0),
-         _mmc_port (default_mmc_port),
-         _mtc_port (default_mtc_port),
-         _midi_port (default_midi_port),
-         _midi_clock_port (default_midi_clock_port),
-         _session_dir (new SessionDirectory(fullpath)),
-         state_tree (0),
-         _butler (new Butler (*this)),
-         _post_transport_work (0),
-         _send_timecode_update (false),
-         diskstreams (new DiskstreamList),
-         routes (new RouteList),
-         _total_free_4k_blocks (0),
-         _bundles (new BundleList),
-         _bundle_xml_node (0),
-         _click_io ((IO*) 0),
-         click_data (0),
-         click_emphasis_data (0),
-         main_outs (0),
-         _metadata (new SessionMetadata()),
-         _have_rec_enabled_diskstream (false)
-
-{
+                  const string& fullpath,
+                  const string& snapshot_name,
+                  BusProfile* bus_profile,
+                  string mix_template)
+       : _engine (eng)
+       , _target_transport_speed (0.0)
+       , _requested_return_frame (-1)
+       , _session_dir (new SessionDirectory(fullpath))
+       , state_tree (0)
+       , _state_of_the_state (Clean)
+       , _butler (new Butler (*this))
+       , _post_transport_work (0)
+       , _send_timecode_update (false)
+       , _all_route_group (new RouteGroup (*this, "all"))
+       , route_graph (new Graph(*this))
+       , routes (new RouteList)
+       , _total_free_4k_blocks (0)
+       , _bundles (new BundleList)
+       , _bundle_xml_node (0)
+       , _current_trans (0)
+       , _click_io ((IO*) 0)
+       , click_data (0)
+       , click_emphasis_data (0)
+       , main_outs (0)
+       , _metadata (new SessionMetadata())
+       , _have_rec_enabled_track (false)
+       , _suspend_timecode_transmission (0)
+{
+       _locations = new Locations (*this);
+               
        playlists.reset (new SessionPlaylists);
-       
-       bool new_session;
+
+       _all_route_group->set_active (true, this);
 
        interpolation.add_channel_to (0, 0);
 
@@ -162,23 +172,21 @@ Session::Session (AudioEngine &eng,
                throw failed_constructor();
        }
 
-       info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
-
-       n_physical_outputs = _engine.n_physical_outputs(DataType::AUDIO);
-       n_physical_inputs =  _engine.n_physical_inputs(DataType::AUDIO);
+       n_physical_outputs = _engine.n_physical_outputs ();
+       n_physical_inputs =  _engine.n_physical_inputs ();
 
        first_stage_init (fullpath, snapshot_name);
 
-       new_session = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+       _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
 
-       if (new_session) {
-               if (create (new_session, mix_template, compute_initial_length())) {
+       if (_is_new) {
+               if (create (mix_template, bus_profile)) {
                        destroy ();
                        throw failed_constructor ();
                }
        }
 
-       if (second_stage_init (new_session)) {
+       if (second_stage_init ()) {
                destroy ();
                throw failed_constructor ();
        }
@@ -189,144 +197,17 @@ Session::Session (AudioEngine &eng,
 
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
 
-       Config->ParameterChanged.connect (*this, boost::bind (&Session::config_changed, this, _1, false));
-       config.ParameterChanged.connect (*this, boost::bind (&Session::config_changed, this, _1, true));
+       Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, false));
+       config.ParameterChanged.connect_same_thread (*this, boost::bind (&Session::config_changed, this, _1, true));
 
        if (was_dirty) {
                DirtyChanged (); /* EMIT SIGNAL */
        }
-}
-
-Session::Session (AudioEngine &eng,
-                 string fullpath,
-                 string snapshot_name,
-                 AutoConnectOption input_ac,
-                 AutoConnectOption output_ac,
-                 uint32_t control_out_channels,
-                 uint32_t master_out_channels,
-                 uint32_t requested_physical_in,
-                 uint32_t requested_physical_out,
-                 nframes_t initial_length)
-
-       : _engine (eng),
-         _target_transport_speed (0.0),
-         _requested_return_frame (-1),
-         _scratch_buffers(new BufferSet()),
-         _silent_buffers(new BufferSet()),
-         _mix_buffers(new BufferSet()),
-         mmc (0),
-         _mmc_port (default_mmc_port),
-         _mtc_port (default_mtc_port),
-         _midi_port (default_midi_port),
-         _midi_clock_port (default_midi_clock_port),
-         _session_dir ( new SessionDirectory(fullpath)),
-         state_tree (0),
-         _butler (new Butler (*this)),
-         _post_transport_work (0),
-         _send_timecode_update (false),
-         diskstreams (new DiskstreamList),
-         routes (new RouteList),
-         _total_free_4k_blocks (0),
-         _bundles (new BundleList),
-         _bundle_xml_node (0),
-         _click_io ((IO *) 0),
-         click_data (0),
-         click_emphasis_data (0),
-         main_outs (0),
-         _metadata (new SessionMetadata()),
-         _have_rec_enabled_diskstream (false)
-{
-       playlists.reset (new SessionPlaylists);
-
-       bool new_session;
-
-       interpolation.add_channel_to (0, 0);
-
-       if (!eng.connected()) {
-               throw failed_constructor();
-       }
-
-       info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
-
-       n_physical_outputs = _engine.n_physical_outputs (DataType::AUDIO);
-       n_physical_inputs = _engine.n_physical_inputs (DataType::AUDIO);
-
-       if (n_physical_inputs) {
-               n_physical_inputs = max (requested_physical_in, n_physical_inputs);
-       }
-
-       if (n_physical_outputs) {
-               n_physical_outputs = max (requested_physical_out, n_physical_outputs);
-       }
-
-       first_stage_init (fullpath, snapshot_name);
-
-       new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
-
-       if (new_session) {
-               if (create (new_session, string(), initial_length)) {
-                       destroy ();
-                       throw failed_constructor ();
-               }
-       }
-
-       {
-               /* set up Master Out and Control Out if necessary */
-
-               RouteList rl;
-               int control_id = 1;
-
-               if (master_out_channels) {
-                       ChanCount count(DataType::AUDIO, master_out_channels);
-                       Route* rt = new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO);
-                       boost_debug_shared_ptr_mark_interesting (rt, typeid (rt).name());
-                       shared_ptr<Route> r (rt);
-                       r->input()->ensure_io (count, false, this);
-                       r->output()->ensure_io (count, false, this);
-                       r->set_remote_control_id (control_id);
-
-                       rl.push_back (r);
-               } else {
-                       /* prohibit auto-connect to master, because there isn't one */
-                       output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster);
-               }
-
-               if (control_out_channels) {
-                       ChanCount count(DataType::AUDIO, control_out_channels);
-                       Route* rt = new Route (*this, _("monitor"), Route::ControlOut, DataType::AUDIO);
-                       boost_debug_shared_ptr_mark_interesting (rt, typeid (rt).name());
-                       shared_ptr<Route> r (rt);
-                       r->input()->ensure_io (count, false, this);
-                       r->output()->ensure_io (count, false, this);
-                       r->set_remote_control_id (control_id++);
-
-                       rl.push_back (r);
-               }
-
-               if (!rl.empty()) {
-                       add_routes (rl, false);
-               }
-
-       }
-
-       if (no_auto_connect()) {
-               input_ac = AutoConnectOption (0);
-               output_ac = AutoConnectOption (0);
-       }
-
-       Config->set_input_auto_connect (input_ac);
-       Config->set_output_auto_connect (output_ac);
-
-       if (second_stage_init (new_session)) {
-               destroy ();
-               throw failed_constructor ();
-       }
 
-       store_recent_sessions (_name, _path);
-
-       _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+       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));
 
-       Config->ParameterChanged.connect (*this, boost::bind (&Session::config_changed, this, _1, false));
+       _is_new = false;
 }
 
 Session::~Session ()
@@ -361,9 +242,10 @@ Session::destroy ()
 
        Stateful::loading_state_version = 0;
 
-       _butler->terminate_thread ();
-       
+       _butler->drop_references ();
+       delete _butler;
        delete midi_control_ui;
+       delete _all_route_group;
 
        if (click_data != default_click) {
                delete [] click_data;
@@ -375,133 +257,72 @@ Session::destroy ()
 
        clear_clicks ();
 
-       delete _scratch_buffers;
-       delete _silent_buffers;
-       delete _mix_buffers;
-
        /* clear out any pending dead wood from RCU managed objects */
 
        routes.flush ();
-       diskstreams.flush ();
        _bundles.flush ();
        
        AudioDiskstream::free_working_buffers();
 
-       // BOOST::SIGNALS: Route::SyncOrderKeys.clear();
-
-       DEBUG_TRACE (DEBUG::Destruction, "delete named selections\n");
-       for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ) {
-               NamedSelectionList::iterator tmp;
+       /* tell everyone who is still standing that we're about to die */
+       drop_references ();
 
-               tmp = i;
-               ++tmp;
+       /* tell everyone to drop references and delete objects as we go */
 
-               delete *i;
-               i = tmp;
-       }
+       DEBUG_TRACE (DEBUG::Destruction, "delete named selections\n");
+       named_selections.clear ();
 
        DEBUG_TRACE (DEBUG::Destruction, "delete regions\n");
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
-               RegionList::iterator tmp;
-
-               tmp = i;
-               ++tmp;
-
-               boost::shared_ptr<Region> keep (i->second);
-               
-               DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for region %1 (%2); pre-ref = %3\n", i->second->name(), i->second.get(), i->second.use_count()));
-               i->second->drop_references ();
-               DEBUG_TRACE(DEBUG::Destruction, string_compose ("region post ref = %1\n", i->second.use_count()));
-               i = tmp;
-       }
-
-       regions.clear ();
+       RegionFactory::delete_all_regions ();
 
        DEBUG_TRACE (DEBUG::Destruction, "delete routes\n");
-       
+
        /* reset these three references to special routes before we do the usual route delete thing */
 
        auditioner.reset ();
        _master_out.reset ();
-       _control_out.reset ();
+       _monitor_out.reset ();
 
        {
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> r = writer.get_copy ();
+
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                        DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for route %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
                        (*i)->drop_references ();
                }
+
                r->clear ();
                /* writer goes out of scope and updates master */
        }
        routes.flush ();
 
-       DEBUG_TRACE (DEBUG::Destruction, "delete diskstreams\n");
-       {
-               RCUWriter<DiskstreamList> dwriter (diskstreams);
-               boost::shared_ptr<DiskstreamList> dsl = dwriter.get_copy();
-               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                       DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for diskstream %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
-                       (*i)->drop_references ();
-               }
-               dsl->clear ();
-       }
-       diskstreams.flush ();
-
        DEBUG_TRACE (DEBUG::Destruction, "delete sources\n");
-       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
-               SourceMap::iterator tmp;
-
-               tmp = i;
-               ++tmp;
-
+       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
                DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for source %1 ; pre-ref = %2\n", i->second->path(), i->second.use_count()));
                i->second->drop_references ();
-
-               i = tmp;
        }
 
        sources.clear ();
 
-
        DEBUG_TRACE (DEBUG::Destruction, "delete route groups\n");
        for (list<RouteGroup *>::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) {
+               
                delete *i;
        }
 
        Crossfade::set_buffer_size (0);
 
-       delete mmc;
-
        /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
        playlists.reset ();
 
-       /* tell everyone who is still standing that we're about to die */
-
-       drop_references ();
-
-       boost_debug_list_ptrs ();
+       delete _locations;
 
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
-}
-
-void
-Session::set_worst_io_latencies ()
-{
-       _worst_output_latency = 0;
-       _worst_input_latency = 0;
-
-       if (!_engine.connected()) {
-               return;
-       }
-
-       boost::shared_ptr<RouteList> r = routes.reader ();
 
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency());
-               _worst_input_latency = max (_worst_input_latency, (*i)->input()->latency());
-       }
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+       boost_debug_list_ptrs ();
+#endif
 }
 
 void
@@ -524,7 +345,7 @@ Session::when_engine_running ()
 
        /* every time we reconnect, recompute worst case output latencies */
 
-       _engine.Running.connect (*this, boost::bind (&Session::set_worst_io_latencies, this));
+       _engine.Running.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies, this));
 
        if (synced_to_jack()) {
                _engine.transport_stop ();
@@ -567,36 +388,42 @@ Session::when_engine_running ()
 
                        /* default state for Click: dual-mono to first 2 physical outputs */
 
-                       for (int physport = 0; physport < 2; ++physport) {
-                               string physical_output = _engine.get_nth_physical_output (DataType::AUDIO, physport);
+                       vector<string> outs;
+                       _engine.get_physical_outputs (DataType::AUDIO, outs);
 
-                               if (physical_output.length()) {
-                                       if (_click_io->add_port (physical_output, this)) {
-                                               // relax, even though its an error
-                                      }
-                              }
-                      }
-
-                       if (_click_io->n_ports () > ChanCount::ZERO) {
-                               _clicking = Config->get_clicking ();
-                       }
+                       for (uint32_t physport = 0; physport < 2; ++physport) {
+                               if (outs.size() > physport) {
+                                       if (_click_io->add_port (outs[physport], this)) {
+                                               // relax, even though its an error
+                                       }
+                               }
+                       }
+                       
+                       if (_click_io->n_ports () > ChanCount::ZERO) {
+                               _clicking = Config->get_clicking ();
+                       }
                }
        }
-
+       
        catch (failed_constructor& err) {
                error << _("cannot setup Click I/O") << endmsg;
        }
 
        BootMessage (_("Compute I/O Latencies"));
 
-       set_worst_io_latencies ();
-
        if (_clicking) {
                // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO?
        }
 
        BootMessage (_("Set up standard connections"));
 
+       vector<string> inputs[DataType::num_types];
+       vector<string> outputs[DataType::num_types];
+       for (uint32_t i = 0; i < DataType::num_types; ++i) {
+               _engine.get_physical_inputs (DataType (DataType::Symbol (i)), inputs[i]);
+               _engine.get_physical_outputs (DataType (DataType::Symbol (i)), outputs[i]);
+       }
+
        /* Create a set of Bundle objects that map
           to the physical I/O currently available.  We create both
           mono and stereo bundles, so that the common cases of mono
@@ -607,28 +434,28 @@ Session::when_engine_running ()
 
        /* mono output bundles */
 
-       for (uint32_t np = 0; np < n_physical_outputs; ++np) {
+       for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); ++np) {
                char buf[32];
                snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
 
-               shared_ptr<Bundle> c (new Bundle (buf, true));
-               c->add_channel (_("mono"));
-               c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+               boost::shared_ptr<Bundle> c (new Bundle (buf, true));
+               c->add_channel (_("mono"), DataType::AUDIO);
+               c->set_port (0, outputs[DataType::AUDIO][np]);
 
                add_bundle (c);
        }
 
        /* stereo output bundles */
 
-       for (uint32_t np = 0; np < n_physical_outputs; np += 2) {
-               if (np + 1 < n_physical_outputs) {
+       for (uint32_t np = 0; np < outputs[DataType::AUDIO].size(); np += 2) {
+               if (np + 1 < outputs[DataType::AUDIO].size()) {
                        char buf[32];
                        snprintf (buf, sizeof(buf), _("out %" PRIu32 "+%" PRIu32), np + 1, np + 2);
-                       shared_ptr<Bundle> c (new Bundle (buf, true));
-                       c->add_channel (_("L"));
-                       c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
-                       c->add_channel (_("R"));
-                       c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
+                        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);
+                       c->set_port (1, outputs[DataType::AUDIO][np + 1]);
 
                        add_bundle (c);
                }
@@ -636,41 +463,69 @@ Session::when_engine_running ()
 
        /* mono input bundles */
 
-       for (uint32_t np = 0; np < n_physical_inputs; ++np) {
+       for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); ++np) {
                char buf[32];
                snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
 
-               shared_ptr<Bundle> c (new Bundle (buf, false));
-               c->add_channel (_("mono"));
-               c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+               boost::shared_ptr<Bundle> c (new Bundle (buf, false));
+               c->add_channel (_("mono"), DataType::AUDIO);
+               c->set_port (0, inputs[DataType::AUDIO][np]);
 
                add_bundle (c);
        }
 
        /* stereo input bundles */
 
-       for (uint32_t np = 0; np < n_physical_inputs; np += 2) {
-               if (np + 1 < n_physical_inputs) {
+       for (uint32_t np = 0; np < inputs[DataType::AUDIO].size(); np += 2) {
+               if (np + 1 < inputs[DataType::AUDIO].size()) {
                        char buf[32];
                        snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2);
 
-                       shared_ptr<Bundle> c (new Bundle (buf, false));
-                       c->add_channel (_("L"));
-                       c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
-                       c->add_channel (_("R"));
-                       c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
+                       boost::shared_ptr<Bundle> c (new Bundle (buf, false));
+                       c->add_channel (_("L"), DataType::AUDIO);
+                       c->set_port (0, inputs[DataType::AUDIO][np]);
+                       c->add_channel (_("R"), DataType::AUDIO);
+                       c->set_port (1, inputs[DataType::AUDIO][np + 1]);
 
                        add_bundle (c);
                }
        }
 
+       /* MIDI input bundles */
+
+       for (uint32_t np = 0; np < inputs[DataType::MIDI].size(); ++np) {
+               string n = inputs[DataType::MIDI][np];
+               boost::erase_first (n, X_("alsa_pcm:"));
+               
+               boost::shared_ptr<Bundle> c (new Bundle (n, false));
+               c->add_channel ("", DataType::MIDI);
+               c->set_port (0, inputs[DataType::MIDI][np]);
+               add_bundle (c);
+       }
+               
+       /* MIDI output bundles */
+
+       for (uint32_t np = 0; np < outputs[DataType::MIDI].size(); ++np) {
+               string n = outputs[DataType::MIDI][np];
+               boost::erase_first (n, X_("alsa_pcm:"));
+
+               boost::shared_ptr<Bundle> c (new Bundle (n, true));
+               c->add_channel ("", DataType::MIDI);
+               c->set_port (0, outputs[DataType::MIDI][np]);
+               add_bundle (c);
+       }
+
        BootMessage (_("Setup signal flow and plugins"));
 
        hookup_io ();
 
-       if (!no_auto_connect()) {
+       if (_is_new && !no_auto_connect()) {
+
+               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock());
+                
+               /* don't connect the master bus outputs if there is a monitor bus */
 
-               if (_master_out && Config->get_auto_connect_standard_busses()) {
+               if (_master_out && Config->get_auto_connect_standard_busses() && !_monitor_out) {
 
                        /* if requested auto-connect the outputs to the first N physical ports.
                         */
@@ -679,7 +534,10 @@ Session::when_engine_running ()
 
                        for (uint32_t n = 0; n < limit; ++n) {
                                Port* p = _master_out->output()->nth (n);
-                               string connect_to = _engine.get_nth_physical_output (DataType (p->type()), n);
+                               string connect_to;
+                               if (outputs[p->type()].size() > n) {
+                                       connect_to = outputs[p->type()][n];
+                               }
 
                                if (!connect_to.empty() && p->connected_to (connect_to) == false) {
                                        if (_master_out->output()->connect (p, connect_to, this)) {
@@ -691,7 +549,7 @@ Session::when_engine_running ()
                        }
                }
 
-               if (_control_out) {
+               if (_monitor_out) {
 
                        /* AUDIO ONLY as of june 29th 2009, because listen semantics for anything else
                           are undefined, at best.
@@ -701,16 +559,16 @@ Session::when_engine_running ()
                           under some conditions)
                        */
 
-                       uint32_t limit = _control_out->n_inputs().n_audio();
+                       uint32_t limit = _monitor_out->n_inputs().n_audio();
 
                        if (_master_out) {
                                for (uint32_t n = 0; n < limit; ++n) {
-                                       AudioPort* p = _control_out->input()->ports().nth_audio_port (n);
+                                       AudioPort* p = _monitor_out->input()->ports().nth_audio_port (n);
                                        AudioPort* o = _master_out->output()->ports().nth_audio_port (n);
 
                                        if (o) {
                                                string connect_to = o->name();
-                                               if (_control_out->input()->connect (p, connect_to, this)) {
+                                               if (_monitor_out->input()->connect (p, connect_to, this)) {
                                                        error << string_compose (_("cannot connect control input %1 to %2"), n, connect_to)
                                                              << endmsg;
                                                        break;
@@ -719,18 +577,17 @@ Session::when_engine_running ()
                                }
                        }
 
-                       /* if control out is not connected,
-                          connect control out to physical outs, but use ones after the master if possible
+                       /* if control out is not connected, connect control out to physical outs
                        */
 
-                       if (!_control_out->output()->connected_to (boost::shared_ptr<IO>())) {
+                       if (!_monitor_out->output()->connected ()) {
 
                                if (!Config->get_monitor_bus_preferred_bundle().empty()) {
 
                                        boost::shared_ptr<Bundle> b = bundle_by_name (Config->get_monitor_bus_preferred_bundle());
 
                                        if (b) {
-                                               _control_out->output()->connect_ports_to_bundle (b, this);
+                                               _monitor_out->output()->connect_ports_to_bundle (b, this);
                                        } else {
                                                warning << string_compose (_("The preferred I/O for the monitor bus (%1) cannot be found"),
                                                                           Config->get_monitor_bus_preferred_bundle())
@@ -739,24 +596,26 @@ Session::when_engine_running ()
 
                                } else {
 
-                                       /* XXX this logic is wrong for mixed port types */
-
-                                       uint32_t shift = _master_out->n_outputs().n_audio();
-                                       uint32_t mod = _engine.n_physical_outputs (DataType::AUDIO);
-                                       limit = _control_out->n_outputs().n_audio();
+                                       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+                                               uint32_t mod = n_physical_outputs.get (*t);
+                                               uint32_t limit = _monitor_out->n_outputs().get(*t);
 
-                                       cerr << "Connecting " << limit << " control out ports, shift is " << shift << " mod is " << mod << endl;
+                                               for (uint32_t n = 0; n < limit; ++n) {
 
-                                       for (uint32_t n = 0; n < limit; ++n) {
-
-                                               Port* p = _control_out->output()->nth (n);
-                                               string connect_to = _engine.get_nth_physical_output (DataType (p->type()), (n+shift) % mod);
+                                                       Port* p = _monitor_out->output()->ports().port(*t, n);
+                                                       string connect_to;
+                                                       if (outputs[*t].size() > (n % mod)) {
+                                                               connect_to = outputs[*t][n % mod];
+                                                       }
 
-                                               if (!connect_to.empty()) {
-                                                       if (_control_out->output()->connect (p, connect_to, this)) {
-                                                               error << string_compose (_("cannot connect control output %1 to %2"), n, connect_to)
-                                                                     << endmsg;
-                                                               break;
+                                                       if (!connect_to.empty()) {
+                                                               if (_monitor_out->output()->connect (p, connect_to, this)) {
+                                                                       error << string_compose (
+                                                                                       _("cannot connect control output %1 to %2"),
+                                                                                       n, connect_to)
+                                                                               << endmsg;
+                                                                       break;
+                                                               }
                                                        }
                                                }
                                        }
@@ -765,15 +624,14 @@ Session::when_engine_running ()
                }
        }
 
-       /* catch up on send+insert cnts */
-
        _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
 
        /* hook us up to the engine */
 
        BootMessage (_("Connect to engine"));
-
        _engine.set_session (this);
+
+       update_latency_compensation (true);
 }
 
 void
@@ -785,16 +643,19 @@ Session::hookup_io ()
 
        _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting);
 
-
        if (!auditioner) {
 
                /* we delay creating the auditioner till now because
                   it makes its own connections to ports.
-                  the engine has to be running for this to work.
                */
 
                try {
-                       auditioner.reset (new Auditioner (*this));
+                       boost::shared_ptr<Auditioner> a (new Auditioner (*this));
+                       if (a->init()) {
+                               throw failed_constructor ();
+                       }
+                       a->use_new_diskstream ();
+                       auditioner = a;
                }
 
                catch (failed_constructor& err) {
@@ -811,26 +672,33 @@ Session::hookup_io ()
        /* Tell all IO objects to connect themselves together */
 
        IO::enable_connecting ();
+       MIDI::Port::MakeConnections ();
 
        /* Now reset all panners */
 
        Delivery::reset_panners ();
 
-       /* Connect tracks to listen/solo etc. busses XXX generalize this beyond control_out */
-
-       if (_control_out) {
+       /* Connect tracks to monitor/listen bus if there is one.  Note that in an
+          existing session, the internal sends will already exist, but we want the
+          routes to notice that they connect to the control out specifically.
+       */
 
+       if (_monitor_out) {
                boost::shared_ptr<RouteList> r = routes.reader ();
-
                for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
+                        
+                       if ((*x)->is_monitor()) {
+                                
+                               /* relax */
+                                
+                       } else if ((*x)->is_master()) {
+                                
+                               /* relax */
+                                
+                       } else {
 
-                       if ((*x)->is_control() || (*x)->is_master()) {
-                               continue;
+                               (*x)->listen_via_monitor ();
                        }
-
-                       (*x)->listen_via (_control_out,
-                                         (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
-                                         false, false);
                }
        }
 
@@ -856,33 +724,20 @@ Session::hookup_io ()
 }
 
 void
-Session::playlist_length_changed ()
-{
-       /* we can't just increase end_location->end() if pl->get_maximum_extent()
-          if larger. if the playlist used to be the longest playlist,
-          and its now shorter, we have to decrease end_location->end(). hence,
-          we have to iterate over all diskstreams and check the
-          playlists currently in use.
-       */
-       find_current_end ();
-}
-
-void
-Session::diskstream_playlist_changed (boost::weak_ptr<Diskstream> wp)
+Session::track_playlist_changed (boost::weak_ptr<Track> wp)
 {
-       boost::shared_ptr<Diskstream> dstream = wp.lock ();
-       if (!dstream) {
+       boost::shared_ptr<Track> track = wp.lock ();
+       if (!track) {
                return;
        }
        
        boost::shared_ptr<Playlist> playlist;
 
-       if ((playlist = dstream->playlist()) != 0) {
-               playlist->LengthChanged.connect (*this, boost::bind (&Session::playlist_length_changed, this));
+       if ((playlist = track->playlist()) != 0) {
+               playlist->RegionAdded.connect_same_thread (*this, boost::bind (&Session::playlist_region_added, this, _1));
+               playlist->RangesMoved.connect_same_thread (*this, boost::bind (&Session::playlist_ranges_moved, this, _1));
+               playlist->RegionsExtended.connect_same_thread (*this, boost::bind (&Session::playlist_regions_extended, this, _1));
        }
-
-       /* see comment in playlist_length_changed () */
-       find_current_end ();
 }
 
 bool
@@ -899,28 +754,26 @@ Session::record_enabling_legal () const
        return true;
 }
 
+void
+Session::set_track_monitor_input_status (bool yn)
+{
+       boost::shared_ptr<RouteList> rl = routes.reader ();
+       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+               boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*i);
+               if (tr && tr->record_enabled ()) {
+                       //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
+                       tr->monitor_input (yn);
+               }
+       }
+}
+
 void
 Session::reset_input_monitor_state ()
 {
        if (transport_rolling()) {
-
-               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                       if ((*i)->record_enabled ()) {
-                               //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
-                               (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input());
-                       }
-               }
+               set_track_monitor_input_status (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input());
        } else {
-               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                       if ((*i)->record_enabled ()) {
-                               //cerr << "switching to input = " << !Config->get_auto_input() << __FILE__ << __LINE__ << endl << endl;
-                               (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring);
-                       }
-               }
+               set_track_monitor_input_status (Config->get_monitoring_model() == HardwareMonitoring);
        }
 }
 
@@ -938,7 +791,7 @@ Session::auto_punch_start_changed (Location* location)
 void
 Session::auto_punch_end_changed (Location* location)
 {
-       nframes_t when_to_stop = location->end();
+       framepos_t when_to_stop = location->end();
        // when_to_stop += _worst_output_latency + _worst_input_latency;
        replace_event (SessionEvent::PunchOut, when_to_stop);
 }
@@ -946,7 +799,7 @@ Session::auto_punch_end_changed (Location* location)
 void
 Session::auto_punch_changed (Location* location)
 {
-       nframes_t when_to_stop = location->end();
+       framepos_t when_to_stop = location->end();
 
        replace_event (SessionEvent::PunchIn, location->start());
        //when_to_stop += _worst_output_latency + _worst_input_latency;
@@ -993,7 +846,7 @@ Session::set_auto_punch_location (Location* location)
 {
        Location* existing;
 
-       if ((existing = _locations.auto_punch_location()) != 0 && existing != 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);
@@ -1014,9 +867,9 @@ Session::set_auto_punch_location (Location* location)
 
        punch_connections.drop_connections ();
 
-       location->start_changed.connect (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, _1));
-       location->end_changed.connect (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, _1));
-       location->changed.connect (punch_connections, boost::bind (&Session::auto_punch_changed, this, _1));
+       location->start_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_start_changed, this, _1));
+       location->end_changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_end_changed, this, _1));
+       location->changed.connect_same_thread (punch_connections, boost::bind (&Session::auto_punch_changed, this, _1));
 
        location->set_auto_punch (true, this);
 
@@ -1030,7 +883,7 @@ Session::set_auto_loop_location (Location* location)
 {
        Location* existing;
 
-       if ((existing = _locations.auto_loop_location()) != 0 && existing != location) {
+       if ((existing = _locations->auto_loop_location()) != 0 && existing != location) {
                loop_connections.drop_connections ();
                existing->set_auto_loop (false, this);
                remove_event (existing->end(), SessionEvent::AutoLoop);
@@ -1052,9 +905,9 @@ Session::set_auto_loop_location (Location* location)
 
        loop_connections.drop_connections ();
 
-       location->start_changed.connect (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
-       location->end_changed.connect (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
-       location->changed.connect (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
+       location->start_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
+       location->end_changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
+       location->changed.connect_same_thread (loop_connections, boost::bind (&Session::auto_loop_changed, this, _1));
 
        location->set_auto_loop (true, this);
 
@@ -1076,7 +929,7 @@ Session::locations_added (Location *)
 void
 Session::locations_changed ()
 {
-       _locations.apply (*this, &Session::handle_locations_changed);
+       _locations->apply (*this, &Session::handle_locations_changed);
 }
 
 void
@@ -1100,11 +953,8 @@ Session::handle_locations_changed (Locations::LocationList& locations)
                        set_loop = true;
                }
 
-               if (location->is_start()) {
-                       start_location = location;
-               }
-               if (location->is_end()) {
-                       end_location = location;
+               if (location->is_session_range()) {
+                       _session_range_location = location;
                }
        }
 
@@ -1121,22 +971,25 @@ Session::handle_locations_changed (Locations::LocationList& locations)
 void
 Session::enable_record ()
 {
-       /* XXX really atomic compare+swap here */
-       if (g_atomic_int_get (&_record_status) != Recording) {
-               g_atomic_int_set (&_record_status, Recording);
-               _last_record_location = _transport_frame;
-               deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location);
+       while (1) {
+               RecordState rs = (RecordState) g_atomic_int_get (&_record_status);
+                
+               if (rs == Recording) {
+                       break;
+               }
 
-               if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
-                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                               if ((*i)->record_enabled ()) {
-                                       (*i)->monitor_input (true);
-                               }
+               if (g_atomic_int_compare_and_exchange (&_record_status, rs, Recording)) {
+                        
+                       _last_record_location = _transport_frame;
+                       MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
+                        
+                       if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
+                               set_track_monitor_input_status (true);
                        }
+                        
+                       RecordStateChanged ();
+                       break;
                }
-
-               RecordStateChanged ();
        }
 }
 
@@ -1149,27 +1002,15 @@ Session::disable_record (bool rt_context, bool force)
 
                if ((!Config->get_latched_record_enable () && !play_loop) || force) {
                        g_atomic_int_set (&_record_status, Disabled);
+                       MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordExit));
                } else {
                        if (rs == Recording) {
                                g_atomic_int_set (&_record_status, Enabled);
                        }
                }
 
-               // FIXME: timestamp correct? [DR]
-               // FIXME FIXME FIXME: rt_context?  this must be called in the process thread.
-               // does this /need/ to be sent in all cases?
-               if (rt_context) {
-                       deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame);
-               }
-
                if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
-                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                               if ((*i)->record_enabled ()) {
-                                       (*i)->monitor_input (false);
-                               }
-                       }
+                       set_track_monitor_input_status (false);
                }
 
                RecordStateChanged (); /* emit signal */
@@ -1183,19 +1024,10 @@ Session::disable_record (bool rt_context, bool force)
 void
 Session::step_back_from_record ()
 {
-       /* XXX really atomic compare+swap here */
-       if (g_atomic_int_get (&_record_status) == Recording) {
-               g_atomic_int_set (&_record_status, Enabled);
+       if (g_atomic_int_compare_and_exchange (&_record_status, Recording, Enabled)) {
 
                if (Config->get_monitoring_model() == HardwareMonitoring && config.get_auto_input()) {
-                       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-                       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                               if ((*i)->record_enabled ()) {
-                                       //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
-                                       (*i)->monitor_input (false);
-                               }
-                       }
+                       set_track_monitor_input_status (false);
                }
        }
 }
@@ -1203,32 +1035,38 @@ Session::step_back_from_record ()
 void
 Session::maybe_enable_record ()
 {
+       if (_step_editors > 0) {
+               return;
+       }
+
        g_atomic_int_set (&_record_status, Enabled);
 
-       /* this function is currently called from somewhere other than an RT thread.
-          this save_state() call therefore doesn't impact anything.
+       /* This function is currently called from somewhere other than an RT thread.
+          This save_state() call therefore doesn't impact anything.  Doing it here
+          means that we save pending state of which sources the next record will use,
+          which gives us some chance of recovering from a crash during the record.
        */
-
+       
        save_state ("", true);
-
+       
        if (_transport_speed) {
                if (!config.get_punch_in()) {
                        enable_record ();
                }
        } else {
-               deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
+               MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
                RecordStateChanged (); /* EMIT SIGNAL */
        }
 
        set_dirty();
 }
 
-nframes64_t
+framepos_t
 Session::audible_frame () const
 {
-       nframes64_t ret;
-       nframes64_t tf;
-       nframes_t offset;
+       framepos_t ret;
+       framepos_t tf;
+       framecnt_t offset;
 
        /* the first of these two possible settings for "offset"
           mean that the audible frame is stationary until
@@ -1240,7 +1078,7 @@ Session::audible_frame () const
           in the absence of any plugin latency compensation
        */
 
-       offset = _worst_output_latency;
+       offset = worst_playback_latency ();
 
        if (offset > current_block_size) {
                offset -= current_block_size;
@@ -1265,17 +1103,20 @@ Session::audible_frame () const
 
                /* MOVING */
 
-               /* check to see if we have passed the first guaranteed
+               /* 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 or changed transport direction.
                */
 
                if (_transport_speed > 0.0f) {
 
                        if (!play_loop || !have_looped) {
-                               if (tf < _last_roll_location + offset) {
-                                       return _last_roll_location;
+                               if (tf < _last_roll_or_reversal_location + offset) {
+                                       return _last_roll_or_reversal_location;
                                }
                        }
 
@@ -1287,8 +1128,8 @@ Session::audible_frame () const
 
                        /* XXX wot? no backward looping? */
 
-                       if (tf > _last_roll_location - offset) {
-                               return _last_roll_location;
+                       if (tf > _last_roll_or_reversal_location - offset) {
+                               return _last_roll_or_reversal_location;
                        } else {
                                /* backwards */
                                ret += offset;
@@ -1300,9 +1141,9 @@ Session::audible_frame () const
 }
 
 void
-Session::set_frame_rate (nframes_t frames_per_second)
+Session::set_frame_rate (framecnt_t frames_per_second)
 {
-       /** \fn void Session::set_frame_size(nframes_t)
+       /** \fn void Session::set_frame_size(framecnt_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
@@ -1313,7 +1154,7 @@ Session::set_frame_rate (nframes_t frames_per_second)
 
        sync_time_vars();
 
-       Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval())));
+       Automatable::set_automation_interval (ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval())));
 
        clear_clicks ();
 
@@ -1326,7 +1167,7 @@ Session::set_frame_rate (nframes_t frames_per_second)
 }
 
 void
-Session::set_block_size (nframes_t nframes)
+Session::set_block_size (pframes_t nframes)
 {
        /* the AudioEngine guarantees
           that it will not be called while we are also in
@@ -1337,12 +1178,7 @@ Session::set_block_size (nframes_t nframes)
        {
                current_block_size = nframes;
 
-               ensure_buffers(_scratch_buffers->available());
-
-               delete [] _gain_automation_buffer;
-               _gain_automation_buffer = new gain_t[nframes];
-
-               allocate_pan_automation_buffers (nframes, _npan_buffers, true);
+               ensure_buffers ();
 
                boost::shared_ptr<RouteList> r = routes.reader ();
 
@@ -1350,61 +1186,30 @@ Session::set_block_size (nframes_t nframes)
                        (*i)->set_block_size (nframes);
                }
 
-               boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-               for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-                       (*i)->set_block_size (nframes);
+               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_block_size (nframes);
+                       }
                }
 
                set_worst_io_latencies ();
        }
 }
 
-void
-Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/)
-{
-#if 0
-       nframes_t fade_frames;
-
-       /* Don't allow fade of less 1 frame */
-
-       if (fade_msecs < (1000.0 * (1.0/_current_frame_rate))) {
-
-               fade_msecs = 0;
-               fade_frames = 0;
-
-       } else {
-
-               fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001);
-
-       }
-
-       default_fade_msecs = fade_msecs;
-       default_fade_steepness = steepness;
-
-       {
-               // jlc, WTF is this!
-               Glib::RWLock::ReaderLock lm (route_lock);
-               AudioRegion::set_default_fade (steepness, fade_frames);
-       }
-
-       set_dirty();
-
-       /* XXX have to do this at some point */
-       /* foreach region using default fade, reset, then
-          refill_all_diskstream_buffers ();
-       */
-#endif
-}
-
 struct RouteSorter {
+    /** @return true to run r1 before r2, otherwise false */
     bool operator() (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> r2) {
-           if (r1->fed_by.find (r2) != r1->fed_by.end()) {
+           if (r2->feeds (r1)) {
+                   /* r1 fed by r2; run r2 early */
                    return false;
-           } else if (r2->fed_by.find (r1) != r2->fed_by.end()) {
+           } else if (r1->feeds (r2)) {
+                   /* r2 fed by r1; run r1 early */
                    return true;
            } else {
-                   if (r1->fed_by.empty()) {
-                           if (r2->fed_by.empty()) {
+                   if (r1->not_fed ()) {
+                           if (r2->not_fed ()) {
                                    /* no ardour-based connections inbound to either route. just use signal order */
                                    return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
                            } else {
@@ -1412,32 +1217,38 @@ struct RouteSorter {
                                    return true;
                            }
                    } else {
-                           return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
+                           if (r2->not_fed()) {
+                                   /* r1 has connections, r2 does not; run r2 early */
+                                   return false;
+                           } else {
+                                   /* both r1 and r2 have connections, but not to each other. just use signal order */
+                                   return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
+                           }
                    }
            }
     }
 };
 
 static void
-trace_terminal (shared_ptr<Route> r1, shared_ptr<Route> rbase)
+trace_terminal (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> rbase)
 {
-       shared_ptr<Route> r2;
+       boost::shared_ptr<Route> r2;
 
-       if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) {
+       if (r1->feeds (rbase) && rbase->feeds (r1)) {
                info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg;
                return;
        }
 
        /* make a copy of the existing list of routes that feed r1 */
 
-       set<weak_ptr<Route> > existing = r1->fed_by;
-
+       Route::FedBy existing (r1->fed_by());
+                        
        /* for each route that feeds r1, recurse, marking it as feeding
           rbase as well.
        */
 
-       for (set<weak_ptr<Route> >::iterator i = existing.begin(); i != existing.end(); ++i) {
-               if (!(r2 = (*i).lock ())) {
+       for (Route::FedBy::iterator i = existing.begin(); i != existing.end(); ++i) {
+               if (!(r2 = i->r.lock ())) {
                        /* (*i) went away, ignore it */
                        continue;
                }
@@ -1446,7 +1257,7 @@ trace_terminal (shared_ptr<Route> r1, shared_ptr<Route> rbase)
                   base as being fed by r2
                */
 
-               rbase->fed_by.insert (r2);
+               rbase->add_fed_by (r2, i->sends_only);
 
                if (r2 != rbase) {
 
@@ -1454,7 +1265,7 @@ trace_terminal (shared_ptr<Route> r1, shared_ptr<Route> rbase)
                           stop here.
                        */
 
-                       if ((r1->fed_by.find (r2) != r1->fed_by.end()) && (r2->fed_by.find (r1) != r2->fed_by.end())) {
+                       if (r1->feeds (r2) && r2->feeds (r1)) {
                                continue;
                        }
 
@@ -1479,24 +1290,40 @@ Session::resort_routes ()
                return;
        }
 
-
        {
-
                RCUWriter<RouteList> writer (routes);
-               shared_ptr<RouteList> r = writer.get_copy ();
+               boost::shared_ptr<RouteList> r = writer.get_copy ();
                resort_routes_using (r);
                /* writer goes out of scope and forces update */
        }
 
+       //route_graph->dump(1);
+
+#ifndef NDEBUG
+       boost::shared_ptr<RouteList> rl = routes.reader ();
+       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
+                
+               const Route::FedBy& fb ((*i)->fed_by());
+
+               for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
+                       boost::shared_ptr<Route> sf = f->r.lock();
+                       if (sf) {
+                               DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+                       }
+               }
+       }
+#endif
+
 }
 void
-Session::resort_routes_using (shared_ptr<RouteList> r)
+Session::resort_routes_using (boost::shared_ptr<RouteList> r)
 {
        RouteList::iterator i, j;
 
        for (i = r->begin(); i != r->end(); ++i) {
 
-               (*i)->fed_by.clear ();
+               (*i)->clear_fed_by ();
 
                for (j = r->begin(); j != r->end(); ++j) {
 
@@ -1510,8 +1337,10 @@ Session::resort_routes_using (shared_ptr<RouteList> r)
                                continue;
                        }
 
-                       if ((*j)->feeds (*i)) {
-                               (*i)->fed_by.insert (*j);
+                       bool via_sends_only;
+
+                       if ((*j)->direct_feeds (*i, &via_sends_only)) {
+                               (*i)->add_fed_by (*j, via_sends_only);
                        }
                }
        }
@@ -1523,133 +1352,130 @@ Session::resort_routes_using (shared_ptr<RouteList> r)
        RouteSorter cmp;
        r->sort (cmp);
 
-#if 0
-       cerr << "finished route resort\n";
+       route_graph->rechain (r);
 
+#ifndef NDEBUG
+       DEBUG_TRACE (DEBUG::Graph, "Routes resorted, order follows:\n");
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl;
+               DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 signal order %2\n", 
+                                                          (*i)->name(), (*i)->order_key ("signal")));
        }
-       cerr << endl;
 #endif
 
 }
 
-list<boost::shared_ptr<MidiTrack> >
-Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many)
+/** Find a route name starting with \a base, maybe followed by the
+ *  lowest \a id.  \a id will always be added if \a definitely_add_number
+ *  is true on entry; otherwise it will only be added if required
+ *  to make the name unique.
+ *
+ *  Names are constructed like e.g. "Audio 3" for base="Audio" and id=3.
+ *  The available route name with the lowest ID will be used, and \a id
+ *  will be set to the ID.
+ *
+ *  \return false if a route name could not be found, and \a track_name
+ *  and \a id do not reflect a free route name.
+ */
+bool
+Session::find_route_name (string const & base, uint32_t& id, char* name, size_t name_len, bool definitely_add_number)
 {
-       char track_name[32];
-       uint32_t track_id = 0;
-       uint32_t n = 0;
-       string port;
-       RouteList new_routes;
-       list<boost::shared_ptr<MidiTrack> > ret;
-       //uint32_t control_id;
+       if (!definitely_add_number && route_by_name (base) == 0) {
+               /* juse use the base */
+               snprintf (name, name_len, "%s", base.c_str());
+               return true;
+       }
+               
+       do {
+               snprintf (name, name_len, "%s %" PRIu32, base.c_str(), id);
 
-       // FIXME: need physical I/O and autoconnect stuff for MIDI
+               if (route_by_name (name) == 0) {
+                       return true;
+               }
 
-       /* count existing midi tracks */
+               ++id;
 
-       {
-               shared_ptr<RouteList> r = routes.reader ();
+       } while (id < (UINT_MAX-1));
 
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if (boost::dynamic_pointer_cast<MidiTrack>(*i) != 0) {
-                               if (!(*i)->is_hidden()) {
-                                       n++;
-                                       //channels_used += (*i)->n_inputs().n_midi();
-                               }
-                       }
-               }
-       }
-
-       vector<string> physinputs;
-       vector<string> physoutputs;
-
-       _engine.get_physical_outputs (DataType::MIDI, physoutputs);
-       _engine.get_physical_inputs (DataType::MIDI, physinputs);
-
-       // control_id = ntracks() + nbusses();
-
-       while (how_many) {
-
-               /* check for duplicate route names, since we might have pre-existing
-                  routes with this name (e.g. create Audio1, Audio2, delete Audio1,
-                  save, close,restart,add new route - first named route is now
-                  Audio2)
-               */
+       return false;
+}
 
+/** Count the total ins and outs of all non-hidden tracks in the session and return them in in and out */
+void
+Session::count_existing_track_channels (ChanCount& in, ChanCount& out)
+{
+       in  = ChanCount::ZERO;
+       out = ChanCount::ZERO;
 
-               do {
-                       ++track_id;
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
-                       snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id);
+       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_hidden()) {
+                        cerr << "Using track i/o counts for " << tr->name() << endl;
+                       in  += tr->n_inputs();
+                       out += tr->n_outputs();
+               }
+       }
+}
 
-                       if (route_by_name (track_name) == 0) {
-                               break;
-                       }
+/** Caller must not hold process lock
+ *  @param name_template string to use for the start of the name, or "" to use "Midi".
+ */
+list<boost::shared_ptr<MidiTrack> >
+Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template)
+{
+       char track_name[32];
+       uint32_t track_id = 0;
+       string port;
+       RouteList new_routes;
+       list<boost::shared_ptr<MidiTrack> > ret;
+       uint32_t control_id;
 
-               } while (track_id < (UINT_MAX-1));
+       control_id = ntracks() + nbusses();
 
-               shared_ptr<MidiTrack> track;
+       bool const use_number = (how_many != 1);
+       
+       while (how_many) {
+               if (!find_route_name (name_template.empty() ? _("Midi") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) {
+                       error << "cannot find name for new midi track" << endmsg;
+                       goto failed;
+               }
 
+               boost::shared_ptr<MidiTrack> track;
+                
                try {
-                       track = boost::shared_ptr<MidiTrack>((new MidiTrack (*this, track_name, Route::Flag (0), mode)));
-
-                       if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
-                               error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
-                               goto failed;
-                       }
+                       track.reset (new MidiTrack (*this, track_name, Route::Flag (0), mode));
 
-
-                       if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
-                               error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+                       if (track->init ()) {
                                goto failed;
                        }
 
-                       /*
-                       if (nphysical_in) {
-                               for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) {
-
-                                       port = "";
-
-                                       if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                               port = physinputs[(channels_used+x)%nphysical_in];
-                                       }
-
-                                       if (port.length() && track->connect_input (track->input (x), port, this)) {
-                                               break;
-                                       }
-                               }
-                       }
-
-                       for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
+                       track->use_new_diskstream();
 
-                               port = "";
-
-                               if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
-                                       port = physoutputs[(channels_used+x)%nphysical_out];
-                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                       if (_master_out) {
-                                               port = _master_out->input (x%_master_out->n_inputs().n_midi())->name();
-                                       }
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+                       boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+#endif
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               if (track->input()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+                                       error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+                                       goto failed;
                                }
 
-                               if (port.length() && track->connect_output (track->output (x), port, this)) {
-                                       break;
+                               if (track->output()->ensure_io (ChanCount(DataType::MIDI, 1), false, this)) {
+                                       error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+                                       goto failed;
                                }
                        }
 
-                       channels_used += track->n_inputs ().n_midi();
-
-                       */
+                       track->non_realtime_input_change();
 
-                       track->midi_diskstream()->non_realtime_input_change();
                        if (route_group) {
                                route_group->add (track);
                        }
 
-                       track->DiskstreamChanged.connect (*this, boost::bind (&Session::resort_routes, this));
-                       //track->set_remote_control_id (control_id);
+                       track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
+                       track->set_remote_control_id (control_id);
 
                        new_routes.push_back (track);
                        ret.push_back (track);
@@ -1657,36 +1483,12 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
 
                catch (failed_constructor &err) {
                        error << _("Session: could not create new midi track.") << endmsg;
-
-                       if (track) {
-                               /* we need to get rid of this, since the track failed to be created */
-                               /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
-
-                               {
-                                       RCUWriter<DiskstreamList> writer (diskstreams);
-                                       boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
-                                       ds->remove (track->midi_diskstream());
-                               }
-                       }
-
                        goto failed;
                }
 
                catch (AudioEngine::PortRegistrationFailure& pfe) {
 
-                       error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
-
-                       if (track) {
-                               /* we need to get rid of this, since the track failed to be created */
-                               /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */
-
-                               {
-                                       RCUWriter<DiskstreamList> writer (diskstreams);
-                                       boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
-                                       ds->remove (track->midi_diskstream());
-                               }
-                       }
-
+                       error << string_compose (_("No more JACK ports are available. You will need to stop %1 and restart JACK with ports if you need this many tracks."), PROGRAM_NAME) << endmsg;
                        goto failed;
                }
 
@@ -1695,135 +1497,186 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
 
   failed:
        if (!new_routes.empty()) {
-               add_routes (new_routes, false);
+               add_routes (new_routes, true, false);
                save_state (_current_snapshot_name);
        }
 
        return ret;
 }
 
-list<boost::shared_ptr<AudioTrack> >
-Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group,  uint32_t how_many)
+/** @param connect_inputs true to connect inputs as well as outputs, false to connect just outputs.
+ *  @param input_start Where to start from when auto-connecting inputs; e.g. if this is 0, auto-connect starting from input 0.
+ *  @param output_start As \a input_start, but for outputs.
+ */
+void
+Session::auto_connect_route (Route* route, ChanCount& existing_inputs, ChanCount& existing_outputs, 
+                             bool with_lock, bool connect_inputs, ChanCount input_start, ChanCount output_start)
 {
-       char track_name[32];
-       uint32_t track_id = 0;
-       uint32_t n = 0;
-       uint32_t channels_used = 0;
-       string port;
-       RouteList new_routes;
-       list<boost::shared_ptr<AudioTrack> > ret;
-       uint32_t control_id;
-
-       /* count existing audio tracks */
+       if (!IO::connecting_legal) {
+                cerr << "Auto-connect ignored because connecting it not legal\n";
+               return;
+       }
 
-       {
-               shared_ptr<RouteList> r = routes.reader ();
+       Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock (), Glib::NOT_LOCK);
 
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if (boost::dynamic_pointer_cast<AudioTrack>(*i) != 0) {
-                               if (!(*i)->is_hidden()) {
-                                       n++;
-                                       channels_used += (*i)->n_inputs().n_audio();
-                               }
-                       }
-               }
+       if (with_lock) {
+               lm.acquire ();
        }
 
-       vector<string> physinputs;
-       vector<string> physoutputs;
+       /* If both inputs and outputs are auto-connected to physical ports,
+          use the max of input and output offsets to ensure auto-connected
+          port numbers always match up (e.g. the first audio input and the
+          first audio output of the route will have the same physical
+          port number).  Otherwise just use the lowest input or output
+          offset possible.
+       */
 
-       _engine.get_physical_outputs (DataType::AUDIO, physoutputs);
-       _engine.get_physical_inputs (DataType::AUDIO, physinputs);
+       DEBUG_TRACE (DEBUG::Graph,
+                    string_compose("Auto-connect: existing in = %1 out = %2\n",
+                                   existing_inputs, existing_outputs));
 
-       control_id = ntracks() + nbusses() + 1;
+       const bool in_out_physical =
+               (Config->get_input_auto_connect() & AutoConnectPhysical)
+               && (Config->get_output_auto_connect() & AutoConnectPhysical)
+               && connect_inputs;
 
-       while (how_many) {
+       const ChanCount in_offset = in_out_physical
+               ? ChanCount::max(existing_inputs, existing_outputs)
+                : existing_inputs;
 
-               /* check for duplicate route names, since we might have pre-existing
-                  routes with this name (e.g. create Audio1, Audio2, delete Audio1,
-                  save, close,restart,add new route - first named route is now
-                  Audio2)
-               */
+       const ChanCount out_offset = in_out_physical
+               ? ChanCount::max(existing_inputs, existing_outputs)
+               : existing_outputs;
 
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               vector<string> physinputs;
+               vector<string> physoutputs;
 
-               do {
-                       ++track_id;
+               _engine.get_physical_outputs (*t, physoutputs);
+               _engine.get_physical_inputs (*t, physinputs);
 
-                       snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id);
+               if (!physinputs.empty() && connect_inputs) {
+                       uint32_t nphysical_in = physinputs.size();
 
-                       if (route_by_name (track_name) == 0) {
-                               break;
-                       }
+                       DEBUG_TRACE (DEBUG::Graph,
+                                    string_compose("There are %1 physical inputs of type %2\n",
+                                                   nphysical_in, *t));
 
-               } while (track_id < (UINT_MAX-1));
+                       for (uint32_t i = input_start.get(*t); i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
+                               string port;
 
-               shared_ptr<AudioTrack> track;
+                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+                                       DEBUG_TRACE (DEBUG::Graph,
+                                                    string_compose("Get index %1 + %2 % %3 = %4\n",
+                                                                   in_offset.get(*t), i, nphysical_in,
+                                                                   (in_offset.get(*t) + i) % nphysical_in));
+                                       port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
+                               }
 
-               try {
-                       AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode);
-                       boost_debug_shared_ptr_mark_interesting (at, typeid (at).name());
-                       track = boost::shared_ptr<AudioTrack>(at);
-
-                       if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
-                               error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
-                                                        input_channels, output_channels)
-                                     << endmsg;
-                               goto failed;
-                       }
+                               DEBUG_TRACE (DEBUG::Graph,
+                                            string_compose("Connect route %1 IN to %2\n",
+                                                           route->name(), port));
 
-                       if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
-                               error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
-                                                        input_channels, output_channels)
-                                     << endmsg;
-                               goto failed;
+                               if (!port.empty() && route->input()->connect (
+                                           route->input()->ports().port(*t, i), port, this)) {
+                                       break;
+                               }
                        }
+               }
 
-                       if (!physinputs.empty()) {
-                               uint32_t nphysical_in = physinputs.size();
-
-                               for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) {
-
-                                       port = "";
+               if (!physoutputs.empty()) {
+                       uint32_t nphysical_out = physoutputs.size();
+                       for (uint32_t i = output_start.get(*t); i < route->n_outputs().get(*t); ++i) {
+                               string port;
 
-                                       if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                               port = physinputs[(channels_used+x)%nphysical_in];
+                               if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+                                       port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
+                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+                                       if (_master_out && _master_out->n_inputs().get(*t) > 0) {
+                                               port = _master_out->input()->ports().port(*t,
+                                                               i % _master_out->input()->n_ports().get(*t))->name();
                                        }
+                               }
 
-                                       if (port.length() && track->input()->connect (track->input()->nth(x), port, this)) {
-                                               break;
-                                       }
+                               DEBUG_TRACE (DEBUG::Graph,
+                                            string_compose("Connect route %1 OUT to %2\n",
+                                                           route->name(), port));
+
+                               if (!port.empty() && route->output()->connect (
+                                               route->output()->ports().port(*t, i), port, this)) {
+                                       break;
                                }
                        }
+               }
+       }
+}
+
+/** Caller must not hold process lock
+ *  @param name_template string to use for the start of the name, or "" to use "Audio".
+ */
+list< boost::shared_ptr<AudioTrack> >
+Session::new_audio_track (
+       int input_channels, int output_channels, TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template
+       )
+{
+       char track_name[32];
+       uint32_t track_id = 0;
+       string port;
+       RouteList new_routes;
+       list<boost::shared_ptr<AudioTrack> > ret;
+       uint32_t control_id;
 
-                       if (!physoutputs.empty()) {
-                               uint32_t nphysical_out = physoutputs.size();
+       control_id = ntracks() + nbusses() + 1;
 
-                               for (uint32_t x = 0; x < track->n_outputs().n_audio(); ++x) {
-                                       port = "";
+       bool const use_number = (how_many != 1);
+       
+       while (how_many) {
+               if (!find_route_name (name_template.empty() ? _("Audio") : name_template, ++track_id, track_name, sizeof(track_name), use_number)) {
+                       error << "cannot find name for new audio track" << endmsg;
+                       goto failed;
+               }
 
-                                       if (Config->get_output_auto_connect() & AutoConnectPhysical) {
-                                               port = physoutputs[(channels_used+x)%nphysical_out];
-                                       } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                               if (_master_out && _master_out->n_inputs().n_audio() > 0) {
-                                                       port = _master_out->input()->nth (x % _master_out->input()->n_ports().n_audio())->name();
-                                               }
-                                       }
+               boost::shared_ptr<AudioTrack> track;
+                
+               try {
+                       track.reset (new AudioTrack (*this, track_name, Route::Flag (0), mode));
 
-                                       if (port.length() && track->output()->connect (track->output()->nth(x), port, this)) {
-                                               break;
-                                       }
-                               }
+                       if (track->init ()) {
+                               goto failed;
                        }
 
-                       channels_used += track->n_inputs ().n_audio();
+                       track->use_new_diskstream();
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+                       boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
+#endif
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
+                               if (track->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
+                                       error << string_compose (
+                                               _("cannot configure %1 in/%2 out configuration for new audio track"),
+                                               input_channels, output_channels)
+                                             << endmsg;
+                                       goto failed;
+                               }
+                               
+                               if (track->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
+                                       error << string_compose (
+                                               _("cannot configure %1 in/%2 out configuration for new audio track"),
+                                               input_channels, output_channels)
+                                             << endmsg;
+                                       goto failed;
+                               }
+                       }
 
                        if (route_group) {
                                route_group->add (track);
                        }
 
-                       track->audio_diskstream()->non_realtime_input_change();
+                       track->non_realtime_input_change();
 
-                       track->DiskstreamChanged.connect (*this, boost::bind (&Session::resort_routes, this));
+                       track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
                        track->set_remote_control_id (control_id);
                        ++control_id;
 
@@ -1833,36 +1686,12 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
                catch (failed_constructor &err) {
                        error << _("Session: could not create new audio track.") << endmsg;
-
-                       if (track) {
-                               /* we need to get rid of this, since the track failed to be created */
-                               /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
-
-                               {
-                                       RCUWriter<DiskstreamList> writer (diskstreams);
-                                       boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
-                                       ds->remove (track->audio_diskstream());
-                               }
-                       }
-
                        goto failed;
                }
 
                catch (AudioEngine::PortRegistrationFailure& pfe) {
 
                        error << pfe.what() << endmsg;
-
-                       if (track) {
-                               /* we need to get rid of this, since the track failed to be created */
-                               /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
-
-                               {
-                                       RCUWriter<DiskstreamList> writer (diskstreams);
-                                       boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
-                                       ds->remove (track->audio_diskstream());
-                               }
-                       }
-
                        goto failed;
                }
 
@@ -1871,7 +1700,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
   failed:
        if (!new_routes.empty()) {
-               add_routes (new_routes, true);
+               add_routes (new_routes, true, true);
        }
 
        return ret;
@@ -1881,134 +1710,86 @@ void
 Session::set_remote_control_ids ()
 {
        RemoteModel m = Config->get_remote_model();
+       bool emit_signal = false;
 
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if ( MixerOrdered == m) {
-                       long order = (*i)->order_key(N_("signal"));
-                       (*i)->set_remote_control_id( order+1 );
-               } else if ( EditorOrdered == m) {
-                       long order = (*i)->order_key(N_("editor"));
-                       (*i)->set_remote_control_id( order+1 );
-               } else if ( UserOrdered == m) {
+               if (MixerOrdered == m) {
+                       int32_t order = (*i)->order_key(N_("signal"));
+                       (*i)->set_remote_control_id (order+1, false);
+                       emit_signal = true;
+               } else if (EditorOrdered == m) {
+                       int32_t order = (*i)->order_key(N_("editor"));
+                       (*i)->set_remote_control_id (order+1, false);
+                       emit_signal = true;
+               } else if (UserOrdered == m) {
                        //do nothing ... only changes to remote id's are initiated by user
                }
        }
-}
 
+       if (emit_signal) {
+               Route::RemoteControlIDChange();
+       }
+}
 
+/** Caller must not hold process lock.
+ *  @param name_template string to use for the start of the name, or "" to use "Bus".
+ */
 RouteList
-Session::new_audio_route (bool aux, int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many)
+Session::new_audio_route (int input_channels, int output_channels, RouteGroup* route_group, uint32_t how_many, string name_template)
 {
        char bus_name[32];
-       uint32_t bus_id = 1;
-       uint32_t n = 0;
-       uint32_t channels_used = 0;
+       uint32_t bus_id = 0;
        string port;
        RouteList ret;
        uint32_t control_id;
 
-       /* count existing audio busses */
-
-       {
-               shared_ptr<RouteList> r = routes.reader ();
-
-               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if (boost::dynamic_pointer_cast<Track>(*i) == 0) {
-                               /* its a bus ? */
-                               if (!(*i)->is_hidden() && (*i)->name() != _("master")) {
-                                       bus_id++;
-                                       n++;
-                                       channels_used += (*i)->n_inputs().n_audio();
-                               }
-                       }
-               }
-       }
-
-       vector<string> physinputs;
-       vector<string> physoutputs;
-
-       _engine.get_physical_outputs (DataType::AUDIO, physoutputs);
-       _engine.get_physical_inputs (DataType::AUDIO, physinputs);
-
-       n_physical_audio_outputs = physoutputs.size();
-       n_physical_audio_inputs = physinputs.size();
-
        control_id = ntracks() + nbusses() + 1;
 
+       bool const use_number = (how_many != 1);
        while (how_many) {
-
-               do {
-                       snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id);
-
-                       bus_id++;
-
-                       if (route_by_name (bus_name) == 0) {
-                               break;
-                       }
-
-               } while (bus_id < (UINT_MAX-1));
+               if (!find_route_name (name_template.empty () ? _("Bus") : name_template, ++bus_id, bus_name, sizeof(bus_name), use_number)) {
+                       error << "cannot find name for new audio bus" << endmsg;
+                       goto failure;
+               }
 
                try {
-                       Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO);
-                       boost_debug_shared_ptr_mark_interesting (rt, typeid (rt).name());
-                       shared_ptr<Route> bus (rt);
-
-                       if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
-                               error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
-                                                        input_channels, output_channels)
-                                     << endmsg;
-                               goto failure;
-                       }
+                       boost::shared_ptr<Route> bus (new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO));
 
-
-                       if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
-                               error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
-                                                        input_channels, output_channels)
-                                     << endmsg;
+                       if (bus->init ()) {
                                goto failure;
                        }
 
-                       for (uint32_t x = 0; n_physical_audio_inputs && x < bus->input()->n_ports().n_audio(); ++x) {
-                               port = "";
-
-                               if (Config->get_input_auto_connect() & AutoConnectPhysical) {
-                                       port = physinputs[((n+x)%n_physical_audio_inputs)];
-                               }
-
-                               if (port.length() && bus->input()->connect (bus->input()->nth (x), port, this)) {
-                                       break;
-                               }
-                       }
-
-                       for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs().n_audio(); ++x) {
-                               port = "";
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+                       boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
+#endif
+                       {
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
-                               if (Config->get_output_auto_connect() & AutoConnectPhysical) {
-                                       port = physoutputs[((n+x)%n_physical_outputs)];
-                               } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
-                                       if (_master_out) {
-                                               port = _master_out->input()->nth (x%_master_out->input()->n_ports().n_audio())->name();
-                                       }
+                               if (bus->input()->ensure_io (ChanCount(DataType::AUDIO, input_channels), false, this)) {
+                                       error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+                                                                input_channels, output_channels)
+                                             << endmsg;
+                                       goto failure;
                                }
-
-                               if (port.length() && bus->output()->connect (bus->output()->nth(x), port, this)) {
-                                       break;
+                               
+                               
+                               if (bus->output()->ensure_io (ChanCount(DataType::AUDIO, output_channels), false, this)) {
+                                       error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+                                                                input_channels, output_channels)
+                                             << endmsg;
+                                       goto failure;
                                }
                        }
 
-                       channels_used += bus->n_inputs ().n_audio();
-
                        if (route_group) {
                                route_group->add (bus);
                        }
                        bus->set_remote_control_id (control_id);
                        ++control_id;
 
-                       if (aux) {
-                               bus->add_internal_return ();
-                       }
+                       bus->add_internal_return ();
 
                        ret.push_back (bus);
                }
@@ -2030,7 +1811,7 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou
 
   failure:
        if (!ret.empty()) {
-               add_routes (ret, true);
+               add_routes (ret, true, true);
        }
 
        return ret;
@@ -2044,7 +1825,7 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
        RouteList ret;
        uint32_t control_id;
        XMLTree tree;
-       uint32_t number = 1;
+       uint32_t number = 0;
 
        if (!tree.read (template_path.c_str())) {
                return ret;
@@ -2061,30 +1842,24 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                std::string node_name = IO::name_from_state (*node_copy.children().front());
 
                /* generate a new name by adding a number to the end of the template name */
-
-               do {
-                       snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number);
-
-                       number++;
-
-                       if (route_by_name (name) == 0) {
-                               break;
-                       }
-
-               } while (number < UINT_MAX);
-
-               if (number == UINT_MAX) {
+               if (!find_route_name (node_name.c_str(), ++number, name, sizeof(name), true)) {
                        fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
                        /*NOTREACHED*/
                }
 
-               IO::set_name_in_state (*node_copy.children().front(), name);
+               /* set IO children to use the new name */
+               XMLNodeList const & children = node_copy.children ();
+               for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+                       if ((*i)->name() == IO::state_node_name) {
+                               IO::set_name_in_state (**i, name);
+                       }
+               }
 
                Track::zero_diskstream_id_in_xml (node_copy);
 
                try {
-                       shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
-           
+                       boost::shared_ptr<Route> route (XMLRouteFactory (node_copy, 3000));
+
                        if (route == 0) {
                                error << _("Session: cannot create track/bus from template description") << endmsg;
                                goto out;
@@ -2095,8 +1870,14 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                                   picks up the configuration of the route. During session
                                   loading this normally happens in a different way.
                                */
-                               route->input()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
-                               route->output()->changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
+                               
+                               Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+                               
+                               IOChange change (IOChange::Type (IOChange::ConfigurationChanged | IOChange::ConnectionsChanged));
+                               change.after = route->input()->n_ports();
+                               route->input()->changed (change, this);
+                               change.after = route->output()->n_ports();
+                               route->output()->changed (change, this);
                        }
 
                        route->set_remote_control_id (control_id);
@@ -2120,18 +1901,23 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
 
   out:
        if (!ret.empty()) {
-               add_routes (ret, true);
+               add_routes (ret, true, true);
        }
 
        return ret;
 }
 
 void
-Session::add_routes (RouteList& new_routes, bool save)
+Session::add_routes (RouteList& new_routes, bool auto_connect, bool save)
 {
+        ChanCount existing_inputs;
+        ChanCount existing_outputs;
+        
+        count_existing_track_channels (existing_inputs, existing_outputs);
+
        {
                RCUWriter<RouteList> writer (routes);
-               shared_ptr<RouteList> r = writer.get_copy ();
+               boost::shared_ptr<RouteList> r = writer.get_copy ();
                r->insert (r->end(), new_routes.begin(), new_routes.end());
 
 
@@ -2141,7 +1927,7 @@ Session::add_routes (RouteList& new_routes, bool save)
                   we will resort when done.
                */
 
-               if (!_control_out && IO::connecting_legal) {
+               if (!_monitor_out && IO::connecting_legal) {
                        resort_routes_using (r);
                }
        }
@@ -2149,32 +1935,52 @@ Session::add_routes (RouteList& new_routes, bool save)
        for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
 
                boost::weak_ptr<Route> wpr (*x);
+               boost::shared_ptr<Route> r (*x);
 
-               (*x)->listen_changed.connect (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr));
-               (*x)->solo_changed.connect (*this, boost::bind (&Session::route_solo_changed, this, _1, wpr));
-               (*x)->mute_changed.connect (*this, boost::bind (&Session::route_mute_changed, this, _1));
-               (*x)->output()->changed.connect (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
-               (*x)->processors_changed.connect (*this, boost::bind (&Session::route_processors_changed, this, _1));
-               (*x)->route_group_changed.connect (*this, boost::bind (&Session::route_group_changed, this));
+               r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr));
+               r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
+               r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, _1, wpr));
+               r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1));
+               r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
+               r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
+               r->order_key_changed.connect_same_thread (*this, boost::bind (&Session::route_order_key_changed, this));
 
-               if ((*x)->is_master()) {
-                       _master_out = (*x);
+               if (r->is_master()) {
+                       _master_out = r;
                }
 
-               if ((*x)->is_control()) {
-                       _control_out = (*x);
+               if (r->is_monitor()) {
+                       _monitor_out = r;
+               }
+
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (r);
+               if (tr) {
+                       tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr<Track> (tr)));
+                       track_playlist_changed (boost::weak_ptr<Track> (tr));
+                       tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_track, this));
+
+                       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
+                       if (mt) {
+                               mt->StepEditStatusChange.connect_same_thread (*this, boost::bind (&Session::step_edit_status_change, this, _1));
+                       }
+               }
+        
+               if (auto_connect) {
+
+                       auto_connect_route (r, existing_inputs, existing_outputs, true);
                }
        }
 
-       if (_control_out && IO::connecting_legal) {
+       if (_monitor_out && IO::connecting_legal) {
 
                for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
-                       if ((*x)->is_control() || (*x)->is_master()) {
-                               continue;
+                       if ((*x)->is_monitor()) {
+                               /* relax */
+                       } else if ((*x)->is_master()) {
+                               /* relax */
+                       } else {
+                               (*x)->listen_via_monitor ();
                        }
-                       (*x)->listen_via (_control_out,
-                                         (Config->get_listen_position() == AfterFaderListen ? PostFader : PreFader),
-                                         false, false);
                }
 
                resort_routes ();
@@ -2187,6 +1993,7 @@ Session::add_routes (RouteList& new_routes, bool save)
        }
 
        RouteAdded (new_routes); /* EMIT SIGNAL */
+       Route::RemoteControlIDChange (); /* EMIT SIGNAL */
 }
 
 void
@@ -2195,13 +2002,9 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr<Route> dest)
        boost::shared_ptr<RouteList> r = routes.reader ();
        boost::shared_ptr<Send> s;
 
-       /* only tracks */
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (boost::dynamic_pointer_cast<Track>(*i)) {
-                       if ((s = (*i)->internal_send_for (dest)) != 0) {
-                               s->amp()->gain_control()->set_value (0.0);
-                       }
+               if ((s = (*i)->internal_send_for (dest)) != 0) {
+                       s->amp()->gain_control()->set_value (0.0);
                }
        }
 }
@@ -2212,13 +2015,9 @@ Session::globally_set_send_gains_to_unity (boost::shared_ptr<Route> dest)
        boost::shared_ptr<RouteList> r = routes.reader ();
        boost::shared_ptr<Send> s;
 
-       /* only tracks */
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (boost::dynamic_pointer_cast<Track>(*i)) {
-                       if ((s = (*i)->internal_send_for (dest)) != 0) {
-                               s->amp()->gain_control()->set_value (1.0);
-                       }
+               if ((s = (*i)->internal_send_for (dest)) != 0) {
+                       s->amp()->gain_control()->set_value (1.0);
                }
        }
 }
@@ -2229,27 +2028,22 @@ Session::globally_set_send_gains_from_track(boost::shared_ptr<Route> dest)
        boost::shared_ptr<RouteList> r = routes.reader ();
        boost::shared_ptr<Send> s;
 
-       /* only tracks */
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (boost::dynamic_pointer_cast<Track>(*i)) {
-                       if ((s = (*i)->internal_send_for (dest)) != 0) {
-                               s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value());
-                       }
+               if ((s = (*i)->internal_send_for (dest)) != 0) {
+                       s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value());
                }
        }
 }
 
+/** @param include_buses true to add sends to buses and tracks, false for just tracks */
 void
-Session::globally_add_internal_sends (boost::shared_ptr<Route> dest, Placement p)
+Session::globally_add_internal_sends (boost::shared_ptr<Route> dest, Placement p, bool include_buses)
 {
        boost::shared_ptr<RouteList> r = routes.reader ();
        boost::shared_ptr<RouteList> t (new RouteList);
 
-       /* only send tracks */
-
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (boost::dynamic_pointer_cast<Track>(*i)) {
+               if (include_buses || boost::dynamic_pointer_cast<Track>(*i)) {
                        t->push_back (*i);
                }
        }
@@ -2260,7 +2054,7 @@ Session::globally_add_internal_sends (boost::shared_ptr<Route> dest, Placement p
 void
 Session::add_internal_sends (boost::shared_ptr<Route> dest, Placement p, boost::shared_ptr<RouteList> senders)
 {
-       if (dest->is_control() || dest->is_master()) {
+       if (dest->is_monitor() || dest->is_master()) {
                return;
        }
 
@@ -2270,47 +2064,28 @@ Session::add_internal_sends (boost::shared_ptr<Route> dest, Placement p, boost::
 
        for (RouteList::iterator i = senders->begin(); i != senders->end(); ++i) {
 
-               if ((*i)->is_control() || (*i)->is_master() || (*i) == dest) {
+               if ((*i)->is_monitor() || (*i)->is_master() || (*i) == dest) {
                        continue;
                }
 
-               (*i)->listen_via (dest, p, true, true);
+               (*i)->listen_via (dest, p);
        }
 
        graph_reordered ();
 }
 
 void
-Session::add_diskstream (boost::shared_ptr<Diskstream> dstream)
+Session::remove_route (boost::shared_ptr<Route> route)
 {
-       /* need to do this in case we're rolling at the time, to prevent false underruns */
-       dstream->do_refill_with_alloc ();
-
-       dstream->set_block_size (current_block_size);
-
-       {
-               RCUWriter<DiskstreamList> writer (diskstreams);
-               boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
-               ds->push_back (dstream);
-               /* writer goes out of scope, copies ds back to main */
+       if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) {
+               return;
        }
 
-       dstream->PlaylistChanged.connect (*this, boost::bind (&Session::diskstream_playlist_changed, this, boost::weak_ptr<Diskstream> (dstream)));
-       /* this will connect to future changes, and check the current length */
-       diskstream_playlist_changed (boost::weak_ptr<Diskstream> (dstream));
-
-       dstream->RecordEnableChanged.connect (*this, boost::bind (&Session::update_have_rec_enabled_diskstream, this));
+       route->set_solo (false, this);
 
-       dstream->prepare ();
-
-}
-
-void
-Session::remove_route (shared_ptr<Route> route)
-{
        {
                RCUWriter<RouteList> writer (routes);
-               shared_ptr<RouteList> rs = writer.get_copy ();
+               boost::shared_ptr<RouteList> rs = writer.get_copy ();
 
                rs->remove (route);
 
@@ -2320,44 +2095,26 @@ Session::remove_route (shared_ptr<Route> route)
                */
 
                if (route == _master_out) {
-                       _master_out = shared_ptr<Route> ();
+                       _master_out = boost::shared_ptr<Route> ();
                }
 
-               if (route == _control_out) {
+               if (route == _monitor_out) {
 
                        /* cancel control outs for all routes */
 
                        for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) {
-                               (*r)->drop_listen (_control_out);
+                               (*r)->drop_listen (_monitor_out);
                        }
 
-                       _control_out.reset ();
+                       _monitor_out.reset ();
                }
 
-               update_route_solo_state ();
-
                /* writer goes out of scope, forces route list update */
        }
 
-       boost::shared_ptr<Track> t;
-       boost::shared_ptr<Diskstream> ds;
-
-       if ((t = boost::dynamic_pointer_cast<Track>(route)) != 0) {
-               ds = t->diskstream();
-       }
-
-       if (ds) {
-
-               {
-                       RCUWriter<DiskstreamList> dsl (diskstreams);
-                       boost::shared_ptr<DiskstreamList> d = dsl.get_copy();
-                       d->remove (ds);
-               }
-       }
-
-       find_current_end ();
+       update_route_solo_state ();
 
-       // We need to disconnect the routes inputs and outputs
+       // We need to disconnect the route's inputs and outputs
 
        route->input()->disconnect (0);
        route->output()->disconnect (0);
@@ -2374,9 +2131,23 @@ Session::remove_route (shared_ptr<Route> route)
                }
        }       
 
-       update_latency_compensation (false, false);
+       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
+       if (mt && mt->step_editing()) {
+               if (_step_editors > 0) {
+                       _step_editors--;
+               }
+       }
+
+       update_latency_compensation ();
        set_dirty();
 
+       /* Re-sort routes to remove the graph's current references to the one that is
+        * going away, then flush old references out of the graph.
+        */
+
+       resort_routes ();
+       route_graph->clear_other_chain ();
+
        /* get rid of it from the dead wood collection in the route list manager */
 
        /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */
@@ -2389,6 +2160,8 @@ Session::remove_route (shared_ptr<Route> route)
 
        sync_order_keys (N_("session"));
 
+       Route::RemoteControlIDChange(); /* EMIT SIGNAL */
+
        /* save the new state of the world */
 
        if (save_state (_current_snapshot_name)) {
@@ -2411,16 +2184,66 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
                return;
        }
 
-       if (route->listening()) {
+       if (route->listening_via_monitor ()) {
+
+               if (Config->get_exclusive_solo()) {
+                       /* new listen: disable all other listen */
+                       boost::shared_ptr<RouteList> r = routes.reader ();
+                       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                               if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
+                                       continue;
+                               } 
+                               (*i)->set_listen (false, this);
+                       }
+               }
+
                _listen_cnt++;
+
        } else if (_listen_cnt > 0) {
+
                _listen_cnt--;
        }
+
+       update_route_solo_state ();
 }
+void
+Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+{
+       boost::shared_ptr<Route> route = wpr.lock ();
+
+       if (!route) {
+               /* should not happen */
+               error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg;
+               return;
+       }
+        
+       bool send_changed = false;
+
+       if (route->solo_isolated()) {
+               if (_solo_isolated_cnt == 0) {
+                       send_changed = true;
+               }
+               _solo_isolated_cnt++;
+       } else if (_solo_isolated_cnt > 0) {
+               _solo_isolated_cnt--;
+               if (_solo_isolated_cnt == 0) {
+                       send_changed = true;
+               }
+       }
 
+       if (send_changed) {
+               IsolatedChanged (); /* EMIT SIGNAL */
+       }
+}
+            
 void
-Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_change, void* /*src*/, boost::weak_ptr<Route> wpr)
 {
+       if (!self_solo_change) {
+               // session doesn't care about changes to soloed-by-others
+               return;
+       }
+
        if (solo_update_disabled) {
                // We know already
                return;
@@ -2433,8 +2256,8 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
                error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg;
                return;
        }
-
-       shared_ptr<RouteList> r = routes.reader ();
+        
+       boost::shared_ptr<RouteList> r = routes.reader ();
        int32_t delta;
 
        if (route->self_soloed()) {
@@ -2442,40 +2265,61 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
        } else {
                delta = -1;
        }
-
-       /* now mod the solo level of all other routes except master & control outs
-          so that they will be silent if appropriate.
-       */
+       if (delta == 1 && Config->get_exclusive_solo()) {
+               /* new solo: disable all other solos */
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
+                               continue;
+                       } 
+                       (*i)->set_solo (false, this);
+               }
+       }
 
        solo_update_disabled = true;
-
+        
+       RouteList uninvolved;
+        
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                bool via_sends_only;
+               bool in_signal_flow;
 
-
-               if ((*i) == route || !(*i)->solo_isolated() || !(*i)->is_master() || !(*i)->is_control() || (*i)->is_hidden()) {
+               if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_hidden()) {
                        continue;
-               } else if ((*i)->feeds (route, &via_sends_only)) {
+               } 
+
+               in_signal_flow = false;
+
+               if ((*i)->feeds (route, &via_sends_only)) {
                        if (!via_sends_only) {
-                               (*i)->mod_solo_by_others (delta);
+                               if (!route->soloed_by_others_upstream()) {
+                                       (*i)->mod_solo_by_others_downstream (delta);
+                               }
+                               in_signal_flow = true;
                        }
                } 
+                
+               if (route->feeds (*i, &via_sends_only)) {
+                       (*i)->mod_solo_by_others_upstream (delta);
+                       in_signal_flow = true;
+               }
+
+               if (!in_signal_flow) {
+                       uninvolved.push_back (*i);
+               }
        }
 
-       /* make sure master is never muted by solo */
+       solo_update_disabled = false;
+       update_route_solo_state (r);
 
-       if (_master_out && route != _master_out && _master_out->soloed_by_others() == 0 && !_master_out->soloed()) {
-               _master_out->mod_solo_by_others (1);
-       }
-       /* ditto for control outs make sure master is never muted by solo */
+       /* now notify that the mute state of the routes not involved in the signal
+          pathway of the just-solo-changed route may have altered.
+       */
 
-       if (_control_out && route != _control_out && _control_out && _control_out->soloed_by_others() == 0) {
-               _control_out->mod_solo_by_others (1);
+       for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
+               (*i)->mute_changed (this);
        }
 
-       solo_update_disabled = false;
-       update_route_solo_state (r);
        SoloChanged (); /* EMIT SIGNAL */
        set_dirty();
 }
@@ -2483,18 +2327,31 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
 void
 Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
 {
-       /* now figure out if anything that matters is soloed */
+       /* now figure out if anything that matters is soloed (or is "listening")*/
 
        bool something_soloed = false;
+       uint32_t listeners = 0;
+       uint32_t isolated = 0;
 
        if (!r) {
                r = routes.reader();
        }
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->self_soloed()) {
+               if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_hidden() && (*i)->self_soloed()) {
                        something_soloed = true;
-                       break;
+               }
+
+               if (!(*i)->is_hidden() && (*i)->listening_via_monitor()) {
+                       if (Config->get_solo_control_is_listen_control()) {
+                               listeners++;
+                       } else {
+                               (*i)->set_listen (false, this);
+                       }
+               }
+
+               if ((*i)->solo_isolated()) {
+                       isolated++;
                }
        }
 
@@ -2502,12 +2359,19 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                _non_soloed_outs_muted = something_soloed;
                SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
        }
+
+       _listen_cnt = listeners;
+
+       if (isolated != _solo_isolated_cnt) {
+               _solo_isolated_cnt = isolated;
+               IsolatedChanged (); /* EMIT SIGNAL */
+       }
 }
 
 boost::shared_ptr<RouteList> 
 Session::get_routes_with_internal_returns() const
 {
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
        boost::shared_ptr<RouteList> rl (new RouteList);
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
@@ -2518,10 +2382,28 @@ Session::get_routes_with_internal_returns() const
        return rl;
 }
 
-shared_ptr<Route>
+bool
+Session::io_name_is_legal (const std::string& name)
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+        
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               if ((*i)->name() == name) {
+                       return false;
+               }
+                
+               if ((*i)->has_io_processor_named (name)) {
+                       return false;
+               }
+       }
+        
+       return true;
+}
+
+boost::shared_ptr<Route>
 Session::route_by_name (string name)
 {
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->name() == name) {
@@ -2529,13 +2411,13 @@ Session::route_by_name (string name)
                }
        }
 
-       return shared_ptr<Route> ((Route*) 0);
+       return boost::shared_ptr<Route> ((Route*) 0);
 }
 
-shared_ptr<Route>
+boost::shared_ptr<Route>
 Session::route_by_id (PBD::ID id)
 {
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->id() == id) {
@@ -2543,13 +2425,13 @@ Session::route_by_id (PBD::ID id)
                }
        }
 
-       return shared_ptr<Route> ((Route*) 0);
+       return boost::shared_ptr<Route> ((Route*) 0);
 }
 
-shared_ptr<Route>
+boost::shared_ptr<Route>
 Session::route_by_remote_id (uint32_t id)
 {
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->remote_control_id() == id) {
@@ -2557,522 +2439,305 @@ Session::route_by_remote_id (uint32_t id)
                }
        }
 
-       return shared_ptr<Route> ((Route*) 0);
+       return boost::shared_ptr<Route> ((Route*) 0);
 }
 
 void
-Session::find_current_end ()
+Session::playlist_region_added (boost::weak_ptr<Region> w)
 {
-       if (_state_of_the_state & Loading) {
+       boost::shared_ptr<Region> r = w.lock ();
+       if (!r) {
                return;
        }
 
-       nframes_t max = get_maximum_extent ();
-
-       if (max > end_location->end()) {
-               end_location->set_end (max);
-               set_dirty();
-               DurationChanged(); /* EMIT SIGNAL */
-       }
-}
+       /* These are the operations that are currently in progress... */
+       list<GQuark> curr = _current_trans_quarks;
+       curr.sort ();
 
-nframes_t
-Session::get_maximum_extent () const
+       /* ...and these are the operations during which we want to update
+          the session range location markers.
+       */
+       list<GQuark> ops;
+       ops.push_back (Operations::capture);
+       ops.push_back (Operations::paste);
+       ops.push_back (Operations::duplicate_region);
+       ops.push_back (Operations::insert_file);
+       ops.push_back (Operations::insert_region);
+       ops.push_back (Operations::drag_region_brush);
+       ops.push_back (Operations::region_drag);
+       ops.push_back (Operations::selection_grab);
+       ops.push_back (Operations::region_fill);
+       ops.push_back (Operations::fill_selection);
+       ops.push_back (Operations::create_region);
+       ops.sort ();
+
+       /* See if any of the current operations match the ones that we want */
+       list<GQuark> in;
+       set_intersection (_current_trans_quarks.begin(), _current_trans_quarks.end(), ops.begin(), ops.end(), back_inserter (in));
+
+       /* If so, update the session range markers */
+       if (!in.empty ()) {
+               maybe_update_session_range (r->position (), r->last_frame ());
+       }
+}
+
+/** Update the session range markers if a is before the current start or
+ *  b is after the current end.
+ */
+void
+Session::maybe_update_session_range (framepos_t a, framepos_t b)
 {
-       nframes_t max = 0;
-       nframes_t me;
-
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+       if (_state_of_the_state & Loading) {
+               return;
+       }
 
-       for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if ((*i)->destructive())  //ignore tape tracks when getting max extents
-                       continue;
-               boost::shared_ptr<Playlist> pl = (*i)->playlist();
-               if ((me = pl->get_maximum_extent()) > max) {
-                       max = me;
+       if (_session_range_location == 0) {
+               
+               add_session_range_location (a, b);
+               
+       } else {
+               
+               if (a < _session_range_location->start()) {
+                       _session_range_location->set_start (a);
+               }
+               
+               if (b > _session_range_location->end()) {
+                       _session_range_location->set_end (b);
                }
        }
-
-       return max;
 }
 
-boost::shared_ptr<Diskstream>
-Session::diskstream_by_name (string name)
+void
+Session::playlist_ranges_moved (list<Evoral::RangeMove<framepos_t> > const & ranges)
 {
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if ((*i)->name() == name) {
-                       return *i;
-               }
+       for (list<Evoral::RangeMove<framepos_t> >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) {
+               maybe_update_session_range (i->to, i->to + i->length);
        }
-
-       return boost::shared_ptr<Diskstream>((Diskstream*) 0);
 }
 
-boost::shared_ptr<Diskstream>
-Session::diskstream_by_id (const PBD::ID& id)
+void
+Session::playlist_regions_extended (list<Evoral::Range<framepos_t> > const & ranges)
 {
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if ((*i)->id() == id) {
-                       return *i;
-               }
+       for (list<Evoral::Range<framepos_t> >::const_iterator i = ranges.begin(); i != ranges.end(); ++i) {
+               maybe_update_session_range (i->from, i->to);
        }
-
-       return boost::shared_ptr<Diskstream>((Diskstream*) 0);
 }
 
 /* Region management */
 
-string
-Session::new_region_name (string old)
+boost::shared_ptr<Region>
+Session::find_whole_file_parent (boost::shared_ptr<Region const> child) const
 {
-       string::size_type last_period;
-       uint32_t number;
-       string::size_type len = old.length() + 64;
-       char buf[len];
-
-       if ((last_period = old.find_last_of ('.')) == string::npos) {
-
-               /* no period present - add one explicitly */
-
-               old += '.';
-               last_period = old.length() - 1;
-               number = 0;
-
-       } else {
-
-               number = atoi (old.substr (last_period+1).c_str());
-
-       }
+       const RegionFactory::RegionMap& regions (RegionFactory::regions());
+       RegionFactory::RegionMap::const_iterator i;
+       boost::shared_ptr<Region> region;
 
-       while (number < (UINT_MAX-1)) {
+       Glib::Mutex::Lock lm (region_lock);
 
-               RegionList::const_iterator i;
-               string sbuf;
+       for (i = regions.begin(); i != regions.end(); ++i) {
 
-               number++;
+               region = i->second;
 
-               snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number);
-               sbuf = buf;
+               if (region->whole_file()) {
 
-               for (i = regions.begin(); i != regions.end(); ++i) {
-                       if (i->second->name() == sbuf) {
-                               break;
+                       if (child->source_equivalent (region)) {
+                               return region;
                        }
                }
-
-               if (i == regions.end()) {
-                       break;
-               }
-       }
-
-       if (number != (UINT_MAX-1)) {
-               return buf;
        }
 
-       error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg;
-       return old;
+       return boost::shared_ptr<Region> ();
 }
 
 int
-Session::region_name (string& result, string base, bool newlevel)
+Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
 {
-       char buf[16];
-       string subbase;
+       set<boost::shared_ptr<Region> > relevant_regions;
 
-       if (base.find("/") != string::npos) {
-               base = base.substr(base.find_last_of("/") + 1);
+       for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ++s) {
+               RegionFactory::get_regions_using_source (*s, relevant_regions);
        }
 
-       if (base == "") {
+       cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl;
 
-               Glib::Mutex::Lock lm (region_lock);
+       for (set<boost::shared_ptr<Region> >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) {
+               set<boost::shared_ptr<Region> >::iterator tmp;
 
-               snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1);
-               result = "region.";
-               result += buf;
+               tmp = r;
+               ++tmp;
 
-       } else {
+               cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl;
 
-               if (newlevel) {
-                       subbase = base;
-               } else {
-                       string::size_type pos;
+               playlists->destroy_region (*r);
+               RegionFactory::map_remove (*r);
 
-                       pos = base.find_last_of ('.');
+               (*r)->drop_sources ();
+               (*r)->drop_references ();
 
-                       /* pos may be npos, but then we just use entire base */
+               cerr << "\tdone UC = " << (*r).use_count() << endl;
 
-                       subbase = base.substr (0, pos);
+               relevant_regions.erase (r);
 
-               }
+               r = tmp;
+       }
 
+       for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ) {
+                
                {
-                       Glib::Mutex::Lock lm (region_lock);
-
-                       map<string,uint32_t>::iterator x;
-
-                       result = subbase;
+                       Glib::Mutex::Lock ls (source_lock);
+                       /* remove from the main source list */
+                       sources.erase ((*s)->id());
+               }
 
-                       if ((x = region_name_map.find (subbase)) == region_name_map.end()) {
-                               result += ".1";
-                               region_name_map[subbase] = 1;
-                       } else {
-                               x->second++;
-                               snprintf (buf, sizeof (buf), ".%d", x->second);
+               (*s)->mark_for_remove ();
+               (*s)->drop_references ();
 
-                               result += buf;
-                       }
-               }
+               s = srcs.erase (s);
        }
 
        return 0;
 }
 
-void
-Session::add_region (boost::shared_ptr<Region> region)
-{
-       vector<boost::shared_ptr<Region> > v;
-       v.push_back (region);
-       add_regions (v);
-}
-
-void
-Session::add_regions (vector<boost::shared_ptr<Region> >& new_regions)
+int
+Session::remove_last_capture ()
 {
-       bool added = false;
-
-       {
-               Glib::Mutex::Lock lm (region_lock);
-
-               for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
-
-                       boost::shared_ptr<Region> region = *ii;
-
-                       if (region == 0) {
-
-                               error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
-
-                       } else {
-
-                               RegionList::iterator x;
-
-                               for (x = regions.begin(); x != regions.end(); ++x) {
-
-                                       if (region->region_list_equivalent (x->second)) {
-                                               break;
-                                       }
-                               }
+       list<boost::shared_ptr<Source> > srcs;
 
-                               if (x == regions.end()) {
-
-                                       pair<RegionList::key_type,RegionList::mapped_type> entry;
-
-                                       entry.first = region->id();
-                                       entry.second = region;
-
-                                       pair<RegionList::iterator,bool> x = regions.insert (entry);
-
-                                       if (!x.second) {
-                                               return;
-                                       }
+       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();
 
-                                       added = true;
-                               }
-                       }
+               if (!l.empty()) {
+                       srcs.insert (srcs.end(), l.begin(), l.end());
+                       l.clear ();
                }
        }
 
-       /* mark dirty because something has changed even if we didn't
-          add the region to the region list.
-       */
-
-       set_dirty ();
+       destroy_sources (srcs);
 
-       if (added) {
+       save_state (_current_snapshot_name);
 
-               vector<boost::weak_ptr<Region> > v;
-               boost::shared_ptr<Region> first_r;
+       return 0;
+}
 
-               for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
+/* Source Management */
 
-                       boost::shared_ptr<Region> region = *ii;
+void
+Session::add_source (boost::shared_ptr<Source> source)
+{
+       pair<SourceMap::key_type, SourceMap::mapped_type> entry;
+       pair<SourceMap::iterator,bool> result;
 
-                       if (region == 0) {
+       entry.first = source->id();
+       entry.second = source;
 
-                               error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
+       {
+               Glib::Mutex::Lock lm (source_lock);
+               result = sources.insert (entry);
+       }
 
-                       } else {
-                               v.push_back (region);
+       if (result.second) {
 
-                               if (!first_r) {
-                                       first_r = region;
-                               }
-                       }
+               /* yay, new source */
 
-                       region->StateChanged.connect (*this, boost::bind (&Session::region_changed, this, _1, boost::weak_ptr<Region>(region)));
-                       region->GoingAway.connect (*this, boost::bind (&Session::remove_region, this, boost::weak_ptr<Region>(region)));
+               set_dirty();
 
-                       update_region_name_map (region);
+               boost::shared_ptr<AudioFileSource> afs;
+                
+               if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
+                       if (Config->get_auto_analyse_audio()) {
+                               Analyser::queue_source_for_analysis (source, false);
+                       }
                }
 
-               if (!v.empty()) {
-                       RegionsAdded (v); /* EMIT SIGNAL */
-               }
+               source->DropReferences.connect_same_thread (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr<Source> (source)));
        }
 }
 
 void
-Session::update_region_name_map (boost::shared_ptr<Region> region)
+Session::remove_source (boost::weak_ptr<Source> src)
 {
-       string::size_type last_period = region->name().find_last_of ('.');
+       if (_state_of_the_state & Deletion) {
+               return;
+       }
+       
+       SourceMap::iterator i;
+       boost::shared_ptr<Source> source = src.lock();
+
+       if (!source) {
+               return;
+       }
+
+       {
+               Glib::Mutex::Lock lm (source_lock);
 
-       if (last_period != string::npos && last_period < region->name().length() - 1) {
+               if ((i = sources.find (source->id())) != sources.end()) {
+                       sources.erase (i);
+               }
+       }
 
-               string base = region->name().substr (0, last_period);
-               string number = region->name().substr (last_period+1);
-               map<string,uint32_t>::iterator x;
+       if (!_state_of_the_state & InCleanup) {
 
-               /* note that if there is no number, we get zero from atoi,
-                  which is just fine
+               /* save state so we don't end up with a session file
+                  referring to non-existent sources.
                */
 
-               region_name_map[base] = atoi (number);
+               save_state (_current_snapshot_name);
        }
 }
 
-void
-Session::region_changed (Change what_changed, boost::weak_ptr<Region> weak_region)
+boost::shared_ptr<Source>
+Session::source_by_id (const PBD::ID& id)
 {
-       boost::shared_ptr<Region> region (weak_region.lock ());
-
-       if (!region) {
-               return;
-       }
+       Glib::Mutex::Lock lm (source_lock);
+       SourceMap::iterator i;
+       boost::shared_ptr<Source> source;
 
-       if (what_changed & Region::HiddenChanged) {
-               /* relay hidden changes */
-               RegionHiddenChange (region);
+       if ((i = sources.find (id)) != sources.end()) {
+               source = i->second;
        }
 
-       if (what_changed & NameChanged) {
-               update_region_name_map (region);
-       }
+       return source;
 }
 
-void
-Session::remove_region (boost::weak_ptr<Region> weak_region)
+boost::shared_ptr<Source>
+Session::source_by_path_and_channel (const string& path, uint16_t chn)
 {
-       RegionList::iterator i;
-       boost::shared_ptr<Region> region (weak_region.lock ());
+       Glib::Mutex::Lock lm (source_lock);
 
-       if (!region) {
-               return;
+       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+               boost::shared_ptr<AudioFileSource> afs
+                       = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
+
+               if (afs && afs->path() == path && chn == afs->channel()) {
+                       return afs;
+               }
        }
+       return boost::shared_ptr<Source>();
+}
 
-       bool removed = false;
+uint32_t
+Session::count_sources_by_origin (const string& path)
+{
+       uint32_t cnt = 0;
+       Glib::Mutex::Lock lm (source_lock);
 
-       {
-               Glib::Mutex::Lock lm (region_lock);
+       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+               boost::shared_ptr<FileSource> fs
+                       = boost::dynamic_pointer_cast<FileSource>(i->second);
 
-               if ((i = regions.find (region->id())) != regions.end()) {
-                       regions.erase (i);
-                       removed = true;
+               if (fs && fs->origin() == path) {
+                       ++cnt;
                }
        }
 
-       /* mark dirty because something has changed even if we didn't
-          remove the region from the region list.
-       */
-
-       set_dirty();
-
-       if (removed) {
-                RegionRemoved(region); /* EMIT SIGNAL */
-       }
-}
-
-boost::shared_ptr<Region>
-Session::find_whole_file_parent (boost::shared_ptr<Region const> child)
-{
-       RegionList::iterator i;
-       boost::shared_ptr<Region> region;
-
-       Glib::Mutex::Lock lm (region_lock);
-
-       for (i = regions.begin(); i != regions.end(); ++i) {
-
-               region = i->second;
-
-               if (region->whole_file()) {
-
-                       if (child->source_equivalent (region)) {
-                               return region;
-                       }
-               }
-       }
-
-       return boost::shared_ptr<Region> ();
-}
-
-int
-Session::destroy_region (boost::shared_ptr<Region> region)
-{
-       vector<boost::shared_ptr<Source> > srcs;
-
-       {
-               if (region->playlist()) {
-                       region->playlist()->destroy_region (region);
-               }
-
-               for (uint32_t n = 0; n < region->n_channels(); ++n) {
-                       srcs.push_back (region->source (n));
-               }
-       }
-
-       region->drop_references ();
-
-       for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
-
-                       (*i)->mark_for_remove ();
-                       (*i)->drop_references ();
-
-                       cerr << "source was not used by any playlist\n";
-       }
-
-       return 0;
-}
-
-int
-Session::destroy_regions (list<boost::shared_ptr<Region> > regions)
-{
-       for (list<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
-               destroy_region (*i);
-       }
-       return 0;
-}
-
-int
-Session::remove_last_capture ()
-{
-       list<boost::shared_ptr<Region> > r;
-
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               list<boost::shared_ptr<Region> >& l = (*i)->last_capture_regions();
-
-               if (!l.empty()) {
-                       r.insert (r.end(), l.begin(), l.end());
-                       l.clear ();
-               }
-       }
-
-       destroy_regions (r);
-
-       save_state (_current_snapshot_name);
-
-       return 0;
-}
-
-int
-Session::remove_region_from_region_list (boost::shared_ptr<Region> r)
-{
-       remove_region (r);
-       return 0;
-}
-
-/* Source Management */
-
-void
-Session::add_source (boost::shared_ptr<Source> source)
-{
-       pair<SourceMap::key_type, SourceMap::mapped_type> entry;
-       pair<SourceMap::iterator,bool> result;
-
-       entry.first = source->id();
-       entry.second = source;
-
-       {
-               Glib::Mutex::Lock lm (source_lock);
-               result = sources.insert (entry);
-       }
-
-       if (result.second) {
-               source->GoingAway.connect (*this, boost::bind (&Session::remove_source, this, boost::weak_ptr<Source> (source)));
-               set_dirty();
-       }
-
-       boost::shared_ptr<AudioFileSource> afs;
-
-       if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
-               if (Config->get_auto_analyse_audio()) {
-                       Analyser::queue_source_for_analysis (source, false);
-               }
-       }
-}
-
-void
-Session::remove_source (boost::weak_ptr<Source> src)
-{
-       SourceMap::iterator i;
-       boost::shared_ptr<Source> source = src.lock();
-
-       if (!source) {
-               return;
-       }
-
-       {
-               Glib::Mutex::Lock lm (source_lock);
-
-               if ((i = sources.find (source->id())) != sources.end()) {
-                       sources.erase (i);
-               }
-       }
-
-       if (!_state_of_the_state & InCleanup) {
-
-               /* save state so we don't end up with a session file
-                  referring to non-existent sources.
-               */
-
-               save_state (_current_snapshot_name);
-       }
-}
-
-boost::shared_ptr<Source>
-Session::source_by_id (const PBD::ID& id)
-{
-       Glib::Mutex::Lock lm (source_lock);
-       SourceMap::iterator i;
-       boost::shared_ptr<Source> source;
-
-       if ((i = sources.find (id)) != sources.end()) {
-               source = i->second;
-       }
-
-       return source;
-}
-
-boost::shared_ptr<Source>
-Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
-{
-       Glib::Mutex::Lock lm (source_lock);
-
-       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
-               cerr << "comparing " << path << " with " << i->second->name() << endl;
-               boost::shared_ptr<AudioFileSource> afs
-                       = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
-
-               if (afs && afs->path() == path && chn == afs->channel()) {
-                       return afs;
-               }
-       }
-       return boost::shared_ptr<Source>();
-}
+       return cnt;
+}
 
 
 string
@@ -3093,18 +2758,12 @@ Session::change_source_path_by_name (string path, string oldname, string newname
                    the task here is to replace NAME with the new name.
                */
 
-               /* find last slash */
-
                string dir;
                string prefix;
-               string::size_type slash;
                string::size_type dash;
 
-               if ((slash = path.find_last_of ('/')) == string::npos) {
-                       return "";
-               }
-
-               dir = path.substr (0, slash+1);
+               dir = Glib::path_get_dirname (path);
+               path = Glib::path_get_basename (path);
 
                /* '-' is not a legal character for the NAME part of the path */
 
@@ -3112,13 +2771,13 @@ Session::change_source_path_by_name (string path, string oldname, string newname
                        return "";
                }
 
-               prefix = path.substr (slash+1, dash-(slash+1));
+               prefix = path.substr (0, dash);
 
-               path = dir;
                path += prefix;
                path += '-';
                path += new_legalized;
-               path += ".wav";  /* XXX gag me with a spoon */
+               path += native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+               path = Glib::build_filename (dir, path);
 
        } else {
 
@@ -3131,17 +2790,11 @@ Session::change_source_path_by_name (string path, string oldname, string newname
 
                string dir;
                string suffix;
-               string::size_type slash;
                string::size_type dash;
                string::size_type postfix;
 
-               /* find last slash */
-
-               if ((slash = path.find_last_of ('/')) == string::npos) {
-                       return "";
-               }
-
-               dir = path.substr (0, slash+1);
+               dir = Glib::path_get_dirname (path);
+               path = Glib::path_get_basename (path);
 
                /* '-' is not a legal character for the NAME part of the path */
 
@@ -3171,19 +2824,21 @@ Session::change_source_path_by_name (string path, string oldname, string newname
 
                for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
 
-                       snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
+                       snprintf (buf, sizeof(buf), "%s-%u%s", newname.c_str(), cnt, suffix.c_str());
 
-                       if (access (buf, F_OK) != 0) {
-                               path = buf;
+                       if (!matching_unsuffixed_filename_exists_in (dir, buf)) {
+                               path = Glib::build_filename (dir, buf);
                                break;
                        }
+
                        path = "";
                }
 
-               if (path == "") {
-                       error << "FATAL ERROR! Could not find a " << endl;
+               if (path.empty()) {
+                       fatal << string_compose (_("FATAL ERROR! Could not find a suitable version of %1 for a rename"),
+                                                newname) << endl;
+                       /*NOTREACHED*/
                }
-
        }
 
        return path;
@@ -3214,11 +2869,11 @@ Session::new_source_path_from_name (DataType type, const string& name)
        return p.to_string();
 }
 
-Glib::ustring
-Session::peak_path (Glib::ustring base) const
+string
+Session::peak_path (string base) const
 {
        sys::path peakfile_path(_session_dir->peak_path());
-       peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
+       peakfile_path /= base + peakfile_suffix;
        return peakfile_path.to_string();
 }
 
@@ -3226,11 +2881,11 @@ Session::peak_path (Glib::ustring base) const
 string
 Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
 {
-       string spath;
        uint32_t cnt;
        char buf[PATH_MAX+1];
        const uint32_t limit = 10000;
        string legalized;
+       string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
 
        buf[0] = '\0';
        legalized = legalize_for_path (base);
@@ -3243,55 +2898,58 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
 
                for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
-                       SessionDirectory sdir((*i).path);
-
-                       spath = sdir.sound_path().to_string();
-
                        if (destructive) {
 
                                if (nchan < 2) {
-                                       snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
-                                                       spath.c_str(), cnt, legalized.c_str());
+                                       snprintf (buf, sizeof(buf), "T%04d-%s%s",
+                                                 cnt, legalized.c_str(), ext.c_str());
                                } else if (nchan == 2) {
                                        if (chan == 0) {
-                                               snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav",
-                                                               spath.c_str(), cnt, legalized.c_str());
+                                               snprintf (buf, sizeof(buf), "T%04d-%s%%L%s",
+                                                         cnt, legalized.c_str(), ext.c_str());
                                        } else {
-                                               snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav",
-                                                               spath.c_str(), cnt, legalized.c_str());
+                                               snprintf (buf, sizeof(buf), "T%04d-%s%%R%s",
+                                                         cnt, legalized.c_str(), ext.c_str());
                                        }
                                } else if (nchan < 26) {
-                                       snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav",
-                                                       spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
+                                       snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s",
+                                                 cnt, legalized.c_str(), 'a' + chan, ext.c_str());
                                } else {
-                                       snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
-                                                       spath.c_str(), cnt, legalized.c_str());
+                                       snprintf (buf, sizeof(buf), "T%04d-%s%s",
+                                                 cnt, legalized.c_str(), ext.c_str());
                                }
 
                        } else {
 
-                               spath += '/';
-                               spath += legalized;
-
                                if (nchan < 2) {
-                                       snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt);
+                                       snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
                                } else if (nchan == 2) {
                                        if (chan == 0) {
-                                               snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt);
+                                               snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str());
                                        } else {
-                                               snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt);
+                                               snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str());
                                        }
                                } else if (nchan < 26) {
-                                       snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan);
+                                       snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str());
                                } else {
-                                       snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt);
+                                       snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
                                }
                        }
 
-                       if (sys::exists(buf)) {
+                       SessionDirectory sdir((*i).path);
+
+                       string spath = sdir.sound_path().to_string();
+
+                       /* note that we search *without* the extension so that
+                          we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" 
+                          in the event that this new name is required for
+                          a file format change.
+                       */
+
+                       if (matching_unsuffixed_filename_exists_in (spath, buf)) {
                                existing++;
+                               break;
                        }
-
                }
 
                if (existing == 0) {
@@ -3306,20 +2964,19 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
                        throw failed_constructor();
                }
        }
-
-       return Glib::path_get_basename(buf);
+        
+       return Glib::path_get_basename (buf);
 }
 
 /** Create a new within-session audio source */
 boost::shared_ptr<AudioFileSource>
-Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive)
+Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive)
 {
-       const size_t n_chans = ds.n_channels().n_audio();
-       const string name    = new_audio_source_name (ds.name(), n_chans, chan, destructive);
+       const string name    = new_audio_source_name (n, n_chans, chan, destructive);
        const string path    = new_source_path_from_name(DataType::AUDIO, name);
 
        return boost::dynamic_pointer_cast<AudioFileSource> (
-               SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+               SourceFactory::createWritable (DataType::AUDIO, *this, path, string(), destructive, frame_rate()));
 }
 
 /** Return a unique name based on \a base for a new internal MIDI source */
@@ -3373,14 +3030,30 @@ Session::new_midi_source_name (const string& base)
 
 /** Create a new within-session MIDI source */
 boost::shared_ptr<MidiSource>
-Session::create_midi_source_for_session (MidiDiskstream& ds)
+Session::create_midi_source_for_session (Track* track, string const & n)
 {
-       const string name = new_midi_source_name (ds.name());
+       /* try to use the existing write source for the track, to keep numbering sane 
+        */
+
+       if (track) {
+               /*MidiTrack* mt = dynamic_cast<Track*> (track);
+                 assert (mt);
+               */
+
+               list<boost::shared_ptr<Source> > l = track->steal_write_sources ();
+                
+               if (!l.empty()) {
+                       assert (boost::dynamic_pointer_cast<MidiSource> (l.front()));
+                       return boost::dynamic_pointer_cast<MidiSource> (l.front());
+               }
+       }
+
+       const string name = new_midi_source_name (n);
        const string path = new_source_path_from_name (DataType::MIDI, name);
 
        return boost::dynamic_pointer_cast<SMFSource> (
-                       SourceFactory::createWritable (
-                                       DataType::MIDI, *this, path, false, frame_rate()));
+               SourceFactory::createWritable (
+                       DataType::MIDI, *this, path, string(), false, frame_rate()));
 }
 
 
@@ -3391,10 +3064,7 @@ Session::add_playlist (boost::shared_ptr<Playlist> playlist, bool unused)
                return;
        }
 
-       bool existing = playlists->add (playlist);
-       if (!existing) {
-               playlist->GoingAway.connect (*this, boost::bind (&Session::remove_playlist, this, boost::weak_ptr<Playlist>(playlist)));
-       }
+       playlists->add (playlist);
 
        if (unused) {
                playlist->release();
@@ -3460,7 +3130,7 @@ Session::audition_region (boost::shared_ptr<Region> r)
 void
 Session::cancel_audition ()
 {
-       if (auditioner->active()) {
+       if (auditioner->auditioning()) {
                auditioner->cancel_audition ();
                AuditionActive (false); /* EMIT SIGNAL */
        }
@@ -3469,43 +3139,13 @@ Session::cancel_audition ()
 bool
 Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
 {
-       return a->order_key(N_("signal")) < b->order_key(N_("signal"));
-}
-
-void
-Session::remove_empty_sounds ()
-{
-       vector<string> audio_filenames;
-
-       get_files_in_directory (_session_dir->sound_path(), audio_filenames);
-
-       Glib::Mutex::Lock lm (source_lock);
-
-       TapeFileMatcher tape_file_matcher;
-
-       remove_if (audio_filenames.begin(), audio_filenames.end(),
-                  boost::bind (&TapeFileMatcher::matches, &tape_file_matcher, _1));
-       
-       for (vector<string>::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) {
-
-               sys::path audio_file_path (_session_dir->sound_path());
-
-               audio_file_path /= *i;
-
-               if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) {
-
-                       try
-                       {
-                               sys::remove (audio_file_path);
-                               const string peakfile = peak_path (audio_file_path.to_string());
-                               sys::remove (peakfile);
-                       }
-                       catch (const sys::filesystem_error& err)
-                       {
-                               error << err.what() << endmsg;
-                       }
-               }
+       if (a->is_monitor()) { 
+               return true;
+       }
+       if (b->is_monitor()) {
+               return false;
        }
+       return a->order_key(N_("signal")) < b->order_key(N_("signal"));
 }
 
 bool
@@ -3513,27 +3153,12 @@ Session::is_auditioning () const
 {
        /* can be called before we have an auditioner object */
        if (auditioner) {
-               return auditioner->active();
+               return auditioner->auditioning();
        } else {
                return false;
        }
 }
 
-uint32_t
-Session::n_diskstreams () const
-{
-       uint32_t n = 0;
-
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if (!(*i)->hidden()) {
-                       n++;
-               }
-       }
-       return n;
-}
-
 void
 Session::graph_reordered ()
 {
@@ -3557,39 +3182,16 @@ Session::graph_reordered ()
           reflect any changes in latencies within the graph.
        */
 
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               (*i)->set_capture_offset ();
-       }
-}
-
-void
-Session::add_processor (Processor* processor)
-{
-       processor->GoingAway.connect (*this, boost::bind (&Session::remove_processor, this, processor));
-       set_dirty();
-}
-
-void
-Session::remove_processor (Processor* processor)
-{
-       Send* send;
-       Return* retrn;
-       PortInsert* port_insert;
-
-       if ((port_insert = dynamic_cast<PortInsert *> (processor)) != 0) {
-               insert_bitset[port_insert->bit_slot()] = false;
-       } else if ((send = dynamic_cast<Send *> (processor)) != 0) {
-               send_bitset[send->bit_slot()] = false;
-       } else if ((retrn = dynamic_cast<Return *> (processor)) != 0) {
-               return_bitset[retrn->bit_slot()] = false;
+       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 ();
+               }
        }
-
-       set_dirty();
 }
 
-nframes_t
+framecnt_t
 Session::available_capture_duration ()
 {
        float sample_bytes_on_disk = 4.0; // keep gcc happy
@@ -3617,15 +3219,15 @@ Session::available_capture_duration ()
 
        double scale = 4096.0 / sample_bytes_on_disk;
 
-       if (_total_free_4k_blocks * scale > (double) max_frames) {
-               return max_frames;
+       if (_total_free_4k_blocks * scale > (double) max_framecnt) {
+               return max_framecnt;
        }
-
-       return (nframes_t) floor (_total_free_4k_blocks * scale);
+        
+       return (framecnt_t) floor (_total_free_4k_blocks * scale);
 }
 
 void
-Session::add_bundle (shared_ptr<Bundle> bundle)
+Session::add_bundle (boost::shared_ptr<Bundle> bundle)
 {
        {
                RCUWriter<BundleList> writer (_bundles);
@@ -3639,7 +3241,7 @@ Session::add_bundle (shared_ptr<Bundle> bundle)
 }
 
 void
-Session::remove_bundle (shared_ptr<Bundle> bundle)
+Session::remove_bundle (boost::shared_ptr<Bundle> bundle)
 {
        bool removed = false;
 
@@ -3661,7 +3263,7 @@ Session::remove_bundle (shared_ptr<Bundle> bundle)
        set_dirty();
 }
 
-shared_ptr<Bundle>
+boost::shared_ptr<Bundle>
 Session::bundle_by_name (string name) const
 {
        boost::shared_ptr<BundleList> b = _bundles.reader ();
@@ -3676,33 +3278,32 @@ Session::bundle_by_name (string name) const
 }
 
 void
-Session::tempo_map_changed (Change)
+Session::tempo_map_changed (const PropertyChange&)
 {
        clear_clicks ();
 
        playlists->update_after_tempo_map_change ();
 
+       _locations->apply (*this, &Session::update_locations_after_tempo_map_change);
+       
        set_dirty ();
 }
 
+void
+Session::update_locations_after_tempo_map_change (Locations::LocationList& loc)
+{
+       for (Locations::LocationList::iterator i = loc.begin(); i != loc.end(); ++i) {
+               (*i)->recompute_frames_from_bbt ();
+       }
+}
+
 /** Ensures that all buffers (scratch, send, silent, etc) are allocated for
  * the given count with the current block size.
  */
 void
 Session::ensure_buffers (ChanCount howmany)
 {
-       if (current_block_size == 0) {
-               return; // too early? (is this ok?)
-       }
-
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-               size_t count = std::max(_scratch_buffers->available().get(*t), howmany.get(*t));
-               _scratch_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t));
-               _mix_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t));
-               _silent_buffers->ensure_buffers (*t, count, _engine.raw_buffer_size(*t));
-       }
-
-       allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false);
+       BufferManager::ensure_buffers (howmany);
 }
 
 void
@@ -3809,39 +3410,60 @@ Session::mark_insert_id (uint32_t id)
        insert_bitset[id] = true;
 }
 
-/* Named Selection management */
-
-NamedSelection *
-Session::named_selection_by_name (string name)
+void
+Session::unmark_send_id (uint32_t id)
 {
-       Glib::Mutex::Lock lm (named_selection_lock);
+       if (id < send_bitset.size()) {
+               send_bitset[id] = false;
+       }
+}
+
+void
+Session::unmark_return_id (uint32_t id)
+{
+       if (id < return_bitset.size()) {
+               return_bitset[id] = false;
+       }
+}
+
+void
+Session::unmark_insert_id (uint32_t id)
+{
+       if (id < insert_bitset.size()) {
+               insert_bitset[id] = false;
+       }
+}
+
+
+/* Named Selection management */
+
+boost::shared_ptr<NamedSelection>
+Session::named_selection_by_name (string name)
+{
+       Glib::Mutex::Lock lm (named_selection_lock);
        for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) {
                if ((*i)->name == name) {
-                       returni;
+                       return *i;
                }
        }
-       return 0;
+       return boost::shared_ptr<NamedSelection>();
 }
 
 void
-Session::add_named_selection (NamedSelection* named_selection)
+Session::add_named_selection (boost::shared_ptr<NamedSelection> named_selection)
 {
        {
                Glib::Mutex::Lock lm (named_selection_lock);
                named_selections.insert (named_selections.begin(), named_selection);
        }
 
-       for (list<boost::shared_ptr<Playlist> >::iterator i = named_selection->playlists.begin(); i != named_selection->playlists.end(); ++i) {
-               add_playlist (*i);
-       }
-
        set_dirty();
 
        NamedSelectionAdded (); /* EMIT SIGNAL */
 }
 
 void
-Session::remove_named_selection (NamedSelection* named_selection)
+Session::remove_named_selection (boost::shared_ptr<NamedSelection> named_selection)
 {
        bool removed = false;
 
@@ -3851,7 +3473,6 @@ Session::remove_named_selection (NamedSelection* named_selection)
                NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection);
 
                if (i != named_selections.end()) {
-                       delete (*i);
                        named_selections.erase (i);
                        set_dirty();
                        removed = true;
@@ -3866,17 +3487,24 @@ Session::remove_named_selection (NamedSelection* named_selection)
 void
 Session::reset_native_file_format ()
 {
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+       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) {
+                       /* don't save state as we do this, there's no point
+                        */
 
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               (*i)->reset_write_sources (false);
+                       _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);
+               }
        }
 }
 
 bool
 Session::route_name_unique (string n) const
 {
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
                if ((*i)->name() == n) {
@@ -3901,35 +3529,10 @@ Session::route_name_internal (string n) const
        return false;
 }
 
-void
-Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force)
-{
-       if (!force && howmany <= _npan_buffers) {
-               return;
-       }
-
-       if (_pan_automation_buffer) {
-
-               for (uint32_t i = 0; i < _npan_buffers; ++i) {
-                       delete [] _pan_automation_buffer[i];
-               }
-
-               delete [] _pan_automation_buffer;
-       }
-
-       _pan_automation_buffer = new pan_t*[howmany];
-
-       for (uint32_t i = 0; i < howmany; ++i) {
-               _pan_automation_buffer[i] = new pan_t[nframes];
-       }
-
-       _npan_buffers = howmany;
-}
-
 int
-Session::freeze (InterThreadInfo& itt)
+Session::freeze_all (InterThreadInfo& itt)
 {
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
 
@@ -3939,7 +3542,7 @@ Session::freeze (InterThreadInfo& itt)
                        /* XXX this is wrong because itt.progress will keep returning to zero at the start
                           of every track.
                        */
-                       t->freeze (itt);
+                       t->freeze_me (itt);
                }
        }
 
@@ -3947,7 +3550,7 @@ Session::freeze (InterThreadInfo& itt)
 }
 
 boost::shared_ptr<Region>
-Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
+Session::write_one_track (AudioTrack& track, framepos_t start, framepos_t end,
                          bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
                          InterThreadInfo& itt, bool enable_processing)
 {
@@ -3956,14 +3559,17 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
        boost::shared_ptr<AudioFileSource> fsource;
        uint32_t x;
        char buf[PATH_MAX+1];
-       ChanCount nchans(track.audio_diskstream()->n_channels());
-       nframes_t position;
-       nframes_t this_chunk;
-       nframes_t to_do;
+       ChanCount diskstream_channels (track.n_channels());
+       framepos_t position;
+       framecnt_t this_chunk;
+       framepos_t to_do;
        BufferSet buffers;
        SessionDirectory sdir(get_best_session_directory_for_new_source ());
        const string sound_dir = sdir.sound_path().to_string();
-       nframes_t len = end - start;
+       framepos_t len = end - start;
+       bool need_block_size_reset = false;
+       string ext;
+       ChanCount const max_proc = track.max_processor_streams ();
 
        if (end <= start) {
                error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
@@ -3971,7 +3577,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
                return result;
        }
 
-       const nframes_t chunk_size = (256 * 1024)/4;
+       const framecnt_t chunk_size = (256 * 1024)/4;
 
        // block all process callback handling
 
@@ -3979,7 +3585,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
 
        /* call tree *MUST* hold route_lock */
 
-       if ((playlist = track.diskstream()->playlist()) == 0) {
+       if ((playlist = track.playlist()) == 0) {
                goto out;
        }
 
@@ -3989,10 +3595,12 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
                goto out;
        }
 
-       for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) {
+       ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+
+       for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) {
 
                for (x = 0; x < 99999; ++x) {
-                       snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1);
+                       snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str());
                        if (access (buf, F_OK) != 0) {
                                break;
                        }
@@ -4005,7 +3613,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
 
                try {
                        fsource = boost::dynamic_pointer_cast<AudioFileSource> (
-                               SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
+                               SourceFactory::createWritable (DataType::AUDIO, *this, buf, string(), false, frame_rate()));
                }
 
                catch (failed_constructor& err) {
@@ -4016,16 +3624,21 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
                srcs.push_back (fsource);
        }
 
+       /* tell redirects that care that we are about to use a much larger blocksize */
+        
+       need_block_size_reset = true;
+       track.set_block_size (chunk_size);
+
        /* XXX need to flush all redirects */
 
        position = start;
        to_do = len;
 
        /* create a set of reasonably-sized buffers */
-       buffers.ensure_buffers(DataType::AUDIO, nchans.n_audio(), chunk_size);
-       buffers.set_count(nchans);
+       buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size);
+       buffers.set_count (max_proc);
 
-       for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
+       for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
                boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
                if (afs)
                        afs->prepare_for_peakfile_writes ();
@@ -4075,9 +3688,14 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
 
                /* construct a region to represent the bounced material */
 
-               result = RegionFactory::create (srcs, 0,
-                               srcs.front()->length(srcs.front()->timeline_position()),
-                               region_name_from_path (srcs.front()->name(), true));
+               PropertyList plist;
+               
+               plist.add (Properties::start, 0);
+               plist.add (Properties::length, srcs.front()->length(srcs.front()->timeline_position()));
+               plist.add (Properties::name, region_name_from_path (srcs.front()->name(), true));
+               
+               result = RegionFactory::create (srcs, plist);
+                          
        }
 
   out:
@@ -4101,52 +3719,51 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
                }
        }
 
+
+       if (need_block_size_reset) {
+               track.set_block_size (get_block_size());
+       }
+        
        unblock_processing ();
 
        return result;
 }
 
-BufferSet&
-Session::get_silent_buffers (ChanCount count)
+gain_t*
+Session::gain_automation_buffer() const
 {
-       assert(_silent_buffers->available() >= count);
-       _silent_buffers->set_count(count);
+       return ProcessThread::gain_automation_buffer ();
+}
 
-       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
-               for (size_t i= 0; i < count.get(*t); ++i) {
-                       _silent_buffers->get(*t, i).clear();
-               }
-       }
+pan_t**
+Session::pan_automation_buffer() const
+{
+       return ProcessThread::pan_automation_buffer ();
+}
 
-       return *_silent_buffers;
+BufferSet&
+Session::get_silent_buffers (ChanCount count)
+{
+       return ProcessThread::get_silent_buffers (count);
 }
 
 BufferSet&
 Session::get_scratch_buffers (ChanCount count)
 {
-       if (count != ChanCount::ZERO) {
-               assert(_scratch_buffers->available() >= count);
-               _scratch_buffers->set_count(count);
-       } else {
-               _scratch_buffers->set_count (_scratch_buffers->available());
-       }
-
-       return *_scratch_buffers;
+       return ProcessThread::get_scratch_buffers (count);
 }
 
 BufferSet&
 Session::get_mix_buffers (ChanCount count)
 {
-       assert(_mix_buffers->available() >= count);
-       _mix_buffers->set_count(count);
-       return *_mix_buffers;
+       return ProcessThread::get_mix_buffers (count);
 }
 
 uint32_t
 Session::ntracks () const
 {
        uint32_t n = 0;
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
                if (boost::dynamic_pointer_cast<Track> (*i)) {
@@ -4161,7 +3778,7 @@ uint32_t
 Session::nbusses () const
 {
        uint32_t n = 0;
-       shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
                if (boost::dynamic_pointer_cast<Track>(*i) == 0) {
@@ -4178,15 +3795,13 @@ Session::add_automation_list(AutomationList *al)
        automation_lists[al->id()] = al;
 }
 
-nframes_t
-Session::compute_initial_length ()
-{
-       return _engine.frame_rate() * 60 * 5;
-}
-
 void
 Session::sync_order_keys (std::string const & base)
 {
+       if (deletion_in_progress()) {
+               return;
+       }
+
        if (!Config->get_sync_all_route_ordering()) {
                /* leave order keys as they are */
                return;
@@ -4199,31 +3814,40 @@ Session::sync_order_keys (std::string const & base)
        }
 
        Route::SyncOrderKeys (base); // EMIT SIGNAL
-}
 
+       /* this might not do anything */
+
+       set_remote_control_ids ();
+}
 
-/** @return true if there is at least one record-enabled diskstream, otherwise false */
+/** @return true if there is at least one record-enabled track, otherwise false */
 bool
-Session::have_rec_enabled_diskstream () const
+Session::have_rec_enabled_track () const
 {
-       return g_atomic_int_get (&_have_rec_enabled_diskstream) == 1;
+       return g_atomic_int_get (&_have_rec_enabled_track) == 1;
 }
 
-/** Update the state of our rec-enabled diskstreams flag */
+/** Update the state of our rec-enabled tracks flag */
 void
-Session::update_have_rec_enabled_diskstream ()
+Session::update_have_rec_enabled_track ()
 {
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader ();
-       DiskstreamList::iterator i = dsl->begin ();
-       while (i != dsl->end () && (*i)->record_enabled () == false) {
+       boost::shared_ptr<RouteList> rl = routes.reader ();
+       RouteList::iterator i = rl->begin();
+       while (i != rl->end ()) {
+
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+               if (tr && tr->record_enabled ()) {
+                       break;
+               }
+               
                ++i;
        }
 
-       int const old = g_atomic_int_get (&_have_rec_enabled_diskstream);
+       int const old = g_atomic_int_get (&_have_rec_enabled_track);
 
-       g_atomic_int_set (&_have_rec_enabled_diskstream, i != dsl->end () ? 1 : 0);
+       g_atomic_int_set (&_have_rec_enabled_track, i != rl->end () ? 1 : 0);
 
-       if (g_atomic_int_get (&_have_rec_enabled_diskstream) != old) {
+       if (g_atomic_int_get (&_have_rec_enabled_track) != old) {
                RecordStateChanged (); /* EMIT SIGNAL */
        }
 }
@@ -4231,22 +3855,10 @@ Session::update_have_rec_enabled_diskstream ()
 void
 Session::listen_position_changed ()
 {
-       Placement p;
-
-       switch (Config->get_listen_position()) {
-       case AfterFaderListen:
-               p = PostFader;
-               break;
-
-       case PreFaderListen:
-               p = PreFader;
-               break;
-       }
-
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               (*i)->put_control_outs_at (p);
+               (*i)->listen_position_changed ();
        }
 }
 
@@ -4255,13 +3867,14 @@ Session::solo_control_mode_changed ()
 {
        /* cancel all solo or all listen when solo control mode changes */
 
-       if (Config->get_solo_control_is_listen_control()) {
-               set_solo (routes.reader(), false);
-       } else {
-               set_listen (routes.reader(), false);
+       if (soloing()) {
+               set_solo (get_routes(), false);
+       } else if (listening()) {
+               set_listen (get_routes(), false);
        }
 }
 
+/** Called when anything about any of our route groups changes (membership, state etc.) */
 void
 Session::route_group_changed ()
 {
@@ -4274,23 +3887,17 @@ Session::get_available_sync_options () const
        vector<SyncSource> ret;
        
        ret.push_back (JACK);
-
-       if (mtc_port()) {
-               ret.push_back (MTC);
-       } 
-
-       if (midi_clock_port()) {
-               ret.push_back (MIDIClock);
-       } 
+       ret.push_back (MTC);
+       ret.push_back (MIDIClock);
 
        return ret;
 }
 
 boost::shared_ptr<RouteList>
-Session::get_routes_with_regions_at (nframes64_t const p) const
+Session::get_routes_with_regions_at (framepos_t const p) const
 {
-       shared_ptr<RouteList> r = routes.reader ();
-       shared_ptr<RouteList> rl (new RouteList);
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       boost::shared_ptr<RouteList> rl (new RouteList);
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
@@ -4298,12 +3905,7 @@ Session::get_routes_with_regions_at (nframes64_t const p) const
                        continue;
                }
                
-               boost::shared_ptr<Diskstream> ds = tr->diskstream ();
-               if (!ds) {
-                       continue;
-               }
-
-               boost::shared_ptr<Playlist> pl = ds->playlist ();
+               boost::shared_ptr<Playlist> pl = tr->playlist ();
                if (!pl) {
                        continue;
                }
@@ -4315,3 +3917,410 @@ Session::get_routes_with_regions_at (nframes64_t const p) const
 
        return rl;
 }
+
+void
+Session::goto_end ()
+{
+       if (_session_range_location) {
+               request_locate (_session_range_location->end(), false);
+       } else {
+               request_locate (0, false);
+       }
+}
+
+void
+Session::goto_start ()
+{
+       if (_session_range_location) {
+               request_locate (_session_range_location->start(), false);
+       } else {
+               request_locate (0, false);
+       }
+}
+
+framepos_t
+Session::current_start_frame () const
+{
+       return _session_range_location ? _session_range_location->start() : 0;
+}
+
+framepos_t
+Session::current_end_frame () const
+{
+       return _session_range_location ? _session_range_location->end() : 0;
+}
+
+void
+Session::add_session_range_location (framepos_t start, framepos_t end)
+{
+       _session_range_location = new Location (*this, start, end, _("session"), Location::IsSessionRange);
+       _locations->add (_session_range_location);
+}
+
+/** Called when one of our routes' order keys has changed */
+void
+Session::route_order_key_changed ()
+{
+       RouteOrderKeyChanged (); /* EMIT SIGNAL */
+}
+
+void
+Session::step_edit_status_change (bool yn)
+{
+       bool send = false;
+
+       bool val = false;
+       if (yn) {
+               send = (_step_editors == 0);
+               val = true;
+
+               _step_editors++;
+       } else {
+               send = (_step_editors == 1);
+               val = false;
+
+               if (_step_editors > 0) {
+                       _step_editors--;
+               }
+       }
+
+       if (send) {
+               StepEditStatusChange (val);
+       }
+}
+
+        
+void
+Session::start_time_changed (framepos_t old)
+{
+       /* Update the auto loop range to match the session range
+          (unless the auto loop range has been changed by the user)
+       */
+
+       Location* s = _locations->session_range_location ();
+       if (s == 0) {
+               return;
+       }
+       
+       Location* l = _locations->auto_loop_location ();
+
+       if (l->start() == old) {
+               l->set_start (s->start(), true);
+       }
+}
+
+void
+Session::end_time_changed (framepos_t old)
+{
+       /* Update the auto loop range to match the session range
+          (unless the auto loop range has been changed by the user)
+       */
+
+       Location* s = _locations->session_range_location ();
+       if (s == 0) {
+               return;
+       }
+       
+       Location* l = _locations->auto_loop_location ();
+
+       if (l->end() == old) {
+               l->set_end (s->end(), true);
+       }
+}
+
+string
+Session::source_search_path (DataType type) const
+{
+       string search_path;
+
+       if (session_dirs.size() == 1) {
+               switch (type) {
+               case DataType::AUDIO:
+                       search_path = _session_dir->sound_path().to_string();
+                       break;
+               case DataType::MIDI:
+                       search_path = _session_dir->midi_path().to_string();
+                       break;
+               }
+       } else {
+               for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+                       SessionDirectory sdir (i->path);
+                       if (!search_path.empty()) {
+                               search_path += ':';
+                       } 
+                       switch (type) {
+                       case DataType::AUDIO:
+                               search_path += sdir.sound_path().to_string();
+                               break;
+                       case DataType::MIDI:
+                               search_path += sdir.midi_path().to_string();
+                               break;
+                       }
+               }
+       }
+                
+       /* now add user-specified locations
+        */
+
+       vector<string> dirs;
+
+       switch (type) {
+       case DataType::AUDIO:
+               split (config.get_audio_search_path (), dirs, ':');
+               break;
+       case DataType::MIDI:
+               split (config.get_midi_search_path (), dirs, ':');
+               break;
+       }
+
+       for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
+               search_path += ':';
+               search_path += *i;
+                        
+       }
+        
+       return search_path;
+}
+
+void
+Session::ensure_search_path_includes (const string& path, DataType type)
+{
+       string search_path;
+       vector<string> dirs;
+
+       if (path == ".") {
+               return;
+       }
+
+       switch (type) {
+       case DataType::AUDIO:
+               search_path = config.get_audio_search_path ();
+               break;
+       case DataType::MIDI:
+               search_path = config.get_midi_search_path ();
+               break;
+       }
+
+       split (search_path, dirs, ':');
+
+       for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
+               if (*i == path) {
+                       return;
+               }
+       }
+
+       if (!search_path.empty()) {
+               search_path += ':';
+       }
+
+       search_path += path;
+        
+       switch (type) {
+       case DataType::AUDIO:
+               config.set_audio_search_path (search_path);
+               break;
+       case DataType::MIDI:
+               config.set_midi_search_path (search_path);
+               break;
+       }
+}
+
+boost::shared_ptr<Speakers>
+Session::get_speakers() 
+{
+       return _speakers;
+}
+
+list<string>
+Session::unknown_processors () const
+{
+       list<string> p;
+
+       boost::shared_ptr<RouteList> r = routes.reader ();
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               list<string> t = (*i)->unknown_processors ();
+               copy (t.begin(), t.end(), back_inserter (p));
+       }
+
+       p.sort ();
+       p.unique ();
+
+       return p;
+}
+
+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)) {
+               return;
+       }
+
+       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 */
+               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.
+       */
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               max_latency = max (max_latency, (*i)->set_private_port_latencies (playback));
+       }
+
+        /* 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 ();
+       }
+
+       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 (); 
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               if (!(*i)->is_hidden() && ((*i)->active())) {
+                       _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
+               }
+                
+               for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+                       (*i)->set_latency_compensation (_worst_track_latency);
+               }
+       }
+}
+
+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 ();
+               }
+       }
+}
+
+void
+Session::set_worst_io_latencies ()
+{
+       set_worst_playback_latency ();
+       set_worst_capture_latency ();
+}
+
+void
+Session::set_worst_playback_latency ()
+{
+       if (_state_of_the_state & (InitialConnecting|Deletion)) {
+               return;
+       }
+
+       _worst_output_latency = 0;
+
+       if (!_engine.connected()) {
+               return;
+       }
+
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               _worst_output_latency = max (_worst_output_latency, (*i)->output()->latency());
+       }
+
+       DEBUG_TRACE (DEBUG::Latency, string_compose ("Worst output latency: %1\n", _worst_output_latency));
+}
+
+void
+Session::set_worst_capture_latency ()
+{
+       if (_state_of_the_state & (InitialConnecting|Deletion)) {
+               return;
+       }
+
+       _worst_input_latency = 0;
+
+       if (!_engine.connected()) {
+               return;
+       }
+
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               _worst_input_latency = max (_worst_input_latency, (*i)->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_hidden() && ((*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")));
+
+       if (force_whole_graph || some_track_latency_changed) {
+               /* trigger a full recompute of latency numbers for the graph.
+                  everything else that we need to do will be done in the latency
+                  callback.
+               */
+               _engine.update_total_latencies ();
+               return; // everything else will be done in the latency callback
+       } 
+
+       DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n")
+}
+