#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 <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
+#include <glibmm/thread.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)
/* XXX maybe this too */
char AudioFileSource::bwf_serial_number[13] = "000000000000";
+struct SizedSampleBuffer {
+ nframes_t size;
+ Sample* buf;
+
+ SizedSampleBuffer (nframes_t sz) : size (sz) {
+ buf = new Sample[size];
+ }
+
+ ~SizedSampleBuffer() {
+ delete [] buf;
+ }
+};
+
+Glib::StaticPrivate<SizedSampleBuffer> thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT;
+
+/** Constructor used for existing internal-to-session files. File must exist. */
AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags)
: AudioSource (s, path), _flags (flags),
_channel (0)
{
- /* constructor used for existing external to session files. file must exist already */
_is_embedded = AudioFileSource::determine_embeddedness (path);
if (init (path, true)) {
}
+/** Constructor used for new internal-to-session files. File cannot exist. */
AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format)
: AudioSource (s, path), _flags (flags),
_channel (0)
{
- /* constructor used for new internal-to-session files. file cannot exist */
_is_embedded = false;
if (init (path, false)) {
}
}
+/** Constructor used for existing internal-to-session files. File must exist. */
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);
return -1;
}
- ustring newpath;
-
if (!writable()) {
return -1;
}
- /* don't move the file across filesystems, just
- stick it in the `trash_dir_name' directory
- on whichever filesystem it was already on.
+ /* don't move the file across filesystems, just stick it in the
+ trash_dir_name directory on whichever filesystem it was already on
*/
+ ustring newpath;
newpath = Glib::path_get_dirname (_path);
newpath = Glib::path_get_dirname (newpath);
- cerr << "from " << _path << " dead dir looks like " << newpath << endl;
-
- newpath += '/';
- newpath += trash_dir_name;
- newpath += '/';
+ newpath += string("/") + trash_dir_name + "/";
newpath += Glib::path_get_basename (_path);
+ /* the new path already exists, try versioning */
if (access (newpath.c_str(), F_OK) == 0) {
-
- /* the new path already exists, try versioning */
-
char buf[PATH_MAX+1];
int version = 1;
ustring newpath_v;
}
if (version == 999) {
- error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
- newpath)
- << endmsg;
+ PBD::error << string_compose (
+ _("there are already 1000 files with names like %1; versioning discontinued"),
+ newpath)
+ << endmsg;
} else {
newpath = newpath_v;
}
-
- } else {
-
- /* it doesn't exist, or we can't read it or something */
-
}
if (::rename (_path.c_str(), newpath.c_str()) != 0) {
- error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
- _path, newpath, strerror (errno))
- << endmsg;
+ PBD::error << string_compose (
+ _("cannot rename audio file source from %1 to %2 (%3)"),
+ _path, newpath, strerror (errno)) << endmsg;
return -1;
}
peakpath = "";
/* file can not be removed twice, since the operation is not idempotent */
-
_flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
return 0;
}
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;
- /* 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 ((pos = pathstr.find_last_of (':')) != ustring::npos) {
- if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS)) {
- /* its a real file, no problem */
-
- } else {
-
- if (must_exist) {
-
- /* older session using file:channel syntax */
-
- warning << string_compose (_("This older session references an embedded\n\
-non-mono audio file:\n\n%1\n\n \
-The session file may be edited or the file must be removed before it can be used."),
- short_path (pathstr, 48))
- << endmsg;
- return false;
-
- } else {
-
- /* new derived file (e.g. for timefx) being created in a newer session */
-
- }
- }
- }
-
if (pathstr[0] != '/') {
/* non-absolute pathname: find pathstr in search path */
cnt = 0;
for (vector<ustring>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
-
fullpath = *i;
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;
}
}
-
+
+ /* Current find() is unable to parse relative path names to yet non-existant
+ sources. QuickFix(tm) */
+ if (keeppath == "") {
+ if (must_exist) {
+ error << "AudioFileSource::find(), keeppath = \"\", but the file must exist" << endl;
+ } else {
+ keeppath = pathstr;
+ }
+ }
+
_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
bool
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 &&
+ const char* suffixes[] = {
+ ".wav", ".WAV",
+ ".aiff", ".AIFF",
+ ".caf", ".CAF",
+ ".aif", ".AIF",
+ ".amb", ".AMB",
+ ".snd", ".SND",
+ ".au", ".AU",
+ ".raw", ".RAW",
+ ".sf", ".SF",
+ ".cdr", ".CDR",
+ ".smp", ".SMP",
+ ".maud", ".MAUD",
+ ".vwe", ".VWE",
+ ".paf", ".PAF",
+ ".voc", ".VOC",
#ifdef HAVE_FLAC
- file.rfind(".flac")== ustring::npos &&
+ ".flac", ".FLAC",
#endif // HAVE_FLAC
#ifdef HAVE_COREAUDIO
- file.rfind(".mp3") == ustring::npos &&
- file.rfind(".aac") == ustring::npos &&
- file.rfind(".mp4") == ustring::npos &&
+ ".mp3", ".MP3",
+ ".aac", ".AAC",
+ ".mp4", ".MP4",
#endif // HAVE_COREAUDIO
- file.rfind(".voc") == ustring::npos);
+ };
+
+ for (size_t n = 0; n < sizeof(suffixes)/sizeof(suffixes[0]); ++n) {
+ if (file.rfind (suffixes[n]) == file.length() - strlen (suffixes[n])) {
+ return true;
+ }
+ }
+
+ return false;
}
void
_flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename));
}
}
+
+
+Sample*
+AudioFileSource::get_interleave_buffer (nframes_t size)
+{
+ SizedSampleBuffer* ssb;
+
+ if ((ssb = thread_interleave_buffer.get()) == 0) {
+ ssb = new SizedSampleBuffer (size);
+ thread_interleave_buffer.set (ssb);
+ }
+
+ if (ssb->size < size) {
+ ssb = new SizedSampleBuffer (size);
+ thread_interleave_buffer.set (ssb);
+ }
+
+ return ssb->buf;
+}