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 <glib/gstdio.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 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
157 abort(); /*NOTREACHED*/
164 fmt |= SF_FORMAT_FLOAT;
168 fmt |= SF_FORMAT_PCM_24;
172 fmt |= SF_FORMAT_PCM_16;
177 _info.samplerate = rate;
180 if (_flags & Destructive) {
182 throw failed_constructor();
185 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
190 /** Constructor to be called for recovering files being used for
191 * capture. They are in-session, they already exist, they should not
192 * be writable. They are an odd hybrid (from a constructor point of
193 * view) of the previous two constructors.
195 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
196 : Source (s, DataType::AUDIO, path, Flag (0))
197 /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
198 , AudioFileSource (s, path, Flag (0))
200 , _broadcast_info (0)
201 , _capture_start (false)
202 , _capture_end (false)
210 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
214 throw failed_constructor ();
219 SndFileSource::init_sndfile ()
221 /* although libsndfile says we don't need to set this,
222 valgrind and source code shows us that we do.
225 memset (&_info, 0, sizeof(_info));
228 xfade_buf = new Sample[xfade_frames];
229 _timeline_position = header_position_offset;
232 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
236 SndFileSource::close ()
245 SndFileSource::open ()
251 // We really only want to use g_open for all platforms but because of this
252 // method(SndfileSource::open), the compiler(or at least GCC) is confused
253 // because g_open will expand to "open" on non-POSIX systems and needs the
254 // global namespace qualifer. The problem is since since C99 ::g_open will
255 // apparently expand to ":: open"
256 #ifdef PLATFORM_WINDOWS
257 int fd = g_open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
259 int fd = ::open (_path.c_str(), writable() ? O_CREAT | O_RDWR : O_RDONLY, writable() ? 0644 : 0444);
263 error << string_compose (
264 _ ("SndFileSource: cannot open file \"%1\" for %2"),
266 (writable () ? "read+write" : "reading")) << endmsg;
270 _sndfile = sf_open_fd (fd, writable() ? SFM_RDWR : SFM_READ, &_info, true);
274 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
275 #ifndef HAVE_COREAUDIO
276 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
277 so we don't want to see this message.
280 cerr << "failed to open " << _path << " with name " << _name << endl;
282 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
283 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
288 if (_channel >= _info.channels) {
289 #ifndef HAVE_COREAUDIO
290 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
297 _length = _info.frames;
299 if (!_broadcast_info) {
300 _broadcast_info = new BroadcastInfo;
303 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
305 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
306 /* newly created files will not have a BWF header at this point in time.
307 * Import will have called Source::set_timeline_position() if one exists
308 * in the original. */
309 header_position_offset = _timeline_position;
312 /* Set our timeline position to either the time reference from a BWF header or the current
313 start of the session.
315 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
317 if (_length != 0 && !bwf_info_exists) {
318 delete _broadcast_info;
320 _flags = Flag (_flags & ~Broadcast);
323 /* Set the broadcast flag if the BWF info is already there. We need
324 * this when recovering or using existing files.
327 if (bwf_info_exists) {
328 _flags = Flag (_flags | Broadcast);
332 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
334 if (_flags & Broadcast) {
336 if (!_broadcast_info) {
337 _broadcast_info = new BroadcastInfo;
340 _broadcast_info->set_from_session (_session, header_position_offset);
341 _broadcast_info->set_description (string_compose ("BWF %1", _name));
343 if (!_broadcast_info->write_to_file (_sndfile)) {
344 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
345 _path, _broadcast_info->get_error())
347 _flags = Flag (_flags & ~Broadcast);
348 delete _broadcast_info;
357 SndFileSource::~SndFileSource ()
360 delete _broadcast_info;
365 SndFileSource::sample_rate () const
367 return _info.samplerate;
371 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
380 if (writable() && !_sndfile) {
381 /* file has not been opened yet - nothing written to it */
382 memset (dst, 0, sizeof (Sample) * cnt);
386 if (const_cast<SndFileSource*>(this)->open()) {
387 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
391 if (start > _length) {
393 /* read starts beyond end of data, just memset to zero */
397 } else if (start + cnt > _length) {
399 /* read ends beyond end of data, read some, memset the rest */
401 file_cnt = _length - start;
405 /* read is entirely within data */
410 assert (file_cnt >= 0);
412 if (file_cnt != cnt) {
413 framepos_t delta = cnt - file_cnt;
414 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
419 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
421 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
422 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
426 if (_info.channels == 1) {
427 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
428 if (ret != file_cnt) {
430 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
431 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;
437 real_cnt = cnt * _info.channels;
439 Sample* interleave_buf = get_interleave_buffer (real_cnt);
441 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
442 ptr = interleave_buf + _channel;
443 nread /= _info.channels;
445 /* stride through the interleaved data */
447 for (framecnt_t n = 0; n < nread; ++n) {
449 ptr += _info.channels;
456 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
463 return destructive_write_unlocked (data, cnt);
465 return nondestructive_write_unlocked (data, cnt);
470 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
473 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
477 if (_info.channels != 1) {
478 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
479 abort(); /*NOTREACHED*/
483 framepos_t frame_pos = _length;
485 if (write_float (data, frame_pos, cnt) != cnt) {
489 update_length (_length + cnt);
491 if (_build_peakfiles) {
492 compute_and_write_peaks (data, frame_pos, cnt, true, true);
499 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
502 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
506 if (_capture_start && _capture_end) {
508 /* start and end of capture both occur within the data we are writing,
509 so do both crossfades.
512 _capture_start = false;
513 _capture_end = false;
515 /* move to the correct location place */
516 file_pos = capture_start_frame - _timeline_position;
519 framecnt_t subcnt = cnt / 2;
520 framecnt_t ofilepos = file_pos;
523 if (crossfade (data, subcnt, 1) != subcnt) {
528 Sample * tmpdata = data + subcnt;
531 subcnt = cnt - subcnt;
532 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
536 file_pos = ofilepos; // adjusted below
538 } else if (_capture_start) {
540 /* start of capture both occur within the data we are writing,
544 _capture_start = false;
545 _capture_end = false;
547 /* move to the correct location place */
548 file_pos = capture_start_frame - _timeline_position;
550 if (crossfade (data, cnt, 1) != cnt) {
554 } else if (_capture_end) {
556 /* end of capture both occur within the data we are writing,
560 _capture_start = false;
561 _capture_end = false;
563 if (crossfade (data, cnt, 0) != cnt) {
569 /* in the middle of recording */
571 if (write_float (data, file_pos, cnt) != cnt) {
576 update_length (file_pos + cnt);
578 if (_build_peakfiles) {
579 compute_and_write_peaks (data, file_pos, cnt, true, true);
588 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
590 set_timeline_position (when);
592 if (_flags & Broadcast) {
593 if (setup_broadcast_info (when, now, tnow)) {
598 return flush_header ();
602 SndFileSource::flush_header ()
605 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
610 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
614 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
620 SndFileSource::flush ()
623 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
628 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
632 // Hopefully everything OK
633 sf_write_sync (_sndfile);
637 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
640 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
645 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
649 if (!(_flags & Broadcast) || !_broadcast_info) {
653 _broadcast_info->set_originator_ref_from_session (_session);
654 _broadcast_info->set_origination_time (&now);
656 /* now update header position taking header offset into account */
658 set_header_timeline_position ();
664 SndFileSource::set_header_timeline_position ()
666 if (!(_flags & Broadcast)) {
669 assert (_broadcast_info);
671 _broadcast_info->set_time_reference (_timeline_position);
673 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
674 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
675 _path, _broadcast_info->get_error())
677 _flags = Flag (_flags & ~Broadcast);
678 delete _broadcast_info;
684 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
686 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
688 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
689 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
693 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
701 SndFileSource::natural_position() const
703 return _timeline_position;
707 SndFileSource::set_destructive (bool yn)
710 _flags = Flag (_flags | Writable | Destructive);
712 xfade_buf = new Sample[xfade_frames];
714 clear_capture_marks ();
715 _timeline_position = header_position_offset;
717 _flags = Flag (_flags & ~Destructive);
718 _timeline_position = 0;
719 /* leave xfade buf alone in case we need it again later */
726 SndFileSource::clear_capture_marks ()
728 _capture_start = false;
729 _capture_end = false;
732 /** @param pos Capture start position in session frames */
734 SndFileSource::mark_capture_start (framepos_t pos)
737 if (pos < _timeline_position) {
738 _capture_start = false;
740 _capture_start = true;
741 capture_start_frame = pos;
747 SndFileSource::mark_capture_end()
755 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
757 framecnt_t xfade = min (xfade_frames, cnt);
758 framecnt_t nofade = cnt - xfade;
759 Sample* fade_data = 0;
760 framepos_t fade_position = 0; // in frames
765 fade_position = file_pos;
768 fade_position = file_pos + nofade;
769 fade_data = data + nofade;
772 if (fade_position > _length) {
774 /* read starts beyond end of data, just memset to zero */
778 } else if (fade_position + xfade > _length) {
780 /* read ends beyond end of data, read some, memset the rest */
782 file_cnt = _length - fade_position;
786 /* read is entirely within data */
793 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
794 if (retval >= 0 && errno == EAGAIN) {
795 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
796 * short or no data there */
797 memset (xfade_buf, 0, xfade * sizeof(Sample));
799 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
805 if (file_cnt != xfade) {
806 framecnt_t delta = xfade - file_cnt;
807 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
810 if (nofade && !fade_in) {
811 if (write_float (data, file_pos, nofade) != nofade) {
812 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
817 if (xfade == xfade_frames) {
821 /* use the standard xfade curve */
825 /* fade new material in */
827 for (n = 0; n < xfade; ++n) {
828 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
834 /* fade new material out */
836 for (n = 0; n < xfade; ++n) {
837 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
841 } else if (xfade < xfade_frames) {
843 std::vector<gain_t> in(xfade);
844 std::vector<gain_t> out(xfade);
846 /* short xfade, compute custom curve */
848 compute_equal_power_fades (xfade, &in[0], &out[0]);
850 for (framecnt_t n = 0; n < xfade; ++n) {
851 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
856 /* long xfade length, has to be computed across several calls */
861 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
862 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
867 if (fade_in && nofade) {
868 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
869 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
878 SndFileSource::last_capture_start_frame () const
881 return capture_start_frame;
888 SndFileSource::handle_header_position_change ()
891 if ( _length != 0 ) {
892 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
893 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
894 } else if (writable()) {
895 _timeline_position = header_position_offset;
896 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
902 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
904 /* This static method is assumed to have been called by the Session
905 before any DFS's are created.
908 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
910 delete [] out_coefficient;
911 delete [] in_coefficient;
913 out_coefficient = new gain_t[xfade_frames];
914 in_coefficient = new gain_t[xfade_frames];
916 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
920 SndFileSource::set_timeline_position (framepos_t pos)
922 // destructive track timeline postion does not change
923 // except at instantion or when header_position_offset
924 // (session start) changes
926 if (!destructive()) {
927 AudioFileSource::set_timeline_position (pos);
932 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
938 sf_info.format = 0; // libsndfile says to clear this before sf_open().
940 if (path.empty() || Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) {
944 #ifdef PLATFORM_WINDOWS
945 int fd = g_open (path.c_str(), O_RDONLY, 0444);
947 int fd = ::open (path.c_str(), O_RDONLY, 0444);
951 error << string_compose ( _("SndFileSource: cannot open file \"%1\" for reading"), path)
955 if ((sf = sf_open_fd (fd, SFM_READ, &sf_info, true)) == 0) {
957 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
961 info.samplerate = sf_info.samplerate;
962 info.channels = sf_info.channels;
963 info.length = sf_info.frames;
965 string major = sndfile_major_format(sf_info.format);
966 string minor = sndfile_minor_format(sf_info.format);
968 if (major.length() + minor.length() < 16) { /* arbitrary */
969 info.format_name = string_compose("%1/%2", major, minor);
971 info.format_name = string_compose("%1\n%2", major, minor);
974 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
982 SndFileSource::one_of_several_channels () const
984 return _info.channels > 1;
988 SndFileSource::clamped_at_unity () const
990 int const type = _info.format & SF_FORMAT_TYPEMASK;
991 int const sub = _info.format & SF_FORMAT_SUBMASK;
992 /* XXX: this may not be the full list of formats that are unclamped */
993 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
997 SndFileSource::file_closed ()
999 /* stupid libsndfile updated the headers on close,
1000 so touch the peakfile if it exists and has data
1001 to make sure its time is as new as the audio
1009 SndFileSource::set_path (const string& p)
1011 FileSource::set_path (p);