2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <boost/lexical_cast.hpp>
27 #include "pbd/convert.h"
28 #include "pbd/stateful_diff_command.h"
29 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/playlist.h"
33 #include "ardour/session.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/region_sorters.h"
37 #include "ardour/playlist_factory.h"
38 #include "ardour/playlist_source.h"
39 #include "ardour/transient_detector.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
67 Playlist::make_property_quarks ()
69 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
70 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
71 Properties::regions.property_id));
74 RegionListProperty::RegionListProperty (Playlist& pl)
75 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
81 RegionListProperty::RegionListProperty (RegionListProperty const & p)
82 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
83 , _playlist (p._playlist)
89 RegionListProperty::clone () const
91 return new RegionListProperty (*this);
95 RegionListProperty::create () const
97 return new RegionListProperty (_playlist);
101 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
103 /* All regions (even those which are deleted) have their state saved by other
104 code, so we can just store ID here.
107 node.add_property ("id", region->id().to_s ());
110 boost::shared_ptr<Region>
111 RegionListProperty::get_content_from_xml (XMLNode const & node) const
113 XMLProperty const * prop = node.property ("id");
116 PBD::ID id (prop->value ());
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
128 : SessionObject(sess, nom)
133 first_set_state = false;
138 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
139 : SessionObject(sess, "unnamed playlist")
144 XMLProperty const * prop = node.property("type");
145 assert(!prop || DataType(prop->value()) == _type);
149 _name = "unnamed"; /* reset by set_state */
152 /* set state called by derived class */
155 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
156 : SessionObject(other->_session, namestr)
158 , _type(other->_type)
159 , _orig_track_id (other->_orig_track_id)
164 other->copy_regions (tmp);
168 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
169 add_region_internal( (*x), (*x)->position());
174 _splicing = other->_splicing;
175 _rippling = other->_rippling;
176 _nudging = other->_nudging;
177 _edit_mode = other->_edit_mode;
180 first_set_state = false;
182 in_partition = false;
184 _frozen = other->_frozen;
187 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
188 : SessionObject(other->_session, str)
190 , _type(other->_type)
191 , _orig_track_id (other->_orig_track_id)
193 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
195 framepos_t end = start + cnt - 1;
201 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
203 boost::shared_ptr<Region> region;
204 boost::shared_ptr<Region> new_region;
205 frameoffset_t offset = 0;
206 framepos_t position = 0;
209 Evoral::OverlapType overlap;
213 overlap = region->coverage (start, end);
216 case Evoral::OverlapNone:
219 case Evoral::OverlapInternal:
220 offset = start - region->position();
225 case Evoral::OverlapStart:
227 position = region->position() - start;
228 len = end - region->position();
231 case Evoral::OverlapEnd:
232 offset = start - region->position();
234 len = region->length() - offset;
237 case Evoral::OverlapExternal:
239 position = region->position() - start;
240 len = region->length();
244 RegionFactory::region_name (new_name, region->name(), false);
248 plist.add (Properties::start, region->start() + offset);
249 plist.add (Properties::length, len);
250 plist.add (Properties::name, new_name);
251 plist.add (Properties::layer, region->layer());
252 plist.add (Properties::layering_index, region->layering_index());
254 new_region = RegionFactory::create (region, plist);
256 add_region_internal (new_region, position);
259 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
260 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
261 _end_space = cnt - (get_extent().second - get_extent().first);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionReadLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (RegionFactory::create (*i, true));
297 Playlist::init (bool hide)
299 add_property (regions);
300 _xml_node_name = X_("Playlist");
302 g_atomic_int_set (&block_notifications, 0);
303 g_atomic_int_set (&ignore_state_changes, 0);
304 pending_contents_change = false;
305 pending_layering = false;
306 first_set_state = true;
315 _edit_mode = Config->get_edit_mode();
317 in_partition = false;
320 _capture_insertion_underway = false;
324 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
325 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
327 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
330 Playlist::~Playlist ()
332 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
335 RegionReadLock rl (this);
337 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
338 (*i)->set_playlist (boost::shared_ptr<Playlist>());
342 /* GoingAway must be emitted by derived classes */
346 Playlist::_set_sort_id ()
349 Playlists are given names like <track name>.<id>
350 or <track name>.<edit group name>.<id> where id
351 is an integer. We extract the id and sort by that.
354 size_t dot_position = _name.val().find_last_of(".");
356 if (dot_position == string::npos) {
359 string t = _name.val().substr(dot_position + 1);
362 _sort_id = boost::lexical_cast<int>(t);
365 catch (boost::bad_lexical_cast e) {
372 Playlist::set_name (const string& str)
374 /* in a typical situation, a playlist is being used
375 by one diskstream and also is referenced by the
376 Session. if there are more references than that,
377 then don't change the name.
384 bool ret = SessionObject::set_name(str);
391 /***********************************************************************
392 CHANGE NOTIFICATION HANDLING
394 Notifications must be delayed till the region_lock is released. This
395 is necessary because handlers for the signals may need to acquire
396 the lock (e.g. to read from the playlist).
397 ***********************************************************************/
400 Playlist::begin_undo ()
407 Playlist::end_undo ()
416 delay_notifications ();
417 g_atomic_int_inc (&ignore_state_changes);
420 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
422 Playlist::thaw (bool from_undo)
424 g_atomic_int_dec_and_test (&ignore_state_changes);
425 release_notifications (from_undo);
430 Playlist::delay_notifications ()
432 g_atomic_int_inc (&block_notifications);
435 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
437 Playlist::release_notifications (bool from_undo)
439 if (g_atomic_int_dec_and_test (&block_notifications)) {
440 flush_notifications (from_undo);
445 Playlist::notify_contents_changed ()
447 if (holding_state ()) {
448 pending_contents_change = true;
450 pending_contents_change = false;
451 ContentsChanged(); /* EMIT SIGNAL */
456 Playlist::notify_layering_changed ()
458 if (holding_state ()) {
459 pending_layering = true;
461 pending_layering = false;
462 LayeringChanged(); /* EMIT SIGNAL */
467 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
469 if (holding_state ()) {
470 pending_removes.insert (r);
471 pending_contents_change = true;
473 /* this might not be true, but we have to act
474 as though it could be.
476 pending_contents_change = false;
477 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
478 ContentsChanged (); /* EMIT SIGNAL */
483 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
485 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
487 if (holding_state ()) {
489 pending_range_moves.push_back (move);
493 list< Evoral::RangeMove<framepos_t> > m;
495 RangesMoved (m, false);
501 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
503 if (r->position() >= r->last_position()) {
504 /* trimmed shorter */
508 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
510 if (holding_state ()) {
512 pending_region_extensions.push_back (extra);
516 list<Evoral::Range<framepos_t> > r;
524 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
526 if (r->length() < r->last_length()) {
527 /* trimmed shorter */
530 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
532 if (holding_state ()) {
534 pending_region_extensions.push_back (extra);
538 list<Evoral::Range<framepos_t> > r;
546 Playlist::notify_region_added (boost::shared_ptr<Region> r)
548 /* the length change might not be true, but we have to act
549 as though it could be.
552 if (holding_state()) {
553 pending_adds.insert (r);
554 pending_contents_change = true;
557 pending_contents_change = false;
558 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
559 ContentsChanged (); /* EMIT SIGNAL */
564 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
566 Playlist::flush_notifications (bool from_undo)
568 set<boost::shared_ptr<Region> >::iterator s;
569 bool regions_changed = false;
577 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
578 regions_changed = true;
581 /* XXX: it'd be nice if we could use pending_bounds for
582 RegionsExtended and RegionsMoved.
585 /* we have no idea what order the regions ended up in pending
586 bounds (it could be based on selection order, for example).
587 so, to preserve layering in the "most recently moved is higher"
588 model, sort them by existing layer, then timestamp them.
591 // RegionSortByLayer cmp;
592 // pending_bounds.sort (cmp);
594 list<Evoral::Range<framepos_t> > crossfade_ranges;
596 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
597 crossfade_ranges.push_back ((*r)->last_range ());
598 crossfade_ranges.push_back ((*r)->range ());
601 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
602 crossfade_ranges.push_back ((*s)->range ());
603 remove_dependents (*s);
604 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
607 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
608 crossfade_ranges.push_back ((*s)->range ());
609 /* don't emit RegionAdded signal until relayering is done,
610 so that the region is fully setup by the time
611 anyone hears that its been added
615 /* notify about contents/region changes first so that layering changes
616 * in a UI will take place on the new contents.
619 if (regions_changed || pending_contents_change) {
620 pending_layering = true;
621 ContentsChanged (); /* EMIT SIGNAL */
624 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
625 (*s)->clear_changes ();
626 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
629 if ((regions_changed && !in_set_state) || pending_layering) {
633 coalesce_and_check_crossfades (crossfade_ranges);
635 if (!pending_range_moves.empty ()) {
636 /* We don't need to check crossfades for these as pending_bounds has
639 RangesMoved (pending_range_moves, from_undo);
642 if (!pending_region_extensions.empty ()) {
643 RegionsExtended (pending_region_extensions);
652 Playlist::clear_pending ()
654 pending_adds.clear ();
655 pending_removes.clear ();
656 pending_bounds.clear ();
657 pending_range_moves.clear ();
658 pending_region_extensions.clear ();
659 pending_contents_change = false;
660 pending_layering = false;
663 /*************************************************************
665 *************************************************************/
667 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
669 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, const int32_t sub_num)
671 RegionWriteLock rlock (this);
672 times = fabs (times);
674 int itimes = (int) floor (times);
676 framepos_t pos = position;
678 if (times == 1 && auto_partition){
680 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
681 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
682 (*i)->resume_property_changes ();
687 add_region_internal (region, pos, sub_num);
688 set_layer (region, DBL_MAX);
689 pos += region->length();
694 /* note that itimes can be zero if we being asked to just
695 insert a single fraction of the region.
698 for (int i = 0; i < itimes; ++i) {
699 boost::shared_ptr<Region> copy = RegionFactory::create (region, true, sub_num);
700 add_region_internal (copy, pos, sub_num);
701 set_layer (copy, DBL_MAX);
702 pos += region->length();
705 framecnt_t length = 0;
707 if (floor (times) != times) {
708 length = (framecnt_t) floor (region->length() * (times - floor (times)));
710 RegionFactory::region_name (name, region->name(), false);
715 plist.add (Properties::start, region->start());
716 plist.add (Properties::length, length);
717 plist.add (Properties::name, name);
718 plist.add (Properties::layer, region->layer());
720 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
721 add_region_internal (sub, pos, sub_num);
722 set_layer (sub, DBL_MAX);
726 possibly_splice_unlocked (position, (pos + length) - position, region);
730 Playlist::set_region_ownership ()
732 RegionWriteLock rl (this);
733 RegionList::iterator i;
734 boost::weak_ptr<Playlist> pl (shared_from_this());
736 for (i = regions.begin(); i != regions.end(); ++i) {
737 (*i)->set_playlist (pl);
742 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, const int32_t sub_num)
744 if (region->data_type() != _type) {
748 RegionSortByPosition cmp;
750 if (!first_set_state) {
751 boost::shared_ptr<Playlist> foo (shared_from_this());
752 region->set_playlist (boost::weak_ptr<Playlist>(foo));
755 region->set_position (position, sub_num);
757 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
758 all_regions.insert (region);
760 possibly_splice_unlocked (position, region->length(), region);
762 if (!holding_state ()) {
763 /* layers get assigned from XML state, and are not reset during undo/redo */
767 /* we need to notify the existence of new region before checking dependents. Ick. */
769 notify_region_added (region);
771 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
772 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
778 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
780 RegionWriteLock rlock (this);
782 bool old_sp = _splicing;
785 remove_region_internal (old);
786 add_region_internal (newr, pos);
787 set_layer (newr, old->layer ());
791 possibly_splice_unlocked (pos, old->length() - newr->length());
795 Playlist::remove_region (boost::shared_ptr<Region> region)
797 RegionWriteLock rlock (this);
798 remove_region_internal (region);
802 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
804 RegionList::iterator i;
808 region->set_playlist (boost::weak_ptr<Playlist>());
811 /* XXX should probably freeze here .... */
813 for (i = regions.begin(); i != regions.end(); ++i) {
816 framepos_t pos = (*i)->position();
817 framecnt_t distance = (*i)->length();
821 possibly_splice_unlocked (pos, -distance);
823 if (!holding_state ()) {
825 remove_dependents (region);
828 notify_region_removed (region);
837 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
839 if (Config->get_use_overlap_equivalency()) {
840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
841 if ((*i)->overlap_equivalent (other)) {
842 results.push_back (*i);
846 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
847 if ((*i)->equivalent (other)) {
848 results.push_back (*i);
855 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
857 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
859 if ((*i) && (*i)->region_list_equivalent (other)) {
860 results.push_back (*i);
866 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
868 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
870 if ((*i) && (*i)->any_source_equivalent (other)) {
871 results.push_back (*i);
877 Playlist::partition (framepos_t start, framepos_t end, bool cut)
881 RegionWriteLock lock(this);
882 partition_internal (start, end, cut, thawlist);
885 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
886 (*i)->resume_property_changes ();
890 /** Go through each region on the playlist and cut them at start and end, removing the section between
891 * start and end if cutting == true. Regions that lie entirely within start and end are always
896 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
898 RegionList new_regions;
902 boost::shared_ptr<Region> region;
903 boost::shared_ptr<Region> current;
905 RegionList::iterator tmp;
906 Evoral::OverlapType overlap;
907 framepos_t pos1, pos2, pos3, pos4;
911 /* need to work from a copy, because otherwise the regions we add during the process
912 get operated on as well.
915 RegionList copy = regions.rlist();
917 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
924 if (current->first_frame() >= start && current->last_frame() < end) {
927 remove_region_internal (current);
933 /* coverage will return OverlapStart if the start coincides
934 with the end point. we do not partition such a region,
935 so catch this special case.
938 if (current->first_frame() >= end) {
942 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
946 pos1 = current->position();
949 pos4 = current->last_frame();
951 if (overlap == Evoral::OverlapInternal) {
952 /* split: we need 3 new regions, the front, middle and end.
953 cut: we need 2 regions, the front and end.
958 ---------------*************************------------
961 ---------------*****++++++++++++++++====------------
963 ---------------*****----------------====------------
968 /* "middle" ++++++ */
970 RegionFactory::region_name (new_name, current->name(), false);
974 plist.add (Properties::start, current->start() + (pos2 - pos1));
975 plist.add (Properties::length, pos3 - pos2);
976 plist.add (Properties::name, new_name);
977 plist.add (Properties::layer, current->layer ());
978 plist.add (Properties::layering_index, current->layering_index ());
979 plist.add (Properties::automatic, true);
980 plist.add (Properties::left_of_split, true);
981 plist.add (Properties::right_of_split, true);
983 region = RegionFactory::create (current, plist);
984 add_region_internal (region, start);
985 new_regions.push_back (region);
990 RegionFactory::region_name (new_name, current->name(), false);
994 plist.add (Properties::start, current->start() + (pos3 - pos1));
995 plist.add (Properties::length, pos4 - pos3);
996 plist.add (Properties::name, new_name);
997 plist.add (Properties::layer, current->layer ());
998 plist.add (Properties::layering_index, current->layering_index ());
999 plist.add (Properties::automatic, true);
1000 plist.add (Properties::right_of_split, true);
1002 region = RegionFactory::create (current, plist);
1004 add_region_internal (region, end);
1005 new_regions.push_back (region);
1009 current->suspend_property_changes ();
1010 thawlist.push_back (current);
1011 current->cut_end (pos2 - 1);
1013 } else if (overlap == Evoral::OverlapEnd) {
1017 ---------------*************************------------
1020 ---------------**************+++++++++++------------
1022 ---------------**************-----------------------
1029 RegionFactory::region_name (new_name, current->name(), false);
1033 plist.add (Properties::start, current->start() + (pos2 - pos1));
1034 plist.add (Properties::length, pos4 - pos2);
1035 plist.add (Properties::name, new_name);
1036 plist.add (Properties::layer, current->layer ());
1037 plist.add (Properties::layering_index, current->layering_index ());
1038 plist.add (Properties::automatic, true);
1039 plist.add (Properties::left_of_split, true);
1041 region = RegionFactory::create (current, plist);
1043 add_region_internal (region, start);
1044 new_regions.push_back (region);
1049 current->suspend_property_changes ();
1050 thawlist.push_back (current);
1051 current->cut_end (pos2 - 1);
1053 } else if (overlap == Evoral::OverlapStart) {
1055 /* split: we need 2 regions: the front and the end.
1056 cut: just trim current to skip the cut area
1061 ---------------*************************------------
1065 ---------------****+++++++++++++++++++++------------
1067 -------------------*********************------------
1073 RegionFactory::region_name (new_name, current->name(), false);
1077 plist.add (Properties::start, current->start());
1078 plist.add (Properties::length, pos3 - pos1);
1079 plist.add (Properties::name, new_name);
1080 plist.add (Properties::layer, current->layer ());
1081 plist.add (Properties::layering_index, current->layering_index ());
1082 plist.add (Properties::automatic, true);
1083 plist.add (Properties::right_of_split, true);
1085 region = RegionFactory::create (current, plist);
1087 add_region_internal (region, pos1);
1088 new_regions.push_back (region);
1093 current->suspend_property_changes ();
1094 thawlist.push_back (current);
1095 current->trim_front (pos3);
1096 } else if (overlap == Evoral::OverlapExternal) {
1098 /* split: no split required.
1099 cut: remove the region.
1104 ---------------*************************------------
1108 ---------------*************************------------
1110 ----------------------------------------------------
1115 remove_region_internal (current);
1118 new_regions.push_back (current);
1122 in_partition = false;
1125 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1126 framepos_t wanted_length = end-start;
1127 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1130 boost::shared_ptr<Playlist>
1131 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1133 boost::shared_ptr<Playlist> ret;
1134 boost::shared_ptr<Playlist> pl;
1137 if (ranges.empty()) {
1138 return boost::shared_ptr<Playlist>();
1141 start = ranges.front().start;
1143 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1145 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1147 if (i == ranges.begin()) {
1151 /* paste the next section into the nascent playlist,
1152 offset to reflect the start of the first range we
1156 ret->paste (pl, (*i).start - start, 1.0f, 0);
1163 boost::shared_ptr<Playlist>
1164 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1167 return cut_copy (pmf, ranges, result_is_hidden);
1170 boost::shared_ptr<Playlist>
1171 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1174 return cut_copy (pmf, ranges, result_is_hidden);
1177 boost::shared_ptr<Playlist>
1178 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1180 boost::shared_ptr<Playlist> the_copy;
1181 RegionList thawlist;
1184 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1185 string new_name = _name;
1189 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1190 return boost::shared_ptr<Playlist>();
1194 RegionWriteLock rlock (this);
1195 partition_internal (start, start+cnt-1, true, thawlist);
1198 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1199 (*i)->resume_property_changes();
1205 boost::shared_ptr<Playlist>
1206 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1210 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1211 string new_name = _name;
1215 // 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... )
1217 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1221 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1223 times = fabs (times);
1226 RegionReadLock rl2 (other.get());
1228 int itimes = (int) floor (times);
1229 framepos_t pos = position;
1230 framecnt_t const shift = other->_get_extent().second;
1231 layer_t top = top_layer ();
1234 RegionWriteLock rl1 (this);
1236 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1237 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1239 /* put these new regions on top of all existing ones, but preserve
1240 the ordering they had in the original playlist.
1243 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1244 set_layer (copy_of_region, copy_of_region->layer() + top);
1256 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1258 duplicate(region, position, region->length(), times);
1261 /** @param gap from the beginning of the region to the next beginning */
1263 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1265 times = fabs (times);
1267 RegionWriteLock rl (this);
1268 int itimes = (int) floor (times);
1271 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1272 add_region_internal (copy, position);
1273 set_layer (copy, DBL_MAX);
1277 if (floor (times) != times) {
1278 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1280 RegionFactory::region_name (name, region->name(), false);
1285 plist.add (Properties::start, region->start());
1286 plist.add (Properties::length, length);
1287 plist.add (Properties::name, name);
1289 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1290 add_region_internal (sub, position);
1291 set_layer (sub, DBL_MAX);
1296 /** @param gap from the beginning of the region to the next beginning */
1297 /** @param end the first frame that does _not_ contain a duplicated frame */
1299 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1301 RegionWriteLock rl (this);
1303 while (position + region->length() - 1 < end) {
1304 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1305 add_region_internal (copy, position);
1306 set_layer (copy, DBL_MAX);
1310 if (position < end) {
1311 framecnt_t length = min (region->length(), end - position);
1313 RegionFactory::region_name (name, region->name(), false);
1318 plist.add (Properties::start, region->start());
1319 plist.add (Properties::length, length);
1320 plist.add (Properties::name, name);
1322 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1323 add_region_internal (sub, position);
1324 set_layer (sub, DBL_MAX);
1330 Playlist::duplicate_range (AudioRange& range, float times)
1332 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1333 framecnt_t offset = range.end - range.start;
1334 paste (pl, range.start + offset, times, 0);
1338 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1340 if (ranges.empty()) {
1344 framepos_t min_pos = max_framepos;
1345 framepos_t max_pos = 0;
1347 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1350 min_pos = min (min_pos, (*i).start);
1351 max_pos = max (max_pos, (*i).end);
1354 framecnt_t offset = max_pos - min_pos;
1357 int itimes = (int) floor (times);
1359 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1360 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1361 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1368 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1370 RegionWriteLock rlock (this);
1371 RegionList copy (regions.rlist());
1374 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1376 if ((*r)->last_frame() < at) {
1381 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1382 /* intersected region */
1383 if (!move_intersected) {
1388 /* do not move regions glued to music time - that
1389 has to be done separately.
1392 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1393 fixup.push_back (*r);
1397 (*r)->set_position ((*r)->position() + distance);
1400 /* XXX: may not be necessary; Region::post_set should do this, I think */
1401 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1402 (*r)->recompute_position_from_lock_style (0);
1407 Playlist::split (framepos_t at, const int32_t sub_num)
1409 RegionWriteLock rlock (this);
1410 RegionList copy (regions.rlist());
1412 /* use a copy since this operation can modify the region list
1415 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1416 _split_region (*r, at, sub_num);
1421 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1423 RegionWriteLock rl (this);
1424 _split_region (region, playlist_position, sub_num);
1428 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1430 if (!region->covers (playlist_position)) {
1434 if (region->position() == playlist_position ||
1435 region->last_frame() == playlist_position) {
1439 boost::shared_ptr<Region> left;
1440 boost::shared_ptr<Region> right;
1441 frameoffset_t before;
1442 frameoffset_t after;
1446 /* split doesn't change anything about length, so don't try to splice */
1448 bool old_sp = _splicing;
1451 before = playlist_position - region->position();
1452 after = region->length() - before;
1454 RegionFactory::region_name (before_name, region->name(), false);
1459 plist.add (Properties::length, before);
1460 plist.add (Properties::name, before_name);
1461 plist.add (Properties::left_of_split, true);
1462 plist.add (Properties::layering_index, region->layering_index ());
1463 plist.add (Properties::layer, region->layer ());
1465 /* note: we must use the version of ::create with an offset here,
1466 since it supplies that offset to the Region constructor, which
1467 is necessary to get audio region gain envelopes right.
1469 left = RegionFactory::create (region, 0, plist, true, sub_num);
1472 RegionFactory::region_name (after_name, region->name(), false);
1477 plist.add (Properties::length, after);
1478 plist.add (Properties::name, after_name);
1479 plist.add (Properties::right_of_split, true);
1480 plist.add (Properties::layering_index, region->layering_index ());
1481 plist.add (Properties::layer, region->layer ());
1483 /* same note as above */
1484 right = RegionFactory::create (region, before, plist, true, sub_num);
1487 add_region_internal (left, region->position());
1488 add_region_internal (right, region->position() + before);
1489 remove_region_internal (region);
1495 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1497 if (_splicing || in_set_state) {
1498 /* don't respond to splicing moves or state setting */
1502 if (_edit_mode == Splice) {
1503 splice_locked (at, distance, exclude);
1508 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1510 if (_splicing || in_set_state) {
1511 /* don't respond to splicing moves or state setting */
1515 if (_edit_mode == Splice) {
1516 splice_unlocked (at, distance, exclude);
1521 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1524 RegionWriteLock rl (this);
1525 core_splice (at, distance, exclude);
1530 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1532 core_splice (at, distance, exclude);
1536 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1540 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1542 if (exclude && (*i) == exclude) {
1546 if ((*i)->position() >= at) {
1547 framepos_t new_pos = (*i)->position() + distance;
1550 } else if (new_pos >= max_framepos - (*i)->length()) {
1551 new_pos = max_framepos - (*i)->length();
1554 (*i)->set_position (new_pos);
1560 notify_contents_changed ();
1564 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1567 RegionWriteLock rl (this);
1568 core_ripple (at, distance, exclude);
1573 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1575 core_ripple (at, distance, exclude);
1579 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1581 if (distance == 0) {
1586 RegionListProperty copy = regions;
1587 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1588 assert (i != copy.end());
1591 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1596 if ((*i)->position() >= at) {
1597 framepos_t new_pos = (*i)->position() + distance;
1598 framepos_t limit = max_framepos - (*i)->length();
1601 } else if (new_pos >= limit ) {
1605 (*i)->set_position (new_pos);
1610 notify_contents_changed ();
1615 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1617 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1621 if (what_changed.contains (Properties::position)) {
1623 /* remove it from the list then add it back in
1624 the right place again.
1627 RegionSortByPosition cmp;
1629 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1631 if (i == regions.end()) {
1632 /* the region bounds are being modified but its not currently
1633 in the region list. we will use its bounds correctly when/if
1640 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1643 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1645 frameoffset_t delta = 0;
1647 if (what_changed.contains (Properties::position)) {
1648 delta = region->position() - region->last_position();
1651 if (what_changed.contains (Properties::length)) {
1652 delta += region->length() - region->last_length();
1656 possibly_splice (region->last_position() + region->last_length(), delta, region);
1659 if (holding_state ()) {
1660 pending_bounds.push_back (region);
1662 notify_contents_changed ();
1664 list<Evoral::Range<framepos_t> > xf;
1665 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1666 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1667 coalesce_and_check_crossfades (xf);
1673 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1675 boost::shared_ptr<Region> region (weak_region.lock());
1681 /* this makes a virtual call to the right kind of playlist ... */
1683 region_changed (what_changed, region);
1687 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1689 PropertyChange our_interests;
1690 PropertyChange bounds;
1691 PropertyChange pos_and_length;
1694 if (in_set_state || in_flush) {
1698 our_interests.add (Properties::muted);
1699 our_interests.add (Properties::layer);
1700 our_interests.add (Properties::opaque);
1702 bounds.add (Properties::start);
1703 bounds.add (Properties::position);
1704 bounds.add (Properties::length);
1706 pos_and_length.add (Properties::position);
1707 pos_and_length.add (Properties::length);
1709 if (what_changed.contains (bounds)) {
1710 region_bounds_changed (what_changed, region);
1711 save = !(_splicing || _nudging);
1714 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1715 notify_region_moved (region);
1716 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1717 notify_region_end_trimmed (region);
1718 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1719 notify_region_start_trimmed (region);
1722 /* don't notify about layer changes, since we are the only object that can initiate
1723 them, and we notify in ::relayer()
1726 if (what_changed.contains (our_interests)) {
1730 mark_session_dirty ();
1736 Playlist::drop_regions ()
1738 RegionWriteLock rl (this);
1740 all_regions.clear ();
1744 Playlist::sync_all_regions_with_regions ()
1746 RegionWriteLock rl (this);
1748 all_regions.clear ();
1750 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1751 all_regions.insert (*i);
1756 Playlist::clear (bool with_signals)
1759 RegionWriteLock rl (this);
1761 region_state_changed_connections.drop_connections ();
1762 region_drop_references_connections.drop_connections ();
1764 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1765 pending_removes.insert (*i);
1770 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1771 remove_dependents (*s);
1777 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1778 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1781 pending_removes.clear ();
1782 pending_contents_change = false;
1788 /* *********************************************************************
1790 **********************************************************************/
1792 boost::shared_ptr<RegionList>
1793 Playlist::region_list()
1795 RegionReadLock rlock (this);
1796 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1801 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1803 RegionReadLock rlock (const_cast<Playlist*>(this));
1805 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1806 (*i)->deep_sources (sources);
1810 boost::shared_ptr<RegionList>
1811 Playlist::regions_at (framepos_t frame)
1813 RegionReadLock rlock (this);
1814 return find_regions_at (frame);
1818 Playlist::count_regions_at (framepos_t frame) const
1820 RegionReadLock rlock (const_cast<Playlist*>(this));
1823 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1824 if ((*i)->covers (frame)) {
1832 boost::shared_ptr<Region>
1833 Playlist::top_region_at (framepos_t frame)
1836 RegionReadLock rlock (this);
1837 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1838 boost::shared_ptr<Region> region;
1840 if (rlist->size()) {
1841 RegionSortByLayer cmp;
1843 region = rlist->back();
1849 boost::shared_ptr<Region>
1850 Playlist::top_unmuted_region_at (framepos_t frame)
1853 RegionReadLock rlock (this);
1854 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1856 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1858 RegionList::iterator tmp = i;
1862 if ((*i)->muted()) {
1869 boost::shared_ptr<Region> region;
1871 if (rlist->size()) {
1872 RegionSortByLayer cmp;
1874 region = rlist->back();
1880 boost::shared_ptr<RegionList>
1881 Playlist::find_regions_at (framepos_t frame)
1883 /* Caller must hold lock */
1885 boost::shared_ptr<RegionList> rlist (new RegionList);
1887 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1888 if ((*i)->covers (frame)) {
1889 rlist->push_back (*i);
1896 boost::shared_ptr<RegionList>
1897 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1899 RegionReadLock rlock (this);
1900 boost::shared_ptr<RegionList> rlist (new RegionList);
1902 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1903 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1904 rlist->push_back (*i);
1911 boost::shared_ptr<RegionList>
1912 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1914 RegionReadLock rlock (this);
1915 boost::shared_ptr<RegionList> rlist (new RegionList);
1917 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1918 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1919 rlist->push_back (*i);
1926 /** @param start Range start.
1927 * @param end Range end.
1928 * @return regions which have some part within this range.
1930 boost::shared_ptr<RegionList>
1931 Playlist::regions_touched (framepos_t start, framepos_t end)
1933 RegionReadLock rlock (this);
1934 return regions_touched_locked (start, end);
1937 boost::shared_ptr<RegionList>
1938 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1940 boost::shared_ptr<RegionList> rlist (new RegionList);
1942 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1943 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1944 rlist->push_back (*i);
1952 Playlist::find_next_transient (framepos_t from, int dir)
1954 RegionReadLock rlock (this);
1955 AnalysisFeatureList points;
1956 AnalysisFeatureList these_points;
1958 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1960 if ((*i)->last_frame() < from) {
1964 if ((*i)->first_frame() > from) {
1969 (*i)->get_transients (these_points);
1971 /* add first frame, just, err, because */
1973 these_points.push_back ((*i)->first_frame());
1975 points.insert (points.end(), these_points.begin(), these_points.end());
1976 these_points.clear ();
1979 if (points.empty()) {
1983 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1984 bool reached = false;
1987 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1992 if (reached && (*x) > from) {
1997 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2002 if (reached && (*x) < from) {
2011 boost::shared_ptr<Region>
2012 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2014 RegionReadLock rlock (this);
2015 boost::shared_ptr<Region> ret;
2016 framepos_t closest = max_framepos;
2018 bool end_iter = false;
2020 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2024 frameoffset_t distance;
2025 boost::shared_ptr<Region> r = (*i);
2030 pos = r->first_frame ();
2033 pos = r->last_frame ();
2036 pos = r->sync_position ();
2041 case 1: /* forwards */
2044 if ((distance = pos - frame) < closest) {
2053 default: /* backwards */
2056 if ((distance = frame - pos) < closest) {
2072 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2074 RegionReadLock rlock (this);
2076 framepos_t closest = max_framepos;
2077 framepos_t ret = -1;
2081 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2083 boost::shared_ptr<Region> r = (*i);
2084 frameoffset_t distance;
2085 const framepos_t first_frame = r->first_frame();
2086 const framepos_t last_frame = r->last_frame();
2088 if (first_frame > frame) {
2090 distance = first_frame - frame;
2092 if (distance < closest) {
2098 if (last_frame > frame) {
2100 distance = last_frame - frame;
2102 if (distance < closest) {
2111 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2113 boost::shared_ptr<Region> r = (*i);
2114 frameoffset_t distance;
2115 const framepos_t first_frame = r->first_frame();
2116 const framepos_t last_frame = r->last_frame();
2118 if (last_frame < frame) {
2120 distance = frame - last_frame;
2122 if (distance < closest) {
2128 if (first_frame < frame) {
2130 distance = frame - first_frame;
2132 if (distance < closest) {
2144 /***********************************************************************/
2150 Playlist::mark_session_dirty ()
2152 if (!in_set_state && !holding_state ()) {
2153 _session.set_dirty();
2158 Playlist::rdiff (vector<Command*>& cmds) const
2160 RegionReadLock rlock (const_cast<Playlist *> (this));
2161 Stateful::rdiff (cmds);
2165 Playlist::clear_owned_changes ()
2167 RegionReadLock rlock (this);
2168 Stateful::clear_owned_changes ();
2172 Playlist::update (const RegionListProperty::ChangeRecord& change)
2174 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2175 name(), change.added.size(), change.removed.size()));
2178 /* add the added regions */
2179 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2180 add_region_internal ((*i), (*i)->position());
2182 /* remove the removed regions */
2183 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2191 Playlist::set_state (const XMLNode& node, int version)
2195 XMLNodeConstIterator niter;
2196 XMLPropertyList plist;
2197 XMLPropertyConstIterator piter;
2198 XMLProperty const * prop;
2199 boost::shared_ptr<Region> region;
2201 bool seen_region_nodes = false;
2206 if (node.name() != "Playlist") {
2213 plist = node.properties();
2217 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2221 if (prop->name() == X_("name")) {
2222 _name = prop->value();
2224 } else if (prop->name() == X_("orig-diskstream-id")) {
2225 /* XXX legacy session: fix up later */
2226 _orig_track_id = prop->value ();
2227 } else if (prop->name() == X_("orig-track-id")) {
2228 _orig_track_id = prop->value ();
2229 } else if (prop->name() == X_("frozen")) {
2230 _frozen = string_is_affirmative (prop->value());
2231 } else if (prop->name() == X_("combine-ops")) {
2232 _combine_ops = atoi (prop->value());
2238 nlist = node.children();
2240 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2244 if (child->name() == "Region") {
2246 seen_region_nodes = true;
2248 if ((prop = child->property ("id")) == 0) {
2249 error << _("region state node has no ID, ignored") << endmsg;
2253 ID id = prop->value ();
2255 if ((region = region_by_id (id))) {
2257 region->suspend_property_changes ();
2259 if (region->set_state (*child, version)) {
2260 region->resume_property_changes ();
2264 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2265 region->suspend_property_changes ();
2267 error << _("Playlist: cannot create region from XML") << endmsg;
2272 RegionWriteLock rlock (this);
2273 add_region_internal (region, region->position());
2276 region->resume_property_changes ();
2281 if (seen_region_nodes && regions.empty()) {
2286 notify_contents_changed ();
2289 first_set_state = false;
2295 Playlist::get_state()
2297 return state (true);
2301 Playlist::get_template()
2303 return state (false);
2306 /** @param full_state true to include regions in the returned state, otherwise false.
2309 Playlist::state (bool full_state)
2311 XMLNode *node = new XMLNode (X_("Playlist"));
2314 node->add_property (X_("id"), id().to_s());
2315 node->add_property (X_("name"), _name);
2316 node->add_property (X_("type"), _type.to_string());
2318 _orig_track_id.print (buf, sizeof (buf));
2319 node->add_property (X_("orig-track-id"), buf);
2320 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2323 RegionReadLock rlock (this);
2325 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2326 node->add_property ("combine-ops", buf);
2328 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2329 node->add_child_nocopy ((*i)->get_state());
2334 node->add_child_copy (*_extra_xml);
2341 Playlist::empty() const
2343 RegionReadLock rlock (const_cast<Playlist *>(this));
2344 return regions.empty();
2348 Playlist::n_regions() const
2350 RegionReadLock rlock (const_cast<Playlist *>(this));
2351 return regions.size();
2354 /** @return true if the all_regions list is empty, ie this playlist
2355 * has never had a region added to it.
2358 Playlist::all_regions_empty() const
2360 RegionReadLock rl (const_cast<Playlist *> (this));
2361 return all_regions.empty();
2364 pair<framepos_t, framepos_t>
2365 Playlist::get_extent () const
2367 RegionReadLock rlock (const_cast<Playlist *>(this));
2368 return _get_extent ();
2371 pair<framepos_t, framepos_t>
2372 Playlist::get_extent_with_endspace () const
2374 pair<framepos_t, framepos_t> l = get_extent();
2375 l.second += _end_space;
2379 pair<framepos_t, framepos_t>
2380 Playlist::_get_extent () const
2382 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2384 if (regions.empty()) {
2389 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2390 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2391 if (e.first < ext.first) {
2392 ext.first = e.first;
2394 if (e.second > ext.second) {
2395 ext.second = e.second;
2403 Playlist::bump_name (string name, Session &session)
2405 string newname = name;
2408 newname = bump_name_once (newname, '.');
2409 } while (session.playlists->by_name (newname)!=NULL);
2416 Playlist::top_layer() const
2418 RegionReadLock rlock (const_cast<Playlist *> (this));
2421 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2422 top = max (top, (*i)->layer());
2428 Playlist::set_edit_mode (EditMode mode)
2433 struct RelayerSort {
2434 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2435 return a->layering_index() < b->layering_index();
2439 /** Set a new layer for a region. This adjusts the layering indices of all
2440 * regions in the playlist to put the specified region in the appropriate
2441 * place. The actual layering will be fixed up when relayer() happens.
2445 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2447 /* Remove the layer we are setting from our region list, and sort it
2448 * using the layer indeces.
2451 RegionList copy = regions.rlist();
2452 copy.remove (region);
2453 copy.sort (RelayerSort ());
2455 /* Put region back in the right place */
2456 RegionList::iterator i = copy.begin();
2457 while (i != copy.end ()) {
2458 if ((*i)->layer() > new_layer) {
2464 copy.insert (i, region);
2466 setup_layering_indices (copy);
2470 Playlist::setup_layering_indices (RegionList const & regions)
2474 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2475 (*k)->set_layering_index (j++);
2479 struct LaterHigherSort {
2480 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2481 return a->position() < b->position();
2485 /** Take the layering indices of each of our regions, compute the layers
2486 * that they should be on, and write the layers back to the regions.
2489 Playlist::relayer ()
2491 /* never compute layers when setting from XML */
2497 /* Build up a new list of regions on each layer, stored in a set of lists
2498 each of which represent some period of time on some layer. The idea
2499 is to avoid having to search the entire region list to establish whether
2500 each region overlaps another */
2502 /* how many pieces to divide this playlist's time up into */
2503 int const divisions = 512;
2505 /* find the start and end positions of the regions on this playlist */
2506 framepos_t start = INT64_MAX;
2508 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2509 start = min (start, (*i)->position());
2510 end = max (end, (*i)->position() + (*i)->length());
2513 /* hence the size of each time division */
2514 double const division_size = (end - start) / double (divisions);
2516 vector<vector<RegionList> > layers;
2517 layers.push_back (vector<RegionList> (divisions));
2519 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2520 RegionList copy = regions.rlist();
2521 switch (Config->get_layer_model()) {
2523 copy.sort (LaterHigherSort ());
2526 copy.sort (RelayerSort ());
2530 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2531 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2532 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2535 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2537 /* find the time divisions that this region covers; if there are no regions on the list,
2538 division_size will equal 0 and in this case we'll just say that
2539 start_division = end_division = 0.
2541 int start_division = 0;
2542 int end_division = 0;
2544 if (division_size > 0) {
2545 start_division = floor ( ((*i)->position() - start) / division_size);
2546 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2547 if (end_division == divisions) {
2552 assert (divisions == 0 || end_division < divisions);
2554 /* find the lowest layer that this region can go on */
2555 size_t j = layers.size();
2557 /* try layer j - 1; it can go on if it overlaps no other region
2558 that is already on that layer
2561 bool overlap = false;
2562 for (int k = start_division; k <= end_division; ++k) {
2563 RegionList::iterator l = layers[j-1][k].begin ();
2564 while (l != layers[j-1][k].end()) {
2565 if ((*l)->overlap_equivalent (*i)) {
2578 /* overlap, so we must use layer j */
2585 if (j == layers.size()) {
2586 /* we need a new layer for this region */
2587 layers.push_back (vector<RegionList> (divisions));
2590 /* put a reference to this region in each of the divisions that it exists in */
2591 for (int k = start_division; k <= end_division; ++k) {
2592 layers[j][k].push_back (*i);
2595 (*i)->set_layer (j);
2598 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2599 relayering because we just removed the only region on the top layer, nothing will
2600 appear to have changed, but the StreamView must still sort itself out. We could
2601 probably keep a note of the top layer last time we relayered, and check that,
2602 but premature optimisation &c...
2604 notify_layering_changed ();
2606 /* This relayer() may have been called as a result of a region removal, in which
2607 case we need to setup layering indices to account for the one that has just
2610 setup_layering_indices (copy);
2614 Playlist::raise_region (boost::shared_ptr<Region> region)
2616 set_layer (region, region->layer() + 1.5);
2621 Playlist::lower_region (boost::shared_ptr<Region> region)
2623 set_layer (region, region->layer() - 1.5);
2628 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2630 set_layer (region, DBL_MAX);
2635 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2637 set_layer (region, -0.5);
2642 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2644 RegionList::iterator i;
2650 RegionWriteLock rlock (const_cast<Playlist *> (this));
2652 for (i = regions.begin(); i != regions.end(); ++i) {
2654 if ((*i)->position() >= start) {
2660 if ((*i)->last_frame() > max_framepos - distance) {
2661 new_pos = max_framepos - (*i)->length();
2663 new_pos = (*i)->position() + distance;
2668 if ((*i)->position() > distance) {
2669 new_pos = (*i)->position() - distance;
2675 (*i)->set_position (new_pos);
2683 notify_contents_changed ();
2689 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2691 RegionReadLock rlock (const_cast<Playlist*> (this));
2693 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2694 /* Note: passing the second argument as false can cause at best
2695 incredibly deep and time-consuming recursion, and at worst
2696 cycles if the user has managed to create cycles of reference
2697 between compound regions. We generally only this during
2698 cleanup, and @param shallow is passed as true.
2700 if ((*r)->uses_source (src, shallow)) {
2709 boost::shared_ptr<Region>
2710 Playlist::find_region (const ID& id) const
2712 RegionReadLock rlock (const_cast<Playlist*> (this));
2714 /* searches all regions currently in use by the playlist */
2716 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2717 if ((*i)->id() == id) {
2722 return boost::shared_ptr<Region> ();
2726 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2728 RegionReadLock rlock (const_cast<Playlist*> (this));
2731 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2737 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2738 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2739 /* check if region is used in a compound */
2740 if (it->second == r) {
2741 /* region is referenced as 'original' of a compound */
2745 if (r->whole_file() && r->max_source_level() > 0) {
2746 /* region itself ia a compound.
2747 * the compound regions are not referenced -> check regions inside compound
2749 const SourceList& sl = r->sources();
2750 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2751 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2753 if (ps->playlist()->region_use_count(it->first)) {
2754 // break out of both loops
2763 boost::shared_ptr<Region>
2764 Playlist::region_by_id (const ID& id) const
2766 /* searches all regions ever added to this playlist */
2768 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2769 if ((*i)->id() == id) {
2773 return boost::shared_ptr<Region> ();
2777 Playlist::dump () const
2779 boost::shared_ptr<Region> r;
2781 cerr << "Playlist \"" << _name << "\" " << endl
2782 << regions.size() << " regions "
2785 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2787 cerr << " " << r->name() << " ["
2788 << r->start() << "+" << r->length()
2798 Playlist::set_frozen (bool yn)
2804 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2808 if (region->locked()) {
2815 RegionWriteLock rlock (const_cast<Playlist*> (this));
2820 RegionList::iterator next;
2822 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2823 if ((*i) == region) {
2827 if (next != regions.end()) {
2829 if ((*next)->locked()) {
2835 if ((*next)->position() != region->last_frame() + 1) {
2836 /* they didn't used to touch, so after shuffle,
2837 just have them swap positions.
2839 new_pos = (*next)->position();
2841 /* they used to touch, so after shuffle,
2842 make sure they still do. put the earlier
2843 region where the later one will end after
2846 new_pos = region->position() + (*next)->length();
2849 (*next)->set_position (region->position());
2850 region->set_position (new_pos);
2852 /* avoid a full sort */
2854 regions.erase (i); // removes the region from the list */
2856 regions.insert (next, region); // adds it back after next
2865 RegionList::iterator prev = regions.end();
2867 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2868 if ((*i) == region) {
2870 if (prev != regions.end()) {
2872 if ((*prev)->locked()) {
2877 if (region->position() != (*prev)->last_frame() + 1) {
2878 /* they didn't used to touch, so after shuffle,
2879 just have them swap positions.
2881 new_pos = region->position();
2883 /* they used to touch, so after shuffle,
2884 make sure they still do. put the earlier
2885 one where the later one will end after
2887 new_pos = (*prev)->position() + region->length();
2890 region->set_position ((*prev)->position());
2891 (*prev)->set_position (new_pos);
2893 /* avoid a full sort */
2895 regions.erase (i); // remove region
2896 regions.insert (prev, region); // insert region before prev
2912 notify_contents_changed();
2918 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2920 RegionReadLock rlock (const_cast<Playlist*> (this));
2922 if (regions.size() > 1) {
2930 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2932 ripple_locked (at, distance, exclude);
2936 Playlist::update_after_tempo_map_change ()
2938 RegionWriteLock rlock (const_cast<Playlist*> (this));
2939 RegionList copy (regions.rlist());
2943 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2944 (*i)->update_after_tempo_map_change ();
2946 /* possibly causes a contents changed notification (flush_notifications()) */
2951 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2953 RegionReadLock rl (this);
2954 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2960 Playlist::has_region_at (framepos_t const p) const
2962 RegionReadLock (const_cast<Playlist *> (this));
2964 RegionList::const_iterator i = regions.begin ();
2965 while (i != regions.end() && !(*i)->covers (p)) {
2969 return (i != regions.end());
2972 /** Look from a session frame time and find the start time of the next region
2973 * which is on the top layer of this playlist.
2974 * @param t Time to look from.
2975 * @return Position of next top-layered region, or max_framepos if there isn't one.
2978 Playlist::find_next_top_layer_position (framepos_t t) const
2980 RegionReadLock rlock (const_cast<Playlist *> (this));
2982 layer_t const top = top_layer ();
2984 RegionList copy = regions.rlist ();
2985 copy.sort (RegionSortByPosition ());
2987 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2988 if ((*i)->position() >= t && (*i)->layer() == top) {
2989 return (*i)->position();
2993 return max_framepos;
2996 boost::shared_ptr<Region>
2997 Playlist::combine (const RegionList& r)
3000 uint32_t channels = 0;
3002 framepos_t earliest_position = max_framepos;
3003 vector<TwoRegions> old_and_new_regions;
3004 vector<boost::shared_ptr<Region> > originals;
3005 vector<boost::shared_ptr<Region> > copies;
3008 uint32_t max_level = 0;
3010 /* find the maximum depth of all the regions we're combining */
3012 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3013 max_level = max (max_level, (*i)->max_source_level());
3016 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3017 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3019 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3021 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3022 earliest_position = min (earliest_position, (*i)->position());
3025 /* enable this so that we do not try to create xfades etc. as we add
3029 pl->in_partition = true;
3031 /* sort by position then layer.
3032 * route_time_axis passes 'selected_regions' - which is not sorted.
3033 * here we need the top-most first, then every layer's region sorted by position.
3035 RegionList sorted(r);
3036 sorted.sort(RegionSortByLayerAndPosition());
3038 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3040 /* copy the region */
3042 boost::shared_ptr<Region> original_region = (*i);
3043 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3045 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3046 originals.push_back (original_region);
3047 copies.push_back (copied_region);
3049 RegionFactory::add_compound_association (original_region, copied_region);
3051 /* make position relative to zero */
3053 pl->add_region (copied_region, original_region->position() - earliest_position);
3054 copied_region->set_layer (original_region->layer ());
3056 /* use the maximum number of channels for any region */
3058 channels = max (channels, original_region->n_channels());
3060 /* it will go above the layer of the highest existing region */
3062 layer = max (layer, original_region->layer());
3065 pl->in_partition = false;
3067 pre_combine (copies);
3069 /* now create a new PlaylistSource for each channel in the new playlist */
3072 pair<framepos_t,framepos_t> extent = pl->get_extent();
3074 for (uint32_t chn = 0; chn < channels; ++chn) {
3075 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3079 /* now a new whole-file region using the list of sources */
3081 plist.add (Properties::start, 0);
3082 plist.add (Properties::length, extent.second);
3083 plist.add (Properties::name, parent_name);
3084 plist.add (Properties::whole_file, true);
3086 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3088 /* now the non-whole-file region that we will actually use in the
3093 plist.add (Properties::start, 0);
3094 plist.add (Properties::length, extent.second);
3095 plist.add (Properties::name, child_name);
3096 plist.add (Properties::layer, layer+1);
3098 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3100 /* remove all the selected regions from the current playlist
3105 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3109 /* do type-specific stuff with the originals and the new compound
3113 post_combine (originals, compound_region);
3115 /* add the new region at the right location */
3117 add_region (compound_region, earliest_position);
3123 return compound_region;
3127 Playlist::uncombine (boost::shared_ptr<Region> target)
3129 boost::shared_ptr<PlaylistSource> pls;
3130 boost::shared_ptr<const Playlist> pl;
3131 vector<boost::shared_ptr<Region> > originals;
3132 vector<TwoRegions> old_and_new_regions;
3134 // (1) check that its really a compound region
3136 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3140 pl = pls->playlist();
3142 framepos_t adjusted_start = 0; // gcc isn't smart enough
3143 framepos_t adjusted_end = 0; // gcc isn't smart enough
3145 /* the leftmost (earliest) edge of the compound region
3146 starts at zero in its source, or larger if it
3147 has been trimmed or content-scrolled.
3149 the rightmost (latest) edge of the compound region
3150 relative to its source is the starting point plus
3151 the length of the region.
3154 // (2) get all the original regions
3156 const RegionList& rl (pl->region_list_property().rlist());
3157 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3158 frameoffset_t move_offset = 0;
3160 /* there are two possibilities here:
3161 1) the playlist that the playlist source was based on
3162 is us, so just add the originals (which belonged to
3163 us anyway) back in the right place.
3165 2) the playlist that the playlist source was based on
3166 is NOT us, so we need to make copies of each of
3167 the original regions that we find, and add them
3170 bool same_playlist = (pls->original() == id());
3172 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3174 boost::shared_ptr<Region> current (*i);
3176 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3178 if (ca == cassocs.end()) {
3182 boost::shared_ptr<Region> original (ca->second);
3184 bool modified_region;
3186 if (i == rl.begin()) {
3187 move_offset = (target->position() - original->position()) - target->start();
3188 adjusted_start = original->position() + target->start();
3189 adjusted_end = adjusted_start + target->length();
3192 if (!same_playlist) {
3193 framepos_t pos = original->position();
3194 /* make a copy, but don't announce it */
3195 original = RegionFactory::create (original, false);
3196 /* the pure copy constructor resets position() to zero,
3199 original->set_position (pos);
3202 /* check to see how the original region (in the
3203 * playlist before compounding occurred) overlaps
3204 * with the new state of the compound region.
3207 original->clear_changes ();
3208 modified_region = false;
3210 switch (original->coverage (adjusted_start, adjusted_end)) {
3211 case Evoral::OverlapNone:
3212 /* original region does not cover any part
3213 of the current state of the compound region
3217 case Evoral::OverlapInternal:
3218 /* overlap is just a small piece inside the
3219 * original so trim both ends
3221 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3222 modified_region = true;
3225 case Evoral::OverlapExternal:
3226 /* overlap fully covers original, so leave it
3231 case Evoral::OverlapEnd:
3232 /* overlap starts within but covers end,
3233 so trim the front of the region
3235 original->trim_front (adjusted_start);
3236 modified_region = true;
3239 case Evoral::OverlapStart:
3240 /* overlap covers start but ends within, so
3241 * trim the end of the region.
3243 original->trim_end (adjusted_end);
3244 modified_region = true;
3249 /* fix the position to match any movement of the compound region.
3251 original->set_position (original->position() + move_offset);
3252 modified_region = true;
3255 if (modified_region) {
3256 _session.add_command (new StatefulDiffCommand (original));
3259 /* and add to the list of regions waiting to be
3263 originals.push_back (original);
3264 old_and_new_regions.push_back (TwoRegions (*i, original));
3267 pre_uncombine (originals, target);
3269 in_partition = true;
3272 // (3) remove the compound region
3274 remove_region (target);
3276 // (4) add the constituent regions
3278 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3279 add_region ((*i), (*i)->position());
3280 set_layer((*i), (*i)->layer());
3281 if (!RegionFactory::region_by_id((*i)->id())) {
3282 RegionFactory::map_add(*i);
3286 in_partition = false;
3291 Playlist::fade_range (list<AudioRange>& ranges)
3293 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3294 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3295 (*i)->fade_range ((*r).start, (*r).end);
3301 Playlist::max_source_level () const
3303 RegionReadLock rlock (const_cast<Playlist *> (this));
3306 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3307 lvl = max (lvl, (*i)->max_source_level());
3314 Playlist::set_orig_track_id (const PBD::ID& id)
3316 _orig_track_id = id;
3319 /** Take a list of ranges, coalesce any that can be coalesced, then call
3320 * check_crossfades for each one.
3323 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3325 /* XXX: it's a shame that this coalesce algorithm also exists in
3326 TimeSelection::consolidate().
3329 /* XXX: xfade: this is implemented in Evoral::RangeList */
3332 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3333 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3339 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3340 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3341 i->from = min (i->from, j->from);
3342 i->to = max (i->to, j->to);
3351 Playlist::set_capture_insertion_in_progress (bool yn)
3353 _capture_insertion_underway = yn;