move all destructive functionality into SndFileSource as a mode, and drop Destructive...
[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 #include <vector>
21
22 #include <sys/time.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <errno.h>
26
27 #include <pbd/mountpoint.h>
28 #include <pbd/pathscanner.h>
29 #include <pbd/stl_delete.h>
30 #include <pbd/strsplit.h>
31
32 #include <sndfile.h>
33
34 #include <glibmm/miscutils.h>
35
36 #include <ardour/audiofilesource.h>
37 #include <ardour/sndfile_helpers.h>
38 #include <ardour/sndfilesource.h>
39 #include <ardour/destructive_filesource.h>
40 #include <ardour/session.h>
41 #include <ardour/source_factory.h>
42
43 // if these headers come before sigc++ is included
44 // the parser throws ObjC++ errors. (nil is a keyword)
45 #ifdef HAVE_COREAUDIO 
46 #include <ardour/coreaudiosource.h>
47 #include <AudioToolbox/ExtendedAudioFile.h>
48 #include <AudioToolbox/AudioFormat.h>
49 #endif // HAVE_COREAUDIO
50
51 #include "i18n.h"
52
53 using namespace ARDOUR;
54 using namespace PBD;
55
56 string AudioFileSource::peak_dir = "";
57 string AudioFileSource::search_path;
58
59 sigc::signal<void> AudioFileSource::HeaderPositionOffsetChanged;
60 uint64_t           AudioFileSource::header_position_offset = 0;
61
62 /* XXX maybe this too */
63 char   AudioFileSource::bwf_serial_number[13] = "000000000000";
64
65 AudioFileSource::AudioFileSource (Session& s, string idstr, Flag flags)
66         : AudioSource (s, idstr), _flags (flags)
67 {
68         /* constructor used for existing external to session files. file must exist already */
69         _is_embedded = AudioFileSource::determine_embeddedness (idstr);
70
71         if (init (idstr, true)) {
72                 throw failed_constructor ();
73         }
74
75 }
76
77 AudioFileSource::AudioFileSource (Session& s, std::string path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format)
78         : AudioSource (s, path), _flags (flags)
79 {
80         /* constructor used for new internal-to-session files. file cannot exist */
81         _is_embedded = false;
82
83         if (init (path, false)) {
84                 throw failed_constructor ();
85         }
86 }
87
88 AudioFileSource::AudioFileSource (Session& s, const XMLNode& node)
89         : AudioSource (s, node), _flags (Flag (Writable|CanRename))
90 {
91         /* constructor used for existing internal-to-session files. file must exist */
92
93         if (set_state (node)) {
94                 throw failed_constructor ();
95         }
96         
97         if (init (_name, true)) {
98                 throw failed_constructor ();
99         }
100 }
101
102 AudioFileSource::~AudioFileSource ()
103 {
104         if (removable()) {
105                 unlink (_path.c_str());
106                 unlink (peakpath.c_str());
107         }
108 }
109
110 bool
111 AudioFileSource::determine_embeddedness (std::string path)
112 {
113         return (path.find("/") == 0);
114 }
115
116 bool
117 AudioFileSource::removable () const
118 {
119         return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0));
120 }
121
122 int
123 AudioFileSource::init (string pathstr, bool must_exist)
124 {
125         bool is_new = false;
126
127         _length = 0;
128         timeline_position = 0;
129         next_peak_clear_should_notify = false;
130         _peaks_built = false;
131         file_is_new = false;
132
133         if (!find (pathstr, must_exist, is_new)) {
134                 return -1;
135         }
136
137         if (is_new && must_exist) {
138                 return -1;
139         }
140
141         return 0;
142 }
143
144
145 string
146 AudioFileSource::peak_path (string audio_path)
147 {
148         return _session.peak_path_from_audio_path (audio_path);
149 }
150
151 string
152 AudioFileSource::old_peak_path (string audio_path)
153 {
154         /* XXX hardly bombproof! fix me */
155
156         struct stat stat_file;
157         struct stat stat_mount;
158
159         string mp = mountpoint (audio_path);
160
161         stat (audio_path.c_str(), &stat_file);
162         stat (mp.c_str(), &stat_mount);
163
164         char buf[32];
165 #ifdef __APPLE__
166         snprintf (buf, sizeof (buf), "%u-%u-%d.peak", stat_mount.st_ino, stat_file.st_ino, channel);
167 #else
168         snprintf (buf, sizeof (buf), "%ld-%ld-%d.peak", stat_mount.st_ino, stat_file.st_ino, channel);
169 #endif
170
171         string res = peak_dir;
172         res += buf;
173
174         return res;
175 }
176
177 bool
178 AudioFileSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg)
179 {
180 #ifdef HAVE_COREAUDIO
181         OSStatus err = noErr;
182     FSRef ref; 
183         ExtAudioFileRef af = 0;
184         size_t size;
185     CFStringRef name;
186
187     err = FSPathMakeRef ((UInt8*)path.c_str(), &ref, 0);
188         if (err != noErr) {
189         ExtAudioFileDispose (af);
190                 goto libsndfile;
191         }
192
193         err = ExtAudioFileOpen(&ref, &af);
194         if (err != noErr) {
195         ExtAudioFileDispose (af);
196                 goto libsndfile;
197         }
198
199         AudioStreamBasicDescription absd;
200         memset(&absd, 0, sizeof(absd));
201         size = sizeof(AudioStreamBasicDescription);
202         err = ExtAudioFileGetProperty(af,
203                         kExtAudioFileProperty_FileDataFormat, &size, &absd);
204         if (err != noErr) {
205         ExtAudioFileDispose (af);
206                 goto libsndfile;
207         }
208
209         _info.samplerate = absd.mSampleRate;
210         _info.channels   = absd.mChannelsPerFrame;
211
212     size = sizeof(_info.length);
213     err = ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &size, &_info.length);
214     if (err != noErr) {
215         ExtAudioFileDispose (af);
216                 goto libsndfile;
217     }
218
219         size = sizeof(CFStringRef);
220         err = AudioFormatGetProperty(
221                         kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name);
222         if (err != noErr) {
223         ExtAudioFileDispose (af);
224                 goto libsndfile;
225         }
226
227         _info.format_name = CFStringRefToStdString(name);
228
229     ExtAudioFileDispose (af);
230         return true;
231         
232 libsndfile:
233 #endif // HAVE_COREAUDIO
234
235         SNDFILE *sf;
236         SF_INFO sf_info;
237
238         sf_info.format = 0; // libsndfile says to clear this before sf_open().
239
240         if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) { 
241                 char errbuf[256];
242                 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
243                 return false;
244         }
245
246         sf_close (sf);
247
248         _info.samplerate  = sf_info.samplerate;
249         _info.channels    = sf_info.channels;
250         _info.length      = sf_info.frames;
251         _info.format_name = string_compose("Format: %1, %2",
252                                            sndfile_major_format(sf_info.format),
253                                            sndfile_minor_format(sf_info.format));
254         return true;
255 }
256
257 XMLNode&
258 AudioFileSource::get_state ()
259 {
260         XMLNode& root (AudioSource::get_state());
261         char buf[16];
262         snprintf (buf, sizeof (buf), "0x%x", (int)_flags);
263         root.add_property ("flags", buf);
264         return root;
265 }
266
267 int
268 AudioFileSource::set_state (const XMLNode& node)
269 {
270         const XMLProperty* prop;
271
272         if (AudioSource::set_state (node)) {
273                 return -1;
274         }
275
276         if ((prop = node.property (X_("flags"))) != 0) {
277
278                 int ival;
279                 sscanf (prop->value().c_str(), "0x%x", &ival);
280                 _flags = Flag (ival);
281
282         } else {
283
284                 _flags = Flag (0);
285
286         }
287
288         if ((prop = node.property (X_("name"))) != 0) {
289                 _is_embedded = AudioFileSource::determine_embeddedness (prop->value());
290         } else {
291                 _is_embedded = false;
292         }
293
294         if ((prop = node.property (X_("destructive"))) != 0) {
295                 /* old style, from the period when we had DestructiveFileSource */
296                 _flags = Flag (_flags | Destructive);
297         }
298
299         return 0;
300 }
301
302 void
303 AudioFileSource::mark_for_remove ()
304 {
305         if (!writable()) {
306                 return;
307         }
308
309         _flags = Flag (_flags | Removable | RemoveAtDestroy);
310 }
311
312 void
313 AudioFileSource::mark_streaming_write_completed ()
314 {
315         if (!writable()) {
316                 return;
317         }
318
319         Glib::Mutex::Lock lm (_lock);
320
321         next_peak_clear_should_notify = true;
322
323         if (_peaks_built || pending_peak_builds.empty()) {
324                 _peaks_built = true;
325                  PeaksReady (); /* EMIT SIGNAL */
326         }
327 }
328
329 void
330 AudioFileSource::mark_take (string id)
331 {
332         if (writable()) {
333                 _take_id = id;
334         }
335 }
336
337 int
338 AudioFileSource::move_to_trash (const string trash_dir_name)
339 {
340         if (is_embedded()) {
341                 cerr << "tried to move an embedded region to trash" << endl;
342                 return -1;
343         }
344
345         string newpath;
346
347         if (!writable()) {
348                 return -1;
349         }
350
351         /* don't move the file across filesystems, just
352            stick it in the `trash_dir_name' directory
353            on whichever filesystem it was already on.
354         */
355
356         newpath = Glib::path_get_dirname (_path);
357         newpath = Glib::path_get_dirname (newpath);
358
359         newpath += '/';
360         newpath += trash_dir_name;
361         newpath += '/';
362         newpath += Glib::path_get_basename (_path);
363
364         if (access (newpath.c_str(), F_OK) == 0) {
365
366                 /* the new path already exists, try versioning */
367                 
368                 char buf[PATH_MAX+1];
369                 int version = 1;
370                 string newpath_v;
371
372                 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
373                 newpath_v = buf;
374
375                 while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
376                         snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
377                         newpath_v = buf;
378                 }
379                 
380                 if (version == 999) {
381                         error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
382                                           newpath)
383                               << endmsg;
384                 } else {
385                         newpath = newpath_v;
386                 }
387
388         } else {
389
390                 /* it doesn't exist, or we can't read it or something */
391
392         }
393
394         if (::rename (_path.c_str(), newpath.c_str()) != 0) {
395                 error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
396                                   _path, newpath, strerror (errno))
397                       << endmsg;
398                 return -1;
399         }
400
401         if (::unlink (peakpath.c_str()) != 0) {
402                 error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
403                                   peakpath, _path, strerror (errno))
404                       << endmsg;
405                 /* try to back out */
406                 rename (newpath.c_str(), _path.c_str());
407                 return -1;
408         }
409             
410         _path = newpath;
411         peakpath = "";
412         
413         /* file can not be removed twice, since the operation is not idempotent */
414
415         _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
416
417         return 0;
418 }
419
420 bool
421 AudioFileSource::find (string pathstr, bool must_exist, bool& isnew)
422 {
423         string::size_type pos;
424         bool ret = false;
425
426         isnew = false;
427
428         /* clean up PATH:CHANNEL notation so that we are looking for the correct path */
429
430         if ((pos = pathstr.find_last_of (':')) == string::npos) {
431                 pathstr = pathstr;
432         } else {
433                 pathstr = pathstr.substr (0, pos);
434         }
435
436         if (pathstr[0] != '/') {
437
438                 /* non-absolute pathname: find pathstr in search path */
439
440                 vector<string> dirs;
441                 int cnt;
442                 string fullpath;
443                 string keeppath;
444
445                 if (search_path.length() == 0) {
446                         error << _("FileSource: search path not set") << endmsg;
447                         goto out;
448                 }
449
450                 split (search_path, dirs, ':');
451
452                 cnt = 0;
453                 
454                 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
455
456                         fullpath = *i;
457                         if (fullpath[fullpath.length()-1] != '/') {
458                                 fullpath += '/';
459                         }
460                         fullpath += pathstr;
461                         
462                         if (access (fullpath.c_str(), R_OK) == 0) {
463                                 keeppath = fullpath;
464                                 ++cnt;
465                         } 
466                 }
467
468                 if (cnt > 1) {
469
470                         error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, search_path) << endmsg;
471                         goto out;
472
473                 } else if (cnt == 0) {
474
475                         if (must_exist) {
476                                 error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, search_path) << endmsg;
477                                 goto out;
478                         } else {
479                                 isnew = true;
480                         }
481                 }
482                 
483                 _name = pathstr;
484                 _path = keeppath;
485                 ret = true;
486
487         } else {
488                 
489                 /* external files and/or very very old style sessions include full paths */
490                 
491                 _path = pathstr;
492                 if (is_embedded()) {
493                         _name = pathstr;
494                 } else {
495                         _name = pathstr.substr (pathstr.find_last_of ('/') + 1);
496                 }
497                 
498                 if (access (_path.c_str(), R_OK) != 0) {
499
500                         /* file does not exist or we cannot read it */
501
502                         if (must_exist) {
503                                 error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
504                                 goto out;
505                         }
506                         
507                         if (errno != ENOENT) {
508                                 error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
509                                 goto out;
510                         }
511                         
512                         /* a new file */
513
514                         isnew = true;
515                         ret = true;
516
517                 } else {
518                         
519                         /* already exists */
520
521                         ret = true;
522                 }
523         }
524         
525   out:
526         return ret;
527 }
528
529 void
530 AudioFileSource::set_search_path (string p)
531 {
532         search_path = p;
533 }
534
535 void
536 AudioFileSource::set_header_position_offset (nframes_t offset)
537 {
538         header_position_offset = offset;
539         HeaderPositionOffsetChanged ();
540 }
541
542 void
543 AudioFileSource::set_timeline_position (nframes_t pos)
544 {
545         timeline_position = pos;
546 }
547
548 void
549 AudioFileSource::set_allow_remove_if_empty (bool yn)
550 {
551         if (!writable()) {
552                 return;
553         }
554
555         if (yn) {
556                 _flags = Flag (_flags | RemovableIfEmpty);
557         } else {
558                 _flags = Flag (_flags & ~RemovableIfEmpty);
559         }
560 }
561
562 int
563 AudioFileSource::set_name (string newname, bool destructive)
564 {
565         Glib::Mutex::Lock lm (_lock);
566         string oldpath = _path;
567         string newpath = Session::change_audio_path_by_name (oldpath, _name, newname, destructive);
568
569         if (newpath.empty()) {
570                 error << string_compose (_("programming error: %1"), "cannot generate a changed audio path") << endmsg;
571                 return -1;
572         }
573
574         // Test whether newpath exists, if yes notify the user but continue. 
575         if (access(newpath.c_str(),F_OK) == 0) {
576                 error << _("Programming error! Ardour tried to rename a file over another file! It's safe to continue working, but please report this to the developers.") << endmsg;
577                 return -1;
578         }
579
580         if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
581                 error << string_compose (_("cannot rename audio file for %1 to %2"), _name, newpath) << endmsg;
582                 return -1;
583         }
584
585         _name = Glib::path_get_basename (newpath);
586         _path = newpath;
587
588         return rename_peakfile (peak_path (_path));
589 }
590
591 bool
592 AudioFileSource::is_empty (Session& s, string path)
593 {
594         bool ret = false;
595         boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (s, path, NoPeakFile, false));
596
597         if (afs) {
598                 ret = (afs->length() == 0);
599         }
600
601         return ret;
602 }
603
604 int
605 AudioFileSource::setup_peakfile ()
606 {
607         if (!(_flags & NoPeakFile)) {
608                 return initialize_peakfile (file_is_new, _path);
609         } else {
610                 return 0;
611         }
612 }