+
+static void set_progress (Progress* p, size_t n, size_t t)
+{
+ p->set_progress (float (n) / float(t));
+}
+
+int
+Session::archive_session (const std::string& dest,
+ const std::string& name,
+ ArchiveEncode compress_audio,
+ bool only_used_sources,
+ Progress* progress)
+{
+ if (dest.empty () || name.empty ()) {
+ return -1;
+ }
+
+ /* save current values */
+ bool was_dirty = dirty ();
+ string old_path = _path;
+ string old_name = _name;
+ string old_snapshot = _current_snapshot_name;
+ string old_sd = _session_dir->root_path();
+ string old_config_search_path[DataType::num_types];
+ old_config_search_path[DataType::AUDIO] = config.get_audio_search_path ();
+ old_config_search_path[DataType::MIDI] = config.get_midi_search_path ();
+
+ /* ensure that session-path is included in search-path */
+ bool ok = false;
+ for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
+ if ((*sd).path == old_path) {
+ ok = true;
+ }
+ }
+ if (!ok) {
+ return -1;
+ }
+
+ /* create temporary dir to save session to */
+#ifdef PLATFORM_WINDOWS
+ char tmp[256] = "C:\\TEMP\\";
+ GetTempPath (sizeof (tmp), tmp);
+#else
+ char const* tmp = getenv("TMPDIR");
+ if (!tmp) {
+ tmp = "/tmp/";
+ }
+#endif
+ if ((strlen (tmp) + 21) > 1024) {
+ return -1;
+ }
+
+ char tmptpl[1024];
+ strcpy (tmptpl, tmp);
+ strcat (tmptpl, "ardourarchive-XXXXXX");
+ char* tmpdir = g_mkdtemp (tmptpl);
+
+ if (!tmpdir) {
+ return -1;
+ }
+
+ std::string to_dir = std::string (tmpdir);
+
+ /* switch session directory temporarily */
+ (*_session_dir) = to_dir;
+
+ if (!_session_dir->create()) {
+ (*_session_dir) = old_sd;
+ remove_directory (to_dir);
+ return -1;
+ }
+
+ /* prepare archive */
+ string archive = Glib::build_filename (dest, name + ".tar.xz");
+
+ PBD::ScopedConnectionList progress_connection;
+ PBD::FileArchive ar (archive);
+ if (progress) {
+ ar.progress.connect_same_thread (progress_connection, boost::bind (&set_progress, progress, _1, _2));
+ }
+
+ /* collect files to archive */
+ std::map<string,string> filemap;
+
+ vector<string> do_not_copy_extensions;
+ do_not_copy_extensions.push_back (statefile_suffix);
+ do_not_copy_extensions.push_back (pending_suffix);
+ do_not_copy_extensions.push_back (backup_suffix);
+ do_not_copy_extensions.push_back (temp_suffix);
+ do_not_copy_extensions.push_back (history_suffix);
+
+ vector<string> blacklist_dirs;
+ blacklist_dirs.push_back (string (peak_dir_name) + G_DIR_SEPARATOR);
+ blacklist_dirs.push_back (string (analysis_dir_name) + G_DIR_SEPARATOR);
+ blacklist_dirs.push_back (string (dead_dir_name) + G_DIR_SEPARATOR);
+ blacklist_dirs.push_back (string (export_dir_name) + G_DIR_SEPARATOR);
+ blacklist_dirs.push_back (string (externals_dir_name) + G_DIR_SEPARATOR);
+ blacklist_dirs.push_back (string (plugins_dir_name) + G_DIR_SEPARATOR);
+
+ std::map<boost::shared_ptr<AudioFileSource>, std::string> orig_sources;
+ std::map<boost::shared_ptr<AudioFileSource>, float> orig_gain;
+
+ set<boost::shared_ptr<Source> > sources_used_by_this_snapshot;
+ if (only_used_sources) {
+ playlists->sync_all_regions_with_regions ();
+ playlists->foreach (boost::bind (merge_all_sources, _1, &sources_used_by_this_snapshot), false);
+ }
+
+ // collect audio sources for this session, calc total size for encoding
+ // add option to only include *used* sources (see Session::cleanup_sources)
+ size_t total_size = 0;
+ {
+ Glib::Threads::Mutex::Lock lm (source_lock);
+ for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (i->second);
+ if (!afs || afs->readable_length () == 0) {
+ continue;
+ }
+
+ if (only_used_sources) {
+ if (!afs->used()) {
+ continue;
+ }
+ if (sources_used_by_this_snapshot.find (afs) == sources_used_by_this_snapshot.end ()) {
+ continue;
+ }
+ }
+
+ std::string from = afs->path();
+
+ if (compress_audio != NO_ENCODE) {
+ total_size += afs->readable_length ();
+ } else {
+ if (afs->within_session()) {
+ filemap[from] = make_new_media_path (from, name, name);
+ } else {
+ filemap[from] = make_new_media_path (from, name, name);
+ remove_dir_from_search_path (Glib::path_get_dirname (from), DataType::AUDIO);
+ }
+ }
+ }
+ }
+
+ /* encode audio */
+ if (compress_audio != NO_ENCODE) {
+ if (progress) {
+ progress->set_progress (2); // set to "encoding"
+ progress->set_progress (0);
+ }
+
+ Glib::Threads::Mutex::Lock lm (source_lock);
+ for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (i->second);
+ if (!afs || afs->readable_length () == 0) {
+ continue;
+ }
+
+ if (only_used_sources) {
+ if (!afs->used()) {
+ continue;
+ }
+ if (sources_used_by_this_snapshot.find (afs) == sources_used_by_this_snapshot.end ()) {
+ continue;
+ }
+ }
+
+ orig_sources[afs] = afs->path();
+ orig_gain[afs] = afs->gain();
+
+ std::string new_path = make_new_media_path (afs->path (), to_dir, name);
+ new_path = Glib::build_filename (Glib::path_get_dirname (new_path), PBD::basename_nosuffix (new_path) + ".flac");
+ g_mkdir_with_parents (Glib::path_get_dirname (new_path).c_str (), 0755);
+
+ if (progress) {
+ progress->descend ((float)afs->readable_length () / total_size);
+ }
+
+ try {
+ SndFileSource* ns = new SndFileSource (*this, *(afs.get()), new_path, compress_audio == FLAC_16BIT, progress);
+ afs->replace_file (new_path);
+ afs->set_gain (ns->gain(), true);
+ delete ns;
+ } catch (...) {
+ cerr << "failed to encode " << afs->path() << " to " << new_path << "\n";
+ }
+
+ if (progress) {
+ progress->ascend ();
+ }
+ }
+ }
+
+ if (progress) {
+ progress->set_progress (-1); // set to "archiving"
+ progress->set_progress (0);
+ }
+
+ /* index files relevant for this session */
+ for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
+ vector<string> files;
+
+ size_t prefix_len = (*sd).path.size();
+ if (prefix_len > 0 && (*sd).path.at (prefix_len - 1) != G_DIR_SEPARATOR) {
+ ++prefix_len;
+ }
+
+ find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
+
+ static const std::string audiofile_dir_string = string (sound_dir_name) + G_DIR_SEPARATOR;
+ static const std::string videofile_dir_string = string (video_dir_name) + G_DIR_SEPARATOR;
+ static const std::string midifile_dir_string = string (midi_dir_name) + G_DIR_SEPARATOR;
+
+ for (vector<string>::const_iterator i = files.begin (); i != files.end (); ++i) {
+ std::string from = *i;
+
+#ifdef __APPLE__
+ string filename = Glib::path_get_basename (from);
+ std::transform (filename.begin(), filename.end(), filename.begin(), ::toupper);
+ if (filename == ".DS_STORE") {
+ continue;
+ }
+#endif
+
+ if (from.find (audiofile_dir_string) != string::npos) {
+ ; // handled above
+ } else if (from.find (midifile_dir_string) != string::npos) {
+ filemap[from] = make_new_media_path (from, name, name);
+ } else if (from.find (videofile_dir_string) != string::npos) {
+ filemap[from] = make_new_media_path (from, name, name);
+ } else {
+ bool do_copy = true;
+ for (vector<string>::iterator v = blacklist_dirs.begin(); v != blacklist_dirs.end(); ++v) {
+ if (from.find (*v) != string::npos) {
+ do_copy = false;
+ break;
+ }
+ }
+ for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
+ if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
+ do_copy = false;
+ break;
+ }
+ }
+
+ if (do_copy) {
+ filemap[from] = name + G_DIR_SEPARATOR + from.substr (prefix_len);
+ }
+ }
+ }
+ }
+
+ /* write session file */
+ _path = to_dir;
+ g_mkdir_with_parents (externals_dir ().c_str (), 0755);
+#ifdef LV2_SUPPORT
+ PBD::Unwinder<bool> uw (LV2Plugin::force_state_save, true);
+#endif
+ save_state (name);
+ save_default_options ();
+
+ size_t prefix_len = _path.size();
+ if (prefix_len > 0 && _path.at (prefix_len - 1) != G_DIR_SEPARATOR) {
+ ++prefix_len;
+ }
+
+ /* collect session-state files */
+ vector<string> files;
+ do_not_copy_extensions.clear ();
+ do_not_copy_extensions.push_back (history_suffix);
+
+ blacklist_dirs.clear ();
+ blacklist_dirs.push_back (string (externals_dir_name) + G_DIR_SEPARATOR);
+
+ find_files_matching_filter (files, to_dir, accept_all_files, 0, false, true, true);
+ for (vector<string>::const_iterator i = files.begin (); i != files.end (); ++i) {
+ std::string from = *i;
+ bool do_copy = true;
+ for (vector<string>::iterator v = blacklist_dirs.begin(); v != blacklist_dirs.end(); ++v) {
+ if (from.find (*v) != string::npos) {
+ do_copy = false;
+ break;
+ }
+ }
+ for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
+ if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
+ do_copy = false;
+ break;
+ }
+ }
+ if (do_copy) {
+ filemap[from] = name + G_DIR_SEPARATOR + from.substr (prefix_len);
+ }
+ }
+
+ /* restore original values */
+ _path = old_path;
+ _name = old_name;
+ set_snapshot_name (old_snapshot);
+ (*_session_dir) = old_sd;
+ if (was_dirty) {
+ set_dirty ();
+ }
+ config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
+ config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
+
+ for (std::map<boost::shared_ptr<AudioFileSource>, std::string>::iterator i = orig_sources.begin (); i != orig_sources.end (); ++i) {
+ i->first->replace_file (i->second);
+ }
+ for (std::map<boost::shared_ptr<AudioFileSource>, float>::iterator i = orig_gain.begin (); i != orig_gain.end (); ++i) {
+ i->first->set_gain (i->second, true);
+ }
+
+ int rv = ar.create (filemap);
+ remove_directory (to_dir);
+
+ return rv;
+}
+
+void
+Session::undo (uint32_t n)
+{
+ if (actively_recording()) {
+ return;
+ }
+
+ _history.undo (n);
+}
+
+void
+Session::redo (uint32_t n)
+{
+ if (actively_recording()) {
+ return;
+ }
+
+ _history.redo (n);
+}