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, DataType type, bool hide)
74 : SessionObject(sess, nom)
78 first_set_state = false;
83 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
84 : SessionObject(sess, "unnamed playlist")
87 const XMLProperty* prop = node.property("type");
88 assert(!prop || DataType(prop->value()) == _type);
91 _name = "unnamed"; /* reset by set_state */
93 /* set state called by derived class */
96 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
97 : SessionObject(other->_session, namestr), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
102 other->copy_regions (tmp);
106 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
107 add_region_internal( (*x), (*x)->position());
112 _splicing = other->_splicing;
113 _nudging = other->_nudging;
114 _edit_mode = other->_edit_mode;
117 first_set_state = false;
119 in_partition = false;
121 _read_data_count = 0;
122 _frozen = other->_frozen;
124 layer_op_counter = other->layer_op_counter;
125 freeze_length = other->freeze_length;
128 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
129 : SessionObject(other->_session, str), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
131 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
133 nframes_t end = start + cnt - 1;
139 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
141 boost::shared_ptr<Region> region;
142 boost::shared_ptr<Region> new_region;
143 nframes_t offset = 0;
144 nframes_t position = 0;
151 overlap = region->coverage (start, end);
157 case OverlapInternal:
158 offset = start - region->position();
165 position = region->position() - start;
166 len = end - region->position();
170 offset = start - region->position();
172 len = region->length() - offset;
175 case OverlapExternal:
177 position = region->position() - start;
178 len = region->length();
182 _session.region_name (new_name, region->name(), false);
184 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
186 add_region_internal (new_region, position);
190 first_set_state = false;
192 /* this constructor does NOT notify others (session) */
199 InUse (true); /* EMIT SIGNAL */
210 InUse (false); /* EMIT SIGNAL */
215 Playlist::copy_regions (RegionList& newlist) const
217 RegionLock rlock (const_cast<Playlist *> (this));
219 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
220 newlist.push_back (RegionFactory::RegionFactory::create (*i));
225 Playlist::init (bool hide)
227 g_atomic_int_set (&block_notifications, 0);
228 g_atomic_int_set (&ignore_state_changes, 0);
229 pending_modified = false;
230 pending_length = false;
231 first_set_state = true;
237 _edit_mode = Config->get_edit_mode();
239 in_partition = false;
241 _read_data_count = 0;
243 layer_op_counter = 0;
246 Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
249 Playlist::Playlist (const Playlist& pl)
250 : SessionObject(pl._session, pl._name)
251 , _type(pl.data_type())
253 fatal << _("playlist const copy constructor called") << endmsg;
256 Playlist::Playlist (Playlist& pl)
257 : SessionObject(pl._session, pl._name)
258 , _type(pl.data_type())
260 fatal << _("playlist non-const copy constructor called") << endmsg;
263 Playlist::~Playlist ()
266 RegionLock rl (this);
268 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
269 (*i)->set_playlist (boost::shared_ptr<Playlist>());
273 /* GoingAway must be emitted by derived classes */
277 Playlist::set_name (const string& str)
279 /* in a typical situation, a playlist is being used
280 by one diskstream and also is referenced by the
281 Session. if there are more references than that,
282 then don't change the name.
288 return SessionObject::set_name(str);
292 /***********************************************************************
293 CHANGE NOTIFICATION HANDLING
295 Notifications must be delayed till the region_lock is released. This
296 is necessary because handlers for the signals may need to acquire
297 the lock (e.g. to read from the playlist).
298 ***********************************************************************/
303 delay_notifications ();
304 g_atomic_int_inc (&ignore_state_changes);
310 g_atomic_int_dec_and_test (&ignore_state_changes);
311 release_notifications ();
316 Playlist::delay_notifications ()
318 g_atomic_int_inc (&block_notifications);
319 freeze_length = _get_maximum_extent();
323 Playlist::release_notifications ()
325 if (g_atomic_int_dec_and_test (&block_notifications)) {
326 flush_notifications ();
332 Playlist::notify_modified ()
334 if (holding_state ()) {
335 pending_modified = true;
337 pending_modified = false;
338 Modified(); /* EMIT SIGNAL */
343 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
345 if (holding_state ()) {
346 pending_removes.insert (r);
347 pending_modified = true;
348 pending_length = true;
350 /* this might not be true, but we have to act
351 as though it could be.
353 LengthChanged (); /* EMIT SIGNAL */
354 Modified (); /* EMIT SIGNAL */
359 Playlist::notify_region_added (boost::shared_ptr<Region> r)
361 /* the length change might not be true, but we have to act
362 as though it could be.
365 if (holding_state()) {
366 pending_adds.insert (r);
367 pending_modified = true;
368 pending_length = true;
370 LengthChanged (); /* EMIT SIGNAL */
371 Modified (); /* EMIT SIGNAL */
376 Playlist::notify_length_changed ()
378 if (holding_state ()) {
379 pending_length = true;
381 LengthChanged(); /* EMIT SIGNAL */
382 Modified (); /* EMIT SIGNAL */
387 Playlist::flush_notifications ()
389 set<boost::shared_ptr<Region> > dependent_checks_needed;
390 set<boost::shared_ptr<Region> >::iterator s;
399 /* we have no idea what order the regions ended up in pending
400 bounds (it could be based on selection order, for example).
401 so, to preserve layering in the "most recently moved is higher"
402 model, sort them by existing layer, then timestamp them.
405 // RegionSortByLayer cmp;
406 // pending_bounds.sort (cmp);
408 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
409 if (Config->get_layer_model() == MoveAddHigher) {
410 timestamp_layer_op (*r);
412 pending_length = true;
413 dependent_checks_needed.insert (*r);
417 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
418 dependent_checks_needed.insert (*s);
422 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
423 remove_dependents (*s);
427 if ((freeze_length != _get_maximum_extent()) || pending_length) {
429 LengthChanged(); /* EMIT SIGNAL */
433 if (n || pending_modified) {
438 pending_modified = false;
439 Modified (); /* EMIT SIGNAL */
442 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
443 check_dependents (*s, false);
446 pending_adds.clear ();
447 pending_removes.clear ();
448 pending_bounds.clear ();
453 /*************************************************************
455 *************************************************************/
458 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
460 RegionLock rlock (this);
462 times = fabs (times);
464 int itimes = (int) floor (times);
466 nframes_t pos = position;
469 add_region_internal (region, pos);
470 pos += region->length();
474 /* later regions will all be spliced anyway */
476 if (!holding_state ()) {
477 possibly_splice_unlocked ();
480 /* note that itimes can be zero if we being asked to just
481 insert a single fraction of the region.
484 for (int i = 0; i < itimes; ++i) {
485 boost::shared_ptr<Region> copy = RegionFactory::create (region);
486 add_region_internal (copy, pos);
487 pos += region->length();
490 if (floor (times) != times) {
491 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
493 _session.region_name (name, region->name(), false);
494 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
495 add_region_internal (sub, pos);
500 Playlist::set_region_ownership ()
502 RegionLock rl (this);
503 RegionList::iterator i;
504 boost::weak_ptr<Playlist> pl (shared_from_this());
506 for (i = regions.begin(); i != regions.end(); ++i) {
507 (*i)->set_playlist (pl);
512 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
514 RegionSortByPosition cmp;
515 nframes_t old_length = 0;
517 if (!holding_state()) {
518 old_length = _get_maximum_extent();
521 if (!first_set_state) {
522 boost::shared_ptr<Playlist> foo (shared_from_this());
523 region->set_playlist (boost::weak_ptr<Playlist>(foo));
526 region->set_position (position, this);
528 timestamp_layer_op (region);
530 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
531 all_regions.insert (region);
533 if (!holding_state () && !in_set_state) {
534 /* layers get assigned from XML state */
538 /* we need to notify the existence of new region before checking dependents. Ick. */
540 notify_region_added (region);
542 if (!holding_state ()) {
543 check_dependents (region, false);
544 if (old_length != _get_maximum_extent()) {
545 notify_length_changed ();
549 region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
550 boost::weak_ptr<Region> (region)));
554 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
556 RegionLock rlock (this);
558 remove_region_internal (old);
559 add_region_internal (newr, pos);
561 if (!holding_state ()) {
562 possibly_splice_unlocked ();
567 Playlist::remove_region (boost::shared_ptr<Region> region)
569 RegionLock rlock (this);
570 remove_region_internal (region);
572 if (!holding_state ()) {
573 possibly_splice_unlocked ();
578 Playlist::remove_region_internal (boost::shared_ptr<Region>region)
580 RegionList::iterator i;
581 nframes_t old_length = 0;
583 if (!holding_state()) {
584 old_length = _get_maximum_extent();
589 region->set_playlist (boost::weak_ptr<Playlist>());
592 for (i = regions.begin(); i != regions.end(); ++i) {
597 if (!holding_state ()) {
599 remove_dependents (region);
601 if (old_length != _get_maximum_extent()) {
602 notify_length_changed ();
606 notify_region_removed (region);
614 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
616 if (Config->get_use_overlap_equivalency()) {
617 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
618 if ((*i)->overlap_equivalent (other)) {
619 results.push_back ((*i));
623 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
624 if ((*i)->equivalent (other)) {
625 results.push_back ((*i));
632 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
634 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
636 if ((*i) && (*i)->region_list_equivalent (other)) {
637 results.push_back (*i);
643 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
647 partition_internal (start, end, false, thawlist);
649 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
650 (*i)->thaw ("separation");
655 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
657 RegionLock rlock (this);
658 boost::shared_ptr<Region> region;
659 boost::shared_ptr<Region> current;
661 RegionList::iterator tmp;
663 nframes_t pos1, pos2, pos3, pos4;
664 RegionList new_regions;
668 /* need to work from a copy, because otherwise the regions we add during the process
669 get operated on as well.
672 RegionList copy = regions;
674 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
681 if (current->first_frame() == start && current->last_frame() == end) {
683 remove_region_internal (current);
688 if ((overlap = current->coverage (start, end)) == OverlapNone) {
692 pos1 = current->position();
695 pos4 = current->last_frame();
697 if (overlap == OverlapInternal) {
699 /* split: we need 3 new regions, the front, middle and end.
700 cut: we need 2 regions, the front and end.
705 ---------------*************************------------
708 ---------------*****++++++++++++++++====------------
710 ---------------*****----------------====------------
716 /* "middle" ++++++ */
718 _session.region_name (new_name, current->name(), false);
719 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
720 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
721 add_region_internal (region, start);
722 new_regions.push_back (region);
727 _session.region_name (new_name, current->name(), false);
728 region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
729 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
731 add_region_internal (region, end);
732 new_regions.push_back (region);
737 thawlist.push_back (current);
738 current->trim_end (pos2, this);
740 } else if (overlap == OverlapEnd) {
744 ---------------*************************------------
747 ---------------**************+++++++++++------------
749 ---------------**************-----------------------
757 _session.region_name (new_name, current->name(), false);
758 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
759 Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
760 add_region_internal (region, start);
761 new_regions.push_back (region);
767 thawlist.push_back (current);
768 current->trim_end (pos2, this);
770 } else if (overlap == OverlapStart) {
772 /* split: we need 2 regions: the front and the end.
773 cut: just trim current to skip the cut area
778 ---------------*************************------------
782 ---------------****+++++++++++++++++++++------------
784 -------------------*********************------------
791 _session.region_name (new_name, current->name(), false);
792 region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
793 regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
794 add_region_internal (region, pos1);
795 new_regions.push_back (region);
801 thawlist.push_back (current);
802 current->trim_front (pos3, this);
804 } else if (overlap == OverlapExternal) {
806 /* split: no split required.
807 cut: remove the region.
812 ---------------*************************------------
816 ---------------*************************------------
818 ----------------------------------------------------
823 remove_region_internal (current);
825 new_regions.push_back (current);
829 in_partition = false;
831 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
832 check_dependents (*i, false);
836 boost::shared_ptr<Playlist>
837 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
839 boost::shared_ptr<Playlist> ret;
840 boost::shared_ptr<Playlist> pl;
843 if (ranges.empty()) {
844 return boost::shared_ptr<Playlist>();
847 start = ranges.front().start;
849 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
851 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
853 if (i == ranges.begin()) {
857 /* paste the next section into the nascent playlist,
858 offset to reflect the start of the first range we
862 ret->paste (pl, (*i).start - start, 1.0f);
869 boost::shared_ptr<Playlist>
870 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
872 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
873 return cut_copy (pmf, ranges, result_is_hidden);
876 boost::shared_ptr<Playlist>
877 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
879 boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
880 return cut_copy (pmf, ranges, result_is_hidden);
883 boost::shared_ptr<Playlist>
884 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
886 boost::shared_ptr<Playlist> the_copy;
890 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
891 string new_name = _name;
895 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
896 return boost::shared_ptr<Playlist>();
899 partition_internal (start, start+cnt-1, true, thawlist);
902 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
903 (*i)->thaw ("playlist cut");
909 boost::shared_ptr<Playlist>
910 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
914 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
915 string new_name = _name;
919 cnt = min (_get_maximum_extent() - start, cnt);
920 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
924 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
926 times = fabs (times);
927 nframes_t old_length;
930 RegionLock rl1 (this);
931 RegionLock rl2 (other.get());
933 old_length = _get_maximum_extent();
935 int itimes = (int) floor (times);
936 nframes_t pos = position;
937 nframes_t shift = other->_get_maximum_extent();
938 layer_t top_layer = regions.size();
941 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
942 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
944 /* put these new regions on top of all existing ones, but preserve
945 the ordering they had in the original playlist.
948 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
949 add_region_internal (copy_of_region, copy_of_region->position() + pos);
954 possibly_splice_unlocked ();
956 /* XXX shall we handle fractional cases at some point? */
958 if (old_length != _get_maximum_extent()) {
959 notify_length_changed ();
970 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
972 times = fabs (times);
974 RegionLock rl (this);
975 int itimes = (int) floor (times);
976 nframes_t pos = position;
979 boost::shared_ptr<Region> copy = RegionFactory::create (region);
980 add_region_internal (copy, pos);
981 pos += region->length();
984 if (floor (times) != times) {
985 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
987 _session.region_name (name, region->name(), false);
988 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
989 add_region_internal (sub, pos);
994 Playlist::split_region (boost::shared_ptr<Region> region, 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;
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());
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)) {
1047 Playlist::possibly_splice ()
1049 if (_edit_mode == Splice) {
1055 Playlist::possibly_splice_unlocked ()
1057 if (_edit_mode == Splice) {
1063 Playlist::splice_locked ()
1066 RegionLock rl (this);
1070 notify_length_changed ();
1074 Playlist::splice_unlocked ()
1077 notify_length_changed ();
1081 Playlist::core_splice ()
1085 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1087 RegionList::iterator next;
1092 if (next == regions.end()) {
1096 (*next)->set_position ((*i)->last_frame() + 1, this);
1103 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1105 if (in_set_state || _splicing || _nudging) {
1109 if (what_changed & ARDOUR::PositionChanged) {
1111 /* remove it from the list then add it back in
1112 the right place again.
1115 RegionSortByPosition cmp;
1117 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1119 if (i == regions.end()) {
1120 warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1121 _name, region->name())
1127 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1131 if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1132 if (holding_state ()) {
1133 pending_bounds.push_back (region);
1135 if (Config->get_layer_model() == MoveAddHigher) {
1136 /* it moved or changed length, so change the timestamp */
1137 timestamp_layer_op (region);
1141 notify_length_changed ();
1143 check_dependents (region, false);
1149 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1151 boost::shared_ptr<Region> region (weak_region.lock());
1158 /* this makes a virtual call to the right kind of playlist ... */
1160 region_changed (what_changed, region);
1164 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1166 Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1169 if (in_set_state || in_flush) {
1174 if (what_changed & BoundsChanged) {
1175 region_bounds_changed (what_changed, region);
1176 save = !(_splicing || _nudging);
1179 if ((what_changed & Region::MuteChanged) &&
1180 !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1181 check_dependents (region, false);
1184 if (what_changed & our_interests) {
1193 Playlist::drop_regions ()
1195 RegionLock rl (this);
1197 all_regions.clear ();
1201 Playlist::clear (bool with_signals)
1204 RegionLock rl (this);
1205 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1206 pending_removes.insert (*i);
1218 /***********************************************************************
1220 **********************************************************************/
1222 Playlist::RegionList *
1223 Playlist::regions_at (nframes_t frame)
1226 RegionLock rlock (this);
1227 return find_regions_at (frame);
1230 boost::shared_ptr<Region>
1231 Playlist::top_region_at (nframes_t frame)
1234 RegionLock rlock (this);
1235 RegionList *rlist = find_regions_at (frame);
1236 boost::shared_ptr<Region> region;
1238 if (rlist->size()) {
1239 RegionSortByLayer cmp;
1241 region = rlist->back();
1248 Playlist::RegionList *
1249 Playlist::find_regions_at (nframes_t frame)
1251 RegionList *rlist = new RegionList;
1253 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1254 if ((*i)->covers (frame)) {
1255 rlist->push_back (*i);
1262 Playlist::RegionList *
1263 Playlist::regions_touched (nframes_t start, nframes_t end)
1265 RegionLock rlock (this);
1266 RegionList *rlist = new RegionList;
1268 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1269 if ((*i)->coverage (start, end) != OverlapNone) {
1270 rlist->push_back (*i);
1278 boost::shared_ptr<Region>
1279 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1281 RegionLock rlock (this);
1282 boost::shared_ptr<Region> ret;
1283 nframes_t closest = max_frames;
1286 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1289 boost::shared_ptr<Region> r = (*i);
1294 pos = r->first_frame ();
1297 pos = r->last_frame ();
1300 pos = r->adjust_to_sync (r->first_frame());
1305 case 1: /* forwards */
1308 if ((distance = pos - frame) < closest) {
1316 default: /* backwards */
1319 if ((distance = frame - pos) < closest) {
1331 /***********************************************************************/
1336 Playlist::mark_session_dirty ()
1338 if (!in_set_state && !holding_state ()) {
1339 _session.set_dirty();
1344 Playlist::set_state (const XMLNode& node)
1348 XMLNodeConstIterator niter;
1349 XMLPropertyList plist;
1350 XMLPropertyConstIterator piter;
1352 boost::shared_ptr<Region> region;
1357 if (node.name() != "Playlist") {
1364 plist = node.properties();
1366 for (piter = plist.begin(); piter != plist.end(); ++piter) {
1370 if (prop->name() == X_("name")) {
1371 _name = prop->value();
1372 } else if (prop->name() == X_("orig_diskstream_id")) {
1373 _orig_diskstream_id = prop->value ();
1374 } else if (prop->name() == X_("frozen")) {
1375 _frozen = (prop->value() == X_("yes"));
1381 nlist = node.children();
1383 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1387 if (child->name() == "Region") {
1389 if ((prop = child->property ("id")) == 0) {
1390 error << _("region state node has no ID, ignored") << endmsg;
1394 ID id = prop->value ();
1396 if ((region = region_by_id (id))) {
1398 Change what_changed = Change (0);
1400 if (region->set_live_state (*child, what_changed, true)) {
1401 error << _("Playlist: cannot reset region state from XML") << endmsg;
1405 } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1406 error << _("Playlist: cannot create region from XML") << endmsg;
1410 add_region (region, region->position(), 1.0);
1412 // So that layer_op ordering doesn't get screwed up
1413 region->set_last_layer_op( region->layer());
1422 /* update dependents, which was not done during add_region_internal
1423 due to in_set_state being true
1426 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1427 check_dependents (*r, false);
1431 first_set_state = false;
1436 Playlist::get_state()
1442 Playlist::get_template()
1444 return state(false);
1448 Playlist::state (bool full_state)
1450 XMLNode *node = new XMLNode (X_("Playlist"));
1453 node->add_property (X_("name"), _name);
1454 node->add_property (X_("type"), _type.to_string());
1456 _orig_diskstream_id.print (buf, sizeof (buf));
1457 node->add_property (X_("orig_diskstream_id"), buf);
1458 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1461 RegionLock rlock (this, false);
1462 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1463 node->add_child_nocopy ((*i)->get_state());
1468 node->add_child_copy (*_extra_xml);
1475 Playlist::empty() const
1477 RegionLock rlock (const_cast<Playlist *>(this), false);
1478 return regions.empty();
1482 Playlist::n_regions() const
1484 RegionLock rlock (const_cast<Playlist *>(this), false);
1485 return regions.size();
1489 Playlist::get_maximum_extent () const
1491 RegionLock rlock (const_cast<Playlist *>(this), false);
1492 return _get_maximum_extent ();
1496 Playlist::_get_maximum_extent () const
1498 RegionList::const_iterator i;
1499 nframes_t max_extent = 0;
1502 for (i = regions.begin(); i != regions.end(); ++i) {
1503 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1512 Playlist::bump_name (string name, Session &session)
1514 string newname = name;
1517 newname = Playlist::bump_name_once (newname);
1518 } while (session.playlist_by_name (newname)!=NULL);
1524 Playlist::bump_name_once (string name)
1526 string::size_type period;
1529 if ((period = name.find_last_of ('.')) == string::npos) {
1534 const char *last_element = name.c_str() + period + 1;
1535 for (size_t i = 0; i < strlen(last_element); i++) {
1536 if (!isdigit(last_element[i])) {
1543 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1545 if (isnumber == 0 || errno != 0) {
1546 // last_element is not a number, or is too large
1552 snprintf (buf, sizeof(buf), "%ld", version+1);
1554 newname = name.substr (0, period+1);
1563 Playlist::top_layer() const
1565 RegionLock rlock (const_cast<Playlist *> (this));
1568 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1569 top = max (top, (*i)->layer());
1575 Playlist::set_edit_mode (EditMode mode)
1580 /********************
1582 ********************/
1585 Playlist::relayer ()
1587 /* don't send multiple Modified notifications
1588 when multiple regions are relayered.
1593 /* build up a new list of regions on each layer */
1595 std::vector<RegionList> layers;
1597 /* we want to go through regions from desired lowest to desired highest layer,
1598 which depends on the layer model
1601 RegionList copy = regions;
1603 /* sort according to the model */
1605 if (Config->get_layer_model() == MoveAddHigher || Config->get_layer_model() == AddHigher) {
1606 RegionSortByLastLayerOp cmp;
1611 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1613 /* find the lowest layer that this region can go on */
1614 size_t j = layers.size();
1616 /* try layer j - 1; it can go on if it overlaps no other region
1617 that is already on that layer
1619 RegionList::iterator k = layers[j - 1].begin();
1620 while (k != layers[j - 1].end()) {
1621 if ((*k)->overlap_equivalent (*i)) {
1627 if (k != layers[j - 1].end()) {
1628 /* no overlap, so we can use this layer */
1635 if (j == layers.size()) {
1636 /* we need a new layer for this region */
1637 layers.push_back (RegionList ());
1640 layers[j].push_back (*i);
1643 /* first pass: set up the layer numbers in the regions */
1644 for (size_t j = 0; j < layers.size(); ++j) {
1645 for (RegionList::iterator i = layers[j].begin(); i != layers[j].end(); ++i) {
1646 (*i)->set_layer (j);
1650 /* sending Modified means that various kinds of layering
1651 models operate correctly at the GUI
1652 level. slightly inefficient, but only slightly.
1654 We force a Modified signal here in case no layers actually
1663 /* XXX these layer functions are all deprecated */
1666 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1668 /* does nothing useful if layering mode is later=higher */
1669 if ((Config->get_layer_model() == MoveAddHigher) ||
1670 (Config->get_layer_model() == AddHigher)) {
1671 timestamp_layer_op (region);
1677 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1679 /* does nothing useful if layering mode is later=higher */
1680 if ((Config->get_layer_model() == MoveAddHigher) ||
1681 (Config->get_layer_model() == AddHigher)) {
1682 region->set_last_layer_op (0);
1688 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1690 RegionList::iterator i;
1697 RegionLock rlock (const_cast<Playlist *> (this));
1699 for (i = regions.begin(); i != regions.end(); ++i) {
1701 if ((*i)->position() >= start) {
1705 if ((*i)->last_frame() > max_frames - distance) {
1706 new_pos = max_frames - (*i)->length();
1708 new_pos = (*i)->position() + distance;
1713 if ((*i)->position() > distance) {
1714 new_pos = (*i)->position() - distance;
1720 (*i)->set_position (new_pos, this);
1728 notify_length_changed ();
1733 boost::shared_ptr<Region>
1734 Playlist::find_region (const ID& id) const
1736 RegionLock rlock (const_cast<Playlist*> (this));
1738 /* searches all regions currently in use by the playlist */
1740 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1741 if ((*i)->id() == id) {
1746 return boost::shared_ptr<Region> ();
1749 boost::shared_ptr<Region>
1750 Playlist::region_by_id (ID id)
1752 /* searches all regions ever added to this playlist */
1754 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
1755 if ((*i)->id() == id) {
1759 return boost::shared_ptr<Region> ();
1763 Playlist::dump () const
1765 boost::shared_ptr<Region> r;
1767 cerr << "Playlist \"" << _name << "\" " << endl
1768 << regions.size() << " regions "
1771 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1773 cerr << " " << r->name() << " ["
1774 << r->start() << "+" << r->length()
1784 Playlist::set_frozen (bool yn)
1790 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1792 // struct timeval tv;
1793 // gettimeofday (&tv, 0);
1794 region->set_last_layer_op (++layer_op_counter);