cleanup up cleanup at session destruction; clarify the meaning of 3 signals (DropRefe...
[ardour.git] / libs / ardour / audio_playlist.cc
index 3eda5b57cfc4e6d8450e59683003afb6d29d2baf..1ecba28653f615f44838ed73c4a0c83c42bd8a45 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2003 Paul Davis 
+    Copyright (C) 2003 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
 
 #include <cstdlib>
 
-#include <sigc++/bind.h>
 
-#include <ardour/types.h>
-#include <ardour/configuration.h>
-#include <ardour/audioplaylist.h>
-#include <ardour/audioregion.h>
-#include <ardour/crossfade.h>
-#include <ardour/crossfade_compare.h>
-#include <ardour/session.h>
-#include <pbd/enumwriter.h>
+#include "ardour/types.h"
+#include "ardour/configuration.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/audioregion.h"
+#include "ardour/crossfade.h"
+#include "ardour/crossfade_compare.h"
+#include "ardour/session.h"
+#include "pbd/enumwriter.h"
 
 #include "i18n.h"
 
 using namespace ARDOUR;
-using namespace sigc;
 using namespace std;
 using namespace PBD;
 
@@ -46,7 +44,7 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden
        assert(!prop || DataType(prop->value()) == DataType::AUDIO);
 
        in_set_state++;
-       set_state (node);
+       set_state (node, Stateful::loading_state_version);
        in_set_state--;
 }
 
@@ -65,7 +63,6 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
                boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
 
                // We look only for crossfades which begin with the current region, so we don't get doubles
-
                for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
                        if ((*xfades)->in() == ar) {
                                // We found one! Now copy it!
@@ -74,9 +71,9 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
                                RegionList::const_iterator out_n = regions.begin();
 
                                while (out_o != other->regions.end()) {
-                                       
+
                                        boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
-                                       
+
                                        if ((*xfades)->out() == ar2) {
                                                boost::shared_ptr<AudioRegion>in  = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
                                                boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
@@ -84,7 +81,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, stri
                                                add_crossfade(new_fade);
                                                break;
                                        }
-                                       
+
                                        out_o++;
                                        out_n++;
                                }
@@ -105,30 +102,9 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nfra
 
 AudioPlaylist::~AudioPlaylist ()
 {
-       GoingAway (); /* EMIT SIGNAL */
-
-       /* drop connections to signals */
-
-       notify_callbacks ();
-       
        _crossfades.clear ();
 }
 
-void
-AudioPlaylist::copy_regions (RegionList& newlist) const
-{
-       RegionLock rlock (const_cast<Playlist *> (this));
-
-       Playlist::copy_regions (newlist, false);
-
-       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               if (!(*i)->is_dependent()) {
-                       newlist.push_back (RegionFactory::RegionFactory::create (*i));
-               }
-       }
-}
-
-
 struct RegionSortByLayer {
     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
            return a->layer() < b->layer();
@@ -141,11 +117,13 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
 {
        nframes_t ret = cnt;
        nframes_t end;
+       nframes_t read_frames;
+       nframes_t skip_frames;
 
        /* optimizing this memset() away involves a lot of conditionals
-          that may well cause more of a hit due to cache misses 
+          that may well cause more of a hit due to cache misses
           and related stuff than just doing this here.
-          
+
           it would be great if someone could measure this
           at some point.
 
@@ -157,21 +135,31 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
 
        memset (buf, 0, sizeof (Sample) * cnt);
 
-       /* this function is never called from a realtime thread, so 
+       /* this function is never called from a realtime thread, so
           its OK to block (for short intervals).
        */
 
-       Glib::Mutex::Lock rm (region_lock);
+       Glib::RecMutex::Lock rm (region_lock);
 
        end =  start + cnt - 1;
+       read_frames = 0;
+       skip_frames = 0;
+       _read_data_count = 0;
 
        _read_data_count = 0;
 
+       RegionList* rlist = regions_to_read (start, start+cnt);
+
+       if (rlist->empty()) {
+               delete rlist;
+               return cnt;
+       }
+
        map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
        map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
        vector<uint32_t> relevant_layers;
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+       for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
                if ((*i)->coverage (start, end) != OverlapNone) {
                        relevant_regions[(*i)->layer()].push_back (*i);
                        relevant_layers.push_back ((*i)->layer());
@@ -203,10 +191,10 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
                for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
                        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
                        assert(ar);
-                       ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
+                       ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
                        _read_data_count += ar->read_data_count();
                }
-               
+
                for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
                        (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
 
@@ -216,6 +204,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf
                }
        }
 
+       delete rlist;
        return ret;
 }
 
@@ -228,17 +217,17 @@ AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
        if (in_set_state) {
                return;
        }
-       
+
        if (r == 0) {
                fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
                      << endmsg;
                return;
        }
-       
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
-               
-               if ((*i)->depends_on (r)) {
-                       i = regions.erase (i);
+
+       for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
+
+               if ((*i)->involves (r)) {
+                       i = _crossfades.erase (i);
                } else {
                        ++i;
                }
@@ -263,7 +252,7 @@ AudioPlaylist::flush_notifications ()
        }
 
        _pending_xfade_adds.clear ();
-       
+
        in_flush = false;
 }
 
@@ -277,22 +266,23 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
                return;
        }
 
-       for (RegionList::iterator x = regions.begin(); x != regions.end();) {
-               
-               RegionList::iterator tmp;
-               
+       for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
+
+               Crossfades::iterator tmp;
+
                tmp = x;
                ++tmp;
 
                /* only update them once */
 
-               if ((*x)->depends_on (ar)) {
+               if ((*x)->involves (ar)) {
 
-                       if (find (updated.begin(), updated.end(), *x) == updated.end()) {
-                               try { 
-                                       if ((*x)->refresh ()) {
-                                               updated.insert (*x);
-                                       }
+                       pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
+
+                       if (u.second) {
+                               /* x was successfully inserted into the set, so it has not already been updated */
+                               try {
+                                       (*x)->refresh ();
                                }
 
                                catch (Crossfade::NoCrossfadeHere& err) {
@@ -312,43 +302,35 @@ AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared
        boost::shared_ptr<AudioRegion> left  = boost::dynamic_pointer_cast<AudioRegion>(l);
        boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
 
-       for (RegionList::iterator x = regions.begin(); x !=regions.end();) {
+       for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
                Crossfades::iterator tmp;
-               boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade>(*x);
-
-               if (!xfade) {
-                       ++x;
-                       continue;
-               }
-
                tmp = x;
                ++tmp;
 
                boost::shared_ptr<Crossfade> fade;
-               
-               if (xfade->_in == orig) {
-                       if (! xfade->covers(right->position())) {
-                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfade, left, xfade->_out));
+
+               if ((*x)->_in == orig) {
+                       if (! (*x)->covers(right->position())) {
+                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
                        } else {
                                // Overlap, the crossfade is copied on the left side of the right region instead
-                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfade, right, xfade->_out));
+                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
                        }
                }
-               
-               if (xfade->_out == orig) {
-                       if (! xfade->covers(right->position())) {
-                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfade, xfade->_in, right));
+
+               if ((*x)->_out == orig) {
+                       if (! (*x)->covers(right->position())) {
+                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
                        } else {
                                // Overlap, the crossfade is copied on the right side of the left region instead
-                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfade, xfade->_in, left));
+                               fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
                        }
                }
-               
+
                if (fade) {
-                       regions.erase (*x);
+                       _crossfades.remove (*x);
                        add_crossfade (fade);
                }
-
                x = tmp;
        }
 }
@@ -361,6 +343,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
        boost::shared_ptr<AudioRegion> top;
        boost::shared_ptr<AudioRegion> bottom;
        boost::shared_ptr<Crossfade>   xfade;
+       RegionList*  touched_regions = 0;
 
        if (in_set_state || in_partition) {
                return;
@@ -377,7 +360,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
        }
 
 
-       if (!Config->get_auto_xfade()) {
+       if (!_session.config.get_auto_xfade()) {
                return;
        }
 
@@ -394,7 +377,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                if (other->muted() || region->muted()) {
                        continue;
                }
-               
+
 
                if (other->layer() < region->layer()) {
                        top = region;
@@ -404,10 +387,15 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                        bottom = region;
                }
 
-
+               if (!top->opaque()) {
+                       continue;
+               }
 
                OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
 
+               delete touched_regions;
+               touched_regions = 0;
+
                try {
                        switch (c) {
                        case OverlapNone:
@@ -424,32 +412,85 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                                /*     [ -------- top ------- ]
                                 * {=========== bottom =============}
                                 */
-                               
+
                                /* to avoid discontinuities at the region boundaries of an internal
                                   overlap (this region is completely within another), we create
                                   two hidden crossfades at each boundary. this is not dependent
                                   on the auto-xfade option, because we require it as basic
                                   audio engineering.
                                */
-                               
+
                                xfade_length = min ((nframes_t) 720, top->length());
-                               
-                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
-                               add_crossfade (xfade);
+
+                               if (top_region_at (top->first_frame()) == top) {
+
+                                       xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
+                                       add_crossfade (xfade);
+                               }
 
                                if (top_region_at (top->last_frame() - 1) == top) {
-                                       /* 
-                                          only add a fade out if there is no region on top of the end of 'top' (which 
+
+                                       /*
+                                          only add a fade out if there is no region on top of the end of 'top' (which
                                           would cover it).
                                        */
-                                       
+
                                        xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
                                        add_crossfade (xfade);
                                }
                                break;
-                               
+                       case OverlapStart:
+
+                               /*                   { ==== top ============ }
+                                *   [---- bottom -------------------]
+                                */
+
+                               if (_session.config.get_xfade_model() == FullCrossfade) {
+                                       touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
+                                       if (touched_regions->size() <= 2) {
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
+                                               add_crossfade (xfade);
+                                       }
+                               } else {
+
+                                       touched_regions = regions_touched (top->first_frame(),
+                                                                          top->first_frame() + min ((nframes_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
+                                                                                                    top->length()));
+                                       if (touched_regions->size() <= 2) {
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
+                                               add_crossfade (xfade);
+                                       }
+                               }
+                               break;
+                       case OverlapEnd:
+
+
+                               /* [---- top ------------------------]
+                                *                { ==== bottom ============ }
+                                */
+
+                               if (_session.config.get_xfade_model() == FullCrossfade) {
+
+                                       touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
+                                       if (touched_regions->size() <= 2) {
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
+                                                                                                    _session.config.get_xfade_model(), _session.config.get_xfades_active()));
+                                               add_crossfade (xfade);
+                                       }
+
+                               } else {
+                                       touched_regions = regions_touched (bottom->first_frame(),
+                                                                          bottom->first_frame() + min ((nframes_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
+                                                                                                       bottom->length()));
+                                       if (touched_regions->size() <= 2) {
+                                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
+                                               add_crossfade (xfade);
+                                       }
+                               }
+                               break;
                        default:
-                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+                               xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
+                                                                                    _session.config.get_xfade_model(), _session.config.get_xfades_active()));
                                add_crossfade (xfade);
                        }
                }
@@ -457,41 +498,39 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
                catch (failed_constructor& err) {
                        continue;
                }
-               
+
                catch (Crossfade::NoCrossfadeHere& err) {
                        continue;
                }
-               
+
        }
