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