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