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