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