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.
26 #include <sys/utsname.h>
29 #include <glibmm/miscutils.h>
31 #include "ardour/sndfilesource.h"
32 #include "ardour/sndfile_helpers.h"
33 #include "ardour/utils.h"
34 #include "ardour/version.h"
39 using namespace ARDOUR;
43 gain_t* SndFileSource::out_coefficient = 0;
44 gain_t* SndFileSource::in_coefficient = 0;
45 nframes_t SndFileSource::xfade_frames = 64;
46 const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
49 Source::RemovableIfEmpty |
52 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
54 , AudioFileSource (s, node)
59 throw failed_constructor ();
63 /** Files created this way are never writable or removable */
64 SndFileSource::SndFileSource (Session& s, const ustring& path, bool embedded, int chn, Flag flags)
65 : Source(s, DataType::AUDIO, path, flags)
66 , AudioFileSource (s, path, embedded,
67 Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
74 throw failed_constructor ();
78 /** This constructor is used to construct new files, not open existing ones. */
79 SndFileSource::SndFileSource (Session& s, const ustring& path, bool embedded,
80 SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
81 : Source(s, DataType::AUDIO, path, flags)
82 , AudioFileSource (s, path, embedded, flags, sfmt, hf)
93 _flags = Flag (_flags & ~Broadcast);
98 _flags = Flag (_flags & ~Broadcast);
103 _flags = Flag (_flags | Broadcast);
108 _flags = Flag (_flags & ~Broadcast);
113 _flags = Flag (_flags & ~Broadcast);
117 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
125 fmt |= SF_FORMAT_FLOAT;
129 fmt |= SF_FORMAT_PCM_24;
133 fmt |= SF_FORMAT_PCM_16;
138 _info.samplerate = rate;
142 throw failed_constructor();
145 if (writable() && (_flags & Broadcast)) {
147 if (!_broadcast_info) {
148 _broadcast_info = new BroadcastInfo;
151 _broadcast_info->set_from_session (s, header_position_offset);
152 _broadcast_info->set_description (string_compose ("BWF %1", _name));
154 if (!_broadcast_info->write_to_file (sf)) {
155 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
156 _path, _broadcast_info->get_error())
158 _flags = Flag (_flags & ~Broadcast);
159 delete _broadcast_info;
166 SndFileSource::init_sndfile ()
170 // lets try to keep the object initalizations here at the top
178 _name = Glib::path_get_basename (_path);
181 /* although libsndfile says we don't need to set this,
182 valgrind and source code shows us that we do.
185 memset (&_info, 0, sizeof(_info));
187 _capture_start = false;
188 _capture_end = false;
192 xfade_buf = new Sample[xfade_frames];
193 _timeline_position = header_position_offset;
196 AudioFileSource::HeaderPositionOffsetChanged.connect (
197 mem_fun (*this, &SndFileSource::handle_header_position_change));
201 SndFileSource::open ()
203 if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
205 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
206 #ifndef HAVE_COREAUDIO
207 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
208 so we don't want to see this message.
211 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
212 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
217 if (_channel >= _info.channels) {
218 #ifndef HAVE_COREAUDIO
219 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
226 _length = _info.frames;
228 if (!_broadcast_info) {
229 _broadcast_info = new BroadcastInfo;
232 bool bwf_info_exists = _broadcast_info->load_from_file (sf);
234 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
236 if (_length != 0 && !bwf_info_exists) {
237 delete _broadcast_info;
239 _flags = Flag (_flags & ~Broadcast);
243 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
249 SndFileSource::~SndFileSource ()
251 GoingAway (); /* EMIT SIGNAL */
257 /* stupid libsndfile updated the headers on close,
258 so touch the peakfile if it exists and has data
259 to make sure its time is as new as the audio
266 delete _broadcast_info;
271 SndFileSource::sample_rate () const
273 return _info.samplerate;
277 SndFileSource::read_unlocked (Sample *dst, sframes_t start, nframes_t cnt) const
284 if (start > _length) {
286 /* read starts beyond end of data, just memset to zero */
290 } else if (start + cnt > _length) {
292 /* read ends beyond end of data, read some, memset the rest */
294 file_cnt = _length - start;
298 /* read is entirely within data */
303 if (file_cnt != cnt) {
304 nframes_t delta = cnt - file_cnt;
305 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
310 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
312 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
313 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
317 if (_info.channels == 1) {
318 nframes_t ret = sf_read_float (sf, dst, file_cnt);
319 _read_data_count = ret * sizeof(float);
320 if (ret != file_cnt) {
322 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
323 cerr << string_compose(_("SndFileSource: @ %1 could not read %2 within %3 (%4) (len = %5)"), start, file_cnt, _name.substr (1), errbuf, _length) << endl;
329 real_cnt = cnt * _info.channels;
331 Sample* interleave_buf = get_interleave_buffer (real_cnt);
333 nread = sf_read_float (sf, interleave_buf, real_cnt);
334 ptr = interleave_buf + _channel;
335 nread /= _info.channels;
337 /* stride through the interleaved data */
339 for (int32_t n = 0; n < nread; ++n) {
341 ptr += _info.channels;
344 _read_data_count = cnt * sizeof(float);
350 SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
353 return destructive_write_unlocked (data, cnt);
355 return nondestructive_write_unlocked (data, cnt);
360 SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
363 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
367 if (_info.channels != 1) {
368 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
374 int32_t frame_pos = _length;
376 if (write_float (data, frame_pos, cnt) != cnt) {
381 update_length (oldlen, cnt);
383 if (_build_peakfiles) {
384 compute_and_write_peaks (data, frame_pos, cnt, false, true);
387 _write_data_count = cnt;
393 SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
395 nframes_t old_file_pos;
398 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
402 if (_capture_start && _capture_end) {
404 /* start and end of capture both occur within the data we are writing,
405 so do both crossfades.
408 _capture_start = false;
409 _capture_end = false;
411 /* move to the correct location place */
412 file_pos = capture_start_frame - _timeline_position;
415 nframes_t subcnt = cnt / 2;
416 nframes_t ofilepos = file_pos;
419 if (crossfade (data, subcnt, 1) != subcnt) {
424 Sample * tmpdata = data + subcnt;
427 subcnt = cnt - subcnt;
428 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
432 file_pos = ofilepos; // adjusted below
434 } else if (_capture_start) {
436 /* start of capture both occur within the data we are writing,
440 _capture_start = false;
441 _capture_end = false;
443 /* move to the correct location place */
444 file_pos = capture_start_frame - _timeline_position;
446 if (crossfade (data, cnt, 1) != cnt) {
450 } else if (_capture_end) {
452 /* end of capture both occur within the data we are writing,
456 _capture_start = false;
457 _capture_end = false;
459 if (crossfade (data, cnt, 0) != cnt) {
465 /* in the middle of recording */
467 if (write_float (data, file_pos, cnt) != cnt) {
472 old_file_pos = file_pos;
473 update_length (file_pos, cnt);
475 if (_build_peakfiles) {
476 compute_and_write_peaks (data, file_pos, cnt, false, true);
485 SndFileSource::update_header (sframes_t when, struct tm& now, time_t tnow)
487 set_timeline_position (when);
489 if (_flags & Broadcast) {
490 if (setup_broadcast_info (when, now, tnow)) {
495 return flush_header ();
499 SndFileSource::flush_header ()
501 if (!writable() || (sf == 0)) {
502 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
505 return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
509 SndFileSource::setup_broadcast_info (sframes_t when, struct tm& now, time_t tnow)
512 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
516 if (!(_flags & Broadcast)) {
520 _broadcast_info->set_originator_ref ();
521 _broadcast_info->set_origination_time (&now);
523 /* now update header position taking header offset into account */
525 set_header_timeline_position ();
527 if (!_broadcast_info->write_to_file (sf)) {
528 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
529 _path, _broadcast_info->get_error())
531 _flags = Flag (_flags & ~Broadcast);
532 delete _broadcast_info;
540 SndFileSource::set_header_timeline_position ()
542 if (!(_flags & Broadcast)) {
546 _broadcast_info->set_time_reference (_timeline_position);
548 if (!_broadcast_info->write_to_file (sf)) {
549 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
550 _path, _broadcast_info->get_error())
552 _flags = Flag (_flags & ~Broadcast);
553 delete _broadcast_info;
559 SndFileSource::write_float (Sample* data, sframes_t frame_pos, nframes_t cnt)
561 if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
563 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
564 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
568 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
576 SndFileSource::natural_position() const
578 return _timeline_position;
582 SndFileSource::set_destructive (bool yn)
585 _flags = Flag (_flags | Destructive);
587 xfade_buf = new Sample[xfade_frames];
589 clear_capture_marks ();
590 _timeline_position = header_position_offset;
592 _flags = Flag (_flags & ~Destructive);
593 _timeline_position = 0;
594 /* leave xfade buf alone in case we need it again later */
601 SndFileSource::clear_capture_marks ()
603 _capture_start = false;
604 _capture_end = false;
608 SndFileSource::mark_capture_start (sframes_t pos)
611 if (pos < _timeline_position) {
612 _capture_start = false;
614 _capture_start = true;
615 capture_start_frame = pos;
621 SndFileSource::mark_capture_end()
629 SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
631 nframes_t xfade = min (xfade_frames, cnt);
632 nframes_t nofade = cnt - xfade;
633 Sample* fade_data = 0;
634 nframes_t fade_position = 0; // in frames
639 fade_position = file_pos;
642 fade_position = file_pos + nofade;
643 fade_data = data + nofade;
646 if (fade_position > _length) {
648 /* read starts beyond end of data, just memset to zero */
652 } else if (fade_position + xfade > _length) {
654 /* read ends beyond end of data, read some, memset the rest */
656 file_cnt = _length - fade_position;
660 /* read is entirely within data */
667 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
668 if (retval >= 0 && errno == EAGAIN) {
669 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
670 * short or no data there */
671 memset (xfade_buf, 0, xfade * sizeof(Sample));
673 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
679 if (file_cnt != xfade) {
680 nframes_t delta = xfade - file_cnt;
681 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
684 if (nofade && !fade_in) {
685 if (write_float (data, file_pos, nofade) != nofade) {
686 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
691 if (xfade == xfade_frames) {
695 /* use the standard xfade curve */
699 /* fade new material in */
701 for (n = 0; n < xfade; ++n) {
702 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
708 /* fade new material out */
710 for (n = 0; n < xfade; ++n) {
711 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
715 } else if (xfade < xfade_frames) {
720 /* short xfade, compute custom curve */
722 compute_equal_power_fades (xfade, in, out);
724 for (nframes_t n = 0; n < xfade; ++n) {
725 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
730 /* long xfade length, has to be computed across several calls */
735 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
736 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
741 if (fade_in && nofade) {
742 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
743 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
752 SndFileSource::last_capture_start_frame () const
755 return capture_start_frame;
762 SndFileSource::handle_header_position_change ()
765 if ( _length != 0 ) {
766 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
767 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
768 } else if (writable()) {
769 _timeline_position = header_position_offset;
770 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
776 SndFileSource::setup_standard_crossfades (nframes_t rate)
778 /* This static method is assumed to have been called by the Session
779 before any DFS's are created.
782 xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
784 delete [] out_coefficient;
785 delete [] in_coefficient;
787 out_coefficient = new gain_t[xfade_frames];
788 in_coefficient = new gain_t[xfade_frames];
790 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
794 SndFileSource::set_timeline_position (int64_t pos)
796 // destructive track timeline postion does not change
797 // except at instantion or when header_position_offset
798 // (session start) changes
800 if (!destructive()) {
801 AudioFileSource::set_timeline_position (pos);
806 SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
812 sf_info.format = 0; // libsndfile says to clear this before sf_open().
814 if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
816 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
820 info.samplerate = sf_info.samplerate;
821 info.channels = sf_info.channels;
822 info.length = sf_info.frames;
823 info.format_name = string_compose("Format: %1, %2",
824 sndfile_major_format(sf_info.format),
825 sndfile_minor_format(sf_info.format));
827 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
835 SndFileSource::one_of_several_channels () const
837 return _info.channels > 1;