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 if (set_state (node)) {
95 throw failed_constructor();
99 Playlist::Playlist (const Playlist& other, string namestr, bool hide)
100 : _name (namestr), _session (other._session), _type(other._type), _orig_diskstream_id(other._orig_diskstream_id)
105 other.copy_regions (tmp);
109 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
110 add_region_internal( (*x), (*x)->position() );
115 _splicing = other._splicing;
116 _nudging = other._nudging;
117 _edit_mode = other._edit_mode;
121 in_partition = false;
123 _read_data_count = 0;
124 _frozen = other._frozen;
126 layer_op_counter = other.layer_op_counter;
127 freeze_length = other.freeze_length;
131 Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, string str, bool hide)
132 : _name (str), _session (other._session), _type(other._type), _orig_diskstream_id(other._orig_diskstream_id)
134 RegionLock rlock2 (&((Playlist&)other));
136 nframes_t end = start + cnt - 1;
140 for (RegionList::const_iterator i = other.regions.begin(); i != other.regions.end(); i++) {
142 boost::shared_ptr<Region> region;
143 boost::shared_ptr<Region> new_region;
144 nframes_t offset = 0;
145 nframes_t position = 0;
152 overlap = region->coverage (start, end);
158 case OverlapInternal:
159 offset = start - region->position();
166 position = region->position() - start;
167 len = end - region->position();
171 offset = start - region->position();
173 len = region->length() - offset;
176 case OverlapExternal:
178 position = region->position() - start;
179 len = region->length();
183 _session.region_name (new_name, region->name(), false);
185 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
187 add_region_internal (new_region, position, true);
190 /* this constructor does NOT notify others (session) */
197 InUse (this, true); /* EMIT SIGNAL */
207 InUse (this, false); /* EMIT SIGNAL */
210 /* nobody knows we exist */
218 Playlist::copy_regions (RegionList& newlist) const
220 RegionLock rlock (const_cast<Playlist *> (this));
222 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
223 newlist.push_back (RegionFactory::RegionFactory::create (*i));
228 Playlist::init (bool hide)
230 g_atomic_int_set (&block_notifications, 0);
231 g_atomic_int_set (&ignore_state_changes, 0);
232 pending_modified = false;
233 pending_length = false;
239 _edit_mode = Config->get_edit_mode();
241 in_partition = false;
243 _read_data_count = 0;
245 layer_op_counter = 0;
248 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
251 Playlist::Playlist (const Playlist& pl)
252 : _session (pl._session)
253 , _type(pl.data_type())
255 fatal << _("playlist const copy constructor called") << endmsg;
258 Playlist::Playlist (Playlist& pl)
259 : _session (pl._session)
260 , _type(pl.data_type())
262 fatal << _("playlist non-const copy constructor called") << endmsg;
265 Playlist::~Playlist ()
267 /* GoingAway must be emitted by derived classes */
271 Playlist::set_name (const string& str)
273 /* in a typical situation, a playlist is being used
274 by one diskstream and also is referenced by the
275 Session. if there are more references than that,
276 then don't change the name.
284 NameChanged(); /* EMIT SIGNAL */
287 /***********************************************************************
288 CHANGE NOTIFICATION HANDLING
290 Notifications must be delayed till the region_lock is released. This
291 is necessary because handlers for the signals may need to acquire
292 the lock (e.g. to read from the playlist).
293 ***********************************************************************/
298 delay_notifications ();
299 g_atomic_int_inc (&ignore_state_changes);
305 g_atomic_int_dec_and_test (&ignore_state_changes);
306 release_notifications ();
311 Playlist::delay_notifications ()
313 g_atomic_int_inc (&block_notifications);
314 freeze_length = _get_maximum_extent();
318 Playlist::release_notifications ()
320 if (g_atomic_int_dec_and_test (&block_notifications)) {
321 flush_notifications ();
327 Playlist::notify_modified ()
329 if (holding_state ()) {
330 pending_modified = true;
332 pending_modified = false;
333 Modified(); /* EMIT SIGNAL */
338 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
340 if (holding_state ()) {
341 pending_removes.insert (r);
342 pending_modified = true;
343 pending_length = true;
345 /* this might not be true, but we have to act
346 as though it could be.
348 LengthChanged (); /* EMIT SIGNAL */
349 Modified (); /* EMIT SIGNAL */
354 Playlist::notify_region_added (boost::shared_ptr<Region> r)
356 /* the length change might not be true, but we have to act
357 as though it could be.
360 if (holding_state()) {
361 pending_adds.insert (r);
362 pending_modified = true;
363 pending_length = true;
365 LengthChanged (); /* EMIT SIGNAL */
366 Modified (); /* EMIT SIGNAL */
371 Playlist::notify_length_changed ()
373 if (holding_state ()) {
374 pending_length = true;
376 LengthChanged(); /* EMIT SIGNAL */
377 Modified (); /* EMIT SIGNAL */
382 Playlist::flush_notifications ()
384 set<boost::shared_ptr<Region> > dependent_checks_needed;
385 set<boost::shared_ptr<Region> >::iterator s;
394 /* we have no idea what order the regions ended up in pending
395 bounds (it could be based on selection order, for example).
396 so, to preserve layering in the "most recently moved is higher"
397 model, sort them by existing layer, then timestamp them.
400 // RegionSortByLayer cmp;
401 // pending_bounds.sort (cmp);
403 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
404 if (Config->get_layer_model() == MoveAddHigher) {
405 timestamp_layer_op (*r);
407 pending_length = true;
411 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
412 dependent_checks_needed.insert (*r);
413 /* don't increment n again - its the same list */
416 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
417 dependent_checks_needed.insert (*s);
421 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
422 check_dependents (*s, false);
425 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
426 remove_dependents (*s);
430 if ((freeze_length != _get_maximum_extent()) || pending_length) {
432 LengthChanged(); /* EMIT SIGNAL */
436 if (n || pending_modified) {
441 pending_modified = false;
442 Modified (); /* EMIT SIGNAL */
445 pending_adds.clear ();
446 pending_removes.clear ();
447 pending_bounds.clear ();
452 /*************************************************************
454 *************************************************************/
457 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
459 RegionLock rlock (this);
461 times = fabs (times);
463 int itimes = (int) floor (times);
465 nframes_t pos = position;
468 add_region_internal (region, pos, true);
469 pos += region->length();
473 /* later regions will all be spliced anyway */
475 if (!holding_state ()) {
476 possibly_splice_unlocked ();
479 /* note that itimes can be zero if we being asked to just
480 insert a single fraction of the region.
483 for (int i = 0; i < itimes; ++i) {
484 boost::shared_ptr<Region> copy = RegionFactory::create (region);
485 add_region_internal (copy, pos, true);
486 pos += region->length();
489 if (floor (times) != times) {
490 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
492 _session.region_name (name, region->name(), false);
493 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
494 add_region_internal (sub, pos, true);
499 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position, bool delay_sort)
501 RegionSortByPosition cmp;
502 nframes_t old_length = 0;
504 if (!holding_state()) {
505 old_length = _get_maximum_extent();
508 region->set_playlist (this);
509 region->set_position (position, this);
511 timestamp_layer_op (region);
513 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
514 all_regions.insert (region);
516 if (!holding_state () && !in_set_state) {
517 /* layers get assigned from XML state */
521 /* we need to notify the existence of new region before checking dependents. Ick. */
523 notify_region_added (region);
525 if (!holding_state ()) {
526 check_dependents (region, false);
527 if (old_length != _get_maximum_extent()) {
528 notify_length_changed ();
532 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
533 boost::weak_ptr<Region> (region)));
537 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
539 RegionLock rlock (this);
541 remove_region_internal (old);
542 add_region_internal (newr, pos);
544 if (!holding_state ()) {
545 possibly_splice_unlocked ();
550 Playlist::remove_region (boost::shared_ptr<Region> region)
552 RegionLock rlock (this);
553 remove_region_internal (region);
555 if (!holding_state ()) {
556 possibly_splice_unlocked ();
561 Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort)
563 RegionList::iterator i;
564 nframes_t old_length = 0;
566 cerr << "removing region " << region->name() << " holding = " << holding_state() << endl;
568 if (!holding_state()) {
569 old_length = _get_maximum_extent();
572 for (i = regions.begin(); i != regions.end(); ++i) {
577 if (!holding_state ()) {
579 remove_dependents (region);
581 if (old_length != _get_maximum_extent()) {
582 notify_length_changed ();
586 notify_region_removed (region);
594 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
596 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
597 if (Config->get_use_overlap_equivalency()) {
598 if ((*i)->overlap_equivalent (other)) {
599 results.push_back ((*i));
600 } else if ((*i)->equivalent (other)) {
601 results.push_back ((*i));
608 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
610 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
612 if ((*i) && (*i)->region_list_equivalent (other)) {
613 results.push_back (*i);
619 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
623 partition_internal (start, end, false, thawlist);
625 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
626 (*i)->thaw ("separation");
631 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
633 RegionLock rlock (this);
634 boost::shared_ptr<Region> region;
635 boost::shared_ptr<Region> current;
637 RegionList::iterator tmp;
639 nframes_t pos1, pos2, pos3, pos4;
640 RegionList new_regions;
644 /* need to work from a copy, because otherwise the regions we add during the process
645 get operated on as well.
648 RegionList copy = regions;
650 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
657 if (current->first_frame() == start && current->last_frame() == end) {
659 remove_region_internal (current);
664 if ((overlap = current->coverage (start, end)) == OverlapNone) {
668 pos1 = current->position();
671 pos4 = current->last_frame();
673 if (overlap == OverlapInternal) {
675 /* split: we need 3 new regions, the front, middle and end.
676 cut: we need 2 regions, the front and end.
681 ---------------*************************------------
684 ---------------*****++++++++++++++++====------------
686 ---------------*****----------------====------------
692 /* "middle" ++++++ */
694 _session.region_name (new_name, current->name(), false);
695 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
696 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
697 add_region_internal (region, start, true);
698 new_regions.push_back (region);
703 _session.region_name (new_name, current->name(), false);
704 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
705 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
707 add_region_internal (region, end, true);
708 new_regions.push_back (region);
713 thawlist.push_back (current);
714 current->trim_end (pos2, this);
716 } else if (overlap == OverlapEnd) {
720 ---------------*************************------------
723 ---------------**************+++++++++++------------
725 ---------------**************-----------------------
733 _session.region_name (new_name, current->name(), false);
734 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
735 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
736 add_region_internal (region, start, true);
737 new_regions.push_back (region);
743 thawlist.push_back (current);
744 current->trim_end (pos2, this);
746 } else if (overlap == OverlapStart) {
748 /* split: we need 2 regions: the front and the end.
749 cut: just trim current to skip the cut area
754 ---------------*************************------------
758 ---------------****+++++++++++++++++++++------------
760 -------------------*********************------------
767 _session.region_name (new_name, current->name(), false);
768 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
769 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
770 add_region_internal (region, pos1, true);
771 new_regions.push_back (region);
777 thawlist.push_back (current);
778 current->trim_front (pos3, this);
780 } else if (overlap == OverlapExternal) {
782 /* split: no split required.
783 cut: remove the region.
788 ---------------*************************------------
792 ---------------*************************------------
794 ----------------------------------------------------
799 remove_region_internal (current);
801 new_regions.push_back (current);
805 in_partition = false;
807 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
808 check_dependents (*i, false);
813 Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
819 if (ranges.empty()) {
823 start = ranges.front().start;
826 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
828 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
830 if (i == ranges.begin()) {
834 /* paste the next section into the nascent playlist,
835 offset to reflect the start of the first range we
839 ret->paste (*pl, (*i).start - start, 1.0f);
845 /* manually notify session of new playlist here
846 because the playlists were constructed without notifying
848 PlaylistCreated (ret);
855 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
857 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
858 return cut_copy (pmf, ranges, result_is_hidden);
862 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
864 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
865 return cut_copy (pmf, ranges, result_is_hidden);
869 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
875 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
876 string new_name = _name;
880 if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
884 partition_internal (start, start+cnt-1, true, thawlist);
887 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
888 (*i)->thaw ("playlist cut");
895 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
899 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
900 string new_name = _name;
904 cnt = min (_get_maximum_extent() - start, cnt);
905 return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
909 Playlist::paste (Playlist& other, nframes_t position, float times)
911 times = fabs (times);
912 nframes_t old_length;
915 RegionLock rl1 (this);
916 RegionLock rl2 (&other);
918 old_length = _get_maximum_extent();
920 int itimes = (int) floor (times);
921 nframes_t pos = position;
922 nframes_t shift = other._get_maximum_extent();
923 layer_t top_layer = regions.size();
926 for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
927 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
929 /* put these new regions on top of all existing ones, but preserve
930 the ordering they had in the original playlist.
933 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
934 add_region_internal (copy_of_region, copy_of_region->position() + pos);
939 possibly_splice_unlocked ();
941 /* XXX shall we handle fractional cases at some point? */
943 if (old_length != _get_maximum_extent()) {
944 notify_length_changed ();
955 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
957 times = fabs (times);
959 RegionLock rl (this);
960 int itimes = (int) floor (times);
961 nframes_t pos = position;
964 boost::shared_ptr<Region> copy = RegionFactory::create (region);
965 add_region_internal (copy, pos, true);
966 pos += region->length();
969 if (floor (times) != times) {
970 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
972 _session.region_name (name, region->name(), false);
973 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
974 add_region_internal (sub, pos, true);
979 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
981 RegionLock rl (this);
983 if (!region->covers (playlist_position)) {
987 if (region->position() == playlist_position ||
988 region->last_frame() == playlist_position) {
992 boost::shared_ptr<Region> left;
993 boost::shared_ptr<Region> right;
999 before = playlist_position - region->position();
1000 after = region->length() - before;
1003 _session.region_name (before_name, region->name(), false);
1004 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1006 _session.region_name (after_name, region->name(), false);
1007 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1009 add_region_internal (left, region->position(), true);
1010 add_region_internal (right, region->position() + before);
1012 uint64_t orig_layer_op = region->last_layer_op();
1013 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1014 if ((*i)->last_layer_op() > orig_layer_op) {
1015 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1019 left->set_last_layer_op ( orig_layer_op );
1020 right->set_last_layer_op ( orig_layer_op + 1);
1024 finalize_split_region (region, left, right);
1026 if (remove_region_internal (region, true)) {
1032 Playlist::possibly_splice ()
1034 if (_edit_mode == Splice) {
1040 Playlist::possibly_splice_unlocked ()
1042 if (_edit_mode == Splice) {
1048 Playlist::splice_locked ()
1051 RegionLock rl (this);
1055 notify_length_changed ();
1059 Playlist::splice_unlocked ()
1062 notify_length_changed ();
1066 Playlist::core_splice ()
1070 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1072 RegionList::iterator next;
1077 if (next == regions.end()) {
1081 (*next)->set_position ((*i)->last_frame() + 1, this);
1088 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1090 if (in_set_state || _splicing || _nudging) {
1094 if (what_changed & ARDOUR::PositionChanged) {
1096 /* remove it from the list then add it back in
1097 the right place again.
1100 RegionSortByPosition cmp;
1102 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1104 if (i == regions.end()) {
1105 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1106 _name, region->name())
1112 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1116 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1118 if (holding_state ()) {
1119 pending_bounds.push_back (region);
1121 if (Config->get_layer_model() == MoveAddHigher) {
1122 /* it moved or changed length, so change the timestamp */
1123 timestamp_layer_op (region);
1127 check_dependents (region, false);
1128 notify_length_changed ();
1135 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1137 boost::shared_ptr<Region> region (weak_region.lock());
1143 /* this makes a virtual call to the right kind of playlist ... */
1145 region_changed (what_changed, region);
1149 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1151 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1154 if (in_set_state || in_flush) {
1159 if (what_changed & BoundsChanged) {
1160 region_bounds_changed (what_changed, region);
1161 save = !(_splicing || _nudging);
1164 if ((what_changed & Region::MuteChanged) &&
1165 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1166 check_dependents (region, false);
1169 if (what_changed & our_interests) {
1178 Playlist::clear (bool with_signals)
1181 RegionLock rl (this);
1182 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1183 pending_removes.insert (*i);
1195 /***********************************************************************
1197 **********************************************************************/
1199 Playlist::RegionList *
1200 Playlist::regions_at (nframes_t frame)
1203 RegionLock rlock (this);
1204 return find_regions_at (frame);
1207 boost::shared_ptr<Region>
1208 Playlist::top_region_at (nframes_t frame)
1211 RegionLock rlock (this);
1212 RegionList *rlist = find_regions_at (frame);
1213 boost::shared_ptr<Region> region;
1215 if (rlist->size()) {
1216 RegionSortByLayer cmp;
1218 region = rlist->back();
1225 Playlist::RegionList *
1226 Playlist::find_regions_at (nframes_t frame)
1228 RegionList *rlist = new RegionList;
1230 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1231 if ((*i)->covers (frame)) {
1232 rlist->push_back (*i);
1239 Playlist::RegionList *
1240 Playlist::regions_touched (nframes_t start, nframes_t end)
1242 RegionLock rlock (this);
1243 RegionList *rlist = new RegionList;
1245 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1246 if ((*i)->coverage (start, end) != OverlapNone) {
1247 rlist->push_back (*i);
1255 boost::shared_ptr<Region>
1256 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1258 RegionLock rlock (this);
1259 boost::shared_ptr<Region> ret;
1260 nframes_t closest = max_frames;
1262 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1265 boost::shared_ptr<Region> r = (*i);
1270 pos = r->first_frame ();
1273 pos = r->last_frame ();
1276 pos = r->adjust_to_sync (r->first_frame());
1281 case 1: /* forwards */
1284 if ((distance = pos - frame) < closest) {
1292 default: /* backwards */
1295 if ((distance = frame - pos) < closest) {
1307 /***********************************************************************/
1312 Playlist::mark_session_dirty ()
1314 if (!in_set_state && !holding_state ()) {
1315 _session.set_dirty();
1320 Playlist::set_state (const XMLNode& node)
1324 XMLNodeConstIterator niter;
1325 XMLPropertyList plist;
1326 XMLPropertyConstIterator piter;
1328 boost::shared_ptr<Region> region;
1333 if (node.name() != "Playlist") {
1340 plist = node.properties();
1342 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1346 if (prop->name() == X_("name")) {
1347 _name = prop->value();
1348 } else if (prop->name() == X_("orig_diskstream_id")) {
1349 _orig_diskstream_id = prop->value ();
1350 } else if (prop->name() == X_("frozen")) {
1351 _frozen = (prop->value() == X_("yes"));
1357 nlist = node.children();
1359 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1363 if (child->name() == "Region") {
1365 if ((prop = child->property ("id")) == 0) {
1366 error << _("region state node has no ID, ignored") << endmsg;
1370 ID id = prop->value ();
1372 if ((region = region_by_id (id))) {
1374 Change what_changed = Change (0);
1376 if (region->set_live_state (*child, what_changed, true)) {
1377 error << _("Playlist: cannot reset region state from XML") << endmsg;
1381 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1382 error << _("Playlist: cannot create region from XML") << endmsg;
1386 add_region (region, region->position(), 1.0);
1388 // So that layer_op ordering doesn't get screwed up
1389 region->set_last_layer_op( region->layer());
1394 /* update dependents, which was not done during add_region_internal
1395 due to in_set_state being true
1398 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1399 check_dependents (*r, false);
1412 Playlist::get_state()
1418 Playlist::get_template()
1420 return state(false);
1424 Playlist::state (bool full_state)
1426 XMLNode *node = new XMLNode (X_("Playlist"));
1429 node->add_property (X_("name"), _name);
1430 node->add_property (X_("type"), _type.to_string());
1432 _orig_diskstream_id.print (buf, sizeof (buf));
1433 node->add_property (X_("orig_diskstream_id"), buf);
1434 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1437 RegionLock rlock (this, false);
1439 cerr << _name << " getting region state for " << regions.size() << endl;
1441 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1442 cerr << "\t" << " now at " << (*i) << endl;
1443 cerr << "\t\t" << (*i)->name() << endl;
1444 node->add_child_nocopy ((*i)->get_state());
1449 node->add_child_copy (*_extra_xml);
1456 Playlist::empty() const
1458 return regions.empty();
1462 Playlist::get_maximum_extent () const
1464 RegionLock rlock (const_cast<Playlist *>(this));
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);