fix yet another meter-locked tempo ordering thinko.
[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 /** Constructor to losslessly compress existing source to flac */
236 SndFileSource::SndFileSource (Session& s, const AudioFileSource& other, const string& path, bool use16bits, Progress* progress)
237         : Source(s, DataType::AUDIO, path, Flag ((other.flags () | default_writable_flags | NoPeakFile) & ~RF64_RIFF))
238         , AudioFileSource (s, path, "", Flag ((other.flags () | default_writable_flags | NoPeakFile) & ~RF64_RIFF), /*unused*/ FormatFloat, /*unused*/ WAVE64)
239         , _sndfile (0)
240         , _broadcast_info (0)
241         , _capture_start (false)
242         , _capture_end (false)
243         , file_pos (0)
244         , xfade_buf (0)
245 {
246         if (other.readable_length () == 0) {
247                 throw failed_constructor();
248         }
249
250         assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
251
252         _channel = other.channel ();
253         init_sndfile ();
254
255         _file_is_new = true;
256
257         _info.channels = other.n_channels();
258         _info.samplerate = other.sample_rate ();
259         _info.format = SF_FORMAT_FLAC | (use16bits ? SF_FORMAT_PCM_16 : SF_FORMAT_PCM_24);
260
261         /* flac is either read or write -- never both,
262          * so we need to special-case ::open () */
263 #ifdef PLATFORM_WINDOWS
264         int fd = g_open (_path.c_str(), O_CREAT | O_RDWR, 0644);
265 #else
266         int fd = ::open (_path.c_str(), O_CREAT | O_RDWR, 0644);
267 #endif
268         if (fd == -1) {
269                 throw failed_constructor();
270         }
271
272         _sndfile = sf_open_fd (fd, SFM_WRITE, &_info, true);
273
274         if (_sndfile == 0) {
275                 throw failed_constructor();
276         }
277
278         /* copy file */
279         Sample buf[8192];
280         framecnt_t off = 0;
281         framecnt_t len = other.read (buf, off, 8192, /*channel*/0);
282         while (len > 0) {
283                 write (buf, len);
284                 off += len;
285                 len = other.read (buf, off, 8192, /*channel*/0);
286                 if (progress) {
287                         progress->set_progress ((float) off / other.readable_length ());
288                 }
289         }
290 }
291
292 void
293 SndFileSource::init_sndfile ()
294 {
295         /* although libsndfile says we don't need to set this,
296            valgrind and source code shows us that we do.
297         */
298
299         memset (&_info, 0, sizeof(_info));
300
301         if (destructive()) {
302                 xfade_buf = new Sample[xfade_frames];
303                 _timeline_position = header_position_offset;
304         }
305
306         AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
307 }
308
309 void
310 SndFileSource::close ()
311 {
312         if (_sndfile) {
313                 sf_close (_sndfile);
314                 _sndfile = 0;
315                 file_closed ();
316         }
317 }
318
319 int
320 SndFileSource::open ()
321 {
322         if (_sndfile) {
323                 return 0;
324         }
325
326 // We really only want to use g_open for all platforms but because of this
327 // method(SndfileSource::open), the compiler(or at least GCC) is confused
328 // because g_open will expand to "open" on non-POSIX systems and needs the
329 // global namespace qualifer. The problem is since since C99 ::g_open will
330 // apparently expand to ":: open"
331 #ifdef PLATFORM_WINDOWS
332         int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
333 #else
334         int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
335 #endif
336
337         if (fd == -1) {
338                 error << string_compose (
339                              _ ("SndFileSource: cannot open file \"%1\" for %2"),
340                              _path,
341                              (writable () ? "read+write" : "reading")) << endmsg;
342                 return -1;
343         }
344
345         if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
346                 assert (!writable());
347                 _sndfile = sf_open_fd (fd, SFM_READ, &_info, true);
348         } else {
349                 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
350         }
351
352         if (_sndfile == 0) {
353                 char errbuf[1024];
354                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
355 #ifndef HAVE_COREAUDIO
356                 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
357                    so we don't want to see this message.
358                 */
359
360                 cerr << "failed to open " << _path << " with name " << _name << endl;
361
362                 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
363                                         _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
364 #endif
365                 return -1;
366         }
367
368         if (_channel >= _info.channels) {
369 #ifndef HAVE_COREAUDIO
370                 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
371 #endif
372                 sf_close (_sndfile);
373                 _sndfile = 0;
374                 return -1;
375         }
376
377         _length = _info.frames;
378
379 #ifdef HAVE_RF64_RIFF
380         if (_file_is_new && _length == 0 && writable()) {
381                 if (_flags & RF64_RIFF) {
382                         if (sf_command (_sndfile, SFC_RF64_AUTO_DOWNGRADE, 0, 0) != SF_TRUE) {
383                                 char errbuf[256];
384                                 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
385                                 error << string_compose (_("Cannot mark RF64 audio file for automatic downgrade to WAV: %1"), errbuf)
386                                       << endmsg;
387                         }
388                 }
389         }
390 #endif
391
392         if (!_broadcast_info) {
393                 _broadcast_info = new BroadcastInfo;
394         }
395
396         bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
397
398         if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
399                 /* newly created files will not have a BWF header at this point in time.
400                  * Import will have called Source::set_timeline_position() if one exists
401                  * in the original. */
402                 header_position_offset = _timeline_position;
403         }
404
405         /* Set our timeline position to either the time reference from a BWF header or the current
406            start of the session.
407         */
408         set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
409
410         if (_length != 0 && !bwf_info_exists) {
411                 delete _broadcast_info;
412                 _broadcast_info = 0;
413                 _flags = Flag (_flags & ~Broadcast);
414         }
415
416         /* Set the broadcast flag if the BWF info is already there. We need
417          * this when recovering or using existing files.
418          */
419
420         if (bwf_info_exists) {
421                 _flags = Flag (_flags | Broadcast);
422         }
423
424         if (writable()) {
425                 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
426
427                 if (_flags & Broadcast) {
428
429                         if (!_broadcast_info) {
430                                 _broadcast_info = new BroadcastInfo;
431                         }
432
433                         _broadcast_info->set_from_session (_session, header_position_offset);
434                         _broadcast_info->set_description (string_compose ("BWF %1", _name));
435
436                         if (!_broadcast_info->write_to_file (_sndfile)) {
437                                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
438                                                          _path, _broadcast_info->get_error())
439                                       << endmsg;
440                                 _flags = Flag (_flags & ~Broadcast);
441                                 delete _broadcast_info;
442                                 _broadcast_info = 0;
443                         }
444                 }
445         }
446
447         return 0;
448 }
449
450 SndFileSource::~SndFileSource ()
451 {
452         close ();
453         delete _broadcast_info;
454         delete [] xfade_buf;
455 }
456
457 float
458 SndFileSource::sample_rate () const
459 {
460         return _info.samplerate;
461 }
462
463 framecnt_t
464 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
465 {
466         assert (cnt >= 0);
467
468         framecnt_t nread;
469         float *ptr;
470         framecnt_t real_cnt;
471         framepos_t file_cnt;
472
473         if (writable() && !_sndfile) {
474                 /* file has not been opened yet - nothing written to it */
475                 memset (dst, 0, sizeof (Sample) * cnt);
476                 return cnt;
477         }
478
479         if (const_cast<SndFileSource*>(this)->open()) {
480                 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
481                 return 0;
482         }
483
484         if (start > _length) {
485
486                 /* read starts beyond end of data, just memset to zero */
487
488                 file_cnt = 0;
489
490         } else if (start + cnt > _length) {
491
492                 /* read ends beyond end of data, read some, memset the rest */
493
494                 file_cnt = _length - start;
495
496         } else {
497
498                 /* read is entirely within data */
499
500                 file_cnt = cnt;
501         }
502
503         assert (file_cnt >= 0);
504
505         if (file_cnt != cnt) {
506                 framepos_t delta = cnt - file_cnt;
507                 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
508         }
509
510         if (file_cnt) {
511
512                 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
513                         char errbuf[256];
514                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
515                         error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
516                         return 0;
517                 }
518
519                 if (_info.channels == 1) {
520                         framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
521                         if (ret != file_cnt) {
522                                 char errbuf[256];
523                                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
524                                 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;
525                         }
526                         return ret;
527                 }
528         }
529
530         real_cnt = cnt * _info.channels;
531
532         Sample* interleave_buf = get_interleave_buffer (real_cnt);
533
534         nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
535         ptr = interleave_buf + _channel;
536         nread /= _info.channels;
537
538         /* stride through the interleaved data */
539
540         for (framecnt_t n = 0; n < nread; ++n) {
541                 dst[n] = *ptr;
542                 ptr += _info.channels;
543         }
544
545         return nread;
546 }
547
548 framecnt_t
549 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
550 {
551         if (open()) {
552                 return 0; // failure
553         }
554
555         if (destructive()) {
556                 return destructive_write_unlocked (data, cnt);
557         } else {
558                 return nondestructive_write_unlocked (data, cnt);
559         }
560 }
561
562 framecnt_t
563 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
564 {
565         if (!writable()) {
566                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
567                 return 0;
568         }
569
570         if (_info.channels != 1) {
571                 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
572                 abort(); /*NOTREACHED*/
573                 return 0;
574         }
575
576         framepos_t frame_pos = _length;
577
578         if (write_float (data, frame_pos, cnt) != cnt) {
579                 return 0;
580         }
581
582         update_length (_length + cnt);
583
584         if (_build_peakfiles) {
585                 compute_and_write_peaks (data, frame_pos, cnt, true, true);
586         }
587
588         return cnt;
589 }
590
591 framecnt_t
592 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
593 {
594         if (!writable()) {
595                 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
596                 return 0;
597         }
598
599         if (_capture_start && _capture_end) {
600
601                 /* start and end of capture both occur within the data we are writing,
602                    so do both crossfades.
603                 */
604
605                 _capture_start = false;
606                 _capture_end = false;
607
608                 /* move to the correct location place */
609                 file_pos = capture_start_frame - _timeline_position;
610
611                 // split cnt in half
612                 framecnt_t subcnt = cnt / 2;
613                 framecnt_t ofilepos = file_pos;
614
615                 // fade in
616                 if (crossfade (data, subcnt, 1) != subcnt) {
617                         return 0;
618                 }
619
620                 file_pos += subcnt;
621                 Sample * tmpdata = data + subcnt;
622
623                 // fade out
624                 subcnt = cnt - subcnt;
625                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
626                         return 0;
627                 }
628
629                 file_pos = ofilepos; // adjusted below
630
631         } else if (_capture_start) {
632
633                 /* start of capture both occur within the data we are writing,
634                    so do the fade in
635                 */
636
637                 _capture_start = false;
638                 _capture_end = false;
639
640                 /* move to the correct location place */
641                 file_pos = capture_start_frame - _timeline_position;
642
643                 if (crossfade (data, cnt, 1) != cnt) {
644                         return 0;
645                 }
646
647         } else if (_capture_end) {
648
649                 /* end of capture both occur within the data we are writing,
650                    so do the fade out
651                 */
652
653                 _capture_start = false;
654                 _capture_end = false;
655
656                 if (crossfade (data, cnt, 0) != cnt) {
657                         return 0;
658                 }
659
660         } else {
661
662                 /* in the middle of recording */
663
664                 if (write_float (data, file_pos, cnt) != cnt) {
665                         return 0;
666                 }
667         }
668
669         update_length (file_pos + cnt);
670
671         if (_build_peakfiles) {
672                 compute_and_write_peaks (data, file_pos, cnt, true, true);
673         }
674
675         file_pos += cnt;
676
677         return cnt;
678 }
679
680 int
681 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
682 {
683         set_timeline_position (when);
684
685         if (_flags & Broadcast) {
686                 if (setup_broadcast_info (when, now, tnow)) {
687                         return -1;
688                 }
689         }
690
691         return flush_header ();
692 }
693
694 int
695 SndFileSource::flush_header ()
696 {
697         if (!writable()) {
698                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
699                 return -1;
700         }
701
702         if (_sndfile == 0) {
703                 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
704                 return -1;
705         }
706
707         int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
708
709         return r;
710 }
711
712 void
713 SndFileSource::flush ()
714 {
715         if (!writable()) {
716                 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
717                 return;
718         }
719
720         if (_sndfile == 0) {
721                 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
722                 return;
723         }
724
725         // Hopefully everything OK
726         sf_write_sync (_sndfile);
727 }
728
729 int
730 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
731 {
732         if (!writable()) {
733                 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
734                 return -1;
735         }
736
737         if (!_sndfile) {
738                 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
739                 return -1;
740         }
741
742         if (!(_flags & Broadcast) || !_broadcast_info) {
743                 return 0;
744         }
745
746         _broadcast_info->set_originator_ref_from_session (_session);
747         _broadcast_info->set_origination_time (&now);
748
749         /* now update header position taking header offset into account */
750
751         set_header_timeline_position ();
752
753         return 0;
754 }
755
756 void
757 SndFileSource::set_header_timeline_position ()
758 {
759         if (!(_flags & Broadcast)) {
760                 return;
761         }
762         assert (_broadcast_info);
763
764         _broadcast_info->set_time_reference (_timeline_position);
765
766         if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
767                 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
768                                            _path, _broadcast_info->get_error())
769                       << endmsg;
770                 _flags = Flag (_flags & ~Broadcast);
771                 delete _broadcast_info;
772                 _broadcast_info = 0;
773         }
774 }
775
776 framecnt_t
777 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
778 {
779         if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
780                 assert (_length == frame_pos);
781         }
782         else if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
783                 char errbuf[256];
784                 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
785                 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
786                 return 0;
787         }
788
789         if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
790                 return 0;
791         }
792
793         return cnt;
794 }
795
796 framepos_t
797 SndFileSource::natural_position() const
798 {
799         return _timeline_position;
800 }
801
802 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
803 bool
804 SndFileSource::set_destructive (bool yn)
805 {
806         if (yn) {
807                 _flags = Flag (_flags | Writable | Destructive);
808                 if (!xfade_buf) {
809                         xfade_buf = new Sample[xfade_frames];
810                 }
811                 clear_capture_marks ();
812                 _timeline_position = header_position_offset;
813         } else {
814                 _flags = Flag (_flags & ~Destructive);
815                 _timeline_position = 0;
816                 /* leave xfade buf alone in case we need it again later */
817         }
818
819         return true;
820 }
821 #endif
822
823 void
824 SndFileSource::clear_capture_marks ()
825 {
826         _capture_start = false;
827         _capture_end = false;
828 }
829
830 /** @param pos Capture start position in session frames */
831 void
832 SndFileSource::mark_capture_start (framepos_t pos)
833 {
834         if (destructive()) {
835                 if (pos < _timeline_position) {
836                         _capture_start = false;
837                 } else {
838                         _capture_start = true;
839                         capture_start_frame = pos;
840                 }
841         }
842 }
843
844 void
845 SndFileSource::mark_capture_end()
846 {
847         if (destructive()) {
848                 _capture_end = true;
849         }
850 }
851
852 framecnt_t
853 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
854 {
855         framecnt_t xfade = min (xfade_frames, cnt);
856         framecnt_t nofade = cnt - xfade;
857         Sample* fade_data = 0;
858         framepos_t fade_position = 0; // in frames
859         ssize_t retval;
860         framecnt_t file_cnt;
861
862         if (fade_in) {
863                 fade_position = file_pos;
864                 fade_data = data;
865         } else {
866                 fade_position = file_pos + nofade;
867                 fade_data = data + nofade;
868         }
869
870         if (fade_position > _length) {
871
872                 /* read starts beyond end of data, just memset to zero */
873
874                 file_cnt = 0;
875
876         } else if (fade_position + xfade > _length) {
877
878                 /* read ends beyond end of data, read some, memset the rest */
879
880                 file_cnt = _length - fade_position;
881
882         } else {
883
884                 /* read is entirely within data */
885
886                 file_cnt = xfade;
887         }
888
889         if (file_cnt) {
890
891                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
892                         if (retval >= 0 && errno == EAGAIN) {
893                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
894                                  * short or no data there */
895                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
896                         } else {
897                                 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
898                                 return 0;
899                         }
900                 }
901         }
902
903         if (file_cnt != xfade) {
904                 framecnt_t delta = xfade - file_cnt;
905                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
906         }
907
908         if (nofade && !fade_in) {
909                 if (write_float (data, file_pos, nofade) != nofade) {
910                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
911                         return 0;
912                 }
913         }
914
915         if (xfade == xfade_frames) {
916
917                 framecnt_t n;
918
919                 /* use the standard xfade curve */
920
921                 if (fade_in) {
922
923                         /* fade new material in */
924
925                         for (n = 0; n < xfade; ++n) {
926                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
927                         }
928
929                 } else {
930
931
932                         /* fade new material out */
933
934                         for (n = 0; n < xfade; ++n) {
935                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
936                         }
937                 }
938
939         } else if (xfade < xfade_frames) {
940
941                 std::vector<gain_t> in(xfade);
942                 std::vector<gain_t> out(xfade);
943
944                 /* short xfade, compute custom curve */
945
946                 compute_equal_power_fades (xfade, &in[0], &out[0]);
947
948                 for (framecnt_t n = 0; n < xfade; ++n) {
949                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
950                 }
951
952         } else if (xfade) {
953
954                 /* long xfade length, has to be computed across several calls */
955
956         }
957
958         if (xfade) {
959                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
960                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
961                         return 0;
962                 }
963         }
964
965         if (fade_in && nofade) {
966                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
967                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
968                         return 0;
969                 }
970         }
971
972         return cnt;
973 }
974
975 framepos_t
976 SndFileSource::last_capture_start_frame () const
977 {
978         if (destructive()) {
979                 return capture_start_frame;
980         } else {
981                 return 0;
982         }
983 }
984
985 void
986 SndFileSource::handle_header_position_change ()
987 {
988         if (destructive()) {
989                 if ( _length != 0 ) {
990                         error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
991                         //in the future, pop up a dialog here that allows user to regenerate file with new start offset
992                 } else if (writable()) {
993                         _timeline_position = header_position_offset;
994                         set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
995                 }
996         }
997 }
998
999 void
1000 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
1001 {
1002         /* This static method is assumed to have been called by the Session
1003            before any DFS's are created.
1004         */
1005
1006         xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
1007
1008         delete [] out_coefficient;
1009         delete [] in_coefficient;
1010
1011         out_coefficient = new gain_t[xfade_frames];
1012         in_coefficient = new gain_t[xfade_frames];
1013
1014         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
1015 }
1016
1017 void
1018 SndFileSource::set_timeline_position (framepos_t pos)
1019 {
1020         // destructive track timeline postion does not change
1021         // except at instantion or when header_position_offset
1022         // (session start) changes
1023
1024         if (!destructive()) {
1025                 AudioFileSource::set_timeline_position (pos);
1026         }
1027 }
1028
1029 int
1030 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
1031 {
1032         SNDFILE *sf;
1033         SF_INFO sf_info;
1034         BroadcastInfo binfo;
1035
1036         sf_info.format = 0; // libsndfile says to clear this before sf_open().
1037
1038         if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
1039                 return false;
1040         }
1041
1042 #ifdef PLATFORM_WINDOWS
1043         int fd = g_open (path.c_str(), O_RDONLY, 0444);
1044 #else
1045         int fd = ::open (path.c_str(), O_RDONLY, 0444);
1046 #endif
1047
1048         if (fd == -1) {
1049                 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
1050                       << endmsg;
1051                 return false;
1052         }
1053         if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
1054                 char errbuf[1024];
1055                 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
1056                 return false;
1057         }
1058
1059         info.samplerate  = sf_info.samplerate;
1060         info.channels    = sf_info.channels;
1061         info.length      = sf_info.frames;
1062
1063         string major = sndfile_major_format(sf_info.format);
1064         string minor = sndfile_minor_format(sf_info.format);
1065
1066         if (major.length() + minor.length() < 16) { /* arbitrary */
1067                 info.format_name = string_compose("%1/%2", major, minor);
1068         } else {
1069                 info.format_name = string_compose("%1\n%2", major, minor);
1070         }
1071
1072         info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1073
1074         sf_close (sf);
1075
1076         return true;
1077 }
1078
1079 bool
1080 SndFileSource::one_of_several_channels () const
1081 {
1082         return _info.channels > 1;
1083 }
1084
1085 bool
1086 SndFileSource::clamped_at_unity () const
1087 {
1088         int const type = _info.format & SF_FORMAT_TYPEMASK;
1089         int const sub = _info.format & SF_FORMAT_SUBMASK;
1090         /* XXX: this may not be the full list of formats that are unclamped */
1091         return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1092 }
1093
1094 void
1095 SndFileSource::file_closed ()
1096 {
1097         /* stupid libsndfile updated the headers on close,
1098            so touch the peakfile if it exists and has data
1099            to make sure its time is as new as the audio
1100            file.
1101         */
1102
1103         touch_peakfile ();
1104 }
1105
1106 void
1107 SndFileSource::set_path (const string& p)
1108 {
1109         FileSource::set_path (p);
1110 }
1111