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>
63 using namespace ARDOUR;
66 gain_t* DestructiveFileSource::out_coefficient = 0;
67 gain_t* DestructiveFileSource::in_coefficient = 0;
68 jack_nframes_t DestructiveFileSource::xfade_frames = 64;
70 DestructiveFileSource::DestructiveFileSource (string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_nframes_t rate, Flag flags)
71 : SndFileSource (path, samp_format, hdr_format, rate, flags)
77 DestructiveFileSource::DestructiveFileSource (string path, Flag flags)
78 : SndFileSource (path, flags)
83 DestructiveFileSource::DestructiveFileSource (const XMLNode& node)
84 : SndFileSource (node)
90 DestructiveFileSource::init ()
92 xfade_buf = new Sample[xfade_frames];
94 _capture_start = false;
98 timeline_position = header_position_offset;
99 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
102 DestructiveFileSource::~DestructiveFileSource()
108 DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
110 /* This static method is assumed to have been called by the Session
111 before any DFS's are created.
114 xfade_frames = (jack_nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
116 if (out_coefficient) {
117 delete [] out_coefficient;
120 if (in_coefficient) {
121 delete [] in_coefficient;
124 out_coefficient = new gain_t[xfade_frames];
125 in_coefficient = new gain_t[xfade_frames];
127 for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
129 /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
131 in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
132 out_coefficient[n] = 1.0 - in_coefficient[n]; /* 1 .. 0 */
137 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
139 if (pos < timeline_position) {
140 _capture_start = false;
142 _capture_start = true;
143 capture_start_frame = pos;
148 DestructiveFileSource::mark_capture_end()
154 DestructiveFileSource::clear_capture_marks ()
156 _capture_start = false;
157 _capture_end = false;
161 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in, char * workbuf)
163 jack_nframes_t xfade = min (xfade_frames, cnt);
164 jack_nframes_t nofade = cnt - xfade;
165 Sample* fade_data = 0;
166 jack_nframes_t fade_position = 0; // in frames
168 jack_nframes_t file_cnt;
171 fade_position = file_pos;
174 fade_position = file_pos + nofade;
175 fade_data = data + nofade;
178 if (fade_position > _length) {
180 /* read starts beyond end of data, just memset to zero */
184 } else if (fade_position + xfade > _length) {
186 /* read ends beyond end of data, read some, memset the rest */
188 file_cnt = _length - fade_position;
192 /* read is entirely within data */
198 if ((retval = write_float (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
199 if (retval >= 0 && errno == EAGAIN) {
200 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
201 * short or no data there */
202 memset (xfade_buf, 0, xfade * sizeof(Sample));
204 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
210 if (file_cnt != xfade) {
211 jack_nframes_t delta = xfade - file_cnt;
212 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
215 if (nofade && !fade_in) {
216 if (write_float (data, file_pos, nofade) != nofade) {
217 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
222 if (xfade == xfade_frames) {
226 /* use the standard xfade curve */
230 /* fade new material in */
232 for (n = 0; n < xfade; ++n) {
233 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
239 /* fade new material out */
241 for (n = 0; n < xfade; ++n) {
242 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
248 /* short xfade, compute custom curve */
250 /* XXX COMPUTE THE CURVE, DAMMIT! */
252 for (jack_nframes_t n = 0; n < xfade; ++n) {
253 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[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, jack_nframes_t cnt, char * workbuf)
277 jack_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 jack_nframes_t subcnt = cnt / 2;
297 jack_nframes_t ofilepos = file_pos;
300 if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
305 Sample * tmpdata = data + subcnt;
308 subcnt = cnt - subcnt;
309 if (crossfade (tmpdata, subcnt, 0, workbuf) != 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;
327 if (crossfade (data, cnt, 1, workbuf) != 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, workbuf) != cnt) {
346 /* in the middle of recording */
348 if (write_float (data, file_pos, cnt) != cnt) {
353 old_file_pos = file_pos;
354 update_length (file_pos, cnt);
357 if (_build_peakfiles) {
358 PeakBuildRecord *pbr = 0;
360 if (pending_peak_builds.size()) {
361 pbr = pending_peak_builds.back();
364 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
366 /* the last PBR extended to the start of the current write,
367 so just extend it again.
372 pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
375 _peaks_built = false;
378 if (_build_peakfiles) {
379 queue_for_peaks (*this);
386 DestructiveFileSource::last_capture_start_frame () const
388 return capture_start_frame;
392 DestructiveFileSource::get_state ()
394 XMLNode& node = AudioFileSource::get_state ();
395 node.add_property (X_("destructive"), "true");
400 DestructiveFileSource::handle_header_position_change ()
402 if ( _length != 0 ) {
403 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
404 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
405 } else if (writable()) {
406 timeline_position = header_position_offset;
407 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
412 DestructiveFileSource::set_timeline_position (jack_nframes_t pos)
414 //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes