try not thinning when loading old-school automation lists
[ardour.git] / libs / ardour / playlist.cc
index d234ab58c3174b56d395bc0b3dc50ed02326b080..3b0528ed3b7fc9f9975e8f6f8b0bcc3bebf462a0 100644 (file)
 
 #include <stdint.h>
 #include <set>
-#include <fstream>
 #include <algorithm>
-#include <unistd.h>
-#include <cerrno>
 #include <string>
-#include <climits>
 
 #include <boost/lexical_cast.hpp>
 
 #include "pbd/convert.h"
-#include "pbd/failed_constructor.h"
 #include "pbd/stateful_diff_command.h"
 #include "pbd/xml++.h"
 
@@ -52,9 +47,9 @@ using namespace ARDOUR;
 using namespace PBD;
 
 namespace ARDOUR {
-namespace Properties {
-PBD::PropertyDescriptor<bool> regions;
-}
+       namespace Properties {
+               PBD::PropertyDescriptor<bool> regions;
+       }
 }
 
 struct ShowMeTheList {
@@ -186,9 +181,6 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
        in_partition = false;
        subcnt = 0;
        _frozen = other->_frozen;
-
-       layer_op_counter = other->layer_op_counter;
-       freeze_length = other->freeze_length;
 }
 
 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
@@ -197,7 +189,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
        , _type(other->_type)
        , _orig_track_id (other->_orig_track_id)
 {
-       RegionLock rlock2 (const_cast<Playlist*> (other.get()));
+       RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
 
        framepos_t end = start + cnt - 1;
 
@@ -213,35 +205,35 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
                framepos_t position = 0;
                framecnt_t len = 0;
                string    new_name;
-               OverlapType overlap;
+               Evoral::OverlapType overlap;
 
                region = *i;
 
                overlap = region->coverage (start, end);
 
                switch (overlap) {
-               case OverlapNone:
+               case Evoral::OverlapNone:
                        continue;
 
-               case OverlapInternal:
+               case Evoral::OverlapInternal:
                        offset = start - region->position();
                        position = 0;
                        len = cnt;
                        break;
 
-               case OverlapStart:
+               case Evoral::OverlapStart:
                        offset = 0;
                        position = region->position() - start;
                        len = end - region->position();
                        break;
 
-               case OverlapEnd:
+               case Evoral::OverlapEnd:
                        offset = start - region->position();
                        position = 0;
                        len = region->length() - offset;
                        break;
 
-               case OverlapExternal:
+               case Evoral::OverlapExternal:
                        offset = 0;
                        position = region->position() - start;
                        len = region->length();
@@ -256,6 +248,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
                plist.add (Properties::length, len);
                plist.add (Properties::name, new_name);
                plist.add (Properties::layer, region->layer());
+               plist.add (Properties::layering_index, region->layering_index());
 
                new_region = RegionFactory::RegionFactory::create (region, plist);
 
@@ -288,7 +281,7 @@ Playlist::release ()
 void
 Playlist::copy_regions (RegionList& newlist) const
 {
-       RegionLock rlock (const_cast<Playlist *> (this));
+       RegionReadLock rlock (const_cast<Playlist *> (this));
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
                newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
@@ -312,15 +305,13 @@ Playlist::init (bool hide)
        _shuffling = false;
        _nudging = false;
        in_set_state = 0;
-       in_update = false;
+       in_undo = false;
        _edit_mode = Config->get_edit_mode();
        in_flush = false;
        in_partition = false;
        subcnt = 0;
        _frozen = false;
-       layer_op_counter = 0;
-       freeze_length = 0;
-       _explicit_relayering = false;
+       _capture_insertion_underway = false;
        _combine_ops = 0;
 
        _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
@@ -334,7 +325,7 @@ Playlist::~Playlist ()
        DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
 
        {
-               RegionLock rl (this);
+               RegionReadLock rl (this);
 
                for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
                        (*i)->set_playlist (boost::shared_ptr<Playlist>());
@@ -401,7 +392,7 @@ Playlist::set_name (const string& str)
 void
 Playlist::begin_undo ()
 {
-       in_update = true;
+       in_undo = true;
        freeze ();
 }
 
@@ -409,7 +400,7 @@ void
 Playlist::end_undo ()
 {
        thaw (true);
-       in_update = false;
+       in_undo = false;
 }
 
 void
@@ -432,7 +423,6 @@ void
 Playlist::delay_notifications ()
 {
        g_atomic_int_inc (&block_notifications);
-       freeze_length = _get_extent().second;
 }
 
 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
@@ -563,22 +553,12 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
        }
 }
 
-void
-Playlist::notify_length_changed ()
-{
-       if (!holding_state ()) {
-               pending_contents_change = false;
-               ContentsChanged (); /* EMIT SIGNAL */
-       }
-}
-
 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
 void
 Playlist::flush_notifications (bool from_undo)
 {
-       set<boost::shared_ptr<Region> > dependent_checks_needed;
        set<boost::shared_ptr<Region> >::iterator s;
-       uint32_t regions_changed = false;
+       bool regions_changed = false;
 
        if (in_flush) {
                return;
@@ -590,6 +570,10 @@ Playlist::flush_notifications (bool from_undo)
                regions_changed = true;
        }
 
+       /* XXX: it'd be nice if we could use pending_bounds for
+          RegionsExtended and RegionsMoved.
+       */
+
        /* we have no idea what order the regions ended up in pending
           bounds (it could be based on selection order, for example).
           so, to preserve layering in the "most recently moved is higher"
@@ -599,36 +583,34 @@ Playlist::flush_notifications (bool from_undo)
        // RegionSortByLayer cmp;
        // pending_bounds.sort (cmp);
 
+       list<Evoral::Range<framepos_t> > crossfade_ranges;
+
        for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
-               if (_session.config.get_layer_model() == MoveAddHigher) {
-                       timestamp_layer_op (*r);
-               }
-               dependent_checks_needed.insert (*r);
+               crossfade_ranges.push_back ((*r)->last_range ());
+               crossfade_ranges.push_back ((*r)->range ());
        }
 
        for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+               crossfade_ranges.push_back ((*s)->range ());
                remove_dependents (*s);
-               // cerr << _name << " sends RegionRemoved\n";
                RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
        }
-
+       
        for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
-                // cerr << _name << " sends RegionAdded\n";
-                /* don't emit RegionAdded signal until relayering is done,
-                   so that the region is fully setup by the time
-                   anyone hear's that its been added
-                */
-                dependent_checks_needed.insert (*s);
-        }
+               crossfade_ranges.push_back ((*s)->range ());
+               /* don't emit RegionAdded signal until relayering is done,
+                  so that the region is fully setup by the time
+                  anyone hears that its been added
+               */
+       }
+
+       if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
+               relayer ();
+       }
 
         if (regions_changed || pending_contents_change) {
-                if (!in_set_state) {
-                        relayer ();
-                }
                 pending_contents_change = false;
-                // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
                 ContentsChanged (); /* EMIT SIGNAL */
-                // cerr << _name << "done contents change @ " << get_microseconds() << endl;
         }
 
         for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
