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