minor but important fix for the wrap-buffer case in a recording audio diskstream
[ardour.git] / libs / ardour / session_state.cc
index 02aee5d3ef74aad52c0a17470d6773050b8fc12c..95c59fd5e686619c2ec063d2211f6082302c4753 100644 (file)
@@ -48,6 +48,8 @@
 #include <sys/mount.h>
 #endif
 
 #include <sys/mount.h>
 #endif
 
+#include <glib.h>
+
 #include <glibmm.h>
 #include <glibmm/thread.h>
 
 #include <glibmm.h>
 #include <glibmm/thread.h>
 
 #include "ardour/processor.h"
 #include "ardour/port.h"
 #include "ardour/proxy_controllable.h"
 #include "ardour/processor.h"
 #include "ardour/port.h"
 #include "ardour/proxy_controllable.h"
+#include "ardour/recent_sessions.h"
 #include "ardour/region_factory.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
 #include "ardour/region_factory.h"
 #include "ardour/route_group.h"
 #include "ardour/send.h"
@@ -221,7 +224,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
         _speakers.reset (new Speakers);
 
        AudioDiskstream::allocate_working_buffers();
         _speakers.reset (new Speakers);
 
        AudioDiskstream::allocate_working_buffers();
-       AudioSource::allocate_working_buffers ();
 
        /* default short fade = 15ms */
 
 
        /* default short fade = 15ms */
 
@@ -551,7 +553,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
        /* set initial start + end point */
 
        _state_of_the_state = Clean;
        /* set initial start + end point */
 
        _state_of_the_state = Clean;
-        
+
         /* set up Master Out and Control Out if necessary */
 
         if (bus_profile) {
         /* set up Master Out and Control Out if necessary */
 
         if (bus_profile) {
@@ -566,7 +568,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                                 return -1;
                         }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                                 return -1;
                         }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       boost_debug_shared_ptr_mark_interesting (rt.get(), "Route");
+                       boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                        {
                                Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 #endif
                        {
                                Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
@@ -583,7 +585,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                                         return -1;
                                 }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                                         return -1;
                                 }
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                                boost_debug_shared_ptr_mark_interesting (rt, "Route");
+                                boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                                {
                                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 #endif
                                {
                                        Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
@@ -591,7 +593,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                                        r->output()->ensure_io (count, false, this);
                                }
                                 r->set_remote_control_id (control_id);
                                        r->output()->ensure_io (count, false, this);
                                }
                                 r->set_remote_control_id (control_id);
-                                
+
                                 rl.push_back (r);
                         }
 
                                 rl.push_back (r);
                         }
 
@@ -611,7 +613,7 @@ Session::create (const string& mix_template, BusProfile* bus_profile)
                         bus_profile->input_ac = AutoConnectOption (0);
                         bus_profile->output_ac = AutoConnectOption (0);
                 }
                         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);
         }
@@ -648,7 +650,8 @@ Session::remove_pending_capture_state ()
 }
 
 /** Rename a state file.
 }
 
 /** Rename a state file.
- * @param snapshot_name Snapshot name.
+ *  @param old_name Old snapshot name.
+ *  @param new_name New snapshot name.
  */
 void
 Session::rename_state (string old_name, string new_name)
  */
 void
 Session::rename_state (string old_name, string new_name)
@@ -676,7 +679,7 @@ Session::rename_state (string old_name, string new_name)
 }
 
 /** Remove a state file.
 }
 
 /** Remove a state file.
- * @param snapshot_name Snapshot name.
+ *  @param snapshot_name Snapshot name.
  */
 void
 Session::remove_state (string snapshot_name)
  */
 void
 Session::remove_state (string snapshot_name)
