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)
73 xfade_buf = new Sample[xfade_frames];
75 _capture_start = false;
79 timeline_position = header_position_offset;
80 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
83 DestructiveFileSource::DestructiveFileSource (const XMLNode& node)
84 : SndFileSource (node)
86 xfade_buf = new Sample[xfade_frames];
88 _capture_start = false;
92 timeline_position = header_position_offset;
93 AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
96 DestructiveFileSource::~DestructiveFileSource()
102 DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
104 /* This static method is assumed to have been called by the Session
105 before any DFS's are created.
108 xfade_frames = (jack_nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
110 if (out_coefficient) {
111 delete [] out_coefficient;
114 if (in_coefficient) {
115 delete [] in_coefficient;
118 out_coefficient = new gain_t[xfade_frames];
119 in_coefficient = new gain_t[xfade_frames];
121 for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
123 /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
125 in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
126 out_coefficient[n] = 1.0 - in_coefficient[n]; /* 1 .. 0 */
131 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
133 if (pos < timeline_position) {
134 _capture_start = false;
136 _capture_start = true;
137 capture_start_frame = pos;
142 DestructiveFileSource::mark_capture_end()
148 DestructiveFileSource::clear_capture_marks ()
150 _capture_start = false;
151 _capture_end = false;
155 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in, char * workbuf)
157 jack_nframes_t xfade = min (xfade_frames, cnt);
158 jack_nframes_t nofade = cnt - xfade;
159 Sample* fade_data = 0;
160 jack_nframes_t fade_position = 0; // in frames
162 jack_nframes_t file_cnt;
165 fade_position = file_pos;
168 fade_position = file_pos + nofade;
169 fade_data = data + nofade;
172 if (fade_position > _length) {
174 /* read starts beyond end of data, just memset to zero */
178 } else if (fade_position + xfade > _length) {
180 /* read ends beyond end of data, read some, memset the rest */
182 file_cnt = _length - fade_position;
186 /* read is entirely within data */
192 if ((retval = write_float (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
193 if (retval >= 0 && errno == EAGAIN) {
194 /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
195 * short or no data there */
196 memset (xfade_buf, 0, xfade * sizeof(Sample));
198 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
204 if (file_cnt != xfade) {
205 jack_nframes_t delta = xfade - file_cnt;
206 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
209 if (nofade && !fade_in) {
210 if (write_float (data, file_pos, nofade) != nofade) {
211 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
216 if (xfade == xfade_frames) {
220 /* use the standard xfade curve */
224 /* fade new material in */
226 for (n = 0; n < xfade; ++n) {
227 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
233 /* fade new material out */
235 for (n = 0; n < xfade; ++n) {
236 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
242 /* short xfade, compute custom curve */
244 /* XXX COMPUTE THE CURVE, DAMMIT! */
246 for (jack_nframes_t n = 0; n < xfade; ++n) {
247 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
252 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
253 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
258 if (fade_in && nofade) {
259 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
260 error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
269 DestructiveFileSource::write_unlocked (Sample* data, jack_nframes_t cnt, char * workbuf)
271 jack_nframes_t old_file_pos;
277 if (_capture_start && _capture_end) {
279 /* start and end of capture both occur within the data we are writing,
280 so do both crossfades.
283 _capture_start = false;
284 _capture_end = false;
286 /* move to the correct location place */
287 file_pos = capture_start_frame;
290 jack_nframes_t subcnt = cnt / 2;
291 jack_nframes_t ofilepos = file_pos;
294 if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
299 Sample * tmpdata = data + subcnt;
302 subcnt = cnt - subcnt;
303 if (crossfade (tmpdata, subcnt, 0, workbuf) != subcnt) {
307 file_pos = ofilepos; // adjusted below
309 } else if (_capture_start) {
311 /* start of capture both occur within the data we are writing,
315 _capture_start = false;
316 _capture_end = false;
318 /* move to the correct location place */
319 file_pos = capture_start_frame;
321 if (crossfade (data, cnt, 1, workbuf) != cnt) {
325 } else if (_capture_end) {
327 /* end of capture both occur within the data we are writing,
331 _capture_start = false;
332 _capture_end = false;
334 if (crossfade (data, cnt, 0, workbuf) != cnt) {
340 /* in the middle of recording */
342 if (write_float (data, file_pos, cnt) != cnt) {
347 old_file_pos = file_pos;
348 update_length (file_pos, cnt);
351 if (_build_peakfiles) {
352 PeakBuildRecord *pbr = 0;
354 if (pending_peak_builds.size()) {
355 pbr = pending_peak_builds.back();
358 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
360 /* the last PBR extended to the start of the current write,
361 so just extend it again.
366 pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
369 _peaks_built = false;
372 if (_build_peakfiles) {
373 queue_for_peaks (*this);
380 DestructiveFileSource::last_capture_start_frame () const
382 return capture_start_frame;
386 DestructiveFileSource::get_state ()
388 XMLNode& node = AudioFileSource::get_state ();
389 node.add_property (X_("destructive"), "true");
394 DestructiveFileSource::handle_header_position_change ()
396 if ( _length != 0 ) {
397 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
398 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
399 } else if (writable()) {
400 timeline_position = header_position_offset;
401 set_header_timeline_position (); //this will get flushed if/when the file is recorded to
406 DestructiveFileSource::set_timeline_position (jack_nframes_t pos)
408 //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes