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.
28 #include "pbd/failed_constructor.h"
29 #include "pbd/stl_delete.h"
30 #include "pbd/xml++.h"
31 #include "pbd/stacktrace.h"
33 #include "ardour/debug.h"
34 #include "ardour/playlist.h"
35 #include "ardour/session.h"
36 #include "ardour/region.h"
37 #include "ardour/region_factory.h"
38 #include "ardour/playlist_factory.h"
39 #include "ardour/transient_detector.h"
40 #include "ardour/session_playlists.h"
45 using namespace ARDOUR;
49 namespace Properties {
50 PBD::PropertyDescriptor<bool> regions;
54 struct ShowMeTheList {
55 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
57 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
59 boost::shared_ptr<Playlist> playlist;
63 struct RegionSortByLayer {
64 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
65 return a->layer() < b->layer();
69 struct RegionSortByLayerWithPending {
70 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
72 double p = a->layer ();
73 if (a->pending_explicit_relayer()) {
77 double q = b->layer ();
78 if (b->pending_explicit_relayer()) {
86 struct RegionSortByPosition {
87 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
88 return a->position() < b->position();
92 struct RegionSortByLastLayerOp {
93 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
94 return a->last_layer_op() < b->last_layer_op();
99 Playlist::make_property_quarks ()
101 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
102 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
105 RegionListProperty::RegionListProperty (Playlist& pl)
106 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
111 boost::shared_ptr<Region>
112 RegionListProperty::lookup_id (const ID& id)
114 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
117 ret = _playlist.session().region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 RegionListProperty::copy_for_history () const
129 RegionListProperty* copy = new RegionListProperty (_playlist);
130 /* this is all we need */
131 copy->_change = _change;
136 RegionListProperty::diff (PropertyList& before, PropertyList& after) const
139 RegionListProperty* a = copy_for_history ();
140 RegionListProperty* b = copy_for_history ();
142 b->invert_changes ();
147 cerr << "pdiff on " << _playlist.name() << " before contains "
148 << b->change().added.size() << " adds and " << b->change().removed.size() << " removes\n";
149 cerr << "pdiff on " << _playlist.name() << " after contains "
150 << a->change().added.size() << " adds and " << a->change().removed.size() << " removes\n";
155 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
156 : SessionObject(sess, nom)
161 first_set_state = false;
166 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
167 : SessionObject(sess, "unnamed playlist")
172 const XMLProperty* prop = node.property("type");
173 assert(!prop || DataType(prop->value()) == _type);
176 _name = "unnamed"; /* reset by set_state */
178 /* set state called by derived class */
181 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
182 : SessionObject(other->_session, namestr)
184 , _type(other->_type)
185 , _orig_diskstream_id(other->_orig_diskstream_id)
190 other->copy_regions (tmp);
194 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
195 add_region_internal( (*x), (*x)->position());
200 _splicing = other->_splicing;
201 _nudging = other->_nudging;
202 _edit_mode = other->_edit_mode;
205 first_set_state = false;
207 in_partition = false;
209 _read_data_count = 0;
210 _frozen = other->_frozen;
212 layer_op_counter = other->layer_op_counter;
213 freeze_length = other->freeze_length;
216 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
217 : SessionObject(other->_session, str)
219 , _type(other->_type)
220 , _orig_diskstream_id(other->_orig_diskstream_id)
222 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
224 framepos_t end = start + cnt - 1;
230 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
232 boost::shared_ptr<Region> region;
233 boost::shared_ptr<Region> new_region;
234 frameoffset_t offset = 0;
235 framepos_t position = 0;
242 overlap = region->coverage (start, end);
248 case OverlapInternal:
249 offset = start - region->position();
256 position = region->position() - start;
257 len = end - region->position();
261 offset = start - region->position();
263 len = region->length() - offset;
266 case OverlapExternal:
268 position = region->position() - start;
269 len = region->length();
273 _session.region_name (new_name, region->name(), false);
277 plist.add (Properties::start, offset);
278 plist.add (Properties::length, len);
279 plist.add (Properties::name, new_name);
280 plist.add (Properties::layer, region->layer());
282 new_region = RegionFactory::RegionFactory::create (region, plist);
284 add_region_internal (new_region, position);
288 first_set_state = false;
290 /* this constructor does NOT notify others (session) */
297 InUse (true); /* EMIT SIGNAL */
308 InUse (false); /* EMIT SIGNAL */
313 Playlist::copy_regions (RegionList& newlist) const
315 RegionLock rlock (const_cast<Playlist *> (this));
317 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
318 newlist.push_back (RegionFactory::RegionFactory::create (*i));
323 Playlist::init (bool hide)
325 add_property (regions);
326 _xml_node_name = X_("Playlist");
328 g_atomic_int_set (&block_notifications, 0);
329 g_atomic_int_set (&ignore_state_changes, 0);
330 pending_contents_change = false;
331 pending_length = false;
332 pending_layering = false;
333 first_set_state = true;
340 _edit_mode = Config->get_edit_mode();
342 in_partition = false;
344 _read_data_count = 0;
346 layer_op_counter = 0;
348 _explicit_relayering = false;
350 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
351 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
353 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
356 Playlist::~Playlist ()
358 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
361 RegionLock rl (this);
363 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
364 (*i)->set_playlist (boost::shared_ptr<Playlist>());
368 /* GoingAway must be emitted by derived classes */
372 Playlist::set_name (const string& str)
374 /* in a typical situation, a playlist is being used
375 by one diskstream and also is referenced by the
376 Session. if there are more references than that,
377 then don't change the name.
383 return SessionObject::set_name(str);
387 /***********************************************************************
388 CHANGE NOTIFICATION HANDLING
390 Notifications must be delayed till the region_lock is released. This
391 is necessary because handlers for the signals may need to acquire
392 the lock (e.g. to read from the playlist).
393 ***********************************************************************/
396 Playlist::begin_undo ()
402 Playlist::end_undo ()
410 delay_notifications ();
411 g_atomic_int_inc (&ignore_state_changes);
417 g_atomic_int_dec_and_test (&ignore_state_changes);
418 release_notifications ();
423 Playlist::delay_notifications ()
425 g_atomic_int_inc (&block_notifications);
426 freeze_length = _get_maximum_extent();
430 Playlist::release_notifications ()
432 if (g_atomic_int_dec_and_test (&block_notifications)) {
433 flush_notifications ();
438 Playlist::notify_contents_changed ()
440 if (holding_state ()) {
441 pending_contents_change = true;
443 pending_contents_change = false;
444 ContentsChanged(); /* EMIT SIGNAL */
449 Playlist::notify_layering_changed ()
451 if (holding_state ()) {
452 pending_layering = true;
454 pending_layering = false;
455 LayeringChanged(); /* EMIT SIGNAL */
460 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
462 if (holding_state ()) {
463 pending_removes.insert (r);
464 pending_contents_change = true;
465 pending_length = true;
467 /* this might not be true, but we have to act
468 as though it could be.
470 pending_length = false;
471 LengthChanged (); /* EMIT SIGNAL */
472 pending_contents_change = false;
473 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
474 ContentsChanged (); /* EMIT SIGNAL */
479 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
481 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
483 if (holding_state ()) {
485 pending_range_moves.push_back (move);
489 list< Evoral::RangeMove<framepos_t> > m;
497 Playlist::notify_region_added (boost::shared_ptr<Region> r)
499 /* the length change might not be true, but we have to act
500 as though it could be.
503 if (holding_state()) {
504 pending_adds.insert (r);
505 pending_contents_change = true;
506 pending_length = true;
508 pending_length = false;
509 LengthChanged (); /* EMIT SIGNAL */
510 pending_contents_change = false;
511 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
512 ContentsChanged (); /* EMIT SIGNAL */
517 Playlist::notify_length_changed ()
519 if (holding_state ()) {
520 pending_length = true;
522 pending_length = false;
523 LengthChanged(); /* EMIT SIGNAL */
524 pending_contents_change = false;
525 ContentsChanged (); /* EMIT SIGNAL */
530 Playlist::flush_notifications ()
532 set<boost::shared_ptr<Region> > dependent_checks_needed;
533 set<boost::shared_ptr<Region> >::iterator s;
534 uint32_t regions_changed = false;
535 bool check_length = false;
536 framecnt_t old_length = 0;
544 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
545 regions_changed = true;
546 if (!pending_length) {
547 old_length = _get_maximum_extent ();
552 /* we have no idea what order the regions ended up in pending
553 bounds (it could be based on selection order, for example).
554 so, to preserve layering in the "most recently moved is higher"
555 model, sort them by existing layer, then timestamp them.
558 // RegionSortByLayer cmp;
559 // pending_bounds.sort (cmp);
561 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
562 if (_session.config.get_layer_model() == MoveAddHigher) {
563 timestamp_layer_op (*r);
565 dependent_checks_needed.insert (*r);
568 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
569 remove_dependents (*s);
570 // cerr << _name << " sends RegionRemoved\n";
571 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
574 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
575 // cerr << _name << " sends RegionAdded\n";
576 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
577 dependent_checks_needed.insert (*s);
581 if (old_length != _get_maximum_extent()) {
582 pending_length = true;
583 // cerr << _name << " length has changed\n";
587 if (pending_length || (freeze_length != _get_maximum_extent())) {
588 pending_length = false;
589 // cerr << _name << " sends LengthChanged\n";
590 LengthChanged(); /* EMIT SIGNAL */
593 if (regions_changed || pending_contents_change) {
597 pending_contents_change = false;
598 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
599 ContentsChanged (); /* EMIT SIGNAL */
600 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
603 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
604 check_dependents (*s, false);
607 if (!pending_range_moves.empty ()) {
608 // cerr << _name << " sends RangesMoved\n";
609 RangesMoved (pending_range_moves);
618 Playlist::clear_pending ()
620 pending_adds.clear ();
621 pending_removes.clear ();
622 pending_bounds.clear ();
623 pending_range_moves.clear ();
624 pending_contents_change = false;
625 pending_length = false;
628 /*************************************************************
630 *************************************************************/
633 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
635 RegionLock rlock (this);
636 times = fabs (times);
638 int itimes = (int) floor (times);
640 framepos_t pos = position;
642 if (times == 1 && auto_partition){
643 partition(pos, (pos + region->length()), true);
647 add_region_internal (region, pos);
648 pos += region->length();
653 /* note that itimes can be zero if we being asked to just
654 insert a single fraction of the region.
657 for (int i = 0; i < itimes; ++i) {
658 boost::shared_ptr<Region> copy = RegionFactory::create (region);
659 add_region_internal (copy, pos);
660 pos += region->length();
663 framecnt_t length = 0;
665 if (floor (times) != times) {
666 length = (framecnt_t) floor (region->length() * (times - floor (times)));
668 _session.region_name (name, region->name(), false);
673 plist.add (Properties::start, 0);
674 plist.add (Properties::length, length);
675 plist.add (Properties::name, name);
676 plist.add (Properties::layer, region->layer());
678 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
679 add_region_internal (sub, pos);
683 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
687 Playlist::set_region_ownership ()
689 RegionLock rl (this);
690 RegionList::iterator i;
691 boost::weak_ptr<Playlist> pl (shared_from_this());
693 for (i = regions.begin(); i != regions.end(); ++i) {
694 (*i)->set_playlist (pl);
699 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
701 if (region->data_type() != _type){
705 RegionSortByPosition cmp;
707 framecnt_t old_length = 0;
709 if (!holding_state()) {
710 old_length = _get_maximum_extent();
713 if (!first_set_state) {
714 boost::shared_ptr<Playlist> foo (shared_from_this());
715 region->set_playlist (boost::weak_ptr<Playlist>(foo));
718 region->set_position (position, this);
720 timestamp_layer_op (region);
722 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
723 all_regions.insert (region);
725 possibly_splice_unlocked (position, region->length(), region);
727 if (!holding_state () && !in_set_state) {
728 /* layers get assigned from XML state */
732 /* we need to notify the existence of new region before checking dependents. Ick. */
734 notify_region_added (region);
736 if (!holding_state ()) {
738 check_dependents (region, false);
740 if (old_length != _get_maximum_extent()) {
741 notify_length_changed ();
745 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
751 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
753 RegionLock rlock (this);
755 bool old_sp = _splicing;
758 remove_region_internal (old);
759 add_region_internal (newr, pos);
763 possibly_splice_unlocked (pos, old->length() - newr->length());
767 Playlist::remove_region (boost::shared_ptr<Region> region)
769 RegionLock rlock (this);
770 remove_region_internal (region);
774 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
776 RegionList::iterator i;
777 framecnt_t old_length = 0;
780 if (!holding_state()) {
781 old_length = _get_maximum_extent();
786 region->set_playlist (boost::weak_ptr<Playlist>());
789 /* XXX should probably freeze here .... */
791 for (i = regions.begin(); i != regions.end(); ++i) {
794 framepos_t pos = (*i)->position();
795 framecnt_t distance = (*i)->length();
799 possibly_splice_unlocked (pos, -distance);
801 if (!holding_state ()) {
803 remove_dependents (region);
805 if (old_length != _get_maximum_extent()) {
806 notify_length_changed ();
810 notify_region_removed (region);
816 /* XXX and thaw ... */
822 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
824 if (Config->get_use_overlap_equivalency()) {
825 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
826 if ((*i)->overlap_equivalent (other)) {
827 results.push_back ((*i));
831 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
832 if ((*i)->equivalent (other)) {
833 results.push_back ((*i));
840 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
842 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
844 if ((*i) && (*i)->region_list_equivalent (other)) {
845 results.push_back (*i);
851 Playlist::partition (framepos_t start, framepos_t end, bool cut)
855 partition_internal (start, end, cut, thawlist);
857 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
863 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
865 RegionList new_regions;
868 RegionLock rlock (this);
870 boost::shared_ptr<Region> region;
871 boost::shared_ptr<Region> current;
873 RegionList::iterator tmp;
875 framepos_t pos1, pos2, pos3, pos4;
879 /* need to work from a copy, because otherwise the regions we add during the process
880 get operated on as well.
883 RegionList copy = regions.rlist();
885 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
892 if (current->first_frame() >= start && current->last_frame() < end) {
895 remove_region_internal (current);
901 /* coverage will return OverlapStart if the start coincides
902 with the end point. we do not partition such a region,
903 so catch this special case.
906 if (current->first_frame() >= end) {
910 if ((overlap = current->coverage (start, end)) == OverlapNone) {
914 pos1 = current->position();
917 pos4 = current->last_frame();
919 if (overlap == OverlapInternal) {
920 /* split: we need 3 new regions, the front, middle and end.
921 cut: we need 2 regions, the front and end.
926 ---------------*************************------------
929 ---------------*****++++++++++++++++====------------
931 ---------------*****----------------====------------
936 /* "middle" ++++++ */
938 _session.region_name (new_name, current->name(), false);
942 plist.add (Properties::start, pos2 - pos1);
943 plist.add (Properties::length, pos3 - pos2);
944 plist.add (Properties::name, new_name);
945 plist.add (Properties::layer, regions.size());
946 plist.add (Properties::automatic, true);
947 plist.add (Properties::left_of_split, true);
948 plist.add (Properties::right_of_split, true);
950 region = RegionFactory::create (current, plist);
951 add_region_internal (region, start);
952 new_regions.push_back (region);
957 _session.region_name (new_name, current->name(), false);
961 plist.add (Properties::start, pos3 - pos1);
962 plist.add (Properties::length, pos4 - pos3);
963 plist.add (Properties::name, new_name);
964 plist.add (Properties::layer, regions.size());
965 plist.add (Properties::automatic, true);
966 plist.add (Properties::right_of_split, true);
968 region = RegionFactory::create (current, plist);
970 add_region_internal (region, end);
971 new_regions.push_back (region);
976 thawlist.push_back (current);
977 current->trim_end (pos2, this);
979 } else if (overlap == OverlapEnd) {
983 ---------------*************************------------
986 ---------------**************+++++++++++------------
988 ---------------**************-----------------------
995 _session.region_name (new_name, current->name(), false);
999 plist.add (Properties::start, pos2 - pos1);
1000 plist.add (Properties::length, pos4 - pos2);
1001 plist.add (Properties::name, new_name);
1002 plist.add (Properties::layer, regions.size());
1003 plist.add (Properties::automatic, true);
1004 plist.add (Properties::left_of_split, true);
1006 region = RegionFactory::create (current, plist);
1008 add_region_internal (region, start);
1009 new_regions.push_back (region);
1015 thawlist.push_back (current);
1016 current->trim_end (pos2, this);
1018 } else if (overlap == OverlapStart) {
1020 /* split: we need 2 regions: the front and the end.
1021 cut: just trim current to skip the cut area
1026 ---------------*************************------------
1030 ---------------****+++++++++++++++++++++------------
1032 -------------------*********************------------
1038 _session.region_name (new_name, current->name(), false);
1042 plist.add (Properties::start, 0);
1043 plist.add (Properties::length, pos3 - pos1);
1044 plist.add (Properties::name, new_name);
1045 plist.add (Properties::layer, regions.size());
1046 plist.add (Properties::automatic, true);
1047 plist.add (Properties::right_of_split, true);
1049 region = RegionFactory::create (current, plist);
1051 add_region_internal (region, pos1);
1052 new_regions.push_back (region);
1058 thawlist.push_back (current);
1059 current->trim_front (pos3, this);
1060 } else if (overlap == OverlapExternal) {
1062 /* split: no split required.
1063 cut: remove the region.
1068 ---------------*************************------------
1072 ---------------*************************------------
1074 ----------------------------------------------------
1079 remove_region_internal (current);
1082 new_regions.push_back (current);
1086 in_partition = false;
1089 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1090 check_dependents (*i, false);
1094 boost::shared_ptr<Playlist>
1095 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1097 boost::shared_ptr<Playlist> ret;
1098 boost::shared_ptr<Playlist> pl;
1101 if (ranges.empty()) {
1102 return boost::shared_ptr<Playlist>();
1105 start = ranges.front().start;
1107 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1109 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1111 if (i == ranges.begin()) {
1115 /* paste the next section into the nascent playlist,
1116 offset to reflect the start of the first range we
1120 ret->paste (pl, (*i).start - start, 1.0f);
1127 boost::shared_ptr<Playlist>
1128 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1130 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1131 return cut_copy (pmf, ranges, result_is_hidden);
1134 boost::shared_ptr<Playlist>
1135 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1137 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1138 return cut_copy (pmf, ranges, result_is_hidden);
1141 boost::shared_ptr<Playlist>
1142 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1144 boost::shared_ptr<Playlist> the_copy;
1145 RegionList thawlist;
1148 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1149 string new_name = _name;
1153 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1154 return boost::shared_ptr<Playlist>();
1157 partition_internal (start, start+cnt-1, true, thawlist);
1159 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1166 boost::shared_ptr<Playlist>
1167 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1171 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1172 string new_name = _name;
1176 cnt = min (_get_maximum_extent() - start, cnt);
1177 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1181 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1183 times = fabs (times);
1186 RegionLock rl1 (this);
1187 RegionLock rl2 (other.get());
1189 framecnt_t old_length = _get_maximum_extent();
1191 int itimes = (int) floor (times);
1192 framepos_t pos = position;
1193 framecnt_t shift = other->_get_maximum_extent();
1194 layer_t top_layer = regions.size();
1197 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1198 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1200 /* put these new regions on top of all existing ones, but preserve
1201 the ordering they had in the original playlist.
1204 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1205 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1211 /* XXX shall we handle fractional cases at some point? */
1213 if (old_length != _get_maximum_extent()) {
1214 notify_length_changed ();
1225 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1227 times = fabs (times);
1229 RegionLock rl (this);
1230 int itimes = (int) floor (times);
1231 framepos_t pos = position;
1234 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1235 add_region_internal (copy, pos);
1236 pos += region->length();
1239 if (floor (times) != times) {
1240 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1242 _session.region_name (name, region->name(), false);
1247 plist.add (Properties::start, 0);
1248 plist.add (Properties::length, length);
1249 plist.add (Properties::name, name);
1251 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1252 add_region_internal (sub, pos);
1258 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1260 RegionLock rlock (this);
1261 RegionList copy (regions.rlist());
1264 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1266 if ((*r)->last_frame() < at) {
1271 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1272 /* intersected region */
1273 if (!move_intersected) {
1278 /* do not move regions glued to music time - that
1279 has to be done separately.
1282 if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
1283 fixup.push_back (*r);
1287 (*r)->set_position ((*r)->position() + distance, this);
1290 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1291 (*r)->recompute_position_from_lock_style ();
1296 Playlist::split (framepos_t at)
1298 RegionLock rlock (this);
1299 RegionList copy (regions.rlist());
1301 /* use a copy since this operation can modify the region list
1304 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1305 _split_region (*r, at);
1310 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1312 RegionLock rl (this);
1313 _split_region (region, playlist_position);
1317 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1319 if (!region->covers (playlist_position)) {
1323 if (region->position() == playlist_position ||
1324 region->last_frame() == playlist_position) {
1328 boost::shared_ptr<Region> left;
1329 boost::shared_ptr<Region> right;
1330 frameoffset_t before;
1331 frameoffset_t after;
1335 /* split doesn't change anything about length, so don't try to splice */
1337 bool old_sp = _splicing;
1340 before = playlist_position - region->position();
1341 after = region->length() - before;
1343 _session.region_name (before_name, region->name(), false);
1348 plist.add (Properties::start, 0);
1349 plist.add (Properties::length, before);
1350 plist.add (Properties::name, before_name);
1351 plist.add (Properties::left_of_split, true);
1353 left = RegionFactory::create (region, plist);
1356 _session.region_name (after_name, region->name(), false);
1361 plist.add (Properties::start, before);
1362 plist.add (Properties::length, after);
1363 plist.add (Properties::name, after_name);
1364 plist.add (Properties::right_of_split, true);
1366 right = RegionFactory::create (region, plist);
1369 add_region_internal (left, region->position());
1370 add_region_internal (right, region->position() + before);
1372 uint64_t orig_layer_op = region->last_layer_op();
1373 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1374 if ((*i)->last_layer_op() > orig_layer_op) {
1375 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1379 left->set_last_layer_op ( orig_layer_op );
1380 right->set_last_layer_op ( orig_layer_op + 1);
1384 finalize_split_region (region, left, right);
1386 remove_region_internal (region);
1392 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1394 if (_splicing || in_set_state) {
1395 /* don't respond to splicing moves or state setting */
1399 if (_edit_mode == Splice) {
1400 splice_locked (at, distance, exclude);
1405 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1407 if (_splicing || in_set_state) {
1408 /* don't respond to splicing moves or state setting */
1412 if (_edit_mode == Splice) {
1413 splice_unlocked (at, distance, exclude);
1418 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1421 RegionLock rl (this);
1422 core_splice (at, distance, exclude);
1427 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1429 core_splice (at, distance, exclude);
1433 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1437 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1439 if (exclude && (*i) == exclude) {
1443 if ((*i)->position() >= at) {
1444 framepos_t new_pos = (*i)->position() + distance;
1447 } else if (new_pos >= max_frames - (*i)->length()) {
1448 new_pos = max_frames - (*i)->length();
1451 (*i)->set_position (new_pos, this);
1457 notify_length_changed ();
1461 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1463 if (in_set_state || _splicing || _nudging || _shuffling) {
1467 if (what_changed.contains (Properties::position)) {
1469 /* remove it from the list then add it back in
1470 the right place again.
1473 RegionSortByPosition cmp;
1475 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1477 if (i == regions.end()) {
1478 /* the region bounds are being modified but its not currently
1479 in the region list. we will use its bounds correctly when/if
1486 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1489 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1491 frameoffset_t delta = 0;
1493 if (what_changed.contains (Properties::position)) {
1494 delta = region->position() - region->last_position();
1497 if (what_changed.contains (Properties::length)) {
1498 delta += region->length() - region->last_length();
1502 possibly_splice (region->last_position() + region->last_length(), delta, region);
1505 if (holding_state ()) {
1506 pending_bounds.push_back (region);
1508 if (_session.config.get_layer_model() == MoveAddHigher) {
1509 /* it moved or changed length, so change the timestamp */
1510 timestamp_layer_op (region);
1513 notify_length_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)) {
1567 notify_region_moved (region);
1571 /* don't notify about layer changes, since we are the only object that can initiate
1572 them, and we notify in ::relayer()
1575 if (what_changed.contains (our_interests)) {
1583 Playlist::drop_regions ()
1585 RegionLock rl (this);
1587 all_regions.clear ();
1591 Playlist::clear (bool with_signals)
1594 RegionLock rl (this);
1596 region_state_changed_connections.drop_connections ();
1598 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1599 pending_removes.insert (*i);
1606 pending_length = false;
1608 pending_contents_change = false;
1614 /***********************************************************************
1616 **********************************************************************/
1618 Playlist::RegionList *
1619 Playlist::regions_at (framepos_t frame)
1622 RegionLock rlock (this);
1623 return find_regions_at (frame);
1626 boost::shared_ptr<Region>
1627 Playlist::top_region_at (framepos_t frame)
1630 RegionLock rlock (this);
1631 RegionList *rlist = find_regions_at (frame);
1632 boost::shared_ptr<Region> region;
1634 if (rlist->size()) {
1635 RegionSortByLayer cmp;
1637 region = rlist->back();
1644 boost::shared_ptr<Region>
1645 Playlist::top_unmuted_region_at (framepos_t frame)
1648 RegionLock rlock (this);
1649 RegionList *rlist = find_regions_at (frame);
1651 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1653 RegionList::iterator tmp = i;
1656 if ((*i)->muted()) {
1663 boost::shared_ptr<Region> region;
1665 if (rlist->size()) {
1666 RegionSortByLayer cmp;
1668 region = rlist->back();
1675 Playlist::RegionList*
1676 Playlist::regions_to_read (framepos_t start, framepos_t end)
1678 /* Caller must hold lock */
1680 RegionList covering;
1681 set<framepos_t> to_check;
1682 set<boost::shared_ptr<Region> > unique;
1684 to_check.insert (start);
1685 to_check.insert (end);
1687 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1689 /* find all/any regions that span start+end */
1691 switch ((*i)->coverage (start, end)) {
1695 case OverlapInternal:
1696 covering.push_back (*i);
1700 to_check.insert ((*i)->position());
1701 covering.push_back (*i);
1705 to_check.insert ((*i)->last_frame());
1706 covering.push_back (*i);
1709 case OverlapExternal:
1710 covering.push_back (*i);
1711 to_check.insert ((*i)->position());
1712 to_check.insert ((*i)->last_frame());
1716 /* don't go too far */
1718 if ((*i)->position() > end) {
1723 RegionList* rlist = new RegionList;
1725 /* find all the regions that cover each position .... */
1727 if (covering.size() == 1) {
1729 rlist->push_back (covering.front());
1734 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1738 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1740 if ((*x)->covers (*t)) {
1741 here.push_back (*x);
1745 RegionSortByLayer cmp;
1748 /* ... and get the top/transparent regions at "here" */
1750 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1754 if ((*c)->opaque()) {
1756 /* the other regions at this position are hidden by this one */
1763 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1764 rlist->push_back (*s);
1767 if (rlist->size() > 1) {
1768 /* now sort by time order */
1770 RegionSortByPosition cmp;
1778 Playlist::RegionList *
1779 Playlist::find_regions_at (framepos_t frame)
1781 /* Caller must hold lock */
1783 RegionList *rlist = new RegionList;
1785 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1786 if ((*i)->covers (frame)) {
1787 rlist->push_back (*i);
1794 Playlist::RegionList *
1795 Playlist::regions_touched (framepos_t start, framepos_t end)
1797 RegionLock rlock (this);
1798 RegionList *rlist = new RegionList;
1800 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1801 if ((*i)->coverage (start, end) != OverlapNone) {
1802 rlist->push_back (*i);
1810 Playlist::find_next_transient (framepos_t from, int dir)
1812 RegionLock rlock (this);
1813 AnalysisFeatureList points;
1814 AnalysisFeatureList these_points;
1816 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1818 if ((*i)->last_frame() < from) {
1822 if ((*i)->first_frame() > from) {
1827 (*i)->get_transients (these_points);
1829 /* add first frame, just, err, because */
1831 these_points.push_back ((*i)->first_frame());
1833 points.insert (points.end(), these_points.begin(), these_points.end());
1834 these_points.clear ();
1837 if (points.empty()) {
1841 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1842 bool reached = false;
1845 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1850 if (reached && (*x) > from) {
1855 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1860 if (reached && (*x) < from) {
1869 boost::shared_ptr<Region>
1870 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1872 RegionLock rlock (this);
1873 boost::shared_ptr<Region> ret;
1874 framepos_t closest = max_frames;
1876 bool end_iter = false;
1878 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1882 frameoffset_t distance;
1883 boost::shared_ptr<Region> r = (*i);
1888 pos = r->first_frame ();
1891 pos = r->last_frame ();
1894 pos = r->sync_position ();
1895 // r->adjust_to_sync (r->first_frame());
1900 case 1: /* forwards */
1903 if ((distance = pos - frame) < closest) {
1912 default: /* backwards */
1915 if ((distance = frame - pos) < closest) {
1932 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1934 RegionLock rlock (this);
1936 framepos_t closest = max_frames;
1937 framepos_t ret = -1;
1941 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1943 boost::shared_ptr<Region> r = (*i);
1944 frameoffset_t distance;
1946 if (r->first_frame() > frame) {
1948 distance = r->first_frame() - frame;
1950 if (distance < closest) {
1951 ret = r->first_frame();
1956 if (r->last_frame () > frame) {
1958 distance = r->last_frame () - frame;
1960 if (distance < closest) {
1961 ret = r->last_frame ();
1969 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1971 boost::shared_ptr<Region> r = (*i);
1972 frameoffset_t distance;
1974 if (r->last_frame() < frame) {
1976 distance = frame - r->last_frame();
1978 if (distance < closest) {
1979 ret = r->last_frame();
1984 if (r->first_frame() < frame) {
1986 distance = frame - r->first_frame();
1988 if (distance < closest) {
1989 ret = r->first_frame();
1999 /***********************************************************************/
2005 Playlist::mark_session_dirty ()
2007 if (!in_set_state && !holding_state ()) {
2008 _session.set_dirty();
2013 Playlist::set_property (const PropertyBase& prop)
2015 if (prop == Properties::regions.property_id) {
2016 const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
2017 regions.update (change);
2018 return (!change.added.empty() && !change.removed.empty());
2024 Playlist::update (const RegionListProperty::ChangeRecord& change)
2026 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2027 name(), change.added.size(), change.removed.size()));
2030 /* add the added regions */
2031 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2032 add_region ((*i), (*i)->position());
2034 /* remove the removed regions */
2035 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2042 Playlist::property_factory (const XMLNode& history_node) const
2044 const XMLNodeList& children (history_node.children());
2045 PropertyList* prop_list = 0;
2047 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2049 /* XXX property name needs capitalizing */
2051 if ((*i)->name() == capitalize (regions.property_name())) {
2053 RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
2055 if (rlp->load_history_state (**i)) {
2057 prop_list = new PropertyList();
2059 prop_list->add (rlp);
2070 Playlist::set_state (const XMLNode& node, int version)
2074 XMLNodeConstIterator niter;
2075 XMLPropertyList plist;
2076 XMLPropertyConstIterator piter;
2078 boost::shared_ptr<Region> region;
2083 if (node.name() != "Playlist") {
2090 plist = node.properties();
2092 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2096 if (prop->name() == X_("name")) {
2097 _name = prop->value();
2098 } else if (prop->name() == X_("id")) {
2099 _id = prop->value();
2100 } else if (prop->name() == X_("orig_diskstream_id")) {
2101 _orig_diskstream_id = prop->value ();
2102 } else if (prop->name() == X_("frozen")) {
2103 _frozen = string_is_affirmative (prop->value());
2109 nlist = node.children();
2111 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2115 if (child->name() == "Region") {
2117 if ((prop = child->property ("id")) == 0) {
2118 error << _("region state node has no ID, ignored") << endmsg;
2122 ID id = prop->value ();
2124 if ((region = region_by_id (id))) {
2128 if (region->set_state (*child, version)) {
2133 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2136 error << _("Playlist: cannot create region from XML") << endmsg;
2140 add_region (region, region->position(), 1.0);
2142 // So that layer_op ordering doesn't get screwed up
2143 region->set_last_layer_op( region->layer());
2148 /* update dependents, which was not done during add_region_internal
2149 due to in_set_state being true
2152 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2153 check_dependents (*r, false);
2156 clear_pending (); // this makes thaw() do nothing
2158 notify_contents_changed ();
2161 first_set_state = false;
2166 Playlist::get_state()
2168 return state (true);
2172 Playlist::get_template()
2174 return state (false);
2177 /** @param full_state true to include regions in the returned state, otherwise false.
2180 Playlist::state (bool full_state)
2182 XMLNode *node = new XMLNode (X_("Playlist"));
2185 node->add_property (X_("id"), id().to_s());
2186 node->add_property (X_("name"), _name);
2187 node->add_property (X_("type"), _type.to_string());
2189 _orig_diskstream_id.print (buf, sizeof (buf));
2190 node->add_property (X_("orig_diskstream_id"), buf);
2191 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2194 RegionLock rlock (this, false);
2195 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2196 node->add_child_nocopy ((*i)->get_state());
2201 node->add_child_copy (*_extra_xml);
2208 Playlist::empty() const
2210 RegionLock rlock (const_cast<Playlist *>(this), false);
2211 return regions.empty();
2215 Playlist::n_regions() const
2217 RegionLock rlock (const_cast<Playlist *>(this), false);
2218 return regions.size();
2222 Playlist::get_maximum_extent () const
2224 RegionLock rlock (const_cast<Playlist *>(this), false);
2225 return _get_maximum_extent ();
2229 Playlist::_get_maximum_extent () const
2231 RegionList::const_iterator i;
2232 framecnt_t max_extent = 0;
2235 for (i = regions.begin(); i != regions.end(); ++i) {
2236 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
2245 Playlist::bump_name (string name, Session &session)
2247 string newname = name;
2250 newname = bump_name_once (newname);
2251 } while (session.playlists->by_name (newname)!=NULL);
2258 Playlist::top_layer() const
2260 RegionLock rlock (const_cast<Playlist *> (this));
2263 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2264 top = max (top, (*i)->layer());
2270 Playlist::set_edit_mode (EditMode mode)
2275 /********************
2277 ********************/
2280 Playlist::relayer ()
2282 bool changed = false;
2284 /* Build up a new list of regions on each layer, stored in a set of lists
2285 each of which represent some period of time on some layer. The idea
2286 is to avoid having to search the entire region list to establish whether
2287 each region overlaps another */
2289 /* how many pieces to divide this playlist's time up into */
2290 int const divisions = 512;
2292 /* find the start and end positions of the regions on this playlist */
2293 framepos_t start = UINT_MAX;
2295 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2296 start = min (start, (*i)->position());
2297 end = max (end, (*i)->position() + (*i)->length());
2300 /* hence the size of each time division */
2301 double const division_size = (end - start) / double (divisions);
2303 vector<vector<RegionList> > layers;
2304 layers.push_back (vector<RegionList> (divisions));
2306 /* we want to go through regions from desired lowest to desired highest layer,
2307 which depends on the layer model
2310 RegionList copy = regions.rlist();
2312 /* sort according to the model and the layering mode that we're in */
2314 if (_explicit_relayering) {
2316 copy.sort (RegionSortByLayerWithPending ());
2318 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2320 copy.sort (RegionSortByLastLayerOp ());
2325 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2327 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2328 (*i)->set_pending_explicit_relayer (false);
2330 /* find the time divisions that this region covers */
2331 int const start_division = floor ( ((*i)->position() - start) / division_size);
2332 int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2333 if (end_division == divisions) {
2337 assert (end_division < divisions);
2339 /* find the lowest layer that this region can go on */
2340 size_t j = layers.size();
2342 /* try layer j - 1; it can go on if it overlaps no other region
2343 that is already on that layer
2346 bool overlap = false;
2347 for (int k = start_division; k <= end_division; ++k) {
2348 RegionList::iterator l = layers[j-1][k].begin ();
2349 while (l != layers[j-1][k].end()) {
2350 if ((*l)->overlap_equivalent (*i)) {
2363 /* overlap, so we must use layer j */
2370 if (j == layers.size()) {
2371 /* we need a new layer for this region */
2372 layers.push_back (vector<RegionList> (divisions));
2375 /* put a reference to this region in each of the divisions that it exists in */
2376 for (int k = start_division; k <= end_division; ++k) {
2377 layers[j][k].push_back (*i);
2380 if ((*i)->layer() != j) {
2384 (*i)->set_layer (j);
2388 notify_layering_changed ();
2392 /* XXX these layer functions are all deprecated */
2395 Playlist::raise_region (boost::shared_ptr<Region> region)
2397 uint32_t rsz = regions.size();
2398 layer_t target = region->layer() + 1U;
2400 if (target >= rsz) {
2401 /* its already at the effective top */
2405 move_region_to_layer (target, region, 1);
2409 Playlist::lower_region (boost::shared_ptr<Region> region)
2411 if (region->layer() == 0) {
2412 /* its already at the bottom */
2416 layer_t target = region->layer() - 1U;
2418 move_region_to_layer (target, region, -1);
2422 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2424 /* does nothing useful if layering mode is later=higher */
2425 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2426 (_session.config.get_layer_model() == AddHigher)) {
2427 timestamp_layer_op (region);
2433 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2435 /* does nothing useful if layering mode is later=higher */
2436 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2437 (_session.config.get_layer_model() == AddHigher)) {
2438 region->set_last_layer_op (0);
2444 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2446 RegionList::iterator i;
2447 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2448 list<LayerInfo> layerinfo;
2451 RegionLock rlock (const_cast<Playlist *> (this));
2453 for (i = regions.begin(); i != regions.end(); ++i) {
2463 /* region is moving up, move all regions on intermediate layers
2467 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2468 dest = (*i)->layer() - 1;
2475 /* region is moving down, move all regions on intermediate layers
2479 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2480 dest = (*i)->layer() + 1;
2490 newpair.second = dest;
2492 layerinfo.push_back (newpair);
2496 /* now reset the layers without holding the region lock */
2498 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2499 x->first->set_layer (x->second);
2502 region->set_layer (target_layer);
2505 /* now check all dependents */
2507 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2508 check_dependents (x->first, false);
2511 check_dependents (region, false);
2518 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2520 RegionList::iterator i;
2526 RegionLock rlock (const_cast<Playlist *> (this));
2528 for (i = regions.begin(); i != regions.end(); ++i) {
2530 if ((*i)->position() >= start) {
2536 if ((*i)->last_frame() > max_frames - distance) {
2537 new_pos = max_frames - (*i)->length();
2539 new_pos = (*i)->position() + distance;
2544 if ((*i)->position() > distance) {
2545 new_pos = (*i)->position() - distance;
2551 (*i)->set_position (new_pos, this);
2559 notify_length_changed ();
2564 boost::shared_ptr<Region>
2565 Playlist::find_region (const ID& id) const
2567 RegionLock rlock (const_cast<Playlist*> (this));
2569 /* searches all regions currently in use by the playlist */
2571 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2572 if ((*i)->id() == id) {
2577 return boost::shared_ptr<Region> ();
2580 boost::shared_ptr<Region>
2581 Playlist::region_by_id (const ID& id)
2583 /* searches all regions ever added to this playlist */
2585 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2586 if ((*i)->id() == id) {
2590 return boost::shared_ptr<Region> ();
2594 Playlist::dump () const
2596 boost::shared_ptr<Region> r;
2598 cerr << "Playlist \"" << _name << "\" " << endl
2599 << regions.size() << " regions "
2602 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2604 cerr << " " << r->name() << " ["
2605 << r->start() << "+" << r->length()
2615 Playlist::set_frozen (bool yn)
2621 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2623 // struct timeval tv;
2624 // gettimeofday (&tv, 0);
2625 region->set_last_layer_op (++layer_op_counter);
2630 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2634 if (region->locked()) {
2641 RegionLock rlock (const_cast<Playlist*> (this));
2646 RegionList::iterator next;
2648 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2649 if ((*i) == region) {
2653 if (next != regions.end()) {
2655 if ((*next)->locked()) {
2661 if ((*next)->position() != region->last_frame() + 1) {
2662 /* they didn't used to touch, so after shuffle,
2663 just have them swap positions.
2665 new_pos = (*next)->position();
2667 /* they used to touch, so after shuffle,
2668 make sure they still do. put the earlier
2669 region where the later one will end after
2672 new_pos = region->position() + (*next)->length();
2675 (*next)->set_position (region->position(), this);
2676 region->set_position (new_pos, this);
2678 /* avoid a full sort */
2680 regions.erase (i); // removes the region from the list */
2682 regions.insert (next, region); // adds it back after next
2691 RegionList::iterator prev = regions.end();
2693 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2694 if ((*i) == region) {
2696 if (prev != regions.end()) {
2698 if ((*prev)->locked()) {
2703 if (region->position() != (*prev)->last_frame() + 1) {
2704 /* they didn't used to touch, so after shuffle,
2705 just have them swap positions.
2707 new_pos = region->position();
2709 /* they used to touch, so after shuffle,
2710 make sure they still do. put the earlier
2711 one where the later one will end after
2713 new_pos = (*prev)->position() + region->length();
2716 region->set_position ((*prev)->position(), this);
2717 (*prev)->set_position (new_pos, this);
2719 /* avoid a full sort */
2721 regions.erase (i); // remove region
2722 regions.insert (prev, region); // insert region before prev
2738 check_dependents (region, false);
2740 notify_contents_changed();
2746 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2748 RegionLock rlock (const_cast<Playlist*> (this));
2750 if (regions.size() > 1) {
2758 Playlist::update_after_tempo_map_change ()
2760 RegionLock rlock (const_cast<Playlist*> (this));
2761 RegionList copy (regions.rlist());
2765 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2766 (*i)->update_position_after_tempo_map_change ();
2773 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2775 RegionLock rl (this, false);
2776 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2782 Playlist::set_explicit_relayering (bool e)
2784 if (e == false && _explicit_relayering == true) {
2786 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2787 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2788 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2789 at this point would keep regions on the same layers.
2791 From then on in, it's just you and your towel.
2794 RegionLock rl (this);
2795 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2796 (*i)->set_last_layer_op ((*i)->layer ());
2800 _explicit_relayering = e;
2805 Playlist::has_region_at (framepos_t const p) const
2807 RegionLock (const_cast<Playlist *> (this));
2809 RegionList::const_iterator i = regions.begin ();
2810 while (i != regions.end() && !(*i)->covers (p)) {
2814 return (i != regions.end());