From fcabd5d8ee172e9d27423864448902ad99634ac5 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 8 Jul 2014 00:53:06 -0400 Subject: initial implementation of "bring all media into session folder". Incomplete but basically functional for audio files --- libs/ardour/ardour/file_source.h | 2 + libs/ardour/ardour/session.h | 6 + libs/ardour/ardour/sndfilesource.h | 2 + libs/ardour/audiofilesource.cc | 2 + libs/ardour/file_source.cc | 8 ++ libs/ardour/session.cc | 238 ++++++++++++++++++++++++++----------- libs/ardour/session_state.cc | 89 ++++++++++++++ libs/ardour/smf_source.cc | 4 +- libs/ardour/sndfilesource.cc | 9 ++ 9 files changed, 291 insertions(+), 69 deletions(-) (limited to 'libs') diff --git a/libs/ardour/ardour/file_source.h b/libs/ardour/ardour/file_source.h index 8b8adfeb66..8cbbfed0d9 100644 --- a/libs/ardour/ardour/file_source.h +++ b/libs/ardour/ardour/file_source.h @@ -89,6 +89,8 @@ public: */ int rename (const std::string& name); + virtual void release_descriptor () {} + protected: FileSource (Session& session, DataType type, const std::string& path, diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 430c0d7a8c..53215af2b6 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -196,11 +196,16 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop std::string peak_path (std::string) const; std::string peak_path_from_audio_path (std::string) const; + bool audio_source_name_is_unique (const std::string& name, uint32_t chan); + std::string format_audio_source_name (const std::string& legalized_base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required, uint32_t cnt, bool related_exists); + std::string new_audio_source_path_for_embedded (const std::string& existing_path); std::string new_audio_source_path (const std::string&, uint32_t nchans, uint32_t chan, bool destructive, bool take_required); std::string new_midi_source_path (const std::string&); RouteList new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name); std::vector get_paths_for_new_sources (bool allow_replacing, const std::string& import_file_path, uint32_t channels); + int bring_all_sources_into_session (boost::function callback); + void process (pframes_t nframes); BufferSet& get_silent_buffers (ChanCount count = ChanCount::ZERO); @@ -863,6 +868,7 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop std::vector source_search_path(DataType) const; void ensure_search_path_includes (const std::string& path, DataType type); + void remove_dir_from_search_path (const std::string& path, DataType type); std::list unknown_processors () const; diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index 831f8db5f7..99fb9f4d09 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -75,6 +75,8 @@ class LIBARDOUR_API SndFileSource : public AudioFileSource { static int get_soundfile_info (const std::string& path, SoundFileInfo& _info, std::string& error_msg); + void release_descriptor (); + protected: void set_path (const std::string& p); void set_header_timeline_position (); diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 7d34b9d9a5..9c1b969190 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -32,6 +32,7 @@ #include "pbd/convert.h" #include "pbd/basename.h" +#include "pbd/file_utils.h" #include "pbd/mountpoint.h" #include "pbd/stl_delete.h" #include "pbd/strsplit.h" @@ -413,3 +414,4 @@ AudioFileSource::get_interleave_buffer (framecnt_t size) return ssb->buf; } + diff --git a/libs/ardour/file_source.cc b/libs/ardour/file_source.cc index 8c41f981b9..bb6d3562fa 100644 --- a/libs/ardour/file_source.cc +++ b/libs/ardour/file_source.cc @@ -546,6 +546,12 @@ void FileSource::set_path (const std::string& newpath) { _path = newpath; + set_within_session_from_path (newpath); + if (_within_session) { + _origin = Glib::path_get_basename (newpath); + } else { + _origin = newpath; + } } void @@ -597,3 +603,5 @@ FileSource::rename (const string& newpath) return 0; } + + diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 989b916065..19a081e17b 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -44,6 +44,7 @@ #include "pbd/stacktrace.h" #include "pbd/file_utils.h" #include "pbd/convert.h" +#include "pbd/md5.h" #include "pbd/unwind.h" #include "pbd/search_path.h" @@ -3442,94 +3443,168 @@ Session::peak_path (string base) const return Glib::build_filename (_session_dir->peak_path(), base + peakfile_suffix); } -/** Return a unique name based on \a base for a new internal audio source */ string -Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required) +Session::new_audio_source_path_for_embedded (const std::string& path) { - uint32_t cnt; - string possible_name; - const uint32_t limit = 9999; // arbitrary limit on number of files with the same basic name - string legalized; - string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); - bool some_related_source_name_exists = false; + /* embedded source: + * + * we know that the filename is already unique because it exists + * out in the filesystem. + * + * However, when we bring it into the session, we could get a + * collision. + * + * Eg. two embedded files: + * + * /foo/bar/baz.wav + * /frob/nic/baz.wav + * + * When merged into session, these collide. + * + * There will not be a conflict with in-memory sources + * because when the source was created we already picked + * a unique name for it. + * + * This collision is not likely to be common, but we have to guard + * against it. So, if there is a collision, take the md5 hash of the + * the path, and use that as the filename instead. + */ - possible_name[0] = '\0'; - legalized = legalize_for_path (base); + SessionDirectory sdir (get_best_session_directory_for_new_audio()); + string base = Glib::path_get_basename (path); + string newpath = Glib::build_filename (sdir.sound_path(), base); + + if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) { - std::vector sdirs = source_search_path(DataType::AUDIO); + MD5 md5; - // Find a "version" of the base name that doesn't exist in any of the possible directories. + md5.digestString (path.c_str()); + md5.writeToString (); + base = md5.digestChars; - for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { + /* XXX base needs suffix from path */ + + newpath = Glib::build_filename (sdir.sound_path(), base); - vector::iterator i; - uint32_t existing = 0; + /* if this collides, we're screwed */ - for (vector::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) { + if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) { + error << string_compose (_("Merging embedded file %1: name collision AND md5 hash collision!"), path) << endmsg; + return string(); + } - ostringstream sstr; + } - if (destructive) { - sstr << 'T'; - sstr << setfill ('0') << setw (4) << cnt; - sstr << legalized; - } else { - sstr << legalized; - - if (take_required || some_related_source_name_exists) { - sstr << '-'; - sstr << cnt; - } - } - - if (nchan == 2) { - if (chan == 0) { - sstr << "%L"; - } else { - sstr << "%R"; - } - } else if (nchan > 2 && nchan < 26) { - sstr << '%'; - sstr << 'a' + chan; - } + return newpath; +} - sstr << ext; +bool +Session::audio_source_name_is_unique (const string& name, uint32_t chan) +{ + std::vector sdirs = source_search_path (DataType::AUDIO); + vector::iterator i; + uint32_t existing = 0; + string basename = PBD::basename_nosuffix (name); - possible_name = sstr.str(); - const string spath = (*i); + for (vector::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) { + + /* note that we search *without* the extension so that + we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" + in the event that this new name is required for + a file format change. + */ - /* note that we search *without* the extension so that - we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf" - in the event that this new name is required for - a file format change. - */ + const string spath = *i; + + if (matching_unsuffixed_filename_exists_in (spath, basename)) { + existing++; + break; + } + + /* it is possible that we have the path already + * assigned to a source that has not yet been written + * (ie. the write source for a diskstream). we have to + * check this in order to make sure that our candidate + * path isn't used again, because that can lead to + * two Sources point to the same file with different + * notions of their removability. + */ + + + string possible_path = Glib::build_filename (spath, name); - if (matching_unsuffixed_filename_exists_in (spath, possible_name)) { - existing++; - break; - } + if (audio_source_by_path_and_channel (possible_path, chan)) { + existing++; + break; + } + } + + return (existing == 0); +} - /* it is possible that we have the path already - * assigned to a source that has not yet been written - * (ie. the write source for a diskstream). we have to - * check this in order to make sure that our candidate - * path isn't used again, because that can lead to - * two Sources point to the same file with different - * notions of their removability. - */ +string +Session::format_audio_source_name (const string& legalized_base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required, uint32_t cnt, bool related_exists) +{ + ostringstream sstr; + const string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO); + + if (destructive) { + sstr << 'T'; + sstr << setfill ('0') << setw (4) << cnt; + sstr << legalized_base; + } else { + sstr << legalized_base; + + if (take_required || related_exists) { + sstr << '-'; + sstr << cnt; + } + } + + if (nchan == 2) { + if (chan == 0) { + sstr << "%L"; + } else { + sstr << "%R"; + } + } else if (nchan > 2) { + if (nchan < 26) { + sstr << '%'; + sstr << 'a' + chan; + } else { + /* XXX what? more than 26 channels! */ + sstr << '%'; + sstr << chan+1; + } + } + + sstr << ext; - string possible_path = Glib::build_filename (spath, possible_name); + return sstr.str(); +} - if (audio_source_by_path_and_channel (possible_path, chan)) { - existing++; - break; - } - } +/** Return a unique name based on \a base for a new internal audio source */ +string +Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required) +{ + uint32_t cnt; + string possible_name; + const uint32_t limit = 9999; // arbitrary limit on number of files with the same basic name + string legalized; + bool some_related_source_name_exists = false; - if (existing == 0) { + legalized = legalize_for_path (base); + + // Find a "version" of the base name that doesn't exist in any of the possible directories. + + for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) { + + possible_name = format_audio_source_name (legalized, nchan, chan, destructive, take_required, cnt, some_related_source_name_exists); + + if (audio_source_name_is_unique (possible_name, chan)) { break; } - + some_related_source_name_exists = true; if (cnt > limit) { @@ -4776,6 +4851,33 @@ Session::ensure_search_path_includes (const string& path, DataType type) } } +void +Session::remove_dir_from_search_path (const string& dir, DataType type) +{ + Searchpath sp; + + switch (type) { + case DataType::AUDIO: + sp = Searchpath(config.get_audio_search_path ()); + break; + case DataType::MIDI: + sp = Searchpath (config.get_midi_search_path ()); + break; + } + + sp -= dir; + + switch (type) { + case DataType::AUDIO: + config.set_audio_search_path (sp.to_string()); + break; + case DataType::MIDI: + config.set_midi_search_path (sp.to_string()); + break; + } + +} + boost::shared_ptr Session::get_speakers() { diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 9321973f7e..21088227d5 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -3773,3 +3773,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 > SeveralFileSources; +typedef std::map SourcePathMap; + +int +Session::bring_all_sources_into_session (boost::function callback) +{ + uint32_t total = 0; + uint32_t n = 0; + SourcePathMap source_path_map; + string new_path; + boost::shared_ptr 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 fs = boost::dynamic_pointer_cast (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; +} diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index e39ef3f548..6ec7c5a78c 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -26,6 +26,7 @@ #include #include +#include "pbd/file_utils.h" #include "pbd/stl_delete.h" #include "pbd/strsplit.h" @@ -717,4 +718,5 @@ SMFSource::prevent_deletion () _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy)); } - + + diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index af25b3e76f..58fbab233b 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -1016,3 +1016,12 @@ SndFileSource::set_path (const string& p) _descriptor->set_path (_path); } } + +void +SndFileSource::release_descriptor () +{ + if (_descriptor) { + _descriptor->release (); + _descriptor = 0; + } +} -- cgit v1.2.3