pulling trunk
[ardour.git] / libs / ardour / session_state.cc
index bda826508c0e138e64917778c2ab7db4296380eb..cf510e9881d2dc30490d4a0756e372b2a9ed4ffe 100644 (file)
 #include <sys/param.h>
 #endif
 
+#include <glibmm.h>
+
 #include <midi++/mmc.h>
 #include <midi++/port.h>
 #include <pbd/error.h>
-#include <pbd/dirname.h>
-#include <pbd/lockmonitor.h>
+
+#include <glibmm/thread.h>
 #include <pbd/pathscanner.h>
 #include <pbd/pthread_utils.h>
-#include <pbd/basename.h>
 #include <pbd/strsplit.h>
 
 #include <ardour/audioengine.h>
@@ -62,6 +63,7 @@
 #include <ardour/audioplaylist.h>
 #include <ardour/source.h>
 #include <ardour/filesource.h>
+#include <ardour/destructive_filesource.h>
 #include <ardour/sndfilesource.h>
 #include <ardour/sndfile_helpers.h>
 #include <ardour/auditioner.h>
@@ -80,6 +82,7 @@
 #include <ardour/location.h>
 #include <ardour/audioregion.h>
 #include <ardour/crossfade.h>
+#include <ardour/control_protocol_manager.h>
 
 #include "i18n.h"
 #include <locale.h>
@@ -96,7 +99,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
 
        char buf[PATH_MAX+1];
        if (!realpath(fullpath.c_str(), buf) && (errno != ENOENT)) {
-               error << compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg;
+               error << string_compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg;
                throw failed_constructor();
        }
        _path = string(buf);
@@ -116,7 +119,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        _tempo_map = new TempoMap (_current_frame_rate);
        _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed));
 
-       atomic_set (&processing_prohibited, 0);
+       g_atomic_int_set (&processing_prohibited, 0);
        send_cnt = 0;
        insert_cnt = 0;
        _transport_speed = 0;
@@ -125,8 +128,9 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        _transport_frame = 0;
        last_stop_frame = 0;
        end_location = new Location (0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd)));
+       start_location = new Location (0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart)));
        _end_location_is_free = true;
-       atomic_set (&_record_status, Disabled);
+       g_atomic_int_set (&_record_status, Disabled);
        auto_play = false;
        punch_in = false;
        punch_out = false;
@@ -153,6 +157,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        _solo_model = InverseMute;
        solo_update_disabled = false;
        currently_soloing = false;
+       _have_captured = false;
        _worst_output_latency = 0;
        _worst_input_latency = 0;
        _worst_track_latency = 0;
@@ -163,26 +168,23 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        butler_gain_buffer = 0;
        auditioner = 0;
        mmc_control = false;
-       midi_feedback = false;
        midi_control = true;
        mmc = 0;
        post_transport_work = PostTransportWork (0);
-       atomic_set (&butler_should_do_transport_work, 0);
-       atomic_set (&butler_active, 0);
-       atomic_set (&_playback_load, 100);
-       atomic_set (&_capture_load, 100);
-       atomic_set (&_playback_load_min, 100);
-       atomic_set (&_capture_load_min, 100);
+       g_atomic_int_set (&butler_should_do_transport_work, 0);
+       g_atomic_int_set (&butler_active, 0);
+       g_atomic_int_set (&_playback_load, 100);
+       g_atomic_int_set (&_capture_load, 100);
+       g_atomic_int_set (&_playback_load_min, 100);
+       g_atomic_int_set (&_capture_load_min, 100);
        pending_audition_region = 0;
        _edit_mode = Slide;
        pending_edit_mode = _edit_mode;
        _play_range = false;
-       align_style = ExistingMaterial;
        _control_out = 0;
        _master_out = 0;
        input_auto_connect = AutoConnectOption (0);
        output_auto_connect = AutoConnectOption (0);
-       _have_captured = false;
        waiting_to_start = false;
        _exporting = false;
        _gain_automation_buffer = 0;
@@ -191,6 +193,11 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        pending_abort = false;
        layer_model = MoveAddHigher;
        xfade_model = ShortCrossfade;
+       destructive_index = 0;
+
+       /* allocate conversion buffers */
+       _conversion_buffers[ButlerContext] = new char[DiskStream::disk_io_frames() * 4];
+       _conversion_buffers[TransportContext] = new char[DiskStream::disk_io_frames() * 4];
        
        /* default short fade = 15ms */
 
@@ -231,7 +238,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
 
        /* default configuration */
 
-       recording_plugins = false;
+       do_not_record_plugins = false;
        over_length_short = 2;
        over_length_long = 10;
        send_midi_timecode = false;
