Fix some capitalisation in the preferences dialog.
[ardour.git] / libs / ardour / session_state.cc
index e847ba45ce215a5d5926b3190c304c4f706ee6f7..07f10e9bc10236538e2207dd3532d2b9b9c9a3ef 100644 (file)
 #include <unistd.h>
 #include <sys/stat.h>
 #include <climits>
-#include <fcntl.h>
-#include <poll.h>
 #include <signal.h>
-#include <sys/mman.h>
 #include <sys/time.h>
 
 #ifdef HAVE_SYS_VFS_H
 #include <sys/vfs.h>
-#else
+#endif
+
+#ifdef __APPLE__
 #include <sys/param.h>
 #include <sys/mount.h>
 #endif
 #include "pbd/enumwriter.h"
 #include "pbd/error.h"
 #include "pbd/file_utils.h"
+#include "pbd/pathexpand.h"
 #include "pbd/pathscanner.h"
 #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"
 #include "ardour/audio_diskstream.h"
@@ -135,15 +136,8 @@ Session::pre_engine_init (string fullpath)
 
        /* discover canonical fullpath */
 
-       char buf[PATH_MAX+1];
-       if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
-               error << string_compose(_("Could not use path %1 (%2)"), buf, strerror(errno)) << endmsg;
-               destroy ();
-               throw failed_constructor();
-       }
+       _path = canonical_path(fullpath);
 
-       _path = string(buf);
-       
        /* we require _path to end with a dir separator */
 
        if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
@@ -210,16 +204,6 @@ Session::post_engine_init ()
        set_block_size (_engine.samples_per_cycle());
        set_frame_rate (_engine.sample_rate());
 
-       if (how_many_dsp_threads () > 1) {
-               /* For now, only create the graph if we are using >1 DSP threads, as
-                  it is a bit slower than the old code with 1 thread.
-               */
-               _process_graph.reset (new Graph (*this));
-       }
-
-       n_physical_outputs = _engine.n_physical_outputs ();
-       n_physical_inputs =  _engine.n_physical_inputs ();
-
        BootMessage (_("Using configuration"));
 
        _midi_ports = new MidiPortManager;
@@ -280,8 +264,47 @@ Session::post_engine_init ()
                Config->map_parameters (ff);
                config.map_parameters (ft);
 
-               when_engine_running ();
-
+               /* Reset all panners */
+               
+               Delivery::reset_panners ();
+               
+               /* this will cause the CPM to instantiate any protocols that are in use
+                * (or mandatory), which will pass it this Session, and then call
+                * set_state() on each instantiated protocol to match stored state.
+                */
+               
+               ControlProtocolManager::instance().set_session (this);
+               
+               /* This must be done after the ControlProtocolManager set_session above,
+                  as it will set states for ports which the ControlProtocolManager creates.
+               */
+               
+               // XXX set state of MIDI::Port's
+               // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
+               
+               /* And this must be done after the MIDI::Manager::set_port_states as
+                * it will try to make connections whose details are loaded by set_port_states.
+                */
+               
+               hookup_io ();
+               
+               /* Let control protocols know that we are now all connected, so they
+                * could start talking to surfaces if they want to.
+                */
+               
+               ControlProtocolManager::instance().midi_connectivity_established ();
+               
+               if (_is_new && !no_auto_connect()) {
+                       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock());
+                       auto_connect_master_bus ();
+               }
+               
+               _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
+               
+               /* update latencies */
+               
+               initialize_latencies ();
+               
                _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));
                
