#include <pbd/error.h>
#include <ardour/destructive_filesource.h>
+#include <ardour/utils.h>
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
+using namespace PBD;
gain_t* DestructiveFileSource::out_coefficient = 0;
gain_t* DestructiveFileSource::in_coefficient = 0;
jack_nframes_t DestructiveFileSource::xfade_frames = 64;
-DestructiveFileSource::DestructiveFileSource (string path, jack_nframes_t rate, bool repair_first)
- : FileSource (path, rate, repair_first)
+DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags)
+ : SndFileSource (s, path, samp_format, hdr_format, rate, flags)
{
- if (out_coefficient == 0) {
- setup_standard_crossfades (rate);
- }
+ init ();
+}
- xfade_buf = new Sample[xfade_frames];
- _capture_start = false;
- _capture_end = false;
+DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags)
+ : SndFileSource (s, path, flags)
+{
+ init ();
}
-DestructiveFileSource::DestructiveFileSource (const XMLNode& node, jack_nframes_t rate)
- : FileSource (node, rate)
+DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node)
+ : SndFileSource (s, node)
{
- if (out_coefficient == 0) {
- setup_standard_crossfades (rate);
- }
+ init ();
+}
+void
+DestructiveFileSource::init ()
+{
xfade_buf = new Sample[xfade_frames];
_capture_start = false;
_capture_end = false;
+ file_pos = 0;
+
+ timeline_position = header_position_offset;
+ AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
}
DestructiveFileSource::~DestructiveFileSource()
void
DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
{
- xfade_frames = (jack_nframes_t) floor ((/*Config->get_destructive_crossfade_msecs()*/ 64 / 1000.0) * rate);
-
- out_coefficient = new gain_t[xfade_frames];
- in_coefficient = new gain_t[xfade_frames];
+ /* This static method is assumed to have been called by the Session
+ before any DFS's are created.
+ */
- for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
+ xfade_frames = (jack_nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
- /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
+ if (out_coefficient) {
+ delete [] out_coefficient;
+ }
- in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
- out_coefficient[n] = 1.0 - in_coefficient[n]; /* 1 .. 0 */
+ if (in_coefficient) {
+ delete [] in_coefficient;
}
-}
-int
-DestructiveFileSource::seek (jack_nframes_t frame)
-{
-// file_pos = data_offset + (sizeof (Sample) * frame);
- cerr << _name << " Seek to " << frame << " = " << data_offset + (sizeof (Sample) * frame) << endl;
- return 0;
+ 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
DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
{
- _capture_start = true;
- capture_start_frame = pos;
+ if (pos < timeline_position) {
+ _capture_start = false;
+ } else {
+ _capture_start = true;
+ capture_start_frame = pos;
+ }
}
void
DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in)
{
jack_nframes_t xfade = min (xfade_frames, cnt);
- jack_nframes_t xfade_bytes = xfade * sizeof (Sample);
jack_nframes_t nofade = cnt - xfade;
- jack_nframes_t nofade_bytes = nofade * sizeof (Sample);
Sample* fade_data = 0;
- off_t fade_position = 0;
+ jack_nframes_t fade_position = 0; // in frames
+ ssize_t retval;
+ jack_nframes_t file_cnt;
if (fade_in) {
fade_position = file_pos;
fade_data = data;
} else {
- fade_position = file_pos + nofade_bytes;
+ fade_position = file_pos + nofade;
fade_data = data + nofade;
}
- if (::pread64 (fd, (char *) xfade_buf, xfade_bytes, fade_position) != (off64_t) xfade_bytes) {
- if (errno == EAGAIN) {
- /* no data there, so no xfade */
+ if (fade_position > _length) {
+
+ /* read starts beyond end of data, just memset to zero */
+
+ file_cnt = 0;
- xfade = 0;
- xfade_bytes = 0;
- nofade = cnt;
- nofade_bytes = nofade * sizeof (Sample);
+ } else if (fade_position + xfade > _length) {
+
+ /* read ends beyond end of data, read some, memset the rest */
+
+ file_cnt = _length - fade_position;
- } else {
- error << string_compose(_("FileSource: \"%1\" bad read (%2: %3)"), _path, errno, strerror (errno)) << endmsg;
- return 0;
- }
+ } 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(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
+ return 0;
+ }
+ }
+ }
+
+ if (file_cnt != xfade) {
+ jack_nframes_t delta = xfade - file_cnt;
+ memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
+ }
+
if (nofade && !fade_in) {
- cerr << "write " << nofade_bytes << " of prefade OUT data to " << file_pos << " .. " << file_pos + nofade_bytes << endl;
- if (::pwrite64 (fd, (char *) data, nofade_bytes, file_pos) != (off64_t) nofade_bytes) {
- error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ if (write_float (data, file_pos - timeline_position, nofade) != nofade) {
+ error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
return 0;
}
}
} else if (xfade) {
+ gain_t in[xfade];
+ gain_t out[xfade];
+
/* short xfade, compute custom curve */
- /* XXX COMPUTE THE CURVE, DAMMIT! */
+ compute_equal_power_fades (xfade, in, out);
for (jack_nframes_t n = 0; n < xfade; ++n) {
- xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
+ xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
}
}
if (xfade) {
- cerr << "write " << xfade_bytes << " of xfade data to " << fade_position << " .. " << fade_position + xfade_bytes << endl;
- if (::pwrite64 (fd, (char *) xfade_buf, xfade_bytes, fade_position) != (off64_t) xfade_bytes) {
- error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ if (write_float (xfade_buf, fade_position - timeline_position, xfade) != xfade) {
+ error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
return 0;
}
}
if (fade_in && nofade) {
- cerr << "write " << nofade_bytes << " of postfade IN data to " << file_pos + xfade_bytes << " .. "
- << file_pos + xfade_bytes + nofade_bytes << endl;
- if (::pwrite64 (fd, (char *) (data + xfade), nofade_bytes, file_pos + xfade_bytes) != (off64_t) nofade_bytes) {
- error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ if (write_float (data + xfade, file_pos + xfade - timeline_position, nofade) != nofade) {
+ error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
return 0;
}
}
}
jack_nframes_t
-DestructiveFileSource::write (Sample* data, jack_nframes_t cnt)
+DestructiveFileSource::write_unlocked (Sample* data, jack_nframes_t cnt)
{
- cerr << _name << ": write " << cnt << " to " << file_pos << " start ? " << _capture_start << " end ? " << _capture_end << endl;
+ jack_nframes_t old_file_pos;
- {
- LockMonitor lm (_lock, __LINE__, __FILE__);
-
- int32_t byte_cnt = cnt * sizeof (Sample);
- jack_nframes_t oldlen;
+ if (!writable()) {
+ return 0;
+ }
- if (_capture_start) {
- _capture_start = false;
- _capture_end = false;
+ if (_capture_start && _capture_end) {
- /* move to the correct location place */
- file_pos = data_offset + (capture_start_frame * sizeof (Sample));
+ /* start and end of capture both occur within the data we are writing,
+ so do both crossfades.
+ */
- cerr << "First byte of capture will be at " << file_pos << endl;
-
- if (crossfade (data, cnt, 1) != cnt) {
- return 0;
- }
+ _capture_start = false;
+ _capture_end = false;
+
+ /* move to the correct location place */
+ file_pos = capture_start_frame;
+
+ // split cnt in half
+ jack_nframes_t subcnt = cnt / 2;
+ jack_nframes_t ofilepos = file_pos;
+
+ // fade in
+ if (crossfade (data, subcnt, 1) != subcnt) {
+ return 0;
+ }
+
+ file_pos += subcnt;
+ Sample * tmpdata = data + subcnt;
+
+ // fade out
+ subcnt = cnt - subcnt;
+ if (crossfade (tmpdata, subcnt, 0) != subcnt) {
+ return 0;
+ }
+
+ file_pos = ofilepos; // adjusted below
- } else if (_capture_end) {
- _capture_start = false;
- _capture_end = false;
+ } else if (_capture_start) {
- if (crossfade (data, cnt, 0) != cnt) {
- return 0;
- }
- } else {
- if (::pwrite64 (fd, (char *) data, byte_cnt, file_pos) != (off64_t) byte_cnt) {
- error << string_compose(_("FileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
- return 0;
- }
+ /* start of capture both occur within the data we are writing,
+ so do the fade in
+ */
+
+ _capture_start = false;
+ _capture_end = false;
+
+ /* move to the correct location place */
+ file_pos = capture_start_frame;
+
+ if (crossfade (data, cnt, 1) != cnt) {
+ return 0;
}
+
+ } else if (_capture_end) {
+
+ /* end of capture both occur within the data we are writing,
+ so do the fade out
+ */
- oldlen = _length;
- if (file_pos + cnt > _length) {
- _length += cnt;
+ _capture_start = false;
+ _capture_end = false;
+
+ if (crossfade (data, cnt, 0) != cnt) {
+ return 0;
}
- _write_data_count = byte_cnt;
- file_pos += byte_cnt;
- cerr << "at end of write, file_pos = " << file_pos << endl;
+ } else {
- if (_build_peakfiles) {
- PeakBuildRecord *pbr = 0;
-
- if (pending_peak_builds.size()) {
- pbr = pending_peak_builds.back();
- }
+ /* in the middle of recording */
+
+ if (write_float (data, file_pos - timeline_position, cnt) != cnt) {
+ return 0;
+ }
+ }
+
+ old_file_pos = file_pos;
+ update_length (file_pos, cnt);
+ file_pos += cnt;
+
+ if (_build_peakfiles) {
+ PeakBuildRecord *pbr = 0;
+
+ if (pending_peak_builds.size()) {
+ pbr = pending_peak_builds.back();
+ }
+
+ if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
- if (pbr && pbr->frame + pbr->cnt == oldlen) {
-
- /* the last PBR extended to the start of the current write,
- so just extend it again.
- */
-
- pbr->cnt += cnt;
- } else {
- pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt));
- }
+ /* the last PBR extended to the start of the current write,
+ so just extend it again.
+ */
- _peaks_built = false;
+ pbr->cnt += cnt;
+ } else {
+ pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
}
-
+
+ _peaks_built = false;
}
-
if (_build_peakfiles) {
- queue_for_peaks (*this);
+ queue_for_peaks (this);
}
-
+
return cnt;
}
{
return capture_start_frame;
}
+
+XMLNode&
+DestructiveFileSource::get_state ()
+{
+ XMLNode& node = AudioFileSource::get_state ();
+ node.add_property (X_("destructive"), "true");
+ return node;
+}
+
+void
+DestructiveFileSource::handle_header_position_change ()
+{
+ 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
+DestructiveFileSource::set_timeline_position (jack_nframes_t pos)
+{
+ //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
+}