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"
31 #include <glib/gstdio.h>
33 #ifdef PLATFORM_WINDOWS
34 #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"
49 using namespace ARDOUR;
53 gain_t* SndFileSource::out_coefficient = 0;
54 gain_t* SndFileSource::in_coefficient = 0;
55 framecnt_t SndFileSource::xfade_frames = 64;
56 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
59 Source::RemovableIfEmpty |
62 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
64 , AudioFileSource (s, node)
67 , _capture_start (false)
68 , _capture_end (false)
74 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
78 throw failed_constructor ();
82 /** Constructor for existing external-to-session files.
83 Files created this way are never writable or removable
85 SndFileSource::SndFileSource (Session& s, const string& path, int chn, Flag flags)
86 : Source(s, DataType::AUDIO, path, flags)
87 /* note that the origin of an external file is itself */
88 , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
91 , _capture_start (false)
92 , _capture_end (false)
100 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
104 throw failed_constructor ();
108 /** This constructor is used to construct new internal-to-session files,
109 not open existing ones.
111 SndFileSource::SndFileSource (Session& s, const string& path, const string& origin,
112 SampleFormat sfmt, HeaderFormat hf, framecnt_t rate, Flag flags)
113 : Source(s, DataType::AUDIO, path, flags)
114 , AudioFileSource (s, path, origin, flags, sfmt, hf)
116 , _broadcast_info (0)
117 , _capture_start (false)
118 , _capture_end (false)
126 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
134 _flags = Flag (_flags & ~Broadcast);
138 fmt = SF_FORMAT_AIFF;
139 _flags = Flag (_flags & ~Broadcast);
144 _flags = Flag (_flags | Broadcast);
149 _flags = Flag (_flags & ~Broadcast);
154 _flags = Flag (_flags & ~Broadcast);
158 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
159 abort(); /*NOTREACHED*/
166 fmt |= SF_FORMAT_FLOAT;
170 fmt |= SF_FORMAT_PCM_24;
174 fmt |= SF_FORMAT_PCM_16;
179 _info.samplerate = rate;
182 if (_flags & Destructive) {
184 throw failed_constructor();
187 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
192 /** Constructor to be called for recovering files being used for
193 * capture. They are in-session, they already exist, they should not
194 * be writable. They are an odd hybrid (from a constructor point of
195 * view) of the previous two constructors.
197 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
198 : Source (s, DataType::AUDIO, path, Flag (0))
199 /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
200 , AudioFileSource (s, path, Flag (0))
202 , _broadcast_info (0)
203 , _capture_start (false)
204 , _capture_end (false)
212 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
216 throw failed_constructor ();
221 SndFileSource::init_sndfile ()
223 /* although libsndfile says we don't need to set this,
224 valgrind and source code shows us that we do.
227 memset (&_info, 0, sizeof(_info));
230 xfade_buf = new Sample[xfade_frames];
231 _timeline_position = header_position_offset;
234 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
238 SndFileSource::close ()
247 SndFileSource::open ()
255 #ifdef PLATFORM_WINDOWS
256 path_to_open = Glib::locale_from_utf8(_path);
258 path_to_open = _path;
261 _sndfile = sf_open (path_to_open.c_str(), writable() ? SFM_RDWR : SFM_READ, &_info);
265 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
266 #ifndef HAVE_COREAUDIO
267 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
268 so we don't want to see this message.
271 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
273 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
274 path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
279 if (_channel >= _info.channels) {
280 #ifndef HAVE_COREAUDIO
281 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
288 _length = _info.frames;
290 if (!_broadcast_info) {
291 _broadcast_info = new BroadcastInfo;
294 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
296 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
297 /* newly created files will not have a BWF header at this point in time.
298 * Import will have called Source::set_timeline_position() if one exists
299 * in the original. */
300 header_position_offset = _timeline_position;
303 /* Set our timeline position to either the time reference from a BWF header or the current
304 start of the session.
306 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
308 if (_length != 0 && !bwf_info_exists) {
309 delete _broadcast_info;
311 _flags = Flag (_flags & ~Broadcast);
314 /* Set the broadcast flag if the BWF info is already there. We need
315 * this when recovering or using existing files.
318 if (bwf_info_exists) {
319 _flags = Flag (_flags | Broadcast);
323 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
325 if (_flags & Broadcast) {
327 if (!_broadcast_info) {
328 _broadcast_info = new BroadcastInfo;
331 _broadcast_info->set_from_session (_session, header_position_offset);
332 _broadcast_info->set_description (string_compose ("BWF %1", _name));
334 if (!_broadcast_info->write_to_file (_sndfile)) {
335 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
336 path_to_open, _broadcast_info->get_error())
338 _flags = Flag (_flags & ~Broadcast);
339 delete _broadcast_info;
348 SndFileSource::~SndFileSource ()
351 delete _broadcast_info;
356 SndFileSource::sample_rate () const
358 return _info.samplerate;
362 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
371 if (writable() && !_sndfile) {
372 /* file has not been opened yet - nothing written to it */
373 memset (dst, 0, sizeof (Sample) * cnt);
377 if (const_cast<SndFileSource*>(this)->open()) {
378 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
382 if (start > _length) {
384 /* read starts beyond end of data, just memset to zero */
388 } else if (start + cnt > _length) {
390 /* read ends beyond end of data, read some, memset the rest */
392 file_cnt = _length - start;
396 /* read is entirely within data */
401 assert (file_cnt >= 0);
403 if (file_cnt != cnt) {
404 framepos_t delta = cnt - file_cnt;
405 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
410 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
412 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
413 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
417 if (_info.channels == 1) {
418 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
419 if (ret != file_cnt) {
421 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
422 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;
428 real_cnt = cnt * _info.channels;
430 Sample* interleave_buf = get_interleave_buffer (real_cnt);
432 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
433 ptr = interleave_buf + _channel;
434 nread /= _info.channels;
436 /* stride through the interleaved data */
438 for (framecnt_t n = 0; n < nread; ++n) {
440 ptr += _info.channels;
447 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
454 return destructive_write_unlocked (data, cnt);
456 return nondestructive_write_unlocked (data, cnt);
461 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
464 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
468 if (_info.channels != 1) {
469 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
470 abort(); /*NOTREACHED*/
474 framepos_t frame_pos = _length;
476 if (write_float (data, frame_pos, cnt) != cnt) {
480 update_length (_length + cnt);
482 if (_build_peakfiles) {
483 compute_and_write_peaks (data, frame_pos, cnt, true, true);
490 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
493 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
497 if (_capture_start && _capture_end) {
499 /* start and end of capture both occur within the data we are writing,
500 so do both crossfades.
503 _capture_start = false;
504 _capture_end = false;
506 /* move to the correct location place */
507 file_pos = capture_start_frame - _timeline_position;
510 framecnt_t subcnt = cnt / 2;
511 framecnt_t ofilepos = file_pos;
514 if (crossfade (data, subcnt, 1) != subcnt) {
519 Sample * tmpdata = data + subcnt;
522 subcnt = cnt - subcnt;
523 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
527 file_pos = ofilepos; // adjusted below
529 } else if (_capture_start) {
531 /* start of capture both occur within the data we are writing,
535 _capture_start = false;
536 _capture_end = false;
538 /* move to the correct location place */
539 file_pos = capture_start_frame - _timeline_position;
541 if (crossfade (data, cnt, 1) != cnt) {
545 } else if (_capture_end) {
547 /* end of capture both occur within the data we are writing,
551 _capture_start = false;
552 _capture_end = false;
554 if (crossfade (data, cnt, 0) != cnt) {
560 /* in the middle of recording */
562 if (write_float (data, file_pos, cnt) != cnt) {
567 update_length (file_pos + cnt);
569 if (_build_peakfiles) {
570 compute_and_write_peaks (data, file_pos, cnt, true, true);
579 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
581 set_timeline_position (when);
583 if (_flags & Broadcast) {
584 if (setup_broadcast_info (when, now, tnow)) {
589 return flush_header ();
593 SndFileSource::flush_header ()
596 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
601 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
605 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
611 SndFileSource::flush ()
614 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
619 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
623 // Hopefully everything OK
624 sf_write_sync (_sndfile);
628 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
631 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
636 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
640 if (!(_flags & Broadcast) || !_broadcast_info) {
644 _broadcast_info->set_originator_ref_from_session (_session);
645 _broadcast_info->set_origination_time (&now);
647 /* now update header position taking header offset into account */
649 set_header_timeline_position ();
655 SndFileSource::set_header_timeline_position ()
657 if (!(_flags & Broadcast)) {
660 assert (_broadcast_info);
662 _broadcast_info->set_time_reference (_timeline_position);
664 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
665 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
666 _path, _broadcast_info->get_error())
668 _flags = Flag (_flags & ~Broadcast);
669 delete _broadcast_info;
675 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
677 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
679 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
680 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
684 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
692 SndFileSource::natural_position() const
694 return _timeline_position;
698 SndFileSource::set_destructive (bool yn)
701 _flags = Flag (_flags | Writable | Destructive);
703 xfade_buf = new Sample[xfade_frames];
705 clear_capture_marks ();
706 _timeline_position = header_position_offset;
708 _flags = Flag (_flags & ~Destructive);
709 _timeline_position = 0;
710 /* leave xfade buf alone in case we need it again later */
717 SndFileSource::clear_capture_marks ()
719 _capture_start = false;
720 _capture_end = false;
723 /** @param pos Capture start position in session frames */
725 SndFileSource::mark_capture_start (framepos_t pos)
728 if (pos < _timeline_position) {
729 _capture_start = false;
731 _capture_start = true;
732 capture_start_frame = pos;
738 SndFileSource::mark_capture_end()
746 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
748 framecnt_t xfade = min (xfade_frames, cnt);
749 framecnt_t nofade = cnt - xfade;
750 Sample* fade_data = 0;
751 framepos_t fade_position = 0; // in frames
756 fade_position = file_pos;
759 fade_position = file_pos + nofade;
760 fade_data = data + nofade;
763 if (fade_position > _length) {
765 /* read starts beyond end of data, just memset to zero */
769 } else if (fade_position + xfade > _length) {
771 /* read ends beyond end of data, read some, memset the rest */
773 file_cnt = _length - fade_position;
777 /* read is entirely within data */
784 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
785 if (retval >= 0 && errno == EAGAIN) {
786 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
787 * short or no data there */
788 memset (xfade_buf, 0, xfade * sizeof(Sample));
790 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
796 if (file_cnt != xfade) {
797 framecnt_t delta = xfade - file_cnt;
798 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
801 if (nofade && !fade_in) {
802 if (write_float (data, file_pos, nofade) != nofade) {
803 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
808 if (xfade == xfade_frames) {
812 /* use the standard xfade curve */
816 /* fade new material in */
818 for (n = 0; n < xfade; ++n) {
819 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
825 /* fade new material out */
827 for (n = 0; n < xfade; ++n) {
828 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
832 } else if (xfade < xfade_frames) {
834 std::vector<gain_t> in(xfade);
835 std::vector<gain_t> out(xfade);
837 /* short xfade, compute custom curve */
839 compute_equal_power_fades (xfade, &in[0], &out[0]);
841 for (framecnt_t n = 0; n < xfade; ++n) {
842 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
847 /* long xfade length, has to be computed across several calls */
852 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
853 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
858 if (fade_in && nofade) {
859 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
860 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
869 SndFileSource::last_capture_start_frame () const
872 return capture_start_frame;
879 SndFileSource::handle_header_position_change ()
882 if ( _length != 0 ) {
883 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
884 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
885 } else if (writable()) {
886 _timeline_position = header_position_offset;
887 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
893 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
895 /* This static method is assumed to have been called by the Session
896 before any DFS's are created.
899 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
901 delete [] out_coefficient;
902 delete [] in_coefficient;
904 out_coefficient = new gain_t[xfade_frames];
905 in_coefficient = new gain_t[xfade_frames];
907 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
911 SndFileSource::set_timeline_position (framepos_t pos)
913 // destructive track timeline postion does not change
914 // except at instantion or when header_position_offset
915 // (session start) changes
917 if (!destructive()) {
918 AudioFileSource::set_timeline_position (pos);
923 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
931 sf_info.format = 0; // libsndfile says to clear this before sf_open().
933 if ((-1) == (fd = g_open (path.c_str(), O_RDONLY, 0664))) {
934 sprintf (errbuf, "SndFileSource::get_soundfile_info - cannot open file \"%s\"", path.c_str());
938 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
939 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
943 info.samplerate = sf_info.samplerate;
944 info.channels = sf_info.channels;
945 info.length = sf_info.frames;
947 string major = sndfile_major_format(sf_info.format);
948 string minor = sndfile_minor_format(sf_info.format);
950 if (major.length() + minor.length() < 16) { /* arbitrary */
951 info.format_name = string_compose("%1/%2", major, minor);
953 info.format_name = string_compose("%1\n%2", major, minor);
956 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
964 SndFileSource::one_of_several_channels () const
966 return _info.channels > 1;
970 SndFileSource::clamped_at_unity () const
972 int const type = _info.format & SF_FORMAT_TYPEMASK;
973 int const sub = _info.format & SF_FORMAT_SUBMASK;
974 /* XXX: this may not be the full list of formats that are unclamped */
975 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
979 SndFileSource::file_closed ()
981 /* stupid libsndfile updated the headers on close,
982 so touch the peakfile if it exists and has data
983 to make sure its time is as new as the audio
991 SndFileSource::set_path (const string& p)
993 FileSource::set_path (p);