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