@@ -636,11 +618,12 @@ Playlist::flush_notifications (bool from_undo)
                 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
         }
 
-        for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
-                check_dependents (*s, false);
-        }
+        coalesce_and_check_crossfades (crossfade_ranges);
 
         if (!pending_range_moves.empty ()) {
+                /* We don't need to check crossfades for these as pending_bounds has
+                   already covered it.
+                */
                 RangesMoved (pending_range_moves, from_undo);
         }
 
@@ -668,10 +651,11 @@ Playlist::flush_notifications (bool from_undo)
    PLAYLIST OPERATIONS
   *************************************************************/
 
+/** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
  void
  Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
  {
-        RegionLock rlock (this);
+        RegionWriteLock rlock (this);
         times = fabs (times);
 
         int itimes = (int) floor (times);
@@ -684,6 +668,7 @@ Playlist::flush_notifications (bool from_undo)
 
         if (itimes >= 1) {
                 add_region_internal (region, pos);
+                set_layer (region, DBL_MAX);
                 pos += region->length();
                 --itimes;
         }
@@ -696,6 +681,7 @@ Playlist::flush_notifications (bool from_undo)
         for (int i = 0; i < itimes; ++i) {
                 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                 add_region_internal (copy, pos);
+                set_layer (copy, DBL_MAX);
                 pos += region->length();
         }
 
@@ -716,6 +702,7 @@ Playlist::flush_notifications (bool from_undo)
 
                         boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
                         add_region_internal (sub, pos);
+                        set_layer (sub, DBL_MAX);
                 }
         }
 
@@ -725,7 +712,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::set_region_ownership ()
  {
-        RegionLock rl (this);
+        RegionWriteLock rl (this);
         RegionList::iterator i;
         boost::weak_ptr<Playlist> pl (shared_from_this());
 
@@ -737,18 +724,12 @@ Playlist::flush_notifications (bool from_undo)
  bool
  Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
  {
-        if (region->data_type() != _type){
+        if (region->data_type() != _type) {
                 return false;
         }
 
         RegionSortByPosition cmp;
 
-        framecnt_t old_length = 0;
-
-        if (!holding_state()) {
-                 old_length = _get_extent().second;
-        }
-
         if (!first_set_state) {
                 boost::shared_ptr<Playlist> foo (shared_from_this());
                 region->set_playlist (boost::weak_ptr<Playlist>(foo));
@@ -756,8 +737,6 @@ Playlist::flush_notifications (bool from_undo)
 
         region->set_position (position);
 
-        timestamp_layer_op (region);
-
         regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
         all_regions.insert (region);
 
@@ -772,14 +751,8 @@ Playlist::flush_notifications (bool from_undo)
 
         notify_region_added (region);
 
-
         if (!holding_state ()) {
-
-                check_dependents (region, false);
-
-                if (old_length != _get_extent().second) {
-                        notify_length_changed ();
-                }
+                check_crossfades (region->range ());
         }
 
         region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
@@ -790,13 +763,14 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
  {
-        RegionLock rlock (this);
+        RegionWriteLock rlock (this);
 
         bool old_sp = _splicing;
         _splicing = true;
 
         remove_region_internal (old);
         add_region_internal (newr, pos);
+        set_layer (newr, old->layer ());
 
         _splicing = old_sp;
 
@@ -806,7 +780,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::remove_region (boost::shared_ptr<Region> region)
  {
-        RegionLock rlock (this);
+        RegionWriteLock rlock (this);
         remove_region_internal (region);
  }
 
@@ -814,11 +788,6 @@ Playlist::flush_notifications (bool from_undo)
  Playlist::remove_region_internal (boost::shared_ptr<Region> region)
  {
         RegionList::iterator i;
-        framecnt_t old_length = 0;
-
-        if (!holding_state()) {
-                old_length = _get_extent().second;
-        }
 
         if (!in_set_state) {
                 /* unset playlist */
@@ -840,10 +809,6 @@ Playlist::flush_notifications (bool from_undo)
                         if (!holding_state ()) {
                                 relayer ();
                                 remove_dependents (region);
-
-                                if (old_length != _get_extent().second) {
-                                        notify_length_changed ();
-                                }
                         }
 
                         notify_region_removed (region);
@@ -860,13 +825,13 @@ Playlist::flush_notifications (bool from_undo)
         if (Config->get_use_overlap_equivalency()) {
                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                         if ((*i)->overlap_equivalent (other)) {
-                                results.push_back ((*i));
+                                results.push_back (*i);
                         }
                 }
         } else {
                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                         if ((*i)->equivalent (other)) {
-                                results.push_back ((*i));
+                                results.push_back (*i);
                         }
                 }
         }
@@ -895,19 +860,24 @@ Playlist::flush_notifications (bool from_undo)
         }
  }
 
