8df88a2732f5a9648dcb85e5e4fdcaa11c43a536
[ardour.git] / libs / ardour / sndfilesource.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 <cstring>
25 #include <cerrno>
26 #include <climits>
27 #include <cstdarg>
28 #include <fcntl.h>
29
30 #include <sys/stat.h>
31
32 #include <glib.h>
33 #include <glib/gstdio.h>
34
35 #include <glibmm/convert.h>
36 #include <glibmm/fileutils.h>
37 #include <glibmm/miscutils.h>
38
39 #include "ardour/sndfilesource.h"
40 #include "ardour/sndfile_helpers.h"
41 #include "ardour/utils.h"
42 #include "ardour/session.h"
43
44 #include "i18n.h"
45
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49 using std::string;
50
51 gain_t* SndFileSource::out_coefficient = 0;
52 gain_t* SndFileSource::in_coefficient = 0;
53 framecnt_t SndFileSource::xfade_frames = 64;
54 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
55                 Source::Writable |
56                 Source::Removable |
57                 Source::RemovableIfEmpty |
58                 Source::CanRename );
59
60 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
61         : Source(s, node)
62         , AudioFileSource (s, node)
63         , _sndfile (0)
64         , _broadcast_info (0)
65         , _capture_start (false)
66         , _capture_end (false)
67         , file_pos (0)
68         , xfade_buf (0)
69 {
70         init_sndfile ();
71
72         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
73         existence_check ();
74
75         if (open()) {
76                 throw failed_constructor ();
77         }
78 }
79
80 /** Constructor for existing external-to-session files.
81     Files created this way are never writable or removable 
82 */
83 SndFileSource::SndFileSource (Session& s, const string& path, int chn, Flag flags)
84         : Source(s, DataType::AUDIO, path, flags)
85           /* note that the origin of an external file is itself */
86         , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
87         , _sndfile (0)
88         , _broadcast_info (0)
89         , _capture_start (false)
90         , _capture_end (false)
91         , file_pos (0)
92         , xfade_buf (0)
93 {
94         _channel = chn;
95
96         init_sndfile ();
97
98         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
99         existence_check ();
100
101         if (open()) {
102                 throw failed_constructor ();
103         }
104 }
105
106 /** This constructor is used to construct new internal-to-session files, 
107     not open existing ones. 
108 */
109 SndFileSource::SndFileSource (Session& s, const string& path, const string& origin,
110                               SampleFormat sfmt, HeaderFormat hf, framecnt_t rate, Flag flags)
111         : Source(s, DataType::AUDIO, path, flags)
112         , AudioFileSource (s, path, origin, flags, sfmt, hf)
113         , _sndfile (0)
114         , _broadcast_info (0)
115         , _capture_start (false)
116         , _capture_end (false)
117         , file_pos (0)
118         , xfade_buf (0)
119 {
120         int fmt = 0;
121
122         init_sndfile ();
123
124         assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
125         existence_check ();
126
127         _file_is_new = true;
128
129         switch (hf) {
130         case CAF:
131                 fmt = SF_FORMAT_CAF;
132                 _flags = Flag (_flags & ~Broadcast);
133                 break;
134
135         case AIFF:
136                 fmt = SF_FORMAT_AIFF;
137                 _flags = Flag (_flags & ~Broadcast);
138                 break;
139
140         case BWF:
141                 fmt = SF_FORMAT_WAV;
142                 _flags = Flag (_flags | Broadcast);
143                 break;
144
145         case WAVE:
146                 fmt = SF_FORMAT_WAV;
147                 _flags = Flag (_flags & ~Broadcast);
148                 break;
149
150         case WAVE64:
151                 fmt = SF_FORMAT_W64;
152                 _flags = Flag (_flags & ~Broadcast);
153                 break;
154
155         default:
156                 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
157                 abort(); /*NOTREACHED*/
158                 break;
159
160         }
161
162         switch (sfmt) {
163         case FormatFloat:
164                 fmt |= SF_FORMAT_FLOAT;
165                 break;
166
167         case FormatInt24:
168                 fmt |= SF_FORMAT_PCM_24;
169                 break;
170
171         case FormatInt16:
172                 fmt |= SF_FORMAT_PCM_16;
173                 break;
174         }
175
176         _info.channels = 1;
177         _info.samplerate = rate;
178         _info.format = fmt;
179
180         if (_flags & Destructive) {
181                 if (open()) {
182                         throw failed_constructor();
183                 }
184         } else {
185                 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
186                  */
187         }
188 }
189
190 /** Constructor to be called for recovering files being used for
191  * capture. They are in-session, they already exist, they should not
192  * be writable. They are an odd hybrid (from a constructor point of
193  * view) of the previous two constructors.
194  */
195 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
196         : Source (s, DataType::AUDIO, path, Flag (0))
197           /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
198         , AudioFileSource (s, path, Flag (0))
199         , _sndfile (0)
200         , _broadcast_info (0)
201         , _capture_start (false)
202         , _capture_end (false)
203         , file_pos (0)
204         , xfade_buf (0)
205 {
206         _channel = chn;
207
208         init_sndfile ();
209
210         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
211         existence_check ();
212
213         if (open()) {
214                 throw failed_constructor ();
215         }
216 }
217
218 void
219 SndFileSource::init_sndfile ()
220 {
221         /* although libsndfile says we don't need to set this,
222            valgrind and source code shows us that we do.
223         */
224
225         memset (&_info, 0, sizeof(_info));
226
227         if (destructive()) {
228                 xfade_buf = new Sample[xfade_frames];
229                 _timeline_position = header_position_offset;
230         }
231
232         AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
233 }
234
235 void
236 SndFileSource::close ()
237 {
238         if (_sndfile) {
239                 sf_close (_sndfile);
240                 _sndfile = 0;
241         }
242 }
243
244 int
245 SndFileSource::open ()
246 {
247         if (_sndfile) {
248                 return 0;
249         }
250
251 // We really only want to use g_open for all platforms but because of this
252 // method(SndfileSource::open), the compiler(or at least GCC) is confused
253 // because g_open will expand to "open" on non-POSIX systems and needs the
254 // global namespace qualifer. The problem is since since C99 ::g_open will
255 // apparently expand to ":: open"
256 #ifdef PLATFORM_WINDOWS
257         int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
258 #else
259         int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
260 #endif
261
262         if (fd == -1) {
263                 error << string_compose (
264                              _ ("SndFileSource: cannot open file \"%1\" for %2"),
265                              _path,
266                              (writable () ? "read+write" : "reading")) << endmsg;
267                 return false;
268         }
269
270         _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
271
272         if (_sndfile == 0) {
273                 char errbuf[1024];
274                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
275 #ifndef HAVE_COREAUDIO
276                 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
277                    so we don't want to see this message.
278                 */
279
280                 cerr << "failed to open " << _path << " with name " << _name << endl;
281
282                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
283                                         _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
284 #endif
285                 return -1;
286         }
287
288         if (_channel >= _info.channels) {
289 #ifndef HAVE_COREAUDIO
290                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
291 #endif
292                 sf_close (_sndfile);
293                 _sndfile = 0;
294                 return -1;
295         }
296
297         _length = _info.frames;
298
299         if (!_broadcast_info) {
300                 _broadcast_info = new BroadcastInfo;
301         }
302
303         bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
304
305         if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
306                 /* newly created files will not have a BWF header at this point in time.
307                  * Import will have called Source::set_timeline_position() if one exists
308                  * in the original. */
309                 header_position_offset = _timeline_position;
310         }
311
312         /* Set our timeline position to either the time reference from a BWF header or the current
313            start of the session.
314         */
315         set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
316
317         if (_length != 0 && !bwf_info_exists) {
318                 delete _broadcast_info;
319                 _broadcast_info = 0;
320                 _flags = Flag (_flags & ~Broadcast);
321         } 
322
323         /* Set the broadcast flag if the BWF info is already there. We need
324          * this when recovering or using existing files.
325          */
326         
327         if (bwf_info_exists) {
328                 _flags = Flag (_flags | Broadcast);
329         }
330
331         if (writable()) {
332                 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
333
334                 if (_flags & Broadcast) {
335
336                         if (!_broadcast_info) {
337                                 _broadcast_info = new BroadcastInfo;
338                         }
339
340                         _broadcast_info->set_from_session (_session, header_position_offset);
341                         _broadcast_info->set_description (string_compose ("BWF %1", _name));
342
343                         if (!_broadcast_info->write_to_file (_sndfile)) {
344                                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
345                                                          _path, _broadcast_info->get_error())
346                                       << endmsg;
347                                 _flags = Flag (_flags & ~Broadcast);
348                                 delete _broadcast_info;
349                                 _broadcast_info = 0;
350                         }
351                 }
352         }
353         
354         return 0;
355 }
356
357 SndFileSource::~SndFileSource ()
358 {
359         close ();
360         delete _broadcast_info;
361         delete [] xfade_buf;
362 }
363
364 float
365 SndFileSource::sample_rate () const
366 {
367         return _info.samplerate;
368 }
369
370 framecnt_t
371 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
372 {
373         assert (cnt >= 0);
374         
375         framecnt_t nread;
376         float *ptr;
377         framecnt_t real_cnt;
378         framepos_t file_cnt;
379
380         if (writable() && !_sndfile) {
381                 /* file has not been opened yet - nothing written to it */
382                 memset (dst, 0, sizeof (Sample) * cnt);
383                 return cnt;
384         }
385
386         if (const_cast<SndFileSource*>(this)->open()) {
387                 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
388                 return 0;
389         }
390
391         if (start > _length) {
392
393                 /* read starts beyond end of data, just memset to zero */
394
395                 file_cnt = 0;
396
397         } else if (start + cnt > _length) {
398
399                 /* read ends beyond end of data, read some, memset the rest */
400
401                 file_cnt = _length - start;
402
403         } else {
404
405                 /* read is entirely within data */
406
407                 file_cnt = cnt;
408         }
409
410         assert (file_cnt >= 0);
411
412         if (file_cnt != cnt) {
413                 framepos_t delta = cnt - file_cnt;
414                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
415         }
416
417         if (file_cnt) {
418
419                 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
420                         char errbuf[256];
421                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
422                         error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
423                         return 0;
424                 }
425
426                 if (_info.channels == 1) {
427                         framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
428                         if (ret != file_cnt) {
429                                 char errbuf[256];
430                                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
431                                 error << string_compose(_("SndFileSource: @ %1 could not read %2 within %3 (%4) (len = %5, ret was %6)"), start, file_cnt, _name.val().substr (1), errbuf, _length, ret) << endl;
432                         }
433                         return ret;
434                 }
435         }
436
437         real_cnt = cnt * _info.channels;
438
439         Sample* interleave_buf = get_interleave_buffer (real_cnt);
440
441         nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
442         ptr = interleave_buf + _channel;
443         nread /= _info.channels;
444
445         /* stride through the interleaved data */
446
447         for (framecnt_t n = 0; n < nread; ++n) {
448                 dst[n] = *ptr;
449                 ptr += _info.channels;
450         }
451
452         return nread;
453 }
454
455 framecnt_t
456 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
457 {
458         if (open()) {
459                 return 0; // failure
460         }
461
462         if (destructive()) {
463                 return destructive_write_unlocked (data, cnt);
464         } else {
465                 return nondestructive_write_unlocked (data, cnt);
466         }
467 }
468
469 framecnt_t
470 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
471 {
472         if (!writable()) {
473                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
474                 return 0;
475         }
476
477         if (_info.channels != 1) {
478                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
479                 abort(); /*NOTREACHED*/
480                 return 0;
481         }
482
483         framepos_t frame_pos = _length;
484
485         if (write_float (data, frame_pos, cnt) != cnt) {
486                 return 0;
487         }
488
489         update_length (_length + cnt);
490
491         if (_build_peakfiles) {
492                 compute_and_write_peaks (data, frame_pos, cnt, true, true);
493         }
494
495         return cnt;
496 }
497
498 framecnt_t
499 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
500 {
501         if (!writable()) {
502                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
503                 return 0;
504         }
505
506         if (_capture_start && _capture_end) {
507
508                 /* start and end of capture both occur within the data we are writing,
509                    so do both crossfades.
510                 */
511
512                 _capture_start = false;
513                 _capture_end = false;
514
515                 /* move to the correct location place */
516                 file_pos = capture_start_frame - _timeline_position;
517
518                 // split cnt in half
519                 framecnt_t subcnt = cnt / 2;
520                 framecnt_t ofilepos = file_pos;
521
522                 // fade in
523                 if (crossfade (data, subcnt, 1) != subcnt) {
524                         return 0;
525                 }
526
527                 file_pos += subcnt;
528                 Sample * tmpdata = data + subcnt;
529
530                 // fade out
531                 subcnt = cnt - subcnt;
532                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
533                         return 0;
534                 }
535
536                 file_pos = ofilepos; // adjusted below
537
538         } else if (_capture_start) {
539
540                 /* start of capture both occur within the data we are writing,
541                    so do the fade in
542                 */
543
544                 _capture_start = false;
545                 _capture_end = false;
546
547                 /* move to the correct location place */
548                 file_pos = capture_start_frame - _timeline_position;
549
550                 if (crossfade (data, cnt, 1) != cnt) {
551                         return 0;
552                 }
553
554         } else if (_capture_end) {
555
556                 /* end of capture both occur within the data we are writing,
557                    so do the fade out
558                 */
559
560                 _capture_start = false;
561                 _capture_end = false;
562
563                 if (crossfade (data, cnt, 0) != cnt) {
564                         return 0;
565                 }
566
567         } else {
568
569                 /* in the middle of recording */
570
571                 if (write_float (data, file_pos, cnt) != cnt) {
572                         return 0;
573                 }
574         }
575
576         update_length (file_pos + cnt);
577
578         if (_build_peakfiles) {
579                 compute_and_write_peaks (data, file_pos, cnt, true, true);
580         }
581
582         file_pos += cnt;
583
584         return cnt;
585 }
586
587 int
588 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
589 {
590         set_timeline_position (when);
591
592         if (_flags & Broadcast) {
593                 if (setup_broadcast_info (when, now, tnow)) {
594                         return -1;
595                 }
596         }
597
598         return flush_header ();
599 }
600
601 int
602 SndFileSource::flush_header ()
603 {
604         if (!writable()) {
605                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
606                 return -1;
607         }
608
609         if (_sndfile == 0) {
610                 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
611                 return -1;
612         }
613
614         int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
615
616         return r;
617 }
618
619 void
620 SndFileSource::flush ()
621 {
622         if (!writable()) {
623                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
624                 return;
625         }
626
627         if (_sndfile == 0) {
628                 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
629                 return;
630         }
631
632         // Hopefully everything OK
633         sf_write_sync (_sndfile);
634 }
635
636 int
637 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
638 {
639         if (!writable()) {
640                 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
641                 return -1;
642         }
643
644         if (!_sndfile) {
645                 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
646                 return -1;
647         }
648
649         if (!(_flags & Broadcast) || !_broadcast_info) {
650                 return 0;
651         }
652
653         _broadcast_info->set_originator_ref_from_session (_session);
654         _broadcast_info->set_origination_time (&now);
655
656         /* now update header position taking header offset into account */
657
658         set_header_timeline_position ();
659
660         return 0;
661 }
662
663 void
664 SndFileSource::set_header_timeline_position ()
665 {
666         if (!(_flags & Broadcast)) {
667                 return;
668         }
669         assert (_broadcast_info);
670
671         _broadcast_info->set_time_reference (_timeline_position);
672
673         if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
674                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
675                                            _path, _broadcast_info->get_error())
676                       << endmsg;
677                 _flags = Flag (_flags & ~Broadcast);
678                 delete _broadcast_info;
679                 _broadcast_info = 0;
680         }
681 }
682
683 framecnt_t
684 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
685 {
686         if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
687                 char errbuf[256];
688                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
689                 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
690                 return 0;
691         }
692
693         if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
694                 return 0;
695         }
696
697         return cnt;
698 }
699
700 framepos_t
701 SndFileSource::natural_position() const
702 {
703         return _timeline_position;
704 }
705
706 bool
707 SndFileSource::set_destructive (bool yn)
708 {
709         if (yn) {
710                 _flags = Flag (_flags | Writable | Destructive);
711                 if (!xfade_buf) {
712                         xfade_buf = new Sample[xfade_frames];
713                 }
714                 clear_capture_marks ();
715                 _timeline_position = header_position_offset;
716         } else {
717                 _flags = Flag (_flags & ~Destructive);
718                 _timeline_position = 0;
719                 /* leave xfade buf alone in case we need it again later */
720         }
721
722         return true;
723 }
724
725 void
726 SndFileSource::clear_capture_marks ()
727 {
728         _capture_start = false;
729         _capture_end = false;
730 }
731
732 /** @param pos Capture start position in session frames */
733 void
734 SndFileSource::mark_capture_start (framepos_t pos)
735 {
736         if (destructive()) {
737                 if (pos < _timeline_position) {
738                         _capture_start = false;
739                 } else {
740                         _capture_start = true;
741                         capture_start_frame = pos;
742                 }
743         }
744 }
745
746 void
747 SndFileSource::mark_capture_end()
748 {
749         if (destructive()) {
750                 _capture_end = true;
751         }
752 }
753
754 framecnt_t
755 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
756 {
757         framecnt_t xfade = min (xfade_frames, cnt);
758         framecnt_t nofade = cnt - xfade;
759         Sample* fade_data = 0;
760         framepos_t fade_position = 0; // in frames
761         ssize_t retval;
762         framecnt_t file_cnt;
763
764         if (fade_in) {
765                 fade_position = file_pos;
766                 fade_data = data;
767         } else {
768                 fade_position = file_pos + nofade;
769                 fade_data = data + nofade;
770         }
771
772         if (fade_position > _length) {
773
774                 /* read starts beyond end of data, just memset to zero */
775
776                 file_cnt = 0;
777
778         } else if (fade_position + xfade > _length) {
779
780                 /* read ends beyond end of data, read some, memset the rest */
781
782                 file_cnt = _length - fade_position;
783
784         } else {
785
786                 /* read is entirely within data */
787
788                 file_cnt = xfade;
789         }
790
791         if (file_cnt) {
792
793                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
794                         if (retval >= 0 && errno == EAGAIN) {
795                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
796                                  * short or no data there */
797                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
798                         } else {
799                                 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
800                                 return 0;
801                         }
802                 }
803         }
804
805         if (file_cnt != xfade) {
806                 framecnt_t delta = xfade - file_cnt;
807                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
808         }
809
810         if (nofade && !fade_in) {
811                 if (write_float (data, file_pos, nofade) != nofade) {
812                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
813                         return 0;
814                 }
815         }
816
817         if (xfade == xfade_frames) {
818
819                 framecnt_t n;
820
821                 /* use the standard xfade curve */
822
823                 if (fade_in) {
824
825                         /* fade new material in */
826
827                         for (n = 0; n < xfade; ++n) {
828                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
829                         }
830
831                 } else {
832
833
834                         /* fade new material out */
835
836                         for (n = 0; n < xfade; ++n) {
837                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
838                         }
839                 }
840
841         } else if (xfade < xfade_frames) {
842
843                 std::vector<gain_t> in(xfade);
844                 std::vector<gain_t> out(xfade);
845
846                 /* short xfade, compute custom curve */
847
848                 compute_equal_power_fades (xfade, &in[0], &out[0]);
849
850                 for (framecnt_t n = 0; n < xfade; ++n) {
851                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
852                 }
853
854         } else if (xfade) {
855
856                 /* long xfade length, has to be computed across several calls */
857
858         }
859
860         if (xfade) {
861                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
862                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
863                         return 0;
864                 }
865         }
866
867         if (fade_in && nofade) {
868                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
869                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
870                         return 0;
871                 }
872         }
873
874         return cnt;
875 }
876
877 framepos_t
878 SndFileSource::last_capture_start_frame () const
879 {
880         if (destructive()) {
881                 return capture_start_frame;
882         } else {
883                 return 0;
884         }
885 }
886
887 void
888 SndFileSource::handle_header_position_change ()
889 {
890         if (destructive()) {
891                 if ( _length != 0 ) {
892                         error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
893                         //in the future, pop up a dialog here that allows user to regenerate file with new start offset
894                 } else if (writable()) {
895                         _timeline_position = header_position_offset;
896                         set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
897                 }
898         }
899 }
900
901 void
902 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
903 {
904         /* This static method is assumed to have been called by the Session
905            before any DFS's are created.
906         */
907
908         xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
909
910         delete [] out_coefficient;
911         delete [] in_coefficient;
912
913         out_coefficient = new gain_t[xfade_frames];
914         in_coefficient = new gain_t[xfade_frames];
915
916         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
917 }
918
919 void
920 SndFileSource::set_timeline_position (framepos_t pos)
921 {
922         // destructive track timeline postion does not change
923         // except at instantion or when header_position_offset
924         // (session start) changes
925
926         if (!destructive()) {
927                 AudioFileSource::set_timeline_position (pos);
928         }
929 }
930
931 int
932 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
933 {
934         SNDFILE *sf;
935         SF_INFO sf_info;
936         BroadcastInfo binfo;
937
938         sf_info.format = 0; // libsndfile says to clear this before sf_open().
939
940         if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
941                 return false;
942         }
943
944 #ifdef PLATFORM_WINDOWS
945         int fd = g_open (path.c_str(), O_RDONLY, 0444);
946 #else
947         int fd = ::open (path.c_str(), O_RDONLY, 0444);
948 #endif
949
950         if (fd == -1) {
951                 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
952                       << endmsg;
953                 return false;
954         }
955         if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
956                 char errbuf[1024];
957                 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
958                 return false;
959         }
960
961         info.samplerate  = sf_info.samplerate;
962         info.channels    = sf_info.channels;
963         info.length      = sf_info.frames;
964
965         string major = sndfile_major_format(sf_info.format);
966         string minor = sndfile_minor_format(sf_info.format);
967
968         if (major.length() + minor.length() < 16) { /* arbitrary */
969                 info.format_name = string_compose("%1/%2", major, minor);
970         } else {
971                 info.format_name = string_compose("%1\n%2", major, minor);
972         }
973
974         info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
975
976         sf_close (sf);
977
978         return true;
979 }
980
981 bool
982 SndFileSource::one_of_several_channels () const
983 {
984         return _info.channels > 1;
985 }
986
987 bool
988 SndFileSource::clamped_at_unity () const
989 {
990         int const type = _info.format & SF_FORMAT_TYPEMASK;
991         int const sub = _info.format & SF_FORMAT_SUBMASK;
992         /* XXX: this may not be the full list of formats that are unclamped */
993         return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
994 }
995
996 void
997 SndFileSource::file_closed ()
998 {
999         /* stupid libsndfile updated the headers on close,
1000            so touch the peakfile if it exists and has data
1001            to make sure its time is as new as the audio
1002            file.
1003         */
1004
1005         touch_peakfile ();
1006 }
1007
1008 void
1009 SndFileSource::set_path (const string& p)
1010 {
1011         FileSource::set_path (p);
1012 }
1013