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