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 <ardour/destructive_filesource.h>
59 #include <ardour/utils.h>
64 using namespace ARDOUR;
67 gain_t* DestructiveFileSource::out_coefficient = 0;
68 gain_t* DestructiveFileSource::in_coefficient = 0;
69 jack_nframes_t DestructiveFileSource::xfade_frames = 64;
71 DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags)
72 : SndFileSource (s, path, samp_format, hdr_format, rate, flags)
78 DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags)
79 : SndFileSource (s, path, flags)
84 DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node)
85 : SndFileSource (s, node)
91 DestructiveFileSource::init ()
93 xfade_buf = new Sample[xfade_frames];
95 _capture_start = false;
99 timeline_position = header_position_offset;
100 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
103 DestructiveFileSource::~DestructiveFileSource()
109 DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
111 /* This static method is assumed to have been called by the Session
112 before any DFS's are created.
115 xfade_frames = (jack_nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
117 if (out_coefficient) {
118 delete [] out_coefficient;
121 if (in_coefficient) {
122 delete [] in_coefficient;
125 out_coefficient = new gain_t[xfade_frames];
126 in_coefficient = new gain_t[xfade_frames];
128 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
132 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
134 if (pos < timeline_position) {
135 _capture_start = false;
137 _capture_start = true;
138 capture_start_frame = pos;
143 DestructiveFileSource::mark_capture_end()
149 DestructiveFileSource::clear_capture_marks ()
151 _capture_start = false;
152 _capture_end = false;
156 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in)
158 jack_nframes_t xfade = min (xfade_frames, cnt);
159 jack_nframes_t nofade = cnt - xfade;
160 Sample* fade_data = 0;
161 jack_nframes_t fade_position = 0; // in frames
163 jack_nframes_t file_cnt;
166 fade_position = file_pos;
169 fade_position = file_pos + nofade;
170 fade_data = data + nofade;
173 if (fade_position > _length) {
175 /* read starts beyond end of data, just memset to zero */
179 } else if (fade_position + xfade > _length) {
181 /* read ends beyond end of data, read some, memset the rest */
183 file_cnt = _length - fade_position;
187 /* read is entirely within data */
193 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
194 if (retval >= 0 && errno == EAGAIN) {
195 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
196 * short or no data there */
197 memset (xfade_buf, 0, xfade * sizeof(Sample));
199 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
205 if (file_cnt != xfade) {
206 jack_nframes_t delta = xfade - file_cnt;
207 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
210 if (nofade && !fade_in) {
211 if (write_float (data, file_pos - timeline_position, nofade) != nofade) {
212 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
217 if (xfade == xfade_frames) {
221 /* use the standard xfade curve */
225 /* fade new material in */
227 for (n = 0; n < xfade; ++n) {
228 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
234 /* fade new material out */
236 for (n = 0; n < xfade; ++n) {
237 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
246 /* short xfade, compute custom curve */
248 compute_equal_power_fades (xfade, in, out);
250 for (jack_nframes_t n = 0; n < xfade; ++n) {
251 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
256 if (write_float (xfade_buf, fade_position - timeline_position, xfade) != xfade) {
257 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
262 if (fade_in && nofade) {
263 if (write_float (data + xfade, file_pos + xfade - timeline_position, nofade) != nofade) {
264 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
273 DestructiveFileSource::write_unlocked (Sample* data, jack_nframes_t cnt)
275 jack_nframes_t old_file_pos;
281 if (_capture_start && _capture_end) {
283 /* start and end of capture both occur within the data we are writing,
284 so do both crossfades.
287 _capture_start = false;
288 _capture_end = false;
290 /* move to the correct location place */
291 file_pos = capture_start_frame;
294 jack_nframes_t subcnt = cnt / 2;
295 jack_nframes_t ofilepos = file_pos;
298 if (crossfade (data, subcnt, 1) != subcnt) {
303 Sample * tmpdata = data + subcnt;
306 subcnt = cnt - subcnt;
307 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
311 file_pos = ofilepos; // adjusted below
313 } else if (_capture_start) {
315 /* start of capture both occur within the data we are writing,
319 _capture_start = false;
320 _capture_end = false;
322 /* move to the correct location place */
323 file_pos = capture_start_frame;
325 if (crossfade (data, cnt, 1) != cnt) {
329 } else if (_capture_end) {
331 /* end of capture both occur within the data we are writing,
335 _capture_start = false;
336 _capture_end = false;
338 if (crossfade (data, cnt, 0) != cnt) {
344 /* in the middle of recording */
346 if (write_float (data, file_pos - timeline_position, cnt) != cnt) {
351 old_file_pos = file_pos;
352 update_length (file_pos, cnt);
355 if (_build_peakfiles) {
356 PeakBuildRecord *pbr = 0;
358 if (pending_peak_builds.size()) {
359 pbr = pending_peak_builds.back();
362 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
364 /* the last PBR extended to the start of the current write,
365 so just extend it again.
370 pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
373 _peaks_built = false;
376 if (_build_peakfiles) {
377 queue_for_peaks (this);
384 DestructiveFileSource::last_capture_start_frame () const
386 return capture_start_frame;
390 DestructiveFileSource::get_state ()
392 XMLNode& node = AudioFileSource::get_state ();
393 node.add_property (X_("destructive"), "true");
398 DestructiveFileSource::handle_header_position_change ()
400 if ( _length != 0 ) {
401 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
402 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
403 } else if (writable()) {
404 timeline_position = header_position_offset;
405 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
410 DestructiveFileSource::set_timeline_position (jack_nframes_t pos)
412 //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes