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, DataType type, bool hide)
78 first_set_state = false;
83 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
87 const XMLProperty* prop = node.property("type");
88 assert(!prop || DataType(prop->value()) == _type);
91 _name = "unnamed"; /* reset by set_state */
93 /* set state called by derived class */
96 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
97 : _name (namestr), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
102 other->copy_regions (tmp);
106 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
107 add_region_internal( (*x), (*x)->position());
112 _splicing = other->_splicing;
113 _nudging = other->_nudging;
114 _edit_mode = other->_edit_mode;
117 first_set_state = false;
119 in_partition = false;
121 _read_data_count = 0;
122 _frozen = other->_frozen;
124 layer_op_counter = other->layer_op_counter;
125 freeze_length = other->freeze_length;
128 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
129 : _name (str), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
131 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
133 nframes_t end = start + cnt - 1;
139 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
141 boost::shared_ptr<Region> region;
142 boost::shared_ptr<Region> new_region;
143 nframes_t offset = 0;
144 nframes_t position = 0;
151 overlap = region->coverage (start, end);
157 case OverlapInternal:
158 offset = start - region->position();
165 position = region->position() - start;
166 len = end - region->position();
170 offset = start - region->position();
172 len = region->length() - offset;
175 case OverlapExternal:
177 position = region->position() - start;
178 len = region->length();
182 _session.region_name (new_name, region->name(), false);
184 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
186 add_region_internal (new_region, position);
190 first_set_state = false;
192 /* this constructor does NOT notify others (session) */
199 InUse (true); /* EMIT SIGNAL */
210 InUse (false); /* EMIT SIGNAL */
215 Playlist::copy_regions (RegionList& newlist) const
217 RegionLock rlock (const_cast<Playlist *> (this));
219 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
220 newlist.push_back (RegionFactory::RegionFactory::create (*i));
225 Playlist::init (bool hide)
227 g_atomic_int_set (&block_notifications, 0);
228 g_atomic_int_set (&ignore_state_changes, 0);
229 pending_modified = false;
230 pending_length = false;
231 first_set_state = true;
237 _edit_mode = Config->get_edit_mode();
239 in_partition = false;
241 _read_data_count = 0;
243 layer_op_counter = 0;
246 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
249 Playlist::Playlist (const Playlist& pl)
250 : _session (pl._session)
251 , _type(pl.data_type())
253 fatal << _("playlist const copy constructor called") << endmsg;
256 Playlist::Playlist (Playlist& pl)
257 : _session (pl._session)
258 , _type(pl.data_type())
260 fatal << _("playlist non-const copy constructor called") << endmsg;
263 Playlist::~Playlist ()
266 RegionLock rl (this);
268 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
269 (*i)->set_playlist (boost::shared_ptr<Playlist>());
273 /* GoingAway must be emitted by derived classes */
277 Playlist::set_name (string str)
279 /* in a typical situation, a playlist is being used
280 by one diskstream and also is referenced by the
281 Session. if there are more references than that,
282 then don't change the name.
290 NameChanged(); /* EMIT SIGNAL */
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 LengthChanged (); /* EMIT SIGNAL */
355 Modified (); /* EMIT SIGNAL */
360 Playlist::notify_region_added (boost::shared_ptr<Region> r)
362 /* the length change might not be true, but we have to act
363 as though it could be.
366 if (holding_state()) {
367 pending_adds.insert (r);
368 pending_modified = true;
369 pending_length = true;
371 LengthChanged (); /* EMIT SIGNAL */
372 Modified (); /* EMIT SIGNAL */
377 Playlist::notify_length_changed ()
379 if (holding_state ()) {
380 pending_length = true;
382 LengthChanged(); /* EMIT SIGNAL */
383 Modified (); /* EMIT SIGNAL */
388 Playlist::flush_notifications ()
390 set<boost::shared_ptr<Region> > dependent_checks_needed;
391 set<boost::shared_ptr<Region> >::iterator s;
400 /* we have no idea what order the regions ended up in pending
401 bounds (it could be based on selection order, for example).
402 so, to preserve layering in the "most recently moved is higher"
403 model, sort them by existing layer, then timestamp them.
406 // RegionSortByLayer cmp;
407 // pending_bounds.sort (cmp);
409 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
410 if (Config->get_layer_model() == MoveAddHigher) {
411 timestamp_layer_op (*r);
413 pending_length = true;
414 dependent_checks_needed.insert (*r);
418 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
419 dependent_checks_needed.insert (*s);
423 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
424 remove_dependents (*s);
428 if ((freeze_length != _get_maximum_extent()) || pending_length) {
430 LengthChanged(); /* EMIT SIGNAL */
434 if (n || pending_modified) {
439 pending_modified = false;
440 Modified (); /* EMIT SIGNAL */
443 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
444 check_dependents (*s, false);
447 pending_adds.clear ();
448 pending_removes.clear ();
449 pending_bounds.clear ();
454 /*************************************************************
456 *************************************************************/
459 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
461 RegionLock rlock (this);
463 times = fabs (times);
465 int itimes = (int) floor (times);
467 nframes_t pos = position;
470 add_region_internal (region, pos);
471 pos += region->length();
475 /* later regions will all be spliced anyway */
477 if (!holding_state ()) {
478 possibly_splice_unlocked ();
481 /* note that itimes can be zero if we being asked to just
482 insert a single fraction of the region.
485 for (int i = 0; i < itimes; ++i) {
486 boost::shared_ptr<Region> copy = RegionFactory::create (region);
487 add_region_internal (copy, pos);
488 pos += region->length();
491 if (floor (times) != times) {
492 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
494 _session.region_name (name, region->name(), false);
495 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
496 add_region_internal (sub, pos);
501 Playlist::set_region_ownership ()
503 RegionLock rl (this);
504 RegionList::iterator i;
505 boost::weak_ptr<Playlist> pl (shared_from_this());
507 for (i = regions.begin(); i != regions.end(); ++i) {
508 (*i)->set_playlist (pl);
513 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
515 RegionSortByPosition cmp;
516 nframes_t old_length = 0;
518 if (!holding_state()) {
519 old_length = _get_maximum_extent();
522 if (!first_set_state) {
523 boost::shared_ptr<Playlist> foo (shared_from_this());
524 region->set_playlist (boost::weak_ptr<Playlist>(foo));
527 region->set_position (position, this);
529 timestamp_layer_op (region);
531 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
532 all_regions.insert (region);
534 if (!holding_state () && !in_set_state) {
535 /* layers get assigned from XML state */
539 /* we need to notify the existence of new region before checking dependents. Ick. */
541 notify_region_added (region);
543 if (!holding_state ()) {
544 check_dependents (region, false);
545 if (old_length != _get_maximum_extent()) {
546 notify_length_changed ();
550 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
551 boost::weak_ptr<Region> (region)));
555 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
557 RegionLock rlock (this);
559 remove_region_internal (old);
560 add_region_internal (newr, pos);
562 if (!holding_state ()) {
563 possibly_splice_unlocked ();
568 Playlist::remove_region (boost::shared_ptr<Region> region)
570 RegionLock rlock (this);
571 remove_region_internal (region);
573 if (!holding_state ()) {
574 possibly_splice_unlocked ();
579 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
581 RegionList::iterator i;
582 nframes_t old_length = 0;
584 if (!holding_state()) {
585 old_length = _get_maximum_extent();
590 region->set_playlist (boost::weak_ptr<Playlist>());
593 for (i = regions.begin(); i != regions.end(); ++i) {
598 if (!holding_state ()) {
600 remove_dependents (region);
602 if (old_length != _get_maximum_extent()) {
603 notify_length_changed ();
607 notify_region_removed (region);
615 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
617 if (Config->get_use_overlap_equivalency()) {
618 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
619 if ((*i)->overlap_equivalent (other)) {
620 results.push_back ((*i));
624 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
625 if ((*i)->equivalent (other)) {
626 results.push_back ((*i));
633 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
635 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
637 if ((*i) && (*i)->region_list_equivalent (other)) {
638 results.push_back (*i);
644 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
648 partition_internal (start, end, false, thawlist);
650 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
651 (*i)->thaw ("separation");
656 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
658 RegionLock rlock (this);
659 boost::shared_ptr<Region> region;
660 boost::shared_ptr<Region> current;
662 RegionList::iterator tmp;
664 nframes_t pos1, pos2, pos3, pos4;
665 RegionList new_regions;
669 /* need to work from a copy, because otherwise the regions we add during the process
670 get operated on as well.
673 RegionList copy = regions;
675 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
682 if (current->first_frame() == start && current->last_frame() == end) {
684 remove_region_internal (current);
689 if ((overlap = current->coverage (start, end)) == OverlapNone) {
693 pos1 = current->position();
696 pos4 = current->last_frame();
698 if (overlap == OverlapInternal) {
700 /* split: we need 3 new regions, the front, middle and end.
701 cut: we need 2 regions, the front and end.
706 ---------------*************************------------
709 ---------------*****++++++++++++++++====------------
711 ---------------*****----------------====------------
717 /* "middle" ++++++ */
719 _session.region_name (new_name, current->name(), false);
720 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
721 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
722 add_region_internal (region, start);
723 new_regions.push_back (region);
728 _session.region_name (new_name, current->name(), false);
729 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
730 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
732 add_region_internal (region, end);
733 new_regions.push_back (region);
738 thawlist.push_back (current);
739 current->trim_end (pos2, this);
741 } else if (overlap == OverlapEnd) {
745 ---------------*************************------------
748 ---------------**************+++++++++++------------
750 ---------------**************-----------------------
758 _session.region_name (new_name, current->name(), false);
759 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
760 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
761 add_region_internal (region, start);
762 new_regions.push_back (region);
768 thawlist.push_back (current);
769 current->trim_end (pos2, this);
771 } else if (overlap == OverlapStart) {
773 /* split: we need 2 regions: the front and the end.
774 cut: just trim current to skip the cut area
779 ---------------*************************------------
783 ---------------****+++++++++++++++++++++------------
785 -------------------*********************------------
792 _session.region_name (new_name, current->name(), false);
793 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
794 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
795 add_region_internal (region, pos1);
796 new_regions.push_back (region);
802 thawlist.push_back (current);
803 current->trim_front (pos3, this);
805 } else if (overlap == OverlapExternal) {
807 /* split: no split required.
808 cut: remove the region.
813 ---------------*************************------------
817 ---------------*************************------------
819 ----------------------------------------------------
824 remove_region_internal (current);
826 new_regions.push_back (current);
830 in_partition = false;
832 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
833 check_dependents (*i, false);
837 boost::shared_ptr<Playlist>
838 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
840 boost::shared_ptr<Playlist> ret;
841 boost::shared_ptr<Playlist> pl;
844 if (ranges.empty()) {
845 return boost::shared_ptr<Playlist>();
848 start = ranges.front().start;
850 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
852 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
854 if (i == ranges.begin()) {
858 /* paste the next section into the nascent playlist,
859 offset to reflect the start of the first range we
863 ret->paste (pl, (*i).start - start, 1.0f);
870 boost::shared_ptr<Playlist>
871 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
873 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
874 return cut_copy (pmf, ranges, result_is_hidden);
877 boost::shared_ptr<Playlist>
878 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
880 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
881 return cut_copy (pmf, ranges, result_is_hidden);
884 boost::shared_ptr<Playlist>
885 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
887 boost::shared_ptr<Playlist> the_copy;
891 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
892 string new_name = _name;
896 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
897 return boost::shared_ptr<Playlist>();
900 partition_internal (start, start+cnt-1, true, thawlist);
903 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
904 (*i)->thaw ("playlist cut");
910 boost::shared_ptr<Playlist>
911 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
915 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
916 string new_name = _name;
920 cnt = min (_get_maximum_extent() - start, cnt);
921 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
925 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
927 times = fabs (times);
928 nframes_t old_length;
931 RegionLock rl1 (this);
932 RegionLock rl2 (other.get());
934 old_length = _get_maximum_extent();
936 int itimes = (int) floor (times);
937 nframes_t pos = position;
938 nframes_t shift = other->_get_maximum_extent();
939 layer_t top_layer = regions.size();
942 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
943 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
945 /* put these new regions on top of all existing ones, but preserve
946 the ordering they had in the original playlist.
949 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
950 add_region_internal (copy_of_region, copy_of_region->position() + pos);
955 possibly_splice_unlocked ();
957 /* XXX shall we handle fractional cases at some point? */
959 if (old_length != _get_maximum_extent()) {
960 notify_length_changed ();
971 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
973 times = fabs (times);
975 RegionLock rl (this);
976 int itimes = (int) floor (times);
977 nframes_t pos = position;
980 boost::shared_ptr<Region> copy = RegionFactory::create (region);
981 add_region_internal (copy, pos);
982 pos += region->length();
985 if (floor (times) != times) {
986 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
988 _session.region_name (name, region->name(), false);
989 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
990 add_region_internal (sub, pos);
995 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
997 RegionLock rl (this);
999 if (!region->covers (playlist_position)) {
1003 if (region->position() == playlist_position ||
1004 region->last_frame() == playlist_position) {
1008 boost::shared_ptr<Region> left;
1009 boost::shared_ptr<Region> right;
1015 before = playlist_position - region->position();
1016 after = region->length() - before;
1019 _session.region_name (before_name, region->name(), false);
1020 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1022 _session.region_name (after_name, region->name(), false);
1023 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1025 add_region_internal (left, region->position());
1026 add_region_internal (right, region->position() + before);
1028 uint64_t orig_layer_op = region->last_layer_op();
1029 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1030 if ((*i)->last_layer_op() > orig_layer_op) {
1031 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1035 left->set_last_layer_op ( orig_layer_op );
1036 right->set_last_layer_op ( orig_layer_op + 1);
1040 finalize_split_region (region, left, right);
1042 if (remove_region_internal (region)) {
1048 Playlist::possibly_splice ()
1050 if (_edit_mode == Splice) {
1056 Playlist::possibly_splice_unlocked ()
1058 if (_edit_mode == Splice) {
1064 Playlist::splice_locked ()
1067 RegionLock rl (this);
1071 notify_length_changed ();
1075 Playlist::splice_unlocked ()
1078 notify_length_changed ();
1082 Playlist::core_splice ()
1086 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1088 RegionList::iterator next;
1093 if (next == regions.end()) {
1097 (*next)->set_position ((*i)->last_frame() + 1, this);
1104 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1106 if (in_set_state || _splicing || _nudging) {
1110 if (what_changed & ARDOUR::PositionChanged) {
1112 /* remove it from the list then add it back in
1113 the right place again.
1116 RegionSortByPosition cmp;
1118 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1120 if (i == regions.end()) {
1121 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1122 _name, region->name())
1128 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1132 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1133 if (holding_state ()) {
1134 pending_bounds.push_back (region);
1136 if (Config->get_layer_model() == MoveAddHigher) {
1137 /* it moved or changed length, so change the timestamp */
1138 timestamp_layer_op (region);
1142 notify_length_changed ();
1144 check_dependents (region, false);
1150 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1152 boost::shared_ptr<Region> region (weak_region.lock());
1159 /* this makes a virtual call to the right kind of playlist ... */
1161 region_changed (what_changed, region);
1165 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1167 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1170 if (in_set_state || in_flush) {
1175 if (what_changed & BoundsChanged) {
1176 region_bounds_changed (what_changed, region);
1177 save = !(_splicing || _nudging);
1180 if ((what_changed & Region::MuteChanged) &&
1181 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1182 check_dependents (region, false);
1185 if (what_changed & our_interests) {
1194 Playlist::drop_regions ()
1196 RegionLock rl (this);
1198 all_regions.clear ();
1202 Playlist::clear (bool with_signals)
1205 RegionLock rl (this);
1206 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1207 pending_removes.insert (*i);
1219 /***********************************************************************
1221 **********************************************************************/
1223 Playlist::RegionList *
1224 Playlist::regions_at (nframes_t frame)
1227 RegionLock rlock (this);
1228 return find_regions_at (frame);
1231 boost::shared_ptr<Region>
1232 Playlist::top_region_at (nframes_t frame)
1235 RegionLock rlock (this);
1236 RegionList *rlist = find_regions_at (frame);
1237 boost::shared_ptr<Region> region;
1239 if (rlist->size()) {
1240 RegionSortByLayer cmp;
1242 region = rlist->back();
1249 Playlist::RegionList *
1250 Playlist::find_regions_at (nframes_t frame)
1252 RegionList *rlist = new RegionList;
1254 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1255 if ((*i)->covers (frame)) {
1256 rlist->push_back (*i);
1263 Playlist::RegionList *
1264 Playlist::regions_touched (nframes_t start, nframes_t end)
1266 RegionLock rlock (this);
1267 RegionList *rlist = new RegionList;
1269 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1270 if ((*i)->coverage (start, end) != OverlapNone) {
1271 rlist->push_back (*i);
1279 boost::shared_ptr<Region>
1280 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1282 RegionLock rlock (this);
1283 boost::shared_ptr<Region> ret;
1284 nframes_t closest = max_frames;
1287 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1290 boost::shared_ptr<Region> r = (*i);
1295 pos = r->first_frame ();
1298 pos = r->last_frame ();
1301 pos = r->adjust_to_sync (r->first_frame());
1306 case 1: /* forwards */
1309 if ((distance = pos - frame) < closest) {
1317 default: /* backwards */
1320 if ((distance = frame - pos) < closest) {
1332 /***********************************************************************/
1337 Playlist::mark_session_dirty ()
1339 if (!in_set_state && !holding_state ()) {
1340 _session.set_dirty();
1345 Playlist::set_state (const XMLNode& node)
1349 XMLNodeConstIterator niter;
1350 XMLPropertyList plist;
1351 XMLPropertyConstIterator piter;
1353 boost::shared_ptr<Region> region;
1358 if (node.name() != "Playlist") {
1365 plist = node.properties();
1367 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1371 if (prop->name() == X_("name")) {
1372 _name = prop->value();
1373 } else if (prop->name() == X_("orig_diskstream_id")) {
1374 _orig_diskstream_id = prop->value ();
1375 } else if (prop->name() == X_("frozen")) {
1376 _frozen = (prop->value() == X_("yes"));
1382 nlist = node.children();
1384 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1388 if (child->name() == "Region") {
1390 if ((prop = child->property ("id")) == 0) {
1391 error << _("region state node has no ID, ignored") << endmsg;
1395 ID id = prop->value ();
1397 if ((region = region_by_id (id))) {
1399 Change what_changed = Change (0);
1401 if (region->set_live_state (*child, what_changed, true)) {
1402 error << _("Playlist: cannot reset region state from XML") << endmsg;
1406 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1407 error << _("Playlist: cannot create region from XML") << endmsg;
1411 add_region (region, region->position(), 1.0);
1413 // So that layer_op ordering doesn't get screwed up
1414 region->set_last_layer_op( region->layer());
1423 /* update dependents, which was not done during add_region_internal
1424 due to in_set_state being true
1427 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1428 check_dependents (*r, false);
1432 first_set_state = false;
1437 Playlist::get_state()
1443 Playlist::get_template()
1445 return state(false);
1449 Playlist::state (bool full_state)
1451 XMLNode *node = new XMLNode (X_("Playlist"));
1454 node->add_property (X_("name"), _name);
1455 node->add_property (X_("type"), _type.to_string());
1457 _orig_diskstream_id.print (buf, sizeof (buf));
1458 node->add_property (X_("orig_diskstream_id"), buf);
1459 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1462 RegionLock rlock (this, false);
1464 cerr << _name << " getting region state for " << regions.size() << endl;
1466 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1467 cerr << "\t" << " now at " << (*i) << endl;
1468 cerr << "\t\t" << (*i)->name() << endl;
1469 node->add_child_nocopy ((*i)->get_state());
1474 node->add_child_copy (*_extra_xml);
1481 Playlist::empty() const
1483 RegionLock rlock (const_cast<Playlist *>(this), false);
1484 return regions.empty();
1488 Playlist::n_regions() const
1490 RegionLock rlock (const_cast<Playlist *>(this), false);
1491 return regions.size();
1495 Playlist::get_maximum_extent () const
1497 RegionLock rlock (const_cast<Playlist *>(this), false);
1498 return _get_maximum_extent ();
1502 Playlist::_get_maximum_extent () const
1504 RegionList::const_iterator i;
1505 nframes_t max_extent = 0;
1508 for (i = regions.begin(); i != regions.end(); ++i) {
1509 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1518 Playlist::bump_name (string name, Session &session)
1520 string newname = name;
1523 newname = Playlist::bump_name_once (newname);
1524 } while (session.playlist_by_name (newname)!=NULL);
1530 Playlist::bump_name_once (string name)
1532 string::size_type period;
1535 if ((period = name.find_last_of ('.')) == string::npos) {
1540 const char *last_element = name.c_str() + period + 1;
1541 for (size_t i = 0; i < strlen(last_element); i++) {
1542 if (!isdigit(last_element[i])) {
1549 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1551 if (isnumber == 0 || errno != 0) {
1552 // last_element is not a number, or is too large
1558 snprintf (buf, sizeof(buf), "%ld", version+1);
1560 newname = name.substr (0, period+1);
1569 Playlist::top_layer() const
1571 RegionLock rlock (const_cast<Playlist *> (this));
1574 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1575 top = max (top, (*i)->layer());
1581 Playlist::set_edit_mode (EditMode mode)
1586 /********************
1588 ********************/
1591 Playlist::relayer ()
1593 RegionList::iterator i;
1596 /* don't send multiple Modified notifications
1597 when multiple regions are relayered.
1602 if (Config->get_layer_model() == MoveAddHigher ||
1603 Config->get_layer_model() == AddHigher) {
1605 RegionSortByLastLayerOp cmp;
1606 RegionList copy = regions;
1610 for (i = copy.begin(); i != copy.end(); ++i) {
1611 (*i)->set_layer (layer++);
1616 /* Session::LaterHigher model */
1618 for (i = regions.begin(); i != regions.end(); ++i) {
1619 (*i)->set_layer (layer++);
1623 /* sending Modified means that various kinds of layering
1624 models operate correctly at the GUI
1625 level. slightly inefficient, but only slightly.
1627 We force a Modified signal here in case no layers actually
1636 /* XXX these layer functions are all deprecated */
1639 Playlist::raise_region (boost::shared_ptr<Region> region)
1641 uint32_t rsz = regions.size();
1642 layer_t target = region->layer() + 1U;
1644 if (target >= rsz) {
1645 /* its already at the effective top */
1649 move_region_to_layer (target, region, 1);
1653 Playlist::lower_region (boost::shared_ptr<Region> region)
1655 if (region->layer() == 0) {
1656 /* its already at the bottom */
1660 layer_t target = region->layer() - 1U;
1662 move_region_to_layer (target, region, -1);
1666 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1668 /* does nothing useful if layering mode is later=higher */
1669 if ((Config->get_layer_model() == MoveAddHigher) ||
1670 (Config->get_layer_model() == AddHigher)) {
1671 timestamp_layer_op (region);
1677 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1679 /* does nothing useful if layering mode is later=higher */
1680 if ((Config->get_layer_model() == MoveAddHigher) ||
1681 (Config->get_layer_model() == AddHigher)) {
1682 region->set_last_layer_op (0);
1688 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1690 RegionList::iterator i;
1691 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1692 list<LayerInfo> layerinfo;
1696 RegionLock rlock (const_cast<Playlist *> (this));
1698 for (i = regions.begin(); i != regions.end(); ++i) {
1706 /* region is moving up, move all regions on intermediate layers
1710 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1711 dest = (*i)->layer() - 1;
1718 /* region is moving down, move all regions on intermediate layers
1722 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1723 dest = (*i)->layer() + 1;
1733 newpair.second = dest;
1735 layerinfo.push_back (newpair);
1739 /* now reset the layers without holding the region lock */
1741 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1742 x->first->set_layer (x->second);
1745 region->set_layer (target_layer);
1748 /* now check all dependents */
1750 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1751 check_dependents (x->first, false);
1754 check_dependents (region, false);
1761 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1763 RegionList::iterator i;
1770 RegionLock rlock (const_cast<Playlist *> (this));
1772 for (i = regions.begin(); i != regions.end(); ++i) {
1774 if ((*i)->position() >= start) {
1778 if ((*i)->last_frame() > max_frames - distance) {
1779 new_pos = max_frames - (*i)->length();
1781 new_pos = (*i)->position() + distance;
1786 if ((*i)->position() > distance) {
1787 new_pos = (*i)->position() - distance;
1793 (*i)->set_position (new_pos, this);
1801 notify_length_changed ();
1806 boost::shared_ptr<Region>
1807 Playlist::find_region (const ID& id) const
1809 RegionLock rlock (const_cast<Playlist*> (this));
1811 /* searches all regions currently in use by the playlist */
1813 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1814 if ((*i)->id() == id) {
1819 return boost::shared_ptr<Region> ();
1822 boost::shared_ptr<Region>
1823 Playlist::region_by_id (ID id)
1825 /* searches all regions ever added to this playlist */
1827 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1828 if ((*i)->id() == id) {
1832 return boost::shared_ptr<Region> ();
1836 Playlist::dump () const
1838 boost::shared_ptr<Region> r;
1840 cerr << "Playlist \"" << _name << "\" " << endl
1841 << regions.size() << " regions "
1844 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1846 cerr << " " << r->name() << " ["
1847 << r->start() << "+" << r->length()
1857 Playlist::set_frozen (bool yn)
1863 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1865 // struct timeval tv;
1866 // gettimeofday (&tv, 0);
1867 region->set_last_layer_op (++layer_op_counter);