newly created files for use in recording appear in a .stubs folder, and are moved out of it when recording stops

git-svn-id: svn://localhost/ardour2/branches/3.0@7426 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-07-16 14:55:11 +00:00
parent e9ab577177
commit c8e3f32533
31 changed files with 345 additions and 194 deletions

View file

@ -33,11 +33,12 @@
#include "pbd/memento_command.h" #include "pbd/memento_command.h"
#include "pbd/stateful_diff_command.h" #include "pbd/stateful_diff_command.h"
#include <gtkmm2ext/gtk_ui.h> #include "gtkmm2ext/gtk_ui.h"
#include <gtkmm2ext/selector.h> #include "gtkmm2ext/selector.h"
#include <gtkmm2ext/bindable_button.h> #include "gtkmm2ext/bindable_button.h"
#include <gtkmm2ext/utils.h> #include "gtkmm2ext/utils.h"
#include "ardour/file_source.h"
#include "ardour/midi_playlist.h" #include "ardour/midi_playlist.h"
#include "ardour/midi_diskstream.h" #include "ardour/midi_diskstream.h"
#include "ardour/midi_patch_manager.h" #include "ardour/midi_patch_manager.h"
@ -961,7 +962,6 @@ MidiTimeAxisView::add_region (nframes64_t pos)
boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(), boost::shared_ptr<Source> src = _session->create_midi_source_for_session (view()->trackview().track().get(),
view()->trackview().track()->name()); view()->trackview().track()->name());
PropertyList plist; PropertyList plist;
plist.add (ARDOUR::Properties::start, 0); plist.add (ARDOUR::Properties::start, 0);
@ -969,7 +969,7 @@ MidiTimeAxisView::add_region (nframes64_t pos)
plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name())); plist.add (ARDOUR::Properties::name, PBD::basename_nosuffix(src->name()));
boost::shared_ptr<Region> region = (RegionFactory::create (src, plist)); boost::shared_ptr<Region> region = (RegionFactory::create (src, plist));
playlist()->add_region (region, start); playlist()->add_region (region, start);
_session->add_command (new StatefulDiffCommand (playlist())); _session->add_command (new StatefulDiffCommand (playlist()));

View file

@ -34,6 +34,8 @@ class CoreAudioSource : public AudioFileSource {
CoreAudioSource (ARDOUR::Session&, const string& path, int chn, Flag); CoreAudioSource (ARDOUR::Session&, const string& path, int chn, Flag);
~CoreAudioSource (); ~CoreAudioSource ();
void set_path (const std::string& p);
float sample_rate() const; float sample_rate() const;
int update_header (sframes_t when, struct tm&, time_t); int update_header (sframes_t when, struct tm&, time_t);

View file

@ -20,6 +20,7 @@ extern const char* const templates_dir_name;
extern const char* const route_templates_dir_name; extern const char* const route_templates_dir_name;
extern const char* const surfaces_dir_name; extern const char* const surfaces_dir_name;
extern const char* const user_config_dir_name; extern const char* const user_config_dir_name;
extern const char* const stub_dir_name;
}; };

View file

@ -35,6 +35,9 @@ public:
class FileSource : virtual public Source { class FileSource : virtual public Source {
public: public:
const Glib::ustring& path() const { return _path; } const Glib::ustring& path() const { return _path; }
int unstubify ();
void stubify ();
virtual bool safe_file_extension (const Glib::ustring& path) const = 0; virtual bool safe_file_extension (const Glib::ustring& path) const = 0;
@ -57,6 +60,8 @@ public:
bool must_exist, bool& is_new, uint16_t& chan, bool must_exist, bool& is_new, uint16_t& chan,
Glib::ustring& found_path); Glib::ustring& found_path);
void inc_use_count ();
protected: protected:
FileSource (Session& session, DataType type, FileSource (Session& session, DataType type,
const Glib::ustring& path, const Glib::ustring& path,
@ -66,6 +71,7 @@ protected:
virtual int init (const Glib::ustring& idstr, bool must_exist); virtual int init (const Glib::ustring& idstr, bool must_exist);
virtual void set_path (const std::string&);
virtual int move_dependents_to_trash() { return 0; } virtual int move_dependents_to_trash() { return 0; }
void set_within_session_from_path (const std::string&); void set_within_session_from_path (const std::string&);

View file

@ -185,7 +185,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
std::string peak_path_from_audio_path (std::string) const; std::string peak_path_from_audio_path (std::string) const;
std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive); std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive);
std::string new_midi_source_name (const std::string&); std::string new_midi_source_name (const std::string&);
std::string new_source_path_from_name (DataType type, const std::string&); std::string new_source_path_from_name (DataType type, const std::string&, bool as_stub = false);
RouteList new_route_from_template (uint32_t how_many, const std::string& template_path); RouteList new_route_from_template (uint32_t how_many, const std::string& template_path);
void process (nframes_t nframes); void process (nframes_t nframes);
@ -533,9 +533,10 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
*/ */
static PBD::Signal0<int> AskAboutPendingState; static PBD::Signal0<int> AskAboutPendingState;
boost::shared_ptr<AudioFileSource> create_audio_source_for_session (size_t, std::string const &, uint32_t, bool); boost::shared_ptr<AudioFileSource> create_audio_source_for_session (size_t, std::string const &, uint32_t,
bool destructive, bool as_stub = false);
boost::shared_ptr<MidiSource> create_midi_source_for_session (Track*, std::string const &);
boost::shared_ptr<MidiSource> create_midi_source_for_session (Track*, std::string const &, bool as_stub = false);
boost::shared_ptr<Source> source_by_id (const PBD::ID&); boost::shared_ptr<Source> source_by_id (const PBD::ID&);
boost::shared_ptr<Source> source_by_path_and_channel (const Glib::ustring&, uint16_t); boost::shared_ptr<Source> source_by_path_and_channel (const Glib::ustring&, uint16_t);
@ -1415,6 +1416,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
void add_session_range_location (nframes_t, nframes_t); void add_session_range_location (nframes_t, nframes_t);
void setup_midi_machine_control (); void setup_midi_machine_control ();
void cleanup_stubfiles ();
}; };
} // namespace ARDOUR } // namespace ARDOUR

