improve, cleanup, rationalize Session::cleanup_sources() and supporting infrastructure

git-svn-id: svn://localhost/ardour2/branches/3.0@9015 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis
2011-03-01 21:54:54 +00:00
parent 3553ba71fa
commit b37b23af97
16 changed files with 261 additions and 111 deletions

View File

@@ -11,8 +11,7 @@ extern const char* const old_sound_dir_name;
extern const char* const sound_dir_name;
extern const char* const midi_dir_name;
extern const char* const midi_patch_dir_name;
extern const char* const dead_sound_dir_name;
extern const char* const dead_midi_dir_name;
extern const char* const dead_dir_name;
extern const char* const interchange_dir_name;
extern const char* const peak_dir_name;
extern const char* const export_dir_name;

View File

@@ -50,6 +50,9 @@ public:
int unstubify ();
void stubify ();
bool is_stub () const;
static bool is_stub_path (const std::string& path);
virtual bool safe_file_extension (const std::string& path) const = 0;

View File

@@ -159,6 +159,7 @@ public:
bool region_is_shuffle_constrained (boost::shared_ptr<Region>);
bool has_region_at (framepos_t const) const;
bool uses_source (boost::shared_ptr<const Source> src) const;
framepos_t find_next_transient (framepos_t position, int dir);
@@ -204,6 +205,8 @@ public:
virtual bool destroy_region (boost::shared_ptr<Region>) = 0;
void sync_all_regions_with_regions ();
/* special case function used by UI selection objects, which have playlists that actually own the regions
within them.
*/

View File

@@ -176,6 +176,7 @@ class Region
bool region_list_equivalent (boost::shared_ptr<const Region>) const;
bool source_equivalent (boost::shared_ptr<const Region>) const;
bool uses_source (boost::shared_ptr<const Source>) const;
bool uses_source_path (const std::string&) const;
std::string source_string () const;

View File

@@ -80,6 +80,7 @@ public:
static boost::shared_ptr<Region> create (SourceList& srcs, const XMLNode&);
static void get_regions_using_source (boost::shared_ptr<Source>, std::set<boost::shared_ptr<Region> >& );
static void remove_regions_using_source (boost::shared_ptr<Source>);
static void map_remove (boost::shared_ptr<Region>);
static void map_remove_with_equivalents (boost::shared_ptr<Region>);

View File

@@ -173,7 +173,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
std::string sound_dir (bool with_path = true) const;
std::string peak_dir () const;
std::string dead_sound_dir () const;
std::string dead_dir () const;
std::string automation_dir () const;
std::string analysis_dir() const;
std::string plugins_dir() const;

View File

