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, DataType type, bool hide)
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;
118 in_partition = false;
120 _read_data_count = 0;
121 _frozen = other->_frozen;
123 layer_op_counter = other->layer_op_counter;
124 freeze_length = other->freeze_length;
127 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
128 : _name (str), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
130 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
132 nframes_t end = start + cnt - 1;
138 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
140 boost::shared_ptr<Region> region;
141 boost::shared_ptr<Region> new_region;
142 nframes_t offset = 0;
143 nframes_t position = 0;
150 overlap = region->coverage (start, end);
156 case OverlapInternal:
157 offset = start - region->position();
164 position = region->position() - start;
165 len = end - region->position();
169 offset = start - region->position();
171 len = region->length() - offset;
174 case OverlapExternal:
176 position = region->position() - start;
177 len = region->length();
181 _session.region_name (new_name, region->name(), false);
183 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
185 add_region_internal (new_region, position);
190 /* this constructor does NOT notify others (session) */
197 InUse (true); /* EMIT SIGNAL */
208 InUse (false); /* EMIT SIGNAL */
214 Playlist::copy_regions (RegionList& newlist) const
216 RegionLock rlock (const_cast<Playlist *> (this));
218 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
219 newlist.push_back (RegionFactory::RegionFactory::create (*i));
224 Playlist::init (bool hide)
226 g_atomic_int_set (&block_notifications, 0);
227 g_atomic_int_set (&ignore_state_changes, 0);
228 pending_modified = false;
229 pending_length = false;
235 _edit_mode = Config->get_edit_mode();
237 in_partition = false;
239 _read_data_count = 0;
241 layer_op_counter = 0;
244 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
247 Playlist::Playlist (const Playlist& pl)
248 : _session (pl._session)
249 , _type(pl.data_type())
251 fatal << _("playlist const copy constructor called") << endmsg;
254 Playlist::Playlist (Playlist& pl)
255 : _session (pl._session)
256 , _type(pl.data_type())
258 fatal << _("playlist non-const copy constructor called") << endmsg;
261 Playlist::~Playlist ()
264 RegionLock rl (this);
266 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
267 (*i)->set_playlist (boost::shared_ptr<Playlist>());
271 /* GoingAway must be emitted by derived classes */
275 Playlist::set_name (string str)
277 /* in a typical situation, a playlist is being used
278 by one diskstream and also is referenced by the
279 Session. if there are more references than that,
280 then don't change the name.
288 NameChanged(); /* EMIT SIGNAL */
291 /***********************************************************************
292 CHANGE NOTIFICATION HANDLING
294 Notifications must be delayed till the region_lock is released. This
295 is necessary because handlers for the signals may need to acquire
296 the lock (e.g. to read from the playlist).
297 ***********************************************************************/
302 delay_notifications ();
303 g_atomic_int_inc (&ignore_state_changes);
309 g_atomic_int_dec_and_test (&ignore_state_changes);
310 release_notifications ();
315 Playlist::delay_notifications ()
317 g_atomic_int_inc (&block_notifications);
318 freeze_length = _get_maximum_extent();
322 Playlist::release_notifications ()
324 if (g_atomic_int_dec_and_test (&block_notifications)) {
325 flush_notifications ();
331 Playlist::notify_modified ()
333 if (holding_state ()) {
334 pending_modified = true;
336 pending_modified = false;
337 Modified(); /* EMIT SIGNAL */
342 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
344 if (holding_state ()) {
345 pending_removes.insert (r);
346 pending_modified = true;
347 pending_length = true;
349 /* this might not be true, but we have to act
350 as though it could be.
352 LengthChanged (); /* EMIT SIGNAL */
353 Modified (); /* EMIT SIGNAL */
358 Playlist::notify_region_added (boost::shared_ptr<Region> r)
360 /* the length change might not be true, but we have to act
361 as though it could be.
364 if (holding_state()) {
365 pending_adds.insert (r);
366 pending_modified = true;
367 pending_length = true;
369 LengthChanged (); /* EMIT SIGNAL */
370 Modified (); /* EMIT SIGNAL */
375 Playlist::notify_length_changed ()
377 if (holding_state ()) {
378 pending_length = true;
380 LengthChanged(); /* EMIT SIGNAL */
381 Modified (); /* EMIT SIGNAL */
386 Playlist::flush_notifications ()
388 set<boost::shared_ptr<Region> > dependent_checks_needed;
389 set<boost::shared_ptr<Region> >::iterator s;
398 /* we have no idea what order the regions ended up in pending
399 bounds (it could be based on selection order, for example).
400 so, to preserve layering in the "most recently moved is higher"
401 model, sort them by existing layer, then timestamp them.
404 // RegionSortByLayer cmp;
405 // pending_bounds.sort (cmp);
407 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
408 if (Config->get_layer_model() == MoveAddHigher) {
409 timestamp_layer_op (*r);
411 pending_length = true;
412 dependent_checks_needed.insert (*r);
416 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
417 dependent_checks_needed.insert (*s);
421 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
422 remove_dependents (*s);
426 if ((freeze_length != _get_maximum_extent()) || pending_length) {
428 LengthChanged(); /* EMIT SIGNAL */
432 if (n || pending_modified) {
437 pending_modified = false;
438 Modified (); /* EMIT SIGNAL */
441 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
442 check_dependents (*s, false);
445 pending_adds.clear ();
446 pending_removes.clear ();
447 pending_bounds.clear ();
452 /*************************************************************
454 *************************************************************/
457 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
459 RegionLock rlock (this);
461 times = fabs (times);
463 int itimes = (int) floor (times);
465 nframes_t pos = position;
468 add_region_internal (region, pos);
469 pos += region->length();
473 /* later regions will all be spliced anyway */
475 if (!holding_state ()) {
476 possibly_splice_unlocked ();
479 /* note that itimes can be zero if we being asked to just
480 insert a single fraction of the region.
483 for (int i = 0; i < itimes; ++i) {
484 boost::shared_ptr<Region> copy = RegionFactory::create (region);
485 add_region_internal (copy, pos);
486 pos += region->length();
489 if (floor (times) != times) {
490 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
492 _session.region_name (name, region->name(), false);
493 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
494 add_region_internal (sub, pos);
499 Playlist::set_region_ownership ()
501 RegionLock rl (this);
502 RegionList::iterator i;
503 boost::weak_ptr<Playlist> pl (shared_from_this());
505 for (i = regions.begin(); i != regions.end(); ++i) {
506 (*i)->set_playlist (pl);
511 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
513 RegionSortByPosition cmp;
514 nframes_t old_length = 0;
516 if (!holding_state()) {
517 old_length = _get_maximum_extent();
521 boost::shared_ptr<Playlist> foo (shared_from_this());
522 region->set_playlist (boost::weak_ptr<Playlist>(foo));
525 region->set_position (position, this);
527 timestamp_layer_op (region);
529 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
530 all_regions.insert (region);
532 if (!holding_state () && !in_set_state) {
533 /* layers get assigned from XML state */
537 /* we need to notify the existence of new region before checking dependents. Ick. */
539 notify_region_added (region);
541 if (!holding_state ()) {
542 check_dependents (region, false);
543 if (old_length != _get_maximum_extent()) {
544 notify_length_changed ();
548 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
549 boost::weak_ptr<Region> (region)));
553 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
555 RegionLock rlock (this);
557 remove_region_internal (old);
558 add_region_internal (newr, pos);
560 if (!holding_state ()) {
561 possibly_splice_unlocked ();
566 Playlist::remove_region (boost::shared_ptr<Region> region)
568 RegionLock rlock (this);
569 remove_region_internal (region);
571 if (!holding_state ()) {
572 possibly_splice_unlocked ();
577 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
579 RegionList::iterator i;
580 nframes_t old_length = 0;
582 if (!holding_state()) {
583 old_length = _get_maximum_extent();
586 for (i = regions.begin(); i != regions.end(); ++i) {
591 if (!holding_state ()) {
593 remove_dependents (region);
595 if (old_length != _get_maximum_extent()) {
596 notify_length_changed ();
600 notify_region_removed (region);
608 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
610 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
611 if (Config->get_use_overlap_equivalency()) {
612 if ((*i)->overlap_equivalent (other)) {
613 results.push_back ((*i));
614 } else if ((*i)->equivalent (other)) {
615 results.push_back ((*i));
622 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
624 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
626 if ((*i) && (*i)->region_list_equivalent (other)) {
627 results.push_back (*i);
633 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
637 partition_internal (start, end, false, thawlist);
639 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
640 (*i)->thaw ("separation");
645 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
647 RegionLock rlock (this);
648 boost::shared_ptr<Region> region;
649 boost::shared_ptr<Region> current;
651 RegionList::iterator tmp;
653 nframes_t pos1, pos2, pos3, pos4;
654 RegionList new_regions;
658 /* need to work from a copy, because otherwise the regions we add during the process
659 get operated on as well.
662 RegionList copy = regions;
664 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
671 if (current->first_frame() == start && current->last_frame() == end) {
673 remove_region_internal (current);
678 if ((overlap = current->coverage (start, end)) == OverlapNone) {
682 pos1 = current->position();
685 pos4 = current->last_frame();
687 if (overlap == OverlapInternal) {
689 /* split: we need 3 new regions, the front, middle and end.
690 cut: we need 2 regions, the front and end.
695 ---------------*************************------------
698 ---------------*****++++++++++++++++====------------
700 ---------------*****----------------====------------
706 /* "middle" ++++++ */
708 _session.region_name (new_name, current->name(), false);
709 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
710 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
711 add_region_internal (region, start);
712 new_regions.push_back (region);
717 _session.region_name (new_name, current->name(), false);
718 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
719 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
721 add_region_internal (region, end);
722 new_regions.push_back (region);
727 thawlist.push_back (current);
728 current->trim_end (pos2, this);
730 } else if (overlap == OverlapEnd) {
734 ---------------*************************------------
737 ---------------**************+++++++++++------------
739 ---------------**************-----------------------
747 _session.region_name (new_name, current->name(), false);
748 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
749 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
750 add_region_internal (region, start);
751 new_regions.push_back (region);
757 thawlist.push_back (current);
758 current->trim_end (pos2, this);
760 } else if (overlap == OverlapStart) {
762 /* split: we need 2 regions: the front and the end.
763 cut: just trim current to skip the cut area
768 ---------------*************************------------
772 ---------------****+++++++++++++++++++++------------
774 -------------------*********************------------
781 _session.region_name (new_name, current->name(), false);
782 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
783 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
784 add_region_internal (region, pos1);
785 new_regions.push_back (region);
791 thawlist.push_back (current);
792 current->trim_front (pos3, this);
794 } else if (overlap == OverlapExternal) {
796 /* split: no split required.
797 cut: remove the region.
802 ---------------*************************------------
806 ---------------*************************------------
808 ----------------------------------------------------
813 remove_region_internal (current);
815 new_regions.push_back (current);
819 in_partition = false;
821 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
822 check_dependents (*i, false);
826 boost::shared_ptr<Playlist>
827 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
829 boost::shared_ptr<Playlist> ret;
830 boost::shared_ptr<Playlist> pl;
833 if (ranges.empty()) {
834 return boost::shared_ptr<Playlist>();
837 start = ranges.front().start;
839 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
841 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
843 if (i == ranges.begin()) {
847 /* paste the next section into the nascent playlist,
848 offset to reflect the start of the first range we
852 ret->paste (pl, (*i).start - start, 1.0f);
859 boost::shared_ptr<Playlist>
860 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
862 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
863 return cut_copy (pmf, ranges, result_is_hidden);
866 boost::shared_ptr<Playlist>
867 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
869 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
870 return cut_copy (pmf, ranges, result_is_hidden);
873 boost::shared_ptr<Playlist>
874 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
876 boost::shared_ptr<Playlist> the_copy;
880 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
881 string new_name = _name;
885 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
886 return boost::shared_ptr<Playlist>();
889 partition_internal (start, start+cnt-1, true, thawlist);
892 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
893 (*i)->thaw ("playlist cut");
899 boost::shared_ptr<Playlist>
900 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
904 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
905 string new_name = _name;
909 cnt = min (_get_maximum_extent() - start, cnt);
910 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
914 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
916 times = fabs (times);
917 nframes_t old_length;
920 RegionLock rl1 (this);
921 RegionLock rl2 (other.get());
923 old_length = _get_maximum_extent();
925 int itimes = (int) floor (times);
926 nframes_t pos = position;
927 nframes_t shift = other->_get_maximum_extent();
928 layer_t top_layer = regions.size();
931 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
932 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
934 /* put these new regions on top of all existing ones, but preserve
935 the ordering they had in the original playlist.
938 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
939 add_region_internal (copy_of_region, copy_of_region->position() + pos);
944 possibly_splice_unlocked ();
946 /* XXX shall we handle fractional cases at some point? */
948 if (old_length != _get_maximum_extent()) {
949 notify_length_changed ();
960 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
962 times = fabs (times);
964 RegionLock rl (this);
965 int itimes = (int) floor (times);
966 nframes_t pos = position;
969 boost::shared_ptr<Region> copy = RegionFactory::create (region);
970 add_region_internal (copy, pos);
971 pos += region->length();
974 if (floor (times) != times) {
975 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
977 _session.region_name (name, region->name(), false);
978 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
979 add_region_internal (sub, pos);
984 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
986 RegionLock rl (this);
988 if (!region->covers (playlist_position)) {
992 if (region->position() == playlist_position ||
993 region->last_frame() == playlist_position) {
997 boost::shared_ptr<Region> left;
998 boost::shared_ptr<Region> right;
1004 before = playlist_position - region->position();
1005 after = region->length() - before;
1008 _session.region_name (before_name, region->name(), false);
1009 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1011 _session.region_name (after_name, region->name(), false);
1012 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1014 add_region_internal (left, region->position());
1015 add_region_internal (right, region->position() + before);
1017 uint64_t orig_layer_op = region->last_layer_op();
1018 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1019 if ((*i)->last_layer_op() > orig_layer_op) {
1020 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1024 left->set_last_layer_op ( orig_layer_op );
1025 right->set_last_layer_op ( orig_layer_op + 1);
1029 finalize_split_region (region, left, right);
1031 if (remove_region_internal (region)) {
1037 Playlist::possibly_splice ()
1039 if (_edit_mode == Splice) {
1045 Playlist::possibly_splice_unlocked ()
1047 if (_edit_mode == Splice) {
1053 Playlist::splice_locked ()
1056 RegionLock rl (this);
1060 notify_length_changed ();
1064 Playlist::splice_unlocked ()
1067 notify_length_changed ();
1071 Playlist::core_splice ()
1075 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1077 RegionList::iterator next;
1082 if (next == regions.end()) {
1086 (*next)->set_position ((*i)->last_frame() + 1, this);
1093 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1095 if (in_set_state || _splicing || _nudging) {
1099 if (what_changed & ARDOUR::PositionChanged) {
1101 /* remove it from the list then add it back in
1102 the right place again.
1105 RegionSortByPosition cmp;
1107 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1109 if (i == regions.end()) {
1110 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1111 _name, region->name())
1117 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1121 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1122 if (holding_state ()) {
1123 pending_bounds.push_back (region);
1125 if (Config->get_layer_model() == MoveAddHigher) {
1126 /* it moved or changed length, so change the timestamp */
1127 timestamp_layer_op (region);
1131 notify_length_changed ();
1133 check_dependents (region, false);
1139 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1141 boost::shared_ptr<Region> region (weak_region.lock());
1148 /* this makes a virtual call to the right kind of playlist ... */
1150 region_changed (what_changed, region);
1154 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1156 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1159 if (in_set_state || in_flush) {
1164 if (what_changed & BoundsChanged) {
1165 region_bounds_changed (what_changed, region);
1166 save = !(_splicing || _nudging);
1169 if ((what_changed & Region::MuteChanged) &&
1170 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1171 check_dependents (region, false);
1174 if (what_changed & our_interests) {
1183 Playlist::clear (bool with_signals)
1186 RegionLock rl (this);
1187 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1188 pending_removes.insert (*i);
1200 /***********************************************************************
1202 **********************************************************************/
1204 Playlist::RegionList *
1205 Playlist::regions_at (nframes_t frame)
1208 RegionLock rlock (this);
1209 return find_regions_at (frame);
1212 boost::shared_ptr<Region>
1213 Playlist::top_region_at (nframes_t frame)
1216 RegionLock rlock (this);
1217 RegionList *rlist = find_regions_at (frame);
1218 boost::shared_ptr<Region> region;
1220 if (rlist->size()) {
1221 RegionSortByLayer cmp;
1223 region = rlist->back();
1230 Playlist::RegionList *
1231 Playlist::find_regions_at (nframes_t frame)
1233 RegionList *rlist = new RegionList;
1235 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1236 if ((*i)->covers (frame)) {
1237 rlist->push_back (*i);
1244 Playlist::RegionList *
1245 Playlist::regions_touched (nframes_t start, nframes_t end)
1247 RegionLock rlock (this);
1248 RegionList *rlist = new RegionList;
1250 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1251 if ((*i)->coverage (start, end) != OverlapNone) {
1252 rlist->push_back (*i);
1260 boost::shared_ptr<Region>
1261 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1263 RegionLock rlock (this);
1264 boost::shared_ptr<Region> ret;
1265 nframes_t closest = max_frames;
1268 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1271 boost::shared_ptr<Region> r = (*i);
1276 pos = r->first_frame ();
1279 pos = r->last_frame ();
1282 pos = r->adjust_to_sync (r->first_frame());
1287 case 1: /* forwards */
1290 if ((distance = pos - frame) < closest) {
1298 default: /* backwards */
1301 if ((distance = frame - pos) < closest) {
1313 /***********************************************************************/
1318 Playlist::mark_session_dirty ()
1320 if (!in_set_state && !holding_state ()) {
1321 _session.set_dirty();
1326 Playlist::set_state (const XMLNode& node)
1330 XMLNodeConstIterator niter;
1331 XMLPropertyList plist;
1332 XMLPropertyConstIterator piter;
1334 boost::shared_ptr<Region> region;
1339 if (node.name() != "Playlist") {
1346 plist = node.properties();
1348 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1352 if (prop->name() == X_("name")) {
1353 _name = prop->value();
1354 } else if (prop->name() == X_("orig_diskstream_id")) {
1355 _orig_diskstream_id = prop->value ();
1356 } else if (prop->name() == X_("frozen")) {
1357 _frozen = (prop->value() == X_("yes"));
1363 nlist = node.children();
1365 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1369 if (child->name() == "Region") {
1371 if ((prop = child->property ("id")) == 0) {
1372 error << _("region state node has no ID, ignored") << endmsg;
1376 ID id = prop->value ();
1378 if ((region = region_by_id (id))) {
1380 Change what_changed = Change (0);
1382 if (region->set_live_state (*child, what_changed, true)) {
1383 error << _("Playlist: cannot reset region state from XML") << endmsg;
1387 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1388 error << _("Playlist: cannot create region from XML") << endmsg;
1392 add_region (region, region->position(), 1.0);
1394 // So that layer_op ordering doesn't get screwed up
1395 region->set_last_layer_op( region->layer());
1404 /* update dependents, which was not done during add_region_internal
1405 due to in_set_state being true
1408 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1409 check_dependents (*r, false);
1418 Playlist::get_state()
1424 Playlist::get_template()
1426 return state(false);
1430 Playlist::state (bool full_state)
1432 XMLNode *node = new XMLNode (X_("Playlist"));
1435 node->add_property (X_("name"), _name);
1436 node->add_property (X_("type"), _type.to_string());
1438 _orig_diskstream_id.print (buf, sizeof (buf));
1439 node->add_property (X_("orig_diskstream_id"), buf);
1440 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1443 RegionLock rlock (this, false);
1445 cerr << _name << " getting region state for " << regions.size() << endl;
1447 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1448 cerr << "\t" << " now at " << (*i) << endl;
1449 cerr << "\t\t" << (*i)->name() << endl;
1450 node->add_child_nocopy ((*i)->get_state());
1455 node->add_child_copy (*_extra_xml);
1462 Playlist::empty() const
1464 RegionLock rlock (const_cast<Playlist *>(this), false);
1465 return regions.empty();
1469 Playlist::n_regions() const
1471 RegionLock rlock (const_cast<Playlist *>(this), false);
1472 return regions.size();
1476 Playlist::get_maximum_extent () const
1478 RegionLock rlock (const_cast<Playlist *>(this), false);
1479 return _get_maximum_extent ();
1483 Playlist::_get_maximum_extent () const
1485 RegionList::const_iterator i;
1486 nframes_t max_extent = 0;
1489 for (i = regions.begin(); i != regions.end(); ++i) {
1490 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1499 Playlist::bump_name (string name, Session &session)
1501 string newname = name;
1504 newname = Playlist::bump_name_once (newname);
1505 } while (session.playlist_by_name (newname)!=NULL);
1511 Playlist::bump_name_once (string name)
1513 string::size_type period;
1516 if ((period = name.find_last_of ('.')) == string::npos) {
1523 sscanf (name.substr (period+1).c_str(), "%d", &version);
1524 snprintf (buf, sizeof(buf), "%d", version+1);
1526 newname = name.substr (0, period+1);
1534 Playlist::top_layer() const
1536 RegionLock rlock (const_cast<Playlist *> (this));
1539 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1540 top = max (top, (*i)->layer());
1546 Playlist::set_edit_mode (EditMode mode)
1551 /********************
1553 ********************/
1556 Playlist::relayer ()
1558 RegionList::iterator i;
1561 /* don't send multiple Modified notifications
1562 when multiple regions are relayered.
1567 if (Config->get_layer_model() == MoveAddHigher ||
1568 Config->get_layer_model() == AddHigher) {
1570 RegionSortByLastLayerOp cmp;
1571 RegionList copy = regions;
1575 for (i = copy.begin(); i != copy.end(); ++i) {
1576 (*i)->set_layer (layer++);
1581 /* Session::LaterHigher model */
1583 for (i = regions.begin(); i != regions.end(); ++i) {
1584 (*i)->set_layer (layer++);
1588 /* sending Modified means that various kinds of layering
1589 models operate correctly at the GUI
1590 level. slightly inefficient, but only slightly.
1592 We force a Modified signal here in case no layers actually
1601 /* XXX these layer functions are all deprecated */
1604 Playlist::raise_region (boost::shared_ptr<Region> region)
1606 uint32_t rsz = regions.size();
1607 layer_t target = region->layer() + 1U;
1609 if (target >= rsz) {
1610 /* its already at the effective top */
1614 move_region_to_layer (target, region, 1);
1618 Playlist::lower_region (boost::shared_ptr<Region> region)
1620 if (region->layer() == 0) {
1621 /* its already at the bottom */
1625 layer_t target = region->layer() - 1U;
1627 move_region_to_layer (target, region, -1);
1631 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1633 /* does nothing useful if layering mode is later=higher */
1634 if ((Config->get_layer_model() == MoveAddHigher) ||
1635 (Config->get_layer_model() == AddHigher)) {
1636 timestamp_layer_op (region);
1642 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1644 /* does nothing useful if layering mode is later=higher */
1645 if ((Config->get_layer_model() == MoveAddHigher) ||
1646 (Config->get_layer_model() == AddHigher)) {
1647 region->set_last_layer_op (0);
1653 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1655 RegionList::iterator i;
1656 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1657 list<LayerInfo> layerinfo;
1661 RegionLock rlock (const_cast<Playlist *> (this));
1663 for (i = regions.begin(); i != regions.end(); ++i) {
1671 /* region is moving up, move all regions on intermediate layers
1675 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1676 dest = (*i)->layer() - 1;
1683 /* region is moving down, move all regions on intermediate layers
1687 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1688 dest = (*i)->layer() + 1;
1698 newpair.second = dest;
1700 layerinfo.push_back (newpair);
1704 /* now reset the layers without holding the region lock */
1706 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1707 x->first->set_layer (x->second);
1710 region->set_layer (target_layer);
1712 /* now check all dependents */
1714 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1715 check_dependents (x->first, false);
1718 check_dependents (region, false);
1724 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1726 RegionList::iterator i;
1733 RegionLock rlock (const_cast<Playlist *> (this));
1735 for (i = regions.begin(); i != regions.end(); ++i) {
1737 if ((*i)->position() >= start) {
1741 if ((*i)->last_frame() > max_frames - distance) {
1742 new_pos = max_frames - (*i)->length();
1744 new_pos = (*i)->position() + distance;
1749 if ((*i)->position() > distance) {
1750 new_pos = (*i)->position() - distance;
1756 (*i)->set_position (new_pos, this);
1764 notify_length_changed ();
1769 boost::shared_ptr<Region>
1770 Playlist::find_region (const ID& id) const
1772 RegionLock rlock (const_cast<Playlist*> (this));
1774 /* searches all regions currently in use by the playlist */
1776 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1777 if ((*i)->id() == id) {
1782 return boost::shared_ptr<Region> ();
1785 boost::shared_ptr<Region>
1786 Playlist::region_by_id (ID id)
1788 /* searches all regions ever added to this playlist */
1790 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1791 if ((*i)->id() == id) {
1795 return boost::shared_ptr<Region> ();
1799 Playlist::dump () const
1801 boost::shared_ptr<Region> r;
1803 cerr << "Playlist \"" << _name << "\" " << endl
1804 << regions.size() << " regions "
1807 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1809 cerr << " " << r->name() << " ["
1810 << r->start() << "+" << r->length()
1820 Playlist::set_frozen (bool yn)
1826 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1828 // struct timeval tv;
1829 // gettimeofday (&tv, 0);
1830 region->set_last_layer_op (++layer_op_counter);