c&p debug
[ardour2.git] / libs / ardour / playlist.cc
blobb0a36ec422dfc3ac2b58f13232a04e6d4cf497cb
1 /*
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.
20 #include <stdint.h>
21 #include <set>
22 #include <fstream>
23 #include <algorithm>
24 #include <unistd.h>
25 #include <cerrno>
26 #include <string>
27 #include <climits>
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/failed_constructor.h"
32 #include "pbd/stateful_diff_command.h"
33 #include "pbd/xml++.h"
34 #include "pbd/stacktrace.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/playlist_factory.h"
42 #include "ardour/transient_detector.h"
43 #include "ardour/session_playlists.h"
45 #include "i18n.h"
47 using namespace std;
48 using namespace ARDOUR;
49 using namespace PBD;
51 namespace ARDOUR {
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> regions;
57 struct ShowMeTheList {
58 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
59 ~ShowMeTheList () {
60 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
62 boost::shared_ptr<Playlist> playlist;
63 string name;
66 struct RegionSortByLayer {
67 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
68 return a->layer() < b->layer();
72 struct RegionSortByLayerWithPending {
73 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
75 double p = a->layer ();
76 if (a->pending_explicit_relayer()) {
77 p += 0.5;
80 double q = b->layer ();
81 if (b->pending_explicit_relayer()) {
82 q += 0.5;
85 return p < q;
89 struct RegionSortByPosition {
90 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
91 return a->position() < b->position();
95 struct RegionSortByLastLayerOp {
96 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
97 return a->last_layer_op() < b->last_layer_op();
101 void
102 Playlist::make_property_quarks ()
104 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
105 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
108 RegionListProperty::RegionListProperty (Playlist& pl)
109 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
110 , _playlist (pl)
115 RegionListProperty::RegionListProperty (RegionListProperty const & p)
116 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
117 , _playlist (p._playlist)
122 RegionListProperty *
123 RegionListProperty::clone () const
125 return new RegionListProperty (*this);
128 RegionListProperty *
129 RegionListProperty::create () const
131 return new RegionListProperty (_playlist);
134 void
135 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
137 /* All regions (even those which are deleted) have their state saved by other
138 code, so we can just store ID here.
141 node.add_property ("id", region->id().to_s ());
144 boost::shared_ptr<Region>
145 RegionListProperty::get_content_from_xml (XMLNode const & node) const
147 XMLProperty const * prop = node.property ("id");
148 assert (prop);
150 PBD::ID id (prop->value ());
152 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
154 if (!ret) {
155 ret = RegionFactory::region_by_id (id);
158 return ret;
161 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
162 : SessionObject(sess, nom)
163 , regions (*this)
164 , _type(type)
166 init (hide);
167 first_set_state = false;
168 _name = nom;
169 _set_sort_id ();
173 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
174 : SessionObject(sess, "unnamed playlist")
175 , regions (*this)
176 , _type(type)
179 #ifndef NDEBUG
180 const XMLProperty* prop = node.property("type");
181 assert(!prop || DataType(prop->value()) == _type);
182 #endif
184 init (hide);
185 _name = "unnamed"; /* reset by set_state */
186 _set_sort_id ();
188 /* set state called by derived class */
191 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
192 : SessionObject(other->_session, namestr)
193 , regions (*this)
194 , _type(other->_type)
195 , _orig_diskstream_id (other->_orig_diskstream_id)
197 init (hide);
199 RegionList tmp;
200 other->copy_regions (tmp);
202 in_set_state++;
204 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
205 add_region_internal( (*x), (*x)->position());
208 in_set_state--;
210 _splicing = other->_splicing;
211 _nudging = other->_nudging;
212 _edit_mode = other->_edit_mode;
214 in_set_state = 0;
215 first_set_state = false;
216 in_flush = false;
217 in_partition = false;
218 subcnt = 0;
219 _read_data_count = 0;
220 _frozen = other->_frozen;
222 layer_op_counter = other->layer_op_counter;
223 freeze_length = other->freeze_length;
226 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
227 : SessionObject(other->_session, str)
228 , regions (*this)
229 , _type(other->_type)
230 , _orig_diskstream_id (other->_orig_diskstream_id)
232 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
234 framepos_t end = start + cnt - 1;
236 init (hide);
238 in_set_state++;
240 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
242 boost::shared_ptr<Region> region;
243 boost::shared_ptr<Region> new_region;
244 frameoffset_t offset = 0;
245 framepos_t position = 0;
246 framecnt_t len = 0;
247 string new_name;
248 OverlapType overlap;
250 region = *i;
252 overlap = region->coverage (start, end);
254 switch (overlap) {
255 case OverlapNone:
256 continue;
258 case OverlapInternal:
259 offset = start - region->position();
260 position = 0;
261 len = cnt;
262 break;
264 case OverlapStart:
265 offset = 0;
266 position = region->position() - start;
267 len = end - region->position();
268 break;
270 case OverlapEnd:
271 offset = start - region->position();
272 position = 0;
273 len = region->length() - offset;
274 break;
276 case OverlapExternal:
277 offset = 0;
278 position = region->position() - start;
279 len = region->length();
280 break;
283 RegionFactory::region_name (new_name, region->name(), false);
285 PropertyList plist;
287 plist.add (Properties::start, region->start() + offset);
288 plist.add (Properties::length, len);
289 plist.add (Properties::name, new_name);
290 plist.add (Properties::layer, region->layer());
292 new_region = RegionFactory::RegionFactory::create (region, plist);
294 add_region_internal (new_region, position);
297 in_set_state--;
298 first_set_state = false;
300 /* this constructor does NOT notify others (session) */
303 void
304 Playlist::use ()
306 ++_refcnt;
307 InUse (true); /* EMIT SIGNAL */
310 void
311 Playlist::release ()
313 if (_refcnt > 0) {
314 _refcnt--;
317 if (_refcnt == 0) {
318 InUse (false); /* EMIT SIGNAL */
322 void
323 Playlist::copy_regions (RegionList& newlist) const
325 RegionLock rlock (const_cast<Playlist *> (this));
327 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
328 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
332 void
333 Playlist::init (bool hide)
335 add_property (regions);
336 _xml_node_name = X_("Playlist");
338 g_atomic_int_set (&block_notifications, 0);
339 g_atomic_int_set (&ignore_state_changes, 0);
340 pending_contents_change = false;
341 pending_length = false;
342 pending_layering = false;
343 first_set_state = true;
344 _refcnt = 0;
345 _hidden = hide;
346 _splicing = false;
347 _shuffling = false;
348 _nudging = false;
349 in_set_state = 0;
350 in_update = false;
351 _edit_mode = Config->get_edit_mode();
352 in_flush = false;
353 in_partition = false;
354 subcnt = 0;
355 _read_data_count = 0;
356 _frozen = false;
357 layer_op_counter = 0;
358 freeze_length = 0;
359 _explicit_relayering = false;
361 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
362 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
364 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
367 Playlist::~Playlist ()
369 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
372 RegionLock rl (this);
374 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
375 (*i)->set_playlist (boost::shared_ptr<Playlist>());
379 /* GoingAway must be emitted by derived classes */
382 void
383 Playlist::_set_sort_id ()
386 Playlists are given names like <track name>.<id>
387 or <track name>.<edit group name>.<id> where id
388 is an integer. We extract the id and sort by that.
391 size_t dot_position = _name.val().find_last_of(".");
393 if (dot_position == string::npos) {
394 _sort_id = 0;
395 } else {
396 string t = _name.val().substr(dot_position + 1);
398 try {
399 _sort_id = boost::lexical_cast<int>(t);
402 catch (boost::bad_lexical_cast e) {
403 _sort_id = 0;
408 bool
409 Playlist::set_name (const string& str)
411 /* in a typical situation, a playlist is being used
412 by one diskstream and also is referenced by the
413 Session. if there are more references than that,
414 then don't change the name.
417 if (_refcnt > 2) {
418 return false;
421 bool ret = SessionObject::set_name(str);
422 if (ret) {
423 _set_sort_id ();
425 return ret;
428 /***********************************************************************
429 CHANGE NOTIFICATION HANDLING
431 Notifications must be delayed till the region_lock is released. This
432 is necessary because handlers for the signals may need to acquire
433 the lock (e.g. to read from the playlist).
434 ***********************************************************************/
436 void
437 Playlist::begin_undo ()
439 in_update = true;
440 freeze ();
443 void
444 Playlist::end_undo ()
446 thaw (true);
447 in_update = false;
450 void
451 Playlist::freeze ()
453 delay_notifications ();
454 g_atomic_int_inc (&ignore_state_changes);
457 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
458 void
459 Playlist::thaw (bool from_undo)
461 g_atomic_int_dec_and_test (&ignore_state_changes);
462 release_notifications (from_undo);
466 void
467 Playlist::delay_notifications ()
469 g_atomic_int_inc (&block_notifications);
470 freeze_length = _get_extent().second;
473 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
474 void
475 Playlist::release_notifications (bool from_undo)
477 if (g_atomic_int_dec_and_test (&block_notifications)) {
478 flush_notifications (from_undo);
483 void
484 Playlist::notify_contents_changed ()
486 if (holding_state ()) {
487 pending_contents_change = true;
488 } else {
489 pending_contents_change = false;
490 ContentsChanged(); /* EMIT SIGNAL */
494 void
495 Playlist::notify_layering_changed ()
497 if (holding_state ()) {
498 pending_layering = true;
499 } else {
500 pending_layering = false;
501 LayeringChanged(); /* EMIT SIGNAL */
505 void
506 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
508 if (holding_state ()) {
509 pending_removes.insert (r);
510 pending_contents_change = true;
511 pending_length = true;
512 } else {
513 /* this might not be true, but we have to act
514 as though it could be.
516 pending_length = false;
517 LengthChanged (); /* EMIT SIGNAL */
518 pending_contents_change = false;
519 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
520 ContentsChanged (); /* EMIT SIGNAL */
524 void
525 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
527 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
529 if (holding_state ()) {
531 pending_range_moves.push_back (move);
533 } else {
535 list< Evoral::RangeMove<framepos_t> > m;
536 m.push_back (move);
537 RangesMoved (m, false);
542 void
543 Playlist::notify_region_added (boost::shared_ptr<Region> r)
545 /* the length change might not be true, but we have to act
546 as though it could be.
549 if (holding_state()) {
550 pending_adds.insert (r);
551 pending_contents_change = true;
552 pending_length = true;
553 } else {
554 r->clear_changes ();
555 pending_length = false;
556 LengthChanged (); /* EMIT SIGNAL */
557 pending_contents_change = false;
558 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
559 ContentsChanged (); /* EMIT SIGNAL */
563 void
564 Playlist::notify_length_changed ()
566 if (holding_state ()) {
567 pending_length = true;
568 } else {
569 pending_length = false;
570 LengthChanged(); /* EMIT SIGNAL */
571 pending_contents_change = false;
572 ContentsChanged (); /* EMIT SIGNAL */
576 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
577 void
578 Playlist::flush_notifications (bool from_undo)
580 set<boost::shared_ptr<Region> > dependent_checks_needed;
581 set<boost::shared_ptr<Region> >::iterator s;
582 uint32_t regions_changed = false;
583 bool check_length = false;
584 framecnt_t old_length = 0;
586 if (in_flush) {
587 return;
590 in_flush = true;
592 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
593 regions_changed = true;
594 if (!pending_length) {
595 old_length = _get_extent ().second;
596 check_length = true;
600 /* we have no idea what order the regions ended up in pending
601 bounds (it could be based on selection order, for example).
602 so, to preserve layering in the "most recently moved is higher"
603 model, sort them by existing layer, then timestamp them.
606 // RegionSortByLayer cmp;
607 // pending_bounds.sort (cmp);
609 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
610 if (_session.config.get_layer_model() == MoveAddHigher) {
611 timestamp_layer_op (*r);
613 dependent_checks_needed.insert (*r);
616 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
617 remove_dependents (*s);
618 // cerr << _name << " sends RegionRemoved\n";
619 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
622 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
623 // cerr << _name << " sends RegionAdded\n";
624 /* don't emit RegionAdded signal until relayering is done,
625 so that the region is fully setup by the time
626 anyone hear's that its been added
628 dependent_checks_needed.insert (*s);
631 if (check_length) {
632 if (old_length != _get_extent().second) {
633 pending_length = true;
634 // cerr << _name << " length has changed\n";
638 if (pending_length || (freeze_length != _get_extent().second)) {
639 pending_length = false;
640 // cerr << _name << " sends LengthChanged\n";
641 LengthChanged(); /* EMIT SIGNAL */
644 if (regions_changed || pending_contents_change) {
645 if (!in_set_state) {
646 relayer ();
648 pending_contents_change = false;
649 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
650 ContentsChanged (); /* EMIT SIGNAL */
651 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
654 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
655 (*s)->clear_changes ();
656 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
659 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
660 check_dependents (*s, false);
663 if (!pending_range_moves.empty ()) {
664 RangesMoved (pending_range_moves, from_undo);
667 clear_pending ();
669 in_flush = false;
672 void
673 Playlist::clear_pending ()
675 pending_adds.clear ();
676 pending_removes.clear ();
677 pending_bounds.clear ();
678 pending_range_moves.clear ();
679 pending_contents_change = false;
680 pending_length = false;
683 /*************************************************************
684 PLAYLIST OPERATIONS
685 *************************************************************/
687 void
688 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
690 RegionLock rlock (this);
691 times = fabs (times);
693 int itimes = (int) floor (times);
695 framepos_t pos = position;
697 if (times == 1 && auto_partition){
698 partition(pos - 1, (pos + region->length()), true);
701 if (itimes >= 1) {
702 add_region_internal (region, pos);
703 pos += region->length();
704 --itimes;
708 /* note that itimes can be zero if we being asked to just
709 insert a single fraction of the region.
712 for (int i = 0; i < itimes; ++i) {
713 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
714 add_region_internal (copy, pos);
715 pos += region->length();
718 framecnt_t length = 0;
720 if (floor (times) != times) {
721 length = (framecnt_t) floor (region->length() * (times - floor (times)));
722 string name;
723 RegionFactory::region_name (name, region->name(), false);
726 PropertyList plist;
728 plist.add (Properties::start, region->start());
729 plist.add (Properties::length, length);
730 plist.add (Properties::name, name);
731 plist.add (Properties::layer, region->layer());
733 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
734 add_region_internal (sub, pos);
738 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
741 void
742 Playlist::set_region_ownership ()
744 RegionLock rl (this);
745 RegionList::iterator i;
746 boost::weak_ptr<Playlist> pl (shared_from_this());
748 for (i = regions.begin(); i != regions.end(); ++i) {
749 (*i)->set_playlist (pl);
753 bool
754 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
756 if (region->data_type() != _type){
757 return false;
760 RegionSortByPosition cmp;
762 framecnt_t old_length = 0;
764 if (!holding_state()) {
765 old_length = _get_extent().second;
768 if (!first_set_state) {
769 boost::shared_ptr<Playlist> foo (shared_from_this());
770 region->set_playlist (boost::weak_ptr<Playlist>(foo));
773 region->set_position (position, this);
775 timestamp_layer_op (region);
777 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
778 all_regions.insert (region);
780 possibly_splice_unlocked (position, region->length(), region);
782 if (!holding_state ()) {
783 /* layers get assigned from XML state, and are not reset during undo/redo */
784 relayer ();
787 /* we need to notify the existence of new region before checking dependents. Ick. */
789 notify_region_added (region);
791 if (!holding_state ()) {
793 check_dependents (region, false);
795 if (old_length != _get_extent().second) {
796 notify_length_changed ();
800 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
802 return true;
805 void
806 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
808 RegionLock rlock (this);
810 bool old_sp = _splicing;
811 _splicing = true;
813 remove_region_internal (old);
814 add_region_internal (newr, pos);
816 _splicing = old_sp;
818 possibly_splice_unlocked (pos, old->length() - newr->length());
821 void
822 Playlist::remove_region (boost::shared_ptr<Region> region)
824 RegionLock rlock (this);
825 remove_region_internal (region);
829 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
831 RegionList::iterator i;
832 framecnt_t old_length = 0;
833 int ret = -1;
835 if (!holding_state()) {
836 old_length = _get_extent().second;
839 if (!in_set_state) {
840 /* unset playlist */
841 region->set_playlist (boost::weak_ptr<Playlist>());
844 /* XXX should probably freeze here .... */
846 for (i = regions.begin(); i != regions.end(); ++i) {
847 if (*i == region) {
849 framepos_t pos = (*i)->position();
850 framecnt_t distance = (*i)->length();
852 regions.erase (i);
854 possibly_splice_unlocked (pos, -distance);
856 if (!holding_state ()) {
857 relayer ();
858 remove_dependents (region);
860 if (old_length != _get_extent().second) {
861 notify_length_changed ();
865 notify_region_removed (region);
866 ret = 0;
867 break;
871 return -1;
874 void
875 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
877 if (Config->get_use_overlap_equivalency()) {
878 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
879 if ((*i)->overlap_equivalent (other)) {
880 results.push_back ((*i));
883 } else {
884 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
885 if ((*i)->equivalent (other)) {
886 results.push_back ((*i));
892 void
893 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
895 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
897 if ((*i) && (*i)->region_list_equivalent (other)) {
898 results.push_back (*i);
903 void
904 Playlist::partition (framepos_t start, framepos_t end, bool cut)
906 RegionList thawlist;
908 partition_internal (start, end, cut, thawlist);
910 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
911 (*i)->resume_property_changes ();
915 void
916 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
918 RegionList new_regions;
921 RegionLock rlock (this);
923 boost::shared_ptr<Region> region;
924 boost::shared_ptr<Region> current;
925 string new_name;
926 RegionList::iterator tmp;
927 OverlapType overlap;
928 framepos_t pos1, pos2, pos3, pos4;
930 in_partition = true;
932 /* need to work from a copy, because otherwise the regions we add during the process
933 get operated on as well.
936 RegionList copy = regions.rlist();
938 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
940 tmp = i;
941 ++tmp;
943 current = *i;
945 if (current->first_frame() >= start && current->last_frame() < end) {
947 if (cutting) {
948 remove_region_internal (current);
951 continue;
954 /* coverage will return OverlapStart if the start coincides
955 with the end point. we do not partition such a region,
956 so catch this special case.
959 if (current->first_frame() >= end) {
960 continue;
963 if ((overlap = current->coverage (start, end)) == OverlapNone) {
964 continue;
967 pos1 = current->position();
968 pos2 = start;
969 pos3 = end;
970 pos4 = current->last_frame();
972 if (overlap == OverlapInternal) {
973 /* split: we need 3 new regions, the front, middle and end.
974 cut: we need 2 regions, the front and end.
978 start end
979 ---------------*************************------------
980 P1 P2 P3 P4
981 SPLIT:
982 ---------------*****++++++++++++++++====------------
984 ---------------*****----------------====------------
988 if (!cutting) {
989 /* "middle" ++++++ */
991 RegionFactory::region_name (new_name, current->name(), false);
993 PropertyList plist;
995 plist.add (Properties::start, current->start() + (pos2 - pos1));
996 plist.add (Properties::length, pos3 - pos2);
997 plist.add (Properties::name, new_name);
998 plist.add (Properties::layer, regions.size());
999 plist.add (Properties::automatic, true);
1000 plist.add (Properties::left_of_split, true);
1001 plist.add (Properties::right_of_split, true);
1003 region = RegionFactory::create (current, plist);
1004 add_region_internal (region, start);
1005 new_regions.push_back (region);
1008 /* "end" ====== */
1010 RegionFactory::region_name (new_name, current->name(), false);
1012 PropertyList plist;
1014 plist.add (Properties::start, current->start() + (pos3 - pos1));
1015 plist.add (Properties::length, pos4 - pos3);
1016 plist.add (Properties::name, new_name);
1017 plist.add (Properties::layer, regions.size());
1018 plist.add (Properties::automatic, true);
1019 plist.add (Properties::right_of_split, true);
1021 region = RegionFactory::create (current, plist);
1023 add_region_internal (region, end);
1024 new_regions.push_back (region);
1026 /* "front" ***** */
1028 current->suspend_property_changes ();
1029 thawlist.push_back (current);
1030 current->cut_end (pos2 - 1, this);
1032 } else if (overlap == OverlapEnd) {
1035 start end
1036 ---------------*************************------------
1037 P1 P2 P4 P3
1038 SPLIT:
1039 ---------------**************+++++++++++------------
1040 CUT:
1041 ---------------**************-----------------------
1044 if (!cutting) {
1046 /* end +++++ */
1048 RegionFactory::region_name (new_name, current->name(), false);
1050 PropertyList plist;
1052 plist.add (Properties::start, current->start() + (pos2 - pos1));
1053 plist.add (Properties::length, pos4 - pos2);
1054 plist.add (Properties::name, new_name);
1055 plist.add (Properties::layer, regions.size());
1056 plist.add (Properties::automatic, true);
1057 plist.add (Properties::left_of_split, true);
1059 region = RegionFactory::create (current, plist);
1061 add_region_internal (region, start);
1062 new_regions.push_back (region);
1065 /* front ****** */
1067 current->suspend_property_changes ();
1068 thawlist.push_back (current);
1069 current->cut_end (pos2 - 1, this);
1071 } else if (overlap == OverlapStart) {
1073 /* split: we need 2 regions: the front and the end.
1074 cut: just trim current to skip the cut area
1078 start end
1079 ---------------*************************------------
1080 P2 P1 P3 P4
1082 SPLIT:
1083 ---------------****+++++++++++++++++++++------------
1084 CUT:
1085 -------------------*********************------------
1089 if (!cutting) {
1090 /* front **** */
1091 RegionFactory::region_name (new_name, current->name(), false);
1093 PropertyList plist;
1095 plist.add (Properties::start, current->start());
1096 plist.add (Properties::length, pos3 - pos1);
1097 plist.add (Properties::name, new_name);
1098 plist.add (Properties::layer, regions.size());
1099 plist.add (Properties::automatic, true);
1100 plist.add (Properties::right_of_split, true);
1102 region = RegionFactory::create (current, plist);
1104 add_region_internal (region, pos1);
1105 new_regions.push_back (region);
1108 /* end */
1110 current->suspend_property_changes ();
1111 thawlist.push_back (current);
1112 current->trim_front (pos3, this);
1113 } else if (overlap == OverlapExternal) {
1115 /* split: no split required.
1116 cut: remove the region.
1120 start end
1121 ---------------*************************------------
1122 P2 P1 P3 P4
1124 SPLIT:
1125 ---------------*************************------------
1126 CUT:
1127 ----------------------------------------------------
1131 if (cutting) {
1132 remove_region_internal (current);
1135 new_regions.push_back (current);
1139 in_partition = false;
1142 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1143 check_dependents (*i, false);
1147 boost::shared_ptr<Playlist>
1148 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1150 boost::shared_ptr<Playlist> ret;
1151 boost::shared_ptr<Playlist> pl;
1152 framepos_t start;
1154 if (ranges.empty()) {
1155 return boost::shared_ptr<Playlist>();
1158 start = ranges.front().start;
1160 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1162 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1164 if (i == ranges.begin()) {
1165 ret = pl;
1166 } else {
1168 /* paste the next section into the nascent playlist,
1169 offset to reflect the start of the first range we
1170 chopped.
1173 ret->paste (pl, (*i).start - start, 1.0f);
1177 return ret;
1180 boost::shared_ptr<Playlist>
1181 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1183 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1184 return cut_copy (pmf, ranges, result_is_hidden);
1187 boost::shared_ptr<Playlist>
1188 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1190 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1191 return cut_copy (pmf, ranges, result_is_hidden);
1194 boost::shared_ptr<Playlist>
1195 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1197 boost::shared_ptr<Playlist> the_copy;
1198 RegionList thawlist;
1199 char buf[32];
1201 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1202 string new_name = _name;
1203 new_name += '.';
1204 new_name += buf;
1206 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1207 return boost::shared_ptr<Playlist>();
1210 partition_internal (start, start+cnt-1, true, thawlist);
1212 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1213 (*i)->resume_property_changes();
1216 return the_copy;
1219 boost::shared_ptr<Playlist>
1220 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1222 char buf[32];
1224 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1225 string new_name = _name;
1226 new_name += '.';
1227 new_name += buf;
1229 cnt = min (_get_extent().second - start, cnt);
1230 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1234 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1236 times = fabs (times);
1239 RegionLock rl1 (this);
1240 RegionLock rl2 (other.get());
1242 framecnt_t const old_length = _get_extent().second;
1244 int itimes = (int) floor (times);
1245 framepos_t pos = position;
1246 framecnt_t const shift = other->_get_extent().second;
1247 layer_t top_layer = regions.size();
1249 while (itimes--) {
1250 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1251 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1253 /* put these new regions on top of all existing ones, but preserve
1254 the ordering they had in the original playlist.
1257 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1258 cerr << "DEBUG: add new region at " << pos << endl;
1259 add_region_internal (copy_of_region, (*i)->position() + pos);
1261 pos += shift;
1265 /* XXX shall we handle fractional cases at some point? */
1267 if (old_length != _get_extent().second) {
1268 notify_length_changed ();
1274 return 0;
1278 void
1279 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1281 times = fabs (times);
1283 RegionLock rl (this);
1284 int itimes = (int) floor (times);
1285 framepos_t pos = position + 1;
1287 while (itimes--) {
1288 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1289 add_region_internal (copy, pos);
1290 pos += region->length();
1293 if (floor (times) != times) {
1294 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1295 string name;
1296 RegionFactory::region_name (name, region->name(), false);
1299 PropertyList plist;
1301 plist.add (Properties::start, region->start());
1302 plist.add (Properties::length, length);
1303 plist.add (Properties::name, name);
1305 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1306 add_region_internal (sub, pos);
1311 void
1312 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1314 RegionLock rlock (this);
1315 RegionList copy (regions.rlist());
1316 RegionList fixup;
1318 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1320 if ((*r)->last_frame() < at) {
1321 /* too early */
1322 continue;
1325 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1326 /* intersected region */
1327 if (!move_intersected) {
1328 continue;
1332 /* do not move regions glued to music time - that
1333 has to be done separately.
1336 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1337 fixup.push_back (*r);
1338 continue;
1341 (*r)->set_position ((*r)->position() + distance, this);
1344 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1345 (*r)->recompute_position_from_lock_style ();
1349 void
1350 Playlist::split (framepos_t at)
1352 RegionLock rlock (this);
1353 RegionList copy (regions.rlist());
1355 /* use a copy since this operation can modify the region list
1358 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1359 _split_region (*r, at);
1363 void
1364 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1366 RegionLock rl (this);
1367 _split_region (region, playlist_position);
1370 void
1371 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1373 if (!region->covers (playlist_position)) {
1374 return;
1377 if (region->position() == playlist_position ||
1378 region->last_frame() == playlist_position) {
1379 return;
1382 boost::shared_ptr<Region> left;
1383 boost::shared_ptr<Region> right;
1384 frameoffset_t before;
1385 frameoffset_t after;
1386 string before_name;
1387 string after_name;
1389 /* split doesn't change anything about length, so don't try to splice */
1391 bool old_sp = _splicing;
1392 _splicing = true;
1394 before = playlist_position - region->position();
1395 after = region->length() - before;
1397 RegionFactory::region_name (before_name, region->name(), false);
1400 PropertyList plist;
1402 plist.add (Properties::position, region->position ());
1403 plist.add (Properties::length, before);
1404 plist.add (Properties::name, before_name);
1405 plist.add (Properties::left_of_split, true);
1407 /* note: we must use the version of ::create with an offset here,
1408 since it supplies that offset to the Region constructor, which
1409 is necessary to get audio region gain envelopes right.
1411 left = RegionFactory::create (region, 0, plist);
1414 RegionFactory::region_name (after_name, region->name(), false);
1417 PropertyList plist;
1419 plist.add (Properties::position, region->position() + before);
1420 plist.add (Properties::length, after);
1421 plist.add (Properties::name, after_name);
1422 plist.add (Properties::right_of_split, true);
1424 /* same note as above */
1425 right = RegionFactory::create (region, before, plist);
1428 add_region_internal (left, region->position());
1429 add_region_internal (right, region->position() + before);
1431 uint64_t orig_layer_op = region->last_layer_op();
1432 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1433 if ((*i)->last_layer_op() > orig_layer_op) {
1434 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1438 left->set_last_layer_op ( orig_layer_op );
1439 right->set_last_layer_op ( orig_layer_op + 1);
1441 layer_op_counter++;
1443 finalize_split_region (region, left, right);
1445 remove_region_internal (region);
1447 _splicing = old_sp;
1450 void
1451 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1453 if (_splicing || in_set_state) {
1454 /* don't respond to splicing moves or state setting */
1455 return;
1458 if (_edit_mode == Splice) {
1459 splice_locked (at, distance, exclude);
1463 void
1464 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1466 if (_splicing || in_set_state) {
1467 /* don't respond to splicing moves or state setting */
1468 return;
1471 if (_edit_mode == Splice) {
1472 splice_unlocked (at, distance, exclude);
1476 void
1477 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1480 RegionLock rl (this);
1481 core_splice (at, distance, exclude);
1485 void
1486 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1488 core_splice (at, distance, exclude);
1491 void
1492 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1494 _splicing = true;
1496 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1498 if (exclude && (*i) == exclude) {
1499 continue;
1502 if ((*i)->position() >= at) {
1503 framepos_t new_pos = (*i)->position() + distance;
1504 if (new_pos < 0) {
1505 new_pos = 0;
1506 } else if (new_pos >= max_framepos - (*i)->length()) {
1507 new_pos = max_framepos - (*i)->length();
1510 (*i)->set_position (new_pos, this);
1514 _splicing = false;
1516 notify_length_changed ();
1519 void
1520 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1522 if (in_set_state || _splicing || _nudging || _shuffling) {
1523 return;
1526 if (what_changed.contains (Properties::position)) {
1528 /* remove it from the list then add it back in
1529 the right place again.
1532 RegionSortByPosition cmp;
1534 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1536 if (i == regions.end()) {
1537 /* the region bounds are being modified but its not currently
1538 in the region list. we will use its bounds correctly when/if
1539 it is added
1541 return;
1544 regions.erase (i);
1545 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1548 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1550 frameoffset_t delta = 0;
1552 if (what_changed.contains (Properties::position)) {
1553 delta = region->position() - region->last_position();
1556 if (what_changed.contains (Properties::length)) {
1557 delta += region->length() - region->last_length();
1560 if (delta) {
1561 possibly_splice (region->last_position() + region->last_length(), delta, region);
1564 if (holding_state ()) {
1565 pending_bounds.push_back (region);
1566 } else {
1567 if (_session.config.get_layer_model() == MoveAddHigher) {
1568 /* it moved or changed length, so change the timestamp */
1569 timestamp_layer_op (region);
1572 notify_length_changed ();
1573 relayer ();
1574 check_dependents (region, false);
1579 void
1580 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1582 boost::shared_ptr<Region> region (weak_region.lock());
1584 if (!region) {
1585 return;
1588 /* this makes a virtual call to the right kind of playlist ... */
1590 region_changed (what_changed, region);
1593 bool
1594 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1596 PropertyChange our_interests;
1597 PropertyChange bounds;
1598 PropertyChange pos_and_length;
1599 bool save = false;
1601 if (in_set_state || in_flush) {
1602 return false;
1605 our_interests.add (Properties::muted);
1606 our_interests.add (Properties::layer);
1607 our_interests.add (Properties::opaque);
1609 bounds.add (Properties::start);
1610 bounds.add (Properties::position);
1611 bounds.add (Properties::length);
1613 pos_and_length.add (Properties::position);
1614 pos_and_length.add (Properties::length);
1616 if (what_changed.contains (bounds)) {
1617 region_bounds_changed (what_changed, region);
1618 save = !(_splicing || _nudging);
1621 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1622 check_dependents (region, false);
1625 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1626 notify_region_moved (region);
1630 /* don't notify about layer changes, since we are the only object that can initiate
1631 them, and we notify in ::relayer()
1634 if (what_changed.contains (our_interests)) {
1635 save = true;
1638 return save;
1641 void
1642 Playlist::drop_regions ()
1644 RegionLock rl (this);
1645 regions.clear ();
1646 all_regions.clear ();
1649 void
1650 Playlist::clear (bool with_signals)
1653 RegionLock rl (this);
1655 region_state_changed_connections.drop_connections ();
1657 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1658 pending_removes.insert (*i);
1661 regions.clear ();
1663 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1664 remove_dependents (*s);
1668 if (with_signals) {
1670 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1671 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1674 pending_removes.clear ();
1675 pending_length = false;
1676 LengthChanged ();
1677 pending_contents_change = false;
1678 ContentsChanged ();
1683 /***********************************************************************
1684 FINDING THINGS
1685 **********************************************************************/
1687 Playlist::RegionList *
1688 Playlist::regions_at (framepos_t frame)
1691 RegionLock rlock (this);
1692 return find_regions_at (frame);
1695 uint32_t
1696 Playlist::count_regions_at (framepos_t frame)
1698 RegionLock rlock (this);
1699 uint32_t cnt = 0;
1701 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1702 if ((*i)->covers (frame)) {
1703 cnt++;
1707 return cnt;
1710 boost::shared_ptr<Region>
1711 Playlist::top_region_at (framepos_t frame)
1714 RegionLock rlock (this);
1715 RegionList *rlist = find_regions_at (frame);
1716 boost::shared_ptr<Region> region;
1718 if (rlist->size()) {
1719 RegionSortByLayer cmp;
1720 rlist->sort (cmp);
1721 region = rlist->back();
1724 delete rlist;
1725 return region;
1728 boost::shared_ptr<Region>
1729 Playlist::top_unmuted_region_at (framepos_t frame)
1732 RegionLock rlock (this);
1733 RegionList *rlist = find_regions_at (frame);
1735 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1737 RegionList::iterator tmp = i;
1738 ++tmp;
1740 if ((*i)->muted()) {
1741 rlist->erase (i);
1744 i = tmp;
1747 boost::shared_ptr<Region> region;
1749 if (rlist->size()) {
1750 RegionSortByLayer cmp;
1751 rlist->sort (cmp);
1752 region = rlist->back();
1755 delete rlist;
1756 return region;
1759 Playlist::RegionList*
1760 Playlist::regions_to_read (framepos_t start, framepos_t end)
1762 /* Caller must hold lock */
1764 RegionList covering;
1765 set<framepos_t> to_check;
1766 set<boost::shared_ptr<Region> > unique;
1768 to_check.insert (start);
1769 to_check.insert (end);
1771 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1773 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1775 /* find all/any regions that span start+end */
1777 switch ((*i)->coverage (start, end)) {
1778 case OverlapNone:
1779 break;
1781 case OverlapInternal:
1782 covering.push_back (*i);
1783 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1784 break;
1786 case OverlapStart:
1787 to_check.insert ((*i)->position());
1788 if ((*i)->position() != 0) {
1789 to_check.insert ((*i)->position()-1);
1791 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1792 covering.push_back (*i);
1793 break;
1795 case OverlapEnd:
1796 to_check.insert ((*i)->last_frame());
1797 to_check.insert ((*i)->last_frame()+1);
1798 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1799 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1800 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1801 covering.push_back (*i);
1802 break;
1804 case OverlapExternal:
1805 covering.push_back (*i);
1806 to_check.insert ((*i)->position());
1807 if ((*i)->position() != 0) {
1808 to_check.insert ((*i)->position()-1);
1810 to_check.insert ((*i)->last_frame());
1811 to_check.insert ((*i)->last_frame()+1);
1812 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1813 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1814 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1815 break;
1818 /* don't go too far */
1820 if ((*i)->position() > end) {
1821 break;
1825 RegionList* rlist = new RegionList;
1827 /* find all the regions that cover each position .... */
1829 if (covering.size() == 1) {
1831 rlist->push_back (covering.front());
1832 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1834 } else {
1836 RegionList here;
1837 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1839 here.clear ();
1841 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1843 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1845 if ((*x)->covers (*t)) {
1846 here.push_back (*x);
1847 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1848 (*x)->name(),
1849 (*t)));
1850 } else {
1851 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1852 (*x)->name(),
1853 (*t)));
1858 RegionSortByLayer cmp;
1859 here.sort (cmp);
1861 /* ... and get the top/transparent regions at "here" */
1863 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1865 unique.insert (*c);
1867 if ((*c)->opaque()) {
1869 /* the other regions at this position are hidden by this one */
1870 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1871 (*c)->name()));
1872 break;
1877 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1878 rlist->push_back (*s);
1881 if (rlist->size() > 1) {
1882 /* now sort by time order */
1884 RegionSortByPosition cmp;
1885 rlist->sort (cmp);
1889 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1891 return rlist;
1894 Playlist::RegionList *
1895 Playlist::find_regions_at (framepos_t frame)
1897 /* Caller must hold lock */
1899 RegionList *rlist = new RegionList;
1901 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1902 if ((*i)->covers (frame)) {
1903 rlist->push_back (*i);
1907 return rlist;
1910 Playlist::RegionList *
1911 Playlist::regions_touched (framepos_t start, framepos_t end)
1913 RegionLock rlock (this);
1914 RegionList *rlist = new RegionList;
1916 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1917 if ((*i)->coverage (start, end) != OverlapNone) {
1918 rlist->push_back (*i);
1922 return rlist;
1925 framepos_t
1926 Playlist::find_next_transient (framepos_t from, int dir)
1928 RegionLock rlock (this);
1929 AnalysisFeatureList points;
1930 AnalysisFeatureList these_points;
1932 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1933 if (dir > 0) {
1934 if ((*i)->last_frame() < from) {
1935 continue;
1937 } else {
1938 if ((*i)->first_frame() > from) {
1939 continue;
1943 (*i)->get_transients (these_points);
1945 /* add first frame, just, err, because */
1947 these_points.push_back ((*i)->first_frame());
1949 points.insert (points.end(), these_points.begin(), these_points.end());
1950 these_points.clear ();
1953 if (points.empty()) {
1954 return -1;
1957 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1958 bool reached = false;
1960 if (dir > 0) {
1961 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1962 if ((*x) >= from) {
1963 reached = true;
1966 if (reached && (*x) > from) {
1967 return *x;
1970 } else {
1971 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1972 if ((*x) <= from) {
1973 reached = true;
1976 if (reached && (*x) < from) {
1977 return *x;
1982 return -1;
1985 boost::shared_ptr<Region>
1986 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1988 RegionLock rlock (this);
1989 boost::shared_ptr<Region> ret;
1990 framepos_t closest = max_framepos;
1992 bool end_iter = false;
1994 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1996 if(end_iter) break;
1998 frameoffset_t distance;
1999 boost::shared_ptr<Region> r = (*i);
2000 framepos_t pos = 0;
2002 switch (point) {
2003 case Start:
2004 pos = r->first_frame ();
2005 break;
2006 case End:
2007 pos = r->last_frame ();
2008 break;
2009 case SyncPoint:
2010 pos = r->sync_position ();
2011 break;
2014 switch (dir) {
2015 case 1: /* forwards */
2017 if (pos > frame) {
2018 if ((distance = pos - frame) < closest) {
2019 closest = distance;
2020 ret = r;
2021 end_iter = true;
2025 break;
2027 default: /* backwards */
2029 if (pos < frame) {
2030 if ((distance = frame - pos) < closest) {
2031 closest = distance;
2032 ret = r;
2035 else {
2036 end_iter = true;
2039 break;
2043 return ret;
2046 framepos_t
2047 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2049 RegionLock rlock (this);
2051 framepos_t closest = max_framepos;
2052 framepos_t ret = -1;
2054 if (dir > 0) {
2056 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2058 boost::shared_ptr<Region> r = (*i);
2059 frameoffset_t distance;
2061 if (r->first_frame() > frame) {
2063 distance = r->first_frame() - frame;
2065 if (distance < closest) {
2066 ret = r->first_frame();
2067 closest = distance;
2071 if (r->last_frame () > frame) {
2073 distance = r->last_frame () - frame;
2075 if (distance < closest) {
2076 ret = r->last_frame ();
2077 closest = distance;
2082 } else {
2084 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2086 boost::shared_ptr<Region> r = (*i);
2087 frameoffset_t distance;
2089 if (r->last_frame() < frame) {
2091 distance = frame - r->last_frame();
2093 if (distance < closest) {
2094 ret = r->last_frame();
2095 closest = distance;
2099 if (r->first_frame() < frame) {
2101 distance = frame - r->first_frame();
2103 if (distance < closest) {
2104 ret = r->first_frame();
2105 closest = distance;
2111 return ret;
2115 /***********************************************************************/
2120 void
2121 Playlist::mark_session_dirty ()
2123 if (!in_set_state && !holding_state ()) {
2124 _session.set_dirty();
2128 void
2129 Playlist::rdiff (vector<Command*>& cmds) const
2131 RegionLock rlock (const_cast<Playlist *> (this));
2132 Stateful::rdiff (cmds);
2135 void
2136 Playlist::clear_owned_changes ()
2138 RegionLock rlock (this);
2139 Stateful::clear_owned_changes ();
2142 void
2143 Playlist::update (const RegionListProperty::ChangeRecord& change)
2145 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2146 name(), change.added.size(), change.removed.size()));
2148 freeze ();
2149 /* add the added regions */
2150 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2151 add_region ((*i), (*i)->position());
2153 /* remove the removed regions */
2154 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2155 remove_region (*i);
2158 thaw ();
2162 Playlist::set_state (const XMLNode& node, int version)
2164 XMLNode *child;
2165 XMLNodeList nlist;
2166 XMLNodeConstIterator niter;
2167 XMLPropertyList plist;
2168 XMLPropertyConstIterator piter;
2169 XMLProperty *prop;
2170 boost::shared_ptr<Region> region;
2171 string region_name;
2173 in_set_state++;
2175 if (node.name() != "Playlist") {
2176 in_set_state--;
2177 return -1;
2180 freeze ();
2182 plist = node.properties();
2184 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2186 prop = *piter;
2188 if (prop->name() == X_("name")) {
2189 _name = prop->value();
2190 _set_sort_id ();
2191 } else if (prop->name() == X_("id")) {
2192 _id = prop->value();
2193 } else if (prop->name() == X_("orig_diskstream_id")) {
2194 _orig_diskstream_id = prop->value ();
2195 } else if (prop->name() == X_("frozen")) {
2196 _frozen = string_is_affirmative (prop->value());
2200 clear (true);
2202 nlist = node.children();
2204 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2206 child = *niter;
2208 if (child->name() == "Region") {
2210 if ((prop = child->property ("id")) == 0) {
2211 error << _("region state node has no ID, ignored") << endmsg;
2212 continue;
2215 ID id = prop->value ();
2217 if ((region = region_by_id (id))) {
2219 region->suspend_property_changes ();
2221 if (region->set_state (*child, version)) {
2222 region->resume_property_changes ();
2223 continue;
2226 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2227 region->suspend_property_changes ();
2228 } else {
2229 error << _("Playlist: cannot create region from XML") << endmsg;
2230 continue;
2234 add_region (region, region->position(), 1.0);
2236 // So that layer_op ordering doesn't get screwed up
2237 region->set_last_layer_op( region->layer());
2238 region->resume_property_changes ();
2242 /* update dependents, which was not done during add_region_internal
2243 due to in_set_state being true
2246 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2247 check_dependents (*r, false);
2250 thaw ();
2251 notify_contents_changed ();
2253 in_set_state--;
2254 first_set_state = false;
2255 return 0;
2258 XMLNode&
2259 Playlist::get_state()
2261 return state (true);
2264 XMLNode&
2265 Playlist::get_template()
2267 return state (false);
2270 /** @param full_state true to include regions in the returned state, otherwise false.
2272 XMLNode&
2273 Playlist::state (bool full_state)
2275 XMLNode *node = new XMLNode (X_("Playlist"));
2276 char buf[64] = "";
2278 node->add_property (X_("id"), id().to_s());
2279 node->add_property (X_("name"), _name);
2280 node->add_property (X_("type"), _type.to_string());
2282 _orig_diskstream_id.print (buf, sizeof (buf));
2283 node->add_property (X_("orig_diskstream_id"), buf);
2284 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2286 if (full_state) {
2287 RegionLock rlock (this, false);
2289 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2290 node->add_child_nocopy ((*i)->get_state());
2294 if (_extra_xml) {
2295 node->add_child_copy (*_extra_xml);
2298 return *node;
2301 bool
2302 Playlist::empty() const
2304 RegionLock rlock (const_cast<Playlist *>(this), false);
2305 return regions.empty();
2308 uint32_t
2309 Playlist::n_regions() const
2311 RegionLock rlock (const_cast<Playlist *>(this), false);
2312 return regions.size();
2315 pair<framepos_t, framepos_t>
2316 Playlist::get_extent () const
2318 RegionLock rlock (const_cast<Playlist *>(this), false);
2319 return _get_extent ();
2322 pair<framepos_t, framepos_t>
2323 Playlist::_get_extent () const
2325 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2327 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2328 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2329 if (e.first < ext.first) {
2330 ext.first = e.first;
2332 if (e.second > ext.second) {
2333 ext.second = e.second;
2337 return ext;
2340 string
2341 Playlist::bump_name (string name, Session &session)
2343 string newname = name;
2345 do {
2346 newname = bump_name_once (newname, '.');
2347 } while (session.playlists->by_name (newname)!=NULL);
2349 return newname;
2353 layer_t
2354 Playlist::top_layer() const
2356 RegionLock rlock (const_cast<Playlist *> (this));
2357 layer_t top = 0;
2359 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2360 top = max (top, (*i)->layer());
2362 return top;
2365 void
2366 Playlist::set_edit_mode (EditMode mode)
2368 _edit_mode = mode;
2371 /********************
2372 * Region Layering
2373 ********************/
2375 void
2376 Playlist::relayer ()
2378 /* never compute layers when changing state for undo/redo or setting from XML */
2380 if (in_update || in_set_state) {
2381 return;
2384 bool changed = false;
2386 /* Build up a new list of regions on each layer, stored in a set of lists
2387 each of which represent some period of time on some layer. The idea
2388 is to avoid having to search the entire region list to establish whether
2389 each region overlaps another */
2391 /* how many pieces to divide this playlist's time up into */
2392 int const divisions = 512;
2394 /* find the start and end positions of the regions on this playlist */
2395 framepos_t start = INT64_MAX;
2396 framepos_t end = 0;
2397 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2398 start = min (start, (*i)->position());
2399 end = max (end, (*i)->position() + (*i)->length());
2402 /* hence the size of each time division */
2403 double const division_size = (end - start) / double (divisions);
2405 vector<vector<RegionList> > layers;
2406 layers.push_back (vector<RegionList> (divisions));
2408 /* we want to go through regions from desired lowest to desired highest layer,
2409 which depends on the layer model
2412 RegionList copy = regions.rlist();
2414 /* sort according to the model and the layering mode that we're in */
2416 if (_explicit_relayering) {
2418 copy.sort (RegionSortByLayerWithPending ());
2420 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2422 copy.sort (RegionSortByLastLayerOp ());
2427 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2429 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2430 (*i)->set_pending_explicit_relayer (false);
2432 /* find the time divisions that this region covers; if there are no regions on the list,
2433 division_size will equal 0 and in this case we'll just say that
2434 start_division = end_division = 0.
2436 int start_division = 0;
2437 int end_division = 0;
2439 if (division_size > 0) {
2440 start_division = floor ( ((*i)->position() - start) / division_size);
2441 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2442 if (end_division == divisions) {
2443 end_division--;
2447 assert (divisions == 0 || end_division < divisions);
2449 /* find the lowest layer that this region can go on */
2450 size_t j = layers.size();
2451 while (j > 0) {
2452 /* try layer j - 1; it can go on if it overlaps no other region
2453 that is already on that layer
2456 bool overlap = false;
2457 for (int k = start_division; k <= end_division; ++k) {
2458 RegionList::iterator l = layers[j-1][k].begin ();
2459 while (l != layers[j-1][k].end()) {
2460 if ((*l)->overlap_equivalent (*i)) {
2461 overlap = true;
2462 break;
2464 l++;
2467 if (overlap) {
2468 break;
2472 if (overlap) {
2473 /* overlap, so we must use layer j */
2474 break;
2477 --j;
2480 if (j == layers.size()) {
2481 /* we need a new layer for this region */
2482 layers.push_back (vector<RegionList> (divisions));
2485 /* put a reference to this region in each of the divisions that it exists in */
2486 for (int k = start_division; k <= end_division; ++k) {
2487 layers[j][k].push_back (*i);
2490 if ((*i)->layer() != j) {
2491 changed = true;
2494 (*i)->set_layer (j);
2497 if (changed) {
2498 notify_layering_changed ();
2502 /* XXX these layer functions are all deprecated */
2504 void
2505 Playlist::raise_region (boost::shared_ptr<Region> region)
2507 uint32_t top = regions.size() - 1;
2508 layer_t target = region->layer() + 1U;
2510 if (target >= top) {
2511 /* its already at the effective top */
2512 return;
2515 move_region_to_layer (target, region, 1);
2518 void
2519 Playlist::lower_region (boost::shared_ptr<Region> region)
2521 if (region->layer() == 0) {
2522 /* its already at the bottom */
2523 return;
2526 layer_t target = region->layer() - 1U;
2528 move_region_to_layer (target, region, -1);
2531 void
2532 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2534 /* does nothing useful if layering mode is later=higher */
2535 switch (_session.config.get_layer_model()) {
2536 case LaterHigher:
2537 return;
2538 default:
2539 break;
2542 layer_t top = regions.size() - 1;
2544 if (region->layer() >= top) {
2545 /* already on the top */
2546 return;
2549 move_region_to_layer (top, region, 1);
2550 /* mark the region's last_layer_op as now, so that it remains on top when
2551 doing future relayers (until something else takes over)
2553 timestamp_layer_op (region);
2556 void
2557 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2559 /* does nothing useful if layering mode is later=higher */
2560 switch (_session.config.get_layer_model()) {
2561 case LaterHigher:
2562 return;
2563 default:
2564 break;
2567 if (region->layer() == 0) {
2568 /* already on the bottom */
2569 return;
2572 move_region_to_layer (0, region, -1);
2573 /* force region's last layer op to zero so that it stays at the bottom
2574 when doing future relayers
2576 region->set_last_layer_op (0);
2580 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2582 RegionList::iterator i;
2583 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2584 list<LayerInfo> layerinfo;
2587 RegionLock rlock (const_cast<Playlist *> (this));
2589 for (i = regions.begin(); i != regions.end(); ++i) {
2591 if (region == *i) {
2592 continue;
2595 layer_t dest;
2597 if (dir > 0) {
2599 /* region is moving up, move all regions on intermediate layers
2600 down 1
2603 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2604 dest = (*i)->layer() - 1;
2605 } else {
2606 /* not affected */
2607 continue;
2609 } else {
2611 /* region is moving down, move all regions on intermediate layers
2612 up 1
2615 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2616 dest = (*i)->layer() + 1;
2617 } else {
2618 /* not affected */
2619 continue;
2623 LayerInfo newpair;
2625 newpair.first = *i;
2626 newpair.second = dest;
2628 layerinfo.push_back (newpair);
2632 freeze ();
2634 /* now reset the layers without holding the region lock */
2636 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2637 x->first->set_layer (x->second);
2640 region->set_layer (target_layer);
2642 /* now check all dependents, since we changed the layering */
2644 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2645 check_dependents (x->first, false);
2648 check_dependents (region, false);
2649 notify_layering_changed ();
2651 thaw ();
2653 return 0;
2656 void
2657 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2659 RegionList::iterator i;
2660 bool moved = false;
2662 _nudging = true;
2665 RegionLock rlock (const_cast<Playlist *> (this));
2667 for (i = regions.begin(); i != regions.end(); ++i) {
2669 if ((*i)->position() >= start) {
2671 framepos_t new_pos;
2673 if (forwards) {
2675 if ((*i)->last_frame() > max_framepos - distance) {
2676 new_pos = max_framepos - (*i)->length();
2677 } else {
2678 new_pos = (*i)->position() + distance;
2681 } else {
2683 if ((*i)->position() > distance) {
2684 new_pos = (*i)->position() - distance;
2685 } else {
2686 new_pos = 0;
2690 (*i)->set_position (new_pos, this);
2691 moved = true;
2696 if (moved) {
2697 _nudging = false;
2698 notify_length_changed ();
2703 boost::shared_ptr<Region>
2704 Playlist::find_region (const ID& id) const
2706 RegionLock rlock (const_cast<Playlist*> (this));
2708 /* searches all regions currently in use by the playlist */
2710 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2711 if ((*i)->id() == id) {
2712 return *i;
2716 return boost::shared_ptr<Region> ();
2719 uint32_t
2720 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2722 RegionLock rlock (const_cast<Playlist*> (this));
2723 uint32_t cnt = 0;
2725 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2726 if ((*i) == r) {
2727 cnt++;
2731 return cnt;
2734 boost::shared_ptr<Region>
2735 Playlist::region_by_id (const ID& id) const
2737 /* searches all regions ever added to this playlist */
2739 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2740 if ((*i)->id() == id) {
2741 return *i;
2744 return boost::shared_ptr<Region> ();
2747 void
2748 Playlist::dump () const
2750 boost::shared_ptr<Region> r;
2752 cerr << "Playlist \"" << _name << "\" " << endl
2753 << regions.size() << " regions "
2754 << endl;
2756 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2757 r = *i;
2758 cerr << " " << r->name() << " ["
2759 << r->start() << "+" << r->length()
2760 << "] at "
2761 << r->position()
2762 << " on layer "
2763 << r->layer ()
2764 << endl;
2768 void
2769 Playlist::set_frozen (bool yn)
2771 _frozen = yn;
2774 void
2775 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2777 region->set_last_layer_op (++layer_op_counter);
2781 void
2782 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2784 bool moved = false;
2786 if (region->locked()) {
2787 return;
2790 _shuffling = true;
2793 RegionLock rlock (const_cast<Playlist*> (this));
2796 if (dir > 0) {
2798 RegionList::iterator next;
2800 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2801 if ((*i) == region) {
2802 next = i;
2803 ++next;
2805 if (next != regions.end()) {
2807 if ((*next)->locked()) {
2808 break;
2811 framepos_t new_pos;
2813 if ((*next)->position() != region->last_frame() + 1) {
2814 /* they didn't used to touch, so after shuffle,
2815 just have them swap positions.
2817 new_pos = (*next)->position();
2818 } else {
2819 /* they used to touch, so after shuffle,
2820 make sure they still do. put the earlier
2821 region where the later one will end after
2822 it is moved.
2824 new_pos = region->position() + (*next)->length();
2827 (*next)->set_position (region->position(), this);
2828 region->set_position (new_pos, this);
2830 /* avoid a full sort */
2832 regions.erase (i); // removes the region from the list */
2833 next++;
2834 regions.insert (next, region); // adds it back after next
2836 moved = true;
2838 break;
2841 } else {
2843 RegionList::iterator prev = regions.end();
2845 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2846 if ((*i) == region) {
2848 if (prev != regions.end()) {
2850 if ((*prev)->locked()) {
2851 break;
2854 framepos_t new_pos;
2855 if (region->position() != (*prev)->last_frame() + 1) {
2856 /* they didn't used to touch, so after shuffle,
2857 just have them swap positions.
2859 new_pos = region->position();
2860 } else {
2861 /* they used to touch, so after shuffle,
2862 make sure they still do. put the earlier
2863 one where the later one will end after
2865 new_pos = (*prev)->position() + region->length();
2868 region->set_position ((*prev)->position(), this);
2869 (*prev)->set_position (new_pos, this);
2871 /* avoid a full sort */
2873 regions.erase (i); // remove region
2874 regions.insert (prev, region); // insert region before prev
2876 moved = true;
2879 break;
2885 _shuffling = false;
2887 if (moved) {
2889 relayer ();
2890 check_dependents (region, false);
2892 notify_contents_changed();
2897 bool
2898 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2900 RegionLock rlock (const_cast<Playlist*> (this));
2902 if (regions.size() > 1) {
2903 return true;
2906 return false;
2909 void
2910 Playlist::update_after_tempo_map_change ()
2912 RegionLock rlock (const_cast<Playlist*> (this));
2913 RegionList copy (regions.rlist());
2915 freeze ();
2917 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2918 (*i)->update_position_after_tempo_map_change ();
2921 thaw ();
2924 void
2925 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2927 RegionLock rl (this, false);
2928 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2929 s (*i);
2933 void
2934 Playlist::set_explicit_relayering (bool e)
2936 if (e == false && _explicit_relayering == true) {
2938 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2939 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2940 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2941 at this point would keep regions on the same layers.
2943 From then on in, it's just you and your towel.
2946 RegionLock rl (this);
2947 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2948 (*i)->set_last_layer_op ((*i)->layer ());
2952 _explicit_relayering = e;
2956 bool
2957 Playlist::has_region_at (framepos_t const p) const
2959 RegionLock (const_cast<Playlist *> (this));
2961 RegionList::const_iterator i = regions.begin ();
2962 while (i != regions.end() && !(*i)->covers (p)) {
2963 ++i;
2966 return (i != regions.end());
2969 /** Remove any region that uses a given source */
2970 void
2971 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2973 RegionLock rl (this);
2975 RegionList::iterator i = regions.begin();
2976 while (i != regions.end()) {
2977 RegionList::iterator j = i;
2978 ++j;
2980 if ((*i)->uses_source (s)) {
2981 remove_region_internal (*i);
2984 i = j;
2988 /** Look from a session frame time and find the start time of the next region
2989 * which is on the top layer of this playlist.
2990 * @param t Time to look from.
2991 * @return Position of next top-layered region, or max_framepos if there isn't one.
2993 framepos_t
2994 Playlist::find_next_top_layer_position (framepos_t t) const
2996 RegionLock rlock (const_cast<Playlist *> (this));
2998 layer_t const top = top_layer ();
3000 RegionList copy = regions.rlist ();
3001 copy.sort (RegionSortByPosition ());
3003 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3004 if ((*i)->position() >= t && (*i)->layer() == top) {
3005 return (*i)->position();
3009 return max_framepos;