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