first vaguely working version using PresentationInfo
[ardour.git] / libs / ardour / session_state.cc
index bb4a8265ef393e9182d5b1c16aa439f00a533ac3..5b10389a3f620f0a058db2719f4d91adacf3e8b3 100644 (file)
 #include <stdint.h>
 
 #include <algorithm>
 #include <stdint.h>
 
 #include <algorithm>
-#include <fstream>
 #include <string>
 #include <cerrno>
 #include <cstdio> /* snprintf(3) ... grrr */
 #include <cmath>
 #include <unistd.h>
 #include <string>
 #include <cerrno>
 #include <cstdio> /* snprintf(3) ... grrr */
 #include <cmath>
 #include <unistd.h>
-#include <sys/stat.h>
 #include <climits>
 #include <signal.h>
 #include <sys/time.h>
 #include <climits>
 #include <signal.h>
 #include <sys/time.h>
@@ -50,7 +48,7 @@
 #endif
 
 #include <glib.h>
 #endif
 
 #include <glib.h>
-#include <glib/gstdio.h>
+#include "pbd/gstdio_compat.h"
 
 #include <glibmm.h>
 #include <glibmm/threads.h>
 
 #include <glibmm.h>
 #include <glibmm/threads.h>
@@ -63,7 +61,6 @@
 
 #include "evoral/SMF.hpp"
 
 
 #include "evoral/SMF.hpp"
 
-#include "pbd/boost_debug.h"
 #include "pbd/basename.h"
 #include "pbd/controllable_descriptor.h"
 #include "pbd/debug.h"
 #include "pbd/basename.h"
 #include "pbd/controllable_descriptor.h"
 #include "pbd/debug.h"
@@ -75,6 +72,7 @@
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
 #include "pbd/localtime_r.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
 #include "pbd/localtime_r.h"
+#include "pbd/unwind.h"
 
 #include "ardour/amp.h"
 #include "ardour/async_midi_port.h"
 
 #include "ardour/amp.h"
 #include "ardour/async_midi_port.h"
@@ -84,6 +82,7 @@
 #include "ardour/audiofilesource.h"
 #include "ardour/audioregion.h"
 #include "ardour/automation_control.h"
 #include "ardour/audiofilesource.h"
 #include "ardour/audioregion.h"
 #include "ardour/automation_control.h"
+#include "ardour/boost_debug.h"
 #include "ardour/butler.h"
 #include "ardour/control_protocol_manager.h"
 #include "ardour/directory_names.h"
 #include "ardour/butler.h"
 #include "ardour/control_protocol_manager.h"
 #include "ardour/directory_names.h"
 #include "ardour/tempo.h"
 #include "ardour/ticker.h"
 #include "ardour/user_bundle.h"
 #include "ardour/tempo.h"
 #include "ardour/ticker.h"
 #include "ardour/user_bundle.h"
+#include "ardour/vca.h"
+#include "ardour/vca_manager.h"
 
 #include "control_protocol/control_protocol.h"
 
 
 #include "control_protocol/control_protocol.h"
 
+#include "LuaBridge/LuaBridge.h"
+
 #include "i18n.h"
 #include <locale.h>
 
 #include "i18n.h"
 #include <locale.h>
 
@@ -152,7 +155,7 @@ Session::pre_engine_init (string fullpath)
 
                string full_session_name = Glib::build_filename( fullpath, _name );
                full_session_name += statefile_suffix;
 
                string full_session_name = Glib::build_filename( fullpath, _name );
                full_session_name += statefile_suffix;
-               
+
                _is_new = !Glib::file_test (full_session_name, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
        } else {
                _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
                _is_new = !Glib::file_test (full_session_name, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
        } else {
                _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
@@ -170,6 +173,7 @@ Session::pre_engine_init (string fullpath)
        set_next_event ();
        _all_route_group->set_active (true, this);
        interpolation.add_channel_to (0, 0);
        set_next_event ();
        _all_route_group->set_active (true, this);
        interpolation.add_channel_to (0, 0);
+       _vca_manager = new VCAManager (*this);
 
        if (config.get_use_video_sync()) {
                waiting_for_sync_offset = true;
 
        if (config.get_use_video_sync()) {
                waiting_for_sync_offset = true;
@@ -180,7 +184,7 @@ Session::pre_engine_init (string fullpath)
        last_rr_session_dir = session_dirs.begin();
 
        set_history_depth (Config->get_history_depth());
        last_rr_session_dir = session_dirs.begin();
 
        set_history_depth (Config->get_history_depth());
-       
+
         /* default: assume simple stereo speaker configuration */
 
         _speakers->setup_default_speakers (2);
         /* default: assume simple stereo speaker configuration */
 
         _speakers->setup_default_speakers (2);
@@ -202,8 +206,6 @@ Session::pre_engine_init (string fullpath)
 
        Delivery::disable_panners ();
        IO::disable_connecting ();
 
        Delivery::disable_panners ();
        IO::disable_connecting ();
-
-       AudioFileSource::set_peak_dir (_session_dir->peak_path());
 }
 
 int
 }
 
 int
@@ -217,26 +219,26 @@ Session::post_engine_init ()
        BootMessage (_("Using configuration"));
 
        _midi_ports = new MidiPortManager;
        BootMessage (_("Using configuration"));
 
        _midi_ports = new MidiPortManager;
-       
+
        MIDISceneChanger* msc;
 
        _scene_changer = msc = new MIDISceneChanger (*this);
        MIDISceneChanger* msc;
 
        _scene_changer = msc = new MIDISceneChanger (*this);
-       msc->set_input_port (scene_input_port());
-       msc->set_output_port (scene_out());
+       msc->set_input_port (boost::dynamic_pointer_cast<MidiPort>(scene_input_port()));
+       msc->set_output_port (boost::dynamic_pointer_cast<MidiPort>(scene_output_port()));
 
        boost::function<framecnt_t(void)> timer_func (boost::bind (&Session::audible_frame, this));
 
        boost::function<framecnt_t(void)> timer_func (boost::bind (&Session::audible_frame, this));
-       boost::dynamic_pointer_cast<AsyncMIDIPort>(scene_in())->set_timer (timer_func);
+       boost::dynamic_pointer_cast<AsyncMIDIPort>(scene_input_port())->set_timer (timer_func);
 
        setup_midi_machine_control ();
 
        setup_midi_machine_control ();
-       
+
        if (_butler->start_thread()) {
                return -1;
        }
        if (_butler->start_thread()) {
                return -1;
        }
-       
+
        if (start_midi_thread ()) {
                return -1;
        }
        if (start_midi_thread ()) {
                return -1;
        }
-       
+
        setup_click_sounds (0);
        setup_midi_control ();
 
        setup_click_sounds (0);
        setup_midi_control ();
 
@@ -249,9 +251,11 @@ Session::post_engine_init ()
                delete _tempo_map;
                _tempo_map = new TempoMap (_current_frame_rate);
                _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
                delete _tempo_map;
                _tempo_map = new TempoMap (_current_frame_rate);
                _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
-               
+               _tempo_map->MetricPositionChanged.connect_same_thread (*this, boost::bind (&Session::gui_tempo_map_changed, this));
+
                /* MidiClock requires a tempo map */
 
                /* MidiClock requires a tempo map */
 
+               delete midi_clock;
                midi_clock = new MidiClockTicker ();
                midi_clock->set_session (this);
 
                midi_clock = new MidiClockTicker ();
                midi_clock->set_session (this);
 
@@ -259,14 +263,14 @@ Session::post_engine_init ()
 
                SndFileSource::setup_standard_crossfades (*this, frame_rate());
                _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
 
                SndFileSource::setup_standard_crossfades (*this, frame_rate());
                _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
-               
+
                AudioDiskstream::allocate_working_buffers();
                refresh_disk_space ();
                AudioDiskstream::allocate_working_buffers();
                refresh_disk_space ();
-               
+
                /* we're finally ready to call set_state() ... all objects have
                 * been created, the engine is running.
                 */
                /* we're finally ready to call set_state() ... all objects have
                 * been created, the engine is running.
                 */
-               
+
                if (state_tree) {
                        if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
                                return -1;
                if (state_tree) {
                        if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
                                return -1;
@@ -281,56 +285,56 @@ Session::post_engine_init ()
 
                boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
                boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
 
                boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
                boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
-               
+
                Config->map_parameters (ff);
                config.map_parameters (ft);
                 _butler->map_parameters ();
 
                /* Reset all panners */
                Config->map_parameters (ff);
                config.map_parameters (ft);
                 _butler->map_parameters ();
 
                /* Reset all panners */
-               
+
                Delivery::reset_panners ();
                Delivery::reset_panners ();
-               
+
                /* this will cause the CPM to instantiate any protocols that are in use
                 * (or mandatory), which will pass it this Session, and then call
                 * set_state() on each instantiated protocol to match stored state.
                 */
                /* this will cause the CPM to instantiate any protocols that are in use
                 * (or mandatory), which will pass it this Session, and then call
                 * set_state() on each instantiated protocol to match stored state.
                 */
-               
+
                ControlProtocolManager::instance().set_session (this);
                ControlProtocolManager::instance().set_session (this);
-               
+
                /* This must be done after the ControlProtocolManager set_session above,
                   as it will set states for ports which the ControlProtocolManager creates.
                */
                /* This must be done after the ControlProtocolManager set_session above,
                   as it will set states for ports which the ControlProtocolManager creates.
                */
-               
+
                // XXX set state of MIDI::Port's
                // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
                // XXX set state of MIDI::Port's
                // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
-               
+
                /* And this must be done after the MIDI::Manager::set_port_states as
                 * it will try to make connections whose details are loaded by set_port_states.
                 */
                /* And this must be done after the MIDI::Manager::set_port_states as
                 * it will try to make connections whose details are loaded by set_port_states.
                 */
-               
+
                hookup_io ();
                hookup_io ();
-               
+
                /* Let control protocols know that we are now all connected, so they
                 * could start talking to surfaces if they want to.
                 */
                /* Let control protocols know that we are now all connected, so they
                 * could start talking to surfaces if they want to.
                 */
-               
+
                ControlProtocolManager::instance().midi_connectivity_established ();
                ControlProtocolManager::instance().midi_connectivity_established ();
-               
+
                if (_is_new && !no_auto_connect()) {
                        Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock());
                        auto_connect_master_bus ();
                }
                if (_is_new && !no_auto_connect()) {
                        Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock());
                        auto_connect_master_bus ();
                }
-               
+
                _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
                _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
-               
+
                /* update latencies */
                /* update latencies */
-               
+
                initialize_latencies ();
                initialize_latencies ();
-               
+
                _locations->added.connect_same_thread (*this, boost::bind (&Session::location_added, this, _1));
                _locations->removed.connect_same_thread (*this, boost::bind (&Session::location_removed, this, _1));
                _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
                _locations->added.connect_same_thread (*this, boost::bind (&Session::location_added, this, _1));
                _locations->removed.connect_same_thread (*this, boost::bind (&Session::location_removed, this, _1));
                _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
-               
+
        } catch (AudioEngine::PortRegistrationFailure& err) {
                /* handle this one in a different way than all others, so that its clear what happened */
                error << err.what() << endmsg;
        } catch (AudioEngine::PortRegistrationFailure& err) {
                /* handle this one in a different way than all others, so that its clear what happened */
                error << err.what() << endmsg;
@@ -347,7 +351,7 @@ Session::post_engine_init ()
        send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
        send_immediate_mmc (MIDI::MachineControlCommand (Timecode::Time ()));
 
        send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
        send_immediate_mmc (MIDI::MachineControlCommand (Timecode::Time ()));
 
-       MIDI::Name::MidiPatchManager::instance().set_session (this);
+       MIDI::Name::MidiPatchManager::instance().add_search_path (session_directory().midi_patch_path() );
 
        ltc_tx_initialize();
        /* initial program change will be delivered later; see ::config_changed() */
 
        ltc_tx_initialize();
        /* initial program change will be delivered later; see ::config_changed() */
@@ -367,9 +371,9 @@ Session::post_engine_init ()
        }
 
        /* Now, finally, we can fill the playback buffers */
        }
 
        /* Now, finally, we can fill the playback buffers */
