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