+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)
+{
+ 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;
+
+ _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 (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;
+ }
+
+ /* 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;
+}
+