2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include <pbd/failed_constructor.h>
32 #include <pbd/stl_delete.h>
33 #include <pbd/xml++.h>
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/region.h>
38 #include <ardour/region_factory.h>
39 #include <ardour/playlist_factory.h>
44 using namespace ARDOUR;
47 struct ShowMeTheList {
48 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
50 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
52 boost::shared_ptr<Playlist> playlist;
56 struct RegionSortByLayer {
57 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
58 return a->layer() < b->layer();
62 struct RegionSortByPosition {
63 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
64 return a->position() < b->position();
68 struct RegionSortByLastLayerOp {
69 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
70 return a->last_layer_op() < b->last_layer_op();
74 Playlist::Playlist (Session& sess, string nom, bool hide)
82 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
86 _name = "unnamed"; /* reset by set_state */
88 /* set state called by derived class */
91 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
92 : _name (namestr), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
97 other->copy_regions (tmp);
101 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
102 add_region_internal( (*x), (*x)->position());
107 _splicing = other->_splicing;
108 _nudging = other->_nudging;
109 _edit_mode = other->_edit_mode;
113 in_partition = false;
115 _read_data_count = 0;
116 _frozen = other->_frozen;
118 layer_op_counter = other->layer_op_counter;
119 freeze_length = other->freeze_length;
122 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
123 : _name (str), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
125 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
127 nframes_t end = start + cnt - 1;
133 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
135 boost::shared_ptr<Region> region;
136 boost::shared_ptr<Region> new_region;
137 nframes_t offset = 0;
138 nframes_t position = 0;
145 overlap = region->coverage (start, end);
151 case OverlapInternal:
152 offset = start - region->position();
159 position = region->position() - start;
160 len = end - region->position();
164 offset = start - region->position();
166 len = region->length() - offset;
169 case OverlapExternal:
171 position = region->position() - start;
172 len = region->length();
176 _session.region_name (new_name, region->name(), false);
178 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
180 add_region_internal (new_region, position);
185 /* this constructor does NOT notify others (session) */
192 InUse (true); /* EMIT SIGNAL */
203 InUse (false); /* EMIT SIGNAL */
208 Playlist::copy_regions (RegionList& newlist) const
210 RegionLock rlock (const_cast<Playlist *> (this));
212 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
213 newlist.push_back (RegionFactory::RegionFactory::create (*i));
218 Playlist::init (bool hide)
220 g_atomic_int_set (&block_notifications, 0);
221 g_atomic_int_set (&ignore_state_changes, 0);
222 pending_modified = false;
223 pending_length = false;
229 _edit_mode = Config->get_edit_mode();
231 in_partition = false;
233 _read_data_count = 0;
235 layer_op_counter = 0;
238 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
241 Playlist::Playlist (const Playlist& pl)
242 : _session (pl._session)
244 fatal << _("playlist const copy constructor called") << endmsg;
247 Playlist::Playlist (Playlist& pl)
248 : _session (pl._session)
250 fatal << _("playlist non-const copy constructor called") << endmsg;
253 Playlist::~Playlist ()
256 RegionLock rl (this);
258 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
259 (*i)->set_playlist (boost::shared_ptr<Playlist>());
263 /* GoingAway must be emitted by derived classes */
267 Playlist::set_name (string str)
269 /* in a typical situation, a playlist is being used
270 by one diskstream and also is referenced by the
271 Session. if there are more references than that,
272 then don't change the name.
280 NameChanged(); /* EMIT SIGNAL */
283 /***********************************************************************
284 CHANGE NOTIFICATION HANDLING
286 Notifications must be delayed till the region_lock is released. This
287 is necessary because handlers for the signals may need to acquire
288 the lock (e.g. to read from the playlist).
289 ***********************************************************************/
294 delay_notifications ();
295 g_atomic_int_inc (&ignore_state_changes);
301 g_atomic_int_dec_and_test (&ignore_state_changes);
302 release_notifications ();
307 Playlist::delay_notifications ()
309 g_atomic_int_inc (&block_notifications);
310 freeze_length = _get_maximum_extent();
314 Playlist::release_notifications ()
316 if (g_atomic_int_dec_and_test (&block_notifications)) {
317 flush_notifications ();
323 Playlist::notify_modified ()
325 if (holding_state ()) {
326 pending_modified = true;
328 pending_modified = false;
329 Modified(); /* EMIT SIGNAL */
334 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
336 if (holding_state ()) {
337 pending_removes.insert (r);
338 pending_modified = true;
339 pending_length = true;
341 /* this might not be true, but we have to act
342 as though it could be.
344 LengthChanged (); /* EMIT SIGNAL */
345 Modified (); /* EMIT SIGNAL */
350 Playlist::notify_region_added (boost::shared_ptr<Region> r)
352 /* the length change might not be true, but we have to act
353 as though it could be.
356 if (holding_state()) {
357 pending_adds.insert (r);
358 pending_modified = true;
359 pending_length = true;
361 LengthChanged (); /* EMIT SIGNAL */
362 Modified (); /* EMIT SIGNAL */
367 Playlist::notify_length_changed ()
369 if (holding_state ()) {
370 pending_length = true;
372 LengthChanged(); /* EMIT SIGNAL */
373 Modified (); /* EMIT SIGNAL */
378 Playlist::flush_notifications ()
380 set<boost::shared_ptr<Region> > dependent_checks_needed;
381 set<boost::shared_ptr<Region> >::iterator s;
390 /* we have no idea what order the regions ended up in pending
391 bounds (it could be based on selection order, for example).
392 so, to preserve layering in the "most recently moved is higher"
393 model, sort them by existing layer, then timestamp them.
396 // RegionSortByLayer cmp;
397 // pending_bounds.sort (cmp);
399 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
400 if (Config->get_layer_model() == MoveAddHigher) {
401 timestamp_layer_op (*r);
403 pending_length = true;
404 dependent_checks_needed.insert (*r);
408 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
409 dependent_checks_needed.insert (*s);
413 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
414 remove_dependents (*s);
418 if ((freeze_length != _get_maximum_extent()) || pending_length) {
420 LengthChanged(); /* EMIT SIGNAL */
424 if (n || pending_modified) {
429 pending_modified = false;
430 Modified (); /* EMIT SIGNAL */
433 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
434 check_dependents (*s, false);
437 pending_adds.clear ();
438 pending_removes.clear ();
439 pending_bounds.clear ();
444 /*************************************************************
446 *************************************************************/
449 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
451 RegionLock rlock (this);
453 times = fabs (times);
455 int itimes = (int) floor (times);
457 nframes_t pos = position;
460 add_region_internal (region, pos);
461 pos += region->length();
465 /* later regions will all be spliced anyway */
467 if (!holding_state ()) {
468 possibly_splice_unlocked ();
471 /* note that itimes can be zero if we being asked to just
472 insert a single fraction of the region.
475 for (int i = 0; i < itimes; ++i) {
476 boost::shared_ptr<Region> copy = RegionFactory::create (region);
477 add_region_internal (copy, pos);
478 pos += region->length();
481 if (floor (times) != times) {
482 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
484 _session.region_name (name, region->name(), false);
485 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
486 add_region_internal (sub, pos);
491 Playlist::set_region_ownership ()
493 RegionLock rl (this);
494 RegionList::iterator i;
495 boost::weak_ptr<Playlist> pl (shared_from_this());
497 for (i = regions.begin(); i != regions.end(); ++i) {
498 (*i)->set_playlist (pl);
503 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
505 RegionSortByPosition cmp;
506 nframes_t old_length = 0;
508 if (!holding_state()) {
509 old_length = _get_maximum_extent();
513 boost::shared_ptr<Playlist> foo (shared_from_this());
514 region->set_playlist (boost::weak_ptr<Playlist>(foo));
517 region->set_position (position, this);
519 timestamp_layer_op (region);
521 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
522 all_regions.insert (region);
524 if (!holding_state () && !in_set_state) {
525 /* layers get assigned from XML state */
529 /* we need to notify the existence of new region before checking dependents. Ick. */
531 notify_region_added (region);
533 if (!holding_state ()) {
534 check_dependents (region, false);
535 if (old_length != _get_maximum_extent()) {
536 notify_length_changed ();
540 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
541 boost::weak_ptr<Region> (region)));
545 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
547 RegionLock rlock (this);
549 remove_region_internal (old);
550 add_region_internal (newr, pos);
552 if (!holding_state ()) {
553 possibly_splice_unlocked ();
558 Playlist::remove_region (boost::shared_ptr<Region> region)
560 RegionLock rlock (this);
561 remove_region_internal (region);
563 if (!holding_state ()) {
564 possibly_splice_unlocked ();
569 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
571 RegionList::iterator i;
572 nframes_t old_length = 0;
574 if (!holding_state()) {
575 old_length = _get_maximum_extent();
578 for (i = regions.begin(); i != regions.end(); ++i) {
583 if (!holding_state ()) {
585 remove_dependents (region);
587 if (old_length != _get_maximum_extent()) {
588 notify_length_changed ();
592 notify_region_removed (region);
600 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
602 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
603 if (Config->get_use_overlap_equivalency()) {
604 if ((*i)->overlap_equivalent (other)) {
605 results.push_back ((*i));
606 } else if ((*i)->equivalent (other)) {
607 results.push_back ((*i));
614 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
616 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
618 if ((*i) && (*i)->region_list_equivalent (other)) {
619 results.push_back (*i);
625 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
629 partition_internal (start, end, false, thawlist);
631 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
632 (*i)->thaw ("separation");
637 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
639 RegionLock rlock (this);
640 boost::shared_ptr<Region> region;
641 boost::shared_ptr<Region> current;
643 RegionList::iterator tmp;
645 nframes_t pos1, pos2, pos3, pos4;
646 RegionList new_regions;
650 /* need to work from a copy, because otherwise the regions we add during the process
651 get operated on as well.
654 RegionList copy = regions;
656 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
663 if (current->first_frame() == start && current->last_frame() == end) {
665 remove_region_internal (current);
670 if ((overlap = current->coverage (start, end)) == OverlapNone) {
674 pos1 = current->position();
677 pos4 = current->last_frame();
679 if (overlap == OverlapInternal) {
681 /* split: we need 3 new regions, the front, middle and end.
682 cut: we need 2 regions, the front and end.
687 ---------------*************************------------
690 ---------------*****++++++++++++++++====------------
692 ---------------*****----------------====------------
698 /* "middle" ++++++ */
700 _session.region_name (new_name, current->name(), false);
701 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
702 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
703 add_region_internal (region, start);
704 new_regions.push_back (region);
709 _session.region_name (new_name, current->name(), false);
710 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
711 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
713 add_region_internal (region, end);
714 new_regions.push_back (region);
719 thawlist.push_back (current);
720 current->trim_end (pos2, this);
722 } else if (overlap == OverlapEnd) {
726 ---------------*************************------------
729 ---------------**************+++++++++++------------
731 ---------------**************-----------------------
739 _session.region_name (new_name, current->name(), false);
740 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
741 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
742 add_region_internal (region, start);
743 new_regions.push_back (region);
749 thawlist.push_back (current);
750 current->trim_end (pos2, this);
752 } else if (overlap == OverlapStart) {
754 /* split: we need 2 regions: the front and the end.
755 cut: just trim current to skip the cut area
760 ---------------*************************------------
764 ---------------****+++++++++++++++++++++------------
766 -------------------*********************------------
773 _session.region_name (new_name, current->name(), false);
774 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
775 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
776 add_region_internal (region, pos1);
777 new_regions.push_back (region);
783 thawlist.push_back (current);
784 current->trim_front (pos3, this);
786 } else if (overlap == OverlapExternal) {
788 /* split: no split required.
789 cut: remove the region.
794 ---------------*************************------------
798 ---------------*************************------------
800 ----------------------------------------------------
805 remove_region_internal (current);
807 new_regions.push_back (current);
811 in_partition = false;
813 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
814 check_dependents (*i, false);
818 boost::shared_ptr<Playlist>
819 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
821 boost::shared_ptr<Playlist> ret;
822 boost::shared_ptr<Playlist> pl;
825 if (ranges.empty()) {
826 return boost::shared_ptr<Playlist>();
829 start = ranges.front().start;
831 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
833 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
835 if (i == ranges.begin()) {
839 /* paste the next section into the nascent playlist,
840 offset to reflect the start of the first range we
844 ret->paste (pl, (*i).start - start, 1.0f);
851 boost::shared_ptr<Playlist>
852 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
854 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
855 return cut_copy (pmf, ranges, result_is_hidden);
858 boost::shared_ptr<Playlist>
859 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
861 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
862 return cut_copy (pmf, ranges, result_is_hidden);
865 boost::shared_ptr<Playlist>
866 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
868 boost::shared_ptr<Playlist> the_copy;
872 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
873 string new_name = _name;
877 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
878 return boost::shared_ptr<Playlist>();
881 partition_internal (start, start+cnt-1, true, thawlist);
884 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
885 (*i)->thaw ("playlist cut");
891 boost::shared_ptr<Playlist>
892 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
896 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
897 string new_name = _name;
901 cnt = min (_get_maximum_extent() - start, cnt);
902 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
906 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
908 times = fabs (times);
909 nframes_t old_length;
912 RegionLock rl1 (this);
913 RegionLock rl2 (other.get());
915 old_length = _get_maximum_extent();
917 int itimes = (int) floor (times);
918 nframes_t pos = position;
919 nframes_t shift = other->_get_maximum_extent();
920 layer_t top_layer = regions.size();
923 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
924 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
926 /* put these new regions on top of all existing ones, but preserve
927 the ordering they had in the original playlist.
930 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
931 add_region_internal (copy_of_region, copy_of_region->position() + pos);
936 possibly_splice_unlocked ();
938 /* XXX shall we handle fractional cases at some point? */
940 if (old_length != _get_maximum_extent()) {
941 notify_length_changed ();
952 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
954 times = fabs (times);
956 RegionLock rl (this);
957 int itimes = (int) floor (times);
958 nframes_t pos = position;
961 boost::shared_ptr<Region> copy = RegionFactory::create (region);
962 add_region_internal (copy, pos);
963 pos += region->length();
966 if (floor (times) != times) {
967 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
969 _session.region_name (name, region->name(), false);
970 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
971 add_region_internal (sub, pos);
976 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
978 RegionLock rl (this);
980 if (!region->covers (playlist_position)) {
984 if (region->position() == playlist_position ||
985 region->last_frame() == playlist_position) {
989 boost::shared_ptr<Region> left;
990 boost::shared_ptr<Region> right;
996 before = playlist_position - region->position();
997 after = region->length() - before;
1000 _session.region_name (before_name, region->name(), false);
1001 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1003 _session.region_name (after_name, region->name(), false);
1004 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1006 add_region_internal (left, region->position());
1007 add_region_internal (right, region->position() + before);
1009 uint64_t orig_layer_op = region->last_layer_op();
1010 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1011 if ((*i)->last_layer_op() > orig_layer_op) {
1012 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1016 left->set_last_layer_op ( orig_layer_op );
1017 right->set_last_layer_op ( orig_layer_op + 1);
1021 finalize_split_region (region, left, right);
1023 if (remove_region_internal (region)) {
1029 Playlist::possibly_splice ()
1031 if (_edit_mode == Splice) {
1037 Playlist::possibly_splice_unlocked ()
1039 if (_edit_mode == Splice) {
1045 Playlist::splice_locked ()
1048 RegionLock rl (this);
1052 notify_length_changed ();
1056 Playlist::splice_unlocked ()
1059 notify_length_changed ();
1063 Playlist::core_splice ()
1067 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1069 RegionList::iterator next;
1074 if (next == regions.end()) {
1078 (*next)->set_position ((*i)->last_frame() + 1, this);
1085 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1087 if (in_set_state || _splicing || _nudging) {
1091 if (what_changed & ARDOUR::PositionChanged) {
1093 /* remove it from the list then add it back in
1094 the right place again.
1097 RegionSortByPosition cmp;
1099 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1101 if (i == regions.end()) {
1102 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1103 _name, region->name())
1109 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1113 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1114 if (holding_state ()) {
1115 pending_bounds.push_back (region);
1117 if (Config->get_layer_model() == MoveAddHigher) {
1118 /* it moved or changed length, so change the timestamp */
1119 timestamp_layer_op (region);
1123 notify_length_changed ();
1125 check_dependents (region, false);
1131 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1133 boost::shared_ptr<Region> region (weak_region.lock());
1140 /* this makes a virtual call to the right kind of playlist ... */
1142 region_changed (what_changed, region);
1146 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1148 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1151 if (in_set_state || in_flush) {
1156 if (what_changed & BoundsChanged) {
1157 region_bounds_changed (what_changed, region);
1158 save = !(_splicing || _nudging);
1161 if ((what_changed & Region::MuteChanged) &&
1162 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1163 check_dependents (region, false);
1166 if (what_changed & our_interests) {
1175 Playlist::clear (bool with_signals)
1178 RegionLock rl (this);
1179 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1180 pending_removes.insert (*i);
1192 /***********************************************************************
1194 **********************************************************************/
1196 Playlist::RegionList *
1197 Playlist::regions_at (nframes_t frame)
1200 RegionLock rlock (this);
1201 return find_regions_at (frame);
1204 boost::shared_ptr<Region>
1205 Playlist::top_region_at (nframes_t frame)
1208 RegionLock rlock (this);
1209 RegionList *rlist = find_regions_at (frame);
1210 boost::shared_ptr<Region> region;
1212 if (rlist->size()) {
1213 RegionSortByLayer cmp;
1215 region = rlist->back();
1222 Playlist::RegionList *
1223 Playlist::find_regions_at (nframes_t frame)
1225 RegionList *rlist = new RegionList;
1227 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1228 if ((*i)->covers (frame)) {
1229 rlist->push_back (*i);
1236 Playlist::RegionList *
1237 Playlist::regions_touched (nframes_t start, nframes_t end)
1239 RegionLock rlock (this);
1240 RegionList *rlist = new RegionList;
1242 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1243 if ((*i)->coverage (start, end) != OverlapNone) {
1244 rlist->push_back (*i);
1252 boost::shared_ptr<Region>
1253 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1255 RegionLock rlock (this);
1256 boost::shared_ptr<Region> ret;
1257 nframes_t closest = max_frames;
1260 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1263 boost::shared_ptr<Region> r = (*i);
1268 pos = r->first_frame ();
1271 pos = r->last_frame ();
1274 pos = r->adjust_to_sync (r->first_frame());
1279 case 1: /* forwards */
1282 if ((distance = pos - frame) < closest) {
1290 default: /* backwards */
1293 if ((distance = frame - pos) < closest) {
1305 /***********************************************************************/
1310 Playlist::mark_session_dirty ()
1312 if (!in_set_state && !holding_state ()) {
1313 _session.set_dirty();
1318 Playlist::set_state (const XMLNode& node)
1322 XMLNodeConstIterator niter;
1323 XMLPropertyList plist;
1324 XMLPropertyConstIterator piter;
1326 boost::shared_ptr<Region> region;
1331 if (node.name() != "Playlist") {
1338 plist = node.properties();
1340 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1344 if (prop->name() == X_("name")) {
1345 _name = prop->value();
1346 } else if (prop->name() == X_("orig_diskstream_id")) {
1347 _orig_diskstream_id = prop->value ();
1348 } else if (prop->name() == X_("frozen")) {
1349 _frozen = (prop->value() == X_("yes"));
1355 nlist = node.children();
1357 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1361 if (child->name() == "Region") {
1363 if ((prop = child->property ("id")) == 0) {
1364 error << _("region state node has no ID, ignored") << endmsg;
1368 ID id = prop->value ();
1370 if ((region = region_by_id (id))) {
1372 Change what_changed = Change (0);
1374 if (region->set_live_state (*child, what_changed, true)) {
1375 error << _("Playlist: cannot reset region state from XML") << endmsg;
1379 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1380 error << _("Playlist: cannot create region from XML") << endmsg;
1384 add_region (region, region->position(), 1.0);
1386 // So that layer_op ordering doesn't get screwed up
1387 region->set_last_layer_op( region->layer());
1396 /* update dependents, which was not done during add_region_internal
1397 due to in_set_state being true
1400 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1401 check_dependents (*r, false);
1410 Playlist::get_state()
1416 Playlist::get_template()
1418 return state(false);
1422 Playlist::state (bool full_state)
1424 XMLNode *node = new XMLNode (X_("Playlist"));
1427 node->add_property (X_("name"), _name);
1429 _orig_diskstream_id.print (buf, sizeof (buf));
1430 node->add_property (X_("orig_diskstream_id"), buf);
1431 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1434 RegionLock rlock (this, false);
1435 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1436 node->add_child_nocopy ((*i)->get_state());
1441 node->add_child_copy (*_extra_xml);
1448 Playlist::empty() const
1450 RegionLock rlock (const_cast<Playlist *>(this), false);
1451 return regions.empty();
1455 Playlist::n_regions() const
1457 RegionLock rlock (const_cast<Playlist *>(this), false);
1458 return regions.size();
1462 Playlist::get_maximum_extent () const
1464 RegionLock rlock (const_cast<Playlist *>(this), false);
1465 return _get_maximum_extent ();
1469 Playlist::_get_maximum_extent () const
1471 RegionList::const_iterator i;
1472 nframes_t max_extent = 0;
1475 for (i = regions.begin(); i != regions.end(); ++i) {
1476 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1485 Playlist::bump_name (string name, Session &session)
1487 string newname = name;
1490 newname = Playlist::bump_name_once (newname);
1491 } while (session.playlist_by_name (newname)!=NULL);
1497 Playlist::bump_name_once (string name)
1499 string::size_type period;
1502 if ((period = name.find_last_of ('.')) == string::npos) {
1509 sscanf (name.substr (period+1).c_str(), "%d", &version);
1510 snprintf (buf, sizeof(buf), "%d", version+1);
1512 newname = name.substr (0, period+1);
1520 Playlist::top_layer() const
1522 RegionLock rlock (const_cast<Playlist *> (this));
1525 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1526 top = max (top, (*i)->layer());
1532 Playlist::set_edit_mode (EditMode mode)
1537 /********************
1539 ********************/
1542 Playlist::relayer ()
1544 RegionList::iterator i;
1547 /* don't send multiple Modified notifications
1548 when multiple regions are relayered.
1553 if (Config->get_layer_model() == MoveAddHigher ||
1554 Config->get_layer_model() == AddHigher) {
1556 RegionSortByLastLayerOp cmp;
1557 RegionList copy = regions;
1561 for (i = copy.begin(); i != copy.end(); ++i) {
1562 (*i)->set_layer (layer++);
1567 /* Session::LaterHigher model */
1569 for (i = regions.begin(); i != regions.end(); ++i) {
1570 (*i)->set_layer (layer++);
1574 /* sending Modified means that various kinds of layering
1575 models operate correctly at the GUI
1576 level. slightly inefficient, but only slightly.
1578 We force a Modified signal here in case no layers actually
1587 /* XXX these layer functions are all deprecated */
1590 Playlist::raise_region (boost::shared_ptr<Region> region)
1592 uint32_t rsz = regions.size();
1593 layer_t target = region->layer() + 1U;
1595 if (target >= rsz) {
1596 /* its already at the effective top */
1600 move_region_to_layer (target, region, 1);
1604 Playlist::lower_region (boost::shared_ptr<Region> region)
1606 if (region->layer() == 0) {
1607 /* its already at the bottom */
1611 layer_t target = region->layer() - 1U;
1613 move_region_to_layer (target, region, -1);
1617 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1619 /* does nothing useful if layering mode is later=higher */
1620 if ((Config->get_layer_model() == MoveAddHigher) ||
1621 (Config->get_layer_model() == AddHigher)) {
1622 timestamp_layer_op (region);
1628 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1630 /* does nothing useful if layering mode is later=higher */
1631 if ((Config->get_layer_model() == MoveAddHigher) ||
1632 (Config->get_layer_model() == AddHigher)) {
1633 region->set_last_layer_op (0);
1639 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1641 RegionList::iterator i;
1642 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1643 list<LayerInfo> layerinfo;
1647 RegionLock rlock (const_cast<Playlist *> (this));
1649 for (i = regions.begin(); i != regions.end(); ++i) {
1657 /* region is moving up, move all regions on intermediate layers
1661 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1662 dest = (*i)->layer() - 1;
1669 /* region is moving down, move all regions on intermediate layers
1673 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1674 dest = (*i)->layer() + 1;
1684 newpair.second = dest;
1686 layerinfo.push_back (newpair);
1690 /* now reset the layers without holding the region lock */
1692 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1693 x->first->set_layer (x->second);
1696 region->set_layer (target_layer);
1698 /* now check all dependents */
1700 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1701 check_dependents (x->first, false);
1704 check_dependents (region, false);
1710 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1712 RegionList::iterator i;
1719 RegionLock rlock (const_cast<Playlist *> (this));
1721 for (i = regions.begin(); i != regions.end(); ++i) {
1723 if ((*i)->position() >= start) {
1727 if ((*i)->last_frame() > max_frames - distance) {
1728 new_pos = max_frames - (*i)->length();
1730 new_pos = (*i)->position() + distance;
1735 if ((*i)->position() > distance) {
1736 new_pos = (*i)->position() - distance;
1742 (*i)->set_position (new_pos, this);
1750 notify_length_changed ();
1755 boost::shared_ptr<Region>
1756 Playlist::find_region (const ID& id) const
1758 RegionLock rlock (const_cast<Playlist*> (this));
1760 /* searches all regions currently in use by the playlist */
1762 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1763 if ((*i)->id() == id) {
1768 return boost::shared_ptr<Region> ();
1771 boost::shared_ptr<Region>
1772 Playlist::region_by_id (ID id)
1774 /* searches all regions ever added to this playlist */
1776 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1777 if ((*i)->id() == id) {
1781 return boost::shared_ptr<Region> ();
1785 Playlist::dump () const
1787 boost::shared_ptr<Region> r;
1789 cerr << "Playlist \"" << _name << "\" " << endl
1790 << regions.size() << " regions "
1793 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1795 cerr << " " << r->name() << " ["
1796 << r->start() << "+" << r->length()
1806 Playlist::set_frozen (bool yn)
1812 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1814 // struct timeval tv;
1815 // gettimeofday (&tv, 0);
1816 region->set_last_layer_op (++layer_op_counter);