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
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 ()
238 #ifdef PLATFORM_WINDOWS
239 path_to_open = Glib::locale_from_utf8(_path);
241 path_to_open = _path;
244 _descriptor = new SndFileDescriptor (path_to_open.c_str(), writable(), &_info);
245 _descriptor->Closed.connect_same_thread (file_manager_connection, boost::bind (&SndFileSource::file_closed, this));
246 SNDFILE* sf = _descriptor->allocate ();
250 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
251 #ifndef HAVE_COREAUDIO
252 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
253 so we don't want to see this message.
256 cerr << "failed to open " << path_to_open << " with name " << _name << endl;
258 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
259 path_to_open, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
264 if (_channel >= _info.channels) {
265 #ifndef HAVE_COREAUDIO
266 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
273 _length = _info.frames;
275 if (!_broadcast_info) {
276 _broadcast_info = new BroadcastInfo;
279 bool bwf_info_exists = _broadcast_info->load_from_file (sf);
281 if (_file_is_new && _length == 0 && writable() && !bwf_info_exists) {
282 /* newly created files will not have a BWF header at this point in time.
283 * Import will have called Source::set_timeline_position() if one exists
284 * in the original. */
285 header_position_offset = _timeline_position;
288 /* Set our timeline position to either the time reference from a BWF header or the current
289 start of the session.
291 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
293 if (_length != 0 && !bwf_info_exists) {
294 delete _broadcast_info;
296 _flags = Flag (_flags & ~Broadcast);
299 /* Set the broadcast flag if the BWF info is already there. We need
300 * this when recovering or using existing files.
303 if (bwf_info_exists) {
304 _flags = Flag (_flags | Broadcast);
308 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
310 if (_flags & Broadcast) {
312 if (!_broadcast_info) {
313 _broadcast_info = new BroadcastInfo;
316 _broadcast_info->set_from_session (_session, header_position_offset);
317 _broadcast_info->set_description (string_compose ("BWF %1", _name));
319 if (!_broadcast_info->write_to_file (sf)) {
320 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
321 path_to_open, _broadcast_info->get_error())
323 _flags = Flag (_flags & ~Broadcast);
324 delete _broadcast_info;
330 _descriptor->release ();
335 SndFileSource::~SndFileSource ()
338 delete _broadcast_info;
343 SndFileSource::sample_rate () const
345 return _info.samplerate;
349 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
358 if (writable() && !_open) {
359 /* file has not been opened yet - nothing written to it */
360 memset (dst, 0, sizeof (Sample) * cnt);
364 SNDFILE* sf = _descriptor->allocate ();
367 error << string_compose (_("could not allocate file %1 for reading."), _path) << endmsg;
371 if (start > _length) {
373 /* read starts beyond end of data, just memset to zero */
377 } else if (start + cnt > _length) {
379 /* read ends beyond end of data, read some, memset the rest */
381 file_cnt = _length - start;
385 /* read is entirely within data */
390 assert (file_cnt >= 0);
392 if (file_cnt != cnt) {
393 framepos_t delta = cnt - file_cnt;
394 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
399 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
401 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
402 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.val().substr (1), errbuf) << endmsg;
403 _descriptor->release ();
407 if (_info.channels == 1) {
408 framecnt_t ret = sf_read_float (sf, 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;
414 _descriptor->release ();
419 real_cnt = cnt * _info.channels;
421 Sample* interleave_buf = get_interleave_buffer (real_cnt);
423 nread = sf_read_float (sf, interleave_buf, real_cnt);
424 ptr = interleave_buf + _channel;
425 nread /= _info.channels;
427 /* stride through the interleaved data */
429 for (int32_t n = 0; n < nread; ++n) {
431 ptr += _info.channels;
434 _descriptor->release ();
439 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
441 if (!_open && open()) {
446 return destructive_write_unlocked (data, cnt);
448 return nondestructive_write_unlocked (data, cnt);
453 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
456 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
460 if (_info.channels != 1) {
461 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
466 int32_t frame_pos = _length;
468 if (write_float (data, frame_pos, cnt) != cnt) {
472 update_length (_length + cnt);
474 if (_build_peakfiles) {
475 compute_and_write_peaks (data, frame_pos, cnt, false, true);
482 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
485 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
489 if (_capture_start && _capture_end) {
491 /* start and end of capture both occur within the data we are writing,
492 so do both crossfades.
495 _capture_start = false;
496 _capture_end = false;
498 /* move to the correct location place */
499 file_pos = capture_start_frame - _timeline_position;
502 framecnt_t subcnt = cnt / 2;
503 framecnt_t ofilepos = file_pos;
506 if (crossfade (data, subcnt, 1) != subcnt) {
511 Sample * tmpdata = data + subcnt;
514 subcnt = cnt - subcnt;
515 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
519 file_pos = ofilepos; // adjusted below
521 } else if (_capture_start) {
523 /* start of capture both occur within the data we are writing,
527 _capture_start = false;
528 _capture_end = false;
530 /* move to the correct location place */
531 file_pos = capture_start_frame - _timeline_position;
533 if (crossfade (data, cnt, 1) != cnt) {
537 } else if (_capture_end) {
539 /* end of capture both occur within the data we are writing,
543 _capture_start = false;
544 _capture_end = false;
546 if (crossfade (data, cnt, 0) != cnt) {
552 /* in the middle of recording */
554 if (write_float (data, file_pos, cnt) != cnt) {
559 update_length (file_pos + cnt);
561 if (_build_peakfiles) {
562 compute_and_write_peaks (data, file_pos, cnt, false, true);
571 SndFileSource::update_header (framepos_t when, struct tm& now, time_t tnow)
573 set_timeline_position (when);
575 if (_flags & Broadcast) {
576 if (setup_broadcast_info (when, now, tnow)) {
581 return flush_header ();
585 SndFileSource::flush_header ()
588 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
593 warning << string_compose (_("attempt to flush an un-opened audio file source (%1)"), _path) << endmsg;
597 SNDFILE* sf = _descriptor->allocate ();
599 error << string_compose (_("could not allocate file %1 to write header"), _path) << endmsg;
603 int const r = sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE;
604 _descriptor->release ();
610 SndFileSource::flush ()
613 warning << string_compose (_("attempt to flush an un-opened audio file source (%1)"), _path) << endmsg;
618 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
622 SNDFILE* sf = _descriptor->allocate ();
624 error << string_compose (_("could not allocate file %1 to flush contents"), _path) << endmsg;
628 // Hopefully everything OK
630 _descriptor->release ();
634 SndFileSource::setup_broadcast_info (framepos_t /*when*/, struct tm& now, time_t /*tnow*/)
637 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
642 warning << string_compose (_("attempt to set BWF info for an un-opened audio file source (%1)"), _path) << endmsg;
646 if (!(_flags & Broadcast)) {
650 _broadcast_info->set_originator_ref_from_session (_session);
651 _broadcast_info->set_origination_time (&now);
653 /* now update header position taking header offset into account */
655 set_header_timeline_position ();
657 SNDFILE* sf = _descriptor->allocate ();
659 if (sf == 0 || !_broadcast_info->write_to_file (sf)) {
660 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
661 _path, _broadcast_info->get_error())
663 _flags = Flag (_flags & ~Broadcast);
664 delete _broadcast_info;
668 _descriptor->release ();
673 SndFileSource::set_header_timeline_position ()
675 if (!(_flags & Broadcast)) {
679 _broadcast_info->set_time_reference (_timeline_position);
681 SNDFILE* sf = _descriptor->allocate ();
683 if (sf == 0 || !_broadcast_info->write_to_file (sf)) {
684 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
685 _path, _broadcast_info->get_error())
687 _flags = Flag (_flags & ~Broadcast);
688 delete _broadcast_info;
692 _descriptor->release ();
696 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
698 SNDFILE* sf = _descriptor->allocate ();
700 if (sf == 0 || sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
702 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
703 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3)"), _path, frame_pos, errbuf) << endmsg;
704 _descriptor->release ();
708 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
709 _descriptor->release ();
713 _descriptor->release ();
718 SndFileSource::natural_position() const
720 return _timeline_position;
724 SndFileSource::set_destructive (bool yn)
727 _flags = Flag (_flags | Writable | Destructive);
729 xfade_buf = new Sample[xfade_frames];
731 clear_capture_marks ();
732 _timeline_position = header_position_offset;
734 _flags = Flag (_flags & ~Destructive);
735 _timeline_position = 0;
736 /* leave xfade buf alone in case we need it again later */
743 SndFileSource::clear_capture_marks ()
745 _capture_start = false;
746 _capture_end = false;
749 /** @param pos Capture start position in session frames */
751 SndFileSource::mark_capture_start (framepos_t pos)
754 if (pos < _timeline_position) {
755 _capture_start = false;
757 _capture_start = true;
758 capture_start_frame = pos;
764 SndFileSource::mark_capture_end()
772 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
774 framecnt_t xfade = min (xfade_frames, cnt);
775 framecnt_t nofade = cnt - xfade;
776 Sample* fade_data = 0;
777 framepos_t fade_position = 0; // in frames
782 fade_position = file_pos;
785 fade_position = file_pos + nofade;
786 fade_data = data + nofade;
789 if (fade_position > _length) {
791 /* read starts beyond end of data, just memset to zero */
795 } else if (fade_position + xfade > _length) {
797 /* read ends beyond end of data, read some, memset the rest */
799 file_cnt = _length - fade_position;
803 /* read is entirely within data */
810 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
811 if (retval >= 0 && errno == EAGAIN) {
812 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
813 * short or no data there */
814 memset (xfade_buf, 0, xfade * sizeof(Sample));
816 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
822 if (file_cnt != xfade) {
823 framecnt_t delta = xfade - file_cnt;
824 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
827 if (nofade && !fade_in) {
828 if (write_float (data, file_pos, nofade) != nofade) {
829 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
834 if (xfade == xfade_frames) {
838 /* use the standard xfade curve */
842 /* fade new material in */
844 for (n = 0; n < xfade; ++n) {
845 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
851 /* fade new material out */
853 for (n = 0; n < xfade; ++n) {
854 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
858 } else if (xfade < xfade_frames) {
860 std::vector<gain_t> in(xfade);
861 std::vector<gain_t> out(xfade);
863 /* short xfade, compute custom curve */
865 compute_equal_power_fades (xfade, &in[0], &out[0]);
867 for (framecnt_t n = 0; n < xfade; ++n) {
868 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
873 /* long xfade length, has to be computed across several calls */
878 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
879 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
884 if (fade_in && nofade) {
885 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
886 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
895 SndFileSource::last_capture_start_frame () const
898 return capture_start_frame;
905 SndFileSource::handle_header_position_change ()
908 if ( _length != 0 ) {
909 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
910 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
911 } else if (writable()) {
912 _timeline_position = header_position_offset;
913 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
919 SndFileSource::setup_standard_crossfades (Session const & s, framecnt_t rate)
921 /* This static method is assumed to have been called by the Session
922 before any DFS's are created.
925 xfade_frames = (framecnt_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
927 delete [] out_coefficient;
928 delete [] in_coefficient;
930 out_coefficient = new gain_t[xfade_frames];
931 in_coefficient = new gain_t[xfade_frames];
933 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
937 SndFileSource::set_timeline_position (framepos_t pos)
939 // destructive track timeline postion does not change
940 // except at instantion or when header_position_offset
941 // (session start) changes
943 if (!destructive()) {
944 AudioFileSource::set_timeline_position (pos);
949 SndFileSource::get_soundfile_info (const string& path, SoundFileInfo& info, string& error_msg)
955 sf_info.format = 0; // libsndfile says to clear this before sf_open().
957 if ((sf = sf_open (const_cast<char*>(path.c_str()), SFM_READ, &sf_info)) == 0) {
959 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
963 info.samplerate = sf_info.samplerate;
964 info.channels = sf_info.channels;
965 info.length = sf_info.frames;
967 string major = sndfile_major_format(sf_info.format);
968 string minor = sndfile_minor_format(sf_info.format);
970 if (major.length() + minor.length() < 16) { /* arbitrary */
971 info.format_name = string_compose("%1/%2", major, minor);
973 info.format_name = string_compose("%1\n%2", major, minor);
976 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
984 SndFileSource::one_of_several_channels () const
986 return _info.channels > 1;
990 SndFileSource::clamped_at_unity () const
992 int const type = _info.format & SF_FORMAT_TYPEMASK;
993 int const sub = _info.format & SF_FORMAT_SUBMASK;
994 /* XXX: this may not be the full list of formats that are unclamped */
995 return (sub != SF_FORMAT_FLOAT && sub != SF_FORMAT_DOUBLE && type != SF_FORMAT_OGG);
999 SndFileSource::file_closed ()
1001 /* stupid libsndfile updated the headers on close,
1002 so touch the peakfile if it exists and has data
1003 to make sure its time is as new as the audio
1011 SndFileSource::set_path (const string& p)
1013 FileSource::set_path (p);
1016 _descriptor->set_path (_path);