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