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