1 /*
2 * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
3 * Copyright (C) 2006-2017 Paul Davis <paul@linuxaudiosystems.com>
4 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
6 * Copyright (C) 2015-2018 Ben Loftis <ben@harrisonconsoles.com>
7 * Copyright (C) 2016-2017 Nick Mainsbridge <mainsbridge@gmail.com>
8 * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24
25 #include <cmath>
26 #include <climits>
27 #include <cfloat>
28 #include <algorithm>
29
30 #include <set>
31
32 #include <boost/scoped_array.hpp>
33 #include <boost/shared_ptr.hpp>
34
35 #include <glibmm/threads.h>
36
37 #include "pbd/basename.h"
38 #include "pbd/xml++.h"
39 #include "pbd/enumwriter.h"
40 #include "pbd/convert.h"
41
42 #include "evoral/Curve.h"
43
44 #include "ardour/audioregion.h"
45 #include "ardour/session.h"
46 #include "ardour/dB.h"
47 #include "ardour/debug.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/playlist.h"
50 #include "ardour/audiofilesource.h"
51 #include "ardour/region_factory.h"
52 #include "ardour/runtime_functions.h"
53 #include "ardour/transient_detector.h"
54 #include "ardour/parameter_descriptor.h"
55 #include "ardour/progress.h"
56
57 #include "ardour/sndfilesource.h"
58
59 #include "pbd/i18n.h"
60 #include <locale.h>
61
62 using namespace std;
63 using namespace ARDOUR;
64 using namespace PBD;
65
66 namespace ARDOUR {
67 namespace Properties {
68 PBD::PropertyDescriptor<bool> envelope_active;
69 PBD::PropertyDescriptor<bool> default_fade_in;
70 PBD::PropertyDescriptor<bool> default_fade_out;
71 PBD::PropertyDescriptor<bool> fade_in_active;
72 PBD::PropertyDescriptor<bool> fade_out_active;
73 PBD::PropertyDescriptor<float> scale_amplitude;
74 PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > fade_in;
75 PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > inverse_fade_in;
76 PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > fade_out;
77 PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > inverse_fade_out;
78 PBD::PropertyDescriptor<boost::shared_ptr<AutomationList> > envelope;
79 }
80 }
81
82 /* Curve manipulations */
83
84 static void
reverse_curve(boost::shared_ptr<Evoral::ControlList> dst,boost::shared_ptr<const Evoral::ControlList> src)85 reverse_curve (boost::shared_ptr<Evoral::ControlList> dst, boost::shared_ptr<const Evoral::ControlList> src)
86 {
87 size_t len = src->when(false);
88 // TODO read-lock of src (!)
89 for (Evoral::ControlList::const_reverse_iterator it = src->rbegin(); it!=src->rend(); it++) {
90 dst->fast_simple_add (len - (*it)->when, (*it)->value);
91 }
92 }
93
94 static void
generate_inverse_power_curve(boost::shared_ptr<Evoral::ControlList> dst,boost::shared_ptr<const Evoral::ControlList> src)95 generate_inverse_power_curve (boost::shared_ptr<Evoral::ControlList> dst, boost::shared_ptr<const Evoral::ControlList> src)
96 {
97 // calc inverse curve using sum of squares
98 for (Evoral::ControlList::const_iterator it = src->begin(); it!=src->end(); ++it ) {
99 float value = (*it)->value;
100 value = 1 - powf(value,2);
101 value = sqrtf(value);
102 dst->fast_simple_add ( (*it)->when, value );
103 }
104 }
105
106 static void
generate_db_fade(boost::shared_ptr<Evoral::ControlList> dst,double len,int num_steps,float dB_drop)107 generate_db_fade (boost::shared_ptr<Evoral::ControlList> dst, double len, int num_steps, float dB_drop)
108 {
109 dst->clear ();
110 dst->fast_simple_add (0, 1);
111
112 //generate a fade-out curve by successively applying a gain drop
113 float fade_speed = dB_to_coefficient(dB_drop / (float) num_steps);
114 float coeff = GAIN_COEFF_UNITY;
115 for (int i = 1; i < (num_steps-1); i++) {
116 coeff *= fade_speed;
117 dst->fast_simple_add (len*(double)i/(double)num_steps, coeff);
118 }
119
120 dst->fast_simple_add (len, GAIN_COEFF_SMALL);
121 }
122
123 static void
merge_curves(boost::shared_ptr<Evoral::ControlList> dst,boost::shared_ptr<const Evoral::ControlList> curve1,boost::shared_ptr<const Evoral::ControlList> curve2)124 merge_curves (boost::shared_ptr<Evoral::ControlList> dst,
125 boost::shared_ptr<const Evoral::ControlList> curve1,
126 boost::shared_ptr<const Evoral::ControlList> curve2)
127 {
128 Evoral::ControlList::EventList::size_type size = curve1->size();
129
130 //curve lengths must match for now
131 if (size != curve2->size()) {
132 return;
133 }
134
135 Evoral::ControlList::const_iterator c1 = curve1->begin();
136 int count = 0;
137 for (Evoral::ControlList::const_iterator c2 = curve2->begin(); c2!=curve2->end(); c2++ ) {
138 float v1 = accurate_coefficient_to_dB((*c1)->value);
139 float v2 = accurate_coefficient_to_dB((*c2)->value);
140
141 double interp = v1 * ( 1.0-( (double)count / (double)size) );
142 interp += v2 * ( (double)count / (double)size );
143
144 interp = dB_to_coefficient(interp);
145 dst->fast_simple_add ( (*c1)->when, interp );
146 c1++;
147 count++;
148 }
149 }
150
151 void
make_property_quarks()152 AudioRegion::make_property_quarks ()
153 {
154 Properties::envelope_active.property_id = g_quark_from_static_string (X_("envelope-active"));
155 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for envelope-active = %1\n", Properties::envelope_active.property_id));
156 Properties::default_fade_in.property_id = g_quark_from_static_string (X_("default-fade-in"));
157 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for default-fade-in = %1\n", Properties::default_fade_in.property_id));
158 Properties::default_fade_out.property_id = g_quark_from_static_string (X_("default-fade-out"));
159 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for default-fade-out = %1\n", Properties::default_fade_out.property_id));
160 Properties::fade_in_active.property_id = g_quark_from_static_string (X_("fade-in-active"));
161 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-in-active = %1\n", Properties::fade_in_active.property_id));
162 Properties::fade_out_active.property_id = g_quark_from_static_string (X_("fade-out-active"));
163 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for fade-out-active = %1\n", Properties::fade_out_active.property_id));
164 Properties::scale_amplitude.property_id = g_quark_from_static_string (X_("scale-amplitude"));
165 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for scale-amplitude = %1\n", Properties::scale_amplitude.property_id));
166 Properties::fade_in.property_id = g_quark_from_static_string (X_("FadeIn"));
167 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for FadeIn = %1\n", Properties::fade_in.property_id));
168 Properties::inverse_fade_in.property_id = g_quark_from_static_string (X_("InverseFadeIn"));
169 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for InverseFadeIn = %1\n", Properties::inverse_fade_in.property_id));
170 Properties::fade_out.property_id = g_quark_from_static_string (X_("FadeOut"));
171 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for FadeOut = %1\n", Properties::fade_out.property_id));
172 Properties::inverse_fade_out.property_id = g_quark_from_static_string (X_("InverseFadeOut"));
173 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for InverseFadeOut = %1\n", Properties::inverse_fade_out.property_id));
174 Properties::envelope.property_id = g_quark_from_static_string (X_("Envelope"));
175 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for Envelope = %1\n", Properties::envelope.property_id));
176 }
177
178 void
register_properties()179 AudioRegion::register_properties ()
180 {
181 /* no need to register parent class properties */
182
183 add_property (_envelope_active);
184 add_property (_default_fade_in);
185 add_property (_default_fade_out);
186 add_property (_fade_in_active);
187 add_property (_fade_out_active);
188 add_property (_scale_amplitude);
189 add_property (_fade_in);
190 add_property (_inverse_fade_in);
191 add_property (_fade_out);
192 add_property (_inverse_fade_out);
193 add_property (_envelope);
194 }
195
196 #define AUDIOREGION_STATE_DEFAULT \
197 _envelope_active (Properties::envelope_active, false) \
198 , _default_fade_in (Properties::default_fade_in, true) \
199 , _default_fade_out (Properties::default_fade_out, true) \
200 , _fade_in_active (Properties::fade_in_active, true) \
201 , _fade_out_active (Properties::fade_out_active, true) \
202 , _scale_amplitude (Properties::scale_amplitude, 1.0) \
203 , _fade_in (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation)))) \
204 , _inverse_fade_in (Properties::inverse_fade_in, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeInAutomation)))) \
205 , _fade_out (Properties::fade_out, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeOutAutomation)))) \
206 , _inverse_fade_out (Properties::inverse_fade_out, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter (FadeOutAutomation))))
207
208 #define AUDIOREGION_COPY_STATE(other) \
209 _envelope_active (Properties::envelope_active, other->_envelope_active) \
210 , _default_fade_in (Properties::default_fade_in, other->_default_fade_in) \
211 , _default_fade_out (Properties::default_fade_out, other->_default_fade_out) \
212 , _fade_in_active (Properties::fade_in_active, other->_fade_in_active) \
213 , _fade_out_active (Properties::fade_out_active, other->_fade_out_active) \
214 , _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) \
215 , _fade_in (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_fade_in.val()))) \
216 , _inverse_fade_in (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_in.val()))) \
217 , _fade_out (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_fade_out.val()))) \
218 , _inverse_fade_out (Properties::fade_in, boost::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_out.val()))) \
219 /* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
220
221 void
init()222 AudioRegion::init ()
223 {
224 register_properties ();
225
226 suspend_property_changes();
227 set_default_fades ();
228 set_default_envelope ();
229 resume_property_changes();
230
231 listen_to_my_curves ();
232 connect_to_analysis_changed ();
233 connect_to_header_position_offset_changed ();
234 }
235
236 /** Constructor for use by derived types only */
AudioRegion(Session & s,samplepos_t start,samplecnt_t len,std::string name)237 AudioRegion::AudioRegion (Session& s, samplepos_t start, samplecnt_t len, std::string name)
238 : Region (s, start, len, name, DataType::AUDIO)
239 , AUDIOREGION_STATE_DEFAULT
240 , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter(EnvelopeAutomation))))
241 , _automatable (s)
242 , _fade_in_suspended (0)
243 , _fade_out_suspended (0)
244 {
245 init ();
246 assert (_sources.size() == _master_sources.size());
247 }
248
249 /** Basic AudioRegion constructor */
AudioRegion(const SourceList & srcs)250 AudioRegion::AudioRegion (const SourceList& srcs)
251 : Region (srcs)
252 , AUDIOREGION_STATE_DEFAULT
253 , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (Evoral::Parameter(EnvelopeAutomation))))
254 , _automatable(srcs[0]->session())
255 , _fade_in_suspended (0)
256 , _fade_out_suspended (0)
257 {
258 init ();
259 assert (_sources.size() == _master_sources.size());
260 }
261
AudioRegion(boost::shared_ptr<const AudioRegion> other)262 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
263 : Region (other)
264 , AUDIOREGION_COPY_STATE (other)
265 /* As far as I can see, the _envelope's times are relative to region position, and have nothing
266 * to do with sources (and hence _start). So when we copy the envelope, we just use the supplied offset.
267 */
268 , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (*other->_envelope.val(), 0, other->_length)))
269 , _automatable (other->session())
270 , _fade_in_suspended (0)
271 , _fade_out_suspended (0)
272 {
273 /* don't use init here, because we got fade in/out from the other region
274 */
275 register_properties ();
276 listen_to_my_curves ();
277 connect_to_analysis_changed ();
278 connect_to_header_position_offset_changed ();
279
280 assert(_type == DataType::AUDIO);
281 assert (_sources.size() == _master_sources.size());
282 }
283
AudioRegion(boost::shared_ptr<const AudioRegion> other,MusicSample offset)284 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, MusicSample offset)
285 : Region (other, offset)
286 , AUDIOREGION_COPY_STATE (other)
287 /* As far as I can see, the _envelope's times are relative to region position, and have nothing
288 to do with sources (and hence _start). So when we copy the envelope, we just use the supplied offset.
289 */
290 , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (*other->_envelope.val(), offset.sample, other->_length)))
291 , _automatable (other->session())
292 , _fade_in_suspended (0)
293 , _fade_out_suspended (0)
294 {
295 /* don't use init here, because we got fade in/out from the other region
296 */
297 register_properties ();
298 listen_to_my_curves ();
299 connect_to_analysis_changed ();
300 connect_to_header_position_offset_changed ();
301
302 assert(_type == DataType::AUDIO);
303 assert (_sources.size() == _master_sources.size());
304 }
305
AudioRegion(boost::shared_ptr<const AudioRegion> other,const SourceList & srcs)306 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, const SourceList& srcs)
307 : Region (boost::static_pointer_cast<const Region>(other), srcs)
308 , AUDIOREGION_COPY_STATE (other)
309 , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList (*other->_envelope.val())))
310 , _automatable (other->session())
311 , _fade_in_suspended (0)
312 , _fade_out_suspended (0)
313 {
314 /* make-a-sort-of-copy-with-different-sources constructor (used by audio filter) */
315
316 register_properties ();
317
318 listen_to_my_curves ();
319 connect_to_analysis_changed ();
320 connect_to_header_position_offset_changed ();
321
322 assert (_sources.size() == _master_sources.size());
323 }
324
AudioRegion(SourceList & srcs)325 AudioRegion::AudioRegion (SourceList& srcs)
326 : Region (srcs)
327 , AUDIOREGION_STATE_DEFAULT
328 , _envelope (Properties::envelope, boost::shared_ptr<AutomationList> (new AutomationList(Evoral::Parameter(EnvelopeAutomation))))
329 , _automatable(srcs[0]->session())
330 , _fade_in_suspended (0)
331 , _fade_out_suspended (0)
332 {
333 init ();
334
335 assert(_type == DataType::AUDIO);
336 assert (_sources.size() == _master_sources.size());
337 }
338
~AudioRegion()339 AudioRegion::~AudioRegion ()
340 {
341 }
342
343 void
post_set(const PropertyChange &)344 AudioRegion::post_set (const PropertyChange& /*ignored*/)
345 {
346 if (!_sync_marked) {
347 _sync_position = _start;
348 }
349
350 /* return to default fades if the existing ones are too long */
351
352 if (_left_of_split) {
353 if (_fade_in->when(false) >= _length) {
354 set_default_fade_in ();
355 }
356 set_default_fade_out ();
357 _left_of_split = false;
358 }
359
360 if (_right_of_split) {
361 if (_fade_out->when(false) >= _length) {
362 set_default_fade_out ();
363 }
364
365 set_default_fade_in ();
366 _right_of_split = false;
367 }
368
369 /* If _length changed, adjust our gain envelope accordingly */
370 _envelope->truncate_end (_length);
371 }
372
373 void
connect_to_analysis_changed()374 AudioRegion::connect_to_analysis_changed ()
375 {
376 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
377 (*i)->AnalysisChanged.connect_same_thread (*this, boost::bind (&AudioRegion::maybe_invalidate_transients, this));
378 }
379 }
380
381 void
connect_to_header_position_offset_changed()382 AudioRegion::connect_to_header_position_offset_changed ()
383 {
384 set<boost::shared_ptr<Source> > unique_srcs;
385
386 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
387
388 /* connect only once to HeaderPositionOffsetChanged, even if sources are replicated
389 */
390
391 if (unique_srcs.find (*i) == unique_srcs.end ()) {
392 unique_srcs.insert (*i);
393 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
394 if (afs) {
395 afs->HeaderPositionOffsetChanged.connect_same_thread (*this, boost::bind (&AudioRegion::source_offset_changed, this));
396 }
397 }
398 }
399 }
400
401 void
listen_to_my_curves()402 AudioRegion::listen_to_my_curves ()
403 {
404 _envelope->StateChanged.connect_same_thread (*this, boost::bind (&AudioRegion::envelope_changed, this));
405 _fade_in->StateChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fade_in_changed, this));
406 _fade_out->StateChanged.connect_same_thread (*this, boost::bind (&AudioRegion::fade_out_changed, this));
407 }
408
409 void
set_envelope_active(bool yn)410 AudioRegion::set_envelope_active (bool yn)
411 {
412 if (envelope_active() != yn) {
413 _envelope_active = yn;
414 send_change (PropertyChange (Properties::envelope_active));
415 }
416 }
417
418 /** @param buf Buffer to put peak data in.
419 * @param npeaks Number of peaks to read (ie the number of PeakDatas in buf)
420 * @param offset Start position, as an offset from the start of this region's source.
421 * @param cnt Number of samples to read.
422 * @param chan_n Channel.
423 * @param samples_per_pixel Number of samples to use to generate one peak value.
424 */
425
426 ARDOUR::samplecnt_t
read_peaks(PeakData * buf,samplecnt_t npeaks,samplecnt_t offset,samplecnt_t cnt,uint32_t chan_n,double samples_per_pixel) const427 AudioRegion::read_peaks (PeakData *buf, samplecnt_t npeaks, samplecnt_t offset, samplecnt_t cnt, uint32_t chan_n, double samples_per_pixel) const
428 {
429 if (chan_n >= _sources.size()) {
430 return 0;
431 }
432
433 if (audio_source(chan_n)->read_peaks (buf, npeaks, offset, cnt, samples_per_pixel)) {
434 return 0;
435 }
436
437 if (_scale_amplitude != 1.0f) {
438 for (samplecnt_t n = 0; n < npeaks; ++n) {
439 buf[n].max *= _scale_amplitude;
440 buf[n].min *= _scale_amplitude;
441 }
442 }
443
444 return npeaks;
445 }
446
447 /** @param buf Buffer to write data to (existing data will be overwritten).
448 * @param pos Position to read from as an offset from the region position.
449 * @param cnt Number of samples to read.
450 * @param channel Channel to read from.
451 */
452 samplecnt_t
read(Sample * buf,samplepos_t pos,samplecnt_t cnt,int channel) const453 AudioRegion::read (Sample* buf, samplepos_t pos, samplecnt_t cnt, int channel) const
454 {
455 /* raw read, no fades, no gain, nada */
456 return read_from_sources (_sources, _length, buf, _position + pos, cnt, channel);
457 }
458
459 samplecnt_t
master_read_at(Sample * buf,Sample *,float *,samplepos_t position,samplecnt_t cnt,uint32_t chan_n) const460 AudioRegion::master_read_at (Sample *buf, Sample* /*mixdown_buffer*/, float* /*gain_buffer*/,
461 samplepos_t position, samplecnt_t cnt, uint32_t chan_n) const
462 {
463 /* do not read gain/scaling/fades and do not count this disk i/o in statistics */
464
465 assert (cnt >= 0);
466 return read_from_sources (
467 _master_sources, _master_sources.front()->length (_master_sources.front()->natural_position()),
468 buf, position, cnt, chan_n
469 );
470 }
471
472 /** @param buf Buffer to mix data into.
473 * @param mixdown_buffer Scratch buffer for audio data.
474 * @param gain_buffer Scratch buffer for gain data.
475 * @param position Position within the session to read from.
476 * @param cnt Number of samples to read.
477 * @param chan_n Channel number to read.
478 */
479 samplecnt_t
read_at(Sample * buf,Sample * mixdown_buffer,float * gain_buffer,samplepos_t position,samplecnt_t cnt,uint32_t chan_n) const480 AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
481 samplepos_t position,
482 samplecnt_t cnt,
483 uint32_t chan_n) const
484 {
485 /* We are reading data from this region into buf (possibly via mixdown_buffer).
486 The caller has verified that we cover the desired section.
487 */
488
489 /* See doc/region_read.svg for a drawing which might help to explain
490 what is going on.
491 */
492
493 assert (cnt >= 0);
494
495 if (n_channels() == 0) {
496 return 0;
497 }
498
499 /* WORK OUT WHERE TO GET DATA FROM */
500
501 samplecnt_t to_read;
502
503 assert (position >= _position);
504 sampleoffset_t const internal_offset = position - _position;
505
506 if (internal_offset >= _length) {
507 return 0; /* read nothing */
508 }
509
510 if ((to_read = min (cnt, _length - internal_offset)) == 0) {
511 return 0; /* read nothing */
512 }
513
514 boost::shared_ptr<Playlist> pl (playlist());
515 if (!pl){
516 return 0;
517 }
518
519 /* COMPUTE DETAILS OF ANY FADES INVOLVED IN THIS READ */
520
521 /* Amount (length) of fade in that we are dealing with in this read */
522 samplecnt_t fade_in_limit = 0;
523
524 /* Offset from buf / mixdown_buffer of the start
525 of any fade out that we are dealing with
526 */
527 sampleoffset_t fade_out_offset = 0;
528
529 /* Amount (length) of fade out that we are dealing with in this read */
530 samplecnt_t fade_out_limit = 0;
531
532 samplecnt_t fade_interval_start = 0;
533
534 /* Fade in */
535
536 if (_fade_in_active && _session.config.get_use_region_fades()) {
537
538 samplecnt_t fade_in_length = (samplecnt_t) _fade_in->when(false);
539
540 /* see if this read is within the fade in */
541
542 if (internal_offset < fade_in_length) {
543 fade_in_limit = min (to_read, fade_in_length - internal_offset);
544 }
545 }
546
547 /* Fade out */
548
549 if (_fade_out_active && _session.config.get_use_region_fades()) {
550
551 /* see if some part of this read is within the fade out */
552
553 /* ................. >| REGION
554 * _length
555 *
556 * { } FADE
557 * fade_out_length
558 * ^
559 * _length - fade_out_length
560 *
561 * |--------------|
562 * ^internal_offset
563 * ^internal_offset + to_read
564 *
565 * we need the intersection of [internal_offset,internal_offset+to_read] with
566 * [_length - fade_out_length, _length]
567 *
568 */
569
570 fade_interval_start = max (internal_offset, _length - samplecnt_t (_fade_out->when(false)));
571 samplecnt_t fade_interval_end = min(internal_offset + to_read, _length.val());
572
573 if (fade_interval_end > fade_interval_start) {
574 /* (part of the) the fade out is in this buffer */
575 fade_out_limit = fade_interval_end - fade_interval_start;
576 fade_out_offset = fade_interval_start - internal_offset;
577 }
578 }
579
580 /* READ DATA FROM THE SOURCE INTO mixdown_buffer.
581 We can never read directly into buf, since it may contain data
582 from a region `below' this one in the stack, and our fades (if they exist)
583 may need to mix with the existing data.
584 */
585
586 if (read_from_sources (_sources, _length, mixdown_buffer, position, to_read, chan_n) != to_read) {
587 return 0;
588 }
589
590 /* APPLY REGULAR GAIN CURVES AND SCALING TO mixdown_buffer */
591
592 if (envelope_active()) {
593 _envelope->curve().get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
594
595 if (_scale_amplitude != 1.0f) {
596 for (samplecnt_t n = 0; n < to_read; ++n) {
597 mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
598 }
599 } else {
600 for (samplecnt_t n = 0; n < to_read; ++n) {
601 mixdown_buffer[n] *= gain_buffer[n];
602 }
603 }
604 } else if (_scale_amplitude != 1.0f) {
605 apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
606 }
607
608 /* APPLY FADES TO THE DATA IN mixdown_buffer AND MIX THE RESULTS INTO
609 * buf. The key things to realize here: (1) the fade being applied is
610 * (as of April 26th 2012) just the inverse of the fade in curve (2)
611 * "buf" contains data from lower regions already. So this operation
612 * fades out the existing material.
613 */
614
615 bool is_opaque = opaque();
616
617 if (fade_in_limit != 0) {
618
619 if (is_opaque) {
620 if (_inverse_fade_in) {
621
622 /* explicit inverse fade in curve (e.g. for constant
623 * power), so we have to fetch it.
624 */
625
626 _inverse_fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit);
627
628 /* Fade the data from lower layers out */
629 for (samplecnt_t n = 0; n < fade_in_limit; ++n) {
630 buf[n] *= gain_buffer[n];
631 }
632
633 /* refill gain buffer with the fade in */
634
635 _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit);
636
637 } else {
638
639 /* no explicit inverse fade in, so just use (1 - fade
640 * in) for the fade out of lower layers
641 */
642
643 _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit);
644
645 for (samplecnt_t n = 0; n < fade_in_limit; ++n) {
646 buf[n] *= 1 - gain_buffer[n];
647 }
648 }
649 } else {
650 _fade_in->curve().get_vector (internal_offset, internal_offset + fade_in_limit, gain_buffer, fade_in_limit);
651 }
652
653 /* Mix our newly-read data in, with the fade */
654 for (samplecnt_t n = 0; n < fade_in_limit; ++n) {
655 buf[n] += mixdown_buffer[n] * gain_buffer[n];
656 }
657 }
658
659 if (fade_out_limit != 0) {
660
661 samplecnt_t const curve_offset = fade_interval_start - (_length - _fade_out->when(false));
662
663 if (is_opaque) {
664 if (_inverse_fade_out) {
665
666 _inverse_fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit);
667
668 /* Fade the data from lower levels in */
669 for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
670 buf[m] *= gain_buffer[n];
671 }
672
673 /* fetch the actual fade out */
674
675 _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit);
676
677 } else {
678
679 /* no explicit inverse fade out (which is
680 * actually a fade in), so just use (1 - fade
681 * out) for the fade in of lower layers
682 */
683
684 _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit);
685
686 for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
687 buf[m] *= 1 - gain_buffer[n];
688 }
689 }
690 } else {
691 _fade_out->curve().get_vector (curve_offset, curve_offset + fade_out_limit, gain_buffer, fade_out_limit);
692 }
693
694 /* Mix our newly-read data with whatever was already there,
695 with the fade out applied to our data.
696 */
697 for (samplecnt_t n = 0, m = fade_out_offset; n < fade_out_limit; ++n, ++m) {
698 buf[m] += mixdown_buffer[m] * gain_buffer[n];
699 }
700 }
701
702 /* MIX OR COPY THE REGION BODY FROM mixdown_buffer INTO buf */
703
704 samplecnt_t const N = to_read - fade_in_limit - fade_out_limit;
705 if (N > 0) {
706 if (is_opaque) {
707 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Region %1 memcpy into buf @ %2 + %3, from mixdown buffer @ %4 + %5, len = %6 cnt was %7\n",
708 name(), buf, fade_in_limit, mixdown_buffer, fade_in_limit, N, cnt));
709 memcpy (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N * sizeof (Sample));
710 } else {
711 mix_buffers_no_gain (buf + fade_in_limit, mixdown_buffer + fade_in_limit, N);
712 }
713 }
714
715 return to_read;
716 }
717
718 /** Read data directly from one of our sources, accounting for the situation when the track has a different channel
719 * count to the region.
720 *
721 * @param srcs Source list to get our source from.
722 * @param limit Furthest that we should read, as an offset from the region position.
723 * @param buf Buffer to write data into (existing contents of the buffer will be overwritten)
724 * @param position Position to read from, in session samples.
725 * @param cnt Number of samples to read.
726 * @param chan_n Channel to read from.
727 * @return Number of samples read.
728 */
729
730 samplecnt_t
read_from_sources(SourceList const & srcs,samplecnt_t limit,Sample * buf,samplepos_t position,samplecnt_t cnt,uint32_t chan_n) const731 AudioRegion::read_from_sources (SourceList const & srcs, samplecnt_t limit, Sample* buf, samplepos_t position, samplecnt_t cnt, uint32_t chan_n) const
732 {
733 sampleoffset_t const internal_offset = position - _position;
734 if (internal_offset >= limit) {
735 return 0;
736 }
737
738 samplecnt_t const to_read = min (cnt, limit - internal_offset);
739 if (to_read == 0) {
740 return 0;
741 }
742
743 if (chan_n < n_channels()) {
744
745 boost::shared_ptr<AudioSource> src = boost::dynamic_pointer_cast<AudioSource> (srcs[chan_n]);
746 if (src->read (buf, _start + internal_offset, to_read) != to_read) {
747 return 0; /* "read nothing" */
748 }
749
750 } else {
751
752 /* track is N-channel, this region has fewer channels; silence the ones
753 we don't have.
754 */
755
756 if (Config->get_replicate_missing_region_channels()) {
757
758 /* copy an existing channel's data in for this non-existant one */
759
760 uint32_t channel = chan_n % n_channels();
761 boost::shared_ptr<AudioSource> src = boost::dynamic_pointer_cast<AudioSource> (srcs[channel]);
762
763 if (src->read (buf, _start + internal_offset, to_read) != to_read) {
764 return 0; /* "read nothing" */
765 }
766
767 } else {
768
769 /* use silence */
770 memset (buf, 0, sizeof (Sample) * to_read);
771 }
772 }
773
774 return to_read;
775 }
776
777 XMLNode&
get_basic_state()778 AudioRegion::get_basic_state ()
779 {
780 XMLNode& node (Region::state ());
781
782 node.set_property ("channels", (uint32_t)_sources.size());
783
784 return node;
785 }
786
787 XMLNode&
state()788 AudioRegion::state ()
789 {
790 XMLNode& node (get_basic_state());
791 XMLNode *child;
792
793 child = node.add_child ("Envelope");
794
795 bool default_env = false;
796
797 // If there are only two points, the points are in the start of the region and the end of the region
798 // so, if they are both at 1.0f, that means the default region.
799
800 if (_envelope->size() == 2 &&
801 _envelope->front()->value == GAIN_COEFF_UNITY &&
802 _envelope->back()->value==GAIN_COEFF_UNITY) {
803 if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
804 default_env = true;
805 }
806 }
807
808 if (default_env) {
809 child->set_property ("default", "yes");
810 } else {
811 child->add_child_nocopy (_envelope->get_state ());
812 }
813
814 child = node.add_child (X_("FadeIn"));
815
816 if (_default_fade_in) {
817 child->set_property ("default", "yes");
818 } else {
819 child->add_child_nocopy (_fade_in->get_state ());
820 }
821
822 if (_inverse_fade_in) {
823 child = node.add_child (X_("InverseFadeIn"));
824 child->add_child_nocopy (_inverse_fade_in->get_state ());
825 }
826
827 child = node.add_child (X_("FadeOut"));
828
829 if (_default_fade_out) {
830 child->set_property ("default", "yes");
831 } else {
832 child->add_child_nocopy (_fade_out->get_state ());
833 }
834
835 if (_inverse_fade_out) {
836 child = node.add_child (X_("InverseFadeOut"));
837 child->add_child_nocopy (_inverse_fade_out->get_state ());
838 }
839
840 return node;
841 }
842
843 int
_set_state(const XMLNode & node,int version,PropertyChange & what_changed,bool send)844 AudioRegion::_set_state (const XMLNode& node, int version, PropertyChange& what_changed, bool send)
845 {
846 const XMLNodeList& nlist = node.children();
847 boost::shared_ptr<Playlist> the_playlist (_playlist.lock());
848
849 suspend_property_changes ();
850
851 if (the_playlist) {
852 the_playlist->freeze ();
853 }
854
855
856 /* this will set all our State members and stuff controlled by the Region.
857 It should NOT send any changed signals - that is our responsibility.
858 */
859
860 Region::_set_state (node, version, what_changed, false);
861
862 float val;
863 if (node.get_property ("scale-gain", val)) {
864 if (val != _scale_amplitude) {
865 _scale_amplitude = val;
866 what_changed.add (Properties::scale_amplitude);
867 }
868 }
869
870 /* Now find envelope description and other related child items */
871
872 _envelope->freeze ();
873
874 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
875 XMLNode *child;
876 XMLProperty const * prop;
877
878 child = (*niter);
879
880 if (child->name() == "Envelope") {
881
882 _envelope->clear ();
883
884 if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child, version)) {
885 set_default_envelope ();
886 }
887
888 _envelope->truncate_end (_length);
889
890
891 } else if (child->name() == "FadeIn") {
892
893 _fade_in->clear ();
894
895 bool is_default;
896 if ((child->get_property ("default", is_default) && is_default) || (prop = child->property ("steepness")) != 0) {
897 set_default_fade_in ();
898 } else {
899 XMLNode* grandchild = child->child ("AutomationList");
900 if (grandchild) {
901 _fade_in->set_state (*grandchild, version);
902 }
903 }
904
905 bool is_active;
906 if (child->get_property ("active", is_active)) {
907 set_fade_in_active (is_active);
908 }
909
910 } else if (child->name() == "FadeOut") {
911
912 _fade_out->clear ();
913
914 bool is_default;
915 if ((child->get_property ("default", is_default) && is_default) || (prop = child->property ("steepness")) != 0) {
916 set_default_fade_out ();
917 } else {
918 XMLNode* grandchild = child->child ("AutomationList");
919 if (grandchild) {
920 _fade_out->set_state (*grandchild, version);
921 }
922 }
923
924 bool is_active;
925 if (child->get_property ("active", is_active)) {
926 set_fade_out_active (is_active);
927 }
928
929 } else if ( (child->name() == "InverseFadeIn") || (child->name() == "InvFadeIn") ) {
930 XMLNode* grandchild = child->child ("AutomationList");
931 if (grandchild) {
932 _inverse_fade_in->set_state (*grandchild, version);
933 }
934 } else if ( (child->name() == "InverseFadeOut") || (child->name() == "InvFadeOut") ) {
935 XMLNode* grandchild = child->child ("AutomationList");
936 if (grandchild) {
937 _inverse_fade_out->set_state (*grandchild, version);
938 }
939 }
940 }
941
942 _envelope->thaw ();
943 resume_property_changes ();
944
945 if (send) {
946 send_change (what_changed);
947 }
948
949 if (the_playlist) {
950 the_playlist->thaw ();
951 }
952
953 return 0;
954 }
955
956 int
set_state(const XMLNode & node,int version)957 AudioRegion::set_state (const XMLNode& node, int version)
958 {
959 PropertyChange what_changed;
960 return _set_state (node, version, what_changed, true);
961 }
962
963 void
fade_range(samplepos_t start,samplepos_t end)964 AudioRegion::fade_range (samplepos_t start, samplepos_t end)
965 {
966 samplepos_t s, e;
967
968 switch (coverage (start, end)) {
969 case Evoral::OverlapStart:
970 trim_front(start);
971 s = _position;
972 e = end;
973 set_fade_in (FadeConstantPower, e - s);
974 break;
975 case Evoral::OverlapEnd:
976 trim_end(end);
977 s = start;
978 e = _position + _length;
979 set_fade_out (FadeConstantPower, e - s);
980 break;
981 case Evoral::OverlapInternal:
982 /* needs addressing, perhaps. Difficult to do if we can't
983 * control one edge of the fade relative to the relevant edge
984 * of the region, which we cannot - fades are currently assumed
985 * to start/end at the start/end of the region
986 */
987 break;
988 default:
989 return;
990 }
991 }
992
993 void
set_fade_in_shape(FadeShape shape)994 AudioRegion::set_fade_in_shape (FadeShape shape)
995 {
996 set_fade_in (shape, (samplecnt_t) _fade_in->when(false));
997 }
998
999 void
set_fade_out_shape(FadeShape shape)1000 AudioRegion::set_fade_out_shape (FadeShape shape)
1001 {
1002 set_fade_out (shape, (samplecnt_t) _fade_out->when(false));
1003 }
1004
1005 void
set_fade_in(boost::shared_ptr<AutomationList> f)1006 AudioRegion::set_fade_in (boost::shared_ptr<AutomationList> f)
1007 {
1008 _fade_in->freeze ();
1009 *(_fade_in.val()) = *f;
1010 _fade_in->thaw ();
1011 _default_fade_in = false;
1012
1013 send_change (PropertyChange (Properties::fade_in));
1014 }
1015
1016 void
set_fade_in(FadeShape shape,samplecnt_t len)1017 AudioRegion::set_fade_in (FadeShape shape, samplecnt_t len)
1018 {
1019 const ARDOUR::ParameterDescriptor desc(FadeInAutomation);
1020 boost::shared_ptr<Evoral::ControlList> c1 (new Evoral::ControlList (FadeInAutomation, desc));
1021 boost::shared_ptr<Evoral::ControlList> c2 (new Evoral::ControlList (FadeInAutomation, desc));
1022 boost::shared_ptr<Evoral::ControlList> c3 (new Evoral::ControlList (FadeInAutomation, desc));
1023
1024 _fade_in->freeze ();
1025 _fade_in->clear ();
1026 _inverse_fade_in->clear ();
1027
1028 const int num_steps = 32;
1029
1030 switch (shape) {
1031 case FadeLinear:
1032 _fade_in->fast_simple_add (0.0, GAIN_COEFF_SMALL);
1033 _fade_in->fast_simple_add (len, GAIN_COEFF_UNITY);
1034 reverse_curve (_inverse_fade_in.val(), _fade_in.val());
1035 break;
1036
1037 case FadeFast:
1038 generate_db_fade (_fade_in.val(), len, num_steps, -60);
1039 reverse_curve (c1, _fade_in.val());
1040 _fade_in->copy_events (*c1);
1041 generate_inverse_power_curve (_inverse_fade_in.val(), _fade_in.val());
1042 break;
1043
1044 case FadeSlow:
1045 generate_db_fade (c1, len, num_steps, -1); // start off with a slow fade
1046 generate_db_fade (c2, len, num_steps, -80); // end with a fast fade
1047 merge_curves (_fade_in.val(), c1, c2);
1048 reverse_curve (c3, _fade_in.val());
1049 _fade_in->copy_events (*c3);
1050 generate_inverse_power_curve (_inverse_fade_in.val(), _fade_in.val());
1051 break;
1052
1053 case FadeConstantPower:
1054 _fade_in->fast_simple_add (0.0, GAIN_COEFF_SMALL);
1055 for (int i = 1; i < num_steps; ++i) {
1056 const float dist = i / (num_steps + 1.f);
1057 _fade_in->fast_simple_add (len * dist, sin (dist * M_PI / 2.0));
1058 }
1059 _fade_in->fast_simple_add (len, GAIN_COEFF_UNITY);
1060 reverse_curve (_inverse_fade_in.val(), _fade_in.val());
1061 break;
1062
1063 case FadeSymmetric:
1064 //start with a nearly linear cuve
1065 _fade_in->fast_simple_add (0, 1);
1066 _fade_in->fast_simple_add (0.5 * len, 0.6);
1067 //now generate a fade-out curve by successively applying a gain drop
1068 const double breakpoint = 0.7; //linear for first 70%
1069 for (int i = 2; i < 9; ++i) {
1070 const float coeff = (1.f - breakpoint) * powf (0.5, i);
1071 _fade_in->fast_simple_add (len * (breakpoint + ((GAIN_COEFF_UNITY - breakpoint) * (double)i / 9.0)), coeff);
1072 }
1073 _fade_in->fast_simple_add (len, GAIN_COEFF_SMALL);
1074 reverse_curve (c3, _fade_in.val());
1075 _fade_in->copy_events (*c3);
1076 reverse_curve (_inverse_fade_in.val(), _fade_in.val());
1077 break;
1078 }
1079
1080 _fade_in->set_interpolation(Evoral::ControlList::Curved);
1081 _inverse_fade_in->set_interpolation(Evoral::ControlList::Curved);
1082
1083 _default_fade_in = false;
1084 _fade_in->thaw ();
1085 send_change (PropertyChange (Properties::fade_in));
1086 }
1087
1088 void
set_fade_out(boost::shared_ptr<AutomationList> f)1089 AudioRegion::set_fade_out (boost::shared_ptr<AutomationList> f)
1090 {
1091 _fade_out->freeze ();
1092 *(_fade_out.val()) = *f;
1093 _fade_out->thaw ();
1094 _default_fade_out = false;
1095
1096 send_change (PropertyChange (Properties::fade_out));
1097 }
1098
1099 void
set_fade_out(FadeShape shape,samplecnt_t len)1100 AudioRegion::set_fade_out (FadeShape shape, samplecnt_t len)
1101 {
1102 const ARDOUR::ParameterDescriptor desc(FadeOutAutomation);
1103 boost::shared_ptr<Evoral::ControlList> c1 (new Evoral::ControlList (FadeOutAutomation, desc));
1104 boost::shared_ptr<Evoral::ControlList> c2 (new Evoral::ControlList (FadeOutAutomation, desc));
1105
1106 _fade_out->freeze ();
1107 _fade_out->clear ();
1108 _inverse_fade_out->clear ();
1109
1110 const int num_steps = 32;
1111
1112 switch (shape) {
1113 case FadeLinear:
1114 _fade_out->fast_simple_add (0.0, GAIN_COEFF_UNITY);
1115 _fade_out->fast_simple_add (len, GAIN_COEFF_SMALL);
1116 reverse_curve (_inverse_fade_out.val(), _fade_out.val());
1117 break;
1118
1119 case FadeFast:
1120 generate_db_fade (_fade_out.val(), len, num_steps, -60);
1121 generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val());
1122 break;
1123
1124 case FadeSlow:
1125 generate_db_fade (c1, len, num_steps, -1); //start off with a slow fade
1126 generate_db_fade (c2, len, num_steps, -80); //end with a fast fade
1127 merge_curves (_fade_out.val(), c1, c2);
1128 generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val());
1129 break;
1130
1131 case FadeConstantPower:
1132 //constant-power fades use a sin/cos relationship
1133 //the cutoff is abrupt but it has the benefit of being symmetrical
1134 _fade_out->fast_simple_add (0.0, GAIN_COEFF_UNITY);
1135 for (int i = 1; i < num_steps; ++i) {
1136 const float dist = i / (num_steps + 1.f);
1137 _fade_out->fast_simple_add (len * dist, cos (dist * M_PI / 2.0));
1138 }
1139 _fade_out->fast_simple_add (len, GAIN_COEFF_SMALL);
1140 reverse_curve (_inverse_fade_out.val(), _fade_out.val());
1141 break;
1142
1143 case FadeSymmetric:
1144 //start with a nearly linear cuve
1145 _fade_out->fast_simple_add (0, 1);
1146 _fade_out->fast_simple_add (0.5 * len, 0.6);
1147 //now generate a fade-out curve by successively applying a gain drop
1148 const double breakpoint = 0.7; //linear for first 70%
1149 for (int i = 2; i < 9; ++i) {
1150 const float coeff = (1.f - breakpoint) * powf (0.5, i);
1151 _fade_out->fast_simple_add (len * (breakpoint + ((GAIN_COEFF_UNITY - breakpoint) * (double)i / 9.0)), coeff);
1152 }
1153 _fade_out->fast_simple_add (len, GAIN_COEFF_SMALL);
1154 reverse_curve (_inverse_fade_out.val(), _fade_out.val());
1155 break;
1156 }
1157
1158 _fade_out->set_interpolation(Evoral::ControlList::Curved);
1159 _inverse_fade_out->set_interpolation(Evoral::ControlList::Curved);
1160
1161 _default_fade_out = false;
1162 _fade_out->thaw ();
1163 send_change (PropertyChange (Properties::fade_out));
1164 }
1165
1166 void
set_fade_in_length(samplecnt_t len)1167 AudioRegion::set_fade_in_length (samplecnt_t len)
1168 {
1169 if (len > _length) {
1170 len = _length - 1;
1171 }
1172
1173 if (len < 64) {
1174 len = 64;
1175 }
1176
1177 bool changed = _fade_in->extend_to (len);
1178
1179 if (changed) {
1180 if (_inverse_fade_in) {
1181 _inverse_fade_in->extend_to (len);
1182 }
1183
1184 _default_fade_in = false;
1185 send_change (PropertyChange (Properties::fade_in));
1186 }
1187 }
1188
1189 void
set_fade_out_length(samplecnt_t len)1190 AudioRegion::set_fade_out_length (samplecnt_t len)
1191 {
1192 if (len > _length) {
1193 len = _length - 1;
1194 }
1195
1196 if (len < 64) {
1197 len = 64;
1198 }
1199
1200 bool changed = _fade_out->extend_to (len);
1201
1202 if (changed) {
1203
1204 if (_inverse_fade_out) {
1205 _inverse_fade_out->extend_to (len);
1206 }
1207 _default_fade_out = false;
1208
1209 send_change (PropertyChange (Properties::fade_out));
1210 }
1211 }
1212
1213 void
set_fade_in_active(bool yn)1214 AudioRegion::set_fade_in_active (bool yn)
1215 {
1216 if (yn == _fade_in_active) {
1217 return;
1218 }
1219
1220 _fade_in_active = yn;
1221 send_change (PropertyChange (Properties::fade_in_active));
1222 }
1223
1224 void
set_fade_out_active(bool yn)1225 AudioRegion::set_fade_out_active (bool yn)
1226 {
1227 if (yn == _fade_out_active) {
1228 return;
1229 }
1230 _fade_out_active = yn;
1231 send_change (PropertyChange (Properties::fade_out_active));
1232 }
1233
1234 bool
fade_in_is_default() const1235 AudioRegion::fade_in_is_default () const
1236 {
1237 return _fade_in->size() == 2 && _fade_in->when(true) == 0 && _fade_in->when(false) == 64;
1238 }
1239
1240 bool
fade_out_is_default() const1241 AudioRegion::fade_out_is_default () const
1242 {
1243 return _fade_out->size() == 2 && _fade_out->when(true) == 0 && _fade_out->when(false) == 64;
1244 }
1245
1246 void
set_default_fade_in()1247 AudioRegion::set_default_fade_in ()
1248 {
1249 _fade_in_suspended = 0;
1250 set_fade_in (Config->get_default_fade_shape(), 64);
1251 }
1252
1253 void
set_default_fade_out()1254 AudioRegion::set_default_fade_out ()
1255 {
1256 _fade_out_suspended = 0;
1257 set_fade_out (Config->get_default_fade_shape(), 64);
1258 }
1259
1260 void
set_default_fades()1261 AudioRegion::set_default_fades ()
1262 {
1263 set_default_fade_in ();
1264 set_default_fade_out ();
1265 }
1266
1267 void
set_default_envelope()1268 AudioRegion::set_default_envelope ()
1269 {
1270 _envelope->freeze ();
1271 _envelope->clear ();
1272 _envelope->fast_simple_add (0, GAIN_COEFF_UNITY);
1273 _envelope->fast_simple_add (_length, GAIN_COEFF_UNITY);
1274 _envelope->thaw ();
1275 }
1276
1277 void
recompute_at_end()1278 AudioRegion::recompute_at_end ()
1279 {
1280 /* our length has changed. recompute a new final point by interpolating
1281 based on the the existing curve.
1282 */
1283
1284 _envelope->freeze ();
1285 _envelope->truncate_end (_length);
1286 _envelope->thaw ();
1287
1288 suspend_property_changes();
1289
1290 if (_left_of_split) {
1291 set_default_fade_out ();
1292 _left_of_split = false;
1293 } else if (_fade_out->when(false) > _length) {
1294 _fade_out->extend_to (_length);
1295 send_change (PropertyChange (Properties::fade_out));
1296 }
1297
1298 if (_fade_in->when(false) > _length) {
1299 _fade_in->extend_to (_length);
1300 send_change (PropertyChange (Properties::fade_in));
1301 }
1302
1303 resume_property_changes();
1304 }
1305
1306 void
recompute_at_start()1307 AudioRegion::recompute_at_start ()
1308 {
1309 /* as above, but the shift was from the front */
1310
1311 _envelope->truncate_start (_length);
1312
1313 suspend_property_changes();
1314
1315 if (_right_of_split) {
1316 set_default_fade_in ();
1317 _right_of_split = false;
1318 } else if (_fade_in->when(false) > _length) {
1319 _fade_in->extend_to (_length);
1320 send_change (PropertyChange (Properties::fade_in));
1321 }
1322
1323 if (_fade_out->when(false) > _length) {
1324 _fade_out->extend_to (_length);
1325 send_change (PropertyChange (Properties::fade_out));
1326 }
1327
1328 resume_property_changes();
1329 }
1330
1331 int
separate_by_channel(vector<boost::shared_ptr<Region>> & v) const1332 AudioRegion::separate_by_channel (vector<boost::shared_ptr<Region> >& v) const
1333 {
1334 SourceList srcs;
1335 string new_name;
1336 int n = 0;
1337
1338 if (_sources.size() < 2) {
1339 return 0;
1340 }
1341
1342 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1343 srcs.clear ();
1344 srcs.push_back (*i);
1345
1346 new_name = _name;
1347
1348 if (_sources.size() == 2) {
1349 if (n == 0) {
1350 new_name += "-L";
1351 } else {
1352 new_name += "-R";
1353 }
1354 } else {
1355 new_name += '-';
1356 new_name += ('0' + n + 1);
1357 }
1358
1359 /* create a copy with just one source. prevent if from being thought of as
1360 "whole file" even if it covers the entire source file(s).
1361 */
1362
1363 PropertyList plist;
1364
1365 plist.add (Properties::start, _start.val());
1366 plist.add (Properties::length, _length.val());
1367 plist.add (Properties::name, new_name);
1368 plist.add (Properties::layer, layer ());
1369 plist.add (Properties::whole_file, true);
1370
1371 v.push_back(RegionFactory::create (srcs, plist));
1372
1373 ++n;
1374 }
1375
1376 return 0;
1377 }
1378
1379 samplecnt_t
read_raw_internal(Sample * buf,samplepos_t pos,samplecnt_t cnt,int channel) const1380 AudioRegion::read_raw_internal (Sample* buf, samplepos_t pos, samplecnt_t cnt, int channel) const
1381 {
1382 return audio_source(channel)->read (buf, pos, cnt);
1383 }
1384
1385 void
set_scale_amplitude(gain_t g)1386 AudioRegion::set_scale_amplitude (gain_t g)
1387 {
1388 boost::shared_ptr<Playlist> pl (playlist());
1389
1390 _scale_amplitude = g;
1391
1392 send_change (PropertyChange (Properties::scale_amplitude));
1393 }
1394
1395 double
maximum_amplitude(Progress * p) const1396 AudioRegion::maximum_amplitude (Progress* p) const
1397 {
1398 samplepos_t fpos = _start;
1399 samplepos_t const fend = _start + _length;
1400 double maxamp = 0;
1401
1402 samplecnt_t const blocksize = 64 * 1024;
1403 Sample buf[blocksize];
1404
1405 while (fpos < fend) {
1406
1407 uint32_t n;
1408
1409 samplecnt_t const to_read = min (fend - fpos, blocksize);
1410
1411 for (n = 0; n < n_channels(); ++n) {
1412
1413 /* read it in */
1414
1415 if (read_raw_internal (buf, fpos, to_read, n) != to_read) {
1416 return 0;
1417 }
1418
1419 maxamp = compute_peak (buf, to_read, maxamp);
1420 }
1421
1422 fpos += to_read;
1423 if (p) {
1424 p->set_progress (float (fpos - _start) / _length);
1425 if (p->cancelled ()) {
1426 return -1;
1427 }
1428 }
1429 }
1430
1431 return maxamp;
1432 }
1433
1434 double
rms(Progress * p) const1435 AudioRegion::rms (Progress* p) const
1436 {
1437 samplepos_t fpos = _start;
1438 samplepos_t const fend = _start + _length;
1439 uint32_t const n_chan = n_channels ();
1440 double rms = 0;
1441
1442 samplecnt_t const blocksize = 64 * 1024;
1443 Sample buf[blocksize];
1444
1445 samplecnt_t total = 0;
1446
1447 if (n_chan == 0 || fend == fpos) {
1448 return 0;
1449 }
1450
1451 while (fpos < fend) {
1452 samplecnt_t const to_read = min (fend - fpos, blocksize);
1453 for (uint32_t c = 0; c < n_chan; ++c) {
1454 if (read_raw_internal (buf, fpos, to_read, c) != to_read) {
1455 return 0;
1456 }
1457 for (samplepos_t i = 0; i < to_read; ++i) {
1458 rms += buf[i] * buf[i];
1459 }
1460 }
1461 total += to_read;
1462 fpos += to_read;
1463 if (p) {
1464 p->set_progress (float (fpos - _start) / _length);
1465 if (p->cancelled ()) {
1466 return -1;
1467 }
1468 }
1469 }
1470 return sqrt (2. * rms / (double)(total * n_chan));
1471 }
1472
1473 /** Normalize using a given maximum amplitude and target, so that region
1474 * _scale_amplitude becomes target / max_amplitude.
1475 */
1476 void
normalize(float max_amplitude,float target_dB)1477 AudioRegion::normalize (float max_amplitude, float target_dB)
1478 {
1479 gain_t target = dB_to_coefficient (target_dB);
1480
1481 if (target == GAIN_COEFF_UNITY) {
1482 /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
1483 that we may have clipped.
1484 */
1485 target -= FLT_EPSILON;
1486 }
1487
1488 if (max_amplitude < GAIN_COEFF_SMALL) {
1489 /* don't even try */
1490 return;
1491 }
1492
1493 if (max_amplitude == target) {
1494 /* we can't do anything useful */
1495 return;
1496 }
1497
1498 set_scale_amplitude (target / max_amplitude);
1499 }
1500
1501 void
fade_in_changed()1502 AudioRegion::fade_in_changed ()
1503 {
1504 send_change (PropertyChange (Properties::fade_in));
1505 }
1506
1507 void
fade_out_changed()1508 AudioRegion::fade_out_changed ()
1509 {
1510 send_change (PropertyChange (Properties::fade_out));
1511 }
1512
1513 void
envelope_changed()1514 AudioRegion::envelope_changed ()
1515 {
1516 send_change (PropertyChange (Properties::envelope));
1517 }
1518
1519 void
suspend_fade_in()1520 AudioRegion::suspend_fade_in ()
1521 {
1522 if (++_fade_in_suspended == 1) {
1523 if (fade_in_is_default()) {
1524 set_fade_in_active (false);
1525 }
1526 }
1527 }
1528
1529 void
resume_fade_in()1530 AudioRegion::resume_fade_in ()
1531 {
1532 if (--_fade_in_suspended == 0 && _fade_in_suspended) {
1533 set_fade_in_active (true);
1534 }
1535 }
1536
1537 void
suspend_fade_out()1538 AudioRegion::suspend_fade_out ()
1539 {
1540 if (++_fade_out_suspended == 1) {
1541 if (fade_out_is_default()) {
1542 set_fade_out_active (false);
1543 }
1544 }
1545 }
1546
1547 void
resume_fade_out()1548 AudioRegion::resume_fade_out ()
1549 {
1550 if (--_fade_out_suspended == 0 &&_fade_out_suspended) {
1551 set_fade_out_active (true);
1552 }
1553 }
1554
1555 bool
speed_mismatch(float sr) const1556 AudioRegion::speed_mismatch (float sr) const
1557 {
1558 if (_sources.empty()) {
1559 /* impossible, but ... */
1560 return false;
1561 }
1562
1563 float fsr = audio_source()->sample_rate();
1564
1565 return fsr != sr;
1566 }
1567
1568 void
source_offset_changed()1569 AudioRegion::source_offset_changed ()
1570 {
1571 /* XXX this fixes a crash that should not occur. It does occur
1572 becauses regions are not being deleted when a session
1573 is unloaded. That bug must be fixed.
1574 */
1575
1576 if (_sources.empty()) {
1577 return;
1578 }
1579
1580 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(_sources.front());
1581 }
1582
1583 boost::shared_ptr<AudioSource>
audio_source(uint32_t n) const1584 AudioRegion::audio_source (uint32_t n) const
1585 {
1586 // Guaranteed to succeed (use a static cast for speed?)
1587 return boost::dynamic_pointer_cast<AudioSource>(source(n));
1588 }
1589
1590 void
clear_transients()1591 AudioRegion::clear_transients () // yet unused
1592 {
1593 _user_transients.clear ();
1594 _valid_transients = false;
1595 send_change (PropertyChange (Properties::valid_transients));
1596 }
1597
1598 void
add_transient(samplepos_t where)1599 AudioRegion::add_transient (samplepos_t where)
1600 {
1601 if (where < first_sample () || where >= last_sample ()) {
1602 return;
1603 }
1604 where -= _position;
1605
1606 if (!_valid_transients) {
1607 _transient_user_start = _start;
1608 _valid_transients = true;
1609 }
1610 sampleoffset_t offset = _transient_user_start - _start;
1611
1612 if (where < offset) {
1613 if (offset <= 0) {
1614 return;
1615 }
1616 // region start changed (extend to front), shift points and offset
1617 for (AnalysisFeatureList::iterator x = _transients.begin(); x != _transients.end(); ++x) {
1618 (*x) += offset;
1619 }
1620 _transient_user_start -= offset;
1621 offset = 0;
1622 }
1623
1624 const samplepos_t p = where - offset;
1625 _user_transients.push_back(p);
1626 send_change (PropertyChange (Properties::valid_transients));
1627 }
1628
1629 void
update_transient(samplepos_t old_position,samplepos_t new_position)1630 AudioRegion::update_transient (samplepos_t old_position, samplepos_t new_position)
1631 {
1632 bool changed = false;
1633 if (!_onsets.empty ()) {
1634 const samplepos_t p = old_position - _position;
1635 AnalysisFeatureList::iterator x = std::find (_onsets.begin (), _onsets.end (), p);
1636 if (x != _transients.end ()) {
1637 (*x) = new_position - _position;
1638 changed = true;
1639 }
1640 }
1641
1642 if (_valid_transients) {
1643 const sampleoffset_t offset = _position + _transient_user_start - _start;
1644 const samplepos_t p = old_position - offset;
1645 AnalysisFeatureList::iterator x = std::find (_user_transients.begin (), _user_transients.end (), p);
1646 if (x != _transients.end ()) {
1647 (*x) = new_position - offset;
1648 changed = true;
1649 }
1650 }
1651
1652 if (changed) {
1653 send_change (PropertyChange (Properties::valid_transients));
1654 }
1655 }
1656
1657 void
remove_transient(samplepos_t where)1658 AudioRegion::remove_transient (samplepos_t where)
1659 {
1660 bool changed = false;
1661 if (!_onsets.empty ()) {
1662 const samplepos_t p = where - _position;
1663 AnalysisFeatureList::iterator i = std::find (_onsets.begin (), _onsets.end (), p);
1664 if (i != _onsets.end ()) {
1665 _onsets.erase (i);
1666 changed = true;
1667 }
1668 }
1669
1670 if (_valid_transients) {
1671 const samplepos_t p = where - (_position + _transient_user_start - _start);
1672 AnalysisFeatureList::iterator i = std::find (_user_transients.begin (), _user_transients.end (), p);
1673 if (i != _user_transients.end ()) {
1674 _user_transients.erase (i);
1675 changed = true;
1676 }
1677 }
1678
1679 if (changed) {
1680 send_change (PropertyChange (Properties::valid_transients));
1681 }
1682 }
1683
1684 void
set_onsets(AnalysisFeatureList & results)1685 AudioRegion::set_onsets (AnalysisFeatureList& results)
1686 {
1687 _onsets.clear();
1688 _onsets = results;
1689 send_change (PropertyChange (Properties::valid_transients));
1690 }
1691
1692 void
build_transients()1693 AudioRegion::build_transients ()
1694 {
1695 _transients.clear ();
1696 _transient_analysis_start = _transient_analysis_end = 0;
1697
1698 boost::shared_ptr<Playlist> pl = playlist();
1699
1700 if (!pl) {
1701 return;
1702 }
1703
1704 /* check analyzed sources first */
1705 SourceList::iterator s;
1706 for (s = _sources.begin() ; s != _sources.end(); ++s) {
1707 if (!(*s)->has_been_analysed()) {
1708 #ifndef NDEBUG
1709 cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
1710 #endif
1711 break;
1712 }
1713 }
1714
1715 if (s == _sources.end()) {
1716 /* all sources are analyzed, merge data from each one */
1717 for (s = _sources.begin() ; s != _sources.end(); ++s) {
1718
1719 /* find the set of transients within the bounds of this region */
1720 AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(),
1721 (*s)->transients.end(),
1722 _start);
1723
1724 AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
1725 (*s)->transients.end(),
1726 _start + _length);
1727
1728 /* and add them */
1729 _transients.insert (_transients.end(), low, high);
1730 }
1731
1732 TransientDetector::cleanup_transients (_transients, pl->session().sample_rate(), 3.0);
1733
1734 /* translate all transients to current position */
1735 for (AnalysisFeatureList::iterator x = _transients.begin(); x != _transients.end(); ++x) {
1736 (*x) -= _start;
1737 }
1738
1739 _transient_analysis_start = _start;
1740 _transient_analysis_end = _start + _length;
1741 return;
1742 }
1743
1744 /* no existing/complete transient info */
1745
1746 static bool analyse_dialog_shown = false; /* global per instance of Ardour */
1747
1748 if (!Config->get_auto_analyse_audio()) {
1749 if (!analyse_dialog_shown) {
1750 pl->session().Dialog (string_compose (_("\
1751 You have requested an operation that requires audio analysis.\n\n\
1752 You currently have \"auto-analyse-audio\" disabled, which means \
1753 that transient data must be generated every time it is required.\n\n\
1754 If you are doing work that will require transient data on a \
1755 regular basis, you should probably enable \"auto-analyse-audio\" \
1756 in Preferences > Audio > Regions, then quit %1 and restart.\n\n\
1757 This dialog will not display again. But you may notice a slight delay \
1758 in this and future transient-detection operations.\n\
1759 "), PROGRAM_NAME));
1760 analyse_dialog_shown = true;
1761 }
1762 }
1763
1764 try {
1765 TransientDetector t (pl->session().sample_rate());
1766 for (uint32_t i = 0; i < n_channels(); ++i) {
1767
1768 AnalysisFeatureList these_results;
1769
1770 t.reset ();
1771
1772 /* this produces analysis result relative to current position
1773 * ::read() sample 0 is at _position */
1774 if (t.run ("", this, i, these_results)) {
1775 return;
1776 }
1777
1778 /* merge */
1779 _transients.insert (_transients.end(), these_results.begin(), these_results.end());
1780 }
1781 } catch (...) {
1782 error << string_compose(_("Transient Analysis failed for %1."), _("Audio Region")) << endmsg;
1783 return;
1784 }
1785
1786 TransientDetector::cleanup_transients (_transients, pl->session().sample_rate(), 3.0);
1787 _transient_analysis_start = _start;
1788 _transient_analysis_end = _start + _length;
1789 }
1790
1791 /* Transient analysis uses ::read() which is relative to _start,
1792 * at the time of analysis and spans _length samples.
1793 *
1794 * This is true for RhythmFerret::run_analysis and the
1795 * TransientDetector here.
1796 *
1797 * We store _start and length in _transient_analysis_start,
1798 * _transient_analysis_end in case the region is trimmed or split after analysis.
1799 *
1800 * Various methods (most notably Playlist::find_next_transient and
1801 * RhythmFerret::do_split_action) span multiple regions and *merge/combine*
1802 * Analysis results.
1803 * We therefore need to translate the analysis timestamps to absolute session-time
1804 * and include the _position of the region.
1805 *
1806 * Note: we should special case the AudioRegionView. The region-view itself
1807 * is located at _position (currently ARV subtracts _position again)
1808 */
1809 void
get_transients(AnalysisFeatureList & results)1810 AudioRegion::get_transients (AnalysisFeatureList& results)
1811 {
1812 boost::shared_ptr<Playlist> pl = playlist();
1813 if (!playlist ()) {
1814 return;
1815 }
1816
1817 Region::merge_features (results, _user_transients, _position + _transient_user_start - _start);
1818
1819 if (!_onsets.empty ()) {
1820 // onsets are invalidated when start or length changes
1821 merge_features (results, _onsets, _position);
1822 return;
1823 }
1824
1825 if ((_transient_analysis_start == _transient_analysis_end)
1826 || _transient_analysis_start > _start
1827 || _transient_analysis_end < _start + _length) {
1828 build_transients ();
1829 }
1830
1831 merge_features (results, _transients, _position + _transient_analysis_start - _start);
1832 }
1833
1834 /** Find areas of `silence' within a region.
1835 *
1836 * @param threshold Threshold below which signal is considered silence (as a sample value)
1837 * @param min_length Minimum length of silent period to be reported.
1838 * @return Silent intervals, measured relative to the region start in the source
1839 */
1840
1841 AudioIntervalResult
find_silence(Sample threshold,samplecnt_t min_length,samplecnt_t fade_length,InterThreadInfo & itt) const1842 AudioRegion::find_silence (Sample threshold, samplecnt_t min_length, samplecnt_t fade_length, InterThreadInfo& itt) const
1843 {
1844 samplecnt_t const block_size = 64 * 1024;
1845 boost::scoped_array<Sample> loudest (new Sample[block_size]);
1846 boost::scoped_array<Sample> buf (new Sample[block_size]);
1847
1848 assert (fade_length >= 0);
1849 assert (min_length > 0);
1850
1851 samplepos_t pos = _start;
1852 samplepos_t const end = _start + _length;
1853
1854 AudioIntervalResult silent_periods;
1855
1856 bool in_silence = true;
1857 sampleoffset_t silence_start = _start;
1858
1859 while (pos < end && !itt.cancel) {
1860
1861 samplecnt_t cur_samples = 0;
1862 samplecnt_t const to_read = min (end - pos, block_size);
1863 /* fill `loudest' with the loudest absolute sample at each instant, across all channels */
1864 memset (loudest.get(), 0, sizeof (Sample) * block_size);
1865
1866 for (uint32_t n = 0; n < n_channels(); ++n) {
1867
1868 cur_samples = read_raw_internal (buf.get(), pos, to_read, n);
1869 for (samplecnt_t i = 0; i < cur_samples; ++i) {
1870 loudest[i] = max (loudest[i], abs (buf[i]));
1871 }
1872 }
1873
1874 /* now look for silence */
1875 for (samplecnt_t i = 0; i < cur_samples; ++i) {
1876 bool const silence = abs (loudest[i]) < threshold;
1877 if (silence && !in_silence) {
1878 /* non-silence to silence */
1879 in_silence = true;
1880 silence_start = pos + i + fade_length;
1881 } else if (!silence && in_silence) {
1882 /* silence to non-silence */
1883 in_silence = false;
1884 sampleoffset_t silence_end = pos + i - 1 - fade_length;
1885
1886 if (silence_end - silence_start >= min_length) {
1887 silent_periods.push_back (std::make_pair (silence_start, silence_end));
1888 }
1889 }
1890 }
1891
1892 pos += cur_samples;
1893 itt.progress = (end - pos) / (double)_length;
1894
1895 if (cur_samples == 0) {
1896 assert (pos >= end);
1897 break;
1898 }
1899 }
1900
1901 if (in_silence && !itt.cancel) {
1902 /* last block was silent, so finish off the last period */
1903 if (end - 1 - silence_start >= min_length + fade_length) {
1904 silent_periods.push_back (std::make_pair (silence_start, end - 1));
1905 }
1906 }
1907
1908 itt.done = true;
1909
1910 return silent_periods;
1911 }
1912
1913 Evoral::Range<samplepos_t>
body_range() const1914 AudioRegion::body_range () const
1915 {
1916 return Evoral::Range<samplepos_t> (first_sample() + _fade_in->when(false) + 1, last_sample() - _fade_out->when(false));
1917 }
1918
1919 boost::shared_ptr<Region>
get_single_other_xfade_region(bool start) const1920 AudioRegion::get_single_other_xfade_region (bool start) const
1921 {
1922 boost::shared_ptr<Playlist> pl (playlist());
1923
1924 if (!pl) {
1925 /* not currently in a playlist - xfade length is unbounded
1926 (and irrelevant)
1927 */
1928 return boost::shared_ptr<AudioRegion> ();
1929 }
1930
1931 boost::shared_ptr<RegionList> rl;
1932
1933 if (start) {
1934 rl = pl->regions_at (position());
1935 } else {
1936 rl = pl->regions_at (last_sample());
1937 }
1938
1939 RegionList::iterator i;
1940 boost::shared_ptr<Region> other;
1941 uint32_t n = 0;
1942
1943 /* count and find the other region in a single pass through the list */
1944
1945 for (i = rl->begin(); i != rl->end(); ++i) {
1946 if ((*i).get() != this) {
1947 other = *i;
1948 }
1949 ++n;
1950 }
1951
1952 if (n != 2) {
1953 /* zero or multiple regions stacked here - don't care about xfades */
1954 return boost::shared_ptr<AudioRegion> ();
1955 }
1956
1957 return other;
1958 }
1959
1960 samplecnt_t
verify_xfade_bounds(samplecnt_t len,bool start)1961 AudioRegion::verify_xfade_bounds (samplecnt_t len, bool start)
1962 {
1963 /* this is called from a UI to check on whether a new proposed
1964 length for an xfade is legal or not. it returns the legal
1965 length corresponding to @a len which may be shorter than or
1966 equal to @a len itself.
1967 */
1968
1969 boost::shared_ptr<Region> other = get_single_other_xfade_region (start);
1970 samplecnt_t maxlen;
1971
1972 if (!other) {
1973 /* zero or > 2 regions here, don't care about len, but
1974 it can't be longer than the region itself.
1975 */
1976 return min (length(), len);
1977 }
1978
1979 /* we overlap a single region. clamp the length of an xfade to
1980 the maximum possible duration of the overlap (if the other
1981 region were trimmed appropriately).
1982 */
1983
1984 if (start) {
1985 maxlen = other->latest_possible_sample() - position();
1986 } else {
1987 maxlen = last_sample() - other->earliest_possible_position();
1988 }
1989
1990 return min (length(), min (maxlen, len));
1991
1992 }
1993
1994