View file

@ -51,6 +51,17 @@ public:
*/ */
const PBD::sys::path sound_path () const; const PBD::sys::path sound_path () const;
/**
* @return the absolute path to the directory in which
* the session stores STUB audio files.
*
* If the session is an older session with an existing
* "sounds" directory then it will return a path to that
* directory otherwise it will return the new location
* of root_path()/interchange/session_name/audiofiles/.stubs
*/
const PBD::sys::path sound_stub_path () const;
/** /**
* @return the absolute path to the directory in which * @return the absolute path to the directory in which
* the session stores MIDI files, ie * the session stores MIDI files, ie
@ -58,6 +69,13 @@ public:
*/ */
const PBD::sys::path midi_path () const; const PBD::sys::path midi_path () const;
/**
* @return the absolute path to the directory in which
* the session stores STUB MIDI files, ie
* root_path()/interchange/session_name/midifiles/.stubs
*/
const PBD::sys::path midi_stub_path () const;
/** /**
* @return the absolute path to the directory in which * @return the absolute path to the directory in which
* the session stores MIDNAM patch files, ie * the session stores MIDNAM patch files, ie

View file

@ -69,7 +69,10 @@ public:
static bool safe_midi_file_extension (const Glib::ustring& path); static bool safe_midi_file_extension (const Glib::ustring& path);
private: protected:
void set_path (const std::string& newpath);
private:
nframes_t read_unlocked (Evoral::EventSink<nframes_t>& dst, nframes_t read_unlocked (Evoral::EventSink<nframes_t>& dst,
sframes_t position, sframes_t position,
sframes_t start, sframes_t start,

View file

@ -66,6 +66,7 @@ class SndFileSource : public AudioFileSource {
static int get_soundfile_info (const Glib::ustring& path, SoundFileInfo& _info, std::string& error_msg); static int get_soundfile_info (const Glib::ustring& path, SoundFileInfo& _info, std::string& error_msg);
protected: protected:
void set_path (const std::string& p);
void set_header_timeline_position (); void set_header_timeline_position ();
framecnt_t read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const; framecnt_t read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const;

View file

@ -105,8 +105,8 @@ class Source : public SessionObject
Glib::Mutex& mutex() { return _lock; } Glib::Mutex& mutex() { return _lock; }
Flag flags() const { return _flags; } Flag flags() const { return _flags; }
void inc_use_count () { g_atomic_int_inc (&_use_count); } virtual void inc_use_count ();
void dec_use_count (); virtual void dec_use_count ();
int use_count() const { return g_atomic_int_get (&_use_count); } int use_count() const { return g_atomic_int_get (&_use_count); }
bool used() const { return use_count() > 0; } bool used() const { return use_count() > 0; }

View file

@ -27,6 +27,7 @@
#include <istream> #include <istream>
#include <vector> #include <vector>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <sys/types.h>
#include <inttypes.h> #include <inttypes.h>
#include <jack/types.h> #include <jack/types.h>
@ -440,7 +441,7 @@ namespace ARDOUR {
struct CleanupReport { struct CleanupReport {
std::vector<std::string> paths; std::vector<std::string> paths;
int64_t space; size_t space;
}; };
enum PositionLockStyle { enum PositionLockStyle {

View file

@ -1388,6 +1388,7 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
(*chan)->write_source->mark_for_remove (); (*chan)->write_source->mark_for_remove ();
(*chan)->write_source->drop_references (); (*chan)->write_source->drop_references ();
_session.remove_source ((*chan)->write_source);
(*chan)->write_source.reset (); (*chan)->write_source.reset ();
} }
@ -1409,9 +1410,13 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
if (s) { if (s) {
srcs.push_back (s); srcs.push_back (s);
if (s->unstubify ()) {
error << string_compose (_("Could not move capture file from %1"), s->path()) << endmsg;
}
s->update_header (capture_info.front()->start, when, twhen); s->update_header (capture_info.front()->start, when, twhen);
s->set_captured_for (_name.val()); s->set_captured_for (_name.val());
s->mark_immutable (); s->mark_immutable ();
if (Config->get_auto_analyse_audio()) { if (Config->get_auto_analyse_audio()) {
Analyser::queue_source_for_analysis (s, true); Analyser::queue_source_for_analysis (s, true);
} }
@ -1880,7 +1885,13 @@ AudioDiskstream::use_new_write_source (uint32_t n)
} }
try { try {
if ((chan->write_source = _session.create_audio_source_for_session (n_channels().n_audio(), name(), n, destructive())) == 0) { /* file starts off as a stub file, it will be converted
when we're done with a capture pass.
*/
if ((chan->write_source = _session.create_audio_source_for_session (n_channels().n_audio(),
name(), n, destructive(),
true)) == 0) {
throw failed_constructor(); throw failed_constructor();
} }
} }
@ -1895,10 +1906,6 @@ AudioDiskstream::use_new_write_source (uint32_t n)
chan->write_source->set_allow_remove_if_empty (!destructive()); chan->write_source->set_allow_remove_if_empty (!destructive());
/* until we write, this file is considered removable */
chan->write_source->mark_for_remove ();
return 0; return 0;
} }

