possible fix for race between diskstream buffer overwrite and channel setup
[ardour2.git] / libs / ardour / sndfilesource.cc
blob05de692016117e465ebd1b9388f853163744bd6b
1 /*
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.
20 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
24 #include <cstring>
25 #include <cerrno>
26 #include <climits>
27 #include <cstdarg>
29 #include <pwd.h>
30 #include <sys/utsname.h>
31 #include <sys/stat.h>
33 #include <glibmm/miscutils.h>
35 #include "ardour/sndfilesource.h"
36 #include "ardour/sndfile_helpers.h"
37 #include "ardour/utils.h"
38 #include "ardour/version.h"
39 #include "ardour/rc_configuration.h"
40 #include "ardour/session.h"
42 #include "i18n.h"
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using Glib::ustring;
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 (
53 Source::Writable |
54 Source::Removable |
55 Source::RemovableIfEmpty |
56 Source::CanRename );
58 SndFileSource::SndFileSource (Session& s, const XMLNode& node)
59 : Source(s, node)
60 , AudioFileSource (s, node)
62 init_sndfile ();
64 if (open()) {
65 throw failed_constructor ();
69 /** Files created this way are never writable or removable */
70 SndFileSource::SndFileSource (Session& s, const ustring& path, int chn, Flag flags)
71 : Source(s, DataType::AUDIO, path, flags)
72 , AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
74 _channel = chn;
76 init_sndfile ();
78 if (open()) {
79 throw failed_constructor ();
83 /** This constructor is used to construct new files, not open existing ones. */
84 SndFileSource::SndFileSource (Session& s, const ustring& path,
85 SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
86 : Source(s, DataType::AUDIO, path, flags)
87 , AudioFileSource (s, path, flags, sfmt, hf)
89 int fmt = 0;
91 init_sndfile ();
93 _file_is_new = true;
95 switch (hf) {
96 case CAF:
97 fmt = SF_FORMAT_CAF;
98 _flags = Flag (_flags & ~Broadcast);
99 break;
101 case AIFF:
102 fmt = SF_FORMAT_AIFF;
103 _flags = Flag (_flags & ~Broadcast);
104 break;
106 case BWF:
107 fmt = SF_FORMAT_WAV;
108 _flags = Flag (_flags | Broadcast);
109 break;
111 case WAVE:
112 fmt = SF_FORMAT_WAV;
113 _flags = Flag (_flags & ~Broadcast);
114 break;
116 case WAVE64:
117 fmt = SF_FORMAT_W64;
118 _flags = Flag (_flags & ~Broadcast);
119 break;
121 default:
122 fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
123 /*NOTREACHED*/
124 break;
128 switch (sfmt) {
129 case FormatFloat:
130 fmt |= SF_FORMAT_FLOAT;
131 break;
133 case FormatInt24:
134 fmt |= SF_FORMAT_PCM_24;
135 break;
137 case FormatInt16:
138 fmt |= SF_FORMAT_PCM_16;
139 break;
142 _info.channels = 1;
143 _info.samplerate = rate;
144 _info.format = fmt;
146 if (open()) {
147 throw failed_constructor();
150 if (writable() && (_flags & Broadcast)) {
152 if (!_broadcast_info) {
153 _broadcast_info = new BroadcastInfo;
156 _broadcast_info->set_from_session (s, header_position_offset);
157 _broadcast_info->set_description (string_compose ("BWF %1", _name));
159 if (!_broadcast_info->write_to_file (sf)) {
160 error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"),
161 _path, _broadcast_info->get_error())
162 << endmsg;
163 _flags = Flag (_flags & ~Broadcast);
164 delete _broadcast_info;
165 _broadcast_info = 0;
170 void
171 SndFileSource::init_sndfile ()
173 ustring file;
175 // lets try to keep the object initalizations here at the top
176 xfade_buf = 0;
177 sf = 0;
178 _broadcast_info = 0;
180 /* although libsndfile says we don't need to set this,
181 valgrind and source code shows us that we do.
184 memset (&_info, 0, sizeof(_info));
186 _capture_start = false;
187 _capture_end = false;
188 file_pos = 0;
190 if (destructive()) {
191 xfade_buf = new Sample[xfade_frames];
192 _timeline_position = header_position_offset;
195 AudioFileSource::HeaderPositionOffsetChanged.connect_same_thread (header_position_connection, boost::bind (&SndFileSource::handle_header_position_change, this));
199 SndFileSource::open ()
201 if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
202 char errbuf[256];
203 sf_error_str (0, errbuf, sizeof (errbuf) - 1);
204 #ifndef HAVE_COREAUDIO
205 /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
206 so we don't want to see this message.
209 error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
210 _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
211 #endif
212 return -1;
215 if (_channel >= _info.channels) {
216 #ifndef HAVE_COREAUDIO
217 error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
218 #endif
219 sf_close (sf);
220 sf = 0;
221 return -1;
224 _length = _info.frames;
226 if (!_broadcast_info) {
227 _broadcast_info = new BroadcastInfo;
230 bool bwf_info_exists = _broadcast_info->load_from_file (sf);
232 set_timeline_position (bwf_info_exists ? _broadcast_info->get_time_reference() : header_position_offset);
234 if (_length != 0 && !bwf_info_exists) {
235 delete _broadcast_info;
236 _broadcast_info = 0;
237 _flags = Flag (_flags & ~Broadcast);
240 if (writable()) {
241 sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
244 return 0;
247 SndFileSource::~SndFileSource ()
249 if (sf) {
250 sf_close (sf);
251 sf = 0;
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
256 file.
259 touch_peakfile ();
262 delete _broadcast_info;
263 delete [] xfade_buf;
266 float
267 SndFileSource::sample_rate () const
269 return _info.samplerate;
272 framecnt_t
273 SndFileSource::read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const
275 int32_t nread;
276 float *ptr;
277 uint32_t real_cnt;
278 framepos_t file_cnt;
280 if (start > _length) {
282 /* read starts beyond end of data, just memset to zero */
284 file_cnt = 0;
286 } else if (start + cnt > _length) {
288 /* read ends beyond end of data, read some, memset the rest */
290 file_cnt = _length - start;
292 } else {
294 /* read is entirely within data */
296 file_cnt = cnt;
299 if (file_cnt != cnt) {
300 framepos_t delta = cnt - file_cnt;
301 memset (dst+file_cnt, 0, sizeof (Sample) * delta);
304 if (file_cnt) {
306 if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
307 char errbuf[256];
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.val().substr (1), errbuf) << endmsg;
310 return 0;
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) {
317 char errbuf[256];
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.val().substr (1), errbuf, _length) << endl;
321 return ret;
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) {
336 dst[n] = *ptr;
337 ptr += _info.channels;
340 _read_data_count = cnt * sizeof(float);
342 return nread;
345 framecnt_t
346 SndFileSource::write_unlocked (Sample *data, framecnt_t cnt)
348 if (destructive()) {
349 return destructive_write_unlocked (data, cnt);
350 } else {
351 return nondestructive_write_unlocked (data, cnt);
355 framecnt_t
356 SndFileSource::nondestructive_write_unlocked (Sample *data, framecnt_t cnt)
358 if (!writable()) {
359 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
360 return 0;
363 if (_info.channels != 1) {
364 fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
365 /*NOTREACHED*/
366 return 0;
369 framecnt_t oldlen;
370 int32_t frame_pos = _length;
372 if (write_float (data, frame_pos, cnt) != cnt) {
373 return 0;
376 oldlen = _length;
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;
385 return cnt;
388 framecnt_t
389 SndFileSource::destructive_write_unlocked (Sample* data, framecnt_t cnt)
391 framepos_t old_file_pos;
393 if (!writable()) {
394 warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
395 return 0;
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;
410 // split cnt in half
411 nframes_t subcnt = cnt / 2;
412 nframes_t ofilepos = file_pos;
414 // fade in
415 if (crossfade (data, subcnt, 1) != subcnt) {
416 return 0;
419 file_pos += subcnt;
420 Sample * tmpdata = data + subcnt;
422 // fade out
423 subcnt = cnt - subcnt;
424 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
425 return 0;
428 file_pos = ofilepos; // adjusted below
430 } else if (_capture_start) {
432 /* start of capture both occur within the data we are writing,
433 so do the fade in
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) {
443 return 0;
446 } else if (_capture_end) {
448 /* end of capture both occur within the data we are writing,
449 so do the fade out
452 _capture_start = false;
453 _capture_end = false;
455 if (crossfade (data, cnt, 0) != cnt) {
456 return 0;
459 } else {
461 /* in the middle of recording */
463 if (write_float (data, file_pos, cnt) != cnt) {
464 return 0;
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);
475 file_pos += cnt;
477 return cnt;
481 SndFileSource::update_header (sframes_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)) {
487 return -1;
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;
499 return -1;
501 return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
505 SndFileSource::setup_broadcast_info (sframes_t /*when*/, struct tm& now, time_t /*tnow*/)
507 if (!writable()) {
508 warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
509 return -1;
512 if (!(_flags & Broadcast)) {
513 return 0;
516 _broadcast_info->set_originator_ref (_session);
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())
526 << endmsg;
527 _flags = Flag (_flags & ~Broadcast);
528 delete _broadcast_info;
529 _broadcast_info = 0;
532 return 0;
535 void
536 SndFileSource::set_header_timeline_position ()
538 if (!(_flags & Broadcast)) {
539 return;
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())
547 << endmsg;
548 _flags = Flag (_flags & ~Broadcast);
549 delete _broadcast_info;
550 _broadcast_info = 0;
554 framecnt_t
555 SndFileSource::write_float (Sample* data, framepos_t frame_pos, framecnt_t cnt)
557 if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
558 char errbuf[256];
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;
561 return 0;
564 if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
565 return 0;
568 return cnt;
571 framepos_t
572 SndFileSource::natural_position() const
574 return _timeline_position;
577 bool
578 SndFileSource::set_destructive (bool yn)
580 if (yn) {
581 _flags = Flag (_flags | Destructive);
582 if (!xfade_buf) {
583 xfade_buf = new Sample[xfade_frames];
585 clear_capture_marks ();
586 _timeline_position = header_position_offset;
587 } else {
588 _flags = Flag (_flags & ~Destructive);
589 _timeline_position = 0;
590 /* leave xfade buf alone in case we need it again later */
593 return true;
596 void
597 SndFileSource::clear_capture_marks ()
599 _capture_start = false;
600 _capture_end = false;
603 void
604 SndFileSource::mark_capture_start (sframes_t pos)
606 if (destructive()) {
607 if (pos < _timeline_position) {
608 _capture_start = false;
609 } else {
610 _capture_start = true;
611 capture_start_frame = pos;
616 void
617 SndFileSource::mark_capture_end()
619 if (destructive()) {
620 _capture_end = true;
624 framecnt_t
625 SndFileSource::crossfade (Sample* data, framecnt_t cnt, int fade_in)
627 framecnt_t xfade = min (xfade_frames, cnt);
628 framecnt_t nofade = cnt - xfade;
629 Sample* fade_data = 0;
630 framepos_t fade_position = 0; // in frames
631 ssize_t retval;
632 framecnt_t file_cnt;
634 if (fade_in) {
635 fade_position = file_pos;
636 fade_data = data;
637 } else {
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 */
646 file_cnt = 0;
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;
654 } else {
656 /* read is entirely within data */
658 file_cnt = xfade;
661 if (file_cnt) {
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));
668 } else {
669 error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
670 return 0;
675 if (file_cnt != xfade) {
676 framecnt_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;
683 return 0;
687 if (xfade == xfade_frames) {
689 framecnt_t n;
691 /* use the standard xfade curve */
693 if (fade_in) {
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]);
701 } else {
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) {
713 gain_t in[xfade];
714 gain_t out[xfade];
716 /* short xfade, compute custom curve */
718 compute_equal_power_fades (xfade, in, out);
720 for (framecnt_t n = 0; n < xfade; ++n) {
721 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
724 } else if (xfade) {
726 /* long xfade length, has to be computed across several calls */
730 if (xfade) {
731 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
732 error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
733 return 0;
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;
740 return 0;
744 return cnt;
747 sframes_t
748 SndFileSource::last_capture_start_frame () const
750 if (destructive()) {
751 return capture_start_frame;
752 } else {
753 return 0;
757 void
758 SndFileSource::handle_header_position_change ()
760 if (destructive()) {
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
771 void
772 SndFileSource::setup_standard_crossfades (Session const & s, 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 = (framecnt_t) floor ((s.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);
789 void
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)
804 SNDFILE *sf;
805 SF_INFO sf_info;
806 BroadcastInfo binfo;
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) {
811 char errbuf[256];
812 error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
813 return false;
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;
825 sf_close (sf);
827 return true;
830 bool
831 SndFileSource::one_of_several_channels () const
833 return _info.channels > 1;