2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/convert.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/stacktrace.h"
34 #include "pbd/stateful_diff_command.h"
35 #include "pbd/xml++.h"
37 #include "ardour/debug.h"
38 #include "ardour/playlist.h"
39 #include "ardour/session.h"
40 #include "ardour/region.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/region_sorters.h"
43 #include "ardour/playlist_factory.h"
44 #include "ardour/playlist_source.h"
45 #include "ardour/transient_detector.h"
46 #include "ardour/session_playlists.h"
47 #include "ardour/source_factory.h"
52 using namespace ARDOUR;
56 namespace Properties {
57 PBD::PropertyDescriptor<bool> regions;
61 struct ShowMeTheList {
62 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
64 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
66 boost::shared_ptr<Playlist> playlist;
73 Playlist::make_property_quarks ()
75 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
76 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
77 Properties::regions.property_id));
80 RegionListProperty::RegionListProperty (Playlist& pl)
81 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
87 RegionListProperty::RegionListProperty (RegionListProperty const & p)
88 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
89 , _playlist (p._playlist)
95 RegionListProperty::clone () const
97 return new RegionListProperty (*this);
101 RegionListProperty::create () const
103 return new RegionListProperty (_playlist);
107 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
109 /* All regions (even those which are deleted) have their state saved by other
110 code, so we can just store ID here.
113 node.add_property ("id", region->id().to_s ());
116 boost::shared_ptr<Region>
117 RegionListProperty::get_content_from_xml (XMLNode const & node) const
119 XMLProperty const * prop = node.property ("id");
122 PBD::ID id (prop->value ());
124 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
127 ret = RegionFactory::region_by_id (id);
133 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
134 : SessionObject(sess, nom)
139 first_set_state = false;
144 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
145 : SessionObject(sess, "unnamed playlist")
150 const XMLProperty* prop = node.property("type");
151 assert(!prop || DataType(prop->value()) == _type);
155 _name = "unnamed"; /* reset by set_state */
158 /* set state called by derived class */
161 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
162 : SessionObject(other->_session, namestr)
164 , _type(other->_type)
165 , _orig_track_id (other->_orig_track_id)
170 other->copy_regions (tmp);
174 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
175 add_region_internal( (*x), (*x)->position());
180 _splicing = other->_splicing;
181 _nudging = other->_nudging;
182 _edit_mode = other->_edit_mode;
185 first_set_state = false;
187 in_partition = false;
189 _frozen = other->_frozen;
191 layer_op_counter = other->layer_op_counter;
192 freeze_length = other->freeze_length;
195 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
196 : SessionObject(other->_session, str)
198 , _type(other->_type)
199 , _orig_track_id (other->_orig_track_id)
201 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
203 framepos_t end = start + cnt - 1;
209 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
211 boost::shared_ptr<Region> region;
212 boost::shared_ptr<Region> new_region;
213 frameoffset_t offset = 0;
214 framepos_t position = 0;
221 overlap = region->coverage (start, end);
227 case OverlapInternal:
228 offset = start - region->position();
235 position = region->position() - start;
236 len = end - region->position();
240 offset = start - region->position();
242 len = region->length() - offset;
245 case OverlapExternal:
247 position = region->position() - start;
248 len = region->length();
252 RegionFactory::region_name (new_name, region->name(), false);
256 plist.add (Properties::start, region->start() + offset);
257 plist.add (Properties::length, len);
258 plist.add (Properties::name, new_name);
259 plist.add (Properties::layer, region->layer());
261 new_region = RegionFactory::RegionFactory::create (region, plist);
263 add_region_internal (new_region, position);
267 first_set_state = false;
274 InUse (true); /* EMIT SIGNAL */
285 InUse (false); /* EMIT SIGNAL */
290 Playlist::copy_regions (RegionList& newlist) const
292 RegionLock rlock (const_cast<Playlist *> (this));
294 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
295 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
300 Playlist::init (bool hide)
302 add_property (regions);
303 _xml_node_name = X_("Playlist");
305 g_atomic_int_set (&block_notifications, 0);
306 g_atomic_int_set (&ignore_state_changes, 0);
307 pending_contents_change = false;
308 pending_layering = false;
309 first_set_state = true;
317 _edit_mode = Config->get_edit_mode();
319 in_partition = false;
322 layer_op_counter = 0;
325 _relayer_suspended = false;
327 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
328 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
330 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
333 Playlist::~Playlist ()
335 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
338 RegionLock rl (this);
340 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
341 (*i)->set_playlist (boost::shared_ptr<Playlist>());
345 /* GoingAway must be emitted by derived classes */
349 Playlist::_set_sort_id ()
352 Playlists are given names like <track name>.<id>
353 or <track name>.<edit group name>.<id> where id
354 is an integer. We extract the id and sort by that.
357 size_t dot_position = _name.val().find_last_of(".");
359 if (dot_position == string::npos) {
362 string t = _name.val().substr(dot_position + 1);
365 _sort_id = boost::lexical_cast<int>(t);
368 catch (boost::bad_lexical_cast e) {
375 Playlist::set_name (const string& str)
377 /* in a typical situation, a playlist is being used
378 by one diskstream and also is referenced by the
379 Session. if there are more references than that,
380 then don't change the name.
387 bool ret = SessionObject::set_name(str);
394 /***********************************************************************
395 CHANGE NOTIFICATION HANDLING
397 Notifications must be delayed till the region_lock is released. This
398 is necessary because handlers for the signals may need to acquire
399 the lock (e.g. to read from the playlist).
400 ***********************************************************************/
403 Playlist::begin_undo ()
410 Playlist::end_undo ()
419 delay_notifications ();
420 g_atomic_int_inc (&ignore_state_changes);
423 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
425 Playlist::thaw (bool from_undo)
427 g_atomic_int_dec_and_test (&ignore_state_changes);
428 release_notifications (from_undo);
433 Playlist::delay_notifications ()
435 g_atomic_int_inc (&block_notifications);
436 freeze_length = _get_extent().second;
439 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
441 Playlist::release_notifications (bool from_undo)
443 if (g_atomic_int_dec_and_test (&block_notifications)) {
444 flush_notifications (from_undo);
449 Playlist::notify_contents_changed ()
451 if (holding_state ()) {
452 pending_contents_change = true;
454 pending_contents_change = false;
455 ContentsChanged(); /* EMIT SIGNAL */
460 Playlist::notify_layering_changed ()
462 if (holding_state ()) {
463 pending_layering = true;
465 pending_layering = false;
466 LayeringChanged(); /* EMIT SIGNAL */
471 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
473 if (holding_state ()) {
474 pending_removes.insert (r);
475 pending_contents_change = true;
477 /* this might not be true, but we have to act
478 as though it could be.
480 pending_contents_change = false;
481 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
482 ContentsChanged (); /* EMIT SIGNAL */
487 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
489 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
491 /* We could timestamp the region's layer op here, but we're doing it in region_bounds_changed */
493 if (holding_state ()) {
495 pending_range_moves.push_back (move);
499 list< Evoral::RangeMove<framepos_t> > m;
501 RangesMoved (m, false);
507 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
509 timestamp_layer_op (LayerOpBoundsChange, r);
511 if (r->position() >= r->last_position()) {
512 /* trimmed shorter */
516 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
518 if (holding_state ()) {
520 pending_region_extensions.push_back (extra);
524 list<Evoral::Range<framepos_t> > r;
532 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
534 timestamp_layer_op (LayerOpBoundsChange, r);
536 if (r->length() < r->last_length()) {
537 /* trimmed shorter */
540 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
542 if (holding_state ()) {
544 pending_region_extensions.push_back (extra);
548 list<Evoral::Range<framepos_t> > r;
556 Playlist::notify_region_added (boost::shared_ptr<Region> r)
558 /* the length change might not be true, but we have to act
559 as though it could be.
562 timestamp_layer_op (LayerOpAdd, r);
564 if (holding_state()) {
565 pending_adds.insert (r);
566 pending_contents_change = true;
569 pending_contents_change = false;
570 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
571 ContentsChanged (); /* EMIT SIGNAL */
576 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
578 Playlist::flush_notifications (bool from_undo)
580 set<boost::shared_ptr<Region> > dependent_checks_needed;
581 set<boost::shared_ptr<Region> >::iterator s;
582 uint32_t regions_changed = false;
592 pending_bounds: regions whose bounds position and/or length changes
593 pending_removes: regions which were removed
594 pending_adds: regions which were added
595 pending_length: true if the playlist length might have changed
596 pending_contents_change: true if there was almost any change in the playlist
597 pending_range_moves: details of periods of time that have been moved about (when regions have been moved)
601 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
602 regions_changed = true;
605 /* Make a list of regions that need relayering */
606 RegionList regions_to_relayer;
608 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
609 regions_to_relayer.push_back (*r);
610 dependent_checks_needed.insert (*r);
613 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
614 remove_dependents (*s);
615 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
618 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
619 /* don't emit RegionAdded signal until relayering is done,
620 so that the region is fully setup by the time
621 anyone hear's that its been added
623 dependent_checks_needed.insert (*s);
626 if (regions_changed || pending_contents_change) {
627 pending_contents_change = false;
628 ContentsChanged (); /* EMIT SIGNAL */
631 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
632 (*s)->clear_changes ();
633 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
634 regions_to_relayer.push_back (*s);
637 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
638 check_dependents (*s, false);
641 if (!pending_range_moves.empty ()) {
642 RangesMoved (pending_range_moves, from_undo);
645 if (!pending_region_extensions.empty ()) {
646 RegionsExtended (pending_region_extensions);
649 if (!regions_to_relayer.empty ()) {
650 relayer (regions_to_relayer);
659 Playlist::clear_pending ()
661 pending_adds.clear ();
662 pending_removes.clear ();
663 pending_bounds.clear ();
664 pending_range_moves.clear ();
665 pending_region_extensions.clear ();
666 pending_contents_change = false;
669 /*************************************************************
671 *************************************************************/
674 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
676 RegionLock rlock (this);
677 times = fabs (times);
679 int itimes = (int) floor (times);
681 framepos_t pos = position;
683 if (times == 1 && auto_partition){
684 partition(pos - 1, (pos + region->length()), true);
688 add_region_internal (region, pos);
689 pos += region->length();
694 /* note that itimes can be zero if we being asked to just
695 insert a single fraction of the region.
698 for (int i = 0; i < itimes; ++i) {
699 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
700 add_region_internal (copy, pos);
701 pos += region->length();
704 framecnt_t length = 0;
706 if (floor (times) != times) {
707 length = (framecnt_t) floor (region->length() * (times - floor (times)));
709 RegionFactory::region_name (name, region->name(), false);
714 plist.add (Properties::start, region->start());
715 plist.add (Properties::length, length);
716 plist.add (Properties::name, name);
717 plist.add (Properties::layer, region->layer());
719 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
720 add_region_internal (sub, pos);
724 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
728 Playlist::set_region_ownership ()
730 RegionLock rl (this);
731 RegionList::iterator i;
732 boost::weak_ptr<Playlist> pl (shared_from_this());
734 for (i = regions.begin(); i != regions.end(); ++i) {
735 (*i)->set_playlist (pl);
740 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
742 if (region->data_type() != _type) {
746 RegionSortByPosition cmp;
748 if (!first_set_state) {
749 boost::shared_ptr<Playlist> foo (shared_from_this());
750 region->set_playlist (boost::weak_ptr<Playlist>(foo));
753 region->set_position (position);
755 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
756 all_regions.insert (region);
758 possibly_splice_unlocked (position, region->length(), region);
760 /* we need to notify the existence of new region before checking dependents. Ick. */
762 notify_region_added (region);
764 if (!holding_state ()) {
765 check_dependents (region, false);
768 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
774 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
776 RegionLock rlock (this);
778 bool old_sp = _splicing;
781 remove_region_internal (old);
782 add_region_internal (newr, pos);
786 possibly_splice_unlocked (pos, old->length() - newr->length());
790 Playlist::remove_region (boost::shared_ptr<Region> region)
792 RegionLock rlock (this);
793 remove_region_internal (region);
797 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
799 RegionList::iterator i;
803 region->set_playlist (boost::weak_ptr<Playlist>());
806 /* XXX should probably freeze here .... */
808 for (i = regions.begin(); i != regions.end(); ++i) {
811 framepos_t pos = (*i)->position();
812 framecnt_t distance = (*i)->length();
816 possibly_splice_unlocked (pos, -distance);
818 if (!holding_state ()) {
819 remove_dependents (region);
822 notify_region_removed (region);
831 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
833 if (Config->get_use_overlap_equivalency()) {
834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
835 if ((*i)->overlap_equivalent (other)) {
836 results.push_back ((*i));
840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
841 if ((*i)->equivalent (other)) {
842 results.push_back ((*i));
849 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
853 if ((*i) && (*i)->region_list_equivalent (other)) {
854 results.push_back (*i);
860 Playlist::partition (framepos_t start, framepos_t end, bool cut)
864 partition_internal (start, end, cut, thawlist);
866 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
867 (*i)->resume_property_changes ();
871 /** Go through each region on the playlist and cut them at start and end, removing the section between
872 * start and end if cutting == true. Regions that lie entirely within start and end are always
876 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
878 RegionList new_regions;
880 /* Don't relayer regions that are created during this operation; leave them
881 on the same region as the original.
886 RegionLock rlock (this);
888 boost::shared_ptr<Region> region;
889 boost::shared_ptr<Region> current;
891 RegionList::iterator tmp;
893 framepos_t pos1, pos2, pos3, pos4;
897 /* need to work from a copy, because otherwise the regions we add during the process
898 get operated on as well.
901 RegionList copy = regions.rlist();
903 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
910 if (current->first_frame() >= start && current->last_frame() < end) {
913 remove_region_internal (current);
919 /* coverage will return OverlapStart if the start coincides
920 with the end point. we do not partition such a region,
921 so catch this special case.
924 if (current->first_frame() >= end) {
928 if ((overlap = current->coverage (start, end)) == OverlapNone) {
932 pos1 = current->position();
935 pos4 = current->last_frame();
937 if (overlap == OverlapInternal) {
938 /* split: we need 3 new regions, the front, middle and end.
939 cut: we need 2 regions, the front and end.
944 ---------------*************************------------
947 ---------------*****++++++++++++++++====------------
949 ---------------*****----------------====------------
954 /* "middle" ++++++ */
956 RegionFactory::region_name (new_name, current->name(), false);
960 plist.add (Properties::start, current->start() + (pos2 - pos1));
961 plist.add (Properties::length, pos3 - pos2);
962 plist.add (Properties::name, new_name);
963 plist.add (Properties::layer, current->layer());
964 plist.add (Properties::automatic, true);
965 plist.add (Properties::left_of_split, true);
966 plist.add (Properties::right_of_split, true);
968 region = RegionFactory::create (current, plist);
969 add_region_internal (region, start);
970 new_regions.push_back (region);
975 RegionFactory::region_name (new_name, current->name(), false);
979 plist.add (Properties::start, current->start() + (pos3 - pos1));
980 plist.add (Properties::length, pos4 - pos3);
981 plist.add (Properties::name, new_name);
982 plist.add (Properties::layer, current->layer());
983 plist.add (Properties::automatic, true);
984 plist.add (Properties::right_of_split, true);
986 region = RegionFactory::create (current, plist);
988 add_region_internal (region, end);
989 new_regions.push_back (region);
993 current->suspend_property_changes ();
994 thawlist.push_back (current);
995 current->cut_end (pos2 - 1);
997 } else if (overlap == OverlapEnd) {
1001 ---------------*************************------------
1004 ---------------**************+++++++++++------------
1006 ---------------**************-----------------------
1013 RegionFactory::region_name (new_name, current->name(), false);
1017 plist.add (Properties::start, current->start() + (pos2 - pos1));
1018 plist.add (Properties::length, pos4 - pos2);
1019 plist.add (Properties::name, new_name);
1020 plist.add (Properties::layer, current->layer());
1021 plist.add (Properties::automatic, true);
1022 plist.add (Properties::left_of_split, true);
1024 region = RegionFactory::create (current, plist);
1026 add_region_internal (region, start);
1027 new_regions.push_back (region);
1032 current->suspend_property_changes ();
1033 thawlist.push_back (current);
1034 current->cut_end (pos2 - 1);
1036 } else if (overlap == OverlapStart) {
1038 /* split: we need 2 regions: the front and the end.
1039 cut: just trim current to skip the cut area
1044 ---------------*************************------------
1048 ---------------****+++++++++++++++++++++------------
1050 -------------------*********************------------
1056 RegionFactory::region_name (new_name, current->name(), false);
1060 plist.add (Properties::start, current->start());
1061 plist.add (Properties::length, pos3 - pos1);
1062 plist.add (Properties::name, new_name);
1063 plist.add (Properties::layer, current->layer());
1064 plist.add (Properties::automatic, true);
1065 plist.add (Properties::right_of_split, true);
1067 region = RegionFactory::create (current, plist);
1069 add_region_internal (region, pos1);
1070 new_regions.push_back (region);
1075 current->suspend_property_changes ();
1076 thawlist.push_back (current);
1077 current->trim_front (pos3);
1078 } else if (overlap == OverlapExternal) {
1080 /* split: no split required.
1081 cut: remove the region.
1086 ---------------*************************------------
1090 ---------------*************************------------
1092 ----------------------------------------------------
1097 remove_region_internal (current);
1100 new_regions.push_back (current);
1104 in_partition = false;
1107 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1108 check_dependents (*i, false);
1114 boost::shared_ptr<Playlist>
1115 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1117 boost::shared_ptr<Playlist> ret;
1118 boost::shared_ptr<Playlist> pl;
1121 if (ranges.empty()) {
1122 return boost::shared_ptr<Playlist>();
1125 start = ranges.front().start;
1127 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1129 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1131 if (i == ranges.begin()) {
1135 /* paste the next section into the nascent playlist,
1136 offset to reflect the start of the first range we
1140 ret->paste (pl, (*i).start - start, 1.0f);
1147 boost::shared_ptr<Playlist>
1148 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1150 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1151 return cut_copy (pmf, ranges, result_is_hidden);
1154 boost::shared_ptr<Playlist>
1155 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1157 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1158 return cut_copy (pmf, ranges, result_is_hidden);
1161 boost::shared_ptr<Playlist>
1162 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1164 boost::shared_ptr<Playlist> the_copy;
1165 RegionList thawlist;
1168 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1169 string new_name = _name;
1173 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1174 return boost::shared_ptr<Playlist>();
1177 partition_internal (start, start+cnt-1, true, thawlist);
1179 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1180 (*i)->resume_property_changes();
1186 boost::shared_ptr<Playlist>
1187 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1191 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1192 string new_name = _name;
1196 cnt = min (_get_extent().second - start, cnt);
1197 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1201 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1203 times = fabs (times);
1206 RegionLock rl1 (this);
1207 RegionLock rl2 (other.get());
1209 int itimes = (int) floor (times);
1210 framepos_t pos = position;
1211 framecnt_t const shift = other->_get_extent().second;
1212 layer_t top_layer = regions.size();
1215 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1216 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1218 /* put these new regions on top of all existing ones, but preserve
1219 the ordering they had in the original playlist.
1222 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1223 add_region_internal (copy_of_region, (*i)->position() + pos);
1234 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1236 times = fabs (times);
1238 RegionLock rl (this);
1239 int itimes = (int) floor (times);
1240 framepos_t pos = position + 1;
1243 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1244 add_region_internal (copy, pos);
1245 pos += region->length();
1248 if (floor (times) != times) {
1249 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1251 RegionFactory::region_name (name, region->name(), false);
1256 plist.add (Properties::start, region->start());
1257 plist.add (Properties::length, length);
1258 plist.add (Properties::name, name);
1260 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1261 add_region_internal (sub, pos);
1267 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1269 RegionLock rlock (this);
1270 RegionList copy (regions.rlist());
1273 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1275 if ((*r)->last_frame() < at) {
1280 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1281 /* intersected region */
1282 if (!move_intersected) {
1287 /* do not move regions glued to music time - that
1288 has to be done separately.
1291 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1292 fixup.push_back (*r);
1296 (*r)->set_position ((*r)->position() + distance);
1299 /* XXX: may not be necessary; Region::post_set should do this, I think */
1300 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1301 (*r)->recompute_position_from_lock_style ();
1306 Playlist::split (framepos_t at)
1308 RegionLock rlock (this);
1309 RegionList copy (regions.rlist());
1311 /* use a copy since this operation can modify the region list
1314 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1315 _split_region (*r, at);
1320 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1322 RegionLock rl (this);
1323 _split_region (region, playlist_position);
1327 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1329 if (!region->covers (playlist_position)) {
1333 if (region->position() == playlist_position ||
1334 region->last_frame() == playlist_position) {
1338 boost::shared_ptr<Region> left;
1339 boost::shared_ptr<Region> right;
1340 frameoffset_t before;
1341 frameoffset_t after;
1345 /* split doesn't change anything about length, so don't try to splice */
1347 bool old_sp = _splicing;
1350 before = playlist_position - region->position();
1351 after = region->length() - before;
1353 RegionFactory::region_name (before_name, region->name(), false);
1358 plist.add (Properties::position, region->position ());
1359 plist.add (Properties::length, before);
1360 plist.add (Properties::name, before_name);
1361 plist.add (Properties::left_of_split, true);
1363 /* note: we must use the version of ::create with an offset here,
1364 since it supplies that offset to the Region constructor, which
1365 is necessary to get audio region gain envelopes right.
1367 left = RegionFactory::create (region, 0, plist);
1370 RegionFactory::region_name (after_name, region->name(), false);
1375 plist.add (Properties::position, region->position() + before);
1376 plist.add (Properties::length, after);
1377 plist.add (Properties::name, after_name);
1378 plist.add (Properties::right_of_split, true);
1380 /* same note as above */
1381 right = RegionFactory::create (region, before, plist);
1384 add_region_internal (left, region->position());
1385 add_region_internal (right, region->position() + before);
1387 finalize_split_region (region, left, right);
1389 remove_region_internal (region);
1395 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1397 if (_splicing || in_set_state) {
1398 /* don't respond to splicing moves or state setting */
1402 if (_edit_mode == Splice) {
1403 splice_locked (at, distance, exclude);
1408 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1410 if (_splicing || in_set_state) {
1411 /* don't respond to splicing moves or state setting */
1415 if (_edit_mode == Splice) {
1416 splice_unlocked (at, distance, exclude);
1421 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1424 RegionLock rl (this);
1425 core_splice (at, distance, exclude);
1430 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1432 core_splice (at, distance, exclude);
1436 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1440 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1442 if (exclude && (*i) == exclude) {
1446 if ((*i)->position() >= at) {
1447 framepos_t new_pos = (*i)->position() + distance;
1450 } else if (new_pos >= max_framepos - (*i)->length()) {
1451 new_pos = max_framepos - (*i)->length();
1454 (*i)->set_position (new_pos);
1460 notify_contents_changed ();
1464 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1466 if (in_set_state || _splicing || _nudging || _shuffling) {
1470 if (what_changed.contains (Properties::position)) {
1472 timestamp_layer_op (LayerOpBoundsChange, region);
1474 /* remove it from the list then add it back in
1475 the right place again.
1478 RegionSortByPosition cmp;
1480 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1482 if (i == regions.end()) {
1483 /* the region bounds are being modified but its not currently
1484 in the region list. we will use its bounds correctly when/if
1491 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1494 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1496 frameoffset_t delta = 0;
1498 if (what_changed.contains (Properties::position)) {
1499 delta = region->position() - region->last_position();
1502 if (what_changed.contains (Properties::length)) {
1503 delta += region->length() - region->last_length();
1507 possibly_splice (region->last_position() + region->last_length(), delta, region);
1510 if (holding_state ()) {
1511 pending_bounds.push_back (region);
1513 notify_contents_changed ();
1515 check_dependents (region, false);
1521 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1523 boost::shared_ptr<Region> region (weak_region.lock());
1529 /* this makes a virtual call to the right kind of playlist ... */
1531 region_changed (what_changed, region);
1535 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1537 PropertyChange our_interests;
1538 PropertyChange bounds;
1539 PropertyChange pos_and_length;
1542 if (in_set_state || in_flush) {
1546 our_interests.add (Properties::muted);
1547 our_interests.add (Properties::layer);
1548 our_interests.add (Properties::opaque);
1550 bounds.add (Properties::start);
1551 bounds.add (Properties::position);
1552 bounds.add (Properties::length);
1554 pos_and_length.add (Properties::position);
1555 pos_and_length.add (Properties::length);
1557 if (what_changed.contains (bounds)) {
1558 region_bounds_changed (what_changed, region);
1559 save = !(_splicing || _nudging);
1562 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1563 check_dependents (region, false);
1566 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1567 notify_region_moved (region);
1568 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1569 notify_region_end_trimmed (region);
1570 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1571 notify_region_start_trimmed (region);
1574 if (what_changed.contains (our_interests)) {
1582 Playlist::drop_regions ()
1584 RegionLock rl (this);
1586 all_regions.clear ();
1590 Playlist::sync_all_regions_with_regions ()
1592 RegionLock rl (this);
1594 all_regions.clear ();
1596 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1597 all_regions.insert (*i);
1602 Playlist::clear (bool with_signals)
1605 RegionLock rl (this);
1607 region_state_changed_connections.drop_connections ();
1609 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1610 pending_removes.insert (*i);
1615 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1616 remove_dependents (*s);
1622 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1623 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1626 pending_removes.clear ();
1627 pending_contents_change = false;
1633 /***********************************************************************
1635 **********************************************************************/
1637 Playlist::RegionList *
1638 Playlist::regions_at (framepos_t frame)
1641 RegionLock rlock (this);
1642 return find_regions_at (frame);
1646 Playlist::count_regions_at (framepos_t frame) const
1648 RegionLock rlock (const_cast<Playlist*>(this));
1651 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1652 if ((*i)->covers (frame)) {
1660 boost::shared_ptr<Region>
1661 Playlist::top_region_at (framepos_t frame)
1664 RegionLock rlock (this);
1665 RegionList *rlist = find_regions_at (frame);
1666 boost::shared_ptr<Region> region;
1668 if (rlist->size()) {
1669 RegionSortByLayer cmp;
1671 region = rlist->back();
1678 boost::shared_ptr<Region>
1679 Playlist::top_unmuted_region_at (framepos_t frame)
1682 RegionLock rlock (this);
1683 RegionList *rlist = find_regions_at (frame);
1685 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1687 RegionList::iterator tmp = i;
1690 if ((*i)->muted()) {
1697 boost::shared_ptr<Region> region;
1699 if (rlist->size()) {
1700 RegionSortByLayer cmp;
1702 region = rlist->back();
1709 Playlist::RegionList*
1710 Playlist::regions_to_read (framepos_t start, framepos_t end)
1712 /* Caller must hold lock */
1714 RegionList covering;
1715 set<framepos_t> to_check;
1716 set<boost::shared_ptr<Region> > unique;
1718 to_check.insert (start);
1719 to_check.insert (end);
1721 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1723 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1725 /* find all/any regions that span start+end */
1727 switch ((*i)->coverage (start, end)) {
1731 case OverlapInternal:
1732 covering.push_back (*i);
1733 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1737 to_check.insert ((*i)->position());
1738 if ((*i)->position() != 0) {
1739 to_check.insert ((*i)->position()-1);
1741 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1742 covering.push_back (*i);
1746 to_check.insert ((*i)->last_frame());
1747 to_check.insert ((*i)->last_frame()+1);
1748 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1749 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1750 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1751 covering.push_back (*i);
1754 case OverlapExternal:
1755 covering.push_back (*i);
1756 to_check.insert ((*i)->position());
1757 if ((*i)->position() != 0) {
1758 to_check.insert ((*i)->position()-1);
1760 to_check.insert ((*i)->last_frame());
1761 to_check.insert ((*i)->last_frame()+1);
1762 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1763 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1764 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1768 /* don't go too far */
1770 if ((*i)->position() > end) {
1775 RegionList* rlist = new RegionList;
1777 /* find all the regions that cover each position .... */
1779 if (covering.size() == 1) {
1781 rlist->push_back (covering.front());
1782 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1787 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1791 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1793 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1795 if ((*x)->covers (*t)) {
1796 here.push_back (*x);
1797 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1801 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1808 RegionSortByLayer cmp;
1811 /* ... and get the top/transparent regions at "here" */
1813 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1817 if ((*c)->opaque()) {
1819 /* the other regions at this position are hidden by this one */
1820 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1827 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1828 rlist->push_back (*s);
1831 if (rlist->size() > 1) {
1832 /* now sort by time order */
1834 RegionSortByPosition cmp;
1839 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1844 Playlist::RegionList *
1845 Playlist::find_regions_at (framepos_t frame)
1847 /* Caller must hold lock */
1849 RegionList *rlist = new RegionList;
1851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1852 if ((*i)->covers (frame)) {
1853 rlist->push_back (*i);
1860 Playlist::RegionList *
1861 Playlist::regions_touched (framepos_t start, framepos_t end)
1863 RegionLock rlock (this);
1864 RegionList *rlist = new RegionList;
1866 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1867 if ((*i)->coverage (start, end) != OverlapNone) {
1868 rlist->push_back (*i);
1876 Playlist::find_next_transient (framepos_t from, int dir)
1878 RegionLock rlock (this);
1879 AnalysisFeatureList points;
1880 AnalysisFeatureList these_points;
1882 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1884 if ((*i)->last_frame() < from) {
1888 if ((*i)->first_frame() > from) {
1893 (*i)->get_transients (these_points);
1895 /* add first frame, just, err, because */
1897 these_points.push_back ((*i)->first_frame());
1899 points.insert (points.end(), these_points.begin(), these_points.end());
1900 these_points.clear ();
1903 if (points.empty()) {
1907 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1908 bool reached = false;
1911 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1916 if (reached && (*x) > from) {
1921 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1926 if (reached && (*x) < from) {
1935 boost::shared_ptr<Region>
1936 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1938 RegionLock rlock (this);
1939 boost::shared_ptr<Region> ret;
1940 framepos_t closest = max_framepos;
1942 bool end_iter = false;
1944 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1948 frameoffset_t distance;
1949 boost::shared_ptr<Region> r = (*i);
1954 pos = r->first_frame ();
1957 pos = r->last_frame ();
1960 pos = r->sync_position ();
1965 case 1: /* forwards */
1968 if ((distance = pos - frame) < closest) {
1977 default: /* backwards */
1980 if ((distance = frame - pos) < closest) {
1997 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1999 RegionLock rlock (this);
2001 framepos_t closest = max_framepos;
2002 framepos_t ret = -1;
2006 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2008 boost::shared_ptr<Region> r = (*i);
2009 frameoffset_t distance;
2011 if (r->first_frame() > frame) {
2013 distance = r->first_frame() - frame;
2015 if (distance < closest) {
2016 ret = r->first_frame();
2021 if (r->last_frame () > frame) {
2023 distance = r->last_frame () - frame;
2025 if (distance < closest) {
2026 ret = r->last_frame ();
2034 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2036 boost::shared_ptr<Region> r = (*i);
2037 frameoffset_t distance;
2039 if (r->last_frame() < frame) {
2041 distance = frame - r->last_frame();
2043 if (distance < closest) {
2044 ret = r->last_frame();
2049 if (r->first_frame() < frame) {
2051 distance = frame - r->first_frame();
2053 if (distance < closest) {
2054 ret = r->first_frame();
2065 /***********************************************************************/
2071 Playlist::mark_session_dirty ()
2073 if (!in_set_state && !holding_state ()) {
2074 _session.set_dirty();
2079 Playlist::rdiff (vector<Command*>& cmds) const
2081 RegionLock rlock (const_cast<Playlist *> (this));
2082 Stateful::rdiff (cmds);
2086 Playlist::clear_owned_changes ()
2088 RegionLock rlock (this);
2089 Stateful::clear_owned_changes ();
2093 Playlist::update (const RegionListProperty::ChangeRecord& change)
2095 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2096 name(), change.added.size(), change.removed.size()));
2099 /* add the added regions */
2100 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2101 add_region ((*i), (*i)->position());
2103 /* remove the removed regions */
2104 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2112 Playlist::set_state (const XMLNode& node, int version)
2116 XMLNodeConstIterator niter;
2117 XMLPropertyList plist;
2118 XMLPropertyConstIterator piter;
2120 boost::shared_ptr<Region> region;
2122 bool seen_region_nodes = false;
2127 if (node.name() != "Playlist") {
2135 plist = node.properties();
2139 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2143 if (prop->name() == X_("name")) {
2144 _name = prop->value();
2146 } else if (prop->name() == X_("orig-diskstream-id")) {
2147 /* XXX legacy session: fix up later */
2148 _orig_track_id = prop->value ();
2149 } else if (prop->name() == X_("orig-track-id")) {
2150 _orig_track_id = prop->value ();
2151 } else if (prop->name() == X_("frozen")) {
2152 _frozen = string_is_affirmative (prop->value());
2153 } else if (prop->name() == X_("combine-ops")) {
2154 _combine_ops = atoi (prop->value());
2160 nlist = node.children();
2162 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2166 if (child->name() == "Region") {
2168 seen_region_nodes = true;
2170 if ((prop = child->property ("id")) == 0) {
2171 error << _("region state node has no ID, ignored") << endmsg;
2175 ID id = prop->value ();
2177 if ((region = region_by_id (id))) {
2179 region->suspend_property_changes ();
2181 if (region->set_state (*child, version)) {
2182 region->resume_property_changes ();
2186 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2187 region->suspend_property_changes ();
2189 error << _("Playlist: cannot create region from XML") << endmsg;
2193 add_region (region, region->position(), 1.0);
2195 region->resume_property_changes ();
2200 if (seen_region_nodes && regions.empty()) {
2204 /* update dependents, which was not done during add_region_internal
2205 due to in_set_state being true
2208 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2209 check_dependents (*r, false);
2214 notify_contents_changed ();
2218 first_set_state = false;
2223 Playlist::get_state()
2225 return state (true);
2229 Playlist::get_template()
2231 return state (false);
2234 /** @param full_state true to include regions in the returned state, otherwise false.
2237 Playlist::state (bool full_state)
2239 XMLNode *node = new XMLNode (X_("Playlist"));
2242 node->add_property (X_("id"), id().to_s());
2243 node->add_property (X_("name"), _name);
2244 node->add_property (X_("type"), _type.to_string());
2246 _orig_track_id.print (buf, sizeof (buf));
2247 node->add_property (X_("orig-track-id"), buf);
2248 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2251 RegionLock rlock (this, false);
2253 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2254 node->add_property ("combine-ops", buf);
2256 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2257 node->add_child_nocopy ((*i)->get_state());
2262 node->add_child_copy (*_extra_xml);
2269 Playlist::empty() const
2271 RegionLock rlock (const_cast<Playlist *>(this), false);
2272 return regions.empty();
2276 Playlist::n_regions() const
2278 RegionLock rlock (const_cast<Playlist *>(this), false);
2279 return regions.size();
2282 pair<framepos_t, framepos_t>
2283 Playlist::get_extent () const
2285 RegionLock rlock (const_cast<Playlist *>(this), false);
2286 return _get_extent ();
2289 pair<framepos_t, framepos_t>
2290 Playlist::_get_extent () const
2292 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2294 if (regions.empty()) {
2299 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2300 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2301 if (e.first < ext.first) {
2302 ext.first = e.first;
2304 if (e.second > ext.second) {
2305 ext.second = e.second;
2313 Playlist::bump_name (string name, Session &session)
2315 string newname = name;
2318 newname = bump_name_once (newname, '.');
2319 } while (session.playlists->by_name (newname)!=NULL);
2326 Playlist::top_layer() const
2328 RegionLock rlock (const_cast<Playlist *> (this));
2331 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2332 top = max (top, (*i)->layer());
2338 Playlist::set_edit_mode (EditMode mode)
2343 /** Relayer a region. See the other relayer() methods for commentary. */
2345 Playlist::relayer (boost::shared_ptr<Region> region)
2347 if (_relayer_suspended) {
2352 r.push_back (region);
2356 Playlist::TemporaryLayers
2357 Playlist::compute_temporary_layers (RegionList const & relayer_regions)
2359 TemporaryLayers temporary_layers;
2360 OverlapCache cache (this);
2362 for (RegionList::const_iterator i = relayer_regions.begin(); i != relayer_regions.end(); ++i) {
2364 /* current_overlaps: regions that overlap *i now */
2365 RegionList current_overlaps = cache.get ((*i)->bounds ());
2366 current_overlaps.remove (*i);
2368 /* overlaps_to_preserve: regions that overlap *i now, but which aren't being
2369 worked on during this relayer: these will have their relationship with
2372 RegionList overlaps_to_preserve;
2374 /* overlaps_to_check: regions that overlap *i now, and must be checked to
2375 see if *i is still on the correct layer with respect to them (according
2376 to current layering rules).
2378 RegionList overlaps_to_check;
2380 if (_session.config.get_relayer_on_all_edits () || (*i)->last_layer_op (LayerOpAdd) > (*i)->last_layer_op (LayerOpBoundsChange)) {
2381 /* We're configured to relayer on all edits, or this region has had
2382 no edit since it was added to the playlist, so we're relayering
2383 the whole lot; in this case there are no `overlaps_to_preserve'.
2385 overlaps_to_check = current_overlaps;
2387 /* We're only relayering new overlaps; find them */
2388 RegionList last_overlaps = cache.get ((*i)->last_relayer_bounds ());
2389 last_overlaps.remove (*i);
2390 for (RegionList::const_iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) {
2391 if (find (last_overlaps.begin(), last_overlaps.end(), *j) == last_overlaps.end()) {
2392 /* This is a new overlap, which must be checked */
2393 overlaps_to_check.push_back (*j);
2395 /* This is an existing overlap, which must be preserved */
2396 overlaps_to_preserve.push_back (*j);
2401 if (overlaps_to_check.empty ()) {
2402 /* There are no overlaps to check, so just leave everything as it is */
2406 /* Put *i on our overlaps_to_check_list */
2407 overlaps_to_check.push_back (*i);
2409 /* And sort it according to the current layer model */
2410 switch (_session.config.get_layer_model()) {
2412 overlaps_to_check.sort (RegionSortByPosition ());
2414 case AddOrBoundsChangeHigher:
2415 overlaps_to_check.sort (RegionSortByAddOrBounds ());
2418 overlaps_to_check.sort (RegionSortByAdd ());
2422 /* Now find *i in our overlaps_to_check list; within this list it will be in the
2423 right place wrt the current layering rules, so we can work out the layers of the
2424 nearest regions below and above.
2426 double previous_layer = -DBL_MAX;
2427 double next_layer = DBL_MAX;
2428 RegionList::const_iterator j = overlaps_to_check.begin();
2430 previous_layer = temporary_layers.get (*j);
2434 /* we must have found *i */
2435 assert (j != overlaps_to_check.end ());
2438 if (j != overlaps_to_check.end ()) {
2439 next_layer = temporary_layers.get (*j);
2442 /* Now we know where *i and overlaps_to_preserve should go: between previous_layer and
2446 /* Recurse into overlaps_to_preserve to find dependents */
2447 RegionList recursed_overlaps_to_preserve;
2449 for (RegionList::const_iterator k = overlaps_to_preserve.begin(); k != overlaps_to_preserve.end(); ++k) {
2450 recursed_overlaps_to_preserve.push_back (*k);
2451 RegionList touched = recursive_regions_touched (*k, cache, *i);
2452 for (RegionList::iterator m = touched.begin(); m != touched.end(); ++m) {
2453 if (find (recursed_overlaps_to_preserve.begin(), recursed_overlaps_to_preserve.end(), *m) == recursed_overlaps_to_preserve.end()) {
2454 recursed_overlaps_to_preserve.push_back (*m);
2459 /* Put *i into the overlaps_to_preserve list */
2460 recursed_overlaps_to_preserve.push_back (*i);
2462 /* Sort it by layer, so that we preserve layering */
2463 recursed_overlaps_to_preserve.sort (SortByTemporaryLayer (temporary_layers));
2465 /* Divide available space up into chunks so that we can relayer everything in that space */
2466 double const space = (next_layer - previous_layer) / (recursed_overlaps_to_preserve.size() + 1);
2470 for (RegionList::const_iterator k = recursed_overlaps_to_preserve.begin(); k != recursed_overlaps_to_preserve.end(); ++k) {
2471 temporary_layers.set (*k, previous_layer + space * m);
2476 return temporary_layers;
2479 /** Take a list of temporary layer indices and set up the layers of all regions
2483 Playlist::commit_temporary_layers (TemporaryLayers const & temporary_layers)
2485 /* Sort all the playlist's regions by layer, ascending */
2486 RegionList all_regions = regions.rlist ();
2487 all_regions.sort (SortByTemporaryLayer (temporary_layers));
2489 for (RegionList::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2491 /* Go through the regions that we have already layered and hence work
2492 out the maximum layer index that is in used at some point during
2496 layer_t max_layer_here = 0;
2497 bool have_overlap = false;
2498 for (RegionList::iterator j = all_regions.begin(); j != i; ++j) {
2499 if ((*j)->overlap_equivalent (*i)) {
2500 max_layer_here = max ((*j)->layer (), max_layer_here);
2501 have_overlap = true;
2506 /* *i overlaps something, so put it on the next available layer */
2507 (*i)->set_layer (max_layer_here + 1);
2509 /* no overlap, so put on the bottom layer */
2510 (*i)->set_layer (0);
2514 notify_layering_changed ();
2517 /** Relayer a list of regions.
2519 * Taking each region R in turn, this method examines the regions O that overlap R in time.
2520 * If the session configuration option "relayer-on-all-moves" is false, we reduce O so that
2521 * it contains only those regions with which new overlaps have been formed since the last
2524 * We then change the layer of R and its indirect overlaps so that R meets the current
2525 * Session layer model with respect to O. See doc/layering.
2529 Playlist::relayer (RegionList const & relayer_regions)
2531 if (_relayer_suspended) {
2535 /* We do this in two parts: first; compute `temporary layer' indices for
2536 regions on the playlist. These are (possibly) fractional indices, which
2537 are a convenient means of working with things when you want to insert layers
2541 TemporaryLayers temporary_layers = compute_temporary_layers (relayer_regions);
2543 /* Second, we fix up these temporary layers and `commit' them by writing
2544 them to the regions involved.
2547 commit_temporary_layers (temporary_layers);
2550 /** Put a region on some fractional layer and sort everything else out around it.
2551 * This can be used to force a region into some layering; for example, calling
2552 * this method with temporary_layer == -0.5 will put the region at the bottom of
2557 Playlist::relayer (boost::shared_ptr<Region> region, double temporary_layer)
2559 if (_relayer_suspended) {
2564 t.set (region, temporary_layer);
2565 commit_temporary_layers (t);
2569 Playlist::raise_region (boost::shared_ptr<Region> region)
2571 relayer (region, region->layer() + 0.5);
2575 Playlist::lower_region (boost::shared_ptr<Region> region)
2577 relayer (region, region->layer() - 0.5);
2581 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2583 relayer (region, max_layer);
2587 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2589 relayer (region, -0.5);
2593 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2595 RegionList::iterator i;
2601 RegionLock rlock (const_cast<Playlist *> (this));
2603 for (i = regions.begin(); i != regions.end(); ++i) {
2605 if ((*i)->position() >= start) {
2611 if ((*i)->last_frame() > max_framepos - distance) {
2612 new_pos = max_framepos - (*i)->length();
2614 new_pos = (*i)->position() + distance;
2619 if ((*i)->position() > distance) {
2620 new_pos = (*i)->position() - distance;
2626 (*i)->set_position (new_pos);
2634 notify_contents_changed ();
2640 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2642 RegionLock rlock (const_cast<Playlist*> (this));
2644 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2645 if ((*r)->uses_source (src)) {
2653 boost::shared_ptr<Region>
2654 Playlist::find_region (const ID& id) const
2656 RegionLock rlock (const_cast<Playlist*> (this));
2658 /* searches all regions currently in use by the playlist */
2660 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2661 if ((*i)->id() == id) {
2666 return boost::shared_ptr<Region> ();
2670 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2672 RegionLock rlock (const_cast<Playlist*> (this));
2675 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2684 boost::shared_ptr<Region>
2685 Playlist::region_by_id (const ID& id) const
2687 /* searches all regions ever added to this playlist */
2689 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2690 if ((*i)->id() == id) {
2694 return boost::shared_ptr<Region> ();
2698 Playlist::dump () const
2700 boost::shared_ptr<Region> r;
2702 cerr << "Playlist \"" << _name << "\" " << endl
2703 << regions.size() << " regions "
2706 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2708 cerr << " " << r->name() << " ["
2709 << r->start() << "+" << r->length()
2719 Playlist::set_frozen (bool yn)
2725 Playlist::timestamp_layer_op (LayerOp op, boost::shared_ptr<Region> region)
2727 region->set_last_layer_op (op, ++layer_op_counter);
2731 /** Find the next or previous region after `region' (next if dir > 0, previous otherwise)
2732 * and swap its position with `region'.
2735 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2737 /* As regards layering, the calls we make to set_position() will
2738 perform layering as if the regions had been moved, which I think
2744 if (region->locked()) {
2751 RegionLock rlock (const_cast<Playlist*> (this));
2756 RegionList::iterator next;
2758 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2759 if ((*i) == region) {
2763 if (next != regions.end()) {
2765 if ((*next)->locked()) {
2771 if ((*next)->position() != region->last_frame() + 1) {
2772 /* they didn't used to touch, so after shuffle,
2773 just have them swap positions.
2775 new_pos = (*next)->position();
2777 /* they used to touch, so after shuffle,
2778 make sure they still do. put the earlier
2779 region where the later one will end after
2782 new_pos = region->position() + (*next)->length();
2785 (*next)->set_position (region->position());
2786 region->set_position (new_pos);
2788 /* avoid a full sort */
2790 regions.erase (i); // removes the region from the list */
2792 regions.insert (next, region); // adds it back after next
2801 RegionList::iterator prev = regions.end();
2803 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2804 if ((*i) == region) {
2806 if (prev != regions.end()) {
2808 if ((*prev)->locked()) {
2813 if (region->position() != (*prev)->last_frame() + 1) {
2814 /* they didn't used to touch, so after shuffle,
2815 just have them swap positions.
2817 new_pos = region->position();
2819 /* they used to touch, so after shuffle,
2820 make sure they still do. put the earlier
2821 one where the later one will end after
2823 new_pos = (*prev)->position() + region->length();
2826 region->set_position ((*prev)->position());
2827 (*prev)->set_position (new_pos);
2829 /* avoid a full sort */
2831 regions.erase (i); // remove region
2832 regions.insert (prev, region); // insert region before prev
2846 check_dependents (region, false);
2847 notify_contents_changed();
2852 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2854 RegionLock rlock (const_cast<Playlist*> (this));
2856 if (regions.size() > 1) {
2864 Playlist::update_after_tempo_map_change ()
2866 RegionLock rlock (const_cast<Playlist*> (this));
2867 RegionList copy (regions.rlist());
2871 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2872 (*i)->update_after_tempo_map_change ();
2879 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2881 RegionLock rl (this, false);
2882 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2888 Playlist::has_region_at (framepos_t const p) const
2890 RegionLock (const_cast<Playlist *> (this));
2892 RegionList::const_iterator i = regions.begin ();
2893 while (i != regions.end() && !(*i)->covers (p)) {
2897 return (i != regions.end());
2900 /** Remove any region that uses a given source */
2902 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2904 RegionLock rl (this);
2906 RegionList::iterator i = regions.begin();
2907 while (i != regions.end()) {
2908 RegionList::iterator j = i;
2911 if ((*i)->uses_source (s)) {
2912 remove_region_internal (*i);
2919 /** Look from a session frame time and find the start time of the next region
2920 * which is on the top layer of this playlist.
2921 * @param t Time to look from.
2922 * @return Position of next top-layered region, or max_framepos if there isn't one.
2925 Playlist::find_next_top_layer_position (framepos_t t) const
2927 RegionLock rlock (const_cast<Playlist *> (this));
2929 layer_t const top = top_layer ();
2931 RegionList copy = regions.rlist ();
2932 copy.sort (RegionSortByPosition ());
2934 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2935 if ((*i)->position() >= t && (*i)->layer() == top) {
2936 return (*i)->position();
2940 return max_framepos;
2943 boost::shared_ptr<Region>
2944 Playlist::combine (const RegionList& r)
2947 uint32_t channels = 0;
2949 framepos_t earliest_position = max_framepos;
2950 vector<TwoRegions> old_and_new_regions;
2951 vector<boost::shared_ptr<Region> > originals;
2952 vector<boost::shared_ptr<Region> > copies;
2955 uint32_t max_level = 0;
2957 /* find the maximum depth of all the regions we're combining */
2959 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2960 max_level = max (max_level, (*i)->max_source_level());
2963 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2964 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2966 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2968 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2969 earliest_position = min (earliest_position, (*i)->position());
2972 /* enable this so that we do not try to create xfades etc. as we add
2976 pl->in_partition = true;
2978 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2980 /* copy the region */
2982 boost::shared_ptr<Region> original_region = (*i);
2983 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2985 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2986 originals.push_back (original_region);
2987 copies.push_back (copied_region);
2989 RegionFactory::add_compound_association (original_region, copied_region);
2991 /* make position relative to zero */
2993 pl->add_region (copied_region, original_region->position() - earliest_position);
2995 /* use the maximum number of channels for any region */
2997 channels = max (channels, original_region->n_channels());
2999 /* it will go above the layer of the highest existing region */
3001 layer = max (layer, original_region->layer());
3004 pl->in_partition = false;
3006 pre_combine (copies);
3008 /* add any dependent regions to the new playlist */
3010 copy_dependents (old_and_new_regions, pl.get());
3012 /* now create a new PlaylistSource for each channel in the new playlist */
3015 pair<framepos_t,framepos_t> extent = pl->get_extent();
3017 for (uint32_t chn = 0; chn < channels; ++chn) {
3018 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3022 /* now a new whole-file region using the list of sources */
3024 plist.add (Properties::start, 0);
3025 plist.add (Properties::length, extent.second);
3026 plist.add (Properties::name, parent_name);
3027 plist.add (Properties::whole_file, true);
3029 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3031 /* now the non-whole-file region that we will actually use in the
3036 plist.add (Properties::start, 0);
3037 plist.add (Properties::length, extent.second);
3038 plist.add (Properties::name, child_name);
3039 plist.add (Properties::layer, layer+1);
3041 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3043 /* remove all the selected regions from the current playlist
3048 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3052 /* do type-specific stuff with the originals and the new compound
3056 post_combine (originals, compound_region);
3058 /* add the new region at the right location */
3060 add_region (compound_region, earliest_position);
3066 return compound_region;
3070 Playlist::uncombine (boost::shared_ptr<Region> target)
3072 boost::shared_ptr<PlaylistSource> pls;
3073 boost::shared_ptr<const Playlist> pl;
3074 vector<boost::shared_ptr<Region> > originals;
3075 vector<TwoRegions> old_and_new_regions;
3077 // (1) check that its really a compound region
3079 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3083 pl = pls->playlist();
3085 framepos_t adjusted_start = 0; // gcc isn't smart enough
3086 framepos_t adjusted_end = 0; // gcc isn't smart enough
3088 /* the leftmost (earliest) edge of the compound region
3089 starts at zero in its source, or larger if it
3090 has been trimmed or content-scrolled.
3092 the rightmost (latest) edge of the compound region
3093 relative to its source is the starting point plus
3094 the length of the region.
3097 // (2) get all the original regions
3099 const RegionList& rl (pl->region_list().rlist());
3100 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3101 frameoffset_t move_offset = 0;
3103 /* there are two possibilities here:
3104 1) the playlist that the playlist source was based on
3105 is us, so just add the originals (which belonged to
3106 us anyway) back in the right place.
3108 2) the playlist that the playlist source was based on
3109 is NOT us, so we need to make copies of each of
3110 the original regions that we find, and add them
3113 bool same_playlist = (pls->original() == id());
3115 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3117 boost::shared_ptr<Region> current (*i);
3119 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3121 if (ca == cassocs.end()) {
3125 boost::shared_ptr<Region> original (ca->second);
3126 bool modified_region;
3128 if (i == rl.begin()) {
3129 move_offset = (target->position() - original->position()) - target->start();
3130 adjusted_start = original->position() + target->start();
3131 adjusted_end = adjusted_start + target->length();
3134 if (!same_playlist) {
3135 framepos_t pos = original->position();
3136 /* make a copy, but don't announce it */
3137 original = RegionFactory::create (original, false);
3138 /* the pure copy constructor resets position() to zero,
3141 original->set_position (pos);
3144 /* check to see how the original region (in the
3145 * playlist before compounding occured) overlaps
3146 * with the new state of the compound region.
3149 original->clear_changes ();
3150 modified_region = false;
3152 switch (original->coverage (adjusted_start, adjusted_end)) {
3154 /* original region does not cover any part
3155 of the current state of the compound region
3159 case OverlapInternal:
3160 /* overlap is just a small piece inside the
3161 * original so trim both ends
3163 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3164 modified_region = true;
3167 case OverlapExternal:
3168 /* overlap fully covers original, so leave it
3174 /* overlap starts within but covers end,
3175 so trim the front of the region
3177 original->trim_front (adjusted_start);
3178 modified_region = true;
3182 /* overlap covers start but ends within, so
3183 * trim the end of the region.
3185 original->trim_end (adjusted_end);
3186 modified_region = true;
3191 /* fix the position to match any movement of the compound region.
3193 original->set_position (original->position() + move_offset);
3194 modified_region = true;
3197 if (modified_region) {
3198 _session.add_command (new StatefulDiffCommand (original));
3201 /* and add to the list of regions waiting to be
3205 originals.push_back (original);
3206 old_and_new_regions.push_back (TwoRegions (*i, original));
3209 pre_uncombine (originals, target);
3211 in_partition = true;
3214 // (3) remove the compound region
3216 remove_region (target);
3218 // (4) add the constituent regions
3220 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3221 add_region ((*i), (*i)->position());
3224 /* now move dependent regions back from the compound to this playlist */
3226 pl->copy_dependents (old_and_new_regions, this);
3228 in_partition = false;
3233 Playlist::max_source_level () const
3235 RegionLock rlock (const_cast<Playlist *> (this));
3238 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3239 lvl = max (lvl, (*i)->max_source_level());
3247 Playlist::count_joined_regions () const
3249 RegionLock rlock (const_cast<Playlist *> (this));
3252 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3253 if ((*i)->max_source_level() > 0) {
3262 Playlist::set_orig_track_id (const PBD::ID& id)
3264 _orig_track_id = id;
3267 /** Set the temporary layer for a region */
3269 Playlist::TemporaryLayers::set (boost::shared_ptr<Region> r, double l)
3274 /** Return the temporary layer for a region, if one has been specified
3275 * to this TemporaryLayers object; if not return the region's current
3279 Playlist::TemporaryLayers::get (boost::shared_ptr<Region> r) const
3281 Map::const_iterator i = _map.find (r);
3282 if (i != _map.end ()) {
3286 return double (r->layer ());
3289 int const Playlist::OverlapCache::_divisions = 512;
3291 /** Set up an OverlapCache for a playlist; the cache will only be valid until
3292 * the Playlist is changed.
3294 Playlist::OverlapCache::OverlapCache (Playlist* playlist)
3297 /* Find the start and end positions of the regions on this playlist */
3298 _range = Evoral::Range<framepos_t> (max_framepos, 0);
3299 RegionList const & rl = playlist->region_list().rlist ();
3300 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3301 Evoral::Range<framepos_t> const b = (*i)->bounds ();
3302 _range.from = min (_range.from, b.from);
3303 _range.to = max (_range.to, b.to);
3306 /* Hence the size of each time divison */
3307 _division_size = (_range.to - _range.from) / double (_divisions);
3309 _cache.resize (_divisions);
3311 /* Build the cache */
3312 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3313 pair<int, int> ind = cache_indices ((*i)->bounds ());
3314 for (int j = ind.first; j < ind.second; ++j) {
3315 _cache[j].push_back (*i);
3320 /** @param range Range, in frames.
3321 * @return From and to cache indices for (to is exclusive).
3324 Playlist::OverlapCache::cache_indices (Evoral::Range<framepos_t> range) const
3326 range.from = max (range.from, _range.from);
3327 range.to = min (range.to, _range.to);
3329 pair<int, int> const p = make_pair (
3330 floor ((range.from - _range.from) / _division_size),
3331 ceil ((range.to - _range.from) / _division_size)
3334 assert (p.first >= 0);
3335 assert (p.second <= _divisions);
3340 /** Return the regions that overlap a given range. The returned list
3341 * is not guaranteed to be in the same order as the Playlist that it was
3344 Playlist::RegionList
3345 Playlist::OverlapCache::get (Evoral::Range<framepos_t> range) const
3347 if (_range.from == max_framepos) {
3348 return RegionList ();
3353 pair<int, int> ind = cache_indices (range);
3354 for (int i = ind.first; i < ind.second; ++i) {
3355 for (RegionList::const_iterator j = _cache[i].begin(); j != _cache[i].end(); ++j) {
3356 if ((*j)->coverage (range.from, range.to) != OverlapNone) {
3369 Playlist::suspend_relayer ()
3371 _relayer_suspended = true;
3375 Playlist::resume_relayer ()
3377 _relayer_suspended = false;
3380 /** Examine a region and return regions which overlap it, and also those which overlap those which overlap etc.
3381 * @param ignore Optional region which should be treated as if it doesn't exist (ie not returned in the list,
3382 * and not recursed into).
3384 Playlist::RegionList
3385 Playlist::recursive_regions_touched (boost::shared_ptr<Region> region, OverlapCache const & cache, boost::shared_ptr<Region> ignore) const
3388 recursive_regions_touched_sub (region, cache, ignore, touched);
3390 touched.remove (region);
3394 /** Recursive sub-routine of recursive_regions_touched */
3396 Playlist::recursive_regions_touched_sub (
3397 boost::shared_ptr<Region> region, OverlapCache const & cache, boost::shared_ptr<Region> ignore, RegionList & touched
3400 RegionList r = cache.get (region->bounds ());
3401 for (RegionList::iterator i = r.begin(); i != r.end(); ++i) {
3402 if (find (touched.begin(), touched.end(), *i) == touched.end() && *i != ignore) {
3403 touched.push_back (*i);
3404 recursive_regions_touched_sub (*i, cache, ignore, touched);