do not mark session dirty during loading process; sync with loaded locations state...
[ardour.git] / libs / ardour / session_state.cc
index 43d756d826675f7c7277922761bf504081d65898..cfd6b9c1e01348671f86f285b0c1713fda0ea187 100644 (file)
@@ -72,7 +72,6 @@
 #include "pbd/pthread_utils.h"
 #include "pbd/stacktrace.h"
 #include "pbd/convert.h"
-#include "pbd/clear_dir.h"
 #include "pbd/localtime_r.h"
 
 #include "ardour/amp.h"
@@ -319,6 +318,8 @@ Session::post_engine_init ()
                _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
                _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
                
+               
+               
        } catch (AudioEngine::PortRegistrationFailure& err) {
                /* handle this one in a different way than all others, so that its clear what happened */
                error << err.what() << endmsg;
@@ -541,7 +542,7 @@ Session::create (const string& session_template, BusProfile* bus_profile)
                 ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
 
                if (bus_profile->master_out_channels) {
-                       boost::shared_ptr<Route> r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO));
+                       boost::shared_ptr<Route> r (new Route (*this, _("Master"), Route::MasterOut, DataType::AUDIO));
                         if (r->init ()) {
                                 return -1;
                         }
@@ -666,10 +667,20 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        XMLTree tree;
        std::string xml_path(_session_dir->root_path());
 
+       /* prevent concurrent saves from different threads */
+
+       Glib::Threads::Mutex::Lock lm (save_state_lock);
+
        if (!_writable || (_state_of_the_state & CannotSave)) {
                return 1;
        }
 
+       if (g_atomic_int_get(&_suspend_save)) {
+               _save_queued = true;
+               return 1;
+       }
+       _save_queued = false;
+
        if (!_engine.connected ()) {
                error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"),
                                          PROGRAM_NAME)
@@ -874,6 +885,12 @@ Session::load_options (const XMLNode& node)
        return 0;
 }
 
+bool
+Session::save_default_options ()
+{
+       return config.save_state();
+}
+
 XMLNode&
 Session::get_state()
 {
@@ -1128,7 +1145,7 @@ Session::set_state (const XMLNode& node, int version)
 
        if (node.name() != X_("Session")) {
                fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
-               return -1;
+               goto out;
        }
 
        if ((prop = node.property ("name")) != 0) {
@@ -1142,7 +1159,7 @@ Session::set_state (const XMLNode& node, int version)
                if (_nominal_frame_rate != _current_frame_rate) {
                         boost::optional<int> r = AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate);
                        if (r.get_value_or (0)) {
-                               return -1;
+                               goto out;
                        }
                }
        }
@@ -1217,20 +1234,7 @@ Session::set_state (const XMLNode& node, int version)
                goto out;
        }
 
-       Location* location;
-
-       if ((location = _locations->auto_loop_location()) != 0) {
-               set_auto_loop_location (location);
-       }
-
-       if ((location = _locations->auto_punch_location()) != 0) {
-               set_auto_punch_location (location);
-       }
-
-       if ((location = _locations->session_range_location()) != 0) {
-               delete _session_range_location;
-               _session_range_location = location;
-       }
+       locations_changed ();
 
        if (_session_range_location) {
                AudioFileSource::set_header_position_offset (_session_range_location->start());
@@ -1335,9 +1339,13 @@ Session::set_state (const XMLNode& node, int version)
 
        StateReady (); /* EMIT SIGNAL */
 
+       delete state_tree;
+       state_tree = 0;
        return 0;
 
   out:
+       delete state_tree;
+       state_tree = 0;
        return ret;
 }
 
@@ -1839,41 +1847,6 @@ Session::get_sources_as_xml ()
        return *node;
 }
 