@@ -278,7 +285,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
 int
 Session::second_stage_init (bool new_session)
 {
-       SndFileSource::set_peak_dir (peak_dir());
+       ExternalSource::set_peak_dir (peak_dir());
 
        if (!new_session) {
                if (load_state (_current_snapshot_name)) {
@@ -295,10 +302,6 @@ Session::second_stage_init (bool new_session)
                return -1;
        }
 
-       if (init_feedback ()) {
-               return -1;
-       }
-
        if (state_tree) {
                if (set_state (*state_tree->root())) {
                        return -1;
@@ -338,7 +341,8 @@ Session::second_stage_init (bool new_session)
        _engine.transport_locate (0);
        deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
        deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
-       send_all_midi_feedback();
+
+       ControlProtocolManager::instance().set_session (*this);
 
        if (new_session) {
                _end_location_is_free = true;
@@ -396,13 +400,32 @@ Session::setup_raid_path (string path)
 
        if (colons == 0) {
 
-               /* no multiple search path, just one directory (common case) */
+               /* no multiple search path, just one location (common case) */
 
                sp.path = path;
                sp.blocks = 0;
                session_dirs.push_back (sp);
+
+               string fspath;
+
+               /* sounds dir */
+
+               fspath += sp.path;
+               if (fspath[fspath.length()-1] != '/') {
+                       fspath += '/';
+               }
+               fspath += sound_dir_name;
+               fspath += ':';
+
+               /* tape dir */
+
+               fspath += sp.path;
+               if (fspath[fspath.length()-1] != '/') {
+                       fspath += '/';
+               }
+               fspath += tape_dir_name;
                
-               FileSource::set_search_path (path + sound_dir_name);
+               FileSource::set_search_path (fspath);
 
                return;
        }
@@ -413,6 +436,9 @@ Session::setup_raid_path (string path)
                
                sp.blocks = 0;
                sp.path = remaining.substr (0, colon);
+               session_dirs.push_back (sp);
+
+               /* add sounds to file search path */
 
                fspath += sp.path;
                if (fspath[fspath.length()-1] != '/') {
@@ -421,7 +447,14 @@ Session::setup_raid_path (string path)
                fspath += sound_dir_name;
                fspath += ':';
 
-               session_dirs.push_back (sp);
+               /* add tape dir to file search path */
+
+               fspath += sp.path;
+               if (fspath[fspath.length()-1] != '/') {
+                       fspath += '/';
+               }
+               fspath += tape_dir_name;
+               fspath += ':';
 
                remaining = remaining.substr (colon+1);
        }
@@ -431,11 +464,19 @@ Session::setup_raid_path (string path)
                sp.blocks = 0;
                sp.path = remaining;
 
+               fspath += ':';
                fspath += sp.path;
                if (fspath[fspath.length()-1] != '/') {
                        fspath += '/';
                }
                fspath += sound_dir_name;
+               fspath += ':';
+
+               fspath += sp.path;
+               if (fspath[fspath.length()-1] != '/') {
+                       fspath += '/';
+               }
+               fspath += tape_dir_name;
 
                session_dirs.push_back (sp);
        }
@@ -458,7 +499,7 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
                if (errno == EEXIST) {
                        new_session = false;
                } else {
-                       error << compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
+                       error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
                        return -1;
                }
        } else {
@@ -469,7 +510,7 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
 
        if (mkdir (dir.c_str(), 0755) < 0) {
                if (errno != EEXIST) {
-                       error << compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+                       error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                        return -1;
                }
        }
@@ -478,7 +519,16 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
 
        if (mkdir (dir.c_str(), 0755) < 0) {
                if (errno != EEXIST) {
-                       error << compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+                       error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+                       return -1;
+               }
+       }
+
+       dir = tape_dir ();
+
+       if (mkdir (dir.c_str(), 0755) < 0) {
+               if (errno != EEXIST) {
+                       error << string_compose(_("Session: cannot create session tape dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                        return -1;
                }
        }
@@ -487,7 +537,7 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
 
        if (mkdir (dir.c_str(), 0755) < 0) {
                if (errno != EEXIST) {
-                       error << compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+                       error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                        return -1;
                }
        }
@@ -496,7 +546,7 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
 
        if (mkdir (dir.c_str(), 0755) < 0) {
                if (errno != EEXIST) {
-                       error << compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+                       error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                        return -1;
                }
        }
@@ -506,33 +556,7 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
        
        if (mix_template) {
                if (new_session){
-
-                       string lookfor = "^";
-                       lookfor += *mix_template;
-                       lookfor += _template_suffix;
-                       lookfor += '$';
-
-                       PathScanner scanner;
-                       string tpath = template_path();
-                       string in_path;
-                       string out_path;
-
-                       vector<string*>* result= scanner (tpath, lookfor, false, true);
-
-                       if (result == 0) {
-                               error << compose (_("Could not find a template called %1 in %2"), *mix_template, tpath)
-                                     << endmsg;
-                               *mix_template = "";
-                       }
-
-                       if (result->size() == 0) {
-                               delete result;
-                               error << compose (_("Could not find a template called %1 in %2"), *mix_template, tpath)
-                                     << endmsg;
-                               *mix_template = "";
-                       }
-
-                       in_path = *(result->front());
+                       std::string in_path = *mix_template;
 
                        ifstream in(in_path.c_str());
                        
@@ -553,13 +577,13 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
                                        return 0;
                                        
                                } else {
-                                       error << compose (_("Could not open %1 for writing mix template"), out_path) 
+                                       error << string_compose (_("Could not open %1 for writing mix template"), out_path) 
                                              << endmsg;
                                        return -1;
                                }
                                
                        } else {
-                               error << compose (_("Could not open mix template %1 for reading"), in_path) 
+                               error << string_compose (_("Could not open mix template %1 for reading"), in_path) 
                                      << endmsg;
                                return -1;
                        }
@@ -573,11 +597,14 @@ Session::create (bool& new_session, string* mix_template, jack_nframes_t initial
 
        if (new_session) {
 
-               /* set an initial end point */
+               /* set initial start + end point */
+
+               start_location->set_end (0);
+               _locations.add (start_location);
 
                end_location->set_end (initial_length);
                _locations.add (end_location);
-
+               
                _state_of_the_state = Clean;
 
                if (save_state (_current_snapshot_name)) {
@@ -668,18 +695,18 @@ Session::save_state (string snapshot_name, bool pending)
        }
 
        if (!tree.write (xml_path)) {
-               error << compose (_("state could not be saved to %1"), xml_path) << endmsg;
+               error << string_compose (_("state could not be saved to %1"), xml_path) << endmsg;
 
                /* don't leave a corrupt file lying around if it is
                   possible to fix.
                */
 
                if (unlink (xml_path.c_str())) {
-                       error << compose (_("could not remove corrupt state file %1"), xml_path) << endmsg;
+                       error << string_compose (_("could not remove corrupt state file %1"), xml_path) << endmsg;
                } else {
                        if (!pending) {
                                if (rename (bak_path.c_str(), xml_path.c_str())) {
-                                       error << compose (_("could not restore state file from backup %1"), bak_path) << endmsg;
+                                       error << string_compose (_("could not restore state file from backup %1"), bak_path) << endmsg;
                                }
                        }
                }
@@ -690,7 +717,7 @@ Session::save_state (string snapshot_name, bool pending)
        if (!pending) {
 
                bool was_dirty = dirty();
-               
+
                _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
                
                if (was_dirty) {
@@ -748,7 +775,7 @@ Session::load_state (string snapshot_name)
        }
 
        if (access (xmlpath.c_str(), F_OK)) {
-               error << compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg;
+               error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg;
                return 1;
        }
 
@@ -759,7 +786,7 @@ Session::load_state (string snapshot_name)
        if (state_tree->read (xmlpath)) {
                return 0;
        } else {
-               error << compose(_("Could not understand ardour file %1"), xmlpath) << endmsg;
+               error << string_compose(_("Could not understand ardour file %1"), xmlpath) << endmsg;
        }
 
        delete state_tree;
@@ -972,9 +999,15 @@ Session::load_options (const XMLNode& node)
                        set_midi_feedback (prop->value() == "yes");
                }
        }
+       // Legacy support for <recording-plugins>
        if ((child = find_named_node (node, "recording-plugins")) != 0) {
                if ((prop = child->property ("val")) != 0) {
-                       set_recording_plugins (prop->value() == "yes");
+                       set_do_not_record_plugins (prop->value() == "no");
+               }
+       }
+       if ((child = find_named_node (node, "do-not-record-plugins")) != 0) {
+               if ((prop = child->property ("val")) != 0) {
+                       set_do_not_record_plugins (prop->value() == "yes");
                }
        }
        if ((child = find_named_node (node, "crossfades-active")) != 0) {
@@ -988,13 +1021,9 @@ Session::load_options (const XMLNode& node)
                }
        }
 
-       if ((child = find_named_node (node, "align-style")) != 0) {
+       if ((child = find_named_node (node, "end-marker-is-free")) != 0) {
                if ((prop = child->property ("val")) != 0) {
-                       if (prop->value() == "capture") {
-                               set_align_style (CaptureTime);
-                       } else {
-                               set_align_style (ExistingMaterial);
-                       }
+                       _end_location_is_free = (prop->value() == "yes");
                }
        }
 
@@ -1177,12 +1206,14 @@ Session::get_options () const
        child->add_property ("val", get_midi_control () ? "yes" : "no");
        child = opthead->add_child ("midi-feedback");
        child->add_property ("val", get_midi_feedback () ? "yes" : "no");
-       child = opthead->add_child ("recording-plugins");
-       child->add_property ("val", get_recording_plugins () ? "yes" : "no");
+       child = opthead->add_child ("do-not-record-plugins");
+       child->add_property ("val", get_do_not_record_plugins () ? "yes" : "no");
        child = opthead->add_child ("auto-crossfade");
        child->add_property ("val", get_crossfades_active () ? "yes" : "no");
        child = opthead->add_child ("audible-click");
        child->add_property ("val", get_clicking () ? "yes" : "no");
+       child = opthead->add_child ("end-marker-is-free");
+       child->add_property ("val", _end_location_is_free ? "yes" : "no");
 
        if (click_sound.length()) {
                child = opthead->add_child ("click-sound");
@@ -1197,9 +1228,6 @@ Session::get_options () const
        child = opthead->add_child ("solo-model");
        child->add_property ("val", _solo_model == SoloBus ? "SoloBus" : "InverseMute");
 
-       child = opthead->add_child ("align-style");
-       child->add_property ("val", (align_style == ExistingMaterial ? "existing" : "capture"));
-
        child = opthead->add_child ("layer-model");
        switch (layer_model) {
        case LaterHigher:
@@ -1248,9 +1276,8 @@ Session::get_template()
           sources in their state node. 
        */
        
-       disable_record ();
+       disable_record (false);
 
-       cerr << "STart get template\n";
        return state(false);
 }
 
@@ -1308,17 +1335,25 @@ Session::state(bool full_state)
        child = node->add_child ("Sources");
 
        if (full_state) {
-               LockMonitor sl (source_lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock sl (source_lock);
 
                for (SourceList::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
                        
                        /* Don't save information about FileSources that are empty */
                        
                        FileSource* fs;
-                       
+
                        if ((fs = dynamic_cast<FileSource*> ((*siter).second)) != 0) {
-                               if (fs->length() == 0) {
-                                       continue;
+                               DestructiveFileSource* dfs = dynamic_cast<DestructiveFileSource*> (fs);
+
+                               /* destructive file sources are OK if they are empty, because
+                                  we will re-use them every time.
+                               */
+
+                               if (!dfs) {
+                                       if (fs->length() == 0) {
+                                               continue;
+                                       }
                                }
                        }
                        
@@ -1329,7 +1364,7 @@ Session::state(bool full_state)
        child = node->add_child ("Regions");
 
        if (full_state) { 
-               LockMonitor rl (region_lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock rl (region_lock);
 
                for (AudioRegionList::const_iterator i = audio_regions.begin(); i != audio_regions.end(); ++i) {
                        
@@ -1344,7 +1379,7 @@ Session::state(bool full_state)
        child = node->add_child ("DiskStreams");
 
        { 
-               LockMonitor dl (diskstream_lock, __LINE__, __FILE__);
+               Glib::RWLock::ReaderLock dl (diskstream_lock);
                for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
                        if (!(*i)->hidden()) {
                                child->add_child_nocopy ((*i)->get_state());
@@ -1356,7 +1391,7 @@ Session::state(bool full_state)
        
        child = node->add_child ("Connections");
        {
-               LockMonitor lm (connection_lock, __LINE__, __FILE__);
+               Glib::Mutex::Lock lm (connection_lock);
                for (ConnectionList::iterator i = _connections.begin(); i != _connections.end(); ++i) {
                        if (!(*i)->system_dependent()) {
                                child->add_child_nocopy ((*i)->get_state());
@@ -1366,7 +1401,7 @@ Session::state(bool full_state)
 
        child = node->add_child ("Routes");
        {
-               LockMonitor lm (route_lock, __LINE__, __FILE__);
+               Glib::RWLock::ReaderLock lm (route_lock);
                
                RoutePublicOrderSorter cmp;
                RouteList public_order(routes);
@@ -1450,6 +1485,7 @@ Session::set_state (const XMLNode& node)
        XMLNodeList nlist;
        XMLNode* child;
        const XMLProperty* prop;
+       int ret = -1;
 
        _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
        
@@ -1458,6 +1494,8 @@ Session::set_state (const XMLNode& node)
                return -1;
        }
 
+       StateManager::prohibit_save ();
+
        if ((prop = node.property ("name")) != 0) {
                _name = prop->value ();
        }
@@ -1483,7 +1521,6 @@ Session::set_state (const XMLNode& node)
        */
 
        if (use_config_midi_ports ()) {
-               return -1;
        }
 
        if ((child = find_named_node (node, "Path")) != 0) {
@@ -1500,63 +1537,61 @@ Session::set_state (const XMLNode& node)
 
        if ((child = find_named_node (node, "Options")) == 0) {
                error << _("Session: XML state has no options section") << endmsg;
-               return -1;
        } else if (load_options (*child)) {
-               return -1;
        }
 
        if ((child = find_named_node (node, "Sources")) == 0) {
                error << _("Session: XML state has no sources section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_sources (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "Regions")) == 0) {
                error << _("Session: XML state has no Regions section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_regions (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "Playlists")) == 0) {
                error << _("Session: XML state has no playlists section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_playlists (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "UnusedPlaylists")) == 0) {
                // this is OK
        } else if (load_unused_playlists (*child)) {
-               return -1;
+               goto out;
        }
        
        if ((child = find_named_node (node, "NamedSelections")) != 0) {
                if (load_named_selections (*child)) {
-                       return -1;
+                       goto out;
                }
        }
 
        if ((child = find_named_node (node, "DiskStreams")) == 0) {
                error << _("Session: XML state has no diskstreams section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_diskstreams (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "Connections")) == 0) {
                error << _("Session: XML state has no connections section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_connections (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "Locations")) == 0) {
                error << _("Session: XML state has no locations section") << endmsg;
-               return -1;
+               goto out;
        } else if (_locations.set_state (*child)) {
-               return -1;
+               goto out;
        }
 
        Location* location;
@@ -1572,37 +1607,45 @@ Session::set_state (const XMLNode& node)
        if ((location = _locations.end_location()) == 0) {
                _locations.add (end_location);
        } else {
+               delete end_location;
                end_location = location;
        }
 
+       if ((location = _locations.start_location()) == 0) {
+               _locations.add (start_location);
+       } else {
+               delete start_location;
+               start_location = location;
+       }
+
        _locations.save_state (_("initial state"));
 
        if ((child = find_named_node (node, "EditGroups")) == 0) {
                error << _("Session: XML state has no edit groups section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_edit_groups (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "MixGroups")) == 0) {
                error << _("Session: XML state has no mix groups section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_mix_groups (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "TempoMap")) == 0) {
                error << _("Session: XML state has no Tempo Map section") << endmsg;
-               return -1;
+               goto out;
        } else if (_tempo_map->set_state (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "Routes")) == 0) {
                error << _("Session: XML state has no routes section") << endmsg;
-               return -1;
+               goto out;
        } else if (load_routes (*child)) {
-               return -1;
+               goto out;
        }
 
        if ((child = find_named_node (node, "Click")) == 0) {
@@ -1621,6 +1664,8 @@ Session::set_state (const XMLNode& node)
 
        _state_of_the_state = Clean;
 
+       StateManager::allow_save (_("initial state"), true);
+
        if (state_was_pending) {
                save_state (_current_snapshot_name);
                remove_pending_capture_state ();
@@ -1628,6 +1673,11 @@ Session::set_state (const XMLNode& node)
        }
 
        return 0;
+
+  out:
+       /* we failed, re-enable state saving but don't actually save internal state */
+       StateManager::allow_save (X_("ignored"), false);
+       return ret;
 }
 
 int
@@ -1715,10 +1765,10 @@ Session::XMLRegionFactory (const XMLNode& node, bool full)
                }
        }
 
-       sscanf (prop->value().c_str(), "%llu", &s_id);
+       sscanf (prop->value().c_str(), "%" PRIu64, &s_id);
 
        if ((source = get_source (s_id)) == 0) {
-               error << compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
+               error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
                return 0;
        }
 
@@ -1729,10 +1779,10 @@ Session::XMLRegionFactory (const XMLNode& node, bool full)
        for (uint32_t n=1; n < nchans; ++n) {
                snprintf (buf, sizeof(buf), X_("source-%d"), n);
                if ((prop = node.property (buf)) != 0) {
-                       sscanf (prop->value().c_str(), "%llu", &s_id);
+                       sscanf (prop->value().c_str(), "%" PRIu64, &s_id);
                        
                        if ((source = get_source (s_id)) == 0) {
-                               error << compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
+                               error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
                                return 0;
                        }
                        sources.push_back(source);
@@ -1754,7 +1804,7 @@ Session::get_sources_as_xml ()
 
 {
        XMLNode* node = new XMLNode (X_("Sources"));
-       LockMonitor lm (source_lock, __LINE__, __FILE__);
+       Glib::Mutex::Lock lm (source_lock);
 
        for (SourceList::iterator i = sources.begin(); i != sources.end(); ++i) {
                node->add_child_nocopy ((*i).second->get_state());
@@ -1810,20 +1860,24 @@ Session::load_sources (const XMLNode& node)
 Source *
 Session::XMLSourceFactory (const XMLNode& node)
 {
-       Source *src;
+       Source *src = 0;
 
        if (node.name() != "Source") {
                return 0;
        }
 
        try {
-               src = new FileSource (node, frame_rate());
+               if (node.property (X_("destructive")) != 0) {
+                       src = new DestructiveFileSource (node, frame_rate());
+               } else {
+                       src = new FileSource (node, frame_rate());
+               }
        }
        
        catch (failed_constructor& err) {
 
                try {
-                       src = new SndFileSource (node);
+                       src = ExternalSource::create (node);
                }
 
                catch (failed_constructor& err) {
@@ -1852,7 +1906,7 @@ Session::save_template (string template_name)
                closedir (dp);
        } else {
                if (mkdir (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)<0) {
-                       error << compose(_("Could not create mix templates directory \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+                       error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
                        return -1;
                }
        }
@@ -1866,7 +1920,7 @@ Session::save_template (string template_name)
        ifstream in(xml_path.c_str());
        
        if (in) {
-               warning << compose(_("Template \"%1\" already exists - new version not created"), template_name) << endmsg;
+               warning << string_compose(_("Template \"%1\" already exists - new version not created"), template_name) << endmsg;
                return -1;
        } else {
                in.close();
@@ -1905,7 +1959,7 @@ Session::refresh_disk_space ()
 #if HAVE_SYS_VFS_H
        struct statfs statfsbuf;
        vector<space_and_path>::iterator i;
-       LockMonitor lm (space_lock, __LINE__, __FILE__);
+       Glib::Mutex::Lock lm (space_lock);
        double scale;
 
        /* get freespace on every FS that is part of the session path */
@@ -1933,7 +1987,7 @@ Session::ensure_sound_dir (string path, string& result)
        
        if (mkdir (path.c_str(), 0775)) {
                if (errno != EEXIST) {
-                       error << compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg;
+                       error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg;
                        return -1;
                }
        }
@@ -1946,7 +2000,7 @@ Session::ensure_sound_dir (string path, string& result)
        
        if (mkdir (result.c_str(), 0775)) {
                if (errno != EEXIST) {
-                       error << compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg;
+                       error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg;
                        return -1;
                }
        }
@@ -1957,7 +2011,7 @@ Session::ensure_sound_dir (string path, string& result)
        
        if (mkdir (dead.c_str(), 0775)) {
                if (errno != EEXIST) {
-                       error << compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg;
+                       error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg;
                        return -1;
                }
        }
@@ -1968,7 +2022,7 @@ Session::ensure_sound_dir (string path, string& result)
        
        if (mkdir (peak.c_str(), 0775)) {
                if (errno != EEXIST) {
-                       error << compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg;
+                       error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg;
                        return -1;
                }
        }
@@ -1980,11 +2034,17 @@ Session::ensure_sound_dir (string path, string& result)
 }      
 
 string
-Session::discover_best_sound_dir ()
+Session::discover_best_sound_dir (bool destructive)
 {
        vector<space_and_path>::iterator i;
        string result;
 
+       /* destructive files all go into the same place */
+
+       if (destructive) {
+               return tape_dir();
+       }
+
        /* handle common case without system calls */
 
        if (session_dirs.size() == 1) {
@@ -1992,7 +2052,7 @@ Session::discover_best_sound_dir ()
        }
 
        /* OK, here's the algorithm we're following here:
-
+          
        We want to select which directory to use for 
        the next file source to be created. Ideally,
        we'd like to use a round-robin process so as to
@@ -2198,6 +2258,15 @@ Session::sound_dir () const
        return res;
 }
 
+string
+Session::tape_dir () const
+{
+       string res = _path;
+       res += tape_dir_name;
+       res += '/';
+       return res;
+}
+
 string
 Session::peak_dir () const
 {
@@ -2218,22 +2287,27 @@ Session::automation_dir () const
 string
 Session::template_dir ()
 {
-       string path = Config->get_user_ardour_path();
+       string path = get_user_ardour_path();
        path += "templates/";
 
        return path;
 }
 
 string
-Session::template_path ()
+Session::suffixed_search_path (string suffix, bool data)
 {
        string path;
 
-       path += Config->get_user_ardour_path();
+       path += get_user_ardour_path();
        if (path[path.length()-1] != ':') {
                path += ':';
        }
-       path += Config->get_system_ardour_path();
+
+       if (data) {
+               path += get_system_data_path();
+       } else {
+               path += get_system_module_path();
+       }
 
        vector<string> split_path;
        
@@ -2242,7 +2316,8 @@ Session::template_path ()
 
        for (vector<string>::iterator i = split_path.begin(); i != split_path.end(); ++i) {
                path += *i;
-               path += "templates/";
+               path += suffix;
+               path += '/';
                
                if (distance (i, split_path.end()) != 1) {
                        path += ':';
@@ -2252,6 +2327,18 @@ Session::template_path ()
        return path;
 }
 
+string
+Session::template_path ()
+{
+       return suffixed_search_path (X_("templates"), true);
+}
+
+string
+Session::control_protocol_path ()
+{
+       return suffixed_search_path (X_("surfaces"), false);
+}
+
 int
 Session::load_connections (const XMLNode& node)
 {
@@ -2266,7 +2353,7 @@ Session::load_connections (const XMLNode& node)
                } else if ((*niter)->name() == "OutputConnection") {
                        add_connection (new ARDOUR::OutputConnection (**niter));
                } else {
-                       error << compose(_("Unknown node \"%1\" found in Connections list from state file"), (*niter)->name()) << endmsg;
+                       error << string_compose(_("Unknown node \"%1\" found in Connections list from state file"), (*niter)->name()) << endmsg;
                        return -1;
                }
        }
@@ -2291,18 +2378,18 @@ Session::load_route_groups (const XMLNode& node, bool edit)
 {
        XMLNodeList nlist = node.children();
        XMLNodeConstIterator niter;
-       RouteGroup* route;
+       RouteGroup* rg;
 
        set_dirty();
 
        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                if ((*niter)->name() == "RouteGroup") {
                        if (edit) {
-                               route = add_edit_group ("");
-                               route->set_state (**niter);
+                               rg = add_edit_group ("");
+                               rg->set_state (**niter);
                        } else {
-                               route = add_mix_group ("");
-                               route->set_state (**niter);
+                               rg = add_mix_group ("");
+                               rg->set_state (**niter);
                        }
                }
        }
@@ -2313,7 +2400,7 @@ Session::load_route_groups (const XMLNode& node, bool edit)
 void
 Session::swap_configuration(Configuration** new_config)
 {
-       LockMonitor lm (route_lock, __LINE__, __FILE__);
+       Glib::RWLock::WriterLock lm (route_lock); // jlc - WHY?
        Configuration* tmp = *new_config;
        *new_config = Config;
        Config = tmp;
@@ -2323,7 +2410,7 @@ Session::swap_configuration(Configuration** new_config)
 void
 Session::copy_configuration(Configuration* new_config)
 {
-       LockMonitor lm (route_lock, __LINE__, __FILE__);
+       Glib::RWLock::WriterLock lm (route_lock);
        new_config = new Configuration(*Config);
 }
 
@@ -2386,7 +2473,7 @@ Session::auto_save()
 RouteGroup *
 Session::add_edit_group (string name)
 {
-       RouteGroup* rg = new RouteGroup (name);
+       RouteGroup* rg = new RouteGroup (*this, name);
        edit_groups.push_back (rg);
        edit_group_added (rg); /* EMIT SIGNAL */
        set_dirty();
@@ -2396,13 +2483,41 @@ Session::add_edit_group (string name)
 RouteGroup *
 Session::add_mix_group (string name)
 {
-       RouteGroup* rg = new RouteGroup (name, RouteGroup::Relative);
+       RouteGroup* rg = new RouteGroup (*this, name, RouteGroup::Relative);
        mix_groups.push_back (rg);
        mix_group_added (rg); /* EMIT SIGNAL */
        set_dirty();
        return rg;
 }
 
+void
+Session::remove_edit_group (RouteGroup& rg)
+{
+       list<RouteGroup*>::iterator i;
+
+       if ((i = find (edit_groups.begin(), edit_groups.end(), &rg)) != edit_groups.end()) {
+               (*i)->apply (&Route::drop_edit_group, this);
+               edit_groups.erase (i);
+               edit_group_removed (); /* EMIT SIGNAL */
+       }
+
+       delete &rg;
+}
+
+void
+Session::remove_mix_group (RouteGroup& rg)
+{
+       list<RouteGroup*>::iterator i;
+
+       if ((i = find (mix_groups.begin(), mix_groups.end(), &rg)) != mix_groups.end()) {
+               (*i)->apply (&Route::drop_mix_group, this);
+               mix_groups.erase (i);
+               mix_group_removed (); /* EMIT SIGNAL */
+       }
+
+       delete &rg;
+}
+
 RouteGroup *
 Session::mix_group_by_name (string name)
 {
@@ -2474,7 +2589,7 @@ Session::GlobalRouteBooleanState
 Session::get_global_route_boolean (bool (Route::*method)(void) const)
 {
        GlobalRouteBooleanState s;
-       LockMonitor lm (route_lock, __LINE__, __FILE__);
+       Glib::RWLock::ReaderLock lm (route_lock);
 
        for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
                if (!(*i)->hidden()) {
@@ -2494,7 +2609,7 @@ Session::GlobalRouteMeterState
 Session::get_global_route_metering ()
 {
        GlobalRouteMeterState s;
-       LockMonitor lm (route_lock, __LINE__, __FILE__);
+       Glib::RWLock::ReaderLock lm (route_lock);
 
        for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
                if (!(*i)->hidden()) {
@@ -2603,7 +2718,7 @@ Session::get_template_list (list<string> &template_names)
 int
 Session::read_favorite_dirs (FavoriteDirs & favs)
 {
-       string path = Config->get_user_ardour_path();
+       string path = get_user_ardour_path();
        path += "/favorite_dirs";
 
        ifstream fav (path.c_str());
@@ -2612,7 +2727,7 @@ Session::read_favorite_dirs (FavoriteDirs & favs)
        
        if (!fav) {
                if (errno != ENOENT) {
-                       //error << compose (_("cannot open favorite file %1 (%2)"), path, strerror (errno)) << endmsg;
+                       //error << string_compose (_("cannot open favorite file %1 (%2)"), path, strerror (errno)) << endmsg;
                        return -1;
                } else {
                        return 1;
@@ -2638,7 +2753,7 @@ Session::read_favorite_dirs (FavoriteDirs & favs)
 int
 Session::write_favorite_dirs (FavoriteDirs & favs)
 {
-       string path = Config->get_user_ardour_path();
+       string path = get_user_ardour_path();
        path += "/favorite_dirs";
 
        ofstream fav (path.c_str());
@@ -2906,11 +3021,11 @@ Session::cleanup_sources (Session::cleanup_report& rep)
 
        for (SourceList::iterator i = sources.begin(); i != sources.end(); ++i) {
                FileSource* fs;
-               SndFileSource* sfs;
+               ExternalSource* sfs;
                
                if ((fs = dynamic_cast<FileSource*> ((*i).second)) != 0) {
                        all_sources.insert (fs->path());
-               } else if ((sfs = dynamic_cast<SndFileSource*> ((*i).second)) != 0) {
+               } else if ((sfs = dynamic_cast<ExternalSource*> ((*i).second)) != 0) {
                        all_sources.insert (sfs->path());
                } 
        }
@@ -2951,13 +3066,13 @@ Session::cleanup_sources (Session::cleanup_report& rep)
                   on whichever filesystem it was already on.
                */
 
-               newpath = PBD::dirname (*x);
-               newpath = PBD::dirname (newpath);
+               newpath = Glib::path_get_dirname (*x);
+               newpath = Glib::path_get_dirname (newpath);
 
                newpath += '/';
                newpath += dead_sound_dir_name;
                newpath += '/';
-               newpath += PBD::basename ((*x));
+               newpath += Glib::path_get_basename ((*x));
                
                if (access (newpath.c_str(), F_OK) == 0) {
                        
@@ -2976,7 +3091,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
                        }
                        
                        if (version == 999) {
-                               error << compose (_("there are already 1000 files with names like %1; versioning discontinued"),
+                               error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
                                                  newpath)
                                      << endmsg;
                        } else {
@@ -2990,7 +3105,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
                }
 
                if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
-                       error << compose (_("cannot rename audio file source from %1 to %2 (%3)"),
+                       error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
                                          (*x), newpath, strerror (errno))
                              << endmsg;
                        goto out;
@@ -3005,7 +3120,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
 
                if (access (peakpath.c_str(), W_OK) == 0) {
                        if (::unlink (peakpath.c_str()) != 0) {
-                               error << compose (_("cannot remove peakfile %1 for %2 (%3)"),
+                               error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
                                                  peakpath, _path, strerror (errno))
                                      << endmsg;
                                /* try to back out */
@@ -3078,7 +3193,7 @@ Session::cleanup_trash_sources (Session::cleanup_report& rep)
                        }
 
                        if (unlink (fullpath.c_str())) {
-                               error << compose (_("cannot remove dead sound file %1 (%2)"),
+                               error << string_compose (_("cannot remove dead sound file %1 (%2)"),
                                                  fullpath, strerror (errno))
                                      << endmsg;
                        }
@@ -3098,7 +3213,7 @@ void
 Session::set_dirty ()
 {
        bool was_dirty = dirty();
-       
+
        _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
 
        if (!was_dirty) {