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.
24 #include <sys/utsname.h>
27 #include <glibmm/miscutils.h>
29 #include <ardour/sndfilesource.h>
30 #include <ardour/sndfile_helpers.h>
31 #include <ardour/utils.h>
36 using namespace ARDOUR;
40 gain_t* SndFileSource::out_coefficient = 0;
41 gain_t* SndFileSource::in_coefficient = 0;
42 nframes_t SndFileSource::xfade_frames = 64;
43 const AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSource::Flag (AudioFileSource::Writable|
44 AudioFileSource::Removable|
45 AudioFileSource::RemovableIfEmpty|
46 AudioFileSource::CanRename);
48 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
49 : AudioFileSource (s, node)
54 throw failed_constructor ();
58 SndFileSource::SndFileSource (Session& s, ustring path, int chn, Flag flags)
59 /* files created this way are never writable or removable */
60 : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
67 throw failed_constructor ();
71 SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
72 : AudioFileSource (s, path, flags, sfmt, hf)
78 /* this constructor is used to construct new files, not open
87 _flags = Flag (_flags & ~Broadcast);
92 _flags = Flag (_flags & ~Broadcast);
97 _flags = Flag (_flags | Broadcast);
102 _flags = Flag (_flags & ~Broadcast);
107 _flags = Flag (_flags & ~Broadcast);
111 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
119 fmt |= SF_FORMAT_FLOAT;
123 fmt |= SF_FORMAT_PCM_24;
128 _info.samplerate = rate;
132 throw failed_constructor();
135 if (writable() && (_flags & Broadcast)) {
137 _broadcast_info = new SF_BROADCAST_INFO;
138 memset (_broadcast_info, 0, sizeof (*_broadcast_info));
140 snprintf (_broadcast_info->description, sizeof (_broadcast_info->description), "BWF %s", _name.c_str());
142 struct utsname utsinfo;
144 if (uname (&utsinfo)) {
145 error << string_compose(_("FileSource: cannot get host information for BWF header (%1)"), strerror(errno)) << endmsg;
149 snprintf (_broadcast_info->originator, sizeof (_broadcast_info->originator), "ardour:%s:%s:%s:%s:%s)",
150 Glib::get_real_name().c_str(),
156 _broadcast_info->version = 1;
157 _broadcast_info->time_reference_low = 0;
158 _broadcast_info->time_reference_high = 0;
160 /* XXX do something about this field */
162 snprintf (_broadcast_info->umid, sizeof (_broadcast_info->umid), "%s", "fnord");
164 /* coding history is added by libsndfile */
166 if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (_broadcast_info)) != SF_TRUE) {
168 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
169 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"), _path, errbuf) << endmsg;
170 _flags = Flag (_flags & ~Broadcast);
171 delete _broadcast_info;
179 SndFileSource::init ()
183 // lets try to keep the object initalizations here at the top
186 interleave_bufsize = 0;
193 _name = Glib::path_get_basename (_path);
196 /* although libsndfile says we don't need to set this,
197 valgrind and source code shows us that we do.
200 memset (&_info, 0, sizeof(_info));
202 _capture_start = false;
203 _capture_end = false;
207 xfade_buf = new Sample[xfade_frames];
208 timeline_position = header_position_offset;
211 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change));
215 SndFileSource::open ()
217 if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
219 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
220 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
221 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
225 if (_channel >= _info.channels) {
226 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
232 _length = _info.frames;
234 _broadcast_info = new SF_BROADCAST_INFO;
235 memset (_broadcast_info, 0, sizeof (*_broadcast_info));
237 bool timecode_info_exists;
239 set_timeline_position (get_timecode_info (sf, _broadcast_info, timecode_info_exists));
241 if (!timecode_info_exists) {
242 delete _broadcast_info;
244 _flags = Flag (_flags & ~Broadcast);
248 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
254 SndFileSource::~SndFileSource ()
256 GoingAway (); /* EMIT SIGNAL */
262 /* stupid libsndfile updated the headers on close,
263 so touch the peakfile if it exists and has data
264 to make sure its time is as new as the audio
271 if (interleave_buf) {
272 delete [] interleave_buf;
275 if (_broadcast_info) {
276 delete _broadcast_info;
285 SndFileSource::sample_rate () const
287 return _info.samplerate;
291 SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const
298 if (start > _length) {
300 /* read starts beyond end of data, just memset to zero */
304 } else if (start + cnt > _length) {
306 /* read ends beyond end of data, read some, memset the rest */
308 file_cnt = _length - start;
312 /* read is entirely within data */
319 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
321 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
322 error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
326 if (_info.channels == 1) {
327 nframes_t ret = sf_read_float (sf, dst, file_cnt);
328 _read_data_count = cnt * sizeof(float);
333 if (file_cnt != cnt) {
334 nframes_t delta = cnt - file_cnt;
335 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
338 real_cnt = cnt * _info.channels;
340 if (interleave_bufsize < real_cnt) {
342 if (interleave_buf) {
343 delete [] interleave_buf;
345 interleave_bufsize = real_cnt;
346 interleave_buf = new float[interleave_bufsize];
349 nread = sf_read_float (sf, interleave_buf, real_cnt);
350 ptr = interleave_buf + _channel;
351 nread /= _info.channels;
353 /* stride through the interleaved data */
355 for (int32_t n = 0; n < nread; ++n) {
357 ptr += _info.channels;
360 _read_data_count = cnt * sizeof(float);
366 SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
369 return destructive_write_unlocked (data, cnt);
371 return nondestructive_write_unlocked (data, cnt);
376 SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
379 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
383 if (_info.channels != 1) {
384 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
390 int32_t frame_pos = _length;
392 if (write_float (data, frame_pos, cnt) != cnt) {
397 update_length (oldlen, cnt);
399 if (_build_peakfiles) {
400 compute_and_write_peaks (data, frame_pos, cnt, false);
403 _write_data_count = cnt;
409 SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
411 nframes_t old_file_pos;
414 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
418 if (_capture_start && _capture_end) {
420 /* start and end of capture both occur within the data we are writing,
421 so do both crossfades.
424 _capture_start = false;
425 _capture_end = false;
427 /* move to the correct location place */
428 file_pos = capture_start_frame - timeline_position;
431 nframes_t subcnt = cnt / 2;
432 nframes_t ofilepos = file_pos;
435 if (crossfade (data, subcnt, 1) != subcnt) {
440 Sample * tmpdata = data + subcnt;
443 subcnt = cnt - subcnt;
444 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
448 file_pos = ofilepos; // adjusted below
450 } else if (_capture_start) {
452 /* start of capture both occur within the data we are writing,
456 _capture_start = false;
457 _capture_end = false;
459 /* move to the correct location place */
460 file_pos = capture_start_frame - timeline_position;
462 if (crossfade (data, cnt, 1) != cnt) {
466 } else if (_capture_end) {
468 /* end of capture both occur within the data we are writing,
472 _capture_start = false;
473 _capture_end = false;
475 if (crossfade (data, cnt, 0) != cnt) {
481 /* in the middle of recording */
483 if (write_float (data, file_pos, cnt) != cnt) {
488 old_file_pos = file_pos;
489 update_length (file_pos, cnt);
491 if (_build_peakfiles) {
492 compute_and_write_peaks (data, file_pos, cnt, false);
501 SndFileSource::update_header (nframes_t when, struct tm& now, time_t tnow)
503 set_timeline_position (when);
505 if (_flags & Broadcast) {
506 if (setup_broadcast_info (when, now, tnow)) {
511 return flush_header ();
515 SndFileSource::flush_header ()
517 if (!writable() || (sf == 0)) {
518 warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
521 return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
525 SndFileSource::setup_broadcast_info (nframes_t when, struct tm& now, time_t tnow)
528 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
532 if (!(_flags & Broadcast)) {
536 /* random code is 9 digits */
538 int random_code = random() % 999999999;
540 snprintf (_broadcast_info->originator_reference, sizeof (_broadcast_info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
541 Config->get_bwf_country_code().c_str(),
542 Config->get_bwf_organization_code().c_str(),
549 snprintf (_broadcast_info->origination_date, sizeof (_broadcast_info->origination_date), "%4d-%02d-%02d",
554 snprintf (_broadcast_info->origination_time, sizeof (_broadcast_info->origination_time), "%02d:%02d:%02d",
559 /* now update header position taking header offset into account */
561 set_header_timeline_position ();
563 if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
564 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
565 _flags = Flag (_flags & ~Broadcast);
566 delete _broadcast_info;
575 SndFileSource::set_header_timeline_position ()
577 if (!(_flags & Broadcast)) {
581 _broadcast_info->time_reference_high = (timeline_position >> 32);
582 _broadcast_info->time_reference_low = (timeline_position & 0xffffffff);
584 if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
585 error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
586 _flags = Flag (_flags & ~Broadcast);
587 delete _broadcast_info;
593 SndFileSource::write_float (Sample* data, nframes_t frame_pos, nframes_t cnt)
595 if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
597 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
598 error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
602 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
610 SndFileSource::natural_position() const
612 return timeline_position;
616 SndFileSource::set_destructive (bool yn)
619 _flags = Flag (_flags | Destructive);
621 xfade_buf = new Sample[xfade_frames];
623 clear_capture_marks ();
624 timeline_position = header_position_offset;
626 _flags = Flag (_flags & ~Destructive);
627 timeline_position = 0;
628 /* leave xfade buf alone in case we need it again later */
635 SndFileSource::clear_capture_marks ()
637 _capture_start = false;
638 _capture_end = false;
642 SndFileSource::mark_capture_start (nframes_t pos)
645 if (pos < timeline_position) {
646 _capture_start = false;
648 _capture_start = true;
649 capture_start_frame = pos;
655 SndFileSource::mark_capture_end()
663 SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
665 nframes_t xfade = min (xfade_frames, cnt);
666 nframes_t nofade = cnt - xfade;
667 Sample* fade_data = 0;
668 nframes_t fade_position = 0; // in frames
673 fade_position = file_pos;
676 fade_position = file_pos + nofade;
677 fade_data = data + nofade;
680 if (fade_position > _length) {
682 /* read starts beyond end of data, just memset to zero */
686 } else if (fade_position + xfade > _length) {
688 /* read ends beyond end of data, read some, memset the rest */
690 file_cnt = _length - fade_position;
694 /* read is entirely within data */
701 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
702 if (retval >= 0 && errno == EAGAIN) {
703 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
704 * short or no data there */
705 memset (xfade_buf, 0, xfade * sizeof(Sample));
707 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
713 if (file_cnt != xfade) {
714 nframes_t delta = xfade - file_cnt;
715 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
718 if (nofade && !fade_in) {
719 if (write_float (data, file_pos, nofade) != nofade) {
720 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
725 if (xfade == xfade_frames) {
729 /* use the standard xfade curve */
733 /* fade new material in */
735 for (n = 0; n < xfade; ++n) {
736 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
742 /* fade new material out */
744 for (n = 0; n < xfade; ++n) {
745 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
749 } else if (xfade < xfade_frames) {
754 /* short xfade, compute custom curve */
756 compute_equal_power_fades (xfade, in, out);
758 for (nframes_t n = 0; n < xfade; ++n) {
759 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
764 /* long xfade length, has to be computed across several calls */
769 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
770 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
775 if (fade_in && nofade) {
776 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
777 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
786 SndFileSource::last_capture_start_frame () const
789 return capture_start_frame;
796 SndFileSource::handle_header_position_change ()
799 if ( _length != 0 ) {
800 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
801 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
802 } else if (writable()) {
803 timeline_position = header_position_offset;
804 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
810 SndFileSource::setup_standard_crossfades (nframes_t rate)
812 /* This static method is assumed to have been called by the Session
813 before any DFS's are created.
816 xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
818 if (out_coefficient) {
819 delete [] out_coefficient;
822 if (in_coefficient) {
823 delete [] in_coefficient;
826 out_coefficient = new gain_t[xfade_frames];
827 in_coefficient = new gain_t[xfade_frames];
829 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
833 SndFileSource::set_timeline_position (int64_t pos)
835 // destructive track timeline postion does not change
836 // except at instantion or when header_position_offset
837 // (session start) changes
839 if (!destructive()) {
840 AudioFileSource::set_timeline_position (pos);
845 SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
849 SF_BROADCAST_INFO binfo;
850 bool timecode_exists;
852 sf_info.format = 0; // libsndfile says to clear this before sf_open().
854 if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
856 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
860 info.samplerate = sf_info.samplerate;
861 info.channels = sf_info.channels;
862 info.length = sf_info.frames;
863 info.format_name = string_compose("Format: %1, %2",
864 sndfile_major_format(sf_info.format),
865 sndfile_minor_format(sf_info.format));
867 memset (&binfo, 0, sizeof (binfo));
868 info.timecode = get_timecode_info (sf, &binfo, timecode_exists);
870 if (!timecode_exists) {
880 SndFileSource::get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists)
882 if (sf_command (sf, SFC_GET_BROADCAST_INFO, binfo, sizeof (*binfo)) != SF_TRUE) {
884 return (header_position_offset);
887 /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits
888 of the time reference.
892 int64_t ret = (uint32_t) binfo->time_reference_high;
894 ret |= (uint32_t) binfo->time_reference_low;
899 SndFileSource::one_of_several_channels () const
901 return _info.channels > 1;