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.
28 #include <sigc++/bind.h>
30 #include <pbd/failed_constructor.h>
31 #include <pbd/stl_delete.h>
32 #include <pbd/xml++.h>
34 #include <ardour/playlist.h>
35 #include <ardour/session.h>
36 #include <ardour/region.h>
37 #include <ardour/region_factory.h>
38 #include <ardour/playlist_factory.h>
43 using namespace ARDOUR;
46 struct ShowMeTheList {
47 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
49 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
51 boost::shared_ptr<Playlist> playlist;
55 struct RegionSortByLayer {
56 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
57 return a->layer() < b->layer();
61 struct RegionSortByPosition {
62 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
63 return a->position() < b->position();
67 struct RegionSortByLastLayerOp {
68 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
69 return a->last_layer_op() < b->last_layer_op();
73 Playlist::Playlist (Session& sess, string nom, bool hide)
81 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
85 _name = "unnamed"; /* reset by set_state */
87 /* set state called by derived class */
90 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
91 : _name (namestr), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
96 other->copy_regions (tmp);
100 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
101 add_region_internal( (*x), (*x)->position());
106 _splicing = other->_splicing;
107 _nudging = other->_nudging;
108 _edit_mode = other->_edit_mode;
112 in_partition = false;
114 _read_data_count = 0;
115 _frozen = other->_frozen;
117 layer_op_counter = other->layer_op_counter;
118 freeze_length = other->freeze_length;
121 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
122 : _name (str), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
124 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
126 nframes_t end = start + cnt - 1;
132 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
134 boost::shared_ptr<Region> region;
135 boost::shared_ptr<Region> new_region;
136 nframes_t offset = 0;
137 nframes_t position = 0;
144 overlap = region->coverage (start, end);
150 case OverlapInternal:
151 offset = start - region->position();
158 position = region->position() - start;
159 len = end - region->position();
163 offset = start - region->position();
165 len = region->length() - offset;
168 case OverlapExternal:
170 position = region->position() - start;
171 len = region->length();
175 _session.region_name (new_name, region->name(), false);
177 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
179 add_region_internal (new_region, position);
184 /* this constructor does NOT notify others (session) */
191 InUse (true); /* EMIT SIGNAL */
202 InUse (false); /* EMIT SIGNAL */
207 Playlist::copy_regions (RegionList& newlist) const
209 RegionLock rlock (const_cast<Playlist *> (this));
211 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
212 newlist.push_back (RegionFactory::RegionFactory::create (*i));
217 Playlist::init (bool hide)
219 g_atomic_int_set (&block_notifications, 0);
220 g_atomic_int_set (&ignore_state_changes, 0);
221 pending_modified = false;
222 pending_length = false;
228 _edit_mode = Config->get_edit_mode();
230 in_partition = false;
232 _read_data_count = 0;
234 layer_op_counter = 0;
237 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
240 Playlist::Playlist (const Playlist& pl)
241 : _session (pl._session)
243 fatal << _("playlist const copy constructor called") << endmsg;
246 Playlist::Playlist (Playlist& pl)
247 : _session (pl._session)
249 fatal << _("playlist non-const copy constructor called") << endmsg;
252 Playlist::~Playlist ()
255 RegionLock rl (this);
257 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
258 (*i)->set_playlist (boost::shared_ptr<Playlist>());
262 /* GoingAway must be emitted by derived classes */
266 Playlist::set_name (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_removes.insert (r);
337 pending_modified = true;
338 pending_length = true;
340 /* this might not be true, but we have to act
341 as though it could be.
343 LengthChanged (); /* EMIT SIGNAL */
344 Modified (); /* EMIT SIGNAL */
349 Playlist::notify_region_added (boost::shared_ptr<Region> r)
351 /* the length change might not be true, but we have to act
352 as though it could be.
355 if (holding_state()) {
356 pending_adds.insert (r);
357 pending_modified = true;
358 pending_length = true;
360 LengthChanged (); /* EMIT SIGNAL */
361 Modified (); /* EMIT SIGNAL */
366 Playlist::notify_length_changed ()
368 if (holding_state ()) {
369 pending_length = true;
371 LengthChanged(); /* EMIT SIGNAL */
372 Modified (); /* EMIT SIGNAL */
377 Playlist::flush_notifications ()
379 set<boost::shared_ptr<Region> > dependent_checks_needed;
380 set<boost::shared_ptr<Region> >::iterator s;
389 /* we have no idea what order the regions ended up in pending
390 bounds (it could be based on selection order, for example).
391 so, to preserve layering in the "most recently moved is higher"
392 model, sort them by existing layer, then timestamp them.
395 // RegionSortByLayer cmp;
396 // pending_bounds.sort (cmp);
398 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
399 if (Config->get_layer_model() == MoveAddHigher) {
400 timestamp_layer_op (*r);
402 pending_length = true;
403 dependent_checks_needed.insert (*r);
407 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
408 dependent_checks_needed.insert (*s);
412 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
413 remove_dependents (*s);
417 if ((freeze_length != _get_maximum_extent()) || pending_length) {
419 LengthChanged(); /* EMIT SIGNAL */
423 if (n || pending_modified) {
428 pending_modified = false;
429 Modified (); /* EMIT SIGNAL */
432 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
433 check_dependents (*s, false);
436 pending_adds.clear ();
437 pending_removes.clear ();
438 pending_bounds.clear ();
443 /*************************************************************
445 *************************************************************/
448 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
450 RegionLock rlock (this);
452 times = fabs (times);
454 int itimes = (int) floor (times);
456 nframes_t pos = position;
459 add_region_internal (region, pos);
460 pos += region->length();
464 /* later regions will all be spliced anyway */
466 if (!holding_state ()) {
467 possibly_splice_unlocked ();
470 /* note that itimes can be zero if we being asked to just
471 insert a single fraction of the region.
474 for (int i = 0; i < itimes; ++i) {
475 boost::shared_ptr<Region> copy = RegionFactory::create (region);
476 add_region_internal (copy, pos);
477 pos += region->length();
480 if (floor (times) != times) {
481 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
483 _session.region_name (name, region->name(), false);
484 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
485 add_region_internal (sub, pos);
490 Playlist::set_region_ownership ()
492 RegionLock rl (this);
493 RegionList::iterator i;
494 boost::weak_ptr<Playlist> pl (shared_from_this());
496 for (i = regions.begin(); i != regions.end(); ++i) {
497 (*i)->set_playlist (pl);
502 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
504 RegionSortByPosition cmp;
505 nframes_t old_length = 0;
507 if (!holding_state()) {
508 old_length = _get_maximum_extent();
512 boost::shared_ptr<Playlist> foo (shared_from_this());
513 region->set_playlist (boost::weak_ptr<Playlist>(foo));
516 region->set_position (position, this);
518 timestamp_layer_op (region);
520 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
521 all_regions.insert (region);
523 if (!holding_state () && !in_set_state) {
524 /* layers get assigned from XML state */
528 /* we need to notify the existence of new region before checking dependents. Ick. */
530 notify_region_added (region);
532 if (!holding_state ()) {
533 check_dependents (region, false);
534 if (old_length != _get_maximum_extent()) {
535 notify_length_changed ();
539 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
540 boost::weak_ptr<Region> (region)));
544 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
546 RegionLock rlock (this);
548 remove_region_internal (old);
549 add_region_internal (newr, pos);
551 if (!holding_state ()) {
552 possibly_splice_unlocked ();
557 Playlist::remove_region (boost::shared_ptr<Region> region)
559 RegionLock rlock (this);
560 remove_region_internal (region);
562 if (!holding_state ()) {
563 possibly_splice_unlocked ();
568 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
570 RegionList::iterator i;
571 nframes_t old_length = 0;
573 if (!holding_state()) {
574 old_length = _get_maximum_extent();
577 for (i = regions.begin(); i != regions.end(); ++i) {
582 if (!holding_state ()) {
584 remove_dependents (region);
586 if (old_length != _get_maximum_extent()) {
587 notify_length_changed ();
591 notify_region_removed (region);
599 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
601 if (Config->get_use_overlap_equivalency()) {
602 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
603 if ((*i)->overlap_equivalent (other)) {
604 results.push_back ((*i));
608 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
609 if ((*i)->equivalent (other)) {
610 results.push_back ((*i));
617 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
619 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
621 if ((*i) && (*i)->region_list_equivalent (other)) {
622 results.push_back (*i);
628 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
632 partition_internal (start, end, false, thawlist);
634 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
635 (*i)->thaw ("separation");
640 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
642 RegionLock rlock (this);
643 boost::shared_ptr<Region> region;
644 boost::shared_ptr<Region> current;
646 RegionList::iterator tmp;
648 nframes_t pos1, pos2, pos3, pos4;
649 RegionList new_regions;
653 /* need to work from a copy, because otherwise the regions we add during the process
654 get operated on as well.
657 RegionList copy = regions;
659 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
666 if (current->first_frame() == start && current->last_frame() == end) {
668 remove_region_internal (current);
673 if ((overlap = current->coverage (start, end)) == OverlapNone) {
677 pos1 = current->position();
680 pos4 = current->last_frame();
682 if (overlap == OverlapInternal) {
684 /* split: we need 3 new regions, the front, middle and end.
685 cut: we need 2 regions, the front and end.
690 ---------------*************************------------
693 ---------------*****++++++++++++++++====------------
695 ---------------*****----------------====------------
701 /* "middle" ++++++ */
703 _session.region_name (new_name, current->name(), false);
704 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
705 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
706 add_region_internal (region, start);
707 new_regions.push_back (region);
712 _session.region_name (new_name, current->name(), false);
713 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
714 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
716 add_region_internal (region, end);
717 new_regions.push_back (region);
722 thawlist.push_back (current);
723 current->trim_end (pos2, this);
725 } else if (overlap == OverlapEnd) {
729 ---------------*************************------------
732 ---------------**************+++++++++++------------
734 ---------------**************-----------------------
742 _session.region_name (new_name, current->name(), false);
743 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
744 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
745 add_region_internal (region, start);
746 new_regions.push_back (region);
752 thawlist.push_back (current);
753 current->trim_end (pos2, this);
755 } else if (overlap == OverlapStart) {
757 /* split: we need 2 regions: the front and the end.
758 cut: just trim current to skip the cut area
763 ---------------*************************------------
767 ---------------****+++++++++++++++++++++------------
769 -------------------*********************------------
776 _session.region_name (new_name, current->name(), false);
777 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
778 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
779 add_region_internal (region, pos1);
780 new_regions.push_back (region);
786 thawlist.push_back (current);
787 current->trim_front (pos3, this);
789 } else if (overlap == OverlapExternal) {
791 /* split: no split required.
792 cut: remove the region.
797 ---------------*************************------------
801 ---------------*************************------------
803 ----------------------------------------------------
808 remove_region_internal (current);
810 new_regions.push_back (current);
814 in_partition = false;
816 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
817 check_dependents (*i, false);
821 boost::shared_ptr<Playlist>
822 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
824 boost::shared_ptr<Playlist> ret;
825 boost::shared_ptr<Playlist> pl;
828 if (ranges.empty()) {
829 return boost::shared_ptr<Playlist>();
832 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);
854 boost::shared_ptr<Playlist>
855 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
857 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
858 return cut_copy (pmf, ranges, result_is_hidden);
861 boost::shared_ptr<Playlist>
862 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
864 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
865 return cut_copy (pmf, ranges, result_is_hidden);
868 boost::shared_ptr<Playlist>
869 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
871 boost::shared_ptr<Playlist> the_copy;
875 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
876 string new_name = _name;
880 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
881 return boost::shared_ptr<Playlist>();
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");
894 boost::shared_ptr<Playlist>
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 PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
909 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
911 times = fabs (times);
912 nframes_t old_length;
915 RegionLock rl1 (this);
916 RegionLock rl2 (other.get());
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);
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);
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());
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)) {
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)) {
1117 if (holding_state ()) {
1118 pending_bounds.push_back (region);
1120 if (Config->get_layer_model() == MoveAddHigher) {
1121 /* it moved or changed length, so change the timestamp */
1122 timestamp_layer_op (region);
1126 notify_length_changed ();
1128 check_dependents (region, false);
1134 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1136 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;
1263 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1266 boost::shared_ptr<Region> r = (*i);
1271 pos = r->first_frame ();
1274 pos = r->last_frame ();
1277 pos = r->adjust_to_sync (r->first_frame());
1282 case 1: /* forwards */
1285 if ((distance = pos - frame) < closest) {
1293 default: /* backwards */
1296 if ((distance = frame - pos) < closest) {
1308 /***********************************************************************/
1313 Playlist::mark_session_dirty ()
1315 if (!in_set_state && !holding_state ()) {
1316 _session.set_dirty();
1321 Playlist::set_state (const XMLNode& node)
1325 XMLNodeConstIterator niter;
1326 XMLPropertyList plist;
1327 XMLPropertyConstIterator piter;
1329 boost::shared_ptr<Region> region;
1334 if (node.name() != "Playlist") {
1341 plist = node.properties();
1343 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1347 if (prop->name() == X_("name")) {
1348 _name = prop->value();
1349 } else if (prop->name() == X_("orig_diskstream_id")) {
1350 _orig_diskstream_id = prop->value ();
1351 } else if (prop->name() == X_("frozen")) {
1352 _frozen = (prop->value() == X_("yes"));
1358 nlist = node.children();
1360 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1364 if (child->name() == "Region") {
1366 if ((prop = child->property ("id")) == 0) {
1367 error << _("region state node has no ID, ignored") << endmsg;
1371 ID id = prop->value ();
1373 if ((region = region_by_id (id))) {
1375 Change what_changed = Change (0);
1377 if (region->set_live_state (*child, what_changed, true)) {
1378 error << _("Playlist: cannot reset region state from XML") << endmsg;
1382 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1383 error << _("Playlist: cannot create region from XML") << endmsg;
1387 add_region (region, region->position(), 1.0);
1389 // So that layer_op ordering doesn't get screwed up
1390 region->set_last_layer_op( region->layer());
1399 /* update dependents, which was not done during add_region_internal
1400 due to in_set_state being true
1403 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1404 check_dependents (*r, false);
1413 Playlist::get_state()
1419 Playlist::get_template()
1421 return state(false);
1425 Playlist::state (bool full_state)
1427 XMLNode *node = new XMLNode (X_("Playlist"));
1430 node->add_property (X_("name"), _name);
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);
1438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1439 node->add_child_nocopy ((*i)->get_state());
1444 node->add_child_copy (*_extra_xml);
1451 Playlist::empty() const
1453 RegionLock rlock (const_cast<Playlist *>(this), false);
1454 return regions.empty();
1458 Playlist::n_regions() const
1460 RegionLock rlock (const_cast<Playlist *>(this), false);
1461 return regions.size();
1465 Playlist::get_maximum_extent () const
1467 RegionLock rlock (const_cast<Playlist *>(this), false);
1468 return _get_maximum_extent ();
1472 Playlist::_get_maximum_extent () const
1474 RegionList::const_iterator i;
1475 nframes_t max_extent = 0;
1478 for (i = regions.begin(); i != regions.end(); ++i) {
1479 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1488 Playlist::bump_name (string name, Session &session)
1490 string newname = name;
1493 newname = Playlist::bump_name_once (newname);
1494 } while (session.playlist_by_name (newname)!=NULL);
1500 Playlist::bump_name_once (string name)
1502 string::size_type period;
1505 if ((period = name.find_last_of ('.')) == string::npos) {
1510 const char *last_element = name.c_str() + period + 1;
1511 for (size_t i = 0; i < strlen(last_element); i++) {
1512 if (!isdigit(last_element[i])) {
1519 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1521 if (isnumber == 0 || errno != 0) {
1522 // last_element is not a number, or is too large
1528 snprintf (buf, sizeof(buf), "%ld", version+1);
1530 newname = name.substr (0, period+1);
1539 Playlist::top_layer() const
1541 RegionLock rlock (const_cast<Playlist *> (this));
1544 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1545 top = max (top, (*i)->layer());
1551 Playlist::set_edit_mode (EditMode mode)
1556 /********************
1558 ********************/
1561 Playlist::relayer ()
1563 RegionList::iterator i;
1566 /* don't send multiple Modified notifications
1567 when multiple regions are relayered.
1572 if (Config->get_layer_model() == MoveAddHigher ||
1573 Config->get_layer_model() == AddHigher) {
1575 RegionSortByLastLayerOp cmp;
1576 RegionList copy = regions;
1580 for (i = copy.begin(); i != copy.end(); ++i) {
1581 (*i)->set_layer (layer++);
1586 /* Session::LaterHigher model */
1588 for (i = regions.begin(); i != regions.end(); ++i) {
1589 (*i)->set_layer (layer++);
1593 /* sending Modified means that various kinds of layering
1594 models operate correctly at the GUI
1595 level. slightly inefficient, but only slightly.
1597 We force a Modified signal here in case no layers actually
1606 /* XXX these layer functions are all deprecated */
1609 Playlist::raise_region (boost::shared_ptr<Region> region)
1611 uint32_t rsz = regions.size();
1612 layer_t target = region->layer() + 1U;
1614 if (target >= rsz) {
1615 /* its already at the effective top */
1619 move_region_to_layer (target, region, 1);
1623 Playlist::lower_region (boost::shared_ptr<Region> region)
1625 if (region->layer() == 0) {
1626 /* its already at the bottom */
1630 layer_t target = region->layer() - 1U;
1632 move_region_to_layer (target, region, -1);
1636 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1638 /* does nothing useful if layering mode is later=higher */
1639 if ((Config->get_layer_model() == MoveAddHigher) ||
1640 (Config->get_layer_model() == AddHigher)) {
1641 timestamp_layer_op (region);
1647 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1649 /* does nothing useful if layering mode is later=higher */
1650 if ((Config->get_layer_model() == MoveAddHigher) ||
1651 (Config->get_layer_model() == AddHigher)) {
1652 region->set_last_layer_op (0);
1658 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1660 RegionList::iterator i;
1661 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1662 list<LayerInfo> layerinfo;
1666 RegionLock rlock (const_cast<Playlist *> (this));
1668 for (i = regions.begin(); i != regions.end(); ++i) {
1676 /* region is moving up, move all regions on intermediate layers
1680 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1681 dest = (*i)->layer() - 1;
1688 /* region is moving down, move all regions on intermediate layers
1692 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1693 dest = (*i)->layer() + 1;
1703 newpair.second = dest;
1705 layerinfo.push_back (newpair);
1709 /* now reset the layers without holding the region lock */
1711 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1712 x->first->set_layer (x->second);
1715 region->set_layer (target_layer);
1718 /* now check all dependents */
1720 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1721 check_dependents (x->first, false);
1724 check_dependents (region, false);
1731 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1733 RegionList::iterator i;
1740 RegionLock rlock (const_cast<Playlist *> (this));
1742 for (i = regions.begin(); i != regions.end(); ++i) {
1744 if ((*i)->position() >= start) {
1748 if ((*i)->last_frame() > max_frames - distance) {
1749 new_pos = max_frames - (*i)->length();
1751 new_pos = (*i)->position() + distance;
1756 if ((*i)->position() > distance) {
1757 new_pos = (*i)->position() - distance;
1763 (*i)->set_position (new_pos, this);
1771 notify_length_changed ();
1776 boost::shared_ptr<Region>
1777 Playlist::find_region (const ID& id) const
1779 RegionLock rlock (const_cast<Playlist*> (this));
1781 /* searches all regions currently in use by the playlist */
1783 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1784 if ((*i)->id() == id) {
1789 return boost::shared_ptr<Region> ();
1792 boost::shared_ptr<Region>
1793 Playlist::region_by_id (ID id)
1795 /* searches all regions ever added to this playlist */
1797 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1798 if ((*i)->id() == id) {
1802 return boost::shared_ptr<Region> ();
1806 Playlist::dump () const
1808 boost::shared_ptr<Region> r;
1810 cerr << "Playlist \"" << _name << "\" " << endl
1811 << regions.size() << " regions "
1814 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1816 cerr << " " << r->name() << " ["
1817 << r->start() << "+" << r->length()
1827 Playlist::set_frozen (bool yn)
1833 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1835 // struct timeval tv;
1836 // gettimeofday (&tv, 0);
1837 region->set_last_layer_op (++layer_op_counter);