-    
+
        BootMessage (_("Filling playback buffers"));
        BootMessage (_("Filling playback buffers"));
-    
+
        boost::shared_ptr<RouteList> rl = routes.reader();
        for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
                boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (*r);
        boost::shared_ptr<RouteList> rl = routes.reader();
        for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
                boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (*r);
@@ -385,11 +389,11 @@ void
 Session::session_loaded ()
 {
        SessionLoaded();
 Session::session_loaded ()
 {
        SessionLoaded();
-       
+
        _state_of_the_state = Clean;
        _state_of_the_state = Clean;
-       
+
        DirtyChanged (); /* EMIT SIGNAL */
        DirtyChanged (); /* EMIT SIGNAL */
-       
+
        if (_is_new) {
                save_state ("");
        } else if (state_was_pending) {
        if (_is_new) {
                save_state ("");
        } else if (state_was_pending) {
@@ -397,9 +401,9 @@ Session::session_loaded ()
                remove_pending_capture_state ();
                state_was_pending = false;
        }
                remove_pending_capture_state ();
                state_was_pending = false;
        }
-       
+
        /* Now, finally, we can fill the playback buffers */
        /* Now, finally, we can fill the playback buffers */
-       
+
        BootMessage (_("Filling playback buffers"));
        force_locate (_transport_frame, false);
 }
        BootMessage (_("Filling playback buffers"));
        force_locate (_transport_frame, false);
 }
