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_FLAC;
138 if (sfmt == FormatFloat) {
141 _flags = Flag (_flags & ~Broadcast);
142 _flags = Flag (_flags & ~Destructive); // XXX or force WAV if destructive?
146 fmt = SF_FORMAT_AIFF;
147 _flags = Flag (_flags & ~Broadcast);
152 _flags = Flag (_flags | Broadcast);
157 _flags = Flag (_flags & ~Broadcast);
162 _flags = Flag (_flags & ~Broadcast);
166 fmt = SF_FORMAT_RF64;
167 _flags = Flag (_flags & ~Broadcast);
168 _flags = Flag (_flags | RF64_RIFF);
172 fmt = SF_FORMAT_RF64;
173 _flags = Flag (_flags | Broadcast);
174 _flags = Flag (_flags | RF64_RIFF);
178 fmt = SF_FORMAT_RF64;
179 _flags = Flag (_flags & ~Broadcast);
183 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
184 abort(); /*NOTREACHED*/
191 fmt |= SF_FORMAT_FLOAT;
195 fmt |= SF_FORMAT_PCM_24;
199 fmt |= SF_FORMAT_PCM_16;
204 _info.samplerate = rate;
207 if (_flags & Destructive) {
209 throw failed_constructor();
212 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
217 /** Constructor to be called for recovering files being used for
218 * capture. They are in-session, they already exist, they should not
219 * be writable. They are an odd hybrid (from a constructor point of
220 * view) of the previous two constructors.
222 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
223 : Source (s, DataType::AUDIO, path, Flag (0))
224 /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
225 , AudioFileSource (s, path, Flag (0))
227 , _broadcast_info (0)
228 , _capture_start (false)
229 , _capture_end (false)
237 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
241 throw failed_constructor ();
245 /** Constructor to losslessly compress existing source to flac */
246 SndFileSource::SndFileSource (Session& s, const AudioFileSource& other, const string& path, bool use16bits, Progress* progress)
247 : Source(s, DataType::AUDIO, path, Flag ((other.flags () | default_writable_flags | NoPeakFile) & ~RF64_RIFF))
248 , AudioFileSource (s, path, "", Flag ((other.flags () | default_writable_flags | NoPeakFile) & ~RF64_RIFF), /*unused*/ FormatFloat, /*unused*/ WAVE64)
250 , _broadcast_info (0)
251 , _capture_start (false)
252 , _capture_end (false)
256 if (other.readable_length () == 0) {
257 throw failed_constructor();
260 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
268 _info.samplerate = other.sample_rate ();
269 _info.format = SF_FORMAT_FLAC | (use16bits ? SF_FORMAT_PCM_16 : SF_FORMAT_PCM_24);
271 /* flac is either read or write -- never both,
272 * so we need to special-case ::open () */
273 #ifdef PLATFORM_WINDOWS
274 int fd = g_open (_path.c_str(), O_CREAT | O_RDWR, 0644);
276 int fd = ::open (_path.c_str(), O_CREAT | O_RDWR, 0644);
279 throw failed_constructor();
282 _sndfile = sf_open_fd (fd, SFM_WRITE, &_info, true);
285 throw failed_constructor();
289 /* setting flac compression quality above the default does not produce a significant size
290 * improvement (not for large raw recordings anyway, the_CLA tests 2017-10-02, >> 250MB files,
291 * ~1% smaller), but does have a significant encoding speed penalty.
293 * We still may expose this as option someday though, perhaps for opposite reason: "fast encoding"
295 double flac_quality = 1; // libsndfile uses range 0..1 (mapped to flac 0..8), default is (5/8)
296 if (sf_command (_sndfile, SFC_SET_COMPRESSION_LEVEL, &flac_quality, sizeof (double)) != SF_TRUE) {
298 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
299 error << string_compose (_("Cannot set flac compression level: %1"), errbuf) << endmsg;
308 /* normalize before converting to fixed point, calc gain factor */
309 samplecnt_t len = other.read (buf, off, 8192, other.channel ());
311 peak = compute_peak (buf, len, peak);
313 len = other.read (buf, off, 8192, other.channel ());
315 progress->set_progress (0.5f * (float) off / other.readable_length ());
326 len = other.read (buf, off, 8192, other.channel ());
329 for (samplecnt_t i = 0; i < len; ++i) {
335 len = other.read (buf, off, 8192, other.channel ());
337 progress->set_progress (0.5f + 0.5f * (float) off / other.readable_length ());
343 SndFileSource::init_sndfile ()
345 /* although libsndfile says we don't need to set this,
346 valgrind and source code shows us that we do.
349 memset (&_info, 0, sizeof(_info));
352 xfade_buf = new Sample[xfade_samples];
353 _timeline_position = header_position_offset;
356 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
360 SndFileSource::close ()
370 SndFileSource::open ()
376 // We really only want to use g_open for all platforms but because of this
377 // method(SndfileSource::open), the compiler(or at least GCC) is confused
378 // because g_open will expand to "open" on non-POSIX systems and needs the
379 // global namespace qualifer. The problem is since since C99 ::g_open will
380 // apparently expand to ":: open"
381 #ifdef PLATFORM_WINDOWS
382 int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
384 int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
388 error << string_compose (
389 _ ("SndFileSource: cannot open file \"%1\" for %2"),
391 (writable () ? "read+write" : "reading")) << endmsg;
395 if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
396 assert (!destructive());
397 _sndfile = sf_open_fd (fd, writable () ? SFM_WRITE : SFM_READ, &_info, true);
399 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
404 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
405 #ifndef HAVE_COREAUDIO
406 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
407 so we don't want to see this message.
410 cerr << "failed to open " << _path << " with name " << _name << endl;
412 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
413 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
418 if (_channel >= _info.channels) {
419 #ifndef HAVE_COREAUDIO
420 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
427 _length = _info.frames;
429 #ifdef HAVE_RF64_RIFF
430 if (_file_is_new && _length == 0 && writable()) {
431 if (_flags & RF64_RIFF) {
432 if (sf_command (_sndfile, SFC_RF64_AUTO_DOWNGRADE, 0, 0) != SF_TRUE) {
434 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
435 error << string_compose (_("Cannot mark RF64 audio file for automatic downgrade to WAV: %1"), errbuf)
442 if (!_broadcast_info) {
443 _broadcast_info = new BroadcastInfo;
446 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
448 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
449 /* newly created files will not have a BWF header at this point in time.
450 * Import will have called Source::set_timeline_position() if one exists
451 * in the original. */
452 header_position_offset = _timeline_position;
455 /* Set our timeline position to either the time reference from a BWF header or the current
456 start of the session.
458 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
460 if (_length != 0 && !bwf_info_exists) {
461 delete _broadcast_info;
463 _flags = Flag (_flags & ~Broadcast);
466 /* Set the broadcast flag if the BWF info is already there. We need
467 * this when recovering or using existing files.
470 if (bwf_info_exists) {
471 _flags = Flag (_flags | Broadcast);
475 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
477 if (_flags & Broadcast) {
479 if (!_broadcast_info) {
480 _broadcast_info = new BroadcastInfo;
483 _broadcast_info->set_from_session (_session, header_position_offset);
484 _broadcast_info->set_description (string_compose ("BWF %1", _name));
486 if (!_broadcast_info->write_to_file (_sndfile)) {
487 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
488 _path, _broadcast_info->get_error())
490 _flags = Flag (_flags & ~Broadcast);
491 delete _broadcast_info;
500 SndFileSource::~SndFileSource ()
503 delete _broadcast_info;
508 SndFileSource::sample_rate () const
510 return _info.samplerate;
514 SndFileSource::read_unlocked (Sample *dst, samplepos_t start, samplecnt_t cnt) const
520 samplecnt_t real_cnt;
521 samplepos_t file_cnt;
523 if (writable() && !_sndfile) {
524 /* file has not been opened yet - nothing written to it */
525 memset (dst, 0, sizeof (Sample) * cnt);
529 if (const_cast<SndFileSource*>(this)->open()) {
530 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
534 if (start > _length) {
536 /* read starts beyond end of data, just memset to zero */
540 } else if (start + cnt > _length) {
542 /* read ends beyond end of data, read some, memset the rest */
544 file_cnt = _length - start;
548 /* read is entirely within data */
553 assert (file_cnt >= 0);
555 if (file_cnt != cnt) {
556 samplepos_t delta = cnt - file_cnt;
557 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
562 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
564 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
565 error << string_compose(_("SndFileSource: could not seek to sample %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
569 if (_info.channels == 1) {
570 samplecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
571 if (ret != file_cnt) {
573 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
574 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;
577 for (samplecnt_t i = 0; i < ret; ++i) {
585 real_cnt = cnt * _info.channels;
587 Sample* interleave_buf = get_interleave_buffer (real_cnt);
589 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
590 ptr = interleave_buf + _channel;
591 nread /= _info.channels;
593 /* stride through the interleaved data */
596 for (samplecnt_t n = 0; n < nread; ++n) {
597 dst[n] = *ptr * _gain;
598 ptr += _info.channels;
601 for (samplecnt_t n = 0; n < nread; ++n) {
603 ptr += _info.channels;
611 SndFileSource::write_unlocked (Sample *data, samplecnt_t cnt)
618 return destructive_write_unlocked (data, cnt);
620 return nondestructive_write_unlocked (data, cnt);
625 SndFileSource::nondestructive_write_unlocked (Sample *data, samplecnt_t cnt)
628 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
632 if (_info.channels != 1) {
633 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
634 abort(); /*NOTREACHED*/
638 samplepos_t sample_pos = _length;
640 if (write_float (data, sample_pos, cnt) != cnt) {
644 update_length (_length + cnt);
646 if (_build_peakfiles) {
647 compute_and_write_peaks (data, sample_pos, cnt, true, true);
654 SndFileSource::destructive_write_unlocked (Sample* data, samplecnt_t cnt)
657 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
661 if (_capture_start && _capture_end) {
663 /* start and end of capture both occur within the data we are writing,
664 so do both crossfades.
667 _capture_start = false;
668 _capture_end = false;
670 /* move to the correct location place */
671 file_pos = capture_start_sample - _timeline_position;
674 samplecnt_t subcnt = cnt / 2;
675 samplecnt_t ofilepos = file_pos;
678 if (crossfade (data, subcnt, 1) != subcnt) {
683 Sample * tmpdata = data + subcnt;
686 subcnt = cnt - subcnt;
687 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
691 file_pos = ofilepos; // adjusted below
693 } else if (_capture_start) {
695 /* start of capture both occur within the data we are writing,
699 _capture_start = false;
700 _capture_end = false;
702 /* move to the correct location place */
703 file_pos = capture_start_sample - _timeline_position;
705 if (crossfade (data, cnt, 1) != cnt) {
709 } else if (_capture_end) {
711 /* end of capture both occur within the data we are writing,
715 _capture_start = false;
716 _capture_end = false;
718 if (crossfade (data, cnt, 0) != cnt) {
724 /* in the middle of recording */
726 if (write_float (data, file_pos, cnt) != cnt) {
731 update_length (file_pos + cnt);
733 if (_build_peakfiles) {
734 compute_and_write_peaks (data, file_pos, cnt, true, true);
743 SndFileSource::update_header (samplepos_t when, struct tm& now, time_t tnow)
745 set_timeline_position (when);
747 if (_flags & Broadcast) {
748 if (setup_broadcast_info (when, now, tnow)) {
753 return flush_header ();
757 SndFileSource::flush_header ()
760 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
765 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
769 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
775 SndFileSource::flush ()
778 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
783 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
787 // Hopefully everything OK
788 sf_write_sync (_sndfile);
792 SndFileSource::setup_broadcast_info (samplepos_t /*when*/, struct tm& now, time_t /*tnow*/)
795 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
800 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
804 if (!(_flags & Broadcast) || !_broadcast_info) {
808 _broadcast_info->set_originator_ref_from_session (_session);
809 _broadcast_info->set_origination_time (&now);
811 /* now update header position taking header offset into account */
813 set_header_timeline_position ();
819 SndFileSource::set_header_timeline_position ()
821 if (!(_flags & Broadcast)) {
824 assert (_broadcast_info);
826 _broadcast_info->set_time_reference (_timeline_position);
828 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
829 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
830 _path, _broadcast_info->get_error())
832 _flags = Flag (_flags & ~Broadcast);
833 delete _broadcast_info;
839 SndFileSource::write_float (Sample* data, samplepos_t sample_pos, samplecnt_t cnt)
841 if ((_info.format & SF_FORMAT_TYPEMASK ) == SF_FORMAT_FLAC) {
842 assert (_length == sample_pos);
844 else if (_sndfile == 0 || sf_seek (_sndfile, sample_pos, SEEK_SET|SFM_WRITE) < 0) {
846 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
847 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, sample_pos, errbuf) << endmsg;
851 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
859 SndFileSource::natural_position() const
861 return _timeline_position;
865 SndFileSource::clear_capture_marks ()
867 _capture_start = false;
868 _capture_end = false;
871 /** @param pos Capture start position in session samples */
873 SndFileSource::mark_capture_start (samplepos_t pos)
876 if (pos < _timeline_position) {
877 _capture_start = false;
879 _capture_start = true;
880 capture_start_sample = pos;
886 SndFileSource::mark_capture_end()
894 SndFileSource::crossfade (Sample* data, samplecnt_t cnt, int fade_in)
896 samplecnt_t xfade = min (xfade_samples, cnt);
897 samplecnt_t nofade = cnt - xfade;
898 Sample* fade_data = 0;
899 samplepos_t fade_position = 0; // in samples
901 samplecnt_t file_cnt;
904 fade_position = file_pos;
907 fade_position = file_pos + nofade;
908 fade_data = data + nofade;
911 if (fade_position > _length) {
913 /* read starts beyond end of data, just memset to zero */
917 } else if (fade_position + xfade > _length) {
919 /* read ends beyond end of data, read some, memset the rest */
921 file_cnt = _length - fade_position;
925 /* read is entirely within data */
932 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
933 if (retval >= 0 && errno == EAGAIN) {
934 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
935 * short or no data there */
936 memset (xfade_buf, 0, xfade * sizeof(Sample));
938 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
944 if (file_cnt != xfade) {
945 samplecnt_t delta = xfade - file_cnt;
946 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
949 if (nofade && !fade_in) {
950 if (write_float (data, file_pos, nofade) != nofade) {
951 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
956 if (xfade == xfade_samples) {
960 /* use the standard xfade curve */
964 /* fade new material in */
966 for (n = 0; n < xfade; ++n) {
967 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
973 /* fade new material out */
975 for (n = 0; n < xfade; ++n) {
976 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
980 } else if (xfade < xfade_samples) {
982 std::vector<gain_t> in(xfade);
983 std::vector<gain_t> out(xfade);
985 /* short xfade, compute custom curve */
987 compute_equal_power_fades (xfade, &in[0], &out[0]);
989 for (samplecnt_t n = 0; n < xfade; ++n) {
990 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
995 /* long xfade length, has to be computed across several calls */
1000 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
1001 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
1006 if (fade_in && nofade) {
1007 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
1008 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
1017 SndFileSource::last_capture_start_sample () const
1019 if (destructive()) {
1020 return capture_start_sample;
1027 SndFileSource::handle_header_position_change ()
1029 if (destructive()) {
1030 if ( _length != 0 ) {
1031 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
1032 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
1033 } else if (writable()) {
1034 _timeline_position = header_position_offset;
1035 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
1041 SndFileSource::setup_standard_crossfades (Session const & s, samplecnt_t rate)
1043 /* This static method is assumed to have been called by the Session
1044 before any DFS's are created.
1047 xfade_samples = (samplecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
1049 delete [] out_coefficient;
1050 delete [] in_coefficient;
1052 out_coefficient = new gain_t[xfade_samples];
1053 in_coefficient = new gain_t[xfade_samples];
1055 compute_equal_power_fades (xfade_samples, in_coefficient, out_coefficient);
1059 SndFileSource::set_timeline_position (samplepos_t pos)
1061 // destructive track timeline postion does not change
1062 // except at instantion or when header_position_offset
1063 // (session start) changes
1065 if (!destructive()) {
1066 AudioFileSource::set_timeline_position (pos);
1071 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
1075 BroadcastInfo binfo;
1077 sf_info.format = 0; // libsndfile says to clear this before sf_open().
1079 if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
1083 #ifdef PLATFORM_WINDOWS
1084 int fd = g_open (path.c_str(), O_RDONLY, 0444);
1086 int fd = ::open (path.c_str(), O_RDONLY, 0444);
1090 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
1094 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
1096 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
1100 info.samplerate = sf_info.samplerate;
1101 info.channels = sf_info.channels;
1102 info.length = sf_info.frames;
1104 string major = sndfile_major_format(sf_info.format);
1105 string minor = sndfile_minor_format(sf_info.format);
1107 if (major.length() + minor.length() < 16) { /* arbitrary */
1108 info.format_name = string_compose("%1/%2", major, minor);
1110 info.format_name = string_compose("%1\n%2", major, minor);
1113 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1121 SndFileSource::one_of_several_channels () const
1123 return _info.channels > 1;
1127 SndFileSource::clamped_at_unity () const
1129 int const type = _info.format & SF_FORMAT_TYPEMASK;
1130 int const sub = _info.format & SF_FORMAT_SUBMASK;
1131 /* XXX: this may not be the full list of formats that are unclamped */
1132 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1136 SndFileSource::file_closed ()
1138 /* stupid libsndfile updated the headers on close,
1139 so touch the peakfile if it exists and has data
1140 to make sure its time is as new as the audio
1148 SndFileSource::set_path (const string& p)
1150 FileSource::set_path (p);