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 "pbd/failed_constructor.h"
30 #include "pbd/stl_delete.h"
31 #include "pbd/xml++.h"
32 #include "pbd/stacktrace.h"
34 #include "ardour/debug.h"
35 #include "ardour/playlist.h"
36 #include "ardour/session.h"
37 #include "ardour/region.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/playlist_factory.h"
40 #include "ardour/transient_detector.h"
41 #include "ardour/session_playlists.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
64 struct RegionSortByLayer {
65 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
66 return a->layer() < b->layer();
70 struct RegionSortByLayerWithPending {
71 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
73 double p = a->layer ();
74 if (a->pending_explicit_relayer()) {
78 double q = b->layer ();
79 if (b->pending_explicit_relayer()) {
87 struct RegionSortByPosition {
88 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
89 return a->position() < b->position();
93 struct RegionSortByLastLayerOp {
94 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
95 return a->last_layer_op() < b->last_layer_op();
100 Playlist::make_property_quarks ()
102 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
103 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
106 RegionListProperty::RegionListProperty (Playlist& pl)
107 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
112 boost::shared_ptr<Region>
113 RegionListProperty::lookup_id (const ID& id)
115 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
118 ret = _playlist.session().region_by_id (id);
122 ret = RegionFactory::region_by_id (id);
128 RegionListProperty::copy_for_history () const
130 RegionListProperty* copy = new RegionListProperty (_playlist);
131 /* this is all we need */
132 copy->_change = _change;
137 RegionListProperty::diff (PropertyList& before, PropertyList& after) const
140 RegionListProperty* a = copy_for_history ();
141 RegionListProperty* b = copy_for_history ();
143 b->invert_changes ();
148 cerr << "pdiff on " << _playlist.name() << " before contains "
149 << b->change().added.size() << " adds and " << b->change().removed.size() << " removes\n";
150 cerr << "pdiff on " << _playlist.name() << " after contains "
151 << a->change().added.size() << " adds and " << a->change().removed.size() << " removes\n";
156 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
157 : SessionObject(sess, nom)
162 first_set_state = false;
167 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
168 : SessionObject(sess, "unnamed playlist")
173 const XMLProperty* prop = node.property("type");
174 assert(!prop || DataType(prop->value()) == _type);
177 _name = "unnamed"; /* reset by set_state */
179 /* set state called by derived class */
182 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
183 : SessionObject(other->_session, namestr)
185 , _type(other->_type)
186 , _orig_diskstream_id(other->_orig_diskstream_id)
191 other->copy_regions (tmp);
195 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
196 add_region_internal( (*x), (*x)->position());
201 _splicing = other->_splicing;
202 _nudging = other->_nudging;
203 _edit_mode = other->_edit_mode;
206 first_set_state = false;
208 in_partition = false;
210 _read_data_count = 0;
211 _frozen = other->_frozen;
213 layer_op_counter = other->layer_op_counter;
214 freeze_length = other->freeze_length;
217 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
218 : SessionObject(other->_session, str)
220 , _type(other->_type)
221 , _orig_diskstream_id(other->_orig_diskstream_id)
223 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
225 framepos_t end = start + cnt - 1;
231 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
233 boost::shared_ptr<Region> region;
234 boost::shared_ptr<Region> new_region;
235 frameoffset_t offset = 0;
236 framepos_t position = 0;
243 overlap = region->coverage (start, end);
249 case OverlapInternal:
250 offset = start - region->position();
257 position = region->position() - start;
258 len = end - region->position();
262 offset = start - region->position();
264 len = region->length() - offset;
267 case OverlapExternal:
269 position = region->position() - start;
270 len = region->length();
274 _session.region_name (new_name, region->name(), false);
278 plist.add (Properties::start, offset);
279 plist.add (Properties::length, len);
280 plist.add (Properties::name, new_name);
281 plist.add (Properties::layer, region->layer());
283 new_region = RegionFactory::RegionFactory::create (region, plist);
285 add_region_internal (new_region, position);
289 first_set_state = false;
291 /* this constructor does NOT notify others (session) */
298 InUse (true); /* EMIT SIGNAL */
309 InUse (false); /* EMIT SIGNAL */
314 Playlist::copy_regions (RegionList& newlist) const
316 RegionLock rlock (const_cast<Playlist *> (this));
318 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
319 newlist.push_back (RegionFactory::RegionFactory::create (*i));
324 Playlist::init (bool hide)
326 add_property (regions);
327 _xml_node_name = X_("Playlist");
329 g_atomic_int_set (&block_notifications, 0);
330 g_atomic_int_set (&ignore_state_changes, 0);
331 pending_contents_change = false;
332 pending_length = false;
333 pending_layering = false;
334 first_set_state = true;
341 _edit_mode = Config->get_edit_mode();
343 in_partition = false;
345 _read_data_count = 0;
347 layer_op_counter = 0;
349 _explicit_relayering = false;
351 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
352 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
354 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
357 Playlist::~Playlist ()
359 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
362 RegionLock rl (this);
364 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
365 (*i)->set_playlist (boost::shared_ptr<Playlist>());
369 /* GoingAway must be emitted by derived classes */
373 Playlist::set_name (const string& str)
375 /* in a typical situation, a playlist is being used
376 by one diskstream and also is referenced by the
377 Session. if there are more references than that,
378 then don't change the name.
384 return SessionObject::set_name(str);
388 /***********************************************************************
389 CHANGE NOTIFICATION HANDLING
391 Notifications must be delayed till the region_lock is released. This
392 is necessary because handlers for the signals may need to acquire
393 the lock (e.g. to read from the playlist).
394 ***********************************************************************/
397 Playlist::begin_undo ()
403 Playlist::end_undo ()
411 delay_notifications ();
412 g_atomic_int_inc (&ignore_state_changes);
418 g_atomic_int_dec_and_test (&ignore_state_changes);
419 release_notifications ();
424 Playlist::delay_notifications ()
426 g_atomic_int_inc (&block_notifications);
427 freeze_length = _get_maximum_extent();
431 Playlist::release_notifications ()
433 if (g_atomic_int_dec_and_test (&block_notifications)) {
434 flush_notifications ();
439 Playlist::notify_contents_changed ()
441 if (holding_state ()) {
442 pending_contents_change = true;
444 pending_contents_change = false;
445 ContentsChanged(); /* EMIT SIGNAL */
450 Playlist::notify_layering_changed ()
452 if (holding_state ()) {
453 pending_layering = true;
455 pending_layering = false;
456 LayeringChanged(); /* EMIT SIGNAL */
461 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
463 if (holding_state ()) {
464 pending_removes.insert (r);
465 pending_contents_change = true;
466 pending_length = true;
468 /* this might not be true, but we have to act
469 as though it could be.
471 pending_length = false;
472 LengthChanged (); /* EMIT SIGNAL */
473 pending_contents_change = false;
474 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
475 ContentsChanged (); /* EMIT SIGNAL */
480 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
482 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
484 if (holding_state ()) {
486 pending_range_moves.push_back (move);
490 list< Evoral::RangeMove<framepos_t> > m;
498 Playlist::notify_region_added (boost::shared_ptr<Region> r)
500 /* the length change might not be true, but we have to act
501 as though it could be.
504 if (holding_state()) {
505 pending_adds.insert (r);
506 pending_contents_change = true;
507 pending_length = true;
509 pending_length = false;
510 LengthChanged (); /* EMIT SIGNAL */
511 pending_contents_change = false;
512 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
513 ContentsChanged (); /* EMIT SIGNAL */
518 Playlist::notify_length_changed ()
520 if (holding_state ()) {
521 pending_length = true;
523 pending_length = false;
524 LengthChanged(); /* EMIT SIGNAL */
525 pending_contents_change = false;
526 ContentsChanged (); /* EMIT SIGNAL */
531 Playlist::flush_notifications ()
533 set<boost::shared_ptr<Region> > dependent_checks_needed;
534 set<boost::shared_ptr<Region> >::iterator s;
535 uint32_t regions_changed = false;
536 bool check_length = false;
537 framecnt_t old_length = 0;
545 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
546 regions_changed = true;
547 if (!pending_length) {
548 old_length = _get_maximum_extent ();
553 /* we have no idea what order the regions ended up in pending
554 bounds (it could be based on selection order, for example).
555 so, to preserve layering in the "most recently moved is higher"
556 model, sort them by existing layer, then timestamp them.
559 // RegionSortByLayer cmp;
560 // pending_bounds.sort (cmp);
562 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
563 if (_session.config.get_layer_model() == MoveAddHigher) {
564 timestamp_layer_op (*r);
566 dependent_checks_needed.insert (*r);
569 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
570 remove_dependents (*s);
571 // cerr << _name << " sends RegionRemoved\n";
572 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
575 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
576 // cerr << _name << " sends RegionAdded\n";
577 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
578 dependent_checks_needed.insert (*s);
582 if (old_length != _get_maximum_extent()) {
583 pending_length = true;
584 // cerr << _name << " length has changed\n";
588 if (pending_length || (freeze_length != _get_maximum_extent())) {
589 pending_length = false;
590 // cerr << _name << " sends LengthChanged\n";
591 LengthChanged(); /* EMIT SIGNAL */
594 if (regions_changed || pending_contents_change) {
598 pending_contents_change = false;
599 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
600 ContentsChanged (); /* EMIT SIGNAL */
601 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
604 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
605 check_dependents (*s, false);
608 if (!pending_range_moves.empty ()) {
609 // cerr << _name << " sends RangesMoved\n";
610 RangesMoved (pending_range_moves);
619 Playlist::clear_pending ()
621 pending_adds.clear ();
622 pending_removes.clear ();
623 pending_bounds.clear ();
624 pending_range_moves.clear ();
625 pending_contents_change = false;
626 pending_length = false;
629 /*************************************************************
631 *************************************************************/
634 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
636 RegionLock rlock (this);
637 times = fabs (times);
639 int itimes = (int) floor (times);
641 framepos_t pos = position;
643 if (times == 1 && auto_partition){
644 partition(pos, (pos + region->length()), true);
648 add_region_internal (region, pos);
649 pos += region->length();
654 /* note that itimes can be zero if we being asked to just
655 insert a single fraction of the region.
658 for (int i = 0; i < itimes; ++i) {
659 boost::shared_ptr<Region> copy = RegionFactory::create (region);
660 add_region_internal (copy, pos);
661 pos += region->length();
664 framecnt_t length = 0;
666 if (floor (times) != times) {
667 length = (framecnt_t) floor (region->length() * (times - floor (times)));
669 _session.region_name (name, region->name(), false);
674 plist.add (Properties::start, 0);
675 plist.add (Properties::length, length);
676 plist.add (Properties::name, name);
677 plist.add (Properties::layer, region->layer());
679 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
680 add_region_internal (sub, pos);
684 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
688 Playlist::set_region_ownership ()
690 RegionLock rl (this);
691 RegionList::iterator i;
692 boost::weak_ptr<Playlist> pl (shared_from_this());
694 for (i = regions.begin(); i != regions.end(); ++i) {
695 (*i)->set_playlist (pl);
700 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
702 if (region->data_type() != _type){
706 RegionSortByPosition cmp;
708 framecnt_t old_length = 0;
710 if (!holding_state()) {
711 old_length = _get_maximum_extent();
714 if (!first_set_state) {
715 boost::shared_ptr<Playlist> foo (shared_from_this());
716 region->set_playlist (boost::weak_ptr<Playlist>(foo));
719 region->set_position (position, this);
721 timestamp_layer_op (region);
723 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
724 all_regions.insert (region);
726 possibly_splice_unlocked (position, region->length(), region);
728 if (!holding_state () && !in_set_state) {
729 /* layers get assigned from XML state */
733 /* we need to notify the existence of new region before checking dependents. Ick. */
735 notify_region_added (region);
737 if (!holding_state ()) {
739 check_dependents (region, false);
741 if (old_length != _get_maximum_extent()) {
742 notify_length_changed ();
746 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
752 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
754 RegionLock rlock (this);
756 bool old_sp = _splicing;
759 remove_region_internal (old);
760 add_region_internal (newr, pos);
764 possibly_splice_unlocked (pos, old->length() - newr->length());
768 Playlist::remove_region (boost::shared_ptr<Region> region)
770 RegionLock rlock (this);
771 remove_region_internal (region);
775 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
777 RegionList::iterator i;
778 framecnt_t old_length = 0;
781 if (!holding_state()) {
782 old_length = _get_maximum_extent();
787 region->set_playlist (boost::weak_ptr<Playlist>());
790 /* XXX should probably freeze here .... */
792 for (i = regions.begin(); i != regions.end(); ++i) {
795 framepos_t pos = (*i)->position();
796 framecnt_t distance = (*i)->length();
800 possibly_splice_unlocked (pos, -distance);
802 if (!holding_state ()) {
804 remove_dependents (region);
806 if (old_length != _get_maximum_extent()) {
807 notify_length_changed ();
811 notify_region_removed (region);
817 /* XXX and thaw ... */
823 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
825 if (Config->get_use_overlap_equivalency()) {
826 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
827 if ((*i)->overlap_equivalent (other)) {
828 results.push_back ((*i));
832 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
833 if ((*i)->equivalent (other)) {
834 results.push_back ((*i));
841 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
843 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
845 if ((*i) && (*i)->region_list_equivalent (other)) {
846 results.push_back (*i);
852 Playlist::partition (framepos_t start, framepos_t end, bool cut)
856 partition_internal (start, end, cut, thawlist);
858 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
864 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
866 RegionList new_regions;
869 RegionLock rlock (this);
871 boost::shared_ptr<Region> region;
872 boost::shared_ptr<Region> current;
874 RegionList::iterator tmp;
876 framepos_t pos1, pos2, pos3, pos4;
880 /* need to work from a copy, because otherwise the regions we add during the process
881 get operated on as well.
884 RegionList copy = regions.rlist();
886 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
893 if (current->first_frame() >= start && current->last_frame() < end) {
896 remove_region_internal (current);
902 /* coverage will return OverlapStart if the start coincides
903 with the end point. we do not partition such a region,
904 so catch this special case.
907 if (current->first_frame() >= end) {
911 if ((overlap = current->coverage (start, end)) == OverlapNone) {
915 pos1 = current->position();
918 pos4 = current->last_frame();
920 if (overlap == OverlapInternal) {
921 /* split: we need 3 new regions, the front, middle and end.
922 cut: we need 2 regions, the front and end.
927 ---------------*************************------------
930 ---------------*****++++++++++++++++====------------
932 ---------------*****----------------====------------
937 /* "middle" ++++++ */
939 _session.region_name (new_name, current->name(), false);
943 plist.add (Properties::start, pos2 - pos1);
944 plist.add (Properties::length, pos3 - pos2);
945 plist.add (Properties::name, new_name);
946 plist.add (Properties::layer, regions.size());
947 plist.add (Properties::automatic, true);
948 plist.add (Properties::left_of_split, true);
949 plist.add (Properties::right_of_split, true);
951 region = RegionFactory::create (current, plist);
952 add_region_internal (region, start);
953 new_regions.push_back (region);
958 _session.region_name (new_name, current->name(), false);
962 plist.add (Properties::start, pos3 - pos1);
963 plist.add (Properties::length, pos4 - pos3);
964 plist.add (Properties::name, new_name);
965 plist.add (Properties::layer, regions.size());
966 plist.add (Properties::automatic, true);
967 plist.add (Properties::right_of_split, true);
969 region = RegionFactory::create (current, plist);
971 add_region_internal (region, end);
972 new_regions.push_back (region);
977 thawlist.push_back (current);
978 current->trim_end (pos2, this);
980 } else if (overlap == OverlapEnd) {
984 ---------------*************************------------
987 ---------------**************+++++++++++------------
989 ---------------**************-----------------------
996 _session.region_name (new_name, current->name(), false);
1000 plist.add (Properties::start, pos2 - pos1);
1001 plist.add (Properties::length, pos4 - pos2);
1002 plist.add (Properties::name, new_name);
1003 plist.add (Properties::layer, regions.size());
1004 plist.add (Properties::automatic, true);
1005 plist.add (Properties::left_of_split, true);
1007 region = RegionFactory::create (current, plist);
1009 add_region_internal (region, start);
1010 new_regions.push_back (region);
1016 thawlist.push_back (current);
1017 current->trim_end (pos2, this);
1019 } else if (overlap == OverlapStart) {
1021 /* split: we need 2 regions: the front and the end.
1022 cut: just trim current to skip the cut area
1027 ---------------*************************------------
1031 ---------------****+++++++++++++++++++++------------
1033 -------------------*********************------------
1039 _session.region_name (new_name, current->name(), false);
1043 plist.add (Properties::start, 0);
1044 plist.add (Properties::length, pos3 - pos1);
1045 plist.add (Properties::name, new_name);
1046 plist.add (Properties::layer, regions.size());
1047 plist.add (Properties::automatic, true);
1048 plist.add (Properties::right_of_split, true);
1050 region = RegionFactory::create (current, plist);
1052 add_region_internal (region, pos1);
1053 new_regions.push_back (region);
1059 thawlist.push_back (current);
1060 current->trim_front (pos3, this);
1061 } else if (overlap == OverlapExternal) {
1063 /* split: no split required.
1064 cut: remove the region.
1069 ---------------*************************------------
1073 ---------------*************************------------
1075 ----------------------------------------------------
1080 remove_region_internal (current);
1083 new_regions.push_back (current);
1087 in_partition = false;
1090 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1091 check_dependents (*i, false);
1095 boost::shared_ptr<Playlist>
1096 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1098 boost::shared_ptr<Playlist> ret;
1099 boost::shared_ptr<Playlist> pl;
1102 if (ranges.empty()) {
1103 return boost::shared_ptr<Playlist>();
1106 start = ranges.front().start;
1108 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1110 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1112 if (i == ranges.begin()) {
1116 /* paste the next section into the nascent playlist,
1117 offset to reflect the start of the first range we
1121 ret->paste (pl, (*i).start - start, 1.0f);
1128 boost::shared_ptr<Playlist>
1129 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1131 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1132 return cut_copy (pmf, ranges, result_is_hidden);
1135 boost::shared_ptr<Playlist>
1136 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1138 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1139 return cut_copy (pmf, ranges, result_is_hidden);
1142 boost::shared_ptr<Playlist>
1143 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1145 boost::shared_ptr<Playlist> the_copy;
1146 RegionList thawlist;
1149 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1150 string new_name = _name;
1154 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1155 return boost::shared_ptr<Playlist>();
1158 partition_internal (start, start+cnt-1, true, thawlist);
1160 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1167 boost::shared_ptr<Playlist>
1168 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1172 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1173 string new_name = _name;
1177 cnt = min (_get_maximum_extent() - start, cnt);
1178 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1182 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1184 times = fabs (times);
1187 RegionLock rl1 (this);
1188 RegionLock rl2 (other.get());
1190 framecnt_t old_length = _get_maximum_extent();
1192 int itimes = (int) floor (times);
1193 framepos_t pos = position;
1194 framecnt_t shift = other->_get_maximum_extent();
1195 layer_t top_layer = regions.size();
1198 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1199 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1201 /* put these new regions on top of all existing ones, but preserve
1202 the ordering they had in the original playlist.
1205 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1206 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1212 /* XXX shall we handle fractional cases at some point? */
1214 if (old_length != _get_maximum_extent()) {
1215 notify_length_changed ();
1226 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1228 times = fabs (times);
1230 RegionLock rl (this);
1231 int itimes = (int) floor (times);
1232 framepos_t pos = position;
1235 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1236 add_region_internal (copy, pos);
1237 pos += region->length();
1240 if (floor (times) != times) {
1241 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1243 _session.region_name (name, region->name(), false);
1248 plist.add (Properties::start, 0);
1249 plist.add (Properties::length, length);
1250 plist.add (Properties::name, name);
1252 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1253 add_region_internal (sub, pos);
1259 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1261 RegionLock rlock (this);
1262 RegionList copy (regions.rlist());
1265 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1267 if ((*r)->last_frame() < at) {
1272 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1273 /* intersected region */
1274 if (!move_intersected) {
1279 /* do not move regions glued to music time - that
1280 has to be done separately.
1283 if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
1284 fixup.push_back (*r);
1288 (*r)->set_position ((*r)->position() + distance, this);
1291 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1292 (*r)->recompute_position_from_lock_style ();
1297 Playlist::split (framepos_t at)
1299 RegionLock rlock (this);
1300 RegionList copy (regions.rlist());
1302 /* use a copy since this operation can modify the region list
1305 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1306 _split_region (*r, at);
1311 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1313 RegionLock rl (this);
1314 _split_region (region, playlist_position);
1318 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1320 if (!region->covers (playlist_position)) {
1324 if (region->position() == playlist_position ||
1325 region->last_frame() == playlist_position) {
1329 boost::shared_ptr<Region> left;
1330 boost::shared_ptr<Region> right;
1331 frameoffset_t before;
1332 frameoffset_t after;
1336 /* split doesn't change anything about length, so don't try to splice */
1338 bool old_sp = _splicing;
1341 before = playlist_position - region->position();
1342 after = region->length() - before;
1344 _session.region_name (before_name, region->name(), false);
1349 plist.add (Properties::start, 0);
1350 plist.add (Properties::length, before);
1351 plist.add (Properties::name, before_name);
1352 plist.add (Properties::left_of_split, true);
1354 left = RegionFactory::create (region, plist);
1357 _session.region_name (after_name, region->name(), false);
1362 plist.add (Properties::start, before);
1363 plist.add (Properties::length, after);
1364 plist.add (Properties::name, after_name);
1365 plist.add (Properties::right_of_split, true);
1367 right = RegionFactory::create (region, plist);
1370 add_region_internal (left, region->position());
1371 add_region_internal (right, region->position() + before);
1373 uint64_t orig_layer_op = region->last_layer_op();
1374 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1375 if ((*i)->last_layer_op() > orig_layer_op) {
1376 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1380 left->set_last_layer_op ( orig_layer_op );
1381 right->set_last_layer_op ( orig_layer_op + 1);
1385 finalize_split_region (region, left, right);
1387 remove_region_internal (region);
1393 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1395 if (_splicing || in_set_state) {
1396 /* don't respond to splicing moves or state setting */
1400 if (_edit_mode == Splice) {
1401 splice_locked (at, distance, exclude);
1406 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1408 if (_splicing || in_set_state) {
1409 /* don't respond to splicing moves or state setting */
1413 if (_edit_mode == Splice) {
1414 splice_unlocked (at, distance, exclude);
1419 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1422 RegionLock rl (this);
1423 core_splice (at, distance, exclude);
1428 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1430 core_splice (at, distance, exclude);
1434 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1440 if (exclude && (*i) == exclude) {
1444 if ((*i)->position() >= at) {
1445 framepos_t new_pos = (*i)->position() + distance;
1448 } else if (new_pos >= max_frames - (*i)->length()) {
1449 new_pos = max_frames - (*i)->length();
1452 (*i)->set_position (new_pos, this);
1458 notify_length_changed ();
1462 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1464 if (in_set_state || _splicing || _nudging || _shuffling) {
1468 if (what_changed.contains (Properties::position)) {
1470 /* remove it from the list then add it back in
1471 the right place again.
1474 RegionSortByPosition cmp;
1476 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1478 if (i == regions.end()) {
1479 /* the region bounds are being modified but its not currently
1480 in the region list. we will use its bounds correctly when/if
1487 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1490 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1492 frameoffset_t delta = 0;
1494 if (what_changed.contains (Properties::position)) {
1495 delta = region->position() - region->last_position();
1498 if (what_changed.contains (Properties::length)) {
1499 delta += region->length() - region->last_length();
1503 possibly_splice (region->last_position() + region->last_length(), delta, region);
1506 if (holding_state ()) {
1507 pending_bounds.push_back (region);
1509 if (_session.config.get_layer_model() == MoveAddHigher) {
1510 /* it moved or changed length, so change the timestamp */
1511 timestamp_layer_op (region);
1514 notify_length_changed ();
1516 check_dependents (region, false);
1522 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1524 boost::shared_ptr<Region> region (weak_region.lock());
1530 /* this makes a virtual call to the right kind of playlist ... */
1532 region_changed (what_changed, region);
1536 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1538 PropertyChange our_interests;
1539 PropertyChange bounds;
1540 PropertyChange pos_and_length;
1543 if (in_set_state || in_flush) {
1547 our_interests.add (Properties::muted);
1548 our_interests.add (Properties::layer);
1549 our_interests.add (Properties::opaque);
1551 bounds.add (Properties::start);
1552 bounds.add (Properties::position);
1553 bounds.add (Properties::length);
1555 pos_and_length.add (Properties::position);
1556 pos_and_length.add (Properties::length);
1558 if (what_changed.contains (bounds)) {
1559 region_bounds_changed (what_changed, region);
1560 save = !(_splicing || _nudging);
1563 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1564 check_dependents (region, false);
1567 if (what_changed.contains (Properties::position)) {
1568 notify_region_moved (region);
1572 /* don't notify about layer changes, since we are the only object that can initiate
1573 them, and we notify in ::relayer()
1576 if (what_changed.contains (our_interests)) {
1584 Playlist::drop_regions ()
1586 RegionLock rl (this);
1588 all_regions.clear ();
1592 Playlist::clear (bool with_signals)
1595 RegionLock rl (this);
1597 region_state_changed_connections.drop_connections ();
1599 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1600 pending_removes.insert (*i);
1607 pending_length = false;
1609 pending_contents_change = false;
1615 /***********************************************************************
1617 **********************************************************************/
1619 Playlist::RegionList *
1620 Playlist::regions_at (framepos_t frame)
1623 RegionLock rlock (this);
1624 return find_regions_at (frame);
1627 boost::shared_ptr<Region>
1628 Playlist::top_region_at (framepos_t frame)
1631 RegionLock rlock (this);
1632 RegionList *rlist = find_regions_at (frame);
1633 boost::shared_ptr<Region> region;
1635 if (rlist->size()) {
1636 RegionSortByLayer cmp;
1638 region = rlist->back();
1645 boost::shared_ptr<Region>
1646 Playlist::top_unmuted_region_at (framepos_t frame)
1649 RegionLock rlock (this);
1650 RegionList *rlist = find_regions_at (frame);
1652 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1654 RegionList::iterator tmp = i;
1657 if ((*i)->muted()) {
1664 boost::shared_ptr<Region> region;
1666 if (rlist->size()) {
1667 RegionSortByLayer cmp;
1669 region = rlist->back();
1676 Playlist::RegionList*
1677 Playlist::regions_to_read (framepos_t start, framepos_t end)
1679 /* Caller must hold lock */
1681 RegionList covering;
1682 set<framepos_t> to_check;
1683 set<boost::shared_ptr<Region> > unique;
1685 to_check.insert (start);
1686 to_check.insert (end);
1688 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1690 /* find all/any regions that span start+end */
1692 switch ((*i)->coverage (start, end)) {
1696 case OverlapInternal:
1697 covering.push_back (*i);
1701 to_check.insert ((*i)->position());
1702 covering.push_back (*i);
1706 to_check.insert ((*i)->last_frame());
1707 covering.push_back (*i);
1710 case OverlapExternal:
1711 covering.push_back (*i);
1712 to_check.insert ((*i)->position());
1713 to_check.insert ((*i)->last_frame());
1717 /* don't go too far */
1719 if ((*i)->position() > end) {
1724 RegionList* rlist = new RegionList;
1726 /* find all the regions that cover each position .... */
1728 if (covering.size() == 1) {
1730 rlist->push_back (covering.front());
1735 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1739 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1741 if ((*x)->covers (*t)) {
1742 here.push_back (*x);
1746 RegionSortByLayer cmp;
1749 /* ... and get the top/transparent regions at "here" */
1751 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1755 if ((*c)->opaque()) {
1757 /* the other regions at this position are hidden by this one */
1764 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1765 rlist->push_back (*s);
1768 if (rlist->size() > 1) {
1769 /* now sort by time order */
1771 RegionSortByPosition cmp;
1779 Playlist::RegionList *
1780 Playlist::find_regions_at (framepos_t frame)
1782 /* Caller must hold lock */
1784 RegionList *rlist = new RegionList;
1786 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1787 if ((*i)->covers (frame)) {
1788 rlist->push_back (*i);
1795 Playlist::RegionList *
1796 Playlist::regions_touched (framepos_t start, framepos_t end)
1798 RegionLock rlock (this);
1799 RegionList *rlist = new RegionList;
1801 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1802 if ((*i)->coverage (start, end) != OverlapNone) {
1803 rlist->push_back (*i);
1811 Playlist::find_next_transient (framepos_t from, int dir)
1813 RegionLock rlock (this);
1814 AnalysisFeatureList points;
1815 AnalysisFeatureList these_points;
1817 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1819 if ((*i)->last_frame() < from) {
1823 if ((*i)->first_frame() > from) {
1828 (*i)->get_transients (these_points);
1830 /* add first frame, just, err, because */
1832 these_points.push_back ((*i)->first_frame());
1834 points.insert (points.end(), these_points.begin(), these_points.end());
1835 these_points.clear ();
1838 if (points.empty()) {
1842 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1843 bool reached = false;
1846 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1851 if (reached && (*x) > from) {
1856 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1861 if (reached && (*x) < from) {
1870 boost::shared_ptr<Region>
1871 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1873 RegionLock rlock (this);
1874 boost::shared_ptr<Region> ret;
1875 framepos_t closest = max_frames;
1877 bool end_iter = false;
1879 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1883 frameoffset_t distance;
1884 boost::shared_ptr<Region> r = (*i);
1889 pos = r->first_frame ();
1892 pos = r->last_frame ();
1895 pos = r->sync_position ();
1896 // r->adjust_to_sync (r->first_frame());
1901 case 1: /* forwards */
1904 if ((distance = pos - frame) < closest) {
1913 default: /* backwards */
1916 if ((distance = frame - pos) < closest) {
1933 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1935 RegionLock rlock (this);
1937 framepos_t closest = max_frames;
1938 framepos_t ret = -1;
1942 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1944 boost::shared_ptr<Region> r = (*i);
1945 frameoffset_t distance;
1947 if (r->first_frame() > frame) {
1949 distance = r->first_frame() - frame;
1951 if (distance < closest) {
1952 ret = r->first_frame();
1957 if (r->last_frame () > frame) {
1959 distance = r->last_frame () - frame;
1961 if (distance < closest) {
1962 ret = r->last_frame ();
1970 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1972 boost::shared_ptr<Region> r = (*i);
1973 frameoffset_t distance;
1975 if (r->last_frame() < frame) {
1977 distance = frame - r->last_frame();
1979 if (distance < closest) {
1980 ret = r->last_frame();
1985 if (r->first_frame() < frame) {
1987 distance = frame - r->first_frame();
1989 if (distance < closest) {
1990 ret = r->first_frame();
2000 /***********************************************************************/
2006 Playlist::mark_session_dirty ()
2008 if (!in_set_state && !holding_state ()) {
2009 _session.set_dirty();
2014 Playlist::set_property (const PropertyBase& prop)
2016 if (prop == Properties::regions.property_id) {
2017 const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
2018 regions.update (change);
2019 return (!change.added.empty() && !change.removed.empty());
2025 Playlist::update (const RegionListProperty::ChangeRecord& change)
2027 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2028 name(), change.added.size(), change.removed.size()));
2031 /* add the added regions */
2032 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2033 add_region ((*i), (*i)->position());
2035 /* remove the removed regions */
2036 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2043 Playlist::property_factory (const XMLNode& history_node) const
2045 const XMLNodeList& children (history_node.children());
2046 PropertyList* prop_list = 0;
2048 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2050 /* XXX property name needs capitalizing */
2052 if ((*i)->name() == regions.property_name()) {
2054 RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
2056 if (rlp->load_history_state (**i)) {
2058 prop_list = new PropertyList();
2060 prop_list->add (rlp);
2071 Playlist::set_state (const XMLNode& node, int version)
2075 XMLNodeConstIterator niter;
2076 XMLPropertyList plist;
2077 XMLPropertyConstIterator piter;
2079 boost::shared_ptr<Region> region;
2084 if (node.name() != "Playlist") {
2091 plist = node.properties();
2093 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2097 if (prop->name() == X_("name")) {
2098 _name = prop->value();
2099 } else if (prop->name() == X_("id")) {
2100 _id = prop->value();
2101 } else if (prop->name() == X_("orig_diskstream_id")) {
2102 _orig_diskstream_id = prop->value ();
2103 } else if (prop->name() == X_("frozen")) {
2104 _frozen = string_is_affirmative (prop->value());
2110 nlist = node.children();
2112 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2116 if (child->name() == "Region") {
2118 if ((prop = child->property ("id")) == 0) {
2119 error << _("region state node has no ID, ignored") << endmsg;
2123 ID id = prop->value ();
2125 if ((region = region_by_id (id))) {
2129 if (region->set_state (*child, version)) {
2134 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2137 error << _("Playlist: cannot create region from XML") << endmsg;
2141 add_region (region, region->position(), 1.0);
2143 // So that layer_op ordering doesn't get screwed up
2144 region->set_last_layer_op( region->layer());
2149 /* update dependents, which was not done during add_region_internal
2150 due to in_set_state being true
2153 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2154 check_dependents (*r, false);
2157 clear_pending (); // this makes thaw() do nothing
2159 notify_contents_changed ();
2162 first_set_state = false;
2167 Playlist::get_state()
2169 return state (true);
2173 Playlist::get_template()
2175 return state (false);
2178 /** @param full_state true to include regions in the returned state, otherwise false.
2181 Playlist::state (bool full_state)
2183 XMLNode *node = new XMLNode (X_("Playlist"));
2186 node->add_property (X_("id"), id().to_s());
2187 node->add_property (X_("name"), _name);
2188 node->add_property (X_("type"), _type.to_string());
2190 _orig_diskstream_id.print (buf, sizeof (buf));
2191 node->add_property (X_("orig_diskstream_id"), buf);
2192 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2195 RegionLock rlock (this, false);
2196 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2197 node->add_child_nocopy ((*i)->get_state());
2202 node->add_child_copy (*_extra_xml);
2209 Playlist::empty() const
2211 RegionLock rlock (const_cast<Playlist *>(this), false);
2212 return regions.empty();
2216 Playlist::n_regions() const
2218 RegionLock rlock (const_cast<Playlist *>(this), false);
2219 return regions.size();
2223 Playlist::get_maximum_extent () const
2225 RegionLock rlock (const_cast<Playlist *>(this), false);
2226 return _get_maximum_extent ();
2230 Playlist::_get_maximum_extent () const
2232 RegionList::const_iterator i;
2233 framecnt_t max_extent = 0;
2236 for (i = regions.begin(); i != regions.end(); ++i) {
2237 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
2246 Playlist::bump_name (string name, Session &session)
2248 string newname = name;
2251 newname = bump_name_once (newname);
2252 } while (session.playlists->by_name (newname)!=NULL);
2259 Playlist::top_layer() const
2261 RegionLock rlock (const_cast<Playlist *> (this));
2264 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2265 top = max (top, (*i)->layer());
2271 Playlist::set_edit_mode (EditMode mode)
2276 /********************
2278 ********************/
2281 Playlist::relayer ()
2283 bool changed = false;
2285 /* Build up a new list of regions on each layer, stored in a set of lists
2286 each of which represent some period of time on some layer. The idea
2287 is to avoid having to search the entire region list to establish whether
2288 each region overlaps another */
2290 /* how many pieces to divide this playlist's time up into */
2291 int const divisions = 512;
2293 /* find the start and end positions of the regions on this playlist */
2294 framepos_t start = UINT_MAX;
2296 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2297 start = min (start, (*i)->position());
2298 end = max (end, (*i)->position() + (*i)->length());
2301 /* hence the size of each time division */
2302 double const division_size = (end - start) / double (divisions);
2304 vector<vector<RegionList> > layers;
2305 layers.push_back (vector<RegionList> (divisions));
2307 /* we want to go through regions from desired lowest to desired highest layer,
2308 which depends on the layer model
2311 RegionList copy = regions.rlist();
2313 /* sort according to the model and the layering mode that we're in */
2315 if (_explicit_relayering) {
2317 copy.sort (RegionSortByLayerWithPending ());
2319 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2321 copy.sort (RegionSortByLastLayerOp ());
2326 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2328 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2329 (*i)->set_pending_explicit_relayer (false);
2331 /* find the time divisions that this region covers */
2332 int const start_division = floor ( ((*i)->position() - start) / division_size);
2333 int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2334 if (end_division == divisions) {
2338 assert (end_division < divisions);
2340 /* find the lowest layer that this region can go on */
2341 size_t j = layers.size();
2343 /* try layer j - 1; it can go on if it overlaps no other region
2344 that is already on that layer
2347 bool overlap = false;
2348 for (int k = start_division; k <= end_division; ++k) {
2349 RegionList::iterator l = layers[j-1][k].begin ();
2350 while (l != layers[j-1][k].end()) {
2351 if ((*l)->overlap_equivalent (*i)) {
2364 /* overlap, so we must use layer j */
2371 if (j == layers.size()) {
2372 /* we need a new layer for this region */
2373 layers.push_back (vector<RegionList> (divisions));
2376 /* put a reference to this region in each of the divisions that it exists in */
2377 for (int k = start_division; k <= end_division; ++k) {
2378 layers[j][k].push_back (*i);
2381 if ((*i)->layer() != j) {
2385 (*i)->set_layer (j);
2389 notify_layering_changed ();
2393 /* XXX these layer functions are all deprecated */
2396 Playlist::raise_region (boost::shared_ptr<Region> region)
2398 uint32_t rsz = regions.size();
2399 layer_t target = region->layer() + 1U;
2401 if (target >= rsz) {
2402 /* its already at the effective top */
2406 move_region_to_layer (target, region, 1);
2410 Playlist::lower_region (boost::shared_ptr<Region> region)
2412 if (region->layer() == 0) {
2413 /* its already at the bottom */
2417 layer_t target = region->layer() - 1U;
2419 move_region_to_layer (target, region, -1);
2423 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2425 /* does nothing useful if layering mode is later=higher */
2426 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2427 (_session.config.get_layer_model() == AddHigher)) {
2428 timestamp_layer_op (region);
2434 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2436 /* does nothing useful if layering mode is later=higher */
2437 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2438 (_session.config.get_layer_model() == AddHigher)) {
2439 region->set_last_layer_op (0);
2445 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2447 RegionList::iterator i;
2448 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2449 list<LayerInfo> layerinfo;
2452 RegionLock rlock (const_cast<Playlist *> (this));
2454 for (i = regions.begin(); i != regions.end(); ++i) {
2464 /* region is moving up, move all regions on intermediate layers
2468 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2469 dest = (*i)->layer() - 1;
2476 /* region is moving down, move all regions on intermediate layers
2480 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2481 dest = (*i)->layer() + 1;
2491 newpair.second = dest;
2493 layerinfo.push_back (newpair);
2497 /* now reset the layers without holding the region lock */
2499 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2500 x->first->set_layer (x->second);
2503 region->set_layer (target_layer);
2506 /* now check all dependents */
2508 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2509 check_dependents (x->first, false);
2512 check_dependents (region, false);
2519 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2521 RegionList::iterator i;
2527 RegionLock rlock (const_cast<Playlist *> (this));
2529 for (i = regions.begin(); i != regions.end(); ++i) {
2531 if ((*i)->position() >= start) {
2537 if ((*i)->last_frame() > max_frames - distance) {
2538 new_pos = max_frames - (*i)->length();
2540 new_pos = (*i)->position() + distance;
2545 if ((*i)->position() > distance) {
2546 new_pos = (*i)->position() - distance;
2552 (*i)->set_position (new_pos, this);
2560 notify_length_changed ();
2565 boost::shared_ptr<Region>
2566 Playlist::find_region (const ID& id) const
2568 RegionLock rlock (const_cast<Playlist*> (this));
2570 /* searches all regions currently in use by the playlist */
2572 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2573 if ((*i)->id() == id) {
2578 return boost::shared_ptr<Region> ();
2581 boost::shared_ptr<Region>
2582 Playlist::region_by_id (const ID& id)
2584 /* searches all regions ever added to this playlist */
2586 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2587 if ((*i)->id() == id) {
2591 return boost::shared_ptr<Region> ();
2595 Playlist::dump () const
2597 boost::shared_ptr<Region> r;
2599 cerr << "Playlist \"" << _name << "\" " << endl
2600 << regions.size() << " regions "
2603 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2605 cerr << " " << r->name() << " ["
2606 << r->start() << "+" << r->length()
2616 Playlist::set_frozen (bool yn)
2622 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2624 // struct timeval tv;
2625 // gettimeofday (&tv, 0);
2626 region->set_last_layer_op (++layer_op_counter);
2631 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2635 if (region->locked()) {
2642 RegionLock rlock (const_cast<Playlist*> (this));
2647 RegionList::iterator next;
2649 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2650 if ((*i) == region) {
2654 if (next != regions.end()) {
2656 if ((*next)->locked()) {
2662 if ((*next)->position() != region->last_frame() + 1) {
2663 /* they didn't used to touch, so after shuffle,
2664 just have them swap positions.
2666 new_pos = (*next)->position();
2668 /* they used to touch, so after shuffle,
2669 make sure they still do. put the earlier
2670 region where the later one will end after
2673 new_pos = region->position() + (*next)->length();
2676 (*next)->set_position (region->position(), this);
2677 region->set_position (new_pos, this);
2679 /* avoid a full sort */
2681 regions.erase (i); // removes the region from the list */
2683 regions.insert (next, region); // adds it back after next
2692 RegionList::iterator prev = regions.end();
2694 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2695 if ((*i) == region) {
2697 if (prev != regions.end()) {
2699 if ((*prev)->locked()) {
2704 if (region->position() != (*prev)->last_frame() + 1) {
2705 /* they didn't used to touch, so after shuffle,
2706 just have them swap positions.
2708 new_pos = region->position();
2710 /* they used to touch, so after shuffle,
2711 make sure they still do. put the earlier
2712 one where the later one will end after
2714 new_pos = (*prev)->position() + region->length();
2717 region->set_position ((*prev)->position(), this);
2718 (*prev)->set_position (new_pos, this);
2720 /* avoid a full sort */
2722 regions.erase (i); // remove region
2723 regions.insert (prev, region); // insert region before prev
2739 check_dependents (region, false);
2741 notify_contents_changed();
2747 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2749 RegionLock rlock (const_cast<Playlist*> (this));
2751 if (regions.size() > 1) {
2759 Playlist::update_after_tempo_map_change ()
2761 RegionLock rlock (const_cast<Playlist*> (this));
2762 RegionList copy (regions.rlist());
2766 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2767 (*i)->update_position_after_tempo_map_change ();
2774 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2776 RegionLock rl (this, false);
2777 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2783 Playlist::set_explicit_relayering (bool e)
2785 if (e == false && _explicit_relayering == true) {
2787 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2788 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2789 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2790 at this point would keep regions on the same layers.
2792 From then on in, it's just you and your towel.
2795 RegionLock rl (this);
2796 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2797 (*i)->set_last_layer_op ((*i)->layer ());
2801 _explicit_relayering = e;
2806 Playlist::has_region_at (framepos_t const p) const
2808 RegionLock (const_cast<Playlist *> (this));
2810 RegionList::const_iterator i = regions.begin ();
2811 while (i != regions.end() && !(*i)->covers (p)) {
2815 return (i != regions.end());