Don't strip suffixes twice from peakfile names, otherwise source names like fred...
[ardour.git] / libs / ardour / audiofilesource.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
23
24 #include <vector>
25
26 #include <sys/time.h>
27 #include <sys/stat.h>
28 #include <stdio.h> // for rename(), sigh
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32
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"
40
41 #include <sndfile.h>
42
43 #include <glibmm/miscutils.h>
44 #include <glibmm/fileutils.h>
45 #include <glibmm/thread.h>
46
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"
55
56 // if these headers come before sigc++ is included
57 // the parser throws ObjC++ errors. (nil is a keyword)
58 #ifdef HAVE_COREAUDIO
59 #include "ardour/coreaudiosource.h"
60 #include <AudioToolbox/ExtendedAudioFile.h>
61 #include <AudioToolbox/AudioFormat.h>
62 #endif // HAVE_COREAUDIO
63
64 #include "i18n.h"
65
66 using namespace std;
67 using namespace ARDOUR;
68 using namespace PBD;
69 using namespace Glib;
70
71 string AudioFileSource::peak_dir = "";
72
73 PBD::Signal0<void> AudioFileSource::HeaderPositionOffsetChanged;
74 uint64_t           AudioFileSource::header_position_offset = 0;
75
76 /* XXX maybe this too */
77 char AudioFileSource::bwf_serial_number[13] = "000000000000";
78
79 struct SizedSampleBuffer {
80     nframes_t size;
81     Sample* buf;
82
83     SizedSampleBuffer (nframes_t sz) : size (sz) {
84             buf = new Sample[size];
85     }
86
87     ~SizedSampleBuffer() {
88             delete [] buf;
89     }
90 };
91
92 Glib::StaticPrivate<SizedSampleBuffer> thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT;
93
94 /** Constructor used for existing internal-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         , FileSource (s, DataType::AUDIO, path, flags)
99 {
100         if (init (_path, true)) {
101                 throw failed_constructor ();
102         }
103
104 }
105
106 /** Constructor used for new internal-to-session files. */
107 AudioFileSource::AudioFileSource (Session& s, const string& path, Source::Flag flags,
108                                   SampleFormat /*samp_format*/, HeaderFormat /*hdr_format*/)
109         : Source (s, DataType::AUDIO, path, flags)
110         , AudioSource (s, path)
111         , FileSource (s, DataType::AUDIO, path, flags)
112 {
113         if (init (_path, false)) {
114                 throw failed_constructor ();
115         }
116 }
117
118 /** Constructor used for existing internal-to-session files via XML.  File must exist. */
119 AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
120         : Source (s, node)
121         , AudioSource (s, node)
122         , FileSource (s, node, must_exist)
123 {
124         if (set_state (node, Stateful::loading_state_version)) {
125                 throw failed_constructor ();
126         }
127         
128         if (init (_path, must_exist)) {
129                 throw failed_constructor ();
130         }
131 }
132
133 AudioFileSource::~AudioFileSource ()
134 {
135         DEBUG_TRACE (DEBUG::Destruction, string_compose ("AudioFileSource destructor %1, removable? %2\n", _path, removable()));
136         if (removable()) {
137                 unlink (_path.c_str());
138                 unlink (peakpath.c_str());
139         }
140 }
141
142 int
143 AudioFileSource::init (const string& pathstr, bool must_exist)
144 {
145         return FileSource::init (pathstr, must_exist);
146 }
147
148 string
149 AudioFileSource::peak_path (string audio_path)
150 {
151         string base;
152
153         base = PBD::basename_nosuffix (audio_path);
154         base += '%';
155         base += (char) ('A' + _channel);
156
157         return _session.peak_path (base);
158 }
159
160 string
161 AudioFileSource::find_broken_peakfile (string peak_path, string audio_path)
162 {
163         string str;
164
165         /* check for the broken location in use by 2.0 for several months */
166
167         str = broken_peak_path (audio_path);
168
169         if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
170
171                 if (!within_session()) {
172
173                         /* it would be nice to rename it but the nature of
174                            the bug means that we can't reliably use it.
175                         */
176
177                         peak_path = str;
178
179                 } else {
180                         /* all native files are mono, so we can just rename
181                            it.
182                         */
183                         ::rename (str.c_str(), peak_path.c_str());
184                 }
185
186         } else {
187                 /* Nasty band-aid for older sessions that were created before we
188                    used libsndfile for all audio files.
189                 */
190
191
192                 str = old_peak_path (audio_path);
193                 if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
194                         peak_path = str;
195                 }
196         }
197
198         return peak_path;
199 }
200
201 string
202 AudioFileSource::broken_peak_path (string audio_path)
203 {
204         return _session.peak_path (basename_nosuffix (audio_path));
205 }
206
207 string
208 AudioFileSource::old_peak_path (string audio_path)
209 {
210         /* XXX hardly bombproof! fix me */
211
212         struct stat stat_file;
213         struct stat stat_mount;
214
215         string mp = mountpoint (audio_path);
216
217         stat (audio_path.c_str(), &stat_file);
218         stat (mp.c_str(), &stat_mount);
219
220         char buf[32];
221 #ifdef __APPLE__
222         snprintf (buf, sizeof (buf), "%u-%u-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel);
223 #else
224         snprintf (buf, sizeof (buf), "%" PRId64 "-%" PRId64 "-%d.peak", (int64_t) stat_mount.st_ino, (int64_t) stat_file.st_ino, _channel);
225 #endif
226
227         string res = peak_dir;
228         res += buf;
229         res += peakfile_suffix;
230
231         return res;
232 }
233
234 bool
235 AudioFileSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg)
236 {
237         /* try sndfile first because it gets timecode info from .wav (BWF) if it exists,
238            which at present, ExtAudioFile from Apple seems unable to do.
239         */
240
241         if (SndFileSource::get_soundfile_info (path, _info, error_msg) != 0) {
242                 return true;
243         }
244         
245 #ifdef HAVE_COREAUDIO
246         if (CoreAudioSource::get_soundfile_info (path, _info, error_msg) == 0) {
247                 return true;
248         }
249 #endif // HAVE_COREAUDIO
250
251         return false;
252 }
253
254 XMLNode&
255 AudioFileSource::get_state ()
256 {
257         XMLNode& root (AudioSource::get_state());
258         char buf[32];
259         snprintf (buf, sizeof (buf), "%u", _channel);
260         root.add_property (X_("channel"), buf);
261         return root;
262 }
263
264 int
265 AudioFileSource::set_state (const XMLNode& node, int version)
266 {
267         if (Source::set_state (node, version)) {
268                 return -1;
269         }
270
271         if (AudioSource::set_state (node, version)) {
272                 return -1;
273         }
274
275         if (FileSource::set_state (node, version)) {
276                 return -1;
277         }
278
279         return 0;
280 }
281
282 void
283 AudioFileSource::mark_streaming_write_completed ()
284 {
285         if (!writable()) {
286                 return;
287         }
288
289         AudioSource::mark_streaming_write_completed ();
290 }
291
292 int
293 AudioFileSource::move_dependents_to_trash()
294 {
295         return ::unlink (peakpath.c_str());
296 }
297
298 void
299 AudioFileSource::set_header_position_offset (nframes_t offset)
300 {
301         header_position_offset = offset;
302         HeaderPositionOffsetChanged ();
303 }
304
305 bool
306 AudioFileSource::is_empty (Session& /*s*/, string path)
307 {
308         SoundFileInfo info;
309         string err;
310
311         if (!get_soundfile_info (path, info, err)) {
312                 /* dangerous: we can't get info, so assume that its not empty */
313                 return false;
314         }
315
316         return info.length == 0;
317 }
318
319 int
320 AudioFileSource::setup_peakfile ()
321 {
322         if (!(_flags & NoPeakFile)) {
323                 return initialize_peakfile (_file_is_new, _path);
324         } else {
325                 return 0;
326         }
327 }
328
329 bool
330 AudioFileSource::safe_audio_file_extension(const string& file)
331 {
332         const char* suffixes[] = {
333                 ".aif", ".AIF",
334                 ".aifc", ".AIFC",
335                 ".aiff", ".AIFF",
336                 ".amb", ".AMB",
337                 ".au", ".AU",
338                 ".caf", ".CAF",
339                 ".cdr", ".CDR",
340                 ".flac", ".FLAC",
341                 ".htk", ".HTK",
342                 ".iff", ".IFF",
343                 ".mat", ".MAT",
344                 ".oga", ".OGA",
345                 ".ogg", ".OGG",
346                 ".paf", ".PAF",
347                 ".pvf", ".PVF",
348                 ".sf", ".SF",
349                 ".smp", ".SMP",
350                 ".snd", ".SND",
351                 ".maud", ".MAUD",
352                 ".voc", ".VOC"
353                 ".vwe", ".VWE",
354                 ".w64", ".W64",
355                 ".wav", ".WAV",
356 #ifdef HAVE_COREAUDIO
357                 ".aac", ".AAC",
358                 ".adts", ".ADTS",
359                 ".ac3", ".AC3",
360                 ".amr", ".AMR",
361                 ".mpa", ".MPA",
362                 ".mpeg", ".MPEG",
363                 ".mp1", ".MP1",
364                 ".mp2", ".MP2",
365                 ".mp3", ".MP3",
366                 ".mp4", ".MP4",
367                 ".m4a", ".M4A",
368                 ".sd2", ".SD2",         // libsndfile supports sd2 also, but the resource fork is required to open.
369 #endif // HAVE_COREAUDIO
370         };
371
372         for (size_t n = 0; n < sizeof(suffixes)/sizeof(suffixes[0]); ++n) {
373                 if (file.rfind (suffixes[n]) == file.length() - strlen (suffixes[n])) {
374                         return true;
375                 }
376         }
377
378         return false;
379 }
380
381 Sample*
382 AudioFileSource::get_interleave_buffer (nframes_t size)
383 {
384         SizedSampleBuffer* ssb;
385
386         if ((ssb = thread_interleave_buffer.get()) == 0) {
387                 ssb = new SizedSampleBuffer (size);
388                 thread_interleave_buffer.set (ssb);
389         }
390
391         if (ssb->size < size) {
392                 ssb = new SizedSampleBuffer (size);
393                 thread_interleave_buffer.set (ssb);
394         }
395
396         return ssb->buf;
397 }