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 "pbd/failed_constructor.h"
29 #include "pbd/stateful_diff_command.h"
30 #include "pbd/xml++.h"
32 #include "ardour/debug.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/region.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/playlist_factory.h"
38 #include "ardour/transient_detector.h"
39 #include "ardour/session_playlists.h"
44 using namespace ARDOUR;
48 namespace Properties {
49 PBD::PropertyDescriptor<bool> regions;
53 struct ShowMeTheList {
54 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
56 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
58 boost::shared_ptr<Playlist> playlist;
62 struct RegionSortByLayer {
63 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
64 return a->layer() < b->layer();
68 struct RegionSortByLayerWithPending {
69 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
71 double p = a->layer ();
72 if (a->pending_explicit_relayer()) {
76 double q = b->layer ();
77 if (b->pending_explicit_relayer()) {
85 struct RegionSortByPosition {
86 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
87 return a->position() < b->position();
91 struct RegionSortByLastLayerOp {
92 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
93 return a->last_layer_op() < b->last_layer_op();
98 Playlist::make_property_quarks ()
100 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
101 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
104 RegionListProperty::RegionListProperty (Playlist& pl)
105 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
110 boost::shared_ptr<Region>
111 RegionListProperty::lookup_id (const ID& id)
113 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
116 ret = RegionFactory::region_by_id (id);
123 RegionListProperty::copy_for_history () const
125 RegionListProperty* copy = new RegionListProperty (_playlist);
126 /* this is all we need */
127 copy->_change = _change;
132 RegionListProperty::diff (PropertyList& before, PropertyList& after) const
135 RegionListProperty* a = copy_for_history ();
136 RegionListProperty* b = copy_for_history ();
138 b->invert_changes ();
145 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
146 : SessionObject(sess, nom)
151 first_set_state = false;
156 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
157 : SessionObject(sess, "unnamed playlist")
162 const XMLProperty* prop = node.property("type");
163 assert(!prop || DataType(prop->value()) == _type);
166 _name = "unnamed"; /* reset by set_state */
168 /* set state called by derived class */
171 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
172 : SessionObject(other->_session, namestr)
174 , _type(other->_type)
175 , _orig_diskstream_id(other->_orig_diskstream_id)
180 other->copy_regions (tmp);
184 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
185 add_region_internal( (*x), (*x)->position());
190 _splicing = other->_splicing;
191 _nudging = other->_nudging;
192 _edit_mode = other->_edit_mode;
195 first_set_state = false;
197 in_partition = false;
199 _read_data_count = 0;
200 _frozen = other->_frozen;
202 layer_op_counter = other->layer_op_counter;
203 freeze_length = other->freeze_length;
206 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
207 : SessionObject(other->_session, str)
209 , _type(other->_type)
210 , _orig_diskstream_id(other->_orig_diskstream_id)
212 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
214 framepos_t end = start + cnt - 1;
220 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
222 boost::shared_ptr<Region> region;
223 boost::shared_ptr<Region> new_region;
224 frameoffset_t offset = 0;
225 framepos_t position = 0;
232 overlap = region->coverage (start, end);
238 case OverlapInternal:
239 offset = start - region->position();
246 position = region->position() - start;
247 len = end - region->position();
251 offset = start - region->position();
253 len = region->length() - offset;
256 case OverlapExternal:
258 position = region->position() - start;
259 len = region->length();
263 _session.region_name (new_name, region->name(), false);
267 plist.add (Properties::start, region->start() + offset);
268 plist.add (Properties::length, len);
269 plist.add (Properties::name, new_name);
270 plist.add (Properties::layer, region->layer());
272 new_region = RegionFactory::RegionFactory::create (region, plist);
274 add_region_internal (new_region, position);
278 first_set_state = false;
280 /* this constructor does NOT notify others (session) */
287 InUse (true); /* EMIT SIGNAL */
298 InUse (false); /* EMIT SIGNAL */
303 Playlist::copy_regions (RegionList& newlist) const
305 RegionLock rlock (const_cast<Playlist *> (this));
307 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
308 newlist.push_back (RegionFactory::RegionFactory::create (*i));
313 Playlist::init (bool hide)
315 add_property (regions);
316 _xml_node_name = X_("Playlist");
318 g_atomic_int_set (&block_notifications, 0);
319 g_atomic_int_set (&ignore_state_changes, 0);
320 pending_contents_change = false;
321 pending_length = false;
322 pending_layering = false;
323 first_set_state = true;
331 _edit_mode = Config->get_edit_mode();
333 in_partition = false;
335 _read_data_count = 0;
337 layer_op_counter = 0;
339 _explicit_relayering = false;
341 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
342 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
344 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
347 Playlist::~Playlist ()
349 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
352 RegionLock rl (this);
354 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
355 (*i)->set_playlist (boost::shared_ptr<Playlist>());
359 /* GoingAway must be emitted by derived classes */
363 Playlist::set_name (const string& str)
365 /* in a typical situation, a playlist is being used
366 by one diskstream and also is referenced by the
367 Session. if there are more references than that,
368 then don't change the name.
374 return SessionObject::set_name(str);
378 /***********************************************************************
379 CHANGE NOTIFICATION HANDLING
381 Notifications must be delayed till the region_lock is released. This
382 is necessary because handlers for the signals may need to acquire
383 the lock (e.g. to read from the playlist).
384 ***********************************************************************/
387 Playlist::begin_undo ()
394 Playlist::end_undo ()
403 delay_notifications ();
404 g_atomic_int_inc (&ignore_state_changes);
410 g_atomic_int_dec_and_test (&ignore_state_changes);
411 release_notifications ();
416 Playlist::delay_notifications ()
418 g_atomic_int_inc (&block_notifications);
419 freeze_length = _get_maximum_extent();
423 Playlist::release_notifications ()
425 if (g_atomic_int_dec_and_test (&block_notifications)) {
426 flush_notifications ();
432 Playlist::notify_contents_changed ()
434 if (holding_state ()) {
435 pending_contents_change = true;
437 pending_contents_change = false;
438 ContentsChanged(); /* EMIT SIGNAL */
443 Playlist::notify_layering_changed ()
445 if (holding_state ()) {
446 pending_layering = true;
448 pending_layering = false;
449 LayeringChanged(); /* EMIT SIGNAL */
454 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
456 if (holding_state ()) {
457 pending_removes.insert (r);
458 pending_contents_change = true;
459 pending_length = true;
461 /* this might not be true, but we have to act
462 as though it could be.
464 pending_length = false;
465 LengthChanged (); /* EMIT SIGNAL */
466 pending_contents_change = false;
467 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
468 ContentsChanged (); /* EMIT SIGNAL */
473 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
475 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
477 if (holding_state ()) {
479 pending_range_moves.push_back (move);
483 list< Evoral::RangeMove<framepos_t> > m;
491 Playlist::notify_region_added (boost::shared_ptr<Region> r)
493 /* the length change might not be true, but we have to act
494 as though it could be.
497 if (holding_state()) {
498 pending_adds.insert (r);
499 pending_contents_change = true;
500 pending_length = true;
502 pending_length = false;
503 LengthChanged (); /* EMIT SIGNAL */
504 pending_contents_change = false;
505 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
506 ContentsChanged (); /* EMIT SIGNAL */
511 Playlist::notify_length_changed ()
513 if (holding_state ()) {
514 pending_length = true;
516 pending_length = false;
517 LengthChanged(); /* EMIT SIGNAL */
518 pending_contents_change = false;
519 ContentsChanged (); /* EMIT SIGNAL */
524 Playlist::flush_notifications ()
526 set<boost::shared_ptr<Region> > dependent_checks_needed;
527 set<boost::shared_ptr<Region> >::iterator s;
528 uint32_t regions_changed = false;
529 bool check_length = false;
530 framecnt_t old_length = 0;
538 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
539 regions_changed = true;
540 if (!pending_length) {
541 old_length = _get_maximum_extent ();
546 /* we have no idea what order the regions ended up in pending
547 bounds (it could be based on selection order, for example).
548 so, to preserve layering in the "most recently moved is higher"
549 model, sort them by existing layer, then timestamp them.
552 // RegionSortByLayer cmp;
553 // pending_bounds.sort (cmp);
555 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
556 if (_session.config.get_layer_model() == MoveAddHigher) {
557 timestamp_layer_op (*r);
559 dependent_checks_needed.insert (*r);
562 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
563 remove_dependents (*s);
564 // cerr << _name << " sends RegionRemoved\n";
565 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
568 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
569 // cerr << _name << " sends RegionAdded\n";
570 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
571 dependent_checks_needed.insert (*s);
575 if (old_length != _get_maximum_extent()) {
576 pending_length = true;
577 // cerr << _name << " length has changed\n";
581 if (pending_length || (freeze_length != _get_maximum_extent())) {
582 pending_length = false;
583 // cerr << _name << " sends LengthChanged\n";
584 LengthChanged(); /* EMIT SIGNAL */
587 if (regions_changed || pending_contents_change) {
591 pending_contents_change = false;
592 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
593 ContentsChanged (); /* EMIT SIGNAL */
594 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
597 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
598 check_dependents (*s, false);
601 if (!pending_range_moves.empty ()) {
602 // cerr << _name << " sends RangesMoved\n";
603 RangesMoved (pending_range_moves);
612 Playlist::clear_pending ()
614 pending_adds.clear ();
615 pending_removes.clear ();
616 pending_bounds.clear ();
617 pending_range_moves.clear ();
618 pending_contents_change = false;
619 pending_length = false;
622 /*************************************************************
624 *************************************************************/
627 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
629 RegionLock rlock (this);
630 times = fabs (times);
632 int itimes = (int) floor (times);
634 framepos_t pos = position;
636 if (times == 1 && auto_partition){
637 partition(pos, (pos + region->length()), true);
641 add_region_internal (region, pos);
642 pos += region->length();
647 /* note that itimes can be zero if we being asked to just
648 insert a single fraction of the region.
651 for (int i = 0; i < itimes; ++i) {
652 boost::shared_ptr<Region> copy = RegionFactory::create (region);
653 add_region_internal (copy, pos);
654 pos += region->length();
657 framecnt_t length = 0;
659 if (floor (times) != times) {
660 length = (framecnt_t) floor (region->length() * (times - floor (times)));
662 _session.region_name (name, region->name(), false);
667 plist.add (Properties::start, region->start());
668 plist.add (Properties::length, length);
669 plist.add (Properties::name, name);
670 plist.add (Properties::layer, region->layer());
672 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
673 add_region_internal (sub, pos);
677 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
681 Playlist::set_region_ownership ()
683 RegionLock rl (this);
684 RegionList::iterator i;
685 boost::weak_ptr<Playlist> pl (shared_from_this());
687 for (i = regions.begin(); i != regions.end(); ++i) {
688 (*i)->set_playlist (pl);
693 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
695 if (region->data_type() != _type){
699 RegionSortByPosition cmp;
701 framecnt_t old_length = 0;
703 if (!holding_state()) {
704 old_length = _get_maximum_extent();
707 if (!first_set_state) {
708 boost::shared_ptr<Playlist> foo (shared_from_this());
709 region->set_playlist (boost::weak_ptr<Playlist>(foo));
712 region->set_position (position, this);
714 timestamp_layer_op (region);
716 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
717 all_regions.insert (region);
719 possibly_splice_unlocked (position, region->length(), region);
721 if (!holding_state ()) {
722 /* layers get assigned from XML state, and are not reset during undo/redo */
726 /* we need to notify the existence of new region before checking dependents. Ick. */
728 notify_region_added (region);
730 if (!holding_state ()) {
732 check_dependents (region, false);
734 if (old_length != _get_maximum_extent()) {
735 notify_length_changed ();
739 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
745 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
747 RegionLock rlock (this);
749 bool old_sp = _splicing;
752 remove_region_internal (old);
753 add_region_internal (newr, pos);
757 possibly_splice_unlocked (pos, old->length() - newr->length());
761 Playlist::remove_region (boost::shared_ptr<Region> region)
763 RegionLock rlock (this);
764 remove_region_internal (region);
768 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
770 RegionList::iterator i;
771 framecnt_t old_length = 0;
774 if (!holding_state()) {
775 old_length = _get_maximum_extent();
780 region->set_playlist (boost::weak_ptr<Playlist>());
783 /* XXX should probably freeze here .... */
785 for (i = regions.begin(); i != regions.end(); ++i) {
788 framepos_t pos = (*i)->position();
789 framecnt_t distance = (*i)->length();
793 possibly_splice_unlocked (pos, -distance);
795 if (!holding_state ()) {
797 remove_dependents (region);
799 if (old_length != _get_maximum_extent()) {
800 notify_length_changed ();
804 notify_region_removed (region);
810 /* XXX and thaw ... */
816 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
818 if (Config->get_use_overlap_equivalency()) {
819 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
820 if ((*i)->overlap_equivalent (other)) {
821 results.push_back ((*i));
825 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
826 if ((*i)->equivalent (other)) {
827 results.push_back ((*i));
834 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
836 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
838 if ((*i) && (*i)->region_list_equivalent (other)) {
839 results.push_back (*i);
845 Playlist::partition (framepos_t start, framepos_t end, bool cut)
849 partition_internal (start, end, cut, thawlist);
851 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
852 (*i)->resume_property_changes ();
857 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
859 RegionList new_regions;
862 RegionLock rlock (this);
864 boost::shared_ptr<Region> region;
865 boost::shared_ptr<Region> current;
867 RegionList::iterator tmp;
869 framepos_t pos1, pos2, pos3, pos4;
873 /* need to work from a copy, because otherwise the regions we add during the process
874 get operated on as well.
877 RegionList copy = regions.rlist();
879 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
886 if (current->first_frame() >= start && current->last_frame() < end) {
889 remove_region_internal (current);
895 /* coverage will return OverlapStart if the start coincides
896 with the end point. we do not partition such a region,
897 so catch this special case.
900 if (current->first_frame() >= end) {
904 if ((overlap = current->coverage (start, end)) == OverlapNone) {
908 pos1 = current->position();
911 pos4 = current->last_frame();
913 if (overlap == OverlapInternal) {
914 /* split: we need 3 new regions, the front, middle and end.
915 cut: we need 2 regions, the front and end.
920 ---------------*************************------------
923 ---------------*****++++++++++++++++====------------
925 ---------------*****----------------====------------
930 /* "middle" ++++++ */
932 _session.region_name (new_name, current->name(), false);
936 plist.add (Properties::start, current->start() + (pos2 - pos1));
937 plist.add (Properties::length, pos3 - pos2);
938 plist.add (Properties::name, new_name);
939 plist.add (Properties::layer, regions.size());
940 plist.add (Properties::automatic, true);
941 plist.add (Properties::left_of_split, true);
942 plist.add (Properties::right_of_split, true);
944 region = RegionFactory::create (current, plist);
945 add_region_internal (region, start);
946 new_regions.push_back (region);
951 _session.region_name (new_name, current->name(), false);
955 plist.add (Properties::start, current->start() + (pos3 - pos1));
956 plist.add (Properties::length, pos4 - pos3);
957 plist.add (Properties::name, new_name);
958 plist.add (Properties::layer, regions.size());
959 plist.add (Properties::automatic, true);
960 plist.add (Properties::right_of_split, true);
962 region = RegionFactory::create (current, plist);
964 add_region_internal (region, end);
965 new_regions.push_back (region);
969 current->suspend_property_changes ();
970 thawlist.push_back (current);
971 current->trim_end (pos2, this);
973 } else if (overlap == OverlapEnd) {
977 ---------------*************************------------
980 ---------------**************+++++++++++------------
982 ---------------**************-----------------------
989 _session.region_name (new_name, current->name(), false);
993 plist.add (Properties::start, current->start() + (pos2 - pos1));
994 plist.add (Properties::length, pos4 - pos2);
995 plist.add (Properties::name, new_name);
996 plist.add (Properties::layer, regions.size());
997 plist.add (Properties::automatic, true);
998 plist.add (Properties::left_of_split, true);
1000 region = RegionFactory::create (current, plist);
1002 add_region_internal (region, start);
1003 new_regions.push_back (region);
1008 current->suspend_property_changes ();
1009 thawlist.push_back (current);
1010 current->trim_end (pos2, this);
1012 } else if (overlap == OverlapStart) {
1014 /* split: we need 2 regions: the front and the end.
1015 cut: just trim current to skip the cut area
1020 ---------------*************************------------
1024 ---------------****+++++++++++++++++++++------------
1026 -------------------*********************------------
1032 _session.region_name (new_name, current->name(), false);
1036 plist.add (Properties::start, current->start());
1037 plist.add (Properties::length, pos3 - pos1);
1038 plist.add (Properties::name, new_name);
1039 plist.add (Properties::layer, regions.size());
1040 plist.add (Properties::automatic, true);
1041 plist.add (Properties::right_of_split, true);
1043 region = RegionFactory::create (current, plist);
1045 add_region_internal (region, pos1);
1046 new_regions.push_back (region);
1051 current->suspend_property_changes ();
1052 thawlist.push_back (current);
1053 current->trim_front (pos3, this);
1054 } else if (overlap == OverlapExternal) {
1056 /* split: no split required.
1057 cut: remove the region.
1062 ---------------*************************------------
1066 ---------------*************************------------
1068 ----------------------------------------------------
1073 remove_region_internal (current);
1076 new_regions.push_back (current);
1080 in_partition = false;
1083 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1084 check_dependents (*i, false);
1088 boost::shared_ptr<Playlist>
1089 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1091 boost::shared_ptr<Playlist> ret;
1092 boost::shared_ptr<Playlist> pl;
1095 if (ranges.empty()) {
1096 return boost::shared_ptr<Playlist>();
1099 start = ranges.front().start;
1101 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1103 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1105 if (i == ranges.begin()) {
1109 /* paste the next section into the nascent playlist,
1110 offset to reflect the start of the first range we
1114 ret->paste (pl, (*i).start - start, 1.0f);
1121 boost::shared_ptr<Playlist>
1122 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1124 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1125 return cut_copy (pmf, ranges, result_is_hidden);
1128 boost::shared_ptr<Playlist>
1129 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1131 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1132 return cut_copy (pmf, ranges, result_is_hidden);
1135 boost::shared_ptr<Playlist>
1136 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1138 boost::shared_ptr<Playlist> the_copy;
1139 RegionList thawlist;
1142 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1143 string new_name = _name;
1147 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1148 return boost::shared_ptr<Playlist>();
1151 partition_internal (start, start+cnt-1, true, thawlist);
1153 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1154 (*i)->resume_property_changes();
1160 boost::shared_ptr<Playlist>
1161 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1165 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1166 string new_name = _name;
1170 cnt = min (_get_maximum_extent() - start, cnt);
1171 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1175 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1177 times = fabs (times);
1180 RegionLock rl1 (this);
1181 RegionLock rl2 (other.get());
1183 framecnt_t old_length = _get_maximum_extent();
1185 int itimes = (int) floor (times);
1186 framepos_t pos = position;
1187 framecnt_t shift = other->_get_maximum_extent();
1188 layer_t top_layer = regions.size();
1191 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1192 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1194 /* put these new regions on top of all existing ones, but preserve
1195 the ordering they had in the original playlist.
1198 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1199 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1205 /* XXX shall we handle fractional cases at some point? */
1207 if (old_length != _get_maximum_extent()) {
1208 notify_length_changed ();
1219 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1221 times = fabs (times);
1223 RegionLock rl (this);
1224 int itimes = (int) floor (times);
1225 framepos_t pos = position;
1228 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1229 add_region_internal (copy, pos);
1230 pos += region->length();
1233 if (floor (times) != times) {
1234 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1236 _session.region_name (name, region->name(), false);
1241 plist.add (Properties::start, region->start());
1242 plist.add (Properties::length, length);
1243 plist.add (Properties::name, name);
1245 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1246 add_region_internal (sub, pos);
1252 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1254 RegionLock rlock (this);
1255 RegionList copy (regions.rlist());
1258 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1260 if ((*r)->last_frame() < at) {
1265 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1266 /* intersected region */
1267 if (!move_intersected) {
1272 /* do not move regions glued to music time - that
1273 has to be done separately.
1276 if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
1277 fixup.push_back (*r);
1281 (*r)->set_position ((*r)->position() + distance, this);
1284 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1285 (*r)->recompute_position_from_lock_style ();
1290 Playlist::split (framepos_t at)
1292 RegionLock rlock (this);
1293 RegionList copy (regions.rlist());
1295 /* use a copy since this operation can modify the region list
1298 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1299 _split_region (*r, at);
1304 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1306 RegionLock rl (this);
1307 _split_region (region, playlist_position);
1311 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1313 if (!region->covers (playlist_position)) {
1317 if (region->position() == playlist_position ||
1318 region->last_frame() == playlist_position) {
1322 boost::shared_ptr<Region> left;
1323 boost::shared_ptr<Region> right;
1324 frameoffset_t before;
1325 frameoffset_t after;
1329 /* split doesn't change anything about length, so don't try to splice */
1331 bool old_sp = _splicing;
1334 before = playlist_position - region->position();
1335 after = region->length() - before;
1337 _session.region_name (before_name, region->name(), false);
1342 plist.add (Properties::start, region->start());
1343 plist.add (Properties::length, before);
1344 plist.add (Properties::name, before_name);
1345 plist.add (Properties::left_of_split, true);
1347 left = RegionFactory::create (region, plist);
1350 _session.region_name (after_name, region->name(), false);
1355 plist.add (Properties::start, region->start() + before);
1356 plist.add (Properties::length, after);
1357 plist.add (Properties::name, after_name);
1358 plist.add (Properties::right_of_split, true);
1360 right = RegionFactory::create (region, plist);
1363 add_region_internal (left, region->position());
1364 add_region_internal (right, region->position() + before);
1366 uint64_t orig_layer_op = region->last_layer_op();
1367 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1368 if ((*i)->last_layer_op() > orig_layer_op) {
1369 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1373 left->set_last_layer_op ( orig_layer_op );
1374 right->set_last_layer_op ( orig_layer_op + 1);
1378 finalize_split_region (region, left, right);
1380 remove_region_internal (region);
1386 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1388 if (_splicing || in_set_state) {
1389 /* don't respond to splicing moves or state setting */
1393 if (_edit_mode == Splice) {
1394 splice_locked (at, distance, exclude);
1399 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1401 if (_splicing || in_set_state) {
1402 /* don't respond to splicing moves or state setting */
1406 if (_edit_mode == Splice) {
1407 splice_unlocked (at, distance, exclude);
1412 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1415 RegionLock rl (this);
1416 core_splice (at, distance, exclude);
1421 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1423 core_splice (at, distance, exclude);
1427 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1431 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1433 if (exclude && (*i) == exclude) {
1437 if ((*i)->position() >= at) {
1438 framepos_t new_pos = (*i)->position() + distance;
1441 } else if (new_pos >= max_frames - (*i)->length()) {
1442 new_pos = max_frames - (*i)->length();
1445 (*i)->set_position (new_pos, this);
1451 notify_length_changed ();
1455 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1457 if (in_set_state || _splicing || _nudging || _shuffling) {
1461 if (what_changed.contains (Properties::position)) {
1463 /* remove it from the list then add it back in
1464 the right place again.
1467 RegionSortByPosition cmp;
1469 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1471 if (i == regions.end()) {
1472 /* the region bounds are being modified but its not currently
1473 in the region list. we will use its bounds correctly when/if
1480 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1483 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1485 frameoffset_t delta = 0;
1487 if (what_changed.contains (Properties::position)) {
1488 delta = region->position() - region->last_position();
1491 if (what_changed.contains (Properties::length)) {
1492 delta += region->length() - region->last_length();
1496 possibly_splice (region->last_position() + region->last_length(), delta, region);
1499 if (holding_state ()) {
1500 pending_bounds.push_back (region);
1502 if (_session.config.get_layer_model() == MoveAddHigher) {
1503 /* it moved or changed length, so change the timestamp */
1504 timestamp_layer_op (region);
1507 notify_length_changed ();
1509 check_dependents (region, false);
1515 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1517 boost::shared_ptr<Region> region (weak_region.lock());
1523 /* this makes a virtual call to the right kind of playlist ... */
1525 region_changed (what_changed, region);
1529 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1531 PropertyChange our_interests;
1532 PropertyChange bounds;
1533 PropertyChange pos_and_length;
1536 if (in_set_state || in_flush) {
1540 our_interests.add (Properties::muted);
1541 our_interests.add (Properties::layer);
1542 our_interests.add (Properties::opaque);
1544 bounds.add (Properties::start);
1545 bounds.add (Properties::position);
1546 bounds.add (Properties::length);
1548 pos_and_length.add (Properties::position);
1549 pos_and_length.add (Properties::length);
1551 if (what_changed.contains (bounds)) {
1552 region_bounds_changed (what_changed, region);
1553 save = !(_splicing || _nudging);
1556 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1557 check_dependents (region, false);
1560 if (what_changed.contains (Properties::position)) {
1561 notify_region_moved (region);
1565 /* don't notify about layer changes, since we are the only object that can initiate
1566 them, and we notify in ::relayer()
1569 if (what_changed.contains (our_interests)) {
1577 Playlist::drop_regions ()
1579 RegionLock rl (this);
1581 all_regions.clear ();
1585 Playlist::clear (bool with_signals)
1588 RegionLock rl (this);
1590 region_state_changed_connections.drop_connections ();
1592 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1593 pending_removes.insert (*i);
1598 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1599 remove_dependents (*s);
1605 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1606 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1609 pending_removes.clear ();
1610 pending_length = false;
1612 pending_contents_change = false;
1618 /***********************************************************************
1620 **********************************************************************/
1622 Playlist::RegionList *
1623 Playlist::regions_at (framepos_t frame)
1626 RegionLock rlock (this);
1627 return find_regions_at (frame);
1630 boost::shared_ptr<Region>
1631 Playlist::top_region_at (framepos_t frame)
1634 RegionLock rlock (this);
1635 RegionList *rlist = find_regions_at (frame);
1636 boost::shared_ptr<Region> region;
1638 if (rlist->size()) {
1639 RegionSortByLayer cmp;
1641 region = rlist->back();
1648 boost::shared_ptr<Region>
1649 Playlist::top_unmuted_region_at (framepos_t frame)
1652 RegionLock rlock (this);
1653 RegionList *rlist = find_regions_at (frame);
1655 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1657 RegionList::iterator tmp = i;
1660 if ((*i)->muted()) {
1667 boost::shared_ptr<Region> region;
1669 if (rlist->size()) {
1670 RegionSortByLayer cmp;
1672 region = rlist->back();
1679 Playlist::RegionList*
1680 Playlist::regions_to_read (framepos_t start, framepos_t end)
1682 /* Caller must hold lock */
1684 RegionList covering;
1685 set<framepos_t> to_check;
1686 set<boost::shared_ptr<Region> > unique;
1688 to_check.insert (start);
1689 to_check.insert (end);
1691 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1693 /* find all/any regions that span start+end */
1695 switch ((*i)->coverage (start, end)) {
1699 case OverlapInternal:
1700 covering.push_back (*i);
1704 to_check.insert ((*i)->position());
1705 covering.push_back (*i);
1709 to_check.insert ((*i)->last_frame());
1710 covering.push_back (*i);
1713 case OverlapExternal:
1714 covering.push_back (*i);
1715 to_check.insert ((*i)->position());
1716 to_check.insert ((*i)->last_frame());
1720 /* don't go too far */
1722 if ((*i)->position() > end) {
1727 RegionList* rlist = new RegionList;
1729 /* find all the regions that cover each position .... */
1731 if (covering.size() == 1) {
1733 rlist->push_back (covering.front());
1738 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1742 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1744 if ((*x)->covers (*t)) {
1745 here.push_back (*x);
1749 RegionSortByLayer cmp;
1752 /* ... and get the top/transparent regions at "here" */
1754 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1758 if ((*c)->opaque()) {
1760 /* the other regions at this position are hidden by this one */
1767 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1768 rlist->push_back (*s);
1771 if (rlist->size() > 1) {
1772 /* now sort by time order */
1774 RegionSortByPosition cmp;
1782 Playlist::RegionList *
1783 Playlist::find_regions_at (framepos_t frame)
1785 /* Caller must hold lock */
1787 RegionList *rlist = new RegionList;
1789 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1790 if ((*i)->covers (frame)) {
1791 rlist->push_back (*i);
1798 Playlist::RegionList *
1799 Playlist::regions_touched (framepos_t start, framepos_t end)
1801 RegionLock rlock (this);
1802 RegionList *rlist = new RegionList;
1804 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1805 if ((*i)->coverage (start, end) != OverlapNone) {
1806 rlist->push_back (*i);
1814 Playlist::find_next_transient (framepos_t from, int dir)
1816 RegionLock rlock (this);
1817 AnalysisFeatureList points;
1818 AnalysisFeatureList these_points;
1820 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1822 if ((*i)->last_frame() < from) {
1826 if ((*i)->first_frame() > from) {
1831 (*i)->get_transients (these_points);
1833 /* add first frame, just, err, because */
1835 these_points.push_back ((*i)->first_frame());
1837 points.insert (points.end(), these_points.begin(), these_points.end());
1838 these_points.clear ();
1841 if (points.empty()) {
1845 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1846 bool reached = false;
1849 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1854 if (reached && (*x) > from) {
1859 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1864 if (reached && (*x) < from) {
1873 boost::shared_ptr<Region>
1874 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1876 RegionLock rlock (this);
1877 boost::shared_ptr<Region> ret;
1878 framepos_t closest = max_frames;
1880 bool end_iter = false;
1882 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1886 frameoffset_t distance;
1887 boost::shared_ptr<Region> r = (*i);
1892 pos = r->first_frame ();
1895 pos = r->last_frame ();
1898 pos = r->sync_position ();
1899 // r->adjust_to_sync (r->first_frame());
1904 case 1: /* forwards */
1907 if ((distance = pos - frame) < closest) {
1916 default: /* backwards */
1919 if ((distance = frame - pos) < closest) {
1936 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1938 RegionLock rlock (this);
1940 framepos_t closest = max_frames;
1941 framepos_t ret = -1;
1945 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1947 boost::shared_ptr<Region> r = (*i);
1948 frameoffset_t distance;
1950 if (r->first_frame() > frame) {
1952 distance = r->first_frame() - frame;
1954 if (distance < closest) {
1955 ret = r->first_frame();
1960 if (r->last_frame () > frame) {
1962 distance = r->last_frame () - frame;
1964 if (distance < closest) {
1965 ret = r->last_frame ();
1973 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1975 boost::shared_ptr<Region> r = (*i);
1976 frameoffset_t distance;
1978 if (r->last_frame() < frame) {
1980 distance = frame - r->last_frame();
1982 if (distance < closest) {
1983 ret = r->last_frame();
1988 if (r->first_frame() < frame) {
1990 distance = frame - r->first_frame();
1992 if (distance < closest) {
1993 ret = r->first_frame();
2003 /***********************************************************************/
2009 Playlist::mark_session_dirty ()
2011 if (!in_set_state && !holding_state ()) {
2012 _session.set_dirty();
2017 Playlist::set_property (const PropertyBase& prop)
2019 if (prop == Properties::regions.property_id) {
2020 const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
2021 regions.update (change);
2022 return (!change.added.empty() && !change.removed.empty());
2028 Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
2030 RegionLock rlock (const_cast<Playlist *> (this));
2032 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2033 if ((*i)->changed ()) {
2034 StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
2035 cmds.push_back (sdc);
2041 Playlist::clear_owned_history ()
2043 RegionLock rlock (this);
2045 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2046 (*i)->clear_history ();
2051 Playlist::update (const RegionListProperty::ChangeRecord& change)
2053 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2054 name(), change.added.size(), change.removed.size()));
2057 /* add the added regions */
2058 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2059 add_region ((*i), (*i)->position());
2061 /* remove the removed regions */
2062 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2070 Playlist::property_factory (const XMLNode& history_node) const
2072 const XMLNodeList& children (history_node.children());
2073 PropertyList* prop_list = 0;
2075 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2077 if ((*i)->name() == capitalize (regions.property_name())) {
2079 RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
2081 if (rlp->load_history_state (**i)) {
2083 prop_list = new PropertyList();
2085 prop_list->add (rlp);
2096 Playlist::set_state (const XMLNode& node, int version)
2100 XMLNodeConstIterator niter;
2101 XMLPropertyList plist;
2102 XMLPropertyConstIterator piter;
2104 boost::shared_ptr<Region> region;
2109 if (node.name() != "Playlist") {
2116 plist = node.properties();
2118 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2122 if (prop->name() == X_("name")) {
2123 _name = prop->value();
2124 } else if (prop->name() == X_("id")) {
2125 _id = prop->value();
2126 } else if (prop->name() == X_("orig_diskstream_id")) {
2127 _orig_diskstream_id = prop->value ();
2128 } else if (prop->name() == X_("frozen")) {
2129 _frozen = string_is_affirmative (prop->value());
2135 nlist = node.children();
2137 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2141 if (child->name() == "Region") {
2143 if ((prop = child->property ("id")) == 0) {
2144 error << _("region state node has no ID, ignored") << endmsg;
2148 ID id = prop->value ();
2150 if ((region = region_by_id (id))) {
2152 region->suspend_property_changes ();
2154 if (region->set_state (*child, version)) {
2155 region->resume_property_changes ();
2159 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2160 region->suspend_property_changes ();
2162 error << _("Playlist: cannot create region from XML") << endmsg;
2166 add_region (region, region->position(), 1.0);
2168 // So that layer_op ordering doesn't get screwed up
2169 region->set_last_layer_op( region->layer());
2170 region->resume_property_changes ();
2174 /* update dependents, which was not done during add_region_internal
2175 due to in_set_state being true
2178 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2179 check_dependents (*r, false);
2183 notify_contents_changed ();
2186 first_set_state = false;
2191 Playlist::get_state()
2193 return state (true);
2197 Playlist::get_template()
2199 return state (false);
2202 /** @param full_state true to include regions in the returned state, otherwise false.
2205 Playlist::state (bool full_state)
2207 XMLNode *node = new XMLNode (X_("Playlist"));
2210 node->add_property (X_("id"), id().to_s());
2211 node->add_property (X_("name"), _name);
2212 node->add_property (X_("type"), _type.to_string());
2214 _orig_diskstream_id.print (buf, sizeof (buf));
2215 node->add_property (X_("orig_diskstream_id"), buf);
2216 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2219 RegionLock rlock (this, false);
2220 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2221 node->add_child_nocopy ((*i)->get_state());
2226 node->add_child_copy (*_extra_xml);
2233 Playlist::empty() const
2235 RegionLock rlock (const_cast<Playlist *>(this), false);
2236 return regions.empty();
2240 Playlist::n_regions() const
2242 RegionLock rlock (const_cast<Playlist *>(this), false);
2243 return regions.size();
2247 Playlist::get_maximum_extent () const
2249 RegionLock rlock (const_cast<Playlist *>(this), false);
2250 return _get_maximum_extent ();
2254 Playlist::_get_maximum_extent () const
2256 RegionList::const_iterator i;
2257 framecnt_t max_extent = 0;
2260 for (i = regions.begin(); i != regions.end(); ++i) {
2261 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
2270 Playlist::bump_name (string name, Session &session)
2272 string newname = name;
2275 newname = bump_name_once (newname);
2276 } while (session.playlists->by_name (newname)!=NULL);
2283 Playlist::top_layer() const
2285 RegionLock rlock (const_cast<Playlist *> (this));
2288 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2289 top = max (top, (*i)->layer());
2295 Playlist::set_edit_mode (EditMode mode)
2300 /********************
2302 ********************/
2305 Playlist::relayer ()
2307 /* never compute layers when changing state for undo/redo or setting from XML*/
2309 if (in_update || in_set_state) {
2313 bool changed = false;
2315 /* Build up a new list of regions on each layer, stored in a set of lists
2316 each of which represent some period of time on some layer. The idea
2317 is to avoid having to search the entire region list to establish whether
2318 each region overlaps another */
2320 /* how many pieces to divide this playlist's time up into */
2321 int const divisions = 512;
2323 /* find the start and end positions of the regions on this playlist */
2324 framepos_t start = UINT_MAX;
2326 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2327 start = min (start, (*i)->position());
2328 end = max (end, (*i)->position() + (*i)->length());
2331 /* hence the size of each time division */
2332 double const division_size = (end - start) / double (divisions);
2334 vector<vector<RegionList> > layers;
2335 layers.push_back (vector<RegionList> (divisions));
2337 /* we want to go through regions from desired lowest to desired highest layer,
2338 which depends on the layer model
2341 RegionList copy = regions.rlist();
2343 /* sort according to the model and the layering mode that we're in */
2345 if (_explicit_relayering) {
2347 copy.sort (RegionSortByLayerWithPending ());
2349 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2351 copy.sort (RegionSortByLastLayerOp ());
2356 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2358 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2359 (*i)->set_pending_explicit_relayer (false);
2361 /* find the time divisions that this region covers */
2362 int const start_division = floor ( ((*i)->position() - start) / division_size);
2363 int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2364 if (end_division == divisions) {
2368 assert (end_division < divisions);
2370 /* find the lowest layer that this region can go on */
2371 size_t j = layers.size();
2373 /* try layer j - 1; it can go on if it overlaps no other region
2374 that is already on that layer
2377 bool overlap = false;
2378 for (int k = start_division; k <= end_division; ++k) {
2379 RegionList::iterator l = layers[j-1][k].begin ();
2380 while (l != layers[j-1][k].end()) {
2381 if ((*l)->overlap_equivalent (*i)) {
2394 /* overlap, so we must use layer j */
2401 if (j == layers.size()) {
2402 /* we need a new layer for this region */
2403 layers.push_back (vector<RegionList> (divisions));
2406 /* put a reference to this region in each of the divisions that it exists in */
2407 for (int k = start_division; k <= end_division; ++k) {
2408 layers[j][k].push_back (*i);
2411 if ((*i)->layer() != j) {
2415 (*i)->set_layer (j);
2419 notify_layering_changed ();
2423 /* XXX these layer functions are all deprecated */
2426 Playlist::raise_region (boost::shared_ptr<Region> region)
2428 uint32_t rsz = regions.size();
2429 layer_t target = region->layer() + 1U;
2431 if (target >= rsz) {
2432 /* its already at the effective top */
2436 move_region_to_layer (target, region, 1);
2440 Playlist::lower_region (boost::shared_ptr<Region> region)
2442 if (region->layer() == 0) {
2443 /* its already at the bottom */
2447 layer_t target = region->layer() - 1U;
2449 move_region_to_layer (target, region, -1);
2453 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2455 /* does nothing useful if layering mode is later=higher */
2456 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2457 (_session.config.get_layer_model() == AddHigher)) {
2458 timestamp_layer_op (region);
2464 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2466 /* does nothing useful if layering mode is later=higher */
2467 if ((_session.config.get_layer_model() == MoveAddHigher) ||
2468 (_session.config.get_layer_model() == AddHigher)) {
2469 region->set_last_layer_op (0);
2475 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2477 RegionList::iterator i;
2478 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2479 list<LayerInfo> layerinfo;
2482 RegionLock rlock (const_cast<Playlist *> (this));
2484 for (i = regions.begin(); i != regions.end(); ++i) {
2494 /* region is moving up, move all regions on intermediate layers
2498 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2499 dest = (*i)->layer() - 1;
2506 /* region is moving down, move all regions on intermediate layers
2510 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2511 dest = (*i)->layer() + 1;
2521 newpair.second = dest;
2523 layerinfo.push_back (newpair);
2527 /* now reset the layers without holding the region lock */
2529 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2530 x->first->set_layer (x->second);
2533 region->set_layer (target_layer);
2536 /* now check all dependents */
2538 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2539 check_dependents (x->first, false);
2542 check_dependents (region, false);
2549 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2551 RegionList::iterator i;
2557 RegionLock rlock (const_cast<Playlist *> (this));
2559 for (i = regions.begin(); i != regions.end(); ++i) {
2561 if ((*i)->position() >= start) {
2567 if ((*i)->last_frame() > max_frames - distance) {
2568 new_pos = max_frames - (*i)->length();
2570 new_pos = (*i)->position() + distance;
2575 if ((*i)->position() > distance) {
2576 new_pos = (*i)->position() - distance;
2582 (*i)->set_position (new_pos, this);
2590 notify_length_changed ();
2595 boost::shared_ptr<Region>
2596 Playlist::find_region (const ID& id) const
2598 RegionLock rlock (const_cast<Playlist*> (this));
2600 /* searches all regions currently in use by the playlist */
2602 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2603 if ((*i)->id() == id) {
2608 return boost::shared_ptr<Region> ();
2611 boost::shared_ptr<Region>
2612 Playlist::region_by_id (const ID& id)
2614 /* searches all regions ever added to this playlist */
2616 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2617 if ((*i)->id() == id) {
2621 return boost::shared_ptr<Region> ();
2625 Playlist::dump () const
2627 boost::shared_ptr<Region> r;
2629 cerr << "Playlist \"" << _name << "\" " << endl
2630 << regions.size() << " regions "
2633 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2635 cerr << " " << r->name() << " ["
2636 << r->start() << "+" << r->length()
2646 Playlist::set_frozen (bool yn)
2652 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2654 // struct timeval tv;
2655 // gettimeofday (&tv, 0);
2656 region->set_last_layer_op (++layer_op_counter);
2661 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2665 if (region->locked()) {
2672 RegionLock rlock (const_cast<Playlist*> (this));
2677 RegionList::iterator next;
2679 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2680 if ((*i) == region) {
2684 if (next != regions.end()) {
2686 if ((*next)->locked()) {
2692 if ((*next)->position() != region->last_frame() + 1) {
2693 /* they didn't used to touch, so after shuffle,
2694 just have them swap positions.
2696 new_pos = (*next)->position();
2698 /* they used to touch, so after shuffle,
2699 make sure they still do. put the earlier
2700 region where the later one will end after
2703 new_pos = region->position() + (*next)->length();
2706 (*next)->set_position (region->position(), this);
2707 region->set_position (new_pos, this);
2709 /* avoid a full sort */
2711 regions.erase (i); // removes the region from the list */
2713 regions.insert (next, region); // adds it back after next
2722 RegionList::iterator prev = regions.end();
2724 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2725 if ((*i) == region) {
2727 if (prev != regions.end()) {
2729 if ((*prev)->locked()) {
2734 if (region->position() != (*prev)->last_frame() + 1) {
2735 /* they didn't used to touch, so after shuffle,
2736 just have them swap positions.
2738 new_pos = region->position();
2740 /* they used to touch, so after shuffle,
2741 make sure they still do. put the earlier
2742 one where the later one will end after
2744 new_pos = (*prev)->position() + region->length();
2747 region->set_position ((*prev)->position(), this);
2748 (*prev)->set_position (new_pos, this);
2750 /* avoid a full sort */
2752 regions.erase (i); // remove region
2753 regions.insert (prev, region); // insert region before prev
2769 check_dependents (region, false);
2771 notify_contents_changed();
2777 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2779 RegionLock rlock (const_cast<Playlist*> (this));
2781 if (regions.size() > 1) {
2789 Playlist::update_after_tempo_map_change ()
2791 RegionLock rlock (const_cast<Playlist*> (this));
2792 RegionList copy (regions.rlist());
2796 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2797 (*i)->update_position_after_tempo_map_change ();
2804 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2806 RegionLock rl (this, false);
2807 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2813 Playlist::set_explicit_relayering (bool e)
2815 if (e == false && _explicit_relayering == true) {
2817 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2818 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2819 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2820 at this point would keep regions on the same layers.
2822 From then on in, it's just you and your towel.
2825 RegionLock rl (this);
2826 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2827 (*i)->set_last_layer_op ((*i)->layer ());
2831 _explicit_relayering = e;
2836 Playlist::has_region_at (framepos_t const p) const
2838 RegionLock (const_cast<Playlist *> (this));
2840 RegionList::const_iterator i = regions.begin ();
2841 while (i != regions.end() && !(*i)->covers (p)) {
2845 return (i != regions.end());