X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Fcrossfade.cc;h=fb4325865c0dcf11702b64d5a8955f31e1f8c08b;hb=b5ec66ae6cb60fa43c343d3d29340b2370d0b9d1;hp=1e9543519b214b83d66d915d7fb068fcd83f3536;hpb=8af0757b61990767f2a85e68f535a5af9976fd79;p=ardour.git diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 1e9543519b..fb4325865c 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2003 Paul Davis + Copyright (C) 2003-2006 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,11 +15,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include +#include + #include #include #include @@ -27,16 +28,18 @@ #include #include #include +#include #include "i18n.h" #include using namespace std; using namespace ARDOUR; -//using namespace sigc; +using namespace PBD; -jack_nframes_t Crossfade::_short_xfade_length = 0; -Change Crossfade::ActiveChanged = ARDOUR::new_change(); +nframes_t Crossfade::_short_xfade_length = 0; +Change Crossfade::ActiveChanged = new_change(); +Change Crossfade::FollowOverlapChanged = new_change(); /* XXX if and when we ever implement parallel processing of the process() callback, these will need to be handled on a per-thread basis. @@ -46,7 +49,7 @@ Sample* Crossfade::crossfade_buffer_out = 0; Sample* Crossfade::crossfade_buffer_in = 0; void -Crossfade::set_buffer_size (jack_nframes_t sz) +Crossfade::set_buffer_size (nframes_t sz) { if (crossfade_buffer_out) { delete [] crossfade_buffer_out; @@ -70,28 +73,30 @@ Crossfade::operator== (const Crossfade& other) return (_in == other._in) && (_out == other._out); } -Crossfade::Crossfade (ARDOUR::AudioRegion& in, ARDOUR::AudioRegion& out, - jack_nframes_t length, - jack_nframes_t position, +Crossfade::Crossfade (boost::shared_ptr in, boost::shared_ptr out, + nframes_t length, + nframes_t position, AnchorPoint ap) - : _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB - _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB + : AudioRegion (in->session(), position, length, "foobar"), + _fade_in (Evoral::Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB + _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB + { - _in = ∈ - _out = &out; - _length = length; - _position = position; + _in = in; + _out = out; _anchor_point = ap; _follow_overlap = false; - _active = true; + + _active = Config->get_xfades_active (); _fixed = true; - + initialize (); } -Crossfade::Crossfade (ARDOUR::AudioRegion& a, ARDOUR::AudioRegion& b, CrossfadeModel model, bool act) - : _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB - _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB +Crossfade::Crossfade (boost::shared_ptr a, boost::shared_ptr b, CrossfadeModel model, bool act) + : AudioRegion (a->session(), 0, 0, "foobar"), + _fade_in (Evoral::Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB + _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB { _in_update = false; _fixed = false; @@ -104,15 +109,17 @@ Crossfade::Crossfade (ARDOUR::AudioRegion& a, ARDOUR::AudioRegion& b, CrossfadeM initialize (); + } Crossfade::Crossfade (const Playlist& playlist, XMLNode& node) - : _fade_in (0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB - _fade_out (0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB + : AudioRegion (playlist.session(), 0, 0, "foobar"), + _fade_in (Evoral::Parameter(FadeInAutomation)), // linear (gain coefficient) => -inf..+6dB + _fade_out (Evoral::Parameter(FadeOutAutomation)) // linear (gain coefficient) => -inf..+6dB + { - Region* r; + boost::shared_ptr r; XMLProperty* prop; - id_t id; LocaleGuard lg (X_("POSIX")); /* we have to find the in/out regions before we can do anything else */ @@ -122,15 +129,15 @@ Crossfade::Crossfade (const Playlist& playlist, XMLNode& node) throw failed_constructor(); } - sscanf (prop->value().c_str(), "%llu", &id); + PBD::ID id (prop->value()); if ((r = playlist.find_region (id)) == 0) { - error << compose (_("Crossfade: no \"in\" region %1 found in playlist %2"), id, playlist.name()) + error << string_compose (_("Crossfade: no \"in\" region %1 found in playlist %2"), id, playlist.name()) << endmsg; throw failed_constructor(); } - if ((_in = dynamic_cast (r)) == 0) { + if ((_in = boost::dynamic_pointer_cast (r)) == 0) { throw failed_constructor(); } @@ -139,36 +146,70 @@ Crossfade::Crossfade (const Playlist& playlist, XMLNode& node) throw failed_constructor(); } - sscanf (prop->value().c_str(), "%llu", &id); + PBD::ID id2 (prop->value()); - if ((r = playlist.find_region (id)) == 0) { - error << compose (_("Crossfade: no \"out\" region %1 found in playlist %2"), id, playlist.name()) + if ((r = playlist.find_region (id2)) == 0) { + error << string_compose (_("Crossfade: no \"out\" region %1 found in playlist %2"), id2, playlist.name()) << endmsg; throw failed_constructor(); } - - if ((_out = dynamic_cast (r)) == 0) { + + if ((_out = boost::dynamic_pointer_cast (r)) == 0) { throw failed_constructor(); } _length = 0; initialize(); + _active = true; if (set_state (node)) { throw failed_constructor(); } } +Crossfade::Crossfade (boost::shared_ptr orig, boost::shared_ptr newin, boost::shared_ptr newout) + : AudioRegion (boost::dynamic_pointer_cast (orig)), + _fade_in (orig->_fade_in), + _fade_out (orig->_fade_out) +{ + _active = orig->_active; + _in_update = orig->_in_update; + _anchor_point = orig->_anchor_point; + _follow_overlap = orig->_follow_overlap; + _fixed = orig->_fixed; + + _in = newin; + _out = newout; + + // copied from Crossfade::initialize() + _in_update = false; + + _out->suspend_fade_out (); + _in->suspend_fade_in (); + + overlap_type = _in->coverage (_out->position(), _out->last_frame()); + layer_relation = (int32_t) (_in->layer() - _out->layer()); + + // Let's make sure the fade isn't too long + set_length(_length); +} + + Crossfade::~Crossfade () { - for (StateMap::iterator i = states.begin(); i != states.end(); ++i) { - delete *i; - } + notify_callbacks (); } void Crossfade::initialize () { + /* merge source lists from regions */ + + _sources = _in->sources(); + _sources.insert (_sources.end(), _out->sources().begin(), _out->sources().end()); + _master_sources = _in->master_sources(); + _master_sources.insert(_master_sources.end(), _out->master_sources().begin(), _out->master_sources().end()); + _in_update = false; _out->suspend_fade_out (); @@ -194,35 +235,262 @@ Crossfade::initialize () _fade_in.add (_length, 1.0); _fade_in.thaw (); -// _in->StateChanged.connect (slot (*this, &Crossfade::member_changed)); -// _out->StateChanged.connect (slot (*this, &Crossfade::member_changed)); - overlap_type = _in->coverage (_out->position(), _out->last_frame()); - - save_state ("initial"); + layer_relation = (int32_t) (_in->layer() - _out->layer()); +} + +nframes_t +Crossfade::read_raw_internal (Sample* buf, nframes_t start, nframes_t cnt) const +{ +#if 0 + Sample* mixdown = new Sample[cnt]; + float* gain = new float[cnt]; + nframes_t ret; + + ret = read_at (buf, mixdown, gain, start, cnt, chan_n, cnt); + + delete [] mixdown; + delete [] gain; + + return ret; +#endif + return cnt; +} + +nframes_t +Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, + float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n, + nframes_t read_frames, nframes_t skip_frames) const +{ + nframes_t offset; + nframes_t to_write; + + if (!_active) { + return 0; + } + + if (start < _position) { + + /* handle an initial section of the read area that we do not + cover. + */ + + offset = _position - start; + + if (offset < cnt) { + cnt -= offset; + } else { + return 0; + } + + start = _position; + buf += offset; + to_write = min (_length, cnt); + + } else { + + to_write = min (_length - (start - _position), cnt); + + } + + offset = start - _position; + + /* Prevent data from piling up inthe crossfade buffers when reading a transparent region */ + if (!(_out->opaque())) { + memset (crossfade_buffer_out, 0, sizeof (Sample) * to_write); + } else if (!(_in->opaque())) { + memset (crossfade_buffer_in, 0, sizeof (Sample) * to_write); + } + + _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames); + _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames); + + float* fiv = new float[to_write]; + float* fov = new float[to_write]; + + _fade_in.curve().get_vector (offset, offset+to_write, fiv, to_write); + _fade_out.curve().get_vector (offset, offset+to_write, fov, to_write); + + /* note: although we have not explicitly taken into account the return values + from _out->read_at() or _in->read_at(), the length() function does this + implicitly. why? because it computes a value based on the in+out regions' + position and length, and so we know precisely how much data they could return. + */ + + for (nframes_t n = 0; n < to_write; ++n) { + buf[n] = (crossfade_buffer_out[n] * fov[n]) + (crossfade_buffer_in[n] * fiv[n]); + } + + delete [] fov; + delete [] fiv; + + return to_write; } +OverlapType +Crossfade::coverage (nframes_t start, nframes_t end) const +{ + nframes_t my_end = _position + _length; + + if ((start >= _position) && (end <= my_end)) { + return OverlapInternal; + } + if ((end >= _position) && (end <= my_end)) { + return OverlapStart; + } + if ((start >= _position) && (start <= my_end)) { + return OverlapEnd; + } + if ((_position >= start) && (_position <= end) && (my_end <= end)) { + return OverlapExternal; + } + return OverlapNone; +} + +void +Crossfade::set_active (bool yn) +{ + if (_active != yn) { + _active = yn; + StateChanged (ActiveChanged); + } +} + +bool +Crossfade::refresh () +{ + /* crossfades must be between non-muted regions */ + + if (_out->muted() || _in->muted()) { + Invalidated (shared_from_this ()); + return false; + } + + /* Top layer shouldn't be transparent */ + + if (!((layer_relation > 0 ? _in : _out)->opaque())) { + Invalidated (shared_from_this()); + return false; + } + + /* layer ordering cannot change */ + + int32_t new_layer_relation = (int32_t) (_in->layer() - _out->layer()); + + if (new_layer_relation * layer_relation < 0) { // different sign, layers rotated + Invalidated (shared_from_this ()); + return false; + } + + OverlapType ot = _in->coverage (_out->first_frame(), _out->last_frame()); + + if (ot == OverlapNone) { + Invalidated (shared_from_this ()); + return false; + } + + bool send_signal; + + if (ot != overlap_type) { + + if (_follow_overlap) { + + try { + compute (_in, _out, Config->get_xfade_model()); + } + + catch (NoCrossfadeHere& err) { + Invalidated (shared_from_this ()); + return false; + } + + send_signal = true; + + } else { + + Invalidated (shared_from_this ()); + return false; + } + + } else { + + send_signal = update (); + } + + if (send_signal) { + StateChanged (BoundsChanged); /* EMIT SIGNAL */ + } + + _in_update = false; + + return true; +} + +bool +Crossfade::update () +{ + nframes_t newlen; + + if (_follow_overlap) { + newlen = _out->first_frame() + _out->length() - _in->first_frame(); + } else { + newlen = _length; + } + + if (newlen == 0) { + Invalidated (shared_from_this ()); + return false; + } + + _in_update = true; + + if ((_follow_overlap && newlen != _length) || (_length > newlen)) { + + double factor = newlen / (double) _length; + + _fade_out.x_scale (factor); + _fade_in.x_scale (factor); + + _length = newlen; + } + + switch (_anchor_point) { + case StartOfIn: + _position = _in->first_frame(); + break; + + case EndOfIn: + _position = _in->last_frame() - _length; + break; + + case EndOfOut: + _position = _out->last_frame() - _length; + } + + return true; +} + int -Crossfade::compute (AudioRegion& a, AudioRegion& b, CrossfadeModel model) +Crossfade::compute (boost::shared_ptr a, boost::shared_ptr b, CrossfadeModel model) { - AudioRegion* top; - AudioRegion* bottom; - jack_nframes_t short_xfade_length; + boost::shared_ptr top; + boost::shared_ptr bottom; + nframes_t short_xfade_length; short_xfade_length = _short_xfade_length; - if (a.layer() < b.layer()) { - top = &b; - bottom = &a; + if (a->layer() < b->layer()) { + top = b; + bottom = a; } else { - top = &a; - bottom = &b; + top = a; + bottom = b; } /* first check for matching ends */ if (top->first_frame() == bottom->first_frame()) { - + /* Both regions start at the same point */ if (top->last_frame() < bottom->last_frame()) { @@ -323,15 +591,16 @@ Crossfade::compute (AudioRegion& a, AudioRegion& b, CrossfadeModel model) _in = bottom; _out = top; - _position = bottom->first_frame(); - _anchor_point = StartOfIn; + _anchor_point = EndOfOut; if (model == FullCrossfade) { + _position = bottom->first_frame(); // "{" _length = _out->first_frame() + _out->length() - _in->first_frame(); /* leave active alone */ _follow_overlap = true; } else { _length = min (short_xfade_length, top->length()); + _position = top->last_frame() - _length; // "]" - length _active = true; _follow_overlap = false; @@ -367,277 +636,6 @@ Crossfade::compute (AudioRegion& a, AudioRegion& b, CrossfadeModel model) return 0; } -jack_nframes_t -Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, - float *gain_buffer, jack_nframes_t start, jack_nframes_t cnt, uint32_t chan_n, - jack_nframes_t read_frames, jack_nframes_t skip_frames) -{ - jack_nframes_t offset; - jack_nframes_t to_write; - - if (!_active) { - return 0; - } - - if (start < _position) { - - /* handle an initial section of the read area that we do not - cover. - */ - - offset = _position - start; - - if (offset < cnt) { - cnt -= offset; - } else { - return 0; - } - - start = _position; - buf += offset; - to_write = min (_length, cnt); - - } else { - - to_write = min (_length - (start - _position), cnt); - - } - - offset = start - _position; - - _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames); - _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames); - - float* fiv = new float[to_write]; - float* fov = new float[to_write]; - - _fade_in.get_vector (offset, offset+to_write, fiv, to_write); - _fade_out.get_vector (offset, offset+to_write, fov, to_write); - - /* note: although we have not explicitly taken into account the return values - from _out->read_at() or _in->read_at(), the length() function does this - implicitly. why? because it computes a value based on the in+out regions' - position and length, and so we know precisely how much data they could return. - */ - - for (jack_nframes_t n = 0; n < to_write; ++n) { - buf[n] = (crossfade_buffer_out[n] * fov[n]) + (crossfade_buffer_in[n] * fiv[n]); - } - - delete [] fov; - delete [] fiv; - - return to_write; -} - -OverlapType -Crossfade::coverage (jack_nframes_t start, jack_nframes_t end) const -{ - jack_nframes_t my_end = _position + _length; - - if ((start >= _position) && (end <= my_end)) { - return OverlapInternal; - } - if ((end >= _position) && (end <= my_end)) { - return OverlapStart; - } - if ((start >= _position) && (start <= my_end)) { - return OverlapEnd; - } - if ((_position >= start) && (_position <= end) && (my_end <= end)) { - return OverlapExternal; - } - return OverlapNone; -} - -void -Crossfade::set_active (bool yn) -{ - if (_active != yn) { - _active = yn; - save_state (_("active changed")); - send_state_changed (ActiveChanged); - } -} - -bool -Crossfade::refresh () -{ - /* crossfades must be between non-muted regions */ - - if (_out->muted() || _in->muted()) { - Invalidated (this); - return false; - } - - /* overlap type must be Start, End or External */ - - OverlapType ot; - - ot = _in->coverage (_out->first_frame(), _out->last_frame()); - - switch (ot) { - case OverlapNone: - case OverlapInternal: - Invalidated (this); - return false; - - default: - break; - } - - /* overlap type must not have altered */ - - if (ot != overlap_type) { - Invalidated (this); - return false; - } - - /* time to update */ - - return update (false); -} - -bool -Crossfade::update (bool force) -{ - jack_nframes_t newlen; - bool save = false; - - if (_follow_overlap) { - newlen = _out->first_frame() + _out->length() - _in->first_frame(); - } else { - newlen = _length; - } - - if (newlen == 0) { - Invalidated (this); - return false; - } - - _in_update = true; - - if (force || (_follow_overlap && newlen != _length) || (_length > newlen)) { - - double factor = newlen / (double) _length; - - _fade_out.x_scale (factor); - _fade_in.x_scale (factor); - - _length = newlen; - - save = true; - - } - - switch (_anchor_point) { - case StartOfIn: - if (_position != _in->first_frame()) { - _position = _in->first_frame(); - save = true; - } - break; - - case EndOfIn: - if (_position != _in->last_frame() - _length) { - _position = _in->last_frame() - _length; - save = true; - } - break; - - case EndOfOut: - if (_position != _out->last_frame() - _length) { - _position = _out->last_frame() - _length; - save = true; - } - } - - if (save) { - save_state ("updated"); - } - - /* UI's may need to know that the overlap changed even - though the xfade length did not. - */ - - send_state_changed (BoundsChanged); /* EMIT SIGNAL */ - - _in_update = false; - - return true; -} - -void -Crossfade::member_changed (Change what_changed) -{ - Change what_we_care_about = Change (Region::MuteChanged| - Region::LayerChanged| - ARDOUR::BoundsChanged); - - if (what_changed & what_we_care_about) { - refresh (); - } -} - -Change -Crossfade::restore_state (StateManager::State& state) -{ - CrossfadeState* xfstate = dynamic_cast (&state); - Change what_changed = Change (0); - - _in_update = true; - - xfstate->fade_in_memento (); - xfstate->fade_out_memento (); - - if (_length != xfstate->length) { - what_changed = Change (what_changed|LengthChanged); - _length = xfstate->length; - } - if (_active != xfstate->active) { - what_changed = Change (what_changed|ActiveChanged); - _active = xfstate->active; - } - if (_position != xfstate->position) { - what_changed = Change (what_changed|PositionChanged); - _position = xfstate->position; - } - - /* XXX what to do about notifications for these? I don't - think (G)UI cares about them because they are - implicit in the bounds. - */ - - _follow_overlap = xfstate->follow_overlap; - _anchor_point = xfstate->anchor_point; - - _in_update = false; - - return Change (what_changed); -} - -StateManager::State* -Crossfade::state_factory (std::string why) const -{ - CrossfadeState* state = new CrossfadeState (why); - - state->fade_in_memento = _fade_in.get_memento (); - state->fade_out_memento = _fade_out.get_memento (); - state->active = _active; - state->length = _length; - state->position = _position; - state->follow_overlap = _follow_overlap; - state->anchor_point = _anchor_point; - - return state; -} - -UndoAction -Crossfade::get_memento() const -{ - return sigc::bind (mem_fun (*(const_cast (this)), &StateManager::use_state), _current_state_id); -} - XMLNode& Crossfade::get_state () { @@ -646,9 +644,9 @@ Crossfade::get_state () char buf[64]; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof(buf), "%" PRIu64, _out->id()); + _out->id().print (buf, sizeof (buf)); node->add_property ("out", buf); - snprintf (buf, sizeof(buf), "%" PRIu64, _in->id()); + _in->id().print (buf, sizeof (buf)); node->add_property ("in", buf); node->add_property ("active", (_active ? "yes" : "no")); node->add_property ("follow-overlap", (_follow_overlap ? "yes" : "no")); @@ -667,9 +665,9 @@ Crossfade::get_state () pnode = new XMLNode ("point"); - snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*ii)->when)); + snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*ii)->when)); pnode->add_property ("x", buf); - snprintf (buf, sizeof (buf), "%f", (*ii)->value); + snprintf (buf, sizeof (buf), "%.12g", (*ii)->value); pnode->add_property ("y", buf); child->add_child_nocopy (*pnode); } @@ -681,9 +679,9 @@ Crossfade::get_state () pnode = new XMLNode ("point"); - snprintf (buf, sizeof (buf), "%" PRIu32, (jack_nframes_t) floor ((*ii)->when)); + snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*ii)->when)); pnode->add_property ("x", buf); - snprintf (buf, sizeof (buf), "%f", (*ii)->value); + snprintf (buf, sizeof (buf), "%.12g", (*ii)->value); pnode->add_property ("y", buf); child->add_child_nocopy (*pnode); } @@ -700,16 +698,26 @@ Crossfade::set_state (const XMLNode& node) XMLNode* fo; const XMLProperty* prop; LocaleGuard lg (X_("POSIX")); + Change what_changed = Change (0); + nframes_t val; if ((prop = node.property ("position")) != 0) { - _position = atoi (prop->value().c_str()); + sscanf (prop->value().c_str(), "%" PRIu32, &val); + if (val != _position) { + _position = val; + what_changed = Change (what_changed | PositionChanged); + } } else { warning << _("old-style crossfade information - no position information") << endmsg; _position = _in->first_frame(); } if ((prop = node.property ("active")) != 0) { - _active = (prop->value() == "yes"); + bool x = (prop->value() == "yes"); + if (x != _active) { + _active = x; + what_changed = Change (what_changed | ActiveChanged); + } } else { _active = true; } @@ -734,7 +742,11 @@ Crossfade::set_state (const XMLNode& node) if ((prop = node.property ("length")) != 0) { - _length = atol (prop->value().c_str()); + sscanf (prop->value().c_str(), "%" PRIu32, &val); + if (val != _length) { + _length = atol (prop->value().c_str()); + what_changed = Change (what_changed | LengthChanged); + } } else { @@ -757,13 +769,14 @@ Crossfade::set_state (const XMLNode& node) /* fade in */ + _fade_in.freeze (); _fade_in.clear (); children = fi->children(); for (i = children.begin(); i != children.end(); ++i) { if ((*i)->name() == "point") { - jack_nframes_t x; + nframes_t x; float y; prop = (*i)->property ("x"); @@ -775,16 +788,19 @@ Crossfade::set_state (const XMLNode& node) _fade_in.add (x, y); } } + + _fade_in.thaw (); /* fade out */ + _fade_out.freeze (); _fade_out.clear (); children = fo->children(); for (i = children.begin(); i != children.end(); ++i) { if ((*i)->name() == "point") { - jack_nframes_t x; + nframes_t x; float y; XMLProperty* prop; @@ -798,6 +814,10 @@ Crossfade::set_state (const XMLNode& node) } } + _fade_out.thaw (); + + StateChanged (what_changed); /* EMIT SIGNAL */ + return 0; } @@ -821,12 +841,14 @@ Crossfade::set_follow_overlap (bool yn) } else { set_length (_out->first_frame() + _out->length() - _in->first_frame()); } + + StateChanged (FollowOverlapChanged); } -jack_nframes_t -Crossfade::set_length (jack_nframes_t len) +nframes_t +Crossfade::set_length (nframes_t len) { - jack_nframes_t limit; + nframes_t limit; switch (_anchor_point) { case StartOfIn: @@ -854,14 +876,12 @@ Crossfade::set_length (jack_nframes_t len) _length = len; - save_state ("length changed"); - - send_state_changed (LengthChanged); + StateChanged (LengthChanged); return len; } -jack_nframes_t +nframes_t Crossfade::overlap_length () const { if (_fixed) { @@ -871,7 +891,13 @@ Crossfade::overlap_length () const } void -Crossfade::set_short_xfade_length (jack_nframes_t n) +Crossfade::set_short_xfade_length (nframes_t n) { _short_xfade_length = n; } + +void +Crossfade::invalidate () +{ + Invalidated (shared_from_this ()); /* EMIT SIGNAL */ +}