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 #ifdef PLATFORM_WINDOWS
32 #include <glibmm/convert.h>
34 #include <glibmm/fileutils.h>
35 #include <glibmm/miscutils.h>
37 #include "ardour/sndfilesource.h"
38 #include "ardour/sndfile_helpers.h"
39 #include "ardour/utils.h"
40 #include "ardour/session.h"
45 using namespace ARDOUR;
49 gain_t* SndFileSource::out_coefficient = 0;
50 gain_t* SndFileSource::in_coefficient = 0;
51 framecnt_t SndFileSource::xfade_frames = 64;
52 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
55 Source::RemovableIfEmpty |
58 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
60 , AudioFileSource (s, node)
63 , _capture_start (false)
64 , _capture_end (false)
70 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
74 throw failed_constructor ();
78 /** Constructor for existing external-to-session files.
79 Files created this way are never writable or removable
81 SndFileSource::SndFileSource (Session& s, const string& path, int chn, Flag flags)
82 : Source(s, DataType::AUDIO, path, flags)
83 /* note that the origin of an external file is itself */
84 , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
87 , _capture_start (false)
88 , _capture_end (false)
96 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
100 throw failed_constructor ();
104 /** This constructor is used to construct new internal-to-session files,
105 not open existing ones.
107 SndFileSource::SndFileSource (Session& s, const string& path, const string& origin,
108 SampleFormat sfmt, HeaderFormat hf, framecnt_t rate, Flag flags)
109 : Source(s, DataType::AUDIO, path, flags)
110 , AudioFileSource (s, path, origin, flags, sfmt, hf)
112 , _broadcast_info (0)
113 , _capture_start (false)
114 , _capture_end (false)
122 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
130 _flags = Flag (_flags & ~Broadcast);
134 fmt = SF_FORMAT_AIFF;
135 _flags = Flag (_flags & ~Broadcast);
140 _flags = Flag (_flags | Broadcast);
145 _flags = Flag (_flags & ~Broadcast);
150 _flags = Flag (_flags & ~Broadcast);
154 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
155 abort(); /*NOTREACHED*/
162 fmt |= SF_FORMAT_FLOAT;
166 fmt |= SF_FORMAT_PCM_24;
170 fmt |= SF_FORMAT_PCM_16;
175 _info.samplerate = rate;
178 if (_flags & Destructive) {
180 throw failed_constructor();
183 /* normal mode: do not open the file here - do that in {read,write}_unlocked() as needed
188 /** Constructor to be called for recovering files being used for
189 * capture. They are in-session, they already exist, they should not
190 * be writable. They are an odd hybrid (from a constructor point of
191 * view) of the previous two constructors.
193 SndFileSource::SndFileSource (Session& s, const string& path, int chn)
194 : Source (s, DataType::AUDIO, path, Flag (0))
195 /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
196 , AudioFileSource (s, path, Flag (0))
198 , _broadcast_info (0)
199 , _capture_start (false)
200 , _capture_end (false)
208 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
212 throw failed_constructor ();
217 SndFileSource::init_sndfile ()
219 /* although libsndfile says we don't need to set this,
220 valgrind and source code shows us that we do.
223 memset (&_info, 0, sizeof(_info));
226 xfade_buf = new Sample[xfade_frames];
227 _timeline_position = header_position_offset;
230 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
234 SndFileSource::close ()
243 SndFileSource::open ()
251 #ifdef PLATFORM_WINDOWS
252 path_to_open = Glib::locale_from_utf8(_path);
254 path_to_open = _path;
257 _sndfile = sf_open (path_to_open.c_str(), writable() ? SFM_RDWR : SFM_READ, &_info);
261 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
262 #ifndef HAVE_COREAUDIO
263 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
264 so we don't want to see this message.
267 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
269 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
270 path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
275 if (_channel >= _info.channels) {
276 #ifndef HAVE_COREAUDIO
277 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
284 _length = _info.frames;
286 if (!_broadcast_info) {
287 _broadcast_info = new BroadcastInfo;
290 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
292 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
293 /* newly created files will not have a BWF header at this point in time.
294 * Import will have called Source::set_timeline_position() if one exists
295 * in the original. */
296 header_position_offset = _timeline_position;
299 /* Set our timeline position to either the time reference from a BWF header or the current
300 start of the session.
302 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
304 if (_length != 0 && !bwf_info_exists) {
305 delete _broadcast_info;
307 _flags = Flag (_flags & ~Broadcast);
310 /* Set the broadcast flag if the BWF info is already there. We need
311 * this when recovering or using existing files.
314 if (bwf_info_exists) {
315 _flags = Flag (_flags | Broadcast);
319 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
321 if (_flags & Broadcast) {
323 if (!_broadcast_info) {
324 _broadcast_info = new BroadcastInfo;
327 _broadcast_info->set_from_session (_session, header_position_offset);
328 _broadcast_info->set_description (string_compose ("BWF %1", _name));
330 if (!_broadcast_info->write_to_file (_sndfile)) {
331 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
332 path_to_open, _broadcast_info->get_error())
334 _flags = Flag (_flags & ~Broadcast);
335 delete _broadcast_info;
344 SndFileSource::~SndFileSource ()
347 delete _broadcast_info;
352 SndFileSource::sample_rate () const
354 return _info.samplerate;
358 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
367 if (writable() && !_sndfile) {
368 /* file has not been opened yet - nothing written to it */
369 memset (dst, 0, sizeof (Sample) * cnt);
373 if (const_cast<SndFileSource*>(this)->open()) {
374 error << string_compose (_("could not open file %1 for reading."), _path) << endmsg;
378 if (start > _length) {
380 /* read starts beyond end of data, just memset to zero */
384 } else if (start + cnt > _length) {
386 /* read ends beyond end of data, read some, memset the rest */
388 file_cnt = _length - start;
392 /* read is entirely within data */
397 assert (file_cnt >= 0);
399 if (file_cnt != cnt) {
400 framepos_t delta = cnt - file_cnt;
401 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
406 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
408 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
409 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
413 if (_info.channels == 1) {
414 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
415 if (ret != file_cnt) {
417 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
418 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;
424 real_cnt = cnt * _info.channels;
426 Sample* interleave_buf = get_interleave_buffer (real_cnt);
428 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
429 ptr = interleave_buf + _channel;
430 nread /= _info.channels;
432 /* stride through the interleaved data */
434 for (framecnt_t n = 0; n < nread; ++n) {
436 ptr += _info.channels;
443 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
450 return destructive_write_unlocked (data, cnt);
452 return nondestructive_write_unlocked (data, cnt);
457 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
460 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
464 if (_info.channels != 1) {
465 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
466 abort(); /*NOTREACHED*/
470 framepos_t frame_pos = _length;
472 if (write_float (data, frame_pos, cnt) != cnt) {
476 update_length (_length + cnt);
478 if (_build_peakfiles) {
479 compute_and_write_peaks (data, frame_pos, cnt, true, true);
486 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
489 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
493 if (_capture_start && _capture_end) {
495 /* start and end of capture both occur within the data we are writing,
496 so do both crossfades.
499 _capture_start = false;
500 _capture_end = false;
502 /* move to the correct location place */
503 file_pos = capture_start_frame - _timeline_position;
506 framecnt_t subcnt = cnt / 2;
507 framecnt_t ofilepos = file_pos;
510 if (crossfade (data, subcnt, 1) != subcnt) {
515 Sample * tmpdata = data + subcnt;
518 subcnt = cnt - subcnt;
519 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
523 file_pos = ofilepos; // adjusted below
525 } else if (_capture_start) {
527 /* start of capture both occur within the data we are writing,
531 _capture_start = false;
532 _capture_end = false;
534 /* move to the correct location place */
535 file_pos = capture_start_frame - _timeline_position;
537 if (crossfade (data, cnt, 1) != cnt) {
541 } else if (_capture_end) {
543 /* end of capture both occur within the data we are writing,
547 _capture_start = false;
548 _capture_end = false;
550 if (crossfade (data, cnt, 0) != cnt) {
556 /* in the middle of recording */
558 if (write_float (data, file_pos, cnt) != cnt) {
563 update_length (file_pos + cnt);
565 if (_build_peakfiles) {
566 compute_and_write_peaks (data, file_pos, cnt, true, true);
575 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
577 set_timeline_position (when);
579 if (_flags & Broadcast) {
580 if (setup_broadcast_info (when, now, tnow)) {
585 return flush_header ();
589 SndFileSource::flush_header ()
592 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
597 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
601 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
607 SndFileSource::flush ()
610 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
615 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
619 // Hopefully everything OK
620 sf_write_sync (_sndfile);
624 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
627 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
632 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
636 if (!(_flags & Broadcast) || !_broadcast_info) {
640 _broadcast_info->set_originator_ref_from_session (_session);
641 _broadcast_info->set_origination_time (&now);
643 /* now update header position taking header offset into account */
645 set_header_timeline_position ();
651 SndFileSource::set_header_timeline_position ()
653 if (!(_flags & Broadcast)) {
656 assert (_broadcast_info);
658 _broadcast_info->set_time_reference (_timeline_position);
660 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
661 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
662 _path, _broadcast_info->get_error())
664 _flags = Flag (_flags & ~Broadcast);
665 delete _broadcast_info;
671 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
673 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
675 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
676 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
680 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
688 SndFileSource::natural_position() const
690 return _timeline_position;
694 SndFileSource::set_destructive (bool yn)
697 _flags = Flag (_flags | Writable | Destructive);
699 xfade_buf = new Sample[xfade_frames];
701 clear_capture_marks ();
702 _timeline_position = header_position_offset;
704 _flags = Flag (_flags & ~Destructive);
705 _timeline_position = 0;
706 /* leave xfade buf alone in case we need it again later */
713 SndFileSource::clear_capture_marks ()
715 _capture_start = false;
716 _capture_end = false;
719 /** @param pos Capture start position in session frames */
721 SndFileSource::mark_capture_start (framepos_t pos)
724 if (pos < _timeline_position) {
725 _capture_start = false;
727 _capture_start = true;
728 capture_start_frame = pos;
734 SndFileSource::mark_capture_end()
742 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
744 framecnt_t xfade = min (xfade_frames, cnt);
745 framecnt_t nofade = cnt - xfade;
746 Sample* fade_data = 0;
747 framepos_t fade_position = 0; // in frames
752 fade_position = file_pos;
755 fade_position = file_pos + nofade;
756 fade_data = data + nofade;
759 if (fade_position > _length) {
761 /* read starts beyond end of data, just memset to zero */
765 } else if (fade_position + xfade > _length) {
767 /* read ends beyond end of data, read some, memset the rest */
769 file_cnt = _length - fade_position;
773 /* read is entirely within data */
780 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
781 if (retval >= 0 && errno == EAGAIN) {
782 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
783 * short or no data there */
784 memset (xfade_buf, 0, xfade * sizeof(Sample));
786 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
792 if (file_cnt != xfade) {
793 framecnt_t delta = xfade - file_cnt;
794 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
797 if (nofade && !fade_in) {
798 if (write_float (data, file_pos, nofade) != nofade) {
799 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
804 if (xfade == xfade_frames) {
808 /* use the standard xfade curve */
812 /* fade new material in */
814 for (n = 0; n < xfade; ++n) {
815 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
821 /* fade new material out */
823 for (n = 0; n < xfade; ++n) {
824 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
828 } else if (xfade < xfade_frames) {
830 std::vector<gain_t> in(xfade);
831 std::vector<gain_t> out(xfade);
833 /* short xfade, compute custom curve */
835 compute_equal_power_fades (xfade, &in[0], &out[0]);
837 for (framecnt_t n = 0; n < xfade; ++n) {
838 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
843 /* long xfade length, has to be computed across several calls */
848 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
849 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
854 if (fade_in && nofade) {
855 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
856 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
865 SndFileSource::last_capture_start_frame () const
868 return capture_start_frame;
875 SndFileSource::handle_header_position_change ()
878 if ( _length != 0 ) {
879 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
880 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
881 } else if (writable()) {
882 _timeline_position = header_position_offset;
883 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
889 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
891 /* This static method is assumed to have been called by the Session
892 before any DFS's are created.
895 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
897 delete [] out_coefficient;
898 delete [] in_coefficient;
900 out_coefficient = new gain_t[xfade_frames];
901 in_coefficient = new gain_t[xfade_frames];
903 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
907 SndFileSource::set_timeline_position (framepos_t pos)
909 // destructive track timeline postion does not change
910 // except at instantion or when header_position_offset
911 // (session start) changes
913 if (!destructive()) {
914 AudioFileSource::set_timeline_position (pos);
919 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
925 sf_info.format = 0; // libsndfile says to clear this before sf_open().
927 if ((sf = sf_open (const_cast<char*>(path.c_str()), SFM_READ, &sf_info)) == 0) {
929 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
933 info.samplerate = sf_info.samplerate;
934 info.channels = sf_info.channels;
935 info.length = sf_info.frames;
937 string major = sndfile_major_format(sf_info.format);
938 string minor = sndfile_minor_format(sf_info.format);
940 if (major.length() + minor.length() < 16) { /* arbitrary */
941 info.format_name = string_compose("%1/%2", major, minor);
943 info.format_name = string_compose("%1\n%2", major, minor);
946 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
954 SndFileSource::one_of_several_channels () const
956 return _info.channels > 1;
960 SndFileSource::clamped_at_unity () const
962 int const type = _info.format & SF_FORMAT_TYPEMASK;
963 int const sub = _info.format & SF_FORMAT_SUBMASK;
964 /* XXX: this may not be the full list of formats that are unclamped */
965 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
969 SndFileSource::file_closed ()
971 /* stupid libsndfile updated the headers on close,
972 so touch the peakfile if it exists and has data
973 to make sure its time is as new as the audio
981 SndFileSource::set_path (const string& p)
983 FileSource::set_path (p);