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.
25 #include <boost/lexical_cast.hpp>
27 #include "pbd/convert.h"
28 #include "pbd/stateful_diff_command.h"
29 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/playlist.h"
33 #include "ardour/session.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/region_sorters.h"
37 #include "ardour/playlist_factory.h"
38 #include "ardour/playlist_source.h"
39 #include "ardour/transient_detector.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
67 Playlist::make_property_quarks ()
69 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
70 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
71 Properties::regions.property_id));
74 RegionListProperty::RegionListProperty (Playlist& pl)
75 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
81 RegionListProperty::RegionListProperty (RegionListProperty const & p)
82 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
83 , _playlist (p._playlist)
89 RegionListProperty::clone () const
91 return new RegionListProperty (*this);
95 RegionListProperty::create () const
97 return new RegionListProperty (_playlist);
101 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
103 /* All regions (even those which are deleted) have their state saved by other
104 code, so we can just store ID here.
107 node.add_property ("id", region->id().to_s ());
110 boost::shared_ptr<Region>
111 RegionListProperty::get_content_from_xml (XMLNode const & node) const
113 XMLProperty const * prop = node.property ("id");
116 PBD::ID id (prop->value ());
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
128 : SessionObject(sess, nom)
133 first_set_state = false;
138 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
139 : SessionObject(sess, "unnamed playlist")
144 const XMLProperty* prop = node.property("type");
145 assert(!prop || DataType(prop->value()) == _type);
149 _name = "unnamed"; /* reset by set_state */
152 /* set state called by derived class */
155 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
156 : SessionObject(other->_session, namestr)
158 , _type(other->_type)
159 , _orig_track_id (other->_orig_track_id)
164 other->copy_regions (tmp);
168 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
169 add_region_internal( (*x), (*x)->position());
174 _splicing = other->_splicing;
175 _nudging = other->_nudging;
176 _edit_mode = other->_edit_mode;
179 first_set_state = false;
181 in_partition = false;
183 _frozen = other->_frozen;
186 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
187 : SessionObject(other->_session, str)
189 , _type(other->_type)
190 , _orig_track_id (other->_orig_track_id)
192 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
194 framepos_t end = start + cnt - 1;
200 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
202 boost::shared_ptr<Region> region;
203 boost::shared_ptr<Region> new_region;
204 frameoffset_t offset = 0;
205 framepos_t position = 0;
208 Evoral::OverlapType overlap;
212 overlap = region->coverage (start, end);
215 case Evoral::OverlapNone:
218 case Evoral::OverlapInternal:
219 offset = start - region->position();
224 case Evoral::OverlapStart:
226 position = region->position() - start;
227 len = end - region->position();
230 case Evoral::OverlapEnd:
231 offset = start - region->position();
233 len = region->length() - offset;
236 case Evoral::OverlapExternal:
238 position = region->position() - start;
239 len = region->length();
243 RegionFactory::region_name (new_name, region->name(), false);
247 plist.add (Properties::start, region->start() + offset);
248 plist.add (Properties::length, len);
249 plist.add (Properties::name, new_name);
250 plist.add (Properties::layer, region->layer());
251 plist.add (Properties::layering_index, region->layering_index());
253 new_region = RegionFactory::RegionFactory::create (region, plist);
255 add_region_internal (new_region, position);
259 first_set_state = false;
266 InUse (true); /* EMIT SIGNAL */
277 InUse (false); /* EMIT SIGNAL */
282 Playlist::copy_regions (RegionList& newlist) const
284 RegionReadLock rlock (const_cast<Playlist *> (this));
286 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
287 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
292 Playlist::init (bool hide)
294 add_property (regions);
295 _xml_node_name = X_("Playlist");
297 g_atomic_int_set (&block_notifications, 0);
298 g_atomic_int_set (&ignore_state_changes, 0);
299 pending_contents_change = false;
300 pending_layering = false;
301 first_set_state = true;
309 _edit_mode = Config->get_edit_mode();
311 in_partition = false;
316 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
317 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
319 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
322 Playlist::~Playlist ()
324 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
327 RegionReadLock rl (this);
329 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
330 (*i)->set_playlist (boost::shared_ptr<Playlist>());
334 /* GoingAway must be emitted by derived classes */
338 Playlist::_set_sort_id ()
341 Playlists are given names like <track name>.<id>
342 or <track name>.<edit group name>.<id> where id
343 is an integer. We extract the id and sort by that.
346 size_t dot_position = _name.val().find_last_of(".");
348 if (dot_position == string::npos) {
351 string t = _name.val().substr(dot_position + 1);
354 _sort_id = boost::lexical_cast<int>(t);
357 catch (boost::bad_lexical_cast e) {
364 Playlist::set_name (const string& str)
366 /* in a typical situation, a playlist is being used
367 by one diskstream and also is referenced by the
368 Session. if there are more references than that,
369 then don't change the name.
376 bool ret = SessionObject::set_name(str);
383 /***********************************************************************
384 CHANGE NOTIFICATION HANDLING
386 Notifications must be delayed till the region_lock is released. This
387 is necessary because handlers for the signals may need to acquire
388 the lock (e.g. to read from the playlist).
389 ***********************************************************************/
392 Playlist::begin_undo ()
399 Playlist::end_undo ()
408 delay_notifications ();
409 g_atomic_int_inc (&ignore_state_changes);
412 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
414 Playlist::thaw (bool from_undo)
416 g_atomic_int_dec_and_test (&ignore_state_changes);
417 release_notifications (from_undo);
422 Playlist::delay_notifications ()
424 g_atomic_int_inc (&block_notifications);
427 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
429 Playlist::release_notifications (bool from_undo)
431 if (g_atomic_int_dec_and_test (&block_notifications)) {
432 flush_notifications (from_undo);
437 Playlist::notify_contents_changed ()
439 if (holding_state ()) {
440 pending_contents_change = true;
442 pending_contents_change = false;
443 ContentsChanged(); /* EMIT SIGNAL */
448 Playlist::notify_layering_changed ()
450 if (holding_state ()) {
451 pending_layering = true;
453 pending_layering = false;
454 LayeringChanged(); /* EMIT SIGNAL */
459 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
461 if (holding_state ()) {
462 pending_removes.insert (r);
463 pending_contents_change = true;
465 /* this might not be true, but we have to act
466 as though it could be.
468 pending_contents_change = false;
469 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
470 ContentsChanged (); /* EMIT SIGNAL */
475 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
477 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
479 if (holding_state ()) {
481 pending_range_moves.push_back (move);
485 list< Evoral::RangeMove<framepos_t> > m;
487 RangesMoved (m, false);
493 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
495 if (r->position() >= r->last_position()) {
496 /* trimmed shorter */
500 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
502 if (holding_state ()) {
504 pending_region_extensions.push_back (extra);
508 list<Evoral::Range<framepos_t> > r;
516 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
518 if (r->length() < r->last_length()) {
519 /* trimmed shorter */
522 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
524 if (holding_state ()) {
526 pending_region_extensions.push_back (extra);
530 list<Evoral::Range<framepos_t> > r;
538 Playlist::notify_region_added (boost::shared_ptr<Region> r)
540 /* the length change might not be true, but we have to act
541 as though it could be.
544 if (holding_state()) {
545 pending_adds.insert (r);
546 pending_contents_change = true;
549 pending_contents_change = false;
550 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
551 ContentsChanged (); /* EMIT SIGNAL */
555 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
557 Playlist::flush_notifications (bool from_undo)
559 set<boost::shared_ptr<Region> >::iterator s;
560 bool regions_changed = false;
568 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
569 regions_changed = true;
572 /* XXX: it'd be nice if we could use pending_bounds for
573 RegionsExtended and RegionsMoved.
576 /* we have no idea what order the regions ended up in pending
577 bounds (it could be based on selection order, for example).
578 so, to preserve layering in the "most recently moved is higher"
579 model, sort them by existing layer, then timestamp them.
582 // RegionSortByLayer cmp;
583 // pending_bounds.sort (cmp);
585 list<Evoral::Range<framepos_t> > crossfade_ranges;
587 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
588 crossfade_ranges.push_back ((*r)->last_range ());
589 crossfade_ranges.push_back ((*r)->range ());
592 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
593 crossfade_ranges.push_back ((*s)->range ());
594 remove_dependents (*s);
595 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
598 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
599 crossfade_ranges.push_back ((*s)->range ());
600 /* don't emit RegionAdded signal until relayering is done,
601 so that the region is fully setup by the time
602 anyone hears that its been added
606 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
610 if (regions_changed || pending_contents_change) {
611 pending_contents_change = false;
612 ContentsChanged (); /* EMIT SIGNAL */
615 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
616 (*s)->clear_changes ();
617 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
620 coalesce_and_check_crossfades (crossfade_ranges);
622 if (!pending_range_moves.empty ()) {
623 /* We don't need to check crossfades for these as pending_bounds has
626 RangesMoved (pending_range_moves, from_undo);
629 if (!pending_region_extensions.empty ()) {
630 RegionsExtended (pending_region_extensions);
639 Playlist::clear_pending ()
641 pending_adds.clear ();
642 pending_removes.clear ();
643 pending_bounds.clear ();
644 pending_range_moves.clear ();
645 pending_region_extensions.clear ();
646 pending_contents_change = false;
649 /*************************************************************
651 *************************************************************/
653 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
655 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
657 RegionWriteLock rlock (this);
658 times = fabs (times);
660 int itimes = (int) floor (times);
662 framepos_t pos = position;
664 if (times == 1 && auto_partition){
665 partition(pos - 1, (pos + region->length()), true);
669 add_region_internal (region, pos);
670 set_layer (region, DBL_MAX);
671 pos += region->length();
676 /* note that itimes can be zero if we being asked to just
677 insert a single fraction of the region.
680 for (int i = 0; i < itimes; ++i) {
681 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
682 add_region_internal (copy, pos);
683 set_layer (copy, DBL_MAX);
684 pos += region->length();
687 framecnt_t length = 0;
689 if (floor (times) != times) {
690 length = (framecnt_t) floor (region->length() * (times - floor (times)));
692 RegionFactory::region_name (name, region->name(), false);
697 plist.add (Properties::start, region->start());
698 plist.add (Properties::length, length);
699 plist.add (Properties::name, name);
700 plist.add (Properties::layer, region->layer());
702 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
703 add_region_internal (sub, pos);
704 set_layer (sub, DBL_MAX);
708 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
712 Playlist::set_region_ownership ()
714 RegionWriteLock rl (this);
715 RegionList::iterator i;
716 boost::weak_ptr<Playlist> pl (shared_from_this());
718 for (i = regions.begin(); i != regions.end(); ++i) {
719 (*i)->set_playlist (pl);
724 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
726 if (region->data_type() != _type) {
730 RegionSortByPosition cmp;
732 if (!first_set_state) {
733 boost::shared_ptr<Playlist> foo (shared_from_this());
734 region->set_playlist (boost::weak_ptr<Playlist>(foo));
737 region->set_position (position);
739 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
740 all_regions.insert (region);
742 possibly_splice_unlocked (position, region->length(), region);
744 if (!holding_state ()) {
745 /* layers get assigned from XML state, and are not reset during undo/redo */
749 /* we need to notify the existence of new region before checking dependents. Ick. */
751 notify_region_added (region);
753 if (!holding_state ()) {
754 check_crossfades (region->range ());
757 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
763 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
765 RegionWriteLock rlock (this);
767 bool old_sp = _splicing;
770 remove_region_internal (old);
771 add_region_internal (newr, pos);
772 set_layer (newr, old->layer ());
776 possibly_splice_unlocked (pos, old->length() - newr->length());
780 Playlist::remove_region (boost::shared_ptr<Region> region)
782 RegionWriteLock rlock (this);
783 remove_region_internal (region);
787 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
789 RegionList::iterator i;
793 region->set_playlist (boost::weak_ptr<Playlist>());
796 /* XXX should probably freeze here .... */
798 for (i = regions.begin(); i != regions.end(); ++i) {
801 framepos_t pos = (*i)->position();
802 framecnt_t distance = (*i)->length();
806 possibly_splice_unlocked (pos, -distance);
808 if (!holding_state ()) {
810 remove_dependents (region);
813 notify_region_removed (region);
822 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
824 if (Config->get_use_overlap_equivalency()) {
825 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
826 if ((*i)->overlap_equivalent (other)) {
827 results.push_back (*i);
831 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
832 if ((*i)->equivalent (other)) {
833 results.push_back (*i);
840 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
842 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
844 if ((*i) && (*i)->region_list_equivalent (other)) {
845 results.push_back (*i);
851 Playlist::partition (framepos_t start, framepos_t end, bool cut)
855 partition_internal (start, end, cut, thawlist);
857 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
858 (*i)->resume_property_changes ();
862 /** Go through each region on the playlist and cut them at start and end, removing the section between
863 * start and end if cutting == true. Regions that lie entirely within start and end are always
868 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
870 RegionList new_regions;
873 RegionWriteLock rlock (this);
875 boost::shared_ptr<Region> region;
876 boost::shared_ptr<Region> current;
878 RegionList::iterator tmp;
879 Evoral::OverlapType overlap;
880 framepos_t pos1, pos2, pos3, pos4;
884 /* need to work from a copy, because otherwise the regions we add during the process
885 get operated on as well.
888 RegionList copy = regions.rlist();
890 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
897 if (current->first_frame() >= start && current->last_frame() < end) {
900 remove_region_internal (current);
906 /* coverage will return OverlapStart if the start coincides
907 with the end point. we do not partition such a region,
908 so catch this special case.
911 if (current->first_frame() >= end) {
915 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
919 pos1 = current->position();
922 pos4 = current->last_frame();
924 if (overlap == Evoral::OverlapInternal) {
925 /* split: we need 3 new regions, the front, middle and end.
926 cut: we need 2 regions, the front and end.
931 ---------------*************************------------
934 ---------------*****++++++++++++++++====------------
936 ---------------*****----------------====------------
941 /* "middle" ++++++ */
943 RegionFactory::region_name (new_name, current->name(), false);
947 plist.add (Properties::start, current->start() + (pos2 - pos1));
948 plist.add (Properties::length, pos3 - pos2);
949 plist.add (Properties::name, new_name);
950 plist.add (Properties::layer, current->layer ());
951 plist.add (Properties::layering_index, current->layering_index ());
952 plist.add (Properties::automatic, true);
953 plist.add (Properties::left_of_split, true);
954 plist.add (Properties::right_of_split, true);
956 region = RegionFactory::create (current, plist);
957 add_region_internal (region, start);
958 new_regions.push_back (region);
963 RegionFactory::region_name (new_name, current->name(), false);
967 plist.add (Properties::start, current->start() + (pos3 - pos1));
968 plist.add (Properties::length, pos4 - pos3);
969 plist.add (Properties::name, new_name);
970 plist.add (Properties::layer, current->layer ());
971 plist.add (Properties::layering_index, current->layering_index ());
972 plist.add (Properties::automatic, true);
973 plist.add (Properties::right_of_split, true);
975 region = RegionFactory::create (current, plist);
977 add_region_internal (region, end);
978 new_regions.push_back (region);
982 current->suspend_property_changes ();
983 thawlist.push_back (current);
984 current->cut_end (pos2 - 1);
986 } else if (overlap == Evoral::OverlapEnd) {
990 ---------------*************************------------
993 ---------------**************+++++++++++------------
995 ---------------**************-----------------------
1002 RegionFactory::region_name (new_name, current->name(), false);
1006 plist.add (Properties::start, current->start() + (pos2 - pos1));
1007 plist.add (Properties::length, pos4 - pos2);
1008 plist.add (Properties::name, new_name);
1009 plist.add (Properties::layer, current->layer ());
1010 plist.add (Properties::layering_index, current->layering_index ());
1011 plist.add (Properties::automatic, true);
1012 plist.add (Properties::left_of_split, true);
1014 region = RegionFactory::create (current, plist);
1016 add_region_internal (region, start);
1017 new_regions.push_back (region);
1022 current->suspend_property_changes ();
1023 thawlist.push_back (current);
1024 current->cut_end (pos2 - 1);
1026 } else if (overlap == Evoral::OverlapStart) {
1028 /* split: we need 2 regions: the front and the end.
1029 cut: just trim current to skip the cut area
1034 ---------------*************************------------
1038 ---------------****+++++++++++++++++++++------------
1040 -------------------*********************------------
1046 RegionFactory::region_name (new_name, current->name(), false);
1050 plist.add (Properties::start, current->start());
1051 plist.add (Properties::length, pos3 - pos1);
1052 plist.add (Properties::name, new_name);
1053 plist.add (Properties::layer, current->layer ());
1054 plist.add (Properties::layering_index, current->layering_index ());
1055 plist.add (Properties::automatic, true);
1056 plist.add (Properties::right_of_split, true);
1058 region = RegionFactory::create (current, plist);
1060 add_region_internal (region, pos1);
1061 new_regions.push_back (region);
1066 current->suspend_property_changes ();
1067 thawlist.push_back (current);
1068 current->trim_front (pos3);
1069 } else if (overlap == Evoral::OverlapExternal) {
1071 /* split: no split required.
1072 cut: remove the region.
1077 ---------------*************************------------
1081 ---------------*************************------------
1083 ----------------------------------------------------
1088 remove_region_internal (current);
1091 new_regions.push_back (current);
1095 in_partition = false;
1098 check_crossfades (Evoral::Range<framepos_t> (start, end));
1101 boost::shared_ptr<Playlist>
1102 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1104 boost::shared_ptr<Playlist> ret;
1105 boost::shared_ptr<Playlist> pl;
1108 if (ranges.empty()) {
1109 return boost::shared_ptr<Playlist>();
1112 start = ranges.front().start;
1114 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1116 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1118 if (i == ranges.begin()) {
1122 /* paste the next section into the nascent playlist,
1123 offset to reflect the start of the first range we
1127 ret->paste (pl, (*i).start - start, 1.0f);
1134 boost::shared_ptr<Playlist>
1135 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1137 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1138 return cut_copy (pmf, ranges, result_is_hidden);
1141 boost::shared_ptr<Playlist>
1142 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1144 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1145 return cut_copy (pmf, ranges, result_is_hidden);
1148 boost::shared_ptr<Playlist>
1149 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1151 boost::shared_ptr<Playlist> the_copy;
1152 RegionList thawlist;
1155 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1156 string new_name = _name;
1160 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1161 return boost::shared_ptr<Playlist>();
1164 partition_internal (start, start+cnt-1, true, thawlist);
1166 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1167 (*i)->resume_property_changes();
1173 boost::shared_ptr<Playlist>
1174 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1178 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1179 string new_name = _name;
1183 cnt = min (_get_extent().second - start, cnt);
1184 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1188 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1190 times = fabs (times);
1193 RegionReadLock rl2 (other.get());
1195 int itimes = (int) floor (times);
1196 framepos_t pos = position;
1197 framecnt_t const shift = other->_get_extent().second;
1198 layer_t top = top_layer ();
1201 RegionWriteLock rl1 (this);
1203 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1204 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1206 /* put these new regions on top of all existing ones, but preserve
1207 the ordering they had in the original playlist.
1210 add_region_internal (copy_of_region, (*i)->position() + pos);
1211 set_layer (copy_of_region, copy_of_region->layer() + top);
1223 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1225 times = fabs (times);
1227 RegionWriteLock rl (this);
1228 int itimes = (int) floor (times);
1229 framepos_t pos = position + 1;
1232 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1233 add_region_internal (copy, pos);
1234 set_layer (copy, DBL_MAX);
1235 pos += region->length();
1238 if (floor (times) != times) {
1239 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1241 RegionFactory::region_name (name, region->name(), false);
1246 plist.add (Properties::start, region->start());
1247 plist.add (Properties::length, length);
1248 plist.add (Properties::name, name);
1250 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1251 add_region_internal (sub, pos);
1252 set_layer (sub, DBL_MAX);
1258 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1260 RegionWriteLock rlock (this);
1261 RegionList copy (regions.rlist());
1264 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1266 if ((*r)->last_frame() < at) {
1271 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1272 /* intersected region */
1273 if (!move_intersected) {
1278 /* do not move regions glued to music time - that
1279 has to be done separately.
1282 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1283 fixup.push_back (*r);
1287 (*r)->set_position ((*r)->position() + distance);
1290 /* XXX: may not be necessary; Region::post_set should do this, I think */
1291 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1292 (*r)->recompute_position_from_lock_style ();
1297 Playlist::split (framepos_t at)
1299 RegionWriteLock rlock (this);
1300 RegionList copy (regions.rlist());
1302 /* use a copy since this operation can modify the region list
1305 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1306 _split_region (*r, at);
1311 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1313 RegionWriteLock rl (this);
1314 _split_region (region, playlist_position);
1318 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1320 if (!region->covers (playlist_position)) {
1324 if (region->position() == playlist_position ||
1325 region->last_frame() == playlist_position) {
1329 boost::shared_ptr<Region> left;
1330 boost::shared_ptr<Region> right;
1331 frameoffset_t before;
1332 frameoffset_t after;
1336 /* split doesn't change anything about length, so don't try to splice */
1338 bool old_sp = _splicing;
1341 before = playlist_position - region->position();
1342 after = region->length() - before;
1344 RegionFactory::region_name (before_name, region->name(), false);
1349 plist.add (Properties::position, region->position ());
1350 plist.add (Properties::length, before);
1351 plist.add (Properties::name, before_name);
1352 plist.add (Properties::left_of_split, true);
1353 plist.add (Properties::layering_index, region->layering_index ());
1354 plist.add (Properties::layer, region->layer ());
1356 /* note: we must use the version of ::create with an offset here,
1357 since it supplies that offset to the Region constructor, which
1358 is necessary to get audio region gain envelopes right.
1360 left = RegionFactory::create (region, 0, plist);
1363 RegionFactory::region_name (after_name, region->name(), false);
1368 plist.add (Properties::position, region->position() + before);
1369 plist.add (Properties::length, after);
1370 plist.add (Properties::name, after_name);
1371 plist.add (Properties::right_of_split, true);
1372 plist.add (Properties::layering_index, region->layering_index ());
1373 plist.add (Properties::layer, region->layer ());
1375 /* same note as above */
1376 right = RegionFactory::create (region, before, plist);
1379 add_region_internal (left, region->position());
1380 add_region_internal (right, region->position() + before);
1381 remove_region_internal (region);
1387 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1389 if (_splicing || in_set_state) {
1390 /* don't respond to splicing moves or state setting */
1394 if (_edit_mode == Splice) {
1395 splice_locked (at, distance, exclude);
1400 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1402 if (_splicing || in_set_state) {
1403 /* don't respond to splicing moves or state setting */
1407 if (_edit_mode == Splice) {
1408 splice_unlocked (at, distance, exclude);
1413 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1416 RegionWriteLock rl (this);
1417 core_splice (at, distance, exclude);
1422 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1424 core_splice (at, distance, exclude);
1428 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1432 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1434 if (exclude && (*i) == exclude) {
1438 if ((*i)->position() >= at) {
1439 framepos_t new_pos = (*i)->position() + distance;
1442 } else if (new_pos >= max_framepos - (*i)->length()) {
1443 new_pos = max_framepos - (*i)->length();
1446 (*i)->set_position (new_pos);
1452 notify_contents_changed ();
1456 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1458 if (in_set_state || _splicing || _nudging || _shuffling) {
1462 if (what_changed.contains (Properties::position)) {
1464 /* remove it from the list then add it back in
1465 the right place again.
1468 RegionSortByPosition cmp;
1470 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1472 if (i == regions.end()) {
1473 /* the region bounds are being modified but its not currently
1474 in the region list. we will use its bounds correctly when/if
1481 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1484 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1486 frameoffset_t delta = 0;
1488 if (what_changed.contains (Properties::position)) {
1489 delta = region->position() - region->last_position();
1492 if (what_changed.contains (Properties::length)) {
1493 delta += region->length() - region->last_length();
1497 possibly_splice (region->last_position() + region->last_length(), delta, region);
1500 if (holding_state ()) {
1501 pending_bounds.push_back (region);
1503 notify_contents_changed ();
1505 list<Evoral::Range<framepos_t> > xf;
1506 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1507 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1508 coalesce_and_check_crossfades (xf);
1514 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1516 boost::shared_ptr<Region> region (weak_region.lock());
1522 /* this makes a virtual call to the right kind of playlist ... */
1524 region_changed (what_changed, region);
1528 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1530 PropertyChange our_interests;
1531 PropertyChange bounds;
1532 PropertyChange pos_and_length;
1535 if (in_set_state || in_flush) {
1539 our_interests.add (Properties::muted);
1540 our_interests.add (Properties::layer);
1541 our_interests.add (Properties::opaque);
1543 bounds.add (Properties::start);
1544 bounds.add (Properties::position);
1545 bounds.add (Properties::length);
1547 pos_and_length.add (Properties::position);
1548 pos_and_length.add (Properties::length);
1550 if (what_changed.contains (bounds)) {
1551 region_bounds_changed (what_changed, region);
1552 save = !(_splicing || _nudging);
1555 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1556 check_crossfades (region->range ());
1559 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1560 notify_region_moved (region);
1561 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1562 notify_region_end_trimmed (region);
1563 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1564 notify_region_start_trimmed (region);
1567 /* don't notify about layer changes, since we are the only object that can initiate
1568 them, and we notify in ::relayer()
1571 if (what_changed.contains (our_interests)) {
1579 Playlist::drop_regions ()
1581 RegionWriteLock rl (this);
1583 all_regions.clear ();
1587 Playlist::sync_all_regions_with_regions ()
1589 RegionWriteLock rl (this);
1591 all_regions.clear ();
1593 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1594 all_regions.insert (*i);
1599 Playlist::clear (bool with_signals)
1602 RegionWriteLock rl (this);
1604 region_state_changed_connections.drop_connections ();
1606 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1607 pending_removes.insert (*i);
1612 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1613 remove_dependents (*s);
1619 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1620 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1623 pending_removes.clear ();
1624 pending_contents_change = false;
1630 /***********************************************************************
1632 **********************************************************************/
1634 boost::shared_ptr<RegionList>
1635 Playlist::regions_at (framepos_t frame)
1637 RegionReadLock rlock (this);
1638 return find_regions_at (frame);
1642 Playlist::count_regions_at (framepos_t frame) const
1644 RegionReadLock rlock (const_cast<Playlist*>(this));
1647 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1648 if ((*i)->covers (frame)) {
1656 boost::shared_ptr<Region>
1657 Playlist::top_region_at (framepos_t frame)
1660 RegionReadLock rlock (this);
1661 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1662 boost::shared_ptr<Region> region;
1664 if (rlist->size()) {
1665 RegionSortByLayer cmp;
1667 region = rlist->back();
1673 boost::shared_ptr<Region>
1674 Playlist::top_unmuted_region_at (framepos_t frame)
1677 RegionReadLock rlock (this);
1678 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1680 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1682 RegionList::iterator tmp = i;
1685 if ((*i)->muted()) {
1692 boost::shared_ptr<Region> region;
1694 if (rlist->size()) {
1695 RegionSortByLayer cmp;
1697 region = rlist->back();
1703 boost::shared_ptr<RegionList>
1704 Playlist::find_regions_at (framepos_t frame)
1706 /* Caller must hold lock */
1708 boost::shared_ptr<RegionList> rlist (new RegionList);
1710 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1711 if ((*i)->covers (frame)) {
1712 rlist->push_back (*i);
1719 boost::shared_ptr<RegionList>
1720 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1722 RegionReadLock rlock (this);
1723 boost::shared_ptr<RegionList> rlist (new RegionList);
1725 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1726 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1727 rlist->push_back (*i);
1734 boost::shared_ptr<RegionList>
1735 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1737 RegionReadLock rlock (this);
1738 boost::shared_ptr<RegionList> rlist (new RegionList);
1740 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1741 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1742 rlist->push_back (*i);
1749 /** @param start Range start.
1750 * @param end Range end.
1751 * @return regions which have some part within this range.
1753 boost::shared_ptr<RegionList>
1754 Playlist::regions_touched (framepos_t start, framepos_t end)
1756 RegionReadLock rlock (this);
1757 return regions_touched_locked (start, end);
1760 boost::shared_ptr<RegionList>
1761 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1763 boost::shared_ptr<RegionList> rlist (new RegionList);
1765 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1766 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1767 rlist->push_back (*i);
1775 Playlist::find_next_transient (framepos_t from, int dir)
1777 RegionReadLock rlock (this);
1778 AnalysisFeatureList points;
1779 AnalysisFeatureList these_points;
1781 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1783 if ((*i)->last_frame() < from) {
1787 if ((*i)->first_frame() > from) {
1792 (*i)->get_transients (these_points);
1794 /* add first frame, just, err, because */
1796 these_points.push_back ((*i)->first_frame());
1798 points.insert (points.end(), these_points.begin(), these_points.end());
1799 these_points.clear ();
1802 if (points.empty()) {
1806 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1807 bool reached = false;
1810 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1815 if (reached && (*x) > from) {
1820 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1825 if (reached && (*x) < from) {
1834 boost::shared_ptr<Region>
1835 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1837 RegionReadLock rlock (this);
1838 boost::shared_ptr<Region> ret;
1839 framepos_t closest = max_framepos;
1841 bool end_iter = false;
1843 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1847 frameoffset_t distance;
1848 boost::shared_ptr<Region> r = (*i);
1853 pos = r->first_frame ();
1856 pos = r->last_frame ();
1859 pos = r->sync_position ();
1864 case 1: /* forwards */
1867 if ((distance = pos - frame) < closest) {
1876 default: /* backwards */
1879 if ((distance = frame - pos) < closest) {
1895 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1897 RegionReadLock rlock (this);
1899 framepos_t closest = max_framepos;
1900 framepos_t ret = -1;
1904 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1906 boost::shared_ptr<Region> r = (*i);
1907 frameoffset_t distance;
1909 if (r->first_frame() > frame) {
1911 distance = r->first_frame() - frame;
1913 if (distance < closest) {
1914 ret = r->first_frame();
1919 if (r->last_frame () > frame) {
1921 distance = r->last_frame () - frame;
1923 if (distance < closest) {
1924 ret = r->last_frame ();
1932 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1934 boost::shared_ptr<Region> r = (*i);
1935 frameoffset_t distance;
1937 if (r->last_frame() < frame) {
1939 distance = frame - r->last_frame();
1941 if (distance < closest) {
1942 ret = r->last_frame();
1947 if (r->first_frame() < frame) {
1949 distance = frame - r->first_frame();
1951 if (distance < closest) {
1952 ret = r->first_frame();
1963 /***********************************************************************/
1969 Playlist::mark_session_dirty ()
1971 if (!in_set_state && !holding_state ()) {
1972 _session.set_dirty();
1977 Playlist::rdiff (vector<Command*>& cmds) const
1979 RegionReadLock rlock (const_cast<Playlist *> (this));
1980 Stateful::rdiff (cmds);
1984 Playlist::clear_owned_changes ()
1986 RegionReadLock rlock (this);
1987 Stateful::clear_owned_changes ();
1991 Playlist::update (const RegionListProperty::ChangeRecord& change)
1993 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
1994 name(), change.added.size(), change.removed.size()));
1997 /* add the added regions */
1998 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
1999 add_region_internal ((*i), (*i)->position());
2001 /* remove the removed regions */
2002 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2010 Playlist::set_state (const XMLNode& node, int version)
2014 XMLNodeConstIterator niter;
2015 XMLPropertyList plist;
2016 XMLPropertyConstIterator piter;
2018 boost::shared_ptr<Region> region;
2020 bool seen_region_nodes = false;
2025 if (node.name() != "Playlist") {
2032 plist = node.properties();
2036 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2040 if (prop->name() == X_("name")) {
2041 _name = prop->value();
2043 } else if (prop->name() == X_("orig-diskstream-id")) {
2044 /* XXX legacy session: fix up later */
2045 _orig_track_id = prop->value ();
2046 } else if (prop->name() == X_("orig-track-id")) {
2047 _orig_track_id = prop->value ();
2048 } else if (prop->name() == X_("frozen")) {
2049 _frozen = string_is_affirmative (prop->value());
2050 } else if (prop->name() == X_("combine-ops")) {
2051 _combine_ops = atoi (prop->value());
2057 nlist = node.children();
2059 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2063 if (child->name() == "Region") {
2065 seen_region_nodes = true;
2067 if ((prop = child->property ("id")) == 0) {
2068 error << _("region state node has no ID, ignored") << endmsg;
2072 ID id = prop->value ();
2074 if ((region = region_by_id (id))) {
2076 region->suspend_property_changes ();
2078 if (region->set_state (*child, version)) {
2079 region->resume_property_changes ();
2083 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2084 region->suspend_property_changes ();
2086 error << _("Playlist: cannot create region from XML") << endmsg;
2091 RegionWriteLock rlock (this);
2092 add_region_internal (region, region->position());
2095 region->resume_property_changes ();
2100 if (seen_region_nodes && regions.empty()) {
2104 /* update dependents, which was not done during add_region_internal
2105 due to in_set_state being true
2108 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2109 check_crossfades ((*r)->range ());
2114 notify_contents_changed ();
2117 first_set_state = false;
2123 Playlist::get_state()
2125 return state (true);
2129 Playlist::get_template()
2131 return state (false);
2134 /** @param full_state true to include regions in the returned state, otherwise false.
2137 Playlist::state (bool full_state)
2139 XMLNode *node = new XMLNode (X_("Playlist"));
2142 node->add_property (X_("id"), id().to_s());
2143 node->add_property (X_("name"), _name);
2144 node->add_property (X_("type"), _type.to_string());
2146 _orig_track_id.print (buf, sizeof (buf));
2147 node->add_property (X_("orig-track-id"), buf);
2148 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2151 RegionReadLock rlock (this);
2153 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2154 node->add_property ("combine-ops", buf);
2156 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2157 node->add_child_nocopy ((*i)->get_state());
2162 node->add_child_copy (*_extra_xml);
2169 Playlist::empty() const
2171 RegionReadLock rlock (const_cast<Playlist *>(this));
2172 return regions.empty();
2176 Playlist::n_regions() const
2178 RegionReadLock rlock (const_cast<Playlist *>(this));
2179 return regions.size();
2182 pair<framepos_t, framepos_t>
2183 Playlist::get_extent () const
2185 RegionReadLock rlock (const_cast<Playlist *>(this));
2186 return _get_extent ();
2189 pair<framepos_t, framepos_t>
2190 Playlist::_get_extent () const
2192 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2194 if (regions.empty()) {
2199 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2200 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2201 if (e.first < ext.first) {
2202 ext.first = e.first;
2204 if (e.second > ext.second) {
2205 ext.second = e.second;
2213 Playlist::bump_name (string name, Session &session)
2215 string newname = name;
2218 newname = bump_name_once (newname, '.');
2219 } while (session.playlists->by_name (newname)!=NULL);
2226 Playlist::top_layer() const
2228 RegionReadLock rlock (const_cast<Playlist *> (this));
2231 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2232 top = max (top, (*i)->layer());
2238 Playlist::set_edit_mode (EditMode mode)
2243 struct RelayerSort {
2244 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2245 return a->layering_index() < b->layering_index();
2249 /** Set a new layer for a region. This adjusts the layering indices of all
2250 * regions in the playlist to put the specified region in the appropriate
2251 * place. The actual layering will be fixed up when relayer() happens.
2255 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2257 /* Remove the layer we are setting from our region list, and sort it */
2258 RegionList copy = regions.rlist();
2259 copy.remove (region);
2260 copy.sort (RelayerSort ());
2262 /* Put region back in the right place */
2263 RegionList::iterator i = copy.begin();
2264 while (i != copy.end ()) {
2265 if ((*i)->layer() > new_layer) {
2271 copy.insert (i, region);
2273 setup_layering_indices (copy);
2277 Playlist::setup_layering_indices (RegionList const & regions)
2280 list<Evoral::Range<framepos_t> > xf;
2282 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2283 (*k)->set_layering_index (j++);
2287 /** Take the layering indices of each of our regions, compute the layers
2288 * that they should be on, and write the layers back to the regions.
2291 Playlist::relayer ()
2293 /* never compute layers when setting from XML */
2299 /* Build up a new list of regions on each layer, stored in a set of lists
2300 each of which represent some period of time on some layer. The idea
2301 is to avoid having to search the entire region list to establish whether
2302 each region overlaps another */
2304 /* how many pieces to divide this playlist's time up into */
2305 int const divisions = 512;
2307 /* find the start and end positions of the regions on this playlist */
2308 framepos_t start = INT64_MAX;
2310 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2311 start = min (start, (*i)->position());
2312 end = max (end, (*i)->position() + (*i)->length());
2315 /* hence the size of each time division */
2316 double const division_size = (end - start) / double (divisions);
2318 vector<vector<RegionList> > layers;
2319 layers.push_back (vector<RegionList> (divisions));
2321 /* Sort our regions into layering index order */
2322 RegionList copy = regions.rlist();
2323 copy.sort (RelayerSort ());
2325 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2326 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2327 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2330 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2332 /* find the time divisions that this region covers; if there are no regions on the list,
2333 division_size will equal 0 and in this case we'll just say that
2334 start_division = end_division = 0.
2336 int start_division = 0;
2337 int end_division = 0;
2339 if (division_size > 0) {
2340 start_division = floor ( ((*i)->position() - start) / division_size);
2341 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2342 if (end_division == divisions) {
2347 assert (divisions == 0 || end_division < divisions);
2349 /* find the lowest layer that this region can go on */
2350 size_t j = layers.size();
2352 /* try layer j - 1; it can go on if it overlaps no other region
2353 that is already on that layer
2356 bool overlap = false;
2357 for (int k = start_division; k <= end_division; ++k) {
2358 RegionList::iterator l = layers[j-1][k].begin ();
2359 while (l != layers[j-1][k].end()) {
2360 if ((*l)->overlap_equivalent (*i)) {
2373 /* overlap, so we must use layer j */
2380 if (j == layers.size()) {
2381 /* we need a new layer for this region */
2382 layers.push_back (vector<RegionList> (divisions));
2385 /* put a reference to this region in each of the divisions that it exists in */
2386 for (int k = start_division; k <= end_division; ++k) {
2387 layers[j][k].push_back (*i);
2390 (*i)->set_layer (j);
2393 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2394 relayering because we just removed the only region on the top layer, nothing will
2395 appear to have changed, but the StreamView must still sort itself out. We could
2396 probably keep a note of the top layer last time we relayered, and check that,
2397 but premature optimisation &c...
2399 notify_layering_changed ();
2401 /* This relayer() may have been called as a result of a region removal, in which
2402 case we need to setup layering indices to account for the one that has just
2405 setup_layering_indices (copy);
2409 Playlist::raise_region (boost::shared_ptr<Region> region)
2411 set_layer (region, region->layer() + 1.5);
2413 check_crossfades (region->range ());
2417 Playlist::lower_region (boost::shared_ptr<Region> region)
2419 set_layer (region, region->layer() - 1.5);
2421 check_crossfades (region->range ());
2425 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2427 set_layer (region, DBL_MAX);
2429 check_crossfades (region->range ());
2433 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2435 set_layer (region, -0.5);
2437 check_crossfades (region->range ());
2441 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2443 RegionList::iterator i;
2449 RegionWriteLock rlock (const_cast<Playlist *> (this));
2451 for (i = regions.begin(); i != regions.end(); ++i) {
2453 if ((*i)->position() >= start) {
2459 if ((*i)->last_frame() > max_framepos - distance) {
2460 new_pos = max_framepos - (*i)->length();
2462 new_pos = (*i)->position() + distance;
2467 if ((*i)->position() > distance) {
2468 new_pos = (*i)->position() - distance;
2474 (*i)->set_position (new_pos);
2482 notify_contents_changed ();
2488 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2490 RegionReadLock rlock (const_cast<Playlist*> (this));
2492 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2493 if ((*r)->uses_source (src)) {
2501 boost::shared_ptr<Region>
2502 Playlist::find_region (const ID& id) const
2504 RegionReadLock rlock (const_cast<Playlist*> (this));
2506 /* searches all regions currently in use by the playlist */
2508 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2509 if ((*i)->id() == id) {
2514 return boost::shared_ptr<Region> ();
2518 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2520 RegionReadLock rlock (const_cast<Playlist*> (this));
2523 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2532 boost::shared_ptr<Region>
2533 Playlist::region_by_id (const ID& id) const
2535 /* searches all regions ever added to this playlist */
2537 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2538 if ((*i)->id() == id) {
2542 return boost::shared_ptr<Region> ();
2546 Playlist::dump () const
2548 boost::shared_ptr<Region> r;
2550 cerr << "Playlist \"" << _name << "\" " << endl
2551 << regions.size() << " regions "
2554 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2556 cerr << " " << r->name() << " ["
2557 << r->start() << "+" << r->length()
2567 Playlist::set_frozen (bool yn)
2573 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2577 if (region->locked()) {
2584 RegionWriteLock rlock (const_cast<Playlist*> (this));
2589 RegionList::iterator next;
2591 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2592 if ((*i) == region) {
2596 if (next != regions.end()) {
2598 if ((*next)->locked()) {
2604 if ((*next)->position() != region->last_frame() + 1) {
2605 /* they didn't used to touch, so after shuffle,
2606 just have them swap positions.
2608 new_pos = (*next)->position();
2610 /* they used to touch, so after shuffle,
2611 make sure they still do. put the earlier
2612 region where the later one will end after
2615 new_pos = region->position() + (*next)->length();
2618 (*next)->set_position (region->position());
2619 region->set_position (new_pos);
2621 /* avoid a full sort */
2623 regions.erase (i); // removes the region from the list */
2625 regions.insert (next, region); // adds it back after next
2634 RegionList::iterator prev = regions.end();
2636 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2637 if ((*i) == region) {
2639 if (prev != regions.end()) {
2641 if ((*prev)->locked()) {
2646 if (region->position() != (*prev)->last_frame() + 1) {
2647 /* they didn't used to touch, so after shuffle,
2648 just have them swap positions.
2650 new_pos = region->position();
2652 /* they used to touch, so after shuffle,
2653 make sure they still do. put the earlier
2654 one where the later one will end after
2656 new_pos = (*prev)->position() + region->length();
2659 region->set_position ((*prev)->position());
2660 (*prev)->set_position (new_pos);
2662 /* avoid a full sort */
2664 regions.erase (i); // remove region
2665 regions.insert (prev, region); // insert region before prev
2681 notify_contents_changed();
2687 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2689 RegionReadLock rlock (const_cast<Playlist*> (this));
2691 if (regions.size() > 1) {
2699 Playlist::update_after_tempo_map_change ()
2701 RegionWriteLock rlock (const_cast<Playlist*> (this));
2702 RegionList copy (regions.rlist());
2706 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2707 (*i)->update_after_tempo_map_change ();
2714 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2716 RegionWriteLock rl (this, false);
2717 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2723 Playlist::has_region_at (framepos_t const p) const
2725 RegionReadLock (const_cast<Playlist *> (this));
2727 RegionList::const_iterator i = regions.begin ();
2728 while (i != regions.end() && !(*i)->covers (p)) {
2732 return (i != regions.end());
2735 /** Remove any region that uses a given source */
2737 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2739 RegionWriteLock rl (this);
2741 RegionList::iterator i = regions.begin();
2742 while (i != regions.end()) {
2743 RegionList::iterator j = i;
2746 if ((*i)->uses_source (s)) {
2747 remove_region_internal (*i);
2754 /** Look from a session frame time and find the start time of the next region
2755 * which is on the top layer of this playlist.
2756 * @param t Time to look from.
2757 * @return Position of next top-layered region, or max_framepos if there isn't one.
2760 Playlist::find_next_top_layer_position (framepos_t t) const
2762 RegionReadLock rlock (const_cast<Playlist *> (this));
2764 layer_t const top = top_layer ();
2766 RegionList copy = regions.rlist ();
2767 copy.sort (RegionSortByPosition ());
2769 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2770 if ((*i)->position() >= t && (*i)->layer() == top) {
2771 return (*i)->position();
2775 return max_framepos;
2778 boost::shared_ptr<Region>
2779 Playlist::combine (const RegionList& r)
2782 uint32_t channels = 0;
2784 framepos_t earliest_position = max_framepos;
2785 vector<TwoRegions> old_and_new_regions;
2786 vector<boost::shared_ptr<Region> > originals;
2787 vector<boost::shared_ptr<Region> > copies;
2790 uint32_t max_level = 0;
2792 /* find the maximum depth of all the regions we're combining */
2794 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2795 max_level = max (max_level, (*i)->max_source_level());
2798 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2799 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2801 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2803 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2804 earliest_position = min (earliest_position, (*i)->position());
2807 /* enable this so that we do not try to create xfades etc. as we add
2811 pl->in_partition = true;
2813 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2815 /* copy the region */
2817 boost::shared_ptr<Region> original_region = (*i);
2818 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2820 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2821 originals.push_back (original_region);
2822 copies.push_back (copied_region);
2824 RegionFactory::add_compound_association (original_region, copied_region);
2826 /* make position relative to zero */
2828 pl->add_region (copied_region, original_region->position() - earliest_position);
2830 /* use the maximum number of channels for any region */
2832 channels = max (channels, original_region->n_channels());
2834 /* it will go above the layer of the highest existing region */
2836 layer = max (layer, original_region->layer());
2839 pl->in_partition = false;
2841 pre_combine (copies);
2843 /* now create a new PlaylistSource for each channel in the new playlist */
2846 pair<framepos_t,framepos_t> extent = pl->get_extent();
2848 for (uint32_t chn = 0; chn < channels; ++chn) {
2849 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2853 /* now a new whole-file region using the list of sources */
2855 plist.add (Properties::start, 0);
2856 plist.add (Properties::length, extent.second);
2857 plist.add (Properties::name, parent_name);
2858 plist.add (Properties::whole_file, true);
2860 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2862 /* now the non-whole-file region that we will actually use in the
2867 plist.add (Properties::start, 0);
2868 plist.add (Properties::length, extent.second);
2869 plist.add (Properties::name, child_name);
2870 plist.add (Properties::layer, layer+1);
2872 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2874 /* remove all the selected regions from the current playlist
2879 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2883 /* do type-specific stuff with the originals and the new compound
2887 post_combine (originals, compound_region);
2889 /* add the new region at the right location */
2891 add_region (compound_region, earliest_position);
2897 return compound_region;
2901 Playlist::uncombine (boost::shared_ptr<Region> target)
2903 boost::shared_ptr<PlaylistSource> pls;
2904 boost::shared_ptr<const Playlist> pl;
2905 vector<boost::shared_ptr<Region> > originals;
2906 vector<TwoRegions> old_and_new_regions;
2908 // (1) check that its really a compound region
2910 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2914 pl = pls->playlist();
2916 framepos_t adjusted_start = 0; // gcc isn't smart enough
2917 framepos_t adjusted_end = 0; // gcc isn't smart enough
2919 /* the leftmost (earliest) edge of the compound region
2920 starts at zero in its source, or larger if it
2921 has been trimmed or content-scrolled.
2923 the rightmost (latest) edge of the compound region
2924 relative to its source is the starting point plus
2925 the length of the region.
2928 // (2) get all the original regions
2930 const RegionList& rl (pl->region_list().rlist());
2931 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2932 frameoffset_t move_offset = 0;
2934 /* there are two possibilities here:
2935 1) the playlist that the playlist source was based on
2936 is us, so just add the originals (which belonged to
2937 us anyway) back in the right place.
2939 2) the playlist that the playlist source was based on
2940 is NOT us, so we need to make copies of each of
2941 the original regions that we find, and add them
2944 bool same_playlist = (pls->original() == id());
2946 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2948 boost::shared_ptr<Region> current (*i);
2950 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2952 if (ca == cassocs.end()) {
2956 boost::shared_ptr<Region> original (ca->second);
2957 bool modified_region;
2959 if (i == rl.begin()) {
2960 move_offset = (target->position() - original->position()) - target->start();
2961 adjusted_start = original->position() + target->start();
2962 adjusted_end = adjusted_start + target->length();
2965 if (!same_playlist) {
2966 framepos_t pos = original->position();
2967 /* make a copy, but don't announce it */
2968 original = RegionFactory::create (original, false);
2969 /* the pure copy constructor resets position() to zero,
2972 original->set_position (pos);
2975 /* check to see how the original region (in the
2976 * playlist before compounding occured) overlaps
2977 * with the new state of the compound region.
2980 original->clear_changes ();
2981 modified_region = false;
2983 switch (original->coverage (adjusted_start, adjusted_end)) {
2984 case Evoral::OverlapNone:
2985 /* original region does not cover any part
2986 of the current state of the compound region
2990 case Evoral::OverlapInternal:
2991 /* overlap is just a small piece inside the
2992 * original so trim both ends
2994 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
2995 modified_region = true;
2998 case Evoral::OverlapExternal:
2999 /* overlap fully covers original, so leave it
3004 case Evoral::OverlapEnd:
3005 /* overlap starts within but covers end,
3006 so trim the front of the region
3008 original->trim_front (adjusted_start);
3009 modified_region = true;
3012 case Evoral::OverlapStart:
3013 /* overlap covers start but ends within, so
3014 * trim the end of the region.
3016 original->trim_end (adjusted_end);
3017 modified_region = true;
3022 /* fix the position to match any movement of the compound region.
3024 original->set_position (original->position() + move_offset);
3025 modified_region = true;
3028 if (modified_region) {
3029 _session.add_command (new StatefulDiffCommand (original));
3032 /* and add to the list of regions waiting to be
3036 originals.push_back (original);
3037 old_and_new_regions.push_back (TwoRegions (*i, original));
3040 pre_uncombine (originals, target);
3042 in_partition = true;
3045 // (3) remove the compound region
3047 remove_region (target);
3049 // (4) add the constituent regions
3051 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3052 add_region ((*i), (*i)->position());
3055 in_partition = false;
3060 Playlist::max_source_level () const
3062 RegionReadLock rlock (const_cast<Playlist *> (this));
3065 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3066 lvl = max (lvl, (*i)->max_source_level());
3073 Playlist::set_orig_track_id (const PBD::ID& id)
3075 _orig_track_id = id;
3078 /** Take a list of ranges, coalesce any that can be coalesced, then call
3079 * check_crossfades for each one.
3082 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3084 /* XXX: it's a shame that this coalesce algorithm also exists in
3085 TimeSelection::consolidate().
3088 /* XXX: xfade: this is implemented in Evoral::RangeList */
3091 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3092 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3098 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3099 i->from = min (i->from, j->from);
3100 i->to = max (i->to, j->to);
3107 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3108 check_crossfades (*i);