@@ -540,9 +544,9 @@ Session::create (const string& session_template, BusProfile* bus_profile)
        _writable = exists_and_writable (_path);
 
        if (!session_template.empty()) {
        _writable = exists_and_writable (_path);
 
        if (!session_template.empty()) {
-               std::string in_path = (ARDOUR::Profile->get_trx () ? session_template : session_template_dir_to_file (session_template));
+               string in_path = (ARDOUR::Profile->get_trx () ? session_template : session_template_dir_to_file (session_template));
 
 
-               ifstream in(in_path.c_str());
+               FILE* in = g_fopen (in_path.c_str(), "rb");
 
                if (in) {
                        /* no need to call legalize_for_path() since the string
 
                if (in) {
                        /* no need to call legalize_for_path() since the string
@@ -550,23 +554,51 @@ Session::create (const string& session_template, BusProfile* bus_profile)
                         */
                        string out_path = Glib::build_filename (_session_dir->root_path(), _name + statefile_suffix);
 
                         */
                        string out_path = Glib::build_filename (_session_dir->root_path(), _name + statefile_suffix);
 
-                       ofstream out(out_path.c_str());
+                       FILE* out = g_fopen (out_path.c_str(), "wb");
 
                        if (out) {
 
                        if (out) {
-                               out << in.rdbuf();
-                                _is_new = false;
-
-                                if (!ARDOUR::Profile->get_trx()) {
-                                       /* Copy plugin state files from template to new session */
-                                       std::string template_plugins = Glib::build_filename (session_template, X_("plugins"));
-                                       copy_recurse (template_plugins, plugins_dir ());
-                                }
-                                
+                               char buf[1024];
+                               stringstream new_session;
+
+                               while (!feof (in)) {
+                                       size_t charsRead = fread (buf, sizeof(char), 1024, in);
+
+                                       if (ferror (in)) {
+                                               error << string_compose (_("Error reading session template file %1 (%2)"), in_path, strerror (errno)) << endmsg;
+                                               fclose (in);
+                                               fclose (out);
+                                               return -1;
+                                       }
+                                       if (charsRead == 0) {
+                                               break;
+                                       }
+                                       new_session.write (buf, charsRead);
+                               }
+                               fclose (in);
+
+                               string file_contents = new_session.str();
+                               size_t writeSize = file_contents.length();
+                               if (fwrite (file_contents.c_str(), sizeof(char), writeSize, out) != writeSize) {
+                                       error << string_compose (_("Error writing session template file %1 (%2)"), out_path, strerror (errno)) << endmsg;
+                                       fclose (out);
+                                       return -1;
+                               }
+                               fclose (out);
+
+                               _is_new = false;
+
+                               if (!ARDOUR::Profile->get_trx()) {
+                                       /* Copy plugin state files from template to new session */
+                                       std::string template_plugins = Glib::build_filename (session_template, X_("plugins"));
+                                       copy_recurse (template_plugins, plugins_dir ());
+                               }
+
                                return 0;
 
                        } else {
                                error << string_compose (_("Could not open %1 for writing session template"), out_path)
                                        << endmsg;
                                return 0;
 
                        } else {
                                error << string_compose (_("Could not open %1 for writing session template"), out_path)
                                        << endmsg;
+                               fclose(in);
                                return -1;
                        }
 
                                return -1;
                        }
 
@@ -579,16 +611,16 @@ Session::create (const string& session_template, BusProfile* bus_profile)
        }
 
        if (Profile->get_trx()) {
        }
 
        if (Profile->get_trx()) {
-       
+
                /* set initial start + end point : ARDOUR::Session::session_end_shift long.
                   Remember that this is a brand new session. Sessions
                   loaded from saved state will get this range from the saved state.
                */
                /* set initial start + end point : ARDOUR::Session::session_end_shift long.
                   Remember that this is a brand new session. Sessions
                   loaded from saved state will get this range from the saved state.
                */
-               
+
                set_session_range_location (0, 0);
                set_session_range_location (0, 0);
-               
+
                /* Initial loop location, from absolute zero, length 10 seconds  */
                /* Initial loop location, from absolute zero, length 10 seconds  */
-               
+
                Location* loc = new Location (*this, 0, 10.0 * _engine.sample_rate(), _("Loop"),  Location::IsAutoLoop);
                _locations->add (loc, true);
                set_auto_loop_location (loc);
                Location* loc = new Location (*this, 0, 10.0 * _engine.sample_rate(), _("Loop"),  Location::IsAutoLoop);
                _locations->add (loc, true);
                set_auto_loop_location (loc);
@@ -596,7 +628,7 @@ Session::create (const string& session_template, BusProfile* bus_profile)
 
        _state_of_the_state = Clean;
 
 
        _state_of_the_state = Clean;
 
-        /* set up Master Out and Control Out if necessary */
+        /* set up Master Out and Monitor Out if necessary */
 
         if (bus_profile) {
 
 
         if (bus_profile) {
 
@@ -605,14 +637,14 @@ Session::create (const string& session_template, BusProfile* bus_profile)
 
                 // Waves Tracks: always create master bus for Tracks
                 if (ARDOUR::Profile->get_trx() || bus_profile->master_out_channels) {
 
                 // Waves Tracks: always create master bus for Tracks
                 if (ARDOUR::Profile->get_trx() || bus_profile->master_out_channels) {
-                       boost::shared_ptr<Route> r (new Route (*this, _("Master"), Route::MasterOut, DataType::AUDIO));
+                       boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
                         if (r->init ()) {
                                 return -1;
                         }
                         if (r->init ()) {
                                 return -1;
                         }
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
-                       {
+
+                        BOOST_MARK_ROUTE(r);
+
+                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                r->input()->ensure_io (count, false, this);
                                r->output()->ensure_io (count, false, this);
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                r->input()->ensure_io (count, false, this);
                                r->output()->ensure_io (count, false, this);
@@ -626,20 +658,20 @@ Session::create (const string& session_template, BusProfile* bus_profile)
                }
 
                if (!rl.empty()) {
                }
 
                if (!rl.empty()) {
-                       add_routes (rl, false, false, false);
+                       add_routes (rl, false, false, false, PresentationInfo::max_order);
                }
 
                // Waves Tracks: Skip this. Always use autoconnection for Tracks
                if (!ARDOUR::Profile->get_trx()) {
                }
 
                // Waves Tracks: Skip this. Always use autoconnection for Tracks
                if (!ARDOUR::Profile->get_trx()) {
-                       
+
                        /* this allows the user to override settings with an environment variable.
                         */
                        /* this allows the user to override settings with an environment variable.
                         */
-                       
+
                        if (no_auto_connect()) {
                                bus_profile->input_ac = AutoConnectOption (0);
                                bus_profile->output_ac = AutoConnectOption (0);
                        }
                        if (no_auto_connect()) {
                                bus_profile->input_ac = AutoConnectOption (0);
                                bus_profile->output_ac = AutoConnectOption (0);
                        }
-                       
+
                        Config->set_input_auto_connect (bus_profile->input_ac);
                        Config->set_output_auto_connect (bus_profile->output_ac);
                }
                        Config->set_input_auto_connect (bus_profile->input_ac);
                        Config->set_output_auto_connect (bus_profile->output_ac);
                }
@@ -731,6 +763,8 @@ Session::remove_state (string snapshot_name)
 int
 Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot, bool template_only)
 {
 int
 Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot, bool template_only)
 {
+       DEBUG_TRACE (DEBUG::Locale, string_compose ("Session::save_state locale '%1'\n", setlocale (LC_NUMERIC, NULL)));
+
        XMLTree tree;
        std::string xml_path(_session_dir->root_path());
 
        XMLTree tree;
        std::string xml_path(_session_dir->root_path());
 
@@ -755,6 +789,10 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
                return 1;
        }
 
                return 1;
        }
 
+#ifndef NDEBUG
+       const int64_t save_start_time = g_get_monotonic_time();
+#endif
+
        /* tell sources we're saving first, in case they write out to a new file
         * which should be saved with the state rather than the old one */
        for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
        /* tell sources we're saving first, in case they write out to a new file
         * which should be saved with the state rather than the old one */
        for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
@@ -767,7 +805,14 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
 
        SessionSaveUnderway (); /* EMIT SIGNAL */
 
 
        SessionSaveUnderway (); /* EMIT SIGNAL */
 
+       bool mark_as_clean = true;
+
+       if (!snapshot_name.empty() && !switch_to_snapshot) {
+               mark_as_clean = false;
+       }
+
        if (template_only) {
        if (template_only) {
+               mark_as_clean = false;
                tree.set_root (&get_template());
        } else {
                tree.set_root (&get_state());
                tree.set_root (&get_template());
        } else {
                tree.set_root (&get_state());
@@ -776,8 +821,10 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        if (snapshot_name.empty()) {
                snapshot_name = _current_snapshot_name;
        } else if (switch_to_snapshot) {
        if (snapshot_name.empty()) {
                snapshot_name = _current_snapshot_name;
        } else if (switch_to_snapshot) {
-                _current_snapshot_name = snapshot_name;
-        }
+               set_snapshot_name (snapshot_name);
+       }
+
+       assert (!snapshot_name.empty());
 
        if (!pending) {
 
 
        if (!pending) {
 
@@ -802,7 +849,7 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        tmp_path = Glib::build_filename (tmp_path, legalize_for_path (snapshot_name) + temp_suffix);
 
        cerr << "actually writing state to " << tmp_path << endl;
        tmp_path = Glib::build_filename (tmp_path, legalize_for_path (snapshot_name) + temp_suffix);
 
        cerr << "actually writing state to " << tmp_path << endl;
-       
+
        if (!tree.write (tmp_path)) {
                error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg;
                if (g_remove (tmp_path.c_str()) != 0) {
        if (!tree.write (tmp_path)) {
                error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg;
                if (g_remove (tmp_path.c_str()) != 0) {
@@ -814,7 +861,7 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        } else {
 
                cerr << "renaming state to " << xml_path << endl;
        } else {
 
                cerr << "renaming state to " << xml_path << endl;
-               
+
                if (::g_rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
                        error << string_compose (_("could not rename temporary session file %1 to %2 (%3)"),
                                        tmp_path, xml_path, g_strerror(errno)) << endmsg;
                if (::g_rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
                        error << string_compose (_("could not rename temporary session file %1 to %2 (%3)"),
                                        tmp_path, xml_path, g_strerror(errno)) << endmsg;
@@ -830,17 +877,23 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
 
                save_history (snapshot_name);
 
 
                save_history (snapshot_name);
 
-               bool was_dirty = dirty();
+               if (mark_as_clean) {
+                       bool was_dirty = dirty();
 
 
-               _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+                       _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
 
 
-               if (was_dirty) {
-                       DirtyChanged (); /* EMIT SIGNAL */
+                       if (was_dirty) {
+                               DirtyChanged (); /* EMIT SIGNAL */
+                       }
                }
 
                StateSaved (snapshot_name); /* EMIT SIGNAL */
        }
 
                }
 
                StateSaved (snapshot_name); /* EMIT SIGNAL */
        }
 
+#ifndef NDEBUG
+       const int64_t elapsed_time_us = g_get_monotonic_time() - save_start_time;
+       cerr << "saved state in " << fixed << setprecision (1) << elapsed_time_us / 1000. << " ms\n";
+#endif
        return 0;
 }
 
        return 0;
 }
 
@@ -902,7 +955,7 @@ Session::load_state (string snapshot_name)
                return -1;
        }
 
                return -1;
        }
 
-       XMLNode& root (*state_tree->root());
+       XMLNode const & root (*state_tree->root());
 
        if (root.name() != X_("Session")) {
                error << string_compose (_("Session file %1 is not a session"), xmlpath) << endmsg;
 
        if (root.name() != X_("Session")) {
                error << string_compose (_("Session file %1 is not a session"), xmlpath) << endmsg;
@@ -911,7 +964,7 @@ Session::load_state (string snapshot_name)
                return -1;
        }
 
                return -1;
        }
 
-       const XMLProperty* prop;
+       XMLProperty const * prop;
 
        if ((prop = root.property ("version")) == 0) {
                /* no version implies very old version of Ardour */
 
        if ((prop = root.property ("version")) == 0) {
                /* no version implies very old version of Ardour */
@@ -938,22 +991,24 @@ Session::load_state (string snapshot_name)
                // only create a backup for a given statefile version once
 
                if (!Glib::file_test (backup_path, Glib::FILE_TEST_EXISTS)) {
                // only create a backup for a given statefile version once
 
                if (!Glib::file_test (backup_path, Glib::FILE_TEST_EXISTS)) {
-                       
+
                        VersionMismatch (xmlpath, backup_path);
                        VersionMismatch (xmlpath, backup_path);
-                       
+
                        if (!copy_file (xmlpath, backup_path)) {;
                                return -1;
                        }
                }
        }
 
                        if (!copy_file (xmlpath, backup_path)) {;
                                return -1;
                        }
                }
        }
 
+       save_snapshot_name (snapshot_name);
+
        return 0;
 }
 
 int
 Session::load_options (const XMLNode& node)
 {
        return 0;
 }
 
 int
 Session::load_options (const XMLNode& node)
 {
-       LocaleGuard lg (X_("C"));
+       LocaleGuard lg;
        config.set_variables (node);
        return 0;
 }
        config.set_variables (node);
        return 0;
 }
@@ -986,6 +1041,7 @@ Session::get_template()
 XMLNode&
 Session::state (bool full_state)
 {
 XMLNode&
 Session::state (bool full_state)
 {
+       LocaleGuard lg;
        XMLNode* node = new XMLNode("Session");
        XMLNode* child;
 
        XMLNode* node = new XMLNode("Session");
        XMLNode* child;
 
@@ -998,7 +1054,7 @@ Session::state (bool full_state)
        if (full_state) {
 
                node->add_property ("name", _name);
        if (full_state) {
 
                node->add_property ("name", _name);
-               snprintf (buf, sizeof (buf), "%" PRId64, _nominal_frame_rate);
+               snprintf (buf, sizeof (buf), "%" PRId64, _base_frame_rate);
                node->add_property ("sample-rate", buf);
 
                if (session_dirs.size() > 1) {
                node->add_property ("sample-rate", buf);
 
                if (session_dirs.size() > 1) {
@@ -1036,11 +1092,19 @@ Session::state (bool full_state)
        snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter());
        node->add_property ("id-counter", buf);
 
        snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter());
        node->add_property ("id-counter", buf);
 
+       snprintf (buf, sizeof (buf), "%u", name_id_counter ());
+       node->add_property ("name-counter", buf);
+
        /* save the event ID counter */
 
        snprintf (buf, sizeof (buf), "%d", Evoral::event_id_counter());
        node->add_property ("event-counter", buf);
 
        /* save the event ID counter */
 
        snprintf (buf, sizeof (buf), "%d", Evoral::event_id_counter());
        node->add_property ("event-counter", buf);
 
+       /* save the VCA counter */
+
+       snprintf (buf, sizeof (buf), "%" PRIu32, VCA::get_next_vca_number());
+       node->add_property ("vca-counter", buf);
+
        /* various options */
 
        list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
        /* various options */
 
        list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
@@ -1116,13 +1180,13 @@ Session::state (bool full_state)
                        }
                }
        }
                        }
                }
        }
-       
-       
+
+
 
        if (full_state) {
 
        if (full_state) {
-               
+
                if (_locations) {
                if (_locations) {
-                       node->add_child_nocopy (_locations->get_state());       
+                       node->add_child_nocopy (_locations->get_state());
                }
        } else {
                Locations loc (*this);
                }
        } else {
                Locations loc (*this);
@@ -1132,7 +1196,7 @@ Session::state (bool full_state)
                range->set (max_framepos, 0);
                loc.add (range);
                XMLNode& locations_state = loc.get_state();
                range->set (max_framepos, 0);
                loc.add (range);
                XMLNode& locations_state = loc.get_state();
-               
+
                if (ARDOUR::Profile->get_trx() && _locations) {
                        // For tracks we need stored the Auto Loop Range and all MIDI markers.
                        for (Locations::LocationList::const_iterator i = _locations->list ().begin (); i != _locations->list ().end (); ++i) {
                if (ARDOUR::Profile->get_trx() && _locations) {
                        // For tracks we need stored the Auto Loop Range and all MIDI markers.
                        for (Locations::LocationList::const_iterator i = _locations->list ().begin (); i != _locations->list ().end (); ++i) {
@@ -1155,6 +1219,8 @@ Session::state (bool full_state)
                }
        }
 
                }
        }
 
+       node->add_child_nocopy (_vca_manager->get_state());
+
        child = node->add_child ("Routes");
        {
                boost::shared_ptr<RouteList> r = routes.reader ();
        child = node->add_child ("Routes");
        {
                boost::shared_ptr<RouteList> r = routes.reader ();
@@ -1162,9 +1228,9 @@ Session::state (bool full_state)
                RoutePublicOrderSorter cmp;
                RouteList public_order (*r);
                public_order.sort (cmp);
                RoutePublicOrderSorter cmp;
                RouteList public_order (*r);
                public_order.sort (cmp);
-               
+
                /* the sort should have put control outs first */
                /* the sort should have put control outs first */
-               
+
                if (_monitor_out) {
                        assert (_monitor_out == public_order.front());
                }
                if (_monitor_out) {
                        assert (_monitor_out == public_order.front());
                }
@@ -1211,6 +1277,26 @@ Session::state (bool full_state)
                node->add_child_copy (*_extra_xml);
        }
 
                node->add_child_copy (*_extra_xml);
        }
 
+       {
+               Glib::Threads::Mutex::Lock lm (lua_lock);
+               std::string saved;
+               {
+                       luabridge::LuaRef savedstate ((*_lua_save)());
+                       saved = savedstate.cast<std::string>();
+               }
+               lua.collect_garbage ();
+               lm.release ();
+
+               gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
+               std::string b64s (b64);
+               g_free (b64);
+
+               XMLNode* script_node = new XMLNode (X_("Script"));
+               script_node->add_property (X_("lua"), LUA_VERSION);
+               script_node->add_content (b64s);
+               node->add_child_nocopy (*script_node);
+       }
+
        return *node;
 }
 
        return *node;
 }
 
@@ -1224,9 +1310,10 @@ Session::get_control_protocol_state ()
 int
 Session::set_state (const XMLNode& node, int version)
 {
 int
 Session::set_state (const XMLNode& node, int version)
 {
+       LocaleGuard lg;
        XMLNodeList nlist;
        XMLNode* child;
        XMLNodeList nlist;
        XMLNode* child;
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        int ret = -1;
 
        _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
        int ret = -1;
 
        _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
@@ -1242,10 +1329,12 @@ Session::set_state (const XMLNode& node, int version)
 
        if ((prop = node.property (X_("sample-rate"))) != 0) {
 
 
        if ((prop = node.property (X_("sample-rate"))) != 0) {
 
-               _nominal_frame_rate = atoi (prop->value());
+               _base_frame_rate = atoi (prop->value());
+               _nominal_frame_rate = _base_frame_rate;
 
 
-               if (_nominal_frame_rate != _current_frame_rate) {
-                        boost::optional<int> r = AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate);
+               assert (AudioEngine::instance()->running ());
+               if (_base_frame_rate != AudioEngine::instance()->sample_rate ()) {
+                       boost::optional<int> r = AskAboutSampleRateMismatch (_base_frame_rate, _current_frame_rate);
                        if (r.get_value_or (0)) {
                                goto out;
                        }
                        if (r.get_value_or (0)) {
                                goto out;
                        }
@@ -1268,10 +1357,21 @@ Session::set_state (const XMLNode& node, int version)
                ID::init_counter (now);
        }
 
                ID::init_counter (now);
        }
 
-        if ((prop = node.property (X_("event-counter"))) != 0) {
-                Evoral::init_event_id_counter (atoi (prop->value()));
-        }
+       if ((prop = node.property (X_("name-counter"))) != 0) {
+               init_name_id_counter (atoi (prop->value()));
+       }
+
+       if ((prop = node.property (X_("event-counter"))) != 0) {
+               Evoral::init_event_id_counter (atoi (prop->value()));
+       }
 
 
+       if ((prop = node.property (X_("vca-counter"))) != 0) {
+               uint32_t x;
+               sscanf (prop->value().c_str(), "%" PRIu32, &x);
+               VCA::set_next_vca_number (x);
+       } else {
+               VCA::set_next_vca_number (1);
+       }
 
        if ((child = find_named_node (node, "MIDIPorts")) != 0) {
                _midi_ports->set_midi_port_states (child->children());
 
        if ((child = find_named_node (node, "MIDIPorts")) != 0) {
                _midi_ports->set_midi_port_states (child->children());
@@ -1375,6 +1475,10 @@ Session::set_state (const XMLNode& node, int version)
                }
        }
 
                }
        }
 
+       if ((child = find_named_node (node, VCAManager::xml_node_name)) != 0) {
+               _vca_manager->set_state (*child, version);
+       }
+
        if ((child = find_named_node (node, "Routes")) == 0) {
                error << _("Session: XML state has no routes section") << endmsg;
                goto out;
        if ((child = find_named_node (node, "Routes")) == 0) {
                error << _("Session: XML state has no routes section") << endmsg;
                goto out;
@@ -1382,6 +1486,10 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
 
                goto out;
        }
 
+       /* Now that we have Routes and masters loaded, connect them if appropriate */
+
+       Slavable::Assign (_vca_manager); /* EMIT SIGNAL */
+
        /* our diskstreams list is no longer needed as they are now all owned by their Route */
        _diskstreams_2X.clear ();
 
        /* our diskstreams list is no longer needed as they are now all owned by their Route */
        _diskstreams_2X.clear ();
 
@@ -1421,9 +1529,25 @@ Session::set_state (const XMLNode& node, int version)
                ControlProtocolManager::instance().set_state (*child, version);
        }
 
                ControlProtocolManager::instance().set_state (*child, version);
        }
 
+       if ((child = find_named_node (node, "Script"))) {
+               for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
+                       if (!(*n)->is_content ()) { continue; }
+                       gsize size;
+                       guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
+                       try {
+                               Glib::Threads::Mutex::Lock lm (lua_lock);
+                               (*_lua_load)(std::string ((const char*)buf, size));
+                       } catch (luabridge::LuaException const& e) {
+                               cerr << "LuaException:" << e.what () << endl;
+                       }
+                       g_free (buf);
+               }
+       }
+
        update_route_record_state ();
 
        /* here beginneth the second phase ... */
        update_route_record_state ();
 
        /* here beginneth the second phase ... */
+       set_snapshot_name (_current_snapshot_name);
 
        StateReady (); /* EMIT SIGNAL */
 
 
        StateReady (); /* EMIT SIGNAL */
 
@@ -1469,7 +1593,7 @@ Session::load_routes (const XMLNode& node, int version)
 
        BootMessage (_("Tracks/busses loaded;  Adding to Session"));
 
 
        BootMessage (_("Tracks/busses loaded;  Adding to Session"));
 
-       add_routes (new_routes, false, false, false);
+       add_routes (new_routes, false, false, false, PresentationInfo::max_order);
 
        BootMessage (_("Finished adding tracks/busses"));
 
 
        BootMessage (_("Finished adding tracks/busses"));
 
@@ -1488,7 +1612,7 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
        XMLNode* ds_child = find_named_node (node, X_("Diskstream"));
 
        DataType type = DataType::AUDIO;
        XMLNode* ds_child = find_named_node (node, X_("Diskstream"));
 
        DataType type = DataType::AUDIO;
-       const XMLProperty* prop = node.property("default-type");
+       XMLProperty const * prop = node.property("default-type");
 
        if (prop) {
                type = DataType (prop->value());
 
        if (prop) {
                type = DataType (prop->value());
@@ -1514,24 +1638,15 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
                         return ret;
                 }
 
                         return ret;
                 }
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+                BOOST_MARK_TRACK (track);
                 ret = track;
 
        } else {
                 ret = track;
 
        } else {
-               enum Route::Flag flags = Route::Flag(0);
-               const XMLProperty* prop = node.property("flags");
-               if (prop) {
-                       flags = Route::Flag (string_2_enum (prop->value(), flags));
-               }
-
+               PresentationInfo::Flag flags = PresentationInfo::get_flags (node);
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                        // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
+                       BOOST_MARK_ROUTE (r);
                         ret = r;
                 }
        }
                         ret = r;
                 }
        }