View file

@ -368,3 +368,9 @@ CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string&
return ret; return ret;
} }
void
CoreAudioSource::set_path (const string& p)
{
FileSource::set_path (p);
}

View file

@ -17,5 +17,6 @@ const char* const templates_dir_name = X_("templates");
const char* const route_templates_dir_name = X_("route_templates"); const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces"); const char* const surfaces_dir_name = X_("surfaces");
const char* const user_config_dir_name = X_("ardour3"); const char* const user_config_dir_name = X_("ardour3");
const char* const stub_dir_name = X_(".stubs");
} }

View file

@ -39,6 +39,7 @@
#include <glibmm/thread.h> #include <glibmm/thread.h>
#include "ardour/file_source.h" #include "ardour/file_source.h"
#include "ardour/directory_names.h"
#include "ardour/session.h" #include "ardour/session.h"
#include "ardour/session_directory.h" #include "ardour/session_directory.h"
#include "ardour/source_factory.h" #include "ardour/source_factory.h"
@ -51,8 +52,6 @@ using namespace ARDOUR;
using namespace PBD; using namespace PBD;
using namespace Glib; using namespace Glib;
static const std::string PATH_SEP = "/"; // I don't do windows
map<DataType, ustring> FileSource::search_paths; map<DataType, ustring> FileSource::search_paths;
FileSource::FileSource (Session& session, DataType type, const ustring& path, Source::Flag flag) FileSource::FileSource (Session& session, DataType type, const ustring& path, Source::Flag flag)
@ -80,9 +79,14 @@ FileSource::FileSource (Session& session, const XMLNode& node, bool /*must_exist
bool bool
FileSource::removable () const FileSource::removable () const
{ {
return (_flags & Removable) bool r = (_path.find (stub_dir_name) != string::npos) ||
&& ((_flags & RemoveAtDestroy) || ((_flags & Removable)
((_flags & RemovableIfEmpty) && empty() == 0)); && ((_flags & RemoveAtDestroy) ||
((_flags & RemovableIfEmpty) && empty() == 0)));
cerr << "is " << _path << " removable ? " << r << endl;
return r;
} }
int int
@ -140,15 +144,16 @@ FileSource::move_to_trash (const ustring& trash_dir_name)
trash_dir_name directory on whichever filesystem it was already on trash_dir_name directory on whichever filesystem it was already on
*/ */
ustring newpath; vector<string> v;
newpath = Glib::path_get_dirname (_path); v.push_back (Glib::path_get_dirname (Glib::path_get_dirname (_path)));
newpath = Glib::path_get_dirname (newpath); v.push_back (trash_dir_name);
v.push_back (Glib::path_get_basename (_path));
newpath += string(PATH_SEP) + trash_dir_name + PATH_SEP; string newpath = Glib::build_filename (v);
newpath += Glib::path_get_basename (_path);
/* the new path already exists, try versioning */ /* the new path already exists, try versioning */
if (access (newpath.c_str(), F_OK) == 0) {
if (Glib::file_test (newpath.c_str(), Glib::FILE_TEST_EXISTS)) {
char buf[PATH_MAX+1]; char buf[PATH_MAX+1];
int version = 1; int version = 1;
ustring newpath_v; ustring newpath_v;
@ -391,7 +396,7 @@ FileSource::set_source_name (const ustring& newname, bool destructive)
return -1; return -1;
} }
if (rename (oldpath.c_str(), newpath.c_str()) != 0) { if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
error << string_compose (_("cannot rename audio file %1 to %2"), _name, newpath) << endmsg; error << string_compose (_("cannot rename audio file %1 to %2"), _name, newpath) << endmsg;
return -1; return -1;
} }
@ -428,3 +433,42 @@ FileSource::set_within_session_from_path (const std::string& path)
{ {
_within_session = _session.path_is_within_session (path); _within_session = _session.path_is_within_session (path);
} }
int
FileSource::unstubify ()
{
string::size_type pos = _path.find (stub_dir_name);
if (pos == string::npos || (_flags & Destructive)) {
return 0;
}
vector<string> v;
v.push_back (Glib::path_get_dirname (Glib::path_get_dirname (_path)));
v.push_back (Glib::path_get_basename(_path));
string newpath = Glib::build_filename (v);
if (::rename (_path.c_str(), newpath.c_str()) != 0) {
error << string_compose (_("rename from %1 to %2 failed: %3)"), _path, newpath, strerror (errno)) << endmsg;
return -1;
}
set_path (newpath);
return 0;
}
void
FileSource::set_path (const std::string& newpath)
{
_path = newpath;
}
void
FileSource::inc_use_count ()
{
Source::inc_use_count ();
}

View file

