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, bool hide)
83 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
87 _name = "unnamed"; /* reset by set_state */
89 if (set_state (node)) {
90 throw failed_constructor();
94 Playlist::Playlist (const Playlist& other, string namestr, bool hide)
95 : _name (namestr), _session (other._session), _orig_diskstream_id(other._orig_diskstream_id)
100 other.copy_regions (tmp);
104 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
105 add_region_internal( (*x), (*x)->position() );
108 in_set_state = false;
110 _splicing = other._splicing;
111 _nudging = other._nudging;
112 _edit_mode = other._edit_mode;
114 in_set_state = false;
116 in_partition = false;
118 _read_data_count = 0;
119 _frozen = other._frozen;
121 layer_op_counter = other.layer_op_counter;
122 freeze_length = other.freeze_length;
126 Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, string str, bool hide)
127 : _name (str), _session (other._session), _orig_diskstream_id(other._orig_diskstream_id)
129 RegionLock rlock2 (&((Playlist&)other));
131 nframes_t end = start + cnt - 1;
135 for (RegionList::const_iterator i = other.regions.begin(); i != other.regions.end(); i++) {
137 boost::shared_ptr<Region> region;
138 boost::shared_ptr<Region> new_region;
139 nframes_t offset = 0;
140 nframes_t position = 0;
147 overlap = region->coverage (start, end);
153 case OverlapInternal:
154 offset = start - region->position();
161 position = region->position() - start;
162 len = end - region->position();
166 offset = start - region->position();
168 len = region->length() - offset;
171 case OverlapExternal:
173 position = region->position() - start;
174 len = region->length();
178 _session.region_name (new_name, region->name(), false);
180 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
182 add_region_internal (new_region, position, true);
185 /* this constructor does NOT notify others (session) */
192 InUse (this, true); /* EMIT SIGNAL */
202 InUse (this, false); /* EMIT SIGNAL */
205 /* nobody knows we exist */
213 Playlist::copy_regions (RegionList& newlist) const
215 RegionLock rlock (const_cast<Playlist *> (this));
217 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
218 newlist.push_back (RegionFactory::RegionFactory::create (*i));
223 Playlist::init (bool hide)
225 g_atomic_int_set (&block_notifications, 0);
226 g_atomic_int_set (&ignore_state_changes, 0);
227 pending_modified = false;
228 pending_length = false;
233 in_set_state = false;
234 _edit_mode = Config->get_edit_mode();
236 in_partition = false;
238 _read_data_count = 0;
240 layer_op_counter = 0;
243 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
246 Playlist::Playlist (const Playlist& pl)
247 : _session (pl._session)
249 fatal << _("playlist const copy constructor called") << endmsg;
252 Playlist::Playlist (Playlist& pl)
253 : _session (pl._session)
255 fatal << _("playlist non-const copy constructor called") << endmsg;
258 Playlist::~Playlist ()
260 /* GoingAway must be emitted by derived classes */
264 Playlist::set_name (const string& str)
266 /* in a typical situation, a playlist is being used
267 by one diskstream and also is referenced by the
268 Session. if there are more references than that,
269 then don't change the name.
277 NameChanged(); /* EMIT SIGNAL */
280 /***********************************************************************
281 CHANGE NOTIFICATION HANDLING
283 Notifications must be delayed till the region_lock is released. This
284 is necessary because handlers for the signals may need to acquire
285 the lock (e.g. to read from the playlist).
286 ***********************************************************************/
291 delay_notifications ();
292 g_atomic_int_inc (&ignore_state_changes);
298 g_atomic_int_dec_and_test (&ignore_state_changes);
299 release_notifications ();
304 Playlist::delay_notifications ()
306 g_atomic_int_inc (&block_notifications);
307 freeze_length = _get_maximum_extent();
311 Playlist::release_notifications ()
313 if (g_atomic_int_dec_and_test (&block_notifications)) {
314 flush_notifications ();
320 Playlist::notify_modified ()
322 if (holding_state ()) {
323 pending_modified = true;
325 pending_modified = false;
326 Modified(); /* EMIT SIGNAL */
331 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
333 if (holding_state ()) {
334 pending_removes.insert (r);
335 pending_modified = true;
336 pending_length = true;
338 /* this might not be true, but we have to act
339 as though it could be.
341 LengthChanged (); /* EMIT SIGNAL */
342 Modified (); /* EMIT SIGNAL */
347 Playlist::notify_region_added (boost::shared_ptr<Region> r)
349 /* the length change might not be true, but we have to act
350 as though it could be.
353 if (holding_state()) {
354 pending_adds.insert (r);
355 pending_modified = true;
356 pending_length = true;
358 LengthChanged (); /* EMIT SIGNAL */
359 Modified (); /* EMIT SIGNAL */
364 Playlist::notify_length_changed ()
366 if (holding_state ()) {
367 pending_length = true;
369 LengthChanged(); /* EMIT SIGNAL */
370 Modified (); /* EMIT SIGNAL */
375 Playlist::flush_notifications ()
377 set<boost::shared_ptr<Region> > dependent_checks_needed;
378 set<boost::shared_ptr<Region> >::iterator s;
387 /* we have no idea what order the regions ended up in pending
388 bounds (it could be based on selection order, for example).
389 so, to preserve layering in the "most recently moved is higher"
390 model, sort them by existing layer, then timestamp them.
393 // RegionSortByLayer cmp;
394 // pending_bounds.sort (cmp);
396 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
397 if (Config->get_layer_model() == MoveAddHigher) {
398 timestamp_layer_op (*r);
400 pending_length = true;
404 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
405 dependent_checks_needed.insert (*r);
406 /* don't increment n again - its the same list */
409 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
410 dependent_checks_needed.insert (*s);
414 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
415 check_dependents (*s, false);
418 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
419 remove_dependents (*s);
423 if ((freeze_length != _get_maximum_extent()) || pending_length) {
425 LengthChanged(); /* EMIT SIGNAL */
429 if (n || pending_modified) {
434 pending_modified = false;
435 Modified (); /* EMIT SIGNAL */
438 pending_adds.clear ();
439 pending_removes.clear ();
440 pending_bounds.clear ();
445 /*************************************************************
447 *************************************************************/
450 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
452 RegionLock rlock (this);
454 times = fabs (times);
456 int itimes = (int) floor (times);
458 nframes_t pos = position;
461 add_region_internal (region, pos, true);
462 pos += region->length();
466 /* later regions will all be spliced anyway */
468 if (!holding_state ()) {
469 possibly_splice_unlocked ();
472 /* note that itimes can be zero if we being asked to just
473 insert a single fraction of the region.
476 for (int i = 0; i < itimes; ++i) {
477 boost::shared_ptr<Region> copy = RegionFactory::create (region);
478 add_region_internal (copy, pos, true);
479 pos += region->length();
482 if (floor (times) != times) {
483 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
485 _session.region_name (name, region->name(), false);
486 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
487 add_region_internal (sub, pos, true);
492 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position, bool delay_sort)
494 RegionSortByPosition cmp;
495 nframes_t old_length = 0;
497 if (!holding_state()) {
498 old_length = _get_maximum_extent();
501 region->set_playlist (this);
502 region->set_position (position, this);
504 timestamp_layer_op (region);
506 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
507 all_regions.insert (region);
509 if (!holding_state () && !in_set_state) {
510 /* layers get assigned from XML state */
514 /* we need to notify the existence of new region before checking dependents. Ick. */
516 notify_region_added (region);
518 if (!holding_state ()) {
519 check_dependents (region, false);
520 if (old_length != _get_maximum_extent()) {
521 notify_length_changed ();
525 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
526 boost::weak_ptr<Region> (region)));
530 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
532 RegionLock rlock (this);
534 remove_region_internal (old);
535 add_region_internal (newr, pos);
537 if (!holding_state ()) {
538 possibly_splice_unlocked ();
543 Playlist::remove_region (boost::shared_ptr<Region> region)
545 RegionLock rlock (this);
546 remove_region_internal (region);
548 if (!holding_state ()) {
549 possibly_splice_unlocked ();
554 Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort)
556 RegionList::iterator i;
557 nframes_t old_length = 0;
559 cerr << "removing region " << region->name() << " holding = " << holding_state() << endl;
561 if (!holding_state()) {
562 old_length = _get_maximum_extent();
565 for (i = regions.begin(); i != regions.end(); ++i) {
570 if (!holding_state ()) {
572 remove_dependents (region);
574 if (old_length != _get_maximum_extent()) {
575 notify_length_changed ();
579 notify_region_removed (region);
587 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
589 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
590 if (Config->get_use_overlap_equivalency()) {
591 if ((*i)->overlap_equivalent (other)) {
592 results.push_back ((*i));
593 } else if ((*i)->equivalent (other)) {
594 results.push_back ((*i));
601 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
603 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
605 if ((*i) && (*i)->region_list_equivalent (other)) {
606 results.push_back (*i);
612 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
616 partition_internal (start, end, false, thawlist);
618 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
619 (*i)->thaw ("separation");
624 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
626 RegionLock rlock (this);
627 boost::shared_ptr<Region> region;
628 boost::shared_ptr<Region> current;
630 RegionList::iterator tmp;
632 nframes_t pos1, pos2, pos3, pos4;
633 RegionList new_regions;
637 /* need to work from a copy, because otherwise the regions we add during the process
638 get operated on as well.
641 RegionList copy = regions;
643 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
650 if (current->first_frame() == start && current->last_frame() == end) {
652 remove_region_internal (current);
657 if ((overlap = current->coverage (start, end)) == OverlapNone) {
661 pos1 = current->position();
664 pos4 = current->last_frame();
666 if (overlap == OverlapInternal) {
668 /* split: we need 3 new regions, the front, middle and end.
669 cut: we need 2 regions, the front and end.
674 ---------------*************************------------
677 ---------------*****++++++++++++++++====------------
679 ---------------*****----------------====------------
685 /* "middle" ++++++ */
687 _session.region_name (new_name, current->name(), false);
688 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
689 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
690 add_region_internal (region, start, true);
691 new_regions.push_back (region);
696 _session.region_name (new_name, current->name(), false);
697 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
698 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
700 add_region_internal (region, end, true);
701 new_regions.push_back (region);
706 thawlist.push_back (current);
707 current->trim_end (pos2, this);
709 } else if (overlap == OverlapEnd) {
713 ---------------*************************------------
716 ---------------**************+++++++++++------------
718 ---------------**************-----------------------
726 _session.region_name (new_name, current->name(), false);
727 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
728 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
729 add_region_internal (region, start, true);
730 new_regions.push_back (region);
736 thawlist.push_back (current);
737 current->trim_end (pos2, this);
739 } else if (overlap == OverlapStart) {
741 /* split: we need 2 regions: the front and the end.
742 cut: just trim current to skip the cut area
747 ---------------*************************------------
751 ---------------****+++++++++++++++++++++------------
753 -------------------*********************------------
760 _session.region_name (new_name, current->name(), false);
761 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
762 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
763 add_region_internal (region, pos1, true);
764 new_regions.push_back (region);
770 thawlist.push_back (current);
771 current->trim_front (pos3, this);
773 } else if (overlap == OverlapExternal) {
775 /* split: no split required.
776 cut: remove the region.
781 ---------------*************************------------
785 ---------------*************************------------
787 ----------------------------------------------------
792 remove_region_internal (current);
794 new_regions.push_back (current);
798 in_partition = false;
800 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
801 check_dependents (*i, false);
806 Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
812 if (ranges.empty()) {
816 start = ranges.front().start;
819 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
821 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
823 if (i == ranges.begin()) {
827 /* paste the next section into the nascent playlist,
828 offset to reflect the start of the first range we
832 ret->paste (*pl, (*i).start - start, 1.0f);
838 /* manually notify session of new playlist here
839 because the playlists were constructed without notifying
841 PlaylistCreated (ret);
848 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
850 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
851 return cut_copy (pmf, ranges, result_is_hidden);
855 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
857 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
858 return cut_copy (pmf, ranges, result_is_hidden);
862 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
868 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
869 string new_name = _name;
873 if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
877 partition_internal (start, start+cnt-1, true, thawlist);
880 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
881 (*i)->thaw ("playlist cut");
888 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
892 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
893 string new_name = _name;
897 cnt = min (_get_maximum_extent() - start, cnt);
898 return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
902 Playlist::paste (Playlist& other, nframes_t position, float times)
904 times = fabs (times);
905 nframes_t old_length;
908 RegionLock rl1 (this);
909 RegionLock rl2 (&other);
911 old_length = _get_maximum_extent();
913 int itimes = (int) floor (times);
914 nframes_t pos = position;
915 nframes_t shift = other._get_maximum_extent();
916 layer_t top_layer = regions.size();
919 for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
920 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
922 /* put these new regions on top of all existing ones, but preserve
923 the ordering they had in the original playlist.
926 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
927 add_region_internal (copy_of_region, copy_of_region->position() + pos);
932 possibly_splice_unlocked ();
934 /* XXX shall we handle fractional cases at some point? */
936 if (old_length != _get_maximum_extent()) {
937 notify_length_changed ();
948 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
950 times = fabs (times);
952 RegionLock rl (this);
953 int itimes = (int) floor (times);
954 nframes_t pos = position;
957 boost::shared_ptr<Region> copy = RegionFactory::create (region);
958 add_region_internal (copy, pos, true);
959 pos += region->length();
962 if (floor (times) != times) {
963 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
965 _session.region_name (name, region->name(), false);
966 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
967 add_region_internal (sub, pos, true);
972 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
974 RegionLock rl (this);
976 if (!region->covers (playlist_position)) {
980 if (region->position() == playlist_position ||
981 region->last_frame() == playlist_position) {
985 boost::shared_ptr<Region> left;
986 boost::shared_ptr<Region> right;
992 before = playlist_position - region->position();
993 after = region->length() - before;
996 _session.region_name (before_name, region->name(), false);
997 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
999 _session.region_name (after_name, region->name(), false);
1000 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1002 add_region_internal (left, region->position(), true);
1003 add_region_internal (right, region->position() + before);
1005 uint64_t orig_layer_op = region->last_layer_op();
1006 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1007 if ((*i)->last_layer_op() > orig_layer_op) {
1008 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1012 left->set_last_layer_op ( orig_layer_op );
1013 right->set_last_layer_op ( orig_layer_op + 1);
1017 finalize_split_region (region, left, right);
1019 if (remove_region_internal (region, true)) {
1025 Playlist::possibly_splice ()
1027 if (_edit_mode == Splice) {
1033 Playlist::possibly_splice_unlocked ()
1035 if (_edit_mode == Splice) {
1041 Playlist::splice_locked ()
1044 RegionLock rl (this);
1048 notify_length_changed ();
1052 Playlist::splice_unlocked ()
1055 notify_length_changed ();
1059 Playlist::core_splice ()
1063 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1065 RegionList::iterator next;
1070 if (next == regions.end()) {
1074 (*next)->set_position ((*i)->last_frame() + 1, this);
1081 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1083 if (in_set_state || _splicing || _nudging) {
1087 if (what_changed & ARDOUR::PositionChanged) {
1089 /* remove it from the list then add it back in
1090 the right place again.
1093 RegionSortByPosition cmp;
1095 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1097 if (i == regions.end()) {
1098 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1099 _name, region->name())
1105 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1109 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1111 if (holding_state ()) {
1112 pending_bounds.push_back (region);
1114 if (Config->get_layer_model() == MoveAddHigher) {
1115 /* it moved or changed length, so change the timestamp */
1116 timestamp_layer_op (region);
1120 check_dependents (region, false);
1121 notify_length_changed ();
1128 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1130 boost::shared_ptr<Region> region (weak_region.lock());
1136 /* this makes a virtual call to the right kind of playlist ... */
1138 region_changed (what_changed, region);
1142 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1144 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1147 if (in_set_state || in_flush) {
1152 if (what_changed & BoundsChanged) {
1153 region_bounds_changed (what_changed, region);
1154 save = !(_splicing || _nudging);
1157 if ((what_changed & Region::MuteChanged) &&
1158 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1159 check_dependents (region, false);
1162 if (what_changed & our_interests) {
1171 Playlist::clear (bool with_signals)
1174 RegionLock rl (this);
1175 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1176 pending_removes.insert (*i);
1188 /***********************************************************************
1190 **********************************************************************/
1192 Playlist::RegionList *
1193 Playlist::regions_at (nframes_t frame)
1196 RegionLock rlock (this);
1197 return find_regions_at (frame);
1200 boost::shared_ptr<Region>
1201 Playlist::top_region_at (nframes_t frame)
1204 RegionLock rlock (this);
1205 RegionList *rlist = find_regions_at (frame);
1206 boost::shared_ptr<Region> region;
1208 if (rlist->size()) {
1209 RegionSortByLayer cmp;
1211 region = rlist->back();
1218 Playlist::RegionList *
1219 Playlist::find_regions_at (nframes_t frame)
1221 RegionList *rlist = new RegionList;
1223 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1224 if ((*i)->covers (frame)) {
1225 rlist->push_back (*i);
1232 Playlist::RegionList *
1233 Playlist::regions_touched (nframes_t start, nframes_t end)
1235 RegionLock rlock (this);
1236 RegionList *rlist = new RegionList;
1238 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1239 if ((*i)->coverage (start, end) != OverlapNone) {
1240 rlist->push_back (*i);
1248 boost::shared_ptr<Region>
1249 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1251 RegionLock rlock (this);
1252 boost::shared_ptr<Region> ret;
1253 nframes_t closest = max_frames;
1255 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1258 boost::shared_ptr<Region> r = (*i);
1263 pos = r->first_frame ();
1266 pos = r->last_frame ();
1269 pos = r->adjust_to_sync (r->first_frame());
1274 case 1: /* forwards */
1277 if ((distance = pos - frame) < closest) {
1285 default: /* backwards */
1288 if ((distance = frame - pos) < closest) {
1300 /***********************************************************************/
1305 Playlist::mark_session_dirty ()
1307 if (!in_set_state && !holding_state ()) {
1308 _session.set_dirty();
1313 Playlist::set_state (const XMLNode& node)
1317 XMLNodeConstIterator niter;
1318 XMLPropertyList plist;
1319 XMLPropertyConstIterator piter;
1321 boost::shared_ptr<Region> region;
1324 in_set_state = true;
1326 if (node.name() != "Playlist") {
1327 in_set_state = false;
1333 plist = node.properties();
1335 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1339 if (prop->name() == X_("name")) {
1340 _name = prop->value();
1341 } else if (prop->name() == X_("orig_diskstream_id")) {
1342 _orig_diskstream_id = prop->value ();
1343 } else if (prop->name() == X_("frozen")) {
1344 _frozen = (prop->value() == X_("yes"));
1350 nlist = node.children();
1352 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1356 if (child->name() == "Region") {
1358 if ((prop = child->property ("id")) == 0) {
1359 error << _("region state node has no ID, ignored") << endmsg;
1363 ID id = prop->value ();
1365 if ((region = region_by_id (id))) {
1367 Change what_changed = Change (0);
1369 if (region->set_live_state (*child, what_changed, true)) {
1370 error << _("Playlist: cannot reset region state from XML") << endmsg;
1374 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1375 error << _("Playlist: cannot create region from XML") << endmsg;
1379 add_region (region, region->position(), 1.0);
1381 // So that layer_op ordering doesn't get screwed up
1382 region->set_last_layer_op( region->layer());
1387 /* update dependents, which was not done during add_region_internal
1388 due to in_set_state being true
1391 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1392 check_dependents (*r, false);
1397 in_set_state = false;
1405 Playlist::get_state()
1411 Playlist::get_template()
1413 return state(false);
1417 Playlist::state (bool full_state)
1419 XMLNode *node = new XMLNode (X_("Playlist"));
1422 node->add_property (X_("name"), _name);
1424 _orig_diskstream_id.print (buf, sizeof (buf));
1425 node->add_property (X_("orig_diskstream_id"), buf);
1426 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1429 RegionLock rlock (this, false);
1431 cerr << _name << " getting region state for " << regions.size() << endl;
1433 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1434 cerr << "\t" << " now at " << (*i) << endl;
1435 cerr << "\t\t" << (*i)->name() << endl;
1436 node->add_child_nocopy ((*i)->get_state());
1441 node->add_child_copy (*_extra_xml);
1448 Playlist::empty() const
1450 return regions.empty();
1454 Playlist::get_maximum_extent () const
1456 RegionLock rlock (const_cast<Playlist *>(this));
1457 return _get_maximum_extent ();
1461 Playlist::_get_maximum_extent () const
1463 RegionList::const_iterator i;
1464 nframes_t max_extent = 0;
1467 for (i = regions.begin(); i != regions.end(); ++i) {
1468 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1477 Playlist::bump_name (string name, Session &session)
1479 string newname = name;
1482 newname = Playlist::bump_name_once (newname);
1483 } while (session.playlist_by_name(newname)!=NULL);
1489 Playlist::bump_name_once (string name)
1491 string::size_type period;
1494 if ((period = name.find_last_of ('.')) == string::npos) {
1501 sscanf (name.substr (period+1).c_str(), "%d", &version);
1502 snprintf (buf, sizeof(buf), "%d", version+1);
1504 newname = name.substr (0, period+1);
1512 Playlist::top_layer() const
1514 RegionLock rlock (const_cast<Playlist *> (this));
1517 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1518 top = max (top, (*i)->layer());
1524 Playlist::set_edit_mode (EditMode mode)
1529 /********************
1531 ********************/
1534 Playlist::relayer ()
1536 RegionList::iterator i;
1539 /* don't send multiple Modified notifications
1540 when multiple regions are relayered.
1545 if (Config->get_layer_model() == MoveAddHigher ||
1546 Config->get_layer_model() == AddHigher) {
1548 RegionSortByLastLayerOp cmp;
1549 RegionList copy = regions;
1553 for (i = copy.begin(); i != copy.end(); ++i) {
1554 (*i)->set_layer (layer++);
1559 /* Session::LaterHigher model */
1561 for (i = regions.begin(); i != regions.end(); ++i) {
1562 (*i)->set_layer (layer++);
1566 /* sending Modified means that various kinds of layering
1567 models operate correctly at the GUI
1568 level. slightly inefficient, but only slightly.
1570 We force a Modified signal here in case no layers actually
1579 /* XXX these layer functions are all deprecated */
1582 Playlist::raise_region (boost::shared_ptr<Region> region)
1584 uint32_t rsz = regions.size();
1585 layer_t target = region->layer() + 1U;
1587 if (target >= rsz) {
1588 /* its already at the effective top */
1592 move_region_to_layer (target, region, 1);
1596 Playlist::lower_region (boost::shared_ptr<Region> region)
1598 if (region->layer() == 0) {
1599 /* its already at the bottom */
1603 layer_t target = region->layer() - 1U;
1605 move_region_to_layer (target, region, -1);
1609 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1611 /* does nothing useful if layering mode is later=higher */
1612 if ((Config->get_layer_model() == MoveAddHigher) ||
1613 (Config->get_layer_model() == AddHigher)) {
1614 timestamp_layer_op (region);
1620 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1622 /* does nothing useful if layering mode is later=higher */
1623 if ((Config->get_layer_model() == MoveAddHigher) ||
1624 (Config->get_layer_model() == AddHigher)) {
1625 region->set_last_layer_op (0);
1631 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1633 RegionList::iterator i;
1634 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1635 list<LayerInfo> layerinfo;
1639 RegionLock rlock (const_cast<Playlist *> (this));
1641 for (i = regions.begin(); i != regions.end(); ++i) {
1649 /* region is moving up, move all regions on intermediate layers
1653 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1654 dest = (*i)->layer() - 1;
1661 /* region is moving down, move all regions on intermediate layers
1665 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1666 dest = (*i)->layer() + 1;
1676 newpair.second = dest;
1678 layerinfo.push_back (newpair);
1682 /* now reset the layers without holding the region lock */
1684 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1685 x->first->set_layer (x->second);
1688 region->set_layer (target_layer);
1690 /* now check all dependents */
1692 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1693 check_dependents (x->first, false);
1696 check_dependents (region, false);
1702 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1704 RegionList::iterator i;
1711 RegionLock rlock (const_cast<Playlist *> (this));
1713 for (i = regions.begin(); i != regions.end(); ++i) {
1715 if ((*i)->position() >= start) {
1719 if ((*i)->last_frame() > max_frames - distance) {
1720 new_pos = max_frames - (*i)->length();
1722 new_pos = (*i)->position() + distance;
1727 if ((*i)->position() > distance) {
1728 new_pos = (*i)->position() - distance;
1734 (*i)->set_position (new_pos, this);
1742 notify_length_changed ();
1747 boost::shared_ptr<Region>
1748 Playlist::find_region (const ID& id) const
1750 RegionLock rlock (const_cast<Playlist*> (this));
1752 /* searches all regions currently in use by the playlist */
1754 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1755 if ((*i)->id() == id) {
1760 return boost::shared_ptr<Region> ();
1763 boost::shared_ptr<Region>
1764 Playlist::region_by_id (ID id)
1766 /* searches all regions ever added to this playlist */
1768 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1769 if ((*i)->id() == id) {
1773 return boost::shared_ptr<Region> ();
1777 Playlist::dump () const
1779 boost::shared_ptr<Region> r;
1781 cerr << "Playlist \"" << _name << "\" " << endl
1782 << regions.size() << " regions "
1785 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1787 cerr << " " << r->name() << " ["
1788 << r->start() << "+" << r->length()
1798 Playlist::set_frozen (bool yn)
1804 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1806 // struct timeval tv;
1807 // gettimeofday (&tv, 0);
1808 region->set_last_layer_op (++layer_op_counter);