@@ -1554,7 +1669,7 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
        }
 
        DataType type = DataType::AUDIO;
        }
 
        DataType type = DataType::AUDIO;
-       const XMLProperty* prop = node.property("default-type");
+       XMLProperty const * prop = node.property("default-type");
 
        if (prop) {
                type = DataType (prop->value());
 
        if (prop) {
                type = DataType (prop->value());
@@ -1592,24 +1707,15 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
 
                track->set_diskstream (*i);
 
 
                track->set_diskstream (*i);
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+               BOOST_MARK_TRACK (track);
                 ret = track;
 
        } else {
                 ret = track;
 
        } else {
-               enum Route::Flag flags = Route::Flag(0);
-               const XMLProperty* prop = node.property("flags");
-               if (prop) {
-                       flags = Route::Flag (string_2_enum (prop->value(), flags));
-               }
-
+               PresentationInfo::Flag flags = PresentationInfo::get_flags (node);
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                        // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
+                       BOOST_MARK_ROUTE (r);
                         ret = r;
                 }
        }
                         ret = r;
                 }
        }
@@ -1631,7 +1737,7 @@ Session::load_regions (const XMLNode& node)
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                if ((region = XMLRegionFactory (**niter, false)) == 0) {
                        error << _("Session: cannot create Region from XML description.");
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                if ((region = XMLRegionFactory (**niter, false)) == 0) {
                        error << _("Session: cannot create Region from XML description.");
-                       const XMLProperty *name = (**niter).property("name");
+                       XMLProperty const * name = (**niter).property("name");
 
                        if (name) {
                                error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
 
                        if (name) {
                                error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
@@ -1649,7 +1755,7 @@ Session::load_compounds (const XMLNode& node)
 {
        XMLNodeList calist = node.children();
        XMLNodeConstIterator caiter;
 {
        XMLNodeList calist = node.children();
        XMLNodeConstIterator caiter;
-       XMLProperty *caprop;
+       XMLProperty const * caprop;
 
        for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
                XMLNode* ca = *caiter;
 
        for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
                XMLNode* ca = *caiter;
@@ -1693,10 +1799,10 @@ Session::load_nested_sources (const XMLNode& node)
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                if ((*niter)->name() == "Source") {
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                if ((*niter)->name() == "Source") {
 
-                       /* it may already exist, so don't recreate it unnecessarily 
+                       /* it may already exist, so don't recreate it unnecessarily
                         */
 
                         */
 
-                       XMLProperty* prop = (*niter)->property (X_("id"));
+                       XMLProperty const * prop = (*niter)->property (X_("id"));
                        if (!prop) {
                                error << _("Nested source has no ID info in session file! (ignored)") << endmsg;
                                continue;
                        if (!prop) {
                                error << _("Nested source has no ID info in session file! (ignored)") << endmsg;
                                continue;
@@ -1720,7 +1826,7 @@ Session::load_nested_sources (const XMLNode& node)
 boost::shared_ptr<Region>
 Session::XMLRegionFactory (const XMLNode& node, bool full)
 {
 boost::shared_ptr<Region>
 Session::XMLRegionFactory (const XMLNode& node, bool full)
 {
-       const XMLProperty* type = node.property("type");
+       XMLProperty const * type = node.property("type");
 
        try {
 
 
        try {
 
@@ -1749,7 +1855,7 @@ Session::XMLRegionFactory (const XMLNode& node, bool full)
 boost::shared_ptr<AudioRegion>
 Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
 {
 boost::shared_ptr<AudioRegion>
 Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
 {
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        boost::shared_ptr<Source> source;
        boost::shared_ptr<AudioSource> as;
        SourceList sources;
        boost::shared_ptr<Source> source;
        boost::shared_ptr<AudioSource> as;
        SourceList sources;
@@ -1868,7 +1974,7 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
 boost::shared_ptr<MidiRegion>
 Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
 {
 boost::shared_ptr<MidiRegion>
 Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
 {
-       const XMLProperty* prop;
+       XMLProperty const * prop;
        boost::shared_ptr<Source> source;
        boost::shared_ptr<MidiSource> ms;
        SourceList sources;
        boost::shared_ptr<Source> source;
        boost::shared_ptr<MidiSource> ms;
        SourceList sources;
@@ -2067,7 +2173,7 @@ Session::XMLSourceFactory (const XMLNode& node)
 }
 
 int
 }
 
 int
-Session::save_template (string template_name)
+Session::save_template (string template_name, bool replace_existing)
 {
        if ((_state_of_the_state & CannotSave) || template_name.empty ()) {
                return -1;
 {
        if ((_state_of_the_state & CannotSave) || template_name.empty ()) {
                return -1;
@@ -2093,12 +2199,12 @@ Session::save_template (string template_name)
        }
 
        if (!ARDOUR::Profile->get_trx()) {
        }
 
        if (!ARDOUR::Profile->get_trx()) {
-               if (Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
+               if (!replace_existing && Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
                        warning << string_compose(_("Template \"%1\" already exists - new version not created"),
                                                                          template_dir_path) << endmsg;
                        warning << string_compose(_("Template \"%1\" already exists - new version not created"),
                                                                          template_dir_path) << endmsg;
-                       return -1;
+                       return -2;
                }
                }
-               
+
                if (g_mkdir_with_parents (template_dir_path.c_str(), 0755) != 0) {
                        error << string_compose(_("Could not create directory for Session template\"%1\" (%2)"),
                                                                        template_dir_path, g_strerror (errno)) << endmsg;
                if (g_mkdir_with_parents (template_dir_path.c_str(), 0755) != 0) {
                        error << string_compose(_("Could not create directory for Session template\"%1\" (%2)"),
                                                                        template_dir_path, g_strerror (errno)) << endmsg;
@@ -2108,7 +2214,7 @@ Session::save_template (string template_name)
 
        /* file to write */
        std::string template_file_path;
 
        /* file to write */
        std::string template_file_path;
-       
+
        if (ARDOUR::Profile->get_trx()) {
                template_file_path = template_name;
        } else {
        if (ARDOUR::Profile->get_trx()) {
                template_file_path = template_name;
        } else {
@@ -2120,28 +2226,19 @@ Session::save_template (string template_name)
        }
 
        SessionSaveUnderway (); /* EMIT SIGNAL */
        }
 
        SessionSaveUnderway (); /* EMIT SIGNAL */
-       
+
        XMLTree tree;
 
        XMLTree tree;
 
-       tree.set_root (&get_template());
+       {
+               PBD::Unwinder<std::string> uw (_template_state_dir, template_dir_path);
+               tree.set_root (&get_template());
+       }
+
        if (!tree.write (template_file_path)) {
                error << _("template not saved") << endmsg;
                return -1;
        }
 
        if (!tree.write (template_file_path)) {
                error << _("template not saved") << endmsg;
                return -1;
        }
 
-       if (!ARDOUR::Profile->get_trx()) {
-               /* copy plugin state directory */
-
-               std::string template_plugin_state_path (Glib::build_filename (template_dir_path, X_("plugins")));
-
-               if (g_mkdir_with_parents (template_plugin_state_path.c_str(), 0755) != 0) {
-                       error << string_compose(_("Could not create directory for Session template plugin state\"%1\" (%2)"),
-                                                                       template_plugin_state_path, g_strerror (errno)) << endmsg;
-                       return -1;
-               }
-               copy_files (plugins_dir(), template_plugin_state_path);
-       }
-
        store_recent_templates (template_file_path);
 
        return 0;
        store_recent_templates (template_file_path);
 
        return 0;
@@ -2151,7 +2248,7 @@ void
 Session::refresh_disk_space ()
 {
 #if __APPLE__ || (HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H)
 Session::refresh_disk_space ()
 {
 #if __APPLE__ || (HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H)
-       
+
        Glib::Threads::Mutex::Lock lm (space_lock);
 
        /* get freespace on every FS that is part of the session path */
        Glib::Threads::Mutex::Lock lm (space_lock);
 
        /* get freespace on every FS that is part of the session path */
@@ -2539,6 +2636,15 @@ Session::add_command (Command* const cmd)
                            cmd->name ()));
        _current_trans->add_command (cmd);
 }
                            cmd->name ()));
        _current_trans->add_command (cmd);
 }
+
+PBD::StatefulDiffCommand*
+Session::add_stateful_diff_command (boost::shared_ptr<PBD::StatefulDestructible> sfd)
+{
+       PBD::StatefulDiffCommand* cmd = new PBD::StatefulDiffCommand (sfd);
+       add_command (cmd);
+       return cmd;
+}
+
 void
 Session::begin_reversible_command (const string& name)
 {
 void
 Session::begin_reversible_command (const string& name)
 {
@@ -2700,7 +2806,7 @@ Session::find_all_sources (string path, set<string>& result)
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-               XMLProperty* prop;
+               XMLProperty const * prop;
 
                if ((prop = (*niter)->property (X_("type"))) == 0) {
                        continue;
 
                if ((prop = (*niter)->property (X_("type"))) == 0) {
                        continue;
@@ -2791,25 +2897,34 @@ Session::cleanup_regions ()
        bool removed = false;
        const RegionFactory::RegionMap& regions (RegionFactory::regions());
 
        bool removed = false;
        const RegionFactory::RegionMap& regions (RegionFactory::regions());
 
-       for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+       for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end();) {
 
                uint32_t used = playlists->region_use_count (i->second);
 
                if (used == 0 && !i->second->automatic ()) {
 
                uint32_t used = playlists->region_use_count (i->second);
 
                if (used == 0 && !i->second->automatic ()) {
+                       boost::weak_ptr<Region> w = i->second;
+                       ++i;
                        removed = true;
                        removed = true;
-                       RegionFactory::map_remove (i->second);
+                       RegionFactory::map_remove (w);
+               } else {
+                       ++i;
                }
        }
 
        if (removed) {
                // re-check to remove parent references of compound regions
                }
        }
 
        if (removed) {
                // re-check to remove parent references of compound regions
-               for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+               for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end();) {
                        if (!(i->second->whole_file() && i->second->max_source_level() > 0)) {
                        if (!(i->second->whole_file() && i->second->max_source_level() > 0)) {
+                               ++i;
                                continue;
                        }
                        assert(boost::dynamic_pointer_cast<PlaylistSource>(i->second->source (0)) != 0);
                        if (0 == playlists->region_use_count (i->second)) {
                                continue;
                        }
                        assert(boost::dynamic_pointer_cast<PlaylistSource>(i->second->source (0)) != 0);
                        if (0 == playlists->region_use_count (i->second)) {
-                               RegionFactory::map_remove (i->second);
+                               boost::weak_ptr<Region> w = i->second;
+                               ++i;
+                               RegionFactory::map_remove (w);
+                       } else {
+                               ++i;
                        }
                }
        }
                        }
                }
        }
@@ -2820,6 +2935,66 @@ Session::cleanup_regions ()
        save_state ("");
 }
 
        save_state ("");
 }
 
+bool
+Session::can_cleanup_peakfiles () const
+{
+       if (deletion_in_progress()) {
+               return false;
+       }
+       if (!_writable || (_state_of_the_state & CannotSave)) {
+               warning << _("Cannot cleanup peak-files for read-only session.") << endmsg;
+               return false;
+       }
+        if (record_status() == Recording) {
+               error << _("Cannot cleanup peak-files while recording") << endmsg;
+               return false;
+       }
+       return true;
+}
+
+int
+Session::cleanup_peakfiles ()
+{
+       Glib::Threads::Mutex::Lock lm (peak_cleanup_lock, Glib::Threads::TRY_LOCK);
+       if (!lm.locked()) {
+               return -1;
+       }
+
+       assert (can_cleanup_peakfiles ());
+       assert (!peaks_cleanup_in_progres());
+
+       _state_of_the_state = StateOfTheState (_state_of_the_state | PeakCleanup);
+
+       int timeout = 5000; // 5 seconds
+       while (!SourceFactory::files_with_peaks.empty()) {
+               Glib::usleep (1000);
+               if (--timeout < 0) {
+                       warning << _("Timeout waiting for peak-file creation to terminate before cleanup, please try again later.") << endmsg;
+                       _state_of_the_state = StateOfTheState (_state_of_the_state & (~PeakCleanup));
+                       return -1;
+               }
+       }
+
+       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+               boost::shared_ptr<AudioSource> as;
+               if ((as = boost::dynamic_pointer_cast<AudioSource> (i->second)) != 0) {
+                       as->close_peakfile();
+               }
+       }
+
+       PBD::clear_directory (session_directory().peak_path());
+
+       _state_of_the_state = StateOfTheState (_state_of_the_state & (~PeakCleanup));
+
+       for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+               boost::shared_ptr<AudioSource> as;
+               if ((as = boost::dynamic_pointer_cast<AudioSource> (i->second)) != 0) {
+                       SourceFactory::setup_peakfile(as, true);
+               }
+       }
+       return 0;
+}
+
 int
 Session::cleanup_sources (CleanupReport& rep)
 {
 int
 Session::cleanup_sources (CleanupReport& rep)
 {
@@ -2841,6 +3016,17 @@ Session::cleanup_sources (CleanupReport& rep)
 
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
 
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
+       /* this is mostly for windows which doesn't allow file
+        * renaming if the file is in use. But we don't special
+        * case it because we need to know if this causes
+        * problems, and the easiest way to notice that is to
+        * keep it in place for all platforms.
+        */
+
+       request_stop (false);
+       _butler->summon ();
+       _butler->wait_until_finished ();
+
        /* consider deleting all unused playlists */
 
        if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
        /* consider deleting all unused playlists */
 
        if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
@@ -2914,22 +3100,31 @@ Session::cleanup_sources (CleanupReport& rep)
 
                if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
 
 
                if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
 
+                       /* this is mostly for windows which doesn't allow file
+                        * renaming if the file is in use. But we don't special
+                        * case it because we need to know if this causes
+                        * problems, and the easiest way to notice that is to
+                        * keep it in place for all platforms.
+                        */
+
+                       fs->close ();
+
                        if (!fs->is_stub()) {
 
                                if (playlists->source_use_count (fs) != 0) {
                                        all_sources.insert (fs->path());
                                } else {
                        if (!fs->is_stub()) {
 
                                if (playlists->source_use_count (fs) != 0) {
                                        all_sources.insert (fs->path());
                                } else {
-                                       
+
                                        /* we might not remove this source from disk, because it may be used
                                           by other snapshots, but its not being used in this version
                                           so lets get rid of it now, along with any representative regions
                                           in the region list.
                                        */
                                        /* we might not remove this source from disk, because it may be used
                                           by other snapshots, but its not being used in this version
                                           so lets get rid of it now, along with any representative regions
                                           in the region list.
                                        */
-                                       
+
                                        RegionFactory::remove_regions_using_source (i->second);
                                        RegionFactory::remove_regions_using_source (i->second);
-                                       
+
                                        // also remove source from all_sources
                                        // also remove source from all_sources
-                                       
+
                                        for (set<string>::iterator j = all_sources.begin(); j != all_sources.end(); ++j) {
                                                spath = Glib::path_get_basename (*j);
                                                if (spath == i->second->name()) {
                                        for (set<string>::iterator j = all_sources.begin(); j != all_sources.end(); ++j) {
                                                spath = Glib::path_get_basename (*j);
                                                if (spath == i->second->name()) {
@@ -2970,7 +3165,7 @@ Session::cleanup_sources (CleanupReport& rep)
        /* now try to move all unused files into the "dead" directory(ies) */
 
        for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
        /* now try to move all unused files into the "dead" directory(ies) */
 
        for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
-               struct stat statbuf;
+               GStatBuf statbuf;
 
                string newpath;
 
 
                string newpath;
 
@@ -3035,7 +3230,7 @@ Session::cleanup_sources (CleanupReport& rep)
 
                }
 
 
                }
 
-               stat ((*x).c_str(), &statbuf);
+               g_stat ((*x).c_str(), &statbuf);
 
                if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
                        error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"),
 
                if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
                        error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"),
@@ -3066,8 +3261,8 @@ Session::cleanup_sources (CleanupReport& rep)
                }
 
                rep.paths.push_back (*x);
                }
 
                rep.paths.push_back (*x);
-                rep.space += statbuf.st_size;
-        }
+               rep.space += statbuf.st_size;
+       }
 
        /* dump the history list */
 
 
        /* dump the history list */
 
@@ -3218,7 +3413,11 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        }
 
        case ControllableDescriptor::RemoteControlID:
        }
 
        case ControllableDescriptor::RemoteControlID:
-               r = route_by_remote_id (desc.rid());
+               r = get_remote_nth_route (desc.rid());
+               break;
+
+       case ControllableDescriptor::SelectionCount:
+               r = route_by_selected_count (desc.selection_id());
                break;
        }
 
                break;
        }
 
@@ -3254,22 +3453,16 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        }
 
        case ControllableDescriptor::PanDirection:
        }
 
        case ControllableDescriptor::PanDirection:
-        {
-                c = r->pannable()->pan_azimuth_control;
+               c = r->pan_azimuth_control();
                break;
                break;
-        }
 
        case ControllableDescriptor::PanWidth:
 
        case ControllableDescriptor::PanWidth:
-        {
-                c = r->pannable()->pan_width_control;
+               c = r->pan_width_control();
                break;
                break;
-        }
 
        case ControllableDescriptor::PanElevation:
 
        case ControllableDescriptor::PanElevation:
-        {
-                c = r->pannable()->pan_elevation_control;
+               c = r->pan_elevation_control();
                break;
                break;
-        }
 
        case ControllableDescriptor::Balance:
                /* XXX simple pan control */
 
        case ControllableDescriptor::Balance:
                /* XXX simple pan control */
@@ -3299,26 +3492,12 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                break;
        }
 
                break;
        }
 
-       case ControllableDescriptor::SendGain:
-       {
+       case ControllableDescriptor::SendGain: {
                uint32_t send = desc.target (0);
                uint32_t send = desc.target (0);
-
-               /* revert to zero-based counting */
-
                if (send > 0) {
                        --send;
                }
                if (send > 0) {
                        --send;
                }
-
-               boost::shared_ptr<Processor> p = r->nth_send (send);
-
-               if (p) {
-                       boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(p);
-                       boost::shared_ptr<Amp> a = s->amp();
-                       
-                       if (a) {
-                               c = s->amp()->gain_control();
-                       }
-               }
+               c = r->send_level_controllable (send);
                break;
        }
 
                break;
        }
 
@@ -3357,7 +3536,7 @@ Session::save_history (string snapshot_name)
                return 0;
        }
 
                return 0;
        }
 
-       if (!Config->get_save_history() || Config->get_saved_history_depth() < 0 || 
+       if (!Config->get_save_history() || Config->get_saved_history_depth() < 0 ||
            (_history.undo_depth() == 0 && _history.redo_depth() == 0)) {
                return 0;
        }
            (_history.undo_depth() == 0 && _history.redo_depth() == 0)) {
                return 0;
        }
@@ -3619,9 +3798,9 @@ Session::config_changed (std::string p, bool ours)
                }
 
        } else if (p == "click-gain") {
                }
 
        } else if (p == "click-gain") {
-               
+
                if (_click_gain) {
                if (_click_gain) {
-                       _click_gain->set_gain (Config->get_click_gain(), this);
+                       _click_gain->gain_control()->set_value (Config->get_click_gain(), Controllable::NoGroup);
                }
 
        } else if (p == "send-mtc") {
                }
 
        } else if (p == "send-mtc") {
@@ -3690,7 +3869,7 @@ Session::config_changed (std::string p, bool ours)
        } else if (p == "solo-control-is-listen-control") {
                solo_control_mode_changed ();
        } else if (p == "solo-mute-gain") {
        } else if (p == "solo-control-is-listen-control") {
                solo_control_mode_changed ();
        } else if (p == "solo-mute-gain") {
-               _solo_cut_control->Changed();
+               _solo_cut_control->Changed (true, Controllable::NoGroup);
        } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
                last_timecode_valid = false;
        } else if (p == "playback-buffer-seconds") {
        } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
                last_timecode_valid = false;
        } else if (p == "playback-buffer-seconds") {
@@ -3748,7 +3927,20 @@ void
 Session::setup_midi_machine_control ()
 {
        _mmc = new MIDI::MachineControl;
 Session::setup_midi_machine_control ()
 {
        _mmc = new MIDI::MachineControl;
-       _mmc->set_ports (_midi_ports->mmc_input_port(), _midi_ports->mmc_output_port());
+
+       boost::shared_ptr<AsyncMIDIPort> async_in = boost::dynamic_pointer_cast<AsyncMIDIPort> (_midi_ports->mmc_input_port());
+       boost::shared_ptr<AsyncMIDIPort> async_out = boost::dynamic_pointer_cast<AsyncMIDIPort> (_midi_ports->mmc_output_port());
+
+       if (!async_out || !async_out) {
+               return;
+       }
+
+       /* XXXX argh, passing raw pointers back into libmidi++ */
+
+       MIDI::Port* mmc_in = async_in.get();
+       MIDI::Port* mmc_out = async_out.get();
+
+       _mmc->set_ports (mmc_in, mmc_out);
 
        _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
        _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
 
        _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
        _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
@@ -3786,6 +3978,27 @@ Session::solo_cut_control() const
         return _solo_cut_control;
 }
 
         return _solo_cut_control;
 }
 
+void
+Session::save_snapshot_name (const std::string & n)
+{
+       /* assure Stateful::_instant_xml is loaded
+        * add_instant_xml() only adds to existing data and defaults
+        * to use an empty Tree otherwise
+        */
+       instant_xml ("LastUsedSnapshot");
+
+       XMLNode* last_used_snapshot = new XMLNode ("LastUsedSnapshot");
+       last_used_snapshot->add_property ("name", string(n));
+       add_instant_xml (*last_used_snapshot, false);
+}
+
+void
+Session::set_snapshot_name (const std::string & n)
+{
+       _current_snapshot_name = n;
+       save_snapshot_name (n);
+}
+
 int
 Session::rename (const std::string& new_name)
 {
 int
 Session::rename (const std::string& new_name)
 {
@@ -3814,7 +4027,7 @@ Session::rename (const std::string& new_name)
         * interchange subdirectory
         * session file
         * session history
         * interchange subdirectory
         * session file
         * session history
-        
+
         * Backup files are left unchanged and not renamed.
         */
 
         * Backup files are left unchanged and not renamed.
         */
 
@@ -3831,30 +4044,30 @@ Session::rename (const std::string& new_name)
                        fs->close ();
                }
        }
                        fs->close ();
                }
        }
-       
+
        /* pass one: not 100% safe check that the new directory names don't
         * already exist ...
         */
 
        for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
        /* pass one: not 100% safe check that the new directory names don't
         * already exist ...
         */
 
        for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
-               
+
                oldstr = (*i).path;
                oldstr = (*i).path;
-               
+
                /* this is a stupid hack because Glib::path_get_dirname() is
                 * lexical-only, and so passing it /a/b/c/ gives a different
                 * result than passing it /a/b/c ...
                 */
                /* this is a stupid hack because Glib::path_get_dirname() is
                 * lexical-only, and so passing it /a/b/c/ gives a different
                 * result than passing it /a/b/c ...
                 */
-               
+
                if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
                        oldstr = oldstr.substr (0, oldstr.length() - 1);
                }
                if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
                        oldstr = oldstr.substr (0, oldstr.length() - 1);
                }