@ -957,14 +957,18 @@ MidiDiskstream::transport_stopped_wallclock (struct tm& /*when*/, time_t /*twhen
_write_source->mark_streaming_write_completed (); _write_source->mark_streaming_write_completed ();
/* make it not a stub anymore */
_write_source->unstubify ();
/* we will want to be able to keep (over)writing the source /* we will want to be able to keep (over)writing the source
but we don't want it to be removable. this also differs but we don't want it to be removable. this also differs
from the audio situation, where the source at this point from the audio situation, where the source at this point
must be considered immutable must be considered immutable. luckily, we can rely on
MidiSource::mark_streaming_write_completed() to have
already done the necessary work for that.
*/ */
_write_source->mark_nonremovable ();
string whole_file_region_name; string whole_file_region_name;
whole_file_region_name = region_name_from_path (_write_source->name(), true); whole_file_region_name = region_name_from_path (_write_source->name(), true);
@ -1321,7 +1325,14 @@ MidiDiskstream::use_new_write_source (uint32_t n)
_write_source.reset(); _write_source.reset();
try { try {
_write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (0, name ())); /* file starts off as a stub file, it will be converted
when we're done with a capture pass, or when "stolen"
by the GUI.
*/
_write_source = boost::dynamic_pointer_cast<SMFSource>(
_session.create_midi_source_for_session (0, name (), true));
if (!_write_source) { if (!_write_source) {
throw failed_constructor(); throw failed_constructor();
} }
@ -1342,8 +1353,26 @@ list<boost::shared_ptr<Source> >
MidiDiskstream::steal_write_sources() MidiDiskstream::steal_write_sources()
{ {
list<boost::shared_ptr<Source> > ret; list<boost::shared_ptr<Source> > ret;
/* put some data on the disk, even if its just a header for an empty file.
XXX should we not have a more direct method for doing this? Maybe not
since we don't want to mess around with the model/disk relationship
that the Source has to pay attention to.
*/
boost::dynamic_pointer_cast<MidiSource>(_write_source)->session_saved ();
/* make it visible/present */
_write_source->unstubify ();
/* never let it go away */
_write_source->mark_nonremovable ();
ret.push_back (_write_source); ret.push_back (_write_source);
/* get a new one */
use_new_write_source (0); use_new_write_source (0);
return ret; return ret;
} }

View file

@ -321,19 +321,11 @@ MidiSource::session_saved()
XXX do we need to do this every time? XXX do we need to do this every time?
*/ */
flush_midi();
if (_model && _model->edited()) { if (_model && _model->edited()) {
#if 0 // old style: clone the source if necessary on every session save
// and switch to the new source
boost::shared_ptr<MidiSource> newsrc = clone ();
if (newsrc) {
_model->set_midi_source (newsrc);
Switched (newsrc); /* EMIT SIGNAL */ // if the model is edited, write its contents into
}
#else
// new style: if the model is edited, write its contents into
// the current source file (overwiting previous contents. // the current source file (overwiting previous contents.
/* temporarily drop our reference to the model so that /* temporarily drop our reference to the model so that
@ -343,13 +335,19 @@ MidiSource::session_saved()
boost::shared_ptr<MidiModel> mm = _model ; boost::shared_ptr<MidiModel> mm = _model ;
_model.reset (); _model.reset ();
mm->sync_to_source ();
_model = mm;
/* data is in the file now, its not removable */
#endif
}
cerr << name() << " @ " << this << " length at save = " << _length_beats << endl; /* flush model contents to disk
*/
mm->sync_to_source ();
/* reacquire model */
_model = mm;
} else {
flush_midi();
}
} }
void void

View file

