remove a bunch of explicit uses of '/' as a directory separator; use Glib::build_file...
[ardour2.git] / libs / ardour / audiofilesource.cc
blob9141456e22aec0b799390c75fc3dc4350196fa75
1 /*
2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <vector>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24 #include <stdio.h> // for rename(), sigh
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
29 #include <pbd/convert.h>
30 #include <pbd/basename.h>
31 #include <pbd/mountpoint.h>
32 #include <pbd/pathscanner.h>
33 #include <pbd/stl_delete.h>
34 #include <pbd/strsplit.h>
35 #include <pbd/shortpath.h>
36 #include <pbd/enumwriter.h>
38 #include <sndfile.h>
40 #include <glibmm/miscutils.h>
41 #include <glibmm/fileutils.h>
42 #include <glibmm/thread.h>
44 #include <ardour/audiofilesource.h>
45 #include <ardour/sndfile_helpers.h>
46 #include <ardour/sndfilesource.h>
47 #include <ardour/session.h>
48 #include <ardour/source_factory.h>
50 // if these headers come before sigc++ is included
51 // the parser throws ObjC++ errors. (nil is a keyword)
52 #ifdef HAVE_COREAUDIO
53 #include <ardour/coreaudiosource.h>
54 #include <AudioToolbox/ExtendedAudioFile.h>
55 #include <AudioToolbox/AudioFormat.h>
56 #endif // HAVE_COREAUDIO
58 #include "i18n.h"
60 using namespace ARDOUR;
61 using namespace PBD;
62 using namespace Glib;
64 ustring AudioFileSource::peak_dir = "";
65 ustring AudioFileSource::search_path;
67 sigc::signal<void> AudioFileSource::HeaderPositionOffsetChanged;
68 uint64_t AudioFileSource::header_position_offset = 0;
70 /* XXX maybe this too */
71 char AudioFileSource::bwf_serial_number[13] = "000000000000";
73 struct SizedSampleBuffer {
74 nframes_t size;
75 Sample* buf;
77 SizedSampleBuffer (nframes_t sz) : size (sz) {
78 buf = new Sample[size];
81 ~SizedSampleBuffer() {
82 delete [] buf;
86 Glib::StaticPrivate<SizedSampleBuffer> thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT;
88 AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags)
89 : AudioSource (s, path), _flags (flags),
90 _channel (0)
92 /* constructor used for existing external to session files. file must exist already */
93 _is_embedded = AudioFileSource::determine_embeddedness (path);
95 if (init (path, true)) {
96 throw failed_constructor ();
99 fix_writable_flags ();
102 AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format)
103 : AudioSource (s, path), _flags (flags),
104 _channel (0)
106 /* constructor used for new internal-to-session files. file cannot exist */
107 _is_embedded = false;
109 if (init (path, false)) {
110 throw failed_constructor ();
113 fix_writable_flags ();
116 AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
117 : AudioSource (s, node), _flags (Flag (Writable|CanRename))
118 /* _channel is set in set_state() or init() */
120 /* constructor used for existing internal-to-session files. file must exist */
122 if (set_state (node)) {
123 throw failed_constructor ();
126 string foo = _name;
128 if (init (foo, must_exist)) {
129 throw failed_constructor ();
132 fix_writable_flags ();
135 AudioFileSource::~AudioFileSource ()
137 if (removable()) {
138 unlink (_path.c_str());
139 unlink (peakpath.c_str());
143 void
144 AudioFileSource::fix_writable_flags ()
146 if (!_session.writable()) {
147 _flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename));
151 bool
152 AudioFileSource::determine_embeddedness (ustring path)
154 return (path.find("/") == 0);
157 bool
158 AudioFileSource::removable () const
160 return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0));
163 bool
164 AudioFileSource::writable() const
166 return (_flags & Writable);
170 AudioFileSource::init (ustring pathstr, bool must_exist)
172 _length = 0;
173 timeline_position = 0;
174 _peaks_built = false;
176 /* is_embedded() can't work yet, because our _path is not set */
178 bool embedded = determine_embeddedness (pathstr);
180 if (!find (pathstr, must_exist, embedded, file_is_new, _channel, _path, _name)) {
181 throw non_existent_source ();
184 if (file_is_new && must_exist) {
185 return -1;
188 return 0;
192 ustring
193 AudioFileSource::peak_path (ustring audio_path)
195 ustring base;
197 base = PBD::basename_nosuffix (audio_path);
198 base += '%';
199 base += (char) ('A' + _channel);
201 return _session.peak_path (base);
204 ustring
205 AudioFileSource::find_broken_peakfile (ustring peak_path, ustring audio_path)
207 ustring str;
209 /* check for the broken location in use by 2.0 for several months */
211 str = broken_peak_path (audio_path);
213 if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
215 if (is_embedded()) {
217 /* it would be nice to rename it but the nature of
218 the bug means that we can't reliably use it.
221 peak_path = str;
223 } else {
224 /* all native files are mono, so we can just rename
227 ::rename (str.c_str(), peak_path.c_str());
230 } else {
231 /* Nasty band-aid for older sessions that were created before we
232 used libsndfile for all audio files.
236 str = old_peak_path (audio_path);
237 if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
238 peak_path = str;
242 return peak_path;
245 ustring
246 AudioFileSource::broken_peak_path (ustring audio_path)
248 return Glib::build_filename(_session.peak_dir (), PBD::basename_nosuffix (audio_path) + ".peak");
251 ustring
252 AudioFileSource::old_peak_path (ustring audio_path)
254 /* XXX hardly bombproof! fix me */
256 struct stat stat_file;
257 struct stat stat_mount;
259 ustring mp = mountpoint (audio_path);
261 stat (audio_path.c_str(), &stat_file);
262 stat (mp.c_str(), &stat_mount);
264 char buf[32];
265 #ifdef __APPLE__
266 snprintf (buf, sizeof (buf), "%u-%u-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel);
267 #else
268 snprintf (buf, sizeof (buf), "%ld-%ld-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel);
269 #endif
271 ustring res = peak_dir;
272 res += buf;
274 return res;
277 bool
278 AudioFileSource::get_soundfile_info (ustring path, SoundFileInfo& _info, string& error_msg)
280 /* try sndfile first because it gets timecode info from .wav (BWF) if it exists,
281 which at present, ExtAudioFile from Apple seems unable to do.
284 if (SndFileSource::get_soundfile_info (path, _info, error_msg) != 0) {
285 return true;
288 #ifdef HAVE_COREAUDIO
289 if (CoreAudioSource::get_soundfile_info (path, _info, error_msg) == 0) {
290 return true;
292 #endif // HAVE_COREAUDIO
294 return false;
297 XMLNode&
298 AudioFileSource::get_state ()
300 XMLNode& root (AudioSource::get_state());
301 char buf[32];
302 root.add_property (X_("flags"), enum_2_string (_flags));
303 snprintf (buf, sizeof (buf), "%u", _channel);
304 root.add_property (X_("channel"), buf);
305 return root;
309 AudioFileSource::set_state (const XMLNode& node)
311 const XMLProperty* prop;
313 if (AudioSource::set_state (node)) {
314 return -1;
317 if ((prop = node.property (X_("flags"))) != 0) {
318 _flags = Flag (string_2_enum (prop->value(), _flags));
319 } else {
320 _flags = Flag (0);
324 fix_writable_flags ();
326 if ((prop = node.property (X_("channel"))) != 0) {
327 _channel = atoi (prop->value());
328 } else {
329 _channel = 0;
332 if ((prop = node.property (X_("name"))) != 0) {
333 _is_embedded = AudioFileSource::determine_embeddedness (prop->value());
334 } else {
335 _is_embedded = false;
338 if ((prop = node.property (X_("destructive"))) != 0) {
339 /* old style, from the period when we had DestructiveFileSource */
340 _flags = Flag (_flags | Destructive);
343 return 0;
346 void
347 AudioFileSource::mark_for_remove ()
349 // This operation is not allowed for sources for destructive tracks or embedded files.
350 // Fortunately mark_for_remove() is never called for embedded files. This function
351 // must be fixed if that ever happens.
352 if (!_session.writable() || (_flags & Destructive)) {
353 return;
356 _flags = Flag (_flags | Removable | RemoveAtDestroy);
359 void
360 AudioFileSource::mark_streaming_write_completed ()
362 if (!writable()) {
363 return;
366 /* XXX notice that we're readers of _peaks_built
367 but we must hold a solid lock on PeaksReady.
370 Glib::Mutex::Lock lm (_lock);
372 if (_peaks_built) {
373 PeaksReady (); /* EMIT SIGNAL */
377 void
378 AudioFileSource::mark_take (ustring id)
380 if (writable()) {
381 _take_id = id;
386 AudioFileSource::move_to_trash (const ustring& trash_dir_name)
388 if (is_embedded()) {
389 cerr << "tried to move an embedded region to trash" << endl;
390 return -1;
393 ustring newpath;
395 if (!writable()) {
396 return -1;
399 /* don't move the file across filesystems, just
400 stick it in the `trash_dir_name' directory
401 on whichever filesystem it was already on.
404 newpath = Glib::path_get_dirname (_path);
405 newpath = Glib::path_get_dirname (newpath);
407 cerr << "from " << _path << " dead dir looks like " << newpath << endl;
409 vector<string> p;
410 p.push_back (newpath);
411 p.push_back (trash_dir_name);
412 p.push_back (Glib::path_get_basename (_path));
414 newpath = Glib::build_filename (p);
416 if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
418 /* the new path already exists, try versioning */
420 char buf[PATH_MAX+1];
421 int version = 1;
422 ustring newpath_v;
424 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
425 newpath_v = buf;
427 while (Glib::file_test (newpath_v, Glib::FILE_TEST_EXISTS) && version < 999) {
428 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
429 newpath_v = buf;
432 if (version == 999) {
433 error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
434 newpath)
435 << endmsg;
436 } else {
437 newpath = newpath_v;
440 } else {
442 /* it doesn't exist, or we can't read it or something */
446 if (::rename (_path.c_str(), newpath.c_str()) != 0) {
447 error << string_compose (_("cannot 1 rename audio file source from %1 to %2 (%3)"),
448 _path, newpath, strerror (errno))
449 << endmsg;
450 return -1;
453 if (::unlink (peakpath.c_str()) != 0) {
454 error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
455 peakpath, _path, strerror (errno))
456 << endmsg;
457 /* try to back out */
458 rename (newpath.c_str(), _path.c_str());
459 return -1;
462 _path = newpath;
463 peakpath = "";
465 /* file can not be removed twice, since the operation is not idempotent */
467 _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
469 return 0;
472 bool
473 AudioFileSource::find (ustring pathstr, bool must_exist, bool embedded,
474 bool& isnew, uint16_t& chan,
475 ustring& path, std::string& name)
477 ustring::size_type pos;
478 bool ret = false;
480 isnew = false;
482 if (!Glib::path_is_absolute (pathstr)) {
484 /* non-absolute pathname: find pathstr in search path */
486 vector<ustring> dirs;
487 int cnt;
488 ustring fullpath;
489 ustring keeppath;
491 if (search_path.length() == 0) {
492 error << _("FileSource: search path not set") << endmsg;
493 goto out;
496 split (search_path, dirs, ':');
498 cnt = 0;
500 for (vector<ustring>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
502 fullpath = Glib::build_filename (*i, pathstr);
504 /* i (paul) made a nasty design error by using ':' as a special character in
505 Ardour 0.99 .. this hack tries to make things sort of work.
508 if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
510 if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
512 /* its a real file, no problem */
514 keeppath = fullpath;
515 ++cnt;
517 } else {
519 if (must_exist) {
521 /* might be an older session using file:channel syntax. see if the version
522 without the :suffix exists
525 ustring shorter = pathstr.substr (0, pos);
526 fullpath = Glib::build_filename (*i, shorter);
528 if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
529 chan = atoi (pathstr.substr (pos+1));
530 pathstr = shorter;
531 keeppath = fullpath;
532 ++cnt;
535 } else {
537 /* new derived file (e.g. for timefx) being created in a newer session */
542 } else {
544 if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
545 keeppath = fullpath;
546 ++cnt;
551 if (cnt > 1) {
553 error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, search_path) << endmsg;
554 goto out;
556 } else if (cnt == 0) {
558 if (must_exist) {
559 error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, search_path) << endmsg;
560 goto out;
561 } else {
562 isnew = true;
566 name = pathstr;
567 path = keeppath;
568 ret = true;
570 } else {
572 /* external files and/or very very old style sessions include full paths */
574 /* ugh, handle ':' situation */
576 if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
578 ustring shorter = pathstr.substr (0, pos);
580 if (Glib::file_test (shorter, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
581 chan = atoi (pathstr.substr (pos+1));
582 pathstr = shorter;
586 path = pathstr;
588 if (embedded) {
589 name = pathstr;
590 } else {
591 name = Glib::path_get_basename (pathstr);
594 if (!Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
596 /* file does not exist or we cannot read it */
598 if (must_exist) {
599 error << string_compose(_("Filesource: cannot find required file (%1): %2"), pathstr, strerror (errno)) << endmsg;
600 goto out;
603 if (errno != ENOENT) {
604 error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), pathstr, strerror (errno)) << endmsg;
605 goto out;
608 /* a new file */
610 isnew = true;
611 ret = true;
613 } else {
615 /* already exists */
617 ret = true;
622 out:
623 return ret;
626 void
627 AudioFileSource::set_search_path (ustring p)
629 search_path = p;
632 void
633 AudioFileSource::set_header_position_offset (nframes_t offset)
635 header_position_offset = offset;
636 HeaderPositionOffsetChanged ();
639 void
640 AudioFileSource::set_timeline_position (int64_t pos)
642 timeline_position = pos;
645 void
646 AudioFileSource::set_allow_remove_if_empty (bool yn)
648 if (!writable()) {
649 return;
652 if (yn) {
653 _flags = Flag (_flags | RemovableIfEmpty);
654 } else {
655 _flags = Flag (_flags & ~RemovableIfEmpty);
658 fix_writable_flags ();
662 AudioFileSource::set_name (ustring newname, bool destructive)
664 Glib::Mutex::Lock lm (_lock);
665 ustring oldpath = _path;
666 ustring newpath = Session::change_audio_path_by_name (oldpath, _name, newname, destructive);
668 if (newpath.empty()) {
669 error << string_compose (_("programming error: %1"), "cannot generate a changed audio path") << endmsg;
670 return -1;
673 // Test whether newpath exists, if yes notify the user but continue.
674 if (access(newpath.c_str(),F_OK) == 0) {
675 error << _("Programming error! Tried to rename a file over another file! It's safe to continue working, but please report this to the developers.") << endmsg;
676 return -1;
679 if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
680 error << string_compose (_("cannot 2 rename audio file %1 to %2"), _name, newpath) << endmsg;
681 return -1;
684 _name = Glib::path_get_basename (newpath);
685 _path = newpath;
687 return rename_peakfile (peak_path (_path));
690 bool
691 AudioFileSource::is_empty (Session& s, ustring path)
693 SoundFileInfo info;
694 string err;
696 if (!get_soundfile_info (path, info, err)) {
697 /* dangerous: we can't get info, so assume that its not empty */
698 return false;
701 return info.length == 0;
705 AudioFileSource::setup_peakfile ()
707 if (!(_flags & NoPeakFile)) {
708 return initialize_peakfile (file_is_new, _path);
709 } else {
710 return 0;
714 bool
715 AudioFileSource::safe_file_extension(ustring file)
717 const char* suffixes[] = {
718 ".aif", ".AIF",
719 ".aifc", ".AIFC",
720 ".aiff", ".AIFF",
721 ".amb", ".AMB",
722 ".au", ".AU",
723 ".caf", ".CAF",
724 ".cdr", ".CDR",
725 ".flac", ".FLAC",
726 ".htk", ".HTK",
727 ".iff", ".IFF",
728 ".mat", ".MAT",
729 ".oga", ".OGA",
730 ".ogg", ".OGG",
731 ".paf", ".PAF",
732 ".pvf", ".PVF",
733 ".sf", ".SF",
734 ".smp", ".SMP",
735 ".snd", ".SND",
736 ".maud", ".MAUD",
737 ".voc", ".VOC"
738 ".vwe", ".VWE",
739 ".w64", ".W64",
740 ".wav", ".WAV",
741 #ifdef HAVE_COREAUDIO
742 ".aac", ".AAC",
743 ".adts", ".ADTS",
744 ".ac3", ".AC3",
745 ".amr", ".AMR",
746 ".mpa", ".MPA",
747 ".mpeg", ".MPEG",
748 ".mp1", ".MP1",
749 ".mp2", ".MP2",
750 ".mp3", ".MP3",
751 ".mp4", ".MP4",
752 ".m4a", ".M4A",
753 ".sd2", ".SD2", // libsndfile supports sd2 also, but the resource fork is required to open.
754 #endif // HAVE_COREAUDIO
757 for (size_t n = 0; n < sizeof(suffixes)/sizeof(suffixes[0]); ++n) {
758 if (file.rfind (suffixes[n]) == file.length() - strlen (suffixes[n])) {
759 return true;
763 return false;
766 void
767 AudioFileSource::mark_immutable ()
769 /* destructive sources stay writable, and their other flags don't
770 change.
773 if (!(_flags & Destructive)) {
774 _flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename));
779 Sample*
780 AudioFileSource::get_interleave_buffer (nframes_t size)
782 SizedSampleBuffer* ssb;
784 if ((ssb = thread_interleave_buffer.get()) == 0) {
785 ssb = new SizedSampleBuffer (size);
786 thread_interleave_buffer.set (ssb);
789 if (ssb->size < size) {
790 ssb = new SizedSampleBuffer (size);
791 thread_interleave_buffer.set (ssb);
794 return ssb->buf;