-               
+
                string base = Glib::path_get_dirname (oldstr);
                string base = Glib::path_get_dirname (oldstr);
-               
+
                newstr = Glib::build_filename (base, legal_name);
                newstr = Glib::build_filename (base, legal_name);
-               
+
                cerr << "Looking for " << newstr << endl;
                cerr << "Looking for " << newstr << endl;
-               
+
                if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
                        cerr << " exists\n";
                        return -1;
                if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
                        cerr << " exists\n";
                        return -1;
@@ -3864,18 +4077,18 @@ Session::rename (const std::string& new_name)
        /* Session dirs */
 
        first = true;
        /* Session dirs */
 
        first = true;
-       
+
        for (vector<space_and_path>::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
                vector<string> v;
 
                oldstr = (*i).path;
        for (vector<space_and_path>::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
 
                vector<string> v;
 
                oldstr = (*i).path;
-               
+
                /* this is a stupid hack because Glib::path_get_dirname() is
                 * lexical-only, and so passing it /a/b/c/ gives a different
                 * result than passing it /a/b/c ...
                 */
                /* this is a stupid hack because Glib::path_get_dirname() is
                 * lexical-only, and so passing it /a/b/c/ gives a different
                 * result than passing it /a/b/c ...
                 */
-               
+
                if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
                        oldstr = oldstr.substr (0, oldstr.length() - 1);
                }
                if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
                        oldstr = oldstr.substr (0, oldstr.length() - 1);
                }