+/** Go through each region on the playlist and cut them at start and end, removing the section between
+ *  start and end if cutting == true.  Regions that lie entirely within start and end are always
+ *  removed.
+ */
+
  void
  Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
  {
         RegionList new_regions;
 
         {
-                RegionLock rlock (this);
+                RegionWriteLock rlock (this);
 
                 boost::shared_ptr<Region> region;
                 boost::shared_ptr<Region> current;
                 string new_name;
                 RegionList::iterator tmp;
-                OverlapType overlap;
+                Evoral::OverlapType overlap;
                 framepos_t pos1, pos2, pos3, pos4;
 
                 in_partition = true;
@@ -943,7 +913,7 @@ Playlist::flush_notifications (bool from_undo)
                                 continue;
                         }
 
-                        if ((overlap = current->coverage (start, end)) == OverlapNone) {
+                        if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
                                 continue;
                         }
 
@@ -952,7 +922,7 @@ Playlist::flush_notifications (bool from_undo)
                         pos3 = end;
                         pos4 = current->last_frame();
 
-                        if (overlap == OverlapInternal) {
+                        if (overlap == Evoral::OverlapInternal) {
                                 /* split: we need 3 new regions, the front, middle and end.
                                    cut:   we need 2 regions, the front and end.
                                 */
@@ -978,7 +948,8 @@ Playlist::flush_notifications (bool from_undo)
                                         plist.add (Properties::start, current->start() + (pos2 - pos1));
                                         plist.add (Properties::length, pos3 - pos2);
                                         plist.add (Properties::name, new_name);
-                                        plist.add (Properties::layer, regions.size());
+                                        plist.add (Properties::layer, current->layer ());
+                                        plist.add (Properties::layering_index, current->layering_index ());
                                         plist.add (Properties::automatic, true);
                                         plist.add (Properties::left_of_split, true);
                                         plist.add (Properties::right_of_split, true);
@@ -997,7 +968,8 @@ Playlist::flush_notifications (bool from_undo)
                                 plist.add (Properties::start, current->start() + (pos3 - pos1));
                                 plist.add (Properties::length, pos4 - pos3);
                                 plist.add (Properties::name, new_name);
-                                plist.add (Properties::layer, regions.size());
+                                plist.add (Properties::layer, current->layer ());
+                                plist.add (Properties::layering_index, current->layering_index ());
                                 plist.add (Properties::automatic, true);
                                 plist.add (Properties::right_of_split, true);
 
@@ -1012,7 +984,7 @@ Playlist::flush_notifications (bool from_undo)
                                 thawlist.push_back (current);
                                 current->cut_end (pos2 - 1);
 
-                        } else if (overlap == OverlapEnd) {
+                        } else if (overlap == Evoral::OverlapEnd) {
 
                                 /*
                                                               start           end
@@ -1035,7 +1007,8 @@ Playlist::flush_notifications (bool from_undo)
                                         plist.add (Properties::start, current->start() + (pos2 - pos1));
                                         plist.add (Properties::length, pos4 - pos2);
                                         plist.add (Properties::name, new_name);
-                                        plist.add (Properties::layer, regions.size());
+                                        plist.add (Properties::layer, current->layer ());
+                                        plist.add (Properties::layering_index, current->layering_index ());
                                         plist.add (Properties::automatic, true);
                                         plist.add (Properties::left_of_split, true);
 
@@ -1051,7 +1024,7 @@ Playlist::flush_notifications (bool from_undo)
                                 thawlist.push_back (current);
                                 current->cut_end (pos2 - 1);
 
-                        } else if (overlap == OverlapStart) {
+                        } else if (overlap == Evoral::OverlapStart) {
 
                                 /* split: we need 2 regions: the front and the end.
                                    cut: just trim current to skip the cut area
@@ -1078,7 +1051,8 @@ Playlist::flush_notifications (bool from_undo)
                                         plist.add (Properties::start, current->start());
                                         plist.add (Properties::length, pos3 - pos1);
                                         plist.add (Properties::name, new_name);
-                                        plist.add (Properties::layer, regions.size());
+                                        plist.add (Properties::layer, current->layer ());
+                                        plist.add (Properties::layering_index, current->layering_index ());
                                         plist.add (Properties::automatic, true);
                                         plist.add (Properties::right_of_split, true);
 
@@ -1093,7 +1067,7 @@ Playlist::flush_notifications (bool from_undo)
                                 current->suspend_property_changes ();
                                 thawlist.push_back (current);
                                 current->trim_front (pos3);
-                        } else if (overlap == OverlapExternal) {
+                        } else if (overlap == Evoral::OverlapExternal) {
 
                                 /* split: no split required.
                                    cut: remove the region.
@@ -1122,9 +1096,7 @@ Playlist::flush_notifications (bool from_undo)
                 in_partition = false;
         }
 
-        for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
-                check_dependents (*i, false);
-        }
+        check_crossfades (Evoral::Range<framepos_t> (start, end));
  }
 
  boost::shared_ptr<Playlist>
@@ -1219,38 +1191,29 @@ Playlist::flush_notifications (bool from_undo)
         times = fabs (times);
 
         {
-                RegionLock rl1 (this);
-                RegionLock rl2 (other.get());
-
-                framecnt_t const old_length = _get_extent().second;
+                RegionReadLock rl2 (other.get());
 
                 int itimes = (int) floor (times);
                 framepos_t pos = position;
                 framecnt_t const shift = other->_get_extent().second;
-                layer_t top_layer = regions.size();
-
-                while (itimes--) {
-                        for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
-                                boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
-
-                                /* put these new regions on top of all existing ones, but preserve
-                                   the ordering they had in the original playlist.
-                                */
+                layer_t top = top_layer ();
 
-                                copy_of_region->set_layer (copy_of_region->layer() + top_layer);
-                                add_region_internal (copy_of_region, (*i)->position() + pos);
+                {
+                        RegionWriteLock rl1 (this);
+                        while (itimes--) {
+                                for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
+                                        boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
+                                        
+                                        /* put these new regions on top of all existing ones, but preserve
+                                           the ordering they had in the original playlist.
+                                        */
+                                        
+                                        add_region_internal (copy_of_region, (*i)->position() + pos);
+                                        set_layer (copy_of_region, copy_of_region->layer() + top);
+                                }
+                                pos += shift;
                         }
-                        pos += shift;
                 }
-
-
-                /* XXX shall we handle fractional cases at some point? */
-
-                if (old_length != _get_extent().second) {
-                        notify_length_changed ();
-                }
-
-
         }
 
         return 0;
@@ -1262,13 +1225,14 @@ Playlist::flush_notifications (bool from_undo)
  {
         times = fabs (times);
 
-        RegionLock rl (this);
+        RegionWriteLock rl (this);
         int itimes = (int) floor (times);
         framepos_t pos = position + 1;
 
         while (itimes--) {
                 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                 add_region_internal (copy, pos);
+                set_layer (copy, DBL_MAX);
                 pos += region->length();
         }
 
@@ -1286,6 +1250,7 @@ Playlist::flush_notifications (bool from_undo)
 
                         boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
                         add_region_internal (sub, pos);
+                        set_layer (sub, DBL_MAX);
                 }
         }
  }
