69f3cb32c1d577954b1c87a3a02a75c97eb55660
[ardour.git] / libs / ardour / crossfade.cc
1 /*
2     Copyright (C) 2003-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 #include <sigc++/bind.h>
22
23 #include <pbd/stacktrace.h>
24
25 #include <ardour/types.h>
26 #include <ardour/crossfade.h>
27 #include <ardour/crossfade_compare.h>
28 #include <ardour/audioregion.h>
29 #include <ardour/playlist.h>
30 #include <ardour/utils.h>
31 #include <ardour/session.h>
32
33 #include "i18n.h"
34 #include <locale.h>
35
36 using namespace std;
37 using namespace ARDOUR;
38 using namespace PBD;
39
40 nframes_t Crossfade::_short_xfade_length = 0;
41 Change Crossfade::ActiveChanged = new_change();
42 Change Crossfade::FollowOverlapChanged = new_change();
43
44 /* XXX if and when we ever implement parallel processing of the process()
45    callback, these will need to be handled on a per-thread basis.
46 */
47
48 Sample* Crossfade::crossfade_buffer_out = 0;
49 Sample* Crossfade::crossfade_buffer_in = 0;
50
51 void
52 Crossfade::set_buffer_size (nframes_t sz)
53 {
54         if (crossfade_buffer_out) {
55                 delete [] crossfade_buffer_out;
56                 crossfade_buffer_out = 0;
57         }
58
59         if (crossfade_buffer_in) {
60                 delete [] crossfade_buffer_in;
61                 crossfade_buffer_in = 0;
62         }
63
64         if (sz) {
65                 crossfade_buffer_out = new Sample[sz];
66                 crossfade_buffer_in = new Sample[sz];
67         }
68 }
69
70 bool
71 Crossfade::operator== (const Crossfade& other)
72 {
73         return (_in == other._in) && (_out == other._out);
74 }
75
76 Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<AudioRegion> out, 
77                       nframes_t length,
78                       nframes_t position,
79                       AnchorPoint ap)
80         : _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
81           _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
82 {
83         _in = in;
84         _out = out;
85         
86         _length = length;
87         _position = position;
88         _anchor_point = ap;
89
90         _follow_overlap = false;
91
92         cerr << "A: follow overlap = " << _follow_overlap << endl;
93
94         _active = Config->get_xfades_active ();
95         _fixed = true;
96                 
97         initialize ();
98 }
99
100 Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act)
101         : _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
102           _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
103 {
104         _in_update = false;
105         _fixed = false;
106
107         if (compute (a, b, model)) {
108                 throw failed_constructor();
109         }
110
111         _active = act;
112
113         initialize ();
114
115         cerr << "K: follow overlap = " << _follow_overlap << endl;
116 }
117
118 Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
119         :  _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
120            _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
121 {
122         boost::shared_ptr<Region> r;
123         XMLProperty* prop;
124         LocaleGuard lg (X_("POSIX"));
125
126         /* we have to find the in/out regions before we can do anything else */
127
128         if ((prop = node.property ("in")) == 0) {
129                 error << _("Crossfade: no \"in\" region in state") << endmsg;
130                 throw failed_constructor();
131         }
132         
133         PBD::ID id (prop->value());
134
135         if ((r = playlist.find_region (id)) == 0) {
136                 error << string_compose (_("Crossfade: no \"in\" region %1 found in playlist %2"), id, playlist.name())
137                       << endmsg;
138                 throw failed_constructor();
139         }
140         
141         if ((_in = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
142                 throw failed_constructor();
143         }
144
145         if ((prop = node.property ("out")) == 0) {
146                 error << _("Crossfade: no \"out\" region in state") << endmsg;
147                 throw failed_constructor();
148         }
149
150         PBD::ID id2 (prop->value());
151
152         if ((r = playlist.find_region (id2)) == 0) {
153                 error << string_compose (_("Crossfade: no \"out\" region %1 found in playlist %2"), id2, playlist.name())
154                       << endmsg;
155                 throw failed_constructor();
156         }
157         
158         if ((_out = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
159                 throw failed_constructor();
160         }
161
162         _length = 0;
163         _active = Config->get_xfades_active();
164
165         initialize();
166         
167         if (set_state (node)) {
168                 throw failed_constructor();
169         }
170
171         cerr << "D: follow overlap = " << _follow_overlap << endl;
172
173 }
174
175 Crossfade::Crossfade (const Crossfade &orig, boost::shared_ptr<AudioRegion> newin, boost::shared_ptr<AudioRegion> newout)
176         : _fade_in(orig._fade_in),
177           _fade_out(orig._fade_out)
178 {
179         _active           = orig._active;
180         _in_update        = orig._in_update;
181         _length           = orig._length;
182         _position         = orig._position;
183         _anchor_point     = orig._anchor_point;
184         _follow_overlap   = orig._follow_overlap;
185         _fixed            = orig._fixed;
186         
187         _in = newin;
188         _out = newout;
189
190         // copied from Crossfade::initialize()
191         _in_update = false;
192         
193         _out->suspend_fade_out ();
194         _in->suspend_fade_in ();
195
196         overlap_type = _in->coverage (_out->position(), _out->last_frame());
197
198         // Let's make sure the fade isn't too long
199         set_length(_length);
200
201         cerr << "B: follow overlap = " << _follow_overlap << endl;
202
203 }
204
205
206 Crossfade::~Crossfade ()
207 {
208         cerr << this << " Crossfade deleted\n";
209         notify_callbacks ();
210 }
211
212 void
213 Crossfade::initialize ()
214 {
215         _in_update = false;
216         
217         _out->suspend_fade_out ();
218         _in->suspend_fade_in ();
219
220         _fade_out.freeze ();
221         _fade_out.clear ();
222         _fade_out.add (0.0, 1.0);
223         _fade_out.add ((_length * 0.1), 0.99);
224         _fade_out.add ((_length * 0.2), 0.97);
225         _fade_out.add ((_length * 0.8), 0.03);
226         _fade_out.add ((_length * 0.9), 0.01);
227         _fade_out.add (_length, 0.0);
228         _fade_out.thaw ();
229         
230         _fade_in.freeze ();
231         _fade_in.clear ();
232         _fade_in.add (0.0, 0.0);
233         _fade_in.add ((_length * 0.1),  0.01);
234         _fade_in.add ((_length * 0.2),  0.03);
235         _fade_in.add ((_length * 0.8),  0.97);
236         _fade_in.add ((_length * 0.9),  0.99);
237         _fade_in.add (_length, 1.0);
238         _fade_in.thaw ();
239
240         _in->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed));
241         _out->StateChanged.connect (sigc::mem_fun (*this, &Crossfade::member_changed));
242
243         overlap_type = _in->coverage (_out->position(), _out->last_frame());
244 }       
245
246 int
247 Crossfade::compute (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model)
248 {
249         boost::shared_ptr<AudioRegion> top;
250         boost::shared_ptr<AudioRegion> bottom;
251         nframes_t short_xfade_length;
252
253         short_xfade_length = _short_xfade_length; 
254
255         if (a->layer() < b->layer()) {
256                 top = b;
257                 bottom = a;
258         } else {
259                 top = a;
260                 bottom = b;
261         }
262         
263         /* first check for matching ends */
264         
265         if (top->first_frame() == bottom->first_frame()) {
266
267                 /* Both regions start at the same point */
268                 
269                 if (top->last_frame() < bottom->last_frame()) {
270                         
271                         /* top ends before bottom, so put an xfade
272                            in at the end of top.
273                         */
274                         
275                         /* [-------- top ---------- ]
276                          * {====== bottom =====================}
277                          */
278
279                         _in = bottom;
280                         _out = top;
281
282                         if (top->last_frame() < short_xfade_length) {
283                                 _position = 0;
284                         } else {
285                                 _position = top->last_frame() - short_xfade_length;
286                         }
287
288                         _length = min (short_xfade_length, top->length());
289                         _follow_overlap = false;
290                         _anchor_point = EndOfIn;
291                         _active = true;
292                         _fixed = true;
293
294                 } else {
295                         /* top ends after (or same time) as bottom - no xfade
296                          */
297                         
298                         /* [-------- top ------------------------ ]
299                          * {====== bottom =====================}
300                          */
301
302                         throw NoCrossfadeHere();
303                 }
304                 
305         } else if (top->last_frame() == bottom->last_frame()) {
306                 
307                 /* Both regions end at the same point */
308                 
309                 if (top->first_frame() > bottom->first_frame()) {
310                         
311                         /* top starts after bottom, put an xfade in at the
312                            start of top
313                         */
314                         
315                         /*            [-------- top ---------- ]
316                          * {====== bottom =====================}
317                          */
318
319                         _in = top;
320                         _out = bottom;
321                         _position = top->first_frame();
322                         _length = min (short_xfade_length, top->length());
323                         _follow_overlap = false;
324                         _anchor_point = StartOfIn;
325                         _active = true;
326                         _fixed = true;
327                         
328                 } else {
329                         /* top starts before bottom - no xfade
330                          */
331
332                         /* [-------- top ------------------------ ]
333                          *    {====== bottom =====================}
334                          */
335
336                         throw NoCrossfadeHere();
337                 }
338
339         } else {
340         
341                 /* OK, time to do more regular overlapping */
342
343                 OverlapType ot = top->coverage (bottom->first_frame(), bottom->last_frame());
344
345                 switch (ot) {
346                 case OverlapNone:
347                         /* should be NOTREACHED as a precondition of creating
348                            a new crossfade, but we need to handle it here.
349                         */
350                         throw NoCrossfadeHere();
351                         break;
352                         
353                 case OverlapInternal:
354                 case OverlapExternal:
355                         /* should be NOTREACHED because of tests above */
356                         throw NoCrossfadeHere();
357                         break;
358                         
359                 case OverlapEnd: /* top covers start of bottom but ends within it */
360
361                         /* [---- top ------------------------] 
362                          *                { ==== bottom ============ } 
363                          */ 
364
365                         _in = bottom;
366                         _out = top;
367                         _anchor_point = StartOfIn;
368
369                         if (model == FullCrossfade) {
370                                 _position = bottom->first_frame(); // "{"
371                                 _length = _out->first_frame() + _out->length() - _in->first_frame();
372                                 /* leave active alone */
373                                 _follow_overlap = true;
374                         } else {
375                                 _length = min (short_xfade_length, top->length());
376                                 _position = top->last_frame() - _length;  // "]" - length 
377                                 _active = true;
378                                 _follow_overlap = false;
379                                 
380                         }
381                         break;
382                         
383                 case OverlapStart:   /* top starts within bottom but covers bottom's end */
384
385                         /*                   { ==== top ============ } 
386                          *   [---- bottom -------------------] 
387                          */
388
389                         _in = top;
390                         _out = bottom;
391                         _position = top->first_frame();
392                         _anchor_point = StartOfIn;
393
394                         if (model == FullCrossfade) {
395                                 _length = _out->first_frame() + _out->length() - _in->first_frame();
396                                 /* leave active alone */
397                                 _follow_overlap = true;
398                         } else {
399                                 _length = min (short_xfade_length, top->length());
400                                 _active = true;
401                                 _follow_overlap = false;
402                                 
403                         }
404                         
405                         break;
406                 }
407         }
408         
409         return 0;
410 }
411
412 nframes_t 
413 Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, 
414                     float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n,
415                     nframes_t read_frames, nframes_t skip_frames)
416 {
417         nframes_t offset;
418         nframes_t to_write;
419
420         if (!_active) {
421                 return 0;
422         }
423
424         if (start < _position) {
425
426                 /* handle an initial section of the read area that we do not
427                    cover.
428                 */
429
430                 offset = _position - start;
431
432                 if (offset < cnt) {
433                         cnt -= offset;
434                 } else {
435                         return 0;
436                 }
437                 
438                 start = _position;
439                 buf += offset;
440                 to_write = min (_length, cnt);
441
442         } else {
443                 
444                 to_write = min (_length - (start - _position), cnt);
445                 
446         }
447
448         offset = start - _position;
449
450         _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames);
451         _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames);
452
453         float* fiv = new float[to_write];
454         float* fov = new float[to_write];
455
456         _fade_in.get_vector (offset, offset+to_write, fiv, to_write);
457         _fade_out.get_vector (offset, offset+to_write, fov, to_write);
458
459         /* note: although we have not explicitly taken into account the return values
460            from _out->read_at() or _in->read_at(), the length() function does this
461            implicitly. why? because it computes a value based on the in+out regions'
462            position and length, and so we know precisely how much data they could return. 
463         */
464
465         for (nframes_t n = 0; n < to_write; ++n) {
466                 buf[n] = (crossfade_buffer_out[n] * fov[n]) + (crossfade_buffer_in[n] * fiv[n]);
467         }
468
469         delete [] fov;
470         delete [] fiv;
471
472         return to_write;
473 }       
474
475 OverlapType 
476 Crossfade::coverage (nframes_t start, nframes_t end) const
477 {
478         nframes_t my_end = _position + _length;
479
480         if ((start >= _position) && (end <= my_end)) {
481                 return OverlapInternal;
482         }
483         if ((end >= _position) && (end <= my_end)) {
484                 return OverlapStart;
485         }
486         if ((start >= _position) && (start <= my_end)) {
487                 return OverlapEnd;
488         }
489         if ((_position >= start) && (_position <= end) && (my_end <= end)) {
490                 return OverlapExternal;
491         }
492         return OverlapNone;
493 }
494
495 void
496 Crossfade::set_active (bool yn)
497 {
498         if (_active != yn) {
499                 _active = yn;
500                 StateChanged (ActiveChanged);
501         }
502 }
503
504 bool
505 Crossfade::refresh ()
506 {
507         /* crossfades must be between non-muted regions */
508         
509         if (_out->muted() || _in->muted()) {
510                 Invalidated (shared_from_this());
511                 return false;
512         }
513
514 //      if (_in->layer() < _out->layer()) {
515 //              cerr << this << " layer change, invalidated, in on " << _in->layer() << " out on " << _out->layer() << endl;
516 //              Invalidated (shared_from_this());
517 //              return false;
518 //      }
519
520         /* overlap type must be Start, End or External */
521
522         OverlapType ot;
523         
524         ot = _in->coverage (_out->first_frame(), _out->last_frame());
525
526         /* overlap type must not have altered */
527         
528         if (ot != overlap_type) {
529                 cerr << this << " Invalid B\n";
530                 Invalidated (shared_from_this());
531                 return false;
532         } 
533
534         /* time to update */
535
536         return update (false);
537 }
538
539 bool
540 Crossfade::update (bool force)
541 {
542         nframes_t newlen;
543
544         cerr << this << " update, " << _in->name() << " + " << _out->name() << " length = " << _length << endl;
545
546         if (_follow_overlap) {
547                 newlen = _out->first_frame() + _out->length() - _in->first_frame();
548                 cerr << "\tmodify length\n";
549         } else {
550                 newlen = _length;
551         }
552
553         if (newlen == 0) {
554                 cerr << this << " Invalid C\n";
555                 Invalidated (shared_from_this());
556                 return false;
557         }
558
559         _in_update = true;
560
561         if (force || (_follow_overlap && newlen != _length) || (_length > newlen)) {
562
563                 double factor =  newlen / (double) _length;
564                 
565                 _fade_out.x_scale (factor);
566                 _fade_in.x_scale (factor);
567                 
568                 _length = newlen;
569
570         } 
571
572         cerr << "\tFover = " << _follow_overlap << endl;
573
574         switch (_anchor_point) {
575         case StartOfIn:
576                 _position = _in->first_frame();
577                 break;
578
579         case EndOfIn:
580                 _position = _in->last_frame() - _length;
581                 break;
582
583         case EndOfOut:
584                 _position = _out->last_frame() - _length;
585         }
586
587         /* UI's may need to know that the overlap changed even 
588            though the xfade length did not.
589         */
590         
591         StateChanged (BoundsChanged); /* EMIT SIGNAL */
592
593         _in_update = false;
594
595         return true;
596 }
597
598 void
599 Crossfade::member_changed (Change what_changed)
600 {
601         Change what_we_care_about = Change (Region::MuteChanged|
602                                             Region::LayerChanged|
603                                             BoundsChanged);
604
605         if (what_changed & what_we_care_about) {
606                 refresh ();
607         }
608 }
609
610 XMLNode&
611 Crossfade::get_state () 
612 {
613         XMLNode* node = new XMLNode (X_("Crossfade"));
614         XMLNode* child;
615         char buf[64];
616         LocaleGuard lg (X_("POSIX"));
617
618         _out->id().print (buf, sizeof (buf));
619         node->add_property ("out", buf);
620         _in->id().print (buf, sizeof (buf));
621         node->add_property ("in", buf);
622         node->add_property ("active", (_active ? "yes" : "no"));
623         node->add_property ("follow-overlap", (_follow_overlap ? "yes" : "no"));
624         node->add_property ("fixed", (_fixed ? "yes" : "no"));
625         snprintf (buf, sizeof(buf), "%" PRIu32, _length);
626         node->add_property ("length", buf);
627         snprintf (buf, sizeof(buf), "%" PRIu32, (uint32_t) _anchor_point);
628         node->add_property ("anchor-point", buf);
629         snprintf (buf, sizeof(buf), "%" PRIu32, (uint32_t) _position);
630         node->add_property ("position", buf);
631
632         child = node->add_child ("FadeIn");
633
634         for (AutomationList::iterator ii = _fade_in.begin(); ii != _fade_in.end(); ++ii) {
635                 XMLNode* pnode;
636
637                 pnode = new XMLNode ("point");
638
639                 snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*ii)->when));
640                 pnode->add_property ("x", buf);
641                 snprintf (buf, sizeof (buf), "%.12g", (*ii)->value);
642                 pnode->add_property ("y", buf);
643                 child->add_child_nocopy (*pnode);
644         }
645
646         child = node->add_child ("FadeOut");
647
648         for (AutomationList::iterator ii = _fade_out.begin(); ii != _fade_out.end(); ++ii) {
649                 XMLNode* pnode;
650
651                 pnode = new XMLNode ("point");
652
653                 snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*ii)->when));
654                 pnode->add_property ("x", buf);
655                 snprintf (buf, sizeof (buf), "%.12g", (*ii)->value);
656                 pnode->add_property ("y", buf);
657                 child->add_child_nocopy (*pnode);
658         }
659
660         return *node;
661 }
662
663 int
664 Crossfade::set_state (const XMLNode& node)
665 {
666         XMLNodeConstIterator i;
667         XMLNodeList children;
668         XMLNode* fi;
669         XMLNode* fo;
670         const XMLProperty* prop;
671         LocaleGuard lg (X_("POSIX"));
672         Change what_changed = Change (0);
673         nframes_t val;
674
675         if ((prop = node.property ("position")) != 0) {
676                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
677                 if (val != _position) {
678                         _position = val;
679                         what_changed = Change (what_changed | PositionChanged);
680                 }
681         } else {
682                 warning << _("old-style crossfade information - no position information") << endmsg;
683                 _position = _in->first_frame();
684         }
685
686         if ((prop = node.property ("active")) != 0) {
687                 bool x = (prop->value() == "yes");
688                 if (x != _active) {
689                         _active = x;
690                         what_changed = Change (what_changed | ActiveChanged);
691                 }
692         } else {
693                 _active = true;
694         }
695
696         if ((prop = node.property ("follow-overlap")) != 0) {
697                 _follow_overlap = (prop->value() == "yes");
698         } else {
699                 _follow_overlap = false;
700         }
701
702         if ((prop = node.property ("fixed")) != 0) {
703                 _fixed = (prop->value() == "yes");
704         } else {
705                 _fixed = false;
706         }
707
708         if ((prop = node.property ("anchor-point")) != 0) {
709                 _anchor_point = AnchorPoint (atoi ((prop->value().c_str())));
710         } else {
711                 _anchor_point = StartOfIn;
712         }
713
714         if ((prop = node.property ("length")) != 0) {
715
716                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
717                 if (val != _length) {
718                         _length = atol (prop->value().c_str());
719                         what_changed = Change (what_changed | LengthChanged);
720                 }
721
722         } else {
723                 
724                 /* XXX this branch is legacy code from before
725                    the point where we stored xfade lengths.
726                 */
727                 
728                 if ((_length = overlap_length()) == 0) {
729                         throw failed_constructor();
730                 }
731         }
732
733         if ((fi = find_named_node (node, "FadeIn")) == 0) {
734                 return -1;
735         }
736         
737         if ((fo = find_named_node (node, "FadeOut")) == 0) {
738                 return -1;
739         }
740
741         /* fade in */
742         
743         _fade_in.freeze ();
744         _fade_in.clear ();
745         
746         children = fi->children();
747         
748         for (i = children.begin(); i != children.end(); ++i) {
749                 if ((*i)->name() == "point") {
750                         nframes_t x;
751                         float y;
752                         
753                         prop = (*i)->property ("x");
754                         sscanf (prop->value().c_str(), "%" PRIu32, &x);
755                         
756                         prop = (*i)->property ("y");
757                         sscanf (prop->value().c_str(), "%f", &y);
758
759                         _fade_in.add (x, y);
760                 }
761         }
762
763         _fade_in.thaw ();
764         
765         /* fade out */
766         
767         _fade_out.freeze ();
768         _fade_out.clear ();
769
770         children = fo->children();
771         
772         for (i = children.begin(); i != children.end(); ++i) {
773                 if ((*i)->name() == "point") {
774                         nframes_t x;
775                         float y;
776                         XMLProperty* prop;
777
778                         prop = (*i)->property ("x");
779                         sscanf (prop->value().c_str(), "%" PRIu32, &x);
780
781                         prop = (*i)->property ("y");
782                         sscanf (prop->value().c_str(), "%f", &y);
783                         
784                         _fade_out.add (x, y);
785                 }
786         }
787
788         _fade_out.thaw ();
789
790         StateChanged (what_changed); /* EMIT SIGNAL */
791
792         return 0;
793 }
794
795 bool
796 Crossfade::can_follow_overlap () const
797 {
798         return !_fixed;
799 }
800
801 void
802 Crossfade::set_follow_overlap (bool yn)
803 {
804         if (yn == _follow_overlap || _fixed) {
805                 return;
806         }
807
808         _follow_overlap = yn;
809
810         if (!yn) {
811                 set_length (_short_xfade_length);
812         } else {
813                 set_length (_out->first_frame() + _out->length() - _in->first_frame());
814         }
815
816         StateChanged (FollowOverlapChanged);
817 }
818
819 nframes_t
820 Crossfade::set_length (nframes_t len)
821 {
822         nframes_t limit;
823
824         switch (_anchor_point) {
825         case StartOfIn:
826                 limit = _in->length();
827                 break;
828
829         case EndOfIn:
830                 limit = _in->length();
831                 break;
832
833         case EndOfOut:
834                 limit = _out->length();
835                 break;
836                 
837         }
838
839         len = min (limit, len);
840
841         double factor = len / (double) _length;
842
843         _in_update = true;
844         _fade_out.x_scale (factor);
845         _fade_in.x_scale (factor);
846         _in_update = false;
847         
848         _length = len;
849
850         StateChanged (LengthChanged);
851
852         return len;
853 }
854
855 nframes_t
856 Crossfade::overlap_length () const
857 {
858         if (_fixed) {
859                 return _length;
860         }
861         return _out->first_frame() + _out->length() - _in->first_frame();
862 }
863
864 void
865 Crossfade::set_short_xfade_length (nframes_t n)
866 {
867         _short_xfade_length = n;
868 }
869
870 void
871 Crossfade::invalidate ()
872 {
873         Invalidated (shared_from_this()); /* EMIT SIGNAL */
874 }