@@ -90,18 +90,11 @@ public:
const PBD::sys::path peak_path () const;
/**
* @return The absolute path to the directory that audio
* @return The absolute path to the directory that source
* files are moved to when they are no longer part of the
* session.
*/
const PBD::sys::path dead_sound_path () const;
/**
* @return The absolute path to the directory that midi
* files are moved to when they are no longer part of the
* session.
*/
const PBD::sys::path dead_midi_path () const;
const PBD::sys::path dead_path () const;
/**
* @return The absolute path to the directory that audio

View File

@@ -57,6 +57,7 @@ public:
void unassigned (std::list<boost::shared_ptr<Playlist> > & list);
void destroy_region (boost::shared_ptr<Region>);
boost::shared_ptr<Crossfade> find_crossfade (const PBD::ID &);
void sync_all_regions_with_regions ();
private:
friend class Session;

View File

@@ -9,8 +9,7 @@ const char* const sound_dir_name = X_("audiofiles");
const char* const midi_dir_name = X_("midifiles");
const char* const midi_patch_dir_name = X_("patchfiles");
const char* const peak_dir_name = X_("peaks");
const char* const dead_sound_dir_name = X_("dead_sounds");
const char* const dead_midi_dir_name = X_("dead_midi");
const char* const dead_dir_name = X_("dead");
const char* const interchange_dir_name = X_("interchange");
const char* const export_dir_name = X_("export");
const char* const templates_dir_name = X_("templates");

View File

@@ -537,6 +537,18 @@ FileSource::set_within_session_from_path (const std::string& path)
_within_session = _session.path_is_within_session (path);
}
bool
FileSource::is_stub_path (const std::string& path)
{
return path.find (stub_dir_name) != string::npos;
}
bool
FileSource::is_stub () const
{
return is_stub_path (_path);
}
int
FileSource::unstubify ()
{

View File

@@ -827,12 +827,8 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t posi
regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
all_regions.insert (region);
cerr << "Playlist: region added at " << position << endl;
possibly_splice_unlocked (position, region->length(), region);
cerr << "Playlist: post-splice, region @ " << region->position() << endl;
if (!holding_state ()) {
/* layers get assigned from XML state, and are not reset during undo/redo */
relayer ();
@@ -1309,7 +1305,6 @@ Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float t
*/
copy_of_region->set_layer (copy_of_region->layer() + top_layer);
cerr << "DEBUG: add new region at " << pos << endl;
add_region_internal (copy_of_region, (*i)->position() + pos);
}
pos += shift;
@@ -1703,6 +1698,18 @@ Playlist::drop_regions ()
all_regions.clear ();
}
void
Playlist::sync_all_regions_with_regions ()
{
RegionLock rl (this);
all_regions.clear ();
for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
all_regions.insert (*i);
}
}
void
Playlist::clear (bool with_signals)
{
@@ -2757,6 +2764,20 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
}
bool
Playlist::uses_source (boost::shared_ptr<const Source> src) const
{
RegionLock rlock (const_cast<Playlist*> (this));
for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
if ((*r)->uses_source (src)) {
return true;
}
}
return false;
}
boost::shared_ptr<Region>
Playlist::find_region (const ID& id) const
{

View File

@@ -29,14 +29,15 @@
#include "pbd/enumwriter.h"
#include "ardour/debug.h"
#include "ardour/region.h"
#include "ardour/file_source.h"
#include "ardour/filter.h"
#include "ardour/playlist.h"
#include "ardour/profile.h"
#include "ardour/region.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
#include "ardour/source.h"
#include "ardour/tempo.h"
#include "ardour/region_factory.h"
#include "ardour/filter.h"
#include "ardour/profile.h"
#include "ardour/utils.h"
#include "i18n.h"
@@ -1474,6 +1475,20 @@ Region::uses_source (boost::shared_ptr<const Source> source) const
return false;
}
bool
Region::uses_source_path (const std::string& path) const
{
for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
if (fs) {
if (fs->path() == path) {
return true;
}
}
}
return false;
}
framecnt_t
Region::source_length(uint32_t n) const
{

View File

@@ -549,3 +549,15 @@ RegionFactory::get_regions_using_source (boost::shared_ptr<Source> s, std::set<b
}
}
}
void
RegionFactory::remove_regions_using_source (boost::shared_ptr<Source> src)
{
Glib::Mutex::Lock lm (region_map_lock);
for (RegionMap::iterator i = region_map.begin(); i != region_map.end(); ++i) {
if (i->second->uses_source (src)) {
region_map.erase (i);
}
}
}

View File

@@ -135,15 +135,9 @@ SessionDirectory::peak_path () const
}
const path
SessionDirectory::dead_sound_path () const
SessionDirectory::dead_path () const
{
return m_root_path / dead_sound_dir_name;
}
const path
SessionDirectory::dead_midi_path () const
{
return m_root_path / dead_midi_dir_name;
return m_root_path / dead_dir_name;
}
const path
@@ -157,12 +151,11 @@ SessionDirectory::sub_directories () const
{
vector<path> tmp_paths;
tmp_paths.push_back ( sound_path () );
tmp_paths.push_back ( midi_path () );
tmp_paths.push_back ( peak_path () );
tmp_paths.push_back ( dead_sound_path () );
tmp_paths.push_back ( dead_midi_path () );
tmp_paths.push_back ( export_path () );
tmp_paths.push_back (sound_path ());
tmp_paths.push_back (midi_path ());
tmp_paths.push_back (peak_path ());
tmp_paths.push_back (dead_path ());
tmp_paths.push_back (export_path ());
return tmp_paths;
}

View File

@@ -26,6 +26,7 @@
#include "ardour/region.h"
#include "ardour/playlist_factory.h"
#include "ardour/session.h"
#include "ardour/source.h"
#include "i18n.h"
using namespace std;
@@ -246,18 +247,25 @@ uint32_t
SessionPlaylists::source_use_count (boost::shared_ptr<const Source> src) const
{
uint32_t count = 0;
for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
for (Playlist::RegionList::const_iterator r = (*p)->region_list().begin();
r != (*p)->region_list().end(); ++r) {
if ((*r)->uses_source(src)) {
++count;
break;
}
}
if ((*p)->uses_source (src)) {
++count;
break;
}
}
return count;
}
void
SessionPlaylists::sync_all_regions_with_regions ()
{
Glib::Mutex::Lock lm (lock);
for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
(*p)->sync_all_regions_with_regions ();
}
}
void
SessionPlaylists::update_after_tempo_map_change ()

View File