@@ -1293,7 +1258,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
  {
-        RegionLock rlock (this);
+        RegionWriteLock rlock (this);
         RegionList copy (regions.rlist());
         RegionList fixup;
 
@@ -1332,7 +1297,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::split (framepos_t at)
  {
-        RegionLock rlock (this);
+        RegionWriteLock rlock (this);
         RegionList copy (regions.rlist());
 
         /* use a copy since this operation can modify the region list
@@ -1346,7 +1311,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
  {
-        RegionLock rl (this);
+        RegionWriteLock rl (this);
         _split_region (region, playlist_position);
  }
 
@@ -1386,6 +1351,8 @@ Playlist::flush_notifications (bool from_undo)
                 plist.add (Properties::length, before);
                 plist.add (Properties::name, before_name);
                 plist.add (Properties::left_of_split, true);
+                plist.add (Properties::layering_index, region->layering_index ());
+                plist.add (Properties::layer, region->layer ());
 
                 /* note: we must use the version of ::create with an offset here,
                    since it supplies that offset to the Region constructor, which
@@ -1403,6 +1370,8 @@ Playlist::flush_notifications (bool from_undo)
                 plist.add (Properties::length, after);
                 plist.add (Properties::name, after_name);
                 plist.add (Properties::right_of_split, true);
+                plist.add (Properties::layering_index, region->layering_index ());
+                plist.add (Properties::layer, region->layer ());
 
                 /* same note as above */
                 right = RegionFactory::create (region, before, plist);
@@ -1410,21 +1379,6 @@ Playlist::flush_notifications (bool from_undo)
 
         add_region_internal (left, region->position());
         add_region_internal (right, region->position() + before);
-
-        uint64_t orig_layer_op = region->last_layer_op();
-        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                if ((*i)->last_layer_op() > orig_layer_op) {
-                        (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
-                }
-        }
-
-        left->set_last_layer_op ( orig_layer_op );
-        right->set_last_layer_op ( orig_layer_op + 1);
-
-        layer_op_counter++;
-
-        finalize_split_region (region, left, right);
-
         remove_region_internal (region);
 
         _splicing = old_sp;
@@ -1460,7 +1414,7 @@ Playlist::flush_notifications (bool from_undo)
  Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
  {
         {
-                RegionLock rl (this);
+                RegionWriteLock rl (this);
                 core_splice (at, distance, exclude);
         }
  }
@@ -1496,7 +1450,7 @@ Playlist::flush_notifications (bool from_undo)
 
         _splicing = false;
 
-        notify_length_changed ();
+        notify_contents_changed ();
  }
 
  void
@@ -1547,14 +1501,12 @@ Playlist::flush_notifications (bool from_undo)
                 if (holding_state ()) {
                         pending_bounds.push_back (region);
                 } else {
-                        if (_session.config.get_layer_model() == MoveAddHigher) {
-                                /* it moved or changed length, so change the timestamp */
-                                timestamp_layer_op (region);
-                        }
-
-                        notify_length_changed ();
+                        notify_contents_changed ();
                         relayer ();
-                        check_dependents (region, false);
+                        list<Evoral::Range<framepos_t> > xf;
+                        xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
+                        xf.push_back (Evoral::Range<framepos_t> (region->range()));
+                        coalesce_and_check_crossfades (xf);
                 }
         }
  }
@@ -1602,7 +1554,7 @@ Playlist::flush_notifications (bool from_undo)
         }
 
         if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
-                check_dependents (region, false);
+                check_crossfades (region->range ());
         }
 
         if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
@@ -1627,7 +1579,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::drop_regions ()
  {
-        RegionLock rl (this);
+        RegionWriteLock rl (this);
         regions.clear ();
         all_regions.clear ();
  }
@@ -1635,7 +1587,7 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::sync_all_regions_with_regions ()
  {
-        RegionLock rl (this);
+        RegionWriteLock rl (this);
 
         all_regions.clear ();
 
@@ -1648,7 +1600,7 @@ Playlist::flush_notifications (bool from_undo)
  Playlist::clear (bool with_signals)
  {
         {
-                RegionLock rl (this);
+                RegionWriteLock rl (this);
 
                 region_state_changed_connections.drop_connections ();
 
@@ -1680,18 +1632,17 @@ Playlist::flush_notifications (bool from_undo)
   FINDING THINGS
   **********************************************************************/
 
- Playlist::RegionList *
- Playlist::regions_at (framepos_t frame)
-
- {
-        RegionLock rlock (this);
-        return find_regions_at (frame);
- }
+boost::shared_ptr<RegionList>
+Playlist::regions_at (framepos_t frame)
+{
+       RegionReadLock rlock (this);
+       return find_regions_at (frame);
+}
 
  uint32_t
  Playlist::count_regions_at (framepos_t frame) const
  {
-        RegionLock rlock (const_cast<Playlist*>(this));
+        RegionReadLock rlock (const_cast<Playlist*>(this));
         uint32_t cnt = 0;
 
         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -1707,8 +1658,8 @@ Playlist::flush_notifications (bool from_undo)
  Playlist::top_region_at (framepos_t frame)
 
  {
-        RegionLock rlock (this);
-        RegionList *rlist = find_regions_at (frame);
+        RegionReadLock rlock (this);
+        boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
         boost::shared_ptr<Region> region;
 
         if (rlist->size()) {
@@ -1717,7 +1668,6 @@ Playlist::flush_notifications (bool from_undo)
                 region = rlist->back();
         }
 
-        delete rlist;
         return region;
  }
 
@@ -1725,8 +1675,8 @@ Playlist::flush_notifications (bool from_undo)
  Playlist::top_unmuted_region_at (framepos_t frame)
 
  {
-        RegionLock rlock (this);
-        RegionList *rlist = find_regions_at (frame);
+        RegionReadLock rlock (this);
+        boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
 
         for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
 
@@ -1748,301 +1698,204 @@ Playlist::flush_notifications (bool from_undo)
                 region = rlist->back();
         }
 
-        delete rlist;
         return region;
  }
 
- Playlist::RegionList*
- Playlist::regions_to_read (framepos_t start, framepos_t end)
- {
-        /* Caller must hold lock */
-
-        RegionList covering;
-        set<framepos_t> to_check;
-        set<boost::shared_ptr<Region> > unique;
-
-        to_check.insert (start);
-        to_check.insert (end);
-
-        DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
-
-        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
-                /* find all/any regions that span start+end */
-
-                switch ((*i)->coverage (start, end)) {
-                case OverlapNone:
-                        break;
-
-                case OverlapInternal:
-                        covering.push_back (*i);
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
-                        break;
-
-                case OverlapStart:
-                        to_check.insert ((*i)->position());
-                        if ((*i)->position() != 0) {
-                                to_check.insert ((*i)->position()-1);
-                        }
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
-                        covering.push_back (*i);
-                        break;
-
-                case OverlapEnd:
-                        to_check.insert ((*i)->last_frame());
-                        to_check.insert ((*i)->last_frame()+1);
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
-                        covering.push_back (*i);
-                        break;
-
-                case OverlapExternal:
-                        covering.push_back (*i);
-                        to_check.insert ((*i)->position());
-                        if ((*i)->position() != 0) {
-                                to_check.insert ((*i)->position()-1);
-                        }
-                        to_check.insert ((*i)->last_frame());
-                        to_check.insert ((*i)->last_frame()+1);
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
-                        break;
-                }
-
-                /* don't go too far */
-
-                if ((*i)->position() > end) {
-                        break;
-                }
-        }
-
-        RegionList* rlist = new RegionList;
-
-        /* find all the regions that cover each position .... */
-
-        if (covering.size() == 1) {
-
-                rlist->push_back (covering.front());
-                DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
-
-        } else {
-
-                RegionList here;
-                for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
-
-                        here.clear ();
-
-                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
-
-                        for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
-
-                                if ((*x)->covers (*t)) {
-                                        here.push_back (*x);
-                                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
-                                                                                           (*x)->name(),
-                                                                                           (*t)));
-                                } else {
-                                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
-                                                                                           (*x)->name(),
-                                                                                           (*t)));
-                                }
-
-                        }
-
-                        RegionSortByLayer cmp;
-                        here.sort (cmp);
-
-                        /* ... and get the top/transparent regions at "here" */
-
-                        for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
-
-                                unique.insert (*c);
-
-                                if ((*c)->opaque()) {
-
-                                        /* the other regions at this position are hidden by this one */
-                                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
-                                                                                           (*c)->name()));
-                                        break;
-                                }
-                        }
-                }
-
-                for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
-                        rlist->push_back (*s);
-                }
-
-                if (rlist->size() > 1) {
-                        /* now sort by time order */
-
-                        RegionSortByPosition cmp;
-                        rlist->sort (cmp);
-                }
-        }
-
-        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
-
-        return rlist;
- }
-
- Playlist::RegionList *
- Playlist::find_regions_at (framepos_t frame)
- {
-        /* Caller must hold lock */
-
-        RegionList *rlist = new RegionList;
-
-        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                if ((*i)->covers (frame)) {
-                        rlist->push_back (*i);
-                }
-        }
-
-        return rlist;
- }
-
- Playlist::RegionList *
- Playlist::regions_touched (framepos_t start, framepos_t end)
- {
-        RegionLock rlock (this);
-        RegionList *rlist = new RegionList;
-
-        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                if ((*i)->coverage (start, end) != OverlapNone) {
-                        rlist->push_back (*i);
-                }
-        }
-
-        return rlist;
- }
-
- framepos_t
- Playlist::find_next_transient (framepos_t from, int dir)
- {
-        RegionLock rlock (this);
-        AnalysisFeatureList points;
-        AnalysisFeatureList these_points;
-
-        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                if (dir > 0) {
-                        if ((*i)->last_frame() < from) {
-                                continue;
-                        }
-                } else {
-                        if ((*i)->first_frame() > from) {
-                                continue;
-                        }
-                }
-
-                (*i)->get_transients (these_points);
-
-                /* add first frame, just, err, because */
-
-                these_points.push_back ((*i)->first_frame());
-
-                points.insert (points.end(), these_points.begin(), these_points.end());
-                these_points.clear ();
-        }
-
-        if (points.empty()) {
-                return -1;
-        }
-
-        TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
-        bool reached = false;
-
-        if (dir > 0) {
-                for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
-                        if ((*x) >= from) {
-                                reached = true;
-                        }
-
-                        if (reached && (*x) > from) {
-                                return *x;
-                        }
-                }
-        } else {
-                for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
-                        if ((*x) <= from) {
-                                reached = true;
-                        }
-
-                        if (reached && (*x) < from) {
-                                return *x;
-                        }
-                }
-        }
-
-        return -1;
- }
-
- boost::shared_ptr<Region>
- Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
- {
-        RegionLock rlock (this);
-        boost::shared_ptr<Region> ret;
-        framepos_t closest = max_framepos;
-
-        bool end_iter = false;
-
-        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+boost::shared_ptr<RegionList>
+Playlist::find_regions_at (framepos_t frame)
+{
+       /* Caller must hold lock */
+       
+       boost::shared_ptr<RegionList> rlist (new RegionList);
+       
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->covers (frame)) {
+                       rlist->push_back (*i);
+               }
+       }
+       
+       return rlist;
+}
 