@@ -3884,8 +4097,8 @@ Session::rename (const std::string& new_name)
                newstr = Glib::build_filename (base, legal_name);
 
                cerr << "for " << oldstr << " new dir = " << newstr << endl;
                newstr = Glib::build_filename (base, legal_name);
 
                cerr << "for " << oldstr << " new dir = " << newstr << endl;
-               
-               cerr << "Rename " << oldstr << " => " << newstr << endl;                
+
+               cerr << "Rename " << oldstr << " => " << newstr << endl;
                if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
                        cerr << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
                        error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
                if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
                        cerr << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
                        error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
@@ -3893,12 +4106,12 @@ Session::rename (const std::string& new_name)
                }
 
                /* Reset path in "session dirs" */
                }
 
                /* Reset path in "session dirs" */
-               
+
                (*i).path = newstr;
                (*i).blocks = 0;
                (*i).path = newstr;
                (*i).blocks = 0;
-               
+
                /* reset primary SessionDirectory object */
                /* reset primary SessionDirectory object */
-               
+
                if (first) {
                        (*_session_dir) = newstr;
                        new_path = newstr;
                if (first) {
                        (*_session_dir) = newstr;
                        new_path = newstr;
@@ -3911,10 +4124,10 @@ Session::rename (const std::string& new_name)
                string new_interchange_dir;
 
                /* use newstr here because we renamed the path
                string new_interchange_dir;
 
                /* use newstr here because we renamed the path
-                * (folder/directory) that used to be oldstr to newstr above 
-                */     
-               
-               v.push_back (newstr); 
+                * (folder/directory) that used to be oldstr to newstr above
+                */
+
+               v.push_back (newstr);
                v.push_back (interchange_dir_name);
                v.push_back (Glib::path_get_basename (oldstr));
 
                v.push_back (interchange_dir_name);
                v.push_back (Glib::path_get_basename (oldstr));
 
@@ -3924,11 +4137,11 @@ Session::rename (const std::string& new_name)
                v.push_back (newstr);
                v.push_back (interchange_dir_name);
                v.push_back (legal_name);
                v.push_back (newstr);
                v.push_back (interchange_dir_name);
                v.push_back (legal_name);
-               
+
                new_interchange_dir = Glib::build_filename (v);
                new_interchange_dir = Glib::build_filename (v);
-               
+
                cerr << "Rename " << old_interchange_dir << " => " << new_interchange_dir << endl;
                cerr << "Rename " << old_interchange_dir << " => " << new_interchange_dir << endl;
-               
+
                if (::g_rename (old_interchange_dir.c_str(), new_interchange_dir.c_str()) != 0) {
                        cerr << string_compose (_("renaming %s as %2 failed (%3)"),
                                                 old_interchange_dir, new_interchange_dir,
                if (::g_rename (old_interchange_dir.c_str(), new_interchange_dir.c_str()) != 0) {
                        cerr << string_compose (_("renaming %s as %2 failed (%3)"),
                                                 old_interchange_dir, new_interchange_dir,
@@ -3943,11 +4156,11 @@ Session::rename (const std::string& new_name)
        }
 
        /* state file */
        }
 
        /* state file */
-       
+
        oldstr = Glib::build_filename (new_path, _current_snapshot_name + statefile_suffix);
        newstr= Glib::build_filename (new_path, legal_name + statefile_suffix);
        oldstr = Glib::build_filename (new_path, _current_snapshot_name + statefile_suffix);
        newstr= Glib::build_filename (new_path, legal_name + statefile_suffix);
-       
-       cerr << "Rename " << oldstr << " => " << newstr << endl;                
+
+       cerr << "Rename " << oldstr << " => " << newstr << endl;
 
        if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
                cerr << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
 
        if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
                cerr << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
@@ -3956,14 +4169,14 @@ Session::rename (const std::string& new_name)
        }
 
        /* history file */
        }
 
        /* history file */
-       
+
        oldstr = Glib::build_filename (new_path, _current_snapshot_name) + history_suffix;
 
        if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS))  {
                newstr = Glib::build_filename (new_path, legal_name) + history_suffix;
        oldstr = Glib::build_filename (new_path, _current_snapshot_name) + history_suffix;
 
        if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS))  {
                newstr = Glib::build_filename (new_path, legal_name) + history_suffix;
-               
-               cerr << "Rename " << oldstr << " => " << newstr << endl;                
-               
+
+               cerr << "Rename " << oldstr << " => " << newstr << endl;
+
                if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
                        cerr << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
                        error << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
                if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
                        cerr << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
                        error << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
@@ -3974,9 +4187,9 @@ Session::rename (const std::string& new_name)
        /* remove old name from recent sessions */
        remove_recent_sessions (_path);
        _path = new_path;
        /* remove old name from recent sessions */
        remove_recent_sessions (_path);
        _path = new_path;
-       
+
        /* update file source paths */
        /* update file source paths */
-       
+
        for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
                boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
                if (fs) {
        for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
                boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
                if (fs) {
@@ -3987,9 +4200,9 @@ Session::rename (const std::string& new_name)
                }
        }
 
                }
        }
 
-       _current_snapshot_name = new_name;
+       set_snapshot_name (new_name);
        _name = new_name;
        _name = new_name;
-       
+
        set_dirty ();
 
        /* save state again to get everything just right */
        set_dirty ();
 
        /* save state again to get everything just right */
@@ -4030,27 +4243,29 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo
 
        /* sample rate */
 
 
        /* sample rate */
 
-       const XMLProperty* prop;
-       if ((prop = tree.root()->property (X_("sample-rate"))) != 0) {          
+       XMLProperty const * prop;
+       XMLNode const * root (tree.root());
+
+       if ((prop = root->property (X_("sample-rate"))) != 0) {
                sample_rate = atoi (prop->value());
                found_sr = true;
        }
 
                sample_rate = atoi (prop->value());
                found_sr = true;
        }
 
