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 ()
263 SndFileSource::open ()
269 // We really only want to use g_open for all platforms but because of this
270 // method(SndfileSource::open), the compiler(or at least GCC) is confused
271 // because g_open will expand to "open" on non-POSIX systems and needs the
272 // global namespace qualifer. The problem is since since C99 ::g_open will
273 // apparently expand to ":: open"
274 #ifdef PLATFORM_WINDOWS
275 int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
277 int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
281 error << string_compose (
282 _ ("SndFileSource: cannot open file \"%1\" for %2"),
284 (writable () ? "read+write" : "reading")) << endmsg;
288 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
292 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
293 #ifndef HAVE_COREAUDIO
294 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
295 so we don't want to see this message.
298 cerr << "failed to open " << _path << " with name " << _name << endl;
300 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
301 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
306 if (_channel >= _info.channels) {
307 #ifndef HAVE_COREAUDIO
308 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
315 _length = _info.frames;
317 #ifdef HAVE_RF64_RIFF
318 if (_file_is_new && _length == 0 && writable()) {
319 if (_flags & RF64_RIFF) {
320 if (sf_command (_sndfile, SFC_RF64_AUTO_DOWNGRADE, 0, 0) != SF_TRUE) {
322 sf_error_str (_sndfile, errbuf, sizeof (errbuf) - 1);
323 error << string_compose (_("Cannot mark RF64 audio file for automatic downgrade to WAV: %1"), errbuf)
330 if (!_broadcast_info) {
331 _broadcast_info = new BroadcastInfo;
334 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
336 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
337 /* newly created files will not have a BWF header at this point in time.
338 * Import will have called Source::set_timeline_position() if one exists
339 * in the original. */
340 header_position_offset = _timeline_position;
343 /* Set our timeline position to either the time reference from a BWF header or the current
344 start of the session.
346 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
348 if (_length != 0 && !bwf_info_exists) {
349 delete _broadcast_info;
351 _flags = Flag (_flags & ~Broadcast);
354 /* Set the broadcast flag if the BWF info is already there. We need
355 * this when recovering or using existing files.
358 if (bwf_info_exists) {
359 _flags = Flag (_flags | Broadcast);
363 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
365 if (_flags & Broadcast) {
367 if (!_broadcast_info) {
368 _broadcast_info = new BroadcastInfo;
371 _broadcast_info->set_from_session (_session, header_position_offset);
372 _broadcast_info->set_description (string_compose ("BWF %1", _name));
374 if (!_broadcast_info->write_to_file (_sndfile)) {
375 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
376 _path, _broadcast_info->get_error())
378 _flags = Flag (_flags & ~Broadcast);
379 delete _broadcast_info;
388 SndFileSource::~SndFileSource ()
391 delete _broadcast_info;
396 SndFileSource::sample_rate () const
398 return _info.samplerate;
402 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
411 if (writable() && !_sndfile) {
412 /* file has not been opened yet - nothing written to it */
413 memset (dst, 0, sizeof (Sample) * cnt);
417 if (const_cast<SndFileSource*>(this)->open()) {
418 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
422 if (start > _length) {
424 /* read starts beyond end of data, just memset to zero */
428 } else if (start + cnt > _length) {
430 /* read ends beyond end of data, read some, memset the rest */
432 file_cnt = _length - start;
436 /* read is entirely within data */
441 assert (file_cnt >= 0);
443 if (file_cnt != cnt) {
444 framepos_t delta = cnt - file_cnt;
445 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
450 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
452 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
453 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
457 if (_info.channels == 1) {
458 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
459 if (ret != file_cnt) {
461 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
462 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;
468 real_cnt = cnt * _info.channels;
470 Sample* interleave_buf = get_interleave_buffer (real_cnt);
472 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
473 ptr = interleave_buf + _channel;
474 nread /= _info.channels;
476 /* stride through the interleaved data */
478 for (framecnt_t n = 0; n < nread; ++n) {
480 ptr += _info.channels;
487 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
494 return destructive_write_unlocked (data, cnt);
496 return nondestructive_write_unlocked (data, cnt);
501 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
504 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
508 if (_info.channels != 1) {
509 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
510 abort(); /*NOTREACHED*/
514 framepos_t frame_pos = _length;
516 if (write_float (data, frame_pos, cnt) != cnt) {
520 update_length (_length + cnt);
522 if (_build_peakfiles) {
523 compute_and_write_peaks (data, frame_pos, cnt, true, true);
530 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
533 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
537 if (_capture_start && _capture_end) {
539 /* start and end of capture both occur within the data we are writing,
540 so do both crossfades.
543 _capture_start = false;
544 _capture_end = false;
546 /* move to the correct location place */
547 file_pos = capture_start_frame - _timeline_position;
550 framecnt_t subcnt = cnt / 2;
551 framecnt_t ofilepos = file_pos;
554 if (crossfade (data, subcnt, 1) != subcnt) {
559 Sample * tmpdata = data + subcnt;
562 subcnt = cnt - subcnt;
563 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
567 file_pos = ofilepos; // adjusted below
569 } else if (_capture_start) {
571 /* start of capture both occur within the data we are writing,
575 _capture_start = false;
576 _capture_end = false;
578 /* move to the correct location place */
579 file_pos = capture_start_frame - _timeline_position;
581 if (crossfade (data, cnt, 1) != cnt) {
585 } else if (_capture_end) {
587 /* end of capture both occur within the data we are writing,
591 _capture_start = false;
592 _capture_end = false;
594 if (crossfade (data, cnt, 0) != cnt) {
600 /* in the middle of recording */
602 if (write_float (data, file_pos, cnt) != cnt) {
607 update_length (file_pos + cnt);
609 if (_build_peakfiles) {
610 compute_and_write_peaks (data, file_pos, cnt, true, true);
619 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
621 set_timeline_position (when);
623 if (_flags & Broadcast) {
624 if (setup_broadcast_info (when, now, tnow)) {
629 return flush_header ();
633 SndFileSource::flush_header ()
636 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
641 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
645 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
651 SndFileSource::flush ()
654 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
659 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
663 // Hopefully everything OK
664 sf_write_sync (_sndfile);
668 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
671 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
676 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
680 if (!(_flags & Broadcast) || !_broadcast_info) {
684 _broadcast_info->set_originator_ref_from_session (_session);
685 _broadcast_info->set_origination_time (&now);
687 /* now update header position taking header offset into account */
689 set_header_timeline_position ();
695 SndFileSource::set_header_timeline_position ()
697 if (!(_flags & Broadcast)) {
700 assert (_broadcast_info);
702 _broadcast_info->set_time_reference (_timeline_position);
704 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
705 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
706 _path, _broadcast_info->get_error())
708 _flags = Flag (_flags & ~Broadcast);
709 delete _broadcast_info;
715 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
717 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
719 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
720 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
724 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
732 SndFileSource::natural_position() const
734 return _timeline_position;
738 SndFileSource::set_destructive (bool yn)
741 _flags = Flag (_flags | Writable | Destructive);
743 xfade_buf = new Sample[xfade_frames];
745 clear_capture_marks ();
746 _timeline_position = header_position_offset;
748 _flags = Flag (_flags & ~Destructive);
749 _timeline_position = 0;
750 /* leave xfade buf alone in case we need it again later */
757 SndFileSource::clear_capture_marks ()
759 _capture_start = false;
760 _capture_end = false;
763 /** @param pos Capture start position in session frames */
765 SndFileSource::mark_capture_start (framepos_t pos)
768 if (pos < _timeline_position) {
769 _capture_start = false;
771 _capture_start = true;
772 capture_start_frame = pos;
778 SndFileSource::mark_capture_end()
786 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
788 framecnt_t xfade = min (xfade_frames, cnt);
789 framecnt_t nofade = cnt - xfade;
790 Sample* fade_data = 0;
791 framepos_t fade_position = 0; // in frames
796 fade_position = file_pos;
799 fade_position = file_pos + nofade;
800 fade_data = data + nofade;
803 if (fade_position > _length) {
805 /* read starts beyond end of data, just memset to zero */
809 } else if (fade_position + xfade > _length) {
811 /* read ends beyond end of data, read some, memset the rest */
813 file_cnt = _length - fade_position;
817 /* read is entirely within data */
824 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
825 if (retval >= 0 && errno == EAGAIN) {
826 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
827 * short or no data there */
828 memset (xfade_buf, 0, xfade * sizeof(Sample));
830 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
836 if (file_cnt != xfade) {
837 framecnt_t delta = xfade - file_cnt;
838 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
841 if (nofade && !fade_in) {
842 if (write_float (data, file_pos, nofade) != nofade) {
843 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
848 if (xfade == xfade_frames) {
852 /* use the standard xfade curve */
856 /* fade new material in */
858 for (n = 0; n < xfade; ++n) {
859 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
865 /* fade new material out */
867 for (n = 0; n < xfade; ++n) {
868 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
872 } else if (xfade < xfade_frames) {
874 std::vector<gain_t> in(xfade);
875 std::vector<gain_t> out(xfade);
877 /* short xfade, compute custom curve */
879 compute_equal_power_fades (xfade, &in[0], &out[0]);
881 for (framecnt_t n = 0; n < xfade; ++n) {
882 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
887 /* long xfade length, has to be computed across several calls */
892 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
893 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
898 if (fade_in && nofade) {
899 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
900 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
909 SndFileSource::last_capture_start_frame () const
912 return capture_start_frame;
919 SndFileSource::handle_header_position_change ()
922 if ( _length != 0 ) {
923 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
924 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
925 } else if (writable()) {
926 _timeline_position = header_position_offset;
927 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
933 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
935 /* This static method is assumed to have been called by the Session
936 before any DFS's are created.
939 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
941 delete [] out_coefficient;
942 delete [] in_coefficient;
944 out_coefficient = new gain_t[xfade_frames];
945 in_coefficient = new gain_t[xfade_frames];
947 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
951 SndFileSource::set_timeline_position (framepos_t pos)
953 // destructive track timeline postion does not change
954 // except at instantion or when header_position_offset
955 // (session start) changes
957 if (!destructive()) {
958 AudioFileSource::set_timeline_position (pos);
963 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
969 sf_info.format = 0; // libsndfile says to clear this before sf_open().
971 if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
975 #ifdef PLATFORM_WINDOWS
976 int fd = g_open (path.c_str(), O_RDONLY, 0444);
978 int fd = ::open (path.c_str(), O_RDONLY, 0444);
982 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
986 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
988 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
992 info.samplerate = sf_info.samplerate;
993 info.channels = sf_info.channels;
994 info.length = sf_info.frames;
996 string major = sndfile_major_format(sf_info.format);
997 string minor = sndfile_minor_format(sf_info.format);
999 if (major.length() + minor.length() < 16) { /* arbitrary */
1000 info.format_name = string_compose("%1/%2", major, minor);
1002 info.format_name = string_compose("%1\n%2", major, minor);
1005 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
1013 SndFileSource::one_of_several_channels () const
1015 return _info.channels > 1;
1019 SndFileSource::clamped_at_unity () const
1021 int const type = _info.format & SF_FORMAT_TYPEMASK;
1022 int const sub = _info.format & SF_FORMAT_SUBMASK;
1023 /* XXX: this may not be the full list of formats that are unclamped */
1024 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
1028 SndFileSource::file_closed ()
1030 /* stupid libsndfile updated the headers on close,
1031 so touch the peakfile if it exists and has data
1032 to make sure its time is as new as the audio
1040 SndFileSource::set_path (const string& p)
1042 FileSource::set_path (p);