+
+       delete touched_regions;
 }
 
 void
-AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> new_xfade)
+AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
 {
-       RegionList::iterator r;
-       boost::shared_ptr<Crossfade> xfade;
+       Crossfades::iterator ci;
 
-       for (r = regions.begin(); r != regions.end(); ++r) {
-               
-               if ((xfade = boost::dynamic_pointer_cast<Crossfade>(*r))) {
-                       if (*xfade == *new_xfade) { // Crossfade::operator==()
-                               break;
-                       }
+       for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
+               if (*(*ci) == *xfade) { // Crossfade::operator==()
+                       break;
                }
        }
-       
-       if (r != regions.end()) {
+
+       if (ci != _crossfades.end()) {
                // it will just go away
        } else {
-               add_region_internal (new_xfade);
+               _crossfades.push_back (xfade);
 
-               new_xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
-               new_xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
+               xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
+               xfade->StateChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
 
-               notify_crossfade_added (new_xfade);
+               notify_crossfade_added (xfade);
        }
 }
-       
+
 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
 {
        if (g_atomic_int_get(&block_notifications)) {
@@ -504,25 +543,29 @@ void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
 void
 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
 {
+       Crossfades::iterator i;
        boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
-       
+
        xfade->in()->resume_fade_in ();
        xfade->out()->resume_fade_out ();
 
-       remove_region_internal (r);
+       if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
+               _crossfades.erase (i);
+       }
 }
 
 int
-AudioPlaylist::set_state (const XMLNode& node)
+AudioPlaylist::set_state (const XMLNode& node, int version)
 {
        XMLNode *child;
        XMLNodeList nlist;
        XMLNodeConstIterator niter;
 
        in_set_state++;
-       freeze ();
 
-       Playlist::set_state (node);
+       Playlist::set_state (node, version);
+
+       freeze ();
 
        nlist = node.children();
 
@@ -530,22 +573,21 @@ AudioPlaylist::set_state (const XMLNode& node)
 
                child = *niter;
 
-               if (child->name() != Crossfade::node_name()) {
+               if (child->name() != "Crossfade") {
                        continue;
                }
-               
+
                try {
-                       boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (RegionFactory::create (*((const Playlist *) this), *child));
-                       assert (xfade);
-                       add_region_internal (xfade);
-                       xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
-                       xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
+                       boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
+                       _crossfades.push_back (xfade);
+                       xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
+                       xfade->StateChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
                        NewCrossfade(xfade);
                }
-               
+
                catch (failed_constructor& err) {
                        //      cout << string_compose (_("could not create crossfade object in playlist %1"),
-                       //        _name) 
+                       //        _name)
                        //    << endl;
                        continue;
                }
@@ -560,9 +602,24 @@ AudioPlaylist::set_state (const XMLNode& node)
 void
 AudioPlaylist::clear (bool with_signals)
 {
+       _crossfades.clear ();
        Playlist::clear (with_signals);
 }
 
+XMLNode&
+AudioPlaylist::state (bool full_state)
+{
+       XMLNode& node = Playlist::state (full_state);
+
+       if (full_state) {
+               for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+                       node.add_child_nocopy ((*i)->get_state());
+               }
+       }
+
+       return node;
+}
+
 void
 AudioPlaylist::dump () const
 {
@@ -571,32 +628,33 @@ AudioPlaylist::dump () const
 
        cerr << "Playlist \"" << _name << "\" " << endl
             << regions.size() << " regions "
+            << _crossfades.size() << " crossfades"
             << endl;
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
                r = *i;
+               cerr << "  " << r->name() << " @ " << r << " ["
+                    << r->start() << "+" << r->length()
+                    << "] at "
+                    << r->position()
+                    << " on layer "
+                    << r->layer ()
+                    << endl;
+       }
 
-               if ((x = boost::dynamic_pointer_cast<Crossfade> (r))) {
-                       cerr << "  xfade [" 
-                            << x->out()->name()
-                            << ','
-                            << x->in()->name()
-                            << " @ "
-                            << x->position()
-                            << " length = " 
-                            << x->length ()
-                            << " active ? "
-                            << (x->active() ? "yes" : "no")
-                            << endl;
-               } else {
-                       cerr << "  " << r->name() << " @ " << r << " [" 
-                            << r->start() << "+" << r->length() 
-                            << "] at " 
-                            << r->position()
-                            << " on layer "
-                            << r->layer ()
-                            << endl;
-               }
+       for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+               x = *i;
+               cerr << "  xfade ["
+                    << x->out()->name()
+                    << ','
+                    << x->in()->name()
+                    << " @ "
+                    << x->position()
+                    << " length = "
+                    << x->length ()
+                    << " active ? "
+                    << (x->active() ? "yes" : "no")
+                    << endl;
        }
 }
 
@@ -605,6 +663,8 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 {
        boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
        bool changed = false;
+       Crossfades::iterator c, ctmp;
+       set<boost::shared_ptr<Crossfade> > unique_xfades;
 
        if (r == 0) {
                fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
@@ -613,22 +673,19 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
                return false;
        }
 
-       { 
+       {
                RegionLock rlock (this);
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
-                       
+
                        RegionList::iterator tmp = i;
                        ++tmp;
-                       
+
                        if ((*i) == region) {
                                regions.erase (i);
                                changed = true;
-                       } else if ((*i)->depends_on (region)) {
-                               regions.erase (i);
-                               changed = true;
                        }
-                       
+
                        i = tmp;
                }
 
@@ -636,21 +693,30 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 
                        set<boost::shared_ptr<Region> >::iterator xtmp = x;
                        ++xtmp;
-                       
+
                        if ((*x) == region) {
                                all_regions.erase (x);
                                changed = true;
-                       } else if ((*x)->depends_on (region)) {
-                               all_regions.erase (x);
-                               changed = true;
                        }
-                       
+
                        x = xtmp;
                }
 
                region->set_playlist (boost::shared_ptr<Playlist>());
        }
 
+       for (c = _crossfades.begin(); c != _crossfades.end(); ) {
+               ctmp = c;
+               ++ctmp;
+
+               if ((*c)->involves (r)) {
+                       unique_xfades.insert (*c);
+                       _crossfades.erase (c);
+               }
+
+               c = ctmp;
+       }
+
        if (changed) {
                /* overload this, it normally means "removed", not destroyed */
                notify_region_removed (region);
@@ -660,7 +726,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
 }
 
 void
-AudioPlaylist::crossfade_changed (Change ignored)
+AudioPlaylist::crossfade_changed (Change)
 {
        if (in_flush || in_set_state) {
                return;
@@ -697,6 +763,31 @@ AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> re
                notify_modified ();
        }
 
-       return true; 
+       return true;
 }
 
+void
+AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
+{
+       RegionLock rlock (this);
+
+       for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+               nframes_t start, end;
+
+               start = (*i)->position();
+               end = start + (*i)->overlap_length(); // not length(), important difference
+
+               if (frame >= start && frame <= end) {
+                       clist.push_back (*i);
+               }
+       }
+}
+
+void
+AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
+{
+       RegionLock rl (this, false);
+       for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+               s (*i);
+       }
+}