pulling trunk
[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_xfade_msecs () / 1000.0) * rate);
106
107         if (out_coefficient) {
108                 delete [] out_coefficient;
109         }
110
111         if (in_coefficient) {
112                 delete [] in_coefficient;
113         }
114
115         out_coefficient = new gain_t[xfade_frames];
116         in_coefficient = new gain_t[xfade_frames];
117
118         for (jack_nframes_t n = 0; n < xfade_frames; ++n) {
119
120                 /* XXXX THIS IS NOT THE RIGHT XFADE CURVE: USE A PROPER VOLUMETRIC EQUAL POWER CURVE */
121
122                 in_coefficient[n] = n/(gain_t) (xfade_frames-1); /* 0 .. 1 */
123                 out_coefficient[n] = 1.0 - in_coefficient[n];    /* 1 .. 0 */
124         }
125 }
126
127 int
128 DestructiveFileSource::seek (jack_nframes_t frame)
129 {
130         return 0;
131 }
132
133 void
134 DestructiveFileSource::mark_capture_start (jack_nframes_t pos)
135 {
136         _capture_start = true;
137         capture_start_frame = pos;
138 }
139
140 void
141 DestructiveFileSource::mark_capture_end()
142 {
143         _capture_end = true;
144 }
145
146 void
147 DestructiveFileSource::clear_capture_marks ()
148 {
149         _capture_start = false;
150         _capture_end = false;
151 }       
152
153 jack_nframes_t
154 DestructiveFileSource::crossfade (Sample* data, jack_nframes_t cnt, int fade_in, char * workbuf)
155 {
156         jack_nframes_t xfade = min (xfade_frames, cnt);
157         jack_nframes_t nofade = cnt - xfade;
158         Sample* fade_data = 0;
159         jack_nframes_t fade_position = 0; // in frames
160         ssize_t retval;
161         jack_nframes_t file_cnt;
162
163         if (fade_in) {
164                 fade_position = file_pos;
165                 fade_data = data;
166         } else {
167                 fade_position = file_pos + nofade;
168                 fade_data = data + nofade;
169         }
170
171         if (fade_position > _length) {
172                 
173                 /* read starts beyond end of data, just memset to zero */
174                 
175                 file_cnt = 0;
176
177         } else if (fade_position + xfade > _length) {
178                 
179                 /* read ends beyond end of data, read some, memset the rest */
180                 
181                 file_cnt = _length - fade_position;
182
183         } else {
184                 
185                 /* read is entirely within data */
186
187                 file_cnt = xfade;
188         }
189
190         if (file_cnt) {
191                 if ((retval = file_read (xfade_buf, fade_position, file_cnt, workbuf)) != (ssize_t) file_cnt) {
192                         if (retval >= 0 && errno == EAGAIN) {
193                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
194                                  * short or no data there */
195                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
196                         } else {
197                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
198                                 return 0;
199                         }
200                 }
201         } 
202
203         if (file_cnt != xfade) {
204                 jack_nframes_t delta = xfade - file_cnt;
205                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
206         }
207         
208         if (nofade && !fade_in) {
209                 if (file_write (data, file_pos, nofade, workbuf) != (ssize_t) nofade) {
210                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
211                         return 0;
212                 }
213         }
214
215         if (xfade == xfade_frames) {
216
217                 jack_nframes_t n;
218
219                 /* use the standard xfade curve */
220                 
221                 if (fade_in) {
222
223                         /* fade new material in */
224                         
225                         for (n = 0; n < xfade; ++n) {
226                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
227                         }
228
229                 } else {
230
231
232                         /* fade new material out */
233                         
234                         for (n = 0; n < xfade; ++n) {
235                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
236                         }
237                 }
238
239         } else if (xfade) {
240
241                 /* short xfade, compute custom curve */
242
243                 /* XXX COMPUTE THE CURVE, DAMMIT! */
244
245                 for (jack_nframes_t n = 0; n < xfade; ++n) {
246                         xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
247                 }
248         }
249
250         if (xfade) {
251                 if (file_write (xfade_buf, fade_position, xfade, workbuf) != (ssize_t) xfade) {
252                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
253                         return 0;
254                 }
255         }
256         
257         if (fade_in && nofade) {
258                 if (file_write (data + xfade, file_pos + xfade, nofade, workbuf) != (ssize_t) nofade) {
259                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
260                         return 0;
261                 }
262         }
263
264         return cnt;
265 }
266
267 jack_nframes_t
268 DestructiveFileSource::write (Sample* data, jack_nframes_t cnt, char * workbuf)
269 {
270         {
271                 Glib::Mutex::Lock lm (_lock);
272                 
273                 jack_nframes_t old_file_pos;
274
275                 if (_capture_start && _capture_end) {
276                         _capture_start = false;
277                         _capture_end = false;
278
279                         /* move to the correct location place */
280                         file_pos = capture_start_frame;
281                         
282                         // split cnt in half
283                         jack_nframes_t subcnt = cnt / 2;
284                         jack_nframes_t ofilepos = file_pos;
285                         
286                         // fade in
287                         if (crossfade (data, subcnt, 1, workbuf) != subcnt) {
288                                 return 0;
289                         }
290
291                         file_pos += subcnt;
292                         Sample * tmpdata = data + subcnt;
293                         
294                         // fade out
295                         subcnt = cnt - subcnt;
296                         if (crossfade (tmpdata, subcnt, 0, workbuf) != subcnt) {
297                                 return 0;
298                         }
299
300                         file_pos = ofilepos; // adjusted below
301                 }
302                 else if (_capture_start) {
303                         _capture_start = false;
304                         _capture_end = false;
305
306                         /* move to the correct location place */
307                         file_pos = capture_start_frame;
308                         
309                         if (crossfade (data, cnt, 1, workbuf) != cnt) {
310                                 return 0;
311                         }
312
313                 } else if (_capture_end) {
314                         _capture_start = false;
315                         _capture_end = false;
316
317                         if (crossfade (data, cnt, 0, workbuf) != cnt) {
318                                 return 0;
319                         }
320                 } else {
321                         if (file_write(data, file_pos, cnt, workbuf) != (ssize_t) cnt) {
322                                 return 0;
323                         }
324                 }
325
326                 old_file_pos = file_pos;
327                 if (file_pos + cnt > _length) {
328                         _length = file_pos + cnt;
329                 }
330                 file_pos += cnt;
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 == old_file_pos) {
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 (old_file_pos, cnt));
348                         }
349                         
350                         _peaks_built = false;
351                 }
352         }
353
354         if (_build_peakfiles) {
355                 queue_for_peaks (*this);
356         }
357
358         return cnt;
359 }
360
361 jack_nframes_t
362 DestructiveFileSource::last_capture_start_frame () const
363 {
364         return capture_start_frame;
365 }
366
367 XMLNode& 
368 DestructiveFileSource::get_state ()
369 {
370         XMLNode& node = FileSource::get_state ();
371         node.add_property (X_("destructive"), "true");
372         return node;
373 }