add left/right side trim cursors and use them for region trimming, as appropriate
[ardour.git] / libs / ardour / session.cc
index 398c1259354518a18ba84a8474ada4e04e0f5a56..48d2f6982c4c9ab6d84d48a1e6a1f43d0a25c384 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,9 @@
 
 */
 
+#define __STDC_LIMIT_MACROS
+#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 <glibmm/thread.h>
 
 #include "pbd/error.h"
 #include "pbd/boost_debug.h"
@@ -54,6 +55,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"
@@ -72,6 +74,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"
@@ -93,6 +96,7 @@
 #include "ardour/tape_file_matcher.h"
 #include "ardour/tempo.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
 
 #include "midi++/jack.h"
 
@@ -131,9 +135,6 @@ Session::Session (AudioEngine &eng,
        : _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),
@@ -144,7 +145,7 @@ Session::Session (AudioEngine &eng,
          _butler (new Butler (*this)),
          _post_transport_work (0),
          _send_timecode_update (false),
-         diskstreams (new DiskstreamList),
+         route_graph (new Graph(*this)),
          routes (new RouteList),
          _total_free_4k_blocks (0),
          _bundles (new BundleList),
@@ -154,7 +155,7 @@ Session::Session (AudioEngine &eng,
          click_emphasis_data (0),
          main_outs (0),
          _metadata (new SessionMetadata()),
-         _have_rec_enabled_diskstream (false)
+         _have_rec_enabled_track (false)
 
 {
        playlists.reset (new SessionPlaylists);
@@ -173,7 +174,7 @@ Session::Session (AudioEngine &eng,
         _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
 
        if (_is_new) {
-               if (create (mix_template, compute_initial_length(), bus_profile)) {
+               if (create (mix_template, bus_profile)) {
                        destroy ();
                        throw failed_constructor ();
                }
@@ -232,6 +233,7 @@ Session::destroy ()
 
        Stateful::loading_state_version = 0;
 
+       _butler->drop_references ();
        delete _butler;
        delete midi_control_ui;
 
@@ -245,14 +247,9 @@ 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();
@@ -292,19 +289,6 @@ Session::destroy ()
 
        boost::shared_ptr<RouteList> r = routes.reader ();
 
-       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(); ++i) {
                DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for source %1 ; pre-ref = %2\n", i->second->path(), i->second.use_count()));
@@ -638,7 +622,13 @@ Session::hookup_io ()
                */
 
                try {
-                       auditioner.reset (new Auditioner (*this));
+                        Auditioner* a = new Auditioner (*this);
+                        if (a->init()) {
+                                delete a;
+                                throw failed_constructor();
+                        }
+                        a->use_new_diskstream ();
+                       auditioner.reset (a);
                }
 
                catch (failed_constructor& err) {
@@ -712,31 +702,24 @@ 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 ();
+       update_session_range_location_marker ();
 }
 
 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) {
+       if ((playlist = track->playlist()) != 0) {
                playlist->LengthChanged.connect_same_thread (*this, boost::bind (&Session::playlist_length_changed, this));
        }
 
-       /* see comment in playlist_length_changed () */
-       find_current_end ();
+       update_session_range_location_marker ();
 }
 
 bool
@@ -758,21 +741,23 @@ 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 ()) {
+               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->record_enabled ()) {
                                //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
-                               (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !config.get_auto_input());
+                               tr->monitor_input (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 ()) {
+               
+               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->record_enabled ()) {
                                //cerr << "switching to input = " << !Config->get_auto_input() << __FILE__ << __LINE__ << endl << endl;
-                               (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring);
+                               tr->monitor_input (Config->get_monitoring_model() == HardwareMonitoring);
                        }
                }
        }
@@ -954,11 +939,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;
                }
        }
 
@@ -982,10 +964,12 @@ Session::enable_record ()
                deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location);
 
                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);
+                       
+                       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->record_enabled ()) {
+                                       tr->monitor_input (true);
                                }
                        }
                }
@@ -1017,11 +1001,12 @@ Session::disable_record (bool rt_context, bool force)
                }
 
                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);
+                       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->record_enabled ()) {
+                                       tr->monitor_input (false);
                                }
                        }
                }
@@ -1037,17 +1022,15 @@ 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 ()) {
+                       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->record_enabled ()) {
                                        //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
-                                       (*i)->monitor_input (false);
+                                       tr->monitor_input (false);
                                }
                        }
                }
@@ -1119,17 +1102,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;
                                }
                        }
 
@@ -1141,8 +1127,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;
@@ -1191,12 +1177,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 ();
 
