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;
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
189 SndFileSource::init_sndfile ()
191 /* although libsndfile says we don't need to set this,
192 valgrind and source code shows us that we do.
195 memset (&_info, 0, sizeof(_info));
198 xfade_buf = new Sample[xfade_frames];
199 _timeline_position = header_position_offset;
202 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
206 SndFileSource::open ()
210 #ifdef PLATFORM_WINDOWS
211 path_to_open = Glib::locale_from_utf8(_path);
213 path_to_open = _path;
216 _descriptor = new SndFileDescriptor (path_to_open.c_str(), writable(), &_info);
217 _descriptor->Closed.connect_same_thread (file_manager_connection, boost::bind (&SndFileSource::file_closed, this));
218 SNDFILE* sf = _descriptor->allocate ();
222 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
223 #ifndef HAVE_COREAUDIO
224 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
225 so we don't want to see this message.
228 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
230 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
231 path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
236 if (_channel >= _info.channels) {
237 #ifndef HAVE_COREAUDIO
238 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
245 _length = _info.frames;
247 if (!_broadcast_info) {
248 _broadcast_info = new BroadcastInfo;
251 bool bwf_info_exists = _broadcast_info->load_from_file (sf);
253 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
254 /* newly created files will not have a BWF header at this point in time.
255 * Import will have called Source::set_timeline_position() if one exists
256 * in the original. */
257 header_position_offset = _timeline_position;
260 /* Set our timeline position to either the time reference from a BWF header or the current
261 start of the session.
263 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
265 if (_length != 0 && !bwf_info_exists) {
266 delete _broadcast_info;
268 _flags = Flag (_flags & ~Broadcast);
272 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
274 if (_flags & Broadcast) {
276 if (!_broadcast_info) {
277 _broadcast_info = new BroadcastInfo;
280 _broadcast_info->set_from_session (_session, header_position_offset);
281 _broadcast_info->set_description (string_compose ("BWF %1", _name));
283 if (!_broadcast_info->write_to_file (sf)) {
284 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
285 path_to_open, _broadcast_info->get_error())
287 _flags = Flag (_flags & ~Broadcast);
288 delete _broadcast_info;
294 _descriptor->release ();
299 SndFileSource::~SndFileSource ()
302 delete _broadcast_info;
307 SndFileSource::sample_rate () const
309 return _info.samplerate;
313 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
322 if (writable() && !_open) {
323 /* file has not been opened yet - nothing written to it */
324 memset (dst, 0, sizeof (Sample) * cnt);
328 SNDFILE* sf = _descriptor->allocate ();
331 error << string_compose (_("could not allocate file %1 for reading."), _path) << endmsg;
335 if (start > _length) {
337 /* read starts beyond end of data, just memset to zero */
341 } else if (start + cnt > _length) {
343 /* read ends beyond end of data, read some, memset the rest */
345 file_cnt = _length - start;
349 /* read is entirely within data */
354 assert (file_cnt >= 0);
356 if (file_cnt != cnt) {
357 framepos_t delta = cnt - file_cnt;
358 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
363 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
365 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
366 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
367 _descriptor->release ();
371 if (_info.channels == 1) {
372 framecnt_t ret = sf_read_float (sf, dst, file_cnt);
373 if (ret != file_cnt) {
375 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
376 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;
378 _descriptor->release ();
383 real_cnt = cnt * _info.channels;
385 Sample* interleave_buf = get_interleave_buffer (real_cnt);
387 nread = sf_read_float (sf, interleave_buf, real_cnt);
388 ptr = interleave_buf + _channel;
389 nread /= _info.channels;
391 /* stride through the interleaved data */
393 for (int32_t n = 0; n < nread; ++n) {
395 ptr += _info.channels;
398 _descriptor->release ();
403 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
405 if (!_open && open()) {
410 return destructive_write_unlocked (data, cnt);
412 return nondestructive_write_unlocked (data, cnt);
417 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
420 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
424 if (_info.channels != 1) {
425 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
430 int32_t frame_pos = _length;
432 if (write_float (data, frame_pos, cnt) != cnt) {
436 update_length (_length + cnt);
438 if (_build_peakfiles) {
439 compute_and_write_peaks (data, frame_pos, cnt, false, true);
446 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
449 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
453 if (_capture_start && _capture_end) {
455 /* start and end of capture both occur within the data we are writing,
456 so do both crossfades.
459 _capture_start = false;
460 _capture_end = false;
462 /* move to the correct location place */
463 file_pos = capture_start_frame - _timeline_position;
466 framecnt_t subcnt = cnt / 2;
467 framecnt_t ofilepos = file_pos;
470 if (crossfade (data, subcnt, 1) != subcnt) {
475 Sample * tmpdata = data + subcnt;
478 subcnt = cnt - subcnt;
479 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
483 file_pos = ofilepos; // adjusted below
485 } else if (_capture_start) {
487 /* start of capture both occur within the data we are writing,
491 _capture_start = false;
492 _capture_end = false;
494 /* move to the correct location place */
495 file_pos = capture_start_frame - _timeline_position;
497 if (crossfade (data, cnt, 1) != cnt) {
501 } else if (_capture_end) {
503 /* end of capture both occur within the data we are writing,
507 _capture_start = false;
508 _capture_end = false;
510 if (crossfade (data, cnt, 0) != cnt) {
516 /* in the middle of recording */
518 if (write_float (data, file_pos, cnt) != cnt) {
523 update_length (file_pos + cnt);
525 if (_build_peakfiles) {
526 compute_and_write_peaks (data, file_pos, cnt, false, true);
535 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
537 set_timeline_position (when);
539 if (_flags & Broadcast) {
540 if (setup_broadcast_info (when, now, tnow)) {
545 return flush_header ();
549 SndFileSource::flush_header ()
552 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
557 warning << string_compose (_("attempt to flush an un-opened audio file source (%1)"), _path) << endmsg;
561 SNDFILE* sf = _descriptor->allocate ();
563 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
567 int const r = sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
568 _descriptor->release ();
574 SndFileSource::flush ()
577 warning << string_compose (_("attempt to flush an un-opened audio file source (%1)"), _path) << endmsg;
582 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
586 SNDFILE* sf = _descriptor->allocate ();
588 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
592 // Hopefully everything OK
594 _descriptor->release ();
598 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
601 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
606 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
610 if (!(_flags & Broadcast)) {
614 _broadcast_info->set_originator_ref_from_session (_session);
615 _broadcast_info->set_origination_time (&now);
617 /* now update header position taking header offset into account */
619 set_header_timeline_position ();
621 SNDFILE* sf = _descriptor->allocate ();
623 if (sf == 0 || !_broadcast_info->write_to_file (sf)) {
624 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
625 _path, _broadcast_info->get_error())
627 _flags = Flag (_flags & ~Broadcast);
628 delete _broadcast_info;
632 _descriptor->release ();
637 SndFileSource::set_header_timeline_position ()
639 if (!(_flags & Broadcast)) {
643 _broadcast_info->set_time_reference (_timeline_position);
645 SNDFILE* sf = _descriptor->allocate ();
647 if (sf == 0 || !_broadcast_info->write_to_file (sf)) {
648 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
649 _path, _broadcast_info->get_error())
651 _flags = Flag (_flags & ~Broadcast);
652 delete _broadcast_info;
656 _descriptor->release ();
660 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
662 SNDFILE* sf = _descriptor->allocate ();
664 if (sf == 0 || sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
666 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
667 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
668 _descriptor->release ();
672 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
673 _descriptor->release ();
677 _descriptor->release ();
682 SndFileSource::natural_position() const
684 return _timeline_position;
688 SndFileSource::set_destructive (bool yn)
691 _flags = Flag (_flags | Writable | Destructive);
693 xfade_buf = new Sample[xfade_frames];
695 clear_capture_marks ();
696 _timeline_position = header_position_offset;
698 _flags = Flag (_flags & ~Destructive);
699 _timeline_position = 0;
700 /* leave xfade buf alone in case we need it again later */
707 SndFileSource::clear_capture_marks ()
709 _capture_start = false;
710 _capture_end = false;
713 /** @param pos Capture start position in session frames */
715 SndFileSource::mark_capture_start (framepos_t pos)
718 if (pos < _timeline_position) {
719 _capture_start = false;
721 _capture_start = true;
722 capture_start_frame = pos;
728 SndFileSource::mark_capture_end()
736 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
738 framecnt_t xfade = min (xfade_frames, cnt);
739 framecnt_t nofade = cnt - xfade;
740 Sample* fade_data = 0;
741 framepos_t fade_position = 0; // in frames
746 fade_position = file_pos;
749 fade_position = file_pos + nofade;
750 fade_data = data + nofade;
753 if (fade_position > _length) {
755 /* read starts beyond end of data, just memset to zero */
759 } else if (fade_position + xfade > _length) {
761 /* read ends beyond end of data, read some, memset the rest */
763 file_cnt = _length - fade_position;
767 /* read is entirely within data */
774 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
775 if (retval >= 0 && errno == EAGAIN) {
776 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
777 * short or no data there */
778 memset (xfade_buf, 0, xfade * sizeof(Sample));
780 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
786 if (file_cnt != xfade) {
787 framecnt_t delta = xfade - file_cnt;
788 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
791 if (nofade && !fade_in) {
792 if (write_float (data, file_pos, nofade) != nofade) {
793 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
798 if (xfade == xfade_frames) {
802 /* use the standard xfade curve */
806 /* fade new material in */
808 for (n = 0; n < xfade; ++n) {
809 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
815 /* fade new material out */
817 for (n = 0; n < xfade; ++n) {
818 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
822 } else if (xfade < xfade_frames) {
824 std::vector<gain_t> in(xfade);
825 std::vector<gain_t> out(xfade);
827 /* short xfade, compute custom curve */
829 compute_equal_power_fades (xfade, &in[0], &out[0]);
831 for (framecnt_t n = 0; n < xfade; ++n) {
832 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
837 /* long xfade length, has to be computed across several calls */
842 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
843 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
848 if (fade_in && nofade) {
849 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
850 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
859 SndFileSource::last_capture_start_frame () const
862 return capture_start_frame;
869 SndFileSource::handle_header_position_change ()
872 if ( _length != 0 ) {
873 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
874 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
875 } else if (writable()) {
876 _timeline_position = header_position_offset;
877 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
883 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
885 /* This static method is assumed to have been called by the Session
886 before any DFS's are created.
889 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
891 delete [] out_coefficient;
892 delete [] in_coefficient;
894 out_coefficient = new gain_t[xfade_frames];
895 in_coefficient = new gain_t[xfade_frames];
897 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
901 SndFileSource::set_timeline_position (framepos_t pos)
903 // destructive track timeline postion does not change
904 // except at instantion or when header_position_offset
905 // (session start) changes
907 if (!destructive()) {
908 AudioFileSource::set_timeline_position (pos);
913 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
919 sf_info.format = 0; // libsndfile says to clear this before sf_open().
921 if ((sf = sf_open (const_cast<char*>(path.c_str()), SFM_READ, &sf_info)) == 0) {
923 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
927 info.samplerate = sf_info.samplerate;
928 info.channels = sf_info.channels;
929 info.length = sf_info.frames;
931 string major = sndfile_major_format(sf_info.format);
932 string minor = sndfile_minor_format(sf_info.format);
934 if (major.length() + minor.length() < 16) { /* arbitrary */
935 info.format_name = string_compose("%1/%2", major, minor);
937 info.format_name = string_compose("%1\n%2", major, minor);
940 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
948 SndFileSource::one_of_several_channels () const
950 return _info.channels > 1;
954 SndFileSource::clamped_at_unity () const
956 int const type = _info.format & SF_FORMAT_TYPEMASK;
957 int const sub = _info.format & SF_FORMAT_SUBMASK;
958 /* XXX: this may not be the full list of formats that are unclamped */
959 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
963 SndFileSource::file_closed ()
965 /* stupid libsndfile updated the headers on close,
966 so touch the peakfile if it exists and has data
967 to make sure its time is as new as the audio
975 SndFileSource::set_path (const string& p)
977 FileSource::set_path (p);
980 _descriptor->set_path (_path);