@ -40,10 +40,11 @@ PlaylistFactory::create (Session& s, const XMLNode& node, bool hidden, bool unus
boost::shared_ptr<Playlist> pl; boost::shared_ptr<Playlist> pl;
if ( !type || type->value() == "audio" ) if (!type || type->value() == "audio") {
pl = boost::shared_ptr<Playlist> (new AudioPlaylist (s, node, hidden)); pl = boost::shared_ptr<Playlist> (new AudioPlaylist (s, node, hidden));
else if ( type->value() == "midi" ) } else if (type->value() == "midi") {
pl = boost::shared_ptr<Playlist> (new MidiPlaylist (s, node, hidden)); pl = boost::shared_ptr<Playlist> (new MidiPlaylist (s, node, hidden));
}
pl->set_region_ownership (); pl->set_region_ownership ();

View file

@ -1388,7 +1388,6 @@ void
Region::set_master_sources (const SourceList& srcs) Region::set_master_sources (const SourceList& srcs)
{ {
for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) { for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
cerr << name() << " " << id() << " DEC M SMS\n";
(*i)->dec_use_count (); (*i)->dec_use_count ();
} }

View file

@ -226,6 +226,10 @@ Session::destroy ()
delete state_tree; delete state_tree;
/* remove all stubfiles that might still be lurking */
cleanup_stubfiles ();
/* reset dynamic state version back to default */ /* reset dynamic state version back to default */
Stateful::loading_state_version = 0; Stateful::loading_state_version = 0;
@ -2606,6 +2610,7 @@ Session::remove_source (boost::weak_ptr<Source> src)
Glib::Mutex::Lock lm (source_lock); Glib::Mutex::Lock lm (source_lock);
if ((i = sources.find (source->id())) != sources.end()) { if ((i = sources.find (source->id())) != sources.end()) {
cerr << "Removing source " << source->name() << endl;
sources.erase (i); sources.erase (i);
} }
} }
@ -2640,7 +2645,6 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
Glib::Mutex::Lock lm (source_lock); Glib::Mutex::Lock lm (source_lock);
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
cerr << "comparing " << path << " with " << i->second->name() << endl;
boost::shared_ptr<AudioFileSource> afs boost::shared_ptr<AudioFileSource> afs
= boost::dynamic_pointer_cast<AudioFileSource>(i->second); = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
@ -2670,18 +2674,12 @@ Session::change_source_path_by_name (string path, string oldname, string newname
the task here is to replace NAME with the new name. the task here is to replace NAME with the new name.
*/ */
/* find last slash */
string dir; string dir;
string prefix; string prefix;
string::size_type slash;
string::size_type dash; string::size_type dash;
if ((slash = path.find_last_of ('/')) == string::npos) { dir = Glib::path_get_dirname (path);
return ""; path = Glib::path_get_basename (path);
}
dir = path.substr (0, slash+1);
/* '-' is not a legal character for the NAME part of the path */ /* '-' is not a legal character for the NAME part of the path */
@ -2689,7 +2687,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname
return ""; return "";
} }
prefix = path.substr (slash+1, dash-(slash+1)); prefix = path.substr (0, dash);
path = dir; path = dir;
path += prefix; path += prefix;
@ -2708,17 +2706,11 @@ Session::change_source_path_by_name (string path, string oldname, string newname
string dir; string dir;
string suffix; string suffix;
string::size_type slash;
string::size_type dash; string::size_type dash;
string::size_type postfix; string::size_type postfix;
/* find last slash */ dir = Glib::path_get_dirname (path);
path = Glib::path_get_basename (path);
if ((slash = path.find_last_of ('/')) == string::npos) {
return "";
}
dir = path.substr (0, slash+1);
/* '-' is not a legal character for the NAME part of the path */ /* '-' is not a legal character for the NAME part of the path */
@ -2750,7 +2742,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname
snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str()); snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
if (access (buf, F_OK) != 0) { if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) {
path = buf; path = buf;
break; break;
} }
@ -2771,7 +2763,7 @@ Session::change_source_path_by_name (string path, string oldname, string newname
* (e.g. as returned by new_*_source_name) * (e.g. as returned by new_*_source_name)
*/ */
string string
Session::new_source_path_from_name (DataType type, const string& name) Session::new_source_path_from_name (DataType type, const string& name, bool as_stub)
{ {
assert(name.find("/") == string::npos); assert(name.find("/") == string::npos);
@ -2779,9 +2771,9 @@ Session::new_source_path_from_name (DataType type, const string& name)
sys::path p; sys::path p;
if (type == DataType::AUDIO) { if (type == DataType::AUDIO) {
p = sdir.sound_path(); p = (as_stub ? sdir.sound_stub_path() : sdir.sound_path());
} else if (type == DataType::MIDI) { } else if (type == DataType::MIDI) {
p = sdir.midi_path(); p = (as_stub ? sdir.midi_stub_path() : sdir.midi_path());
} else { } else {
error << "Unknown source type, unable to create file path" << endmsg; error << "Unknown source type, unable to create file path" << endmsg;
return ""; return "";
@ -2889,10 +2881,10 @@ Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t cha
/** Create a new within-session audio source */ /** Create a new within-session audio source */
boost::shared_ptr<AudioFileSource> boost::shared_ptr<AudioFileSource>
Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive) Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive, bool as_stub)
{ {
const string name = new_audio_source_name (n, n_chans, chan, destructive); const string name = new_audio_source_name (n, n_chans, chan, destructive);
const string path = new_source_path_from_name(DataType::AUDIO, name); const string path = new_source_path_from_name(DataType::AUDIO, name, as_stub);
return boost::dynamic_pointer_cast<AudioFileSource> ( return boost::dynamic_pointer_cast<AudioFileSource> (
SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate())); SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
@ -2949,7 +2941,7 @@ Session::new_midi_source_name (const string& base)
/** Create a new within-session MIDI source */ /** Create a new within-session MIDI source */
boost::shared_ptr<MidiSource> boost::shared_ptr<MidiSource>
Session::create_midi_source_for_session (Track* track, string const & n) Session::create_midi_source_for_session (Track* track, string const & n, bool as_stub)
{ {
/* try to use the existing write source for the track, to keep numbering sane /* try to use the existing write source for the track, to keep numbering sane
*/ */
@ -2968,7 +2960,7 @@ Session::create_midi_source_for_session (Track* track, string const & n)
} }
const string name = new_midi_source_name (n); const string name = new_midi_source_name (n);
const string path = new_source_path_from_name (DataType::MIDI, name); const string path = new_source_path_from_name (DataType::MIDI, name, as_stub);
return boost::dynamic_pointer_cast<SMFSource> ( return boost::dynamic_pointer_cast<SMFSource> (
SourceFactory::createWritable ( SourceFactory::createWritable (
@ -3067,42 +3059,6 @@ Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::
return a->order_key(N_("signal")) < b->order_key(N_("signal")); return a->order_key(N_("signal")) < b->order_key(N_("signal"));
} }
void
Session::remove_empty_sounds ()
{
vector<string> audio_filenames;
get_files_in_directory (_session_dir->sound_path(), audio_filenames);
Glib::Mutex::Lock lm (source_lock);
TapeFileMatcher tape_file_matcher;
remove_if (audio_filenames.begin(), audio_filenames.end(),
boost::bind (&TapeFileMatcher::matches, &tape_file_matcher, _1));
for (vector<string>::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) {
sys::path audio_file_path (_session_dir->sound_path());
audio_file_path /= *i;
if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) {
try
{
sys::remove (audio_file_path);
const string peakfile = peak_path (audio_file_path.to_string());
sys::remove (peakfile);
}
catch (const sys::filesystem_error& err)
{
error << err.what() << endmsg;
}
}
}
}
bool bool
Session::is_auditioning () const Session::is_auditioning () const
{ {

View file

@ -101,12 +101,27 @@ SessionDirectory::sound_path () const
return sources_root() / sound_dir_name; return sources_root() / sound_dir_name;
} }
const path
SessionDirectory::sound_stub_path () const
{
if(is_directory (old_sound_path ())) return old_sound_path();
// the new style sound directory
return sources_root() / sound_dir_name / stub_dir_name;
}
const path const path
SessionDirectory::midi_path () const SessionDirectory::midi_path () const
{ {
return sources_root() / midi_dir_name; return sources_root() / midi_dir_name;
} }
const path
SessionDirectory::midi_stub_path () const
{
return sources_root() / midi_dir_name / stub_dir_name;
}
const path const path
SessionDirectory::midi_patch_path () const SessionDirectory::midi_patch_path () const
{ {

View file

@ -41,7 +41,6 @@
#include <signal.h> #include <signal.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/time.h> #include <sys/time.h>
#include <dirent.h>
#ifdef HAVE_SYS_VFS_H #ifdef HAVE_SYS_VFS_H
#include <sys/vfs.h> #include <sys/vfs.h>
@ -66,6 +65,7 @@
#include "pbd/search_path.h" #include "pbd/search_path.h"
#include "pbd/stacktrace.h" #include "pbd/stacktrace.h"
#include "pbd/convert.h" #include "pbd/convert.h"
#include "pbd/clear_dir.h"
#include "ardour/amp.h" #include "ardour/amp.h"
#include "ardour/audio_diskstream.h" #include "ardour/audio_diskstream.h"
@ -288,7 +288,7 @@ Session::second_stage_init ()
if (load_state (_current_snapshot_name)) { if (load_state (_current_snapshot_name)) {
return -1; return -1;
} }
remove_empty_sounds (); cleanup_stubfiles ();
} }
if (_butler->start_thread()) { if (_butler->start_thread()) {
@ -460,6 +460,13 @@ Session::ensure_subdirs ()
return -1; return -1;
} }
dir = session_directory().sound_stub_path().to_string();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session stub sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
dir = session_directory().midi_path().to_string(); dir = session_directory().midi_path().to_string();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
@ -467,6 +474,13 @@ Session::ensure_subdirs ()
return -1; return -1;
} }
dir = session_directory().midi_stub_path().to_string();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session stub midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
dir = session_directory().dead_sound_path().to_string(); dir = session_directory().dead_sound_path().to_string();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
@ -1595,15 +1609,11 @@ Session::XMLRegionFactory (const XMLNode& node, bool full)
try { try {
if ( !type || type->value() == "audio" ) { if (!type || type->value() == "audio") {
return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full)); } else if (type->value() == "midi") {
return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
} else if (type->value() == "midi") { }
return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
}
} catch (failed_constructor& err) { } catch (failed_constructor& err) {
return boost::shared_ptr<Region> (); return boost::shared_ptr<Region> ();
@ -1738,24 +1748,16 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
boost::shared_ptr<Source> source; boost::shared_ptr<Source> source;
boost::shared_ptr<MidiSource> ms; boost::shared_ptr<MidiSource> ms;
SourceList sources; SourceList sources;
uint32_t nchans = 1;
if (node.name() != X_("Region")) { if (node.name() != X_("Region")) {
return boost::shared_ptr<MidiRegion>(); return boost::shared_ptr<MidiRegion>();
} }
if ((prop = node.property (X_("channels"))) != 0) {
nchans = atoi (prop->value().c_str());
}
if ((prop = node.property ("name")) == 0) { if ((prop = node.property ("name")) == 0) {
cerr << "no name for this region\n"; cerr << "no name for this region\n";
abort (); abort ();
} }
// Multiple midi channels? that's just crazy talk
assert(nchans == 1);
if ((prop = node.property (X_("source-0"))) == 0) { if ((prop = node.property (X_("source-0"))) == 0) {
if ((prop = node.property ("source")) == 0) { if ((prop = node.property ("source")) == 0) {
error << _("Session: XMLNode describing a MidiRegion is incomplete (no source)") << endmsg; error << _("Session: XMLNode describing a MidiRegion is incomplete (no source)") << endmsg;
@ -2339,6 +2341,10 @@ Session::commit_reversible_command(Command *cmd)
static bool static bool
accept_all_non_peak_files (const string& path, void */*arg*/) accept_all_non_peak_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)); return (path.length() > 5 && path.find (peakfile_suffix) != (path.length() - 5));
} }
@ -2710,9 +2716,6 @@ Session::cleanup_trash_sources (CleanupReport& rep)
vector<space_and_path>::iterator i; vector<space_and_path>::iterator i;
string dead_sound_dir; string dead_sound_dir;
struct dirent* dentry;
struct stat statbuf;
DIR* dead;
rep.paths.clear (); rep.paths.clear ();
rep.space = 0; rep.space = 0;
@ -2722,50 +2725,51 @@ Session::cleanup_trash_sources (CleanupReport& rep)
dead_sound_dir = (*i).path; dead_sound_dir = (*i).path;
dead_sound_dir += dead_sound_dir_name; dead_sound_dir += dead_sound_dir_name;
if ((dead = opendir (dead_sound_dir.c_str())) == 0) { clear_directory (dead_sound_dir, &rep.space, &rep.paths);
continue;
}
while ((dentry = readdir (dead)) != 0) {
/* avoid '.' and '..' */
if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
(dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
continue;
}
string fullpath;
fullpath = dead_sound_dir;
fullpath += '/';
fullpath += dentry->d_name;
if (stat (fullpath.c_str(), &statbuf)) {
continue;
}
if (!S_ISREG (statbuf.st_mode)) {
continue;
}
if (unlink (fullpath.c_str())) {
error << string_compose (_("cannot remove dead sound file %1 (%2)"),
fullpath, strerror (errno))
<< endmsg;
}
rep.paths.push_back (dentry->d_name);
rep.space += statbuf.st_size;
}
closedir (dead);
} }
return 0; return 0;
} }
void
Session::cleanup_stubfiles ()
{
vector<space_and_path>::iterator i;
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
string dir;
string lname = legalize_for_path (_name);
vector<string> v;
/* XXX this is a hack caused by semantic conflicts
between space_and_path and the SessionDirectory concept.
*/
v.push_back ((*i).path);
v.push_back ("interchange");
v.push_back (lname);
v.push_back ("audiofiles");
v.push_back (stub_dir_name);
dir = Glib::build_filename (v);
clear_directory (dir);
v.clear ();
v.push_back ((*i).path);
v.push_back ("interchange");
v.push_back (lname);
v.push_back ("midifiles");
v.push_back (stub_dir_name);
dir = Glib::build_filename (v);
clear_directory (dir);
}
}
void void
Session::set_dirty () Session::set_dirty ()
{ {

View file

@ -432,6 +432,13 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
} }
DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n")); DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n"));
if (abort && did_record) {
/* no reason to save the session file when we remove sources
*/
_state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
}
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i); boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) { if (tr) {
@ -439,6 +446,10 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
} }
} }
if (abort && did_record) {
_state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
}
boost::shared_ptr<RouteList> r = routes.reader (); boost::shared_ptr<RouteList> r = routes.reader ();
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {

View file

@ -99,7 +99,6 @@ SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
SMFSource::~SMFSource () SMFSource::~SMFSource ()
{ {
if (removable()) { if (removable()) {
cerr << name() << " is removable, empty ? " << empty() << " UC " << use_count() << endl;
unlink (_path.c_str()); unlink (_path.c_str());
} }
} }
@ -170,7 +169,7 @@ SMFSource::read_unlocked (Evoral::EventSink<nframes_t>& destination, sframes_t s
assert(time >= start_ticks); assert(time >= start_ticks);
const sframes_t ev_frame_time = converter.to(time / (double)ppqn()) + stamp_offset; const sframes_t ev_frame_time = converter.to(time / (double)ppqn()) + stamp_offset;
#if 1 #if 0
cerr << " frames = " << ev_frame_time cerr << " frames = " << ev_frame_time
<< " w/offset = " << ev_frame_time - negative_stamp_offset << " w/offset = " << ev_frame_time - negative_stamp_offset
<< endl; << endl;
@ -494,3 +493,9 @@ SMFSource::flush_midi ()
mark_nonremovable (); mark_nonremovable ();
} }
void
SMFSource::set_path (const string& p)
{
FileSource::set_path (p);
SMF::set_path (_path);
}

