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.
21 #include "libardour-config.h"
28 #include <stdio.h> // for rename(), sigh
33 #include "pbd/convert.h"
34 #include "pbd/basename.h"
35 #include "pbd/mountpoint.h"
36 #include "pbd/stl_delete.h"
37 #include "pbd/strsplit.h"
38 #include "pbd/shortpath.h"
39 #include "pbd/enumwriter.h"
43 #include <glibmm/miscutils.h>
44 #include <glibmm/fileutils.h>
45 #include <glibmm/thread.h>
47 #include "ardour/audiofilesource.h"
48 #include "ardour/debug.h"
49 #include "ardour/sndfile_helpers.h"
50 #include "ardour/sndfilesource.h"
51 #include "ardour/session.h"
52 #include "ardour/session_directory.h"
53 #include "ardour/source_factory.h"
54 #include "ardour/filename_extensions.h"
56 // if these headers come before sigc++ is included
57 // the parser throws ObjC++ errors. (nil is a keyword)
59 #include "ardour/coreaudiosource.h"
60 #include <AudioToolbox/ExtendedAudioFile.h>
61 #include <AudioToolbox/AudioFormat.h>
62 #endif // HAVE_COREAUDIO
67 using namespace ARDOUR
;
71 string
AudioFileSource::peak_dir
= "";
73 PBD::Signal0
<void> AudioFileSource::HeaderPositionOffsetChanged
;
74 uint64_t AudioFileSource::header_position_offset
= 0;
76 /* XXX maybe this too */
77 char AudioFileSource::bwf_serial_number
[13] = "000000000000";
79 struct SizedSampleBuffer
{
83 SizedSampleBuffer (framecnt_t sz
) : size (sz
) {
84 buf
= new Sample
[size
];
87 ~SizedSampleBuffer() {
92 Glib::StaticPrivate
<SizedSampleBuffer
> thread_interleave_buffer
= GLIBMM_STATIC_PRIVATE_INIT
;
94 /** Constructor used for existing external-to-session files. */
95 AudioFileSource::AudioFileSource (Session
& s
, const string
& path
, Source::Flag flags
)
96 : Source (s
, DataType::AUDIO
, path
, flags
)
97 , AudioSource (s
, path
)
98 /* note that external files have their own path as "origin" */
99 , FileSource (s
, DataType::AUDIO
, path
, path
, flags
)
101 /* note that origin remains empty */
103 if (init (_path
, true)) {
104 throw failed_constructor ();
106 cerr
<< "audiofile source created with path " << path
<< endl
;
109 /** Constructor used for new internal-to-session files. */
110 AudioFileSource::AudioFileSource (Session
& s
, const string
& path
, const string
& origin
, Source::Flag flags
,
111 SampleFormat
/*samp_format*/, HeaderFormat
/*hdr_format*/)
112 : Source (s
, DataType::AUDIO
, path
, flags
)
113 , AudioSource (s
, path
)
114 , FileSource (s
, DataType::AUDIO
, path
, origin
, flags
)
116 /* note that origin remains empty */
118 if (init (_path
, false)) {
119 throw failed_constructor ();
123 /** Constructor used for existing internal-to-session files via XML. File must exist. */
124 AudioFileSource::AudioFileSource (Session
& s
, const XMLNode
& node
, bool must_exist
)
126 , AudioSource (s
, node
)
127 , FileSource (s
, node
, must_exist
)
129 if (set_state (node
, Stateful::loading_state_version
)) {
130 throw failed_constructor ();
133 if (init (_path
, must_exist
)) {
134 throw failed_constructor ();
138 AudioFileSource::~AudioFileSource ()
140 DEBUG_TRACE (DEBUG::Destruction
, string_compose ("AudioFileSource destructor %1, removable? %2\n", _path
, removable()));
142 unlink (_path
.c_str());
143 unlink (peakpath
.c_str());
148 AudioFileSource::init (const string
& pathstr
, bool must_exist
)
150 return FileSource::init (pathstr
, must_exist
);
154 AudioFileSource::peak_path (string audio_path
)
158 base
= PBD::basename_nosuffix (audio_path
);
160 base
+= (char) ('A' + _channel
);
162 return _session
.peak_path (base
);
166 AudioFileSource::find_broken_peakfile (string peak_path
, string audio_path
)
170 /* check for the broken location in use by 2.0 for several months */
172 str
= broken_peak_path (audio_path
);
174 if (Glib::file_test (str
, Glib::FILE_TEST_EXISTS
)) {
176 if (!within_session()) {
178 /* it would be nice to rename it but the nature of
179 the bug means that we can't reliably use it.
185 /* all native files are mono, so we can just rename
188 ::rename (str
.c_str(), peak_path
.c_str());
192 /* Nasty band-aid for older sessions that were created before we
193 used libsndfile for all audio files.
197 str
= old_peak_path (audio_path
);
198 if (Glib::file_test (str
, Glib::FILE_TEST_EXISTS
)) {
207 AudioFileSource::broken_peak_path (string audio_path
)
209 return _session
.peak_path (basename_nosuffix (audio_path
));
213 AudioFileSource::old_peak_path (string audio_path
)
215 /* XXX hardly bombproof! fix me */
217 struct stat stat_file
;
218 struct stat stat_mount
;
220 string mp
= mountpoint (audio_path
);
222 stat (audio_path
.c_str(), &stat_file
);
223 stat (mp
.c_str(), &stat_mount
);
227 snprintf (buf
, sizeof (buf
), "%u-%u-%d.peak", stat_mount
.st_ino
, stat_file
.st_ino
, _channel
);
229 snprintf (buf
, sizeof (buf
), "%" PRId64
"-%" PRId64
"-%d.peak", (int64_t) stat_mount
.st_ino
, (int64_t) stat_file
.st_ino
, _channel
);
232 string res
= peak_dir
;
234 res
+= peakfile_suffix
;
240 AudioFileSource::get_soundfile_info (string path
, SoundFileInfo
& _info
, string
& error_msg
)
242 /* try sndfile first because it gets timecode info from .wav (BWF) if it exists,
243 which at present, ExtAudioFile from Apple seems unable to do.
246 if (SndFileSource::get_soundfile_info (path
, _info
, error_msg
) != 0) {
250 #ifdef HAVE_COREAUDIO
251 if (CoreAudioSource::get_soundfile_info (path
, _info
, error_msg
) == 0) {
254 #endif // HAVE_COREAUDIO
260 AudioFileSource::get_state ()
262 XMLNode
& root (AudioSource::get_state());
264 snprintf (buf
, sizeof (buf
), "%u", _channel
);
265 root
.add_property (X_("channel"), buf
);
266 root
.add_property (X_("origin"), _origin
);
271 AudioFileSource::set_state (const XMLNode
& node
, int version
)
273 if (Source::set_state (node
, version
)) {
277 if (AudioSource::set_state (node
, version
)) {
281 if (FileSource::set_state (node
, version
)) {
289 AudioFileSource::mark_streaming_write_completed ()
295 AudioSource::mark_streaming_write_completed ();
299 AudioFileSource::move_dependents_to_trash()
301 return ::unlink (peakpath
.c_str());
305 AudioFileSource::set_header_position_offset (framecnt_t offset
)
307 header_position_offset
= offset
;
308 HeaderPositionOffsetChanged ();
312 AudioFileSource::is_empty (Session
& /*s*/, string path
)
317 if (!get_soundfile_info (path
, info
, err
)) {
318 /* dangerous: we can't get info, so assume that its not empty */
322 return info
.length
== 0;
326 AudioFileSource::setup_peakfile ()
328 if (!(_flags
& NoPeakFile
)) {
329 return initialize_peakfile (_file_is_new
, _path
);
336 AudioFileSource::safe_audio_file_extension(const string
& file
)
338 const char* suffixes
[] = {
362 #ifdef HAVE_COREAUDIO
374 ".sd2", ".SD2", // libsndfile supports sd2 also, but the resource fork is required to open.
375 #endif // HAVE_COREAUDIO
378 for (size_t n
= 0; n
< sizeof(suffixes
)/sizeof(suffixes
[0]); ++n
) {
379 if (file
.rfind (suffixes
[n
]) == file
.length() - strlen (suffixes
[n
])) {
388 AudioFileSource::get_interleave_buffer (framecnt_t size
)
390 SizedSampleBuffer
* ssb
;
392 if ((ssb
= thread_interleave_buffer
.get()) == 0) {
393 ssb
= new SizedSampleBuffer (size
);
394 thread_interleave_buffer
.set (ssb
);
397 if (ssb
->size
< size
) {
398 ssb
= new SizedSampleBuffer (size
);
399 thread_interleave_buffer
.set (ssb
);