-string
-Session::path_from_region_name (DataType type, string name, string identifier)
-{
-       char buf[PATH_MAX+1];
-       uint32_t n;
-       SessionDirectory sdir(get_best_session_directory_for_new_source());
-       std::string source_dir = ((type == DataType::AUDIO)
-               ? sdir.sound_path() : sdir.midi_path());
-
-        string ext = native_header_format_extension (config.get_native_file_header_format(), type);
-
-       for (n = 0; n < 999999; ++n) {
-               if (identifier.length()) {
-                       snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
-                                 identifier.c_str(), n, ext.c_str());
-               } else {
-                       snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
-                                       n, ext.c_str());
-               }
-
-               std::string source_path = Glib::build_filename (source_dir, buf);
-
-               if (!Glib::file_test (source_path, Glib::FILE_TEST_EXISTS)) {
-                       return source_path;
-               }
-       }
-
-       error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
-                                name, identifier)
-             << endmsg;
-
-       return "";
-}
-
-
 int
 Session::load_sources (const XMLNode& node)
 {
@@ -1923,8 +1896,19 @@ Session::load_sources (const XMLNode& node)
 
                         case -1:
                         default:
-                                warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
-                                source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
+                               switch (err.type) {
+
+                               case DataType::AUDIO:
+                                       warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
+                                       source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
+                                       break;
+
+                               case DataType::MIDI:
+                                       warning << string_compose (_("A MIDI file is missing. %1 cannot currently recover from missing MIDI files"),
+                                                                    PROGRAM_NAME) << endmsg;
+                                       return -1;
+                                       break;
+                               }
                                 break;
                         }
                }
@@ -2110,7 +2094,7 @@ Session::refresh_disk_space ()
 }
 
 string
-Session::get_best_session_directory_for_new_source ()
+Session::get_best_session_directory_for_new_audio ()
 {
        vector<space_and_path>::iterator i;
        string result = _session_dir->root_path();
@@ -2285,12 +2269,6 @@ Session::load_route_groups (const XMLNode& node, int version)
        return 0;
 }
 
-void
-Session::auto_save()
-{
-       save_state (_current_snapshot_name);
-}
-
 static bool
 state_file_filter (const string &str, void* /*arg*/)
 {
@@ -2633,8 +2611,6 @@ Session::cleanup_sources (CleanupReport& rep)
        vector<boost::shared_ptr<Source> > dead_sources;
        string audio_path;
        string midi_path;
-       vector<space_and_path>::iterator i;
-       vector<space_and_path>::iterator nexti;
        vector<string> candidates;
        vector<string> unused;
        set<string> all_sources;
@@ -2643,6 +2619,8 @@ Session::cleanup_sources (CleanupReport& rep)
        int ret = -1;
        string tmppath1;
        string tmppath2;
+       Searchpath asp;
+       Searchpath msp;
 
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
@@ -2684,38 +2662,20 @@ Session::cleanup_sources (CleanupReport& rep)
 
        /* build a list of all the possible audio directories for the session */
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
-               nexti = i;
-               ++nexti;
-
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                SessionDirectory sdir ((*i).path);
-               audio_path += sdir.sound_path();
-
-               if (nexti != session_dirs.end()) {
-                       audio_path += G_SEARCHPATH_SEPARATOR;
-               }
-
-               i = nexti;
+               asp += sdir.sound_path();
        }
+       audio_path += asp.to_string();
 
 
        /* build a list of all the possible midi directories for the session */
 
-       for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
-               nexti = i;
-               ++nexti;
-
+       for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                SessionDirectory sdir ((*i).path);
-               midi_path += sdir.midi_path();
-
-               if (nexti != session_dirs.end()) {
-                       midi_path += G_SEARCHPATH_SEPARATOR;
-               }
-
-               i = nexti;
+               msp += sdir.midi_path();
        }
+       midi_path += msp.to_string();
 
        find_files_matching_filter (candidates, audio_path, accept_all_audio_files, (void *) 0, true, true);
        find_files_matching_filter (candidates, midi_path, accept_all_midi_files, (void *) 0, true, true);
