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 framecnt_t SndFileSource::xfade_frames = 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, framecnt_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();
284 /* normalize before converting to fixed point, calc gain factor */
285 framecnt_t len = other.read (buf, off, 8192, /*channel*/0);
287 peak = compute_peak (buf, len, peak);
289 len = other.read (buf, off, 8192, /*channel*/0);
291 progress->set_progress (0.5f * (float) off / other.readable_length ());
302 len = other.read (buf, off, 8192, /*channel*/0);
305 for (framecnt_t i = 0; i < len; ++i) {
311 len = other.read (buf, off, 8192, /*channel*/0);
313 progress->set_progress (0.5f + 0.5f * (float) off / other.readable_length ());
319 SndFileSource::init_sndfile ()
321 /* although libsndfile says we don't need to set this,
322 valgrind and source code shows us that we do.
325 memset (&_info, 0, sizeof(_info));
328 xfade_buf = new Sample[xfade_frames];
329 _timeline_position = header_position_offset;
332 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
336 SndFileSource::close ()
346 SndFileSource::open ()
352 // We really only want to use g_open for all platforms but because of this
353 // method(SndfileSource::open), the compiler(or at least GCC) is confused
354 // because g_open will expand to "open" on non-POSIX systems and needs the
355 // global namespace qualifer. The problem is since since C99 ::g_open will
356 // apparently expand to ":: open"
357 #ifdef PLATFORM_WINDOWS
358 int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
360 int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
364 error << string_compose (
365 _ ("SndFileSource: cannot open file \"%1\" for %2"),
367 (writable () ? "read+write" : "reading")) << endmsg;
371 if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
372 assert (!writable());
373 _sndfile = sf_open_fd (fd, SFM_READ, &_info, true);
375 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
380 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
381 #ifndef HAVE_COREAUDIO
382 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
383 so we don't want to see this message.
386 cerr << "failed to open " << _path << " with name " << _name << endl;
388 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
389 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
394 if (_channel >= _info.channels) {
395 #ifndef HAVE_COREAUDIO
396 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
403 _length = _info.frames;
405 #ifdef HAVE_RF64_RIFF
406 if (_file_is_new && _length == 0 && writable()) {
407 if (_flags & RF64_RIFF) {
408 if (sf_command (_sndfile, SFC_RF64_AUTO_DOWNGRADE, 0, 0) != SF_TRUE) {
410 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
411 error << string_compose (_("Cannot mark RF64 audio file for automatic downgrade to WAV: %1"), errbuf)
418 if (!_broadcast_info) {
419 _broadcast_info = new BroadcastInfo;
422 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
424 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
425 /* newly created files will not have a BWF header at this point in time.
426 * Import will have called Source::set_timeline_position() if one exists
427 * in the original. */
428 header_position_offset = _timeline_position;
431 /* Set our timeline position to either the time reference from a BWF header or the current
432 start of the session.
434 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
436 if (_length != 0 && !bwf_info_exists) {
437 delete _broadcast_info;
439 _flags = Flag (_flags & ~Broadcast);
442 /* Set the broadcast flag if the BWF info is already there. We need
443 * this when recovering or using existing files.
446 if (bwf_info_exists) {
447 _flags = Flag (_flags | Broadcast);
451 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
453 if (_flags & Broadcast) {
455 if (!_broadcast_info) {
456 _broadcast_info = new BroadcastInfo;
459 _broadcast_info->set_from_session (_session, header_position_offset);
460 _broadcast_info->set_description (string_compose ("BWF %1", _name));
462 if (!_broadcast_info->write_to_file (_sndfile)) {
463 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
464 _path, _broadcast_info->get_error())
466 _flags = Flag (_flags & ~Broadcast);
467 delete _broadcast_info;
476 SndFileSource::~SndFileSource ()
479 delete _broadcast_info;
484 SndFileSource::sample_rate () const
486 return _info.samplerate;
490 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
499 if (writable() && !_sndfile) {
500 /* file has not been opened yet - nothing written to it */
501 memset (dst, 0, sizeof (Sample) * cnt);
505 if (const_cast<SndFileSource*>(this)->open()) {
506 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
510 if (start > _length) {
512 /* read starts beyond end of data, just memset to zero */
516 } else if (start + cnt > _length) {
518 /* read ends beyond end of data, read some, memset the rest */
520 file_cnt = _length - start;
524 /* read is entirely within data */
529 assert (file_cnt >= 0);
531 if (file_cnt != cnt) {
532 framepos_t delta = cnt - file_cnt;
533 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
538 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
540 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
541 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
545 if (_info.channels == 1) {
546 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
547 if (ret != file_cnt) {
549 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
550 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;
553 for (framecnt_t i = 0; i < ret; ++i) {
561 real_cnt = cnt * _info.channels;
563 Sample* interleave_buf = get_interleave_buffer (real_cnt);
565 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
566 ptr = interleave_buf + _channel;
567 nread /= _info.channels;
569 /* stride through the interleaved data */
572 for (framecnt_t n = 0; n < nread; ++n) {
573 dst[n] = *ptr * _gain;
574 ptr += _info.channels;
577 for (framecnt_t n = 0; n < nread; ++n) {
579 ptr += _info.channels;
587 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
594 return destructive_write_unlocked (data, cnt);
596 return nondestructive_write_unlocked (data, cnt);
601 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
604 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
608 if (_info.channels != 1) {
609 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
610 abort(); /*NOTREACHED*/
614 framepos_t frame_pos = _length;
616 if (write_float (data, frame_pos, cnt) != cnt) {
620 update_length (_length + cnt);
622 if (_build_peakfiles) {
623 compute_and_write_peaks (data, frame_pos, cnt, true, true);
630 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
633 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
637 if (_capture_start && _capture_end) {
639 /* start and end of capture both occur within the data we are writing,
640 so do both crossfades.
643 _capture_start = false;
644 _capture_end = false;
646 /* move to the correct location place */
647 file_pos = capture_start_frame - _timeline_position;
650 framecnt_t subcnt = cnt / 2;
651 framecnt_t ofilepos = file_pos;
654 if (crossfade (data, subcnt, 1) != subcnt) {
659 Sample * tmpdata = data + subcnt;
662 subcnt = cnt - subcnt;
663 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
667 file_pos = ofilepos; // adjusted below
669 } else if (_capture_start) {
671 /* start of capture both occur within the data we are writing,
675 _capture_start = false;
676 _capture_end = false;
678 /* move to the correct location place */
679 file_pos = capture_start_frame - _timeline_position;
681 if (crossfade (data, cnt, 1) != cnt) {
685 } else if (_capture_end) {
687 /* end of capture both occur within the data we are writing,
691 _capture_start = false;
692 _capture_end = false;
694 if (crossfade (data, cnt, 0) != cnt) {
700 /* in the middle of recording */
702 if (write_float (data, file_pos, cnt) != cnt) {
707 update_length (file_pos + cnt);
709 if (_build_peakfiles) {
710 compute_and_write_peaks (data, file_pos, cnt, true, true);
719 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
721 set_timeline_position (when);
723 if (_flags & Broadcast) {
724 if (setup_broadcast_info (when, now, tnow)) {
729 return flush_header ();
733 SndFileSource::flush_header ()
736 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
741 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
745 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
751 SndFileSource::flush ()
754 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
759 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
763 // Hopefully everything OK
764 sf_write_sync (_sndfile);
768 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
771 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
776 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
780 if (!(_flags & Broadcast) || !_broadcast_info) {
784 _broadcast_info->set_originator_ref_from_session (_session);
785 _broadcast_info->set_origination_time (&now);
787 /* now update header position taking header offset into account */
789 set_header_timeline_position ();
795 SndFileSource::set_header_timeline_position ()
797 if (!(_flags & Broadcast)) {
800 assert (_broadcast_info);
802 _broadcast_info->set_time_reference (_timeline_position);
804 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
805 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
806 _path, _broadcast_info->get_error())
808 _flags = Flag (_flags & ~Broadcast);
809 delete _broadcast_info;
815 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
817 if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
818 assert (_length == frame_pos);
820 else if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
822 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
823 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
827 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
835 SndFileSource::natural_position() const
837 return _timeline_position;
840 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
842 SndFileSource::set_destructive (bool yn)
845 _flags = Flag (_flags | Writable | Destructive);
847 xfade_buf = new Sample[xfade_frames];
849 clear_capture_marks ();
850 _timeline_position = header_position_offset;
852 _flags = Flag (_flags & ~Destructive);
853 _timeline_position = 0;
854 /* leave xfade buf alone in case we need it again later */
862 SndFileSource::clear_capture_marks ()
864 _capture_start = false;
865 _capture_end = false;
868 /** @param pos Capture start position in session frames */
870 SndFileSource::mark_capture_start (framepos_t pos)
873 if (pos < _timeline_position) {
874 _capture_start = false;
876 _capture_start = true;
877 capture_start_frame = pos;
883 SndFileSource::mark_capture_end()
891 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
893 framecnt_t xfade = min (xfade_frames, cnt);
894 framecnt_t nofade = cnt - xfade;
895 Sample* fade_data = 0;
896 framepos_t fade_position = 0; // in frames
901 fade_position = file_pos;
904 fade_position = file_pos + nofade;
905 fade_data = data + nofade;
908 if (fade_position > _length) {
910 /* read starts beyond end of data, just memset to zero */
914 } else if (fade_position + xfade > _length) {
916 /* read ends beyond end of data, read some, memset the rest */
918 file_cnt = _length - fade_position;
922 /* read is entirely within data */
929 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
930 if (retval >= 0 && errno == EAGAIN) {
931 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
932 * short or no data there */
933 memset (xfade_buf, 0, xfade * sizeof(Sample));
935 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
941 if (file_cnt != xfade) {
942 framecnt_t delta = xfade - file_cnt;
943 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
946 if (nofade && !fade_in) {
947 if (write_float (data, file_pos, nofade) != nofade) {
948 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
953 if (xfade == xfade_frames) {
957 /* use the standard xfade curve */
961 /* fade new material in */
963 for (n = 0; n < xfade; ++n) {
964 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
970 /* fade new material out */
972 for (n = 0; n < xfade; ++n) {
973 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
977 } else if (xfade < xfade_frames) {
979 std::vector<gain_t> in(xfade);
980 std::vector<gain_t> out(xfade);
982 /* short xfade, compute custom curve */
984 compute_equal_power_fades (xfade, &in[0], &out[0]);
986 for (framecnt_t n = 0; n < xfade; ++n) {
987 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
992 /* long xfade length, has to be computed across several calls */
997 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
998 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
1003 if (fade_in && nofade) {
1004 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
1005 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
1014 SndFileSource::last_capture_start_frame () const
1016 if (destructive()) {
1017 return capture_start_frame;
1024 SndFileSource::handle_header_position_change ()
1026 if (destructive()) {
1027 if ( _length != 0 ) {
1028 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
1029 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
1030 } else if (writable()) {
1031 _timeline_position = header_position_offset;
1032 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
1038 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
1040 /* This static method is assumed to have been called by the Session
1041 before any DFS's are created.
1044 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
1046 delete [] out_coefficient;
1047 delete [] in_coefficient;
1049 out_coefficient = new gain_t[xfade_frames];
1050 in_coefficient = new gain_t[xfade_frames];
1052 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
1056 SndFileSource::set_timeline_position (framepos_t pos)
1058 // destructive track timeline postion does not change
1059 // except at instantion or when header_position_offset
1060 // (session start) changes
1062 if (!destructive()) {
1063 AudioFileSource::set_timeline_position (pos);
1068 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
1072 BroadcastInfo binfo;
1074 sf_info.format = 0; // libsndfile says to clear this before sf_open().
1076 if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
1080 #ifdef PLATFORM_WINDOWS
1081 int fd = g_open (path.c_str(), O_RDONLY, 0444);
1083 int fd = ::open (path.c_str(), O_RDONLY, 0444);
1087 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
1091 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
1093 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
1097 info.samplerate = sf_info.samplerate;
1098 info.channels = sf_info.channels;
1099 info.length = sf_info.frames;
1101 string major = sndfile_major_format(sf_info.format);
1102 string minor = sndfile_minor_format(sf_info.format);
1104 if (major.length() + minor.length() < 16) { /* arbitrary */
1105 info.format_name = string_compose("%1/%2", major, minor);
1107 info.format_name = string_compose("%1\n%2", major, minor);
1110 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1118 SndFileSource::one_of_several_channels () const
1120 return _info.channels > 1;
1124 SndFileSource::clamped_at_unity () const
1126 int const type = _info.format & SF_FORMAT_TYPEMASK;
1127 int const sub = _info.format & SF_FORMAT_SUBMASK;
1128 /* XXX: this may not be the full list of formats that are unclamped */
1129 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1133 SndFileSource::file_closed ()
1135 /* stupid libsndfile updated the headers on close,
1136 so touch the peakfile if it exists and has data
1137 to make sure its time is as new as the audio
1145 SndFileSource::set_path (const string& p)
1147 FileSource::set_path (p);