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>
43 using namespace ARDOUR;
46 sigc::signal<void,Playlist*> Playlist::PlaylistCreated;
48 struct ShowMeTheList {
49 ShowMeTheList (Playlist *pl, const string& n) : playlist (pl), name (n) {}
51 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
57 struct RegionSortByLayer {
58 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
59 return a->layer() < b->layer();
63 struct RegionSortByPosition {
64 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
65 return a->position() < b->position();
69 struct RegionSortByLastLayerOp {
70 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
71 return a->last_layer_op() < b->last_layer_op();
75 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
84 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
88 const XMLProperty* prop = node.property("type");
89 assert(!prop || DataType(prop->value()) == _type);
92 _name = "unnamed"; /* reset by set_state */
94 /* derived class calls set_state() */
97 Playlist::Playlist (const Playlist& other, string namestr, bool hide)
98 : _name (namestr), _session (other._session), _type(other._type), _orig_diskstream_id(other._orig_diskstream_id)
103 other.copy_regions (tmp);
107 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
108 add_region_internal( (*x), (*x)->position() );
113 _splicing = other._splicing;
114 _nudging = other._nudging;
115 _edit_mode = other._edit_mode;
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;
129 Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, string str, bool hide)
130 : _name (str), _session (other._session), _type(other._type), _orig_diskstream_id(other._orig_diskstream_id)
132 RegionLock rlock2 (&((Playlist&)other));
134 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, true);
188 /* this constructor does NOT notify others (session) */
195 InUse (this, true); /* EMIT SIGNAL */
205 InUse (this, false); /* EMIT SIGNAL */
208 /* nobody knows we exist */
216 Playlist::copy_regions (RegionList& newlist) const
218 RegionLock rlock (const_cast<Playlist *> (this));
220 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
221 newlist.push_back (RegionFactory::RegionFactory::create (*i));
226 Playlist::init (bool hide)
228 g_atomic_int_set (&block_notifications, 0);
229 g_atomic_int_set (&ignore_state_changes, 0);
230 pending_modified = false;
231 pending_length = false;
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 (0);
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;
417 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
418 dependent_checks_needed.insert (*r);
419 /* don't increment n again - its the same list */
422 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
423 dependent_checks_needed.insert (*s);
427 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
428 check_dependents (*s, false);
431 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
432 remove_dependents (*s);
436 if ((freeze_length != _get_maximum_extent()) || pending_length) {
438 LengthChanged(); /* EMIT SIGNAL */
442 if (n || pending_modified) {
447 pending_modified = false;
448 Modified (); /* EMIT SIGNAL */
451 pending_adds.clear ();
452 pending_removes.clear ();
453 pending_bounds.clear ();
458 /*************************************************************
460 *************************************************************/
463 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
465 RegionLock rlock (this);
467 times = fabs (times);
469 int itimes = (int) floor (times);
471 nframes_t pos = position;
474 add_region_internal (region, pos, true);
475 pos += region->length();
479 /* later regions will all be spliced anyway */
481 if (!holding_state ()) {
482 possibly_splice_unlocked ();
485 /* note that itimes can be zero if we being asked to just
486 insert a single fraction of the region.
489 for (int i = 0; i < itimes; ++i) {
490 boost::shared_ptr<Region> copy = RegionFactory::create (region);
491 add_region_internal (copy, pos, true);
492 pos += region->length();
495 if (floor (times) != times) {
496 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
498 _session.region_name (name, region->name(), false);
499 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
500 add_region_internal (sub, pos, true);
505 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position, bool delay_sort)
507 RegionSortByPosition cmp;
508 nframes_t old_length = 0;
510 if (!holding_state()) {
511 old_length = _get_maximum_extent();
514 region->set_playlist (this);
515 region->set_position (position, this);
517 timestamp_layer_op (region);
519 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
520 all_regions.insert (region);
522 if (!holding_state () && !in_set_state) {
523 /* layers get assigned from XML state */
527 /* we need to notify the existence of new region before checking dependents. Ick. */
529 notify_region_added (region);
531 if (!holding_state ()) {
532 check_dependents (region, false);
533 if (old_length != _get_maximum_extent()) {
534 notify_length_changed ();
538 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
539 boost::weak_ptr<Region> (region)));
543 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
545 RegionLock rlock (this);
547 remove_region_internal (old);
548 add_region_internal (newr, pos);
550 if (!holding_state ()) {
551 possibly_splice_unlocked ();
556 Playlist::remove_region (boost::shared_ptr<Region> region)
558 RegionLock rlock (this);
559 remove_region_internal (region);
561 if (!holding_state ()) {
562 possibly_splice_unlocked ();
567 Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort)
569 RegionList::iterator i;
570 nframes_t old_length = 0;
572 if (!holding_state()) {
573 old_length = _get_maximum_extent();
576 for (i = regions.begin(); i != regions.end(); ++i) {
581 if (!holding_state ()) {
583 remove_dependents (region);
585 if (old_length != _get_maximum_extent()) {
586 notify_length_changed ();
590 notify_region_removed (region);
598 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
600 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
601 if (Config->get_use_overlap_equivalency()) {
602 if ((*i)->overlap_equivalent (other)) {
603 results.push_back ((*i));
604 } else if ((*i)->equivalent (other)) {
605 results.push_back ((*i));
612 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
614 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
616 if ((*i) && (*i)->region_list_equivalent (other)) {
617 results.push_back (*i);
623 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
627 partition_internal (start, end, false, thawlist);
629 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
630 (*i)->thaw ("separation");
635 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
637 RegionLock rlock (this);
638 boost::shared_ptr<Region> region;
639 boost::shared_ptr<Region> current;
641 RegionList::iterator tmp;
643 nframes_t pos1, pos2, pos3, pos4;
644 RegionList new_regions;
648 /* need to work from a copy, because otherwise the regions we add during the process
649 get operated on as well.
652 RegionList copy = regions;
654 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
661 if (current->first_frame() == start && current->last_frame() == end) {
663 remove_region_internal (current);
668 if ((overlap = current->coverage (start, end)) == OverlapNone) {
672 pos1 = current->position();
675 pos4 = current->last_frame();
677 if (overlap == OverlapInternal) {
679 /* split: we need 3 new regions, the front, middle and end.
680 cut: we need 2 regions, the front and end.
685 ---------------*************************------------
688 ---------------*****++++++++++++++++====------------
690 ---------------*****----------------====------------
696 /* "middle" ++++++ */
698 _session.region_name (new_name, current->name(), false);
699 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
700 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
701 add_region_internal (region, start, true);
702 new_regions.push_back (region);
707 _session.region_name (new_name, current->name(), false);
708 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
709 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
711 add_region_internal (region, end, true);
712 new_regions.push_back (region);
717 thawlist.push_back (current);
718 current->trim_end (pos2, this);
720 } else if (overlap == OverlapEnd) {
724 ---------------*************************------------
727 ---------------**************+++++++++++------------
729 ---------------**************-----------------------
737 _session.region_name (new_name, current->name(), false);
738 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
739 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
740 add_region_internal (region, start, true);
741 new_regions.push_back (region);
747 thawlist.push_back (current);
748 current->trim_end (pos2, this);
750 } else if (overlap == OverlapStart) {
752 /* split: we need 2 regions: the front and the end.
753 cut: just trim current to skip the cut area
758 ---------------*************************------------
762 ---------------****+++++++++++++++++++++------------
764 -------------------*********************------------
771 _session.region_name (new_name, current->name(), false);
772 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
773 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
774 add_region_internal (region, pos1, true);
775 new_regions.push_back (region);
781 thawlist.push_back (current);
782 current->trim_front (pos3, this);
784 } else if (overlap == OverlapExternal) {
786 /* split: no split required.
787 cut: remove the region.
792 ---------------*************************------------
796 ---------------*************************------------
798 ----------------------------------------------------
803 remove_region_internal (current);
805 new_regions.push_back (current);
809 in_partition = false;
811 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
812 check_dependents (*i, false);
817 Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
823 if (ranges.empty()) {
827 start = ranges.front().start;
830 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
832 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
834 if (i == ranges.begin()) {
838 /* paste the next section into the nascent playlist,
839 offset to reflect the start of the first range we
843 ret->paste (*pl, (*i).start - start, 1.0f);
849 /* manually notify session of new playlist here
850 because the playlists were constructed without notifying
852 PlaylistCreated (ret);
859 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
861 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
862 return cut_copy (pmf, ranges, result_is_hidden);
866 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
868 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
869 return cut_copy (pmf, ranges, result_is_hidden);
873 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
879 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
880 string new_name = _name;
884 if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
888 partition_internal (start, start+cnt-1, true, thawlist);
891 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
892 (*i)->thaw ("playlist cut");
899 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
903 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
904 string new_name = _name;
908 cnt = min (_get_maximum_extent() - start, cnt);
909 return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
913 Playlist::paste (Playlist& other, nframes_t position, float times)
915 times = fabs (times);
916 nframes_t old_length;
919 RegionLock rl1 (this);
920 RegionLock rl2 (&other);
922 old_length = _get_maximum_extent();
924 int itimes = (int) floor (times);
925 nframes_t pos = position;
926 nframes_t shift = other._get_maximum_extent();
927 layer_t top_layer = regions.size();
930 for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
931 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
933 /* put these new regions on top of all existing ones, but preserve
934 the ordering they had in the original playlist.
937 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
938 add_region_internal (copy_of_region, copy_of_region->position() + pos);
943 possibly_splice_unlocked ();
945 /* XXX shall we handle fractional cases at some point? */
947 if (old_length != _get_maximum_extent()) {
948 notify_length_changed ();
959 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
961 times = fabs (times);
963 RegionLock rl (this);
964 int itimes = (int) floor (times);
965 nframes_t pos = position;
968 boost::shared_ptr<Region> copy = RegionFactory::create (region);
969 add_region_internal (copy, pos, true);
970 pos += region->length();
973 if (floor (times) != times) {
974 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
976 _session.region_name (name, region->name(), false);
977 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
978 add_region_internal (sub, pos, true);
983 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
985 RegionLock rl (this);
987 if (!region->covers (playlist_position)) {
991 if (region->position() == playlist_position ||
992 region->last_frame() == playlist_position) {
996 boost::shared_ptr<Region> left;
997 boost::shared_ptr<Region> right;
1003 before = playlist_position - region->position();
1004 after = region->length() - before;
1007 _session.region_name (before_name, region->name(), false);
1008 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1010 _session.region_name (after_name, region->name(), false);
1011 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1013 add_region_internal (left, region->position(), true);
1014 add_region_internal (right, region->position() + before);
1016 uint64_t orig_layer_op = region->last_layer_op();
1017 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1018 if ((*i)->last_layer_op() > orig_layer_op) {
1019 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1023 left->set_last_layer_op ( orig_layer_op );
1024 right->set_last_layer_op ( orig_layer_op + 1);
1028 finalize_split_region (region, left, right);
1030 if (remove_region_internal (region, true)) {
1036 Playlist::possibly_splice ()
1038 if (_edit_mode == Splice) {
1044 Playlist::possibly_splice_unlocked ()
1046 if (_edit_mode == Splice) {
1052 Playlist::splice_locked ()
1055 RegionLock rl (this);
1059 notify_length_changed ();
1063 Playlist::splice_unlocked ()
1066 notify_length_changed ();
1070 Playlist::core_splice ()
1074 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1076 RegionList::iterator next;
1081 if (next == regions.end()) {
1085 (*next)->set_position ((*i)->last_frame() + 1, this);
1092 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1094 if (in_set_state || _splicing || _nudging) {
1098 if (what_changed & ARDOUR::PositionChanged) {
1100 /* remove it from the list then add it back in
1101 the right place again.
1104 RegionSortByPosition cmp;
1106 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1108 if (i == regions.end()) {
1109 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1110 _name, region->name())
1116 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1120 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 check_dependents (region, false);
1132 notify_length_changed ();
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());
1400 /* update dependents, which was not done during add_region_internal
1401 due to in_set_state being true
1404 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1405 check_dependents (*r, false);
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);