@@ -715,7 +718,7 @@ Session::jack_session_event (jack_session_event_t * event)
         if (event->type == JackSessionSaveTemplate)
         {
                 if (save_template( timebuf )) {
         if (event->type == JackSessionSaveTemplate)
         {
                 if (save_template( timebuf )) {
-                        event->flags = JackSessionSaveError; 
+                        event->flags = JackSessionSaveError;
                 } else {
                         string cmd ("ardour3 -P -U ");
                         cmd += event->client_uuid;
                 } else {
                         string cmd ("ardour3 -P -U ");
                         cmd += event->client_uuid;
@@ -728,7 +731,7 @@ Session::jack_session_event (jack_session_event_t * event)
         else
         {
                 if (save_state (timebuf)) {
         else
         {
                 if (save_state (timebuf)) {
-                        event->flags = JackSessionSaveError; 
+                        event->flags = JackSessionSaveError;
                 } else {
                         sys::path xml_path (_session_dir->root_path());
                         xml_path /= legalize_for_path (timebuf) + statefile_suffix;
                 } else {
                         sys::path xml_path (_session_dir->root_path());
                         xml_path /= legalize_for_path (timebuf) + statefile_suffix;
@@ -817,7 +820,7 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
 
        } else {
 
 
        } else {
 
-               if (rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
+               if (::rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
                        error << string_compose (_("could not rename temporary session file %1 to %2"),
                                        tmp_path.to_string(), xml_path.to_string()) << endmsg;
                        sys::remove (tmp_path);
                        error << string_compose (_("could not rename temporary session file %1 to %2"),
                                        tmp_path.to_string(), xml_path.to_string()) << endmsg;
                        sys::remove (tmp_path);
@@ -925,7 +928,7 @@ Session::load_state (string snapshot_name)
                sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
                Stateful::loading_state_version = (major * 1000) + minor;
        }
                sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
                Stateful::loading_state_version = (major * 1000) + minor;
        }
-               
+
        if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) {
 
                sys::path backup_path(_session_dir->root_path());
        if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) {
 
                sys::path backup_path(_session_dir->root_path());
@@ -1070,7 +1073,7 @@ Session::state(bool full_state)
                                                continue;
                                        }
                                }
                                                continue;
                                        }
                                }
-                               
+
                                child->add_child_nocopy (siter->second->get_state());
                        }
                }
                                child->add_child_nocopy (siter->second->get_state());
                        }
                }
@@ -1088,7 +1091,7 @@ Session::state(bool full_state)
                                 child->add_child_nocopy (r->state ());
                         }
                 }
                                 child->add_child_nocopy (r->state ());
                         }
                 }
-               
+
                RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
 
                if (!cassocs.empty()) {
                RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
 
                if (!cassocs.empty()) {
@@ -1103,7 +1106,7 @@ Session::state(bool full_state)
                                can->add_property (X_("original"), buf);
                                ca->add_child_nocopy (*can);
                        }
                                can->add_property (X_("original"), buf);
                                ca->add_child_nocopy (*can);
                        }
-               }  
+               }
        }
 
        if (full_state) {
        }
 
        if (full_state) {
@@ -1250,9 +1253,7 @@ Session::set_state (const XMLNode& node, int version)
 
        IO::disable_connecting ();
 
 
        IO::disable_connecting ();
 
-       if ((child = find_named_node (node, "Extra")) != 0) {
-               _extra_xml = new XMLNode (*child);
-       }
+       Stateful::save_extra_xml (node);
 
        if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
                load_options (*child);
 
        if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
                load_options (*child);
@@ -1270,6 +1271,24 @@ Session::set_state (const XMLNode& node, int version)
                }
        }
 
                }
        }
 