@@ -1204,9 +1185,12 @@ 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 ();
@@ -1252,13 +1236,13 @@ Session::set_default_fade (float /*steepness*/, float /*fade_msecs*/)
 
 struct RouteSorter {
     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)) {
                    return false;
-           } else if (r2->fed_by.find (r1) != r2->fed_by.end()) {
+           } else if (r1->feeds (r2)) {
                    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 {
@@ -1277,21 +1261,21 @@ trace_terminal (shared_ptr<Route> r1, shared_ptr<Route> rbase)
 {
        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;
                }
@@ -1300,7 +1284,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) {
 
@@ -1308,7 +1292,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;
                        }
 
@@ -1342,6 +1326,24 @@ Session::resort_routes ()
                /* 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)
@@ -1350,7 +1352,7 @@ Session::resort_routes_using (shared_ptr<RouteList> r)
 
        for (i = r->begin(); i != r->end(); ++i) {
 
-               (*i)->fed_by.clear ();
+               (*i)->clear_fed_by ();
 
                for (j = r->begin(); j != r->end(); ++j) {
 
@@ -1364,8 +1366,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);
                        }
                }
        }
@@ -1377,13 +1381,14 @@ 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
 
 }
@@ -1454,6 +1459,14 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
 
                try {
                        MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode);
+
+                        if (mt->init ()) {
+                                delete mt;
+                                goto failed;
+                        }
+
+                        mt->use_new_diskstream();
+
                        boost_debug_shared_ptr_mark_interesting (mt, "Track");
                        track = boost::shared_ptr<MidiTrack>(mt);
 
@@ -1470,7 +1483,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
 
                        auto_connect_route (track, existing_inputs, existing_outputs);
 
-                       track->midi_diskstream()->non_realtime_input_change();
+                       track->non_realtime_input_change();
                        if (route_group) {
                                route_group->add (track);
                        }
@@ -1484,36 +1497,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 << 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;
-
-                       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());
-                               }
-                       }
-
                        goto failed;
                }
 
@@ -1630,6 +1619,14 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
                try {
                        AudioTrack* at = new AudioTrack (*this, track_name, Route::Flag (0), mode);
+
+                        if (at->init ()) {
+                                delete at;
+                                goto failed;
+                        }
+
+                        at->use_new_diskstream();
+
                        boost_debug_shared_ptr_mark_interesting (at, "Track");
                        track = boost::shared_ptr<AudioTrack>(at);
 
@@ -1655,7 +1652,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
                                route_group->add (track);
                        }
 
-                       track->audio_diskstream()->non_realtime_input_change();
+                       track->non_realtime_input_change();
 
                        track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
                        track->set_remote_control_id (control_id);
@@ -1667,36 +1664,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;
                }
 
@@ -1762,6 +1735,12 @@ Session::new_audio_route (bool aux, int input_channels, int output_channels, Rou
 
                try {
                        Route* rt = new Route (*this, bus_name, Route::Flag(0), DataType::AUDIO);
+
+                        if (rt->init ()) {
+                                delete rt;
+                                goto failure;
+                        }
+
                        boost_debug_shared_ptr_mark_interesting (rt, "Route");
                        shared_ptr<Route> bus (rt);
 
@@ -1848,13 +1827,19 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                        /*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));
-           
+
                        if (route == 0) {
                                error << _("Session: cannot create track/bus from template description") << endmsg;
                                goto out;
@@ -1922,7 +1907,8 @@ Session::add_routes (RouteList& new_routes, bool save)
                boost::shared_ptr<Route> r (*x);
 
                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, 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));
@@ -1935,6 +1921,13 @@ Session::add_routes (RouteList& new_routes, bool save)
                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));
+               }
        }
 
        if (_monitor_out && IO::connecting_legal) {
@@ -2055,34 +2048,13 @@ Session::add_internal_sends (boost::shared_ptr<Route> dest, Placement p, boost::
        graph_reordered ();
 }
 
-void
-Session::add_diskstream (boost::shared_ptr<Diskstream> dstream)
-{
-       /* 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 */
-       }
-
-       dstream->PlaylistChanged.connect_same_thread (*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_same_thread (*this, boost::bind (&Session::update_have_rec_enabled_diskstream, this));
-
-       dstream->prepare ();
-
-}
-
 void
 Session::remove_route (shared_ptr<Route> route)
 {
+        if (((route == _master_out) || (route == _monitor_out)) && !Config->get_allow_special_bus_removal()) {
+                return;
+        }
+
        {
                RCUWriter<RouteList> writer (routes);
                shared_ptr<RouteList> rs = writer.get_copy ();
@@ -2109,30 +2081,13 @@ Session::remove_route (shared_ptr<Route> route)
                        _monitor_out.reset ();
                }
 
-               update_route_solo_state ();
-
                /* writer goes out of scope, forces route list update */
        }
