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>
60 #include <ardour/session.h>
65 using namespace ARDOUR;
68 gain_t* DestructiveFileSource::out_coefficient = 0;
69 gain_t* DestructiveFileSource::in_coefficient = 0;
70 nframes_t DestructiveFileSource::xfade_frames = 64;
72 DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate, Flag flags)
73 : SndFileSource (s, path, samp_format, hdr_format, rate, flags)
79 DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags)
80 : SndFileSource (s, path, flags)
85 DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node)
86 : SndFileSource (s, node)
92 DestructiveFileSource::init ()
94 xfade_buf = new Sample[xfade_frames];
96 _capture_start = false;
100 timeline_position = header_position_offset;
101 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
104 DestructiveFileSource::~DestructiveFileSource()
110 DestructiveFileSource::setup_standard_crossfades (nframes_t rate)
112 /* This static method is assumed to have been called by the Session
113 before any DFS's are created.
116 xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
118 if (out_coefficient) {
119 delete [] out_coefficient;
122 if (in_coefficient) {
123 delete [] in_coefficient;
126 out_coefficient = new gain_t[xfade_frames];
127 in_coefficient = new gain_t[xfade_frames];
129 compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
133 DestructiveFileSource::mark_capture_start (nframes_t pos)
135 if (pos < timeline_position) {
136 _capture_start = false;
138 _capture_start = true;
139 capture_start_frame = pos;
144 DestructiveFileSource::mark_capture_end()
150 DestructiveFileSource::clear_capture_marks ()
152 _capture_start = false;
153 _capture_end = false;
157 DestructiveFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
159 nframes_t xfade = min (xfade_frames, cnt);
160 nframes_t nofade = cnt - xfade;
161 Sample* fade_data = 0;
162 nframes_t fade_position = 0; // in frames
167 fade_position = file_pos;
170 fade_position = file_pos + nofade;
171 fade_data = data + nofade;
174 if (fade_position > _length) {
176 /* read starts beyond end of data, just memset to zero */
180 } else if (fade_position + xfade > _length) {
182 /* read ends beyond end of data, read some, memset the rest */
184 file_cnt = _length - fade_position;
188 /* read is entirely within data */
195 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
196 if (retval >= 0 && errno == EAGAIN) {
197 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
198 * short or no data there */
199 memset (xfade_buf, 0, xfade * sizeof(Sample));
201 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
207 if (file_cnt != xfade) {
208 nframes_t delta = xfade - file_cnt;
209 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
212 if (nofade && !fade_in) {
213 if (write_float (data, file_pos, nofade) != nofade) {
214 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
219 if (xfade == xfade_frames) {
223 /* use the standard xfade curve */
227 /* fade new material in */
229 for (n = 0; n < xfade; ++n) {
230 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
236 /* fade new material out */
238 for (n = 0; n < xfade; ++n) {
239 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
248 /* short xfade, compute custom curve */
250 compute_equal_power_fades (xfade, in, out);
252 for (nframes_t n = 0; n < xfade; ++n) {
253 xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
258 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
259 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
264 if (fade_in && nofade) {
265 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
266 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
275 DestructiveFileSource::write_unlocked (Sample* data, nframes_t cnt)
277 nframes_t old_file_pos;
283 if (_capture_start && _capture_end) {
285 /* start and end of capture both occur within the data we are writing,
286 so do both crossfades.
289 _capture_start = false;
290 _capture_end = false;
292 /* move to the correct location place */
293 file_pos = capture_start_frame;
296 nframes_t subcnt = cnt / 2;
297 nframes_t ofilepos = file_pos;
300 if (crossfade (data, subcnt, 1) != subcnt) {
305 Sample * tmpdata = data + subcnt;
308 subcnt = cnt - subcnt;
309 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
313 file_pos = ofilepos; // adjusted below
315 } else if (_capture_start) {
317 /* start of capture both occur within the data we are writing,
321 _capture_start = false;
322 _capture_end = false;
324 /* move to the correct location place */
325 file_pos = capture_start_frame - timeline_position;
327 if (crossfade (data, cnt, 1) != cnt) {
331 } else if (_capture_end) {
333 /* end of capture both occur within the data we are writing,
337 _capture_start = false;
338 _capture_end = false;
340 if (crossfade (data, cnt, 0) != cnt) {
346 /* 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 return AudioFileSource::read_peaks (peaks, npeaks, start, cnt, samples_per_unit);