remove duplicate code
[ardour.git] / libs / ardour / sndfilesource.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
23
24 #include <cstring>
25 #include <cerrno>
26 #include <climits>
27 #include <cstdarg>
28
29 #include <sys/stat.h>
30
31 #ifdef PLATFORM_WINDOWS
32 #include <glibmm/convert.h>
33 #endif
34 #include <glibmm/fileutils.h>
35 #include <glibmm/miscutils.h>
36
37 #include "ardour/sndfilesource.h"
38 #include "ardour/sndfile_helpers.h"
39 #include "ardour/utils.h"
40 #include "ardour/session.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using std::string;
48
49 gain_t* SndFileSource::out_coefficient = 0;
50 gain_t* SndFileSource::in_coefficient = 0;
51 framecnt_t SndFileSource::xfade_frames = 64;
52 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
53                 Source::Writable |
54                 Source::Removable |
55                 Source::RemovableIfEmpty |
56                 Source::CanRename );
57
58 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
59         : Source(s, node)
60         , AudioFileSource (s, node)
61         , _sndfile (0)
62         , _broadcast_info (0)
63         , _capture_start (false)
64         , _capture_end (false)
65         , file_pos (0)
66         , xfade_buf (0)
67 {
68         init_sndfile ();
69
70         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
71         existence_check ();
72
73         if (open()) {
74                 throw failed_constructor ();
75         }
76 }
77
78 /** Constructor for existing external-to-session files.
79     Files created this way are never writable or removable 
80 */
81 SndFileSource::SndFileSource (Session& s, const string& path, int chn, Flag flags)
82         : Source(s, DataType::AUDIO, path, flags)
83           /* note that the origin of an external file is itself */
84         , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
85         , _sndfile (0)
86         , _broadcast_info (0)
87         , _capture_start (false)
88         , _capture_end (false)
89         , file_pos (0)
90         , xfade_buf (0)
91 {
92         _channel = chn;
93
94         init_sndfile ();
95
96         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
97         existence_check ();
98
99         if (open()) {
100                 throw failed_constructor ();
101         }
102 }
103
104 /** This constructor is used to construct new internal-to-session files, 
105     not open existing ones. 
106 */
107 SndFileSource::SndFileSource (Session& s, const string& path, const string& origin,
108                               SampleFormat sfmt, HeaderFormat hf, framecnt_t rate, Flag flags)
109         : Source(s, DataType::AUDIO, path, flags)
110         , AudioFileSource (s, path, origin, flags, sfmt, hf)
111         , _sndfile (0)
112         , _broadcast_info (0)
113         , _capture_start (false)
114         , _capture_end (false)
115         , file_pos (0)
116         , xfade_buf (0)
117 {
118         int fmt = 0;
119
120         init_sndfile ();
121
122         assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
123         existence_check ();
124
125         _file_is_new = true;
126
127         switch (hf) {
128         case CAF:
129                 fmt = SF_FORMAT_CAF;
130                 _flags = Flag (_flags & ~Broadcast);
131                 break;
132
133         case AIFF:
134                 fmt = SF_FORMAT_AIFF;
135                 _flags = Flag (_flags & ~Broadcast);
136                 break;
137
138         case BWF:
139                 fmt = SF_FORMAT_WAV;
140                 _flags = Flag (_flags | Broadcast);
141                 break;
142
143         case WAVE:
144                 fmt = SF_FORMAT_WAV;
145                 _flags = Flag (_flags & ~Broadcast);
146                 break;
147
148         case WAVE64:
149                 fmt = SF_FORMAT_W64;
150                 _flags = Flag (_flags & ~Broadcast);
151                 break;
152
153         default:
154                 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
155                 abort(); /*NOTREACHED*/
156                 break;
157
158         }
159
160         switch (sfmt) {
161         case FormatFloat:
162                 fmt |= SF_FORMAT_FLOAT;
163                 break;
164
165         case FormatInt24:
166                 fmt |= SF_FORMAT_PCM_24;
167                 break;
168
169         case FormatInt16:
170                 fmt |= SF_FORMAT_PCM_16;
171                 break;
172         }
173
174         _info.channels = 1;
175         _info.samplerate = rate;
176         _info.format = fmt;
177
178         if (_flags & Destructive) {
179                 if (open()) {
180                         throw failed_constructor();
181                 }
182         } else {
183                 /* normal mode: do not open the file here - do that in write_unlocked() as needed
184                  */
185         }
186 }
187
188 /** Constructor to be called for recovering files being used for
189  * capture. They are in-session, they already exist, they should not
190  * be writable. They are an odd hybrid (from a constructor point of
191  * view) of the previous two constructors.
192  */
193 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
194         : Source (s, DataType::AUDIO, path, Flag (0))
195           /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
196         , AudioFileSource (s, path, Flag (0))
197         , _sndfile (0)
198         , _broadcast_info (0)
199         , _capture_start (false)
200         , _capture_end (false)
201         , file_pos (0)
202         , xfade_buf (0)
203 {
204         _channel = chn;
205
206         init_sndfile ();
207
208         assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
209         existence_check ();
210
211         if (open()) {
212                 throw failed_constructor ();
213         }
214 }
215
216 void
217 SndFileSource::init_sndfile ()
218 {
219         /* although libsndfile says we don't need to set this,
220            valgrind and source code shows us that we do.
221         */
222
223         memset (&_info, 0, sizeof(_info));
224
225         if (destructive()) {
226                 xfade_buf = new Sample[xfade_frames];
227                 _timeline_position = header_position_offset;
228         }
229
230         AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
231 }
232
233 int
234 SndFileSource::open ()
235 {
236         string path_to_open;
237
238         if (_sndfile) {
239                 return 0;
240         }
241         
242 #ifdef PLATFORM_WINDOWS
243         path_to_open = Glib::locale_from_utf8(_path);
244 #else
245         path_to_open = _path;
246 #endif
247
248         _sndfile = sf_open (path_to_open.c_str(), writable() ? SFM_RDWR : SFM_READ, &_info);
249
250         if (_sndfile == 0) {
251                 char errbuf[1024];
252                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
253 #ifndef HAVE_COREAUDIO
254                 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
255                    so we don't want to see this message.
256                 */
257
258                 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
259
260                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
261                                         path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
262 #endif
263                 return -1;
264         }
265
266         if (_channel >= _info.channels) {
267 #ifndef HAVE_COREAUDIO
268                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
269 #endif
270                 sf_close (_sndfile);
271                 _sndfile = 0;
272                 return -1;
273         }
274
275         _length = _info.frames;
276
277         if (!_broadcast_info) {
278                 _broadcast_info = new BroadcastInfo;
279         }
280
281         bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
282
283         if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
284                 /* newly created files will not have a BWF header at this point in time.
285                  * Import will have called Source::set_timeline_position() if one exists
286                  * in the original. */
287                 header_position_offset = _timeline_position;
288         }
289
290         /* Set our timeline position to either the time reference from a BWF header or the current
291            start of the session.
292         */
293         set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
294
295         if (_length != 0 && !bwf_info_exists) {
296                 delete _broadcast_info;
297                 _broadcast_info = 0;
298                 _flags = Flag (_flags & ~Broadcast);
299         } 
300
301         /* Set the broadcast flag if the BWF info is already there. We need
302          * this when recovering or using existing files.
303          */
304         
305         if (bwf_info_exists) {
306                 _flags = Flag (_flags | Broadcast);
307         }
308
309         if (writable()) {
310                 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
311
312                 if (_flags & Broadcast) {
313
314                         if (!_broadcast_info) {
315                                 _broadcast_info = new BroadcastInfo;
316                         }
317
318                         _broadcast_info->set_from_session (_session, header_position_offset);
319                         _broadcast_info->set_description (string_compose ("BWF %1", _name));
320
321                         if (!_broadcast_info->write_to_file (_sndfile)) {
322                                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
323                                                          path_to_open, _broadcast_info->get_error())
324                                       << endmsg;
325                                 _flags = Flag (_flags & ~Broadcast);
326                                 delete _broadcast_info;
327                                 _broadcast_info = 0;
328                         }
329                 }
330         }
331         
332         return 0;
333 }
334
335 SndFileSource::~SndFileSource ()
336 {
337         if (_sndfile) {
338                 sf_close (_sndfile);
339                 _sndfile = 0;
340         }
341         delete _broadcast_info;
342         delete [] xfade_buf;
343 }
344
345 float
346 SndFileSource::sample_rate () const
347 {
348         return _info.samplerate;
349 }
350
351 framecnt_t
352 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
353 {
354         assert (cnt >= 0);
355         
356         framecnt_t nread;
357         float *ptr;
358         framecnt_t real_cnt;
359         framepos_t file_cnt;
360
361         if (writable() && !_sndfile) {
362                 /* file has not been opened yet - nothing written to it */
363                 memset (dst, 0, sizeof (Sample) * cnt);
364                 return cnt;
365         }
366
367         if (_sndfile == 0) {
368                 error << string_compose (_("could not allocate file %1 for reading."), _path) << endmsg;
369                 return 0;
370         }
371
372         if (start > _length) {
373
374                 /* read starts beyond end of data, just memset to zero */
375
376                 file_cnt = 0;
377
378         } else if (start + cnt > _length) {
379
380                 /* read ends beyond end of data, read some, memset the rest */
381
382                 file_cnt = _length - start;
383
384         } else {
385
386                 /* read is entirely within data */
387
388                 file_cnt = cnt;
389         }
390
391         assert (file_cnt >= 0);
392
393         if (file_cnt != cnt) {
394                 framepos_t delta = cnt - file_cnt;
395                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
396         }
397
398         if (file_cnt) {
399
400                 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
401                         char errbuf[256];
402                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
403                         error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
404                         return 0;
405                 }
406
407                 if (_info.channels == 1) {
408                         framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
409                         if (ret != file_cnt) {
410                                 char errbuf[256];
411                                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
412                                 error << string_compose(_("SndFileSource: @ %1 could not read %2 within %3 (%4) (len = %5, ret was %6)"), start, file_cnt, _name.val().substr (1), errbuf, _length, ret) << endl;
413                         }
414                         return ret;
415                 }
416         }
417
418         real_cnt = cnt * _info.channels;
419
420         Sample* interleave_buf = get_interleave_buffer (real_cnt);
421
422         nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
423         ptr = interleave_buf + _channel;
424         nread /= _info.channels;
425
426         /* stride through the interleaved data */
427
428         for (framecnt_t n = 0; n < nread; ++n) {
429                 dst[n] = *ptr;
430                 ptr += _info.channels;
431         }
432
433         return nread;
434 }
435
436 framecnt_t
437 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
438 {
439         if (open()) {
440                 return 0; // failure
441         }
442
443         if (destructive()) {
444                 return destructive_write_unlocked (data, cnt);
445         } else {
446                 return nondestructive_write_unlocked (data, cnt);
447         }
448 }
449
450 framecnt_t
451 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
452 {
453         if (!writable()) {
454                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
455                 return 0;
456         }
457
458         if (_info.channels != 1) {
459                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
460                 abort(); /*NOTREACHED*/
461                 return 0;
462         }
463
464         framepos_t frame_pos = _length;
465
466         if (write_float (data, frame_pos, cnt) != cnt) {
467                 return 0;
468         }
469
470         update_length (_length + cnt);
471
472         if (_build_peakfiles) {
473                 compute_and_write_peaks (data, frame_pos, cnt, true, true);
474         }
475
476         return cnt;
477 }
478
479 framecnt_t
480 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
481 {
482         if (!writable()) {
483                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
484                 return 0;
485         }
486
487         if (_capture_start && _capture_end) {
488
489                 /* start and end of capture both occur within the data we are writing,
490                    so do both crossfades.
491                 */
492
493                 _capture_start = false;
494                 _capture_end = false;
495
496                 /* move to the correct location place */
497                 file_pos = capture_start_frame - _timeline_position;
498
499                 // split cnt in half
500                 framecnt_t subcnt = cnt / 2;
501                 framecnt_t ofilepos = file_pos;
502
503                 // fade in
504                 if (crossfade (data, subcnt, 1) != subcnt) {
505                         return 0;
506                 }
507
508                 file_pos += subcnt;
509                 Sample * tmpdata = data + subcnt;
510
511                 // fade out
512                 subcnt = cnt - subcnt;
513                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
514                         return 0;
515                 }
516
517                 file_pos = ofilepos; // adjusted below
518
519         } else if (_capture_start) {
520
521                 /* start of capture both occur within the data we are writing,
522                    so do the fade in
523                 */
524
525                 _capture_start = false;
526                 _capture_end = false;
527
528                 /* move to the correct location place */
529                 file_pos = capture_start_frame - _timeline_position;
530
531                 if (crossfade (data, cnt, 1) != cnt) {
532                         return 0;
533                 }
534
535         } else if (_capture_end) {
536
537                 /* end of capture both occur within the data we are writing,
538                    so do the fade out
539                 */
540
541                 _capture_start = false;
542                 _capture_end = false;
543
544                 if (crossfade (data, cnt, 0) != cnt) {
545                         return 0;
546                 }
547
548         } else {
549
550                 /* in the middle of recording */
551
552                 if (write_float (data, file_pos, cnt) != cnt) {
553                         return 0;
554                 }
555         }
556
557         update_length (file_pos + cnt);
558
559         if (_build_peakfiles) {
560                 compute_and_write_peaks (data, file_pos, cnt, true, true);
561         }
562
563         file_pos += cnt;
564
565         return cnt;
566 }
567
568 int
569 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
570 {
571         set_timeline_position (when);
572
573         if (_flags & Broadcast) {
574                 if (setup_broadcast_info (when, now, tnow)) {
575                         return -1;
576                 }
577         }
578
579         return flush_header ();
580 }
581
582 int
583 SndFileSource::flush_header ()
584 {
585         if (!writable()) {
586                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
587                 return -1;
588         }
589
590         if (_sndfile == 0) {
591                 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
592                 return -1;
593         }
594
595         int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
596
597         return r;
598 }
599
600 void
601 SndFileSource::flush ()
602 {
603         if (!writable()) {
604                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
605                 return;
606         }
607
608         if (_sndfile == 0) {
609                 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
610                 return;
611         }
612
613         // Hopefully everything OK
614         sf_write_sync (_sndfile);
615 }
616
617 int
618 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
619 {
620         if (!writable()) {
621                 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
622                 return -1;
623         }
624
625         if (!_sndfile) {
626                 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
627                 return -1;
628         }
629
630         if (!(_flags & Broadcast) || !_broadcast_info) {
631                 return 0;
632         }
633
634         _broadcast_info->set_originator_ref_from_session (_session);
635         _broadcast_info->set_origination_time (&now);
636
637         /* now update header position taking header offset into account */
638
639         set_header_timeline_position ();
640
641         return 0;
642 }
643
644 void
645 SndFileSource::set_header_timeline_position ()
646 {
647         if (!(_flags & Broadcast)) {
648                 return;
649         }
650         assert (_broadcast_info);
651
652         _broadcast_info->set_time_reference (_timeline_position);
653
654         if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
655                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
656                                            _path, _broadcast_info->get_error())
657                       << endmsg;
658                 _flags = Flag (_flags & ~Broadcast);
659                 delete _broadcast_info;
660                 _broadcast_info = 0;
661         }
662 }
663
664 framecnt_t
665 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
666 {
667         if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
668                 char errbuf[256];
669                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
670                 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
671                 return 0;
672         }
673
674         if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
675                 return 0;
676         }
677
678         return cnt;
679 }
680
681 framepos_t
682 SndFileSource::natural_position() const
683 {
684         return _timeline_position;
685 }
686
687 bool
688 SndFileSource::set_destructive (bool yn)
689 {
690         if (yn) {
691                 _flags = Flag (_flags | Writable | Destructive);
692                 if (!xfade_buf) {
693                         xfade_buf = new Sample[xfade_frames];
694                 }
695                 clear_capture_marks ();
696                 _timeline_position = header_position_offset;
697         } else {
698                 _flags = Flag (_flags & ~Destructive);
699                 _timeline_position = 0;
700                 /* leave xfade buf alone in case we need it again later */
701         }
702
703         return true;
704 }
705
706 void
707 SndFileSource::clear_capture_marks ()
708 {
709         _capture_start = false;
710         _capture_end = false;
711 }
712
713 /** @param pos Capture start position in session frames */
714 void
715 SndFileSource::mark_capture_start (framepos_t pos)
716 {
717         if (destructive()) {
718                 if (pos < _timeline_position) {
719                         _capture_start = false;
720                 } else {
721                         _capture_start = true;
722                         capture_start_frame = pos;
723                 }
724         }
725 }
726
727 void
728 SndFileSource::mark_capture_end()
729 {
730         if (destructive()) {
731                 _capture_end = true;
732         }
733 }
734
735 framecnt_t
736 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
737 {
738         framecnt_t xfade = min (xfade_frames, cnt);
739         framecnt_t nofade = cnt - xfade;
740         Sample* fade_data = 0;
741         framepos_t fade_position = 0; // in frames
742         ssize_t retval;
743         framecnt_t file_cnt;
744
745         if (fade_in) {
746                 fade_position = file_pos;
747                 fade_data = data;
748         } else {
749                 fade_position = file_pos + nofade;
750                 fade_data = data + nofade;
751         }
752
753         if (fade_position > _length) {
754
755                 /* read starts beyond end of data, just memset to zero */
756
757                 file_cnt = 0;
758
759         } else if (fade_position + xfade > _length) {
760
761                 /* read ends beyond end of data, read some, memset the rest */
762
763                 file_cnt = _length - fade_position;
764
765         } else {
766
767                 /* read is entirely within data */
768
769                 file_cnt = xfade;
770         }
771
772         if (file_cnt) {
773
774                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
775                         if (retval >= 0 && errno == EAGAIN) {
776                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
777                                  * short or no data there */
778                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
779                         } else {
780                                 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
781                                 return 0;
782                         }
783                 }
784         }
785
786         if (file_cnt != xfade) {
787                 framecnt_t delta = xfade - file_cnt;
788                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
789         }
790
791         if (nofade && !fade_in) {
792                 if (write_float (data, file_pos, nofade) != nofade) {
793                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
794                         return 0;
795                 }
796         }
797
798         if (xfade == xfade_frames) {
799
800                 framecnt_t n;
801
802                 /* use the standard xfade curve */
803
804                 if (fade_in) {
805
806                         /* fade new material in */
807
808                         for (n = 0; n < xfade; ++n) {
809                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
810                         }
811
812                 } else {
813
814
815                         /* fade new material out */
816
817                         for (n = 0; n < xfade; ++n) {
818                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
819                         }
820                 }
821
822         } else if (xfade < xfade_frames) {
823
824                 std::vector<gain_t> in(xfade);
825                 std::vector<gain_t> out(xfade);
826
827                 /* short xfade, compute custom curve */
828
829                 compute_equal_power_fades (xfade, &in[0], &out[0]);
830
831                 for (framecnt_t n = 0; n < xfade; ++n) {
832                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
833                 }
834
835         } else if (xfade) {
836
837                 /* long xfade length, has to be computed across several calls */
838
839         }
840
841         if (xfade) {
842                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
843                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
844                         return 0;
845                 }
846         }
847
848         if (fade_in && nofade) {
849                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
850                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
851                         return 0;
852                 }
853         }
854
855         return cnt;
856 }
857
858 framepos_t
859 SndFileSource::last_capture_start_frame () const
860 {
861         if (destructive()) {
862                 return capture_start_frame;
863         } else {
864                 return 0;
865         }
866 }
867
868 void
869 SndFileSource::handle_header_position_change ()
870 {
871         if (destructive()) {
872                 if ( _length != 0 ) {
873                         error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
874                         //in the future, pop up a dialog here that allows user to regenerate file with new start offset
875                 } else if (writable()) {
876                         _timeline_position = header_position_offset;
877                         set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
878                 }
879         }
880 }
881
882 void
883 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
884 {
885         /* This static method is assumed to have been called by the Session
886            before any DFS's are created.
887         */
888
889         xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
890
891         delete [] out_coefficient;
892         delete [] in_coefficient;
893
894         out_coefficient = new gain_t[xfade_frames];
895         in_coefficient = new gain_t[xfade_frames];
896
897         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
898 }
899
900 void
901 SndFileSource::set_timeline_position (framepos_t pos)
902 {
903         // destructive track timeline postion does not change
904         // except at instantion or when header_position_offset
905         // (session start) changes
906
907         if (!destructive()) {
908                 AudioFileSource::set_timeline_position (pos);
909         }
910 }
911
912 int
913 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
914 {
915         SNDFILE *sf;
916         SF_INFO sf_info;
917         BroadcastInfo binfo;
918
919         sf_info.format = 0; // libsndfile says to clear this before sf_open().
920
921         if ((sf = sf_open (const_cast<char*>(path.c_str()), SFM_READ, &sf_info)) == 0) {
922                 char errbuf[256];
923                 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
924                 return false;
925         }
926
927         info.samplerate  = sf_info.samplerate;
928         info.channels    = sf_info.channels;
929         info.length      = sf_info.frames;
930
931         string major = sndfile_major_format(sf_info.format);
932         string minor = sndfile_minor_format(sf_info.format);
933
934         if (major.length() + minor.length() < 16) { /* arbitrary */
935                 info.format_name = string_compose("%1/%2", major, minor);
936         } else {
937                 info.format_name = string_compose("%1\n%2", major, minor);
938         }
939
940         info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
941
942         sf_close (sf);
943
944         return true;
945 }
946
947 bool
948 SndFileSource::one_of_several_channels () const
949 {
950         return _info.channels > 1;
951 }
952
953 bool
954 SndFileSource::clamped_at_unity () const
955 {
956         int const type = _info.format & SF_FORMAT_TYPEMASK;
957         int const sub = _info.format & SF_FORMAT_SUBMASK;
958         /* XXX: this may not be the full list of formats that are unclamped */
959         return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
960 }
961
962 void
963 SndFileSource::file_closed ()
964 {
965         /* stupid libsndfile updated the headers on close,
966            so touch the peakfile if it exists and has data
967            to make sure its time is as new as the audio
968            file.
969         */
970
971         touch_peakfile ();
972 }
973
974 void
975 SndFileSource::set_path (const string& p)
976 {
977         FileSource::set_path (p);
978 }
979