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)));
773 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
775 RegionWriteLock rlock (this);
777 bool old_sp = _splicing;
780 remove_region_internal (old);
781 add_region_internal (newr, pos);
782 set_layer (newr, old->layer ());
786 possibly_splice_unlocked (pos, old->length() - newr->length());
790 Playlist::remove_region (boost::shared_ptr<Region> region)
792 RegionWriteLock rlock (this);
793 remove_region_internal (region);
797 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
799 RegionList::iterator i;
803 region->set_playlist (boost::weak_ptr<Playlist>());
806 /* XXX should probably freeze here .... */
808 for (i = regions.begin(); i != regions.end(); ++i) {
811 framepos_t pos = (*i)->position();
812 framecnt_t distance = (*i)->length();
816 possibly_splice_unlocked (pos, -distance);
818 if (!holding_state ()) {
820 remove_dependents (region);
823 notify_region_removed (region);
832 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
834 if (Config->get_use_overlap_equivalency()) {
835 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
836 if ((*i)->overlap_equivalent (other)) {
837 results.push_back (*i);
841 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
842 if ((*i)->equivalent (other)) {
843 results.push_back (*i);
850 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
852 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
854 if ((*i) && (*i)->region_list_equivalent (other)) {
855 results.push_back (*i);
861 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
863 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
865 if ((*i) && (*i)->any_source_equivalent (other)) {
866 results.push_back (*i);
872 Playlist::partition (framepos_t start, framepos_t end, bool cut)
876 partition_internal (start, end, cut, thawlist);
878 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
879 (*i)->resume_property_changes ();
883 /** Go through each region on the playlist and cut them at start and end, removing the section between
884 * start and end if cutting == true. Regions that lie entirely within start and end are always
889 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
891 RegionList new_regions;
894 RegionWriteLock rlock (this);
896 boost::shared_ptr<Region> region;
897 boost::shared_ptr<Region> current;
899 RegionList::iterator tmp;
900 Evoral::OverlapType overlap;
901 framepos_t pos1, pos2, pos3, pos4;
905 /* need to work from a copy, because otherwise the regions we add during the process
906 get operated on as well.
909 RegionList copy = regions.rlist();
911 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
918 if (current->first_frame() >= start && current->last_frame() < end) {
921 remove_region_internal (current);
927 /* coverage will return OverlapStart if the start coincides
928 with the end point. we do not partition such a region,
929 so catch this special case.
932 if (current->first_frame() >= end) {
936 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
940 pos1 = current->position();
943 pos4 = current->last_frame();
945 if (overlap == Evoral::OverlapInternal) {
946 /* split: we need 3 new regions, the front, middle and end.
947 cut: we need 2 regions, the front and end.
952 ---------------*************************------------
955 ---------------*****++++++++++++++++====------------
957 ---------------*****----------------====------------
962 /* "middle" ++++++ */
964 RegionFactory::region_name (new_name, current->name(), false);
968 plist.add (Properties::start, current->start() + (pos2 - pos1));
969 plist.add (Properties::length, pos3 - pos2);
970 plist.add (Properties::name, new_name);
971 plist.add (Properties::layer, current->layer ());
972 plist.add (Properties::layering_index, current->layering_index ());
973 plist.add (Properties::automatic, true);
974 plist.add (Properties::left_of_split, true);
975 plist.add (Properties::right_of_split, true);
977 region = RegionFactory::create (current, plist);
978 add_region_internal (region, start);
979 new_regions.push_back (region);
984 RegionFactory::region_name (new_name, current->name(), false);
988 plist.add (Properties::start, current->start() + (pos3 - pos1));
989 plist.add (Properties::length, pos4 - pos3);
990 plist.add (Properties::name, new_name);
991 plist.add (Properties::layer, current->layer ());
992 plist.add (Properties::layering_index, current->layering_index ());
993 plist.add (Properties::automatic, true);
994 plist.add (Properties::right_of_split, true);
996 region = RegionFactory::create (current, plist);
998 add_region_internal (region, end);
999 new_regions.push_back (region);
1003 current->suspend_property_changes ();
1004 thawlist.push_back (current);
1005 current->cut_end (pos2 - 1);
1007 } else if (overlap == Evoral::OverlapEnd) {
1011 ---------------*************************------------
1014 ---------------**************+++++++++++------------
1016 ---------------**************-----------------------
1023 RegionFactory::region_name (new_name, current->name(), false);
1027 plist.add (Properties::start, current->start() + (pos2 - pos1));
1028 plist.add (Properties::length, pos4 - pos2);
1029 plist.add (Properties::name, new_name);
1030 plist.add (Properties::layer, current->layer ());
1031 plist.add (Properties::layering_index, current->layering_index ());
1032 plist.add (Properties::automatic, true);
1033 plist.add (Properties::left_of_split, true);
1035 region = RegionFactory::create (current, plist);
1037 add_region_internal (region, start);
1038 new_regions.push_back (region);
1043 current->suspend_property_changes ();
1044 thawlist.push_back (current);
1045 current->cut_end (pos2 - 1);
1047 } else if (overlap == Evoral::OverlapStart) {
1049 /* split: we need 2 regions: the front and the end.
1050 cut: just trim current to skip the cut area
1055 ---------------*************************------------
1059 ---------------****+++++++++++++++++++++------------
1061 -------------------*********************------------
1067 RegionFactory::region_name (new_name, current->name(), false);
1071 plist.add (Properties::start, current->start());
1072 plist.add (Properties::length, pos3 - pos1);
1073 plist.add (Properties::name, new_name);
1074 plist.add (Properties::layer, current->layer ());
1075 plist.add (Properties::layering_index, current->layering_index ());
1076 plist.add (Properties::automatic, true);
1077 plist.add (Properties::right_of_split, true);
1079 region = RegionFactory::create (current, plist);
1081 add_region_internal (region, pos1);
1082 new_regions.push_back (region);
1087 current->suspend_property_changes ();
1088 thawlist.push_back (current);
1089 current->trim_front (pos3);
1090 } else if (overlap == Evoral::OverlapExternal) {
1092 /* split: no split required.
1093 cut: remove the region.
1098 ---------------*************************------------
1102 ---------------*************************------------
1104 ----------------------------------------------------
1109 remove_region_internal (current);
1112 new_regions.push_back (current);
1116 in_partition = false;
1119 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1120 framepos_t wanted_length = end-start;
1121 _end_space = wanted_length - get_extent().second-get_extent().first;
1124 boost::shared_ptr<Playlist>
1125 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1127 boost::shared_ptr<Playlist> ret;
1128 boost::shared_ptr<Playlist> pl;
1131 if (ranges.empty()) {
1132 return boost::shared_ptr<Playlist>();
1135 start = ranges.front().start;
1137 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1139 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1141 if (i == ranges.begin()) {
1145 /* paste the next section into the nascent playlist,
1146 offset to reflect the start of the first range we
1150 ret->paste (pl, (*i).start - start, 1.0f, 0);
1157 boost::shared_ptr<Playlist>
1158 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1160 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1161 return cut_copy (pmf, ranges, result_is_hidden);
1164 boost::shared_ptr<Playlist>
1165 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1167 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1168 return cut_copy (pmf, ranges, result_is_hidden);
1171 boost::shared_ptr<Playlist>
1172 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1174 boost::shared_ptr<Playlist> the_copy;
1175 RegionList thawlist;
1178 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1179 string new_name = _name;
1183 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1184 return boost::shared_ptr<Playlist>();
1187 partition_internal (start, start+cnt-1, true, thawlist);
1189 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1190 (*i)->resume_property_changes();
1196 boost::shared_ptr<Playlist>
1197 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1201 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1202 string new_name = _name;
1206 // 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... )
1208 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1212 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t& sub_num)
1214 times = fabs (times);
1217 RegionReadLock rl2 (other.get());
1219 int itimes = (int) floor (times);
1220 framepos_t pos = position;
1221 framecnt_t const shift = other->_get_extent().second;
1222 layer_t top = top_layer ();
1225 RegionWriteLock rl1 (this);
1227 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1228 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1230 /* put these new regions on top of all existing ones, but preserve
1231 the ordering they had in the original playlist.
1234 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1235 set_layer (copy_of_region, copy_of_region->layer() + top);
1247 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1249 duplicate(region, position, region->length(), times);
1252 /** @param gap from the beginning of the region to the next beginning */
1254 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1256 times = fabs (times);
1258 RegionWriteLock rl (this);
1259 int itimes = (int) floor (times);
1262 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1263 add_region_internal (copy, position);
1264 set_layer (copy, DBL_MAX);
1268 if (floor (times) != times) {
1269 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1271 RegionFactory::region_name (name, region->name(), false);
1276 plist.add (Properties::start, region->start());
1277 plist.add (Properties::length, length);
1278 plist.add (Properties::name, name);
1280 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1281 add_region_internal (sub, position);
1282 set_layer (sub, DBL_MAX);
1287 /** @param gap from the beginning of the region to the next beginning */
1288 /** @param end the first frame that does _not_ contain a duplicated frame */
1290 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1292 RegionWriteLock rl (this);
1294 while (position + region->length() - 1 < end) {
1295 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1296 add_region_internal (copy, position);
1297 set_layer (copy, DBL_MAX);
1301 if (position < end) {
1302 framecnt_t length = min (region->length(), end - position);
1304 RegionFactory::region_name (name, region->name(), false);
1309 plist.add (Properties::start, region->start());
1310 plist.add (Properties::length, length);
1311 plist.add (Properties::name, name);
1313 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1314 add_region_internal (sub, position);
1315 set_layer (sub, DBL_MAX);
1321 Playlist::duplicate_range (AudioRange& range, float times)
1323 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1324 framecnt_t offset = range.end - range.start;
1325 paste (pl, range.start + offset, times, 0);
1329 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float /* times */)
1331 if (ranges.empty()) {
1335 framepos_t min_pos = max_framepos;
1336 framepos_t max_pos = 0;
1338 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1341 min_pos = min (min_pos, (*i).start);
1342 max_pos = max (max_pos, (*i).end);
1345 framecnt_t offset = max_pos - min_pos;
1347 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1348 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length(), true);
1349 paste (pl, (*i).start + offset, 1.0f, 0); // times ??
1354 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1356 RegionWriteLock rlock (this);
1357 RegionList copy (regions.rlist());
1360 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1362 if ((*r)->last_frame() < at) {
1367 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1368 /* intersected region */
1369 if (!move_intersected) {
1374 /* do not move regions glued to music time - that
1375 has to be done separately.
1378 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1379 fixup.push_back (*r);
1383 (*r)->set_position ((*r)->position() + distance);
1386 /* XXX: may not be necessary; Region::post_set should do this, I think */
1387 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1388 (*r)->recompute_position_from_lock_style (0);
1393 Playlist::split (framepos_t at, const int32_t& sub_num)
1395 RegionWriteLock rlock (this);
1396 RegionList copy (regions.rlist());
1398 /* use a copy since this operation can modify the region list
1401 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1402 _split_region (*r, at, sub_num);
1407 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t& sub_num)
1409 RegionWriteLock rl (this);
1410 _split_region (region, playlist_position, sub_num);
1414 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t& sub_num)
1416 if (!region->covers (playlist_position)) {
1420 if (region->position() == playlist_position ||
1421 region->last_frame() == playlist_position) {
1425 boost::shared_ptr<Region> left;
1426 boost::shared_ptr<Region> right;
1427 frameoffset_t before;
1428 frameoffset_t after;
1432 /* split doesn't change anything about length, so don't try to splice */
1434 bool old_sp = _splicing;
1437 before = playlist_position - region->position();
1438 after = region->length() - before;
1440 RegionFactory::region_name (before_name, region->name(), false);
1445 plist.add (Properties::length, before);
1446 plist.add (Properties::name, before_name);
1447 plist.add (Properties::left_of_split, true);
1448 plist.add (Properties::layering_index, region->layering_index ());
1449 plist.add (Properties::layer, region->layer ());
1451 /* note: we must use the version of ::create with an offset here,
1452 since it supplies that offset to the Region constructor, which
1453 is necessary to get audio region gain envelopes right.
1455 left = RegionFactory::create (region, 0, plist, true, sub_num);
1458 RegionFactory::region_name (after_name, region->name(), false);
1463 plist.add (Properties::length, after);
1464 plist.add (Properties::name, after_name);
1465 plist.add (Properties::right_of_split, true);
1466 plist.add (Properties::layering_index, region->layering_index ());
1467 plist.add (Properties::layer, region->layer ());
1469 /* same note as above */
1470 right = RegionFactory::create (region, before, plist, true, sub_num);
1473 add_region_internal (left, region->position());
1474 add_region_internal (right, region->position() + before);
1475 remove_region_internal (region);
1481 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1483 if (_splicing || in_set_state) {
1484 /* don't respond to splicing moves or state setting */
1488 if (_edit_mode == Splice) {
1489 splice_locked (at, distance, exclude);
1494 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1496 if (_splicing || in_set_state) {
1497 /* don't respond to splicing moves or state setting */
1501 if (_edit_mode == Splice) {
1502 splice_unlocked (at, distance, exclude);
1507 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1510 RegionWriteLock rl (this);
1511 core_splice (at, distance, exclude);
1516 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1518 core_splice (at, distance, exclude);
1522 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1526 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1528 if (exclude && (*i) == exclude) {
1532 if ((*i)->position() >= at) {
1533 framepos_t new_pos = (*i)->position() + distance;
1536 } else if (new_pos >= max_framepos - (*i)->length()) {
1537 new_pos = max_framepos - (*i)->length();
1540 (*i)->set_position (new_pos);
1546 notify_contents_changed ();
1550 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1553 RegionWriteLock rl (this);
1554 core_ripple (at, distance, exclude);
1559 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1561 core_ripple (at, distance, exclude);
1565 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1567 if (distance == 0) {
1572 RegionListProperty copy = regions;
1573 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1574 assert (i != copy.end());
1577 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1582 if ((*i)->position() >= at) {
1583 framepos_t new_pos = (*i)->position() + distance;
1584 framepos_t limit = max_framepos - (*i)->length();
1587 } else if (new_pos >= limit ) {
1591 (*i)->set_position (new_pos);
1596 notify_contents_changed ();
1601 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1603 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1607 if (what_changed.contains (Properties::position)) {
1609 /* remove it from the list then add it back in
1610 the right place again.
1613 RegionSortByPosition cmp;
1615 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1617 if (i == regions.end()) {
1618 /* the region bounds are being modified but its not currently
1619 in the region list. we will use its bounds correctly when/if
1626 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1629 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1631 frameoffset_t delta = 0;
1633 if (what_changed.contains (Properties::position)) {
1634 delta = region->position() - region->last_position();
1637 if (what_changed.contains (Properties::length)) {
1638 delta += region->length() - region->last_length();
1642 possibly_splice (region->last_position() + region->last_length(), delta, region);
1645 if (holding_state ()) {
1646 pending_bounds.push_back (region);
1648 notify_contents_changed ();
1650 list<Evoral::Range<framepos_t> > xf;
1651 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1652 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1653 coalesce_and_check_crossfades (xf);
1659 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1661 boost::shared_ptr<Region> region (weak_region.lock());
1667 /* this makes a virtual call to the right kind of playlist ... */
1669 region_changed (what_changed, region);
1673 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1675 PropertyChange our_interests;
1676 PropertyChange bounds;
1677 PropertyChange pos_and_length;
1680 if (in_set_state || in_flush) {
1684 our_interests.add (Properties::muted);
1685 our_interests.add (Properties::layer);
1686 our_interests.add (Properties::opaque);
1688 bounds.add (Properties::start);
1689 bounds.add (Properties::position);
1690 bounds.add (Properties::length);
1692 pos_and_length.add (Properties::position);
1693 pos_and_length.add (Properties::length);
1695 if (what_changed.contains (bounds)) {
1696 region_bounds_changed (what_changed, region);
1697 save = !(_splicing || _nudging);
1700 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1701 notify_region_moved (region);
1702 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1703 notify_region_end_trimmed (region);
1704 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1705 notify_region_start_trimmed (region);
1708 /* don't notify about layer changes, since we are the only object that can initiate
1709 them, and we notify in ::relayer()
1712 if (what_changed.contains (our_interests)) {
1716 mark_session_dirty ();
1722 Playlist::drop_regions ()
1724 RegionWriteLock rl (this);
1726 all_regions.clear ();
1730 Playlist::sync_all_regions_with_regions ()
1732 RegionWriteLock rl (this);
1734 all_regions.clear ();
1736 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1737 all_regions.insert (*i);
1742 Playlist::clear (bool with_signals)
1745 RegionWriteLock rl (this);
1747 region_state_changed_connections.drop_connections ();
1749 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1750 pending_removes.insert (*i);
1755 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1756 remove_dependents (*s);
1762 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1763 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1766 pending_removes.clear ();
1767 pending_contents_change = false;
1773 /* *********************************************************************
1775 **********************************************************************/
1777 boost::shared_ptr<RegionList>
1778 Playlist::region_list()
1780 RegionReadLock rlock (this);
1781 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1786 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1788 RegionReadLock rlock (const_cast<Playlist*>(this));
1790 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1791 (*i)->deep_sources (sources);
1795 boost::shared_ptr<RegionList>
1796 Playlist::regions_at (framepos_t frame)
1798 RegionReadLock rlock (this);
1799 return find_regions_at (frame);
1803 Playlist::count_regions_at (framepos_t frame) const
1805 RegionReadLock rlock (const_cast<Playlist*>(this));
1808 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1809 if ((*i)->covers (frame)) {
1817 boost::shared_ptr<Region>
1818 Playlist::top_region_at (framepos_t frame)
1821 RegionReadLock rlock (this);
1822 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1823 boost::shared_ptr<Region> region;
1825 if (rlist->size()) {
1826 RegionSortByLayer cmp;
1828 region = rlist->back();
1834 boost::shared_ptr<Region>
1835 Playlist::top_unmuted_region_at (framepos_t frame)
1838 RegionReadLock rlock (this);
1839 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1841 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1843 RegionList::iterator tmp = i;
1847 if ((*i)->muted()) {
1854 boost::shared_ptr<Region> region;
1856 if (rlist->size()) {
1857 RegionSortByLayer cmp;
1859 region = rlist->back();
1865 boost::shared_ptr<RegionList>
1866 Playlist::find_regions_at (framepos_t frame)
1868 /* Caller must hold lock */
1870 boost::shared_ptr<RegionList> rlist (new RegionList);
1872 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1873 if ((*i)->covers (frame)) {
1874 rlist->push_back (*i);
1881 boost::shared_ptr<RegionList>
1882 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1884 RegionReadLock rlock (this);
1885 boost::shared_ptr<RegionList> rlist (new RegionList);
1887 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1888 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1889 rlist->push_back (*i);
1896 boost::shared_ptr<RegionList>
1897 Playlist::regions_with_end_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)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1904 rlist->push_back (*i);
1911 /** @param start Range start.
1912 * @param end Range end.
1913 * @return regions which have some part within this range.
1915 boost::shared_ptr<RegionList>
1916 Playlist::regions_touched (framepos_t start, framepos_t end)
1918 RegionReadLock rlock (this);
1919 return regions_touched_locked (start, end);
1922 boost::shared_ptr<RegionList>
1923 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1925 boost::shared_ptr<RegionList> rlist (new RegionList);
1927 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1928 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1929 rlist->push_back (*i);
1937 Playlist::find_next_transient (framepos_t from, int dir)
1939 RegionReadLock rlock (this);
1940 AnalysisFeatureList points;
1941 AnalysisFeatureList these_points;
1943 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1945 if ((*i)->last_frame() < from) {
1949 if ((*i)->first_frame() > from) {
1954 (*i)->get_transients (these_points);
1956 /* add first frame, just, err, because */
1958 these_points.push_back ((*i)->first_frame());
1960 points.insert (points.end(), these_points.begin(), these_points.end());
1961 these_points.clear ();
1964 if (points.empty()) {
1968 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1969 bool reached = false;
1972 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1977 if (reached && (*x) > from) {
1982 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1987 if (reached && (*x) < from) {
1996 boost::shared_ptr<Region>
1997 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1999 RegionReadLock rlock (this);
2000 boost::shared_ptr<Region> ret;
2001 framepos_t closest = max_framepos;
2003 bool end_iter = false;
2005 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2009 frameoffset_t distance;
2010 boost::shared_ptr<Region> r = (*i);
2015 pos = r->first_frame ();
2018 pos = r->last_frame ();
2021 pos = r->sync_position ();
2026 case 1: /* forwards */
2029 if ((distance = pos - frame) < closest) {
2038 default: /* backwards */
2041 if ((distance = frame - pos) < closest) {
2057 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2059 RegionReadLock rlock (this);
2061 framepos_t closest = max_framepos;
2062 framepos_t ret = -1;
2066 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2068 boost::shared_ptr<Region> r = (*i);
2069 frameoffset_t distance;
2070 const framepos_t first_frame = r->first_frame();
2071 const framepos_t last_frame = r->last_frame();
2073 if (first_frame > frame) {
2075 distance = first_frame - frame;
2077 if (distance < closest) {
2083 if (last_frame > frame) {
2085 distance = last_frame - frame;
2087 if (distance < closest) {
2096 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2098 boost::shared_ptr<Region> r = (*i);
2099 frameoffset_t distance;
2100 const framepos_t first_frame = r->first_frame();
2101 const framepos_t last_frame = r->last_frame();
2103 if (last_frame < frame) {
2105 distance = frame - last_frame;
2107 if (distance < closest) {
2113 if (first_frame < frame) {
2115 distance = frame - first_frame;
2117 if (distance < closest) {
2129 /***********************************************************************/
2135 Playlist::mark_session_dirty ()
2137 if (!in_set_state && !holding_state ()) {
2138 _session.set_dirty();
2143 Playlist::rdiff (vector<Command*>& cmds) const
2145 RegionReadLock rlock (const_cast<Playlist *> (this));
2146 Stateful::rdiff (cmds);
2150 Playlist::clear_owned_changes ()
2152 RegionReadLock rlock (this);
2153 Stateful::clear_owned_changes ();
2157 Playlist::update (const RegionListProperty::ChangeRecord& change)
2159 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2160 name(), change.added.size(), change.removed.size()));
2163 /* add the added regions */
2164 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2165 add_region_internal ((*i), (*i)->position());
2167 /* remove the removed regions */
2168 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2176 Playlist::set_state (const XMLNode& node, int version)
2180 XMLNodeConstIterator niter;
2181 XMLPropertyList plist;
2182 XMLPropertyConstIterator piter;
2183 XMLProperty const * prop;
2184 boost::shared_ptr<Region> region;
2186 bool seen_region_nodes = false;
2191 if (node.name() != "Playlist") {
2198 plist = node.properties();
2202 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2206 if (prop->name() == X_("name")) {
2207 _name = prop->value();
2209 } else if (prop->name() == X_("orig-diskstream-id")) {
2210 /* XXX legacy session: fix up later */
2211 _orig_track_id = prop->value ();
2212 } else if (prop->name() == X_("orig-track-id")) {
2213 _orig_track_id = prop->value ();
2214 } else if (prop->name() == X_("frozen")) {
2215 _frozen = string_is_affirmative (prop->value());
2216 } else if (prop->name() == X_("combine-ops")) {
2217 _combine_ops = atoi (prop->value());
2223 nlist = node.children();
2225 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2229 if (child->name() == "Region") {
2231 seen_region_nodes = true;
2233 if ((prop = child->property ("id")) == 0) {
2234 error << _("region state node has no ID, ignored") << endmsg;
2238 ID id = prop->value ();
2240 if ((region = region_by_id (id))) {
2242 region->suspend_property_changes ();
2244 if (region->set_state (*child, version)) {
2245 region->resume_property_changes ();
2249 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2250 region->suspend_property_changes ();
2252 error << _("Playlist: cannot create region from XML") << endmsg;
2257 RegionWriteLock rlock (this);
2258 add_region_internal (region, region->position());
2261 region->resume_property_changes ();
2266 if (seen_region_nodes && regions.empty()) {
2271 notify_contents_changed ();
2274 first_set_state = false;
2280 Playlist::get_state()
2282 return state (true);
2286 Playlist::get_template()
2288 return state (false);
2291 /** @param full_state true to include regions in the returned state, otherwise false.
2294 Playlist::state (bool full_state)
2296 XMLNode *node = new XMLNode (X_("Playlist"));
2299 node->add_property (X_("id"), id().to_s());
2300 node->add_property (X_("name"), _name);
2301 node->add_property (X_("type"), _type.to_string());
2303 _orig_track_id.print (buf, sizeof (buf));
2304 node->add_property (X_("orig-track-id"), buf);
2305 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2308 RegionReadLock rlock (this);
2310 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2311 node->add_property ("combine-ops", buf);
2313 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2314 node->add_child_nocopy ((*i)->get_state());
2319 node->add_child_copy (*_extra_xml);
2326 Playlist::empty() const
2328 RegionReadLock rlock (const_cast<Playlist *>(this));
2329 return regions.empty();
2333 Playlist::n_regions() const
2335 RegionReadLock rlock (const_cast<Playlist *>(this));
2336 return regions.size();
2339 /** @return true if the all_regions list is empty, ie this playlist
2340 * has never had a region added to it.
2343 Playlist::all_regions_empty() const
2345 RegionReadLock rl (const_cast<Playlist *> (this));
2346 return all_regions.empty();
2349 pair<framepos_t, framepos_t>
2350 Playlist::get_extent () const
2352 RegionReadLock rlock (const_cast<Playlist *>(this));
2353 return _get_extent ();
2356 pair<framepos_t, framepos_t>
2357 Playlist::get_extent_with_endspace () const
2359 pair<framepos_t, framepos_t> l = get_extent();
2360 l.second += _end_space;
2364 pair<framepos_t, framepos_t>
2365 Playlist::_get_extent () const
2367 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2369 if (regions.empty()) {
2374 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2375 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2376 if (e.first < ext.first) {
2377 ext.first = e.first;
2379 if (e.second > ext.second) {
2380 ext.second = e.second;
2388 Playlist::bump_name (string name, Session &session)
2390 string newname = name;
2393 newname = bump_name_once (newname, '.');
2394 } while (session.playlists->by_name (newname)!=NULL);
2401 Playlist::top_layer() const
2403 RegionReadLock rlock (const_cast<Playlist *> (this));
2406 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2407 top = max (top, (*i)->layer());
2413 Playlist::set_edit_mode (EditMode mode)
2418 struct RelayerSort {
2419 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2420 return a->layering_index() < b->layering_index();
2424 /** Set a new layer for a region. This adjusts the layering indices of all
2425 * regions in the playlist to put the specified region in the appropriate
2426 * place. The actual layering will be fixed up when relayer() happens.
2430 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2432 /* Remove the layer we are setting from our region list, and sort it
2433 * using the layer indeces.
2436 RegionList copy = regions.rlist();
2437 copy.remove (region);
2438 copy.sort (RelayerSort ());
2440 /* Put region back in the right place */
2441 RegionList::iterator i = copy.begin();
2442 while (i != copy.end ()) {
2443 if ((*i)->layer() > new_layer) {
2449 copy.insert (i, region);
2451 setup_layering_indices (copy);
2455 Playlist::setup_layering_indices (RegionList const & regions)
2459 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2460 (*k)->set_layering_index (j++);
2464 struct LaterHigherSort {
2465 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2466 return a->position() < b->position();
2470 /** Take the layering indices of each of our regions, compute the layers
2471 * that they should be on, and write the layers back to the regions.
2474 Playlist::relayer ()
2476 /* never compute layers when setting from XML */
2482 /* Build up a new list of regions on each layer, stored in a set of lists
2483 each of which represent some period of time on some layer. The idea
2484 is to avoid having to search the entire region list to establish whether
2485 each region overlaps another */
2487 /* how many pieces to divide this playlist's time up into */
2488 int const divisions = 512;
2490 /* find the start and end positions of the regions on this playlist */
2491 framepos_t start = INT64_MAX;
2493 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2494 start = min (start, (*i)->position());
2495 end = max (end, (*i)->position() + (*i)->length());
2498 /* hence the size of each time division */
2499 double const division_size = (end - start) / double (divisions);
2501 vector<vector<RegionList> > layers;
2502 layers.push_back (vector<RegionList> (divisions));
2504 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2505 RegionList copy = regions.rlist();
2506 switch (Config->get_layer_model()) {
2508 copy.sort (LaterHigherSort ());
2511 copy.sort (RelayerSort ());
2515 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2516 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2517 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2520 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2522 /* find the time divisions that this region covers; if there are no regions on the list,
2523 division_size will equal 0 and in this case we'll just say that
2524 start_division = end_division = 0.
2526 int start_division = 0;
2527 int end_division = 0;
2529 if (division_size > 0) {
2530 start_division = floor ( ((*i)->position() - start) / division_size);
2531 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2532 if (end_division == divisions) {
2537 assert (divisions == 0 || end_division < divisions);
2539 /* find the lowest layer that this region can go on */
2540 size_t j = layers.size();
2542 /* try layer j - 1; it can go on if it overlaps no other region
2543 that is already on that layer
2546 bool overlap = false;
2547 for (int k = start_division; k <= end_division; ++k) {
2548 RegionList::iterator l = layers[j-1][k].begin ();
2549 while (l != layers[j-1][k].end()) {
2550 if ((*l)->overlap_equivalent (*i)) {
2563 /* overlap, so we must use layer j */
2570 if (j == layers.size()) {
2571 /* we need a new layer for this region */
2572 layers.push_back (vector<RegionList> (divisions));
2575 /* put a reference to this region in each of the divisions that it exists in */
2576 for (int k = start_division; k <= end_division; ++k) {
2577 layers[j][k].push_back (*i);
2580 (*i)->set_layer (j);
2583 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2584 relayering because we just removed the only region on the top layer, nothing will
2585 appear to have changed, but the StreamView must still sort itself out. We could
2586 probably keep a note of the top layer last time we relayered, and check that,
2587 but premature optimisation &c...
2589 notify_layering_changed ();
2591 /* This relayer() may have been called as a result of a region removal, in which
2592 case we need to setup layering indices to account for the one that has just
2595 setup_layering_indices (copy);
2599 Playlist::raise_region (boost::shared_ptr<Region> region)
2601 set_layer (region, region->layer() + 1.5);
2606 Playlist::lower_region (boost::shared_ptr<Region> region)
2608 set_layer (region, region->layer() - 1.5);
2613 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2615 set_layer (region, DBL_MAX);
2620 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2622 set_layer (region, -0.5);
2627 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2629 RegionList::iterator i;
2635 RegionWriteLock rlock (const_cast<Playlist *> (this));
2637 for (i = regions.begin(); i != regions.end(); ++i) {
2639 if ((*i)->position() >= start) {
2645 if ((*i)->last_frame() > max_framepos - distance) {
2646 new_pos = max_framepos - (*i)->length();
2648 new_pos = (*i)->position() + distance;
2653 if ((*i)->position() > distance) {
2654 new_pos = (*i)->position() - distance;
2660 (*i)->set_position (new_pos);
2668 notify_contents_changed ();
2674 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2676 RegionReadLock rlock (const_cast<Playlist*> (this));
2678 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2679 /* Note: passing the second argument as false can cause at best
2680 incredibly deep and time-consuming recursion, and at worst
2681 cycles if the user has managed to create cycles of reference
2682 between compound regions. We generally only this during
2683 cleanup, and @param shallow is passed as true.
2685 if ((*r)->uses_source (src, shallow)) {
2694 boost::shared_ptr<Region>
2695 Playlist::find_region (const ID& id) const
2697 RegionReadLock rlock (const_cast<Playlist*> (this));
2699 /* searches all regions currently in use by the playlist */
2701 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2702 if ((*i)->id() == id) {
2707 return boost::shared_ptr<Region> ();
2711 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2713 RegionReadLock rlock (const_cast<Playlist*> (this));
2716 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2722 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2723 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2724 /* check if region is used in a compound */
2725 if (it->second == r) {
2726 /* region is referenced as 'original' of a compound */
2730 if (r->whole_file() && r->max_source_level() > 0) {
2731 /* region itself ia a compound.
2732 * the compound regions are not referenced -> check regions inside compound
2734 const SourceList& sl = r->sources();
2735 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2736 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2738 if (ps->playlist()->region_use_count(it->first)) {
2739 // break out of both loops
2748 boost::shared_ptr<Region>
2749 Playlist::region_by_id (const ID& id) const
2751 /* searches all regions ever added to this playlist */
2753 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2754 if ((*i)->id() == id) {
2758 return boost::shared_ptr<Region> ();
2762 Playlist::dump () const
2764 boost::shared_ptr<Region> r;
2766 cerr << "Playlist \"" << _name << "\" " << endl
2767 << regions.size() << " regions "
2770 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2772 cerr << " " << r->name() << " ["
2773 << r->start() << "+" << r->length()
2783 Playlist::set_frozen (bool yn)
2789 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2793 if (region->locked()) {
2800 RegionWriteLock rlock (const_cast<Playlist*> (this));
2805 RegionList::iterator next;
2807 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2808 if ((*i) == region) {
2812 if (next != regions.end()) {
2814 if ((*next)->locked()) {
2820 if ((*next)->position() != region->last_frame() + 1) {
2821 /* they didn't used to touch, so after shuffle,
2822 just have them swap positions.
2824 new_pos = (*next)->position();
2826 /* they used to touch, so after shuffle,
2827 make sure they still do. put the earlier
2828 region where the later one will end after
2831 new_pos = region->position() + (*next)->length();
2834 (*next)->set_position (region->position());
2835 region->set_position (new_pos);
2837 /* avoid a full sort */
2839 regions.erase (i); // removes the region from the list */
2841 regions.insert (next, region); // adds it back after next
2850 RegionList::iterator prev = regions.end();
2852 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2853 if ((*i) == region) {
2855 if (prev != regions.end()) {
2857 if ((*prev)->locked()) {
2862 if (region->position() != (*prev)->last_frame() + 1) {
2863 /* they didn't used to touch, so after shuffle,
2864 just have them swap positions.
2866 new_pos = region->position();
2868 /* they used to touch, so after shuffle,
2869 make sure they still do. put the earlier
2870 one where the later one will end after
2872 new_pos = (*prev)->position() + region->length();
2875 region->set_position ((*prev)->position());
2876 (*prev)->set_position (new_pos);
2878 /* avoid a full sort */
2880 regions.erase (i); // remove region
2881 regions.insert (prev, region); // insert region before prev
2897 notify_contents_changed();
2903 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2905 RegionReadLock rlock (const_cast<Playlist*> (this));
2907 if (regions.size() > 1) {
2915 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2917 ripple_locked (at, distance, exclude);
2921 Playlist::update_after_tempo_map_change ()
2923 RegionWriteLock rlock (const_cast<Playlist*> (this));
2924 RegionList copy (regions.rlist());
2928 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2929 (*i)->update_after_tempo_map_change ();
2933 notify_contents_changed();
2937 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2939 RegionReadLock rl (this);
2940 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2946 Playlist::has_region_at (framepos_t const p) const
2948 RegionReadLock (const_cast<Playlist *> (this));
2950 RegionList::const_iterator i = regions.begin ();
2951 while (i != regions.end() && !(*i)->covers (p)) {
2955 return (i != regions.end());
2958 /** Look from a session frame time and find the start time of the next region
2959 * which is on the top layer of this playlist.
2960 * @param t Time to look from.
2961 * @return Position of next top-layered region, or max_framepos if there isn't one.
2964 Playlist::find_next_top_layer_position (framepos_t t) const
2966 RegionReadLock rlock (const_cast<Playlist *> (this));
2968 layer_t const top = top_layer ();
2970 RegionList copy = regions.rlist ();
2971 copy.sort (RegionSortByPosition ());
2973 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2974 if ((*i)->position() >= t && (*i)->layer() == top) {
2975 return (*i)->position();
2979 return max_framepos;
2982 boost::shared_ptr<Region>
2983 Playlist::combine (const RegionList& r)
2986 uint32_t channels = 0;
2988 framepos_t earliest_position = max_framepos;
2989 vector<TwoRegions> old_and_new_regions;
2990 vector<boost::shared_ptr<Region> > originals;
2991 vector<boost::shared_ptr<Region> > copies;
2994 uint32_t max_level = 0;
2996 /* find the maximum depth of all the regions we're combining */
2998 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2999 max_level = max (max_level, (*i)->max_source_level());
3002 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3003 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3005 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3007 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3008 earliest_position = min (earliest_position, (*i)->position());
3011 /* enable this so that we do not try to create xfades etc. as we add
3015 pl->in_partition = true;
3017 /* sort by position then layer.
3018 * route_time_axis passes 'selected_regions' - which is not sorted.
3019 * here we need the top-most first, then every layer's region sorted by position.
3021 RegionList sorted(r);
3022 sorted.sort(RegionSortByLayerAndPosition());
3024 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3026 /* copy the region */
3028 boost::shared_ptr<Region> original_region = (*i);
3029 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3031 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3032 originals.push_back (original_region);
3033 copies.push_back (copied_region);
3035 RegionFactory::add_compound_association (original_region, copied_region);
3037 /* make position relative to zero */
3039 pl->add_region (copied_region, original_region->position() - earliest_position);
3040 copied_region->set_layer (original_region->layer ());
3042 /* use the maximum number of channels for any region */
3044 channels = max (channels, original_region->n_channels());
3046 /* it will go above the layer of the highest existing region */
3048 layer = max (layer, original_region->layer());
3051 pl->in_partition = false;
3053 pre_combine (copies);
3055 /* now create a new PlaylistSource for each channel in the new playlist */
3058 pair<framepos_t,framepos_t> extent = pl->get_extent();
3060 for (uint32_t chn = 0; chn < channels; ++chn) {
3061 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3065 /* now a new whole-file region using the list of sources */
3067 plist.add (Properties::start, 0);
3068 plist.add (Properties::length, extent.second);
3069 plist.add (Properties::name, parent_name);
3070 plist.add (Properties::whole_file, true);
3072 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3074 /* now the non-whole-file region that we will actually use in the
3079 plist.add (Properties::start, 0);
3080 plist.add (Properties::length, extent.second);
3081 plist.add (Properties::name, child_name);
3082 plist.add (Properties::layer, layer+1);
3084 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3086 /* remove all the selected regions from the current playlist
3091 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3095 /* do type-specific stuff with the originals and the new compound
3099 post_combine (originals, compound_region);
3101 /* add the new region at the right location */
3103 add_region (compound_region, earliest_position);
3109 return compound_region;
3113 Playlist::uncombine (boost::shared_ptr<Region> target)
3115 boost::shared_ptr<PlaylistSource> pls;
3116 boost::shared_ptr<const Playlist> pl;
3117 vector<boost::shared_ptr<Region> > originals;
3118 vector<TwoRegions> old_and_new_regions;
3120 // (1) check that its really a compound region
3122 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3126 pl = pls->playlist();
3128 framepos_t adjusted_start = 0; // gcc isn't smart enough
3129 framepos_t adjusted_end = 0; // gcc isn't smart enough
3131 /* the leftmost (earliest) edge of the compound region
3132 starts at zero in its source, or larger if it
3133 has been trimmed or content-scrolled.
3135 the rightmost (latest) edge of the compound region
3136 relative to its source is the starting point plus
3137 the length of the region.
3140 // (2) get all the original regions
3142 const RegionList& rl (pl->region_list_property().rlist());
3143 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3144 frameoffset_t move_offset = 0;
3146 /* there are two possibilities here:
3147 1) the playlist that the playlist source was based on
3148 is us, so just add the originals (which belonged to
3149 us anyway) back in the right place.
3151 2) the playlist that the playlist source was based on
3152 is NOT us, so we need to make copies of each of
3153 the original regions that we find, and add them
3156 bool same_playlist = (pls->original() == id());
3158 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3160 boost::shared_ptr<Region> current (*i);
3162 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3164 if (ca == cassocs.end()) {
3168 boost::shared_ptr<Region> original (ca->second);
3170 bool modified_region;
3172 if (i == rl.begin()) {
3173 move_offset = (target->position() - original->position()) - target->start();
3174 adjusted_start = original->position() + target->start();
3175 adjusted_end = adjusted_start + target->length();
3178 if (!same_playlist) {
3179 framepos_t pos = original->position();
3180 /* make a copy, but don't announce it */
3181 original = RegionFactory::create (original, false);
3182 /* the pure copy constructor resets position() to zero,
3185 original->set_position (pos);
3188 /* check to see how the original region (in the
3189 * playlist before compounding occurred) overlaps
3190 * with the new state of the compound region.
3193 original->clear_changes ();
3194 modified_region = false;
3196 switch (original->coverage (adjusted_start, adjusted_end)) {
3197 case Evoral::OverlapNone:
3198 /* original region does not cover any part
3199 of the current state of the compound region
3203 case Evoral::OverlapInternal:
3204 /* overlap is just a small piece inside the
3205 * original so trim both ends
3207 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3208 modified_region = true;
3211 case Evoral::OverlapExternal:
3212 /* overlap fully covers original, so leave it
3217 case Evoral::OverlapEnd:
3218 /* overlap starts within but covers end,
3219 so trim the front of the region
3221 original->trim_front (adjusted_start);
3222 modified_region = true;
3225 case Evoral::OverlapStart:
3226 /* overlap covers start but ends within, so
3227 * trim the end of the region.
3229 original->trim_end (adjusted_end);
3230 modified_region = true;
3235 /* fix the position to match any movement of the compound region.
3237 original->set_position (original->position() + move_offset);
3238 modified_region = true;
3241 if (modified_region) {
3242 _session.add_command (new StatefulDiffCommand (original));
3245 /* and add to the list of regions waiting to be
3249 originals.push_back (original);
3250 old_and_new_regions.push_back (TwoRegions (*i, original));
3253 pre_uncombine (originals, target);
3255 in_partition = true;
3258 // (3) remove the compound region
3260 remove_region (target);
3262 // (4) add the constituent regions
3264 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3265 add_region ((*i), (*i)->position());
3266 set_layer((*i), (*i)->layer());
3267 if (!RegionFactory::region_by_id((*i)->id())) {
3268 RegionFactory::map_add(*i);
3272 in_partition = false;
3277 Playlist::fade_range (list<AudioRange>& ranges)
3279 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3280 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3281 (*i)->fade_range ((*r).start, (*r).end);
3287 Playlist::max_source_level () const
3289 RegionReadLock rlock (const_cast<Playlist *> (this));
3292 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3293 lvl = max (lvl, (*i)->max_source_level());
3300 Playlist::set_orig_track_id (const PBD::ID& id)
3302 _orig_track_id = id;
3305 /** Take a list of ranges, coalesce any that can be coalesced, then call
3306 * check_crossfades for each one.
3309 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3311 /* XXX: it's a shame that this coalesce algorithm also exists in
3312 TimeSelection::consolidate().
3315 /* XXX: xfade: this is implemented in Evoral::RangeList */
3318 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3319 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3325 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3326 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3327 i->from = min (i->from, j->from);
3328 i->to = max (i->to, j->to);
3337 Playlist::set_capture_insertion_in_progress (bool yn)
3339 _capture_insertion_underway = yn;