@@ -56,6 +56,7 @@
#include "midi++/manager.h"
#include "pbd/boost_debug.h"
#include "pbd/basename.h"
#include "pbd/controllable_descriptor.h"
#include "pbd/enumwriter.h"
#include "pbd/error.h"
@@ -146,7 +147,7 @@ Session::first_stage_init (string fullpath, string snapshot_name)
_path = string(buf);
if (_path[_path.length()-1] != '/') {
if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
_path += G_DIR_SEPARATOR;
}
@@ -487,7 +488,7 @@ Session::ensure_subdirs ()
return -1;
}
dir = session_directory().dead_sound_path().to_string();
dir = session_directory().dead_path().to_string();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
@@ -2415,18 +2416,46 @@ Session::commit_reversible_command (Command *cmd)
}
static bool
accept_all_non_peak_files (const string& path, void */*arg*/)
accept_all_non_stub_audio_files (const string& path, void */*arg*/)
{
if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
return false;
}
if (FileSource::is_stub_path (path)) {
return false;
}
if (!AudioFileSource::safe_audio_file_extension (path)) {
return false;
}
return true;
}
static bool
accept_all_non_stub_midi_files (const string& path, void */*arg*/)
{
if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
return false;
}
return (path.length() > 5 && path.find (peakfile_suffix) != (path.length() - 5));
if (FileSource::is_stub_path (path)) {
return false;
}
return ((path.length() > 4 && path.find (".mid") != (path.length() - 4)) ||
(path.length() > 4 && path.find (".smf") != (path.length() - 4)) ||
(path.length() > 5 && path.find (".midi") != (path.length() - 5)));
}
static bool
accept_all_state_files (const string& path, void */*arg*/)
{
if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
return false;
}
return (path.length() > 7 && path.find (".ardour") == (path.length() - 7));
}
@@ -2572,10 +2601,12 @@ Session::cleanup_sources (CleanupReport& rep)
vector<boost::shared_ptr<Source> > dead_sources;
PathScanner scanner;
string sound_path;
string audio_path;
string midi_path;
vector<space_and_path>::iterator i;
vector<space_and_path>::iterator nexti;
vector<string*>* soundfiles;
vector<string*>* candidates;
vector<string*>* candidates2;
vector<string> unused;
set<string> all_sources;
bool used;
@@ -2584,14 +2615,19 @@ Session::cleanup_sources (CleanupReport& rep)
_state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
/* step 1: consider deleting all unused playlists */
/* consider deleting all unused playlists */
if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
ret = 0;
goto out;
}
/* step 2: find all un-used sources */
/* sync the "all regions" property of each playlist with its current state
*/
playlists->sync_all_regions_with_regions ();
/* find all un-used sources */
rep.paths.clear ();
rep.space = 0;
@@ -2615,7 +2651,7 @@ Session::cleanup_sources (CleanupReport& rep)
i = tmp;
}
/* build a list of all the possible sound directories for the session */
/* build a list of all the possible audio directories for the session */
for (i = session_dirs.begin(); i != session_dirs.end(); ) {
@@ -2623,25 +2659,49 @@ Session::cleanup_sources (CleanupReport& rep)
++nexti;
SessionDirectory sdir ((*i).path);
sound_path += sdir.sound_path().to_string();
audio_path += sdir.sound_path().to_string();
if (nexti != session_dirs.end()) {
sound_path += ':';
audio_path += ':';
}
i = nexti;
}
/* now do the same thing for the files that ended up in the sounds dir(s)
but are not referenced as sources in any snapshot.
*/
soundfiles = scanner (sound_path, accept_all_non_peak_files, (void *) 0, false, true);
/* build a list of all the possible midi directories for the session */
if (soundfiles == 0) {
return 0;
for (i = session_dirs.begin(); i != session_dirs.end(); ) {
nexti = i;
++nexti;
SessionDirectory sdir ((*i).path);
midi_path += sdir.midi_path().to_string();
if (nexti != session_dirs.end()) {
midi_path += ':';
}
i = nexti;
}
candidates = scanner (audio_path, accept_all_non_stub_audio_files, (void *) 0, true, true);
candidates2 = scanner (midi_path, accept_all_non_stub_midi_files, (void *) 0, true, true);
/* merge them */
if (candidates) {
if (candidates2) {
for (vector<string*>::iterator i = candidates2->begin(); i != candidates2->end(); ++i) {
candidates->push_back (*i);
}
delete candidates2;
}
} else {
candidates = candidates2; // might still be null
}
/* find all sources, but don't use this snapshot because the
state file on disk still references sources we may have already
dropped.
@@ -2652,61 +2712,82 @@ Session::cleanup_sources (CleanupReport& rep)
/* add our current source list
*/
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
boost::shared_ptr<FileSource> fs;
SourceMap::iterator tmp = i;
++tmp;
if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
all_sources.insert (fs->path());
if (!fs->is_stub()) {
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.
*/
cerr << "Source " << i->second->name() << "ID " << i->second->id() << " not used, remove from source list and also all regions\n";
RegionFactory::remove_regions_using_source (i->second);
sources.erase (i);
}
}
}
i = tmp;
}
char tmppath1[PATH_MAX+1];
char tmppath2[PATH_MAX+1];
for (vector<string*>::iterator x = soundfiles->begin(); x != soundfiles->end(); ++x) {
if (candidates) {
for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
used = false;
spath = **x;
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;
}
if (realpath((*i).c_str(), tmppath2) == 0) {
error << string_compose (_("Cannot expand path %1 (%2)"),
(*i), strerror (errno)) << endmsg;
continue;
}
used = false;
spath = **x;
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;
if (strcmp(tmppath1, tmppath2) == 0) {
used = true;
break;
}
}
if (!used) {
unused.push_back (spath);
}
if (realpath((*i).c_str(), tmppath2) == 0) {
error << string_compose (_("Cannot expand path %1 (%2)"),
(*i), strerror (errno)) << endmsg;
continue;
}
delete *x;
}
if (strcmp(tmppath1, tmppath2) == 0) {
used = true;
break;
}
}
delete candidates;
}
if (!used) {
unused.push_back (spath);
}
}
/* now try to move all unused files into the "dead_sounds" directory(ies) */
/* now try to move all unused files into the "dead" directory(ies) */
for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
struct stat statbuf;
rep.paths.push_back (*x);
if (stat ((*x).c_str(), &statbuf) == 0) {
rep.space += statbuf.st_size;
}
string newpath;
/* don't move the file across filesystems, just
stick it in the `dead_sound_dir_name' directory
stick it in the `dead_dir_name' directory
on whichever filesystem it was already on.
*/
@@ -2721,16 +2802,16 @@ Session::cleanup_sources (CleanupReport& rep)
/* new school, go up 4 levels */
newpath = Glib::path_get_dirname (*x); // "audiofiles"
newpath = Glib::path_get_dirname (*x); // "audiofiles" or "midifiles"
newpath = Glib::path_get_dirname (newpath); // "session-name"
newpath = Glib::path_get_dirname (newpath); // "interchange"
newpath = Glib::path_get_dirname (newpath); // "session-dir"
}
newpath = Glib::build_filename (newpath, dead_sound_dir_name);
newpath = Glib::build_filename (newpath, dead_dir_name);
if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
error << string_compose(_("Session: cannot create dead file folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
return -1;
}
@@ -2747,7 +2828,7 @@ Session::cleanup_sources (CleanupReport& rep)
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
newpath_v = buf;
while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
while (Glib::file_test (newpath_v.c_str(), Glib::FILE_TEST_EXISTS) && version < 999) {
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
newpath_v = buf;
}
@@ -2766,8 +2847,10 @@ Session::cleanup_sources (CleanupReport& rep)
}
stat ((*x).c_str(), &statbuf);
if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"),
(*x), newpath, strerror (errno))
<< endmsg;
goto out;
@@ -2776,22 +2859,27 @@ Session::cleanup_sources (CleanupReport& rep)
/* see if there an easy to find peakfile for this file, and remove it.
*/
string peakpath = (*x).substr (0, (*x).find_last_of ('.'));
peakpath += peakfile_suffix;
if (access (peakpath.c_str(), W_OK) == 0) {
string base = basename_nosuffix (*x);
base += "%A"; /* this is what we add for the channel suffix of all native files,
or for the first channel of embedded files. it will miss
some peakfiles for other channels
*/
string peakpath = peak_path (base);
if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
if (::unlink (peakpath.c_str()) != 0) {
error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
peakpath, _path, strerror (errno))
peakpath, _path, strerror (errno))
<< endmsg;
/* try to back out */
rename (newpath.c_str(), _path.c_str());
goto out;
}
}
}
ret = 0;
rep.paths.push_back (*x);
rep.space += statbuf.st_size;
}
/* dump the history list */
@@ -2802,6 +2890,7 @@ Session::cleanup_sources (CleanupReport& rep)
*/
save_state ("");
ret = 0;
out:
_state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup);
@@ -2815,17 +2904,17 @@ Session::cleanup_trash_sources (CleanupReport& rep)
// FIXME: needs adaptation for MIDI
vector<space_and_path>::iterator i;
string dead_sound_dir;
string dead_dir;
rep.paths.clear ();
rep.space = 0;
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
dead_sound_dir = (*i).path;
dead_sound_dir += dead_sound_dir_name;
dead_dir = (*i).path;
dead_dir += dead_dir_name;
clear_directory (dead_sound_dir, &rep.space, &rep.paths);
clear_directory (dead_dir, &rep.space, &rep.paths);
}
return 0;