+
+nframes_t
+SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
+{
+ nframes_t xfade = min (xfade_frames, cnt);
+ nframes_t nofade = cnt - xfade;
+ Sample* fade_data = 0;
+ nframes_t fade_position = 0; // in frames
+ ssize_t retval;
+ nframes_t file_cnt;
+
+ if (fade_in) {
+ fade_position = file_pos;
+ fade_data = data;
+ } else {
+ fade_position = file_pos + nofade;
+ fade_data = data + nofade;
+ }
+
+ if (fade_position > _length) {
+
+ /* read starts beyond end of data, just memset to zero */
+
+ file_cnt = 0;
+
+ } else if (fade_position + xfade > _length) {
+
+ /* read ends beyond end of data, read some, memset the rest */
+
+ file_cnt = _length - fade_position;
+
+ } else {
+
+ /* read is entirely within data */
+
+ file_cnt = xfade;
+ }
+
+ if (file_cnt) {
+
+ if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
+ if (retval >= 0 && errno == EAGAIN) {
+ /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
+ * short or no data there */
+ memset (xfade_buf, 0, xfade * sizeof(Sample));
+ } else {
+ error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
+ return 0;
+ }
+ }
+ }
+
+ if (file_cnt != xfade) {
+ nframes_t delta = xfade - file_cnt;
+ memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
+ }
+
+ if (nofade && !fade_in) {
+ if (write_float (data, file_pos, nofade) != nofade) {
+ error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ return 0;
+ }
+ }
+
+ if (xfade == xfade_frames) {
+
+ nframes_t n;
+
+ /* use the standard xfade curve */
+
+ if (fade_in) {
+
+ /* fade new material in */
+
+ for (n = 0; n < xfade; ++n) {
+ xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
+ }
+
+ } else {
+
+
+ /* fade new material out */
+
+ for (n = 0; n < xfade; ++n) {
+ xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
+ }
+ }
+
+ } else if (xfade < xfade_frames) {
+
+ gain_t in[xfade];
+ gain_t out[xfade];
+
+ /* short xfade, compute custom curve */
+
+ compute_equal_power_fades (xfade, in, out);
+
+ for (nframes_t n = 0; n < xfade; ++n) {
+ xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
+ }
+
+ } else if (xfade) {
+
+ /* long xfade length, has to be computed across several calls */
+
+ }
+
+ if (xfade) {
+ if (write_float (xfade_buf, fade_position, xfade) != xfade) {
+ error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ return 0;
+ }
+ }
+
+ if (fade_in && nofade) {
+ if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
+ error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ return 0;
+ }
+ }
+
+ return cnt;
+}
+
+sframes_t
+SndFileSource::last_capture_start_frame () const
+{
+ if (destructive()) {
+ return capture_start_frame;
+ } else {
+ return 0;
+ }
+}
+
+void
+SndFileSource::handle_header_position_change ()
+{
+ if (destructive()) {
+ if ( _length != 0 ) {
+ error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
+ //in the future, pop up a dialog here that allows user to regenerate file with new start offset
+ } else if (writable()) {
+ _timeline_position = header_position_offset;
+ set_header_timeline_position (); //this will get flushed if/when the file is recorded to
+ }
+ }
+}
+
+void
+SndFileSource::setup_standard_crossfades (Session const & s, nframes_t rate)
+{
+ /* This static method is assumed to have been called by the Session
+ before any DFS's are created.
+ */
+
+ xfade_frames = (nframes_t) floor ((s.config.get_destructive_xfade_msecs () / 1000.0) * rate);
+
+ delete [] out_coefficient;
+ delete [] in_coefficient;
+
+ out_coefficient = new gain_t[xfade_frames];
+ in_coefficient = new gain_t[xfade_frames];
+
+ compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
+}
+
+void
+SndFileSource::set_timeline_position (int64_t pos)
+{
+ // destructive track timeline postion does not change
+ // except at instantion or when header_position_offset
+ // (session start) changes
+
+ if (!destructive()) {
+ AudioFileSource::set_timeline_position (pos);
+ }
+}
+
+int
+SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
+{
+ SNDFILE *sf;
+ SF_INFO sf_info;
+ BroadcastInfo binfo;
+
+ sf_info.format = 0; // libsndfile says to clear this before sf_open().
+
+ if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
+ char errbuf[256];
+ error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ return false;
+ }
+
+ info.samplerate = sf_info.samplerate;
+ info.channels = sf_info.channels;
+ info.length = sf_info.frames;
+ info.format_name = string_compose("Format: %1, %2",
+ sndfile_major_format(sf_info.format),
+ sndfile_minor_format(sf_info.format));
+
+ info.timecode = binfo.load_from_file (sf) ? binfo.get_time_reference() : 0;
+
+ sf_close (sf);
+
+ return true;
+}
+
+bool
+SndFileSource::one_of_several_channels () const
+{
+ return _info.channels > 1;
+}
+