2 Copyright (C) 2006 Paul Davis
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.
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.
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.
21 #include "libardour-config.h"
33 #include "pbd/gstdio_compat.h"
35 #include <glibmm/convert.h>
36 #include <glibmm/fileutils.h>
37 #include <glibmm/miscutils.h>
39 #include "ardour/runtime_functions.h"
40 #include "ardour/sndfilesource.h"
41 #include "ardour/sndfile_helpers.h"
42 #include "ardour/utils.h"
43 #include "ardour/session.h"
48 using namespace ARDOUR;
52 gain_t* SndFileSource::out_coefficient = 0;
53 gain_t* SndFileSource::in_coefficient = 0;
54 samplecnt_t SndFileSource::xfade_samples = 64;
55 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
58 Source::RemovableIfEmpty |
61 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
63 , AudioFileSource (s, node)
66 , _capture_start (false)
67 , _capture_end (false)
73 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
77 throw failed_constructor ();
81 /** Constructor for existing external-to-session files.
82 Files created this way are never writable or removable
84 SndFileSource::SndFileSource (Session& s, const string& path, int chn, Flag flags)
85 : Source(s, DataType::AUDIO, path, flags)
86 /* note that the origin of an external file is itself */
87 , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
90 , _capture_start (false)
91 , _capture_end (false)
99 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
103 throw failed_constructor ();
107 /** This constructor is used to construct new internal-to-session files,
108 not open existing ones.
110 SndFileSource::SndFileSource (Session& s, const string& path, const string& origin,
111 SampleFormat sfmt, HeaderFormat hf, samplecnt_t rate, Flag flags)
112 : Source(s, DataType::AUDIO, path, flags)
113 , AudioFileSource (s, path, origin, flags, sfmt, hf)
115 , _broadcast_info (0)
116 , _capture_start (false)
117 , _capture_end (false)
125 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
133 _flags = Flag (_flags & ~Broadcast);
137 fmt = SF_FORMAT_AIFF;
138 _flags = Flag (_flags & ~Broadcast);
143 _flags = Flag (_flags | Broadcast);
148 _flags = Flag (_flags & ~Broadcast);
153 _flags = Flag (_flags & ~Broadcast);
157 fmt = SF_FORMAT_RF64;
158 _flags = Flag (_flags & ~Broadcast);
159 _flags = Flag (_flags | RF64_RIFF);
163 fmt = SF_FORMAT_RF64;
164 _flags = Flag (_flags | Broadcast);
165 _flags = Flag (_flags | RF64_RIFF);
169 fmt = SF_FORMAT_RF64;
170 _flags = Flag (_flags & ~Broadcast);
174 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
175 abort(); /*NOTREACHED*/
182 fmt |= SF_FORMAT_FLOAT;
186 fmt |= SF_FORMAT_PCM_24;
190 fmt |= SF_FORMAT_PCM_16;
195 _info.samplerate = rate;
198 if (_flags & Destructive) {
200 throw failed_constructor();
203 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
208 /** Constructor to be called for recovering files being used for
209 * capture. They are in-session, they already exist, they should not
210 * be writable. They are an odd hybrid (from a constructor point of
211 * view) of the previous two constructors.
213 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
214 : Source (s, DataType::AUDIO, path, Flag (0))
215 /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
216 , AudioFileSource (s, path, Flag (0))
218 , _broadcast_info (0)
219 , _capture_start (false)
220 , _capture_end (false)
228 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
232 throw failed_constructor ();
236 /** Constructor to losslessly compress existing source to flac */
237 SndFileSource::SndFileSource (Session& s, const AudioFileSource& other, const string& path, bool use16bits, Progress* progress)
238 : Source(s, DataType::AUDIO, path, Flag ((other.flags () | default_writable_flags | NoPeakFile) & ~RF64_RIFF))
239 , AudioFileSource (s, path, "", Flag ((other.flags () | default_writable_flags | NoPeakFile) & ~RF64_RIFF), /*unused*/ FormatFloat, /*unused*/ WAVE64)
241 , _broadcast_info (0)
242 , _capture_start (false)
243 , _capture_end (false)
247 if (other.readable_length () == 0) {
248 throw failed_constructor();
251 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
253 _channel = other.channel ();
258 _info.channels = other.n_channels();
259 _info.samplerate = other.sample_rate ();
260 _info.format = SF_FORMAT_FLAC | (use16bits ? SF_FORMAT_PCM_16 : SF_FORMAT_PCM_24);
262 /* flac is either read or write -- never both,
263 * so we need to special-case ::open () */
264 #ifdef PLATFORM_WINDOWS
265 int fd = g_open (_path.c_str(), O_CREAT | O_RDWR, 0644);
267 int fd = ::open (_path.c_str(), O_CREAT | O_RDWR, 0644);
270 throw failed_constructor();
273 _sndfile = sf_open_fd (fd, SFM_WRITE, &_info, true);
276 throw failed_constructor();
280 /* setting flac compression quality above the default does not produce a significant size
281 * improvement (not for large raw recordings anyway, the_CLA tests 2017-10-02, >> 250MB files,
282 * ~1% smaller), but does have a significant encoding speed penalty.
284 * We still may expose this as option someday though, perhaps for opposite reason: "fast encoding"
286 double flac_quality = 1; // libsndfile uses range 0..1 (mapped to flac 0..8), default is (5/8)
287 if (sf_command (_sndfile, SFC_SET_COMPRESSION_LEVEL, &flac_quality, sizeof (double)) != SF_TRUE) {
289 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
290 error << string_compose (_("Cannot set flac compression level: %1"), errbuf) << endmsg;
299 /* normalize before converting to fixed point, calc gain factor */
300 samplecnt_t len = other.read (buf, off, 8192, /*channel*/0);
302 peak = compute_peak (buf, len, peak);
304 len = other.read (buf, off, 8192, /*channel*/0);
306 progress->set_progress (0.5f * (float) off / other.readable_length ());
317 len = other.read (buf, off, 8192, /*channel*/0);
320 for (samplecnt_t i = 0; i < len; ++i) {
326 len = other.read (buf, off, 8192, /*channel*/0);
328 progress->set_progress (0.5f + 0.5f * (float) off / other.readable_length ());
334 SndFileSource::init_sndfile ()
336 /* although libsndfile says we don't need to set this,
337 valgrind and source code shows us that we do.
340 memset (&_info, 0, sizeof(_info));
343 xfade_buf = new Sample[xfade_samples];
344 _timeline_position = header_position_offset;
347 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
351 SndFileSource::close ()
361 SndFileSource::open ()
367 // We really only want to use g_open for all platforms but because of this
368 // method(SndfileSource::open), the compiler(or at least GCC) is confused
369 // because g_open will expand to "open" on non-POSIX systems and needs the
370 // global namespace qualifer. The problem is since since C99 ::g_open will
371 // apparently expand to ":: open"
372 #ifdef PLATFORM_WINDOWS
373 int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
375 int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
379 error << string_compose (
380 _ ("SndFileSource: cannot open file \"%1\" for %2"),
382 (writable () ? "read+write" : "reading")) << endmsg;
386 if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
387 assert (!writable());
388 _sndfile = sf_open_fd (fd, SFM_READ, &_info, true);
390 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
395 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
396 #ifndef HAVE_COREAUDIO
397 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
398 so we don't want to see this message.
401 cerr << "failed to open " << _path << " with name " << _name << endl;
403 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
404 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
409 if (_channel >= _info.channels) {
410 #ifndef HAVE_COREAUDIO
411 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
418 _length = _info.frames;
420 #ifdef HAVE_RF64_RIFF
421 if (_file_is_new && _length == 0 && writable()) {
422 if (_flags & RF64_RIFF) {
423 if (sf_command (_sndfile, SFC_RF64_AUTO_DOWNGRADE, 0, 0) != SF_TRUE) {
425 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
426 error << string_compose (_("Cannot mark RF64 audio file for automatic downgrade to WAV: %1"), errbuf)
433 if (!_broadcast_info) {
434 _broadcast_info = new BroadcastInfo;
437 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
439 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
440 /* newly created files will not have a BWF header at this point in time.
441 * Import will have called Source::set_timeline_position() if one exists
442 * in the original. */
443 header_position_offset = _timeline_position;
446 /* Set our timeline position to either the time reference from a BWF header or the current
447 start of the session.
449 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
451 if (_length != 0 && !bwf_info_exists) {
452 delete _broadcast_info;
454 _flags = Flag (_flags & ~Broadcast);
457 /* Set the broadcast flag if the BWF info is already there. We need
458 * this when recovering or using existing files.
461 if (bwf_info_exists) {
462 _flags = Flag (_flags | Broadcast);
466 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
468 if (_flags & Broadcast) {
470 if (!_broadcast_info) {
471 _broadcast_info = new BroadcastInfo;
474 _broadcast_info->set_from_session (_session, header_position_offset);
475 _broadcast_info->set_description (string_compose ("BWF %1", _name));
477 if (!_broadcast_info->write_to_file (_sndfile)) {
478 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
479 _path, _broadcast_info->get_error())
481 _flags = Flag (_flags & ~Broadcast);
482 delete _broadcast_info;
491 SndFileSource::~SndFileSource ()
494 delete _broadcast_info;
499 SndFileSource::sample_rate () const
501 return _info.samplerate;
505 SndFileSource::read_unlocked (Sample *dst, samplepos_t start, samplecnt_t cnt) const
511 samplecnt_t real_cnt;
512 samplepos_t file_cnt;
514 if (writable() && !_sndfile) {
515 /* file has not been opened yet - nothing written to it */
516 memset (dst, 0, sizeof (Sample) * cnt);
520 if (const_cast<SndFileSource*>(this)->open()) {
521 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
525 if (start > _length) {
527 /* read starts beyond end of data, just memset to zero */
531 } else if (start + cnt > _length) {
533 /* read ends beyond end of data, read some, memset the rest */
535 file_cnt = _length - start;
539 /* read is entirely within data */
544 assert (file_cnt >= 0);
546 if (file_cnt != cnt) {
547 samplepos_t delta = cnt - file_cnt;
548 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
553 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
555 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
556 error << string_compose(_("SndFileSource: could not seek to sample %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
560 if (_info.channels == 1) {
561 samplecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
562 if (ret != file_cnt) {
564 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
565 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;
568 for (samplecnt_t i = 0; i < ret; ++i) {
576 real_cnt = cnt * _info.channels;
578 Sample* interleave_buf = get_interleave_buffer (real_cnt);
580 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
581 ptr = interleave_buf + _channel;
582 nread /= _info.channels;
584 /* stride through the interleaved data */
587 for (samplecnt_t n = 0; n < nread; ++n) {
588 dst[n] = *ptr * _gain;
589 ptr += _info.channels;
592 for (samplecnt_t n = 0; n < nread; ++n) {
594 ptr += _info.channels;
602 SndFileSource::write_unlocked (Sample *data, samplecnt_t cnt)
609 return destructive_write_unlocked (data, cnt);
611 return nondestructive_write_unlocked (data, cnt);
616 SndFileSource::nondestructive_write_unlocked (Sample *data, samplecnt_t cnt)
619 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
623 if (_info.channels != 1) {
624 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
625 abort(); /*NOTREACHED*/
629 samplepos_t sample_pos = _length;
631 if (write_float (data, sample_pos, cnt) != cnt) {
635 update_length (_length + cnt);
637 if (_build_peakfiles) {
638 compute_and_write_peaks (data, sample_pos, cnt, true, true);
645 SndFileSource::destructive_write_unlocked (Sample* data, samplecnt_t cnt)
648 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
652 if (_capture_start && _capture_end) {
654 /* start and end of capture both occur within the data we are writing,
655 so do both crossfades.
658 _capture_start = false;
659 _capture_end = false;
661 /* move to the correct location place */
662 file_pos = capture_start_sample - _timeline_position;
665 samplecnt_t subcnt = cnt / 2;
666 samplecnt_t ofilepos = file_pos;
669 if (crossfade (data, subcnt, 1) != subcnt) {
674 Sample * tmpdata = data + subcnt;
677 subcnt = cnt - subcnt;
678 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
682 file_pos = ofilepos; // adjusted below
684 } else if (_capture_start) {
686 /* start of capture both occur within the data we are writing,
690 _capture_start = false;
691 _capture_end = false;
693 /* move to the correct location place */
694 file_pos = capture_start_sample - _timeline_position;
696 if (crossfade (data, cnt, 1) != cnt) {
700 } else if (_capture_end) {
702 /* end of capture both occur within the data we are writing,
706 _capture_start = false;
707 _capture_end = false;
709 if (crossfade (data, cnt, 0) != cnt) {
715 /* in the middle of recording */
717 if (write_float (data, file_pos, cnt) != cnt) {
722 update_length (file_pos + cnt);
724 if (_build_peakfiles) {
725 compute_and_write_peaks (data, file_pos, cnt, true, true);
734 SndFileSource::update_header (samplepos_t when, struct tm& now, time_t tnow)
736 set_timeline_position (when);
738 if (_flags & Broadcast) {
739 if (setup_broadcast_info (when, now, tnow)) {
744 return flush_header ();
748 SndFileSource::flush_header ()
751 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
756 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
760 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
766 SndFileSource::flush ()
769 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
774 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
778 // Hopefully everything OK
779 sf_write_sync (_sndfile);
783 SndFileSource::setup_broadcast_info (samplepos_t /*when*/, struct tm& now, time_t /*tnow*/)
786 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
791 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
795 if (!(_flags & Broadcast) || !_broadcast_info) {
799 _broadcast_info->set_originator_ref_from_session (_session);
800 _broadcast_info->set_origination_time (&now);
802 /* now update header position taking header offset into account */
804 set_header_timeline_position ();
810 SndFileSource::set_header_timeline_position ()
812 if (!(_flags & Broadcast)) {
815 assert (_broadcast_info);
817 _broadcast_info->set_time_reference (_timeline_position);
819 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
820 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
821 _path, _broadcast_info->get_error())
823 _flags = Flag (_flags & ~Broadcast);
824 delete _broadcast_info;
830 SndFileSource::write_float (Sample* data, samplepos_t sample_pos, samplecnt_t cnt)
832 if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
833 assert (_length == sample_pos);
835 else if (_sndfile == 0 || sf_seek (_sndfile, sample_pos, SEEK_SET|SFM_WRITE) < 0) {
837 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
838 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, sample_pos, errbuf) << endmsg;
842 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
850 SndFileSource::natural_position() const
852 return _timeline_position;
856 SndFileSource::clear_capture_marks ()
858 _capture_start = false;
859 _capture_end = false;
862 /** @param pos Capture start position in session samples */
864 SndFileSource::mark_capture_start (samplepos_t pos)
867 if (pos < _timeline_position) {
868 _capture_start = false;
870 _capture_start = true;
871 capture_start_sample = pos;
877 SndFileSource::mark_capture_end()
885 SndFileSource::crossfade (Sample* data, samplecnt_t cnt, int fade_in)
887 samplecnt_t xfade = min (xfade_samples, cnt);
888 samplecnt_t nofade = cnt - xfade;
889 Sample* fade_data = 0;
890 samplepos_t fade_position = 0; // in samples
892 samplecnt_t file_cnt;
895 fade_position = file_pos;
898 fade_position = file_pos + nofade;
899 fade_data = data + nofade;
902 if (fade_position > _length) {
904 /* read starts beyond end of data, just memset to zero */
908 } else if (fade_position + xfade > _length) {
910 /* read ends beyond end of data, read some, memset the rest */
912 file_cnt = _length - fade_position;
916 /* read is entirely within data */
923 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
924 if (retval >= 0 && errno == EAGAIN) {
925 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
926 * short or no data there */
927 memset (xfade_buf, 0, xfade * sizeof(Sample));
929 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
935 if (file_cnt != xfade) {
936 samplecnt_t delta = xfade - file_cnt;
937 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
940 if (nofade && !fade_in) {
941 if (write_float (data, file_pos, nofade) != nofade) {
942 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
947 if (xfade == xfade_samples) {
951 /* use the standard xfade curve */
955 /* fade new material in */
957 for (n = 0; n < xfade; ++n) {
958 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
964 /* fade new material out */
966 for (n = 0; n < xfade; ++n) {
967 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
971 } else if (xfade < xfade_samples) {
973 std::vector<gain_t> in(xfade);
974 std::vector<gain_t> out(xfade);
976 /* short xfade, compute custom curve */
978 compute_equal_power_fades (xfade, &in[0], &out[0]);
980 for (samplecnt_t n = 0; n < xfade; ++n) {
981 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
986 /* long xfade length, has to be computed across several calls */
991 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
992 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
997 if (fade_in && nofade) {
998 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
999 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
1008 SndFileSource::last_capture_start_sample () const
1010 if (destructive()) {
1011 return capture_start_sample;
1018 SndFileSource::handle_header_position_change ()
1020 if (destructive()) {
1021 if ( _length != 0 ) {
1022 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
1023 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
1024 } else if (writable()) {
1025 _timeline_position = header_position_offset;
1026 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
1032 SndFileSource::setup_standard_crossfades (Session const & s, samplecnt_t rate)
1034 /* This static method is assumed to have been called by the Session
1035 before any DFS's are created.
1038 xfade_samples = (samplecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
1040 delete [] out_coefficient;
1041 delete [] in_coefficient;
1043 out_coefficient = new gain_t[xfade_samples];
1044 in_coefficient = new gain_t[xfade_samples];
1046 compute_equal_power_fades (xfade_samples, in_coefficient, out_coefficient);
1050 SndFileSource::set_timeline_position (samplepos_t pos)
1052 // destructive track timeline postion does not change
1053 // except at instantion or when header_position_offset
1054 // (session start) changes
1056 if (!destructive()) {
1057 AudioFileSource::set_timeline_position (pos);
1062 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
1066 BroadcastInfo binfo;
1068 sf_info.format = 0; // libsndfile says to clear this before sf_open().
1070 if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
1074 #ifdef PLATFORM_WINDOWS
1075 int fd = g_open (path.c_str(), O_RDONLY, 0444);
1077 int fd = ::open (path.c_str(), O_RDONLY, 0444);
1081 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
1085 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
1087 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
1091 info.samplerate = sf_info.samplerate;
1092 info.channels = sf_info.channels;
1093 info.length = sf_info.frames;
1095 string major = sndfile_major_format(sf_info.format);
1096 string minor = sndfile_minor_format(sf_info.format);
1098 if (major.length() + minor.length() < 16) { /* arbitrary */
1099 info.format_name = string_compose("%1/%2", major, minor);
1101 info.format_name = string_compose("%1\n%2", major, minor);
1104 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1112 SndFileSource::one_of_several_channels () const
1114 return _info.channels > 1;
1118 SndFileSource::clamped_at_unity () const
1120 int const type = _info.format & SF_FORMAT_TYPEMASK;
1121 int const sub = _info.format & SF_FORMAT_SUBMASK;
1122 /* XXX: this may not be the full list of formats that are unclamped */
1123 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1127 SndFileSource::file_closed ()
1129 /* stupid libsndfile updated the headers on close,
1130 so touch the peakfile if it exists and has data
1131 to make sure its time is as new as the audio
1139 SndFileSource::set_path (const string& p)
1141 FileSource::set_path (p);