Most PluginManager refactoring is out of the way. Time to begin on AudioUnit support...
[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         init ();
74 }
75
76
77 DestructiveFileSource::DestructiveFileSource (string path, Flag flags)
78         : SndFileSource (path, flags)
79 {
80         init ();
81 }
82
83 DestructiveFileSource::DestructiveFileSource (const XMLNode& node)
84         : SndFileSource (node)
85 {
86         init ();
87 }
88
89 void
90 DestructiveFileSource::init ()
91 {
92         xfade_buf = new Sample[xfade_frames];
93
94         _capture_start = false;
95         _capture_end = false;
96         file_pos = 0;
97
98         timeline_position = header_position_offset;
99         AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
100 }
101
102 DestructiveFileSource::~DestructiveFileSource()
103 {
104         delete xfade_buf;
105 }
106
107 void
108 DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
109 {
110         /* This static method is assumed to have been called by the Session
111            before any DFS's are created.
112         */
113
114         xfade_frames = (jack_nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
115
116         if (out_coefficient) {
117                 delete [] out_coefficient;
118         }
119
120         if (in_coefficient) {
121                 delete [] in_coefficient;
122         }
123
124         out_coefficient = new gain_t[xfade_frames];
125         in_coefficient = new gain_t[xfade_frames];
126
127         for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
128
129                 /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
130
131                 in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
132                 out_coefficient[n] = 1.0 - in_coefficient[n];    /* 1 .. 0 */
133         }
134 }
135
136 void
137 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
138 {
139         if (pos < timeline_position) {
140                 _capture_start = false;
141         } else {
142                 _capture_start = true;
143                 capture_start_frame = pos;
144         }
145 }
146
147 void
148 DestructiveFileSource::mark_capture_end()
149 {
150         _capture_end = true;
151 }
152
153 void
154 DestructiveFileSource::clear_capture_marks ()
155 {
156         _capture_start = false;
157         _capture_end = false;
158 }       
159
160 jack_nframes_t
161 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in, char * workbuf)
162 {
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
167         ssize_t retval;
168         jack_nframes_t file_cnt;
169
170         if (fade_in) {
171                 fade_position = file_pos;
172                 fade_data = data;
173         } else {
174                 fade_position = file_pos + nofade;
175                 fade_data = data + nofade;
176         }
177
178         if (fade_position > _length) {
179                 
180                 /* read starts beyond end of data, just memset to zero */
181                 
182                 file_cnt = 0;
183
184         } else if (fade_position + xfade > _length) {
185                 
186                 /* read ends beyond end of data, read some, memset the rest */
187                 
188                 file_cnt = _length - fade_position;
189
190         } else {
191                 
192                 /* read is entirely within data */
193
194                 file_cnt = xfade;
195         }
196
197         if (file_cnt) {
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));
203                         } else {
204                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
205                                 return 0;
206                         }
207                 }
208         } 
209
210         if (file_cnt != xfade) {
211                 jack_nframes_t delta = xfade - file_cnt;
212                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
213         }
214         
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;
218                         return 0;
219                 }
220         }
221
222         if (xfade == xfade_frames) {
223
224                 jack_nframes_t n;
225
226                 /* use the standard xfade curve */
227                 
228                 if (fade_in) {
229
230                         /* fade new material in */
231                         
232                         for (n = 0; n < xfade; ++n) {
233                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
234                         }
235
236                 } else {
237
238
239                         /* fade new material out */
240                         
241                         for (n = 0; n < xfade; ++n) {
242                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
243                         }
244                 }
245
246         } else if (xfade) {
247
248                 /* short xfade, compute custom curve */
249
250                 /* XXX COMPUTE THE CURVE, DAMMIT! */
251
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]);
254                 }
255         }
256
257         if (xfade) {
258                 if (write_float (xfade_buf, fade_position, xfade) != xfade) {
259                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
260                         return 0;
261                 }
262         }
263         
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;
267                         return 0;
268                 }
269         }
270
271         return cnt;
272 }
273
274 jack_nframes_t
275 DestructiveFileSource::write_unlocked (Sample* data, jack_nframes_t cnt, char * workbuf)
276 {
277         jack_nframes_t old_file_pos;
278
279         if (!writable()) {
280                 return 0;
281         }
282
283         if (_capture_start && _capture_end) {
284
285                 /* start and end of capture both occur within the data we are writing,
286                    so do both crossfades.
287                 */
288
289                 _capture_start = false;
290                 _capture_end = false;
291                 
292                 /* move to the correct location place */
293                 file_pos = capture_start_frame;
294                 
295                 // split cnt in half
296                 jack_nframes_t subcnt = cnt / 2;
297                 jack_nframes_t ofilepos = file_pos;
298                 
299                 // fade in
300                 if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
301                         return 0;
302                 }
303                 
304                 file_pos += subcnt;
305                 Sample * tmpdata = data + subcnt;
306                 
307                 // fade out
308                 subcnt = cnt - subcnt;
309                 if (crossfade (tmpdata, subcnt, 0, workbuf) != subcnt) {
310                         return 0;
311                 }
312                 
313                 file_pos = ofilepos; // adjusted below
314
315         } else if (_capture_start) {
316
317                 /* start of capture both occur within the data we are writing,
318                    so do the fade in
319                 */
320
321                 _capture_start = false;
322                 _capture_end = false;
323                 
324                 /* move to the correct location place */
325                 file_pos = capture_start_frame;
326                 
327                 if (crossfade (data, cnt, 1, workbuf) != cnt) {
328                         return 0;
329                 }
330                 
331         } else if (_capture_end) {
332
333                 /* end of capture both occur within the data we are writing,
334                    so do the fade out
335                 */
336
337                 _capture_start = false;
338                 _capture_end = false;
339                 
340                 if (crossfade (data, cnt, 0, workbuf) != cnt) {
341                         return 0;
342                 }
343
344         } else {
345
346                 /* in the middle of recording */
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 (*this);
380         }
381         
382         return cnt;
383 }
384
385 jack_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 (jack_nframes_t pos)
413 {
414         //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
415 }