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 AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSource::Flag (AudioFileSource::Writable|
47 AudioFileSource::Removable|
48 AudioFileSource::RemovableIfEmpty|
49 AudioFileSource::CanRename);
51 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
52 : AudioFileSource (s, node)
57 throw failed_constructor ();
61 SndFileSource::SndFileSource (Session& s, ustring path, int chn, Flag flags)
62 /* files created this way are never writable or removable */
63 : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
70 throw failed_constructor ();
74 SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
75 : AudioFileSource (s, path, flags, sfmt, hf)
81 /* this constructor is used to construct new files, not open
90 _flags = Flag (_flags & ~Broadcast);
95 _flags = Flag (_flags & ~Broadcast);
100 _flags = Flag (_flags | Broadcast);
105 _flags = Flag (_flags & ~Broadcast);
110 _flags = Flag (_flags & ~Broadcast);
114 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
122 fmt |= SF_FORMAT_FLOAT;
126 fmt |= SF_FORMAT_PCM_24;
130 fmt |= SF_FORMAT_PCM_16;
135 _info.samplerate = rate;
139 throw failed_constructor();
142 if (writable() && (_flags & Broadcast)) {
144 if (!_broadcast_info) {
145 _broadcast_info = new BroadcastInfo;
148 _broadcast_info->set_from_session (s, header_position_offset);
149 _broadcast_info->set_description (string_compose ("BWF %1", _name));
151 if (!_broadcast_info->write_to_file (sf)) {
152 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
153 _path, _broadcast_info->get_error())
155 _flags = Flag (_flags & ~Broadcast);
156 delete _broadcast_info;
163 SndFileSource::init ()
167 // lets try to keep the object initalizations here at the top
175 _name = Glib::path_get_basename (_path);
178 /* although libsndfile says we don't need to set this,
179 valgrind and source code shows us that we do.
182 memset (&_info, 0, sizeof(_info));
184 _capture_start = false;
185 _capture_end = false;
189 xfade_buf = new Sample[xfade_frames];
190 timeline_position = header_position_offset;
193 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change));
197 SndFileSource::open ()
199 if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
201 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
202 #ifndef HAVE_COREAUDIO
203 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
204 so we don't want to see this message.
207 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
208 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
213 if (_channel >= _info.channels) {
214 #ifndef HAVE_COREAUDIO
215 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
222 _length = _info.frames;
224 if (!_broadcast_info) {
225 _broadcast_info = new BroadcastInfo;
228 bool bwf_info_exists = _broadcast_info->load_from_file (sf);
230 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
232 if (_length != 0 && !bwf_info_exists) {
233 delete _broadcast_info;
235 _flags = Flag (_flags & ~Broadcast);
239 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
245 SndFileSource::~SndFileSource ()
247 GoingAway (); /* EMIT SIGNAL */
253 /* stupid libsndfile updated the headers on close,
254 so touch the peakfile if it exists and has data
255 to make sure its time is as new as the audio
262 delete _broadcast_info;
267 SndFileSource::sample_rate () const
269 return _info.samplerate;
273 SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const
280 if (start > _length) {
282 /* read starts beyond end of data, just memset to zero */
286 } else if (start + cnt > _length) {
288 /* read ends beyond end of data, read some, memset the rest */
290 file_cnt = _length - start;
294 /* read is entirely within data */
299 if (file_cnt != cnt) {
300 nframes_t delta = cnt - file_cnt;
301 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
306 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
308 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
309 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
313 if (_info.channels == 1) {
314 nframes_t ret = sf_read_float (sf, dst, file_cnt);
315 _read_data_count = ret * sizeof(float);
316 if (ret != file_cnt) {
318 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
319 cerr << string_compose(_("SndFileSource: @ %1 could not read %2 within %3 (%4) (len = %5)"), start, file_cnt, _name.substr (1), errbuf, _length) << endl;
325 real_cnt = cnt * _info.channels;
327 Sample* interleave_buf = get_interleave_buffer (real_cnt);
329 nread = sf_read_float (sf, interleave_buf, real_cnt);
330 ptr = interleave_buf + _channel;
331 nread /= _info.channels;
333 /* stride through the interleaved data */
335 for (int32_t n = 0; n < nread; ++n) {
337 ptr += _info.channels;
340 _read_data_count = cnt * sizeof(float);
346 SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
349 return destructive_write_unlocked (data, cnt);
351 return nondestructive_write_unlocked (data, cnt);
356 SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
359 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
363 if (_info.channels != 1) {
364 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
370 int32_t frame_pos = _length;
372 if (write_float (data, frame_pos, cnt) != cnt) {
377 update_length (oldlen, cnt);
379 if (_build_peakfiles) {
380 compute_and_write_peaks (data, frame_pos, cnt, false, true);
383 _write_data_count = cnt;
389 SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
391 nframes_t old_file_pos;
394 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
398 if (_capture_start && _capture_end) {
400 /* start and end of capture both occur within the data we are writing,
401 so do both crossfades.
404 _capture_start = false;
405 _capture_end = false;
407 /* move to the correct location place */
408 file_pos = capture_start_frame - timeline_position;
411 nframes_t subcnt = cnt / 2;
412 nframes_t ofilepos = file_pos;
415 if (crossfade (data, subcnt, 1) != subcnt) {
420 Sample * tmpdata = data + subcnt;
423 subcnt = cnt - subcnt;
424 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
428 file_pos = ofilepos; // adjusted below
430 } else if (_capture_start) {
432 /* start of capture both occur within the data we are writing,
436 _capture_start = false;
437 _capture_end = false;
439 /* move to the correct location place */
440 file_pos = capture_start_frame - timeline_position;
442 if (crossfade (data, cnt, 1) != cnt) {
446 } else if (_capture_end) {
448 /* end of capture both occur within the data we are writing,
452 _capture_start = false;
453 _capture_end = false;
455 if (crossfade (data, cnt, 0) != cnt) {
461 /* in the middle of recording */
463 if (write_float (data, file_pos, cnt) != cnt) {
468 old_file_pos = file_pos;
469 update_length (file_pos, cnt);
471 if (_build_peakfiles) {
472 compute_and_write_peaks (data, file_pos, cnt, false, true);
481 SndFileSource::update_header (nframes_t when, struct tm& now, time_t tnow)
483 set_timeline_position (when);
485 if (_flags & Broadcast) {
486 if (setup_broadcast_info (when, now, tnow)) {
491 return flush_header ();
495 SndFileSource::flush_header ()
497 if (!writable() || (sf == 0)) {
498 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
501 return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
505 SndFileSource::setup_broadcast_info (nframes_t when, struct tm& now, time_t tnow)
508 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
512 if (!(_flags & Broadcast)) {
516 _broadcast_info->set_originator_ref ();
517 _broadcast_info->set_origination_time (&now);
519 /* now update header position taking header offset into account */
521 set_header_timeline_position ();
523 if (!_broadcast_info->write_to_file (sf)) {
524 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
525 _path, _broadcast_info->get_error())
527 _flags = Flag (_flags & ~Broadcast);
528 delete _broadcast_info;
536 SndFileSource::set_header_timeline_position ()
538 if (!(_flags & Broadcast)) {
542 _broadcast_info->set_time_reference (timeline_position);
544 if (!_broadcast_info->write_to_file (sf)) {
545 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
546 _path, _broadcast_info->get_error())
548 _flags = Flag (_flags & ~Broadcast);
549 delete _broadcast_info;
555 SndFileSource::write_float (Sample* data, nframes_t frame_pos, nframes_t cnt)
557 if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
559 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
560 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
564 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
572 SndFileSource::natural_position() const
574 return timeline_position;
578 SndFileSource::set_destructive (bool yn)
581 _flags = Flag (_flags | Destructive);
583 xfade_buf = new Sample[xfade_frames];
585 clear_capture_marks ();
586 timeline_position = header_position_offset;
588 _flags = Flag (_flags & ~Destructive);
589 timeline_position = 0;
590 /* leave xfade buf alone in case we need it again later */
597 SndFileSource::clear_capture_marks ()
599 _capture_start = false;
600 _capture_end = false;
604 SndFileSource::mark_capture_start (nframes_t pos)
607 if (pos < timeline_position) {
608 _capture_start = false;
610 _capture_start = true;
611 capture_start_frame = pos;
617 SndFileSource::mark_capture_end()
625 SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
627 nframes_t xfade = min (xfade_frames, cnt);
628 nframes_t nofade = cnt - xfade;
629 Sample* fade_data = 0;
630 nframes_t fade_position = 0; // in frames
635 fade_position = file_pos;
638 fade_position = file_pos + nofade;
639 fade_data = data + nofade;
642 if (fade_position > _length) {
644 /* read starts beyond end of data, just memset to zero */
648 } else if (fade_position + xfade > _length) {
650 /* read ends beyond end of data, read some, memset the rest */
652 file_cnt = _length - fade_position;
656 /* read is entirely within data */
663 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
664 if (retval >= 0 && errno == EAGAIN) {
665 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
666 * short or no data there */
667 memset (xfade_buf, 0, xfade * sizeof(Sample));
669 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
675 if (file_cnt != xfade) {
676 nframes_t delta = xfade - file_cnt;
677 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
680 if (nofade && !fade_in) {
681 if (write_float (data, file_pos, nofade) != nofade) {
682 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
687 if (xfade == xfade_frames) {
691 /* use the standard xfade curve */
695 /* fade new material in */
697 for (n = 0; n < xfade; ++n) {
698 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
704 /* fade new material out */
706 for (n = 0; n < xfade; ++n) {
707 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
711 } else if (xfade < xfade_frames) {
716 /* short xfade, compute custom curve */
718 compute_equal_power_fades (xfade, in, out);
720 for (nframes_t n = 0; n < xfade; ++n) {
721 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
726 /* long xfade length, has to be computed across several calls */
731 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
732 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
737 if (fade_in && nofade) {
738 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
739 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
748 SndFileSource::last_capture_start_frame () const
751 return capture_start_frame;
758 SndFileSource::handle_header_position_change ()
761 if ( _length != 0 ) {
762 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
763 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
764 } else if (writable()) {
765 timeline_position = header_position_offset;
766 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
772 SndFileSource::setup_standard_crossfades (nframes_t rate)
774 /* This static method is assumed to have been called by the Session
775 before any DFS's are created.
778 xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
780 delete [] out_coefficient;
781 delete [] in_coefficient;
783 out_coefficient = new gain_t[xfade_frames];
784 in_coefficient = new gain_t[xfade_frames];
786 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
790 SndFileSource::set_timeline_position (int64_t pos)
792 // destructive track timeline postion does not change
793 // except at instantion or when header_position_offset
794 // (session start) changes
796 if (!destructive()) {
797 AudioFileSource::set_timeline_position (pos);
802 SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
808 sf_info.format = 0; // libsndfile says to clear this before sf_open().
810 if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
812 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
816 info.samplerate = sf_info.samplerate;
817 info.channels = sf_info.channels;
818 info.length = sf_info.frames;
819 info.format_name = string_compose("Format: %1, %2",
820 sndfile_major_format(sf_info.format),
821 sndfile_minor_format(sf_info.format));
823 info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
831 SndFileSource::one_of_several_channels () const
833 return _info.channels > 1;