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