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