2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include <pbd/failed_constructor.h>
32 #include <pbd/stl_delete.h>
33 #include <pbd/xml++.h>
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/region.h>
38 #include <ardour/region_factory.h>
39 #include <ardour/playlist_factory.h>
44 using namespace ARDOUR;
47 struct ShowMeTheList {
48 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
50 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
52 boost::shared_ptr<Playlist> playlist;
56 struct RegionSortByLayer {
57 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
58 return a->layer() < b->layer();
62 struct RegionSortByPosition {
63 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
64 return a->position() < b->position();
68 struct RegionSortByLastLayerOp {
69 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
70 return a->last_layer_op() < b->last_layer_op();
74 Playlist::Playlist (Session& sess, string nom, bool hide)
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;
113 in_partition = false;
115 _read_data_count = 0;
116 _frozen = other->_frozen;
118 layer_op_counter = other->layer_op_counter;
119 freeze_length = other->freeze_length;
122 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
123 : _name (str), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
125 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
127 nframes_t end = start + cnt - 1;
133 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
135 boost::shared_ptr<Region> region;
136 boost::shared_ptr<Region> new_region;
137 nframes_t offset = 0;
138 nframes_t position = 0;
145 overlap = region->coverage (start, end);
151 case OverlapInternal:
152 offset = start - region->position();
159 position = region->position() - start;
160 len = end - region->position();
164 offset = start - region->position();
166 len = region->length() - offset;
169 case OverlapExternal:
171 position = region->position() - start;
172 len = region->length();
176 _session.region_name (new_name, region->name(), false);
178 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
180 add_region_internal (new_region, position);
185 /* this constructor does NOT notify others (session) */
192 InUse (true); /* EMIT SIGNAL */
203 InUse (false); /* EMIT SIGNAL */
208 Playlist::copy_regions (RegionList& newlist) const
210 RegionLock rlock (const_cast<Playlist *> (this));
212 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
213 newlist.push_back (RegionFactory::RegionFactory::create (*i));
218 Playlist::init (bool hide)
220 g_atomic_int_set (&block_notifications, 0);
221 g_atomic_int_set (&ignore_state_changes, 0);
222 pending_modified = false;
223 pending_length = false;
229 _edit_mode = Config->get_edit_mode();
231 in_partition = false;
233 _read_data_count = 0;
235 layer_op_counter = 0;
238 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
241 Playlist::Playlist (const Playlist& pl)
242 : _session (pl._session)
244 fatal << _("playlist const copy constructor called") << endmsg;
247 Playlist::Playlist (Playlist& pl)
248 : _session (pl._session)
250 fatal << _("playlist non-const copy constructor called") << endmsg;
253 Playlist::~Playlist ()
256 RegionLock rl (this);
258 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
259 (*i)->set_playlist (boost::shared_ptr<Playlist>());
263 /* GoingAway must be emitted by derived classes */
267 Playlist::set_name (string str)
269 /* in a typical situation, a playlist is being used
270 by one diskstream and also is referenced by the
271 Session. if there are more references than that,
272 then don't change the name.
280 NameChanged(); /* EMIT SIGNAL */
283 /***********************************************************************
284 CHANGE NOTIFICATION HANDLING
286 Notifications must be delayed till the region_lock is released. This
287 is necessary because handlers for the signals may need to acquire
288 the lock (e.g. to read from the playlist).
289 ***********************************************************************/
294 delay_notifications ();
295 g_atomic_int_inc (&ignore_state_changes);
301 g_atomic_int_dec_and_test (&ignore_state_changes);
302 release_notifications ();
307 Playlist::delay_notifications ()
309 g_atomic_int_inc (&block_notifications);
310 freeze_length = _get_maximum_extent();
314 Playlist::release_notifications ()
316 if (g_atomic_int_dec_and_test (&block_notifications)) {
317 flush_notifications ();
323 Playlist::notify_modified ()
325 if (holding_state ()) {
326 pending_modified = true;
328 pending_modified = false;
329 Modified(); /* EMIT SIGNAL */
334 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
336 if (holding_state ()) {
337 pending_removes.insert (r);
338 pending_modified = true;
339 pending_length = true;
341 /* this might not be true, but we have to act
342 as though it could be.
344 LengthChanged (); /* EMIT SIGNAL */
345 Modified (); /* EMIT SIGNAL */
350 Playlist::notify_region_added (boost::shared_ptr<Region> r)
352 /* the length change might not be true, but we have to act
353 as though it could be.
356 if (holding_state()) {
357 pending_adds.insert (r);
358 pending_modified = true;
359 pending_length = true;
361 LengthChanged (); /* EMIT SIGNAL */
362 Modified (); /* EMIT SIGNAL */
367 Playlist::notify_length_changed ()
369 if (holding_state ()) {
370 pending_length = true;
372 LengthChanged(); /* EMIT SIGNAL */
373 Modified (); /* EMIT SIGNAL */
378 Playlist::flush_notifications ()
380 set<boost::shared_ptr<Region> > dependent_checks_needed;
381 set<boost::shared_ptr<Region> >::iterator s;
390 /* we have no idea what order the regions ended up in pending
391 bounds (it could be based on selection order, for example).
392 so, to preserve layering in the "most recently moved is higher"
393 model, sort them by existing layer, then timestamp them.
396 // RegionSortByLayer cmp;
397 // pending_bounds.sort (cmp);
399 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
400 if (Config->get_layer_model() == MoveAddHigher) {
401 timestamp_layer_op (*r);
403 pending_length = true;
404 dependent_checks_needed.insert (*r);
408 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
409 dependent_checks_needed.insert (*s);
413 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
414 remove_dependents (*s);
418 if ((freeze_length != _get_maximum_extent()) || pending_length) {
420 LengthChanged(); /* EMIT SIGNAL */
424 if (n || pending_modified) {
429 pending_modified = false;
430 Modified (); /* EMIT SIGNAL */
433 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
434 check_dependents (*s, false);
437 pending_adds.clear ();
438 pending_removes.clear ();
439 pending_bounds.clear ();
444 /*************************************************************
446 *************************************************************/
449 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
451 RegionLock rlock (this);
453 times = fabs (times);
455 int itimes = (int) floor (times);
457 nframes_t pos = position;
460 add_region_internal (region, pos);
461 pos += region->length();
465 /* later regions will all be spliced anyway */
467 if (!holding_state ()) {
468 possibly_splice_unlocked ();
471 /* note that itimes can be zero if we being asked to just
472 insert a single fraction of the region.
475 for (int i = 0; i < itimes; ++i) {
476 boost::shared_ptr<Region> copy = RegionFactory::create (region);
477 add_region_internal (copy, pos);
478 pos += region->length();
481 if (floor (times) != times) {
482 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
484 _session.region_name (name, region->name(), false);
485 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
486 add_region_internal (sub, pos);
491 Playlist::set_region_ownership ()
493 RegionLock rl (this);
494 RegionList::iterator i;
495 boost::weak_ptr<Playlist> pl (shared_from_this());
497 for (i = regions.begin(); i != regions.end(); ++i) {
498 (*i)->set_playlist (pl);
503 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
505 RegionSortByPosition cmp;
506 nframes_t old_length = 0;
508 if (!holding_state()) {
509 old_length = _get_maximum_extent();
513 boost::shared_ptr<Playlist> foo (shared_from_this());
514 region->set_playlist (boost::weak_ptr<Playlist>(foo));
517 region->set_position (position, this);
519 timestamp_layer_op (region);
521 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
522 all_regions.insert (region);
524 if (!holding_state () && !in_set_state) {
525 /* layers get assigned from XML state */
529 /* we need to notify the existence of new region before checking dependents. Ick. */
531 notify_region_added (region);
533 if (!holding_state ()) {
534 check_dependents (region, false);
535 if (old_length != _get_maximum_extent()) {
536 notify_length_changed ();
540 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
541 boost::weak_ptr<Region> (region)));
545 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
547 RegionLock rlock (this);
549 remove_region_internal (old);
550 add_region_internal (newr, pos);
552 if (!holding_state ()) {
553 possibly_splice_unlocked ();
558 Playlist::remove_region (boost::shared_ptr<Region> region)
560 RegionLock rlock (this);
561 remove_region_internal (region);
563 if (!holding_state ()) {
564 possibly_splice_unlocked ();
569 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
571 RegionList::iterator i;
572 nframes_t old_length = 0;
574 if (!holding_state()) {
575 old_length = _get_maximum_extent();
578 for (i = regions.begin(); i != regions.end(); ++i) {
583 if (!holding_state ()) {
585 remove_dependents (region);
587 if (old_length != _get_maximum_extent()) {
588 notify_length_changed ();
592 notify_region_removed (region);
600 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
602 if (Config->get_use_overlap_equivalency()) {
603 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
604 if ((*i)->overlap_equivalent (other)) {
605 results.push_back ((*i));
609 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
610 if ((*i)->equivalent (other)) {
611 results.push_back ((*i));
618 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
620 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
622 if ((*i) && (*i)->region_list_equivalent (other)) {
623 results.push_back (*i);
629 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
633 partition_internal (start, end, false, thawlist);
635 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
636 (*i)->thaw ("separation");
641 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
643 RegionLock rlock (this);
644 boost::shared_ptr<Region> region;
645 boost::shared_ptr<Region> current;
647 RegionList::iterator tmp;
649 nframes_t pos1, pos2, pos3, pos4;
650 RegionList new_regions;
654 /* need to work from a copy, because otherwise the regions we add during the process
655 get operated on as well.
658 RegionList copy = regions;
660 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
667 if (current->first_frame() == start && current->last_frame() == end) {
669 remove_region_internal (current);
674 if ((overlap = current->coverage (start, end)) == OverlapNone) {
678 pos1 = current->position();
681 pos4 = current->last_frame();
683 if (overlap == OverlapInternal) {
685 /* split: we need 3 new regions, the front, middle and end.
686 cut: we need 2 regions, the front and end.
691 ---------------*************************------------
694 ---------------*****++++++++++++++++====------------
696 ---------------*****----------------====------------
702 /* "middle" ++++++ */
704 _session.region_name (new_name, current->name(), false);
705 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
706 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
707 add_region_internal (region, start);
708 new_regions.push_back (region);
713 _session.region_name (new_name, current->name(), false);
714 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
715 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
717 add_region_internal (region, end);
718 new_regions.push_back (region);
723 thawlist.push_back (current);
724 current->trim_end (pos2, this);
726 } else if (overlap == OverlapEnd) {
730 ---------------*************************------------
733 ---------------**************+++++++++++------------
735 ---------------**************-----------------------
743 _session.region_name (new_name, current->name(), false);
744 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
745 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
746 add_region_internal (region, start);
747 new_regions.push_back (region);
753 thawlist.push_back (current);
754 current->trim_end (pos2, this);
756 } else if (overlap == OverlapStart) {
758 /* split: we need 2 regions: the front and the end.
759 cut: just trim current to skip the cut area
764 ---------------*************************------------
768 ---------------****+++++++++++++++++++++------------
770 -------------------*********************------------
777 _session.region_name (new_name, current->name(), false);
778 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
779 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
780 add_region_internal (region, pos1);
781 new_regions.push_back (region);
787 thawlist.push_back (current);
788 current->trim_front (pos3, this);
790 } else if (overlap == OverlapExternal) {
792 /* split: no split required.
793 cut: remove the region.
798 ---------------*************************------------
802 ---------------*************************------------
804 ----------------------------------------------------
809 remove_region_internal (current);
811 new_regions.push_back (current);
815 in_partition = false;
817 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
818 check_dependents (*i, false);
822 boost::shared_ptr<Playlist>
823 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
825 boost::shared_ptr<Playlist> ret;
826 boost::shared_ptr<Playlist> pl;
829 if (ranges.empty()) {
830 return boost::shared_ptr<Playlist>();
833 start = ranges.front().start;
835 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
837 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
839 if (i == ranges.begin()) {
843 /* paste the next section into the nascent playlist,
844 offset to reflect the start of the first range we
848 ret->paste (pl, (*i).start - start, 1.0f);
855 boost::shared_ptr<Playlist>
856 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
858 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
859 return cut_copy (pmf, ranges, result_is_hidden);
862 boost::shared_ptr<Playlist>
863 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
865 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
866 return cut_copy (pmf, ranges, result_is_hidden);
869 boost::shared_ptr<Playlist>
870 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
872 boost::shared_ptr<Playlist> the_copy;
876 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
877 string new_name = _name;
881 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
882 return boost::shared_ptr<Playlist>();
885 partition_internal (start, start+cnt-1, true, thawlist);
888 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
889 (*i)->thaw ("playlist cut");
895 boost::shared_ptr<Playlist>
896 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
900 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
901 string new_name = _name;
905 cnt = min (_get_maximum_extent() - start, cnt);
906 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
910 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
912 times = fabs (times);
913 nframes_t old_length;
916 RegionLock rl1 (this);
917 RegionLock rl2 (other.get());
919 old_length = _get_maximum_extent();
921 int itimes = (int) floor (times);
922 nframes_t pos = position;
923 nframes_t shift = other->_get_maximum_extent();
924 layer_t top_layer = regions.size();
927 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
928 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
930 /* put these new regions on top of all existing ones, but preserve
931 the ordering they had in the original playlist.
934 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
935 add_region_internal (copy_of_region, copy_of_region->position() + pos);
940 possibly_splice_unlocked ();
942 /* XXX shall we handle fractional cases at some point? */
944 if (old_length != _get_maximum_extent()) {
945 notify_length_changed ();
956 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
958 times = fabs (times);
960 RegionLock rl (this);
961 int itimes = (int) floor (times);
962 nframes_t pos = position;
965 boost::shared_ptr<Region> copy = RegionFactory::create (region);
966 add_region_internal (copy, pos);
967 pos += region->length();
970 if (floor (times) != times) {
971 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
973 _session.region_name (name, region->name(), false);
974 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
975 add_region_internal (sub, pos);
980 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
982 RegionLock rl (this);
984 if (!region->covers (playlist_position)) {
988 if (region->position() == playlist_position ||
989 region->last_frame() == playlist_position) {
993 boost::shared_ptr<Region> left;
994 boost::shared_ptr<Region> right;
1000 before = playlist_position - region->position();
1001 after = region->length() - before;
1004 _session.region_name (before_name, region->name(), false);
1005 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1007 _session.region_name (after_name, region->name(), false);
1008 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1010 add_region_internal (left, region->position());
1011 add_region_internal (right, region->position() + before);
1013 uint64_t orig_layer_op = region->last_layer_op();
1014 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1015 if ((*i)->last_layer_op() > orig_layer_op) {
1016 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1020 left->set_last_layer_op ( orig_layer_op );
1021 right->set_last_layer_op ( orig_layer_op + 1);
1025 finalize_split_region (region, left, right);
1027 if (remove_region_internal (region)) {
1033 Playlist::possibly_splice ()
1035 if (_edit_mode == Splice) {
1041 Playlist::possibly_splice_unlocked ()
1043 if (_edit_mode == Splice) {
1049 Playlist::splice_locked ()
1052 RegionLock rl (this);
1056 notify_length_changed ();
1060 Playlist::splice_unlocked ()
1063 notify_length_changed ();
1067 Playlist::core_splice ()
1071 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1073 RegionList::iterator next;
1078 if (next == regions.end()) {
1082 (*next)->set_position ((*i)->last_frame() + 1, this);
1089 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1091 if (in_set_state || _splicing || _nudging) {
1095 if (what_changed & ARDOUR::PositionChanged) {
1097 /* remove it from the list then add it back in
1098 the right place again.
1101 RegionSortByPosition cmp;
1103 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1105 if (i == regions.end()) {
1106 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1107 _name, region->name())
1113 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1117 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1118 if (holding_state ()) {
1119 pending_bounds.push_back (region);
1121 if (Config->get_layer_model() == MoveAddHigher) {
1122 /* it moved or changed length, so change the timestamp */
1123 timestamp_layer_op (region);
1127 notify_length_changed ();
1129 check_dependents (region, false);
1135 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1137 boost::shared_ptr<Region> region (weak_region.lock());
1144 /* this makes a virtual call to the right kind of playlist ... */
1146 region_changed (what_changed, region);
1150 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1152 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1155 if (in_set_state || in_flush) {
1160 if (what_changed & BoundsChanged) {
1161 region_bounds_changed (what_changed, region);
1162 save = !(_splicing || _nudging);
1165 if ((what_changed & Region::MuteChanged) &&
1166 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1167 check_dependents (region, false);
1170 if (what_changed & our_interests) {
1179 Playlist::clear (bool with_signals)
1182 RegionLock rl (this);
1183 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1184 pending_removes.insert (*i);
1196 /***********************************************************************
1198 **********************************************************************/
1200 Playlist::RegionList *
1201 Playlist::regions_at (nframes_t frame)
1204 RegionLock rlock (this);
1205 return find_regions_at (frame);
1208 boost::shared_ptr<Region>
1209 Playlist::top_region_at (nframes_t frame)
1212 RegionLock rlock (this);
1213 RegionList *rlist = find_regions_at (frame);
1214 boost::shared_ptr<Region> region;
1216 if (rlist->size()) {
1217 RegionSortByLayer cmp;
1219 region = rlist->back();
1226 Playlist::RegionList *
1227 Playlist::find_regions_at (nframes_t frame)
1229 RegionList *rlist = new RegionList;
1231 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1232 if ((*i)->covers (frame)) {
1233 rlist->push_back (*i);
1240 Playlist::RegionList *
1241 Playlist::regions_touched (nframes_t start, nframes_t end)
1243 RegionLock rlock (this);
1244 RegionList *rlist = new RegionList;
1246 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1247 if ((*i)->coverage (start, end) != OverlapNone) {
1248 rlist->push_back (*i);
1256 boost::shared_ptr<Region>
1257 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1259 RegionLock rlock (this);
1260 boost::shared_ptr<Region> ret;
1261 nframes_t closest = max_frames;
1264 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1267 boost::shared_ptr<Region> r = (*i);
1272 pos = r->first_frame ();
1275 pos = r->last_frame ();
1278 pos = r->adjust_to_sync (r->first_frame());
1283 case 1: /* forwards */
1286 if ((distance = pos - frame) < closest) {
1294 default: /* backwards */
1297 if ((distance = frame - pos) < closest) {
1309 /***********************************************************************/
1314 Playlist::mark_session_dirty ()
1316 if (!in_set_state && !holding_state ()) {
1317 _session.set_dirty();
1322 Playlist::set_state (const XMLNode& node)
1326 XMLNodeConstIterator niter;
1327 XMLPropertyList plist;
1328 XMLPropertyConstIterator piter;
1330 boost::shared_ptr<Region> region;
1335 if (node.name() != "Playlist") {
1342 plist = node.properties();
1344 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1348 if (prop->name() == X_("name")) {
1349 _name = prop->value();
1350 } else if (prop->name() == X_("orig_diskstream_id")) {
1351 _orig_diskstream_id = prop->value ();
1352 } else if (prop->name() == X_("frozen")) {
1353 _frozen = (prop->value() == X_("yes"));
1359 nlist = node.children();
1361 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1365 if (child->name() == "Region") {
1367 if ((prop = child->property ("id")) == 0) {
1368 error << _("region state node has no ID, ignored") << endmsg;
1372 ID id = prop->value ();
1374 if ((region = region_by_id (id))) {
1376 Change what_changed = Change (0);
1378 if (region->set_live_state (*child, what_changed, true)) {
1379 error << _("Playlist: cannot reset region state from XML") << endmsg;
1383 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1384 error << _("Playlist: cannot create region from XML") << endmsg;
1388 add_region (region, region->position(), 1.0);
1390 // So that layer_op ordering doesn't get screwed up
1391 region->set_last_layer_op( region->layer());
1400 /* update dependents, which was not done during add_region_internal
1401 due to in_set_state being true
1404 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1405 check_dependents (*r, false);
1414 Playlist::get_state()
1420 Playlist::get_template()
1422 return state(false);
1426 Playlist::state (bool full_state)
1428 XMLNode *node = new XMLNode (X_("Playlist"));
1431 node->add_property (X_("name"), _name);
1433 _orig_diskstream_id.print (buf, sizeof (buf));
1434 node->add_property (X_("orig_diskstream_id"), buf);
1435 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1438 RegionLock rlock (this, false);
1439 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1440 node->add_child_nocopy ((*i)->get_state());
1445 node->add_child_copy (*_extra_xml);
1452 Playlist::empty() const
1454 RegionLock rlock (const_cast<Playlist *>(this), false);
1455 return regions.empty();
1459 Playlist::n_regions() const
1461 RegionLock rlock (const_cast<Playlist *>(this), false);
1462 return regions.size();
1466 Playlist::get_maximum_extent () const
1468 RegionLock rlock (const_cast<Playlist *>(this), false);
1469 return _get_maximum_extent ();
1473 Playlist::_get_maximum_extent () const
1475 RegionList::const_iterator i;
1476 nframes_t max_extent = 0;
1479 for (i = regions.begin(); i != regions.end(); ++i) {
1480 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1489 Playlist::bump_name (string name, Session &session)
1491 string newname = name;
1494 newname = Playlist::bump_name_once (newname);
1495 } while (session.playlist_by_name (newname)!=NULL);
1501 Playlist::bump_name_once (string name)
1503 string::size_type period;
1506 if ((period = name.find_last_of ('.')) == string::npos) {
1513 sscanf (name.substr (period+1).c_str(), "%d", &version);
1514 snprintf (buf, sizeof(buf), "%d", version+1);
1516 newname = name.substr (0, period+1);
1524 Playlist::top_layer() const
1526 RegionLock rlock (const_cast<Playlist *> (this));
1529 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1530 top = max (top, (*i)->layer());
1536 Playlist::set_edit_mode (EditMode mode)
1541 /********************
1543 ********************/
1546 Playlist::relayer ()
1548 RegionList::iterator i;
1551 /* don't send multiple Modified notifications
1552 when multiple regions are relayered.
1557 if (Config->get_layer_model() == MoveAddHigher ||
1558 Config->get_layer_model() == AddHigher) {
1560 RegionSortByLastLayerOp cmp;
1561 RegionList copy = regions;
1565 for (i = copy.begin(); i != copy.end(); ++i) {
1566 (*i)->set_layer (layer++);
1571 /* Session::LaterHigher model */
1573 for (i = regions.begin(); i != regions.end(); ++i) {
1574 (*i)->set_layer (layer++);
1578 /* sending Modified means that various kinds of layering
1579 models operate correctly at the GUI
1580 level. slightly inefficient, but only slightly.
1582 We force a Modified signal here in case no layers actually
1591 /* XXX these layer functions are all deprecated */
1594 Playlist::raise_region (boost::shared_ptr<Region> region)
1596 uint32_t rsz = regions.size();
1597 layer_t target = region->layer() + 1U;
1599 if (target >= rsz) {
1600 /* its already at the effective top */
1604 move_region_to_layer (target, region, 1);
1608 Playlist::lower_region (boost::shared_ptr<Region> region)
1610 if (region->layer() == 0) {
1611 /* its already at the bottom */
1615 layer_t target = region->layer() - 1U;
1617 move_region_to_layer (target, region, -1);
1621 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1623 /* does nothing useful if layering mode is later=higher */
1624 if ((Config->get_layer_model() == MoveAddHigher) ||
1625 (Config->get_layer_model() == AddHigher)) {
1626 timestamp_layer_op (region);
1632 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1634 /* does nothing useful if layering mode is later=higher */
1635 if ((Config->get_layer_model() == MoveAddHigher) ||
1636 (Config->get_layer_model() == AddHigher)) {
1637 region->set_last_layer_op (0);
1643 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1645 RegionList::iterator i;
1646 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1647 list<LayerInfo> layerinfo;
1651 RegionLock rlock (const_cast<Playlist *> (this));
1653 for (i = regions.begin(); i != regions.end(); ++i) {
1661 /* region is moving up, move all regions on intermediate layers
1665 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1666 dest = (*i)->layer() - 1;
1673 /* region is moving down, move all regions on intermediate layers
1677 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1678 dest = (*i)->layer() + 1;
1688 newpair.second = dest;
1690 layerinfo.push_back (newpair);
1694 /* now reset the layers without holding the region lock */
1696 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1697 x->first->set_layer (x->second);
1700 region->set_layer (target_layer);
1703 /* now check all dependents */
1705 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1706 check_dependents (x->first, false);
1709 check_dependents (region, false);
1716 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1718 RegionList::iterator i;
1725 RegionLock rlock (const_cast<Playlist *> (this));
1727 for (i = regions.begin(); i != regions.end(); ++i) {
1729 if ((*i)->position() >= start) {
1733 if ((*i)->last_frame() > max_frames - distance) {
1734 new_pos = max_frames - (*i)->length();
1736 new_pos = (*i)->position() + distance;
1741 if ((*i)->position() > distance) {
1742 new_pos = (*i)->position() - distance;
1748 (*i)->set_position (new_pos, this);
1756 notify_length_changed ();
1761 boost::shared_ptr<Region>
1762 Playlist::find_region (const ID& id) const
1764 RegionLock rlock (const_cast<Playlist*> (this));
1766 /* searches all regions currently in use by the playlist */
1768 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1769 if ((*i)->id() == id) {
1774 return boost::shared_ptr<Region> ();
1777 boost::shared_ptr<Region>
1778 Playlist::region_by_id (ID id)
1780 /* searches all regions ever added to this playlist */
1782 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1783 if ((*i)->id() == id) {
1787 return boost::shared_ptr<Region> ();
1791 Playlist::dump () const
1793 boost::shared_ptr<Region> r;
1795 cerr << "Playlist \"" << _name << "\" " << endl
1796 << regions.size() << " regions "
1799 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1801 cerr << " " << r->name() << " ["
1802 << r->start() << "+" << r->length()
1812 Playlist::set_frozen (bool yn)
1818 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1820 // struct timeval tv;
1821 // gettimeofday (&tv, 0);
1822 region->set_last_layer_op (++layer_op_counter);