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