allow to get custom/product/version independent cach dir
[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 bool
803 SndFileSource::set_destructive (bool yn)
804 {
805         if (yn) {
806                 _flags = Flag (_flags | Writable | Destructive);
807                 if (!xfade_buf) {
808                         xfade_buf = new Sample[xfade_frames];
809                 }
810                 clear_capture_marks ();
811                 _timeline_position = header_position_offset;
812         } else {
813                 _flags = Flag (_flags & ~Destructive);
814                 _timeline_position = 0;
815                 /* leave xfade buf alone in case we need it again later */
816         }
817
818         return true;
819 }
820
821 void
822 SndFileSource::clear_capture_marks ()
823 {
824         _capture_start = false;
825         _capture_end = false;
826 }
827
828 /** @param pos Capture start position in session frames */
829 void
830 SndFileSource::mark_capture_start (framepos_t pos)
831 {
832         if (destructive()) {
833                 if (pos < _timeline_position) {
834                         _capture_start = false;
835                 } else {
836                         _capture_start = true;
837                         capture_start_frame = pos;
838                 }
839         }
840 }
841
842 void
843 SndFileSource::mark_capture_end()
844 {
845         if (destructive()) {
846                 _capture_end = true;
847         }
848 }
849
850 framecnt_t
851 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
852 {
853         framecnt_t xfade = min (xfade_frames, cnt);
854         framecnt_t nofade = cnt - xfade;
855         Sample* fade_data = 0;
856         framepos_t fade_position = 0; // in frames
857         ssize_t retval;
858         framecnt_t file_cnt;
859
860         if (fade_in) {
861                 fade_position = file_pos;
862                 fade_data = data;
863         } else {
864                 fade_position = file_pos + nofade;
865                 fade_data = data + nofade;
866         }
867
868         if (fade_position > _length) {
869
870                 /* read starts beyond end of data, just memset to zero */
871
872                 file_cnt = 0;
873
874         } else if (fade_position + xfade > _length) {
875
876                 /* read ends beyond end of data, read some, memset the rest */
877
878                 file_cnt = _length - fade_position;
879
880         } else {
881
882                 /* read is entirely within data */
883
884                 file_cnt = xfade;
885         }
886
887         if (file_cnt) {
888
889                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
890                         if (retval >= 0 && errno == EAGAIN) {
891                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
892                                  * short or no data there */
893                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
894                         } else {
895                                 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
896                                 return 0;
897                         }
898                 }
899         }
900
901         if (file_cnt != xfade) {
902                 framecnt_t delta = xfade - file_cnt;
903                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
904         }
905
906         if (nofade && !fade_in) {
907                 if (write_float (data, file_pos, nofade) != nofade) {
908                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
909                         return 0;
910                 }
911         }
912
913         if (xfade == xfade_frames) {
914
915                 framecnt_t n;
916
917                 /* use the standard xfade curve */
918
919                 if (fade_in) {
920
921                         /* fade new material in */
922
923                         for (n = 0; n < xfade; ++n) {
924                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
925                         }
926
927                 } else {
928
929
930                         /* fade new material out */
931
932                         for (n = 0; n < xfade; ++n) {
933                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
934                         }
935                 }
936
937         } else if (xfade < xfade_frames) {
938
939                 std::vector<gain_t> in(xfade);
940                 std::vector<gain_t> out(xfade);
941
942                 /* short xfade, compute custom curve */
943
944                 compute_equal_power_fades (xfade, &in[0], &out[0]);
945
946                 for (framecnt_t n = 0; n < xfade; ++n) {
947                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
948                 }
949
950         } else if (xfade) {
951
952                 /* long xfade length, has to be computed across several calls */
953
954         }
955
956         if (xfade) {
957                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
958                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
959                         return 0;
960                 }
961         }
962
963         if (fade_in && nofade) {
964                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
965                         error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
966                         return 0;
967                 }
968         }
969
970         return cnt;
971 }
972
973 framepos_t
974 SndFileSource::last_capture_start_frame () const
975 {
976         if (destructive()) {
977                 return capture_start_frame;
978         } else {
979                 return 0;
980         }
981 }
982
983 void
984 SndFileSource::handle_header_position_change ()
985 {
986         if (destructive()) {
987                 if ( _length != 0 ) {
988                         error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
989                         //in the future, pop up a dialog here that allows user to regenerate file with new start offset
990                 } else if (writable()) {
991                         _timeline_position = header_position_offset;
992                         set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
993                 }
994         }
995 }
996
997 void
998 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
999 {
1000         /* This static method is assumed to have been called by the Session
1001            before any DFS's are created.
1002         */
1003
1004         xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
1005
1006         delete [] out_coefficient;
1007         delete [] in_coefficient;
1008
1009         out_coefficient = new gain_t[xfade_frames];
1010         in_coefficient = new gain_t[xfade_frames];
1011
1012         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
1013 }
1014
1015 void
1016 SndFileSource::set_timeline_position (framepos_t pos)
1017 {
1018         // destructive track timeline postion does not change
1019         // except at instantion or when header_position_offset
1020         // (session start) changes
1021
1022         if (!destructive()) {
1023                 AudioFileSource::set_timeline_position (pos);
1024         }
1025 }
1026
1027 int
1028 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
1029 {
1030         SNDFILE *sf;
1031         SF_INFO sf_info;
1032         BroadcastInfo binfo;
1033
1034         sf_info.format = 0; // libsndfile says to clear this before sf_open().
1035
1036         if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
1037                 return false;
1038         }
1039
1040 #ifdef PLATFORM_WINDOWS
1041         int fd = g_open (path.c_str(), O_RDONLY, 0444);
1042 #else
1043         int fd = ::open (path.c_str(), O_RDONLY, 0444);
1044 #endif
1045
1046         if (fd == -1) {
1047                 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
1048                       << endmsg;
1049                 return false;
1050         }
1051         if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
1052                 char errbuf[1024];
1053                 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
1054                 return false;
1055         }
1056
1057         info.samplerate  = sf_info.samplerate;
1058         info.channels    = sf_info.channels;
1059         info.length      = sf_info.frames;
1060
1061         string major = sndfile_major_format(sf_info.format);
1062         string minor = sndfile_minor_format(sf_info.format);
1063
1064         if (major.length() + minor.length() < 16) { /* arbitrary */
1065                 info.format_name = string_compose("%1/%2", major, minor);
1066         } else {
1067                 info.format_name = string_compose("%1\n%2", major, minor);
1068         }
1069
1070         info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1071
1072         sf_close (sf);
1073
1074         return true;
1075 }
1076
1077 bool
1078 SndFileSource::one_of_several_channels () const
1079 {
1080         return _info.channels > 1;
1081 }
1082
1083 bool
1084 SndFileSource::clamped_at_unity () const
1085 {
1086         int const type = _info.format & SF_FORMAT_TYPEMASK;
1087         int const sub = _info.format & SF_FORMAT_SUBMASK;
1088         /* XXX: this may not be the full list of formats that are unclamped */
1089         return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1090 }
1091
1092 void
1093 SndFileSource::file_closed ()
1094 {
1095         /* stupid libsndfile updated the headers on close,
1096            so touch the peakfile if it exists and has data
1097            to make sure its time is as new as the audio
1098            file.
1099         */
1100
1101         touch_peakfile ();
1102 }
1103
1104 void
1105 SndFileSource::set_path (const string& p)
1106 {
1107         FileSource::set_path (p);
1108 }
1109