-                if(end_iter) break;
+boost::shared_ptr<RegionList>
+Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
+{
+       RegionReadLock rlock (this);
+       boost::shared_ptr<RegionList> rlist (new RegionList);
 
-                frameoffset_t distance;
-                boost::shared_ptr<Region> r = (*i);
-                framepos_t pos = 0;
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
+                       rlist->push_back (*i);
+               }
+       }
 
-                switch (point) {
-                case Start:
-                        pos = r->first_frame ();
-                        break;
-                case End:
-                        pos = r->last_frame ();
-                        break;
-                case SyncPoint:
-                        pos = r->sync_position ();
-                        break;
-                }
+       return rlist;
+}
 
-                switch (dir) {
-                case 1: /* forwards */
+boost::shared_ptr<RegionList>
+Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
+{
+       RegionReadLock rlock (this);
+       boost::shared_ptr<RegionList> rlist (new RegionList);
 
-                        if (pos > frame) {
-                                if ((distance = pos - frame) < closest) {
-                                        closest = distance;
-                                        ret = r;
-                                        end_iter = true;
-                                }
-                        }
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
+                       rlist->push_back (*i);
+               }
+       }
 
-                        break;
+       return rlist;
+}
 
-                default: /* backwards */
+/** @param start Range start.
+ *  @param end Range end.
+ *  @return regions which have some part within this range.
+ */
+boost::shared_ptr<RegionList>
+Playlist::regions_touched (framepos_t start, framepos_t end)
+{
+       RegionReadLock rlock (this);
+       return regions_touched_locked (start, end);
+}
 
-                        if (pos < frame) {
-                                if ((distance = frame - pos) < closest) {
-                                        closest = distance;
-                                        ret = r;
-                                }
-                        }
-                        else {
-                                end_iter = true;
-                        }
+boost::shared_ptr<RegionList>
+Playlist::regions_touched_locked (framepos_t start, framepos_t end)
+{
+       boost::shared_ptr<RegionList> rlist (new RegionList);
+       
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
+                       rlist->push_back (*i);
+               }
+       }
+       
+       return rlist;
+}
 
-                        break;
-                }
-        }
+framepos_t
+Playlist::find_next_transient (framepos_t from, int dir)
+{
+       RegionReadLock rlock (this);
+       AnalysisFeatureList points;
+       AnalysisFeatureList these_points;
+       
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if (dir > 0) {
+                       if ((*i)->last_frame() < from) {
+                               continue;
+                       }
+               } else {
+                       if ((*i)->first_frame() > from) {
+                               continue;
+                       }
+               }
+               
+               (*i)->get_transients (these_points);
+               
+               /* add first frame, just, err, because */
+               
+               these_points.push_back ((*i)->first_frame());
+               
+               points.insert (points.end(), these_points.begin(), these_points.end());
+               these_points.clear ();
+       }
+       
+       if (points.empty()) {
+               return -1;
+       }
+       
+       TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
+       bool reached = false;
+       
+       if (dir > 0) {
+               for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
+                       if ((*x) >= from) {
+                               reached = true;
+                       }
+                       
+                       if (reached && (*x) > from) {
+                               return *x;
+                       }
+               }
+       } else {
+               for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
+                       if ((*x) <= from) {
+                               reached = true;
+                       }
+                       
+                       if (reached && (*x) < from) {
+                               return *x;
+                       }
+               }
+       }
+       
+       return -1;
+}
 
