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 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::open ()
242 #ifdef PLATFORM_WINDOWS
243 path_to_open = Glib::locale_from_utf8(_path);
245 path_to_open = _path;
248 _sndfile = sf_open (path_to_open.c_str(), writable() ? SFM_RDWR : SFM_READ, &_info);
252 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
253 #ifndef HAVE_COREAUDIO
254 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
255 so we don't want to see this message.
258 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
260 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
261 path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
266 if (_channel >= _info.channels) {
267 #ifndef HAVE_COREAUDIO
268 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
275 _length = _info.frames;
277 if (!_broadcast_info) {
278 _broadcast_info = new BroadcastInfo;
281 bool bwf_info_exists = _broadcast_info->load_from_file (_sndfile);
283 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
284 /* newly created files will not have a BWF header at this point in time.
285 * Import will have called Source::set_timeline_position() if one exists
286 * in the original. */
287 header_position_offset = _timeline_position;
290 /* Set our timeline position to either the time reference from a BWF header or the current
291 start of the session.
293 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
295 if (_length != 0 && !bwf_info_exists) {
296 delete _broadcast_info;
298 _flags = Flag (_flags & ~Broadcast);
301 /* Set the broadcast flag if the BWF info is already there. We need
302 * this when recovering or using existing files.
305 if (bwf_info_exists) {
306 _flags = Flag (_flags | Broadcast);
310 sf_command (_sndfile, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
312 if (_flags & Broadcast) {
314 if (!_broadcast_info) {
315 _broadcast_info = new BroadcastInfo;
318 _broadcast_info->set_from_session (_session, header_position_offset);
319 _broadcast_info->set_description (string_compose ("BWF %1", _name));
321 if (!_broadcast_info->write_to_file (_sndfile)) {
322 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
323 path_to_open, _broadcast_info->get_error())
325 _flags = Flag (_flags & ~Broadcast);
326 delete _broadcast_info;
335 SndFileSource::~SndFileSource ()
341 delete _broadcast_info;
346 SndFileSource::sample_rate () const
348 return _info.samplerate;
352 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
361 if (writable() && !_sndfile) {
362 /* file has not been opened yet - nothing written to it */
363 memset (dst, 0, sizeof (Sample) * cnt);
368 error << string_compose (_("could not allocate file %1 for reading."), _path) << endmsg;
372 if (start > _length) {
374 /* read starts beyond end of data, just memset to zero */
378 } else if (start + cnt > _length) {
380 /* read ends beyond end of data, read some, memset the rest */
382 file_cnt = _length - start;
386 /* read is entirely within data */
391 assert (file_cnt >= 0);
393 if (file_cnt != cnt) {
394 framepos_t delta = cnt - file_cnt;
395 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
400 if (sf_seek (_sndfile, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
402 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
403 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
407 if (_info.channels == 1) {
408 framecnt_t ret = sf_read_float (_sndfile, dst, file_cnt);
409 if (ret != file_cnt) {
411 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
412 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;
418 real_cnt = cnt * _info.channels;
420 Sample* interleave_buf = get_interleave_buffer (real_cnt);
422 nread = sf_read_float (_sndfile, interleave_buf, real_cnt);
423 ptr = interleave_buf + _channel;
424 nread /= _info.channels;
426 /* stride through the interleaved data */
428 for (int32_t n = 0; n < nread; ++n) {
430 ptr += _info.channels;
437 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
444 return destructive_write_unlocked (data, cnt);
446 return nondestructive_write_unlocked (data, cnt);
451 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
454 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
458 if (_info.channels != 1) {
459 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
460 abort(); /*NOTREACHED*/
464 int32_t frame_pos = _length;
466 if (write_float (data, frame_pos, cnt) != cnt) {
470 update_length (_length + cnt);
472 if (_build_peakfiles) {
473 compute_and_write_peaks (data, frame_pos, cnt, false, true);
480 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
483 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
487 if (_capture_start && _capture_end) {
489 /* start and end of capture both occur within the data we are writing,
490 so do both crossfades.
493 _capture_start = false;
494 _capture_end = false;
496 /* move to the correct location place */
497 file_pos = capture_start_frame - _timeline_position;
500 framecnt_t subcnt = cnt / 2;
501 framecnt_t ofilepos = file_pos;
504 if (crossfade (data, subcnt, 1) != subcnt) {
509 Sample * tmpdata = data + subcnt;
512 subcnt = cnt - subcnt;
513 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
517 file_pos = ofilepos; // adjusted below
519 } else if (_capture_start) {
521 /* start of capture both occur within the data we are writing,
525 _capture_start = false;
526 _capture_end = false;
528 /* move to the correct location place */
529 file_pos = capture_start_frame - _timeline_position;
531 if (crossfade (data, cnt, 1) != cnt) {
535 } else if (_capture_end) {
537 /* end of capture both occur within the data we are writing,
541 _capture_start = false;
542 _capture_end = false;
544 if (crossfade (data, cnt, 0) != cnt) {
550 /* in the middle of recording */
552 if (write_float (data, file_pos, cnt) != cnt) {
557 update_length (file_pos + cnt);
559 if (_build_peakfiles) {
560 compute_and_write_peaks (data, file_pos, cnt, false, true);
569 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
571 set_timeline_position (when);
573 if (_flags & Broadcast) {
574 if (setup_broadcast_info (when, now, tnow)) {
579 return flush_header ();
583 SndFileSource::flush_header ()
586 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
591 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
595 int const r = sf_command (_sndfile, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
601 SndFileSource::flush ()
604 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
609 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
613 // Hopefully everything OK
614 sf_write_sync (_sndfile);
618 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
621 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
626 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
630 if (!(_flags & Broadcast) || !_broadcast_info) {
634 _broadcast_info->set_originator_ref_from_session (_session);
635 _broadcast_info->set_origination_time (&now);
637 /* now update header position taking header offset into account */
639 set_header_timeline_position ();
641 if (!_broadcast_info->write_to_file (_sndfile)) {
642 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
643 _path, _broadcast_info->get_error())
645 _flags = Flag (_flags & ~Broadcast);
646 delete _broadcast_info;
654 SndFileSource::set_header_timeline_position ()
656 if (!(_flags & Broadcast)) {
660 _broadcast_info->set_time_reference (_timeline_position);
662 if (_sndfile == 0 || !_broadcast_info->write_to_file (_sndfile)) {
663 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
664 _path, _broadcast_info->get_error())
666 _flags = Flag (_flags & ~Broadcast);
667 delete _broadcast_info;
673 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
675 if (_sndfile == 0 || sf_seek (_sndfile, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
677 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
678 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
682 if (sf_writef_float (_sndfile, data, cnt) != (ssize_t) cnt) {
690 SndFileSource::natural_position() const
692 return _timeline_position;
696 SndFileSource::set_destructive (bool yn)
699 _flags = Flag (_flags | Writable | Destructive);
701 xfade_buf = new Sample[xfade_frames];
703 clear_capture_marks ();
704 _timeline_position = header_position_offset;
706 _flags = Flag (_flags & ~Destructive);
707 _timeline_position = 0;
708 /* leave xfade buf alone in case we need it again later */
715 SndFileSource::clear_capture_marks ()
717 _capture_start = false;
718 _capture_end = false;
721 /** @param pos Capture start position in session frames */
723 SndFileSource::mark_capture_start (framepos_t pos)
726 if (pos < _timeline_position) {
727 _capture_start = false;
729 _capture_start = true;
730 capture_start_frame = pos;
736 SndFileSource::mark_capture_end()
744 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
746 framecnt_t xfade = min (xfade_frames, cnt);
747 framecnt_t nofade = cnt - xfade;
748 Sample* fade_data = 0;
749 framepos_t fade_position = 0; // in frames
754 fade_position = file_pos;
757 fade_position = file_pos + nofade;
758 fade_data = data + nofade;
761 if (fade_position > _length) {
763 /* read starts beyond end of data, just memset to zero */
767 } else if (fade_position + xfade > _length) {
769 /* read ends beyond end of data, read some, memset the rest */
771 file_cnt = _length - fade_position;
775 /* read is entirely within data */
782 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
783 if (retval >= 0 && errno == EAGAIN) {
784 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
785 * short or no data there */
786 memset (xfade_buf, 0, xfade * sizeof(Sample));
788 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
794 if (file_cnt != xfade) {
795 framecnt_t delta = xfade - file_cnt;
796 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
799 if (nofade && !fade_in) {
800 if (write_float (data, file_pos, nofade) != nofade) {
801 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
806 if (xfade == xfade_frames) {
810 /* use the standard xfade curve */
814 /* fade new material in */
816 for (n = 0; n < xfade; ++n) {
817 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
823 /* fade new material out */
825 for (n = 0; n < xfade; ++n) {
826 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
830 } else if (xfade < xfade_frames) {
832 std::vector<gain_t> in(xfade);
833 std::vector<gain_t> out(xfade);
835 /* short xfade, compute custom curve */
837 compute_equal_power_fades (xfade, &in[0], &out[0]);
839 for (framecnt_t n = 0; n < xfade; ++n) {
840 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
845 /* long xfade length, has to be computed across several calls */
850 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
851 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
856 if (fade_in && nofade) {
857 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
858 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
867 SndFileSource::last_capture_start_frame () const
870 return capture_start_frame;
877 SndFileSource::handle_header_position_change ()
880 if ( _length != 0 ) {
881 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
882 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
883 } else if (writable()) {
884 _timeline_position = header_position_offset;
885 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
891 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
893 /* This static method is assumed to have been called by the Session
894 before any DFS's are created.
897 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
899 delete [] out_coefficient;
900 delete [] in_coefficient;
902 out_coefficient = new gain_t[xfade_frames];
903 in_coefficient = new gain_t[xfade_frames];
905 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
909 SndFileSource::set_timeline_position (framepos_t pos)
911 // destructive track timeline postion does not change
912 // except at instantion or when header_position_offset
913 // (session start) changes
915 if (!destructive()) {
916 AudioFileSource::set_timeline_position (pos);
921 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
927 sf_info.format = 0; // libsndfile says to clear this before sf_open().
929 if ((sf = sf_open (const_cast<char*>(path.c_str()), SFM_READ, &sf_info)) == 0) {
931 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
935 info.samplerate = sf_info.samplerate;
936 info.channels = sf_info.channels;
937 info.length = sf_info.frames;
939 string major = sndfile_major_format(sf_info.format);
940 string minor = sndfile_minor_format(sf_info.format);
942 if (major.length() + minor.length() < 16) { /* arbitrary */
943 info.format_name = string_compose("%1/%2", major, minor);
945 info.format_name = string_compose("%1\n%2", major, minor);
948 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
956 SndFileSource::one_of_several_channels () const
958 return _info.channels > 1;
962 SndFileSource::clamped_at_unity () const
964 int const type = _info.format & SF_FORMAT_TYPEMASK;
965 int const sub = _info.format & SF_FORMAT_SUBMASK;
966 /* XXX: this may not be the full list of formats that are unclamped */
967 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
971 SndFileSource::file_closed ()
973 /* stupid libsndfile updated the headers on close,
974 so touch the peakfile if it exists and has data
975 to make sure its time is as new as the audio
983 SndFileSource::set_path (const string& p)
985 FileSource::set_path (p);