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