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 XMLProperty const * 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 _rippling = other->_rippling;
176 _nudging = other->_nudging;
177 _edit_mode = other->_edit_mode;
180 first_set_state = false;
182 in_partition = false;
184 _frozen = other->_frozen;
187 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
188 : SessionObject(other->_session, str)
190 , _type(other->_type)
191 , _orig_track_id (other->_orig_track_id)
193 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
195 framepos_t end = start + cnt - 1;
201 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
203 boost::shared_ptr<Region> region;
204 boost::shared_ptr<Region> new_region;
205 frameoffset_t offset = 0;
206 framepos_t position = 0;
209 Evoral::OverlapType overlap;
213 overlap = region->coverage (start, end);
216 case Evoral::OverlapNone:
219 case Evoral::OverlapInternal:
220 offset = start - region->position();
225 case Evoral::OverlapStart:
227 position = region->position() - start;
228 len = end - region->position();
231 case Evoral::OverlapEnd:
232 offset = start - region->position();
234 len = region->length() - offset;
237 case Evoral::OverlapExternal:
239 position = region->position() - start;
240 len = region->length();
244 RegionFactory::region_name (new_name, region->name(), false);
248 plist.add (Properties::start, region->start() + offset);
249 plist.add (Properties::length, len);
250 plist.add (Properties::name, new_name);
251 plist.add (Properties::layer, region->layer());
252 plist.add (Properties::layering_index, region->layering_index());
254 new_region = RegionFactory::create (region, plist);
256 add_region_internal (new_region, position);
259 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
260 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
261 _end_space = cnt - (get_extent().second - get_extent().first);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionReadLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (RegionFactory::create (*i, true));
297 Playlist::init (bool hide)
299 add_property (regions);
300 _xml_node_name = X_("Playlist");
302 g_atomic_int_set (&block_notifications, 0);
303 g_atomic_int_set (&ignore_state_changes, 0);
304 pending_contents_change = false;
305 pending_layering = false;
306 first_set_state = true;
315 _edit_mode = Config->get_edit_mode();
317 in_partition = false;
320 _capture_insertion_underway = false;
324 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
325 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
327 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
330 Playlist::~Playlist ()
332 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
335 RegionReadLock rl (this);
337 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
338 (*i)->set_playlist (boost::shared_ptr<Playlist>());
342 /* GoingAway must be emitted by derived classes */
346 Playlist::_set_sort_id ()
349 Playlists are given names like <track name>.<id>
350 or <track name>.<edit group name>.<id> where id
351 is an integer. We extract the id and sort by that.
354 size_t dot_position = _name.val().find_last_of(".");
356 if (dot_position == string::npos) {
359 string t = _name.val().substr(dot_position + 1);
362 _sort_id = boost::lexical_cast<int>(t);
365 catch (boost::bad_lexical_cast e) {
372 Playlist::set_name (const string& str)
374 /* in a typical situation, a playlist is being used
375 by one diskstream and also is referenced by the
376 Session. if there are more references than that,
377 then don't change the name.
384 bool ret = SessionObject::set_name(str);
391 /***********************************************************************
392 CHANGE NOTIFICATION HANDLING
394 Notifications must be delayed till the region_lock is released. This
395 is necessary because handlers for the signals may need to acquire
396 the lock (e.g. to read from the playlist).
397 ***********************************************************************/
400 Playlist::begin_undo ()
407 Playlist::end_undo ()
416 delay_notifications ();
417 g_atomic_int_inc (&ignore_state_changes);
420 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
422 Playlist::thaw (bool from_undo)
424 g_atomic_int_dec_and_test (&ignore_state_changes);
425 release_notifications (from_undo);
430 Playlist::delay_notifications ()
432 g_atomic_int_inc (&block_notifications);
435 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
437 Playlist::release_notifications (bool from_undo)
439 if (g_atomic_int_dec_and_test (&block_notifications)) {
440 flush_notifications (from_undo);
445 Playlist::notify_contents_changed ()
447 if (holding_state ()) {
448 pending_contents_change = true;
450 pending_contents_change = false;
451 ContentsChanged(); /* EMIT SIGNAL */
456 Playlist::notify_layering_changed ()
458 if (holding_state ()) {
459 pending_layering = true;
461 pending_layering = false;
462 LayeringChanged(); /* EMIT SIGNAL */
467 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
469 if (holding_state ()) {
470 pending_removes.insert (r);
471 pending_contents_change = true;
473 /* this might not be true, but we have to act
474 as though it could be.
476 pending_contents_change = false;
477 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
478 ContentsChanged (); /* EMIT SIGNAL */
483 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
485 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
487 if (holding_state ()) {
489 pending_range_moves.push_back (move);
493 list< Evoral::RangeMove<framepos_t> > m;
495 RangesMoved (m, false);
501 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
503 if (r->position() >= r->last_position()) {
504 /* trimmed shorter */
508 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
510 if (holding_state ()) {
512 pending_region_extensions.push_back (extra);
516 list<Evoral::Range<framepos_t> > r;
524 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
526 if (r->length() < r->last_length()) {
527 /* trimmed shorter */
530 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
532 if (holding_state ()) {
534 pending_region_extensions.push_back (extra);
538 list<Evoral::Range<framepos_t> > r;
546 Playlist::notify_region_added (boost::shared_ptr<Region> r)
548 /* the length change might not be true, but we have to act
549 as though it could be.
552 if (holding_state()) {
553 pending_adds.insert (r);
554 pending_contents_change = true;
557 pending_contents_change = false;
558 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
559 ContentsChanged (); /* EMIT SIGNAL */
564 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
566 Playlist::flush_notifications (bool from_undo)
568 set<boost::shared_ptr<Region> >::iterator s;
569 bool regions_changed = false;
577 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
578 regions_changed = true;
581 /* XXX: it'd be nice if we could use pending_bounds for
582 RegionsExtended and RegionsMoved.
585 /* we have no idea what order the regions ended up in pending
586 bounds (it could be based on selection order, for example).
587 so, to preserve layering in the "most recently moved is higher"
588 model, sort them by existing layer, then timestamp them.
591 // RegionSortByLayer cmp;
592 // pending_bounds.sort (cmp);
594 list<Evoral::Range<framepos_t> > crossfade_ranges;
596 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
597 crossfade_ranges.push_back ((*r)->last_range ());
598 crossfade_ranges.push_back ((*r)->range ());
601 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
602 crossfade_ranges.push_back ((*s)->range ());
603 remove_dependents (*s);
604 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
607 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
608 crossfade_ranges.push_back ((*s)->range ());
609 /* don't emit RegionAdded signal until relayering is done,
610 so that the region is fully setup by the time
611 anyone hears that its been added
615 /* notify about contents/region changes first so that layering changes
616 * in a UI will take place on the new contents.
619 if (regions_changed || pending_contents_change) {
620 pending_layering = true;
621 ContentsChanged (); /* EMIT SIGNAL */
624 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
625 (*s)->clear_changes ();
626 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
629 if ((regions_changed && !in_set_state) || pending_layering) {
633 coalesce_and_check_crossfades (crossfade_ranges);
635 if (!pending_range_moves.empty ()) {
636 /* We don't need to check crossfades for these as pending_bounds has
639 RangesMoved (pending_range_moves, from_undo);
642 if (!pending_region_extensions.empty ()) {
643 RegionsExtended (pending_region_extensions);
652 Playlist::clear_pending ()
654 pending_adds.clear ();
655 pending_removes.clear ();
656 pending_bounds.clear ();
657 pending_range_moves.clear ();
658 pending_region_extensions.clear ();
659 pending_contents_change = false;
660 pending_layering = false;
663 /*************************************************************
665 *************************************************************/
667 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
669 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, const int32_t sub_num)
671 RegionWriteLock rlock (this);
672 times = fabs (times);
674 int itimes = (int) floor (times);
676 framepos_t pos = position;
678 if (times == 1 && auto_partition){
680 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
681 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
682 (*i)->resume_property_changes ();
683 _session.add_command (new StatefulDiffCommand (*i));
688 add_region_internal (region, pos, sub_num);
689 set_layer (region, DBL_MAX);
690 pos += region->length();
695 /* note that itimes can be zero if we being asked to just
696 insert a single fraction of the region.
699 for (int i = 0; i < itimes; ++i) {
700 boost::shared_ptr<Region> copy = RegionFactory::create (region, true, sub_num);
701 add_region_internal (copy, pos, sub_num);
702 set_layer (copy, DBL_MAX);
703 pos += region->length();
706 framecnt_t length = 0;
708 if (floor (times) != times) {
709 length = (framecnt_t) floor (region->length() * (times - floor (times)));
711 RegionFactory::region_name (name, region->name(), false);
716 plist.add (Properties::start, region->start());
717 plist.add (Properties::length, length);
718 plist.add (Properties::name, name);
719 plist.add (Properties::layer, region->layer());
721 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
722 add_region_internal (sub, pos, sub_num);
723 set_layer (sub, DBL_MAX);
727 possibly_splice_unlocked (position, (pos + length) - position, region);
731 Playlist::set_region_ownership ()
733 RegionWriteLock rl (this);
734 RegionList::iterator i;
735 boost::weak_ptr<Playlist> pl (shared_from_this());
737 for (i = regions.begin(); i != regions.end(); ++i) {
738 (*i)->set_playlist (pl);
743 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, const int32_t sub_num)
745 if (region->data_type() != _type) {
749 RegionSortByPosition cmp;
751 if (!first_set_state) {
752 boost::shared_ptr<Playlist> foo (shared_from_this());
753 region->set_playlist (boost::weak_ptr<Playlist>(foo));
756 region->set_position (position, sub_num);
758 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
759 all_regions.insert (region);
761 possibly_splice_unlocked (position, region->length(), region);
763 if (!holding_state ()) {
764 /* layers get assigned from XML state, and are not reset during undo/redo */
768 /* we need to notify the existence of new region before checking dependents. Ick. */
770 notify_region_added (region);
772 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
773 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
779 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
781 RegionWriteLock rlock (this);
783 bool old_sp = _splicing;
786 remove_region_internal (old);
787 add_region_internal (newr, pos);
788 set_layer (newr, old->layer ());
792 possibly_splice_unlocked (pos, old->length() - newr->length());
796 Playlist::remove_region (boost::shared_ptr<Region> region)
798 RegionWriteLock rlock (this);
799 remove_region_internal (region);
803 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
805 RegionList::iterator i;
809 region->set_playlist (boost::weak_ptr<Playlist>());
812 /* XXX should probably freeze here .... */
814 for (i = regions.begin(); i != regions.end(); ++i) {
817 framepos_t pos = (*i)->position();
818 framecnt_t distance = (*i)->length();
822 possibly_splice_unlocked (pos, -distance);
824 if (!holding_state ()) {
826 remove_dependents (region);
829 notify_region_removed (region);
838 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
840 if (Config->get_use_overlap_equivalency()) {
841 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
842 if ((*i)->overlap_equivalent (other)) {
843 results.push_back (*i);
847 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
848 if ((*i)->equivalent (other)) {
849 results.push_back (*i);
856 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
858 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
860 if ((*i) && (*i)->region_list_equivalent (other)) {
861 results.push_back (*i);
867 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
869 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
871 if ((*i) && (*i)->any_source_equivalent (other)) {
872 results.push_back (*i);
878 Playlist::partition (framepos_t start, framepos_t end, bool cut)
882 RegionWriteLock lock(this);
883 partition_internal (start, end, cut, thawlist);
886 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
887 (*i)->resume_property_changes ();
891 /** Go through each region on the playlist and cut them at start and end, removing the section between
892 * start and end if cutting == true. Regions that lie entirely within start and end are always
897 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
899 RegionList new_regions;
903 boost::shared_ptr<Region> region;
904 boost::shared_ptr<Region> current;
906 RegionList::iterator tmp;
907 Evoral::OverlapType overlap;
908 framepos_t pos1, pos2, pos3, pos4;
912 /* need to work from a copy, because otherwise the regions we add during the process
913 get operated on as well.
916 RegionList copy = regions.rlist();
918 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
925 if (current->first_frame() >= start && current->last_frame() < end) {
928 remove_region_internal (current);
934 /* coverage will return OverlapStart if the start coincides
935 with the end point. we do not partition such a region,
936 so catch this special case.
939 if (current->first_frame() >= end) {
943 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
947 pos1 = current->position();
950 pos4 = current->last_frame();
952 if (overlap == Evoral::OverlapInternal) {
953 /* split: we need 3 new regions, the front, middle and end.
954 cut: we need 2 regions, the front and end.
959 ---------------*************************------------
962 ---------------*****++++++++++++++++====------------
964 ---------------*****----------------====------------
969 /* "middle" ++++++ */
971 RegionFactory::region_name (new_name, current->name(), false);
975 plist.add (Properties::start, current->start() + (pos2 - pos1));
976 plist.add (Properties::length, pos3 - pos2);
977 plist.add (Properties::name, new_name);
978 plist.add (Properties::layer, current->layer ());
979 plist.add (Properties::layering_index, current->layering_index ());
980 plist.add (Properties::automatic, true);
981 plist.add (Properties::left_of_split, true);
982 plist.add (Properties::right_of_split, true);
984 region = RegionFactory::create (current, plist);
985 add_region_internal (region, start);
986 new_regions.push_back (region);
991 RegionFactory::region_name (new_name, current->name(), false);
995 plist.add (Properties::start, current->start() + (pos3 - pos1));
996 plist.add (Properties::length, pos4 - pos3);
997 plist.add (Properties::name, new_name);
998 plist.add (Properties::layer, current->layer ());
999 plist.add (Properties::layering_index, current->layering_index ());
1000 plist.add (Properties::automatic, true);
1001 plist.add (Properties::right_of_split, true);
1003 region = RegionFactory::create (current, plist);
1005 add_region_internal (region, end);
1006 new_regions.push_back (region);
1010 current->clear_changes ();
1011 current->suspend_property_changes ();
1012 thawlist.push_back (current);
1013 current->cut_end (pos2 - 1);
1015 } else if (overlap == Evoral::OverlapEnd) {
1019 ---------------*************************------------
1022 ---------------**************+++++++++++------------
1024 ---------------**************-----------------------
1031 RegionFactory::region_name (new_name, current->name(), false);
1035 plist.add (Properties::start, current->start() + (pos2 - pos1));
1036 plist.add (Properties::length, pos4 - pos2);
1037 plist.add (Properties::name, new_name);
1038 plist.add (Properties::layer, current->layer ());
1039 plist.add (Properties::layering_index, current->layering_index ());
1040 plist.add (Properties::automatic, true);
1041 plist.add (Properties::left_of_split, true);
1043 region = RegionFactory::create (current, plist);
1045 add_region_internal (region, start);
1046 new_regions.push_back (region);
1051 current->clear_changes ();
1052 current->suspend_property_changes ();
1053 thawlist.push_back (current);
1054 current->cut_end (pos2 - 1);
1056 } else if (overlap == Evoral::OverlapStart) {
1058 /* split: we need 2 regions: the front and the end.
1059 cut: just trim current to skip the cut area
1064 ---------------*************************------------
1068 ---------------****+++++++++++++++++++++------------
1070 -------------------*********************------------
1076 RegionFactory::region_name (new_name, current->name(), false);
1080 plist.add (Properties::start, current->start());
1081 plist.add (Properties::length, pos3 - pos1);
1082 plist.add (Properties::name, new_name);
1083 plist.add (Properties::layer, current->layer ());
1084 plist.add (Properties::layering_index, current->layering_index ());
1085 plist.add (Properties::automatic, true);
1086 plist.add (Properties::right_of_split, true);
1088 region = RegionFactory::create (current, plist);
1090 add_region_internal (region, pos1);
1091 new_regions.push_back (region);
1096 current->clear_changes ();
1097 current->suspend_property_changes ();
1098 thawlist.push_back (current);
1099 current->trim_front (pos3);
1100 } else if (overlap == Evoral::OverlapExternal) {
1102 /* split: no split required.
1103 cut: remove the region.
1108 ---------------*************************------------
1112 ---------------*************************------------
1114 ----------------------------------------------------
1119 remove_region_internal (current);
1122 new_regions.push_back (current);
1126 in_partition = false;
1129 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1130 framepos_t wanted_length = end-start;
1131 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1134 boost::shared_ptr<Playlist>
1135 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1137 boost::shared_ptr<Playlist> ret;
1138 boost::shared_ptr<Playlist> pl;
1141 if (ranges.empty()) {
1142 return boost::shared_ptr<Playlist>();
1145 start = ranges.front().start;
1147 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1149 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1151 if (i == ranges.begin()) {
1155 /* paste the next section into the nascent playlist,
1156 offset to reflect the start of the first range we
1160 ret->paste (pl, (*i).start - start, 1.0f, 0);
1167 boost::shared_ptr<Playlist>
1168 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1170 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1171 return cut_copy (pmf, ranges, result_is_hidden);
1174 boost::shared_ptr<Playlist>
1175 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1177 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1178 return cut_copy (pmf, ranges, result_is_hidden);
1181 boost::shared_ptr<Playlist>
1182 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1184 boost::shared_ptr<Playlist> the_copy;
1185 RegionList thawlist;
1188 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1189 string new_name = _name;
1193 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1194 return boost::shared_ptr<Playlist>();
1198 RegionWriteLock rlock (this);
1199 partition_internal (start, start+cnt-1, true, thawlist);
1202 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1203 (*i)->resume_property_changes();
1209 boost::shared_ptr<Playlist>
1210 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1214 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1215 string new_name = _name;
1219 // cnt = min (_get_extent().second - start, cnt); (We need the full range length when copy/pasting in Ripple. Why was this limit here? It's not in CUT... )
1221 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1225 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1227 times = fabs (times);
1230 RegionReadLock rl2 (other.get());
1232 int itimes = (int) floor (times);
1233 framepos_t pos = position;
1234 framecnt_t const shift = other->_get_extent().second;
1235 layer_t top = top_layer ();
1238 RegionWriteLock rl1 (this);
1240 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1241 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1243 /* put these new regions on top of all existing ones, but preserve
1244 the ordering they had in the original playlist.
1247 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1248 set_layer (copy_of_region, copy_of_region->layer() + top);
1260 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1262 duplicate(region, position, region->length(), times);
1265 /** @param gap from the beginning of the region to the next beginning */
1267 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1269 times = fabs (times);
1271 RegionWriteLock rl (this);
1272 int itimes = (int) floor (times);
1275 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1276 add_region_internal (copy, position);
1277 set_layer (copy, DBL_MAX);
1281 if (floor (times) != times) {
1282 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1284 RegionFactory::region_name (name, region->name(), false);
1289 plist.add (Properties::start, region->start());
1290 plist.add (Properties::length, length);
1291 plist.add (Properties::name, name);
1293 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1294 add_region_internal (sub, position);
1295 set_layer (sub, DBL_MAX);
1300 /** @param gap from the beginning of the region to the next beginning */
1301 /** @param end the first frame that does _not_ contain a duplicated frame */
1303 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1305 RegionWriteLock rl (this);
1307 while (position + region->length() - 1 < end) {
1308 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1309 add_region_internal (copy, position);
1310 set_layer (copy, DBL_MAX);
1314 if (position < end) {
1315 framecnt_t length = min (region->length(), end - position);
1317 RegionFactory::region_name (name, region->name(), false);
1322 plist.add (Properties::start, region->start());
1323 plist.add (Properties::length, length);
1324 plist.add (Properties::name, name);
1326 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1327 add_region_internal (sub, position);
1328 set_layer (sub, DBL_MAX);
1334 Playlist::duplicate_range (AudioRange& range, float times)
1336 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1337 framecnt_t offset = range.end - range.start;
1338 paste (pl, range.start + offset, times, 0);
1342 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1344 if (ranges.empty()) {
1348 framepos_t min_pos = max_framepos;
1349 framepos_t max_pos = 0;
1351 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1354 min_pos = min (min_pos, (*i).start);
1355 max_pos = max (max_pos, (*i).end);
1358 framecnt_t offset = max_pos - min_pos;
1361 int itimes = (int) floor (times);
1363 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1364 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1365 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1372 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1374 RegionWriteLock rlock (this);
1375 RegionList copy (regions.rlist());
1378 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1380 if ((*r)->last_frame() < at) {
1385 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1386 /* intersected region */
1387 if (!move_intersected) {
1392 /* do not move regions glued to music time - that
1393 has to be done separately.
1396 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1397 fixup.push_back (*r);
1401 (*r)->set_position ((*r)->position() + distance);
1404 /* XXX: may not be necessary; Region::post_set should do this, I think */
1405 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1406 (*r)->recompute_position_from_lock_style (0);
1411 Playlist::split (framepos_t at, const int32_t sub_num)
1413 RegionWriteLock rlock (this);
1414 RegionList copy (regions.rlist());
1416 /* use a copy since this operation can modify the region list
1419 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1420 _split_region (*r, at, sub_num);
1425 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1427 RegionWriteLock rl (this);
1428 _split_region (region, playlist_position, sub_num);
1432 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1434 if (!region->covers (playlist_position)) {
1438 if (region->position() == playlist_position ||
1439 region->last_frame() == playlist_position) {
1443 boost::shared_ptr<Region> left;
1444 boost::shared_ptr<Region> right;
1445 frameoffset_t before;
1446 frameoffset_t after;
1450 /* split doesn't change anything about length, so don't try to splice */
1452 bool old_sp = _splicing;
1455 before = playlist_position - region->position();
1456 after = region->length() - before;
1458 RegionFactory::region_name (before_name, region->name(), false);
1463 plist.add (Properties::length, before);
1464 plist.add (Properties::name, before_name);
1465 plist.add (Properties::left_of_split, true);
1466 plist.add (Properties::layering_index, region->layering_index ());
1467 plist.add (Properties::layer, region->layer ());
1469 /* note: we must use the version of ::create with an offset here,
1470 since it supplies that offset to the Region constructor, which
1471 is necessary to get audio region gain envelopes right.
1473 left = RegionFactory::create (region, 0, plist, true, sub_num);
1476 RegionFactory::region_name (after_name, region->name(), false);
1481 plist.add (Properties::length, after);
1482 plist.add (Properties::name, after_name);
1483 plist.add (Properties::right_of_split, true);
1484 plist.add (Properties::layering_index, region->layering_index ());
1485 plist.add (Properties::layer, region->layer ());
1487 /* same note as above */
1488 right = RegionFactory::create (region, before, plist, true, sub_num);
1491 add_region_internal (left, region->position());
1492 add_region_internal (right, region->position() + before);
1493 remove_region_internal (region);
1499 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1501 if (_splicing || in_set_state) {
1502 /* don't respond to splicing moves or state setting */
1506 if (_edit_mode == Splice) {
1507 splice_locked (at, distance, exclude);
1512 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1514 if (_splicing || in_set_state) {
1515 /* don't respond to splicing moves or state setting */
1519 if (_edit_mode == Splice) {
1520 splice_unlocked (at, distance, exclude);
1525 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1528 RegionWriteLock rl (this);
1529 core_splice (at, distance, exclude);
1534 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1536 core_splice (at, distance, exclude);
1540 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1544 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1546 if (exclude && (*i) == exclude) {
1550 if ((*i)->position() >= at) {
1551 framepos_t new_pos = (*i)->position() + distance;
1554 } else if (new_pos >= max_framepos - (*i)->length()) {
1555 new_pos = max_framepos - (*i)->length();
1558 (*i)->set_position (new_pos);
1564 notify_contents_changed ();
1568 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1571 RegionWriteLock rl (this);
1572 core_ripple (at, distance, exclude);
1577 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1579 core_ripple (at, distance, exclude);
1583 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1585 if (distance == 0) {
1590 RegionListProperty copy = regions;
1591 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1592 assert (i != copy.end());
1595 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1600 if ((*i)->position() >= at) {
1601 framepos_t new_pos = (*i)->position() + distance;
1602 framepos_t limit = max_framepos - (*i)->length();
1605 } else if (new_pos >= limit ) {
1609 (*i)->set_position (new_pos);
1614 notify_contents_changed ();
1619 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1621 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1625 if (what_changed.contains (Properties::position)) {
1627 /* remove it from the list then add it back in
1628 the right place again.
1631 RegionSortByPosition cmp;
1633 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1635 if (i == regions.end()) {
1636 /* the region bounds are being modified but its not currently
1637 in the region list. we will use its bounds correctly when/if
1644 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1647 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1649 frameoffset_t delta = 0;
1651 if (what_changed.contains (Properties::position)) {
1652 delta = region->position() - region->last_position();
1655 if (what_changed.contains (Properties::length)) {
1656 delta += region->length() - region->last_length();
1660 possibly_splice (region->last_position() + region->last_length(), delta, region);
1663 if (holding_state ()) {
1664 pending_bounds.push_back (region);
1666 notify_contents_changed ();
1668 list<Evoral::Range<framepos_t> > xf;
1669 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1670 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1671 coalesce_and_check_crossfades (xf);
1677 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1679 boost::shared_ptr<Region> region (weak_region.lock());
1685 /* this makes a virtual call to the right kind of playlist ... */
1687 region_changed (what_changed, region);
1691 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1693 PropertyChange our_interests;
1694 PropertyChange bounds;
1695 PropertyChange pos_and_length;
1698 if (in_set_state || in_flush) {
1702 our_interests.add (Properties::muted);
1703 our_interests.add (Properties::layer);
1704 our_interests.add (Properties::opaque);
1706 bounds.add (Properties::start);
1707 bounds.add (Properties::position);
1708 bounds.add (Properties::length);
1710 pos_and_length.add (Properties::position);
1711 pos_and_length.add (Properties::length);
1713 if (what_changed.contains (bounds)) {
1714 region_bounds_changed (what_changed, region);
1715 save = !(_splicing || _nudging);
1718 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1719 notify_region_moved (region);
1720 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1721 notify_region_end_trimmed (region);
1722 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1723 notify_region_start_trimmed (region);
1726 /* don't notify about layer changes, since we are the only object that can initiate
1727 them, and we notify in ::relayer()
1730 if (what_changed.contains (our_interests)) {
1734 mark_session_dirty ();
1740 Playlist::drop_regions ()
1742 RegionWriteLock rl (this);
1744 all_regions.clear ();
1748 Playlist::sync_all_regions_with_regions ()
1750 RegionWriteLock rl (this);
1752 all_regions.clear ();
1754 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1755 all_regions.insert (*i);
1760 Playlist::clear (bool with_signals)
1763 RegionWriteLock rl (this);
1765 region_state_changed_connections.drop_connections ();
1766 region_drop_references_connections.drop_connections ();
1768 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1769 pending_removes.insert (*i);
1774 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1775 remove_dependents (*s);
1781 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1782 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1785 pending_removes.clear ();
1786 pending_contents_change = false;
1792 /* *********************************************************************
1794 **********************************************************************/
1796 boost::shared_ptr<RegionList>
1797 Playlist::region_list()
1799 RegionReadLock rlock (this);
1800 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1805 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1807 RegionReadLock rlock (const_cast<Playlist*>(this));
1809 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1810 (*i)->deep_sources (sources);
1814 boost::shared_ptr<RegionList>
1815 Playlist::regions_at (framepos_t frame)
1817 RegionReadLock rlock (this);
1818 return find_regions_at (frame);
1822 Playlist::count_regions_at (framepos_t frame) const
1824 RegionReadLock rlock (const_cast<Playlist*>(this));
1827 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1828 if ((*i)->covers (frame)) {
1836 boost::shared_ptr<Region>
1837 Playlist::top_region_at (framepos_t frame)
1840 RegionReadLock rlock (this);
1841 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1842 boost::shared_ptr<Region> region;
1844 if (rlist->size()) {
1845 RegionSortByLayer cmp;
1847 region = rlist->back();
1853 boost::shared_ptr<Region>
1854 Playlist::top_unmuted_region_at (framepos_t frame)
1857 RegionReadLock rlock (this);
1858 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1860 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1862 RegionList::iterator tmp = i;
1866 if ((*i)->muted()) {
1873 boost::shared_ptr<Region> region;
1875 if (rlist->size()) {
1876 RegionSortByLayer cmp;
1878 region = rlist->back();
1884 boost::shared_ptr<RegionList>
1885 Playlist::find_regions_at (framepos_t frame)
1887 /* Caller must hold lock */
1889 boost::shared_ptr<RegionList> rlist (new RegionList);
1891 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1892 if ((*i)->covers (frame)) {
1893 rlist->push_back (*i);
1900 boost::shared_ptr<RegionList>
1901 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1903 RegionReadLock rlock (this);
1904 boost::shared_ptr<RegionList> rlist (new RegionList);
1906 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1907 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1908 rlist->push_back (*i);
1915 boost::shared_ptr<RegionList>
1916 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1918 RegionReadLock rlock (this);
1919 boost::shared_ptr<RegionList> rlist (new RegionList);
1921 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1922 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1923 rlist->push_back (*i);
1930 /** @param start Range start.
1931 * @param end Range end.
1932 * @return regions which have some part within this range.
1934 boost::shared_ptr<RegionList>
1935 Playlist::regions_touched (framepos_t start, framepos_t end)
1937 RegionReadLock rlock (this);
1938 return regions_touched_locked (start, end);
1941 boost::shared_ptr<RegionList>
1942 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1944 boost::shared_ptr<RegionList> rlist (new RegionList);
1946 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1947 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1948 rlist->push_back (*i);
1956 Playlist::find_next_transient (framepos_t from, int dir)
1958 RegionReadLock rlock (this);
1959 AnalysisFeatureList points;
1960 AnalysisFeatureList these_points;
1962 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1964 if ((*i)->last_frame() < from) {
1968 if ((*i)->first_frame() > from) {
1973 (*i)->get_transients (these_points);
1975 /* add first frame, just, err, because */
1977 these_points.push_back ((*i)->first_frame());
1979 points.insert (points.end(), these_points.begin(), these_points.end());
1980 these_points.clear ();
1983 if (points.empty()) {
1987 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1988 bool reached = false;
1991 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1996 if (reached && (*x) > from) {
2001 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2006 if (reached && (*x) < from) {
2015 boost::shared_ptr<Region>
2016 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2018 RegionReadLock rlock (this);
2019 boost::shared_ptr<Region> ret;
2020 framepos_t closest = max_framepos;
2022 bool end_iter = false;
2024 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2028 frameoffset_t distance;
2029 boost::shared_ptr<Region> r = (*i);
2034 pos = r->first_frame ();
2037 pos = r->last_frame ();
2040 pos = r->sync_position ();
2045 case 1: /* forwards */
2048 if ((distance = pos - frame) < closest) {
2057 default: /* backwards */
2060 if ((distance = frame - pos) < closest) {
2076 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2078 RegionReadLock rlock (this);
2080 framepos_t closest = max_framepos;
2081 framepos_t ret = -1;
2085 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2087 boost::shared_ptr<Region> r = (*i);
2088 frameoffset_t distance;
2089 const framepos_t first_frame = r->first_frame();
2090 const framepos_t last_frame = r->last_frame();
2092 if (first_frame > frame) {
2094 distance = first_frame - frame;
2096 if (distance < closest) {
2102 if (last_frame > frame) {
2104 distance = last_frame - frame;
2106 if (distance < closest) {
2115 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2117 boost::shared_ptr<Region> r = (*i);
2118 frameoffset_t distance;
2119 const framepos_t first_frame = r->first_frame();
2120 const framepos_t last_frame = r->last_frame();
2122 if (last_frame < frame) {
2124 distance = frame - last_frame;
2126 if (distance < closest) {
2132 if (first_frame < frame) {
2134 distance = frame - first_frame;
2136 if (distance < closest) {
2148 /***********************************************************************/
2154 Playlist::mark_session_dirty ()
2156 if (!in_set_state && !holding_state ()) {
2157 _session.set_dirty();
2162 Playlist::rdiff (vector<Command*>& cmds) const
2164 RegionReadLock rlock (const_cast<Playlist *> (this));
2165 Stateful::rdiff (cmds);
2169 Playlist::clear_owned_changes ()
2171 RegionReadLock rlock (this);
2172 Stateful::clear_owned_changes ();
2176 Playlist::update (const RegionListProperty::ChangeRecord& change)
2178 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2179 name(), change.added.size(), change.removed.size()));
2182 /* add the added regions */
2183 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2184 add_region_internal ((*i), (*i)->position());
2186 /* remove the removed regions */
2187 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2195 Playlist::set_state (const XMLNode& node, int version)
2199 XMLNodeConstIterator niter;
2200 XMLPropertyList plist;
2201 XMLPropertyConstIterator piter;
2202 XMLProperty const * prop;
2203 boost::shared_ptr<Region> region;
2205 bool seen_region_nodes = false;
2210 if (node.name() != "Playlist") {
2217 plist = node.properties();
2221 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2225 if (prop->name() == X_("name")) {
2226 _name = prop->value();
2228 } else if (prop->name() == X_("orig-diskstream-id")) {
2229 /* XXX legacy session: fix up later */
2230 _orig_track_id = prop->value ();
2231 } else if (prop->name() == X_("orig-track-id")) {
2232 _orig_track_id = prop->value ();
2233 } else if (prop->name() == X_("frozen")) {
2234 _frozen = string_is_affirmative (prop->value());
2235 } else if (prop->name() == X_("combine-ops")) {
2236 _combine_ops = atoi (prop->value());
2242 nlist = node.children();
2244 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2248 if (child->name() == "Region") {
2250 seen_region_nodes = true;
2252 if ((prop = child->property ("id")) == 0) {
2253 error << _("region state node has no ID, ignored") << endmsg;
2257 ID id = prop->value ();
2259 if ((region = region_by_id (id))) {
2261 region->suspend_property_changes ();
2263 if (region->set_state (*child, version)) {
2264 region->resume_property_changes ();
2268 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2269 region->suspend_property_changes ();
2271 error << _("Playlist: cannot create region from XML") << endmsg;
2276 RegionWriteLock rlock (this);
2277 add_region_internal (region, region->position());
2280 region->resume_property_changes ();
2285 if (seen_region_nodes && regions.empty()) {
2290 notify_contents_changed ();
2293 first_set_state = false;
2299 Playlist::get_state()
2301 return state (true);
2305 Playlist::get_template()
2307 return state (false);
2310 /** @param full_state true to include regions in the returned state, otherwise false.
2313 Playlist::state (bool full_state)
2315 XMLNode *node = new XMLNode (X_("Playlist"));
2318 node->add_property (X_("id"), id().to_s());
2319 node->add_property (X_("name"), _name);
2320 node->add_property (X_("type"), _type.to_string());
2322 _orig_track_id.print (buf, sizeof (buf));
2323 node->add_property (X_("orig-track-id"), buf);
2324 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2327 RegionReadLock rlock (this);
2329 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2330 node->add_property ("combine-ops", buf);
2332 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2333 node->add_child_nocopy ((*i)->get_state());
2338 node->add_child_copy (*_extra_xml);
2345 Playlist::empty() const
2347 RegionReadLock rlock (const_cast<Playlist *>(this));
2348 return regions.empty();
2352 Playlist::n_regions() const
2354 RegionReadLock rlock (const_cast<Playlist *>(this));
2355 return regions.size();
2358 /** @return true if the all_regions list is empty, ie this playlist
2359 * has never had a region added to it.
2362 Playlist::all_regions_empty() const
2364 RegionReadLock rl (const_cast<Playlist *> (this));
2365 return all_regions.empty();
2368 pair<framepos_t, framepos_t>
2369 Playlist::get_extent () const
2371 RegionReadLock rlock (const_cast<Playlist *>(this));
2372 return _get_extent ();
2375 pair<framepos_t, framepos_t>
2376 Playlist::get_extent_with_endspace () const
2378 pair<framepos_t, framepos_t> l = get_extent();
2379 l.second += _end_space;
2383 pair<framepos_t, framepos_t>
2384 Playlist::_get_extent () const
2386 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2388 if (regions.empty()) {
2393 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2394 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2395 if (e.first < ext.first) {
2396 ext.first = e.first;
2398 if (e.second > ext.second) {
2399 ext.second = e.second;
2407 Playlist::bump_name (string name, Session &session)
2409 string newname = name;
2412 newname = bump_name_once (newname, '.');
2413 } while (session.playlists->by_name (newname)!=NULL);
2420 Playlist::top_layer() const
2422 RegionReadLock rlock (const_cast<Playlist *> (this));
2425 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2426 top = max (top, (*i)->layer());
2432 Playlist::set_edit_mode (EditMode mode)
2437 struct RelayerSort {
2438 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2439 return a->layering_index() < b->layering_index();
2443 /** Set a new layer for a region. This adjusts the layering indices of all
2444 * regions in the playlist to put the specified region in the appropriate
2445 * place. The actual layering will be fixed up when relayer() happens.
2449 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2451 /* Remove the layer we are setting from our region list, and sort it
2452 * using the layer indeces.
2455 RegionList copy = regions.rlist();
2456 copy.remove (region);
2457 copy.sort (RelayerSort ());
2459 /* Put region back in the right place */
2460 RegionList::iterator i = copy.begin();
2461 while (i != copy.end ()) {
2462 if ((*i)->layer() > new_layer) {
2468 copy.insert (i, region);
2470 setup_layering_indices (copy);
2474 Playlist::setup_layering_indices (RegionList const & regions)
2478 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2479 (*k)->set_layering_index (j++);
2483 struct LaterHigherSort {
2484 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2485 return a->position() < b->position();
2489 /** Take the layering indices of each of our regions, compute the layers
2490 * that they should be on, and write the layers back to the regions.
2493 Playlist::relayer ()
2495 /* never compute layers when setting from XML */
2501 /* Build up a new list of regions on each layer, stored in a set of lists
2502 each of which represent some period of time on some layer. The idea
2503 is to avoid having to search the entire region list to establish whether
2504 each region overlaps another */
2506 /* how many pieces to divide this playlist's time up into */
2507 int const divisions = 512;
2509 /* find the start and end positions of the regions on this playlist */
2510 framepos_t start = INT64_MAX;
2512 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2513 start = min (start, (*i)->position());
2514 end = max (end, (*i)->position() + (*i)->length());
2517 /* hence the size of each time division */
2518 double const division_size = (end - start) / double (divisions);
2520 vector<vector<RegionList> > layers;
2521 layers.push_back (vector<RegionList> (divisions));
2523 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2524 RegionList copy = regions.rlist();
2525 switch (Config->get_layer_model()) {
2527 copy.sort (LaterHigherSort ());
2530 copy.sort (RelayerSort ());
2534 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2535 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2536 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2539 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2541 /* find the time divisions that this region covers; if there are no regions on the list,
2542 division_size will equal 0 and in this case we'll just say that
2543 start_division = end_division = 0.
2545 int start_division = 0;
2546 int end_division = 0;
2548 if (division_size > 0) {
2549 start_division = floor ( ((*i)->position() - start) / division_size);
2550 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2551 if (end_division == divisions) {
2556 assert (divisions == 0 || end_division < divisions);
2558 /* find the lowest layer that this region can go on */
2559 size_t j = layers.size();
2561 /* try layer j - 1; it can go on if it overlaps no other region
2562 that is already on that layer
2565 bool overlap = false;
2566 for (int k = start_division; k <= end_division; ++k) {
2567 RegionList::iterator l = layers[j-1][k].begin ();
2568 while (l != layers[j-1][k].end()) {
2569 if ((*l)->overlap_equivalent (*i)) {
2582 /* overlap, so we must use layer j */
2589 if (j == layers.size()) {
2590 /* we need a new layer for this region */
2591 layers.push_back (vector<RegionList> (divisions));
2594 /* put a reference to this region in each of the divisions that it exists in */
2595 for (int k = start_division; k <= end_division; ++k) {
2596 layers[j][k].push_back (*i);
2599 (*i)->set_layer (j);
2602 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2603 relayering because we just removed the only region on the top layer, nothing will
2604 appear to have changed, but the StreamView must still sort itself out. We could
2605 probably keep a note of the top layer last time we relayered, and check that,
2606 but premature optimisation &c...
2608 notify_layering_changed ();
2610 /* This relayer() may have been called as a result of a region removal, in which
2611 case we need to setup layering indices to account for the one that has just
2614 setup_layering_indices (copy);
2618 Playlist::raise_region (boost::shared_ptr<Region> region)
2620 set_layer (region, region->layer() + 1.5);
2625 Playlist::lower_region (boost::shared_ptr<Region> region)
2627 set_layer (region, region->layer() - 1.5);
2632 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2634 set_layer (region, DBL_MAX);
2639 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2641 set_layer (region, -0.5);
2646 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2648 RegionList::iterator i;
2654 RegionWriteLock rlock (const_cast<Playlist *> (this));
2656 for (i = regions.begin(); i != regions.end(); ++i) {
2658 if ((*i)->position() >= start) {
2664 if ((*i)->last_frame() > max_framepos - distance) {
2665 new_pos = max_framepos - (*i)->length();
2667 new_pos = (*i)->position() + distance;
2672 if ((*i)->position() > distance) {
2673 new_pos = (*i)->position() - distance;
2679 (*i)->set_position (new_pos);
2687 notify_contents_changed ();
2693 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2695 RegionReadLock rlock (const_cast<Playlist*> (this));
2697 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2698 /* Note: passing the second argument as false can cause at best
2699 incredibly deep and time-consuming recursion, and at worst
2700 cycles if the user has managed to create cycles of reference
2701 between compound regions. We generally only this during
2702 cleanup, and @param shallow is passed as true.
2704 if ((*r)->uses_source (src, shallow)) {
2713 boost::shared_ptr<Region>
2714 Playlist::find_region (const ID& id) const
2716 RegionReadLock rlock (const_cast<Playlist*> (this));
2718 /* searches all regions currently in use by the playlist */
2720 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2721 if ((*i)->id() == id) {
2726 return boost::shared_ptr<Region> ();
2730 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2732 RegionReadLock rlock (const_cast<Playlist*> (this));
2735 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2741 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2742 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2743 /* check if region is used in a compound */
2744 if (it->second == r) {
2745 /* region is referenced as 'original' of a compound */
2749 if (r->whole_file() && r->max_source_level() > 0) {
2750 /* region itself ia a compound.
2751 * the compound regions are not referenced -> check regions inside compound
2753 const SourceList& sl = r->sources();
2754 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2755 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2757 if (ps->playlist()->region_use_count(it->first)) {
2758 // break out of both loops
2767 boost::shared_ptr<Region>
2768 Playlist::region_by_id (const ID& id) const
2770 /* searches all regions ever added to this playlist */
2772 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2773 if ((*i)->id() == id) {
2777 return boost::shared_ptr<Region> ();
2781 Playlist::dump () const
2783 boost::shared_ptr<Region> r;
2785 cerr << "Playlist \"" << _name << "\" " << endl
2786 << regions.size() << " regions "
2789 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2791 cerr << " " << r->name() << " ["
2792 << r->start() << "+" << r->length()
2802 Playlist::set_frozen (bool yn)
2808 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2812 if (region->locked()) {
2819 RegionWriteLock rlock (const_cast<Playlist*> (this));
2824 RegionList::iterator next;
2826 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2827 if ((*i) == region) {
2831 if (next != regions.end()) {
2833 if ((*next)->locked()) {
2839 if ((*next)->position() != region->last_frame() + 1) {
2840 /* they didn't used to touch, so after shuffle,
2841 just have them swap positions.
2843 new_pos = (*next)->position();
2845 /* they used to touch, so after shuffle,
2846 make sure they still do. put the earlier
2847 region where the later one will end after
2850 new_pos = region->position() + (*next)->length();
2853 (*next)->set_position (region->position());
2854 region->set_position (new_pos);
2856 /* avoid a full sort */
2858 regions.erase (i); // removes the region from the list */
2860 regions.insert (next, region); // adds it back after next
2869 RegionList::iterator prev = regions.end();
2871 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2872 if ((*i) == region) {
2874 if (prev != regions.end()) {
2876 if ((*prev)->locked()) {
2881 if (region->position() != (*prev)->last_frame() + 1) {
2882 /* they didn't used to touch, so after shuffle,
2883 just have them swap positions.
2885 new_pos = region->position();
2887 /* they used to touch, so after shuffle,
2888 make sure they still do. put the earlier
2889 one where the later one will end after
2891 new_pos = (*prev)->position() + region->length();
2894 region->set_position ((*prev)->position());
2895 (*prev)->set_position (new_pos);
2897 /* avoid a full sort */
2899 regions.erase (i); // remove region
2900 regions.insert (prev, region); // insert region before prev
2916 notify_contents_changed();
2922 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2924 RegionReadLock rlock (const_cast<Playlist*> (this));
2926 if (regions.size() > 1) {
2934 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2936 ripple_locked (at, distance, exclude);
2940 Playlist::update_after_tempo_map_change ()
2942 RegionWriteLock rlock (const_cast<Playlist*> (this));
2943 RegionList copy (regions.rlist());
2947 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2948 (*i)->update_after_tempo_map_change ();
2950 /* possibly causes a contents changed notification (flush_notifications()) */
2955 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2957 RegionReadLock rl (this);
2958 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2964 Playlist::has_region_at (framepos_t const p) const
2966 RegionReadLock (const_cast<Playlist *> (this));
2968 RegionList::const_iterator i = regions.begin ();
2969 while (i != regions.end() && !(*i)->covers (p)) {
2973 return (i != regions.end());
2976 /** Look from a session frame time and find the start time of the next region
2977 * which is on the top layer of this playlist.
2978 * @param t Time to look from.
2979 * @return Position of next top-layered region, or max_framepos if there isn't one.
2982 Playlist::find_next_top_layer_position (framepos_t t) const
2984 RegionReadLock rlock (const_cast<Playlist *> (this));
2986 layer_t const top = top_layer ();
2988 RegionList copy = regions.rlist ();
2989 copy.sort (RegionSortByPosition ());
2991 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2992 if ((*i)->position() >= t && (*i)->layer() == top) {
2993 return (*i)->position();
2997 return max_framepos;
3000 boost::shared_ptr<Region>
3001 Playlist::combine (const RegionList& r)
3004 uint32_t channels = 0;
3006 framepos_t earliest_position = max_framepos;
3007 vector<TwoRegions> old_and_new_regions;
3008 vector<boost::shared_ptr<Region> > originals;
3009 vector<boost::shared_ptr<Region> > copies;
3012 uint32_t max_level = 0;
3014 /* find the maximum depth of all the regions we're combining */
3016 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3017 max_level = max (max_level, (*i)->max_source_level());
3020 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3021 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3023 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3025 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3026 earliest_position = min (earliest_position, (*i)->position());
3029 /* enable this so that we do not try to create xfades etc. as we add
3033 pl->in_partition = true;
3035 /* sort by position then layer.
3036 * route_time_axis passes 'selected_regions' - which is not sorted.
3037 * here we need the top-most first, then every layer's region sorted by position.
3039 RegionList sorted(r);
3040 sorted.sort(RegionSortByLayerAndPosition());
3042 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3044 /* copy the region */
3046 boost::shared_ptr<Region> original_region = (*i);
3047 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3049 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3050 originals.push_back (original_region);
3051 copies.push_back (copied_region);
3053 RegionFactory::add_compound_association (original_region, copied_region);
3055 /* make position relative to zero */
3057 pl->add_region (copied_region, original_region->position() - earliest_position);
3058 copied_region->set_layer (original_region->layer ());
3060 /* use the maximum number of channels for any region */
3062 channels = max (channels, original_region->n_channels());
3064 /* it will go above the layer of the highest existing region */
3066 layer = max (layer, original_region->layer());
3069 pl->in_partition = false;
3071 pre_combine (copies);
3073 /* now create a new PlaylistSource for each channel in the new playlist */
3076 pair<framepos_t,framepos_t> extent = pl->get_extent();
3078 for (uint32_t chn = 0; chn < channels; ++chn) {
3079 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3083 /* now a new whole-file region using the list of sources */
3085 plist.add (Properties::start, 0);
3086 plist.add (Properties::length, extent.second);
3087 plist.add (Properties::name, parent_name);
3088 plist.add (Properties::whole_file, true);
3090 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3092 /* now the non-whole-file region that we will actually use in the
3097 plist.add (Properties::start, 0);
3098 plist.add (Properties::length, extent.second);
3099 plist.add (Properties::name, child_name);
3100 plist.add (Properties::layer, layer+1);
3102 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3104 /* remove all the selected regions from the current playlist
3109 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3113 /* do type-specific stuff with the originals and the new compound
3117 post_combine (originals, compound_region);
3119 /* add the new region at the right location */
3121 add_region (compound_region, earliest_position);
3127 return compound_region;
3131 Playlist::uncombine (boost::shared_ptr<Region> target)
3133 boost::shared_ptr<PlaylistSource> pls;
3134 boost::shared_ptr<const Playlist> pl;
3135 vector<boost::shared_ptr<Region> > originals;
3136 vector<TwoRegions> old_and_new_regions;
3138 // (1) check that its really a compound region
3140 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3144 pl = pls->playlist();
3146 framepos_t adjusted_start = 0; // gcc isn't smart enough
3147 framepos_t adjusted_end = 0; // gcc isn't smart enough
3149 /* the leftmost (earliest) edge of the compound region
3150 starts at zero in its source, or larger if it
3151 has been trimmed or content-scrolled.
3153 the rightmost (latest) edge of the compound region
3154 relative to its source is the starting point plus
3155 the length of the region.
3158 // (2) get all the original regions
3160 const RegionList& rl (pl->region_list_property().rlist());
3161 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3162 frameoffset_t move_offset = 0;
3164 /* there are two possibilities here:
3165 1) the playlist that the playlist source was based on
3166 is us, so just add the originals (which belonged to
3167 us anyway) back in the right place.
3169 2) the playlist that the playlist source was based on
3170 is NOT us, so we need to make copies of each of
3171 the original regions that we find, and add them
3174 bool same_playlist = (pls->original() == id());
3176 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3178 boost::shared_ptr<Region> current (*i);
3180 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3182 if (ca == cassocs.end()) {
3186 boost::shared_ptr<Region> original (ca->second);
3188 bool modified_region;
3190 if (i == rl.begin()) {
3191 move_offset = (target->position() - original->position()) - target->start();
3192 adjusted_start = original->position() + target->start();
3193 adjusted_end = adjusted_start + target->length();
3196 if (!same_playlist) {
3197 framepos_t pos = original->position();
3198 /* make a copy, but don't announce it */
3199 original = RegionFactory::create (original, false);
3200 /* the pure copy constructor resets position() to zero,
3203 original->set_position (pos);
3206 /* check to see how the original region (in the
3207 * playlist before compounding occurred) overlaps
3208 * with the new state of the compound region.
3211 original->clear_changes ();
3212 modified_region = false;
3214 switch (original->coverage (adjusted_start, adjusted_end)) {
3215 case Evoral::OverlapNone:
3216 /* original region does not cover any part
3217 of the current state of the compound region
3221 case Evoral::OverlapInternal:
3222 /* overlap is just a small piece inside the
3223 * original so trim both ends
3225 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3226 modified_region = true;
3229 case Evoral::OverlapExternal:
3230 /* overlap fully covers original, so leave it
3235 case Evoral::OverlapEnd:
3236 /* overlap starts within but covers end,
3237 so trim the front of the region
3239 original->trim_front (adjusted_start);
3240 modified_region = true;
3243 case Evoral::OverlapStart:
3244 /* overlap covers start but ends within, so
3245 * trim the end of the region.
3247 original->trim_end (adjusted_end);
3248 modified_region = true;
3253 /* fix the position to match any movement of the compound region.
3255 original->set_position (original->position() + move_offset);
3256 modified_region = true;
3259 if (modified_region) {
3260 _session.add_command (new StatefulDiffCommand (original));
3263 /* and add to the list of regions waiting to be
3267 originals.push_back (original);
3268 old_and_new_regions.push_back (TwoRegions (*i, original));
3271 pre_uncombine (originals, target);
3273 in_partition = true;
3276 // (3) remove the compound region
3278 remove_region (target);
3280 // (4) add the constituent regions
3282 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3283 add_region ((*i), (*i)->position());
3284 set_layer((*i), (*i)->layer());
3285 if (!RegionFactory::region_by_id((*i)->id())) {
3286 RegionFactory::map_add(*i);
3290 in_partition = false;
3295 Playlist::fade_range (list<AudioRange>& ranges)
3297 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3298 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3299 (*i)->fade_range ((*r).start, (*r).end);
3305 Playlist::max_source_level () const
3307 RegionReadLock rlock (const_cast<Playlist *> (this));
3310 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3311 lvl = max (lvl, (*i)->max_source_level());
3318 Playlist::set_orig_track_id (const PBD::ID& id)
3320 _orig_track_id = id;
3323 /** Take a list of ranges, coalesce any that can be coalesced, then call
3324 * check_crossfades for each one.
3327 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3329 /* XXX: it's a shame that this coalesce algorithm also exists in
3330 TimeSelection::consolidate().
3333 /* XXX: xfade: this is implemented in Evoral::RangeList */
3336 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3337 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3343 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3344 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3345 i->from = min (i->from, j->from);
3346 i->to = max (i->to, j->to);
3355 Playlist::set_capture_insertion_in_progress (bool yn)
3357 _capture_insertion_underway = yn;