Merged with trunk R992.
[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 #include <ardour/utils.h>
60
61 #include "i18n.h"
62
63 using namespace std;
64 using namespace ARDOUR;
65 using namespace PBD;
66
67 gain_t* DestructiveFileSource::out_coefficient = 0;
68 gain_t* DestructiveFileSource::in_coefficient = 0;
69 nframes_t DestructiveFileSource::xfade_frames = 64;
70
71 DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate, Flag flags)
72         : SndFileSource (s, path, samp_format, hdr_format, rate, flags)
73 {
74         init ();
75 }
76
77
78 DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags)
79         : SndFileSource (s, path, flags)
80 {
81         init ();
82 }
83
84 DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node)
85         : SndFileSource (s, node)
86 {
87         init ();
88 }
89
90 void
91 DestructiveFileSource::init ()
92 {
93         xfade_buf = new Sample[xfade_frames];
94
95         _capture_start = false;
96         _capture_end = false;
97         file_pos = 0;
98
99         timeline_position = header_position_offset;
100         AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
101 }
102
103 DestructiveFileSource::~DestructiveFileSource()
104 {
105         delete xfade_buf;
106 }
107
108 void
109 DestructiveFileSource::setup_standard_crossfades (nframes_t rate)
110 {
111         /* This static method is assumed to have been called by the Session
112            before any DFS's are created.
113         */
114
115         xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
116
117         if (out_coefficient) {
118                 delete [] out_coefficient;
119         }
120
121         if (in_coefficient) {
122                 delete [] in_coefficient;
123         }
124
125         out_coefficient = new gain_t[xfade_frames];
126         in_coefficient = new gain_t[xfade_frames];
127
128         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
129 }
130
131 void
132 DestructiveFileSource::mark_capture_start (nframes_t pos)
133 {
134         if (pos < timeline_position) {
135                 _capture_start = false;
136         } else {
137                 _capture_start = true;
138                 capture_start_frame = pos;
139         }
140 }
141
142 void
143 DestructiveFileSource::mark_capture_end()
144 {
145         _capture_end = true;
146 }
147
148 void
149 DestructiveFileSource::clear_capture_marks ()
150 {
151         _capture_start = false;
152         _capture_end = false;
153 }       
154
155 nframes_t
156 DestructiveFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
157 {
158         nframes_t xfade = min (xfade_frames, cnt);
159         nframes_t nofade = cnt - xfade;
160         Sample* fade_data = 0;
161         nframes_t fade_position = 0; // in frames
162         ssize_t retval;
163         nframes_t file_cnt;
164
165         if (fade_in) {
166                 fade_position = file_pos;
167                 fade_data = data;
168         } else {
169                 fade_position = file_pos + nofade;
170                 fade_data = data + nofade;
171         }
172
173         if (fade_position > _length) {
174                 
175                 /* read starts beyond end of data, just memset to zero */
176                 
177                 file_cnt = 0;
178
179         } else if (fade_position + xfade > _length) {
180                 
181                 /* read ends beyond end of data, read some, memset the rest */
182                 
183                 file_cnt = _length - fade_position;
184
185         } else {
186                 
187                 /* read is entirely within data */
188
189                 file_cnt = xfade;
190         }
191
192         if (file_cnt) {
193                 
194                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
195                         if (retval >= 0 && errno == EAGAIN) {
196                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
197                                  * short or no data there */
198                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
199                         } else {
200                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
201                                 return 0;
202                         }
203                 }
204         } 
205
206         if (file_cnt != xfade) {
207                 nframes_t delta = xfade - file_cnt;
208                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
209         }
210         
211         if (nofade && !fade_in) {
212                 if (write_float (data, file_pos, nofade) != nofade) {
213                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
214                         return 0;
215                 }
216         }
217
218         if (xfade == xfade_frames) {
219
220                 nframes_t n;
221
222                 /* use the standard xfade curve */
223                 
224                 if (fade_in) {
225
226                         /* fade new material in */
227                         
228                         for (n = 0; n < xfade; ++n) {
229                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
230                         }
231
232                 } else {
233
234
235                         /* fade new material out */
236                         
237                         for (n = 0; n < xfade; ++n) {
238                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
239                         }
240                 }
241
242         } else if (xfade) {
243
244                 gain_t in[xfade];
245                 gain_t out[xfade];
246
247                 /* short xfade, compute custom curve */
248
249                 compute_equal_power_fades (xfade, in, out);
250
251                 for (nframes_t n = 0; n < xfade; ++n) {
252                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);                
253                 }
254         }
255
256         if (xfade) {
257                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
258                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
259                         return 0;
260                 }
261         }
262         
263         if (fade_in && nofade) {
264                 if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
265                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
266                         return 0;
267                 }
268         }
269
270         return cnt;
271 }
272
273 nframes_t
274 DestructiveFileSource::write_unlocked (Sample* data, nframes_t cnt)
275 {
276         nframes_t old_file_pos;
277
278         if (!writable()) {
279                 return 0;
280         }
281
282         if (_capture_start && _capture_end) {
283
284                 /* start and end of capture both occur within the data we are writing,
285                    so do both crossfades.
286                 */
287
288                 _capture_start = false;
289                 _capture_end = false;
290                 
291                 /* move to the correct location place */
292                 file_pos = capture_start_frame;
293                 
294                 // split cnt in half
295                 nframes_t subcnt = cnt / 2;
296                 nframes_t ofilepos = file_pos;
297                 
298                 // fade in
299                 if (crossfade (data, subcnt, 1) != subcnt) {
300                         return 0;
301                 }
302                 
303                 file_pos += subcnt;
304                 Sample * tmpdata = data + subcnt;
305                 
306                 // fade out
307                 subcnt = cnt - subcnt;
308                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
309                         return 0;
310                 }
311                 
312                 file_pos = ofilepos; // adjusted below
313
314         } else if (_capture_start) {
315
316                 /* start of capture both occur within the data we are writing,
317                    so do the fade in
318                 */
319
320                 _capture_start = false;
321                 _capture_end = false;
322                 
323                 /* move to the correct location place */
324                 file_pos = capture_start_frame - timeline_position;
325
326                 if (crossfade (data, cnt, 1) != cnt) {
327                         return 0;
328                 }
329                 
330         } else if (_capture_end) {
331
332                 /* end of capture both occur within the data we are writing,
333                    so do the fade out
334                 */
335
336                 _capture_start = false;
337                 _capture_end = false;
338                 
339                 if (crossfade (data, cnt, 0) != cnt) {
340                         return 0;
341                 }
342
343         } else {
344
345                 /* in the middle of recording */
346                 
347
348                 if (write_float (data, file_pos, cnt) != cnt) {
349                         return 0;
350                 }
351         }
352         
353         old_file_pos = file_pos;
354         update_length (file_pos, cnt);
355         file_pos += cnt;
356         
357         if (_build_peakfiles) {
358                 PeakBuildRecord *pbr = 0;
359                 
360                 if (pending_peak_builds.size()) {
361                         pbr = pending_peak_builds.back();
362                 }
363                 
364                 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
365                         
366                         /* the last PBR extended to the start of the current write,
367                            so just extend it again.
368                         */
369                         
370                         pbr->cnt += cnt;
371                 } else {
372                         pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
373                 }
374                 
375                 _peaks_built = false;
376         }
377
378         if (_build_peakfiles) {
379                 queue_for_peaks (shared_from_this ());
380         }
381         
382         return cnt;
383 }
384
385 nframes_t
386 DestructiveFileSource::last_capture_start_frame () const
387 {
388         return capture_start_frame;
389 }
390
391 XMLNode& 
392 DestructiveFileSource::get_state ()
393 {
394         XMLNode& node = AudioFileSource::get_state ();
395         node.add_property (X_("destructive"), "true");
396         return node;
397 }
398
399 void
400 DestructiveFileSource::handle_header_position_change ()
401 {
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
408         }
409 }
410
411 void
412 DestructiveFileSource::set_timeline_position (nframes_t pos)
413 {
414         //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
415 }