+        if ((child = find_named_node (node, X_("Speakers"))) != 0) {
+                _speakers->set_state (*child, version);
+        }
+
+       if ((child = find_named_node (node, "Sources")) == 0) {
+               error << _("Session: XML state has no sources section") << endmsg;
+               goto out;
+       } else if (load_sources (*child)) {
+               goto out;
+       }
+
+       if ((child = find_named_node (node, "TempoMap")) == 0) {
+               error << _("Session: XML state has no Tempo Map section") << endmsg;
+               goto out;
+       } else if (_tempo_map->set_state (*child, version)) {
+               goto out;
+       }
+
        if ((child = find_named_node (node, "Locations")) == 0) {
                error << _("Session: XML state has no locations section") << endmsg;
                goto out;
        if ((child = find_named_node (node, "Locations")) == 0) {
                error << _("Session: XML state has no locations section") << endmsg;
                goto out;
@@ -1277,10 +1296,6 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
 
                goto out;
        }
 
-        if ((child = find_named_node (node, X_("Speakers"))) != 0) {
-                _speakers->set_state (*child, version);
-        }
-
        Location* location;
 
        if ((location = _locations->auto_loop_location()) != 0) {
        Location* location;
 
        if ((location = _locations->auto_loop_location()) != 0) {
@@ -1300,20 +1315,6 @@ Session::set_state (const XMLNode& node, int version)
                AudioFileSource::set_header_position_offset (_session_range_location->start());
        }
 
                AudioFileSource::set_header_position_offset (_session_range_location->start());
        }
 
-       if ((child = find_named_node (node, "Sources")) == 0) {
-               error << _("Session: XML state has no sources section") << endmsg;
-               goto out;
-       } else if (load_sources (*child)) {
-               goto out;
-       }
-
-       if ((child = find_named_node (node, "TempoMap")) == 0) {
-               error << _("Session: XML state has no Tempo Map section") << endmsg;
-               goto out;
-       } else if (_tempo_map->set_state (*child, version)) {
-               goto out;
-       }
-
        if ((child = find_named_node (node, "Regions")) == 0) {
                error << _("Session: XML state has no Regions section") << endmsg;
                goto out;
        if ((child = find_named_node (node, "Regions")) == 0) {
                error << _("Session: XML state has no Regions section") << endmsg;
                goto out;
@@ -1333,13 +1334,13 @@ Session::set_state (const XMLNode& node, int version)
        } else if (playlists->load_unused (*this, *child)) {
                goto out;
        }
        } else if (playlists->load_unused (*this, *child)) {
                goto out;
        }
-       
+
        if ((child = find_named_node (node, "CompoundAssociations")) != 0) {
                if (load_compounds (*child)) {
                        goto out;
                }
        }
        if ((child = find_named_node (node, "CompoundAssociations")) != 0) {
                if (load_compounds (*child)) {
                        goto out;
                }
        }
-       
+
        if ((child = find_named_node (node, "NamedSelections")) != 0) {
                if (load_named_selections (*child)) {
                        goto out;
        if ((child = find_named_node (node, "NamedSelections")) != 0) {
                if (load_named_selections (*child)) {
                        goto out;
@@ -1357,7 +1358,7 @@ Session::set_state (const XMLNode& node, int version)
                        _bundle_xml_node = new XMLNode (*child);
                }
        }
                        _bundle_xml_node = new XMLNode (*child);
                }
        }
-       
+
        if (version < 3000) {
                if ((child = find_named_node (node, X_("DiskStreams"))) == 0) {
                        error << _("Session: XML state has no diskstreams section") << endmsg;
        if (version < 3000) {
                if ((child = find_named_node (node, X_("DiskStreams"))) == 0) {
                        error << _("Session: XML state has no diskstreams section") << endmsg;
@@ -1378,16 +1379,16 @@ Session::set_state (const XMLNode& node, int version)
        _diskstreams_2X.clear ();
 
        if (version >= 3000) {
        _diskstreams_2X.clear ();
 
        if (version >= 3000) {
-               
+
                if ((child = find_named_node (node, "RouteGroups")) == 0) {
                        error << _("Session: XML state has no route groups section") << endmsg;
                        goto out;
                } else if (load_route_groups (*child, version)) {
                        goto out;
                }
                if ((child = find_named_node (node, "RouteGroups")) == 0) {
                        error << _("Session: XML state has no route groups section") << endmsg;
                        goto out;
                } else if (load_route_groups (*child, version)) {
                        goto out;
                }
-               
+
        } else if (version < 3000) {
        } else if (version < 3000) {
-               
+
                if ((child = find_named_node (node, "EditGroups")) == 0) {
                        error << _("Session: XML state has no edit groups section") << endmsg;
                        goto out;
                if ((child = find_named_node (node, "EditGroups")) == 0) {
                        error << _("Session: XML state has no edit groups section") << endmsg;
                        goto out;
@@ -1442,7 +1443,7 @@ Session::load_routes (const XMLNode& node, int version)
                } else {
                        route = XMLRouteFactory (**niter, version);
                }
                } else {
                        route = XMLRouteFactory (**niter, version);
                }
-               
+
                if (route == 0) {
                        error << _("Session: cannot create Route from XML description.") << endmsg;
                        return -1;
                if (route == 0) {
                        error << _("Session: cannot create Route from XML description.") << endmsg;
                        return -1;
@@ -1481,26 +1482,26 @@ Session::XMLRouteFactory (const XMLNode& node, int version)
        if (ds_child) {
 
                boost::shared_ptr<Track> track;
        if (ds_child) {
 
                boost::shared_ptr<Track> track;
-                
+
                 if (type == DataType::AUDIO) {
                         track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
                 } else {
                         track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
                 }
                 if (type == DataType::AUDIO) {
                         track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
                 } else {
                         track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
                 }
-                
+
                 if (track->init()) {
                         return ret;
                 }
                 if (track->init()) {
                         return ret;
                 }
-                
+
                 if (track->set_state (node, version)) {
                         return ret;
                 }
                 if (track->set_state (node, version)) {
                         return ret;
                 }
-                
+
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                 boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                 boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
-                
+
        } else {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
 
        } else {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
 
@@ -1557,28 +1558,28 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version)
                 } else {
                         track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
                 }
                 } else {
                         track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
                 }
-                
+
                 if (track->init()) {
                         return ret;
                 }
                 if (track->init()) {
                         return ret;
                 }
-                
+
                 if (track->set_state (node, version)) {
                         return ret;
                 }
 
                track->set_diskstream (*i);
                 if (track->set_state (node, version)) {
                         return ret;
                 }
 
                track->set_diskstream (*i);
-                
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS                
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
                 boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
                 boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
 #endif
                 ret = track;
-                
+
        } else {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
        } else {
                boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
 
                 if (r->init () == 0 && r->set_state (node, version) == 0) {
 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                        boost_debug_shared_ptr_mark_interesting (rt, "Route");
+                        boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
 #endif
                         ret = r;
                 }
 #endif
                         ret = r;
                 }
@@ -1620,38 +1621,73 @@ Session::load_compounds (const XMLNode& node)
        XMLNodeList calist = node.children();
        XMLNodeConstIterator caiter;
        XMLProperty *caprop;
        XMLNodeList calist = node.children();
        XMLNodeConstIterator caiter;
        XMLProperty *caprop;
-       
+
        for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
                XMLNode* ca = *caiter;
                ID orig_id;
                ID copy_id;
        for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
                XMLNode* ca = *caiter;
                ID orig_id;
                ID copy_id;
-               
+
                if ((caprop = ca->property (X_("original"))) == 0) {
                        continue;
                }
                orig_id = caprop->value();
                if ((caprop = ca->property (X_("original"))) == 0) {
                        continue;
                }
                orig_id = caprop->value();
-               
+
                if ((caprop = ca->property (X_("copy"))) == 0) {
                        continue;
                }
                copy_id = caprop->value();
                if ((caprop = ca->property (X_("copy"))) == 0) {
                        continue;
                }
                copy_id = caprop->value();
-               
+
                boost::shared_ptr<Region> orig = RegionFactory::region_by_id (orig_id);
                boost::shared_ptr<Region> copy = RegionFactory::region_by_id (copy_id);
                boost::shared_ptr<Region> orig = RegionFactory::region_by_id (orig_id);
                boost::shared_ptr<Region> copy = RegionFactory::region_by_id (copy_id);
-               
+
                if (!orig || !copy) {
                        warning << string_compose (_("Regions in compound description not found (ID's %1 and %2): ignored"),
                if (!orig || !copy) {
                        warning << string_compose (_("Regions in compound description not found (ID's %1 and %2): ignored"),
-                                                  orig_id, copy_id) 
+                                                  orig_id, copy_id)
                                << endmsg;
                        continue;
                }
                                << endmsg;
                        continue;
                }
-               
+
                RegionFactory::add_compound_association (orig, copy);
        }
 
        return 0;
 }
 
                RegionFactory::add_compound_association (orig, copy);
        }
 
        return 0;
 }
 
+void
+Session::load_nested_sources (const XMLNode& node)
+{
+       XMLNodeList nlist;
+       XMLNodeConstIterator niter;
+
+       nlist = node.children();
+
+       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+               if ((*niter)->name() == "Source") {
+
+                       /* it may already exist, so don't recreate it unnecessarily 
+                        */
+
+                       XMLProperty* prop = (*niter)->property (X_("id"));
+                       if (!prop) {
+                               error << _("Nested source has no ID info in session state file! (ignored)") << endmsg;
+                               continue;
+                       }
+
+                       ID source_id (prop->value());
+
+                       if (!source_by_id (source_id)) {
+
+                               try {
+                                       SourceFactory::create (*this, **niter, true);
+                               }
+                               catch (failed_constructor& err) {
+                                       error << string_compose (_("Cannot reconstruct nested source for region %1"), name()) << endmsg;
+                               }
+                       }
+               }
+       }
+}
+
 boost::shared_ptr<Region>
 Session::XMLRegionFactory (const XMLNode& node, bool full)
 {
 boost::shared_ptr<Region>
 Session::XMLRegionFactory (const XMLNode& node, bool full)
 {
@@ -1659,6 +1695,15 @@ Session::XMLRegionFactory (const XMLNode& node, bool full)
 
        try {
 
 
        try {
 
+               const XMLNodeList& nlist = node.children();
+
+               for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
+                       XMLNode *child = (*niter);
+                       if (child->name() == "NestedSource") {
+                               load_nested_sources (*child);
+                       }
+               }
+
                 if (!type || type->value() == "audio") {
                         return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
                 } else if (type->value() == "midi") {
                 if (!type || type->value() == "audio") {
                         return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
                 } else if (type->value() == "midi") {
@@ -1932,8 +1977,8 @@ Session::load_sources (const XMLNode& node)
                         case 0:
                                 /* user added a new search location, so try again */
                                 goto retry;
                         case 0:
                                 /* user added a new search location, so try again */
                                 goto retry;
-                                
-                                
+
+
                         case 1:
                                 /* user asked to quit the entire session load
                                  */
                         case 1:
                                 /* user asked to quit the entire session load
                                  */
@@ -2258,7 +2303,7 @@ Session::load_route_groups (const XMLNode& node, int version)
        set_dirty ();
 
        if (version >= 3000) {
        set_dirty ();
 
        if (version >= 3000) {
-               
+
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        if ((*niter)->name() == "RouteGroup") {
                                RouteGroup* rg = new RouteGroup (*this, "");
                for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        if ((*niter)->name() == "RouteGroup") {
                                RouteGroup* rg = new RouteGroup (*this, "");
@@ -2343,9 +2388,10 @@ Session::add_route_group (RouteGroup* g)
        _route_groups.push_back (g);
        route_group_added (g); /* EMIT SIGNAL */
 
        _route_groups.push_back (g);
        route_group_added (g); /* EMIT SIGNAL */
 
-       g->MembershipChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
-       g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
-       
+       g->RouteAdded.connect_same_thread (*this, boost::bind (&Session::route_added_to_route_group, this, _1, _2));
+       g->RouteRemoved.connect_same_thread (*this, boost::bind (&Session::route_removed_from_route_group, this, _1, _2));
+       g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_property_changed, this, g));
+
        set_dirty ();
 }
 
        set_dirty ();
 }
 
@@ -2419,14 +2465,14 @@ Session::begin_reversible_command (GQuark q)
           to hold all the commands that are committed.  This keeps the order of
           commands correct in the history.
        */
           to hold all the commands that are committed.  This keeps the order of
           commands correct in the history.
        */
-       
+
        if (_current_trans == 0) {
                /* start a new transaction */
                assert (_current_trans_quarks.empty ());
                _current_trans = new UndoTransaction();
                _current_trans->set_name (g_quark_to_string (q));
        }
        if (_current_trans == 0) {
                /* start a new transaction */
                assert (_current_trans_quarks.empty ());
                _current_trans = new UndoTransaction();
                _current_trans->set_name (g_quark_to_string (q));
        }
-       
+
        _current_trans_quarks.push_front (q);
 }
 
        _current_trans_quarks.push_front (q);
 }
 
@@ -2435,7 +2481,7 @@ Session::commit_reversible_command (Command *cmd)
 {
        assert (_current_trans);
        assert (!_current_trans_quarks.empty ());
 {
        assert (_current_trans);
        assert (!_current_trans_quarks.empty ());
-       
+
        struct timeval now;
 
        if (cmd) {
        struct timeval now;
 
        if (cmd) {
@@ -2465,7 +2511,7 @@ Session::commit_reversible_command (Command *cmd)
 
 static bool
 accept_all_audio_files (const string& path, void */*arg*/)
 
 static bool
 accept_all_audio_files (const string& path, void */*arg*/)
-{ 
+{
         if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
                 return false;
         }
         if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
                 return false;
         }
@@ -2616,11 +2662,11 @@ Session::cleanup_regions ()
        for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
 
                boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>( i->second);
        for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
 
                boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>( i->second);
-               
+
                if (!audio_region) {
                        continue;
                }
                if (!audio_region) {
                        continue;
                }
-               
+
                uint32_t used = playlists->region_use_count (audio_region);
 
                if (used == 0 && !audio_region->automatic()) {
                uint32_t used = playlists->region_use_count (audio_region);
 
                if (used == 0 && !audio_region->automatic()) {
@@ -2656,7 +2702,7 @@ Session::cleanup_sources (CleanupReport& rep)
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
        /* consider deleting all unused playlists */
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
        /* consider deleting all unused playlists */
-       
+
        if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
                ret = 0;
                goto out;
        if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
                ret = 0;
                goto out;
@@ -2761,13 +2807,13 @@ Session::cleanup_sources (CleanupReport& rep)
                         if (playlists->source_use_count (fs) != 0) {
                                 all_sources.insert (fs->path());
                         } else {
                         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);
                                 sources.erase (i);
                         }
                                 RegionFactory::remove_regions_using_source (i->second);
                                 sources.erase (i);
                         }
@@ -2781,18 +2827,18 @@ Session::cleanup_sources (CleanupReport& rep)
 
         if (candidates) {
                 for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
 
         if (candidates) {
                 for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
-                        
+
                         used = false;
                         spath = **x;
                         used = false;
                         spath = **x;
-                        
+
                         for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
                         for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-                                
+
                                 if (realpath(spath.c_str(), tmppath1) == 0) {
                                         error << string_compose (_("Cannot expand path %1 (%2)"),
                                                                  spath, strerror (errno)) << endmsg;
                                         continue;
                                 }
                                 if (realpath(spath.c_str(), tmppath1) == 0) {
                                         error << string_compose (_("Cannot expand path %1 (%2)"),
                                                                  spath, strerror (errno)) << endmsg;
                                         continue;
                                 }
-                                
+
                                 if (realpath((*i).c_str(),  tmppath2) == 0) {
                                         error << string_compose (_("Cannot expand path %1 (%2)"),
                                                                  (*i), strerror (errno)) << endmsg;
                                 if (realpath((*i).c_str(),  tmppath2) == 0) {
                                         error << string_compose (_("Cannot expand path %1 (%2)"),
                                                                  (*i), strerror (errno)) << endmsg;
@@ -2804,7 +2850,7 @@ Session::cleanup_sources (CleanupReport& rep)
                                         break;
                                 }
                         }
                                         break;
                                 }
                         }
-                        
+
                         if (!used) {
                                 unused.push_back (spath);
                         }
                         if (!used) {
                                 unused.push_back (spath);
                         }
@@ -2852,7 +2898,7 @@ Session::cleanup_sources (CleanupReport& rep)
                }
 
                newpath = Glib::build_filename (newpath, Glib::path_get_basename ((*x)));
                }
 
                newpath = Glib::build_filename (newpath, Glib::path_get_basename ((*x)));
-                
+
                if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
 
                        /* the new path already exists, try versioning */
                if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
 
                        /* the new path already exists, try versioning */
@@ -2896,19 +2942,19 @@ Session::cleanup_sources (CleanupReport& rep)
                 */
 
                 string base = basename_nosuffix (*x);
                 */
 
                 string base = basename_nosuffix (*x);
-                base += "%A"; /* this is what we add for the channel suffix of all native files, 
+                base += "%A"; /* this is what we add for the channel suffix of all native files,
                                  or for the first channel of embedded files. it will miss
                                  some peakfiles for other channels
                               */
                string peakpath = peak_path (base);
                                  or for the first channel of embedded files. it will miss
                                  some peakfiles for other channels
                               */
                string peakpath = peak_path (base);
-                
+
                if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
                        if (::unlink (peakpath.c_str()) != 0) {
                                error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
                                                          peakpath, _path, strerror (errno))
                                      << endmsg;
                                /* try to back out */
                if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
                        if (::unlink (peakpath.c_str()) != 0) {
                                error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
                                                          peakpath, _path, strerror (errno))
                                      << endmsg;
                                /* try to back out */
-                               rename (newpath.c_str(), _path.c_str());
+                               ::rename (newpath.c_str(), _path.c_str());
                                goto out;
                        }
                }
                                goto out;
                        }
                }
@@ -3063,7 +3109,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                r = route_by_remote_id (desc.rid());
                break;
        }
                r = route_by_remote_id (desc.rid());
                break;
        }
-       
+
        if (!r) {
                return c;
        }
        if (!r) {
                return c;
        }
@@ -3084,7 +3130,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        case ControllableDescriptor::Recenable:
        {
                boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
        case ControllableDescriptor::Recenable:
        {
                boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
-               
+
                if (t) {
                        c = t->rec_enable_control ();
                }
                if (t) {
                        c = t->rec_enable_control ();
                }
@@ -3119,17 +3165,17 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                uint32_t parameter_index = desc.target (1);
 
                /* revert to zero based counting */
                uint32_t parameter_index = desc.target (1);
 
                /* revert to zero based counting */
-               
+
                if (plugin > 0) {
                        --plugin;
                }
                if (plugin > 0) {
                        --plugin;
                }
-               
+
                if (parameter_index > 0) {
                        --parameter_index;
                }
 
                boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
                if (parameter_index > 0) {
                        --parameter_index;
                }
 
                boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
-               
+
                if (p) {
                        c = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
                                p->control(Evoral::Parameter(PluginAutomation, 0, parameter_index)));
                if (p) {
                        c = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
                                p->control(Evoral::Parameter(PluginAutomation, 0, parameter_index)));
@@ -3137,18 +3183,18 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
                break;
        }
 
                break;
        }
 
-       case ControllableDescriptor::SendGain: 
+       case ControllableDescriptor::SendGain:
        {
                uint32_t send = desc.target (0);
 
                /* revert to zero-based counting */
        {
                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);
                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 (p) {
                        boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(p);
                        boost::shared_ptr<Amp> a = s->amp();
@@ -3319,7 +3365,7 @@ Session::restore_history (string snapshot_name)
                                } else {
                                        error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
                                }
                                } else {
                                        error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
                                }
-                               
+
                        } else if (n->name() == "PatchChangeDiffCommand") {
 
                                PBD::ID id (n->property("midi-source")->value());
                        } else if (n->name() == "PatchChangeDiffCommand") {
 
                                PBD::ID id (n->property("midi-source")->value());
@@ -3408,11 +3454,11 @@ Session::config_changed (std::string p, bool ours)
 
                //poke_midi_thread ();
 
 
                //poke_midi_thread ();
 
-       } else if (p == "mmc-device-id" || p == "mmc-receive-id") {
+       } else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
 
                MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
 
 
                MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
 
