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){
679 partition(pos - 1, (pos + region->length()), true);
683 add_region_internal (region, pos, sub_num);
684 set_layer (region, DBL_MAX);
685 pos += region->length();
690 /* note that itimes can be zero if we being asked to just
691 insert a single fraction of the region.
694 for (int i = 0; i < itimes; ++i) {
695 boost::shared_ptr<Region> copy = RegionFactory::create (region, true, sub_num);
696 add_region_internal (copy, pos, sub_num);
697 set_layer (copy, DBL_MAX);
698 pos += region->length();
701 framecnt_t length = 0;
703 if (floor (times) != times) {
704 length = (framecnt_t) floor (region->length() * (times - floor (times)));
706 RegionFactory::region_name (name, region->name(), false);
711 plist.add (Properties::start, region->start());
712 plist.add (Properties::length, length);
713 plist.add (Properties::name, name);
714 plist.add (Properties::layer, region->layer());
716 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
717 add_region_internal (sub, pos, sub_num);
718 set_layer (sub, DBL_MAX);
722 possibly_splice_unlocked (position, (pos + length) - position, region);
726 Playlist::set_region_ownership ()
728 RegionWriteLock rl (this);
729 RegionList::iterator i;
730 boost::weak_ptr<Playlist> pl (shared_from_this());
732 for (i = regions.begin(); i != regions.end(); ++i) {
733 (*i)->set_playlist (pl);
738 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, const int32_t sub_num)
740 if (region->data_type() != _type) {
744 RegionSortByPosition cmp;
746 if (!first_set_state) {
747 boost::shared_ptr<Playlist> foo (shared_from_this());
748 region->set_playlist (boost::weak_ptr<Playlist>(foo));
751 region->set_position (position, sub_num);
753 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
754 all_regions.insert (region);
756 possibly_splice_unlocked (position, region->length(), region);
758 if (!holding_state ()) {
759 /* layers get assigned from XML state, and are not reset during undo/redo */
763 /* we need to notify the existence of new region before checking dependents. Ick. */
765 notify_region_added (region);
767 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
768 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
774 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
776 RegionWriteLock rlock (this);
778 bool old_sp = _splicing;
781 remove_region_internal (old);
782 add_region_internal (newr, pos);
783 set_layer (newr, old->layer ());
787 possibly_splice_unlocked (pos, old->length() - newr->length());
791 Playlist::remove_region (boost::shared_ptr<Region> region)
793 RegionWriteLock rlock (this);
794 remove_region_internal (region);
798 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
800 RegionList::iterator i;
804 region->set_playlist (boost::weak_ptr<Playlist>());
807 /* XXX should probably freeze here .... */
809 for (i = regions.begin(); i != regions.end(); ++i) {
812 framepos_t pos = (*i)->position();
813 framecnt_t distance = (*i)->length();
817 possibly_splice_unlocked (pos, -distance);
819 if (!holding_state ()) {
821 remove_dependents (region);
824 notify_region_removed (region);
833 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
835 if (Config->get_use_overlap_equivalency()) {
836 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
837 if ((*i)->overlap_equivalent (other)) {
838 results.push_back (*i);
842 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
843 if ((*i)->equivalent (other)) {
844 results.push_back (*i);
851 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
853 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
855 if ((*i) && (*i)->region_list_equivalent (other)) {
856 results.push_back (*i);
862 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
864 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
866 if ((*i) && (*i)->any_source_equivalent (other)) {
867 results.push_back (*i);
873 Playlist::partition (framepos_t start, framepos_t end, bool cut)
877 partition_internal (start, end, cut, thawlist);
879 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
880 (*i)->resume_property_changes ();
884 /** Go through each region on the playlist and cut them at start and end, removing the section between
885 * start and end if cutting == true. Regions that lie entirely within start and end are always
890 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
892 RegionList new_regions;
895 RegionWriteLock rlock (this);
897 boost::shared_ptr<Region> region;
898 boost::shared_ptr<Region> current;
900 RegionList::iterator tmp;
901 Evoral::OverlapType overlap;
902 framepos_t pos1, pos2, pos3, pos4;
906 /* need to work from a copy, because otherwise the regions we add during the process
907 get operated on as well.
910 RegionList copy = regions.rlist();
912 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
919 if (current->first_frame() >= start && current->last_frame() < end) {
922 remove_region_internal (current);
928 /* coverage will return OverlapStart if the start coincides
929 with the end point. we do not partition such a region,
930 so catch this special case.
933 if (current->first_frame() >= end) {
937 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
941 pos1 = current->position();
944 pos4 = current->last_frame();
946 if (overlap == Evoral::OverlapInternal) {
947 /* split: we need 3 new regions, the front, middle and end.
948 cut: we need 2 regions, the front and end.
953 ---------------*************************------------
956 ---------------*****++++++++++++++++====------------
958 ---------------*****----------------====------------
963 /* "middle" ++++++ */
965 RegionFactory::region_name (new_name, current->name(), false);
969 plist.add (Properties::start, current->start() + (pos2 - pos1));
970 plist.add (Properties::length, pos3 - pos2);
971 plist.add (Properties::name, new_name);
972 plist.add (Properties::layer, current->layer ());
973 plist.add (Properties::layering_index, current->layering_index ());
974 plist.add (Properties::automatic, true);
975 plist.add (Properties::left_of_split, true);
976 plist.add (Properties::right_of_split, true);
978 region = RegionFactory::create (current, plist);
979 add_region_internal (region, start);
980 new_regions.push_back (region);
985 RegionFactory::region_name (new_name, current->name(), false);
989 plist.add (Properties::start, current->start() + (pos3 - pos1));
990 plist.add (Properties::length, pos4 - pos3);
991 plist.add (Properties::name, new_name);
992 plist.add (Properties::layer, current->layer ());
993 plist.add (Properties::layering_index, current->layering_index ());
994 plist.add (Properties::automatic, true);
995 plist.add (Properties::right_of_split, true);
997 region = RegionFactory::create (current, plist);
999 add_region_internal (region, end);
1000 new_regions.push_back (region);
1004 current->suspend_property_changes ();
1005 thawlist.push_back (current);
1006 current->cut_end (pos2 - 1);
1008 } else if (overlap == Evoral::OverlapEnd) {
1012 ---------------*************************------------
1015 ---------------**************+++++++++++------------
1017 ---------------**************-----------------------
1024 RegionFactory::region_name (new_name, current->name(), false);
1028 plist.add (Properties::start, current->start() + (pos2 - pos1));
1029 plist.add (Properties::length, pos4 - pos2);
1030 plist.add (Properties::name, new_name);
1031 plist.add (Properties::layer, current->layer ());
1032 plist.add (Properties::layering_index, current->layering_index ());
1033 plist.add (Properties::automatic, true);
1034 plist.add (Properties::left_of_split, true);
1036 region = RegionFactory::create (current, plist);
1038 add_region_internal (region, start);
1039 new_regions.push_back (region);
1044 current->suspend_property_changes ();
1045 thawlist.push_back (current);
1046 current->cut_end (pos2 - 1);
1048 } else if (overlap == Evoral::OverlapStart) {
1050 /* split: we need 2 regions: the front and the end.
1051 cut: just trim current to skip the cut area
1056 ---------------*************************------------
1060 ---------------****+++++++++++++++++++++------------
1062 -------------------*********************------------
1068 RegionFactory::region_name (new_name, current->name(), false);
1072 plist.add (Properties::start, current->start());
1073 plist.add (Properties::length, pos3 - pos1);
1074 plist.add (Properties::name, new_name);
1075 plist.add (Properties::layer, current->layer ());
1076 plist.add (Properties::layering_index, current->layering_index ());
1077 plist.add (Properties::automatic, true);
1078 plist.add (Properties::right_of_split, true);
1080 region = RegionFactory::create (current, plist);
1082 add_region_internal (region, pos1);
1083 new_regions.push_back (region);
1088 current->suspend_property_changes ();
1089 thawlist.push_back (current);
1090 current->trim_front (pos3);
1091 } else if (overlap == Evoral::OverlapExternal) {
1093 /* split: no split required.
1094 cut: remove the region.
1099 ---------------*************************------------
1103 ---------------*************************------------
1105 ----------------------------------------------------
1110 remove_region_internal (current);
1113 new_regions.push_back (current);
1117 in_partition = false;
1120 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1121 framepos_t wanted_length = end-start;
1122 _end_space = wanted_length - get_extent().second-get_extent().first;
1125 boost::shared_ptr<Playlist>
1126 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1128 boost::shared_ptr<Playlist> ret;
1129 boost::shared_ptr<Playlist> pl;
1132 if (ranges.empty()) {
1133 return boost::shared_ptr<Playlist>();
1136 start = ranges.front().start;
1138 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1140 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1142 if (i == ranges.begin()) {
1146 /* paste the next section into the nascent playlist,
1147 offset to reflect the start of the first range we
1151 ret->paste (pl, (*i).start - start, 1.0f, 0);
1158 boost::shared_ptr<Playlist>
1159 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1161 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1162 return cut_copy (pmf, ranges, result_is_hidden);
1165 boost::shared_ptr<Playlist>
1166 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1168 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1169 return cut_copy (pmf, ranges, result_is_hidden);
1172 boost::shared_ptr<Playlist>
1173 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1175 boost::shared_ptr<Playlist> the_copy;
1176 RegionList thawlist;
1179 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1180 string new_name = _name;
1184 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1185 return boost::shared_ptr<Playlist>();
1188 partition_internal (start, start+cnt-1, true, thawlist);
1190 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1191 (*i)->resume_property_changes();
1197 boost::shared_ptr<Playlist>
1198 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1202 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1203 string new_name = _name;
1207 // 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... )
1209 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1213 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1215 times = fabs (times);
1218 RegionReadLock rl2 (other.get());
1220 int itimes = (int) floor (times);
1221 framepos_t pos = position;
1222 framecnt_t const shift = other->_get_extent().second;
1223 layer_t top = top_layer ();
1226 RegionWriteLock rl1 (this);
1228 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1229 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1231 /* put these new regions on top of all existing ones, but preserve
1232 the ordering they had in the original playlist.
1235 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1236 set_layer (copy_of_region, copy_of_region->layer() + top);
1248 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1250 duplicate(region, position, region->length(), times);
1253 /** @param gap from the beginning of the region to the next beginning */
1255 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1257 times = fabs (times);
1259 RegionWriteLock rl (this);
1260 int itimes = (int) floor (times);
1263 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1264 add_region_internal (copy, position);
1265 set_layer (copy, DBL_MAX);
1269 if (floor (times) != times) {
1270 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1272 RegionFactory::region_name (name, region->name(), false);
1277 plist.add (Properties::start, region->start());
1278 plist.add (Properties::length, length);
1279 plist.add (Properties::name, name);
1281 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1282 add_region_internal (sub, position);
1283 set_layer (sub, DBL_MAX);
1288 /** @param gap from the beginning of the region to the next beginning */
1289 /** @param end the first frame that does _not_ contain a duplicated frame */
1291 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1293 RegionWriteLock rl (this);
1295 while (position + region->length() - 1 < end) {
1296 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1297 add_region_internal (copy, position);
1298 set_layer (copy, DBL_MAX);
1302 if (position < end) {
1303 framecnt_t length = min (region->length(), end - position);
1305 RegionFactory::region_name (name, region->name(), false);
1310 plist.add (Properties::start, region->start());
1311 plist.add (Properties::length, length);
1312 plist.add (Properties::name, name);
1314 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1315 add_region_internal (sub, position);
1316 set_layer (sub, DBL_MAX);
1322 Playlist::duplicate_range (AudioRange& range, float times)
1324 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1325 framecnt_t offset = range.end - range.start;
1326 paste (pl, range.start + offset, times, 0);
1330 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1332 if (ranges.empty()) {
1336 framepos_t min_pos = max_framepos;
1337 framepos_t max_pos = 0;
1339 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1342 min_pos = min (min_pos, (*i).start);
1343 max_pos = max (max_pos, (*i).end);
1346 framecnt_t offset = max_pos - min_pos;
1349 int itimes = (int) floor (times);
1351 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1352 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1353 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1360 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1362 RegionWriteLock rlock (this);
1363 RegionList copy (regions.rlist());
1366 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1368 if ((*r)->last_frame() < at) {
1373 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1374 /* intersected region */
1375 if (!move_intersected) {
1380 /* do not move regions glued to music time - that
1381 has to be done separately.
1384 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1385 fixup.push_back (*r);
1389 (*r)->set_position ((*r)->position() + distance);
1392 /* XXX: may not be necessary; Region::post_set should do this, I think */
1393 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1394 (*r)->recompute_position_from_lock_style (0);
1399 Playlist::split (framepos_t at, const int32_t sub_num)
1401 RegionWriteLock rlock (this);
1402 RegionList copy (regions.rlist());
1404 /* use a copy since this operation can modify the region list
1407 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1408 _split_region (*r, at, sub_num);
1413 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1415 RegionWriteLock rl (this);
1416 _split_region (region, playlist_position, sub_num);
1420 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1422 if (!region->covers (playlist_position)) {
1426 if (region->position() == playlist_position ||
1427 region->last_frame() == playlist_position) {
1431 boost::shared_ptr<Region> left;
1432 boost::shared_ptr<Region> right;
1433 frameoffset_t before;
1434 frameoffset_t after;
1438 /* split doesn't change anything about length, so don't try to splice */
1440 bool old_sp = _splicing;
1443 before = playlist_position - region->position();
1444 after = region->length() - before;
1446 RegionFactory::region_name (before_name, region->name(), false);
1451 plist.add (Properties::length, before);
1452 plist.add (Properties::name, before_name);
1453 plist.add (Properties::left_of_split, true);
1454 plist.add (Properties::layering_index, region->layering_index ());
1455 plist.add (Properties::layer, region->layer ());
1457 /* note: we must use the version of ::create with an offset here,
1458 since it supplies that offset to the Region constructor, which
1459 is necessary to get audio region gain envelopes right.
1461 left = RegionFactory::create (region, 0, plist, true, sub_num);
1464 RegionFactory::region_name (after_name, region->name(), false);
1469 plist.add (Properties::length, after);
1470 plist.add (Properties::name, after_name);
1471 plist.add (Properties::right_of_split, true);
1472 plist.add (Properties::layering_index, region->layering_index ());
1473 plist.add (Properties::layer, region->layer ());
1475 /* same note as above */
1476 right = RegionFactory::create (region, before, plist, true, sub_num);
1479 add_region_internal (left, region->position());
1480 add_region_internal (right, region->position() + before);
1481 remove_region_internal (region);
1487 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1489 if (_splicing || in_set_state) {
1490 /* don't respond to splicing moves or state setting */
1494 if (_edit_mode == Splice) {
1495 splice_locked (at, distance, exclude);
1500 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1502 if (_splicing || in_set_state) {
1503 /* don't respond to splicing moves or state setting */
1507 if (_edit_mode == Splice) {
1508 splice_unlocked (at, distance, exclude);
1513 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1516 RegionWriteLock rl (this);
1517 core_splice (at, distance, exclude);
1522 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1524 core_splice (at, distance, exclude);
1528 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1532 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1534 if (exclude && (*i) == exclude) {
1538 if ((*i)->position() >= at) {
1539 framepos_t new_pos = (*i)->position() + distance;
1542 } else if (new_pos >= max_framepos - (*i)->length()) {
1543 new_pos = max_framepos - (*i)->length();
1546 (*i)->set_position (new_pos);
1552 notify_contents_changed ();
1556 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1559 RegionWriteLock rl (this);
1560 core_ripple (at, distance, exclude);
1565 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1567 core_ripple (at, distance, exclude);
1571 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1573 if (distance == 0) {
1578 RegionListProperty copy = regions;
1579 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1580 assert (i != copy.end());
1583 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1588 if ((*i)->position() >= at) {
1589 framepos_t new_pos = (*i)->position() + distance;
1590 framepos_t limit = max_framepos - (*i)->length();
1593 } else if (new_pos >= limit ) {
1597 (*i)->set_position (new_pos);
1602 notify_contents_changed ();
1607 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1609 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1613 if (what_changed.contains (Properties::position)) {
1615 /* remove it from the list then add it back in
1616 the right place again.
1619 RegionSortByPosition cmp;
1621 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1623 if (i == regions.end()) {
1624 /* the region bounds are being modified but its not currently
1625 in the region list. we will use its bounds correctly when/if
1632 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1635 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1637 frameoffset_t delta = 0;
1639 if (what_changed.contains (Properties::position)) {
1640 delta = region->position() - region->last_position();
1643 if (what_changed.contains (Properties::length)) {
1644 delta += region->length() - region->last_length();
1648 possibly_splice (region->last_position() + region->last_length(), delta, region);
1651 if (holding_state ()) {
1652 pending_bounds.push_back (region);
1654 notify_contents_changed ();
1656 list<Evoral::Range<framepos_t> > xf;
1657 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1658 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1659 coalesce_and_check_crossfades (xf);
1665 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1667 boost::shared_ptr<Region> region (weak_region.lock());
1673 /* this makes a virtual call to the right kind of playlist ... */
1675 region_changed (what_changed, region);
1679 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1681 PropertyChange our_interests;
1682 PropertyChange bounds;
1683 PropertyChange pos_and_length;
1686 if (in_set_state || in_flush) {
1690 our_interests.add (Properties::muted);
1691 our_interests.add (Properties::layer);
1692 our_interests.add (Properties::opaque);
1694 bounds.add (Properties::start);
1695 bounds.add (Properties::position);
1696 bounds.add (Properties::length);
1698 pos_and_length.add (Properties::position);
1699 pos_and_length.add (Properties::length);
1701 if (what_changed.contains (bounds)) {
1702 region_bounds_changed (what_changed, region);
1703 save = !(_splicing || _nudging);
1706 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1707 notify_region_moved (region);
1708 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1709 notify_region_end_trimmed (region);
1710 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1711 notify_region_start_trimmed (region);
1714 /* don't notify about layer changes, since we are the only object that can initiate
1715 them, and we notify in ::relayer()
1718 if (what_changed.contains (our_interests)) {
1722 mark_session_dirty ();
1728 Playlist::drop_regions ()
1730 RegionWriteLock rl (this);
1732 all_regions.clear ();
1736 Playlist::sync_all_regions_with_regions ()
1738 RegionWriteLock rl (this);
1740 all_regions.clear ();
1742 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1743 all_regions.insert (*i);
1748 Playlist::clear (bool with_signals)
1751 RegionWriteLock rl (this);
1753 region_state_changed_connections.drop_connections ();
1754 region_drop_references_connections.drop_connections ();
1756 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1757 pending_removes.insert (*i);
1762 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1763 remove_dependents (*s);
1769 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1770 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1773 pending_removes.clear ();
1774 pending_contents_change = false;
1780 /* *********************************************************************
1782 **********************************************************************/
1784 boost::shared_ptr<RegionList>
1785 Playlist::region_list()
1787 RegionReadLock rlock (this);
1788 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1793 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1795 RegionReadLock rlock (const_cast<Playlist*>(this));
1797 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1798 (*i)->deep_sources (sources);
1802 boost::shared_ptr<RegionList>
1803 Playlist::regions_at (framepos_t frame)
1805 RegionReadLock rlock (this);
1806 return find_regions_at (frame);
1810 Playlist::count_regions_at (framepos_t frame) const
1812 RegionReadLock rlock (const_cast<Playlist*>(this));
1815 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1816 if ((*i)->covers (frame)) {
1824 boost::shared_ptr<Region>
1825 Playlist::top_region_at (framepos_t frame)
1828 RegionReadLock rlock (this);
1829 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1830 boost::shared_ptr<Region> region;
1832 if (rlist->size()) {
1833 RegionSortByLayer cmp;
1835 region = rlist->back();
1841 boost::shared_ptr<Region>
1842 Playlist::top_unmuted_region_at (framepos_t frame)
1845 RegionReadLock rlock (this);
1846 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1848 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1850 RegionList::iterator tmp = i;
1854 if ((*i)->muted()) {
1861 boost::shared_ptr<Region> region;
1863 if (rlist->size()) {
1864 RegionSortByLayer cmp;
1866 region = rlist->back();
1872 boost::shared_ptr<RegionList>
1873 Playlist::find_regions_at (framepos_t frame)
1875 /* Caller must hold lock */
1877 boost::shared_ptr<RegionList> rlist (new RegionList);
1879 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1880 if ((*i)->covers (frame)) {
1881 rlist->push_back (*i);
1888 boost::shared_ptr<RegionList>
1889 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1891 RegionReadLock rlock (this);
1892 boost::shared_ptr<RegionList> rlist (new RegionList);
1894 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1895 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1896 rlist->push_back (*i);
1903 boost::shared_ptr<RegionList>
1904 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1906 RegionReadLock rlock (this);
1907 boost::shared_ptr<RegionList> rlist (new RegionList);
1909 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1910 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1911 rlist->push_back (*i);
1918 /** @param start Range start.
1919 * @param end Range end.
1920 * @return regions which have some part within this range.
1922 boost::shared_ptr<RegionList>
1923 Playlist::regions_touched (framepos_t start, framepos_t end)
1925 RegionReadLock rlock (this);
1926 return regions_touched_locked (start, end);
1929 boost::shared_ptr<RegionList>
1930 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1932 boost::shared_ptr<RegionList> rlist (new RegionList);
1934 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1935 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1936 rlist->push_back (*i);
1944 Playlist::find_next_transient (framepos_t from, int dir)
1946 RegionReadLock rlock (this);
1947 AnalysisFeatureList points;
1948 AnalysisFeatureList these_points;
1950 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1952 if ((*i)->last_frame() < from) {
1956 if ((*i)->first_frame() > from) {
1961 (*i)->get_transients (these_points);
1963 /* add first frame, just, err, because */
1965 these_points.push_back ((*i)->first_frame());
1967 points.insert (points.end(), these_points.begin(), these_points.end());
1968 these_points.clear ();
1971 if (points.empty()) {
1975 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1976 bool reached = false;
1979 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1984 if (reached && (*x) > from) {
1989 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1994 if (reached && (*x) < from) {
2003 boost::shared_ptr<Region>
2004 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2006 RegionReadLock rlock (this);
2007 boost::shared_ptr<Region> ret;
2008 framepos_t closest = max_framepos;
2010 bool end_iter = false;
2012 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2016 frameoffset_t distance;
2017 boost::shared_ptr<Region> r = (*i);
2022 pos = r->first_frame ();
2025 pos = r->last_frame ();
2028 pos = r->sync_position ();
2033 case 1: /* forwards */
2036 if ((distance = pos - frame) < closest) {
2045 default: /* backwards */
2048 if ((distance = frame - pos) < closest) {
2064 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2066 RegionReadLock rlock (this);
2068 framepos_t closest = max_framepos;
2069 framepos_t ret = -1;
2073 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2075 boost::shared_ptr<Region> r = (*i);
2076 frameoffset_t distance;
2077 const framepos_t first_frame = r->first_frame();
2078 const framepos_t last_frame = r->last_frame();
2080 if (first_frame > frame) {
2082 distance = first_frame - frame;
2084 if (distance < closest) {
2090 if (last_frame > frame) {
2092 distance = last_frame - frame;
2094 if (distance < closest) {
2103 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2105 boost::shared_ptr<Region> r = (*i);
2106 frameoffset_t distance;
2107 const framepos_t first_frame = r->first_frame();
2108 const framepos_t last_frame = r->last_frame();
2110 if (last_frame < frame) {
2112 distance = frame - last_frame;
2114 if (distance < closest) {
2120 if (first_frame < frame) {
2122 distance = frame - first_frame;
2124 if (distance < closest) {
2136 /***********************************************************************/
2142 Playlist::mark_session_dirty ()
2144 if (!in_set_state && !holding_state ()) {
2145 _session.set_dirty();
2150 Playlist::rdiff (vector<Command*>& cmds) const
2152 RegionReadLock rlock (const_cast<Playlist *> (this));
2153 Stateful::rdiff (cmds);
2157 Playlist::clear_owned_changes ()
2159 RegionReadLock rlock (this);
2160 Stateful::clear_owned_changes ();
2164 Playlist::update (const RegionListProperty::ChangeRecord& change)
2166 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2167 name(), change.added.size(), change.removed.size()));
2170 /* add the added regions */
2171 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2172 add_region_internal ((*i), (*i)->position());
2174 /* remove the removed regions */
2175 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2183 Playlist::set_state (const XMLNode& node, int version)
2187 XMLNodeConstIterator niter;
2188 XMLPropertyList plist;
2189 XMLPropertyConstIterator piter;
2190 XMLProperty const * prop;
2191 boost::shared_ptr<Region> region;
2193 bool seen_region_nodes = false;
2198 if (node.name() != "Playlist") {
2205 plist = node.properties();
2209 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2213 if (prop->name() == X_("name")) {
2214 _name = prop->value();
2216 } else if (prop->name() == X_("orig-diskstream-id")) {
2217 /* XXX legacy session: fix up later */
2218 _orig_track_id = prop->value ();
2219 } else if (prop->name() == X_("orig-track-id")) {
2220 _orig_track_id = prop->value ();
2221 } else if (prop->name() == X_("frozen")) {
2222 _frozen = string_is_affirmative (prop->value());
2223 } else if (prop->name() == X_("combine-ops")) {
2224 _combine_ops = atoi (prop->value());
2230 nlist = node.children();
2232 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2236 if (child->name() == "Region") {
2238 seen_region_nodes = true;
2240 if ((prop = child->property ("id")) == 0) {
2241 error << _("region state node has no ID, ignored") << endmsg;
2245 ID id = prop->value ();
2247 if ((region = region_by_id (id))) {
2249 region->suspend_property_changes ();
2251 if (region->set_state (*child, version)) {
2252 region->resume_property_changes ();
2256 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2257 region->suspend_property_changes ();
2259 error << _("Playlist: cannot create region from XML") << endmsg;
2264 RegionWriteLock rlock (this);
2265 add_region_internal (region, region->position());
2268 region->resume_property_changes ();
2273 if (seen_region_nodes && regions.empty()) {
2278 notify_contents_changed ();
2281 first_set_state = false;
2287 Playlist::get_state()
2289 return state (true);
2293 Playlist::get_template()
2295 return state (false);
2298 /** @param full_state true to include regions in the returned state, otherwise false.
2301 Playlist::state (bool full_state)
2303 XMLNode *node = new XMLNode (X_("Playlist"));
2306 node->add_property (X_("id"), id().to_s());
2307 node->add_property (X_("name"), _name);
2308 node->add_property (X_("type"), _type.to_string());
2310 _orig_track_id.print (buf, sizeof (buf));
2311 node->add_property (X_("orig-track-id"), buf);
2312 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2315 RegionReadLock rlock (this);
2317 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2318 node->add_property ("combine-ops", buf);
2320 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2321 node->add_child_nocopy ((*i)->get_state());
2326 node->add_child_copy (*_extra_xml);
2333 Playlist::empty() const
2335 RegionReadLock rlock (const_cast<Playlist *>(this));
2336 return regions.empty();
2340 Playlist::n_regions() const
2342 RegionReadLock rlock (const_cast<Playlist *>(this));
2343 return regions.size();
2346 /** @return true if the all_regions list is empty, ie this playlist
2347 * has never had a region added to it.
2350 Playlist::all_regions_empty() const
2352 RegionReadLock rl (const_cast<Playlist *> (this));
2353 return all_regions.empty();
2356 pair<framepos_t, framepos_t>
2357 Playlist::get_extent () const
2359 RegionReadLock rlock (const_cast<Playlist *>(this));
2360 return _get_extent ();
2363 pair<framepos_t, framepos_t>
2364 Playlist::get_extent_with_endspace () const
2366 pair<framepos_t, framepos_t> l = get_extent();
2367 l.second += _end_space;
2371 pair<framepos_t, framepos_t>
2372 Playlist::_get_extent () const
2374 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2376 if (regions.empty()) {
2381 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2382 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2383 if (e.first < ext.first) {
2384 ext.first = e.first;
2386 if (e.second > ext.second) {
2387 ext.second = e.second;
2395 Playlist::bump_name (string name, Session &session)
2397 string newname = name;
2400 newname = bump_name_once (newname, '.');
2401 } while (session.playlists->by_name (newname)!=NULL);
2408 Playlist::top_layer() const
2410 RegionReadLock rlock (const_cast<Playlist *> (this));
2413 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2414 top = max (top, (*i)->layer());
2420 Playlist::set_edit_mode (EditMode mode)
2425 struct RelayerSort {
2426 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2427 return a->layering_index() < b->layering_index();
2431 /** Set a new layer for a region. This adjusts the layering indices of all
2432 * regions in the playlist to put the specified region in the appropriate
2433 * place. The actual layering will be fixed up when relayer() happens.
2437 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2439 /* Remove the layer we are setting from our region list, and sort it
2440 * using the layer indeces.
2443 RegionList copy = regions.rlist();
2444 copy.remove (region);
2445 copy.sort (RelayerSort ());
2447 /* Put region back in the right place */
2448 RegionList::iterator i = copy.begin();
2449 while (i != copy.end ()) {
2450 if ((*i)->layer() > new_layer) {
2456 copy.insert (i, region);
2458 setup_layering_indices (copy);
2462 Playlist::setup_layering_indices (RegionList const & regions)
2466 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2467 (*k)->set_layering_index (j++);
2471 struct LaterHigherSort {
2472 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2473 return a->position() < b->position();
2477 /** Take the layering indices of each of our regions, compute the layers
2478 * that they should be on, and write the layers back to the regions.
2481 Playlist::relayer ()
2483 /* never compute layers when setting from XML */
2489 /* Build up a new list of regions on each layer, stored in a set of lists
2490 each of which represent some period of time on some layer. The idea
2491 is to avoid having to search the entire region list to establish whether
2492 each region overlaps another */
2494 /* how many pieces to divide this playlist's time up into */
2495 int const divisions = 512;
2497 /* find the start and end positions of the regions on this playlist */
2498 framepos_t start = INT64_MAX;
2500 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2501 start = min (start, (*i)->position());
2502 end = max (end, (*i)->position() + (*i)->length());
2505 /* hence the size of each time division */
2506 double const division_size = (end - start) / double (divisions);
2508 vector<vector<RegionList> > layers;
2509 layers.push_back (vector<RegionList> (divisions));
2511 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2512 RegionList copy = regions.rlist();
2513 switch (Config->get_layer_model()) {
2515 copy.sort (LaterHigherSort ());
2518 copy.sort (RelayerSort ());
2522 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2523 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2524 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2527 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2529 /* find the time divisions that this region covers; if there are no regions on the list,
2530 division_size will equal 0 and in this case we'll just say that
2531 start_division = end_division = 0.
2533 int start_division = 0;
2534 int end_division = 0;
2536 if (division_size > 0) {
2537 start_division = floor ( ((*i)->position() - start) / division_size);
2538 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2539 if (end_division == divisions) {
2544 assert (divisions == 0 || end_division < divisions);
2546 /* find the lowest layer that this region can go on */
2547 size_t j = layers.size();
2549 /* try layer j - 1; it can go on if it overlaps no other region
2550 that is already on that layer
2553 bool overlap = false;
2554 for (int k = start_division; k <= end_division; ++k) {
2555 RegionList::iterator l = layers[j-1][k].begin ();
2556 while (l != layers[j-1][k].end()) {
2557 if ((*l)->overlap_equivalent (*i)) {
2570 /* overlap, so we must use layer j */
2577 if (j == layers.size()) {
2578 /* we need a new layer for this region */
2579 layers.push_back (vector<RegionList> (divisions));
2582 /* put a reference to this region in each of the divisions that it exists in */
2583 for (int k = start_division; k <= end_division; ++k) {
2584 layers[j][k].push_back (*i);
2587 (*i)->set_layer (j);
2590 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2591 relayering because we just removed the only region on the top layer, nothing will
2592 appear to have changed, but the StreamView must still sort itself out. We could
2593 probably keep a note of the top layer last time we relayered, and check that,
2594 but premature optimisation &c...
2596 notify_layering_changed ();
2598 /* This relayer() may have been called as a result of a region removal, in which
2599 case we need to setup layering indices to account for the one that has just
2602 setup_layering_indices (copy);
2606 Playlist::raise_region (boost::shared_ptr<Region> region)
2608 set_layer (region, region->layer() + 1.5);
2613 Playlist::lower_region (boost::shared_ptr<Region> region)
2615 set_layer (region, region->layer() - 1.5);
2620 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2622 set_layer (region, DBL_MAX);
2627 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2629 set_layer (region, -0.5);
2634 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2636 RegionList::iterator i;
2642 RegionWriteLock rlock (const_cast<Playlist *> (this));
2644 for (i = regions.begin(); i != regions.end(); ++i) {
2646 if ((*i)->position() >= start) {
2652 if ((*i)->last_frame() > max_framepos - distance) {
2653 new_pos = max_framepos - (*i)->length();
2655 new_pos = (*i)->position() + distance;
2660 if ((*i)->position() > distance) {
2661 new_pos = (*i)->position() - distance;
2667 (*i)->set_position (new_pos);
2675 notify_contents_changed ();
2681 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2683 RegionReadLock rlock (const_cast<Playlist*> (this));
2685 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2686 /* Note: passing the second argument as false can cause at best
2687 incredibly deep and time-consuming recursion, and at worst
2688 cycles if the user has managed to create cycles of reference
2689 between compound regions. We generally only this during
2690 cleanup, and @param shallow is passed as true.
2692 if ((*r)->uses_source (src, shallow)) {
2701 boost::shared_ptr<Region>
2702 Playlist::find_region (const ID& id) const
2704 RegionReadLock rlock (const_cast<Playlist*> (this));
2706 /* searches all regions currently in use by the playlist */
2708 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2709 if ((*i)->id() == id) {
2714 return boost::shared_ptr<Region> ();
2718 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2720 RegionReadLock rlock (const_cast<Playlist*> (this));
2723 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2729 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2730 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2731 /* check if region is used in a compound */
2732 if (it->second == r) {
2733 /* region is referenced as 'original' of a compound */
2737 if (r->whole_file() && r->max_source_level() > 0) {
2738 /* region itself ia a compound.
2739 * the compound regions are not referenced -> check regions inside compound
2741 const SourceList& sl = r->sources();
2742 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2743 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2745 if (ps->playlist()->region_use_count(it->first)) {
2746 // break out of both loops
2755 boost::shared_ptr<Region>
2756 Playlist::region_by_id (const ID& id) const
2758 /* searches all regions ever added to this playlist */
2760 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2761 if ((*i)->id() == id) {
2765 return boost::shared_ptr<Region> ();
2769 Playlist::dump () const
2771 boost::shared_ptr<Region> r;
2773 cerr << "Playlist \"" << _name << "\" " << endl
2774 << regions.size() << " regions "
2777 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2779 cerr << " " << r->name() << " ["
2780 << r->start() << "+" << r->length()
2790 Playlist::set_frozen (bool yn)
2796 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2800 if (region->locked()) {
2807 RegionWriteLock rlock (const_cast<Playlist*> (this));
2812 RegionList::iterator next;
2814 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2815 if ((*i) == region) {
2819 if (next != regions.end()) {
2821 if ((*next)->locked()) {
2827 if ((*next)->position() != region->last_frame() + 1) {
2828 /* they didn't used to touch, so after shuffle,
2829 just have them swap positions.
2831 new_pos = (*next)->position();
2833 /* they used to touch, so after shuffle,
2834 make sure they still do. put the earlier
2835 region where the later one will end after
2838 new_pos = region->position() + (*next)->length();
2841 (*next)->set_position (region->position());
2842 region->set_position (new_pos);
2844 /* avoid a full sort */
2846 regions.erase (i); // removes the region from the list */
2848 regions.insert (next, region); // adds it back after next
2857 RegionList::iterator prev = regions.end();
2859 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2860 if ((*i) == region) {
2862 if (prev != regions.end()) {
2864 if ((*prev)->locked()) {
2869 if (region->position() != (*prev)->last_frame() + 1) {
2870 /* they didn't used to touch, so after shuffle,
2871 just have them swap positions.
2873 new_pos = region->position();
2875 /* they used to touch, so after shuffle,
2876 make sure they still do. put the earlier
2877 one where the later one will end after
2879 new_pos = (*prev)->position() + region->length();
2882 region->set_position ((*prev)->position());
2883 (*prev)->set_position (new_pos);
2885 /* avoid a full sort */
2887 regions.erase (i); // remove region
2888 regions.insert (prev, region); // insert region before prev
2904 notify_contents_changed();
2910 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2912 RegionReadLock rlock (const_cast<Playlist*> (this));
2914 if (regions.size() > 1) {
2922 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2924 ripple_locked (at, distance, exclude);
2928 Playlist::update_after_tempo_map_change ()
2930 RegionWriteLock rlock (const_cast<Playlist*> (this));
2931 RegionList copy (regions.rlist());
2935 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2936 (*i)->update_after_tempo_map_change ();
2938 /* possibly causes a contents changed notification (flush_notifications()) */
2943 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2945 RegionReadLock rl (this);
2946 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2952 Playlist::has_region_at (framepos_t const p) const
2954 RegionReadLock (const_cast<Playlist *> (this));
2956 RegionList::const_iterator i = regions.begin ();
2957 while (i != regions.end() && !(*i)->covers (p)) {
2961 return (i != regions.end());
2964 /** Look from a session frame time and find the start time of the next region
2965 * which is on the top layer of this playlist.
2966 * @param t Time to look from.
2967 * @return Position of next top-layered region, or max_framepos if there isn't one.
2970 Playlist::find_next_top_layer_position (framepos_t t) const
2972 RegionReadLock rlock (const_cast<Playlist *> (this));
2974 layer_t const top = top_layer ();
2976 RegionList copy = regions.rlist ();
2977 copy.sort (RegionSortByPosition ());
2979 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2980 if ((*i)->position() >= t && (*i)->layer() == top) {
2981 return (*i)->position();
2985 return max_framepos;
2988 boost::shared_ptr<Region>
2989 Playlist::combine (const RegionList& r)
2992 uint32_t channels = 0;
2994 framepos_t earliest_position = max_framepos;
2995 vector<TwoRegions> old_and_new_regions;
2996 vector<boost::shared_ptr<Region> > originals;
2997 vector<boost::shared_ptr<Region> > copies;
3000 uint32_t max_level = 0;
3002 /* find the maximum depth of all the regions we're combining */
3004 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3005 max_level = max (max_level, (*i)->max_source_level());
3008 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3009 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3011 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3013 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3014 earliest_position = min (earliest_position, (*i)->position());
3017 /* enable this so that we do not try to create xfades etc. as we add
3021 pl->in_partition = true;
3023 /* sort by position then layer.
3024 * route_time_axis passes 'selected_regions' - which is not sorted.
3025 * here we need the top-most first, then every layer's region sorted by position.
3027 RegionList sorted(r);
3028 sorted.sort(RegionSortByLayerAndPosition());
3030 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3032 /* copy the region */
3034 boost::shared_ptr<Region> original_region = (*i);
3035 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3037 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3038 originals.push_back (original_region);
3039 copies.push_back (copied_region);
3041 RegionFactory::add_compound_association (original_region, copied_region);
3043 /* make position relative to zero */
3045 pl->add_region (copied_region, original_region->position() - earliest_position);
3046 copied_region->set_layer (original_region->layer ());
3048 /* use the maximum number of channels for any region */
3050 channels = max (channels, original_region->n_channels());
3052 /* it will go above the layer of the highest existing region */
3054 layer = max (layer, original_region->layer());
3057 pl->in_partition = false;
3059 pre_combine (copies);
3061 /* now create a new PlaylistSource for each channel in the new playlist */
3064 pair<framepos_t,framepos_t> extent = pl->get_extent();
3066 for (uint32_t chn = 0; chn < channels; ++chn) {
3067 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3071 /* now a new whole-file region using the list of sources */
3073 plist.add (Properties::start, 0);
3074 plist.add (Properties::length, extent.second);
3075 plist.add (Properties::name, parent_name);
3076 plist.add (Properties::whole_file, true);
3078 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3080 /* now the non-whole-file region that we will actually use in the
3085 plist.add (Properties::start, 0);
3086 plist.add (Properties::length, extent.second);
3087 plist.add (Properties::name, child_name);
3088 plist.add (Properties::layer, layer+1);
3090 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3092 /* remove all the selected regions from the current playlist
3097 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3101 /* do type-specific stuff with the originals and the new compound
3105 post_combine (originals, compound_region);
3107 /* add the new region at the right location */
3109 add_region (compound_region, earliest_position);
3115 return compound_region;
3119 Playlist::uncombine (boost::shared_ptr<Region> target)
3121 boost::shared_ptr<PlaylistSource> pls;
3122 boost::shared_ptr<const Playlist> pl;
3123 vector<boost::shared_ptr<Region> > originals;
3124 vector<TwoRegions> old_and_new_regions;
3126 // (1) check that its really a compound region
3128 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3132 pl = pls->playlist();
3134 framepos_t adjusted_start = 0; // gcc isn't smart enough
3135 framepos_t adjusted_end = 0; // gcc isn't smart enough
3137 /* the leftmost (earliest) edge of the compound region
3138 starts at zero in its source, or larger if it
3139 has been trimmed or content-scrolled.
3141 the rightmost (latest) edge of the compound region
3142 relative to its source is the starting point plus
3143 the length of the region.
3146 // (2) get all the original regions
3148 const RegionList& rl (pl->region_list_property().rlist());
3149 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3150 frameoffset_t move_offset = 0;
3152 /* there are two possibilities here:
3153 1) the playlist that the playlist source was based on
3154 is us, so just add the originals (which belonged to
3155 us anyway) back in the right place.
3157 2) the playlist that the playlist source was based on
3158 is NOT us, so we need to make copies of each of
3159 the original regions that we find, and add them
3162 bool same_playlist = (pls->original() == id());
3164 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3166 boost::shared_ptr<Region> current (*i);
3168 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3170 if (ca == cassocs.end()) {
3174 boost::shared_ptr<Region> original (ca->second);
3176 bool modified_region;
3178 if (i == rl.begin()) {
3179 move_offset = (target->position() - original->position()) - target->start();
3180 adjusted_start = original->position() + target->start();
3181 adjusted_end = adjusted_start + target->length();
3184 if (!same_playlist) {
3185 framepos_t pos = original->position();
3186 /* make a copy, but don't announce it */
3187 original = RegionFactory::create (original, false);
3188 /* the pure copy constructor resets position() to zero,
3191 original->set_position (pos);
3194 /* check to see how the original region (in the
3195 * playlist before compounding occurred) overlaps
3196 * with the new state of the compound region.
3199 original->clear_changes ();
3200 modified_region = false;
3202 switch (original->coverage (adjusted_start, adjusted_end)) {
3203 case Evoral::OverlapNone:
3204 /* original region does not cover any part
3205 of the current state of the compound region
3209 case Evoral::OverlapInternal:
3210 /* overlap is just a small piece inside the
3211 * original so trim both ends
3213 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3214 modified_region = true;
3217 case Evoral::OverlapExternal:
3218 /* overlap fully covers original, so leave it
3223 case Evoral::OverlapEnd:
3224 /* overlap starts within but covers end,
3225 so trim the front of the region
3227 original->trim_front (adjusted_start);
3228 modified_region = true;
3231 case Evoral::OverlapStart:
3232 /* overlap covers start but ends within, so
3233 * trim the end of the region.
3235 original->trim_end (adjusted_end);
3236 modified_region = true;
3241 /* fix the position to match any movement of the compound region.
3243 original->set_position (original->position() + move_offset);
3244 modified_region = true;
3247 if (modified_region) {
3248 _session.add_command (new StatefulDiffCommand (original));
3251 /* and add to the list of regions waiting to be
3255 originals.push_back (original);
3256 old_and_new_regions.push_back (TwoRegions (*i, original));
3259 pre_uncombine (originals, target);
3261 in_partition = true;
3264 // (3) remove the compound region
3266 remove_region (target);
3268 // (4) add the constituent regions
3270 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3271 add_region ((*i), (*i)->position());
3272 set_layer((*i), (*i)->layer());
3273 if (!RegionFactory::region_by_id((*i)->id())) {
3274 RegionFactory::map_add(*i);
3278 in_partition = false;
3283 Playlist::fade_range (list<AudioRange>& ranges)
3285 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3286 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3287 (*i)->fade_range ((*r).start, (*r).end);
3293 Playlist::max_source_level () const
3295 RegionReadLock rlock (const_cast<Playlist *> (this));
3298 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3299 lvl = max (lvl, (*i)->max_source_level());
3306 Playlist::set_orig_track_id (const PBD::ID& id)
3308 _orig_track_id = id;
3311 /** Take a list of ranges, coalesce any that can be coalesced, then call
3312 * check_crossfades for each one.
3315 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3317 /* XXX: it's a shame that this coalesce algorithm also exists in
3318 TimeSelection::consolidate().
3321 /* XXX: xfade: this is implemented in Evoral::RangeList */
3324 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3325 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3331 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3332 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3333 i->from = min (i->from, j->from);
3334 i->to = max (i->to, j->to);
3343 Playlist::set_capture_insertion_in_progress (bool yn)
3345 _capture_insertion_underway = yn;