68eeded9b2092215bdd398d07819fa72c6e5d254
[ardour.git] / libs / ardour / destructive_filesource.cc
1 /*
2     Copyright (C) 2006 Paul Davis 
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
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.
26
27    features.h isn't available on osx and it compiles fine without it.
28 */
29
30 #ifdef HAVE_FEATURES_H
31 #include <features.h>
32 #endif
33
34 #if __GNUC__ >= 3
35 // #define _XOPEN_SOURCE 500
36 #include <unistd.h>
37 #else
38 #define __USE_UNIX98
39 #include <unistd.h>
40 #undef  __USE_UNIX98
41 #endif
42
43 // darwin supports 64 by default and doesn't provide wrapper functions.
44 #if defined (__APPLE__)
45 typedef off_t off64_t;
46 #define open64 open
47 #define close64 close
48 #define lseek64 lseek
49 #define pread64 pread
50 #define pwrite64 pwrite
51 #endif
52
53 #include <errno.h>
54 #include <cmath>
55 #include <fcntl.h>
56
57 #include <pbd/error.h>
58 #include <ardour/destructive_filesource.h>
59
60 #include "i18n.h"
61
62 using namespace std;
63 using namespace ARDOUR;
64 using namespace PBD;
65
66 gain_t* DestructiveFileSource::out_coefficient = 0;
67 gain_t* DestructiveFileSource::in_coefficient = 0;
68 jack_nframes_t DestructiveFileSource::xfade_frames = 64;
69
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)
72 {
73         xfade_buf = new Sample[xfade_frames];
74
75         _capture_start = false;
76         _capture_end = false;
77         file_pos = 0;
78
79         timeline_position = header_position_offset;
80         AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
81 }
82
83 DestructiveFileSource::DestructiveFileSource (const XMLNode& node)
84         : SndFileSource (node)
85 {
86         xfade_buf = new Sample[xfade_frames];
87
88         _capture_start = false;
89         _capture_end = false;
90         file_pos = 0;
91
92         timeline_position = header_position_offset;
93         AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
94 }
95
96 DestructiveFileSource::~DestructiveFileSource()
97 {
98         delete xfade_buf;
99 }
100
101 void
102 DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
103 {
104         /* This static method is assumed to have been called by the Session
105            before any DFS's are created.
106         */
107
108         xfade_frames = (jack_nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
109
110         if (out_coefficient) {
111                 delete [] out_coefficient;
112         }
113
114         if (in_coefficient) {
115                 delete [] in_coefficient;
116         }
117
118         out_coefficient = new gain_t[xfade_frames];
119         in_coefficient = new gain_t[xfade_frames];
120
121         for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
122
123                 /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
124
125                 in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
126                 out_coefficient[n] = 1.0 - in_coefficient[n];    /* 1 .. 0 */
127         }
128 }
129
130 void
131 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
132 {
133         if (pos < timeline_position) {
134                 _capture_start = false;
135         } else {
136                 _capture_start = true;
137                 capture_start_frame = pos;
138         }
139 }
140
141 void
142 DestructiveFileSource::mark_capture_end()
143 {
144         _capture_end = true;
145 }
146
147 void
148 DestructiveFileSource::clear_capture_marks ()
149 {
150         _capture_start = false;
151         _capture_end = false;
152 }       
153
154 jack_nframes_t
155 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in, char * workbuf)
156 {
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
161         ssize_t retval;
162         jack_nframes_t file_cnt;
163
164         if (fade_in) {
165                 fade_position = file_pos;
166                 fade_data = data;
167         } else {
168                 fade_position = file_pos + nofade;
169                 fade_data = data + nofade;
170         }
171
172         if (fade_position > _length) {
173                 
174                 /* read starts beyond end of data, just memset to zero */
175                 
176                 file_cnt = 0;
177
178         } else if (fade_position + xfade > _length) {
179                 
180                 /* read ends beyond end of data, read some, memset the rest */
181                 
182                 file_cnt = _length - fade_position;
183
184         } else {
185                 
186                 /* read is entirely within data */
187
188                 file_cnt = xfade;
189         }
190
191         if (file_cnt) {
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));
197                         } else {
198                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
199                                 return 0;
200                         }
201                 }
202         } 
203
204         if (file_cnt != xfade) {
205                 jack_nframes_t delta = xfade - file_cnt;
206                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
207         }
208         
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;
212                         return 0;
213                 }
214         }
215
216         if (xfade == xfade_frames) {
217
218                 jack_nframes_t n;
219
220                 /* use the standard xfade curve */
221                 
222                 if (fade_in) {
223
224                         /* fade new material in */
225                         
226                         for (n = 0; n < xfade; ++n) {
227                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
228                         }
229
230                 } else {
231
232
233                         /* fade new material out */
234                         
235                         for (n = 0; n < xfade; ++n) {
236                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
237                         }
238                 }
239
240         } else if (xfade) {
241
242                 /* short xfade, compute custom curve */
243
244                 /* XXX COMPUTE THE CURVE, DAMMIT! */
245
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]);
248                 }
249         }
250
251         if (xfade) {
252                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
253                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
254                         return 0;
255                 }
256         }
257         
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;
261                         return 0;
262                 }
263         }
264
265         return cnt;
266 }
267
268 jack_nframes_t
269 DestructiveFileSource::write_unlocked (Sample* data, jack_nframes_t cnt, char * workbuf)
270 {
271         jack_nframes_t old_file_pos;
272
273         if (!writable()) {
274                 return 0;
275         }
276
277         if (_capture_start && _capture_end) {
278
279                 /* start and end of capture both occur within the data we are writing,
280                    so do both crossfades.
281                 */
282
283                 _capture_start = false;
284                 _capture_end = false;
285                 
286                 /* move to the correct location place */
287                 file_pos = capture_start_frame;
288                 
289                 // split cnt in half
290                 jack_nframes_t subcnt = cnt / 2;
291                 jack_nframes_t ofilepos = file_pos;
292                 
293                 // fade in
294                 if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
295                         return 0;
296                 }
297                 
298                 file_pos += subcnt;
299                 Sample * tmpdata = data + subcnt;
300                 
301                 // fade out
302                 subcnt = cnt - subcnt;
303                 if (crossfade (tmpdata, subcnt, 0, workbuf) != subcnt) {
304                         return 0;
305                 }
306                 
307                 file_pos = ofilepos; // adjusted below
308
309         } else if (_capture_start) {
310
311                 /* start of capture both occur within the data we are writing,
312                    so do the fade in
313                 */
314
315                 _capture_start = false;
316                 _capture_end = false;
317                 
318                 /* move to the correct location place */
319                 file_pos = capture_start_frame;
320                 
321                 if (crossfade (data, cnt, 1, workbuf) != cnt) {
322                         return 0;
323                 }
324                 
325         } else if (_capture_end) {
326
327                 /* end of capture both occur within the data we are writing,
328                    so do the fade out
329                 */
330
331                 _capture_start = false;
332                 _capture_end = false;
333                 
334                 if (crossfade (data, cnt, 0, workbuf) != cnt) {
335                         return 0;
336                 }
337
338         } else {
339
340                 /* in the middle of recording */
341                 
342                 if (write_float (data, file_pos, cnt) != cnt) {
343                         return 0;
344                 }
345         }
346         
347         old_file_pos = file_pos;
348         update_length (file_pos, cnt);
349         file_pos += cnt;
350         
351         if (_build_peakfiles) {
352                 PeakBuildRecord *pbr = 0;
353                 
354                 if (pending_peak_builds.size()) {
355                         pbr = pending_peak_builds.back();
356                 }
357                 
358                 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
359                         
360                         /* the last PBR extended to the start of the current write,
361                            so just extend it again.
362                         */
363                         
364                         pbr->cnt += cnt;
365                 } else {
366                         pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
367                 }
368                 
369                 _peaks_built = false;
370         }
371
372         if (_build_peakfiles) {
373                 queue_for_peaks (*this);
374         }
375         
376         return cnt;
377 }
378
379 jack_nframes_t
380 DestructiveFileSource::last_capture_start_frame () const
381 {
382         return capture_start_frame;
383 }
384
385 XMLNode& 
386 DestructiveFileSource::get_state ()
387 {
388         XMLNode& node = AudioFileSource::get_state ();
389         node.add_property (X_("destructive"), "true");
390         return node;
391 }
392
393 void
394 DestructiveFileSource::handle_header_position_change ()
395 {
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
402         }
403 }
404
405 void
406 DestructiveFileSource::set_timeline_position (jack_nframes_t pos)
407 {
408         //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
409 }