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 <glibmm/convert.h>
32 #include <glibmm/fileutils.h>
33 #include <glibmm/miscutils.h>
35 #include "ardour/sndfilesource.h"
36 #include "ardour/sndfile_helpers.h"
37 #include "ardour/utils.h"
38 #include "ardour/session.h"
43 using namespace ARDOUR;
47 gain_t* SndFileSource::out_coefficient = 0;
48 gain_t* SndFileSource::in_coefficient = 0;
49 framecnt_t SndFileSource::xfade_frames = 64;
50 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
53 Source::RemovableIfEmpty |
56 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
58 , AudioFileSource (s, node)
61 , _capture_start (false)
62 , _capture_end (false)
68 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
72 throw failed_constructor ();
76 /** Constructor for existing external-to-session files.
77 Files created this way are never writable or removable
79 SndFileSource::SndFileSource (Session& s, const string& path, int chn, Flag flags)
80 : Source(s, DataType::AUDIO, path, flags)
81 /* note that the origin of an external file is itself */
82 , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
85 , _capture_start (false)
86 , _capture_end (false)
94 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
98 throw failed_constructor ();
102 /** This constructor is used to construct new internal-to-session files,
103 not open existing ones.
105 SndFileSource::SndFileSource (Session& s, const string& path, const string& origin,
106 SampleFormat sfmt, HeaderFormat hf, framecnt_t rate, Flag flags)
107 : Source(s, DataType::AUDIO, path, flags)
108 , AudioFileSource (s, path, origin, flags, sfmt, hf)
110 , _broadcast_info (0)
111 , _capture_start (false)
112 , _capture_end (false)
120 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
128 _flags = Flag (_flags & ~Broadcast);
132 fmt = SF_FORMAT_AIFF;
133 _flags = Flag (_flags & ~Broadcast);
138 _flags = Flag (_flags | Broadcast);
143 _flags = Flag (_flags & ~Broadcast);
148 _flags = Flag (_flags & ~Broadcast);
152 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
153 abort(); /*NOTREACHED*/
160 fmt |= SF_FORMAT_FLOAT;
164 fmt |= SF_FORMAT_PCM_24;
168 fmt |= SF_FORMAT_PCM_16;
173 _info.samplerate = rate;
176 if (_flags & Destructive) {
178 throw failed_constructor();
181 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
186 /** Constructor to be called for recovering files being used for
187 * capture. They are in-session, they already exist, they should not
188 * be writable. They are an odd hybrid (from a constructor point of
189 * view) of the previous two constructors.
191 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
192 : Source (s, DataType::AUDIO, path, Flag (0))
193 /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
194 , AudioFileSource (s, path, Flag (0))
196 , _broadcast_info (0)
197 , _capture_start (false)
198 , _capture_end (false)
206 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
210 throw failed_constructor ();
215 SndFileSource::init_sndfile ()
217 /* although libsndfile says we don't need to set this,
218 valgrind and source code shows us that we do.
221 memset (&_info, 0, sizeof(_info));
224 xfade_buf = new Sample[xfade_frames];
225 _timeline_position = header_position_offset;
228 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
232 SndFileSource::close ()
241 SndFileSource::open ()
249 #ifdef PLATFORM_WINDOWS
250 path_to_open = Glib::locale_from_utf8(_path);
252 path_to_open = _path;
255 _sndfile = sf_open (path_to_open.c_str(), writable() ? SFM_RDWR : SFM_READ, &_info);
259 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
260 #ifndef HAVE_COREAUDIO
261 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
262 so we don't want to see this message.
265 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
267 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
268 path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
273 if (_channel >= _info.channels) {
274 #ifndef HAVE_COREAUDIO
275 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
282 _length = _info.frames;
284 if (!_broadcast_info) {
285 _broadcast_info = new BroadcastInfo;
288 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
290 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
291 /* newly created files will not have a BWF header at this point in time.
292 * Import will have called Source::set_timeline_position() if one exists
293 * in the original. */
294 header_position_offset = _timeline_position;
297 /* Set our timeline position to either the time reference from a BWF header or the current
298 start of the session.
300 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
302 if (_length != 0 && !bwf_info_exists) {
303 delete _broadcast_info;
305 _flags = Flag (_flags & ~Broadcast);
308 /* Set the broadcast flag if the BWF info is already there. We need
309 * this when recovering or using existing files.
312 if (bwf_info_exists) {
313 _flags = Flag (_flags | Broadcast);
317 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
319 if (_flags & Broadcast) {
321 if (!_broadcast_info) {
322 _broadcast_info = new BroadcastInfo;
325 _broadcast_info->set_from_session (_session, header_position_offset);
326 _broadcast_info->set_description (string_compose ("BWF %1", _name));
328 if (!_broadcast_info->write_to_file (_sndfile)) {
329 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
330 path_to_open, _broadcast_info->get_error())
332 _flags = Flag (_flags & ~Broadcast);
333 delete _broadcast_info;
342 SndFileSource::~SndFileSource ()
345 delete _broadcast_info;
350 SndFileSource::sample_rate () const
352 return _info.samplerate;
356 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
365 if (writable() && !_sndfile) {
366 /* file has not been opened yet - nothing written to it */
367 memset (dst, 0, sizeof (Sample) * cnt);
371 if (const_cast<SndFileSource*>(this)->open()) {
372 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
376 if (start > _length) {
378 /* read starts beyond end of data, just memset to zero */
382 } else if (start + cnt > _length) {
384 /* read ends beyond end of data, read some, memset the rest */
386 file_cnt = _length - start;
390 /* read is entirely within data */
395 assert (file_cnt >= 0);
397 if (file_cnt != cnt) {
398 framepos_t delta = cnt - file_cnt;
399 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
404 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
406 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
407 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
411 if (_info.channels == 1) {
412 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
413 if (ret != file_cnt) {
415 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
416 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;
422 real_cnt = cnt * _info.channels;
424 Sample* interleave_buf = get_interleave_buffer (real_cnt);
426 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
427 ptr = interleave_buf + _channel;
428 nread /= _info.channels;
430 /* stride through the interleaved data */
432 for (framecnt_t n = 0; n < nread; ++n) {
434 ptr += _info.channels;
441 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
448 return destructive_write_unlocked (data, cnt);
450 return nondestructive_write_unlocked (data, cnt);
455 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
458 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
462 if (_info.channels != 1) {
463 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
464 abort(); /*NOTREACHED*/
468 framepos_t frame_pos = _length;
470 if (write_float (data, frame_pos, cnt) != cnt) {
474 update_length (_length + cnt);
476 if (_build_peakfiles) {
477 compute_and_write_peaks (data, frame_pos, cnt, true, true);
484 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
487 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
491 if (_capture_start && _capture_end) {
493 /* start and end of capture both occur within the data we are writing,
494 so do both crossfades.
497 _capture_start = false;
498 _capture_end = false;
500 /* move to the correct location place */
501 file_pos = capture_start_frame - _timeline_position;
504 framecnt_t subcnt = cnt / 2;
505 framecnt_t ofilepos = file_pos;
508 if (crossfade (data, subcnt, 1) != subcnt) {
513 Sample * tmpdata = data + subcnt;
516 subcnt = cnt - subcnt;
517 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
521 file_pos = ofilepos; // adjusted below
523 } else if (_capture_start) {
525 /* start of capture both occur within the data we are writing,
529 _capture_start = false;
530 _capture_end = false;
532 /* move to the correct location place */
533 file_pos = capture_start_frame - _timeline_position;
535 if (crossfade (data, cnt, 1) != cnt) {
539 } else if (_capture_end) {
541 /* end of capture both occur within the data we are writing,
545 _capture_start = false;
546 _capture_end = false;
548 if (crossfade (data, cnt, 0) != cnt) {
554 /* in the middle of recording */
556 if (write_float (data, file_pos, cnt) != cnt) {
561 update_length (file_pos + cnt);
563 if (_build_peakfiles) {
564 compute_and_write_peaks (data, file_pos, cnt, true, true);
573 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
575 set_timeline_position (when);
577 if (_flags & Broadcast) {
578 if (setup_broadcast_info (when, now, tnow)) {
583 return flush_header ();
587 SndFileSource::flush_header ()
590 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
595 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
599 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
605 SndFileSource::flush ()
608 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
613 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
617 // Hopefully everything OK
618 sf_write_sync (_sndfile);
622 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
625 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
630 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
634 if (!(_flags & Broadcast) || !_broadcast_info) {
638 _broadcast_info->set_originator_ref_from_session (_session);
639 _broadcast_info->set_origination_time (&now);
641 /* now update header position taking header offset into account */
643 set_header_timeline_position ();
649 SndFileSource::set_header_timeline_position ()
651 if (!(_flags & Broadcast)) {
654 assert (_broadcast_info);
656 _broadcast_info->set_time_reference (_timeline_position);
658 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
659 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
660 _path, _broadcast_info->get_error())
662 _flags = Flag (_flags & ~Broadcast);
663 delete _broadcast_info;
669 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
671 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
673 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
674 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
678 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
686 SndFileSource::natural_position() const
688 return _timeline_position;
692 SndFileSource::set_destructive (bool yn)
695 _flags = Flag (_flags | Writable | Destructive);
697 xfade_buf = new Sample[xfade_frames];
699 clear_capture_marks ();
700 _timeline_position = header_position_offset;
702 _flags = Flag (_flags & ~Destructive);
703 _timeline_position = 0;
704 /* leave xfade buf alone in case we need it again later */
711 SndFileSource::clear_capture_marks ()
713 _capture_start = false;
714 _capture_end = false;
717 /** @param pos Capture start position in session frames */
719 SndFileSource::mark_capture_start (framepos_t pos)
722 if (pos < _timeline_position) {
723 _capture_start = false;
725 _capture_start = true;
726 capture_start_frame = pos;
732 SndFileSource::mark_capture_end()
740 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
742 framecnt_t xfade = min (xfade_frames, cnt);
743 framecnt_t nofade = cnt - xfade;
744 Sample* fade_data = 0;
745 framepos_t fade_position = 0; // in frames
750 fade_position = file_pos;
753 fade_position = file_pos + nofade;
754 fade_data = data + nofade;
757 if (fade_position > _length) {
759 /* read starts beyond end of data, just memset to zero */
763 } else if (fade_position + xfade > _length) {
765 /* read ends beyond end of data, read some, memset the rest */
767 file_cnt = _length - fade_position;
771 /* read is entirely within data */
778 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
779 if (retval >= 0 && errno == EAGAIN) {
780 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
781 * short or no data there */
782 memset (xfade_buf, 0, xfade * sizeof(Sample));
784 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
790 if (file_cnt != xfade) {
791 framecnt_t delta = xfade - file_cnt;
792 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
795 if (nofade && !fade_in) {
796 if (write_float (data, file_pos, nofade) != nofade) {
797 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
802 if (xfade == xfade_frames) {
806 /* use the standard xfade curve */
810 /* fade new material in */
812 for (n = 0; n < xfade; ++n) {
813 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
819 /* fade new material out */
821 for (n = 0; n < xfade; ++n) {
822 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
826 } else if (xfade < xfade_frames) {
828 std::vector<gain_t> in(xfade);
829 std::vector<gain_t> out(xfade);
831 /* short xfade, compute custom curve */
833 compute_equal_power_fades (xfade, &in[0], &out[0]);
835 for (framecnt_t n = 0; n < xfade; ++n) {
836 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
841 /* long xfade length, has to be computed across several calls */
846 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
847 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
852 if (fade_in && nofade) {
853 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
854 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
863 SndFileSource::last_capture_start_frame () const
866 return capture_start_frame;
873 SndFileSource::handle_header_position_change ()
876 if ( _length != 0 ) {
877 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
878 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
879 } else if (writable()) {
880 _timeline_position = header_position_offset;
881 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
887 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
889 /* This static method is assumed to have been called by the Session
890 before any DFS's are created.
893 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
895 delete [] out_coefficient;
896 delete [] in_coefficient;
898 out_coefficient = new gain_t[xfade_frames];
899 in_coefficient = new gain_t[xfade_frames];
901 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
905 SndFileSource::set_timeline_position (framepos_t pos)
907 // destructive track timeline postion does not change
908 // except at instantion or when header_position_offset
909 // (session start) changes
911 if (!destructive()) {
912 AudioFileSource::set_timeline_position (pos);
917 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
923 sf_info.format = 0; // libsndfile says to clear this before sf_open().
925 if ((sf = sf_open (const_cast<char*>(Glib::locale_from_utf8(path).c_str()), SFM_READ, &sf_info)) == 0) {
927 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
931 info.samplerate = sf_info.samplerate;
932 info.channels = sf_info.channels;
933 info.length = sf_info.frames;
935 string major = sndfile_major_format(sf_info.format);
936 string minor = sndfile_minor_format(sf_info.format);
938 if (major.length() + minor.length() < 16) { /* arbitrary */
939 info.format_name = string_compose("%1/%2", major, minor);
941 info.format_name = string_compose("%1\n%2", major, minor);
944 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
952 SndFileSource::one_of_several_channels () const
954 return _info.channels > 1;
958 SndFileSource::clamped_at_unity () const
960 int const type = _info.format & SF_FORMAT_TYPEMASK;
961 int const sub = _info.format & SF_FORMAT_SUBMASK;
962 /* XXX: this may not be the full list of formats that are unclamped */
963 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
967 SndFileSource::file_closed ()
969 /* stupid libsndfile updated the headers on close,
970 so touch the peakfile if it exists and has data
971 to make sure its time is as new as the audio
979 SndFileSource::set_path (const string& p)
981 FileSource::set_path (p);