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 <sigc++/bind.h>
30 #include <pbd/failed_constructor.h>
31 #include <pbd/stl_delete.h>
32 #include <pbd/xml++.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>
43 using namespace ARDOUR;
46 struct ShowMeTheList {
47 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
49 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
51 boost::shared_ptr<Playlist> playlist;
55 struct RegionSortByLayer {
56 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
57 return a->layer() < b->layer();
61 struct RegionSortByPosition {
62 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
63 return a->position() < b->position();
67 struct RegionSortByLastLayerOp {
68 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
69 return a->last_layer_op() < b->last_layer_op();
73 Playlist::Playlist (Session& sess, string nom, bool hide)
77 first_set_state = false;
82 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
86 _name = "unnamed"; /* reset by set_state */
88 /* set state called by derived class */
91 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
92 : _name (namestr), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
97 other->copy_regions (tmp);
101 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
102 add_region_internal( (*x), (*x)->position());
107 _splicing = other->_splicing;
108 _nudging = other->_nudging;
109 _edit_mode = other->_edit_mode;
112 first_set_state = false;
114 in_partition = false;
116 _read_data_count = 0;
117 _frozen = other->_frozen;
119 layer_op_counter = other->layer_op_counter;
120 freeze_length = other->freeze_length;
123 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
124 : _name (str), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
126 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
128 nframes_t end = start + cnt - 1;
134 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
136 boost::shared_ptr<Region> region;
137 boost::shared_ptr<Region> new_region;
138 nframes_t offset = 0;
139 nframes_t position = 0;
146 overlap = region->coverage (start, end);
152 case OverlapInternal:
153 offset = start - region->position();
160 position = region->position() - start;
161 len = end - region->position();
165 offset = start - region->position();
167 len = region->length() - offset;
170 case OverlapExternal:
172 position = region->position() - start;
173 len = region->length();
177 _session.region_name (new_name, region->name(), false);
179 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
181 add_region_internal (new_region, position);
185 first_set_state = false;
187 /* this constructor does NOT notify others (session) */
194 InUse (true); /* EMIT SIGNAL */
205 InUse (false); /* EMIT SIGNAL */
210 Playlist::copy_regions (RegionList& newlist) const
212 RegionLock rlock (const_cast<Playlist *> (this));
214 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
215 newlist.push_back (RegionFactory::RegionFactory::create (*i));
220 Playlist::init (bool hide)
222 g_atomic_int_set (&block_notifications, 0);
223 g_atomic_int_set (&ignore_state_changes, 0);
224 pending_modified = false;
225 pending_length = false;
226 first_set_state = true;
232 _edit_mode = Config->get_edit_mode();
234 in_partition = false;
236 _read_data_count = 0;
238 layer_op_counter = 0;
241 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
244 Playlist::Playlist (const Playlist& pl)
245 : _session (pl._session)
247 fatal << _("playlist const copy constructor called") << endmsg;
250 Playlist::Playlist (Playlist& pl)
251 : _session (pl._session)
253 fatal << _("playlist non-const copy constructor called") << endmsg;
256 Playlist::~Playlist ()
259 RegionLock rl (this);
261 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
262 (*i)->set_playlist (boost::shared_ptr<Playlist>());
266 /* GoingAway must be emitted by derived classes */
270 Playlist::set_name (string str)
272 /* in a typical situation, a playlist is being used
273 by one diskstream and also is referenced by the
274 Session. if there are more references than that,
275 then don't change the name.
283 NameChanged(); /* EMIT SIGNAL */
286 /***********************************************************************
287 CHANGE NOTIFICATION HANDLING
289 Notifications must be delayed till the region_lock is released. This
290 is necessary because handlers for the signals may need to acquire
291 the lock (e.g. to read from the playlist).
292 ***********************************************************************/
297 delay_notifications ();
298 g_atomic_int_inc (&ignore_state_changes);
304 g_atomic_int_dec_and_test (&ignore_state_changes);
305 release_notifications ();
310 Playlist::delay_notifications ()
312 g_atomic_int_inc (&block_notifications);
313 freeze_length = _get_maximum_extent();
317 Playlist::release_notifications ()
319 if (g_atomic_int_dec_and_test (&block_notifications)) {
320 flush_notifications ();
326 Playlist::notify_modified ()
328 if (holding_state ()) {
329 pending_modified = true;
331 pending_modified = false;
332 Modified(); /* EMIT SIGNAL */
337 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
339 if (holding_state ()) {
340 pending_removes.insert (r);
341 pending_modified = true;
342 pending_length = true;
344 /* this might not be true, but we have to act
345 as though it could be.
347 LengthChanged (); /* EMIT SIGNAL */
348 Modified (); /* EMIT SIGNAL */
353 Playlist::notify_region_added (boost::shared_ptr<Region> r)
355 /* the length change might not be true, but we have to act
356 as though it could be.
359 if (holding_state()) {
360 pending_adds.insert (r);
361 pending_modified = true;
362 pending_length = true;
364 LengthChanged (); /* EMIT SIGNAL */
365 Modified (); /* EMIT SIGNAL */
370 Playlist::notify_length_changed ()
372 if (holding_state ()) {
373 pending_length = true;
375 LengthChanged(); /* EMIT SIGNAL */
376 Modified (); /* EMIT SIGNAL */
381 Playlist::flush_notifications ()
383 set<boost::shared_ptr<Region> > dependent_checks_needed;
384 set<boost::shared_ptr<Region> >::iterator s;
393 /* we have no idea what order the regions ended up in pending
394 bounds (it could be based on selection order, for example).
395 so, to preserve layering in the "most recently moved is higher"
396 model, sort them by existing layer, then timestamp them.
399 // RegionSortByLayer cmp;
400 // pending_bounds.sort (cmp);
402 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
403 if (Config->get_layer_model() == MoveAddHigher) {
404 timestamp_layer_op (*r);
406 pending_length = true;
407 dependent_checks_needed.insert (*r);
411 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
412 dependent_checks_needed.insert (*s);
416 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
417 remove_dependents (*s);
421 if ((freeze_length != _get_maximum_extent()) || pending_length) {
423 LengthChanged(); /* EMIT SIGNAL */
427 if (n || pending_modified) {
432 pending_modified = false;
433 Modified (); /* EMIT SIGNAL */
436 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
437 check_dependents (*s, false);
440 pending_adds.clear ();
441 pending_removes.clear ();
442 pending_bounds.clear ();
447 /*************************************************************
449 *************************************************************/
452 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
454 RegionLock rlock (this);
456 times = fabs (times);
458 int itimes = (int) floor (times);
460 nframes_t pos = position;
463 add_region_internal (region, pos);
464 pos += region->length();
468 /* later regions will all be spliced anyway */
470 if (!holding_state ()) {
471 possibly_splice_unlocked ();
474 /* note that itimes can be zero if we being asked to just
475 insert a single fraction of the region.
478 for (int i = 0; i < itimes; ++i) {
479 boost::shared_ptr<Region> copy = RegionFactory::create (region);
480 add_region_internal (copy, pos);
481 pos += region->length();
484 if (floor (times) != times) {
485 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
487 _session.region_name (name, region->name(), false);
488 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
489 add_region_internal (sub, pos);
494 Playlist::set_region_ownership ()
496 RegionLock rl (this);
497 RegionList::iterator i;
498 boost::weak_ptr<Playlist> pl (shared_from_this());
500 for (i = regions.begin(); i != regions.end(); ++i) {
501 (*i)->set_playlist (pl);
506 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
508 RegionSortByPosition cmp;
509 nframes_t old_length = 0;
511 if (!holding_state()) {
512 old_length = _get_maximum_extent();
515 if (!first_set_state) {
516 boost::shared_ptr<Playlist> foo (shared_from_this());
517 region->set_playlist (boost::weak_ptr<Playlist>(foo));
520 region->set_position (position, this);
522 timestamp_layer_op (region);
524 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
525 all_regions.insert (region);
527 if (!holding_state () && !in_set_state) {
528 /* layers get assigned from XML state */
532 /* we need to notify the existence of new region before checking dependents. Ick. */
534 notify_region_added (region);
536 if (!holding_state ()) {
537 check_dependents (region, false);
538 if (old_length != _get_maximum_extent()) {
539 notify_length_changed ();
543 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
544 boost::weak_ptr<Region> (region)));
548 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
550 RegionLock rlock (this);
552 remove_region_internal (old);
553 add_region_internal (newr, pos);
555 if (!holding_state ()) {
556 possibly_splice_unlocked ();
561 Playlist::remove_region (boost::shared_ptr<Region> region)
563 RegionLock rlock (this);
564 remove_region_internal (region);
566 if (!holding_state ()) {
567 possibly_splice_unlocked ();
572 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
574 RegionList::iterator i;
575 nframes_t old_length = 0;
577 if (!holding_state()) {
578 old_length = _get_maximum_extent();
583 region->set_playlist (boost::weak_ptr<Playlist>());
586 for (i = regions.begin(); i != regions.end(); ++i) {
591 if (!holding_state ()) {
593 remove_dependents (region);
595 if (old_length != _get_maximum_extent()) {
596 notify_length_changed ();
600 notify_region_removed (region);
608 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
610 if (Config->get_use_overlap_equivalency()) {
611 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
612 if ((*i)->overlap_equivalent (other)) {
613 results.push_back ((*i));
617 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
618 if ((*i)->equivalent (other)) {
619 results.push_back ((*i));
626 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
628 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
630 if ((*i) && (*i)->region_list_equivalent (other)) {
631 results.push_back (*i);
637 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
641 partition_internal (start, end, false, thawlist);
643 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
644 (*i)->thaw ("separation");
649 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
651 RegionLock rlock (this);
652 boost::shared_ptr<Region> region;
653 boost::shared_ptr<Region> current;
655 RegionList::iterator tmp;
657 nframes_t pos1, pos2, pos3, pos4;
658 RegionList new_regions;
662 /* need to work from a copy, because otherwise the regions we add during the process
663 get operated on as well.
666 RegionList copy = regions;
668 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
675 if (current->first_frame() == start && current->last_frame() == end) {
677 remove_region_internal (current);
682 if ((overlap = current->coverage (start, end)) == OverlapNone) {
686 pos1 = current->position();
689 pos4 = current->last_frame();
691 if (overlap == OverlapInternal) {
693 /* split: we need 3 new regions, the front, middle and end.
694 cut: we need 2 regions, the front and end.
699 ---------------*************************------------
702 ---------------*****++++++++++++++++====------------
704 ---------------*****----------------====------------
710 /* "middle" ++++++ */
712 _session.region_name (new_name, current->name(), false);
713 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
714 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
715 add_region_internal (region, start);
716 new_regions.push_back (region);
721 _session.region_name (new_name, current->name(), false);
722 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
723 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
725 add_region_internal (region, end);
726 new_regions.push_back (region);
731 thawlist.push_back (current);
732 current->trim_end (pos2, this);
734 } else if (overlap == OverlapEnd) {
738 ---------------*************************------------
741 ---------------**************+++++++++++------------
743 ---------------**************-----------------------
751 _session.region_name (new_name, current->name(), false);
752 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
753 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
754 add_region_internal (region, start);
755 new_regions.push_back (region);
761 thawlist.push_back (current);
762 current->trim_end (pos2, this);
764 } else if (overlap == OverlapStart) {
766 /* split: we need 2 regions: the front and the end.
767 cut: just trim current to skip the cut area
772 ---------------*************************------------
776 ---------------****+++++++++++++++++++++------------
778 -------------------*********************------------
785 _session.region_name (new_name, current->name(), false);
786 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
787 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
788 add_region_internal (region, pos1);
789 new_regions.push_back (region);
795 thawlist.push_back (current);
796 current->trim_front (pos3, this);
798 } else if (overlap == OverlapExternal) {
800 /* split: no split required.
801 cut: remove the region.
806 ---------------*************************------------
810 ---------------*************************------------
812 ----------------------------------------------------
817 remove_region_internal (current);
819 new_regions.push_back (current);
823 in_partition = false;
825 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
826 check_dependents (*i, false);
830 boost::shared_ptr<Playlist>
831 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
833 boost::shared_ptr<Playlist> ret;
834 boost::shared_ptr<Playlist> pl;
837 if (ranges.empty()) {
838 return boost::shared_ptr<Playlist>();
841 start = ranges.front().start;
843 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
845 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
847 if (i == ranges.begin()) {
851 /* paste the next section into the nascent playlist,
852 offset to reflect the start of the first range we
856 ret->paste (pl, (*i).start - start, 1.0f);
863 boost::shared_ptr<Playlist>
864 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
866 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
867 return cut_copy (pmf, ranges, result_is_hidden);
870 boost::shared_ptr<Playlist>
871 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
873 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
874 return cut_copy (pmf, ranges, result_is_hidden);
877 boost::shared_ptr<Playlist>
878 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
880 boost::shared_ptr<Playlist> the_copy;
884 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
885 string new_name = _name;
889 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
890 return boost::shared_ptr<Playlist>();
893 partition_internal (start, start+cnt-1, true, thawlist);
896 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
897 (*i)->thaw ("playlist cut");
903 boost::shared_ptr<Playlist>
904 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
908 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
909 string new_name = _name;
913 cnt = min (_get_maximum_extent() - start, cnt);
914 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
918 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
920 times = fabs (times);
921 nframes_t old_length;
924 RegionLock rl1 (this);
925 RegionLock rl2 (other.get());
927 old_length = _get_maximum_extent();
929 int itimes = (int) floor (times);
930 nframes_t pos = position;
931 nframes_t shift = other->_get_maximum_extent();
932 layer_t top_layer = regions.size();
935 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
936 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
938 /* put these new regions on top of all existing ones, but preserve
939 the ordering they had in the original playlist.
942 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
943 add_region_internal (copy_of_region, copy_of_region->position() + pos);
948 possibly_splice_unlocked ();
950 /* XXX shall we handle fractional cases at some point? */
952 if (old_length != _get_maximum_extent()) {
953 notify_length_changed ();
964 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
966 times = fabs (times);
968 RegionLock rl (this);
969 int itimes = (int) floor (times);
970 nframes_t pos = position;
973 boost::shared_ptr<Region> copy = RegionFactory::create (region);
974 add_region_internal (copy, pos);
975 pos += region->length();
978 if (floor (times) != times) {
979 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
981 _session.region_name (name, region->name(), false);
982 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
983 add_region_internal (sub, pos);
988 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
990 RegionLock rl (this);
992 if (!region->covers (playlist_position)) {
996 if (region->position() == playlist_position ||
997 region->last_frame() == playlist_position) {
1001 boost::shared_ptr<Region> left;
1002 boost::shared_ptr<Region> right;
1008 before = playlist_position - region->position();
1009 after = region->length() - before;
1012 _session.region_name (before_name, region->name(), false);
1013 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1015 _session.region_name (after_name, region->name(), false);
1016 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1018 add_region_internal (left, region->position());
1019 add_region_internal (right, region->position() + before);
1021 uint64_t orig_layer_op = region->last_layer_op();
1022 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1023 if ((*i)->last_layer_op() > orig_layer_op) {
1024 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1028 left->set_last_layer_op ( orig_layer_op );
1029 right->set_last_layer_op ( orig_layer_op + 1);
1033 finalize_split_region (region, left, right);
1035 if (remove_region_internal (region)) {
1041 Playlist::possibly_splice ()
1043 if (_edit_mode == Splice) {
1049 Playlist::possibly_splice_unlocked ()
1051 if (_edit_mode == Splice) {
1057 Playlist::splice_locked ()
1060 RegionLock rl (this);
1064 notify_length_changed ();
1068 Playlist::splice_unlocked ()
1071 notify_length_changed ();
1075 Playlist::core_splice ()
1079 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1081 RegionList::iterator next;
1086 if (next == regions.end()) {
1090 (*next)->set_position ((*i)->last_frame() + 1, this);
1097 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1099 if (in_set_state || _splicing || _nudging) {
1103 if (what_changed & ARDOUR::PositionChanged) {
1105 /* remove it from the list then add it back in
1106 the right place again.
1109 RegionSortByPosition cmp;
1111 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1113 if (i == regions.end()) {
1114 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1115 _name, region->name())
1121 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1125 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1126 if (holding_state ()) {
1127 pending_bounds.push_back (region);
1129 if (Config->get_layer_model() == MoveAddHigher) {
1130 /* it moved or changed length, so change the timestamp */
1131 timestamp_layer_op (region);
1135 notify_length_changed ();
1137 check_dependents (region, false);
1143 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1145 boost::shared_ptr<Region> region (weak_region.lock());
1152 /* this makes a virtual call to the right kind of playlist ... */
1154 region_changed (what_changed, region);
1158 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1160 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1163 if (in_set_state || in_flush) {
1168 if (what_changed & BoundsChanged) {
1169 region_bounds_changed (what_changed, region);
1170 save = !(_splicing || _nudging);
1173 if ((what_changed & our_interests) &&
1174 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1175 check_dependents (region, false);
1178 if (what_changed & our_interests) {
1187 Playlist::drop_regions ()
1189 RegionLock rl (this);
1191 all_regions.clear ();
1195 Playlist::clear (bool with_signals)
1198 RegionLock rl (this);
1199 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1200 pending_removes.insert (*i);
1212 /***********************************************************************
1214 **********************************************************************/
1216 Playlist::RegionList *
1217 Playlist::regions_at (nframes_t frame)
1220 RegionLock rlock (this);
1221 return find_regions_at (frame);
1224 boost::shared_ptr<Region>
1225 Playlist::top_region_at (nframes_t frame)
1228 RegionLock rlock (this);
1229 RegionList *rlist = find_regions_at (frame);
1230 boost::shared_ptr<Region> region;
1232 if (rlist->size()) {
1233 RegionSortByLayer cmp;
1235 region = rlist->back();
1242 Playlist::RegionList*
1243 Playlist::regions_to_read (nframes_t start, nframes_t end)
1245 /* Caller must hold lock */
1247 RegionList covering;
1248 set<nframes_t> to_check;
1249 set<boost::shared_ptr<Region> > unique;
1252 to_check.insert (start);
1253 to_check.insert (end);
1255 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1257 /* find all/any regions that span start+end */
1259 switch ((*i)->coverage (start, end)) {
1263 case OverlapInternal:
1264 covering.push_back (*i);
1268 to_check.insert ((*i)->position());
1269 covering.push_back (*i);
1273 to_check.insert ((*i)->last_frame());
1274 covering.push_back (*i);
1277 case OverlapExternal:
1278 covering.push_back (*i);
1279 to_check.insert ((*i)->position());
1280 to_check.insert ((*i)->last_frame());
1284 /* don't go too far */
1286 if ((*i)->position() > end) {
1291 RegionList* rlist = new RegionList;
1293 /* find all the regions that cover each position .... */
1295 if (covering.size() == 1) {
1297 rlist->push_back (covering.front());
1301 for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1305 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1307 if ((*x)->covers (*t)) {
1308 here.push_back (*x);
1312 RegionSortByLayer cmp;
1315 /* ... and get the top/transparent regions at "here" */
1317 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1321 if ((*c)->opaque()) {
1323 /* the other regions at this position are hidden by this one */
1330 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1331 rlist->push_back (*s);
1334 if (rlist->size() > 1) {
1335 /* now sort by time order */
1337 RegionSortByPosition cmp;
1345 Playlist::RegionList *
1346 Playlist::find_regions_at (nframes_t frame)
1348 /* Caller must hold lock */
1350 RegionList *rlist = new RegionList;
1352 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1353 if ((*i)->covers (frame)) {
1354 rlist->push_back (*i);
1361 Playlist::RegionList *
1362 Playlist::regions_touched (nframes_t start, nframes_t end)
1364 RegionLock rlock (this);
1365 RegionList *rlist = new RegionList;
1367 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1368 if ((*i)->coverage (start, end) != OverlapNone) {
1369 rlist->push_back (*i);
1377 boost::shared_ptr<Region>
1378 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1380 RegionLock rlock (this);
1381 boost::shared_ptr<Region> ret;
1382 nframes_t closest = max_frames;
1385 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1388 boost::shared_ptr<Region> r = (*i);
1393 pos = r->first_frame ();
1396 pos = r->last_frame ();
1399 pos = r->adjust_to_sync (r->first_frame());
1404 case 1: /* forwards */
1407 if ((distance = pos - frame) < closest) {
1415 default: /* backwards */
1418 if ((distance = frame - pos) < closest) {
1430 /***********************************************************************/
1435 Playlist::mark_session_dirty ()
1437 if (!in_set_state && !holding_state ()) {
1438 _session.set_dirty();
1443 Playlist::set_state (const XMLNode& node)
1447 XMLNodeConstIterator niter;
1448 XMLPropertyList plist;
1449 XMLPropertyConstIterator piter;
1451 boost::shared_ptr<Region> region;
1456 if (node.name() != "Playlist") {
1463 plist = node.properties();
1465 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1469 if (prop->name() == X_("name")) {
1470 _name = prop->value();
1471 } else if (prop->name() == X_("orig_diskstream_id")) {
1472 _orig_diskstream_id = prop->value ();
1473 } else if (prop->name() == X_("frozen")) {
1474 _frozen = (prop->value() == X_("yes"));
1480 nlist = node.children();
1482 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1486 if (child->name() == "Region") {
1488 if ((prop = child->property ("id")) == 0) {
1489 error << _("region state node has no ID, ignored") << endmsg;
1493 ID id = prop->value ();
1495 if ((region = region_by_id (id))) {
1497 Change what_changed = Change (0);
1499 if (region->set_live_state (*child, what_changed, true)) {
1500 error << _("Playlist: cannot reset region state from XML") << endmsg;
1504 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1505 error << _("Playlist: cannot create region from XML") << endmsg;
1509 add_region (region, region->position(), 1.0);
1511 // So that layer_op ordering doesn't get screwed up
1512 region->set_last_layer_op( region->layer());
1521 /* update dependents, which was not done during add_region_internal
1522 due to in_set_state being true
1525 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1526 check_dependents (*r, false);
1530 first_set_state = false;
1535 Playlist::get_state()
1541 Playlist::get_template()
1543 return state(false);
1547 Playlist::state (bool full_state)
1549 XMLNode *node = new XMLNode (X_("Playlist"));
1552 node->add_property (X_("name"), _name);
1554 _orig_diskstream_id.print (buf, sizeof (buf));
1555 node->add_property (X_("orig_diskstream_id"), buf);
1556 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1559 RegionLock rlock (this, false);
1560 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1561 node->add_child_nocopy ((*i)->get_state());
1566 node->add_child_copy (*_extra_xml);
1573 Playlist::empty() const
1575 RegionLock rlock (const_cast<Playlist *>(this), false);
1576 return regions.empty();
1580 Playlist::n_regions() const
1582 RegionLock rlock (const_cast<Playlist *>(this), false);
1583 return regions.size();
1587 Playlist::get_maximum_extent () const
1589 RegionLock rlock (const_cast<Playlist *>(this), false);
1590 return _get_maximum_extent ();
1594 Playlist::_get_maximum_extent () const
1596 RegionList::const_iterator i;
1597 nframes_t max_extent = 0;
1600 for (i = regions.begin(); i != regions.end(); ++i) {
1601 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1610 Playlist::bump_name (string name, Session &session)
1612 string newname = name;
1615 newname = Playlist::bump_name_once (newname);
1616 } while (session.playlist_by_name (newname)!=NULL);
1622 Playlist::bump_name_once (string name)
1624 string::size_type period;
1627 if ((period = name.find_last_of ('.')) == string::npos) {
1632 const char *last_element = name.c_str() + period + 1;
1633 for (size_t i = 0; i < strlen(last_element); i++) {
1634 if (!isdigit(last_element[i])) {
1641 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1643 if (isnumber == 0 || errno != 0) {
1644 // last_element is not a number, or is too large
1650 snprintf (buf, sizeof(buf), "%ld", version+1);
1652 newname = name.substr (0, period+1);
1661 Playlist::top_layer() const
1663 RegionLock rlock (const_cast<Playlist *> (this));
1666 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1667 top = max (top, (*i)->layer());
1673 Playlist::set_edit_mode (EditMode mode)
1678 /********************
1680 ********************/
1683 Playlist::relayer ()
1685 RegionList::iterator i;
1688 /* don't send multiple Modified notifications
1689 when multiple regions are relayered.
1694 if (Config->get_layer_model() == MoveAddHigher ||
1695 Config->get_layer_model() == AddHigher) {
1697 RegionSortByLastLayerOp cmp;
1698 RegionList copy = regions;
1702 for (i = copy.begin(); i != copy.end(); ++i) {
1703 (*i)->set_layer (layer++);
1708 /* Session::LaterHigher model */
1710 for (i = regions.begin(); i != regions.end(); ++i) {
1711 (*i)->set_layer (layer++);
1715 /* sending Modified means that various kinds of layering
1716 models operate correctly at the GUI
1717 level. slightly inefficient, but only slightly.
1719 We force a Modified signal here in case no layers actually
1728 /* XXX these layer functions are all deprecated */
1731 Playlist::raise_region (boost::shared_ptr<Region> region)
1733 uint32_t rsz = regions.size();
1734 layer_t target = region->layer() + 1U;
1736 if (target >= rsz) {
1737 /* its already at the effective top */
1741 move_region_to_layer (target, region, 1);
1745 Playlist::lower_region (boost::shared_ptr<Region> region)
1747 if (region->layer() == 0) {
1748 /* its already at the bottom */
1752 layer_t target = region->layer() - 1U;
1754 move_region_to_layer (target, region, -1);
1758 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1760 /* does nothing useful if layering mode is later=higher */
1761 if ((Config->get_layer_model() == MoveAddHigher) ||
1762 (Config->get_layer_model() == AddHigher)) {
1763 timestamp_layer_op (region);
1769 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1771 /* does nothing useful if layering mode is later=higher */
1772 if ((Config->get_layer_model() == MoveAddHigher) ||
1773 (Config->get_layer_model() == AddHigher)) {
1774 region->set_last_layer_op (0);
1780 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1782 RegionList::iterator i;
1783 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1784 list<LayerInfo> layerinfo;
1788 RegionLock rlock (const_cast<Playlist *> (this));
1790 for (i = regions.begin(); i != regions.end(); ++i) {
1798 /* region is moving up, move all regions on intermediate layers
1802 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1803 dest = (*i)->layer() - 1;
1810 /* region is moving down, move all regions on intermediate layers
1814 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1815 dest = (*i)->layer() + 1;
1825 newpair.second = dest;
1827 layerinfo.push_back (newpair);
1831 /* now reset the layers without holding the region lock */
1833 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1834 x->first->set_layer (x->second);
1837 region->set_layer (target_layer);
1840 /* now check all dependents */
1842 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1843 check_dependents (x->first, false);
1846 check_dependents (region, false);
1853 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1855 RegionList::iterator i;
1862 RegionLock rlock (const_cast<Playlist *> (this));
1864 for (i = regions.begin(); i != regions.end(); ++i) {
1866 if ((*i)->position() >= start) {
1870 if ((*i)->last_frame() > max_frames - distance) {
1871 new_pos = max_frames - (*i)->length();
1873 new_pos = (*i)->position() + distance;
1878 if ((*i)->position() > distance) {
1879 new_pos = (*i)->position() - distance;
1885 (*i)->set_position (new_pos, this);
1893 notify_length_changed ();
1898 boost::shared_ptr<Region>
1899 Playlist::find_region (const ID& id) const
1901 RegionLock rlock (const_cast<Playlist*> (this));
1903 /* searches all regions currently in use by the playlist */
1905 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1906 if ((*i)->id() == id) {
1911 return boost::shared_ptr<Region> ();
1914 boost::shared_ptr<Region>
1915 Playlist::region_by_id (ID id)
1917 /* searches all regions ever added to this playlist */
1919 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1920 if ((*i)->id() == id) {
1924 return boost::shared_ptr<Region> ();
1928 Playlist::dump () const
1930 boost::shared_ptr<Region> r;
1932 cerr << "Playlist \"" << _name << "\" " << endl
1933 << regions.size() << " regions "
1936 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1938 cerr << " " << r->name() << " ["
1939 << r->start() << "+" << r->length()
1949 Playlist::set_frozen (bool yn)
1955 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1957 // struct timeval tv;
1958 // gettimeofday (&tv, 0);
1959 region->set_last_layer_op (++layer_op_counter);