X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fplaylist.cc;h=c5d52f73450f29197c63556d03beea40537a11d1;hb=cebd189239ce6b7bf775d3f2c0cfe312608d04df;hp=814278f0e52c28c91f108c8ec93f7b52936cc923;hpb=f38db9a4e350de376308d89059ba123a16719f1a;p=ardour.git diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 814278f0e5..c5d52f7345 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -19,17 +19,12 @@ #include #include -#include #include -#include -#include #include -#include #include #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 regions; -} + namespace Properties { + PBD::PropertyDescriptor regions; + } } struct ShowMeTheList { @@ -80,7 +75,7 @@ RegionListProperty::RegionListProperty (Playlist& pl) : SequenceProperty > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1)) , _playlist (pl) { - + } RegionListProperty::RegionListProperty (RegionListProperty const & p) @@ -108,7 +103,7 @@ RegionListProperty::get_content_as_xml (boost::shared_ptr region, XMLNod /* All regions (even those which are deleted) have their state saved by other code, so we can just store ID here. */ - + node.add_property ("id", region->id().to_s ()); } @@ -121,7 +116,7 @@ RegionListProperty::get_content_from_xml (XMLNode const & node) const PBD::ID id (prop->value ()); boost::shared_ptr ret = _playlist.region_by_id (id); - + if (!ret) { ret = RegionFactory::region_by_id (id); } @@ -142,7 +137,7 @@ Playlist::Playlist (Session& sess, string nom, DataType type, bool hide) Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide) : SessionObject(sess, "unnamed playlist") - , regions (*this) + , regions (*this) , _type(type) { #ifndef NDEBUG @@ -161,7 +156,7 @@ Playlist::Playlist (boost::shared_ptr other, string namestr, boo : SessionObject(other->_session, namestr) , regions (*this) , _type(other->_type) - , _orig_diskstream_id (other->_orig_diskstream_id) + , _orig_track_id (other->_orig_track_id) { init (hide); @@ -185,20 +180,16 @@ Playlist::Playlist (boost::shared_ptr other, string namestr, boo in_flush = false; in_partition = false; subcnt = 0; - _read_data_count = 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) : SessionObject(other->_session, str) , regions (*this) , _type(other->_type) - , _orig_diskstream_id (other->_orig_diskstream_id) + , _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(); @@ -251,12 +242,13 @@ Playlist::Playlist (boost::shared_ptr other, framepos_t start, f RegionFactory::region_name (new_name, region->name(), false); - PropertyList plist; + PropertyList plist; plist.add (Properties::start, region->start() + offset); 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); @@ -265,8 +257,6 @@ Playlist::Playlist (boost::shared_ptr other, framepos_t start, f in_set_state--; first_set_state = false; - - /* this constructor does NOT notify others (session) */ } void @@ -291,7 +281,7 @@ 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)); @@ -307,7 +297,6 @@ Playlist::init (bool hide) g_atomic_int_set (&block_notifications, 0); g_atomic_int_set (&ignore_state_changes, 0); pending_contents_change = false; - pending_length = false; pending_layering = false; first_set_state = true; _refcnt = 0; @@ -316,21 +305,18 @@ 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; - _read_data_count = 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)); _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this)); - + ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this)); } @@ -339,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()); @@ -357,7 +343,7 @@ Playlist::_set_sort_id () or .. where id is an integer. We extract the id and sort by that. */ - + size_t dot_position = _name.val().find_last_of("."); if (dot_position == string::npos) { @@ -386,7 +372,7 @@ Playlist::set_name (const string& str) if (_refcnt > 2) { return false; - } + } bool ret = SessionObject::set_name(str); if (ret) { @@ -406,7 +392,7 @@ Playlist::set_name (const string& str) void Playlist::begin_undo () { - in_update = true; + in_undo = true; freeze (); } @@ -414,7 +400,7 @@ void Playlist::end_undo () { thaw (true); - in_update = false; + in_undo = false; } void @@ -437,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 */ @@ -477,13 +462,10 @@ Playlist::notify_region_removed (boost::shared_ptr r) if (holding_state ()) { pending_removes.insert (r); pending_contents_change = true; - pending_length = true; } else { /* this might not be true, but we have to act as though it could be. */ - pending_length = false; - LengthChanged (); /* EMIT SIGNAL */ pending_contents_change = false; RegionRemoved (boost::weak_ptr (r)); /* EMIT SIGNAL */ ContentsChanged (); /* EMIT SIGNAL */ @@ -517,17 +499,17 @@ Playlist::notify_region_start_trimmed (boost::shared_ptr r) } Evoral::Range const extra (r->position(), r->last_position()); - + if (holding_state ()) { - + pending_region_extensions.push_back (extra); - + } else { - + list > r; r.push_back (extra); RegionsExtended (r); - + } } @@ -563,39 +545,20 @@ Playlist::notify_region_added (boost::shared_ptr r) if (holding_state()) { pending_adds.insert (r); pending_contents_change = true; - pending_length = true; } else { r->clear_changes (); - pending_length = false; - LengthChanged (); /* EMIT SIGNAL */ pending_contents_change = false; RegionAdded (boost::weak_ptr (r)); /* EMIT SIGNAL */ ContentsChanged (); /* EMIT SIGNAL */ } } -void -Playlist::notify_length_changed () -{ - if (holding_state ()) { - pending_length = true; - } else { - pending_length = false; - LengthChanged(); /* EMIT SIGNAL */ - 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 > dependent_checks_needed; set >::iterator s; - uint32_t regions_changed = false; - bool check_length = false; - framecnt_t old_length = 0; + bool regions_changed = false; if (in_flush) { return; @@ -605,12 +568,12 @@ Playlist::flush_notifications (bool from_undo) if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) { regions_changed = true; - if (!pending_length) { - old_length = _get_extent ().second; - check_length = 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" @@ -620,1348 +583,1203 @@ Playlist::flush_notifications (bool from_undo) // RegionSortByLayer cmp; // pending_bounds.sort (cmp); + list > 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 (*s)); /* EMIT SIGNAL */ } - + for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { - // cerr << _name << " sends RegionAdded\n"; + 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 hear's that its been added + anyone hears that its been added */ - dependent_checks_needed.insert (*s); - } - - if (check_length) { - if (old_length != _get_extent().second) { - pending_length = true; - // cerr << _name << " length has changed\n"; - } - } - - if (pending_length || (freeze_length != _get_extent().second)) { - pending_length = false; - // cerr << _name << " sends LengthChanged\n"; - LengthChanged(); /* EMIT SIGNAL */ - } - - 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) { - (*s)->clear_changes (); - RegionAdded (boost::weak_ptr (*s)); /* EMIT SIGNAL */ - } - - for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) { - check_dependents (*s, false); } - if (!pending_range_moves.empty ()) { - RangesMoved (pending_range_moves, from_undo); - } - - if (!pending_region_extensions.empty ()) { - RegionsExtended (pending_region_extensions); + if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) { + relayer (); } - - clear_pending (); - - in_flush = false; -} - -void -Playlist::clear_pending () -{ - pending_adds.clear (); - pending_removes.clear (); - pending_bounds.clear (); - pending_range_moves.clear (); - pending_region_extensions.clear (); - pending_contents_change = false; - pending_length = false; -} -/************************************************************* - PLAYLIST OPERATIONS - *************************************************************/ + if (regions_changed || pending_contents_change) { + pending_contents_change = false; + ContentsChanged (); /* EMIT SIGNAL */ + } -void -Playlist::add_region (boost::shared_ptr region, framepos_t position, float times, bool auto_partition) -{ - RegionLock rlock (this); - times = fabs (times); + for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { + (*s)->clear_changes (); + RegionAdded (boost::weak_ptr (*s)); /* EMIT SIGNAL */ + } - int itimes = (int) floor (times); + coalesce_and_check_crossfades (crossfade_ranges); - framepos_t pos = position; + 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); + } + + if (!pending_region_extensions.empty ()) { + RegionsExtended (pending_region_extensions); + } + + clear_pending (); + + in_flush = false; + } + + void + Playlist::clear_pending () + { + pending_adds.clear (); + pending_removes.clear (); + pending_bounds.clear (); + pending_range_moves.clear (); + pending_region_extensions.clear (); + pending_contents_change = false; + } + + /************************************************************* + 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) + { + RegionWriteLock rlock (this); + times = fabs (times); + + int itimes = (int) floor (times); + + framepos_t pos = position; + + if (times == 1 && auto_partition){ + partition(pos - 1, (pos + region->length()), true); + } + + if (itimes >= 1) { + add_region_internal (region, pos); + set_layer (region, DBL_MAX); + pos += region->length(); + --itimes; + } + + + /* note that itimes can be zero if we being asked to just + insert a single fraction of the region. + */ - if (times == 1 && auto_partition){ - partition(pos - 1, (pos + region->length()), true); - } + 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(); + } - if (itimes >= 1) { - add_region_internal (region, pos); - pos += region->length(); - --itimes; - } + framecnt_t length = 0; + if (floor (times) != times) { + length = (framecnt_t) floor (region->length() * (times - floor (times))); + string name; + RegionFactory::region_name (name, region->name(), false); - /* note that itimes can be zero if we being asked to just - insert a single fraction of the region. - */ + { + PropertyList plist; - for (int i = 0; i < itimes; ++i) { - boost::shared_ptr copy = RegionFactory::create (region, true); - add_region_internal (copy, pos); - pos += region->length(); - } + plist.add (Properties::start, region->start()); + plist.add (Properties::length, length); + plist.add (Properties::name, name); + plist.add (Properties::layer, region->layer()); - framecnt_t length = 0; + boost::shared_ptr sub = RegionFactory::create (region, plist); + add_region_internal (sub, pos); + set_layer (sub, DBL_MAX); + } + } - if (floor (times) != times) { - length = (framecnt_t) floor (region->length() * (times - floor (times))); - string name; - RegionFactory::region_name (name, region->name(), false); + possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr()); + } - { - PropertyList plist; - - plist.add (Properties::start, region->start()); - plist.add (Properties::length, length); - plist.add (Properties::name, name); - plist.add (Properties::layer, region->layer()); + void + Playlist::set_region_ownership () + { + RegionWriteLock rl (this); + RegionList::iterator i; + boost::weak_ptr pl (shared_from_this()); - boost::shared_ptr sub = RegionFactory::create (region, plist); - add_region_internal (sub, pos); - } - } + for (i = regions.begin(); i != regions.end(); ++i) { + (*i)->set_playlist (pl); + } + } - possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr()); -} + bool + Playlist::add_region_internal (boost::shared_ptr region, framepos_t position) + { + if (region->data_type() != _type) { + return false; + } -void -Playlist::set_region_ownership () -{ - RegionLock rl (this); - RegionList::iterator i; - boost::weak_ptr pl (shared_from_this()); + RegionSortByPosition cmp; + + if (!first_set_state) { + boost::shared_ptr foo (shared_from_this()); + region->set_playlist (boost::weak_ptr(foo)); + } + + region->set_position (position); + + regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region); + all_regions.insert (region); + + 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); + + region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr (region))); + + return true; + } + + void + Playlist::replace_region (boost::shared_ptr old, boost::shared_ptr newr, framepos_t pos) + { + 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; + + possibly_splice_unlocked (pos, old->length() - newr->length()); + } + + void + Playlist::remove_region (boost::shared_ptr region) + { + RegionWriteLock rlock (this); + remove_region_internal (region); + } + + int + Playlist::remove_region_internal (boost::shared_ptr region) + { + RegionList::iterator i; + + if (!in_set_state) { + /* unset playlist */ + region->set_playlist (boost::weak_ptr()); + } + + /* XXX should probably freeze here .... */ + + for (i = regions.begin(); i != regions.end(); ++i) { + if (*i == region) { + + framepos_t pos = (*i)->position(); + framecnt_t distance = (*i)->length(); + + regions.erase (i); + + possibly_splice_unlocked (pos, -distance); + + if (!holding_state ()) { + relayer (); + remove_dependents (region); + } + + notify_region_removed (region); + break; + } + } + + return -1; + } + + void + Playlist::get_equivalent_regions (boost::shared_ptr other, vector >& results) + { + 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); + } + } + } else { + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i)->equivalent (other)) { + results.push_back (*i); + } + } + } + } + + void + Playlist::get_region_list_equivalent_regions (boost::shared_ptr other, vector >& results) + { + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + if ((*i) && (*i)->region_list_equivalent (other)) { + results.push_back (*i); + } + } + } + + 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) + { + RegionList thawlist; + + partition_internal (start, end, cut, thawlist); + + for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { + (*i)->resume_property_changes (); + } + } + +/** 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. + */ - for (i = regions.begin(); i != regions.end(); ++i) { - (*i)->set_playlist (pl); - } -} + void + Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist) + { + RegionList new_regions; -bool -Playlist::add_region_internal (boost::shared_ptr region, framepos_t position) -{ - if (region->data_type() != _type){ - return false; - } + { + RegionWriteLock rlock (this); - RegionSortByPosition cmp; + boost::shared_ptr region; + boost::shared_ptr current; + string new_name; + RegionList::iterator tmp; + Evoral::OverlapType overlap; + framepos_t pos1, pos2, pos3, pos4; - framecnt_t old_length = 0; + in_partition = true; - if (!holding_state()) { - old_length = _get_extent().second; - } + /* need to work from a copy, because otherwise the regions we add during the process + get operated on as well. + */ - if (!first_set_state) { - boost::shared_ptr foo (shared_from_this()); - region->set_playlist (boost::weak_ptr(foo)); - } + RegionList copy = regions.rlist(); - region->set_position (position, this); + for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) { - timestamp_layer_op (region); + tmp = i; + ++tmp; - regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region); - all_regions.insert (region); + current = *i; - possibly_splice_unlocked (position, region->length(), region); + if (current->first_frame() >= start && current->last_frame() < end) { - if (!holding_state ()) { - /* layers get assigned from XML state, and are not reset during undo/redo */ - relayer (); - } + if (cutting) { + remove_region_internal (current); + } - /* we need to notify the existence of new region before checking dependents. Ick. */ + continue; + } - notify_region_added (region); + /* coverage will return OverlapStart if the start coincides + with the end point. we do not partition such a region, + so catch this special case. + */ - if (!holding_state ()) { + if (current->first_frame() >= end) { + continue; + } - check_dependents (region, false); + if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) { + continue; + } - if (old_length != _get_extent().second) { - notify_length_changed (); - } - } + pos1 = current->position(); + pos2 = start; + pos3 = end; + pos4 = current->last_frame(); - region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr (region))); + if (overlap == Evoral::OverlapInternal) { + /* split: we need 3 new regions, the front, middle and end. + cut: we need 2 regions, the front and end. + */ - return true; -} + /* + start end + ---------------*************************------------ + P1 P2 P3 P4 + SPLIT: + ---------------*****++++++++++++++++====------------ + CUT + ---------------*****----------------====------------ -void -Playlist::replace_region (boost::shared_ptr old, boost::shared_ptr newr, framepos_t pos) -{ - RegionLock rlock (this); + */ - bool old_sp = _splicing; - _splicing = true; + if (!cutting) { + /* "middle" ++++++ */ - remove_region_internal (old); - add_region_internal (newr, pos); + RegionFactory::region_name (new_name, current->name(), false); - _splicing = old_sp; + PropertyList plist; - possibly_splice_unlocked (pos, old->length() - newr->length()); -} + 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::layering_index, current->layering_index ()); + plist.add (Properties::automatic, true); + plist.add (Properties::left_of_split, true); + plist.add (Properties::right_of_split, true); -void -Playlist::remove_region (boost::shared_ptr region) -{ - RegionLock rlock (this); - remove_region_internal (region); -} + region = RegionFactory::create (current, plist); + add_region_internal (region, start); + new_regions.push_back (region); + } -int -Playlist::remove_region_internal (boost::shared_ptr region) -{ - RegionList::iterator i; - framecnt_t old_length = 0; - int ret = -1; + /* "end" ====== */ - if (!holding_state()) { - old_length = _get_extent().second; - } + RegionFactory::region_name (new_name, current->name(), false); - if (!in_set_state) { - /* unset playlist */ - region->set_playlist (boost::weak_ptr()); - } + PropertyList plist; - /* XXX should probably freeze here .... */ + 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::layering_index, current->layering_index ()); + plist.add (Properties::automatic, true); + plist.add (Properties::right_of_split, true); - for (i = regions.begin(); i != regions.end(); ++i) { - if (*i == region) { + region = RegionFactory::create (current, plist); - framepos_t pos = (*i)->position(); - framecnt_t distance = (*i)->length(); + add_region_internal (region, end); + new_regions.push_back (region); - regions.erase (i); + /* "front" ***** */ - possibly_splice_unlocked (pos, -distance); + current->suspend_property_changes (); + thawlist.push_back (current); + current->cut_end (pos2 - 1); - if (!holding_state ()) { - relayer (); - remove_dependents (region); + } else if (overlap == Evoral::OverlapEnd) { - if (old_length != _get_extent().second) { - notify_length_changed (); - } - } + /* + start end + ---------------*************************------------ + P1 P2 P4 P3 + SPLIT: + ---------------**************+++++++++++------------ + CUT: + ---------------**************----------------------- + */ - notify_region_removed (region); - ret = 0; - break; - } - } + if (!cutting) { - return -1; -} + /* end +++++ */ -void -Playlist::get_equivalent_regions (boost::shared_ptr other, vector >& results) -{ - 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)); - } - } - } else { - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - if ((*i)->equivalent (other)) { - results.push_back ((*i)); - } - } - } -} + RegionFactory::region_name (new_name, current->name(), false); -void -Playlist::get_region_list_equivalent_regions (boost::shared_ptr other, vector >& results) -{ - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + PropertyList plist; - if ((*i) && (*i)->region_list_equivalent (other)) { - results.push_back (*i); - } - } -} + 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::layering_index, current->layering_index ()); + plist.add (Properties::automatic, true); + plist.add (Properties::left_of_split, true); -void -Playlist::partition (framepos_t start, framepos_t end, bool cut) -{ - RegionList thawlist; + region = RegionFactory::create (current, plist); - partition_internal (start, end, cut, thawlist); + add_region_internal (region, start); + new_regions.push_back (region); + } - for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { - (*i)->resume_property_changes (); - } -} + /* front ****** */ -void -Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist) -{ - RegionList new_regions; + current->suspend_property_changes (); + thawlist.push_back (current); + current->cut_end (pos2 - 1); - { - RegionLock rlock (this); + } else if (overlap == Evoral::OverlapStart) { - boost::shared_ptr region; - boost::shared_ptr current; - string new_name; - RegionList::iterator tmp; - OverlapType overlap; - framepos_t pos1, pos2, pos3, pos4; + /* split: we need 2 regions: the front and the end. + cut: just trim current to skip the cut area + */ - in_partition = true; + /* + start end + ---------------*************************------------ + P2 P1 P3 P4 - /* need to work from a copy, because otherwise the regions we add during the process - get operated on as well. - */ + SPLIT: + ---------------****+++++++++++++++++++++------------ + CUT: + -------------------*********************------------ - RegionList copy = regions.rlist(); + */ - for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) { + if (!cutting) { + /* front **** */ + RegionFactory::region_name (new_name, current->name(), false); - tmp = i; - ++tmp; + PropertyList plist; - current = *i; + 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::layering_index, current->layering_index ()); + plist.add (Properties::automatic, true); + plist.add (Properties::right_of_split, true); - if (current->first_frame() >= start && current->last_frame() < end) { + region = RegionFactory::create (current, plist); - if (cutting) { - remove_region_internal (current); - } + add_region_internal (region, pos1); + new_regions.push_back (region); + } - continue; - } + /* end */ - /* coverage will return OverlapStart if the start coincides - with the end point. we do not partition such a region, - so catch this special case. - */ + current->suspend_property_changes (); + thawlist.push_back (current); + current->trim_front (pos3); + } else if (overlap == Evoral::OverlapExternal) { - if (current->first_frame() >= end) { - continue; - } + /* split: no split required. + cut: remove the region. + */ - if ((overlap = current->coverage (start, end)) == OverlapNone) { - continue; - } + /* + start end + ---------------*************************------------ + P2 P1 P3 P4 - pos1 = current->position(); - pos2 = start; - pos3 = end; - pos4 = current->last_frame(); - - if (overlap == OverlapInternal) { - /* split: we need 3 new regions, the front, middle and end. - cut: we need 2 regions, the front and end. - */ - - /* - start end - ---------------*************************------------ - P1 P2 P3 P4 - SPLIT: - ---------------*****++++++++++++++++====------------ - CUT - ---------------*****----------------====------------ - - */ - - if (!cutting) { - /* "middle" ++++++ */ - - RegionFactory::region_name (new_name, current->name(), false); - - PropertyList plist; - - 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::automatic, true); - plist.add (Properties::left_of_split, true); - plist.add (Properties::right_of_split, true); - - region = RegionFactory::create (current, plist); - add_region_internal (region, start); - new_regions.push_back (region); - } + SPLIT: + ---------------*************************------------ + CUT: + ---------------------------------------------------- - /* "end" ====== */ - - RegionFactory::region_name (new_name, current->name(), false); - - PropertyList plist; - - 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::automatic, true); - plist.add (Properties::right_of_split, true); - - region = RegionFactory::create (current, plist); - - add_region_internal (region, end); - new_regions.push_back (region); - - /* "front" ***** */ - - current->suspend_property_changes (); - thawlist.push_back (current); - current->cut_end (pos2 - 1, this); - - } else if (overlap == OverlapEnd) { - - /* - start end - ---------------*************************------------ - P1 P2 P4 P3 - SPLIT: - ---------------**************+++++++++++------------ - CUT: - ---------------**************----------------------- - */ - - if (!cutting) { - - /* end +++++ */ - - RegionFactory::region_name (new_name, current->name(), false); - - PropertyList plist; - - 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::automatic, true); - plist.add (Properties::left_of_split, true); - - region = RegionFactory::create (current, plist); - - add_region_internal (region, start); - new_regions.push_back (region); - } + */ - /* front ****** */ + if (cutting) { + remove_region_internal (current); + } - current->suspend_property_changes (); - thawlist.push_back (current); - current->cut_end (pos2 - 1, this); + new_regions.push_back (current); + } + } - } else if (overlap == OverlapStart) { + in_partition = false; + } + } - /* split: we need 2 regions: the front and the end. - cut: just trim current to skip the cut area - */ + boost::shared_ptr + Playlist::cut_copy (boost::shared_ptr (Playlist::*pmf)(framepos_t, framecnt_t,bool), list& ranges, bool result_is_hidden) + { + boost::shared_ptr ret; + boost::shared_ptr pl; + framepos_t start; - /* - start end - ---------------*************************------------ - P2 P1 P3 P4 + if (ranges.empty()) { + return boost::shared_ptr(); + } - SPLIT: - ---------------****+++++++++++++++++++++------------ - CUT: - -------------------*********************------------ + start = ranges.front().start; - */ + for (list::iterator i = ranges.begin(); i != ranges.end(); ++i) { - if (!cutting) { - /* front **** */ - RegionFactory::region_name (new_name, current->name(), false); + pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden); - PropertyList plist; - - 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::automatic, true); - plist.add (Properties::right_of_split, true); + if (i == ranges.begin()) { + ret = pl; + } else { - region = RegionFactory::create (current, plist); + /* paste the next section into the nascent playlist, + offset to reflect the start of the first range we + chopped. + */ - add_region_internal (region, pos1); - new_regions.push_back (region); - } + ret->paste (pl, (*i).start - start, 1.0f); + } + } + + return ret; + } + + boost::shared_ptr + Playlist::cut (list& ranges, bool result_is_hidden) + { + boost::shared_ptr (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut; + return cut_copy (pmf, ranges, result_is_hidden); + } + + boost::shared_ptr + Playlist::copy (list& ranges, bool result_is_hidden) + { + boost::shared_ptr (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy; + return cut_copy (pmf, ranges, result_is_hidden); + } + + boost::shared_ptr + Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden) + { + boost::shared_ptr the_copy; + RegionList thawlist; + char buf[32]; + + snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt); + string new_name = _name; + new_name += '.'; + new_name += buf; + + if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) { + return boost::shared_ptr(); + } + + partition_internal (start, start+cnt-1, true, thawlist); + + for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { + (*i)->resume_property_changes(); + } + + return the_copy; + } + + boost::shared_ptr + Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden) + { + char buf[32]; + + snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt); + string new_name = _name; + new_name += '.'; + new_name += buf; + + cnt = min (_get_extent().second - start, cnt); + return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden); + } + + int + Playlist::paste (boost::shared_ptr other, framepos_t position, float times) + { + times = fabs (times); + + { + RegionReadLock rl2 (other.get()); + + int itimes = (int) floor (times); + framepos_t pos = position; + framecnt_t const shift = other->_get_extent().second; + layer_t top = top_layer (); + + { + 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; + } + } + } + + return 0; + } + + + void + Playlist::duplicate (boost::shared_ptr region, framepos_t position, float times) + { + times = fabs (times); + + 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(); + } + + if (floor (times) != times) { + framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times))); + string name; + RegionFactory::region_name (name, region->name(), false); + + { + PropertyList plist; + + plist.add (Properties::start, region->start()); + plist.add (Properties::length, length); + plist.add (Properties::name, name); + + boost::shared_ptr sub = RegionFactory::create (region, plist); + add_region_internal (sub, pos); + set_layer (sub, DBL_MAX); + } + } + } + + void + Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue) + { + RegionWriteLock rlock (this); + RegionList copy (regions.rlist()); + RegionList fixup; + + for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { + + if ((*r)->last_frame() < at) { + /* too early */ + continue; + } + + if (at > (*r)->first_frame() && at < (*r)->last_frame()) { + /* intersected region */ + if (!move_intersected) { + continue; + } + } + + /* do not move regions glued to music time - that + has to be done separately. + */ - /* end */ + if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) { + fixup.push_back (*r); + continue; + } + + (*r)->set_position ((*r)->position() + distance); + } + + /* XXX: may not be necessary; Region::post_set should do this, I think */ + for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) { + (*r)->recompute_position_from_lock_style (); + } + } + + void + Playlist::split (framepos_t at) + { + RegionWriteLock rlock (this); + RegionList copy (regions.rlist()); + + /* use a copy since this operation can modify the region list + */ + + for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { + _split_region (*r, at); + } + } + + void + Playlist::split_region (boost::shared_ptr region, framepos_t playlist_position) + { + RegionWriteLock rl (this); + _split_region (region, playlist_position); + } + + void + Playlist::_split_region (boost::shared_ptr region, framepos_t playlist_position) + { + if (!region->covers (playlist_position)) { + return; + } + + if (region->position() == playlist_position || + region->last_frame() == playlist_position) { + return; + } + + boost::shared_ptr left; + boost::shared_ptr right; + frameoffset_t before; + frameoffset_t after; + string before_name; + string after_name; + + /* split doesn't change anything about length, so don't try to splice */ + + bool old_sp = _splicing; + _splicing = true; + + before = playlist_position - region->position(); + after = region->length() - before; + + RegionFactory::region_name (before_name, region->name(), false); + + { + PropertyList plist; + + plist.add (Properties::position, region->position ()); + 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 + is necessary to get audio region gain envelopes right. + */ + left = RegionFactory::create (region, 0, plist); + } + + RegionFactory::region_name (after_name, region->name(), false); + + { + PropertyList plist; + + plist.add (Properties::position, region->position() + before); + 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); + } + + add_region_internal (left, region->position()); + add_region_internal (right, region->position() + before); + remove_region_internal (region); + + _splicing = old_sp; + } + + void + Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) + { + if (_splicing || in_set_state) { + /* don't respond to splicing moves or state setting */ + return; + } + + if (_edit_mode == Splice) { + splice_locked (at, distance, exclude); + } + } + + void + Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) + { + if (_splicing || in_set_state) { + /* don't respond to splicing moves or state setting */ + return; + } + + if (_edit_mode == Splice) { + splice_unlocked (at, distance, exclude); + } + } + + void + Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) + { + { + RegionWriteLock rl (this); + core_splice (at, distance, exclude); + } + } + + void + Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) + { + core_splice (at, distance, exclude); + } + + void + Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) + { + _splicing = true; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + if (exclude && (*i) == exclude) { + continue; + } + + if ((*i)->position() >= at) { + framepos_t new_pos = (*i)->position() + distance; + if (new_pos < 0) { + new_pos = 0; + } else if (new_pos >= max_framepos - (*i)->length()) { + new_pos = max_framepos - (*i)->length(); + } + + (*i)->set_position (new_pos); + } + } + + _splicing = false; + + notify_contents_changed (); + } + + void + Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr region) + { + if (in_set_state || _splicing || _nudging || _shuffling) { + return; + } + + if (what_changed.contains (Properties::position)) { + + /* remove it from the list then add it back in + the right place again. + */ - current->suspend_property_changes (); - thawlist.push_back (current); - current->trim_front (pos3, this); - } else if (overlap == OverlapExternal) { + RegionSortByPosition cmp; - /* split: no split required. - cut: remove the region. - */ + RegionList::iterator i = find (regions.begin(), regions.end(), region); - /* - start end - ---------------*************************------------ - P2 P1 P3 P4 + if (i == regions.end()) { + /* the region bounds are being modified but its not currently + in the region list. we will use its bounds correctly when/if + it is added + */ + return; + } + + regions.erase (i); + regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region); + } + + if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) { + + frameoffset_t delta = 0; + + if (what_changed.contains (Properties::position)) { + delta = region->position() - region->last_position(); + } + + if (what_changed.contains (Properties::length)) { + delta += region->length() - region->last_length(); + } + + if (delta) { + possibly_splice (region->last_position() + region->last_length(), delta, region); + } + + if (holding_state ()) { + pending_bounds.push_back (region); + } else { + notify_contents_changed (); + relayer (); + list > xf; + xf.push_back (Evoral::Range (region->last_range())); + xf.push_back (Evoral::Range (region->range())); + coalesce_and_check_crossfades (xf); + } + } + } + + void + Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr weak_region) + { + boost::shared_ptr region (weak_region.lock()); + + if (!region) { + return; + } + + /* this makes a virtual call to the right kind of playlist ... */ + + region_changed (what_changed, region); + } + + bool + Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr region) + { + PropertyChange our_interests; + PropertyChange bounds; + PropertyChange pos_and_length; + bool save = false; + + if (in_set_state || in_flush) { + return false; + } + + our_interests.add (Properties::muted); + our_interests.add (Properties::layer); + our_interests.add (Properties::opaque); + + bounds.add (Properties::start); + bounds.add (Properties::position); + bounds.add (Properties::length); + + pos_and_length.add (Properties::position); + pos_and_length.add (Properties::length); + + if (what_changed.contains (bounds)) { + region_bounds_changed (what_changed, region); + save = !(_splicing || _nudging); + } + + 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)) { + notify_region_end_trimmed (region); + } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) { + 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() + */ - SPLIT: - ---------------*************************------------ - CUT: - ---------------------------------------------------- + if (what_changed.contains (our_interests)) { + save = true; + } - */ + return save; + } - if (cutting) { - remove_region_internal (current); - } + void + Playlist::drop_regions () + { + RegionWriteLock rl (this); + regions.clear (); + all_regions.clear (); + } - new_regions.push_back (current); - } - } + void + Playlist::sync_all_regions_with_regions () + { + RegionWriteLock rl (this); - in_partition = false; - } + all_regions.clear (); - for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) { - check_dependents (*i, false); - } -} + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + all_regions.insert (*i); + } + } -boost::shared_ptr -Playlist::cut_copy (boost::shared_ptr (Playlist::*pmf)(framepos_t, framecnt_t,bool), list& ranges, bool result_is_hidden) -{ - boost::shared_ptr ret; - boost::shared_ptr pl; - framepos_t start; + void + Playlist::clear (bool with_signals) + { + { + RegionWriteLock rl (this); - if (ranges.empty()) { - return boost::shared_ptr(); - } + region_state_changed_connections.drop_connections (); - start = ranges.front().start; + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + pending_removes.insert (*i); + } - for (list::iterator i = ranges.begin(); i != ranges.end(); ++i) { + regions.clear (); - pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden); + for (set >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) { + remove_dependents (*s); + } + } - if (i == ranges.begin()) { - ret = pl; - } else { + if (with_signals) { - /* paste the next section into the nascent playlist, - offset to reflect the start of the first range we - chopped. - */ + for (set >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) { + RegionRemoved (boost::weak_ptr (*s)); /* EMIT SIGNAL */ + } - ret->paste (pl, (*i).start - start, 1.0f); - } - } + pending_removes.clear (); + pending_contents_change = false; + ContentsChanged (); + } - return ret; -} + } -boost::shared_ptr -Playlist::cut (list& ranges, bool result_is_hidden) -{ - boost::shared_ptr (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut; - return cut_copy (pmf, ranges, result_is_hidden); -} + /*********************************************************************** + FINDING THINGS + **********************************************************************/ -boost::shared_ptr -Playlist::copy (list& ranges, bool result_is_hidden) +boost::shared_ptr +Playlist::regions_at (framepos_t frame) { - boost::shared_ptr (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy; - return cut_copy (pmf, ranges, result_is_hidden); + RegionReadLock rlock (this); + return find_regions_at (frame); } -boost::shared_ptr -Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden) -{ - boost::shared_ptr the_copy; - RegionList thawlist; - char buf[32]; - - snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt); - string new_name = _name; - new_name += '.'; - new_name += buf; - - if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) { - return boost::shared_ptr(); - } + uint32_t + Playlist::count_regions_at (framepos_t frame) const + { + RegionReadLock rlock (const_cast(this)); + uint32_t cnt = 0; - partition_internal (start, start+cnt-1, true, thawlist); + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i)->covers (frame)) { + cnt++; + } + } - for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { - (*i)->resume_property_changes(); - } + return cnt; + } - return the_copy; -} + boost::shared_ptr + Playlist::top_region_at (framepos_t frame) -boost::shared_ptr -Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden) -{ - char buf[32]; + { + RegionReadLock rlock (this); + boost::shared_ptr rlist = find_regions_at (frame); + boost::shared_ptr region; - snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt); - string new_name = _name; - new_name += '.'; - new_name += buf; + if (rlist->size()) { + RegionSortByLayer cmp; + rlist->sort (cmp); + region = rlist->back(); + } - cnt = min (_get_extent().second - start, cnt); - return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden); -} + return region; + } -int -Playlist::paste (boost::shared_ptr other, framepos_t position, float times) -{ - times = fabs (times); + boost::shared_ptr + Playlist::top_unmuted_region_at (framepos_t frame) - { - RegionLock rl1 (this); - RegionLock rl2 (other.get()); + { + RegionReadLock rlock (this); + boost::shared_ptr rlist = find_regions_at (frame); - framecnt_t const old_length = _get_extent().second; + for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) { - int itimes = (int) floor (times); - framepos_t pos = position; - framecnt_t const shift = other->_get_extent().second; - layer_t top_layer = regions.size(); + RegionList::iterator tmp = i; + ++tmp; - while (itimes--) { - for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) { - boost::shared_ptr copy_of_region = RegionFactory::create (*i, true); + if ((*i)->muted()) { + rlist->erase (i); + } - /* put these new regions on top of all existing ones, but preserve - the ordering they had in the original playlist. - */ + i = tmp; + } - copy_of_region->set_layer (copy_of_region->layer() + top_layer); - add_region_internal (copy_of_region, (*i)->position() + pos); - } - pos += shift; - } + boost::shared_ptr region; + if (rlist->size()) { + RegionSortByLayer cmp; + rlist->sort (cmp); + region = rlist->back(); + } - /* XXX shall we handle fractional cases at some point? */ + return region; + } - if (old_length != _get_extent().second) { - notify_length_changed (); +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 0; + + return rlist; } - -void -Playlist::duplicate (boost::shared_ptr region, framepos_t position, float times) +boost::shared_ptr +Playlist::regions_with_start_within (Evoral::Range range) { - times = fabs (times); + RegionReadLock rlock (this); + boost::shared_ptr rlist (new RegionList); - RegionLock 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); - pos += region->length(); - } - - if (floor (times) != times) { - framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times))); - string name; - RegionFactory::region_name (name, region->name(), false); - - { - PropertyList plist; - - plist.add (Properties::start, region->start()); - plist.add (Properties::length, length); - plist.add (Properties::name, name); - - boost::shared_ptr sub = RegionFactory::create (region, plist); - add_region_internal (sub, pos); + 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); } } + + return rlist; } -void -Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue) +boost::shared_ptr +Playlist::regions_with_end_within (Evoral::Range range) { - RegionLock rlock (this); - RegionList copy (regions.rlist()); - RegionList fixup; - - for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { - - if ((*r)->last_frame() < at) { - /* too early */ - continue; - } - - if (at > (*r)->first_frame() && at < (*r)->last_frame()) { - /* intersected region */ - if (!move_intersected) { - continue; - } - } + RegionReadLock rlock (this); + boost::shared_ptr rlist (new RegionList); - /* do not move regions glued to music time - that - has to be done separately. - */ - - if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) { - fixup.push_back (*r); - continue; + 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); } - - (*r)->set_position ((*r)->position() + distance, this); } - for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) { - (*r)->recompute_position_from_lock_style (); - } + return rlist; } -void -Playlist::split (framepos_t at) +/** @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) { - RegionLock rlock (this); - RegionList copy (regions.rlist()); - - /* use a copy since this operation can modify the region list - */ - - for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) { - _split_region (*r, at); - } + RegionReadLock rlock (this); + return regions_touched_locked (start, end); } -void -Playlist::split_region (boost::shared_ptr region, framepos_t playlist_position) +boost::shared_ptr +Playlist::regions_touched_locked (framepos_t start, framepos_t end) { - RegionLock rl (this); - _split_region (region, playlist_position); + 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; } -void -Playlist::_split_region (boost::shared_ptr region, framepos_t playlist_position) +framepos_t +Playlist::find_next_transient (framepos_t from, int dir) { - if (!region->covers (playlist_position)) { - return; - } - - if (region->position() == playlist_position || - region->last_frame() == playlist_position) { - return; - } - - boost::shared_ptr left; - boost::shared_ptr right; - frameoffset_t before; - frameoffset_t after; - string before_name; - string after_name; - - /* split doesn't change anything about length, so don't try to splice */ - - bool old_sp = _splicing; - _splicing = true; - - before = playlist_position - region->position(); - after = region->length() - before; - - RegionFactory::region_name (before_name, region->name(), false); - - { - PropertyList plist; - - plist.add (Properties::position, region->position ()); - plist.add (Properties::length, before); - plist.add (Properties::name, before_name); - plist.add (Properties::left_of_split, true); - - /* note: we must use the version of ::create with an offset here, - since it supplies that offset to the Region constructor, which - is necessary to get audio region gain envelopes right. - */ - left = RegionFactory::create (region, 0, plist); - } - - RegionFactory::region_name (after_name, region->name(), false); - - { - PropertyList plist; - - plist.add (Properties::position, region->position() + before); - plist.add (Properties::length, after); - plist.add (Properties::name, after_name); - plist.add (Properties::right_of_split, true); - - /* same note as above */ - right = RegionFactory::create (region, before, plist); - } - - 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; -} - -void -Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) -{ - if (_splicing || in_set_state) { - /* don't respond to splicing moves or state setting */ - return; - } - - if (_edit_mode == Splice) { - splice_locked (at, distance, exclude); - } -} - -void -Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) -{ - if (_splicing || in_set_state) { - /* don't respond to splicing moves or state setting */ - return; - } - - if (_edit_mode == Splice) { - splice_unlocked (at, distance, exclude); - } -} - -void -Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) -{ - { - RegionLock rl (this); - core_splice (at, distance, exclude); - } -} - -void -Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) -{ - core_splice (at, distance, exclude); -} - -void -Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) -{ - _splicing = true; - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - - if (exclude && (*i) == exclude) { - continue; - } - - if ((*i)->position() >= at) { - framepos_t new_pos = (*i)->position() + distance; - if (new_pos < 0) { - new_pos = 0; - } else if (new_pos >= max_framepos - (*i)->length()) { - new_pos = max_framepos - (*i)->length(); - } - - (*i)->set_position (new_pos, this); - } - } - - _splicing = false; - - notify_length_changed (); -} - -void -Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr region) -{ - if (in_set_state || _splicing || _nudging || _shuffling) { - return; - } - - if (what_changed.contains (Properties::position)) { - - /* remove it from the list then add it back in - the right place again. - */ - - RegionSortByPosition cmp; - - RegionList::iterator i = find (regions.begin(), regions.end(), region); - - if (i == regions.end()) { - /* the region bounds are being modified but its not currently - in the region list. we will use its bounds correctly when/if - it is added - */ - return; - } - - regions.erase (i); - regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region); - } - - if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) { - - frameoffset_t delta = 0; - - if (what_changed.contains (Properties::position)) { - delta = region->position() - region->last_position(); - } - - if (what_changed.contains (Properties::length)) { - delta += region->length() - region->last_length(); - } - - if (delta) { - possibly_splice (region->last_position() + region->last_length(), delta, region); - } - - 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 (); - relayer (); - check_dependents (region, false); - } - } -} - -void -Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr weak_region) -{ - boost::shared_ptr region (weak_region.lock()); - - if (!region) { - return; - } - - /* this makes a virtual call to the right kind of playlist ... */ - - region_changed (what_changed, region); -} - -bool -Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr region) -{ - PropertyChange our_interests; - PropertyChange bounds; - PropertyChange pos_and_length; - bool save = false; - - if (in_set_state || in_flush) { - return false; - } - - our_interests.add (Properties::muted); - our_interests.add (Properties::layer); - our_interests.add (Properties::opaque); - - bounds.add (Properties::start); - bounds.add (Properties::position); - bounds.add (Properties::length); - - pos_and_length.add (Properties::position); - pos_and_length.add (Properties::length); - - if (what_changed.contains (bounds)) { - region_bounds_changed (what_changed, region); - 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)) { - notify_region_end_trimmed (region); - } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) { - 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; - } - - return save; -} - -void -Playlist::drop_regions () -{ - RegionLock rl (this); - regions.clear (); - all_regions.clear (); -} - -void -Playlist::sync_all_regions_with_regions () -{ - RegionLock rl (this); - - all_regions.clear (); - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - all_regions.insert (*i); - } -} - -void -Playlist::clear (bool with_signals) -{ - { - RegionLock rl (this); - - region_state_changed_connections.drop_connections (); - - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - pending_removes.insert (*i); - } - - regions.clear (); - - for (set >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) { - remove_dependents (*s); - } - } - - if (with_signals) { - - for (set >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) { - RegionRemoved (boost::weak_ptr (*s)); /* EMIT SIGNAL */ - } - - pending_removes.clear (); - pending_length = false; - LengthChanged (); - pending_contents_change = false; - ContentsChanged (); - } - -} - -/*********************************************************************** - FINDING THINGS - **********************************************************************/ - -Playlist::RegionList * -Playlist::regions_at (framepos_t frame) - -{ - RegionLock rlock (this); - return find_regions_at (frame); -} - -uint32_t -Playlist::count_regions_at (framepos_t frame) const -{ - RegionLock rlock (const_cast(this)); - uint32_t cnt = 0; - - for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { - if ((*i)->covers (frame)) { - cnt++; - } - } - - return cnt; -} - -boost::shared_ptr -Playlist::top_region_at (framepos_t frame) - -{ - RegionLock rlock (this); - RegionList *rlist = find_regions_at (frame); - boost::shared_ptr region; - - if (rlist->size()) { - RegionSortByLayer cmp; - rlist->sort (cmp); - region = rlist->back(); - } - - delete rlist; - return region; -} - -boost::shared_ptr -Playlist::top_unmuted_region_at (framepos_t frame) - -{ - RegionLock rlock (this); - RegionList *rlist = find_regions_at (frame); - - for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) { - - RegionList::iterator tmp = i; - ++tmp; - - if ((*i)->muted()) { - rlist->erase (i); - } - - i = tmp; - } - - boost::shared_ptr region; - - if (rlist->size()) { - RegionSortByLayer cmp; - rlist->sort (cmp); - 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); + 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) { @@ -1972,30 +1790,30 @@ Playlist::find_next_transient (framepos_t from, int dir) 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; } @@ -2005,33 +1823,33 @@ Playlist::find_next_transient (framepos_t from, int dir) 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); + 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 (); @@ -2043,10 +1861,10 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir) pos = r->sync_position (); break; } - + switch (dir) { case 1: /* forwards */ - + if (pos > frame) { if ((distance = pos - frame) < closest) { closest = distance; @@ -2054,241 +1872,244 @@ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir) end_iter = true; } } - + break; - + default: /* backwards */ - + if (pos < frame) { if ((distance = frame - pos) < closest) { closest = distance; ret = r; } - } - else { + } else { end_iter = true; } - + break; } } - + return ret; } -framepos_t -Playlist::find_next_region_boundary (framepos_t frame, int dir) -{ - RegionLock rlock (this); - - framepos_t closest = max_framepos; - framepos_t ret = -1; - - if (dir > 0) { + framepos_t + Playlist::find_next_region_boundary (framepos_t frame, int dir) + { + RegionReadLock rlock (this); - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + framepos_t closest = max_framepos; + framepos_t ret = -1; - boost::shared_ptr r = (*i); - frameoffset_t distance; + if (dir > 0) { - if (r->first_frame() > frame) { + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - distance = r->first_frame() - frame; + boost::shared_ptr r = (*i); + frameoffset_t distance; - if (distance < closest) { - ret = r->first_frame(); - closest = distance; - } - } + if (r->first_frame() > frame) { - if (r->last_frame () > frame) { + distance = r->first_frame() - frame; - distance = r->last_frame () - frame; + if (distance < closest) { + ret = r->first_frame(); + closest = distance; + } + } - if (distance < closest) { - ret = r->last_frame (); - closest = distance; - } - } - } + if (r->last_frame () > frame) { - } else { + distance = r->last_frame () - frame; - for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) { + if (distance < closest) { + ret = r->last_frame (); + closest = distance; + } + } + } - boost::shared_ptr r = (*i); - frameoffset_t distance; + } else { - if (r->last_frame() < frame) { + for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) { - distance = frame - r->last_frame(); + boost::shared_ptr r = (*i); + frameoffset_t distance; - if (distance < closest) { - ret = r->last_frame(); - closest = distance; - } - } + if (r->last_frame() < frame) { - if (r->first_frame() < frame) { + distance = frame - r->last_frame(); - distance = frame - r->first_frame(); + if (distance < closest) { + ret = r->last_frame(); + closest = distance; + } + } - if (distance < closest) { - ret = r->first_frame(); - closest = distance; - } - } - } - } + if (r->first_frame() < frame) { - return ret; -} + distance = frame - r->first_frame(); + if (distance < closest) { + ret = r->first_frame(); + closest = distance; + } + } + } + } -/***********************************************************************/ + return ret; + } + /***********************************************************************/ -void -Playlist::mark_session_dirty () -{ - if (!in_set_state && !holding_state ()) { - _session.set_dirty(); - } -} -void -Playlist::rdiff (vector& cmds) const -{ - RegionLock rlock (const_cast (this)); - Stateful::rdiff (cmds); -} -void -Playlist::clear_owned_changes () -{ - RegionLock rlock (this); - Stateful::clear_owned_changes (); -} + void + Playlist::mark_session_dirty () + { + if (!in_set_state && !holding_state ()) { + _session.set_dirty(); + } + } -void -Playlist::update (const RegionListProperty::ChangeRecord& change) -{ - DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n", - name(), change.added.size(), change.removed.size())); - - freeze (); - /* add the added regions */ - for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) { - add_region ((*i), (*i)->position()); - } - /* remove the removed regions */ - for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) { - remove_region (*i); - } + void + Playlist::rdiff (vector& cmds) const + { + RegionReadLock rlock (const_cast (this)); + Stateful::rdiff (cmds); + } - thaw (); -} + void + Playlist::clear_owned_changes () + { + RegionReadLock rlock (this); + Stateful::clear_owned_changes (); + } -int -Playlist::set_state (const XMLNode& node, int version) -{ - XMLNode *child; - XMLNodeList nlist; - XMLNodeConstIterator niter; - XMLPropertyList plist; - XMLPropertyConstIterator piter; - XMLProperty *prop; - boost::shared_ptr region; - string region_name; + void + Playlist::update (const RegionListProperty::ChangeRecord& change) + { + DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n", + name(), change.added.size(), change.removed.size())); - in_set_state++; + freeze (); + /* add the added regions */ + for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) { + add_region_internal ((*i), (*i)->position()); + } + /* remove the removed regions */ + for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) { + remove_region (*i); + } - if (node.name() != "Playlist") { - in_set_state--; - return -1; - } + thaw (); + } - freeze (); + int + Playlist::set_state (const XMLNode& node, int version) + { + XMLNode *child; + XMLNodeList nlist; + XMLNodeConstIterator niter; + XMLPropertyList plist; + XMLPropertyConstIterator piter; + XMLProperty *prop; + boost::shared_ptr region; + string region_name; + bool seen_region_nodes = false; + int ret = 0; - plist = node.properties(); + in_set_state++; - for (piter = plist.begin(); piter != plist.end(); ++piter) { + if (node.name() != "Playlist") { + in_set_state--; + return -1; + } - prop = *piter; + freeze (); - if (prop->name() == X_("name")) { - _name = prop->value(); - _set_sort_id (); - } else if (prop->name() == X_("id")) { - _id = prop->value(); - } else if (prop->name() == X_("orig-diskstream-id")) { - _orig_diskstream_id = prop->value (); - } else if (prop->name() == X_("frozen")) { - _frozen = string_is_affirmative (prop->value()); - } else if (prop->name() == X_("combine-ops")) { - _combine_ops = atoi (prop->value()); - } - } + plist = node.properties(); - clear (true); + set_id (node); - nlist = node.children(); + for (piter = plist.begin(); piter != plist.end(); ++piter) { - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + prop = *piter; - child = *niter; + if (prop->name() == X_("name")) { + _name = prop->value(); + _set_sort_id (); + } else if (prop->name() == X_("orig-diskstream-id")) { + /* XXX legacy session: fix up later */ + _orig_track_id = prop->value (); + } else if (prop->name() == X_("orig-track-id")) { + _orig_track_id = prop->value (); + } else if (prop->name() == X_("frozen")) { + _frozen = string_is_affirmative (prop->value()); + } else if (prop->name() == X_("combine-ops")) { + _combine_ops = atoi (prop->value()); + } + } - if (child->name() == "Region") { + clear (true); - if ((prop = child->property ("id")) == 0) { - error << _("region state node has no ID, ignored") << endmsg; - continue; - } - - ID id = prop->value (); + nlist = node.children(); - if ((region = region_by_id (id))) { + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - region->suspend_property_changes (); + child = *niter; - if (region->set_state (*child, version)) { - region->resume_property_changes (); - continue; - } - - } else if ((region = RegionFactory::create (_session, *child, true)) != 0) { - region->suspend_property_changes (); - } else { - error << _("Playlist: cannot create region from XML") << endmsg; - continue; + if (child->name() == "Region") { + + seen_region_nodes = true; + + if ((prop = child->property ("id")) == 0) { + error << _("region state node has no ID, ignored") << endmsg; + continue; + } + + ID id = prop->value (); + + if ((region = region_by_id (id))) { + + region->suspend_property_changes (); + + if (region->set_state (*child, version)) { + region->resume_property_changes (); + continue; + } + + } else if ((region = RegionFactory::create (_session, *child, true)) != 0) { + region->suspend_property_changes (); + } else { + error << _("Playlist: cannot create region from XML") << endmsg; + return -1; } - - add_region (region, region->position(), 1.0); - - // So that layer_op ordering doesn't get screwed up - region->set_last_layer_op( region->layer()); + { + RegionWriteLock rlock (this); + add_region_internal (region, region->position()); + } + region->resume_property_changes (); - } + } } - /* 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); + if (seen_region_nodes && regions.empty()) { + ret = -1; } - + thaw (); notify_contents_changed (); in_set_state--; first_set_state = false; - return 0; + + return ret; } XMLNode& @@ -2315,12 +2136,12 @@ Playlist::state (bool full_state) node->add_property (X_("name"), _name); node->add_property (X_("type"), _type.to_string()); - _orig_diskstream_id.print (buf, sizeof (buf)); - node->add_property (X_("orig-diskstream-id"), buf); + _orig_track_id.print (buf, sizeof (buf)); + node->add_property (X_("orig-track-id"), buf); 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); @@ -2340,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 (); } @@ -2363,6 +2194,11 @@ Playlist::_get_extent () const { pair ext (max_framepos, 0); + if (regions.empty()) { + ext.first = 0; + return ext; + } + for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { pair const e ((*i)->position(), (*i)->position() + (*i)->length()); if (e.first < ext.first) { @@ -2392,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) { @@ -2407,21 +2243,62 @@ Playlist::set_edit_mode (EditMode mode) _edit_mode = mode; } -/******************** - * Region Layering - ********************/ +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::set_layer (boost::shared_ptr 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 > 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 @@ -2444,30 +2321,17 @@ Playlist::relayer () vector > layers; layers.push_back (vector (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. @@ -2525,171 +2389,51 @@ Playlist::relayer () for (int k = start_division; k <= end_division; ++k) { 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) { - 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 (); } void Playlist::lower_region (boost::shared_ptr 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 (); } void Playlist::raise_region_to_top (boost::shared_ptr 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 (); } void Playlist::lower_region_to_bottom (boost::shared_ptr 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, int dir) -{ - RegionList::iterator i; - typedef pair,layer_t> LayerInfo; - list layerinfo; - - { - RegionLock rlock (const_cast (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::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::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 (); } void @@ -2701,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) { @@ -2726,7 +2470,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards) } } - (*i)->set_position (new_pos, this); + (*i)->set_position (new_pos); moved = true; } } @@ -2734,7 +2478,7 @@ Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards) if (moved) { _nudging = false; - notify_length_changed (); + notify_contents_changed (); } } @@ -2742,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)) { @@ -2756,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 */ @@ -2772,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) { @@ -2824,13 +2568,6 @@ Playlist::set_frozen (bool yn) _frozen = yn; } -void -Playlist::timestamp_layer_op (boost::shared_ptr region) -{ - region->set_last_layer_op (++layer_op_counter); -} - - void Playlist::shuffle (boost::shared_ptr region, int dir) { @@ -2843,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) { @@ -2877,8 +2614,8 @@ Playlist::shuffle (boost::shared_ptr region, int dir) new_pos = region->position() + (*next)->length(); } - (*next)->set_position (region->position(), this); - region->set_position (new_pos, this); + (*next)->set_position (region->position()); + region->set_position (new_pos); /* avoid a full sort */ @@ -2918,8 +2655,8 @@ Playlist::shuffle (boost::shared_ptr region, int dir) new_pos = (*prev)->position() + region->length(); } - region->set_position ((*prev)->position(), this); - (*prev)->set_position (new_pos, this); + region->set_position ((*prev)->position()); + (*prev)->set_position (new_pos); /* avoid a full sort */ @@ -2940,8 +2677,6 @@ Playlist::shuffle (boost::shared_ptr region, int dir) if (moved) { relayer (); - check_dependents (region, false); - notify_contents_changed(); } @@ -2950,7 +2685,7 @@ Playlist::shuffle (boost::shared_ptr region, int dir) 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; @@ -2962,13 +2697,13 @@ 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 (); for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) { - (*i)->update_position_after_tempo_map_change (); + (*i)->update_after_tempo_map_change (); } thaw (); @@ -2977,40 +2712,17 @@ 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); } } -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 (this)); - + RegionReadLock (const_cast (this)); + RegionList::const_iterator i = regions.begin (); while (i != regions.end() && !(*i)->covers (p)) { ++i; @@ -3023,13 +2735,13 @@ 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()) { RegionList::iterator j = i; ++j; - + if ((*i)->uses_source (s)) { remove_region_internal (*i); } @@ -3046,8 +2758,8 @@ 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 (); RegionList copy = regions.rlist (); @@ -3065,12 +2777,13 @@ Playlist::find_next_top_layer_position (framepos_t t) const boost::shared_ptr Playlist::combine (const RegionList& r) { - PropertyList plist; + PropertyList plist; uint32_t channels = 0; uint32_t layer = 0; framepos_t earliest_position = max_framepos; vector old_and_new_regions; vector > originals; + vector > copies; string parent_name; string child_name; uint32_t max_level = 0; @@ -3105,13 +2818,15 @@ Playlist::combine (const RegionList& r) old_and_new_regions.push_back (TwoRegions (original_region,copied_region)); originals.push_back (original_region); + copies.push_back (copied_region); RegionFactory::add_compound_association (original_region, copied_region); /* 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 */ channels = max (channels, original_region->n_channels()); @@ -3123,15 +2838,18 @@ Playlist::combine (const RegionList& r) pl->in_partition = false; + pre_combine (copies); + /* now create a new PlaylistSource for each channel in the new playlist */ SourceList sources; pair extent = pl->get_extent(); - + for (uint32_t chn = 0; chn < channels; ++chn) { - sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, parent_name, chn, 0, extent.second, false, false)); + sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false)); + } - + /* now a new whole-file region using the list of sources */ plist.add (Properties::start, 0); @@ -3142,7 +2860,7 @@ Playlist::combine (const RegionList& r) boost::shared_ptr parent_region = RegionFactory::create (sources, plist, true); /* now the non-whole-file region that we will actually use in the - * playlist + * playlist */ plist.clear (); @@ -3153,27 +2871,23 @@ Playlist::combine (const RegionList& r) boost::shared_ptr compound_region = RegionFactory::create (parent_region, plist, true); - /* add any dependent regions to the new playlist */ - - copy_dependents (old_and_new_regions, pl); - /* remove all the selected regions from the current playlist */ freeze (); - + for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) { remove_region (*i); } /* do type-specific stuff with the originals and the new compound - region + region */ - pre_combine (originals, compound_region); + post_combine (originals, compound_region); /* add the new region at the right location */ - + add_region (compound_region, earliest_position); _combine_ops++; @@ -3186,11 +2900,12 @@ Playlist::combine (const RegionList& r) void Playlist::uncombine (boost::shared_ptr target) { - // (1) check that its really a compound region - boost::shared_ptr pls; boost::shared_ptr pl; vector > originals; + vector old_and_new_regions; + + // (1) check that its really a compound region if ((pls = boost::dynamic_pointer_cast(target->source (0))) == 0) { return; @@ -3204,21 +2919,30 @@ Playlist::uncombine (boost::shared_ptr target) /* the leftmost (earliest) edge of the compound region starts at zero in its source, or larger if it has been trimmed or content-scrolled. - + the rightmost (latest) edge of the compound region relative to its source is the starting point plus the length of the region. */ - - cerr << "Compound region: bounds within nested playlist = " << adjusted_start << " .. " << adjusted_end << endl; - // (2) get all the original regions const RegionList& rl (pl->region_list().rlist()); RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations()); frameoffset_t move_offset = 0; + /* there are two possibilities here: + 1) the playlist that the playlist source was based on + is us, so just add the originals (which belonged to + us anyway) back in the right place. + + 2) the playlist that the playlist source was based on + is NOT us, so we need to make copies of each of + the original regions that we find, and add them + instead. + */ + bool same_playlist = (pls->original() == id()); + for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) { boost::shared_ptr current (*i); @@ -3234,19 +2958,18 @@ Playlist::uncombine (boost::shared_ptr target) if (i == rl.begin()) { move_offset = (target->position() - original->position()) - target->start(); - - - cerr << "Move offset is " << target->position() << " - " << original->position() - << " - " << target->start() << " = " << move_offset - << endl; - adjusted_start = original->position() + target->start(); adjusted_end = adjusted_start + target->length(); + } - cerr << "adjusted range = " << adjusted_start << " based on " - << original->position() << " + " << target->start() << " .. " - << adjusted_end << " from " << target->length() - << endl; + if (!same_playlist) { + framepos_t pos = original->position(); + /* make a copy, but don't announce it */ + original = RegionFactory::create (original, false); + /* the pure copy constructor resets position() to zero, + so fix that up. + */ + original->set_position (pos); } /* check to see how the original region (in the @@ -3254,63 +2977,51 @@ Playlist::uncombine (boost::shared_ptr target) * with the new state of the compound region. */ - cerr << "Original " << original->name() - << " overlaptype = " << enum_2_string (original->coverage (adjusted_start, adjusted_end)) - << " target range: " << adjusted_start << " .. " << adjusted_end - << " orig range: " << original->position() << " .. " << original->last_frame () - << endl; - original->clear_changes (); modified_region = false; switch (original->coverage (adjusted_start, adjusted_end)) { - case OverlapNone: - /* original region does not cover any part + case Evoral::OverlapNone: + /* original region does not cover any part of the current state of the compound region */ - cerr << "Not present - skip\n"; continue; - case OverlapInternal: + case Evoral::OverlapInternal: /* overlap is just a small piece inside the * original so trim both ends */ - original->trim_to (adjusted_start, adjusted_end - adjusted_start, this); + original->trim_to (adjusted_start, adjusted_end - adjusted_start); modified_region = true; - cerr << "trim to\n"; break; - - case OverlapExternal: + + case Evoral::OverlapExternal: /* overlap fully covers original, so leave it as is */ - cerr << "leave as is\n"; break; - case OverlapEnd: + case Evoral::OverlapEnd: /* overlap starts within but covers end, so trim the front of the region */ - original->trim_front (adjusted_start, this); + original->trim_front (adjusted_start); modified_region = true; - cerr << "trim front\n"; break; - - case OverlapStart: + + case Evoral::OverlapStart: /* overlap covers start but ends within, so * trim the end of the region. */ - original->trim_end (adjusted_end, this); + original->trim_end (adjusted_end); modified_region = true; - cerr << "trim end\n"; break; } if (move_offset) { /* fix the position to match any movement of the compound region. */ - cerr << "Moving region to new position based on " << original->position() << " + " << move_offset << endl; - original->set_position (original->position() + move_offset, this); + original->set_position (original->position() + move_offset); modified_region = true; } @@ -3319,10 +3030,11 @@ Playlist::uncombine (boost::shared_ptr target) } /* and add to the list of regions waiting to be - * re-inserted + * re-inserted */ originals.push_back (original); + old_and_new_regions.push_back (TwoRegions (*i, original)); } pre_uncombine (originals, target); @@ -3334,8 +3046,7 @@ Playlist::uncombine (boost::shared_ptr target) remove_region (target); - // (4) add the originals. This will reset their playlist reference back - // to us, which means they are no longer considered owned by the RegionFactory + // (4) add the constituent regions for (vector >::iterator i = originals.begin(); i != originals.end(); ++i) { add_region ((*i), (*i)->position()); @@ -3348,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) { @@ -3358,18 +3069,44 @@ 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 > ranges) { - RegionLock rlock (const_cast (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 >::iterator i = ranges.begin(); i != ranges.end(); ++i) { + for (list >::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; +void +Playlist::set_capture_insertion_in_progress (bool yn) +{ + _capture_insertion_underway = yn; }