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/sndfilesource.h"
40 #include "ardour/sndfile_helpers.h"
41 #include "ardour/utils.h"
42 #include "ardour/session.h"
47 using namespace ARDOUR;
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 (
57 Source::RemovableIfEmpty |
60 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
62 , AudioFileSource (s, node)
65 , _capture_start (false)
66 , _capture_end (false)
72 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
76 throw failed_constructor ();
80 /** Constructor for existing external-to-session files.
81 Files created this way are never writable or removable
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)))
89 , _capture_start (false)
90 , _capture_end (false)
98 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
102 throw failed_constructor ();
106 /** This constructor is used to construct new internal-to-session files,
107 not open existing ones.
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)
114 , _broadcast_info (0)
115 , _capture_start (false)
116 , _capture_end (false)
124 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
132 _flags = Flag (_flags & ~Broadcast);
136 fmt = SF_FORMAT_AIFF;
137 _flags = Flag (_flags & ~Broadcast);
142 _flags = Flag (_flags | Broadcast);
147 _flags = Flag (_flags & ~Broadcast);
152 _flags = Flag (_flags & ~Broadcast);
156 fmt = SF_FORMAT_RF64;
157 _flags = Flag (_flags & ~Broadcast);
158 _flags = Flag (_flags | RF64_RIFF);
162 fmt = SF_FORMAT_RF64;
163 _flags = Flag (_flags | Broadcast);
164 _flags = Flag (_flags | RF64_RIFF);
168 fmt = SF_FORMAT_RF64;
169 _flags = Flag (_flags & ~Broadcast);
173 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
174 abort(); /*NOTREACHED*/
181 fmt |= SF_FORMAT_FLOAT;
185 fmt |= SF_FORMAT_PCM_24;
189 fmt |= SF_FORMAT_PCM_16;
194 _info.samplerate = rate;
197 if (_flags & Destructive) {
199 throw failed_constructor();
202 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
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.
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))
217 , _broadcast_info (0)
218 , _capture_start (false)
219 , _capture_end (false)
227 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
231 throw failed_constructor ();
236 SndFileSource::init_sndfile ()
238 /* although libsndfile says we don't need to set this,
239 valgrind and source code shows us that we do.
242 memset (&_info, 0, sizeof(_info));
245 xfade_buf = new Sample[xfade_frames];
246 _timeline_position = header_position_offset;
249 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
253 SndFileSource::close ()
262 SndFileSource::open ()
268 // We really only want to use g_open for all platforms but because of this
269 // method(SndfileSource::open), the compiler(or at least GCC) is confused
270 // because g_open will expand to "open" on non-POSIX systems and needs the
271 // global namespace qualifer. The problem is since since C99 ::g_open will
272 // apparently expand to ":: open"
273 #ifdef PLATFORM_WINDOWS
274 int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
276 int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
280 error << string_compose (
281 _ ("SndFileSource: cannot open file \"%1\" for %2"),
283 (writable () ? "read+write" : "reading")) << endmsg;
287 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
291 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
292 #ifndef HAVE_COREAUDIO
293 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
294 so we don't want to see this message.
297 cerr << "failed to open " << _path << " with name " << _name << endl;
299 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
300 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
305 if (_channel >= _info.channels) {
306 #ifndef HAVE_COREAUDIO
307 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
314 _length = _info.frames;
316 #ifdef HAVE_RF64_RIFF
317 if (_file_is_new && _length == 0 && writable()) {
318 if (_flags & RF64_RIFF) {
319 if (sf_command (_sndfile, SFC_AUTO_DOWNGRADE_RF64, 0, 0) != SF_TRUE) {
321 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
322 error << string_compose (_("Cannot mark RF64 audio file for automatic downgrade to WAV: %1"), errbuf)
329 if (!_broadcast_info) {
330 _broadcast_info = new BroadcastInfo;
333 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
335 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
336 /* newly created files will not have a BWF header at this point in time.
337 * Import will have called Source::set_timeline_position() if one exists
338 * in the original. */
339 header_position_offset = _timeline_position;
342 /* Set our timeline position to either the time reference from a BWF header or the current
343 start of the session.
345 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
347 if (_length != 0 && !bwf_info_exists) {
348 delete _broadcast_info;
350 _flags = Flag (_flags & ~Broadcast);
353 /* Set the broadcast flag if the BWF info is already there. We need
354 * this when recovering or using existing files.
357 if (bwf_info_exists) {
358 _flags = Flag (_flags | Broadcast);
362 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
364 if (_flags & Broadcast) {
366 if (!_broadcast_info) {
367 _broadcast_info = new BroadcastInfo;
370 _broadcast_info->set_from_session (_session, header_position_offset);
371 _broadcast_info->set_description (string_compose ("BWF %1", _name));
373 if (!_broadcast_info->write_to_file (_sndfile)) {
374 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
375 _path, _broadcast_info->get_error())
377 _flags = Flag (_flags & ~Broadcast);
378 delete _broadcast_info;
387 SndFileSource::~SndFileSource ()
390 delete _broadcast_info;
395 SndFileSource::sample_rate () const
397 return _info.samplerate;
401 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
410 if (writable() && !_sndfile) {
411 /* file has not been opened yet - nothing written to it */
412 memset (dst, 0, sizeof (Sample) * cnt);
416 if (const_cast<SndFileSource*>(this)->open()) {
417 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
421 if (start > _length) {
423 /* read starts beyond end of data, just memset to zero */
427 } else if (start + cnt > _length) {
429 /* read ends beyond end of data, read some, memset the rest */
431 file_cnt = _length - start;
435 /* read is entirely within data */
440 assert (file_cnt >= 0);
442 if (file_cnt != cnt) {
443 framepos_t delta = cnt - file_cnt;
444 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
449 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
451 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
452 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
456 if (_info.channels == 1) {
457 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
458 if (ret != file_cnt) {
460 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
461 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;
467 real_cnt = cnt * _info.channels;
469 Sample* interleave_buf = get_interleave_buffer (real_cnt);
471 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
472 ptr = interleave_buf + _channel;
473 nread /= _info.channels;
475 /* stride through the interleaved data */
477 for (framecnt_t n = 0; n < nread; ++n) {
479 ptr += _info.channels;
486 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
493 return destructive_write_unlocked (data, cnt);
495 return nondestructive_write_unlocked (data, cnt);
500 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
503 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
507 if (_info.channels != 1) {
508 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
509 abort(); /*NOTREACHED*/
513 framepos_t frame_pos = _length;
515 if (write_float (data, frame_pos, cnt) != cnt) {
519 update_length (_length + cnt);
521 if (_build_peakfiles) {
522 compute_and_write_peaks (data, frame_pos, cnt, true, true);
529 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
532 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
536 if (_capture_start && _capture_end) {
538 /* start and end of capture both occur within the data we are writing,
539 so do both crossfades.
542 _capture_start = false;
543 _capture_end = false;
545 /* move to the correct location place */
546 file_pos = capture_start_frame - _timeline_position;
549 framecnt_t subcnt = cnt / 2;
550 framecnt_t ofilepos = file_pos;
553 if (crossfade (data, subcnt, 1) != subcnt) {
558 Sample * tmpdata = data + subcnt;
561 subcnt = cnt - subcnt;
562 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
566 file_pos = ofilepos; // adjusted below
568 } else if (_capture_start) {
570 /* start of capture both occur within the data we are writing,
574 _capture_start = false;
575 _capture_end = false;
577 /* move to the correct location place */
578 file_pos = capture_start_frame - _timeline_position;
580 if (crossfade (data, cnt, 1) != cnt) {
584 } else if (_capture_end) {
586 /* end of capture both occur within the data we are writing,
590 _capture_start = false;
591 _capture_end = false;
593 if (crossfade (data, cnt, 0) != cnt) {
599 /* in the middle of recording */
601 if (write_float (data, file_pos, cnt) != cnt) {
606 update_length (file_pos + cnt);
608 if (_build_peakfiles) {
609 compute_and_write_peaks (data, file_pos, cnt, true, true);
618 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
620 set_timeline_position (when);
622 if (_flags & Broadcast) {
623 if (setup_broadcast_info (when, now, tnow)) {
628 return flush_header ();
632 SndFileSource::flush_header ()
635 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
640 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
644 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
650 SndFileSource::flush ()
653 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
658 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
662 // Hopefully everything OK
663 sf_write_sync (_sndfile);
667 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
670 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
675 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
679 if (!(_flags & Broadcast) || !_broadcast_info) {
683 _broadcast_info->set_originator_ref_from_session (_session);
684 _broadcast_info->set_origination_time (&now);
686 /* now update header position taking header offset into account */
688 set_header_timeline_position ();
694 SndFileSource::set_header_timeline_position ()
696 if (!(_flags & Broadcast)) {
699 assert (_broadcast_info);
701 _broadcast_info->set_time_reference (_timeline_position);
703 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
704 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
705 _path, _broadcast_info->get_error())
707 _flags = Flag (_flags & ~Broadcast);
708 delete _broadcast_info;
714 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
716 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
718 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
719 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
723 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
731 SndFileSource::natural_position() const
733 return _timeline_position;
737 SndFileSource::set_destructive (bool yn)
740 _flags = Flag (_flags | Writable | Destructive);
742 xfade_buf = new Sample[xfade_frames];
744 clear_capture_marks ();
745 _timeline_position = header_position_offset;
747 _flags = Flag (_flags & ~Destructive);
748 _timeline_position = 0;
749 /* leave xfade buf alone in case we need it again later */
756 SndFileSource::clear_capture_marks ()
758 _capture_start = false;
759 _capture_end = false;
762 /** @param pos Capture start position in session frames */
764 SndFileSource::mark_capture_start (framepos_t pos)
767 if (pos < _timeline_position) {
768 _capture_start = false;
770 _capture_start = true;
771 capture_start_frame = pos;
777 SndFileSource::mark_capture_end()
785 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
787 framecnt_t xfade = min (xfade_frames, cnt);
788 framecnt_t nofade = cnt - xfade;
789 Sample* fade_data = 0;
790 framepos_t fade_position = 0; // in frames
795 fade_position = file_pos;
798 fade_position = file_pos + nofade;
799 fade_data = data + nofade;
802 if (fade_position > _length) {
804 /* read starts beyond end of data, just memset to zero */
808 } else if (fade_position + xfade > _length) {
810 /* read ends beyond end of data, read some, memset the rest */
812 file_cnt = _length - fade_position;
816 /* read is entirely within data */
823 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
824 if (retval >= 0 && errno == EAGAIN) {
825 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
826 * short or no data there */
827 memset (xfade_buf, 0, xfade * sizeof(Sample));
829 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
835 if (file_cnt != xfade) {
836 framecnt_t delta = xfade - file_cnt;
837 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
840 if (nofade && !fade_in) {
841 if (write_float (data, file_pos, nofade) != nofade) {
842 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
847 if (xfade == xfade_frames) {
851 /* use the standard xfade curve */
855 /* fade new material in */
857 for (n = 0; n < xfade; ++n) {
858 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
864 /* fade new material out */
866 for (n = 0; n < xfade; ++n) {
867 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
871 } else if (xfade < xfade_frames) {
873 std::vector<gain_t> in(xfade);
874 std::vector<gain_t> out(xfade);
876 /* short xfade, compute custom curve */
878 compute_equal_power_fades (xfade, &in[0], &out[0]);
880 for (framecnt_t n = 0; n < xfade; ++n) {
881 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
886 /* long xfade length, has to be computed across several calls */
891 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
892 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
897 if (fade_in && nofade) {
898 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
899 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
908 SndFileSource::last_capture_start_frame () const
911 return capture_start_frame;
918 SndFileSource::handle_header_position_change ()
921 if ( _length != 0 ) {
922 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
923 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
924 } else if (writable()) {
925 _timeline_position = header_position_offset;
926 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
932 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
934 /* This static method is assumed to have been called by the Session
935 before any DFS's are created.
938 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
940 delete [] out_coefficient;
941 delete [] in_coefficient;
943 out_coefficient = new gain_t[xfade_frames];
944 in_coefficient = new gain_t[xfade_frames];
946 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
950 SndFileSource::set_timeline_position (framepos_t pos)
952 // destructive track timeline postion does not change
953 // except at instantion or when header_position_offset
954 // (session start) changes
956 if (!destructive()) {
957 AudioFileSource::set_timeline_position (pos);
962 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
968 sf_info.format = 0; // libsndfile says to clear this before sf_open().
970 if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
974 #ifdef PLATFORM_WINDOWS
975 int fd = g_open (path.c_str(), O_RDONLY, 0444);
977 int fd = ::open (path.c_str(), O_RDONLY, 0444);
981 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
985 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
987 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
991 info.samplerate = sf_info.samplerate;
992 info.channels = sf_info.channels;
993 info.length = sf_info.frames;
995 string major = sndfile_major_format(sf_info.format);
996 string minor = sndfile_minor_format(sf_info.format);
998 if (major.length() + minor.length() < 16) { /* arbitrary */
999 info.format_name = string_compose("%1/%2", major, minor);
1001 info.format_name = string_compose("%1\n%2", major, minor);
1004 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1012 SndFileSource::one_of_several_channels () const
1014 return _info.channels > 1;
1018 SndFileSource::clamped_at_unity () const
1020 int const type = _info.format & SF_FORMAT_TYPEMASK;
1021 int const sub = _info.format & SF_FORMAT_SUBMASK;
1022 /* XXX: this may not be the full list of formats that are unclamped */
1023 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1027 SndFileSource::file_closed ()
1029 /* stupid libsndfile updated the headers on close,
1030 so touch the peakfile if it exists and has data
1031 to make sure its time is as new as the audio
1039 SndFileSource::set_path (const string& p)
1041 FileSource::set_path (p);