View file

@ -889,3 +889,13 @@ SndFileSource::file_closed ()
touch_peakfile (); touch_peakfile ();
} }
void
SndFileSource::set_path (const string& p)
{
FileSource::set_path (p);
if (_descriptor) {
_descriptor->set_path (_path);
}
}

View file

@ -272,6 +272,12 @@ Source::set_allow_remove_if_empty (bool yn)
} }
} }
void
Source::inc_use_count ()
{
g_atomic_int_inc (&_use_count);
}
void void
Source::dec_use_count () Source::dec_use_count ()
{ {

View file

@ -66,7 +66,10 @@ public:
double round_to_file_precision (double val) const; double round_to_file_precision (double val) const;
private: protected:
void set_path (const std::string& p);
private:
std::string _file_path; std::string _file_path;
smf_t* _smf; smf_t* _smf;
smf_track_t* _smf_track; smf_track_t* _smf_track;

View file

@ -90,14 +90,13 @@ SMF::open(const std::string& path, int track) THROW_FILE_ERROR
return -1; return -1;
} }
_smf = smf_load (f); if ((_smf = smf_load (f)) == 0) {
if (_smf == NULL) {
return -1; return -1;
} }
_smf_track = smf_get_track_by_number(_smf, track); if ((_smf_track = smf_get_track_by_number(_smf, track)) == 0) {
if (!_smf_track)
return -2; return -2;
}
//cerr << "Track " << track << " # events: " << _smf_track->number_of_events << endl; //cerr << "Track " << track << " # events: " << _smf_track->number_of_events << endl;
if (_smf_track->number_of_events == 0) { if (_smf_track->number_of_events == 0) {
@ -157,15 +156,22 @@ void
SMF::close() THROW_FILE_ERROR SMF::close() THROW_FILE_ERROR
{ {
if (_smf) { if (_smf) {
#if 0
/* XXX why would we automatically save-on-close?
*/
PBD::StdioFileDescriptor d (_file_path, "w+"); PBD::StdioFileDescriptor d (_file_path, "w+");
FILE* f = d.allocate (); FILE* f = d.allocate ();
if (f == 0) { if (f == 0) {
throw FileError (); throw FileError ();
} }
cerr << "CLOSE: Save SMF to " << _file_path << endl;
if (smf_save(_smf, f) != 0) { if (smf_save(_smf, f) != 0) {
throw FileError(); throw FileError();
} }
#endif
smf_delete(_smf); smf_delete(_smf);
_smf = 0; _smf = 0;
_smf_track = 0; _smf_track = 0;
@ -294,5 +300,10 @@ SMF::round_to_file_precision (double val) const
return round (val * div) / div; return round (val * div) / div;
} }
void
SMF::set_path (const std::string& p)
{
_file_path = p;
}
} // namespace Evoral } // namespace Evoral

View file

@ -100,19 +100,19 @@ FileManager::allocate (FileDescriptor* d)
DEBUG::FileManager, DEBUG::FileManager,
string_compose ( string_compose (
"closed file for %1 to release file handle; now have %2 of %3 open\n", "closed file for %1 to release file handle; now have %2 of %3 open\n",
(*oldest)->_name, _open, _max_open (*oldest)->_path, _open, _max_open
) )
); );
} }
if (d->open ()) { if (d->open ()) {
DEBUG_TRACE (DEBUG::FileManager, string_compose ("open of %1 failed.\n", d->_name)); DEBUG_TRACE (DEBUG::FileManager, string_compose ("open of %1 failed.\n", d->_path));
return true; return true;
} }
_open++; _open++;
DEBUG_TRACE (DEBUG::FileManager, string_compose ("opened file for %1; now have %2 of %3 open.\n", d->_name, _open, _max_open)); DEBUG_TRACE (DEBUG::FileManager, string_compose ("opened file for %1; now have %2 of %3 open.\n", d->_path, _open, _max_open));
} }
#ifdef __APPLE__ #ifdef __APPLE__
@ -148,7 +148,7 @@ FileManager::remove (FileDescriptor* d)
close (d); close (d);
DEBUG_TRACE ( DEBUG_TRACE (
DEBUG::FileManager, DEBUG::FileManager,
string_compose ("closed file for %1; file is being removed; now have %2 of %3 open\n", d->_name, _open, _max_open) string_compose ("closed file for %1; file is being removed; now have %2 of %3 open\n", d->_path, _open, _max_open)
); );
} }
@ -168,7 +168,7 @@ FileManager::close (FileDescriptor* d)
FileDescriptor::FileDescriptor (string const & n, bool w) FileDescriptor::FileDescriptor (string const & n, bool w)
: _refcount (0) : _refcount (0)
, _last_used (0) , _last_used (0)
, _name (n) , _path (n)
, _writeable (w) , _writeable (w)
{ {
@ -224,7 +224,7 @@ FdFileDescriptor::open ()
{ {
/* we must have a lock on the FileManager's mutex */ /* we must have a lock on the FileManager's mutex */
_fd = ::open (_name.c_str(), _writeable ? (O_RDWR | O_CREAT) : O_RDONLY, _mode); _fd = ::open (_path.c_str(), _writeable ? (O_RDWR | O_CREAT) : O_RDONLY, _mode);
return (_fd == -1); return (_fd == -1);
} }
@ -253,6 +253,13 @@ FdFileDescriptor::allocate ()
} }
void
FileDescriptor::set_path (const string& p)
{
assert (!is_open());
_path = p;
}
/** @param n Filename. /** @param n Filename.
* @param w true to open writeable, otherwise false. * @param w true to open writeable, otherwise false.
*/ */
@ -283,7 +290,7 @@ StdioFileDescriptor::open ()
{ {
/* we must have a lock on the FileManager's mutex */ /* we must have a lock on the FileManager's mutex */
_file = fopen (_name.c_str(), _mode.c_str()); _file = fopen (_path.c_str(), _mode.c_str());
return (_file == 0); return (_file == 0);
} }

