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)
53 : AudioFileSource (s, node)
58 throw failed_constructor ();
62 SndFileSource::SndFileSource (Session& s, ustring path, int chn, Flag flags)
63 /* files created this way are never writable or removable */
64 : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
71 throw failed_constructor ();
75 SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
76 : AudioFileSource (s, path, flags, sfmt, hf)
82 /* this constructor is used to construct new files, not open
91 _flags = Flag (_flags & ~Broadcast);
96 _flags = Flag (_flags & ~Broadcast);
101 _flags = Flag (_flags | Broadcast);
106 _flags = Flag (_flags & ~Broadcast);
111 _flags = Flag (_flags & ~Broadcast);
115 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
123 fmt |= SF_FORMAT_FLOAT;
127 fmt |= SF_FORMAT_PCM_24;
131 fmt |= SF_FORMAT_PCM_16;
136 _info.samplerate = rate;
140 throw failed_constructor();
143 if (writable() && (_flags & Broadcast)) {
145 if (!_broadcast_info) {
146 _broadcast_info = new BroadcastInfo;
149 _broadcast_info->set_from_session (s, header_position_offset);
150 _broadcast_info->set_description (string_compose ("BWF %1", _name));
152 if (!_broadcast_info->write_to_file (sf)) {
153 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
154 _path, _broadcast_info->get_error())
156 _flags = Flag (_flags & ~Broadcast);
157 delete _broadcast_info;
164 SndFileSource::init ()
168 // lets try to keep the object initalizations here at the top
176 _name = Glib::path_get_basename (_path);
179 /* although libsndfile says we don't need to set this,
180 valgrind and source code shows us that we do.
183 memset (&_info, 0, sizeof(_info));
185 _capture_start = false;
186 _capture_end = false;
190 xfade_buf = new Sample[xfade_frames];
191 _timeline_position = header_position_offset;
194 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change));
198 SndFileSource::open ()
200 if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
202 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
203 #ifndef HAVE_COREAUDIO
204 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
205 so we don't want to see this message.
208 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
209 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
214 if (_channel >= _info.channels) {
215 #ifndef HAVE_COREAUDIO
216 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
223 _length = _info.frames;
225 if (!_broadcast_info) {
226 _broadcast_info = new BroadcastInfo;
229 bool bwf_info_exists = _broadcast_info->load_from_file (sf);
231 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
233 if (_length != 0 && !bwf_info_exists) {
234 delete _broadcast_info;
236 _flags = Flag (_flags & ~Broadcast);
240 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
246 SndFileSource::~SndFileSource ()
248 GoingAway (); /* EMIT SIGNAL */
254 /* stupid libsndfile updated the headers on close,
255 so touch the peakfile if it exists and has data
256 to make sure its time is as new as the audio
263 delete _broadcast_info;
268 SndFileSource::sample_rate () const
270 return _info.samplerate;
274 SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const
281 if (start > _length) {
283 /* read starts beyond end of data, just memset to zero */
287 } else if (start + cnt > _length) {
289 /* read ends beyond end of data, read some, memset the rest */
291 file_cnt = _length - start;
295 /* read is entirely within data */
300 if (file_cnt != cnt) {
301 nframes_t delta = cnt - file_cnt;
302 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
307 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
309 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
310 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
314 if (_info.channels == 1) {
315 nframes_t ret = sf_read_float (sf, dst, file_cnt);
316 _read_data_count = ret * sizeof(float);
317 if (ret != file_cnt) {
319 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
320 cerr << string_compose(_("SndFileSource: @ %1 could not read %2 within %3 (%4) (len = %5)"), start, file_cnt, _name.substr (1), errbuf, _length) << endl;
326 real_cnt = cnt * _info.channels;
328 Sample* interleave_buf = get_interleave_buffer (real_cnt);
330 nread = sf_read_float (sf, interleave_buf, real_cnt);
331 ptr = interleave_buf + _channel;
332 nread /= _info.channels;
334 /* stride through the interleaved data */
336 for (int32_t n = 0; n < nread; ++n) {
338 ptr += _info.channels;
341 _read_data_count = cnt * sizeof(float);
347 SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
350 return destructive_write_unlocked (data, cnt);
352 return nondestructive_write_unlocked (data, cnt);
357 SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
360 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
364 if (_info.channels != 1) {
365 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
371 int32_t frame_pos = _length;
373 if (write_float (data, frame_pos, cnt) != cnt) {
378 update_length (oldlen, cnt);
380 if (_build_peakfiles) {
381 compute_and_write_peaks (data, frame_pos, cnt, false, true);
384 _write_data_count = cnt;
390 SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
392 nframes_t old_file_pos;
395 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
399 if (_capture_start && _capture_end) {
401 /* start and end of capture both occur within the data we are writing,
402 so do both crossfades.
405 _capture_start = false;
406 _capture_end = false;
408 /* move to the correct location place */
409 file_pos = capture_start_frame - _timeline_position;
412 nframes_t subcnt = cnt / 2;
413 nframes_t ofilepos = file_pos;
416 if (crossfade (data, subcnt, 1) != subcnt) {
421 Sample * tmpdata = data + subcnt;
424 subcnt = cnt - subcnt;
425 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
429 file_pos = ofilepos; // adjusted below
431 } else if (_capture_start) {
433 /* start of capture both occur within the data we are writing,
437 _capture_start = false;
438 _capture_end = false;
440 /* move to the correct location place */
441 file_pos = capture_start_frame - _timeline_position;
443 if (crossfade (data, cnt, 1) != cnt) {
447 } else if (_capture_end) {
449 /* end of capture both occur within the data we are writing,
453 _capture_start = false;
454 _capture_end = false;
456 if (crossfade (data, cnt, 0) != cnt) {
462 /* in the middle of recording */
464 if (write_float (data, file_pos, cnt) != cnt) {
469 old_file_pos = file_pos;
470 update_length (file_pos, cnt);
472 if (_build_peakfiles) {
473 compute_and_write_peaks (data, file_pos, cnt, false, true);
482 SndFileSource::update_header (nframes_t when, struct tm& now, time_t tnow)
484 set_timeline_position (when);
486 if (_flags & Broadcast) {
487 if (setup_broadcast_info (when, now, tnow)) {
492 return flush_header ();
496 SndFileSource::flush_header ()
498 if (!writable() || (sf == 0)) {
499 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
502 return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
506 SndFileSource::setup_broadcast_info (nframes_t when, struct tm& now, time_t tnow)
509 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
513 if (!(_flags & Broadcast)) {
517 _broadcast_info->set_originator_ref ();
518 _broadcast_info->set_origination_time (&now);
520 /* now update header position taking header offset into account */
522 set_header_timeline_position ();
524 if (!_broadcast_info->write_to_file (sf)) {
525 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
526 _path, _broadcast_info->get_error())
528 _flags = Flag (_flags & ~Broadcast);
529 delete _broadcast_info;
537 SndFileSource::set_header_timeline_position ()
539 if (!(_flags & Broadcast)) {
543 _broadcast_info->set_time_reference (_timeline_position);
545 if (!_broadcast_info->write_to_file (sf)) {
546 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
547 _path, _broadcast_info->get_error())
549 _flags = Flag (_flags & ~Broadcast);
550 delete _broadcast_info;
556 SndFileSource::write_float (Sample* data, nframes_t frame_pos, nframes_t cnt)
558 if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
560 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
561 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
565 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
573 SndFileSource::natural_position() const
575 return _timeline_position;
579 SndFileSource::set_destructive (bool yn)
582 _flags = Flag (_flags | Destructive);
584 xfade_buf = new Sample[xfade_frames];
586 clear_capture_marks ();
587 _timeline_position = header_position_offset;
589 _flags = Flag (_flags & ~Destructive);
590 _timeline_position = 0;
591 /* leave xfade buf alone in case we need it again later */
598 SndFileSource::clear_capture_marks ()
600 _capture_start = false;
601 _capture_end = false;
605 SndFileSource::mark_capture_start (nframes_t pos)
608 if (pos < _timeline_position) {
609 _capture_start = false;
611 _capture_start = true;
612 capture_start_frame = pos;
618 SndFileSource::mark_capture_end()
626 SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
628 nframes_t xfade = min (xfade_frames, cnt);
629 nframes_t nofade = cnt - xfade;
630 Sample* fade_data = 0;
631 nframes_t fade_position = 0; // in frames
636 fade_position = file_pos;
639 fade_position = file_pos + nofade;
640 fade_data = data + nofade;
643 if (fade_position > _length) {
645 /* read starts beyond end of data, just memset to zero */
649 } else if (fade_position + xfade > _length) {
651 /* read ends beyond end of data, read some, memset the rest */
653 file_cnt = _length - fade_position;
657 /* read is entirely within data */
664 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
665 if (retval >= 0 && errno == EAGAIN) {
666 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
667 * short or no data there */
668 memset (xfade_buf, 0, xfade * sizeof(Sample));
670 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
676 if (file_cnt != xfade) {
677 nframes_t delta = xfade - file_cnt;
678 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
681 if (nofade && !fade_in) {
682 if (write_float (data, file_pos, nofade) != nofade) {
683 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
688 if (xfade == xfade_frames) {
692 /* use the standard xfade curve */
696 /* fade new material in */
698 for (n = 0; n < xfade; ++n) {
699 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
705 /* fade new material out */
707 for (n = 0; n < xfade; ++n) {
708 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
712 } else if (xfade < xfade_frames) {
717 /* short xfade, compute custom curve */
719 compute_equal_power_fades (xfade, in, out);
721 for (nframes_t n = 0; n < xfade; ++n) {
722 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
727 /* long xfade length, has to be computed across several calls */
732 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
733 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
738 if (fade_in && nofade) {
739 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
740 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
749 SndFileSource::last_capture_start_frame () const
752 return capture_start_frame;
759 SndFileSource::handle_header_position_change ()
762 if ( _length != 0 ) {
763 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
764 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
765 } else if (writable()) {
766 _timeline_position = header_position_offset;
767 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
773 SndFileSource::setup_standard_crossfades (nframes_t rate)
775 /* This static method is assumed to have been called by the Session
776 before any DFS's are created.
779 xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
781 delete [] out_coefficient;
782 delete [] in_coefficient;
784 out_coefficient = new gain_t[xfade_frames];
785 in_coefficient = new gain_t[xfade_frames];
787 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
791 SndFileSource::set_timeline_position (int64_t pos)
793 // destructive track timeline postion does not change
794 // except at instantion or when header_position_offset
795 // (session start) changes
797 if (!destructive()) {
798 AudioFileSource::set_timeline_position (pos);
803 SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
809 sf_info.format = 0; // libsndfile says to clear this before sf_open().
811 if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
813 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
817 info.samplerate = sf_info.samplerate;
818 info.channels = sf_info.channels;
819 info.length = sf_info.frames;
820 info.format_name = string_compose("Format: %1, %2",
821 sndfile_major_format(sf_info.format),
822 sndfile_minor_format(sf_info.format));
824 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
832 SndFileSource::one_of_several_channels () const
834 return _info.channels > 1;