9081fc9b381105feb0f2b0ef674063e96a4164a8
[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
65 gain_t* DestructiveFileSource::out_coefficient = 0;
66 gain_t* DestructiveFileSource::in_coefficient = 0;
67 jack_nframes_t DestructiveFileSource::xfade_frames = 64;
68
69 DestructiveFileSource::DestructiveFileSource (string path, jack_nframes_t rate, bool repair_first, SampleFormat samp_format)
70         : FileSource (path, rate, repair_first, samp_format)
71 {
72         if (out_coefficient == 0) {
73                 setup_standard_crossfades (rate);
74         }
75
76         xfade_buf = new Sample[xfade_frames];
77
78         _capture_start = false;
79         _capture_end = false;
80         file_pos = 0;
81 }
82
83 DestructiveFileSource::DestructiveFileSource (const XMLNode& node, jack_nframes_t rate)
84         : FileSource (node, rate)
85 {
86         if (out_coefficient == 0) {
87                 setup_standard_crossfades (rate);
88         }
89
90         xfade_buf = new Sample[xfade_frames];
91
92         _capture_start = false;
93         _capture_end = false;
94         file_pos = 0;
95 }
96
97 DestructiveFileSource::~DestructiveFileSource()
98 {
99         delete xfade_buf;
100 }
101
102 void
103 DestructiveFileSource::setup_standard_crossfades (jack_nframes_t rate)
104 {
105         xfade_frames = (jack_nframes_t) floor ((/*Config->get_destructive_crossfade_msecs()*/ 64 / 1000.0) * rate);
106
107         out_coefficient = new gain_t[xfade_frames];
108         in_coefficient = new gain_t[xfade_frames];
109
110         for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
111
112                 /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
113
114                 in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
115                 out_coefficient[n] = 1.0 - in_coefficient[n];    /* 1 .. 0 */
116         }
117 }
118
119 int
120 DestructiveFileSource::seek (jack_nframes_t frame)
121 {
122         return 0;
123 }
124
125 void
126 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
127 {
128         _capture_start = true;
129         capture_start_frame = pos;
130 }
131
132 void
133 DestructiveFileSource::mark_capture_end()
134 {
135         _capture_end = true;
136 }
137
138 void
139 DestructiveFileSource::clear_capture_marks ()
140 {
141         _capture_start = false;
142         _capture_end = false;
143 }       
144
145 jack_nframes_t
146 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in, char * workbuf)
147 {
148         jack_nframes_t xfade = min (xfade_frames, cnt);
149         jack_nframes_t nofade = cnt - xfade;
150         Sample* fade_data = 0;
151         jack_nframes_t fade_position = 0; // in frames
152         ssize_t retval;
153         jack_nframes_t file_cnt;
154
155         if (fade_in) {
156                 fade_position = file_pos;
157                 fade_data = data;
158         } else {
159                 fade_position = file_pos + nofade;
160                 fade_data = data + nofade;
161         }
162
163         if (fade_position > _length) {
164                 
165                 /* read starts beyond end of data, just memset to zero */
166                 
167                 file_cnt = 0;
168
169         } else if (fade_position + xfade > _length) {
170                 
171                 /* read ends beyond end of data, read some, memset the rest */
172                 
173                 file_cnt = _length - fade_position;
174
175         } else {
176                 
177                 /* read is entirely within data */
178
179                 file_cnt = xfade;
180         }
181
182         if (file_cnt) {
183                 if ((retval = file_read (xfade_buf, fade_position, file_cnt, workbuf)) != (ssize_t) file_cnt) {
184                         if (retval >= 0 && errno == EAGAIN) {
185                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
186                                  * short or no data there */
187                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
188                         } else {
189                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
190                                 return 0;
191                         }
192                 }
193         } 
194
195         if (file_cnt != xfade) {
196                 jack_nframes_t delta = xfade - file_cnt;
197                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
198         }
199         
200         if (nofade && !fade_in) {
201                 if (file_write (data, file_pos, nofade, workbuf) != (ssize_t) nofade) {
202                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
203                         return 0;
204                 }
205         }
206
207         if (xfade == xfade_frames) {
208
209                 jack_nframes_t n;
210
211                 /* use the standard xfade curve */
212                 
213                 if (fade_in) {
214
215                         /* fade new material in */
216                         
217                         for (n = 0; n < xfade; ++n) {
218                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
219                         }
220
221                 } else {
222
223
224                         /* fade new material out */
225                         
226                         for (n = 0; n < xfade; ++n) {
227                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
228                         }
229                 }
230
231         } else if (xfade) {
232
233                 /* short xfade, compute custom curve */
234
235                 /* XXX COMPUTE THE CURVE, DAMMIT! */
236
237                 for (jack_nframes_t n = 0; n < xfade; ++n) {
238                         xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
239                 }
240         }
241
242         if (xfade) {
243                 if (file_write (xfade_buf, fade_position, xfade, workbuf) != (ssize_t) xfade) {
244                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
245                         return 0;
246                 }
247         }
248         
249         if (fade_in && nofade) {
250                 if (file_write (data + xfade, file_pos + xfade, nofade, workbuf) != (ssize_t) nofade) {
251                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
252                         return 0;
253                 }
254         }
255
256         return cnt;
257 }
258
259 jack_nframes_t
260 DestructiveFileSource::write (Sample* data, jack_nframes_t cnt, char * workbuf)
261 {
262         cerr << _name << ": write " << cnt << " to " << file_pos << " start ? " << _capture_start << " end ? " << _capture_end << endl;
263
264         {
265                 LockMonitor lm (_lock, __LINE__, __FILE__);
266                 
267                 jack_nframes_t oldlen;
268
269                 if (_capture_start && _capture_end) {
270                         _capture_start = false;
271                         _capture_end = false;
272
273                         /* move to the correct location place */
274                         file_pos = capture_start_frame;
275                         
276                         cerr << "First frame of capture will be at " << file_pos << "  and last at: " << file_pos + cnt << endl;
277
278                         // split cnt in half
279                         jack_nframes_t subcnt = cnt / 2;
280                         jack_nframes_t ofilepos = file_pos;
281                         
282                         // fade in
283                         if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
284                                 return 0;
285                         }
286
287                         file_pos += subcnt;
288                         Sample * tmpdata = data + subcnt;
289                         
290                         // fade out
291                         subcnt = cnt - subcnt;
292                         if (crossfade (tmpdata, subcnt, 0, workbuf) != subcnt) {
293                                 return 0;
294                         }
295
296                         file_pos = ofilepos; // adjusted below
297                 }
298                 else if (_capture_start) {
299                         _capture_start = false;
300                         _capture_end = false;
301
302                         /* move to the correct location place */
303                         file_pos = capture_start_frame;
304                         
305                         cerr << "First frame of capture will be at " << file_pos << endl;
306                         
307                         if (crossfade (data, cnt, 1, workbuf) != cnt) {
308                                 return 0;
309                         }
310
311                 } else if (_capture_end) {
312                         _capture_start = false;
313                         _capture_end = false;
314
315                         if (crossfade (data, cnt, 0, workbuf) != cnt) {
316                                 return 0;
317                         }
318                 } else {
319                         if (file_write(data, file_pos, cnt, workbuf) != (ssize_t) cnt) {
320                                 return 0;
321                         }
322                 }
323
324                 oldlen = _length;
325                 if (file_pos + cnt > _length) {
326                         _length = file_pos + cnt;
327                 }
328                 file_pos += cnt;
329                 
330                 cerr << this << ' ' << _name << " at end of write, file_pos = " << file_pos << " length = " << ((int) &_length - (int) this) << ' ' << &_length << ' ' << _length << endl;
331
332                 if (_build_peakfiles) {
333                         PeakBuildRecord *pbr = 0;
334                         
335                         if (pending_peak_builds.size()) {
336                                 pbr = pending_peak_builds.back();
337                         }
338                         
339                         if (pbr && pbr->frame + pbr->cnt == oldlen) {
340                                 
341                                 /* the last PBR extended to the start of the current write,
342                                    so just extend it again.
343                                 */
344
345                                 pbr->cnt += cnt;
346                         } else {
347                                 pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt));
348                         }
349                         
350                         _peaks_built = false;
351                 }
352
353         }
354
355
356         if (_build_peakfiles) {
357                 queue_for_peaks (*this);
358         }
359
360         return cnt;
361 }
362
363 jack_nframes_t
364 DestructiveFileSource::last_capture_start_frame () const
365 {
366         return capture_start_frame;
367 }
368
369 XMLNode& 
370 DestructiveFileSource::get_state ()
371 {
372         XMLNode& node = FileSource::get_state ();
373         node.add_property (X_("destructive"), "true");
374         return node;
375 }