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();
74 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
75 : SessionObject(sess, nom)
79 first_set_state = false;
84 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
85 : SessionObject(sess, "unnamed playlist")
88 const XMLProperty* prop = node.property("type");
89 assert(!prop || DataType(prop->value()) == _type);
92 _name = "unnamed"; /* reset by set_state */
94 /* set state called by derived class */
97 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
98 : SessionObject(other->_session, namestr), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
103 other->copy_regions (tmp);
107 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
108 add_region_internal( (*x), (*x)->position());
113 _splicing = other->_splicing;
114 _nudging = other->_nudging;
115 _edit_mode = other->_edit_mode;
118 first_set_state = false;
120 in_partition = false;
122 _read_data_count = 0;
123 _frozen = other->_frozen;
125 layer_op_counter = other->layer_op_counter;
126 freeze_length = other->freeze_length;
129 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
130 : SessionObject(other->_session, str), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
132 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
134 nframes_t end = start + cnt - 1;
140 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
142 boost::shared_ptr<Region> region;
143 boost::shared_ptr<Region> new_region;
144 nframes_t offset = 0;
145 nframes_t position = 0;
152 overlap = region->coverage (start, end);
158 case OverlapInternal:
159 offset = start - region->position();
166 position = region->position() - start;
167 len = end - region->position();
171 offset = start - region->position();
173 len = region->length() - offset;
176 case OverlapExternal:
178 position = region->position() - start;
179 len = region->length();
183 _session.region_name (new_name, region->name(), false);
185 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
187 add_region_internal (new_region, position);
191 first_set_state = false;
193 /* this constructor does NOT notify others (session) */
200 InUse (true); /* EMIT SIGNAL */
211 InUse (false); /* EMIT SIGNAL */
216 Playlist::copy_regions (RegionList& newlist) const
218 RegionLock rlock (const_cast<Playlist *> (this));
220 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
221 newlist.push_back (RegionFactory::RegionFactory::create (*i));
226 Playlist::init (bool hide)
228 g_atomic_int_set (&block_notifications, 0);
229 g_atomic_int_set (&ignore_state_changes, 0);
230 pending_modified = false;
231 pending_length = false;
232 first_set_state = true;
238 _edit_mode = Config->get_edit_mode();
240 in_partition = false;
242 _read_data_count = 0;
244 layer_op_counter = 0;
247 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
250 Playlist::Playlist (const Playlist& pl)
251 : SessionObject(pl._session, pl._name)
252 , _type(pl.data_type())
254 fatal << _("playlist const copy constructor called") << endmsg;
257 Playlist::Playlist (Playlist& pl)
258 : SessionObject(pl._session, pl._name)
259 , _type(pl.data_type())
261 fatal << _("playlist non-const copy constructor called") << endmsg;
264 Playlist::~Playlist ()
267 RegionLock rl (this);
269 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
270 (*i)->set_playlist (boost::shared_ptr<Playlist>());
274 /* GoingAway must be emitted by derived classes */
278 Playlist::set_name (const string& str)
280 /* in a typical situation, a playlist is being used
281 by one diskstream and also is referenced by the
282 Session. if there are more references than that,
283 then don't change the name.
289 return SessionObject::set_name(str);
293 /***********************************************************************
294 CHANGE NOTIFICATION HANDLING
296 Notifications must be delayed till the region_lock is released. This
297 is necessary because handlers for the signals may need to acquire
298 the lock (e.g. to read from the playlist).
299 ***********************************************************************/
304 delay_notifications ();
305 g_atomic_int_inc (&ignore_state_changes);
311 g_atomic_int_dec_and_test (&ignore_state_changes);
312 release_notifications ();
317 Playlist::delay_notifications ()
319 g_atomic_int_inc (&block_notifications);
320 freeze_length = _get_maximum_extent();
324 Playlist::release_notifications ()
326 if (g_atomic_int_dec_and_test (&block_notifications)) {
327 flush_notifications ();
333 Playlist::notify_modified ()
335 if (holding_state ()) {
336 pending_modified = true;
338 pending_modified = false;
339 Modified(); /* EMIT SIGNAL */
344 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
346 if (holding_state ()) {
347 pending_removes.insert (r);
348 pending_modified = true;
349 pending_length = true;
351 /* this might not be true, but we have to act
352 as though it could be.
354 pending_length = false;
355 LengthChanged (); /* EMIT SIGNAL */
356 pending_modified = false;
357 Modified (); /* EMIT SIGNAL */
362 Playlist::notify_region_added (boost::shared_ptr<Region> r)
364 /* the length change might not be true, but we have to act
365 as though it could be.
368 if (holding_state()) {
369 pending_adds.insert (r);
370 pending_modified = true;
371 pending_length = true;
373 pending_length = false;
374 LengthChanged (); /* EMIT SIGNAL */
375 pending_modified = false;
376 Modified (); /* EMIT SIGNAL */
381 Playlist::notify_length_changed ()
383 if (holding_state ()) {
384 pending_length = true;
386 pending_length = false;
387 LengthChanged(); /* EMIT SIGNAL */
388 pending_modified = false;
389 Modified (); /* EMIT SIGNAL */
394 Playlist::flush_notifications ()
396 set<boost::shared_ptr<Region> > dependent_checks_needed;
397 set<boost::shared_ptr<Region> >::iterator s;
406 /* we have no idea what order the regions ended up in pending
407 bounds (it could be based on selection order, for example).
408 so, to preserve layering in the "most recently moved is higher"
409 model, sort them by existing layer, then timestamp them.
412 // RegionSortByLayer cmp;
413 // pending_bounds.sort (cmp);
415 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
416 if (Config->get_layer_model() == MoveAddHigher) {
417 timestamp_layer_op (*r);
419 pending_length = true;
420 dependent_checks_needed.insert (*r);
424 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
425 dependent_checks_needed.insert (*s);
429 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
430 remove_dependents (*s);
434 if ((freeze_length != _get_maximum_extent()) || pending_length) {
436 LengthChanged(); /* EMIT SIGNAL */
440 if (n || pending_modified) {
445 pending_modified = false;
446 Modified (); /* EMIT SIGNAL */
450 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
451 check_dependents (*s, false);
454 pending_adds.clear ();
455 pending_removes.clear ();
456 pending_bounds.clear ();
461 /*************************************************************
463 *************************************************************/
466 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
468 RegionLock rlock (this);
469 delay_notifications();
470 times = fabs (times);
472 int itimes = (int) floor (times);
474 nframes_t pos = position;
477 add_region_internal (region, pos);
478 pos += region->length();
482 /* later regions will all be spliced anyway */
484 if (!holding_state ()) {
485 possibly_splice_unlocked ();
488 /* note that itimes can be zero if we being asked to just
489 insert a single fraction of the region.
492 for (int i = 0; i < itimes; ++i) {
493 boost::shared_ptr<Region> copy = RegionFactory::create (region);
494 add_region_internal (copy, pos);
495 pos += region->length();
498 if (floor (times) != times) {
499 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
501 _session.region_name (name, region->name(), false);
502 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
503 add_region_internal (sub, pos);
506 release_notifications ();
510 Playlist::set_region_ownership ()
512 RegionLock rl (this);
513 RegionList::iterator i;
514 boost::weak_ptr<Playlist> pl (shared_from_this());
516 for (i = regions.begin(); i != regions.end(); ++i) {
517 (*i)->set_playlist (pl);
522 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
524 RegionSortByPosition cmp;
525 nframes_t old_length = 0;
527 if (!holding_state()) {
528 old_length = _get_maximum_extent();
531 if (!first_set_state) {
532 boost::shared_ptr<Playlist> foo (shared_from_this());
533 region->set_playlist (boost::weak_ptr<Playlist>(foo));
536 region->set_position (position, this);
538 timestamp_layer_op (region);
540 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
541 all_regions.insert (region);
543 if (!holding_state () && !in_set_state) {
544 /* layers get assigned from XML state */
548 /* we need to notify the existence of new region before checking dependents. Ick. */
550 notify_region_added (region);
552 if (!holding_state ()) {
553 check_dependents (region, false);
554 if (old_length != _get_maximum_extent()) {
555 notify_length_changed ();
559 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
560 boost::weak_ptr<Region> (region)));
564 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
566 RegionLock rlock (this);
568 remove_region_internal (old);
569 add_region_internal (newr, pos);
571 if (!holding_state ()) {
572 possibly_splice_unlocked ();
577 Playlist::remove_region (boost::shared_ptr<Region> region)
579 RegionLock rlock (this);
580 remove_region_internal (region);
582 if (!holding_state ()) {
583 possibly_splice_unlocked ();
588 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
590 RegionList::iterator i;
591 nframes_t old_length = 0;
593 if (!holding_state()) {
594 old_length = _get_maximum_extent();
599 region->set_playlist (boost::weak_ptr<Playlist>());
602 for (i = regions.begin(); i != regions.end(); ++i) {
607 if (!holding_state ()) {
609 remove_dependents (region);
611 if (old_length != _get_maximum_extent()) {
612 notify_length_changed ();
616 notify_region_removed (region);
624 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
626 if (Config->get_use_overlap_equivalency()) {
627 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
628 if ((*i)->overlap_equivalent (other)) {
629 results.push_back ((*i));
633 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
634 if ((*i)->equivalent (other)) {
635 results.push_back ((*i));
642 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
644 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
646 if ((*i) && (*i)->region_list_equivalent (other)) {
647 results.push_back (*i);
653 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
657 partition_internal (start, end, false, thawlist);
659 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
660 (*i)->thaw ("separation");
665 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
667 boost::shared_ptr<Region> region;
668 boost::shared_ptr<Region> current;
670 RegionList::iterator tmp;
672 nframes_t pos1, pos2, pos3, pos4;
673 RegionList new_regions;
678 delay_notifications();
680 /* need to work from a copy, because otherwise the regions we add during the process
681 get operated on as well.
684 RegionLock rlock (this);
688 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
695 if (current->first_frame() == start && current->last_frame() == end) {
697 RegionLock rlock (this);
698 remove_region_internal (current);
703 if ((overlap = current->coverage (start, end)) == OverlapNone) {
707 pos1 = current->position();
710 pos4 = current->last_frame();
712 if (overlap == OverlapInternal) {
714 /* split: we need 3 new regions, the front, middle and end.
715 cut: we need 2 regions, the front and end.
720 ---------------*************************------------
723 ---------------*****++++++++++++++++====------------
725 ---------------*****----------------====------------
731 /* "middle" ++++++ */
733 _session.region_name (new_name, current->name(), false); //takes the session-wide region lock
734 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
735 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
736 RegionLock rlock (this);
737 add_region_internal (region, start);
738 new_regions.push_back (region);
743 _session.region_name (new_name, current->name(), false);
744 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
745 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
747 RegionLock rlock (this);
748 add_region_internal (region, end);
749 new_regions.push_back (region);
754 thawlist.push_back (current);
755 current->trim_end (pos2, this);
757 } else if (overlap == OverlapEnd) {
761 ---------------*************************------------
764 ---------------**************+++++++++++------------
766 ---------------**************-----------------------
774 _session.region_name (new_name, current->name(), false);
775 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, regions.size(),
776 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
777 RegionLock rlock (this);
778 add_region_internal (region, start);
779 new_regions.push_back (region);
785 thawlist.push_back (current);
786 current->trim_end (pos2, this);
788 } else if (overlap == OverlapStart) {
790 /* split: we need 2 regions: the front and the end.
791 cut: just trim current to skip the cut area
796 ---------------*************************------------
800 ---------------****+++++++++++++++++++++------------
802 -------------------*********************------------
809 _session.region_name (new_name, current->name(), false);
810 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
811 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
812 RegionLock rlock (this);
813 add_region_internal (region, pos1);
814 new_regions.push_back (region);
820 thawlist.push_back (current);
821 current->trim_front (pos3, this);
823 } else if (overlap == OverlapExternal) {
825 /* split: no split required.
826 cut: remove the region.
831 ---------------*************************------------
835 ---------------*************************------------
837 ----------------------------------------------------
842 RegionLock rlock (this);
843 remove_region_internal (current);
845 new_regions.push_back (current);
849 in_partition = false;
851 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
852 check_dependents (*i, false);
855 release_notifications ();
858 boost::shared_ptr<Playlist>
859 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
861 boost::shared_ptr<Playlist> ret;
862 boost::shared_ptr<Playlist> pl;
865 if (ranges.empty()) {
866 return boost::shared_ptr<Playlist>();
869 start = ranges.front().start;
871 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
873 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
875 if (i == ranges.begin()) {
879 /* paste the next section into the nascent playlist,
880 offset to reflect the start of the first range we
884 ret->paste (pl, (*i).start - start, 1.0f);
891 boost::shared_ptr<Playlist>
892 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
894 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
895 return cut_copy (pmf, ranges, result_is_hidden);
898 boost::shared_ptr<Playlist>
899 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
901 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
902 return cut_copy (pmf, ranges, result_is_hidden);
905 boost::shared_ptr<Playlist>
906 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
908 boost::shared_ptr<Playlist> the_copy;
912 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
913 string new_name = _name;
917 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
918 return boost::shared_ptr<Playlist>();
921 partition_internal (start, start+cnt-1, true, thawlist);
924 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
925 (*i)->thaw ("playlist cut");
931 boost::shared_ptr<Playlist>
932 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
936 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
937 string new_name = _name;
941 cnt = min (_get_maximum_extent() - start, cnt);
942 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
946 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
948 times = fabs (times);
949 nframes_t old_length;
952 RegionLock rl1 (this);
953 RegionLock rl2 (other.get());
955 old_length = _get_maximum_extent();
957 int itimes = (int) floor (times);
958 nframes_t pos = position;
959 nframes_t shift = other->_get_maximum_extent();
960 layer_t top_layer = regions.size();
963 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
964 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
966 /* put these new regions on top of all existing ones, but preserve
967 the ordering they had in the original playlist.
970 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
971 add_region_internal (copy_of_region, copy_of_region->position() + pos);
976 possibly_splice_unlocked ();
978 /* XXX shall we handle fractional cases at some point? */
980 if (old_length != _get_maximum_extent()) {
981 notify_length_changed ();
992 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
994 times = fabs (times);
996 RegionLock rl (this);
997 int itimes = (int) floor (times);
998 nframes_t pos = position;
1001 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1002 add_region_internal (copy, pos);
1003 pos += region->length();
1006 if (floor (times) != times) {
1007 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
1009 _session.region_name (name, region->name(), false);
1010 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
1011 add_region_internal (sub, pos);
1016 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
1018 RegionLock rl (this);
1020 if (!region->covers (playlist_position)) {
1024 if (region->position() == playlist_position ||
1025 region->last_frame() == playlist_position) {
1029 boost::shared_ptr<Region> left;
1030 boost::shared_ptr<Region> right;
1036 before = playlist_position - region->position();
1037 after = region->length() - before;
1040 _session.region_name (before_name, region->name(), false);
1041 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1043 _session.region_name (after_name, region->name(), false);
1044 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1046 add_region_internal (left, region->position());
1047 add_region_internal (right, region->position() + before);
1049 uint64_t orig_layer_op = region->last_layer_op();
1050 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1051 if ((*i)->last_layer_op() > orig_layer_op) {
1052 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1056 left->set_last_layer_op ( orig_layer_op );
1057 right->set_last_layer_op ( orig_layer_op + 1);
1061 finalize_split_region (region, left, right);
1063 if (remove_region_internal (region)) {
1069 Playlist::possibly_splice ()
1071 if (_edit_mode == Splice) {
1077 Playlist::possibly_splice_unlocked ()
1079 if (_edit_mode == Splice) {
1085 Playlist::splice_locked ()
1088 RegionLock rl (this);
1092 notify_length_changed ();
1096 Playlist::splice_unlocked ()
1099 notify_length_changed ();
1103 Playlist::core_splice ()
1107 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1109 RegionList::iterator next;
1114 if (next == regions.end()) {
1118 (*next)->set_position ((*i)->last_frame() + 1, this);
1125 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1127 if (in_set_state || _splicing || _nudging) {
1131 if (what_changed & ARDOUR::PositionChanged) {
1133 /* remove it from the list then add it back in
1134 the right place again.
1137 RegionSortByPosition cmp;
1139 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1141 if (i == regions.end()) {
1142 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1143 _name, region->name())
1149 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1153 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1154 if (holding_state ()) {
1155 pending_bounds.push_back (region);
1157 if (Config->get_layer_model() == MoveAddHigher) {
1158 /* it moved or changed length, so change the timestamp */
1159 timestamp_layer_op (region);
1163 notify_length_changed ();
1165 check_dependents (region, false);
1171 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1173 boost::shared_ptr<Region> region (weak_region.lock());
1180 /* this makes a virtual call to the right kind of playlist ... */
1182 region_changed (what_changed, region);
1186 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1188 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1191 if (in_set_state || in_flush) {
1196 if (what_changed & BoundsChanged) {
1197 region_bounds_changed (what_changed, region);
1198 save = !(_splicing || _nudging);
1201 if ((what_changed & our_interests) &&
1202 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1203 check_dependents (region, false);
1206 if (what_changed & our_interests) {
1215 Playlist::drop_regions ()
1217 RegionLock rl (this);
1219 all_regions.clear ();
1223 Playlist::clear (bool with_signals)
1226 RegionLock rl (this);
1227 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1228 pending_removes.insert (*i);
1234 pending_length = false;
1236 pending_modified = false;
1242 /***********************************************************************
1244 **********************************************************************/
1246 Playlist::RegionList *
1247 Playlist::regions_at (nframes_t frame)
1250 RegionLock rlock (this);
1251 return find_regions_at (frame);
1254 boost::shared_ptr<Region>
1255 Playlist::top_region_at (nframes_t frame)
1258 RegionLock rlock (this);
1259 RegionList *rlist = find_regions_at (frame);
1260 boost::shared_ptr<Region> region;
1262 if (rlist->size()) {
1263 RegionSortByLayer cmp;
1265 region = rlist->back();
1272 Playlist::RegionList *
1273 Playlist::find_regions_at (nframes_t frame)
1275 RegionList *rlist = new RegionList;
1277 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1278 if ((*i)->covers (frame)) {
1279 rlist->push_back (*i);
1286 Playlist::RegionList *
1287 Playlist::regions_touched (nframes_t start, nframes_t end)
1289 RegionLock rlock (this);
1290 RegionList *rlist = new RegionList;
1292 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1293 if ((*i)->coverage (start, end) != OverlapNone) {
1294 rlist->push_back (*i);
1302 boost::shared_ptr<Region>
1303 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1305 RegionLock rlock (this);
1306 boost::shared_ptr<Region> ret;
1307 nframes_t closest = max_frames;
1310 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1313 boost::shared_ptr<Region> r = (*i);
1318 pos = r->first_frame ();
1321 pos = r->last_frame ();
1324 pos = r->adjust_to_sync (r->first_frame());
1329 case 1: /* forwards */
1332 if ((distance = pos - frame) < closest) {
1340 default: /* backwards */
1343 if ((distance = frame - pos) < closest) {
1355 /***********************************************************************/
1360 Playlist::mark_session_dirty ()
1362 if (!in_set_state && !holding_state ()) {
1363 _session.set_dirty();
1368 Playlist::set_state (const XMLNode& node)
1372 XMLNodeConstIterator niter;
1373 XMLPropertyList plist;
1374 XMLPropertyConstIterator piter;
1376 boost::shared_ptr<Region> region;
1381 if (node.name() != "Playlist") {
1388 plist = node.properties();
1390 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1394 if (prop->name() == X_("name")) {
1395 _name = prop->value();
1396 } else if (prop->name() == X_("orig_diskstream_id")) {
1397 _orig_diskstream_id = prop->value ();
1398 } else if (prop->name() == X_("frozen")) {
1399 _frozen = (prop->value() == X_("yes"));
1405 nlist = node.children();
1407 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1411 if (child->name() == "Region") {
1413 if ((prop = child->property ("id")) == 0) {
1414 error << _("region state node has no ID, ignored") << endmsg;
1418 ID id = prop->value ();
1420 if ((region = region_by_id (id))) {
1422 Change what_changed = Change (0);
1424 if (region->set_live_state (*child, what_changed, true)) {
1425 error << _("Playlist: cannot reset region state from XML") << endmsg;
1429 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1430 error << _("Playlist: cannot create region from XML") << endmsg;
1434 add_region (region, region->position(), 1.0);
1436 // So that layer_op ordering doesn't get screwed up
1437 region->set_last_layer_op( region->layer());
1446 /* update dependents, which was not done during add_region_internal
1447 due to in_set_state being true
1450 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1451 check_dependents (*r, false);
1455 first_set_state = false;
1460 Playlist::get_state()
1466 Playlist::get_template()
1468 return state(false);
1472 Playlist::state (bool full_state)
1474 XMLNode *node = new XMLNode (X_("Playlist"));
1477 node->add_property (X_("name"), _name);
1478 node->add_property (X_("type"), _type.to_string());
1480 _orig_diskstream_id.print (buf, sizeof (buf));
1481 node->add_property (X_("orig_diskstream_id"), buf);
1482 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1485 RegionLock rlock (this, false);
1486 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1487 node->add_child_nocopy ((*i)->get_state());
1492 node->add_child_copy (*_extra_xml);
1499 Playlist::empty() const
1501 RegionLock rlock (const_cast<Playlist *>(this), false);
1502 return regions.empty();
1506 Playlist::n_regions() const
1508 RegionLock rlock (const_cast<Playlist *>(this), false);
1509 return regions.size();
1513 Playlist::get_maximum_extent () const
1515 RegionLock rlock (const_cast<Playlist *>(this), false);
1516 return _get_maximum_extent ();
1520 Playlist::_get_maximum_extent () const
1522 RegionList::const_iterator i;
1523 nframes_t max_extent = 0;
1526 for (i = regions.begin(); i != regions.end(); ++i) {
1527 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1536 Playlist::bump_name (string name, Session &session)
1538 string newname = name;
1541 newname = Playlist::bump_name_once (newname);
1542 } while (session.playlist_by_name (newname)!=NULL);
1548 Playlist::bump_name_once (string name)
1550 string::size_type period;
1553 if ((period = name.find_last_of ('.')) == string::npos) {
1558 const char *last_element = name.c_str() + period + 1;
1559 for (size_t i = 0; i < strlen(last_element); i++) {
1560 if (!isdigit(last_element[i])) {
1567 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1569 if (isnumber == 0 || errno != 0) {
1570 // last_element is not a number, or is too large
1576 snprintf (buf, sizeof(buf), "%ld", version+1);
1578 newname = name.substr (0, period+1);
1587 Playlist::top_layer() const
1589 RegionLock rlock (const_cast<Playlist *> (this));
1592 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1593 top = max (top, (*i)->layer());
1599 Playlist::set_edit_mode (EditMode mode)
1604 /********************
1606 ********************/
1609 Playlist::relayer ()
1611 /* don't send multiple Modified notifications
1612 when multiple regions are relayered.
1617 /* build up a new list of regions on each layer */
1619 std::vector<RegionList> layers;
1621 /* we want to go through regions from desired lowest to desired highest layer,
1622 which depends on the layer model
1625 RegionList copy = regions;
1627 /* sort according to the model */
1629 if (Config->get_layer_model() == MoveAddHigher || Config->get_layer_model() == AddHigher) {
1630 RegionSortByLastLayerOp cmp;
1634 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1636 /* find the lowest layer that this region can go on */
1637 size_t j = layers.size();
1639 /* try layer j - 1; it can go on if it overlaps no other region
1640 that is already on that layer
1642 RegionList::iterator k = layers[j - 1].begin();
1643 while (k != layers[j - 1].end()) {
1644 if ((*k)->overlap_equivalent (*i)) {
1650 if (k != layers[j - 1].end()) {
1651 /* no overlap, so we can use this layer */
1658 if (j == layers.size()) {
1659 /* we need a new layer for this region */
1660 layers.push_back (RegionList ());
1663 layers[j].push_back (*i);
1666 /* first pass: set up the layer numbers in the regions */
1667 for (size_t j = 0; j < layers.size(); ++j) {
1668 for (RegionList::iterator i = layers[j].begin(); i != layers[j].end(); ++i) {
1669 (*i)->set_layer (j);
1673 /* sending Modified means that various kinds of layering
1674 models operate correctly at the GUI
1675 level. slightly inefficient, but only slightly.
1677 We force a Modified signal here in case no layers actually
1686 /* XXX these layer functions are all deprecated */
1689 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1691 /* does nothing useful if layering mode is later=higher */
1692 if ((Config->get_layer_model() == MoveAddHigher) ||
1693 (Config->get_layer_model() == AddHigher)) {
1694 timestamp_layer_op (region);
1700 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1702 /* does nothing useful if layering mode is later=higher */
1703 if ((Config->get_layer_model() == MoveAddHigher) ||
1704 (Config->get_layer_model() == AddHigher)) {
1705 region->set_last_layer_op (0);
1711 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1713 RegionList::iterator i;
1720 RegionLock rlock (const_cast<Playlist *> (this));
1722 for (i = regions.begin(); i != regions.end(); ++i) {
1724 if ((*i)->position() >= start) {
1728 if ((*i)->last_frame() > max_frames - distance) {
1729 new_pos = max_frames - (*i)->length();
1731 new_pos = (*i)->position() + distance;
1736 if ((*i)->position() > distance) {
1737 new_pos = (*i)->position() - distance;
1743 (*i)->set_position (new_pos, this);
1751 notify_length_changed ();
1756 boost::shared_ptr<Region>
1757 Playlist::find_region (const ID& id) const
1759 RegionLock rlock (const_cast<Playlist*> (this));
1761 /* searches all regions currently in use by the playlist */
1763 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1764 if ((*i)->id() == id) {
1769 return boost::shared_ptr<Region> ();
1772 boost::shared_ptr<Region>
1773 Playlist::region_by_id (ID id)
1775 /* searches all regions ever added to this playlist */
1777 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1778 if ((*i)->id() == id) {
1782 return boost::shared_ptr<Region> ();
1786 Playlist::dump () const
1788 boost::shared_ptr<Region> r;
1790 cerr << "Playlist \"" << _name << "\" " << endl
1791 << regions.size() << " regions "
1794 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1796 cerr << " " << r->name() << " ["
1797 << r->start() << "+" << r->length()
1807 Playlist::set_frozen (bool yn)
1813 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1815 // struct timeval tv;
1816 // gettimeofday (&tv, 0);
1817 region->set_last_layer_op (++layer_op_counter);