a few more if (_mmc) tests
[ardour.git] / libs / ardour / session.cc
index d7a71ff0c5f3064673fa0f8ad8c9752c45a529e4..ea48f9c60fbb7a9f22cde0dee50a26aa77b2681c 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"
 #include "ardour/tape_file_matcher.h"
 #include "ardour/tempo.h"
 #include "ardour/utils.h"
+#include "ardour/graph.h"
 
 #include "midi++/jack.h"
+#include "midi++/mmc.h"
 
 #include "i18n.h"
 
@@ -131,11 +136,7 @@ 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),
+         _mmc (0),
          _mtc_port (default_mtc_port),
          _midi_port (default_midi_port),
          _midi_clock_port (default_midi_clock_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()));
@@ -321,7 +305,7 @@ Session::destroy ()
 
        Crossfade::set_buffer_size (0);
 
-       delete mmc;
+       delete _mmc;
 
        /* not strictly necessary, but doing it here allows the shared_ptr debugging to work */
        playlists.reset ();
@@ -457,7 +441,7 @@ Session::when_engine_running ()
                snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
 
                shared_ptr<Bundle> c (new Bundle (buf, true));
-               c->add_channel (_("mono"));
+               c->add_channel (_("mono"), DataType::AUDIO);
                c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
 
                add_bundle (c);
@@ -470,9 +454,9 @@ Session::when_engine_running ()
                        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->add_channel (_("L"), DataType::AUDIO);
                        c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
-                       c->add_channel (_("R"));
+                       c->add_channel (_("R"), DataType::AUDIO);
                        c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
 
                        add_bundle (c);
@@ -486,7 +470,7 @@ Session::when_engine_running ()
                snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
 
                shared_ptr<Bundle> c (new Bundle (buf, false));
-               c->add_channel (_("mono"));
+               c->add_channel (_("mono"), DataType::AUDIO);
                c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
 
                add_bundle (c);
@@ -500,9 +484,9 @@ Session::when_engine_running ()
                        snprintf (buf, sizeof(buf), _("in %" PRIu32 "+%" PRIu32), np + 1, np + 2);
 
                        shared_ptr<Bundle> c (new Bundle (buf, false));
-                       c->add_channel (_("L"));
+                       c->add_channel (_("L"), DataType::AUDIO);
                        c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
-                       c->add_channel (_("R"));
+                       c->add_channel (_("R"), DataType::AUDIO);
                        c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
 
                        add_bundle (c);
@@ -718,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
@@ -764,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);
                        }
                }
        }
@@ -960,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;
                }
        }
 
@@ -985,13 +961,17 @@ Session::enable_record ()
        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);
+                if (_mmc) {
+                        _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordStrobe));
+                }
 
                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);
                                }
                        }
                }
@@ -1009,25 +989,22 @@ Session::disable_record (bool rt_context, bool force)
 
                if ((!Config->get_latched_record_enable () && !play_loop) || force) {
                        g_atomic_int_set (&_record_status, Disabled);
+                        if (_mmc) {
+                                _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);
+                       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);
                                }
                        }
                }
@@ -1043,17 +1020,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);
                                }
                        }
                }
@@ -1076,7 +1051,9 @@ Session::maybe_enable_record ()
                        enable_record ();
                }
        } else {
-               deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
+                if (_mmc) {
+                        _mmc->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdRecordPause));
+                }
                RecordStateChanged (); /* EMIT SIGNAL */
        }
 
@@ -1125,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;
                                }
                        }
 
@@ -1147,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;
@@ -1197,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 ();
 
@@ -1210,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 ();
@@ -1258,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 {
@@ -1283,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;
                }
@@ -1306,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) {
 
@@ -1314,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;
                        }
 
@@ -1339,15 +1317,31 @@ Session::resort_routes ()
                return;
        }
 
-
        {
-
                RCUWriter<RouteList> writer (routes);
                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)
@@ -1356,7 +1350,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) {
 
@@ -1370,8 +1364,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);
                        }
                }
        }
@@ -1383,13 +1379,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
 
 }