-        return ret;
- }
+boost::shared_ptr<Region>
+Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
+{
+       RegionReadLock rlock (this);
+       boost::shared_ptr<Region> ret;
+       framepos_t closest = max_framepos;
+       
+       bool end_iter = false;
+       
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               
+               if(end_iter) break;
+               
+               frameoffset_t distance;
+               boost::shared_ptr<Region> r = (*i);
+               framepos_t pos = 0;
+               
+               switch (point) {
+               case Start:
+                       pos = r->first_frame ();
+                       break;
+               case End:
+                       pos = r->last_frame ();
+                       break;
+               case SyncPoint:
+                       pos = r->sync_position ();
+                       break;
+               }
+               
+               switch (dir) {
+               case 1: /* forwards */
+                       
+                       if (pos > frame) {
+                               if ((distance = pos - frame) < closest) {
+                                       closest = distance;
+                                       ret = r;
+                                       end_iter = true;
+                               }
+                       }
+                       
+                       break;
+                       
+               default: /* backwards */
+                       
+                       if (pos < frame) {
+                               if ((distance = frame - pos) < closest) {
+                                       closest = distance;
+                                       ret = r;
+                               }
+                       } else {
+                               end_iter = true;
+                       }
+                       
+                       break;
+               }
+       }
+       
+       return ret;
+}
 
  framepos_t
  Playlist::find_next_region_boundary (framepos_t frame, int dir)
  {
-        RegionLock rlock (this);
+        RegionReadLock rlock (this);
 
         framepos_t closest = max_framepos;
         framepos_t ret = -1;
@@ -2124,14 +1977,14 @@ Playlist::flush_notifications (bool from_undo)
  void
  Playlist::rdiff (vector<Command*>& cmds) const
  {
-        RegionLock rlock (const_cast<Playlist *> (this));
+        RegionReadLock rlock (const_cast<Playlist *> (this));
         Stateful::rdiff (cmds);
  }
 
  void
  Playlist::clear_owned_changes ()
  {
-        RegionLock rlock (this);
+        RegionReadLock rlock (this);
         Stateful::clear_owned_changes ();
  }
 
@@ -2144,7 +1997,7 @@ Playlist::flush_notifications (bool from_undo)
         freeze ();
         /* add the added regions */
         for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
-                add_region ((*i), (*i)->position());
+                add_region_internal ((*i), (*i)->position());
         }
         /* remove the removed regions */
         for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
@@ -2235,10 +2088,11 @@ Playlist::flush_notifications (bool from_undo)
                                return -1;
                        }
 
-                       add_region (region, region->position(), 1.0);
+                        {
+                                RegionWriteLock rlock (this);
+                                add_region_internal (region, region->position());
+                        }
                        
-                       // So that layer_op ordering doesn't get screwed up
-                       region->set_last_layer_op( region->layer());
                        region->resume_property_changes ();
 
                }
@@ -2253,7 +2107,7 @@ Playlist::flush_notifications (bool from_undo)
                */
                
                for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
-                       check_dependents (*r, false);
+                       check_crossfades ((*r)->range ());
                }
        }
                
@@ -2262,6 +2116,7 @@ Playlist::flush_notifications (bool from_undo)
 
        in_set_state--;
        first_set_state = false;
+
        return ret;
 }
 
@@ -2294,7 +2149,7 @@ Playlist::state (bool full_state)
        node->add_property (X_("frozen"), _frozen ? "yes" : "no");
 
        if (full_state) {
-               RegionLock rlock (this, false);
+               RegionReadLock rlock (this);
 
                snprintf (buf, sizeof (buf), "%u", _combine_ops);
                node->add_property ("combine-ops", buf);
@@ -2314,21 +2169,21 @@ Playlist::state (bool full_state)
 bool
 Playlist::empty() const
 {
-       RegionLock rlock (const_cast<Playlist *>(this), false);
+       RegionReadLock rlock (const_cast<Playlist *>(this));
        return regions.empty();
 }
 
 uint32_t
 Playlist::n_regions() const
 {
-       RegionLock rlock (const_cast<Playlist *>(this), false);
+       RegionReadLock rlock (const_cast<Playlist *>(this));
        return regions.size();
 }
 
 pair<framepos_t, framepos_t>
 Playlist::get_extent () const
 {
-       RegionLock rlock (const_cast<Playlist *>(this), false);
+       RegionReadLock rlock (const_cast<Playlist *>(this));
        return _get_extent ();
 }
 
@@ -2371,7 +2226,7 @@ Playlist::bump_name (string name, Session &session)
 layer_t
 Playlist::top_layer() const
 {
-       RegionLock rlock (const_cast<Playlist *> (this));
+       RegionReadLock rlock (const_cast<Playlist *> (this));
        layer_t top = 0;
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -2386,21 +2241,62 @@ Playlist::set_edit_mode (EditMode mode)
        _edit_mode = mode;
 }
 
-/********************
- * Region Layering
- ********************/
+struct RelayerSort {
+       bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+               return a->layering_index() < b->layering_index();
+       }
+};
+
+/** Set a new layer for a region.  This adjusts the layering indices of all
+ *  regions in the playlist to put the specified region in the appropriate
+ *  place.  The actual layering will be fixed up when relayer() happens.
+ */
 
 void
+Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
+{
+       /* Remove the layer we are setting from our region list, and sort it */
+       RegionList copy = regions.rlist();
+       copy.remove (region);
+       copy.sort (RelayerSort ());
+
+       /* Put region back in the right place */
+       RegionList::iterator i = copy.begin();
+       while (i != copy.end ()) {
+               if ((*i)->layer() > new_layer) {
+                       break;
+               }
+               ++i;
+       }
+       
+       copy.insert (i, region);
+
+       setup_layering_indices (copy);
+}
+
+void
+Playlist::setup_layering_indices (RegionList const & regions)
+{
+       uint64_t j = 0;
+       list<Evoral::Range<framepos_t> > xf;
+
+       for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
+               (*k)->set_layering_index (j++);
+       }
+}
+
+/** Take the layering indices of each of our regions, compute the layers
+ *  that they should be on, and write the layers back to the regions.
+ */
+void
 Playlist::relayer ()
 {
-       /* never compute layers when changing state for undo/redo or setting from XML */
+       /* never compute layers when setting from XML */
 
-       if (in_update || in_set_state) {
+       if (in_set_state) {
                return;
        }
 
-       bool changed = false;
-
        /* Build up a new list of regions on each layer, stored in a set of lists
           each of which represent some period of time on some layer.  The idea
           is to avoid having to search the entire region list to establish whether
@@ -2423,30 +2319,17 @@ Playlist::relayer ()
        vector<vector<RegionList> > layers;
        layers.push_back (vector<RegionList> (divisions));
 
-       /* we want to go through regions from desired lowest to desired highest layer,
-          which depends on the layer model
-       */
-
+       /* Sort our regions into layering index order */
        RegionList copy = regions.rlist();
+       copy.sort (RelayerSort ());
 
-       /* sort according to the model and the layering mode that we're in */
-
-       if (_explicit_relayering) {
-
-               copy.sort (RegionSortByLayerWithPending ());
-
-       } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
-
-               copy.sort (RegionSortByLastLayerOp ());
-
+       DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
+       for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
+               DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
        }
 
-
        for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
 
-               /* reset the pending explicit relayer flag for every region, now that we're relayering */
-               (*i)->set_pending_explicit_relayer (false);
-
                /* find the time divisions that this region covers; if there are no regions on the list,
                   division_size will equal 0 and in this case we'll just say that
                   start_division = end_division = 0.
@@ -2505,170 +2388,54 @@ Playlist::relayer ()
                        layers[j][k].push_back (*i);
                }
 
-               if ((*i)->layer() != j) {
-                       changed = true;
-               }
-
                (*i)->set_layer (j);
        }
 
-       if (changed) {
-               notify_layering_changed ();
-       }
-}
+       /* It's a little tricky to know when we could avoid calling this; e.g. if we are
+          relayering because we just removed the only region on the top layer, nothing will
+          appear to have changed, but the StreamView must still sort itself out.  We could
+          probably keep a note of the top layer last time we relayered, and check that,
+          but premature optimisation &c...
+       */
+       notify_layering_changed ();
 
-/* XXX these layer functions are all deprecated */
+       /* This relayer() may have been called as a result of a region removal, in which
+          case we need to setup layering indices to account for the one that has just
+          gone away.
+       */
+       setup_layering_indices (copy);
+}
 
 void
 Playlist::raise_region (boost::shared_ptr<Region> region)
 {
-       uint32_t top = regions.size() - 1;
-       layer_t target = region->layer() + 1U;
-
-       if (target >= top) {
-               /* its already at the effective top */
-               return;
-       }
-
-       move_region_to_layer (target, region, 1);
+       set_layer (region, region->layer() + 1.5);
+       relayer ();
+       check_crossfades (region->range ());
 }
 
 void
 Playlist::lower_region (boost::shared_ptr<Region> region)
 {
-       if (region->layer() == 0) {
-               /* its already at the bottom */
-               return;
-       }
-
-       layer_t target = region->layer() - 1U;
-
-       move_region_to_layer (target, region, -1);
+       set_layer (region, region->layer() - 1.5);
+       relayer ();
+       check_crossfades (region->range ());
 }
 
 void
 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
 {
-       /* does nothing useful if layering mode is later=higher */
-       switch (_session.config.get_layer_model()) {
-       case LaterHigher:
-               return;
-       default:
-               break;
-       }
-
-       layer_t top = regions.size() - 1;
-
-       if (region->layer() >= top) {
-               /* already on the top */
-               return;
-       }
-
-       move_region_to_layer (top, region, 1);
-       /* mark the region's last_layer_op as now, so that it remains on top when
-          doing future relayers (until something else takes over)
-        */
-       timestamp_layer_op (region);
+       set_layer (region, DBL_MAX);
+       relayer ();
+       check_crossfades (region->range ());
 }
 
 void
 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
 {
-       /* does nothing useful if layering mode is later=higher */
-       switch (_session.config.get_layer_model()) {
-       case LaterHigher:
-               return;
-       default:
-               break;
-       }
-
-       if (region->layer() == 0) {
-               /* already on the bottom */
-               return;
-       }
-
-       move_region_to_layer (0, region, -1);
-       /* force region's last layer op to zero so that it stays at the bottom
-          when doing future relayers
-       */
-       region->set_last_layer_op (0);
-}
-
-int
-Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
-{
-       RegionList::iterator i;
-       typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
-       list<LayerInfo> layerinfo;
-
-       {
-               RegionLock rlock (const_cast<Playlist *> (this));
-
-               for (i = regions.begin(); i != regions.end(); ++i) {
-
-                       if (region == *i) {
-                               continue;
-                       }
-
-                       layer_t dest;
-
-                       if (dir > 0) {
-
-                               /* region is moving up, move all regions on intermediate layers
-                                  down 1
-                               */
-
-                               if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
-                                       dest = (*i)->layer() - 1;
-                               } else {
-                                       /* not affected */
-                                       continue;
-                               }
-                       } else {
-
-                               /* region is moving down, move all regions on intermediate layers
-                                  up 1
-                               */
-
-                               if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
-                                       dest = (*i)->layer() + 1;
-                               } else {
-                                       /* not affected */
-                                       continue;
-                               }
-                       }
-
-                       LayerInfo newpair;
-
-                       newpair.first = *i;
-                       newpair.second = dest;
-
-                       layerinfo.push_back (newpair);
-               }
-       }
-
-       freeze ();
-
-       /* now reset the layers without holding the region lock */
-
-       for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
-               x->first->set_layer (x->second);
-       }
-
-       region->set_layer (target_layer);
-
-       /* now check all dependents, since we changed the layering */
-
-       for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
-               check_dependents (x->first, false);
-       }
-
-       check_dependents (region, false);
-       notify_layering_changed ();
-
-       thaw ();
-
-       return 0;
+       set_layer (region, -0.5);
+       relayer ();
+       check_crossfades (region->range ());
 }
 
 void
