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