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