-       } else if (p == "mmc-send-id") {
+       } else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
 
                MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
 
 
                MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
 
@@ -3529,6 +3575,8 @@ Session::config_changed (std::string p, bool ours)
                solo_control_mode_changed ();
        } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
                last_timecode_valid = false;
                solo_control_mode_changed ();
        } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
                last_timecode_valid = false;
+       } else if (p == "playback-buffer-seconds") {
+               AudioSource::allocate_working_buffers (frame_rate());
        }
 
        set_dirty ();
        }
 
        set_dirty ();
@@ -3574,7 +3622,7 @@ void
 Session::setup_midi_machine_control ()
 {
        MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
 Session::setup_midi_machine_control ()
 {
        MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
-       
+
        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->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, 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));
        mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
@@ -3607,6 +3655,160 @@ Session::solo_cut_control() const
            it up as a Controllable. Changes to the Controllable will just map back to the RCConfiguration
            parameter.
         */
            it up as a Controllable. Changes to the Controllable will just map back to the RCConfiguration
            parameter.
         */
-        
+
         return _solo_cut_control;
 }
         return _solo_cut_control;
 }
+
+int
+Session::rename (const std::string& new_name)
+{
+       string legal_name = legalize_for_path (new_name);
+       string newpath;
+       string oldstr;
+       string newstr;
+       bool first = true;
+
+#define RENAME ::rename
+
+       /* Rename:
+
+        * session directory
+        * interchange subdirectory
+        * session file
+        * session history
+        
+        * Backup files are left unchanged and not renamed.
+        */
+
+       /* 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) {
+               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 ...
+                */
+
+               if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
+                       oldstr = oldstr.substr (0, oldstr.length() - 1);
+               }
+
+               string base = Glib::path_get_dirname (oldstr);
+               string p = Glib::path_get_basename (oldstr);
+
+               newstr = Glib::build_filename (base, legal_name);
+               
+               if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
+                       return -1;
+               }
+       }
+
+       /* Session dirs */
+       
+       for (vector<space_and_path>::const_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 ...
+                */
+
+               if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
+                       oldstr = oldstr.substr (0, oldstr.length() - 1);
+               }
+
+               string base = Glib::path_get_dirname (oldstr);
+               string p = Glib::path_get_basename (oldstr);
+
+               newstr = Glib::build_filename (base, legal_name);
+
+               cerr << "Rename " << oldstr << " => " << newstr << endl;                
+
+               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+                       return 1;
+               }
+
+               if (first) {
+                       (*_session_dir) = newstr;
+                       newpath = newstr;
+                       first = 1;
+               }
+
+               /* directory below interchange */
+
+               v.push_back (newstr);
+               v.push_back (interchange_dir_name);
+               v.push_back (p);
+
+               oldstr = Glib::build_filename (v);
+
+               v.clear ();
+               v.push_back (newstr);
+               v.push_back (interchange_dir_name);
+               v.push_back (legal_name);
+
+               newstr = Glib::build_filename (v);
+               
+               cerr << "Rename " << oldstr << " => " << newstr << endl;
+               
+               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+                       return 1;
+               }
+       }
+
+       /* state file */
+       
+       oldstr = Glib::build_filename (newpath, _current_snapshot_name) + statefile_suffix;
+       newstr= Glib::build_filename (newpath, legal_name) + statefile_suffix;
+       
+       cerr << "Rename " << oldstr << " => " << newstr << endl;                
+
+       if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+               return 1;
+       }
+
+       /* history file */
+
+       
+       oldstr = Glib::build_filename (newpath, _current_snapshot_name) + history_suffix;
+
+       if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS))  {
+               newstr = Glib::build_filename (newpath, legal_name) + history_suffix;
+               
+               cerr << "Rename " << oldstr << " => " << newstr << endl;                
+               
+               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+                       return 1;
+               }
+       }
+
+       /* remove old name from recent sessions */
+
+       remove_recent_sessions (_path);
+
+       _path = newpath;
+       _current_snapshot_name = new_name;
+       _name = new_name;
+
+       set_dirty ();
+
+       /* save state again to get everything just right */
+
+       save_state (_current_snapshot_name);
+
+
+       /* add to recent sessions */
+
+       store_recent_sessions (new_name, _path);
+
+       return 0;
+
+#undef RENAME
+}