@@ -1457,7 +1454,7 @@ Session::new_midi_track (TrackMode mode, RouteGroup* route_group, uint32_t how_m
                }
 
                shared_ptr<MidiTrack> track;
-
+                
                try {
                        MidiTrack* mt = new MidiTrack (*this, track_name, Route::Flag (0), mode);
 
@@ -1484,7 +1481,8 @@ 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);
                        }
@@ -1498,36 +1496,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;
                }
 
@@ -1566,9 +1540,6 @@ Session::auto_connect_route (boost::shared_ptr<Route> route,
                ? ChanCount::max(existing_inputs, existing_outputs)
                : existing_outputs;
 
-       static string empty_string;
-       string& port = empty_string;
-
        for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
                vector<string> physinputs;
                vector<string> physoutputs;
@@ -1579,7 +1550,7 @@ Session::auto_connect_route (boost::shared_ptr<Route> route,
                if (!physinputs.empty()) {
                        uint32_t nphysical_in = physinputs.size();
                        for (uint32_t i = 0; i < route->n_inputs().get(*t) && i < nphysical_in; ++i) {
-                               port = empty_string;
+                               string port;
 
                                if (Config->get_input_auto_connect() & AutoConnectPhysical) {
                                        port = physinputs[(in_offset.get(*t) + i) % nphysical_in];
@@ -1595,7 +1566,7 @@ Session::auto_connect_route (boost::shared_ptr<Route> route,
                if (!physoutputs.empty()) {
                        uint32_t nphysical_out = physoutputs.size();
                        for (uint32_t i = 0; i < route->n_outputs().get(*t); ++i) {
-                               port = empty_string;
+                               string port;
 
                                if (Config->get_output_auto_connect() & AutoConnectPhysical) {
                                        port = physoutputs[(out_offset.get(*t) + i) % nphysical_out];
@@ -1677,7 +1648,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);
@@ -1689,36 +1660,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;
                }
 
@@ -1876,13 +1823,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;
@@ -1950,7 +1903,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));
@@ -1963,6 +1917,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) {
@@ -2083,31 +2044,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));
-}
-
 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 ();
@@ -2134,30 +2077,13 @@ Session::remove_route (shared_ptr<Route> route)
                        _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();
-       }
+        update_route_solo_state ();
+       update_session_range_location_marker ();
 
-       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);
@@ -2177,6 +2103,11 @@ Session::remove_route (shared_ptr<Route> route)
        update_latency_compensation (false, false);
        set_dirty();
 
+        /* flush references out of the graph
+         */
+
+        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) */
@@ -2214,15 +2145,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;
@@ -2235,7 +2214,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;
 
@@ -2244,39 +2223,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();
 }
@@ -2288,6 +2289,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();
@@ -2296,11 +2298,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++;
                 }
        }
 
@@ -2309,8 +2318,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 */
         }
 }
 
@@ -2328,6 +2340,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)
 {
@@ -2370,68 +2400,64 @@ 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 ();
+       pair<nframes_t, nframes_t> const ext = get_extent ();
 
-       if (max > end_location->end()) {
-               end_location->set_end (max);
-               set_dirty();
-               DurationChanged(); /* EMIT SIGNAL */
+       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);
+               }
+       } else {
+               /* update the existing session range */
+               if (ext.first < _session_range_location->start()) {
+                       _session_range_location->set_start (ext.first);
+                       set_dirty ();
+               }
+               
+               if (ext.second > _session_range_location->end()) {
+                       _session_range_location->set_end (ext.second);
+                       set_dirty ();
+               }
+               
        }
 }
 
-nframes_t
-Session::get_maximum_extent () const
+/** @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
 {
-       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
+       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;
-               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;
+               pair<nframes_t, nframes_t> e = tr->playlist()->get_extent ();
+               if (e.first < ext.first) {
+                       ext.first = e.first;
                }
-       }
-
-       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();
-
-       for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-               if ((*i)->id() == id) {
-                       return *i;
+               if (e.second > ext.second) {
+                       ext.second = e.second;
                }
        }
 
-       return boost::shared_ptr<Diskstream>((Diskstream*) 0);
+       return ext;
 }
 
 /* Region management */
