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.
21 /* This is is very hacky way to get pread and pwrite declarations.
22 First, include <features.h> so that we can avoid its #undef __USE_UNIX98.
23 Then define __USE_UNIX98, include <unistd.h>, and then undef it
24 again. If #define _XOPEN_SOURCE actually worked, I'd use that, but
25 despite claims in the header that it does, it doesn't.
27 features.h isn't available on osx and it compiles fine without it.
30 #ifdef HAVE_FEATURES_H
35 // #define _XOPEN_SOURCE 500
43 // darwin supports 64 by default and doesn't provide wrapper functions.
44 #if defined (__APPLE__)
45 typedef off_t off64_t;
50 #define pwrite64 pwrite
57 #include <pbd/error.h>
58 #include <pbd/stacktrace.h>
59 #include <ardour/destructive_filesource.h>
60 #include <ardour/utils.h>
61 #include <ardour/session.h>
66 using namespace ARDOUR;
69 gain_t* DestructiveFileSource::out_coefficient = 0;
70 gain_t* DestructiveFileSource::in_coefficient = 0;
71 nframes_t DestructiveFileSource::xfade_frames = 64;
73 DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate, Flag flags)
74 : SndFileSource (s, path, samp_format, hdr_format, rate, flags)
80 DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags)
81 : SndFileSource (s, path, flags)
86 DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node)
87 : SndFileSource (s, node)
93 DestructiveFileSource::init ()
95 xfade_buf = new Sample[xfade_frames];
97 _capture_start = false;
101 timeline_position = header_position_offset;
102 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
105 DestructiveFileSource::~DestructiveFileSource()
111 DestructiveFileSource::setup_standard_crossfades (nframes_t rate)
113 /* This static method is assumed to have been called by the Session
114 before any DFS's are created.
117 xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
119 if (out_coefficient) {
120 delete [] out_coefficient;
123 if (in_coefficient) {
124 delete [] in_coefficient;
127 out_coefficient = new gain_t[xfade_frames];
128 in_coefficient = new gain_t[xfade_frames];
130 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
134 DestructiveFileSource::mark_capture_start (nframes_t pos)
136 if (pos < timeline_position) {
137 _capture_start = false;
139 _capture_start = true;
140 capture_start_frame = pos;
145 DestructiveFileSource::mark_capture_end()
151 DestructiveFileSource::clear_capture_marks ()
153 _capture_start = false;
154 _capture_end = false;
158 DestructiveFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
160 nframes_t xfade = min (xfade_frames, cnt);
161 nframes_t nofade = cnt - xfade;
162 Sample* fade_data = 0;
163 nframes_t fade_position = 0; // in frames
168 fade_position = file_pos;
171 fade_position = file_pos + nofade;
172 fade_data = data + nofade;
175 if (fade_position > _length) {
177 /* read starts beyond end of data, just memset to zero */
181 } else if (fade_position + xfade > _length) {
183 /* read ends beyond end of data, read some, memset the rest */
185 file_cnt = _length - fade_position;
189 /* read is entirely within data */
196 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
197 if (retval >= 0 && errno == EAGAIN) {
198 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
199 * short or no data there */
200 memset (xfade_buf, 0, xfade * sizeof(Sample));
202 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
208 if (file_cnt != xfade) {
209 nframes_t delta = xfade - file_cnt;
210 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
213 if (nofade && !fade_in) {
214 if (write_float (data, file_pos, nofade) != nofade) {
215 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
220 if (xfade == xfade_frames) {
224 /* use the standard xfade curve */
228 /* fade new material in */
230 for (n = 0; n < xfade; ++n) {
231 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
237 /* fade new material out */
239 for (n = 0; n < xfade; ++n) {
240 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
249 /* short xfade, compute custom curve */
251 compute_equal_power_fades (xfade, in, out);
253 for (nframes_t n = 0; n < xfade; ++n) {
254 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
259 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
260 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
265 if (fade_in && nofade) {
266 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
267 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
276 DestructiveFileSource::write_unlocked (Sample* data, nframes_t cnt)
278 nframes_t old_file_pos;
284 if (_capture_start && _capture_end) {
286 /* start and end of capture both occur within the data we are writing,
287 so do both crossfades.
290 _capture_start = false;
291 _capture_end = false;
293 /* move to the correct location place */
294 file_pos = capture_start_frame - timeline_position;
297 nframes_t subcnt = cnt / 2;
298 nframes_t ofilepos = file_pos;
301 if (crossfade (data, subcnt, 1) != subcnt) {
306 Sample * tmpdata = data + subcnt;
309 subcnt = cnt - subcnt;
310 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
314 file_pos = ofilepos; // adjusted below
316 } else if (_capture_start) {
318 /* start of capture both occur within the data we are writing,
322 _capture_start = false;
323 _capture_end = false;
325 /* move to the correct location place */
326 file_pos = capture_start_frame - timeline_position;
328 if (crossfade (data, cnt, 1) != cnt) {
332 } else if (_capture_end) {
334 /* end of capture both occur within the data we are writing,
338 _capture_start = false;
339 _capture_end = false;
341 if (crossfade (data, cnt, 0) != cnt) {
347 /* in the middle of recording */
349 if (write_float (data, file_pos, cnt) != cnt) {
354 old_file_pos = file_pos;
355 update_length (file_pos, cnt);
358 if (_build_peakfiles) {
359 PeakBuildRecord *pbr = 0;
361 if (pending_peak_builds.size()) {
362 pbr = pending_peak_builds.back();
365 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
367 /* the last PBR extended to the start of the current write,
368 so just extend it again.
373 pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
376 _peaks_built = false;
379 if (_build_peakfiles) {
380 queue_for_peaks (shared_from_this ());
387 DestructiveFileSource::last_capture_start_frame () const
389 return capture_start_frame;
393 DestructiveFileSource::get_state ()
395 XMLNode& node = AudioFileSource::get_state ();
396 node.add_property (X_("destructive"), "true");
401 DestructiveFileSource::handle_header_position_change ()
403 if ( _length != 0 ) {
404 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
405 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
406 } else if (writable()) {
407 timeline_position = header_position_offset;
408 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
413 DestructiveFileSource::set_timeline_position (nframes_t pos)
415 //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
419 DestructiveFileSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const
421 // cerr << _name << " read peaks at " << start << " for " << cnt << " tpos = " << timeline_position << endl;
422 return AudioFileSource::read_peaks (peaks, npeaks, start, cnt, samples_per_unit);