+        
+        update_route_solo_state ();
+       update_session_range_location_marker ();
 
-       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 ();
-
-       // 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);
@@ -2189,15 +2144,63 @@ Session::route_listen_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
        }
 
        if (route->listening()) {
+
+                if (Config->get_exclusive_solo()) {
+                        /* new listen: disable all other listen */
+                        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--;
        }
 }
+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;
@@ -2210,7 +2213,7 @@ 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 ();
        int32_t delta;
 
@@ -2219,39 +2222,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_monitor() || (*i)->is_hidden()) {
                        continue;
-               } else if ((*i)->feeds (route, &via_sends_only)) {
-                       if (!via_sends_only) {
-                               (*i)->mod_solo_by_others (delta);
-                       }
                } 
-       }
 
-       /* make sure master is never muted by solo */
+                in_signal_flow = false;
 
-       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 */
+                if ((*i)->feeds (route, &via_sends_only)) {
+                       if (!via_sends_only) {
+                                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 (_monitor_out && route != _monitor_out && _monitor_out && _monitor_out->soloed_by_others() == 0) {
-               _monitor_out->mod_solo_by_others (1);
+                if (!in_signal_flow) {
+                        uninvolved.push_back (*i);
+                }
        }
 
        solo_update_disabled = false;
        update_route_solo_state (r);
+
+        /* now notify that the mute state of the routes not involved in the signal
+           pathway of the just-solo-changed route may have altered.
+        */
+
+        for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
+                (*i)->mute_changed (this);
+        }
+
        SoloChanged (); /* EMIT SIGNAL */
        set_dirty();
 }
@@ -2263,6 +2288,7 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
 
        bool something_soloed = false;
         uint32_t listeners = 0;
+        uint32_t isolated = 0;
 
        if (!r) {
                r = routes.reader();
@@ -2271,11 +2297,18 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_hidden() && (*i)->self_soloed()) {
                        something_soloed = true;
-                       break;
                }
 
                 if (!(*i)->is_hidden() && (*i)->listening()) {
-                        listeners++;
+                        if (Config->get_solo_control_is_listen_control()) {
+                                listeners++;
+                        } else {
+                                (*i)->set_listen (false, this);
+                        }
+                }
+
+                if ((*i)->solo_isolated()) {
+                        isolated++;
                 }
        }
 
@@ -2284,8 +2317,11 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                 SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
         }
 
-        if (listeners) {
-                 _listen_cnt = listeners;
+        _listen_cnt = listeners;
+
+        if (isolated != _solo_isolated_cnt) {
+                _solo_isolated_cnt = isolated;
+                IsolatedChanged (); /* EMIT SIGNAL */
         }
 }
 
@@ -2303,6 +2339,24 @@ Session::get_routes_with_internal_returns() const
        return rl;
 }
 
+bool
+Session::io_name_is_legal (const std::string& name)
+{
+        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;
+}
+
 shared_ptr<Route>
 Session::route_by_name (string name)
 {
@@ -2345,259 +2399,67 @@ Session::route_by_remote_id (uint32_t id)
        return shared_ptr<Route> ((Route*) 0);
 }
 
+/** If either end of the session range location marker lies inside the current
+ *  session extent, move it to the corresponding session extent.
+ */
 void