-       const XMLNodeList& children (tree.root()->children());
+       const XMLNodeList& children (root->children());
        for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
                const XMLNode* child = *c;
                if (child->name() == "Config") {
                        const XMLNodeList& options (child->children());
                        for (XMLNodeList::const_iterator oc = options.begin(); oc != options.end(); ++oc) {
        for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
                const XMLNode* child = *c;
                if (child->name() == "Config") {
                        const XMLNodeList& options (child->children());
                        for (XMLNodeList::const_iterator oc = options.begin(); oc != options.end(); ++oc) {
-                               const XMLNode* option = *oc;
-                               const XMLProperty* name = option->property("name");
+                               XMLNode const * option = *oc;
+                               XMLProperty const * name = option->property("name");
 
                                if (!name) {
                                        continue;
                                }
 
                                if (name->value() == "native-file-data-format") {
 
                                if (!name) {
                                        continue;
                                }
 
                                if (name->value() == "native-file-data-format") {
-                                       const XMLProperty* value = option->property ("value");
+                                       XMLProperty const * value = option->property ("value");
                                        if (value) {
                                                SampleFormat fmt = (SampleFormat) string_2_enum (option->property ("value")->value(), fmt);
                                                data_format = fmt;
                                        if (value) {
                                                SampleFormat fmt = (SampleFormat) string_2_enum (option->property ("value")->value(), fmt);
                                                data_format = fmt;
@@ -4068,6 +4283,29 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo
        return !(found_sr && found_data_format); // zero if they are both found
 }
 
        return !(found_sr && found_data_format); // zero if they are both found
 }
 
+std::string
+Session::get_snapshot_from_instant (const std::string& session_dir)
+{
+       std::string instant_xml_path = Glib::build_filename (session_dir, "instant.xml");
+
+       if (!Glib::file_test (instant_xml_path, Glib::FILE_TEST_EXISTS)) {
+               return "";
+       }
+
+       XMLTree tree;
+       if (!tree.read (instant_xml_path)) {
+               return "";
+       }
+
+       XMLProperty const * prop;
+       XMLNode *last_used_snapshot = tree.root()->child("LastUsedSnapshot");
+       if (last_used_snapshot && (prop = last_used_snapshot->property ("name")) != 0) {
+               return prop->value();
+       }
+
+       return "";
+}
+
 typedef std::vector<boost::shared_ptr<FileSource> > SeveralFileSources;
 typedef std::map<std::string,SeveralFileSources> SourcePathMap;
 
 typedef std::vector<boost::shared_ptr<FileSource> > SeveralFileSources;
 typedef std::map<std::string,SeveralFileSources> SourcePathMap;
 
@@ -4084,20 +4322,20 @@ Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,
        {
 
                Glib::Threads::Mutex::Lock lm (source_lock);
        {
 
                Glib::Threads::Mutex::Lock lm (source_lock);
-               
+
                cerr << " total sources = " << sources.size();
                cerr << " total sources = " << sources.size();
-               
+
                for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
                        boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
                for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
                        boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
-                       
+
                        if (!fs) {
                                continue;
                        }
                        if (!fs) {
                                continue;
                        }
-                       
+
                        if (fs->within_session()) {
                                continue;
                        }
                        if (fs->within_session()) {
                                continue;
                        }
-                       
+
                        if (source_path_map.find (fs->path()) != source_path_map.end()) {
                                source_path_map[fs->path()].push_back (fs);
                        } else {
                        if (source_path_map.find (fs->path()) != source_path_map.end()) {
                                source_path_map[fs->path()].push_back (fs);
                        } else {
@@ -4105,45 +4343,45 @@ Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,
                                v.push_back (fs);
                                source_path_map.insert (make_pair (fs->path(), v));
                        }
                                v.push_back (fs);
                                source_path_map.insert (make_pair (fs->path(), v));
                        }
-                       
+
                        total++;
                }
                        total++;
                }
-               
+
                cerr << " fsources = " << total << endl;
                cerr << " fsources = " << total << endl;
-               
+
                for (SourcePathMap::iterator i = source_path_map.begin(); i != source_path_map.end(); ++i) {
                for (SourcePathMap::iterator i = source_path_map.begin(); i != source_path_map.end(); ++i) {
-                       
+
                        /* tell caller where we are */
                        /* tell caller where we are */
-                       
+
                        string old_path = i->first;
                        string old_path = i->first;
-                       
+
                        callback (n, total, old_path);
                        callback (n, total, old_path);
-                       
+
                        cerr << old_path << endl;
                        cerr << old_path << endl;
-                       
+
                        new_path.clear ();
                        new_path.clear ();
-                       
+
                        switch (i->second.front()->type()) {
                        case DataType::AUDIO:
                                new_path = new_audio_source_path_for_embedded (old_path);
                                break;
                        switch (i->second.front()->type()) {
                        case DataType::AUDIO:
                                new_path = new_audio_source_path_for_embedded (old_path);
                                break;
-                               
+
                        case DataType::MIDI:
                                /* XXX not implemented yet */
                                break;
                        }
                        case DataType::MIDI:
                                /* XXX not implemented yet */
                                break;
                        }
-                       
+
                        if (new_path.empty()) {
                                continue;
                        }
                        if (new_path.empty()) {
                                continue;
                        }
-                       
+
                        cerr << "Move " << old_path << " => " << new_path << endl;
                        cerr << "Move " << old_path << " => " << new_path << endl;
-                       
+
                        if (!copy_file (old_path, new_path)) {
                                cerr << "failed !\n";
                                ret = -1;
                        }
                        if (!copy_file (old_path, new_path)) {
                                cerr << "failed !\n";
                                ret = -1;
                        }
-                       
+
                        /* make sure we stop looking in the external
                           dir/folder. Remember, this is an all-or-nothing
                           operations, it doesn't merge just some files.
                        /* make sure we stop looking in the external
                           dir/folder. Remember, this is an all-or-nothing
                           operations, it doesn't merge just some files.
@@ -4186,7 +4424,7 @@ make_new_media_path (string old_path, string new_session_folder, string new_sess
        v.push_back (new_session_path);   /* just one directory/folder */
        v.push_back (typedir);
        v.push_back (Glib::path_get_basename (old_path));
        v.push_back (new_session_path);   /* just one directory/folder */
        v.push_back (typedir);
        v.push_back (Glib::path_get_basename (old_path));
-       
+
        return Glib::build_filename (v);
 }
 
        return Glib::build_filename (v);
 }
 
@@ -4213,15 +4451,15 @@ Session::save_as (SaveAs& saveas)
        /* get total size */
 
        for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
        /* get total size */
 
        for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
-               
+
                /* need to clear this because
                 * find_files_matching_filter() is cumulative
                 */
                /* need to clear this because
                 * find_files_matching_filter() is cumulative
                 */
-               
+
                files.clear ();
                files.clear ();
-               
+
                find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
                find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
-               
+
                all += files.size();
 
                for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
                all += files.size();
 
                for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
@@ -4232,7 +4470,7 @@ Session::save_as (SaveAs& saveas)
        }
 
        /* save old values so we can switch back if we are not switching to the new session */
        }
 
        /* save old values so we can switch back if we are not switching to the new session */
-       
+
        string old_path = _path;
        string old_name = _name;
        string old_snapshot = _current_snapshot_name;
        string old_path = _path;
        string old_name = _name;
        string old_snapshot = _current_snapshot_name;
@@ -4242,15 +4480,15 @@ Session::save_as (SaveAs& saveas)
 
        old_search_path[DataType::AUDIO] = source_search_path (DataType::AUDIO);
        old_search_path[DataType::MIDI] = source_search_path (DataType::MIDI);
 
        old_search_path[DataType::AUDIO] = source_search_path (DataType::AUDIO);
        old_search_path[DataType::MIDI] = source_search_path (DataType::MIDI);
-       old_config_search_path[DataType::AUDIO]  = config.get_audio_search_path ();     
-       old_config_search_path[DataType::MIDI]  = config.get_midi_search_path ();       
+       old_config_search_path[DataType::AUDIO]  = config.get_audio_search_path ();
+       old_config_search_path[DataType::MIDI]  = config.get_midi_search_path ();
 
        /* switch session directory */
 
        /* switch session directory */
-       
+
        (*_session_dir) = to_dir;
 
        /* create new tree */
        (*_session_dir) = to_dir;
 
        /* create new tree */
-       
+
        if (!_session_dir->create()) {
                saveas.failure_message = string_compose (_("Cannot create new session folder %1"), to_dir);
                return -1;
        if (!_session_dir->create()) {
                saveas.failure_message = string_compose (_("Cannot create new session folder %1"), to_dir);
                return -1;
@@ -4260,21 +4498,21 @@ Session::save_as (SaveAs& saveas)
                /* copy all relevant files. Find each location in session_dirs,
                 * and copy files from there to target.
                 */
                /* copy all relevant files. Find each location in session_dirs,
                 * and copy files from there to target.
                 */
-               
+
                for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
                for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
-                       
+
                        /* need to clear this because
                         * find_files_matching_filter() is cumulative
                         */
                        /* need to clear this because
                         * find_files_matching_filter() is cumulative
                         */
-                       
+
                        files.clear ();
                        files.clear ();
-                       
+
                        const size_t prefix_len = (*sd).path.size();
                        const size_t prefix_len = (*sd).path.size();
-                       
+
                        /* Work just on the files within this session dir */
                        /* Work just on the files within this session dir */
-                       
+
                        find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
                        find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
-                       
+
                        /* add dir separator to protect against collisions with
                         * track names (e.g. track named "audiofiles" or
                         * "analysis".
                        /* add dir separator to protect against collisions with
                         * track names (e.g. track named "audiofiles" or
                         * "analysis".
@@ -4289,7 +4527,7 @@ Session::save_as (SaveAs& saveas)
                           folder. That really was a bad idea, but I'm not fixing it as part of
                           implementing ::save_as().
                        */
                           folder. That really was a bad idea, but I'm not fixing it as part of
                           implementing ::save_as().
                        */
-                       
+
                        for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
 
                                std::string from = *i;
                        for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
 
                                std::string from = *i;
@@ -4301,25 +4539,25 @@ Session::save_as (SaveAs& saveas)
                                        continue;
                                }
 #endif
                                        continue;
                                }
 #endif
-                               
+
                                if (from.find (audiofile_dir_string) != string::npos) {
                                if (from.find (audiofile_dir_string) != string::npos) {
-                                       
+
                                        /* audio file: only copy if asked */
 
                                        if (saveas.include_media && saveas.copy_media) {
                                        /* audio file: only copy if asked */
 
                                        if (saveas.include_media && saveas.copy_media) {
-                                               
+
                                                string to = make_new_media_path (*i, to_dir, new_folder);
 
                                                info << "media file copying from " << from << " to " << to << endmsg;
                                                string to = make_new_media_path (*i, to_dir, new_folder);
 
                                                info << "media file copying from " << from << " to " << to << endmsg;
-                                               
+
                                                if (!copy_file (from, to)) {
                                                        throw Glib::FileError (Glib::FileError::IO_ERROR,
                                                                                                   string_compose(_("\ncopying \"%1\" failed !"), from));
                                                }
                                        }
                                                if (!copy_file (from, to)) {
                                                        throw Glib::FileError (Glib::FileError::IO_ERROR,
                                                                                                   string_compose(_("\ncopying \"%1\" failed !"), from));
                                                }
                                        }
-                                       
+
                                        /* we found media files inside the session folder */
                                        /* we found media files inside the session folder */
-                                       
+
                                        internal_file_cnt++;
 
                                } else if (from.find (midifile_dir_string) != string::npos) {
                                        internal_file_cnt++;
 
                                } else if (from.find (midifile_dir_string) != string::npos) {
@@ -4329,44 +4567,44 @@ Session::save_as (SaveAs& saveas)
                                         */
 
                                        if (saveas.include_media) {
                                         */
 
                                        if (saveas.include_media) {
-                                       
+
                                                string to = make_new_media_path (*i, to_dir, new_folder);
                                                string to = make_new_media_path (*i, to_dir, new_folder);
-                                               
+
                                                info << "media file copying from " << from << " to " << to << endmsg;
                                                info << "media file copying from " << from << " to " << to << endmsg;
-                                               
+
                                                if (!copy_file (from, to)) {
                                                        throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
                                                }
                                        }
 
                                        /* we found media files inside the session folder */
                                                if (!copy_file (from, to)) {
                                                        throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
                                                }
                                        }
 
                                        /* we found media files inside the session folder */
-                                               
+
                                        internal_file_cnt++;
                                        internal_file_cnt++;
-                                       
+
                                } else if (from.find (analysis_dir_string) != string::npos) {
 
                                        /*  make sure analysis dir exists in
                                         *  new session folder, but we're not
                                         *  copying analysis files here, see
                                } else if (from.find (analysis_dir_string) != string::npos) {
 
                                        /*  make sure analysis dir exists in
                                         *  new session folder, but we're not
                                         *  copying analysis files here, see
-                                        *  below 
+                                        *  below
                                         */
                                         */
-                                       
+
                                        (void) g_mkdir_with_parents (analysis_dir().c_str(), 775);
                                        continue;
                                        (void) g_mkdir_with_parents (analysis_dir().c_str(), 775);
                                        continue;
-                                       
+
                                } else {
                                } else {
-                                       
+
                                        /* normal non-media file. Don't copy state, history, etc.
                                         */
                                        /* normal non-media file. Don't copy state, history, etc.
                                         */
-                                       
+
                                        bool do_copy = true;
                                        bool do_copy = true;
-                                       
+
                                        for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
                                                if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
                                                        /* end of filename matches extension, do not copy file */
                                                        do_copy = false;
                                                        break;
                                        for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
                                                if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
                                                        /* end of filename matches extension, do not copy file */
                                                        do_copy = false;
                                                        break;
-                                               } 
+                                               }
                                        }
 
                                        if (!saveas.copy_media && from.find (peakfile_suffix) != string::npos) {
                                        }
 
                                        if (!saveas.copy_media && from.find (peakfile_suffix) != string::npos) {
@@ -4375,10 +4613,10 @@ Session::save_as (SaveAs& saveas)
                                                 */
                                                do_copy = false;
                                        }
                                                 */
                                                do_copy = false;
                                        }
-                                       
+
                                        if (do_copy) {
                                                string to = Glib::build_filename (to_dir, from.substr (prefix_len));
                                        if (do_copy) {
                                                string to = Glib::build_filename (to_dir, from.substr (prefix_len));
-                                               
+
                                                info << "attempting to make directory/folder " << to << endmsg;
 
                                                if (g_mkdir_with_parents (Glib::path_get_dirname (to).c_str(), 0755)) {
                                                info << "attempting to make directory/folder " << to << endmsg;
 
                                                if (g_mkdir_with_parents (Glib::path_get_dirname (to).c_str(), 0755)) {
@@ -4386,26 +4624,26 @@ Session::save_as (SaveAs& saveas)
                                                }
 
                                                info << "attempting to copy " << from << " to " << to << endmsg;
                                                }
 
                                                info << "attempting to copy " << from << " to " << to << endmsg;
-                                               
+
                                                if (!copy_file (from, to)) {
                                                        throw Glib::FileError (Glib::FileError::IO_ERROR,
                                                                                                   string_compose(_("\ncopying \"%1\" failed !"), from));
                                                }
                                        }
                                }
                                                if (!copy_file (from, to)) {
                                                        throw Glib::FileError (Glib::FileError::IO_ERROR,
                                                                                                   string_compose(_("\ncopying \"%1\" failed !"), from));
                                                }
                                        }
                                }
