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;
662 /*************************************************************
664 *************************************************************/
666 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
668 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
670 RegionWriteLock rlock (this);
671 times = fabs (times);
673 int itimes = (int) floor (times);
675 framepos_t pos = position;
677 if (times == 1 && auto_partition){
678 partition(pos - 1, (pos + region->length()), true);
682 add_region_internal (region, pos);
683 set_layer (region, DBL_MAX);
684 pos += region->length();
689 /* note that itimes can be zero if we being asked to just
690 insert a single fraction of the region.
693 for (int i = 0; i < itimes; ++i) {
694 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
695 add_region_internal (copy, pos);
696 set_layer (copy, DBL_MAX);
697 pos += region->length();
700 framecnt_t length = 0;
702 if (floor (times) != times) {
703 length = (framecnt_t) floor (region->length() * (times - floor (times)));
705 RegionFactory::region_name (name, region->name(), false);
710 plist.add (Properties::start, region->start());
711 plist.add (Properties::length, length);
712 plist.add (Properties::name, name);
713 plist.add (Properties::layer, region->layer());
715 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
716 add_region_internal (sub, pos);
717 set_layer (sub, DBL_MAX);
721 possibly_splice_unlocked (position, (pos + length) - position, region);
725 Playlist::set_region_ownership ()
727 RegionWriteLock rl (this);
728 RegionList::iterator i;
729 boost::weak_ptr<Playlist> pl (shared_from_this());
731 for (i = regions.begin(); i != regions.end(); ++i) {
732 (*i)->set_playlist (pl);
737 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
739 if (region->data_type() != _type) {
743 RegionSortByPosition cmp;
745 if (!first_set_state) {
746 boost::shared_ptr<Playlist> foo (shared_from_this());
747 region->set_playlist (boost::weak_ptr<Playlist>(foo));
750 region->set_position (position);
752 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
753 all_regions.insert (region);
755 possibly_splice_unlocked (position, region->length(), region);
757 if (!holding_state ()) {
758 /* layers get assigned from XML state, and are not reset during undo/redo */
762 /* we need to notify the existence of new region before checking dependents. Ick. */
764 notify_region_added (region);
766 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
772 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
774 RegionWriteLock rlock (this);
776 bool old_sp = _splicing;
779 remove_region_internal (old);
780 add_region_internal (newr, pos);
781 set_layer (newr, old->layer ());
785 possibly_splice_unlocked (pos, old->length() - newr->length());
789 Playlist::remove_region (boost::shared_ptr<Region> region)
791 RegionWriteLock rlock (this);
792 remove_region_internal (region);
796 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
798 RegionList::iterator i;
802 region->set_playlist (boost::weak_ptr<Playlist>());
805 /* XXX should probably freeze here .... */
807 for (i = regions.begin(); i != regions.end(); ++i) {
810 framepos_t pos = (*i)->position();
811 framecnt_t distance = (*i)->length();
815 possibly_splice_unlocked (pos, -distance);
817 if (!holding_state ()) {
819 remove_dependents (region);
822 notify_region_removed (region);
831 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
833 if (Config->get_use_overlap_equivalency()) {
834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
835 if ((*i)->overlap_equivalent (other)) {
836 results.push_back (*i);
840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
841 if ((*i)->equivalent (other)) {
842 results.push_back (*i);
849 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
853 if ((*i) && (*i)->region_list_equivalent (other)) {
854 results.push_back (*i);
860 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
862 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
864 if ((*i) && (*i)->any_source_equivalent (other)) {
865 results.push_back (*i);
871 Playlist::partition (framepos_t start, framepos_t end, bool cut)
875 partition_internal (start, end, cut, thawlist);
877 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
878 (*i)->resume_property_changes ();
882 /** Go through each region on the playlist and cut them at start and end, removing the section between
883 * start and end if cutting == true. Regions that lie entirely within start and end are always
888 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
890 RegionList new_regions;
893 RegionWriteLock rlock (this);
895 boost::shared_ptr<Region> region;
896 boost::shared_ptr<Region> current;
898 RegionList::iterator tmp;
899 Evoral::OverlapType overlap;
900 framepos_t pos1, pos2, pos3, pos4;
904 /* need to work from a copy, because otherwise the regions we add during the process
905 get operated on as well.
908 RegionList copy = regions.rlist();
910 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
917 if (current->first_frame() >= start && current->last_frame() < end) {
920 remove_region_internal (current);
926 /* coverage will return OverlapStart if the start coincides
927 with the end point. we do not partition such a region,
928 so catch this special case.
931 if (current->first_frame() >= end) {
935 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
939 pos1 = current->position();
942 pos4 = current->last_frame();
944 if (overlap == Evoral::OverlapInternal) {
945 /* split: we need 3 new regions, the front, middle and end.
946 cut: we need 2 regions, the front and end.
951 ---------------*************************------------
954 ---------------*****++++++++++++++++====------------
956 ---------------*****----------------====------------
961 /* "middle" ++++++ */
963 RegionFactory::region_name (new_name, current->name(), false);
967 plist.add (Properties::start, current->start() + (pos2 - pos1));
968 plist.add (Properties::length, pos3 - pos2);
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::left_of_split, true);
974 plist.add (Properties::right_of_split, true);
976 region = RegionFactory::create (current, plist);
977 add_region_internal (region, start);
978 new_regions.push_back (region);
983 RegionFactory::region_name (new_name, current->name(), false);
987 plist.add (Properties::start, current->start() + (pos3 - pos1));
988 plist.add (Properties::length, pos4 - pos3);
989 plist.add (Properties::name, new_name);
990 plist.add (Properties::layer, current->layer ());
991 plist.add (Properties::layering_index, current->layering_index ());
992 plist.add (Properties::automatic, true);
993 plist.add (Properties::right_of_split, true);
995 region = RegionFactory::create (current, plist);
997 add_region_internal (region, end);
998 new_regions.push_back (region);
1002 current->suspend_property_changes ();
1003 thawlist.push_back (current);
1004 current->cut_end (pos2 - 1);
1006 } else if (overlap == Evoral::OverlapEnd) {
1010 ---------------*************************------------
1013 ---------------**************+++++++++++------------
1015 ---------------**************-----------------------
1022 RegionFactory::region_name (new_name, current->name(), false);
1026 plist.add (Properties::start, current->start() + (pos2 - pos1));
1027 plist.add (Properties::length, pos4 - pos2);
1028 plist.add (Properties::name, new_name);
1029 plist.add (Properties::layer, current->layer ());
1030 plist.add (Properties::layering_index, current->layering_index ());
1031 plist.add (Properties::automatic, true);
1032 plist.add (Properties::left_of_split, true);
1034 region = RegionFactory::create (current, plist);
1036 add_region_internal (region, start);
1037 new_regions.push_back (region);
1042 current->suspend_property_changes ();
1043 thawlist.push_back (current);
1044 current->cut_end (pos2 - 1);
1046 } else if (overlap == Evoral::OverlapStart) {
1048 /* split: we need 2 regions: the front and the end.
1049 cut: just trim current to skip the cut area
1054 ---------------*************************------------
1058 ---------------****+++++++++++++++++++++------------
1060 -------------------*********************------------
1066 RegionFactory::region_name (new_name, current->name(), false);
1070 plist.add (Properties::start, current->start());
1071 plist.add (Properties::length, pos3 - pos1);
1072 plist.add (Properties::name, new_name);
1073 plist.add (Properties::layer, current->layer ());
1074 plist.add (Properties::layering_index, current->layering_index ());
1075 plist.add (Properties::automatic, true);
1076 plist.add (Properties::right_of_split, true);
1078 region = RegionFactory::create (current, plist);
1080 add_region_internal (region, pos1);
1081 new_regions.push_back (region);
1086 current->suspend_property_changes ();
1087 thawlist.push_back (current);
1088 current->trim_front (pos3);
1089 } else if (overlap == Evoral::OverlapExternal) {
1091 /* split: no split required.
1092 cut: remove the region.
1097 ---------------*************************------------
1101 ---------------*************************------------
1103 ----------------------------------------------------
1108 remove_region_internal (current);
1111 new_regions.push_back (current);
1115 in_partition = false;
1118 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1119 framepos_t wanted_length = end-start;
1120 _end_space = wanted_length - get_extent().second-get_extent().first;
1123 boost::shared_ptr<Playlist>
1124 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1126 boost::shared_ptr<Playlist> ret;
1127 boost::shared_ptr<Playlist> pl;
1130 if (ranges.empty()) {
1131 return boost::shared_ptr<Playlist>();
1134 start = ranges.front().start;
1136 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1138 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1140 if (i == ranges.begin()) {
1144 /* paste the next section into the nascent playlist,
1145 offset to reflect the start of the first range we
1149 ret->paste (pl, (*i).start - start, 1.0f);
1156 boost::shared_ptr<Playlist>
1157 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1159 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1160 return cut_copy (pmf, ranges, result_is_hidden);
1163 boost::shared_ptr<Playlist>
1164 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1167 return cut_copy (pmf, ranges, result_is_hidden);
1170 boost::shared_ptr<Playlist>
1171 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> the_copy;
1174 RegionList thawlist;
1177 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1178 string new_name = _name;
1182 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1183 return boost::shared_ptr<Playlist>();
1186 partition_internal (start, start+cnt-1, true, thawlist);
1188 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1189 (*i)->resume_property_changes();
1195 boost::shared_ptr<Playlist>
1196 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1200 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1201 string new_name = _name;
1205 // 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... )
1207 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1211 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1213 times = fabs (times);
1216 RegionReadLock rl2 (other.get());
1218 int itimes = (int) floor (times);
1219 framepos_t pos = position;
1220 framecnt_t const shift = other->_get_extent().second;
1221 layer_t top = top_layer ();
1224 RegionWriteLock rl1 (this);
1226 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1227 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1229 /* put these new regions on top of all existing ones, but preserve
1230 the ordering they had in the original playlist.
1233 add_region_internal (copy_of_region, (*i)->position() + pos);
1234 set_layer (copy_of_region, copy_of_region->layer() + top);
1246 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1248 duplicate(region, position, region->length(), times);
1251 /** @param gap from the beginning of the region to the next beginning */
1253 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1255 times = fabs (times);
1257 RegionWriteLock rl (this);
1258 int itimes = (int) floor (times);
1261 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1262 add_region_internal (copy, position);
1263 set_layer (copy, DBL_MAX);
1267 if (floor (times) != times) {
1268 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1270 RegionFactory::region_name (name, region->name(), false);
1275 plist.add (Properties::start, region->start());
1276 plist.add (Properties::length, length);
1277 plist.add (Properties::name, name);
1279 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1280 add_region_internal (sub, position);
1281 set_layer (sub, DBL_MAX);
1286 /** @param gap from the beginning of the region to the next beginning */
1287 /** @param end the first frame that does _not_ contain a duplicated frame */
1289 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1291 RegionWriteLock rl (this);
1293 while (position + region->length() - 1 < end) {
1294 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1295 add_region_internal (copy, position);
1296 set_layer (copy, DBL_MAX);
1300 if (position < end) {
1301 framecnt_t length = min (region->length(), end - position);
1303 RegionFactory::region_name (name, region->name(), false);
1308 plist.add (Properties::start, region->start());
1309 plist.add (Properties::length, length);
1310 plist.add (Properties::name, name);
1312 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1313 add_region_internal (sub, position);
1314 set_layer (sub, DBL_MAX);
1320 Playlist::duplicate_range (AudioRange& range, float times)
1322 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1323 framecnt_t offset = range.end - range.start;
1324 paste (pl, range.start + offset, times);
1328 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float /* times */)
1330 if (ranges.empty()) {
1334 framepos_t min_pos = max_framepos;
1335 framepos_t max_pos = 0;
1337 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1340 min_pos = min (min_pos, (*i).start);
1341 max_pos = max (max_pos, (*i).end);
1344 framecnt_t offset = max_pos - min_pos;
1346 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1347 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length(), true);
1348 paste (pl, (*i).start + offset, 1.0f); // times ??
1353 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1355 RegionWriteLock rlock (this);
1356 RegionList copy (regions.rlist());
1359 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1361 if ((*r)->last_frame() < at) {
1366 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1367 /* intersected region */
1368 if (!move_intersected) {
1373 /* do not move regions glued to music time - that
1374 has to be done separately.
1377 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1378 fixup.push_back (*r);
1382 (*r)->set_position ((*r)->position() + distance);
1385 /* XXX: may not be necessary; Region::post_set should do this, I think */
1386 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1387 (*r)->recompute_position_from_lock_style ();
1392 Playlist::split (framepos_t at)
1394 RegionWriteLock rlock (this);
1395 RegionList copy (regions.rlist());
1397 /* use a copy since this operation can modify the region list
1400 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1401 _split_region (*r, at);
1406 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1408 RegionWriteLock rl (this);
1409 _split_region (region, playlist_position);
1413 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1415 if (!region->covers (playlist_position)) {
1419 if (region->position() == playlist_position ||
1420 region->last_frame() == playlist_position) {
1424 boost::shared_ptr<Region> left;
1425 boost::shared_ptr<Region> right;
1426 frameoffset_t before;
1427 frameoffset_t after;
1431 /* split doesn't change anything about length, so don't try to splice */
1433 bool old_sp = _splicing;
1436 before = playlist_position - region->position();
1437 after = region->length() - before;
1439 RegionFactory::region_name (before_name, region->name(), false);
1444 plist.add (Properties::length, before);
1445 plist.add (Properties::name, before_name);
1446 plist.add (Properties::left_of_split, true);
1447 plist.add (Properties::layering_index, region->layering_index ());
1448 plist.add (Properties::layer, region->layer ());
1450 /* note: we must use the version of ::create with an offset here,
1451 since it supplies that offset to the Region constructor, which
1452 is necessary to get audio region gain envelopes right.
1454 left = RegionFactory::create (region, 0, plist);
1457 RegionFactory::region_name (after_name, region->name(), false);
1462 plist.add (Properties::length, after);
1463 plist.add (Properties::name, after_name);
1464 plist.add (Properties::right_of_split, true);
1465 plist.add (Properties::layering_index, region->layering_index ());
1466 plist.add (Properties::layer, region->layer ());
1468 /* same note as above */
1469 right = RegionFactory::create (region, before, plist);
1472 add_region_internal (left, region->position());
1473 add_region_internal (right, region->position() + before);
1474 remove_region_internal (region);
1480 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1482 if (_splicing || in_set_state) {
1483 /* don't respond to splicing moves or state setting */
1487 if (_edit_mode == Splice) {
1488 splice_locked (at, distance, exclude);
1493 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1495 if (_splicing || in_set_state) {
1496 /* don't respond to splicing moves or state setting */
1500 if (_edit_mode == Splice) {
1501 splice_unlocked (at, distance, exclude);
1506 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1509 RegionWriteLock rl (this);
1510 core_splice (at, distance, exclude);
1515 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1517 core_splice (at, distance, exclude);
1521 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1525 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1527 if (exclude && (*i) == exclude) {
1531 if ((*i)->position() >= at) {
1532 framepos_t new_pos = (*i)->position() + distance;
1535 } else if (new_pos >= max_framepos - (*i)->length()) {
1536 new_pos = max_framepos - (*i)->length();
1539 (*i)->set_position (new_pos);
1545 notify_contents_changed ();
1549 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1552 RegionWriteLock rl (this);
1553 core_ripple (at, distance, exclude);
1558 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1560 core_ripple (at, distance, exclude);
1564 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1566 if (distance == 0) {
1571 RegionListProperty copy = regions;
1572 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1573 assert (i != copy.end());
1576 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1581 if ((*i)->position() >= at) {
1582 framepos_t new_pos = (*i)->position() + distance;
1583 framepos_t limit = max_framepos - (*i)->length();
1586 } else if (new_pos >= limit ) {
1590 (*i)->set_position (new_pos);
1595 notify_contents_changed ();
1600 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1602 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1606 if (what_changed.contains (Properties::position)) {
1608 /* remove it from the list then add it back in
1609 the right place again.
1612 RegionSortByPosition cmp;
1614 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1616 if (i == regions.end()) {
1617 /* the region bounds are being modified but its not currently
1618 in the region list. we will use its bounds correctly when/if
1625 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1628 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1630 frameoffset_t delta = 0;
1632 if (what_changed.contains (Properties::position)) {
1633 delta = region->position() - region->last_position();
1636 if (what_changed.contains (Properties::length)) {
1637 delta += region->length() - region->last_length();
1641 possibly_splice (region->last_position() + region->last_length(), delta, region);
1644 if (holding_state ()) {
1645 pending_bounds.push_back (region);
1647 notify_contents_changed ();
1649 list<Evoral::Range<framepos_t> > xf;
1650 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1651 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1652 coalesce_and_check_crossfades (xf);
1658 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1660 boost::shared_ptr<Region> region (weak_region.lock());
1666 /* this makes a virtual call to the right kind of playlist ... */
1668 region_changed (what_changed, region);
1672 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1674 PropertyChange our_interests;
1675 PropertyChange bounds;
1676 PropertyChange pos_and_length;
1679 if (in_set_state || in_flush) {
1683 our_interests.add (Properties::muted);
1684 our_interests.add (Properties::layer);
1685 our_interests.add (Properties::opaque);
1687 bounds.add (Properties::start);
1688 bounds.add (Properties::position);
1689 bounds.add (Properties::length);
1691 pos_and_length.add (Properties::position);
1692 pos_and_length.add (Properties::length);
1694 if (what_changed.contains (bounds)) {
1695 region_bounds_changed (what_changed, region);
1696 save = !(_splicing || _nudging);
1699 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1700 notify_region_moved (region);
1701 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1702 notify_region_end_trimmed (region);
1703 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1704 notify_region_start_trimmed (region);
1707 /* don't notify about layer changes, since we are the only object that can initiate
1708 them, and we notify in ::relayer()
1711 if (what_changed.contains (our_interests)) {
1715 mark_session_dirty ();
1721 Playlist::drop_regions ()
1723 RegionWriteLock rl (this);
1725 all_regions.clear ();
1729 Playlist::sync_all_regions_with_regions ()
1731 RegionWriteLock rl (this);
1733 all_regions.clear ();
1735 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1736 all_regions.insert (*i);
1741 Playlist::clear (bool with_signals)
1744 RegionWriteLock rl (this);
1746 region_state_changed_connections.drop_connections ();
1748 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1749 pending_removes.insert (*i);
1754 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1755 remove_dependents (*s);
1761 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1762 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1765 pending_removes.clear ();
1766 pending_contents_change = false;
1772 /* *********************************************************************
1774 **********************************************************************/
1776 boost::shared_ptr<RegionList>
1777 Playlist::region_list() {
1778 RegionReadLock rlock (this);
1779 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1783 boost::shared_ptr<RegionList>
1784 Playlist::regions_at (framepos_t frame)
1786 RegionReadLock rlock (this);
1787 return find_regions_at (frame);
1791 Playlist::count_regions_at (framepos_t frame) const
1793 RegionReadLock rlock (const_cast<Playlist*>(this));
1796 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1797 if ((*i)->covers (frame)) {
1805 boost::shared_ptr<Region>
1806 Playlist::top_region_at (framepos_t frame)
1809 RegionReadLock rlock (this);
1810 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1811 boost::shared_ptr<Region> region;
1813 if (rlist->size()) {
1814 RegionSortByLayer cmp;
1816 region = rlist->back();
1822 boost::shared_ptr<Region>
1823 Playlist::top_unmuted_region_at (framepos_t frame)
1826 RegionReadLock rlock (this);
1827 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1829 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1831 RegionList::iterator tmp = i;
1835 if ((*i)->muted()) {
1842 boost::shared_ptr<Region> region;
1844 if (rlist->size()) {
1845 RegionSortByLayer cmp;
1847 region = rlist->back();
1853 boost::shared_ptr<RegionList>
1854 Playlist::find_regions_at (framepos_t frame)
1856 /* Caller must hold lock */
1858 boost::shared_ptr<RegionList> rlist (new RegionList);
1860 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1861 if ((*i)->covers (frame)) {
1862 rlist->push_back (*i);
1869 boost::shared_ptr<RegionList>
1870 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1872 RegionReadLock rlock (this);
1873 boost::shared_ptr<RegionList> rlist (new RegionList);
1875 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1876 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1877 rlist->push_back (*i);
1884 boost::shared_ptr<RegionList>
1885 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1887 RegionReadLock rlock (this);
1888 boost::shared_ptr<RegionList> rlist (new RegionList);
1890 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1891 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1892 rlist->push_back (*i);
1899 /** @param start Range start.
1900 * @param end Range end.
1901 * @return regions which have some part within this range.
1903 boost::shared_ptr<RegionList>
1904 Playlist::regions_touched (framepos_t start, framepos_t end)
1906 RegionReadLock rlock (this);
1907 return regions_touched_locked (start, end);
1910 boost::shared_ptr<RegionList>
1911 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1913 boost::shared_ptr<RegionList> rlist (new RegionList);
1915 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1916 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1917 rlist->push_back (*i);
1925 Playlist::find_next_transient (framepos_t from, int dir)
1927 RegionReadLock rlock (this);
1928 AnalysisFeatureList points;
1929 AnalysisFeatureList these_points;
1931 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1933 if ((*i)->last_frame() < from) {
1937 if ((*i)->first_frame() > from) {
1942 (*i)->get_transients (these_points);
1944 /* add first frame, just, err, because */
1946 these_points.push_back ((*i)->first_frame());
1948 points.insert (points.end(), these_points.begin(), these_points.end());
1949 these_points.clear ();
1952 if (points.empty()) {
1956 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1957 bool reached = false;
1960 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1965 if (reached && (*x) > from) {
1970 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1975 if (reached && (*x) < from) {
1984 boost::shared_ptr<Region>
1985 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1987 RegionReadLock rlock (this);
1988 boost::shared_ptr<Region> ret;
1989 framepos_t closest = max_framepos;
1991 bool end_iter = false;
1993 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1997 frameoffset_t distance;
1998 boost::shared_ptr<Region> r = (*i);
2003 pos = r->first_frame ();
2006 pos = r->last_frame ();
2009 pos = r->sync_position ();
2014 case 1: /* forwards */
2017 if ((distance = pos - frame) < closest) {
2026 default: /* backwards */
2029 if ((distance = frame - pos) < closest) {
2045 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2047 RegionReadLock rlock (this);
2049 framepos_t closest = max_framepos;
2050 framepos_t ret = -1;
2054 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2056 boost::shared_ptr<Region> r = (*i);
2057 frameoffset_t distance;
2058 const framepos_t first_frame = r->first_frame();
2059 const framepos_t last_frame = r->last_frame();
2061 if (first_frame > frame) {
2063 distance = first_frame - frame;
2065 if (distance < closest) {
2071 if (last_frame > frame) {
2073 distance = last_frame - frame;
2075 if (distance < closest) {
2084 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2086 boost::shared_ptr<Region> r = (*i);
2087 frameoffset_t distance;
2088 const framepos_t first_frame = r->first_frame();
2089 const framepos_t last_frame = r->last_frame();
2091 if (last_frame < frame) {
2093 distance = frame - last_frame;
2095 if (distance < closest) {
2101 if (first_frame < frame) {
2103 distance = frame - first_frame;
2105 if (distance < closest) {
2117 /***********************************************************************/
2123 Playlist::mark_session_dirty ()
2125 if (!in_set_state && !holding_state ()) {
2126 _session.set_dirty();
2131 Playlist::rdiff (vector<Command*>& cmds) const
2133 RegionReadLock rlock (const_cast<Playlist *> (this));
2134 Stateful::rdiff (cmds);
2138 Playlist::clear_owned_changes ()
2140 RegionReadLock rlock (this);
2141 Stateful::clear_owned_changes ();
2145 Playlist::update (const RegionListProperty::ChangeRecord& change)
2147 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2148 name(), change.added.size(), change.removed.size()));
2151 /* add the added regions */
2152 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2153 add_region_internal ((*i), (*i)->position());
2155 /* remove the removed regions */
2156 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2164 Playlist::set_state (const XMLNode& node, int version)
2168 XMLNodeConstIterator niter;
2169 XMLPropertyList plist;
2170 XMLPropertyConstIterator piter;
2171 XMLProperty const * prop;
2172 boost::shared_ptr<Region> region;
2174 bool seen_region_nodes = false;
2179 if (node.name() != "Playlist") {
2186 plist = node.properties();
2190 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2194 if (prop->name() == X_("name")) {
2195 _name = prop->value();
2197 } else if (prop->name() == X_("orig-diskstream-id")) {
2198 /* XXX legacy session: fix up later */
2199 _orig_track_id = prop->value ();
2200 } else if (prop->name() == X_("orig-track-id")) {
2201 _orig_track_id = prop->value ();
2202 } else if (prop->name() == X_("frozen")) {
2203 _frozen = string_is_affirmative (prop->value());
2204 } else if (prop->name() == X_("combine-ops")) {
2205 _combine_ops = atoi (prop->value());
2211 nlist = node.children();
2213 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2217 if (child->name() == "Region") {
2219 seen_region_nodes = true;
2221 if ((prop = child->property ("id")) == 0) {
2222 error << _("region state node has no ID, ignored") << endmsg;
2226 ID id = prop->value ();
2228 if ((region = region_by_id (id))) {
2230 region->suspend_property_changes ();
2232 if (region->set_state (*child, version)) {
2233 region->resume_property_changes ();
2237 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2238 region->suspend_property_changes ();
2240 error << _("Playlist: cannot create region from XML") << endmsg;
2245 RegionWriteLock rlock (this);
2246 add_region_internal (region, region->position());
2249 region->resume_property_changes ();
2254 if (seen_region_nodes && regions.empty()) {
2259 notify_contents_changed ();
2262 first_set_state = false;
2268 Playlist::get_state()
2270 return state (true);
2274 Playlist::get_template()
2276 return state (false);
2279 /** @param full_state true to include regions in the returned state, otherwise false.
2282 Playlist::state (bool full_state)
2284 XMLNode *node = new XMLNode (X_("Playlist"));
2287 node->add_property (X_("id"), id().to_s());
2288 node->add_property (X_("name"), _name);
2289 node->add_property (X_("type"), _type.to_string());
2291 _orig_track_id.print (buf, sizeof (buf));
2292 node->add_property (X_("orig-track-id"), buf);
2293 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2296 RegionReadLock rlock (this);
2298 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2299 node->add_property ("combine-ops", buf);
2301 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2302 node->add_child_nocopy ((*i)->get_state());
2307 node->add_child_copy (*_extra_xml);
2314 Playlist::empty() const
2316 RegionReadLock rlock (const_cast<Playlist *>(this));
2317 return regions.empty();
2321 Playlist::n_regions() const
2323 RegionReadLock rlock (const_cast<Playlist *>(this));
2324 return regions.size();
2327 /** @return true if the all_regions list is empty, ie this playlist
2328 * has never had a region added to it.
2331 Playlist::all_regions_empty() const
2333 RegionReadLock rl (const_cast<Playlist *> (this));
2334 return all_regions.empty();
2337 pair<framepos_t, framepos_t>
2338 Playlist::get_extent () const
2340 RegionReadLock rlock (const_cast<Playlist *>(this));
2341 return _get_extent ();
2344 pair<framepos_t, framepos_t>
2345 Playlist::get_extent_with_endspace () const
2347 pair<framepos_t, framepos_t> l = get_extent();
2348 l.second += _end_space;
2352 pair<framepos_t, framepos_t>
2353 Playlist::_get_extent () const
2355 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2357 if (regions.empty()) {
2362 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2363 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2364 if (e.first < ext.first) {
2365 ext.first = e.first;
2367 if (e.second > ext.second) {
2368 ext.second = e.second;
2376 Playlist::bump_name (string name, Session &session)
2378 string newname = name;
2381 newname = bump_name_once (newname, '.');
2382 } while (session.playlists->by_name (newname)!=NULL);
2389 Playlist::top_layer() const
2391 RegionReadLock rlock (const_cast<Playlist *> (this));
2394 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2395 top = max (top, (*i)->layer());
2401 Playlist::set_edit_mode (EditMode mode)
2406 struct RelayerSort {
2407 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2408 return a->layering_index() < b->layering_index();
2412 /** Set a new layer for a region. This adjusts the layering indices of all
2413 * regions in the playlist to put the specified region in the appropriate
2414 * place. The actual layering will be fixed up when relayer() happens.
2418 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2420 /* Remove the layer we are setting from our region list, and sort it
2421 * using the layer indeces.
2424 RegionList copy = regions.rlist();
2425 copy.remove (region);
2426 copy.sort (RelayerSort ());
2428 /* Put region back in the right place */
2429 RegionList::iterator i = copy.begin();
2430 while (i != copy.end ()) {
2431 if ((*i)->layer() > new_layer) {
2437 copy.insert (i, region);
2439 setup_layering_indices (copy);
2443 Playlist::setup_layering_indices (RegionList const & regions)
2447 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2448 (*k)->set_layering_index (j++);
2452 struct LaterHigherSort {
2453 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2454 return a->position() < b->position();
2458 /** Take the layering indices of each of our regions, compute the layers
2459 * that they should be on, and write the layers back to the regions.
2462 Playlist::relayer ()
2464 /* never compute layers when setting from XML */
2470 /* Build up a new list of regions on each layer, stored in a set of lists
2471 each of which represent some period of time on some layer. The idea
2472 is to avoid having to search the entire region list to establish whether
2473 each region overlaps another */
2475 /* how many pieces to divide this playlist's time up into */
2476 int const divisions = 512;
2478 /* find the start and end positions of the regions on this playlist */
2479 framepos_t start = INT64_MAX;
2481 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2482 start = min (start, (*i)->position());
2483 end = max (end, (*i)->position() + (*i)->length());
2486 /* hence the size of each time division */
2487 double const division_size = (end - start) / double (divisions);
2489 vector<vector<RegionList> > layers;
2490 layers.push_back (vector<RegionList> (divisions));
2492 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2493 RegionList copy = regions.rlist();
2494 switch (Config->get_layer_model()) {
2496 copy.sort (LaterHigherSort ());
2499 copy.sort (RelayerSort ());
2503 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2504 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2505 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2508 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2510 /* find the time divisions that this region covers; if there are no regions on the list,
2511 division_size will equal 0 and in this case we'll just say that
2512 start_division = end_division = 0.
2514 int start_division = 0;
2515 int end_division = 0;
2517 if (division_size > 0) {
2518 start_division = floor ( ((*i)->position() - start) / division_size);
2519 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2520 if (end_division == divisions) {
2525 assert (divisions == 0 || end_division < divisions);
2527 /* find the lowest layer that this region can go on */
2528 size_t j = layers.size();
2530 /* try layer j - 1; it can go on if it overlaps no other region
2531 that is already on that layer
2534 bool overlap = false;
2535 for (int k = start_division; k <= end_division; ++k) {
2536 RegionList::iterator l = layers[j-1][k].begin ();
2537 while (l != layers[j-1][k].end()) {
2538 if ((*l)->overlap_equivalent (*i)) {
2551 /* overlap, so we must use layer j */
2558 if (j == layers.size()) {
2559 /* we need a new layer for this region */
2560 layers.push_back (vector<RegionList> (divisions));
2563 /* put a reference to this region in each of the divisions that it exists in */
2564 for (int k = start_division; k <= end_division; ++k) {
2565 layers[j][k].push_back (*i);
2568 (*i)->set_layer (j);
2571 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2572 relayering because we just removed the only region on the top layer, nothing will
2573 appear to have changed, but the StreamView must still sort itself out. We could
2574 probably keep a note of the top layer last time we relayered, and check that,
2575 but premature optimisation &c...
2577 notify_layering_changed ();
2579 /* This relayer() may have been called as a result of a region removal, in which
2580 case we need to setup layering indices to account for the one that has just
2583 setup_layering_indices (copy);
2587 Playlist::raise_region (boost::shared_ptr<Region> region)
2589 set_layer (region, region->layer() + 1.5);
2594 Playlist::lower_region (boost::shared_ptr<Region> region)
2596 set_layer (region, region->layer() - 1.5);
2601 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2603 set_layer (region, DBL_MAX);
2608 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2610 set_layer (region, -0.5);
2615 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2617 RegionList::iterator i;
2623 RegionWriteLock rlock (const_cast<Playlist *> (this));
2625 for (i = regions.begin(); i != regions.end(); ++i) {
2627 if ((*i)->position() >= start) {
2633 if ((*i)->last_frame() > max_framepos - distance) {
2634 new_pos = max_framepos - (*i)->length();
2636 new_pos = (*i)->position() + distance;
2641 if ((*i)->position() > distance) {
2642 new_pos = (*i)->position() - distance;
2648 (*i)->set_position (new_pos);
2656 notify_contents_changed ();
2662 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2664 RegionReadLock rlock (const_cast<Playlist*> (this));
2666 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2667 if ((*r)->uses_source (src)) {
2675 boost::shared_ptr<Region>
2676 Playlist::find_region (const ID& id) const
2678 RegionReadLock rlock (const_cast<Playlist*> (this));
2680 /* searches all regions currently in use by the playlist */
2682 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2683 if ((*i)->id() == id) {
2688 return boost::shared_ptr<Region> ();
2692 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2694 RegionReadLock rlock (const_cast<Playlist*> (this));
2697 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2703 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2704 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2705 /* check if region is used in a compound */
2706 if (it->second == r) {
2707 /* region is referenced as 'original' of a compound */
2711 if (r->whole_file() && r->max_source_level() > 0) {
2712 /* region itself ia a compound.
2713 * the compound regions are not referenced -> check regions inside compound
2715 const SourceList& sl = r->sources();
2716 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2717 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2719 if (ps->playlist()->region_use_count(it->first)) {
2720 // break out of both loops
2729 boost::shared_ptr<Region>
2730 Playlist::region_by_id (const ID& id) const
2732 /* searches all regions ever added to this playlist */
2734 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2735 if ((*i)->id() == id) {
2739 return boost::shared_ptr<Region> ();
2743 Playlist::dump () const
2745 boost::shared_ptr<Region> r;
2747 cerr << "Playlist \"" << _name << "\" " << endl
2748 << regions.size() << " regions "
2751 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2753 cerr << " " << r->name() << " ["
2754 << r->start() << "+" << r->length()
2764 Playlist::set_frozen (bool yn)
2770 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2774 if (region->locked()) {
2781 RegionWriteLock rlock (const_cast<Playlist*> (this));
2786 RegionList::iterator next;
2788 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2789 if ((*i) == region) {
2793 if (next != regions.end()) {
2795 if ((*next)->locked()) {
2801 if ((*next)->position() != region->last_frame() + 1) {
2802 /* they didn't used to touch, so after shuffle,
2803 just have them swap positions.
2805 new_pos = (*next)->position();
2807 /* they used to touch, so after shuffle,
2808 make sure they still do. put the earlier
2809 region where the later one will end after
2812 new_pos = region->position() + (*next)->length();
2815 (*next)->set_position (region->position());
2816 region->set_position (new_pos);
2818 /* avoid a full sort */
2820 regions.erase (i); // removes the region from the list */
2822 regions.insert (next, region); // adds it back after next
2831 RegionList::iterator prev = regions.end();
2833 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2834 if ((*i) == region) {
2836 if (prev != regions.end()) {
2838 if ((*prev)->locked()) {
2843 if (region->position() != (*prev)->last_frame() + 1) {
2844 /* they didn't used to touch, so after shuffle,
2845 just have them swap positions.
2847 new_pos = region->position();
2849 /* they used to touch, so after shuffle,
2850 make sure they still do. put the earlier
2851 one where the later one will end after
2853 new_pos = (*prev)->position() + region->length();
2856 region->set_position ((*prev)->position());
2857 (*prev)->set_position (new_pos);
2859 /* avoid a full sort */
2861 regions.erase (i); // remove region
2862 regions.insert (prev, region); // insert region before prev
2878 notify_contents_changed();
2884 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2886 RegionReadLock rlock (const_cast<Playlist*> (this));
2888 if (regions.size() > 1) {
2896 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2898 ripple_locked (at, distance, exclude);
2902 Playlist::update_after_tempo_map_change ()
2904 RegionWriteLock rlock (const_cast<Playlist*> (this));
2905 RegionList copy (regions.rlist());
2909 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2910 (*i)->update_after_tempo_map_change ();
2914 notify_contents_changed();
2918 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2920 RegionReadLock rl (this);
2921 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2927 Playlist::has_region_at (framepos_t const p) const
2929 RegionReadLock (const_cast<Playlist *> (this));
2931 RegionList::const_iterator i = regions.begin ();
2932 while (i != regions.end() && !(*i)->covers (p)) {
2936 return (i != regions.end());
2939 /** Remove any region that uses a given source */
2941 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2943 RegionWriteLock rl (this);
2945 RegionList::iterator i = regions.begin();
2946 while (i != regions.end()) {
2947 RegionList::iterator j = i;
2950 if ((*i)->uses_source (s)) {
2951 remove_region_internal (*i);
2958 /** Look from a session frame time and find the start time of the next region
2959 * which is on the top layer of this playlist.
2960 * @param t Time to look from.
2961 * @return Position of next top-layered region, or max_framepos if there isn't one.
2964 Playlist::find_next_top_layer_position (framepos_t t) const
2966 RegionReadLock rlock (const_cast<Playlist *> (this));
2968 layer_t const top = top_layer ();
2970 RegionList copy = regions.rlist ();
2971 copy.sort (RegionSortByPosition ());
2973 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2974 if ((*i)->position() >= t && (*i)->layer() == top) {
2975 return (*i)->position();
2979 return max_framepos;
2982 boost::shared_ptr<Region>
2983 Playlist::combine (const RegionList& r)
2986 uint32_t channels = 0;
2988 framepos_t earliest_position = max_framepos;
2989 vector<TwoRegions> old_and_new_regions;
2990 vector<boost::shared_ptr<Region> > originals;
2991 vector<boost::shared_ptr<Region> > copies;
2994 uint32_t max_level = 0;
2996 /* find the maximum depth of all the regions we're combining */
2998 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2999 max_level = max (max_level, (*i)->max_source_level());
3002 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3003 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3005 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3007 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3008 earliest_position = min (earliest_position, (*i)->position());
3011 /* enable this so that we do not try to create xfades etc. as we add
3015 pl->in_partition = true;
3017 /* sort by position then layer.
3018 * route_time_axis passes 'selected_regions' - which is not sorted.
3019 * here we need the top-most first, then every layer's region sorted by position.
3021 RegionList sorted(r);
3022 sorted.sort(RegionSortByLayerAndPosition());
3024 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3026 /* copy the region */
3028 boost::shared_ptr<Region> original_region = (*i);
3029 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3031 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3032 originals.push_back (original_region);
3033 copies.push_back (copied_region);
3035 RegionFactory::add_compound_association (original_region, copied_region);
3037 /* make position relative to zero */
3039 pl->add_region (copied_region, original_region->position() - earliest_position);
3040 copied_region->set_layer (original_region->layer ());
3042 /* use the maximum number of channels for any region */
3044 channels = max (channels, original_region->n_channels());
3046 /* it will go above the layer of the highest existing region */
3048 layer = max (layer, original_region->layer());
3051 pl->in_partition = false;
3053 pre_combine (copies);
3055 /* now create a new PlaylistSource for each channel in the new playlist */
3058 pair<framepos_t,framepos_t> extent = pl->get_extent();
3060 for (uint32_t chn = 0; chn < channels; ++chn) {
3061 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3065 /* now a new whole-file region using the list of sources */
3067 plist.add (Properties::start, 0);
3068 plist.add (Properties::length, extent.second);
3069 plist.add (Properties::name, parent_name);
3070 plist.add (Properties::whole_file, true);
3072 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3074 /* now the non-whole-file region that we will actually use in the
3079 plist.add (Properties::start, 0);
3080 plist.add (Properties::length, extent.second);
3081 plist.add (Properties::name, child_name);
3082 plist.add (Properties::layer, layer+1);
3084 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3086 /* remove all the selected regions from the current playlist
3091 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3095 /* do type-specific stuff with the originals and the new compound
3099 post_combine (originals, compound_region);
3101 /* add the new region at the right location */
3103 add_region (compound_region, earliest_position);
3109 return compound_region;
3113 Playlist::uncombine (boost::shared_ptr<Region> target)
3115 boost::shared_ptr<PlaylistSource> pls;
3116 boost::shared_ptr<const Playlist> pl;
3117 vector<boost::shared_ptr<Region> > originals;
3118 vector<TwoRegions> old_and_new_regions;
3120 // (1) check that its really a compound region
3122 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3126 pl = pls->playlist();
3128 framepos_t adjusted_start = 0; // gcc isn't smart enough
3129 framepos_t adjusted_end = 0; // gcc isn't smart enough
3131 /* the leftmost (earliest) edge of the compound region
3132 starts at zero in its source, or larger if it
3133 has been trimmed or content-scrolled.
3135 the rightmost (latest) edge of the compound region
3136 relative to its source is the starting point plus
3137 the length of the region.
3140 // (2) get all the original regions
3142 const RegionList& rl (pl->region_list_property().rlist());
3143 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3144 frameoffset_t move_offset = 0;
3146 /* there are two possibilities here:
3147 1) the playlist that the playlist source was based on
3148 is us, so just add the originals (which belonged to
3149 us anyway) back in the right place.
3151 2) the playlist that the playlist source was based on
3152 is NOT us, so we need to make copies of each of
3153 the original regions that we find, and add them
3156 bool same_playlist = (pls->original() == id());
3158 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3160 boost::shared_ptr<Region> current (*i);
3162 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3164 if (ca == cassocs.end()) {
3168 boost::shared_ptr<Region> original (ca->second);
3170 bool modified_region;
3172 if (i == rl.begin()) {
3173 move_offset = (target->position() - original->position()) - target->start();
3174 adjusted_start = original->position() + target->start();
3175 adjusted_end = adjusted_start + target->length();
3178 if (!same_playlist) {
3179 framepos_t pos = original->position();
3180 /* make a copy, but don't announce it */
3181 original = RegionFactory::create (original, false);
3182 /* the pure copy constructor resets position() to zero,
3185 original->set_position (pos);
3188 /* check to see how the original region (in the
3189 * playlist before compounding occurred) overlaps
3190 * with the new state of the compound region.
3193 original->clear_changes ();
3194 modified_region = false;
3196 switch (original->coverage (adjusted_start, adjusted_end)) {
3197 case Evoral::OverlapNone:
3198 /* original region does not cover any part
3199 of the current state of the compound region
3203 case Evoral::OverlapInternal:
3204 /* overlap is just a small piece inside the
3205 * original so trim both ends
3207 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3208 modified_region = true;
3211 case Evoral::OverlapExternal:
3212 /* overlap fully covers original, so leave it
3217 case Evoral::OverlapEnd:
3218 /* overlap starts within but covers end,
3219 so trim the front of the region
3221 original->trim_front (adjusted_start);
3222 modified_region = true;
3225 case Evoral::OverlapStart:
3226 /* overlap covers start but ends within, so
3227 * trim the end of the region.
3229 original->trim_end (adjusted_end);
3230 modified_region = true;
3235 /* fix the position to match any movement of the compound region.
3237 original->set_position (original->position() + move_offset);
3238 modified_region = true;
3241 if (modified_region) {
3242 _session.add_command (new StatefulDiffCommand (original));
3245 /* and add to the list of regions waiting to be
3249 originals.push_back (original);
3250 old_and_new_regions.push_back (TwoRegions (*i, original));
3253 pre_uncombine (originals, target);
3255 in_partition = true;
3258 // (3) remove the compound region
3260 remove_region (target);
3262 // (4) add the constituent regions
3264 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3265 add_region ((*i), (*i)->position());
3266 set_layer((*i), (*i)->layer());
3267 if (!RegionFactory::region_by_id((*i)->id())) {
3268 RegionFactory::map_add(*i);
3272 in_partition = false;
3277 Playlist::fade_range (list<AudioRange>& ranges)
3279 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3280 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3281 (*i)->fade_range ((*r).start, (*r).end);
3287 Playlist::max_source_level () const
3289 RegionReadLock rlock (const_cast<Playlist *> (this));
3292 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3293 lvl = max (lvl, (*i)->max_source_level());
3300 Playlist::set_orig_track_id (const PBD::ID& id)
3302 _orig_track_id = id;
3305 /** Take a list of ranges, coalesce any that can be coalesced, then call
3306 * check_crossfades for each one.
3309 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3311 /* XXX: it's a shame that this coalesce algorithm also exists in
3312 TimeSelection::consolidate().
3315 /* XXX: xfade: this is implemented in Evoral::RangeList */
3318 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3319 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3325 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3326 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3327 i->from = min (i->from, j->from);
3328 i->to = max (i->to, j->to);
3337 Playlist::set_capture_insertion_in_progress (bool yn)
3339 _capture_insertion_underway = yn;