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;
120 save_on_thaw = false;
122 layer_op_counter = other.layer_op_counter;
123 freeze_length = other.freeze_length;
127 Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, string str, bool hide)
128 : _name (str), _session (other._session), _orig_diskstream_id(other._orig_diskstream_id)
130 RegionLock rlock2 (&((Playlist&)other));
132 nframes_t end = start + cnt - 1;
136 for (RegionList::const_iterator i = other.regions.begin(); i != other.regions.end(); i++) {
138 boost::shared_ptr<Region> region;
139 boost::shared_ptr<Region> new_region;
140 nframes_t offset = 0;
141 nframes_t position = 0;
148 overlap = region->coverage (start, end);
154 case OverlapInternal:
155 offset = start - region->position();
162 position = region->position() - start;
163 len = end - region->position();
167 offset = start - region->position();
169 len = region->length() - offset;
172 case OverlapExternal:
174 position = region->position() - start;
175 len = region->length();
179 _session.region_name (new_name, region->name(), false);
181 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
183 add_region_internal (new_region, position, true);
186 /* this constructor does NOT notify others (session) */
193 InUse (this, true); /* EMIT SIGNAL */
203 InUse (this, false); /* EMIT SIGNAL */
206 /* nobody knows we exist */
214 Playlist::copy_regions (RegionList& newlist) const
216 RegionLock rlock (const_cast<Playlist *> (this));
218 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
219 newlist.push_back (RegionFactory::RegionFactory::create (*i));
224 Playlist::init (bool hide)
226 g_atomic_int_set (&block_notifications, 0);
227 g_atomic_int_set (&ignore_state_changes, 0);
228 pending_modified = false;
229 pending_length = false;
234 in_set_state = false;
235 _edit_mode = Config->get_edit_mode();
237 in_partition = false;
239 _read_data_count = 0;
241 save_on_thaw = false;
242 layer_op_counter = 0;
245 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
248 Playlist::Playlist (const Playlist& pl)
249 : _session (pl._session)
251 fatal << _("playlist const copy constructor called") << endmsg;
254 Playlist::Playlist (Playlist& pl)
255 : _session (pl._session)
257 fatal << _("playlist non-const copy constructor called") << endmsg;
260 Playlist::~Playlist ()
262 /* GoingAway must be emitted by derived classes */
266 Playlist::set_name (const string& str)
268 /* in a typical situation, a playlist is being used
269 by one diskstream and also is referenced by the
270 Session. if there are more references than that,
271 then don't change the name.
279 NameChanged(); /* EMIT SIGNAL */
282 /***********************************************************************
283 CHANGE NOTIFICATION HANDLING
285 Notifications must be delayed till the region_lock is released. This
286 is necessary because handlers for the signals may need to acquire
287 the lock (e.g. to read from the playlist).
288 ***********************************************************************/
293 delay_notifications ();
294 g_atomic_int_inc (&ignore_state_changes);
300 g_atomic_int_dec_and_test (&ignore_state_changes);
301 release_notifications ();
306 Playlist::delay_notifications ()
308 g_atomic_int_inc (&block_notifications);
309 freeze_length = _get_maximum_extent();
313 Playlist::release_notifications ()
315 if (g_atomic_int_dec_and_test (&block_notifications)) {
316 flush_notifications ();
322 Playlist::notify_modified ()
324 if (holding_state ()) {
325 pending_modified = true;
327 pending_modified = false;
328 Modified(); /* EMIT SIGNAL */
333 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
335 if (holding_state ()) {
336 pending_removals.insert (pending_removals.end(), r);
338 RegionRemoved (r); /* EMIT SIGNAL */
339 /* this might not be true, but we have to act
340 as though it could be.
342 LengthChanged (); /* EMIT SIGNAL */
343 Modified (); /* EMIT SIGNAL */
348 Playlist::notify_region_added (boost::shared_ptr<Region> r)
350 if (holding_state()) {
351 pending_adds.insert (pending_adds.end(), r);
353 RegionAdded (r); /* EMIT SIGNAL */
354 /* this might not be true, but we have to act
355 as though it could be.
357 LengthChanged (); /* EMIT SIGNAL */
358 Modified (); /* EMIT SIGNAL */
363 Playlist::notify_length_changed ()
365 if (holding_state ()) {
366 pending_length = true;
368 LengthChanged(); /* EMIT SIGNAL */
369 Modified (); /* EMIT SIGNAL */
374 Playlist::flush_notifications ()
376 RegionList::iterator r;
377 RegionList::iterator a;
378 set<boost::shared_ptr<Region> > dependent_checks_needed;
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 (a = pending_adds.begin(); a != pending_adds.end(); ++a) {
410 dependent_checks_needed.insert (*a);
411 RegionAdded (*a); /* EMIT SIGNAL */
415 for (set<boost::shared_ptr<Region> >::iterator x = dependent_checks_needed.begin(); x != dependent_checks_needed.end(); ++x) {
416 check_dependents (*x, false);
419 for (r = pending_removals.begin(); r != pending_removals.end(); ++r) {
420 remove_dependents (*r);
421 RegionRemoved (*r); /* EMIT SIGNAL */
425 if ((freeze_length != _get_maximum_extent()) || pending_length) {
427 LengthChanged(); /* EMIT SIGNAL */
431 if (n || pending_modified) {
436 pending_modified = false;
437 Modified (); /* EMIT SIGNAL */
440 pending_adds.clear ();
441 pending_removals.clear ();
442 pending_bounds.clear ();
445 save_on_thaw = false;
446 save_state (last_save_reason);
452 /*************************************************************
454 *************************************************************/
457 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times, bool with_save)
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);
498 maybe_save_state (_("add region"));
503 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position, bool delay_sort)
505 RegionSortByPosition cmp;
506 nframes_t old_length = 0;
508 if (!holding_state()) {
509 old_length = _get_maximum_extent();
512 region->set_playlist (this);
513 region->set_position (position, this);
515 timestamp_layer_op (region);
517 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
519 if (!holding_state () && !in_set_state) {
520 /* layers get assigned from XML state */
524 /* we need to notify the existence of new region before checking dependents. Ick. */
526 notify_region_added (region);
528 if (!holding_state ()) {
529 check_dependents (region, false);
530 if (old_length != _get_maximum_extent()) {
531 notify_length_changed ();
535 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), region));
539 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
541 RegionLock rlock (this);
543 remove_region_internal (old);
544 add_region_internal (newr, pos);
546 if (!holding_state ()) {
547 possibly_splice_unlocked ();
550 maybe_save_state (_("replace region"));
554 Playlist::remove_region (boost::shared_ptr<Region> region)
556 RegionLock rlock (this);
557 remove_region_internal (region);
559 if (!holding_state ()) {
560 possibly_splice_unlocked ();
563 maybe_save_state (_("remove region"));
567 Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort)
569 RegionList::iterator i;
570 nframes_t old_length = 0;
572 // cerr << "removing region " << region->name() << endl;
574 if (!holding_state()) {
575 old_length = _get_maximum_extent();
578 for (i = regions.begin(); i != regions.end(); ++i) {
583 if (!holding_state ()) {
585 remove_dependents (region);
587 if (old_length != _get_maximum_extent()) {
588 notify_length_changed ();
592 notify_region_removed (region);
600 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
602 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
603 if (Config->get_use_overlap_equivalency()) {
604 if ((*i)->overlap_equivalent (other)) {
605 results.push_back ((*i));
606 } else if ((*i)->equivalent (other)) {
607 results.push_back ((*i));
614 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
616 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
618 if ((*i) && (*i)->region_list_equivalent (other)) {
619 results.push_back (*i);
625 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
629 partition_internal (start, end, false, thawlist);
631 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
632 (*i)->thaw ("separation");
635 maybe_save_state (_("separate"));
639 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
641 RegionLock rlock (this);
642 boost::shared_ptr<Region> region;
643 boost::shared_ptr<Region> current;
645 RegionList::iterator tmp;
647 nframes_t pos1, pos2, pos3, pos4;
648 RegionList new_regions;
652 /* need to work from a copy, because otherwise the regions we add during the process
653 get operated on as well.
656 RegionList copy = regions;
658 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
665 if (current->first_frame() == start && current->last_frame() == end) {
667 remove_region_internal (current);
672 if ((overlap = current->coverage (start, end)) == OverlapNone) {
676 pos1 = current->position();
679 pos4 = current->last_frame();
681 if (overlap == OverlapInternal) {
683 /* split: we need 3 new regions, the front, middle and end.
684 cut: we need 2 regions, the front and end.
689 ---------------*************************------------
692 ---------------*****++++++++++++++++====------------
694 ---------------*****----------------====------------
700 /* "middle" ++++++ */
702 _session.region_name (new_name, current->name(), false);
703 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
704 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
705 add_region_internal (region, start, true);
706 new_regions.push_back (region);
711 _session.region_name (new_name, current->name(), false);
712 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
713 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
715 add_region_internal (region, end, true);
716 new_regions.push_back (region);
721 thawlist.push_back (current);
722 current->trim_end (pos2, this);
724 } else if (overlap == OverlapEnd) {
728 ---------------*************************------------
731 ---------------**************+++++++++++------------
733 ---------------**************-----------------------
741 _session.region_name (new_name, current->name(), false);
742 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
743 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
744 add_region_internal (region, start, true);
745 new_regions.push_back (region);
751 thawlist.push_back (current);
752 current->trim_end (pos2, this);
754 } else if (overlap == OverlapStart) {
756 /* split: we need 2 regions: the front and the end.
757 cut: just trim current to skip the cut area
762 ---------------*************************------------
766 ---------------****+++++++++++++++++++++------------
768 -------------------*********************------------
775 _session.region_name (new_name, current->name(), false);
776 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
777 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
778 add_region_internal (region, pos1, true);
779 new_regions.push_back (region);
785 thawlist.push_back (current);
786 current->trim_front (pos3, this);
788 } else if (overlap == OverlapExternal) {
790 /* split: no split required.
791 cut: remove the region.
796 ---------------*************************------------
800 ---------------*************************------------
802 ----------------------------------------------------
807 remove_region_internal (current);
809 new_regions.push_back (current);
813 in_partition = false;
815 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
816 check_dependents (*i, false);
821 Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
827 if (ranges.empty()) {
831 start = ranges.front().start;
834 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
836 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
838 if (i == ranges.begin()) {
842 /* paste the next section into the nascent playlist,
843 offset to reflect the start of the first range we
847 ret->paste (*pl, (*i).start - start, 1.0f);
853 /* manually notify session of new playlist here
854 because the playlists were constructed without notifying
856 PlaylistCreated (ret);
863 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
865 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
866 return cut_copy (pmf, ranges, result_is_hidden);
870 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
872 Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
873 return cut_copy (pmf, ranges, result_is_hidden);
877 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
883 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
884 string new_name = _name;
888 if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
892 partition_internal (start, start+cnt-1, true, thawlist);
895 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
896 (*i)->thaw ("playlist cut");
899 maybe_save_state (_("cut"));
905 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
909 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
910 string new_name = _name;
914 cnt = min (_get_maximum_extent() - start, cnt);
915 return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
919 Playlist::paste (Playlist& other, nframes_t position, float times)
921 times = fabs (times);
922 nframes_t old_length;
925 RegionLock rl1 (this);
926 RegionLock rl2 (&other);
928 old_length = _get_maximum_extent();
930 int itimes = (int) floor (times);
931 nframes_t pos = position;
932 nframes_t shift = other._get_maximum_extent();
933 layer_t top_layer = regions.size();
936 for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
937 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
939 /* put these new regions on top of all existing ones, but preserve
940 the ordering they had in the original playlist.
943 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
944 add_region_internal (copy_of_region, copy_of_region->position() + pos);
949 possibly_splice_unlocked ();
951 /* XXX shall we handle fractional cases at some point? */
953 if (old_length != _get_maximum_extent()) {
954 notify_length_changed ();
960 maybe_save_state (_("paste"));
967 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
969 times = fabs (times);
971 RegionLock rl (this);
972 int itimes = (int) floor (times);
973 nframes_t pos = position;
976 boost::shared_ptr<Region> copy = RegionFactory::create (region);
977 add_region_internal (copy, pos, true);
978 pos += region->length();
981 if (floor (times) != times) {
982 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
984 _session.region_name (name, region->name(), false);
985 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
986 add_region_internal (sub, pos, true);
989 maybe_save_state (_("duplicate"));
993 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
995 RegionLock rl (this);
997 if (!region->covers (playlist_position)) {
1001 if (region->position() == playlist_position ||
1002 region->last_frame() == playlist_position) {
1006 boost::shared_ptr<Region> left;
1007 boost::shared_ptr<Region> right;
1013 before = playlist_position - region->position();
1014 after = region->length() - before;
1017 _session.region_name (before_name, region->name(), false);
1018 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1020 _session.region_name (after_name, region->name(), false);
1021 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1023 add_region_internal (left, region->position(), true);
1024 add_region_internal (right, region->position() + before);
1026 uint64_t orig_layer_op = region->last_layer_op();
1027 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1028 if ((*i)->last_layer_op() > orig_layer_op) {
1029 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1033 left->set_last_layer_op ( orig_layer_op );
1034 right->set_last_layer_op ( orig_layer_op + 1);
1038 finalize_split_region (region, left, right);
1040 if (remove_region_internal (region, true)) {
1044 maybe_save_state (_("split"));
1048 Playlist::possibly_splice ()
1050 if (_edit_mode == Splice) {
1056 Playlist::possibly_splice_unlocked ()
1058 if (_edit_mode == Splice) {
1064 Playlist::splice_locked ()
1067 RegionLock rl (this);
1071 notify_length_changed ();
1075 Playlist::splice_unlocked ()
1078 notify_length_changed ();
1082 Playlist::core_splice ()
1086 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1088 RegionList::iterator next;
1093 if (next == regions.end()) {
1097 (*next)->set_position ((*i)->last_frame() + 1, this);
1104 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1106 if (in_set_state || _splicing || _nudging) {
1110 if (what_changed & ARDOUR::PositionChanged) {
1112 /* remove it from the list then add it back in
1113 the right place again.
1116 RegionSortByPosition cmp;
1118 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1120 if (i == regions.end()) {
1121 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1122 _name, region->name())
1128 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1132 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1134 if (holding_state ()) {
1135 pending_bounds.push_back (region);
1137 if (Config->get_layer_model() == MoveAddHigher) {
1138 /* it moved or changed length, so change the timestamp */
1139 timestamp_layer_op (region);
1143 check_dependents (region, false);
1144 notify_length_changed ();
1151 Playlist::region_changed_proxy (Change what_changed, boost::shared_ptr<Region> region)
1153 /* this makes a virtual call to the right kind of playlist ... */
1155 region_changed (what_changed, region);
1159 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1161 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1164 if (in_set_state || in_flush) {
1169 if (what_changed & BoundsChanged) {
1170 region_bounds_changed (what_changed, region);
1171 save = !(_splicing || _nudging);
1174 if ((what_changed & Region::MuteChanged) &&
1175 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1176 check_dependents (region, false);
1179 if (what_changed & our_interests) {
1188 Playlist::clear (bool with_save)
1190 RegionList::iterator i;
1194 RegionLock rl (this);
1199 for (i = tmp.begin(); i != tmp.end(); ++i) {
1200 notify_region_removed (*i);
1204 maybe_save_state (_("clear"));
1208 /***********************************************************************
1210 **********************************************************************/
1212 Playlist::RegionList *
1213 Playlist::regions_at (nframes_t frame)
1216 RegionLock rlock (this);
1217 return find_regions_at (frame);
1220 boost::shared_ptr<Region>
1221 Playlist::top_region_at (nframes_t frame)
1224 RegionLock rlock (this);
1225 RegionList *rlist = find_regions_at (frame);
1226 boost::shared_ptr<Region> region;
1228 if (rlist->size()) {
1229 RegionSortByLayer cmp;
1231 region = rlist->back();
1238 Playlist::RegionList *
1239 Playlist::find_regions_at (nframes_t frame)
1241 RegionList *rlist = new RegionList;
1243 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1244 if ((*i)->covers (frame)) {
1245 rlist->push_back (*i);
1252 Playlist::RegionList *
1253 Playlist::regions_touched (nframes_t start, nframes_t end)
1255 RegionLock rlock (this);
1256 RegionList *rlist = new RegionList;
1258 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1259 if ((*i)->coverage (start, end) != OverlapNone) {
1260 rlist->push_back (*i);
1268 boost::shared_ptr<Region>
1269 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1271 RegionLock rlock (this);
1272 boost::shared_ptr<Region> ret;
1273 nframes_t closest = max_frames;
1275 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1278 boost::shared_ptr<Region> r = (*i);
1283 pos = r->first_frame ();
1286 pos = r->last_frame ();
1289 pos = r->adjust_to_sync (r->first_frame());
1294 case 1: /* forwards */
1297 if ((distance = pos - frame) < closest) {
1305 default: /* backwards */
1308 if ((distance = frame - pos) < closest) {
1320 /***********************************************************************/
1325 Playlist::mark_session_dirty ()
1327 if (!in_set_state && !holding_state ()) {
1328 _session.set_dirty();
1333 Playlist::set_state (const XMLNode& node)
1335 in_set_state = true;
1339 XMLNodeConstIterator niter;
1340 XMLPropertyList plist;
1341 XMLPropertyConstIterator piter;
1343 boost::shared_ptr<Region> region;
1348 if (node.name() != "Playlist") {
1349 in_set_state = false;
1353 plist = node.properties();
1355 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1359 if (prop->name() == X_("name")) {
1360 _name = prop->value();
1361 } else if (prop->name() == X_("orig_diskstream_id")) {
1362 _orig_diskstream_id = prop->value ();
1363 } else if (prop->name() == X_("frozen")) {
1364 _frozen = (prop->value() == X_("yes"));
1368 nlist = node.children();
1370 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1374 if (child->name() == "Region") {
1376 if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1377 error << _("Playlist: cannot create region from state file") << endmsg;
1381 add_region (region, region->position(), 1.0, false);
1383 // So that layer_op ordering doesn't get screwed up
1384 region->set_last_layer_op( region->layer());
1390 /* update dependents, which was not done during add_region_internal
1391 due to in_set_state being true
1394 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1395 check_dependents (*r, false);
1398 in_set_state = false;
1404 Playlist::get_state()
1410 Playlist::get_template()
1412 return state(false);
1416 Playlist::state (bool full_state)
1418 XMLNode *node = new XMLNode (X_("Playlist"));
1421 node->add_property (X_("name"), _name);
1423 _orig_diskstream_id.print (buf);
1424 node->add_property (X_("orig_diskstream_id"), buf);
1425 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1428 RegionLock rlock (this, false);
1430 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1431 node->add_child_nocopy ((*i)->get_state());
1436 node->add_child_copy (*_extra_xml);
1443 Playlist::empty() const
1445 return regions.empty();
1449 Playlist::get_maximum_extent () const
1451 RegionLock rlock (const_cast<Playlist *>(this));
1452 return _get_maximum_extent ();
1456 Playlist::_get_maximum_extent () const
1458 RegionList::const_iterator i;
1459 nframes_t max_extent = 0;
1462 for (i = regions.begin(); i != regions.end(); ++i) {
1463 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1472 Playlist::bump_name (string name, Session &session)
1474 string newname = name;
1477 newname = Playlist::bump_name_once (newname);
1478 } while (session.playlist_by_name(newname)!=NULL);
1484 Playlist::bump_name_once (string name)
1486 string::size_type period;
1489 if ((period = name.find_last_of ('.')) == string::npos) {
1496 sscanf (name.substr (period+1).c_str(), "%d", &version);
1497 snprintf (buf, sizeof(buf), "%d", version+1);
1499 newname = name.substr (0, period+1);
1507 Playlist::top_layer() const
1509 RegionLock rlock (const_cast<Playlist *> (this));
1512 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1513 top = max (top, (*i)->layer());
1519 Playlist::set_edit_mode (EditMode mode)
1524 /********************
1526 ********************/
1529 Playlist::relayer ()
1531 RegionList::iterator i;
1534 /* don't send multiple Modified notifications
1535 when multiple regions are relayered.
1540 if (Config->get_layer_model() == MoveAddHigher ||
1541 Config->get_layer_model() == AddHigher) {
1543 RegionSortByLastLayerOp cmp;
1544 RegionList copy = regions;
1548 for (i = copy.begin(); i != copy.end(); ++i) {
1549 (*i)->set_layer (layer++);
1554 /* Session::LaterHigher model */
1556 for (i = regions.begin(); i != regions.end(); ++i) {
1557 (*i)->set_layer (layer++);
1561 /* sending Modified means that various kinds of layering
1562 models operate correctly at the GUI
1563 level. slightly inefficient, but only slightly.
1565 We force a Modified signal here in case no layers actually
1574 /* XXX these layer functions are all deprecated */
1577 Playlist::raise_region (boost::shared_ptr<Region> region)
1579 uint32_t rsz = regions.size();
1580 layer_t target = region->layer() + 1U;
1582 if (target >= rsz) {
1583 /* its already at the effective top */
1587 move_region_to_layer (target, region, 1);
1591 Playlist::lower_region (boost::shared_ptr<Region> region)
1593 if (region->layer() == 0) {
1594 /* its already at the bottom */
1598 layer_t target = region->layer() - 1U;
1600 move_region_to_layer (target, region, -1);
1604 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1606 /* does nothing useful if layering mode is later=higher */
1607 if ((Config->get_layer_model() == MoveAddHigher) ||
1608 (Config->get_layer_model() == AddHigher)) {
1609 timestamp_layer_op (region);
1615 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1617 /* does nothing useful if layering mode is later=higher */
1618 if ((Config->get_layer_model() == MoveAddHigher) ||
1619 (Config->get_layer_model() == AddHigher)) {
1620 region->set_last_layer_op (0);
1626 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1628 RegionList::iterator i;
1629 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1630 list<LayerInfo> layerinfo;
1634 RegionLock rlock (const_cast<Playlist *> (this));
1636 for (i = regions.begin(); i != regions.end(); ++i) {
1644 /* region is moving up, move all regions on intermediate layers
1648 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1649 dest = (*i)->layer() - 1;
1656 /* region is moving down, move all regions on intermediate layers
1660 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1661 dest = (*i)->layer() + 1;
1671 newpair.second = dest;
1673 layerinfo.push_back (newpair);
1677 /* now reset the layers without holding the region lock */
1679 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1680 x->first->set_layer (x->second);
1683 region->set_layer (target_layer);
1685 /* now check all dependents */
1687 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1688 check_dependents (x->first, false);
1691 check_dependents (region, false);
1697 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1699 RegionList::iterator i;
1706 RegionLock rlock (const_cast<Playlist *> (this));
1708 for (i = regions.begin(); i != regions.end(); ++i) {
1710 if ((*i)->position() >= start) {
1714 if ((*i)->last_frame() > max_frames - distance) {
1715 new_pos = max_frames - (*i)->length();
1717 new_pos = (*i)->position() + distance;
1722 if ((*i)->position() > distance) {
1723 new_pos = (*i)->position() - distance;
1729 (*i)->set_position (new_pos, this);
1737 maybe_save_state (_("nudged"));
1738 notify_length_changed ();
1743 boost::shared_ptr<Region>
1744 Playlist::find_region (const ID& id) const
1746 RegionLock rlock (const_cast<Playlist*> (this));
1747 RegionList::const_iterator i;
1748 boost::shared_ptr<Region> ret;
1750 for (i = regions.begin(); i != regions.end(); ++i) {
1751 if ((*i)->id() == id) {
1760 Playlist::save_state (std::string why)
1762 if (!in_set_state) {
1763 StateManager::save_state (why);
1768 Playlist::dump () const
1770 boost::shared_ptr<Region> r;
1772 cerr << "Playlist \"" << _name << "\" " << endl
1773 << regions.size() << " regions "
1776 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1778 cerr << " " << r->name() << " ["
1779 << r->start() << "+" << r->length()
1789 Playlist::set_frozen (bool yn)
1795 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1797 // struct timeval tv;
1798 // gettimeofday (&tv, 0);
1799 region->set_last_layer_op (++layer_op_counter);
1803 Playlist::maybe_save_state (string why)
1805 if (holding_state ()) {
1806 save_on_thaw = true;
1807 last_save_reason = why;