@@ -2461,59 +2487,75 @@ Session::find_whole_file_parent (boost::shared_ptr<Region const> child) const
 }
 
 int
-Session::destroy_region (boost::shared_ptr<Region> region)
+Session::destroy_sources (list<boost::shared_ptr<Source> > srcs)
 {
-       vector<boost::shared_ptr<Source> > srcs;
+        set<boost::shared_ptr<Region> > relevant_regions;
 
-       {
-               if (region->playlist()) {
-                       region->playlist()->destroy_region (region);
-               }
-
-               for (uint32_t n = 0; n < region->n_channels(); ++n) {
-                       srcs.push_back (region->source (n));
-               }
+       for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ++s) {
+                RegionFactory::get_regions_using_source (*s, relevant_regions);
        }
 
-       region->drop_references ();
+        cerr << "There are " << relevant_regions.size() << " using " << srcs.size() << " sources" << endl;
+
+        for (set<boost::shared_ptr<Region> >::iterator r = relevant_regions.begin(); r != relevant_regions.end(); ) {
+                set<boost::shared_ptr<Region> >::iterator tmp;
 
-       for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
+                tmp = r;
+                ++tmp;
 
-                (*i)->mark_for_remove ();
-                (*i)->drop_references ();
+                cerr << "Cleanup " << (*r)->name() << " UC = " << (*r).use_count() << endl;
+
+                playlists->destroy_region (*r);
+                RegionFactory::map_remove (*r);
+
+                (*r)->drop_sources ();
+                (*r)->drop_references ();
+
+                cerr << "\tdone UC = " << (*r).use_count() << endl;
+
+                relevant_regions.erase (r);
+
+                r = tmp;
+        }
+
+       for (list<boost::shared_ptr<Source> >::iterator s = srcs.begin(); s != srcs.end(); ) {
                 
-                cerr << "source was not used by any playlist\n";
-       }
+                {
+                        Glib::Mutex::Lock ls (source_lock);
+                        /* remove from the main source list */
+                        sources.erase ((*s)->id());
+                }
 
-       return 0;
-}
+                (*s)->mark_for_remove ();
+                (*s)->drop_references ();
+
+                s = srcs.erase (s);
+        }
 
-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();
+       list<boost::shared_ptr<Source> > srcs;
 
-       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<Source> >& l = tr->last_capture_sources();
 
                if (!l.empty()) {
-                       r.insert (r.end(), l.begin(), l.end());
+                       srcs.insert (srcs.end(), l.begin(), l.end());
                        l.clear ();
                }
        }
 
-       destroy_regions (r);
+       destroy_sources (srcs);
 
        save_state (_current_snapshot_name);
 
@@ -2537,16 +2579,19 @@ Session::add_source (boost::shared_ptr<Source> source)
        }
 
        if (result.second) {
-               set_dirty();
-       }
 
-       boost::shared_ptr<AudioFileSource> afs;
+                /* yay, new source */
 
-       if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
-               if (Config->get_auto_analyse_audio()) {
-                       Analyser::queue_source_for_analysis (source, false);
-               }
-       }
+               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
@@ -2846,10 +2891,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> (
@@ -2907,9 +2951,25 @@ 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> (
@@ -3056,21 +3116,6 @@ Session::is_auditioning () const
        }
 }
 
-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 ()
 {
@@ -3094,40 +3139,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
@@ -3232,18 +3250,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
@@ -3350,6 +3357,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>
@@ -3402,10 +3434,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);
+               }
        }
 }
 
@@ -3437,31 +3471,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)
 {
@@ -3492,7 +3501,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;
@@ -3515,7 +3524,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;
        }
 
@@ -3647,9 +3656,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);
 
@@ -3660,11 +3683,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);
@@ -3673,14 +3699,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
@@ -3719,12 +3749,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)
 {
@@ -3750,28 +3774,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 */
        }
 }
@@ -3846,12 +3876,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;
                }
@@ -3863,3 +3888,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);
+}