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