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