remove all duplicated _id members from children of PBD::Stateful.
[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 jack_nframes_t DestructiveFileSource::xfade_frames = 64;
70
71 DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, jack_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 (jack_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 = (jack_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 (jack_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 jack_nframes_t
156 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in)
157 {
158         jack_nframes_t xfade = min (xfade_frames, cnt);
159         jack_nframes_t nofade = cnt - xfade;
160         Sample* fade_data = 0;
161         jack_nframes_t fade_position = 0; // in frames
162         ssize_t retval;
163         jack_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                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
194                         if (retval >= 0 && errno == EAGAIN) {
195                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
196                                  * short or no data there */
197                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
198                         } else {
199                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
200                                 return 0;
201                         }
202                 }
203         } 
204
205         if (file_cnt != xfade) {
206                 jack_nframes_t delta = xfade - file_cnt;
207                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
208         }
209         
210         if (nofade && !fade_in) {
211                 if (write_float (data, file_pos - timeline_position, nofade) != nofade) {
212                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
213                         return 0;
214                 }
215         }
216
217         if (xfade == xfade_frames) {
218
219                 jack_nframes_t n;
220
221                 /* use the standard xfade curve */
222                 
223                 if (fade_in) {
224
225                         /* fade new material in */
226                         
227                         for (n = 0; n < xfade; ++n) {
228                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
229                         }
230
231                 } else {
232
233
234                         /* fade new material out */
235                         
236                         for (n = 0; n < xfade; ++n) {
237                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
238                         }
239                 }
240
241         } else if (xfade) {
242
243                 gain_t in[xfade];
244                 gain_t out[xfade];
245
246                 /* short xfade, compute custom curve */
247
248                 compute_equal_power_fades (xfade, in, out);
249
250                 for (jack_nframes_t n = 0; n < xfade; ++n) {
251                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);                
252                 }
253         }
254
255         if (xfade) {
256                 if (write_float (xfade_buf, fade_position - timeline_position, xfade) != xfade) {
257                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
258                         return 0;
259                 }
260         }
261         
262         if (fade_in && nofade) {
263                 if (write_float (data + xfade, file_pos + xfade - timeline_position, nofade) != nofade) {
264                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
265                         return 0;
266                 }
267         }
268
269         return cnt;
270 }
271
272 jack_nframes_t
273 DestructiveFileSource::write_unlocked (Sample* data, jack_nframes_t cnt)
274 {
275         jack_nframes_t old_file_pos;
276
277         if (!writable()) {
278                 return 0;
279         }
280
281         if (_capture_start && _capture_end) {
282
283                 /* start and end of capture both occur within the data we are writing,
284                    so do both crossfades.
285                 */
286
287                 _capture_start = false;
288                 _capture_end = false;
289                 
290                 /* move to the correct location place */
291                 file_pos = capture_start_frame;
292                 
293                 // split cnt in half
294                 jack_nframes_t subcnt = cnt / 2;
295                 jack_nframes_t ofilepos = file_pos;
296                 
297                 // fade in
298                 if (crossfade (data, subcnt, 1) != subcnt) {
299                         return 0;
300                 }
301                 
302                 file_pos += subcnt;
303                 Sample * tmpdata = data + subcnt;
304                 
305                 // fade out
306                 subcnt = cnt - subcnt;
307                 if (crossfade (tmpdata, subcnt, 0) != subcnt) {
308                         return 0;
309                 }
310                 
311                 file_pos = ofilepos; // adjusted below
312
313         } else if (_capture_start) {
314
315                 /* start of capture both occur within the data we are writing,
316                    so do the fade in
317                 */
318
319                 _capture_start = false;
320                 _capture_end = false;
321                 
322                 /* move to the correct location place */
323                 file_pos = capture_start_frame;
324                 
325                 if (crossfade (data, cnt, 1) != cnt) {
326                         return 0;
327                 }
328                 
329         } else if (_capture_end) {
330
331                 /* end of capture both occur within the data we are writing,
332                    so do the fade out
333                 */
334
335                 _capture_start = false;
336                 _capture_end = false;
337                 
338                 if (crossfade (data, cnt, 0) != cnt) {
339                         return 0;
340                 }
341
342         } else {
343
344                 /* in the middle of recording */
345                 
346                 if (write_float (data, file_pos - timeline_position, cnt) != cnt) {
347                         return 0;
348                 }
349         }
350         
351         old_file_pos = file_pos;
352         update_length (file_pos, cnt);
353         file_pos += cnt;
354         
355         if (_build_peakfiles) {
356                 PeakBuildRecord *pbr = 0;
357                 
358                 if (pending_peak_builds.size()) {
359                         pbr = pending_peak_builds.back();
360                 }
361                 
362                 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
363                         
364                         /* the last PBR extended to the start of the current write,
365                            so just extend it again.
366                         */
367                         
368                         pbr->cnt += cnt;
369                 } else {
370                         pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
371                 }
372                 
373                 _peaks_built = false;
374         }
375
376         if (_build_peakfiles) {
377                 queue_for_peaks (this);
378         }
379         
380         return cnt;
381 }
382
383 jack_nframes_t
384 DestructiveFileSource::last_capture_start_frame () const
385 {
386         return capture_start_frame;
387 }
388
389 XMLNode& 
390 DestructiveFileSource::get_state ()
391 {
392         XMLNode& node = AudioFileSource::get_state ();
393         node.add_property (X_("destructive"), "true");
394         return node;
395 }
396
397 void
398 DestructiveFileSource::handle_header_position_change ()
399 {
400         if ( _length != 0 ) {
401                 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
402                 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
403         } else if (writable()) {
404                 timeline_position = header_position_offset;
405                 set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
406         }
407 }
408
409 void
410 DestructiveFileSource::set_timeline_position (jack_nframes_t pos)
411 {
412         //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
413 }