@@ -326,7 +349,7 @@ Session::post_engine_init ()
 string
 Session::raid_path () const
 {
-       SearchPath raid_search_path;
+       Searchpath raid_search_path;
 
        for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
                raid_search_path += (*i).path;
@@ -347,11 +370,11 @@ Session::setup_raid_path (string path)
 
        session_dirs.clear ();
 
-       SearchPath search_path(path);
-       SearchPath sound_search_path;
-       SearchPath midi_search_path;
+       Searchpath search_path(path);
+       Searchpath sound_search_path;
+       Searchpath midi_search_path;
 
-       for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
+       for (Searchpath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
                sp.path = *i;
                sp.blocks = 0; // not needed
                session_dirs.push_back (sp);
@@ -464,9 +487,10 @@ Session::create (const string& session_template, BusProfile* bus_profile)
                ifstream in(in_path.c_str());
 
                if (in) {
-                       string out_path = _path;
-                       out_path += _name;
-                       out_path += statefile_suffix;
+                       /* no need to call legalize_for_path() since the string
+                        * in session_template is already a legal path name
+                        */
+                       string out_path = Glib::build_filename (_session_dir->root_path(), _name + statefile_suffix);
 
                        ofstream out(out_path.c_str());
 
@@ -696,9 +720,9 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
 
        } else {
 
-               if (::rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
-                       error << string_compose (_("could not rename temporary session file %1 to %2"),
-                                       tmp_path, xml_path) << endmsg;
+               if (::g_rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
+                       error << string_compose (_("could not rename temporary session file %1 to %2 (%3)"),
+                                       tmp_path, xml_path, g_strerror(errno)) << endmsg;
                        if (g_remove (tmp_path.c_str()) != 0) {
                                error << string_compose(_("Could not remove temporary session file at path \"%1\" (%2)"),
                                                tmp_path, g_strerror (errno)) << endmsg;
@@ -774,7 +798,7 @@ Session::load_state (string snapshot_name)
 
        set_dirty();
 
-       _writable = exists_and_writable (xmlpath);
+       _writable = exists_and_writable (xmlpath) && exists_and_writable(Glib::path_get_dirname(xmlpath));
 
        if (!state_tree->read (xmlpath)) {
                error << string_compose(_("Could not understand session file %1"), xmlpath) << endmsg;
@@ -892,7 +916,7 @@ Session::state (bool full_state)
                                p += (*i).path;
 
                                if (next != session_dirs.end()) {
-                                       p += ':';
+                                       p += G_SEARCHPATH_SEPARATOR;
                                } else {
                                        break;
                                }
@@ -1287,13 +1311,7 @@ Session::set_state (const XMLNode& node, int version)
        if ((child = find_named_node (node, "Click")) == 0) {
                warning << _("Session: XML state has no click section") << endmsg;
        } else if (_click_io) {
-               const XMLNodeList& children (child->children());
-               XMLNodeList::const_iterator i = children.begin();
-               _click_io->set_state (**i, version);
-               ++i;
-               if (i != children.end()) {
-                       _click_gain->set_state (**i, version);
-               }
+               setup_click_state (&node);
        }
 
        if ((child = find_named_node (node, ControlProtocolManager::state_node_name)) != 0) {
@@ -2017,6 +2035,54 @@ Session::refresh_disk_space ()
                        _total_free_4k_blocks_uncertain = true;
                }
        }
+#elif defined (COMPILER_MSVC)
+       vector<string> scanned_volumes;
+       vector<string>::iterator j;
+       vector<space_and_path>::iterator i;
+    DWORD nSectorsPerCluster, nBytesPerSector,
+          nFreeClusters, nTotalClusters;
+    char disk_drive[4];
+       bool volume_found;
+
+       _total_free_4k_blocks = 0;
+
+       for (i = session_dirs.begin(); i != session_dirs.end(); i++) {
+               strncpy (disk_drive, (*i).path.c_str(), 3);
+               disk_drive[3] = 0;
+               strupr(disk_drive);
+
+               volume_found = false;
+               if (0 != (GetDiskFreeSpace(disk_drive, &nSectorsPerCluster, &nBytesPerSector, &nFreeClusters, &nTotalClusters)))
+               {
+                       int64_t nBytesPerCluster = nBytesPerSector * nSectorsPerCluster;
+                       int64_t nFreeBytes = nBytesPerCluster * (int64_t)nFreeClusters;
+                       i->blocks = (uint32_t)(nFreeBytes / 4096);
+
+                       for (j = scanned_volumes.begin(); j != scanned_volumes.end(); j++) {
+                               if (0 == j->compare(disk_drive)) {
+                                       volume_found = true;
+                                       break;
+                               }
+                       }
+
+                       if (!volume_found) {
+                               scanned_volumes.push_back(disk_drive);
+                               _total_free_4k_blocks += i->blocks;
+                       }
+               }
+       }
+
+       if (0 == _total_free_4k_blocks) {
+               strncpy (disk_drive, path().c_str(), 3);
+               disk_drive[3] = 0;
+
+               if (0 != (GetDiskFreeSpace(disk_drive, &nSectorsPerCluster, &nBytesPerSector, &nFreeClusters, &nTotalClusters)))
+               {
+                       int64_t nBytesPerCluster = nBytesPerSector * nSectorsPerCluster;
+                       int64_t nFreeBytes = nBytesPerCluster * (int64_t)nFreeClusters;
+                       _total_free_4k_blocks = (uint32_t)(nFreeBytes / 4096);
+               }
+       }
 #endif
 }
 
@@ -2203,7 +2269,7 @@ Session::auto_save()
 }
 
 static bool
-state_file_filter (const string &str, void */*arg*/)
+state_file_filter (const string &str, void/*arg*/)
 {
        return (str.length() > strlen(statefile_suffix) &&
                str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
@@ -2380,7 +2446,7 @@ Session::commit_reversible_command (Command *cmd)
 }
 
 static bool
-accept_all_audio_files (const string& path, void */*arg*/)
+accept_all_audio_files (const string& path, void/*arg*/)
 {
         if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
                 return false;
@@ -2394,7 +2460,7 @@ accept_all_audio_files (const string& path, void */*arg*/)
 }
 
 static bool
-accept_all_midi_files (const string& path, void */*arg*/)
+accept_all_midi_files (const string& path, void/*arg*/)
 {
         if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
                 return false;
@@ -2406,7 +2472,7 @@ accept_all_midi_files (const string& path, void */*arg*/)
 }
 
 static bool
-accept_all_state_files (const string& path, void */*arg*/)
+accept_all_state_files (const string& path, void/*arg*/)
 {
         if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
                 return false;
@@ -2562,6 +2628,8 @@ Session::cleanup_sources (CleanupReport& rep)
        bool used;
        string spath;
        int ret = -1;
+       string tmppath1;
+       string tmppath2;
 
        _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
 
@@ -2612,7 +2680,7 @@ Session::cleanup_sources (CleanupReport& rep)
                audio_path += sdir.sound_path();
 
                if (nexti != session_dirs.end()) {
-                       audio_path += ':';
+                       audio_path += G_SEARCHPATH_SEPARATOR;
                }
 
                i = nexti;
@@ -2630,7 +2698,7 @@ Session::cleanup_sources (CleanupReport& rep)
                midi_path += sdir.midi_path();
 
                if (nexti != session_dirs.end()) {
-                       midi_path += ':';
+                       midi_path += G_SEARCHPATH_SEPARATOR;
                }
 
                i = nexti;
@@ -2668,27 +2736,28 @@ Session::cleanup_sources (CleanupReport& rep)
                 ++tmp;
 
                if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
-                        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.
-                                */
+                       if (!fs->is_stub()) {
 
-                                RegionFactory::remove_regions_using_source (i->second);
-                                sources.erase (i);
-                        }
+                               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.
+                                       */
+                                       
+                                       RegionFactory::remove_regions_using_source (i->second);
+                                       sources.erase (i);
+                               }
+                       }
                }
 
                 i = tmp;
        }
 
-       char tmppath1[PATH_MAX+1];
-       char tmppath2[PATH_MAX+1];
-
         if (candidates) {
                 for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
 
@@ -2697,19 +2766,10 @@ Session::cleanup_sources (CleanupReport& rep)
 
                         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;
-                                }
+                               tmppath1 = canonical_path (spath);
+                               tmppath2 = canonical_path ((*i));
 
-                                if (realpath((*i).c_str(),  tmppath2) == 0) {
-                                        error << string_compose (_("Cannot expand path %1 (%2)"),
-                                                                 (*i), strerror (errno)) << endmsg;
-                                        continue;
-                                }
-
-                                if (strcmp(tmppath1, tmppath2) == 0) {
+                               if (tmppath1 == tmppath2) {
                                         used = true;
                                         break;
                                 }
@@ -2813,7 +2873,7 @@ Session::cleanup_sources (CleanupReport& rep)
                string peakpath = peak_path (base);
 
                if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
-                       if (::unlink (peakpath.c_str()) != 0) {
+                       if (::g_unlink (peakpath.c_str()) != 0) {
                                error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
                                                          peakpath, _path, strerror (errno))
                                      << endmsg;
@@ -3420,22 +3480,6 @@ Session::config_changed (std::string p, bool ours)
                /* XXX DO SOMETHING HERE TO TELL THE GUI THAT WE NEED
                   TO SET REMOTE ID'S
                */
-       } else if (p == "sync-all-route-ordering") {
-
-               /* sync to editor order unless mixer is used for remote IDs 
-                */
-
-               switch (Config->get_remote_model()) {
-               case UserOrdered:
-                       sync_order_keys (EditorSort);
-                       break;
-               case EditorOrdered:
-                       sync_order_keys (EditorSort);
-                       break;
-               case MixerOrdered:
-                       sync_order_keys (MixerSort);
-               }
-                       
        } else if (p == "initial-program-change") {
 
                if (_mmc->output_port() && Config->get_initial_program_change() >= 0) {
@@ -3558,8 +3602,6 @@ Session::rename (const std::string& new_name)
 
        string const old_sources_root = _session_dir->sources_root();
 
-#define RENAME ::rename
-
        /* Rename:
 
         * session directory
@@ -3621,7 +3663,8 @@ Session::rename (const std::string& new_name)
 
                cerr << "Rename " << oldstr << " => " << newstr << endl;                
 
-               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+               if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
+                       error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
                        return 1;
                }
 
@@ -3648,7 +3691,8 @@ Session::rename (const std::string& new_name)
                
                cerr << "Rename " << oldstr << " => " << newstr << endl;
                
-               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+               if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
+                       error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
                        return 1;
                }
        }
@@ -3660,7 +3704,8 @@ Session::rename (const std::string& new_name)
        
        cerr << "Rename " << oldstr << " => " << newstr << endl;                
 
-       if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+       if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
+               error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
                return 1;
        }
 
@@ -3674,7 +3719,8 @@ Session::rename (const std::string& new_name)
                
                cerr << "Rename " << oldstr << " => " << newstr << endl;                
                
-               if (RENAME (oldstr.c_str(), newstr.c_str()) != 0) {
+               if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
+                       error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
                        return 1;
                }
        }
@@ -3698,6 +3744,11 @@ Session::rename (const std::string& new_name)
        _current_snapshot_name = new_name;
        _name = new_name;
 
+       /* re-add directory separator - reverse hack to oldstr above */
+       if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
+               _path += G_DIR_SEPARATOR;
+       }
+
        set_dirty ();
 
        /* save state again to get everything just right */
@@ -3710,6 +3761,69 @@ Session::rename (const std::string& new_name)
        store_recent_sessions (new_name, _path);
 
        return 0;
+}
+
+int
+Session::get_session_info_from_path (XMLTree& tree, const string& xmlpath)
+{
+       if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
+               return -1;
+        }
+
+       if (!tree.read (xmlpath)) {
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFormat& data_format)
+{
+       XMLTree tree;
+       bool found_sr = false;
+       bool found_data_format = false;
+
+       if (get_session_info_from_path (tree, xmlpath)) {
+               return -1;
+       }
+
+       /* sample rate */
+
+       const XMLProperty* prop;
+       if ((prop = tree.root()->property (X_("sample-rate"))) != 0) {          
+               sample_rate = atoi (prop->value());
+               found_sr = true;
+       }
+
+       const XMLNodeList& children (tree.root()->children());
+       for (XMLNodeList::const_iterator c = children.begin(); c != children.end(); ++c) {
+               const XMLNode* child = *c;
+               if (child->name() == "Config") {
+                       const XMLNodeList& options (child->children());
+                       for (XMLNodeList::const_iterator oc = options.begin(); oc != options.end(); ++oc) {
+                               const XMLNode* option = *oc;
+                               const XMLProperty* name = option->property("name");
+
+                               if (!name) {
+                                       continue;
+                               }
+
+                               if (name->value() == "native-file-data-format") {
+                                       const XMLProperty* value = option->property ("value");
+                                       if (value) {
+                                               SampleFormat fmt = (SampleFormat) string_2_enum (option->property ("value")->value(), fmt);
+                                               data_format = fmt;
+                                               found_data_format = true;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               if (found_data_format) {
+                       break;
+               }
+       }
 
-#undef RENAME
+       return !(found_sr && found_data_format); // zero if they are both found
 }