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::find_regions_at (nframes_t frame)
1245 RegionList *rlist = new RegionList;
1247 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1248 if ((*i)->covers (frame)) {
1249 rlist->push_back (*i);
1256 Playlist::RegionList *
1257 Playlist::regions_touched (nframes_t start, nframes_t end)
1259 RegionLock rlock (this);
1260 RegionList *rlist = new RegionList;
1262 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1263 if ((*i)->coverage (start, end) != OverlapNone) {
1264 rlist->push_back (*i);
1272 boost::shared_ptr<Region>
1273 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1275 RegionLock rlock (this);
1276 boost::shared_ptr<Region> ret;
1277 nframes_t closest = max_frames;
1280 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1283 boost::shared_ptr<Region> r = (*i);
1288 pos = r->first_frame ();
1291 pos = r->last_frame ();
1294 pos = r->adjust_to_sync (r->first_frame());
1299 case 1: /* forwards */
1302 if ((distance = pos - frame) < closest) {
1310 default: /* backwards */
1313 if ((distance = frame - pos) < closest) {
1325 /***********************************************************************/
1330 Playlist::mark_session_dirty ()
1332 if (!in_set_state && !holding_state ()) {
1333 _session.set_dirty();
1338 Playlist::set_state (const XMLNode& node)
1342 XMLNodeConstIterator niter;
1343 XMLPropertyList plist;
1344 XMLPropertyConstIterator piter;
1346 boost::shared_ptr<Region> region;
1351 if (node.name() != "Playlist") {
1358 plist = node.properties();
1360 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1364 if (prop->name() == X_("name")) {
1365 _name = prop->value();
1366 } else if (prop->name() == X_("orig_diskstream_id")) {
1367 _orig_diskstream_id = prop->value ();
1368 } else if (prop->name() == X_("frozen")) {
1369 _frozen = (prop->value() == X_("yes"));
1375 nlist = node.children();
1377 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1381 if (child->name() == "Region") {
1383 if ((prop = child->property ("id")) == 0) {
1384 error << _("region state node has no ID, ignored") << endmsg;
1388 ID id = prop->value ();
1390 if ((region = region_by_id (id))) {
1392 Change what_changed = Change (0);
1394 if (region->set_live_state (*child, what_changed, true)) {
1395 error << _("Playlist: cannot reset region state from XML") << endmsg;
1399 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1400 error << _("Playlist: cannot create region from XML") << endmsg;
1404 add_region (region, region->position(), 1.0);
1406 // So that layer_op ordering doesn't get screwed up
1407 region->set_last_layer_op( region->layer());
1416 /* update dependents, which was not done during add_region_internal
1417 due to in_set_state being true
1420 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1421 check_dependents (*r, false);
1425 first_set_state = false;
1430 Playlist::get_state()
1436 Playlist::get_template()
1438 return state(false);
1442 Playlist::state (bool full_state)
1444 XMLNode *node = new XMLNode (X_("Playlist"));
1447 node->add_property (X_("name"), _name);
1449 _orig_diskstream_id.print (buf, sizeof (buf));
1450 node->add_property (X_("orig_diskstream_id"), buf);
1451 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1454 RegionLock rlock (this, false);
1455 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1456 node->add_child_nocopy ((*i)->get_state());
1461 node->add_child_copy (*_extra_xml);
1468 Playlist::empty() const
1470 RegionLock rlock (const_cast<Playlist *>(this), false);
1471 return regions.empty();
1475 Playlist::n_regions() const
1477 RegionLock rlock (const_cast<Playlist *>(this), false);
1478 return regions.size();
1482 Playlist::get_maximum_extent () const
1484 RegionLock rlock (const_cast<Playlist *>(this), false);
1485 return _get_maximum_extent ();
1489 Playlist::_get_maximum_extent () const
1491 RegionList::const_iterator i;
1492 nframes_t max_extent = 0;
1495 for (i = regions.begin(); i != regions.end(); ++i) {
1496 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1505 Playlist::bump_name (string name, Session &session)
1507 string newname = name;
1510 newname = Playlist::bump_name_once (newname);
1511 } while (session.playlist_by_name (newname)!=NULL);
1517 Playlist::bump_name_once (string name)
1519 string::size_type period;
1522 if ((period = name.find_last_of ('.')) == string::npos) {
1527 const char *last_element = name.c_str() + period + 1;
1528 for (size_t i = 0; i < strlen(last_element); i++) {
1529 if (!isdigit(last_element[i])) {
1536 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1538 if (isnumber == 0 || errno != 0) {
1539 // last_element is not a number, or is too large
1545 snprintf (buf, sizeof(buf), "%ld", version+1);
1547 newname = name.substr (0, period+1);
1556 Playlist::top_layer() const
1558 RegionLock rlock (const_cast<Playlist *> (this));
1561 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1562 top = max (top, (*i)->layer());
1568 Playlist::set_edit_mode (EditMode mode)
1573 /********************
1575 ********************/
1578 Playlist::relayer ()
1580 RegionList::iterator i;
1583 /* don't send multiple Modified notifications
1584 when multiple regions are relayered.
1589 if (Config->get_layer_model() == MoveAddHigher ||
1590 Config->get_layer_model() == AddHigher) {
1592 RegionSortByLastLayerOp cmp;
1593 RegionList copy = regions;
1597 for (i = copy.begin(); i != copy.end(); ++i) {
1598 (*i)->set_layer (layer++);
1603 /* Session::LaterHigher model */
1605 for (i = regions.begin(); i != regions.end(); ++i) {
1606 (*i)->set_layer (layer++);
1610 /* sending Modified means that various kinds of layering
1611 models operate correctly at the GUI
1612 level. slightly inefficient, but only slightly.
1614 We force a Modified signal here in case no layers actually
1623 /* XXX these layer functions are all deprecated */
1626 Playlist::raise_region (boost::shared_ptr<Region> region)
1628 uint32_t rsz = regions.size();
1629 layer_t target = region->layer() + 1U;
1631 if (target >= rsz) {
1632 /* its already at the effective top */
1636 move_region_to_layer (target, region, 1);
1640 Playlist::lower_region (boost::shared_ptr<Region> region)
1642 if (region->layer() == 0) {
1643 /* its already at the bottom */
1647 layer_t target = region->layer() - 1U;
1649 move_region_to_layer (target, region, -1);
1653 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1655 /* does nothing useful if layering mode is later=higher */
1656 if ((Config->get_layer_model() == MoveAddHigher) ||
1657 (Config->get_layer_model() == AddHigher)) {
1658 timestamp_layer_op (region);
1664 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1666 /* does nothing useful if layering mode is later=higher */
1667 if ((Config->get_layer_model() == MoveAddHigher) ||
1668 (Config->get_layer_model() == AddHigher)) {
1669 region->set_last_layer_op (0);
1675 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1677 RegionList::iterator i;
1678 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1679 list<LayerInfo> layerinfo;
1683 RegionLock rlock (const_cast<Playlist *> (this));
1685 for (i = regions.begin(); i != regions.end(); ++i) {
1693 /* region is moving up, move all regions on intermediate layers
1697 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1698 dest = (*i)->layer() - 1;
1705 /* region is moving down, move all regions on intermediate layers
1709 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1710 dest = (*i)->layer() + 1;
1720 newpair.second = dest;
1722 layerinfo.push_back (newpair);
1726 /* now reset the layers without holding the region lock */
1728 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1729 x->first->set_layer (x->second);
1732 region->set_layer (target_layer);
1735 /* now check all dependents */
1737 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1738 check_dependents (x->first, false);
1741 check_dependents (region, false);
1748 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1750 RegionList::iterator i;
1757 RegionLock rlock (const_cast<Playlist *> (this));
1759 for (i = regions.begin(); i != regions.end(); ++i) {
1761 if ((*i)->position() >= start) {
1765 if ((*i)->last_frame() > max_frames - distance) {
1766 new_pos = max_frames - (*i)->length();
1768 new_pos = (*i)->position() + distance;
1773 if ((*i)->position() > distance) {
1774 new_pos = (*i)->position() - distance;
1780 (*i)->set_position (new_pos, this);
1788 notify_length_changed ();
1793 boost::shared_ptr<Region>
1794 Playlist::find_region (const ID& id) const
1796 RegionLock rlock (const_cast<Playlist*> (this));
1798 /* searches all regions currently in use by the playlist */
1800 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1801 if ((*i)->id() == id) {
1806 return boost::shared_ptr<Region> ();
1809 boost::shared_ptr<Region>
1810 Playlist::region_by_id (ID id)
1812 /* searches all regions ever added to this playlist */
1814 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1815 if ((*i)->id() == id) {
1819 return boost::shared_ptr<Region> ();
1823 Playlist::dump () const
1825 boost::shared_ptr<Region> r;
1827 cerr << "Playlist \"" << _name << "\" " << endl
1828 << regions.size() << " regions "
1831 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1833 cerr << " " << r->name() << " ["
1834 << r->start() << "+" << r->length()
1844 Playlist::set_frozen (bool yn)
1850 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1852 // struct timeval tv;
1853 // gettimeofday (&tv, 0);
1854 region->set_last_layer_op (++layer_op_counter);