-Session::find_current_end ()
+Session::update_session_range_location_marker ()
 {
        if (_state_of_the_state & Loading) {
                return;
        }
 
-       nframes_t max = get_maximum_extent ();
-
-       if (max > end_location->end()) {
-               end_location->set_end (max);
-               set_dirty();
-               DurationChanged(); /* EMIT SIGNAL */
-       }
-}
-
-nframes_t
-Session::get_maximum_extent () const
-{
-       nframes_t max = 0;
-       nframes_t me;
-
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       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;
-               }
-       }
-
-       return max;
-}
-
-boost::shared_ptr<Diskstream>
-Session::diskstream_by_name (string name)
-{
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if ((*i)->name() == name) {
-                       return *i;
-               }
-       }
-
-       return boost::shared_ptr<Diskstream>((Diskstream*) 0);
-}
-
-boost::shared_ptr<Diskstream>
-Session::diskstream_by_id (const PBD::ID& id)
-{
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+       pair<nframes_t, nframes_t> const ext = get_extent ();
 
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if ((*i)->id() == id) {
-                       return *i;
+       if (_session_range_location == 0) {
+               /* we don't have a session range yet; use this one (provided it is valid) */
+               if (ext.first != max_frames) {
+                       add_session_range_location (ext.first, ext.second);
                }
-       }
-
-       return boost::shared_ptr<Diskstream>((Diskstream*) 0);
-}
-
-/* Region management */
-
-string
-Session::new_region_name (string old)
-{
-       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());
-
-       }
-
-       while (number < (UINT_MAX-1)) {
-
-                const RegionFactory::RegionMap& regions (RegionFactory::regions());
-               RegionFactory::RegionMap::const_iterator i;
-               string sbuf;
-
-               number++;
-
-               snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number);
-               sbuf = buf;
-
-               for (i = regions.begin(); i != regions.end(); ++i) {
-                       if (i->second->name() == sbuf) {
-                               break;
-                       }
+               /* update the existing session range */
+               if (ext.first < _session_range_location->start()) {
+                       _session_range_location->set_start (ext.first);
+                       set_dirty ();
                }
-
-               if (i == regions.end()) {
-                       break;
+               
+               if (ext.second > _session_range_location->end()) {
+                       _session_range_location->set_end (ext.second);
+                       set_dirty ();
                }
+               
        }
-
-       if (number != (UINT_MAX-1)) {
-               return buf;
-       }
-
-       error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg;
-       return old;
 }
 
-int
-Session::region_name (string& result, string base, bool newlevel)
+/** @return Extent of the session's contents; if the session is empty, the first value of
+ *  the pair will equal max_frames.
+ */
+pair<nframes_t, nframes_t>
+Session::get_extent () const
 {
-       char buf[16];
-       string subbase;
-
-       if (base.find("/") != string::npos) {
-               base = base.substr(base.find_last_of("/") + 1);
-       }
-
-       if (base == "") {
-
-               snprintf (buf, sizeof (buf), "%d", RegionFactory::nregions() + 1);
-               result = "region.";
-               result += buf;
-
-       } else {
-
-               if (newlevel) {
-                       subbase = base;
-               } else {
-                       string::size_type pos;
-
-                       pos = base.find_last_of ('.');
-
-                       /* pos may be npos, but then we just use entire base */
-
-                       subbase = base.substr (0, pos);
-
+       pair<nframes_t, nframes_t> ext (max_frames, 0);
+       
+       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->destructive()) {
+                       // ignore tape tracks when getting extents
+                       continue;
                }
 
-               {
-                       Glib::Mutex::Lock lm (region_lock);
-
-                       map<string,uint32_t>::iterator x;
-
-                       result = subbase;
-
-                       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);
-
-                               result += buf;
-                       }
+               pair<nframes_t, nframes_t> e = tr->playlist()->get_extent ();
+               if (e.first < ext.first) {
+                       ext.first = e.first;
+               }
+               if (e.second > ext.second) {
+                       ext.second = e.second;
                }
        }
 
