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, jack_nframes_t start, jack_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 jack_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 jack_nframes_t offset = 0;
141 jack_nframes_t position = 0;
142 jack_nframes_t len = 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 = _session.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 (_session.get_layer_model() == Session::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, jack_nframes_t position, float times, bool with_save)
459 RegionLock rlock (this);
461 times = fabs (times);
463 int itimes = (int) floor (times);
465 jack_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 jack_nframes_t length = (jack_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, jack_nframes_t position, bool delay_sort)
505 RegionSortByPosition cmp;
506 jack_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);
514 region->lock_sources ();
516 timestamp_layer_op (region);
518 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
520 if (!holding_state () && !in_set_state) {
521 /* layers get assigned from XML state */
525 /* we need to notify the existence of new region before checking dependents. Ick. */
527 notify_region_added (region);
529 if (!holding_state ()) {
530 check_dependents (region, false);
531 if (old_length != _get_maximum_extent()) {
532 notify_length_changed ();
536 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), region));
540 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, jack_nframes_t pos)
542 RegionLock rlock (this);
544 remove_region_internal (old);
545 add_region_internal (newr, pos);
547 if (!holding_state ()) {
548 possibly_splice_unlocked ();
551 maybe_save_state (_("replace region"));
555 Playlist::remove_region (boost::shared_ptr<Region> region)
557 RegionLock rlock (this);
558 remove_region_internal (region);
560 if (!holding_state ()) {
561 possibly_splice_unlocked ();
564 maybe_save_state (_("remove region"));
568 Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort)
570 RegionList::iterator i;
571 jack_nframes_t old_length = 0;
573 // cerr << "removing region " << region->name() << endl;
575 if (!holding_state()) {
576 old_length = _get_maximum_extent();
579 for (i = regions.begin(); i != regions.end(); ++i) {
584 if (!holding_state ()) {
586 remove_dependents (region);
588 if (old_length != _get_maximum_extent()) {
589 notify_length_changed ();
593 notify_region_removed (region);
601 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
603 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
604 if (Config->get_use_overlap_equivalency()) {
605 if ((*i)->overlap_equivalent (other)) {
606 results.push_back ((*i));
607 } else if ((*i)->equivalent (other)) {
608 results.push_back ((*i));
615 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
617 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
619 if ((*i) && (*i)->region_list_equivalent (other)) {
620 results.push_back (*i);
626 Playlist::partition (jack_nframes_t start, jack_nframes_t end, bool just_top_level)
630 partition_internal (start, end, false, thawlist);
632 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
633 (*i)->thaw ("separation");
636 maybe_save_state (_("separate"));
640 Playlist::partition_internal (jack_nframes_t start, jack_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 jack_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, true);
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, true);
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, true);
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, true);
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);
822 Playlist::cut_copy (Playlist* (Playlist::*pmf)(jack_nframes_t, jack_nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
826 jack_nframes_t start;
828 if (ranges.empty()) {
832 start = ranges.front().start;
835 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
837 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
839 if (i == ranges.begin()) {
843 /* paste the next section into the nascent playlist,
844 offset to reflect the start of the first range we
848 ret->paste (*pl, (*i).start - start, 1.0f);
854 /* manually notify session of new playlist here
855 because the playlists were constructed without notifying
857 PlaylistCreated (ret);
864 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
866 Playlist* (Playlist::*pmf)(jack_nframes_t,jack_nframes_t,bool) = &Playlist::cut;
867 return cut_copy (pmf, ranges, result_is_hidden);
871 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
873 Playlist* (Playlist::*pmf)(jack_nframes_t,jack_nframes_t,bool) = &Playlist::copy;
874 return cut_copy (pmf, ranges, result_is_hidden);
878 Playlist::cut (jack_nframes_t start, jack_nframes_t cnt, bool result_is_hidden)
884 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
885 string new_name = _name;
889 if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
893 partition_internal (start, start+cnt-1, true, thawlist);
896 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
897 (*i)->thaw ("playlist cut");
900 maybe_save_state (_("cut"));
906 Playlist::copy (jack_nframes_t start, jack_nframes_t cnt, bool result_is_hidden)
910 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
911 string new_name = _name;
915 cnt = min (_get_maximum_extent() - start, cnt);
916 return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
920 Playlist::paste (Playlist& other, jack_nframes_t position, float times)
922 times = fabs (times);
923 jack_nframes_t old_length;
926 RegionLock rl1 (this);
927 RegionLock rl2 (&other);
929 old_length = _get_maximum_extent();
931 int itimes = (int) floor (times);
932 jack_nframes_t pos = position;
933 jack_nframes_t shift = other._get_maximum_extent();
934 layer_t top_layer = regions.size();
937 for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
938 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
940 /* put these new regions on top of all existing ones, but preserve
941 the ordering they had in the original playlist.
944 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
945 add_region_internal (copy_of_region, copy_of_region->position() + pos);
950 possibly_splice_unlocked ();
952 /* XXX shall we handle fractional cases at some point? */
954 if (old_length != _get_maximum_extent()) {
955 notify_length_changed ();
961 maybe_save_state (_("paste"));
968 Playlist::duplicate (boost::shared_ptr<Region> region, jack_nframes_t position, float times)
970 times = fabs (times);
972 RegionLock rl (this);
973 int itimes = (int) floor (times);
974 jack_nframes_t pos = position;
977 boost::shared_ptr<Region> copy = RegionFactory::create (region);
978 add_region_internal (copy, pos, true);
979 pos += region->length();
982 if (floor (times) != times) {
983 jack_nframes_t length = (jack_nframes_t) floor (region->length() * (times - floor (times)));
985 _session.region_name (name, region->name(), false);
986 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
987 add_region_internal (sub, pos, true);
990 maybe_save_state (_("duplicate"));
994 Playlist::split_region (boost::shared_ptr<Region> region, jack_nframes_t playlist_position)
996 RegionLock rl (this);
998 if (!region->covers (playlist_position)) {
1002 if (region->position() == playlist_position ||
1003 region->last_frame() == playlist_position) {
1007 boost::shared_ptr<Region> left;
1008 boost::shared_ptr<Region> right;
1009 jack_nframes_t before;
1010 jack_nframes_t after;
1014 before = playlist_position - region->position();
1015 after = region->length() - before;
1018 _session.region_name (before_name, region->name(), false);
1019 left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1021 _session.region_name (after_name, region->name(), false);
1022 right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1024 add_region_internal (left, region->position(), true);
1025 add_region_internal (right, region->position() + before);
1027 uint64_t orig_layer_op = region->last_layer_op();
1028 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1029 if ((*i)->last_layer_op() > orig_layer_op) {
1030 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1034 left->set_last_layer_op ( orig_layer_op );
1035 right->set_last_layer_op ( orig_layer_op + 1);
1039 finalize_split_region (region, left, right);
1041 if (remove_region_internal (region, true)) {
1045 maybe_save_state (_("split"));
1049 Playlist::possibly_splice ()
1051 if (_edit_mode == Splice) {
1057 Playlist::possibly_splice_unlocked ()
1059 if (_edit_mode == Splice) {
1065 Playlist::splice_locked ()
1068 RegionLock rl (this);
1072 notify_length_changed ();
1076 Playlist::splice_unlocked ()
1079 notify_length_changed ();
1083 Playlist::core_splice ()
1087 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1089 RegionList::iterator next;
1094 if (next == regions.end()) {
1098 (*next)->set_position ((*i)->last_frame() + 1, this);
1105 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1107 if (in_set_state || _splicing || _nudging) {
1111 if (what_changed & ARDOUR::PositionChanged) {
1113 /* remove it from the list then add it back in
1114 the right place again.
1117 RegionSortByPosition cmp;
1119 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1121 if (i == regions.end()) {
1122 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1123 _name, region->name())
1129 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1133 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1135 if (holding_state ()) {
1136 pending_bounds.push_back (region);
1138 if (_session.get_layer_model() == Session::MoveAddHigher) {
1139 /* it moved or changed length, so change the timestamp */
1140 timestamp_layer_op (region);
1144 check_dependents (region, false);
1145 notify_length_changed ();
1152 Playlist::region_changed_proxy (Change what_changed, boost::shared_ptr<Region> region)
1154 /* this makes a virtual call to the right kind of playlist ... */
1156 region_changed (what_changed, region);
1160 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1162 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1165 if (in_set_state || in_flush) {
1170 if (what_changed & BoundsChanged) {
1171 region_bounds_changed (what_changed, region);
1172 save = !(_splicing || _nudging);
1175 if ((what_changed & Region::MuteChanged) &&
1176 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1177 check_dependents (region, false);
1180 if (what_changed & our_interests) {
1189 Playlist::clear (bool with_save)
1191 RegionList::iterator i;
1195 RegionLock rl (this);
1200 for (i = tmp.begin(); i != tmp.end(); ++i) {
1201 notify_region_removed (*i);
1205 maybe_save_state (_("clear"));
1209 /***********************************************************************
1211 **********************************************************************/
1213 Playlist::RegionList *
1214 Playlist::regions_at (jack_nframes_t frame)
1217 RegionLock rlock (this);
1218 return find_regions_at (frame);
1221 boost::shared_ptr<Region>
1222 Playlist::top_region_at (jack_nframes_t frame)
1225 RegionLock rlock (this);
1226 RegionList *rlist = find_regions_at (frame);
1227 boost::shared_ptr<Region> region;
1229 if (rlist->size()) {
1230 RegionSortByLayer cmp;
1232 region = rlist->back();
1239 Playlist::RegionList *
1240 Playlist::find_regions_at (jack_nframes_t frame)
1242 RegionList *rlist = new RegionList;
1244 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1245 if ((*i)->covers (frame)) {
1246 rlist->push_back (*i);
1253 Playlist::RegionList *
1254 Playlist::regions_touched (jack_nframes_t start, jack_nframes_t end)
1256 RegionLock rlock (this);
1257 RegionList *rlist = new RegionList;
1259 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1260 if ((*i)->coverage (start, end) != OverlapNone) {
1261 rlist->push_back (*i);
1269 boost::shared_ptr<Region>
1270 Playlist::find_next_region (jack_nframes_t frame, RegionPoint point, int dir)
1272 RegionLock rlock (this);
1273 boost::shared_ptr<Region> ret;
1274 jack_nframes_t closest = max_frames;
1276 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1278 jack_nframes_t distance;
1279 boost::shared_ptr<Region> r = (*i);
1280 jack_nframes_t pos = 0;
1284 pos = r->first_frame ();
1287 pos = r->last_frame ();
1290 pos = r->adjust_to_sync (r->first_frame());
1295 case 1: /* forwards */
1298 if ((distance = pos - frame) < closest) {
1306 default: /* backwards */
1309 if ((distance = frame - pos) < closest) {
1321 /***********************************************************************/
1326 Playlist::mark_session_dirty ()
1328 if (!in_set_state && !holding_state ()) {
1329 _session.set_dirty();
1334 Playlist::set_state (const XMLNode& node)
1336 in_set_state = true;
1340 XMLNodeConstIterator niter;
1341 XMLPropertyList plist;
1342 XMLPropertyConstIterator piter;
1344 boost::shared_ptr<Region> region;
1349 if (node.name() != "Playlist") {
1350 in_set_state = false;
1354 plist = node.properties();
1356 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1360 if (prop->name() == X_("name")) {
1361 _name = prop->value();
1362 } else if (prop->name() == X_("orig_diskstream_id")) {
1363 _orig_diskstream_id = prop->value ();
1364 } else if (prop->name() == X_("frozen")) {
1365 _frozen = (prop->value() == X_("yes"));
1369 nlist = node.children();
1371 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1375 if (child->name() == "Region") {
1377 if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1378 error << _("Playlist: cannot create region from state file") << endmsg;
1382 add_region (region, region->position(), 1.0, false);
1384 // So that layer_op ordering doesn't get screwed up
1385 region->set_last_layer_op( region->layer());
1391 /* update dependents, which was not done during add_region_internal
1392 due to in_set_state being true
1395 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1396 check_dependents (*r, false);
1399 in_set_state = false;
1405 Playlist::get_state()
1411 Playlist::get_template()
1413 return state(false);
1417 Playlist::state (bool full_state)
1419 XMLNode *node = new XMLNode (X_("Playlist"));
1422 node->add_property (X_("name"), _name);
1424 _orig_diskstream_id.print (buf);
1425 node->add_property (X_("orig_diskstream_id"), buf);
1426 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1429 RegionLock rlock (this, false);
1431 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1432 node->add_child_nocopy ((*i)->get_state());
1437 node->add_child_copy (*_extra_xml);
1444 Playlist::empty() const
1446 return regions.empty();
1450 Playlist::get_maximum_extent () const
1452 RegionLock rlock (const_cast<Playlist *>(this));
1453 return _get_maximum_extent ();
1457 Playlist::_get_maximum_extent () const
1459 RegionList::const_iterator i;
1460 jack_nframes_t max_extent = 0;
1461 jack_nframes_t end = 0;
1463 for (i = regions.begin(); i != regions.end(); ++i) {
1464 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1473 Playlist::bump_name (string name, Session &session)
1475 string newname = name;
1478 newname = Playlist::bump_name_once (newname);
1479 } while (session.playlist_by_name(newname)!=NULL);
1485 Playlist::bump_name_once (string name)
1487 string::size_type period;
1490 if ((period = name.find_last_of ('.')) == string::npos) {
1497 sscanf (name.substr (period+1).c_str(), "%d", &version);
1498 snprintf (buf, sizeof(buf), "%d", version+1);
1500 newname = name.substr (0, period+1);
1508 Playlist::top_layer() const
1510 RegionLock rlock (const_cast<Playlist *> (this));
1513 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1514 top = max (top, (*i)->layer());
1520 Playlist::set_edit_mode (EditMode mode)
1525 /********************
1527 ********************/
1530 Playlist::relayer ()
1532 RegionList::iterator i;
1535 /* don't send multiple Modified notifications
1536 when multiple regions are relayered.
1541 if (_session.get_layer_model() == Session::MoveAddHigher ||
1542 _session.get_layer_model() == Session::AddHigher) {
1544 RegionSortByLastLayerOp cmp;
1545 RegionList copy = regions;
1549 for (i = copy.begin(); i != copy.end(); ++i) {
1550 (*i)->set_layer (layer++);
1555 /* Session::LaterHigher model */
1557 for (i = regions.begin(); i != regions.end(); ++i) {
1558 (*i)->set_layer (layer++);
1562 /* sending Modified means that various kinds of layering
1563 models operate correctly at the GUI
1564 level. slightly inefficient, but only slightly.
1566 We force a Modified signal here in case no layers actually
1575 /* XXX these layer functions are all deprecated */
1578 Playlist::raise_region (boost::shared_ptr<Region> region)
1580 uint32_t rsz = regions.size();
1581 layer_t target = region->layer() + 1U;
1583 if (target >= rsz) {
1584 /* its already at the effective top */
1588 move_region_to_layer (target, region, 1);
1592 Playlist::lower_region (boost::shared_ptr<Region> region)
1594 if (region->layer() == 0) {
1595 /* its already at the bottom */
1599 layer_t target = region->layer() - 1U;
1601 move_region_to_layer (target, region, -1);
1605 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1607 /* does nothing useful if layering mode is later=higher */
1608 if ((_session.get_layer_model() == Session::MoveAddHigher) ||
1609 (_session.get_layer_model() == Session::AddHigher)) {
1610 timestamp_layer_op (region);
1616 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1618 /* does nothing useful if layering mode is later=higher */
1619 if ((_session.get_layer_model() == Session::MoveAddHigher) ||
1620 (_session.get_layer_model() == Session::AddHigher)) {
1621 region->set_last_layer_op (0);
1627 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1629 RegionList::iterator i;
1630 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1631 list<LayerInfo> layerinfo;
1635 RegionLock rlock (const_cast<Playlist *> (this));
1637 for (i = regions.begin(); i != regions.end(); ++i) {
1645 /* region is moving up, move all regions on intermediate layers
1649 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1650 dest = (*i)->layer() - 1;
1657 /* region is moving down, move all regions on intermediate layers
1661 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1662 dest = (*i)->layer() + 1;
1672 newpair.second = dest;
1674 layerinfo.push_back (newpair);
1678 /* now reset the layers without holding the region lock */
1680 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1681 x->first->set_layer (x->second);
1684 region->set_layer (target_layer);
1686 /* now check all dependents */
1688 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1689 check_dependents (x->first, false);
1692 check_dependents (region, false);
1698 Playlist::nudge_after (jack_nframes_t start, jack_nframes_t distance, bool forwards)
1700 RegionList::iterator i;
1701 jack_nframes_t new_pos;
1707 RegionLock rlock (const_cast<Playlist *> (this));
1709 for (i = regions.begin(); i != regions.end(); ++i) {
1711 if ((*i)->position() >= start) {
1715 if ((*i)->last_frame() > max_frames - distance) {
1716 new_pos = max_frames - (*i)->length();
1718 new_pos = (*i)->position() + distance;
1723 if ((*i)->position() > distance) {
1724 new_pos = (*i)->position() - distance;
1730 (*i)->set_position (new_pos, this);
1738 maybe_save_state (_("nudged"));
1739 notify_length_changed ();
1744 boost::shared_ptr<Region>
1745 Playlist::find_region (const ID& id) const
1747 RegionLock rlock (const_cast<Playlist*> (this));
1748 RegionList::const_iterator i;
1749 boost::shared_ptr<Region> ret;
1751 for (i = regions.begin(); i != regions.end(); ++i) {
1752 if ((*i)->id() == id) {
1761 Playlist::save_state (std::string why)
1763 if (!in_set_state) {
1764 StateManager::save_state (why);
1769 Playlist::dump () const
1771 boost::shared_ptr<Region> r;
1773 cerr << "Playlist \"" << _name << "\" " << endl
1774 << regions.size() << " regions "
1777 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1779 cerr << " " << r->name() << " ["
1780 << r->start() << "+" << r->length()
1790 Playlist::set_frozen (bool yn)
1796 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1798 // struct timeval tv;
1799 // gettimeofday (&tv, 0);
1800 region->set_last_layer_op (++layer_op_counter);
1804 Playlist::maybe_save_state (string why)
1806 if (holding_state ()) {
1807 save_on_thaw = true;
1808 last_save_reason = why;