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/sndfile_helpers.h"
49 #include "ardour/sndfilesource.h"
50 #include "ardour/session.h"
51 #include "ardour/session_directory.h"
52 #include "ardour/source_factory.h"
53 #include "ardour/filename_extensions.h"
55 // if these headers come before sigc++ is included
56 // the parser throws ObjC++ errors. (nil is a keyword)
58 #include "ardour/coreaudiosource.h"
59 #include <AudioToolbox/ExtendedAudioFile.h>
60 #include <AudioToolbox/AudioFormat.h>
61 #endif // HAVE_COREAUDIO
66 using namespace ARDOUR
;
70 ustring
AudioFileSource::peak_dir
= "";
72 sigc::signal
<void> AudioFileSource::HeaderPositionOffsetChanged
;
73 uint64_t AudioFileSource::header_position_offset
= 0;
75 /* XXX maybe this too */
76 char AudioFileSource::bwf_serial_number
[13] = "000000000000";
78 struct SizedSampleBuffer
{
82 SizedSampleBuffer (nframes_t sz
) : size (sz
) {
83 buf
= new Sample
[size
];
86 ~SizedSampleBuffer() {
91 Glib::StaticPrivate
<SizedSampleBuffer
> thread_interleave_buffer
= GLIBMM_STATIC_PRIVATE_INIT
;
93 /** Constructor used for existing internal-to-session files. */
94 AudioFileSource::AudioFileSource (Session
& s
, const ustring
& path
, bool embedded
, Source::Flag flags
)
95 : Source (s
, DataType::AUDIO
, path
, flags
)
96 , AudioSource (s
, path
)
97 , FileSource (s
, DataType::AUDIO
, path
, embedded
, flags
)
99 if (init (path
, true)) {
100 throw failed_constructor ();
104 /** Constructor used for new internal-to-session files. */
105 AudioFileSource::AudioFileSource (Session
& s
, const ustring
& path
, bool embedded
, Source::Flag flags
,
106 SampleFormat
/*samp_format*/, HeaderFormat
/*hdr_format*/)
107 : Source (s
, DataType::AUDIO
, path
, flags
)
108 , AudioSource (s
, path
)
109 , FileSource (s
, DataType::AUDIO
, path
, embedded
, flags
)
111 _is_embedded
= false;
113 if (init (path
, false)) {
114 throw failed_constructor ();
118 /** Constructor used for existing internal-to-session files. File must exist. */
119 AudioFileSource::AudioFileSource (Session
& s
, const XMLNode
& node
, bool must_exist
)
121 , AudioSource (s
, node
)
122 , FileSource (s
, node
, must_exist
)
124 if (set_state (node
, Stateful::loading_state_version
)) {
125 throw failed_constructor ();
128 if (init (_name
, must_exist
)) {
129 throw failed_constructor ();
133 AudioFileSource::~AudioFileSource ()
136 unlink (_path
.c_str());
137 unlink (peakpath
.c_str());
142 AudioFileSource::init (const ustring
& pathstr
, bool must_exist
)
144 _peaks_built
= false;
145 return FileSource::init (pathstr
, must_exist
);
149 AudioFileSource::peak_path (ustring audio_path
)
153 base
= PBD::basename_nosuffix (audio_path
);
155 base
+= (char) ('A' + _channel
);
157 return _session
.peak_path (base
);
161 AudioFileSource::find_broken_peakfile (ustring peak_path
, ustring audio_path
)
165 /* check for the broken location in use by 2.0 for several months */
167 str
= broken_peak_path (audio_path
);
169 if (Glib::file_test (str
, Glib::FILE_TEST_EXISTS
)) {
173 /* it would be nice to rename it but the nature of
174 the bug means that we can't reliably use it.
180 /* all native files are mono, so we can just rename
183 ::rename (str
.c_str(), peak_path
.c_str());
187 /* Nasty band-aid for older sessions that were created before we
188 used libsndfile for all audio files.
192 str
= old_peak_path (audio_path
);
193 if (Glib::file_test (str
, Glib::FILE_TEST_EXISTS
)) {
202 AudioFileSource::broken_peak_path (ustring audio_path
)
204 return _session
.peak_path (audio_path
);
208 AudioFileSource::old_peak_path (ustring audio_path
)
210 /* XXX hardly bombproof! fix me */
212 struct stat stat_file
;
213 struct stat stat_mount
;
215 ustring mp
= mountpoint (audio_path
);
217 stat (audio_path
.c_str(), &stat_file
);
218 stat (mp
.c_str(), &stat_mount
);
222 snprintf (buf
, sizeof (buf
), "%u-%u-%d.peak", stat_mount
.st_ino
, stat_file
.st_ino
, _channel
);
224 snprintf (buf
, sizeof (buf
), "%ld-%ld-%d.peak", stat_mount
.st_ino
, stat_file
.st_ino
, _channel
);
227 ustring res
= peak_dir
;
229 res
+= peakfile_suffix
;
235 AudioFileSource::get_soundfile_info (ustring path
, SoundFileInfo
& _info
, string
& error_msg
)
237 #ifdef HAVE_COREAUDIO
238 if (CoreAudioSource::get_soundfile_info (path
, _info
, error_msg
) == 0) {
241 #endif // HAVE_COREAUDIO
243 if (SndFileSource::get_soundfile_info (path
, _info
, error_msg
) != 0) {
251 AudioFileSource::get_state ()
253 XMLNode
& root (AudioSource::get_state());
255 snprintf (buf
, sizeof (buf
), "%u", _channel
);
256 root
.add_property (X_("channel"), buf
);
261 AudioFileSource::set_state (const XMLNode
& node
, int version
)
263 if (Source::set_state (node
, version
)) {
267 if (AudioSource::set_state (node
, version
)) {
271 if (FileSource::set_state (node
, version
)) {
279 AudioFileSource::mark_streaming_write_completed ()
285 /* XXX notice that we're readers of _peaks_built
286 but we must hold a solid lock on PeaksReady.
289 Glib::Mutex::Lock
lm (_lock
);
292 PeaksReady (); /* EMIT SIGNAL */
297 AudioFileSource::move_dependents_to_trash()
299 return ::unlink (peakpath
.c_str());
303 AudioFileSource::set_header_position_offset (nframes_t offset
)
305 header_position_offset
= offset
;
306 HeaderPositionOffsetChanged ();
310 AudioFileSource::is_empty (Session
& /*s*/, ustring path
)
315 if (!get_soundfile_info (path
, info
, err
)) {
316 /* dangerous: we can't get info, so assume that its not empty */
320 return info
.length
== 0;
324 AudioFileSource::setup_peakfile ()
326 if (!(_flags
& NoPeakFile
)) {
327 return initialize_peakfile (_file_is_new
, _path
);
334 AudioFileSource::safe_audio_file_extension(const ustring
& file
)
336 const char* suffixes
[] = {
354 #ifdef HAVE_COREAUDIO
358 #endif // HAVE_COREAUDIO
361 for (size_t n
= 0; n
< sizeof(suffixes
)/sizeof(suffixes
[0]); ++n
) {
362 if (file
.rfind (suffixes
[n
]) == file
.length() - strlen (suffixes
[n
])) {
371 AudioFileSource::get_interleave_buffer (nframes_t size
)
373 SizedSampleBuffer
* ssb
;
375 if ((ssb
= thread_interleave_buffer
.get()) == 0) {
376 ssb
= new SizedSampleBuffer (size
);
377 thread_interleave_buffer
.set (ssb
);
380 if (ssb
->size
< size
) {
381 ssb
= new SizedSampleBuffer (size
);
382 thread_interleave_buffer
.set (ssb
);