-       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)
-{
-       /* mark dirty because something has changed
-       */
-
-       set_dirty ();
-        
-        for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
-                
-                boost::shared_ptr<Region> region = *ii;
-                assert (region);
-                
-                region->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::region_changed, this, _1, boost::weak_ptr<Region>(region)));
-                update_region_name_map (region);
-        }
-        
-        if (!new_regions.empty()) {
-                RegionsAdded (new_regions); /* EMIT SIGNAL */
-        }
-}
-
-void
-Session::update_region_name_map (boost::shared_ptr<Region> region)
-{
-       string::size_type last_period = region->name().find_last_of ('.');
-
-       if (last_period != string::npos && last_period < region->name().length() - 1) {
-
-               string base = region->name().substr (0, last_period);
-               string number = region->name().substr (last_period+1);
-               map<string,uint32_t>::iterator x;
-
-               /* note that if there is no number, we get zero from atoi,
-                  which is just fine
-               */
-
-               region_name_map[base] = atoi (number);
-       }
-}
-
-void
-Session::region_changed (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
-{
-       boost::shared_ptr<Region> region (weak_region.lock ());
-
-       if (!region) {
-               return;
-       }
-
-       if (what_changed.contains (Properties::hidden)) {
-               /* relay hidden changes */
-               RegionHiddenChange (region);
-       }
-
-       if (what_changed.contains (Properties::name)) {
-               update_region_name_map (region);
-       }
+       return ext;
 }
 
-void
-Session::remove_region (boost::weak_ptr<Region> weak_region)
-{
-       boost::shared_ptr<Region> region (weak_region.lock ());
-
-       if (!region) {
-               return;
-       }
-
-        RegionFactory::map_remove (region);
-       set_dirty();
-
-        RegionRemoved(region); /* EMIT SIGNAL */
-}
+/* Region management */
 
 boost::shared_ptr<Region>
 Session::find_whole_file_parent (boost::shared_ptr<Region const> child) const
@@ -2665,10 +2527,14 @@ 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();
+       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<Region> >& l = tr->last_capture_regions();
 
                if (!l.empty()) {
                        r.insert (r.end(), l.begin(), l.end());
@@ -3009,10 +2875,9 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
 
 /** 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> (
@@ -3070,9 +2935,9 @@ 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 (string const & n)
 {
-       const string name = new_midi_source_name (ds.name());
+       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> (
@@ -3154,7 +3019,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 */
        }
@@ -3213,27 +3078,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 ()
 {
@@ -3257,40 +3107,13 @@ 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)
-{
-       /* Session does not own Processors (they belong to a Route) but we do want to track
-          the arrival and departure of port inserts, sends and returns for naming
-          purposes.
-       */
-       processor->DropReferences.connect_same_thread (*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
@@ -3395,18 +3218,7 @@ Session::tempo_map_changed (const PropertyChange&)
 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
@@ -3513,6 +3325,31 @@ Session::mark_insert_id (uint32_t id)
        insert_bitset[id] = true;
 }
 
+void
+Session::unmark_send_id (uint32_t id)
+{
+       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>
@@ -3565,10 +3402,12 @@ Session::remove_named_selection (boost::shared_ptr<NamedSelection> named_selecti
 void
 Session::reset_native_file_format ()
 {
-       boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               (*i)->reset_write_sources (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->reset_write_sources (false);
+               }
        }
 }
 
@@ -3600,31 +3439,6 @@ 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_all (InterThreadInfo& itt)
 {
@@ -3655,7 +3469,7 @@ 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());
+       ChanCount nchans(track.n_channels());
        nframes_t position;
        nframes_t this_chunk;
        nframes_t to_do;
@@ -3678,7 +3492,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;
        }
 
@@ -3810,9 +3624,23 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
        return result;
 }
 
+gain_t*
+Session::gain_automation_buffer() const
+{
+        return ProcessThread::gain_automation_buffer ();
+}
+
+pan_t**
+Session::pan_automation_buffer() const
+{
+        return ProcessThread::pan_automation_buffer ();
+}
+
 BufferSet&
 Session::get_silent_buffers (ChanCount count)
 {
+        return ProcessThread::get_silent_buffers (count);
+#if 0
        assert(_silent_buffers->available() >= count);
        _silent_buffers->set_count(count);
 
@@ -3823,11 +3651,14 @@ Session::get_silent_buffers (ChanCount count)
        }
 
        return *_silent_buffers;
+#endif
 }
 
 BufferSet&
 Session::get_scratch_buffers (ChanCount count)
 {
+        return ProcessThread::get_scratch_buffers (count);
+#if 0
        if (count != ChanCount::ZERO) {
                assert(_scratch_buffers->available() >= count);
                _scratch_buffers->set_count(count);
@@ -3836,14 +3667,18 @@ Session::get_scratch_buffers (ChanCount count)
        }
 
        return *_scratch_buffers;
+#endif
 }
 
 BufferSet&
 Session::get_mix_buffers (ChanCount count)
 {
+        return ProcessThread::get_mix_buffers (count);
+#if 0
        assert(_mix_buffers->available() >= count);
        _mix_buffers->set_count(count);
        return *_mix_buffers;
+#endif
 }
 
 uint32_t
@@ -3882,12 +3717,6 @@ 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)
 {
@@ -3913,28 +3742,34 @@ Session::sync_order_keys (std::string const & base)
        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 */
        }
 }
@@ -4009,12 +3844,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;
                }
@@ -4026,3 +3856,62 @@ 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);
+       }
+}
+
+void
+Session::set_session_start (nframes_t start)
+{
+       if (_session_range_location) {
+               _session_range_location->set_start (start);
+       } else {
+               add_session_range_location (start, start);
+       }
+}
+             
+void
+Session::set_session_end (nframes_t end)
+{
+       if (_session_range_location) {
+               _session_range_location->set_end (end);
+       } else {
+               add_session_range_location (end, end);
+       }
+}
+
+nframes_t
+Session::current_start_frame () const
+{
+       return _session_range_location ? _session_range_location->start() : 0;
+}
+
+nframes_t
+Session::current_end_frame () const
+{
+       return _session_range_location ? _session_range_location->end() : 0;
+}
+
+void
+Session::add_session_range_location (nframes_t start, nframes_t end)
+{
+       _session_range_location = new Location (start, end, _("session"), Location::IsSessionRange);
+       _locations.add (_session_range_location);
+}