-                               
+
                                /* measure file size even if we're not going to copy so that our Progress
                                   signals are correct, since we included these do-not-copy files
                                   in the computation of the total size and file count.
                                */
                                /* measure file size even if we're not going to copy so that our Progress
                                   signals are correct, since we included these do-not-copy files
                                   in the computation of the total size and file count.
                                */
-                               
+
                                GStatBuf gsb;
                                g_stat (from.c_str(), &gsb);
                                copied += gsb.st_size;
                                cnt++;
                                GStatBuf gsb;
                                g_stat (from.c_str(), &gsb);
                                copied += gsb.st_size;
                                cnt++;
-                               
+
                                double fraction = (double) copied / total_bytes;
                                double fraction = (double) copied / total_bytes;
-                               
+
                                bool keep_going = true;
 
                                if (saveas.copy_media) {
                                bool keep_going = true;
 
                                if (saveas.copy_media) {
@@ -4414,11 +4652,11 @@ Session::save_as (SaveAs& saveas)
                                         * media is not being copied, because
                                         * it will be fast(ish).
                                         */
                                         * media is not being copied, because
                                         * it will be fast(ish).
                                         */
-                                       
+
                                        /* tell someone "X percent, file M of N"; M is one-based */
                                        /* tell someone "X percent, file M of N"; M is one-based */
-                                       
+
                                        boost::optional<bool> res = saveas.Progress (fraction, cnt, all);
                                        boost::optional<bool> res = saveas.Progress (fraction, cnt, all);
-                                       
+
                                        if (res) {
                                                keep_going = *res;
                                        }
                                        if (res) {
                                                keep_going = *res;
                                        }
@@ -4452,7 +4690,7 @@ Session::save_as (SaveAs& saveas)
                }
 
                if (saveas.include_media) {
                }
 
                if (saveas.include_media) {
-               
+
                        if (saveas.copy_media) {
 #ifndef PLATFORM_WINDOWS
                                /* There are problems with analysis files on
                        if (saveas.copy_media) {
 #ifndef PLATFORM_WINDOWS
                                /* There are problems with analysis files on
@@ -4462,7 +4700,7 @@ Session::save_as (SaveAs& saveas)
                                 *
                                 * This is a tricky problem to solve so for
                                 * just don't copy these files. They will be
                                 *
                                 * This is a tricky problem to solve so for
                                 * just don't copy these files. They will be
-                                * regenerated as-needed anyway, subject to the 
+                                * regenerated as-needed anyway, subject to the
                                 * existing issue that the filenames will be
                                 * rejected by Windows, which is a separate
                                 * problem (though related).
                                 * existing issue that the filenames will be
                                 * rejected by Windows, which is a separate
                                 * problem (though related).
@@ -4471,19 +4709,19 @@ Session::save_as (SaveAs& saveas)
                                /* only needed if we are copying media, since the
                                 * analysis data refers to media data
                                 */
                                /* only needed if we are copying media, since the
                                 * analysis data refers to media data
                                 */
-                               
+
                                old = analysis_dir ();
                                if (Glib::file_test (old, Glib::FILE_TEST_EXISTS)) {
                                        string newdir = Glib::build_filename (to_dir, "analysis");
                                        copy_files (old, newdir);
                                }
                                old = analysis_dir ();
                                if (Glib::file_test (old, Glib::FILE_TEST_EXISTS)) {
                                        string newdir = Glib::build_filename (to_dir, "analysis");
                                        copy_files (old, newdir);
                                }
-#endif /* PLATFORM_WINDOWS */                          
+#endif /* PLATFORM_WINDOWS */
                        }
                }
                        }
                }
-                       
-               
+
+
                _path = to_dir;
                _path = to_dir;
-               _current_snapshot_name = saveas.new_name;
+               set_snapshot_name (saveas.new_name);
                _name = saveas.new_name;
 
                if (saveas.include_media && !saveas.copy_media) {
                _name = saveas.new_name;
 
                if (saveas.include_media && !saveas.copy_media) {
@@ -4504,12 +4742,12 @@ Session::save_as (SaveAs& saveas)
                                */
                        }
                }
                                */
                        }
                }
-               
+
                bool was_dirty = dirty ();
 
                save_state ("", false, false, !saveas.include_media);
                save_default_options ();
                bool was_dirty = dirty ();
 
                save_state ("", false, false, !saveas.include_media);
                save_default_options ();
-               
+
                if (saveas.copy_media && saveas.copy_external) {
                        if (bring_all_sources_into_session (boost::bind (&Session::save_as_bring_callback, this, _1, _2, _3))) {
                                throw Glib::FileError (Glib::FileError::NO_SPACE_LEFT, "consolidate failed");
                if (saveas.copy_media && saveas.copy_external) {
                        if (bring_all_sources_into_session (boost::bind (&Session::save_as_bring_callback, this, _1, _2, _3))) {
                                throw Glib::FileError (Glib::FileError::NO_SPACE_LEFT, "consolidate failed");
@@ -4519,14 +4757,14 @@ Session::save_as (SaveAs& saveas)
                saveas.final_session_folder_name = _path;
 
                store_recent_sessions (_name, _path);
                saveas.final_session_folder_name = _path;
 
                store_recent_sessions (_name, _path);
-               
+
                if (!saveas.switch_to) {
 
                        /* switch back to the way things were */
 
                        _path = old_path;
                        _name = old_name;
                if (!saveas.switch_to) {
 
                        /* switch back to the way things were */
 
                        _path = old_path;
                        _name = old_name;
-                       _current_snapshot_name = old_snapshot;
+                       set_snapshot_name (old_snapshot);
 
                        (*_session_dir) = old_sd;
 
 
                        (*_session_dir) = old_sd;
 
@@ -4539,7 +4777,7 @@ Session::save_as (SaveAs& saveas)
                                config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
                                config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
                        }
                                config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
                                config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
                        }
-                       
+
                } else {
 
                        /* prune session dirs, and update disk space statistics
                } else {
 
                        /* prune session dirs, and update disk space statistics
@@ -4551,7 +4789,7 @@ Session::save_as (SaveAs& saveas)
                        session_dirs.push_back (sp);
                        refresh_disk_space ();
 
                        session_dirs.push_back (sp);
                        refresh_disk_space ();
 
-                       /* ensure that all existing tracks reset their current capture source paths 
+                       /* ensure that all existing tracks reset their current capture source paths
                         */
                        reset_write_sources (true, true);
 
                         */
                        reset_write_sources (true, true);
 
@@ -4576,27 +4814,27 @@ Session::save_as (SaveAs& saveas)
        } catch (Glib::FileError& e) {
 
                saveas.failure_message = e.what();
        } catch (Glib::FileError& e) {
 
                saveas.failure_message = e.what();
-               
+
                /* recursively remove all the directories */
                /* recursively remove all the directories */
-               
+
                remove_directory (to_dir);
                remove_directory (to_dir);
-               
+
                /* return error */
                /* return error */
-               
+
                return -1;
 
        } catch (...) {
 
                saveas.failure_message = _("unknown reason");
                return -1;
 
        } catch (...) {
 
                saveas.failure_message = _("unknown reason");
-               
+
                /* recursively remove all the directories */
                /* recursively remove all the directories */
-               
+
                remove_directory (to_dir);
                remove_directory (to_dir);
-               
+
                /* return error */
                /* return error */
-               
+
                return -1;
        }
                return -1;
        }
-       
+
        return 0;
 }
        return 0;
 }