X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fplaylist.cc;h=bb79801c9f0b45342f7de4e3d3650aa19d082af3;hb=16a8762dd494adb68756b8d549f349ed7bdccd9b;hp=624ad445101d13028bd364afda129ed227352bab;hpb=1ac41882d3ac4b7dc380095af09b7db84f98205a;p=ardour.git diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 624ad44510..bb79801c9f 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -19,18 +19,12 @@ #include #include -#include #include -#include -#include #include -#include #include #include "pbd/convert.h" -#include "pbd/failed_constructor.h" -#include "pbd/stacktrace.h" #include "pbd/stateful_diff_command.h" #include "pbd/xml++.h" @@ -53,9 +47,9 @@ using namespace ARDOUR; using namespace PBD; namespace ARDOUR { -namespace Properties { -PBD::PropertyDescriptor regions; -} + namespace Properties { + PBD::PropertyDescriptor regions; + } } struct ShowMeTheList { @@ -187,9 +181,6 @@ Playlist::Playlist (boost::shared_ptr 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 other, framepos_t start, framecnt_t cnt, string str, bool hide) @@ -198,7 +189,7 @@ Playlist::Playlist (boost::shared_ptr other, framepos_t start, f , _type(other->_type) , _orig_track_id (other->_orig_track_id) { - RegionLock rlock2 (const_cast (other.get())); + RegionReadLock rlock2 (const_cast (other.get())); framepos_t end = start + cnt - 1; @@ -214,35 +205,35 @@ Playlist::Playlist (boost::shared_ptr 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(); @@ -257,8 +248,9 @@ Playlist::Playlist (boost::shared_ptr 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); + new_region = RegionFactory::create (region, plist); add_region_internal (new_region, position); } @@ -289,10 +281,10 @@ Playlist::release () void Playlist::copy_regions (RegionList& newlist) const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - newlist.push_back (RegionFactory::RegionFactory::create (*i, true)); + newlist.push_back (RegionFactory::create (*i, true)); } } @@ -313,16 +305,14 @@ 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; + _capture_insertion_underway = false; _combine_ops = 0; - _relayer_suspended = false; _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this)); _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this)); @@ -335,7 +325,7 @@ Playlist::~Playlist () DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name)); { - RegionLock rl (this); + RegionReadLock rl (this); for (set >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) { (*i)->set_playlist (boost::shared_ptr()); @@ -402,7 +392,7 @@ Playlist::set_name (const string& str) void Playlist::begin_undo () { - in_update = true; + in_undo = true; freeze (); } @@ -410,7 +400,7 @@ void Playlist::end_undo () { thaw (true); - in_update = false; + in_undo = false; } void @@ -433,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 */ @@ -488,8 +477,6 @@ Playlist::notify_region_moved (boost::shared_ptr r) { Evoral::RangeMove const move (r->last_position (), r->length (), r->position ()); - /* We could timestamp the region's layer op here, but we're doing it in region_bounds_changed */ - if (holding_state ()) { pending_range_moves.push_back (move); @@ -506,8 +493,6 @@ Playlist::notify_region_moved (boost::shared_ptr r) void Playlist::notify_region_start_trimmed (boost::shared_ptr r) { - timestamp_layer_op (LayerOpBoundsChange, r); - if (r->position() >= r->last_position()) { /* trimmed shorter */ return; @@ -531,8 +516,6 @@ Playlist::notify_region_start_trimmed (boost::shared_ptr r) void Playlist::notify_region_end_trimmed (boost::shared_ptr r) { - timestamp_layer_op (LayerOpBoundsChange, r); - if (r->length() < r->last_length()) { /* trimmed shorter */ } @@ -559,8 +542,6 @@ Playlist::notify_region_added (boost::shared_ptr r) as though it could be. */ - timestamp_layer_op (LayerOpAdd, r); - if (holding_state()) { pending_adds.insert (r); pending_contents_change = true; @@ -569,7 +550,6 @@ Playlist::notify_region_added (boost::shared_ptr r) pending_contents_change = false; RegionAdded (boost::weak_ptr (r)); /* EMIT SIGNAL */ ContentsChanged (); /* EMIT SIGNAL */ - relayer (r); } } @@ -577,9 +557,8 @@ Playlist::notify_region_added (boost::shared_ptr r) void Playlist::flush_notifications (bool from_undo) { - set > dependent_checks_needed; set >::iterator s; - uint32_t regions_changed = false; + bool regions_changed = false; if (in_flush) { return; @@ -587,41 +566,47 @@ Playlist::flush_notifications (bool from_undo) in_flush = true; - /* We have: + if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) { + regions_changed = true; + } - pending_bounds: regions whose bounds position and/or length changes - pending_removes: regions which were removed - pending_adds: regions which were added - pending_length: true if the playlist length might have changed - pending_contents_change: true if there was almost any change in the playlist - pending_range_moves: details of periods of time that have been moved about (when regions have been moved) + /* 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" + model, sort them by existing layer, then timestamp them. */ - if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) { - regions_changed = true; - } + // RegionSortByLayer cmp; + // pending_bounds.sort (cmp); - /* Make a list of regions that need relayering */ - RegionList regions_to_relayer; + list > crossfade_ranges; for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) { - regions_to_relayer.push_back (*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); RegionRemoved (boost::weak_ptr (*s)); /* EMIT SIGNAL */ } - + for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { - /* 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) { pending_contents_change = false; @@ -631,14 +616,14 @@ Playlist::flush_notifications (bool from_undo) for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { (*s)->clear_changes (); RegionAdded (boost::weak_ptr (*s)); /* EMIT SIGNAL */ - regions_to_relayer.push_back (*s); } - 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); } @@ -646,10 +631,6 @@ Playlist::flush_notifications (bool from_undo) RegionsExtended (pending_region_extensions); } - if (!regions_to_relayer.empty ()) { - relayer (regions_to_relayer); - } - clear_pending (); in_flush = false; @@ -670,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, framepos_t position, float times, bool auto_partition) { - RegionLock rlock (this); + RegionWriteLock rlock (this); times = fabs (times); int itimes = (int) floor (times); @@ -686,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; } @@ -698,6 +681,7 @@ Playlist::flush_notifications (bool from_undo) for (int i = 0; i < itimes; ++i) { boost::shared_ptr copy = RegionFactory::create (region, true); add_region_internal (copy, pos); + set_layer (copy, DBL_MAX); pos += region->length(); } @@ -718,6 +702,7 @@ Playlist::flush_notifications (bool from_undo) boost::shared_ptr sub = RegionFactory::create (region, plist); add_region_internal (sub, pos); + set_layer (sub, DBL_MAX); } } @@ -727,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 pl (shared_from_this()); @@ -757,14 +742,15 @@ Playlist::flush_notifications (bool from_undo) possibly_splice_unlocked (position, region->length(), region); + if (!holding_state ()) { + /* layers get assigned from XML state, and are not reset during undo/redo */ + relayer (); + } + /* we need to notify the existence of new region before checking dependents. Ick. */ notify_region_added (region); - if (!holding_state ()) { - check_dependents (region, false); - } - region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr (region))); return true; @@ -773,13 +759,14 @@ Playlist::flush_notifications (bool from_undo) void Playlist::replace_region (boost::shared_ptr old, boost::shared_ptr 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; @@ -789,7 +776,7 @@ Playlist::flush_notifications (bool from_undo) void Playlist::remove_region (boost::shared_ptr region) { - RegionLock rlock (this); + RegionWriteLock rlock (this); remove_region_internal (region); } @@ -816,6 +803,7 @@ Playlist::flush_notifications (bool from_undo) possibly_splice_unlocked (pos, -distance); if (!holding_state ()) { + relayer (); remove_dependents (region); } @@ -833,13 +821,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); } } } @@ -856,6 +844,17 @@ Playlist::flush_notifications (bool from_undo) } } + void + Playlist::get_source_equivalent_regions (boost::shared_ptr other, vector >& results) + { + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + if ((*i) && (*i)->any_source_equivalent (other)) { + results.push_back (*i); + } + } + } + void Playlist::partition (framepos_t start, framepos_t end, bool cut) { @@ -872,24 +871,20 @@ Playlist::flush_notifications (bool from_undo) * 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; - /* Don't relayer regions that are created during this operation; leave them - on the same region as the original. - */ - suspend_relayer (); - { - RegionLock rlock (this); + RegionWriteLock rlock (this); boost::shared_ptr region; boost::shared_ptr current; string new_name; RegionList::iterator tmp; - OverlapType overlap; + Evoral::OverlapType overlap; framepos_t pos1, pos2, pos3, pos4; in_partition = true; @@ -925,7 +920,7 @@ Playlist::flush_notifications (bool from_undo) continue; } - if ((overlap = current->coverage (start, end)) == OverlapNone) { + if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) { continue; } @@ -934,7 +929,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. */ @@ -960,7 +955,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, current->layer()); + 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); @@ -979,7 +975,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, current->layer()); + 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); @@ -994,7 +991,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 @@ -1017,7 +1014,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, current->layer()); + 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); @@ -1033,7 +1031,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 @@ -1060,7 +1058,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, current->layer()); + 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); @@ -1075,7 +1074,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. @@ -1103,12 +1102,6 @@ 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); - } - - resume_relayer (); } boost::shared_ptr @@ -1203,26 +1196,28 @@ Playlist::flush_notifications (bool from_undo) times = fabs (times); { - RegionLock rl1 (this); - RegionLock rl2 (other.get()); + 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 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 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; } } @@ -1235,13 +1230,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 copy = RegionFactory::create (region, true); add_region_internal (copy, pos); + set_layer (copy, DBL_MAX); pos += region->length(); } @@ -1259,6 +1255,7 @@ Playlist::flush_notifications (bool from_undo) boost::shared_ptr sub = RegionFactory::create (region, plist); add_region_internal (sub, pos); + set_layer (sub, DBL_MAX); } } } @@ -1266,7 +1263,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; @@ -1305,7 +1302,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 @@ -1319,7 +1316,7 @@ Playlist::flush_notifications (bool from_undo) void Playlist::split_region (boost::shared_ptr region, framepos_t playlist_position) { - RegionLock rl (this); + RegionWriteLock rl (this); _split_region (region, playlist_position); } @@ -1359,6 +1356,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 @@ -1376,6 +1375,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); @@ -1383,9 +1384,6 @@ Playlist::flush_notifications (bool from_undo) add_region_internal (left, region->position()); add_region_internal (right, region->position() + before); - - finalize_split_region (region, left, right); - remove_region_internal (region); _splicing = old_sp; @@ -1421,7 +1419,7 @@ Playlist::flush_notifications (bool from_undo) Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) { { - RegionLock rl (this); + RegionWriteLock rl (this); core_splice (at, distance, exclude); } } @@ -1469,8 +1467,6 @@ Playlist::flush_notifications (bool from_undo) if (what_changed.contains (Properties::position)) { - timestamp_layer_op (LayerOpBoundsChange, region); - /* remove it from the list then add it back in the right place again. */ @@ -1511,8 +1507,11 @@ Playlist::flush_notifications (bool from_undo) pending_bounds.push_back (region); } else { notify_contents_changed (); - relayer (region); - check_dependents (region, false); + relayer (); + list > xf; + xf.push_back (Evoral::Range (region->last_range())); + xf.push_back (Evoral::Range (region->range())); + coalesce_and_check_crossfades (xf); } } } @@ -1559,10 +1558,6 @@ Playlist::flush_notifications (bool from_undo) save = !(_splicing || _nudging); } - if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) { - check_dependents (region, false); - } - if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) { notify_region_moved (region); } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) { @@ -1571,6 +1566,10 @@ Playlist::flush_notifications (bool from_undo) notify_region_start_trimmed (region); } + /* don't notify about layer changes, since we are the only object that can initiate + them, and we notify in ::relayer() + */ + if (what_changed.contains (our_interests)) { save = true; } @@ -1581,7 +1580,7 @@ Playlist::flush_notifications (bool from_undo) void Playlist::drop_regions () { - RegionLock rl (this); + RegionWriteLock rl (this); regions.clear (); all_regions.clear (); } @@ -1589,7 +1588,7 @@ Playlist::flush_notifications (bool from_undo) void Playlist::sync_all_regions_with_regions () { - RegionLock rl (this); + RegionWriteLock rl (this); all_regions.clear (); @@ -1602,7 +1601,7 @@ Playlist::flush_notifications (bool from_undo) Playlist::clear (bool with_signals) { { - RegionLock rl (this); + RegionWriteLock rl (this); region_state_changed_connections.drop_connections (); @@ -1634,18 +1633,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 +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(this)); + RegionReadLock rlock (const_cast(this)); uint32_t cnt = 0; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { @@ -1661,8 +1659,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 rlist = find_regions_at (frame); boost::shared_ptr region; if (rlist->size()) { @@ -1671,7 +1669,6 @@ Playlist::flush_notifications (bool from_undo) region = rlist->back(); } - delete rlist; return region; } @@ -1679,8 +1676,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 rlist = find_regions_at (frame); for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) { @@ -1702,301 +1699,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 to_check; - set > 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::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 >::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 - Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir) - { - RegionLock rlock (this); - boost::shared_ptr ret; - framepos_t closest = max_framepos; - - bool end_iter = false; - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { +boost::shared_ptr +Playlist::find_regions_at (framepos_t frame) +{ + /* Caller must hold lock */ + + boost::shared_ptr 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 +Playlist::regions_with_start_within (Evoral::Range range) +{ + RegionReadLock rlock (this); + boost::shared_ptr rlist (new RegionList); - frameoffset_t distance; - boost::shared_ptr 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 +Playlist::regions_with_end_within (Evoral::Range range) +{ + RegionReadLock rlock (this); + boost::shared_ptr 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 +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 +Playlist::regions_touched_locked (framepos_t start, framepos_t end) +{ + boost::shared_ptr 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 +Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir) +{ + RegionReadLock rlock (this); + boost::shared_ptr 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 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; @@ -2078,14 +1978,14 @@ Playlist::flush_notifications (bool from_undo) void Playlist::rdiff (vector& cmds) const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); Stateful::rdiff (cmds); } void Playlist::clear_owned_changes () { - RegionLock rlock (this); + RegionReadLock rlock (this); Stateful::clear_owned_changes (); } @@ -2098,7 +1998,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) { @@ -2129,7 +2029,6 @@ Playlist::flush_notifications (bool from_undo) return -1; } - suspend_relayer (); freeze (); plist = node.properties(); @@ -2190,8 +2089,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()); + } + region->resume_property_changes (); } @@ -2199,23 +2101,14 @@ Playlist::flush_notifications (bool from_undo) if (seen_region_nodes && regions.empty()) { ret = -1; - } else { - - /* update dependents, which was not done during add_region_internal - due to in_set_state being true - */ - - for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) { - check_dependents (*r, false); - } } thaw (); notify_contents_changed (); - resume_relayer (); in_set_state--; first_set_state = false; + return ret; } @@ -2248,7 +2141,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); @@ -2268,21 +2161,31 @@ Playlist::state (bool full_state) bool Playlist::empty() const { - RegionLock rlock (const_cast(this), false); + RegionReadLock rlock (const_cast(this)); return regions.empty(); } uint32_t Playlist::n_regions() const { - RegionLock rlock (const_cast(this), false); + RegionReadLock rlock (const_cast(this)); return regions.size(); } +/** @return true if the all_regions list is empty, ie this playlist + * has never had a region added to it. + */ +bool +Playlist::all_regions_empty() const +{ + RegionReadLock rl (const_cast (this)); + return all_regions.empty(); +} + pair Playlist::get_extent () const { - RegionLock rlock (const_cast(this), false); + RegionReadLock rlock (const_cast(this)); return _get_extent (); } @@ -2325,7 +2228,7 @@ Playlist::bump_name (string name, Session &session) layer_t Playlist::top_layer() const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); layer_t top = 0; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { @@ -2340,264 +2243,197 @@ Playlist::set_edit_mode (EditMode mode) _edit_mode = mode; } -/** Relayer a region. See the other relayer() methods for commentary. */ +struct RelayerSort { + bool operator () (boost::shared_ptr a, boost::shared_ptr 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::relayer (boost::shared_ptr region) +Playlist::set_layer (boost::shared_ptr region, double new_layer) { - if (_relayer_suspended) { - return; + /* 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; } - RegionList r; - r.push_back (region); - relayer (r); + copy.insert (i, region); + + setup_layering_indices (copy); } -Playlist::TemporaryLayers -Playlist::compute_temporary_layers (RegionList const & relayer_regions) +void +Playlist::setup_layering_indices (RegionList const & regions) { - TemporaryLayers temporary_layers; - OverlapCache cache (this); + uint64_t j = 0; + list > xf; - for (RegionList::const_iterator i = relayer_regions.begin(); i != relayer_regions.end(); ++i) { + for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) { + (*k)->set_layering_index (j++); + } +} - DEBUG_TRACE (DEBUG::Layering, string_compose ("Compute temporary layer for %1\n", (*i)->name())); - - /* current_overlaps: regions that overlap *i now */ - RegionList current_overlaps = cache.get ((*i)->bounds ()); - current_overlaps.remove (*i); +/** 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 setting from XML */ - DEBUG_TRACE (DEBUG::Layering, "Current overlaps:\n"); - for (RegionList::iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) { - DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1\n", (*j)->name())); - } - - /* overlaps_to_preserve: regions that overlap *i now, but which aren't being - worked on during this relayer: these will have their relationship with - *i preserved. - */ - RegionList overlaps_to_preserve; + if (in_set_state) { + return; + } - /* overlaps_to_check: regions that overlap *i now, and must be checked to - see if *i is still on the correct layer with respect to them (according - to current layering rules). - */ - RegionList overlaps_to_check; + /* 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 + each region overlaps another */ - if (_session.config.get_relayer_on_all_edits () || (*i)->last_layer_op (LayerOpAdd) > (*i)->last_layer_op (LayerOpBoundsChange)) { - /* We're configured to relayer on all edits, or this region has had - no edit since it was added to the playlist, so we're relayering - the whole lot; in this case there are no `overlaps_to_preserve'. - */ - overlaps_to_check = current_overlaps; - } else { - /* We're only relayering new overlaps; find them */ - RegionList last_overlaps = cache.get ((*i)->last_relayer_bounds ()); - last_overlaps.remove (*i); - for (RegionList::const_iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) { - if (find (last_overlaps.begin(), last_overlaps.end(), *j) == last_overlaps.end()) { - /* This is a new overlap, which must be checked */ - overlaps_to_check.push_back (*j); - } else { - /* This is an existing overlap, which must be preserved */ - overlaps_to_preserve.push_back (*j); - } - } - } + /* how many pieces to divide this playlist's time up into */ + int const divisions = 512; - if (overlaps_to_check.empty ()) { - /* There are no overlaps to check, so just leave everything as it is */ - continue; - } + /* find the start and end positions of the regions on this playlist */ + framepos_t start = INT64_MAX; + framepos_t end = 0; + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + start = min (start, (*i)->position()); + end = max (end, (*i)->position() + (*i)->length()); + } - /* Put *i on our overlaps_to_check_list */ - overlaps_to_check.push_back (*i); + /* hence the size of each time division */ + double const division_size = (end - start) / double (divisions); - /* And sort it according to the current layer model */ - switch (_session.config.get_layer_model()) { - case LaterHigher: - overlaps_to_check.sort (RegionSortByPosition ()); - break; - case AddOrBoundsChangeHigher: - overlaps_to_check.sort (RegionSortByAddOrBounds ()); - break; - case AddHigher: - overlaps_to_check.sort (RegionSortByAdd ()); - break; - } + vector > layers; + layers.push_back (vector (divisions)); - /* Now find *i in our overlaps_to_check list; within this list it will be in the - right place wrt the current layering rules, so we can work out the layers of the - nearest regions below and above. - */ - double previous_layer = -DBL_MAX; - double next_layer = DBL_MAX; - RegionList::const_iterator j = overlaps_to_check.begin(); - while (*j != *i) { - previous_layer = temporary_layers.get (*j); - ++j; - } + /* Sort our regions into layering index order */ + RegionList copy = regions.rlist(); + copy.sort (RelayerSort ()); - /* we must have found *i */ - assert (j != overlaps_to_check.end ()); + 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())); + } - ++j; - if (j != overlaps_to_check.end ()) { - next_layer = temporary_layers.get (*j); - } + for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) { - /* Now we know where *i and overlaps_to_preserve should go: between previous_layer and - next_layer. + /* 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. */ - - /* Recurse into overlaps_to_preserve to find dependents */ - RegionList recursed_overlaps_to_preserve; - - for (RegionList::const_iterator k = overlaps_to_preserve.begin(); k != overlaps_to_preserve.end(); ++k) { - recursed_overlaps_to_preserve.push_back (*k); - RegionList touched = recursive_regions_touched (*k, cache, *i); - for (RegionList::iterator m = touched.begin(); m != touched.end(); ++m) { - if (find (recursed_overlaps_to_preserve.begin(), recursed_overlaps_to_preserve.end(), *m) == recursed_overlaps_to_preserve.end()) { - recursed_overlaps_to_preserve.push_back (*m); - } + int start_division = 0; + int end_division = 0; + + if (division_size > 0) { + start_division = floor ( ((*i)->position() - start) / division_size); + end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size ); + if (end_division == divisions) { + end_division--; } } - /* Put *i into the overlaps_to_preserve list */ - recursed_overlaps_to_preserve.push_back (*i); - - /* Sort it by layer, so that we preserve layering */ - recursed_overlaps_to_preserve.sort (SortByTemporaryLayer (temporary_layers)); - - /* Divide available space up into chunks so that we can relayer everything in that space */ - double const space = (next_layer - previous_layer) / (recursed_overlaps_to_preserve.size() + 1); - - /* And relayer */ - int m = 1; - for (RegionList::const_iterator k = recursed_overlaps_to_preserve.begin(); k != recursed_overlaps_to_preserve.end(); ++k) { - temporary_layers.set (*k, previous_layer + space * m); - ++m; - } - } - - return temporary_layers; -} + assert (divisions == 0 || end_division < divisions); -/** Take a list of temporary layer indices and set up the layers of all regions - * based on them. - */ -void -Playlist::commit_temporary_layers (TemporaryLayers const & temporary_layers) -{ - /* Sort all the playlist's regions by layer, ascending */ - RegionList all_regions = regions.rlist (); - all_regions.sort (SortByTemporaryLayer (temporary_layers)); + /* find the lowest layer that this region can go on */ + size_t j = layers.size(); + while (j > 0) { + /* try layer j - 1; it can go on if it overlaps no other region + that is already on that layer + */ - DEBUG_TRACE (DEBUG::Layering, "Commit layering:\n"); + bool overlap = false; + for (int k = start_division; k <= end_division; ++k) { + RegionList::iterator l = layers[j-1][k].begin (); + while (l != layers[j-1][k].end()) { + if ((*l)->overlap_equivalent (*i)) { + overlap = true; + break; + } + l++; + } - for (RegionList::iterator i = all_regions.begin(); i != all_regions.end(); ++i) { + if (overlap) { + break; + } + } - /* Go through the regions that we have already layered and hence work - out the maximum layer index that is in used at some point during - region *i. - */ - - layer_t max_layer_here = 0; - bool have_overlap = false; - for (RegionList::iterator j = all_regions.begin(); j != i; ++j) { - if ((*j)->overlap_equivalent (*i)) { - max_layer_here = max ((*j)->layer (), max_layer_here); - have_overlap = true; + if (overlap) { + /* overlap, so we must use layer j */ + break; } - } - if (have_overlap) { - /* *i overlaps something, so put it on the next available layer */ - (*i)->set_layer (max_layer_here + 1); - } else { - /* no overlap, so put on the bottom layer */ - (*i)->set_layer (0); + --j; } - - DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 temporary %2 committed %3\n", (*i)->name(), temporary_layers.get (*i), (*i)->layer())); - } - notify_layering_changed (); -} + if (j == layers.size()) { + /* we need a new layer for this region */ + layers.push_back (vector (divisions)); + } -/** Relayer a list of regions. - * - * Taking each region R in turn, this method examines the regions O that overlap R in time. - * If the session configuration option "relayer-on-all-moves" is false, we reduce O so that - * it contains only those regions with which new overlaps have been formed since the last - * relayer. - * - * We then change the layer of R and its indirect overlaps so that R meets the current - * Session layer model with respect to O. See doc/layering. - */ + /* put a reference to this region in each of the divisions that it exists in */ + for (int k = start_division; k <= end_division; ++k) { + layers[j][k].push_back (*i); + } -void -Playlist::relayer (RegionList const & relayer_regions) -{ - if (_relayer_suspended) { - return; + (*i)->set_layer (j); } - /* We do this in two parts: first; compute `temporary layer' indices for - regions on the playlist. These are (possibly) fractional indices, which - are a convenient means of working with things when you want to insert layers - between others. + /* 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 (); - TemporaryLayers temporary_layers = compute_temporary_layers (relayer_regions); - - /* Second, we fix up these temporary layers and `commit' them by writing - them to the regions involved. + /* 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. */ - - commit_temporary_layers (temporary_layers); -} - -/** Put a region on some fractional layer and sort everything else out around it. - * This can be used to force a region into some layering; for example, calling - * this method with temporary_layer == -0.5 will put the region at the bottom of - * the stack. - */ - -void -Playlist::relayer (boost::shared_ptr region, double temporary_layer) -{ - if (_relayer_suspended) { - return; - } - - TemporaryLayers t; - t.set (region, temporary_layer); - commit_temporary_layers (t); + setup_layering_indices (copy); } void Playlist::raise_region (boost::shared_ptr region) { - relayer (region, region->layer() + 1.5); + set_layer (region, region->layer() + 1.5); + relayer (); } void Playlist::lower_region (boost::shared_ptr region) { - relayer (region, region->layer() - 1.5); + set_layer (region, region->layer() - 1.5); + relayer (); } void Playlist::raise_region_to_top (boost::shared_ptr region) { - relayer (region, max_layer); + set_layer (region, DBL_MAX); + relayer (); } void Playlist::lower_region_to_bottom (boost::shared_ptr region) { - relayer (region, -0.5); + set_layer (region, -0.5); + relayer (); } void @@ -2609,7 +2445,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards) _nudging = true; { - RegionLock rlock (const_cast (this)); + RegionWriteLock rlock (const_cast (this)); for (i = regions.begin(); i != regions.end(); ++i) { @@ -2650,7 +2486,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards) bool Playlist::uses_source (boost::shared_ptr src) const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); for (set >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) { if ((*r)->uses_source (src)) { @@ -2664,7 +2500,7 @@ Playlist::uses_source (boost::shared_ptr src) const boost::shared_ptr Playlist::find_region (const ID& id) const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); /* searches all regions currently in use by the playlist */ @@ -2680,7 +2516,7 @@ Playlist::find_region (const ID& id) const uint32_t Playlist::region_use_count (boost::shared_ptr r) const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); uint32_t cnt = 0; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { @@ -2732,24 +2568,9 @@ Playlist::set_frozen (bool yn) _frozen = yn; } -void -Playlist::timestamp_layer_op (LayerOp op, boost::shared_ptr region) -{ - region->set_last_layer_op (op, ++layer_op_counter); -} - - -/** Find the next or previous region after `region' (next if dir > 0, previous otherwise) - * and swap its position with `region'. - */ void Playlist::shuffle (boost::shared_ptr region, int dir) { - /* As regards layering, the calls we make to set_position() will - perform layering as if the regions had been moved, which I think - is about right. - */ - bool moved = false; if (region->locked()) { @@ -2759,7 +2580,7 @@ Playlist::shuffle (boost::shared_ptr region, int dir) _shuffling = true; { - RegionLock rlock (const_cast (this)); + RegionWriteLock rlock (const_cast (this)); if (dir > 0) { @@ -2854,15 +2675,17 @@ Playlist::shuffle (boost::shared_ptr region, int dir) _shuffling = false; if (moved) { - check_dependents (region, false); + + relayer (); notify_contents_changed(); } + } bool Playlist::region_is_shuffle_constrained (boost::shared_ptr) { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); if (regions.size() > 1) { return true; @@ -2874,7 +2697,7 @@ Playlist::region_is_shuffle_constrained (boost::shared_ptr) void Playlist::update_after_tempo_map_change () { - RegionLock rlock (const_cast (this)); + RegionWriteLock rlock (const_cast (this)); RegionList copy (regions.rlist()); freeze (); @@ -2889,7 +2712,7 @@ Playlist::update_after_tempo_map_change () void Playlist::foreach_region (boost::function)> s) { - RegionLock rl (this, false); + RegionWriteLock rl (this, false); for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { s (*i); } @@ -2898,7 +2721,7 @@ Playlist::foreach_region (boost::function)> s) bool Playlist::has_region_at (framepos_t const p) const { - RegionLock (const_cast (this)); + RegionReadLock (const_cast (this)); RegionList::const_iterator i = regions.begin (); while (i != regions.end() && !(*i)->covers (p)) { @@ -2912,7 +2735,7 @@ Playlist::has_region_at (framepos_t const p) const void Playlist::remove_region_by_source (boost::shared_ptr s) { - RegionLock rl (this); + RegionWriteLock rl (this); RegionList::iterator i = regions.begin(); while (i != regions.end()) { @@ -2935,7 +2758,7 @@ Playlist::remove_region_by_source (boost::shared_ptr s) framepos_t Playlist::find_next_top_layer_position (framepos_t t) const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); layer_t const top = top_layer (); @@ -3002,6 +2825,7 @@ Playlist::combine (const RegionList& r) /* make position relative to zero */ pl->add_region (copied_region, original_region->position() - earliest_position); + copied_region->set_layer (original_region->layer ()); /* use the maximum number of channels for any region */ @@ -3016,10 +2840,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; @@ -3161,13 +2981,13 @@ Playlist::uncombine (boost::shared_ptr 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 */ @@ -3175,13 +2995,13 @@ Playlist::uncombine (boost::shared_ptr 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 */ @@ -3189,7 +3009,7 @@ Playlist::uncombine (boost::shared_ptr target) modified_region = true; break; - case OverlapStart: + case Evoral::OverlapStart: /* overlap covers start but ends within, so * trim the end of the region. */ @@ -3232,10 +3052,6 @@ Playlist::uncombine (boost::shared_ptr 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 (); } @@ -3243,7 +3059,7 @@ Playlist::uncombine (boost::shared_ptr target) uint32_t Playlist::max_source_level () const { - RegionLock rlock (const_cast (this)); + RegionReadLock rlock (const_cast (this)); uint32_t lvl = 0; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { @@ -3253,167 +3069,44 @@ Playlist::max_source_level () const return lvl; } - -uint32_t -Playlist::count_joined_regions () const -{ - RegionLock rlock (const_cast (this)); - uint32_t cnt = 0; - - for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - if ((*i)->max_source_level() > 0) { - cnt++; - } - } - - return cnt; -} - void Playlist::set_orig_track_id (const PBD::ID& id) { _orig_track_id = id; } -/** Set the temporary layer for a region */ -void -Playlist::TemporaryLayers::set (boost::shared_ptr r, double l) -{ - _map[r] = l; -} - -/** Return the temporary layer for a region, if one has been specified - * to this TemporaryLayers object; if not return the region's current - * layer. +/** Take a list of ranges, coalesce any that can be coalesced, then call + * check_crossfades for each one. */ -double -Playlist::TemporaryLayers::get (boost::shared_ptr r) const -{ - Map::const_iterator i = _map.find (r); - if (i != _map.end ()) { - return i->second; - } - - return double (r->layer ()); -} - -int const Playlist::OverlapCache::_divisions = 512; - -/** Set up an OverlapCache for a playlist; the cache will only be valid until - * the Playlist is changed. - */ -Playlist::OverlapCache::OverlapCache (Playlist* playlist) - : _range (0, 0) -{ - /* Find the start and end positions of the regions on this playlist */ - _range = Evoral::Range (max_framepos, 0); - RegionList const & rl = playlist->region_list().rlist (); - for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) { - Evoral::Range const b = (*i)->bounds (); - _range.from = min (_range.from, b.from); - _range.to = max (_range.to, b.to); - } - - /* Hence the size of each time divison */ - _division_size = (_range.to - _range.from) / double (_divisions); - - _cache.resize (_divisions); - - /* Build the cache */ - for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) { - pair ind = cache_indices ((*i)->bounds ()); - for (int j = ind.first; j < ind.second; ++j) { - _cache[j].push_back (*i); - } - } -} - -/** @param range Range, in frames. - * @return From and to cache indices for (to is exclusive). - */ -pair -Playlist::OverlapCache::cache_indices (Evoral::Range range) const +void +Playlist::coalesce_and_check_crossfades (list > ranges) { - range.from = max (range.from, _range.from); - range.to = min (range.to, _range.to); - - pair const p = make_pair ( - floor ((range.from - _range.from) / _division_size), - ceil ((range.to - _range.from) / _division_size) - ); + /* XXX: it's a shame that this coalesce algorithm also exists in + TimeSelection::consolidate(). + */ - assert (p.first >= 0); - assert (p.second <= _divisions); + /* XXX: xfade: this is implemented in Evoral::RangeList */ - return p; -} +restart: + for (list >::iterator i = ranges.begin(); i != ranges.end(); ++i) { + for (list >::iterator j = ranges.begin(); j != ranges.end(); ++j) { -/** Return the regions that overlap a given range. The returned list - * is not guaranteed to be in the same order as the Playlist that it was - * generated from. - */ -Playlist::RegionList -Playlist::OverlapCache::get (Evoral::Range range) const -{ - if (_range.from == max_framepos) { - return RegionList (); - } - - RegionList r; + if (i == j) { + continue; + } - pair ind = cache_indices (range); - for (int i = ind.first; i < ind.second; ++i) { - for (RegionList::const_iterator j = _cache[i].begin(); j != _cache[i].end(); ++j) { - if ((*j)->coverage (range.from, range.to) != OverlapNone) { - r.push_back (*j); + 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; } } } - - r.sort (); - r.unique (); - - return r; -} - -void -Playlist::suspend_relayer () -{ - _relayer_suspended = true; } void -Playlist::resume_relayer () -{ - _relayer_suspended = false; -} - -/** Examine a region and return regions which overlap it, and also those which overlap those which overlap etc. - * @param ignore Optional region which should be treated as if it doesn't exist (ie not returned in the list, - * and not recursed into). - */ -Playlist::RegionList -Playlist::recursive_regions_touched (boost::shared_ptr region, OverlapCache const & cache, boost::shared_ptr ignore) const +Playlist::set_capture_insertion_in_progress (bool yn) { - RegionList touched; - recursive_regions_touched_sub (region, cache, ignore, touched); - - touched.remove (region); - return touched; + _capture_insertion_underway = yn; } - -/** Recursive sub-routine of recursive_regions_touched */ -void -Playlist::recursive_regions_touched_sub ( - boost::shared_ptr region, OverlapCache const & cache, boost::shared_ptr ignore, RegionList & touched - ) const -{ - RegionList r = cache.get (region->bounds ()); - for (RegionList::iterator i = r.begin(); i != r.end(); ++i) { - if (find (touched.begin(), touched.end(), *i) == touched.end() && *i != ignore) { - touched.push_back (*i); - recursive_regions_touched_sub (*i, cache, ignore, touched); - } - } -} -