#include <sys/time.h>
#include <sys/stat.h>
+#include <stdio.h> // for rename(), sigh
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <pbd/convert.h>
+#include <pbd/basename.h>
#include <pbd/mountpoint.h>
-#include <pbd/pathscanner.h>
#include <pbd/stl_delete.h>
#include <pbd/strsplit.h>
+#include <pbd/shortpath.h>
#include <pbd/enumwriter.h>
#include <sndfile.h>
#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
#include <ardour/audiofilesource.h>
#include <ardour/sndfile_helpers.h>
#include <ardour/sndfilesource.h>
#include <ardour/session.h>
+#include <ardour/session_directory.h>
#include <ardour/source_factory.h>
+#include <ardour/filename_extensions.h>
// if these headers come before sigc++ is included
// the parser throws ObjC++ errors. (nil is a keyword)
AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
: AudioSource (s, node), _flags (Flag (Writable|CanRename))
- /* _channel is set in set_state() */
+ /* _channel is set in set_state() or init() */
{
/* constructor used for existing internal-to-session files. file must exist */
if (set_state (node)) {
throw failed_constructor ();
}
+
+ string foo = _name;
- if (init (_name, must_exist)) {
+ if (init (foo, must_exist)) {
throw failed_constructor ();
}
}
int
AudioFileSource::init (ustring pathstr, bool must_exist)
{
- bool is_new = false;
-
_length = 0;
timeline_position = 0;
_peaks_built = false;
- file_is_new = false;
- if (!find (pathstr, must_exist, is_new)) {
+ if (!find (pathstr, must_exist, file_is_new, _channel)) {
throw non_existent_source ();
}
- if (is_new && must_exist) {
+ if (file_is_new && must_exist) {
return -1;
}
ustring
AudioFileSource::peak_path (ustring audio_path)
{
- return _session.peak_path_from_audio_path (audio_path);
+ ustring base;
+
+ base = PBD::basename_nosuffix (audio_path);
+ base += '%';
+ base += (char) ('A' + _channel);
+
+ return _session.peak_path (base);
+}
+
+ustring
+AudioFileSource::find_broken_peakfile (ustring peak_path, ustring audio_path)
+{
+ ustring str;
+
+ /* check for the broken location in use by 2.0 for several months */
+
+ str = broken_peak_path (audio_path);
+
+ if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
+
+ if (is_embedded()) {
+
+ /* it would be nice to rename it but the nature of
+ the bug means that we can't reliably use it.
+ */
+
+ peak_path = str;
+
+ } else {
+ /* all native files are mono, so we can just rename
+ it.
+ */
+ ::rename (str.c_str(), peak_path.c_str());
+ }
+
+ } else {
+ /* Nasty band-aid for older sessions that were created before we
+ used libsndfile for all audio files.
+ */
+
+
+ str = old_peak_path (audio_path);
+ if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
+ peak_path = str;
+ }
+ }
+
+ return peak_path;
+}
+
+ustring
+AudioFileSource::broken_peak_path (ustring audio_path)
+{
+ return _session.peak_path (audio_path);
}
ustring
ustring res = peak_dir;
res += buf;
+ res += peakfile_suffix;
return res;
}
void
AudioFileSource::mark_for_remove ()
{
- if (!writable()) {
+ // This operation is not allowed for sources for destructive tracks or embedded files.
+ // Fortunately mark_for_remove() is never called for embedded files. This function
+ // must be fixed if that ever happens.
+ if (_flags & Destructive) {
return;
}
if (!writable()) {
return;
}
+
+ /* XXX notice that we're readers of _peaks_built
+ but we must hold a solid lock on PeaksReady.
+ */
Glib::Mutex::Lock lm (_lock);
}
bool
-AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew)
+AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& chan)
{
ustring::size_type pos;
bool ret = false;
isnew = false;
- /* clean up PATH:CHANNEL notation so that we are looking for the correct path */
-
- if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
- pathstr = pathstr.substr (0, pos);
- }
-
if (pathstr[0] != '/') {
/* non-absolute pathname: find pathstr in search path */
if (fullpath[fullpath.length()-1] != '/') {
fullpath += '/';
}
+
fullpath += pathstr;
+
+ /* i (paul) made a nasty design error by using ':' as a special character in
+ Ardour 0.99 .. this hack tries to make things sort of work.
+ */
- if (access (fullpath.c_str(), R_OK) == 0) {
- keeppath = fullpath;
- ++cnt;
- }
+ if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
+
+ if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+
+ /* its a real file, no problem */
+
+ keeppath = fullpath;
+ ++cnt;
+
+ } else {
+
+ if (must_exist) {
+
+ /* might be an older session using file:channel syntax. see if the version
+ without the :suffix exists
+ */
+
+ ustring shorter = pathstr.substr (0, pos);
+ fullpath = *i;
+
+ if (fullpath[fullpath.length()-1] != '/') {
+ fullpath += '/';
+ }
+
+ fullpath += shorter;
+
+ if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ chan = atoi (pathstr.substr (pos+1));
+ pathstr = shorter;
+ keeppath = fullpath;
+ ++cnt;
+ }
+
+ } else {
+
+ /* new derived file (e.g. for timefx) being created in a newer session */
+
+ }
+ }
+
+ } else {
+
+ if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ keeppath = fullpath;
+ ++cnt;
+ }
+ }
}
if (cnt > 1) {
isnew = true;
}
}
-
+
_name = pathstr;
_path = keeppath;
ret = true;
} else {
/* external files and/or very very old style sessions include full paths */
+
+ /* ugh, handle ':' situation */
+
+ if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
+
+ ustring shorter = pathstr.substr (0, pos);
+
+ if (Glib::file_test (shorter, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ chan = atoi (pathstr.substr (pos+1));
+ pathstr = shorter;
+ }
+ }
_path = pathstr;
+
if (is_embedded()) {
_name = pathstr;
} else {
_name = pathstr.substr (pathstr.find_last_of ('/') + 1);
}
-
- if (access (_path.c_str(), R_OK) != 0) {
- /* file does not exist or we cannot read it */
+ if (!Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ /* file does not exist or we cannot read it */
+
if (must_exist) {
error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
goto out;
/* already exists */
ret = true;
+
}
}
}
int
-AudioFileSource::set_name (ustring newname, bool destructive)
+AudioFileSource::set_source_name (ustring newname, bool destructive)
{
Glib::Mutex::Lock lm (_lock);
ustring oldpath = _path;
bool
AudioFileSource::is_empty (Session& s, ustring path)
{
- bool ret = false;
- boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (s, path, 0, NoPeakFile, false));
-
- if (afs) {
- ret = (afs->length() == 0);
+ SoundFileInfo info;
+ string err;
+
+ if (!get_soundfile_info (path, info, err)) {
+ /* dangerous: we can't get info, so assume that its not empty */
+ return false;
}
- return ret;
+ return info.length == 0;
}
int
AudioFileSource::safe_file_extension(ustring file)
{
return !(file.rfind(".wav") == ustring::npos &&
- file.rfind(".aiff")== ustring::npos &&
- file.rfind(".aif") == ustring::npos &&
- file.rfind(".snd") == ustring::npos &&
- file.rfind(".au") == ustring::npos &&
- file.rfind(".raw") == ustring::npos &&
- file.rfind(".sf") == ustring::npos &&
- file.rfind(".cdr") == ustring::npos &&
- file.rfind(".smp") == ustring::npos &&
- file.rfind(".maud")== ustring::npos &&
- file.rfind(".vwe") == ustring::npos &&
- file.rfind(".paf") == ustring::npos &&
+ file.rfind(".aiff")== ustring::npos &&
+ file.rfind(".aif") == ustring::npos &&
+ file.rfind(".snd") == ustring::npos &&
+ file.rfind(".au") == ustring::npos &&
+ file.rfind(".raw") == ustring::npos &&
+ file.rfind(".sf") == ustring::npos &&
+ file.rfind(".cdr") == ustring::npos &&
+ file.rfind(".smp") == ustring::npos &&
+ file.rfind(".maud")== ustring::npos &&
+ file.rfind(".vwe") == ustring::npos &&
+ file.rfind(".paf") == ustring::npos &&
+ /* protools convention */
+ file.rfind(".L") == ustring::npos &&
+ file.rfind(".R") == ustring::npos &&
+#ifdef HAVE_FLAC
+ file.rfind(".flac")== ustring::npos &&
+#endif // HAVE_FLAC
#ifdef HAVE_COREAUDIO
- file.rfind(".mp3") == ustring::npos &&
- file.rfind(".aac") == ustring::npos &&
- file.rfind(".mp4") == ustring::npos &&
+ file.rfind(".mp3") == ustring::npos &&
+ file.rfind(".aac") == ustring::npos &&
+ file.rfind(".mp4") == ustring::npos &&
#endif // HAVE_COREAUDIO
- file.rfind(".voc") == ustring::npos);
+ file.rfind(".voc") == ustring::npos);
}
void