allow track selection to be toggled (ctrl-clicked); potential fixes for tape display...
[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 #include <ardour/utils.h>
60 #include <ardour/session.h>
61
62 #include "i18n.h"
63
64 using namespace std;
65 using namespace ARDOUR;
66 using namespace PBD;
67
68 gain_t* DestructiveFileSource::out_coefficient = 0;
69 gain_t* DestructiveFileSource::in_coefficient = 0;
70 nframes_t DestructiveFileSource::xfade_frames = 64;
71
72 DestructiveFileSource::DestructiveFileSource (Session& s, string path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate, Flag flags)
73         : SndFileSource (s, path, samp_format, hdr_format, rate, flags)
74 {
75         init ();
76 }
77
78
79 DestructiveFileSource::DestructiveFileSource (Session& s, string path, Flag flags)
80         : SndFileSource (s, path, flags)
81 {
82         init ();
83 }
84
85 DestructiveFileSource::DestructiveFileSource (Session& s, const XMLNode& node)
86         : SndFileSource (s, node)
87 {
88         init ();
89 }
90
91 void
92 DestructiveFileSource::init ()
93 {
94         xfade_buf = new Sample[xfade_frames];
95
96         _capture_start = false;
97         _capture_end = false;
98         file_pos = 0;
99
100         timeline_position = header_position_offset;
101         AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &DestructiveFileSource::handle_header_position_change));
102 }
103
104 DestructiveFileSource::~DestructiveFileSource()
105 {
106         delete xfade_buf;
107 }
108
109 void
110 DestructiveFileSource::setup_standard_crossfades (nframes_t rate)
111 {
112         /* This static method is assumed to have been called by the Session
113            before any DFS's are created.
114         */
115
116         xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
117
118         if (out_coefficient) {
119                 delete [] out_coefficient;
120         }
121
122         if (in_coefficient) {
123                 delete [] in_coefficient;
124         }
125
126         out_coefficient = new gain_t[xfade_frames];
127         in_coefficient = new gain_t[xfade_frames];
128
129         compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
130 }
131
132 void
133 DestructiveFileSource::mark_capture_start (nframes_t pos)
134 {
135         if (pos < timeline_position) {
136                 _capture_start = false;
137         } else {
138                 _capture_start = true;
139                 capture_start_frame = pos;
140         }
141 }
142
143 void
144 DestructiveFileSource::mark_capture_end()
145 {
146         _capture_end = true;
147 }
148
149 void
150 DestructiveFileSource::clear_capture_marks ()
151 {
152         _capture_start = false;
153         _capture_end = false;
154 }       
155
156 nframes_t
157 DestructiveFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
158 {
159         nframes_t xfade = min (xfade_frames, cnt);
160         nframes_t nofade = cnt - xfade;
161         Sample* fade_data = 0;
162         nframes_t fade_position = 0; // in frames
163         ssize_t retval;
164         nframes_t file_cnt;
165
166         if (fade_in) {
167                 fade_position = file_pos;
168                 fade_data = data;
169         } else {
170                 fade_position = file_pos + nofade;
171                 fade_data = data + nofade;
172         }
173
174         if (fade_position > _length) {
175                 
176                 /* read starts beyond end of data, just memset to zero */
177                 
178                 file_cnt = 0;
179
180         } else if (fade_position + xfade > _length) {
181                 
182                 /* read ends beyond end of data, read some, memset the rest */
183                 
184                 file_cnt = _length - fade_position;
185
186         } else {
187                 
188                 /* read is entirely within data */
189
190                 file_cnt = xfade;
191         }
192
193         if (file_cnt) {
194                 
195                 if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
196                         if (retval >= 0 && errno == EAGAIN) {
197                                 /* XXX - can we really trust that errno is meaningful here?  yes POSIX, i'm talking to you.
198                                  * short or no data there */
199                                 memset (xfade_buf, 0, xfade * sizeof(Sample));
200                         } else {
201                                 error << string_compose(_("DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
202                                 return 0;
203                         }
204                 }
205         } 
206
207         if (file_cnt != xfade) {
208                 nframes_t delta = xfade - file_cnt;
209                 memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
210         }
211         
212         if (nofade && !fade_in) {
213                 if (write_float (data, file_pos, nofade) != nofade) {
214                         error << string_compose(_("DestructiveFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
215                         return 0;
216                 }
217         }
218
219         if (xfade == xfade_frames) {
220
221                 nframes_t n;
222
223                 /* use the standard xfade curve */
224                 
225                 if (fade_in) {
226
227                         /* fade new material in */
228                         
229                         for (n = 0; n < xfade; ++n) {
230                                 xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
231                         }
232
233                 } else {
234
235
236                         /* fade new material out */
237                         
238                         for (n = 0; n < xfade; ++n) {
239                                 xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
240                         }
241                 }
242
243         } else if (xfade) {
244
245                 gain_t in[xfade];
246                 gain_t out[xfade];
247
248                 /* short xfade, compute custom curve */
249
250                 compute_equal_power_fades (xfade, in, out);
251
252                 for (nframes_t n = 0; n < xfade; ++n) {
253                         xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[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 nframes_t
275 DestructiveFileSource::write_unlocked (Sample* data, nframes_t cnt)
276 {
277         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                 nframes_t subcnt = cnt / 2;
297                 nframes_t ofilepos = file_pos;
298                 
299                 // fade in
300                 if (crossfade (data, subcnt, 1) != 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) != 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 - timeline_position;
326
327                 if (crossfade (data, cnt, 1) != 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) != cnt) {
341                         return 0;
342                 }
343
344         } else {
345
346                 /* in the middle of recording */
347                 
348
349                 if (write_float (data, file_pos, cnt) != cnt) {
350                         return 0;
351                 }
352         }
353         
354         old_file_pos = file_pos;
355         update_length (file_pos, cnt);
356         file_pos += cnt;
357
358         if (_build_peakfiles) {
359                 PeakBuildRecord *pbr = 0;
360                 
361                 if (pending_peak_builds.size()) {
362                         pbr = pending_peak_builds.back();
363                 }
364                 
365                 if (pbr && pbr->frame + pbr->cnt == old_file_pos) {
366                         
367                         /* the last PBR extended to the start of the current write,
368                            so just extend it again.
369                         */
370                         
371                         pbr->cnt += cnt;
372                 } else {
373                         pending_peak_builds.push_back (new PeakBuildRecord (old_file_pos, cnt));
374                 }
375                 
376                 _peaks_built = false;
377         }
378
379         if (_build_peakfiles) {
380                 queue_for_peaks (shared_from_this ());
381         }
382         
383         return cnt;
384 }
385
386 nframes_t
387 DestructiveFileSource::last_capture_start_frame () const
388 {
389         return capture_start_frame;
390 }
391
392 XMLNode& 
393 DestructiveFileSource::get_state ()
394 {
395         XMLNode& node = AudioFileSource::get_state ();
396         node.add_property (X_("destructive"), "true");
397         return node;
398 }
399
400 void
401 DestructiveFileSource::handle_header_position_change ()
402 {
403         if ( _length != 0 ) {
404                 error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
405                 //in the future, pop up a dialog here that allows user to regenerate file with new start offset
406         } else if (writable()) {
407                 timeline_position = header_position_offset;
408                 set_header_timeline_position ();  //this will get flushed if/when the file is recorded to
409         }
410 }
411
412 void
413 DestructiveFileSource::set_timeline_position (nframes_t pos)
414 {
415         //destructive track timeline postion does not change except at instantion or when header_position_offset (session start) changes
416 }
417
418 int
419 DestructiveFileSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const
420 {
421         return AudioFileSource::read_peaks (peaks, npeaks, start, cnt, samples_per_unit);
422 }
423