@@ -2680,7 +2447,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
        _nudging = true;
 
        {
-               RegionLock rlock (const_cast<Playlist *> (this));
+               RegionWriteLock rlock (const_cast<Playlist *> (this));
 
                for (i = regions.begin(); i != regions.end(); ++i) {
 
@@ -2713,7 +2480,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
 
        if (moved) {
                _nudging = false;
-               notify_length_changed ();
+               notify_contents_changed ();
        }
 
 }
@@ -2721,7 +2488,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
 bool
 Playlist::uses_source (boost::shared_ptr<const Source> src) const
 {
-       RegionLock rlock (const_cast<Playlist*> (this));
+       RegionReadLock rlock (const_cast<Playlist*> (this));
 
        for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
                if ((*r)->uses_source (src)) {
@@ -2735,7 +2502,7 @@ Playlist::uses_source (boost::shared_ptr<const Source> src) const
 boost::shared_ptr<Region>
 Playlist::find_region (const ID& id) const
 {
-       RegionLock rlock (const_cast<Playlist*> (this));
+       RegionReadLock rlock (const_cast<Playlist*> (this));
 
        /* searches all regions currently in use by the playlist */
 
@@ -2751,7 +2518,7 @@ Playlist::find_region (const ID& id) const
 uint32_t
 Playlist::region_use_count (boost::shared_ptr<Region> r) const
 {
-       RegionLock rlock (const_cast<Playlist*> (this));
+       RegionReadLock rlock (const_cast<Playlist*> (this));
        uint32_t cnt = 0;
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -2803,13 +2570,6 @@ Playlist::set_frozen (bool yn)
        _frozen = yn;
 }
 
-void
-Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
-{
-       region->set_last_layer_op (++layer_op_counter);
-}
-
-
 void
 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
 {
@@ -2822,7 +2582,7 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
        _shuffling = true;
 
        {
-               RegionLock rlock (const_cast<Playlist*> (this));
+               RegionWriteLock rlock (const_cast<Playlist*> (this));
 
 
                if (dir > 0) {
@@ -2919,8 +2679,6 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
        if (moved) {
 
                relayer ();
-               check_dependents (region, false);
-
                notify_contents_changed();
        }
 
@@ -2929,7 +2687,7 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
 bool
 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
 {
-       RegionLock rlock (const_cast<Playlist*> (this));
+       RegionReadLock rlock (const_cast<Playlist*> (this));
 
        if (regions.size() > 1) {
                return true;
@@ -2941,7 +2699,7 @@ Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
 void
 Playlist::update_after_tempo_map_change ()
 {
-       RegionLock rlock (const_cast<Playlist*> (this));
+       RegionWriteLock rlock (const_cast<Playlist*> (this));
        RegionList copy (regions.rlist());
 
        freeze ();
@@ -2956,39 +2714,16 @@ Playlist::update_after_tempo_map_change ()
 void
 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
 {
-       RegionLock rl (this, false);
+       RegionWriteLock rl (this, false);
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                s (*i);
        }
 }
 
-void
-Playlist::set_explicit_relayering (bool e)
-{
-       if (e == false && _explicit_relayering == true) {
-
-               /* We are changing from explicit to implicit relayering; layering may have been changed whilst
-                  we were in explicit mode, and we don't want that to be undone next time an implicit relayer
-                  occurs.  Hence now we'll set up region last_layer_op values so that an implicit relayer
-                  at this point would keep regions on the same layers.
-
-                  From then on in, it's just you and your towel.
-               */
-
-               RegionLock rl (this);
-               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                       (*i)->set_last_layer_op ((*i)->layer ());
-               }
-       }
-
-       _explicit_relayering = e;
-}
-
-
 bool
 Playlist::has_region_at (framepos_t const p) const
 {
-       RegionLock (const_cast<Playlist *> (this));
+       RegionReadLock (const_cast<Playlist *> (this));
 
        RegionList::const_iterator i = regions.begin ();
        while (i != regions.end() && !(*i)->covers (p)) {
@@ -3002,7 +2737,7 @@ Playlist::has_region_at (framepos_t const p) const
 void
 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
 {
-       RegionLock rl (this);
+       RegionWriteLock rl (this);
 
        RegionList::iterator i = regions.begin();
        while (i != regions.end()) {
@@ -3025,7 +2760,7 @@ Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
 framepos_t
 Playlist::find_next_top_layer_position (framepos_t t) const
 {
-       RegionLock rlock (const_cast<Playlist *> (this));
+       RegionReadLock rlock (const_cast<Playlist *> (this));
 
        layer_t const top = top_layer ();
 
@@ -3106,10 +2841,6 @@ Playlist::combine (const RegionList& r)
 
        pre_combine (copies);
 
-       /* add any dependent regions to the new playlist */
-
-       copy_dependents (old_and_new_regions, pl.get());
-
        /* now create a new PlaylistSource for each channel in the new playlist */
 
        SourceList sources;
@@ -3251,13 +2982,13 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                modified_region = false;
 
                switch (original->coverage (adjusted_start, adjusted_end)) {
-               case OverlapNone:
+               case Evoral::OverlapNone:
                        /* original region does not cover any part
                           of the current state of the compound region
                        */
                        continue;
 
-               case OverlapInternal:
+               case Evoral::OverlapInternal:
                        /* overlap is just a small piece inside the
                         * original so trim both ends
                         */
@@ -3265,13 +2996,13 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                        modified_region = true;
                        break;
 
-               case OverlapExternal:
+               case Evoral::OverlapExternal:
                        /* overlap fully covers original, so leave it
                           as is
                        */
                        break;
 
-               case OverlapEnd:
+               case Evoral::OverlapEnd:
                        /* overlap starts within but covers end,
                           so trim the front of the region
                        */
@@ -3279,7 +3010,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                        modified_region = true;
                        break;
 
-               case OverlapStart:
+               case Evoral::OverlapStart:
                        /* overlap covers start but ends within, so
                         * trim the end of the region.
                         */
@@ -3322,10 +3053,6 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                add_region ((*i), (*i)->position());
        }
 
-       /* now move dependent regions back from the compound to this playlist */
-
-       pl->copy_dependents (old_and_new_regions, this);
-
        in_partition = false;
        thaw ();
 }
@@ -3333,7 +3060,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
 uint32_t
 Playlist::max_source_level () const
 {
-       RegionLock rlock (const_cast<Playlist *> (this));
+       RegionReadLock rlock (const_cast<Playlist *> (this));
        uint32_t lvl = 0;
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -3343,24 +3070,48 @@ Playlist::max_source_level () const
        return lvl;
 }
 
+void
+Playlist::set_orig_track_id (const PBD::ID& id)
+{
+       _orig_track_id = id;
+}
 
-uint32_t
-Playlist::count_joined_regions () const
+/** Take a list of ranges, coalesce any that can be coalesced, then call
+ *  check_crossfades for each one.
+ */
+void
+Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
 {
-       RegionLock rlock (const_cast<Playlist *> (this));
-       uint32_t cnt = 0;
+       /* XXX: it's a shame that this coalesce algorithm also exists in
+          TimeSelection::consolidate().
+       */
 
-       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               if ((*i)->max_source_level() > 0) {
-                       cnt++;
+       /* XXX: xfade: this is implemented in Evoral::RangeList */
+
+restart:
+       for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+               for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
+
+                       if (i == j) {
+                               continue;
+                       }
+
+                       if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
+                               i->from = min (i->from, j->from);
+                               i->to = max (i->to, j->to);
+                               ranges.erase (j);
+                               goto restart;
+                       }
                }
        }
 
-       return cnt;
+       for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+               check_crossfades (*i);
+       }
 }
 
 void
-Playlist::set_orig_track_id (const PBD::ID& id)
+Playlist::set_capture_insertion_in_progress (bool yn)
 {
-       _orig_track_id = id;
+       _capture_insertion_underway = yn;
 }