@@ -2922,6 +2882,12 @@ Session::cleanup_trash_sources (CleanupReport& rep)
 void
 Session::set_dirty ()
 {
+       /* never mark session dirty during loading */
+
+       if (_state_of_the_state & Loading) {
+               return;
+       }
+
        bool was_dirty = dirty();
 
        _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
@@ -3013,7 +2979,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        case ControllableDescriptor::NamedRoute:
        {
                std::string str = desc.top_level_name();
-               if (str == "master") {
+               if (str == "Master" || str == "master") {
                        r = _master_out;
                } else if (str == "control" || str == "listen") {
                        r = _monitor_out;
@@ -3159,6 +3125,11 @@ Session::save_history (string snapshot_name)
                return 0;
        }
 
+       if (!Config->get_save_history() || Config->get_saved_history_depth() < 0 || 
+           (_history.undo_depth() == 0 && _history.redo_depth() == 0)) {
+               return 0;
+       }
+
        if (snapshot_name.empty()) {
                snapshot_name = _current_snapshot_name;
        }
@@ -3175,10 +3146,6 @@ Session::save_history (string snapshot_name)
                }
        }
 
-       if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) {
-               return 0;
-       }
-
        tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
 
        if (!tree.write (xml_path))
@@ -3821,3 +3788,92 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo
 
        return !(found_sr && found_data_format); // zero if they are both found
 }
+
+typedef std::vector<boost::shared_ptr<FileSource> > SeveralFileSources;
+typedef std::map<std::string,SeveralFileSources> SourcePathMap;
+
+int
+Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,string)> callback)
+{
+       uint32_t total = 0;
+       uint32_t n = 0;
+       SourcePathMap source_path_map;
+       string new_path;
+       boost::shared_ptr<AudioFileSource> afs;
+       int ret = 0;
+
+       {
+
+               Glib::Threads::Mutex::Lock lm (source_lock);
+               
+               cerr << " total sources = " << sources.size();
+               
+               for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
+                       boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
+                       
+                       if (!fs) {
+                               continue;
+                       }
+                       
+                       if (fs->within_session()) {
+                               cerr << "skip " << fs->name() << endl;
+                               continue;
+                       }
+                       
+                       if (source_path_map.find (fs->path()) != source_path_map.end()) {
+                               source_path_map[fs->path()].push_back (fs);
+                       } else {
+                               SeveralFileSources v;
+                               v.push_back (fs);
+                               source_path_map.insert (make_pair (fs->path(), v));
+                       }
+                       
+                       total++;
+               }
+               
+               cerr << " fsources = " << total << endl;
+               
+               for (SourcePathMap::iterator i = source_path_map.begin(); i != source_path_map.end(); ++i) {
+                       
+                       /* tell caller where we are */
+                       
+                       string old_path = i->first;
+                       
+                       callback (n, total, old_path);
+                       
+                       cerr << old_path << endl;
+                       
+                       new_path.clear ();
+                       
+                       switch (i->second.front()->type()) {
+                       case DataType::AUDIO:
+                               new_path = new_audio_source_path_for_embedded (old_path);
+                               break;
+                               
+                       case DataType::MIDI:
+                               break;
+                       }
+                       
+                       cerr << "Move " << old_path << " => " << new_path << endl;
+                       
+                       if (!copy_file (old_path, new_path)) {
+                               cerr << "failed !\n";
+                               ret = -1;
+                       }
+                       
+                       /* make sure we stop looking in the external
+                          dir/folder. Remember, this is an all-or-nothing
+                          operations, it doesn't merge just some files.
+                       */
+                       remove_dir_from_search_path (Glib::path_get_dirname (old_path), i->second.front()->type());
+
+                       for (SeveralFileSources::iterator f = i->second.begin(); f != i->second.end(); ++f) {
+                               (*f)->set_path (new_path);
+                       }
+               }
+       }
+
+       save_state ("", false, false);
+
+       return ret;
+}