View file

@ -51,7 +51,10 @@ public:
FileDescriptor (std::string const &, bool); FileDescriptor (std::string const &, bool);
virtual ~FileDescriptor () {} virtual ~FileDescriptor () {}
const std::string& path() const { return _path; }
void release (); void release ();
virtual void set_path (const std::string&);
/** Emitted when the file is closed */ /** Emitted when the file is closed */
PBD::Signal0<void> Closed; PBD::Signal0<void> Closed;
@ -71,7 +74,7 @@ protected:
int _refcount; ///< number of active users of this file int _refcount; ///< number of active users of this file
double _last_used; ///< monotonic time that this file was last allocated double _last_used; ///< monotonic time that this file was last allocated
std::string _name; ///< filename std::string _path; ///< file path
bool _writeable; ///< true if it should be opened writeable, otherwise false bool _writeable; ///< true if it should be opened writeable, otherwise false
FileManager* manager (); FileManager* manager ();

View file

@ -86,7 +86,7 @@ SndFileDescriptor::open ()
{ {
/* we must have a lock on the FileManager's mutex */ /* we must have a lock on the FileManager's mutex */
_sndfile = sf_open (_name.c_str(), _writeable ? SFM_RDWR : SFM_READ, _info); _sndfile = sf_open (_path.c_str(), _writeable ? SFM_RDWR : SFM_READ, _info);
return (_sndfile == 0); return (_sndfile == 0);
} }

View file

@ -59,6 +59,7 @@ def build(bld):
convert.cc convert.cc
controllable.cc controllable.cc
controllable_descriptor.cc controllable_descriptor.cc
clear_dir.cc
crossthread.cc crossthread.cc
cpus.cc cpus.cc
debug.cc debug.cc