move all destructive functionality into SndFileSource as a mode, and drop Destructive...
[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
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         interleave_buf = 0;
183         interleave_bufsize = 0;
184         sf = 0;
185         _broadcast_info = 0;
186
187         string tmp_name;
188
189         if ((pos = idstr.find_last_of (':')) == string::npos) {
190                 channel = 0;
191                 tmp_name = idstr;
192         } else {
193                 channel = atoi (idstr.substr (pos+1).c_str());
194                 tmp_name = idstr.substr (0, pos);
195         }
196
197         if (is_embedded()) {
198                 _name = tmp_name;
199         } else {
200                 _name = Glib::path_get_basename (tmp_name);
201         }
202
203         /* although libsndfile says we don't need to set this,
204            valgrind and source code shows us that we do.
205         */
206
207         memset (&_info, 0, sizeof(_info));
208
209         _capture_start = false;
210         _capture_end = false;
211         file_pos = 0;
212
213         if (destructive()) {
214                 xfade_buf = new Sample[xfade_frames];
215                 timeline_position = header_position_offset;
216         } 
217
218         AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change));
219 }
220
221 int
222 SndFileSource::open ()
223 {
224         if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
225                 char errbuf[256];
226                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
227                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"), 
228                                         _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
229                 return -1;
230         }
231
232         if (channel >= _info.channels) {
233                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, channel) << endmsg;
234                 sf_close (sf);
235                 sf = 0;
236                 return -1;
237         }
238
239         _length = _info.frames;
240
241         _broadcast_info = new SF_BROADCAST_INFO;
242         memset (_broadcast_info, 0, sizeof (*_broadcast_info));
243         
244         /* lookup broadcast info */
245         
246         if (sf_command (sf, SFC_GET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
247
248                 /* if the file has data but no broadcast info, then clearly, there is no broadcast info */
249
250                 if (_length) {
251                         delete _broadcast_info;
252                         _broadcast_info = 0;
253                         _flags = Flag (_flags & ~Broadcast);
254                 }
255
256                 set_timeline_position (header_position_offset);
257
258         } else {
259         
260                 /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits
261                    of the time reference.
262                 */
263
264                 set_timeline_position ( _broadcast_info->time_reference_low );
265         }
266
267         if (writable()) {
268                 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
269         }
270
271         return 0;
272 }
273
274 SndFileSource::~SndFileSource ()
275 {
276         GoingAway (); /* EMIT SIGNAL */
277
278         if (sf) {
279                 sf_close (sf);
280                 sf = 0;
281
282                 /* stupid libsndfile updated the headers on close,
283                    so touch the peakfile if it exists and has data
284                    to make sure its time is as new as the audio
285                    file.
286                 */
287
288                 touch_peakfile ();
289         }
290
291         if (interleave_buf) {
292                 delete [] interleave_buf;
293         }
294
295         if (_broadcast_info) {
296                 delete _broadcast_info;
297         }
298
299         if (xfade_buf) {
300                 delete [] xfade_buf;
301         }
302 }
303
304 float
305 SndFileSource::sample_rate () const 
306 {
307         return _info.samplerate;
308 }
309
310 nframes_t
311 SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const
312 {
313         int32_t nread;
314         float *ptr;
315         uint32_t real_cnt;
316         nframes_t file_cnt;
317
318         if (start > _length) {
319
320                 /* read starts beyond end of data, just memset to zero */
321                 
322                 file_cnt = 0;
323
324         } else if (start + cnt > _length) {
325                 
326                 /* read ends beyond end of data, read some, memset the rest */
327                 
328                 file_cnt = _length - start;
329
330         } else {
331                 
332                 /* read is entirely within data */
333
334                 file_cnt = cnt;
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         if (file_cnt != cnt) {
354                 nframes_t delta = cnt - file_cnt;
355                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
356         }
357
358         real_cnt = cnt * _info.channels;
359
360         if (interleave_bufsize < real_cnt) {
361                 
362                 if (interleave_buf) {
363                         delete [] interleave_buf;
364                 }
365                 interleave_bufsize = real_cnt;
366                 interleave_buf = new float[interleave_bufsize];
367         }
368         
369         nread = sf_read_float (sf, interleave_buf, real_cnt);
370         ptr = interleave_buf + channel;
371         nread /= _info.channels;
372         
373         /* stride through the interleaved data */
374         
375         for (int32_t n = 0; n < nread; ++n) {
376                 dst[n] = *ptr;
377                 ptr += _info.channels;
378         }
379
380         _read_data_count = cnt * sizeof(float);
381                 
382         return nread;
383 }
384
385 nframes_t 
386 SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
387 {
388         if (destructive()) {
389                 return destructive_write_unlocked (data, cnt);
390         } else {
391                 return nondestructive_write_unlocked (data, cnt);
392         }
393 }
394
395 nframes_t 
396 SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
397 {
398         if (!writable()) {
399                 return 0;
400         }
401
402         if (_info.channels != 1) {
403                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
404                 /*NOTREACHED*/
405                 return 0;
406         }
407         
408         nframes_t oldlen;
409         int32_t frame_pos = _length;
410         
411         if (write_float (data, frame_pos, cnt) != cnt) {
412                 return 0;
413         }
414
415         oldlen = _length;
416         update_length (oldlen, cnt);
417
418         if (_build_peakfiles) {
419                 PeakBuildRecord *pbr = 0;
420                 
421                 if (pending_peak_builds.size()) {
422                                 pbr = pending_peak_builds.back();
423                         }
424                         
425                         if (pbr && pbr->frame + pbr->cnt == oldlen) {
426                                 
427                                 /* the last PBR extended to the start of the current write,
428                                    so just extend it again.
429                                 */
430
431                                 pbr->cnt += cnt;
432                         } else {
433                                 pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt));
434                         }
435                         
436                         _peaks_built = false;
437         }
438         
439         
440         if (_build_peakfiles) {
441                 queue_for_peaks (shared_from_this ());
442         }
443
444         _write_data_count = cnt;
445         
446         return cnt;
447 }
448
449 nframes_t
450 SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
451 {
452         nframes_t old_file_pos;
453
454         if (!writable()) {
455                 return 0;
456         }
457
458         if (_capture_start && _capture_end) {
459
460                 /* start and end of capture both occur within the data we are writing,
461                    so do both crossfades.
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                 // split cnt in half
471                 nframes_t subcnt = cnt / 2;
472                 nframes_t ofilepos = file_pos;
473                 
474                 // fade in
475                 if (crossfade (data, subcnt, 1) != subcnt) {
476                         return 0;
477                 }
478                 
479                 file_pos += subcnt;
480                 Sample * tmpdata = data + subcnt;
481                 
482                 // fade out
483                 subcnt = cnt - subcnt;
484                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
485                         return 0;
486                 }
487                 
488                 file_pos = ofilepos; // adjusted below
489
490         } else if (_capture_start) {
491
492                 /* start of capture both occur within the data we are writing,
493                    so do the fade in
494                 */
495
496                 _capture_start = false;
497                 _capture_end = false;
498                 
499                 /* move to the correct location place */
500                 file_pos = capture_start_frame - timeline_position;
501
502                 if (crossfade (data, cnt, 1) != cnt) {
503                         return 0;
504                 }
505                 
506         } else if (_capture_end) {
507
508                 /* end of capture both occur within the data we are writing,
509                    so do the fade out
510                 */
511
512                 _capture_start = false;
513                 _capture_end = false;
514                 
515                 if (crossfade (data, cnt, 0) != cnt) {
516                         return 0;
517                 }
518
519         } else {
520
521                 /* in the middle of recording */
522
523                 if (write_float (data, file_pos, cnt) != cnt) {
524                         return 0;
525                 }
526         }
527
528         old_file_pos = file_pos;
529         update_length (file_pos, cnt);
530         file_pos += cnt;
531
532         if (_build_peakfiles) {
533                 PeakBuildRecord *pbr = 0;
534                 
535                 if (pending_peak_builds.size()) {
536                         pbr = pending_peak_builds.back();
537                 }
538                 
539                 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
540                         
541                         /* the last PBR extended to the start of the current write,
542                            so just extend it again.
543                         */
544                         
545                         pbr->cnt += cnt;
546                 } else {
547                         pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
548                 }
549                 
550                 _peaks_built = false;
551         }
552
553         if (_build_peakfiles) {
554                 queue_for_peaks (shared_from_this ());
555         }
556         
557         return cnt;
558 }
559
560 int
561 SndFileSource::update_header (nframes_t when, struct tm& now, time_t tnow)
562 {       
563         set_timeline_position (when);
564
565         if (_flags & Broadcast) {
566                 if (setup_broadcast_info (when, now, tnow)) {
567                         return -1;
568                 }
569         } 
570
571         return flush_header ();
572 }
573
574 int
575 SndFileSource::flush_header ()
576 {
577         if (!writable() || (sf == 0)) {
578                 return -1;
579         }
580         return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
581 }
582
583 int
584 SndFileSource::setup_broadcast_info (nframes_t when, struct tm& now, time_t tnow)
585 {
586         if (!writable()) {
587                 return -1;
588         }
589
590         if (!(_flags & Broadcast)) {
591                 return 0;
592         }
593
594         /* random code is 9 digits */
595         
596         int random_code = random() % 999999999;
597         
598         snprintf (_broadcast_info->originator_reference, sizeof (_broadcast_info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
599                   Config->get_bwf_country_code().c_str(),
600                   Config->get_bwf_organization_code().c_str(),
601                   bwf_serial_number,
602                   now.tm_hour,
603                   now.tm_min,
604                   now.tm_sec,
605                   random_code);
606         
607         snprintf (_broadcast_info->origination_date, sizeof (_broadcast_info->origination_date), "%4d-%02d-%02d",
608                   1900 + now.tm_year,
609                   now.tm_mon,
610                   now.tm_mday);
611         
612         snprintf (_broadcast_info->origination_time, sizeof (_broadcast_info->origination_time), "%02d:%02d:%02d",
613                   now.tm_hour,
614                   now.tm_min,
615                   now.tm_sec);
616
617         /* now update header position taking header offset into account */
618         
619         set_header_timeline_position ();
620
621         if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
622                 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
623                 _flags = Flag (_flags & ~Broadcast);
624                 delete _broadcast_info;
625                 _broadcast_info = 0;
626                 return -1;
627         }
628
629         return 0;
630 }
631
632 void
633 SndFileSource::set_header_timeline_position ()
634 {
635         if (!(_flags & Broadcast)) {
636                 return;
637         }
638
639        _broadcast_info->time_reference_high = (timeline_position >> 32);
640        _broadcast_info->time_reference_low = (timeline_position & 0xffffffff);
641
642         if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
643                 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
644                 _flags = Flag (_flags & ~Broadcast);
645                 delete _broadcast_info;
646                 _broadcast_info = 0;
647         }
648
649         
650
651 }
652
653 nframes_t
654 SndFileSource::write_float (Sample* data, nframes_t frame_pos, nframes_t cnt)
655 {
656         if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
657                 char errbuf[256];
658                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
659                 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
660                 return 0;
661         }
662         
663         if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
664                 return 0;
665         }
666         
667         return cnt;
668 }
669
670 nframes_t
671 SndFileSource::natural_position() const
672 {
673         return timeline_position;
674 }
675
676 bool
677 SndFileSource::set_destructive (bool yn)
678 {
679         if (yn) {
680                 _flags = Flag (_flags | Destructive);
681                 clear_capture_marks ();
682                 timeline_position = header_position_offset;
683         } else {
684                 _flags = Flag (_flags & ~Destructive);
685                 timeline_position = 0;
686         }
687
688         return true;
689 }
690
691 void
692 SndFileSource::clear_capture_marks ()
693 {
694         _capture_start = false;
695         _capture_end = false;
696 }       
697
698 void
699 SndFileSource::mark_capture_start (nframes_t pos)
700 {
701         if (destructive()) {
702                 if (pos < timeline_position) {
703                         _capture_start = false;
704                 } else {
705                         _capture_start = true;
706                         capture_start_frame = pos;
707                 }
708         }
709 }
710
711 void
712 SndFileSource::mark_capture_end()
713 {
714         if (destructive()) {
715                 _capture_end = true;
716         }
717 }
718
719 nframes_t
720 SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
721 {
722         nframes_t xfade = min (xfade_frames, cnt);
723         nframes_t nofade = cnt - xfade;
724         Sample* fade_data = 0;
725         nframes_t fade_position = 0; // in frames
726         ssize_t retval;
727         nframes_t file_cnt;
728
729         if (fade_in) {
730                 fade_position = file_pos;
731                 fade_data = data;
732         } else {
733                 fade_position = file_pos + nofade;
734                 fade_data = data + nofade;
735         }
736
737         if (fade_position > _length) {
738                 
739                 /* read starts beyond end of data, just memset to zero */
740                 
741                 file_cnt = 0;
742
743         } else if (fade_position + xfade > _length) {
744                 
745                 /* read ends beyond end of data, read some, memset the rest */
746                 
747                 file_cnt = _length - fade_position;
748
749         } else {
750                 
751                 /* read is entirely within data */
752
753                 file_cnt = xfade;
754         }
755
756         if (file_cnt) {
757                 
758                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
759                         if (retval >= 0 && errno == EAGAIN) {
760                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
761                                  * short or no data there */
762                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
763                         } else {
764                                 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
765                                 return 0;
766                         }
767                 }
768         } 
769
770         if (file_cnt != xfade) {
771                 nframes_t delta = xfade - file_cnt;
772                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
773         }
774         
775         if (nofade && !fade_in) {
776                 if (write_float (data, file_pos, nofade) != nofade) {
777                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
778                         return 0;
779                 }
780         }
781
782         if (xfade == xfade_frames) {
783
784                 nframes_t n;
785
786                 /* use the standard xfade curve */
787                 
788                 if (fade_in) {
789
790                         /* fade new material in */
791                         
792                         for (n = 0; n < xfade; ++n) {
793                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
794                         }
795
796                 } else {
797
798
799                         /* fade new material out */
800                         
801                         for (n = 0; n < xfade; ++n) {
802                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
803                         }
804                 }
805
806         } else if (xfade) {
807
808                 gain_t in[xfade];
809                 gain_t out[xfade];
810
811                 /* short xfade, compute custom curve */
812
813                 compute_equal_power_fades (xfade, in, out);
814
815                 for (nframes_t n = 0; n < xfade; ++n) {
816                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);                
817                 }
818         }
819
820         if (xfade) {
821                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
822                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
823                         return 0;
824                 }
825         }
826         
827         if (fade_in && nofade) {
828                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
829                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
830                         return 0;
831                 }
832         }
833
834         return cnt;
835 }
836
837 nframes_t
838 SndFileSource::last_capture_start_frame () const
839 {
840         return capture_start_frame;
841 }
842
843 void
844 SndFileSource::handle_header_position_change ()
845 {
846         if (destructive()) {
847                 if ( _length != 0 ) {
848                         error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
849                         //in the future, pop up a dialog here that allows user to regenerate file with new start offset
850                 } else if (writable()) {
851                         timeline_position = header_position_offset;
852                         set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
853                 }
854         }
855 }
856
857 void
858 SndFileSource::setup_standard_crossfades (nframes_t rate)
859 {
860         /* This static method is assumed to have been called by the Session
861            before any DFS's are created.
862         */
863
864         xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
865
866         cerr << "based on " << Config->get_destructive_xfade_msecs() << " msecs, xfade_frames = " << xfade_frames << endl;
867
868         if (out_coefficient) {
869                 delete [] out_coefficient;
870         }
871
872         if (in_coefficient) {
873                 delete [] in_coefficient;
874         }
875
876         out_coefficient = new gain_t[xfade_frames];
877         in_coefficient = new gain_t[xfade_frames];
878
879         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
880 }
881
882 void
883 SndFileSource::set_timeline_position (nframes_t pos)
884 {
885         // destructive track timeline postion does not change
886         // except at instantion or when header_position_offset 
887         // (session start) changes
888
889         if (!destructive()) {
890                 AudioFileSource::set_timeline_position (pos);
891         } 
892 }