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/strsplit.h"
30 #include "pbd/xml++.h"
32 #include "ardour/debug.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/region.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/region_sorters.h"
38 #include "ardour/playlist_factory.h"
39 #include "ardour/playlist_source.h"
40 #include "ardour/transient_detector.h"
41 #include "ardour/session_playlists.h"
42 #include "ardour/source_factory.h"
47 using namespace ARDOUR;
51 namespace Properties {
52 PBD::PropertyDescriptor<bool> regions;
56 struct ShowMeTheList {
57 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
59 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
61 boost::shared_ptr<Playlist> playlist;
68 Playlist::make_property_quarks ()
70 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
71 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
72 Properties::regions.property_id));
75 RegionListProperty::RegionListProperty (Playlist& pl)
76 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
82 RegionListProperty::RegionListProperty (RegionListProperty const & p)
83 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
84 , _playlist (p._playlist)
90 RegionListProperty::clone () const
92 return new RegionListProperty (*this);
96 RegionListProperty::create () const
98 return new RegionListProperty (_playlist);
102 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
104 /* All regions (even those which are deleted) have their state saved by other
105 code, so we can just store ID here.
108 node.add_property ("id", region->id().to_s ());
111 boost::shared_ptr<Region>
112 RegionListProperty::get_content_from_xml (XMLNode const & node) const
114 XMLProperty const * prop = node.property ("id");
117 PBD::ID id (prop->value ());
119 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
122 ret = RegionFactory::region_by_id (id);
128 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
129 : SessionObject(sess, nom)
134 first_set_state = false;
139 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
140 : SessionObject(sess, "unnamed playlist")
145 XMLProperty const * prop = node.property("type");
146 assert(!prop || DataType(prop->value()) == _type);
150 _name = "unnamed"; /* reset by set_state */
153 /* set state called by derived class */
156 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
157 : SessionObject(other->_session, namestr)
159 , _type(other->_type)
160 , _orig_track_id (other->_orig_track_id)
161 , _shared_with_ids (other->_shared_with_ids)
166 other->copy_regions (tmp);
170 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
171 add_region_internal( (*x), (*x)->position());
176 _splicing = other->_splicing;
177 _rippling = other->_rippling;
178 _nudging = other->_nudging;
179 _edit_mode = other->_edit_mode;
182 first_set_state = false;
184 in_partition = false;
186 _frozen = other->_frozen;
189 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
190 : SessionObject(other->_session, str)
192 , _type(other->_type)
193 , _orig_track_id (other->_orig_track_id)
194 , _shared_with_ids (other->_shared_with_ids)
196 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
198 framepos_t end = start + cnt - 1;
204 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
206 boost::shared_ptr<Region> region;
207 boost::shared_ptr<Region> new_region;
208 frameoffset_t offset = 0;
209 framepos_t position = 0;
212 Evoral::OverlapType overlap;
216 overlap = region->coverage (start, end);
219 case Evoral::OverlapNone:
222 case Evoral::OverlapInternal:
223 offset = start - region->position();
228 case Evoral::OverlapStart:
230 position = region->position() - start;
231 len = end - region->position();
234 case Evoral::OverlapEnd:
235 offset = start - region->position();
237 len = region->length() - offset;
240 case Evoral::OverlapExternal:
242 position = region->position() - start;
243 len = region->length();
247 RegionFactory::region_name (new_name, region->name(), false);
251 plist.add (Properties::start, region->start() + offset);
252 plist.add (Properties::length, len);
253 plist.add (Properties::name, new_name);
254 plist.add (Properties::layer, region->layer());
255 plist.add (Properties::layering_index, region->layering_index());
257 new_region = RegionFactory::create (region, plist);
259 add_region_internal (new_region, position);
262 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
263 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
264 _end_space = cnt - (get_extent().second - get_extent().first);
267 first_set_state = false;
274 InUse (true); /* EMIT SIGNAL */
285 InUse (false); /* EMIT SIGNAL */
290 Playlist::copy_regions (RegionList& newlist) const
292 RegionReadLock rlock (const_cast<Playlist *> (this));
294 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
295 newlist.push_back (RegionFactory::create (*i, true));
300 Playlist::init (bool hide)
302 add_property (regions);
303 _xml_node_name = X_("Playlist");
305 g_atomic_int_set (&block_notifications, 0);
306 g_atomic_int_set (&ignore_state_changes, 0);
307 pending_contents_change = false;
308 pending_layering = false;
309 first_set_state = true;
318 _edit_mode = Config->get_edit_mode();
320 in_partition = false;
323 _capture_insertion_underway = false;
327 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
328 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
330 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
333 Playlist::~Playlist ()
335 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
338 RegionReadLock rl (this);
340 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
341 (*i)->set_playlist (boost::shared_ptr<Playlist>());
345 /* GoingAway must be emitted by derived classes */
349 Playlist::_set_sort_id ()
352 Playlists are given names like <track name>.<id>
353 or <track name>.<edit group name>.<id> where id
354 is an integer. We extract the id and sort by that.
357 size_t dot_position = _name.val().find_last_of(".");
359 if (dot_position == string::npos) {
362 string t = _name.val().substr(dot_position + 1);
365 _sort_id = boost::lexical_cast<int>(t);
368 catch (boost::bad_lexical_cast e) {
375 Playlist::set_name (const string& str)
377 /* in a typical situation, a playlist is being used
378 by one diskstream and also is referenced by the
379 Session. if there are more references than that,
380 then don't change the name.
387 bool ret = SessionObject::set_name(str);
394 /***********************************************************************
395 CHANGE NOTIFICATION HANDLING
397 Notifications must be delayed till the region_lock is released. This
398 is necessary because handlers for the signals may need to acquire
399 the lock (e.g. to read from the playlist).
400 ***********************************************************************/
403 Playlist::begin_undo ()
410 Playlist::end_undo ()
419 delay_notifications ();
420 g_atomic_int_inc (&ignore_state_changes);
423 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
425 Playlist::thaw (bool from_undo)
427 g_atomic_int_dec_and_test (&ignore_state_changes);
428 release_notifications (from_undo);
433 Playlist::delay_notifications ()
435 g_atomic_int_inc (&block_notifications);
438 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
440 Playlist::release_notifications (bool from_undo)
442 if (g_atomic_int_dec_and_test (&block_notifications)) {
443 flush_notifications (from_undo);
448 Playlist::notify_contents_changed ()
450 if (holding_state ()) {
451 pending_contents_change = true;
453 pending_contents_change = false;
454 ContentsChanged(); /* EMIT SIGNAL */
459 Playlist::notify_layering_changed ()
461 if (holding_state ()) {
462 pending_layering = true;
464 pending_layering = false;
465 LayeringChanged(); /* EMIT SIGNAL */
470 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
472 if (holding_state ()) {
473 pending_removes.insert (r);
474 pending_contents_change = true;
476 /* this might not be true, but we have to act
477 as though it could be.
479 pending_contents_change = false;
480 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
481 ContentsChanged (); /* EMIT SIGNAL */
486 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
488 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
490 if (holding_state ()) {
492 pending_range_moves.push_back (move);
496 list< Evoral::RangeMove<framepos_t> > m;
498 RangesMoved (m, false);
504 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
506 if (r->position() >= r->last_position()) {
507 /* trimmed shorter */
511 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
513 if (holding_state ()) {
515 pending_region_extensions.push_back (extra);
519 list<Evoral::Range<framepos_t> > r;
527 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
529 if (r->length() < r->last_length()) {
530 /* trimmed shorter */
533 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
535 if (holding_state ()) {
537 pending_region_extensions.push_back (extra);
541 list<Evoral::Range<framepos_t> > r;
549 Playlist::notify_region_added (boost::shared_ptr<Region> r)
551 /* the length change might not be true, but we have to act
552 as though it could be.
555 if (holding_state()) {
556 pending_adds.insert (r);
557 pending_contents_change = true;
560 pending_contents_change = false;
561 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
562 ContentsChanged (); /* EMIT SIGNAL */
567 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
569 Playlist::flush_notifications (bool from_undo)
571 set<boost::shared_ptr<Region> >::iterator s;
572 bool regions_changed = false;
580 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
581 regions_changed = true;
584 /* XXX: it'd be nice if we could use pending_bounds for
585 RegionsExtended and RegionsMoved.
588 /* we have no idea what order the regions ended up in pending
589 bounds (it could be based on selection order, for example).
590 so, to preserve layering in the "most recently moved is higher"
591 model, sort them by existing layer, then timestamp them.
594 // RegionSortByLayer cmp;
595 // pending_bounds.sort (cmp);
597 list<Evoral::Range<framepos_t> > crossfade_ranges;
599 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
600 crossfade_ranges.push_back ((*r)->last_range ());
601 crossfade_ranges.push_back ((*r)->range ());
604 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
605 crossfade_ranges.push_back ((*s)->range ());
606 remove_dependents (*s);
607 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
610 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
611 crossfade_ranges.push_back ((*s)->range ());
612 /* don't emit RegionAdded signal until relayering is done,
613 so that the region is fully setup by the time
614 anyone hears that its been added
618 /* notify about contents/region changes first so that layering changes
619 * in a UI will take place on the new contents.
622 if (regions_changed || pending_contents_change) {
623 pending_layering = true;
624 ContentsChanged (); /* EMIT SIGNAL */
627 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
628 (*s)->clear_changes ();
629 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
632 if ((regions_changed && !in_set_state) || pending_layering) {
636 coalesce_and_check_crossfades (crossfade_ranges);
638 if (!pending_range_moves.empty ()) {
639 /* We don't need to check crossfades for these as pending_bounds has
642 RangesMoved (pending_range_moves, from_undo);
645 if (!pending_region_extensions.empty ()) {
646 RegionsExtended (pending_region_extensions);
655 Playlist::clear_pending ()
657 pending_adds.clear ();
658 pending_removes.clear ();
659 pending_bounds.clear ();
660 pending_range_moves.clear ();
661 pending_region_extensions.clear ();
662 pending_contents_change = false;
663 pending_layering = false;
666 /*************************************************************
668 *************************************************************/
670 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
672 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, const int32_t sub_num)
674 RegionWriteLock rlock (this);
675 times = fabs (times);
677 int itimes = (int) floor (times);
679 framepos_t pos = position;
681 if (times == 1 && auto_partition){
683 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
684 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
685 (*i)->resume_property_changes ();
686 _session.add_command (new StatefulDiffCommand (*i));
691 add_region_internal (region, pos, sub_num);
692 set_layer (region, DBL_MAX);
693 pos += region->length();
698 /* note that itimes can be zero if we being asked to just
699 insert a single fraction of the region.
702 for (int i = 0; i < itimes; ++i) {
703 boost::shared_ptr<Region> copy = RegionFactory::create (region, true, sub_num);
704 add_region_internal (copy, pos, sub_num);
705 set_layer (copy, DBL_MAX);
706 pos += region->length();
709 framecnt_t length = 0;
711 if (floor (times) != times) {
712 length = (framecnt_t) floor (region->length() * (times - floor (times)));
714 RegionFactory::region_name (name, region->name(), false);
719 plist.add (Properties::start, region->start());
720 plist.add (Properties::length, length);
721 plist.add (Properties::name, name);
722 plist.add (Properties::layer, region->layer());
724 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
725 add_region_internal (sub, pos, sub_num);
726 set_layer (sub, DBL_MAX);
730 possibly_splice_unlocked (position, (pos + length) - position, region);
734 Playlist::set_region_ownership ()
736 RegionWriteLock rl (this);
737 RegionList::iterator i;
738 boost::weak_ptr<Playlist> pl (shared_from_this());
740 for (i = regions.begin(); i != regions.end(); ++i) {
741 (*i)->set_playlist (pl);
746 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, const int32_t sub_num)
748 if (region->data_type() != _type) {
752 RegionSortByPosition cmp;
754 if (!first_set_state) {
755 boost::shared_ptr<Playlist> foo (shared_from_this());
756 region->set_playlist (boost::weak_ptr<Playlist>(foo));
759 region->set_position (position, sub_num);
761 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
762 all_regions.insert (region);
764 possibly_splice_unlocked (position, region->length(), region);
766 if (!holding_state ()) {
767 /* layers get assigned from XML state, and are not reset during undo/redo */
771 /* we need to notify the existence of new region before checking dependents. Ick. */
773 notify_region_added (region);
775 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
776 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
782 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
784 RegionWriteLock rlock (this);
786 bool old_sp = _splicing;
789 remove_region_internal (old);
790 add_region_internal (newr, pos);
791 set_layer (newr, old->layer ());
795 possibly_splice_unlocked (pos, old->length() - newr->length());
799 Playlist::remove_region (boost::shared_ptr<Region> region)
801 RegionWriteLock rlock (this);
802 remove_region_internal (region);
806 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
808 RegionList::iterator i;
812 region->set_playlist (boost::weak_ptr<Playlist>());
815 /* XXX should probably freeze here .... */
817 for (i = regions.begin(); i != regions.end(); ++i) {
820 framepos_t pos = (*i)->position();
821 framecnt_t distance = (*i)->length();
825 possibly_splice_unlocked (pos, -distance);
827 if (!holding_state ()) {
829 remove_dependents (region);
832 notify_region_removed (region);
841 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
843 if (Config->get_use_overlap_equivalency()) {
844 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
845 if ((*i)->overlap_equivalent (other)) {
846 results.push_back (*i);
850 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
851 if ((*i)->equivalent (other)) {
852 results.push_back (*i);
859 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
861 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
863 if ((*i) && (*i)->region_list_equivalent (other)) {
864 results.push_back (*i);
870 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
872 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
874 if ((*i) && (*i)->any_source_equivalent (other)) {
875 results.push_back (*i);
881 Playlist::partition (framepos_t start, framepos_t end, bool cut)
885 RegionWriteLock lock(this);
886 partition_internal (start, end, cut, thawlist);
889 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
890 (*i)->resume_property_changes ();
894 /** Go through each region on the playlist and cut them at start and end, removing the section between
895 * start and end if cutting == true. Regions that lie entirely within start and end are always
900 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
902 RegionList new_regions;
906 boost::shared_ptr<Region> region;
907 boost::shared_ptr<Region> current;
909 RegionList::iterator tmp;
910 Evoral::OverlapType overlap;
911 framepos_t pos1, pos2, pos3, pos4;
915 /* need to work from a copy, because otherwise the regions we add during the process
916 get operated on as well.
919 RegionList copy = regions.rlist();
921 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
928 if (current->first_frame() >= start && current->last_frame() < end) {
931 remove_region_internal (current);
937 /* coverage will return OverlapStart if the start coincides
938 with the end point. we do not partition such a region,
939 so catch this special case.
942 if (current->first_frame() >= end) {
946 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
950 pos1 = current->position();
953 pos4 = current->last_frame();
955 if (overlap == Evoral::OverlapInternal) {
956 /* split: we need 3 new regions, the front, middle and end.
957 cut: we need 2 regions, the front and end.
962 ---------------*************************------------
965 ---------------*****++++++++++++++++====------------
967 ---------------*****----------------====------------
972 /* "middle" ++++++ */
974 RegionFactory::region_name (new_name, current->name(), false);
978 plist.add (Properties::start, current->start() + (pos2 - pos1));
979 plist.add (Properties::length, pos3 - pos2);
980 plist.add (Properties::name, new_name);
981 plist.add (Properties::layer, current->layer ());
982 plist.add (Properties::layering_index, current->layering_index ());
983 plist.add (Properties::automatic, true);
984 plist.add (Properties::left_of_split, true);
985 plist.add (Properties::right_of_split, true);
987 region = RegionFactory::create (current, plist);
988 add_region_internal (region, start);
989 new_regions.push_back (region);
994 RegionFactory::region_name (new_name, current->name(), false);
998 plist.add (Properties::start, current->start() + (pos3 - pos1));
999 plist.add (Properties::length, pos4 - pos3);
1000 plist.add (Properties::name, new_name);
1001 plist.add (Properties::layer, current->layer ());
1002 plist.add (Properties::layering_index, current->layering_index ());
1003 plist.add (Properties::automatic, true);
1004 plist.add (Properties::right_of_split, true);
1006 region = RegionFactory::create (current, plist);
1008 add_region_internal (region, end);
1009 new_regions.push_back (region);
1013 current->clear_changes ();
1014 current->suspend_property_changes ();
1015 thawlist.push_back (current);
1016 current->cut_end (pos2 - 1);
1018 } else if (overlap == Evoral::OverlapEnd) {
1022 ---------------*************************------------
1025 ---------------**************+++++++++++------------
1027 ---------------**************-----------------------
1034 RegionFactory::region_name (new_name, current->name(), false);
1038 plist.add (Properties::start, current->start() + (pos2 - pos1));
1039 plist.add (Properties::length, pos4 - pos2);
1040 plist.add (Properties::name, new_name);
1041 plist.add (Properties::layer, current->layer ());
1042 plist.add (Properties::layering_index, current->layering_index ());
1043 plist.add (Properties::automatic, true);
1044 plist.add (Properties::left_of_split, true);
1046 region = RegionFactory::create (current, plist);
1048 add_region_internal (region, start);
1049 new_regions.push_back (region);
1054 current->clear_changes ();
1055 current->suspend_property_changes ();
1056 thawlist.push_back (current);
1057 current->cut_end (pos2 - 1);
1059 } else if (overlap == Evoral::OverlapStart) {
1061 /* split: we need 2 regions: the front and the end.
1062 cut: just trim current to skip the cut area
1067 ---------------*************************------------
1071 ---------------****+++++++++++++++++++++------------
1073 -------------------*********************------------
1079 RegionFactory::region_name (new_name, current->name(), false);
1083 plist.add (Properties::start, current->start());
1084 plist.add (Properties::length, pos3 - pos1);
1085 plist.add (Properties::name, new_name);
1086 plist.add (Properties::layer, current->layer ());
1087 plist.add (Properties::layering_index, current->layering_index ());
1088 plist.add (Properties::automatic, true);
1089 plist.add (Properties::right_of_split, true);
1091 region = RegionFactory::create (current, plist);
1093 add_region_internal (region, pos1);
1094 new_regions.push_back (region);
1099 current->clear_changes ();
1100 current->suspend_property_changes ();
1101 thawlist.push_back (current);
1102 current->trim_front (pos3);
1103 } else if (overlap == Evoral::OverlapExternal) {
1105 /* split: no split required.
1106 cut: remove the region.
1111 ---------------*************************------------
1115 ---------------*************************------------
1117 ----------------------------------------------------
1122 remove_region_internal (current);
1125 new_regions.push_back (current);
1129 in_partition = false;
1132 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1133 framepos_t wanted_length = end-start;
1134 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1137 boost::shared_ptr<Playlist>
1138 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1140 boost::shared_ptr<Playlist> ret;
1141 boost::shared_ptr<Playlist> pl;
1144 if (ranges.empty()) {
1145 return boost::shared_ptr<Playlist>();
1148 start = ranges.front().start;
1150 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1152 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1154 if (i == ranges.begin()) {
1158 /* paste the next section into the nascent playlist,
1159 offset to reflect the start of the first range we
1163 ret->paste (pl, (*i).start - start, 1.0f, 0);
1170 boost::shared_ptr<Playlist>
1171 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1174 return cut_copy (pmf, ranges, result_is_hidden);
1177 boost::shared_ptr<Playlist>
1178 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1180 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1181 return cut_copy (pmf, ranges, result_is_hidden);
1184 boost::shared_ptr<Playlist>
1185 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1187 boost::shared_ptr<Playlist> the_copy;
1188 RegionList thawlist;
1191 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1192 string new_name = _name;
1196 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1197 return boost::shared_ptr<Playlist>();
1201 RegionWriteLock rlock (this);
1202 partition_internal (start, start+cnt-1, true, thawlist);
1205 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1206 (*i)->resume_property_changes();
1212 boost::shared_ptr<Playlist>
1213 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1217 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1218 string new_name = _name;
1222 // 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... )
1224 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1228 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1230 times = fabs (times);
1233 RegionReadLock rl2 (other.get());
1235 int itimes = (int) floor (times);
1236 framepos_t pos = position;
1237 framecnt_t const shift = other->_get_extent().second;
1238 layer_t top = top_layer ();
1241 RegionWriteLock rl1 (this);
1243 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1244 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1246 /* put these new regions on top of all existing ones, but preserve
1247 the ordering they had in the original playlist.
1250 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1251 set_layer (copy_of_region, copy_of_region->layer() + top);
1263 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1265 duplicate(region, position, region->length(), times);
1268 /** @param gap from the beginning of the region to the next beginning */
1270 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1272 times = fabs (times);
1274 RegionWriteLock rl (this);
1275 int itimes = (int) floor (times);
1278 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1279 add_region_internal (copy, position);
1280 set_layer (copy, DBL_MAX);
1284 if (floor (times) != times) {
1285 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1287 RegionFactory::region_name (name, region->name(), false);
1292 plist.add (Properties::start, region->start());
1293 plist.add (Properties::length, length);
1294 plist.add (Properties::name, name);
1296 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1297 add_region_internal (sub, position);
1298 set_layer (sub, DBL_MAX);
1303 /** @param gap from the beginning of the region to the next beginning */
1304 /** @param end the first frame that does _not_ contain a duplicated frame */
1306 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1308 RegionWriteLock rl (this);
1310 while (position + region->length() - 1 < end) {
1311 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1312 add_region_internal (copy, position);
1313 set_layer (copy, DBL_MAX);
1317 if (position < end) {
1318 framecnt_t length = min (region->length(), end - position);
1320 RegionFactory::region_name (name, region->name(), false);
1325 plist.add (Properties::start, region->start());
1326 plist.add (Properties::length, length);
1327 plist.add (Properties::name, name);
1329 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1330 add_region_internal (sub, position);
1331 set_layer (sub, DBL_MAX);
1337 Playlist::duplicate_range (AudioRange& range, float times)
1339 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1340 framecnt_t offset = range.end - range.start;
1341 paste (pl, range.start + offset, times, 0);
1345 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1347 if (ranges.empty()) {
1351 framepos_t min_pos = max_framepos;
1352 framepos_t max_pos = 0;
1354 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1357 min_pos = min (min_pos, (*i).start);
1358 max_pos = max (max_pos, (*i).end);
1361 framecnt_t offset = max_pos - min_pos;
1364 int itimes = (int) floor (times);
1366 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1367 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1368 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1375 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1377 RegionWriteLock rlock (this);
1378 RegionList copy (regions.rlist());
1381 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1383 if ((*r)->last_frame() < at) {
1388 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1389 /* intersected region */
1390 if (!move_intersected) {
1395 /* do not move regions glued to music time - that
1396 has to be done separately.
1399 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1400 fixup.push_back (*r);
1404 (*r)->set_position ((*r)->position() + distance);
1407 /* XXX: may not be necessary; Region::post_set should do this, I think */
1408 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1409 (*r)->recompute_position_from_lock_style (0);
1414 Playlist::split (framepos_t at, const int32_t sub_num)
1416 RegionWriteLock rlock (this);
1417 RegionList copy (regions.rlist());
1419 /* use a copy since this operation can modify the region list
1422 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1423 _split_region (*r, at, sub_num);
1428 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1430 RegionWriteLock rl (this);
1431 _split_region (region, playlist_position, sub_num);
1435 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1437 if (!region->covers (playlist_position)) {
1441 if (region->position() == playlist_position ||
1442 region->last_frame() == playlist_position) {
1446 boost::shared_ptr<Region> left;
1447 boost::shared_ptr<Region> right;
1448 frameoffset_t before;
1449 frameoffset_t after;
1453 /* split doesn't change anything about length, so don't try to splice */
1455 bool old_sp = _splicing;
1458 before = playlist_position - region->position();
1459 after = region->length() - before;
1461 RegionFactory::region_name (before_name, region->name(), false);
1466 plist.add (Properties::length, before);
1467 plist.add (Properties::name, before_name);
1468 plist.add (Properties::left_of_split, true);
1469 plist.add (Properties::layering_index, region->layering_index ());
1470 plist.add (Properties::layer, region->layer ());
1472 /* note: we must use the version of ::create with an offset here,
1473 since it supplies that offset to the Region constructor, which
1474 is necessary to get audio region gain envelopes right.
1476 left = RegionFactory::create (region, 0, plist, true, sub_num);
1479 RegionFactory::region_name (after_name, region->name(), false);
1484 plist.add (Properties::length, after);
1485 plist.add (Properties::name, after_name);
1486 plist.add (Properties::right_of_split, true);
1487 plist.add (Properties::layering_index, region->layering_index ());
1488 plist.add (Properties::layer, region->layer ());
1490 /* same note as above */
1491 right = RegionFactory::create (region, before, plist, true, sub_num);
1494 add_region_internal (left, region->position());
1495 add_region_internal (right, region->position() + before);
1496 remove_region_internal (region);
1502 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1504 if (_splicing || in_set_state) {
1505 /* don't respond to splicing moves or state setting */
1509 if (_edit_mode == Splice) {
1510 splice_locked (at, distance, exclude);
1515 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1517 if (_splicing || in_set_state) {
1518 /* don't respond to splicing moves or state setting */
1522 if (_edit_mode == Splice) {
1523 splice_unlocked (at, distance, exclude);
1528 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1531 RegionWriteLock rl (this);
1532 core_splice (at, distance, exclude);
1537 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1539 core_splice (at, distance, exclude);
1543 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1547 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1549 if (exclude && (*i) == exclude) {
1553 if ((*i)->position() >= at) {
1554 framepos_t new_pos = (*i)->position() + distance;
1557 } else if (new_pos >= max_framepos - (*i)->length()) {
1558 new_pos = max_framepos - (*i)->length();
1561 (*i)->set_position (new_pos);
1567 notify_contents_changed ();
1571 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1574 RegionWriteLock rl (this);
1575 core_ripple (at, distance, exclude);
1580 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1582 core_ripple (at, distance, exclude);
1586 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1588 if (distance == 0) {
1593 RegionListProperty copy = regions;
1594 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1595 assert (i != copy.end());
1598 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1603 if ((*i)->position() >= at) {
1604 framepos_t new_pos = (*i)->position() + distance;
1605 framepos_t limit = max_framepos - (*i)->length();
1608 } else if (new_pos >= limit ) {
1612 (*i)->set_position (new_pos);
1617 notify_contents_changed ();
1622 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1624 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1628 if (what_changed.contains (Properties::position)) {
1630 /* remove it from the list then add it back in
1631 the right place again.
1634 RegionSortByPosition cmp;
1636 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1638 if (i == regions.end()) {
1639 /* the region bounds are being modified but its not currently
1640 in the region list. we will use its bounds correctly when/if
1647 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1650 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1652 frameoffset_t delta = 0;
1654 if (what_changed.contains (Properties::position)) {
1655 delta = region->position() - region->last_position();
1658 if (what_changed.contains (Properties::length)) {
1659 delta += region->length() - region->last_length();
1663 possibly_splice (region->last_position() + region->last_length(), delta, region);
1666 if (holding_state ()) {
1667 pending_bounds.push_back (region);
1669 notify_contents_changed ();
1671 list<Evoral::Range<framepos_t> > xf;
1672 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1673 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1674 coalesce_and_check_crossfades (xf);
1680 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1682 boost::shared_ptr<Region> region (weak_region.lock());
1688 /* this makes a virtual call to the right kind of playlist ... */
1690 region_changed (what_changed, region);
1694 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1696 PropertyChange our_interests;
1697 PropertyChange bounds;
1698 PropertyChange pos_and_length;
1701 if (in_set_state || in_flush) {
1705 our_interests.add (Properties::muted);
1706 our_interests.add (Properties::layer);
1707 our_interests.add (Properties::opaque);
1709 bounds.add (Properties::start);
1710 bounds.add (Properties::position);
1711 bounds.add (Properties::length);
1713 pos_and_length.add (Properties::position);
1714 pos_and_length.add (Properties::length);
1716 if (what_changed.contains (bounds)) {
1717 region_bounds_changed (what_changed, region);
1718 save = !(_splicing || _nudging);
1721 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1722 notify_region_moved (region);
1723 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1724 notify_region_end_trimmed (region);
1725 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1726 notify_region_start_trimmed (region);
1729 /* don't notify about layer changes, since we are the only object that can initiate
1730 them, and we notify in ::relayer()
1733 if (what_changed.contains (our_interests)) {
1737 mark_session_dirty ();
1743 Playlist::drop_regions ()
1745 RegionWriteLock rl (this);
1747 all_regions.clear ();
1751 Playlist::sync_all_regions_with_regions ()
1753 RegionWriteLock rl (this);
1755 all_regions.clear ();
1757 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1758 all_regions.insert (*i);
1763 Playlist::clear (bool with_signals)
1766 RegionWriteLock rl (this);
1768 region_state_changed_connections.drop_connections ();
1769 region_drop_references_connections.drop_connections ();
1771 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1772 pending_removes.insert (*i);
1777 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1778 remove_dependents (*s);
1784 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1785 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1788 pending_removes.clear ();
1789 pending_contents_change = false;
1795 /* *********************************************************************
1797 **********************************************************************/
1799 boost::shared_ptr<RegionList>
1800 Playlist::region_list()
1802 RegionReadLock rlock (this);
1803 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1808 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1810 RegionReadLock rlock (const_cast<Playlist*>(this));
1812 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1813 (*i)->deep_sources (sources);
1817 boost::shared_ptr<RegionList>
1818 Playlist::regions_at (framepos_t frame)
1820 RegionReadLock rlock (this);
1821 return find_regions_at (frame);
1825 Playlist::count_regions_at (framepos_t frame) const
1827 RegionReadLock rlock (const_cast<Playlist*>(this));
1830 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1831 if ((*i)->covers (frame)) {
1839 boost::shared_ptr<Region>
1840 Playlist::top_region_at (framepos_t frame)
1843 RegionReadLock rlock (this);
1844 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1845 boost::shared_ptr<Region> region;
1847 if (rlist->size()) {
1848 RegionSortByLayer cmp;
1850 region = rlist->back();
1856 boost::shared_ptr<Region>
1857 Playlist::top_unmuted_region_at (framepos_t frame)
1860 RegionReadLock rlock (this);
1861 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1863 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1865 RegionList::iterator tmp = i;
1869 if ((*i)->muted()) {
1876 boost::shared_ptr<Region> region;
1878 if (rlist->size()) {
1879 RegionSortByLayer cmp;
1881 region = rlist->back();
1887 boost::shared_ptr<RegionList>
1888 Playlist::find_regions_at (framepos_t frame)
1890 /* Caller must hold lock */
1892 boost::shared_ptr<RegionList> rlist (new RegionList);
1894 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1895 if ((*i)->covers (frame)) {
1896 rlist->push_back (*i);
1903 boost::shared_ptr<RegionList>
1904 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1906 RegionReadLock rlock (this);
1907 boost::shared_ptr<RegionList> rlist (new RegionList);
1909 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1910 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1911 rlist->push_back (*i);
1918 boost::shared_ptr<RegionList>
1919 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1921 RegionReadLock rlock (this);
1922 boost::shared_ptr<RegionList> rlist (new RegionList);
1924 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1925 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1926 rlist->push_back (*i);
1933 /** @param start Range start.
1934 * @param end Range end.
1935 * @return regions which have some part within this range.
1937 boost::shared_ptr<RegionList>
1938 Playlist::regions_touched (framepos_t start, framepos_t end)
1940 RegionReadLock rlock (this);
1941 return regions_touched_locked (start, end);
1944 boost::shared_ptr<RegionList>
1945 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1947 boost::shared_ptr<RegionList> rlist (new RegionList);
1949 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1950 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1951 rlist->push_back (*i);
1959 Playlist::find_next_transient (framepos_t from, int dir)
1961 RegionReadLock rlock (this);
1962 AnalysisFeatureList points;
1963 AnalysisFeatureList these_points;
1965 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1967 if ((*i)->last_frame() < from) {
1971 if ((*i)->first_frame() > from) {
1976 (*i)->get_transients (these_points);
1978 /* add first frame, just, err, because */
1980 these_points.push_back ((*i)->first_frame());
1982 points.insert (points.end(), these_points.begin(), these_points.end());
1983 these_points.clear ();
1986 if (points.empty()) {
1990 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1991 bool reached = false;
1994 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1999 if (reached && (*x) > from) {
2004 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2009 if (reached && (*x) < from) {
2018 boost::shared_ptr<Region>
2019 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2021 RegionReadLock rlock (this);
2022 boost::shared_ptr<Region> ret;
2023 framepos_t closest = max_framepos;
2025 bool end_iter = false;
2027 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2031 frameoffset_t distance;
2032 boost::shared_ptr<Region> r = (*i);
2037 pos = r->first_frame ();
2040 pos = r->last_frame ();
2043 pos = r->sync_position ();
2048 case 1: /* forwards */
2051 if ((distance = pos - frame) < closest) {
2060 default: /* backwards */
2063 if ((distance = frame - pos) < closest) {
2079 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2081 RegionReadLock rlock (this);
2083 framepos_t closest = max_framepos;
2084 framepos_t ret = -1;
2088 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2090 boost::shared_ptr<Region> r = (*i);
2091 frameoffset_t distance;
2092 const framepos_t first_frame = r->first_frame();
2093 const framepos_t last_frame = r->last_frame();
2095 if (first_frame > frame) {
2097 distance = first_frame - frame;
2099 if (distance < closest) {
2105 if (last_frame > frame) {
2107 distance = last_frame - frame;
2109 if (distance < closest) {
2118 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2120 boost::shared_ptr<Region> r = (*i);
2121 frameoffset_t distance;
2122 const framepos_t first_frame = r->first_frame();
2123 const framepos_t last_frame = r->last_frame();
2125 if (last_frame < frame) {
2127 distance = frame - last_frame;
2129 if (distance < closest) {
2135 if (first_frame < frame) {
2137 distance = frame - first_frame;
2139 if (distance < closest) {
2151 /***********************************************************************/
2157 Playlist::mark_session_dirty ()
2159 if (!in_set_state && !holding_state ()) {
2160 _session.set_dirty();
2165 Playlist::rdiff (vector<Command*>& cmds) const
2167 RegionReadLock rlock (const_cast<Playlist *> (this));
2168 Stateful::rdiff (cmds);
2172 Playlist::clear_owned_changes ()
2174 RegionReadLock rlock (this);
2175 Stateful::clear_owned_changes ();
2179 Playlist::update (const RegionListProperty::ChangeRecord& change)
2181 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2182 name(), change.added.size(), change.removed.size()));
2185 /* add the added regions */
2186 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2187 add_region_internal ((*i), (*i)->position());
2189 /* remove the removed regions */
2190 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2198 Playlist::set_state (const XMLNode& node, int version)
2202 XMLNodeConstIterator niter;
2203 XMLPropertyList plist;
2204 XMLPropertyConstIterator piter;
2205 XMLProperty const * prop;
2206 boost::shared_ptr<Region> region;
2208 bool seen_region_nodes = false;
2213 if (node.name() != "Playlist") {
2220 plist = node.properties();
2224 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2228 if (prop->name() == X_("name")) {
2229 _name = prop->value();
2231 } else if (prop->name() == X_("orig-diskstream-id")) {
2232 /* XXX legacy session: fix up later */
2233 _orig_track_id = prop->value ();
2234 } else if (prop->name() == X_("orig-track-id")) {
2235 _orig_track_id = prop->value ();
2236 } else if (prop->name() == X_("frozen")) {
2237 _frozen = string_is_affirmative (prop->value());
2238 } else if (prop->name() == X_("combine-ops")) {
2239 _combine_ops = atoi (prop->value());
2240 } else if (prop->name() == X_("shared-with-ids")) {
2241 string shared_ids = prop->value ();
2242 if (!shared_ids.empty()) {
2243 vector<string> result;
2244 ::split (shared_ids, result, ',');
2245 vector<string>::iterator it = result.begin();
2246 for (; it != result.end(); ++it) {
2247 _shared_with_ids.push_back (PBD::ID(*it));
2255 nlist = node.children();
2257 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2261 if (child->name() == "Region") {
2263 seen_region_nodes = true;
2265 if ((prop = child->property ("id")) == 0) {
2266 error << _("region state node has no ID, ignored") << endmsg;
2270 ID id = prop->value ();
2272 if ((region = region_by_id (id))) {
2274 region->suspend_property_changes ();
2276 if (region->set_state (*child, version)) {
2277 region->resume_property_changes ();
2281 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2282 region->suspend_property_changes ();
2284 error << _("Playlist: cannot create region from XML") << endmsg;
2289 RegionWriteLock rlock (this);
2290 add_region_internal (region, region->position());
2293 region->resume_property_changes ();
2298 if (seen_region_nodes && regions.empty()) {
2303 notify_contents_changed ();
2306 first_set_state = false;
2312 Playlist::get_state()
2314 return state (true);
2318 Playlist::get_template()
2320 return state (false);
2323 /** @param full_state true to include regions in the returned state, otherwise false.
2326 Playlist::state (bool full_state)
2328 XMLNode *node = new XMLNode (X_("Playlist"));
2331 node->add_property (X_("id"), id().to_s());
2332 node->add_property (X_("name"), _name);
2333 node->add_property (X_("type"), _type.to_string());
2335 _orig_track_id.print (buf, sizeof (buf));
2336 node->add_property (X_("orig-track-id"), buf);
2339 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2340 for (; it != _shared_with_ids.end(); ++it) {
2341 shared_ids += "," + (*it).to_s();
2343 if (!shared_ids.empty()) {
2344 shared_ids.erase(0,1);
2347 node->add_property (X_("shared-with-ids"), shared_ids);
2348 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2351 RegionReadLock rlock (this);
2353 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2354 node->add_property ("combine-ops", buf);
2356 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2357 node->add_child_nocopy ((*i)->get_state());
2362 node->add_child_copy (*_extra_xml);
2369 Playlist::empty() const
2371 RegionReadLock rlock (const_cast<Playlist *>(this));
2372 return regions.empty();
2376 Playlist::n_regions() const
2378 RegionReadLock rlock (const_cast<Playlist *>(this));
2379 return regions.size();
2382 /** @return true if the all_regions list is empty, ie this playlist
2383 * has never had a region added to it.
2386 Playlist::all_regions_empty() const
2388 RegionReadLock rl (const_cast<Playlist *> (this));
2389 return all_regions.empty();
2392 pair<framepos_t, framepos_t>
2393 Playlist::get_extent () const
2395 RegionReadLock rlock (const_cast<Playlist *>(this));
2396 return _get_extent ();
2399 pair<framepos_t, framepos_t>
2400 Playlist::get_extent_with_endspace () const
2402 pair<framepos_t, framepos_t> l = get_extent();
2403 l.second += _end_space;
2407 pair<framepos_t, framepos_t>
2408 Playlist::_get_extent () const
2410 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2412 if (regions.empty()) {
2417 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2418 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2419 if (e.first < ext.first) {
2420 ext.first = e.first;
2422 if (e.second > ext.second) {
2423 ext.second = e.second;
2431 Playlist::bump_name (string name, Session &session)
2433 string newname = name;
2436 newname = bump_name_once (newname, '.');
2437 } while (session.playlists->by_name (newname)!=NULL);
2444 Playlist::top_layer() const
2446 RegionReadLock rlock (const_cast<Playlist *> (this));
2449 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2450 top = max (top, (*i)->layer());
2456 Playlist::set_edit_mode (EditMode mode)
2461 struct RelayerSort {
2462 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2463 return a->layering_index() < b->layering_index();
2467 /** Set a new layer for a region. This adjusts the layering indices of all
2468 * regions in the playlist to put the specified region in the appropriate
2469 * place. The actual layering will be fixed up when relayer() happens.
2473 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2475 /* Remove the layer we are setting from our region list, and sort it
2476 * using the layer indeces.
2479 RegionList copy = regions.rlist();
2480 copy.remove (region);
2481 copy.sort (RelayerSort ());
2483 /* Put region back in the right place */
2484 RegionList::iterator i = copy.begin();
2485 while (i != copy.end ()) {
2486 if ((*i)->layer() > new_layer) {
2492 copy.insert (i, region);
2494 setup_layering_indices (copy);
2498 Playlist::setup_layering_indices (RegionList const & regions)
2502 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2503 (*k)->set_layering_index (j++);
2507 struct LaterHigherSort {
2508 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2509 return a->position() < b->position();
2513 /** Take the layering indices of each of our regions, compute the layers
2514 * that they should be on, and write the layers back to the regions.
2517 Playlist::relayer ()
2519 /* never compute layers when setting from XML */
2525 /* Build up a new list of regions on each layer, stored in a set of lists
2526 each of which represent some period of time on some layer. The idea
2527 is to avoid having to search the entire region list to establish whether
2528 each region overlaps another */
2530 /* how many pieces to divide this playlist's time up into */
2531 int const divisions = 512;
2533 /* find the start and end positions of the regions on this playlist */
2534 framepos_t start = INT64_MAX;
2536 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2537 start = min (start, (*i)->position());
2538 end = max (end, (*i)->position() + (*i)->length());
2541 /* hence the size of each time division */
2542 double const division_size = (end - start) / double (divisions);
2544 vector<vector<RegionList> > layers;
2545 layers.push_back (vector<RegionList> (divisions));
2547 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2548 RegionList copy = regions.rlist();
2549 switch (Config->get_layer_model()) {
2551 copy.sort (LaterHigherSort ());
2554 copy.sort (RelayerSort ());
2558 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2559 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2560 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2563 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2565 /* find the time divisions that this region covers; if there are no regions on the list,
2566 division_size will equal 0 and in this case we'll just say that
2567 start_division = end_division = 0.
2569 int start_division = 0;
2570 int end_division = 0;
2572 if (division_size > 0) {
2573 start_division = floor ( ((*i)->position() - start) / division_size);
2574 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2575 if (end_division == divisions) {
2580 assert (divisions == 0 || end_division < divisions);
2582 /* find the lowest layer that this region can go on */
2583 size_t j = layers.size();
2585 /* try layer j - 1; it can go on if it overlaps no other region
2586 that is already on that layer
2589 bool overlap = false;
2590 for (int k = start_division; k <= end_division; ++k) {
2591 RegionList::iterator l = layers[j-1][k].begin ();
2592 while (l != layers[j-1][k].end()) {
2593 if ((*l)->overlap_equivalent (*i)) {
2606 /* overlap, so we must use layer j */
2613 if (j == layers.size()) {
2614 /* we need a new layer for this region */
2615 layers.push_back (vector<RegionList> (divisions));
2618 /* put a reference to this region in each of the divisions that it exists in */
2619 for (int k = start_division; k <= end_division; ++k) {
2620 layers[j][k].push_back (*i);
2623 (*i)->set_layer (j);
2626 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2627 relayering because we just removed the only region on the top layer, nothing will
2628 appear to have changed, but the StreamView must still sort itself out. We could
2629 probably keep a note of the top layer last time we relayered, and check that,
2630 but premature optimisation &c...
2632 notify_layering_changed ();
2634 /* This relayer() may have been called as a result of a region removal, in which
2635 case we need to setup layering indices to account for the one that has just
2638 setup_layering_indices (copy);
2642 Playlist::raise_region (boost::shared_ptr<Region> region)
2644 set_layer (region, region->layer() + 1.5);
2649 Playlist::lower_region (boost::shared_ptr<Region> region)
2651 set_layer (region, region->layer() - 1.5);
2656 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2658 set_layer (region, DBL_MAX);
2663 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2665 set_layer (region, -0.5);
2670 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2672 RegionList::iterator i;
2678 RegionWriteLock rlock (const_cast<Playlist *> (this));
2680 for (i = regions.begin(); i != regions.end(); ++i) {
2682 if ((*i)->position() >= start) {
2688 if ((*i)->last_frame() > max_framepos - distance) {
2689 new_pos = max_framepos - (*i)->length();
2691 new_pos = (*i)->position() + distance;
2696 if ((*i)->position() > distance) {
2697 new_pos = (*i)->position() - distance;
2703 (*i)->set_position (new_pos);
2711 notify_contents_changed ();
2717 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2719 RegionReadLock rlock (const_cast<Playlist*> (this));
2721 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2722 /* Note: passing the second argument as false can cause at best
2723 incredibly deep and time-consuming recursion, and at worst
2724 cycles if the user has managed to create cycles of reference
2725 between compound regions. We generally only this during
2726 cleanup, and @param shallow is passed as true.
2728 if ((*r)->uses_source (src, shallow)) {
2737 boost::shared_ptr<Region>
2738 Playlist::find_region (const ID& id) const
2740 RegionReadLock rlock (const_cast<Playlist*> (this));
2742 /* searches all regions currently in use by the playlist */
2744 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2745 if ((*i)->id() == id) {
2750 return boost::shared_ptr<Region> ();
2754 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2756 RegionReadLock rlock (const_cast<Playlist*> (this));
2759 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2765 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2766 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2767 /* check if region is used in a compound */
2768 if (it->second == r) {
2769 /* region is referenced as 'original' of a compound */
2773 if (r->whole_file() && r->max_source_level() > 0) {
2774 /* region itself ia a compound.
2775 * the compound regions are not referenced -> check regions inside compound
2777 const SourceList& sl = r->sources();
2778 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2779 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2781 if (ps->playlist()->region_use_count(it->first)) {
2782 // break out of both loops
2791 boost::shared_ptr<Region>
2792 Playlist::region_by_id (const ID& id) const
2794 /* searches all regions ever added to this playlist */
2796 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2797 if ((*i)->id() == id) {
2801 return boost::shared_ptr<Region> ();
2805 Playlist::dump () const
2807 boost::shared_ptr<Region> r;
2809 cerr << "Playlist \"" << _name << "\" " << endl
2810 << regions.size() << " regions "
2813 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2815 cerr << " " << r->name() << " ["
2816 << r->start() << "+" << r->length()
2826 Playlist::set_frozen (bool yn)
2832 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2836 if (region->locked()) {
2843 RegionWriteLock rlock (const_cast<Playlist*> (this));
2848 RegionList::iterator next;
2850 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2851 if ((*i) == region) {
2855 if (next != regions.end()) {
2857 if ((*next)->locked()) {
2863 if ((*next)->position() != region->last_frame() + 1) {
2864 /* they didn't used to touch, so after shuffle,
2865 just have them swap positions.
2867 new_pos = (*next)->position();
2869 /* they used to touch, so after shuffle,
2870 make sure they still do. put the earlier
2871 region where the later one will end after
2874 new_pos = region->position() + (*next)->length();
2877 (*next)->set_position (region->position());
2878 region->set_position (new_pos);
2880 /* avoid a full sort */
2882 regions.erase (i); // removes the region from the list */
2884 regions.insert (next, region); // adds it back after next
2893 RegionList::iterator prev = regions.end();
2895 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2896 if ((*i) == region) {
2898 if (prev != regions.end()) {
2900 if ((*prev)->locked()) {
2905 if (region->position() != (*prev)->last_frame() + 1) {
2906 /* they didn't used to touch, so after shuffle,
2907 just have them swap positions.
2909 new_pos = region->position();
2911 /* they used to touch, so after shuffle,
2912 make sure they still do. put the earlier
2913 one where the later one will end after
2915 new_pos = (*prev)->position() + region->length();
2918 region->set_position ((*prev)->position());
2919 (*prev)->set_position (new_pos);
2921 /* avoid a full sort */
2923 regions.erase (i); // remove region
2924 regions.insert (prev, region); // insert region before prev
2940 notify_contents_changed();
2946 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2948 RegionReadLock rlock (const_cast<Playlist*> (this));
2950 if (regions.size() > 1) {
2958 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2960 ripple_locked (at, distance, exclude);
2964 Playlist::update_after_tempo_map_change ()
2966 RegionWriteLock rlock (const_cast<Playlist*> (this));
2967 RegionList copy (regions.rlist());
2971 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2972 (*i)->update_after_tempo_map_change ();
2974 /* possibly causes a contents changed notification (flush_notifications()) */
2979 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2981 RegionReadLock rl (this);
2982 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2988 Playlist::has_region_at (framepos_t const p) const
2990 RegionReadLock (const_cast<Playlist *> (this));
2992 RegionList::const_iterator i = regions.begin ();
2993 while (i != regions.end() && !(*i)->covers (p)) {
2997 return (i != regions.end());
3000 /** Look from a session frame time and find the start time of the next region
3001 * which is on the top layer of this playlist.
3002 * @param t Time to look from.
3003 * @return Position of next top-layered region, or max_framepos if there isn't one.
3006 Playlist::find_next_top_layer_position (framepos_t t) const
3008 RegionReadLock rlock (const_cast<Playlist *> (this));
3010 layer_t const top = top_layer ();
3012 RegionList copy = regions.rlist ();
3013 copy.sort (RegionSortByPosition ());
3015 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3016 if ((*i)->position() >= t && (*i)->layer() == top) {
3017 return (*i)->position();
3021 return max_framepos;
3024 boost::shared_ptr<Region>
3025 Playlist::combine (const RegionList& r)
3028 uint32_t channels = 0;
3030 framepos_t earliest_position = max_framepos;
3031 vector<TwoRegions> old_and_new_regions;
3032 vector<boost::shared_ptr<Region> > originals;
3033 vector<boost::shared_ptr<Region> > copies;
3036 uint32_t max_level = 0;
3038 /* find the maximum depth of all the regions we're combining */
3040 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3041 max_level = max (max_level, (*i)->max_source_level());
3044 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3045 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3047 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3049 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3050 earliest_position = min (earliest_position, (*i)->position());
3053 /* enable this so that we do not try to create xfades etc. as we add
3057 pl->in_partition = true;
3059 /* sort by position then layer.
3060 * route_time_axis passes 'selected_regions' - which is not sorted.
3061 * here we need the top-most first, then every layer's region sorted by position.
3063 RegionList sorted(r);
3064 sorted.sort(RegionSortByLayerAndPosition());
3066 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3068 /* copy the region */
3070 boost::shared_ptr<Region> original_region = (*i);
3071 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3073 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3074 originals.push_back (original_region);
3075 copies.push_back (copied_region);
3077 RegionFactory::add_compound_association (original_region, copied_region);
3079 /* make position relative to zero */
3081 pl->add_region (copied_region, original_region->position() - earliest_position);
3082 copied_region->set_layer (original_region->layer ());
3084 /* use the maximum number of channels for any region */
3086 channels = max (channels, original_region->n_channels());
3088 /* it will go above the layer of the highest existing region */
3090 layer = max (layer, original_region->layer());
3093 pl->in_partition = false;
3095 pre_combine (copies);
3097 /* now create a new PlaylistSource for each channel in the new playlist */
3100 pair<framepos_t,framepos_t> extent = pl->get_extent();
3102 for (uint32_t chn = 0; chn < channels; ++chn) {
3103 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3107 /* now a new whole-file region using the list of sources */
3109 plist.add (Properties::start, 0);
3110 plist.add (Properties::length, extent.second);
3111 plist.add (Properties::name, parent_name);
3112 plist.add (Properties::whole_file, true);
3114 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3116 /* now the non-whole-file region that we will actually use in the
3121 plist.add (Properties::start, 0);
3122 plist.add (Properties::length, extent.second);
3123 plist.add (Properties::name, child_name);
3124 plist.add (Properties::layer, layer+1);
3126 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3128 /* remove all the selected regions from the current playlist
3133 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3137 /* do type-specific stuff with the originals and the new compound
3141 post_combine (originals, compound_region);
3143 /* add the new region at the right location */
3145 add_region (compound_region, earliest_position);
3151 return compound_region;
3155 Playlist::uncombine (boost::shared_ptr<Region> target)
3157 boost::shared_ptr<PlaylistSource> pls;
3158 boost::shared_ptr<const Playlist> pl;
3159 vector<boost::shared_ptr<Region> > originals;
3160 vector<TwoRegions> old_and_new_regions;
3162 // (1) check that its really a compound region
3164 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3168 pl = pls->playlist();
3170 framepos_t adjusted_start = 0; // gcc isn't smart enough
3171 framepos_t adjusted_end = 0; // gcc isn't smart enough
3173 /* the leftmost (earliest) edge of the compound region
3174 starts at zero in its source, or larger if it
3175 has been trimmed or content-scrolled.
3177 the rightmost (latest) edge of the compound region
3178 relative to its source is the starting point plus
3179 the length of the region.
3182 // (2) get all the original regions
3184 const RegionList& rl (pl->region_list_property().rlist());
3185 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3186 frameoffset_t move_offset = 0;
3188 /* there are two possibilities here:
3189 1) the playlist that the playlist source was based on
3190 is us, so just add the originals (which belonged to
3191 us anyway) back in the right place.
3193 2) the playlist that the playlist source was based on
3194 is NOT us, so we need to make copies of each of
3195 the original regions that we find, and add them
3198 bool same_playlist = (pls->original() == id());
3200 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3202 boost::shared_ptr<Region> current (*i);
3204 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3206 if (ca == cassocs.end()) {
3210 boost::shared_ptr<Region> original (ca->second);
3212 bool modified_region;
3214 if (i == rl.begin()) {
3215 move_offset = (target->position() - original->position()) - target->start();
3216 adjusted_start = original->position() + target->start();
3217 adjusted_end = adjusted_start + target->length();
3220 if (!same_playlist) {
3221 framepos_t pos = original->position();
3222 /* make a copy, but don't announce it */
3223 original = RegionFactory::create (original, false);
3224 /* the pure copy constructor resets position() to zero,
3227 original->set_position (pos);
3230 /* check to see how the original region (in the
3231 * playlist before compounding occurred) overlaps
3232 * with the new state of the compound region.
3235 original->clear_changes ();
3236 modified_region = false;
3238 switch (original->coverage (adjusted_start, adjusted_end)) {
3239 case Evoral::OverlapNone:
3240 /* original region does not cover any part
3241 of the current state of the compound region
3245 case Evoral::OverlapInternal:
3246 /* overlap is just a small piece inside the
3247 * original so trim both ends
3249 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3250 modified_region = true;
3253 case Evoral::OverlapExternal:
3254 /* overlap fully covers original, so leave it
3259 case Evoral::OverlapEnd:
3260 /* overlap starts within but covers end,
3261 so trim the front of the region
3263 original->trim_front (adjusted_start);
3264 modified_region = true;
3267 case Evoral::OverlapStart:
3268 /* overlap covers start but ends within, so
3269 * trim the end of the region.
3271 original->trim_end (adjusted_end);
3272 modified_region = true;
3277 /* fix the position to match any movement of the compound region.
3279 original->set_position (original->position() + move_offset);
3280 modified_region = true;
3283 if (modified_region) {
3284 _session.add_command (new StatefulDiffCommand (original));
3287 /* and add to the list of regions waiting to be
3291 originals.push_back (original);
3292 old_and_new_regions.push_back (TwoRegions (*i, original));
3295 pre_uncombine (originals, target);
3297 in_partition = true;
3300 // (3) remove the compound region
3302 remove_region (target);
3304 // (4) add the constituent regions
3306 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3307 add_region ((*i), (*i)->position());
3308 set_layer((*i), (*i)->layer());
3309 if (!RegionFactory::region_by_id((*i)->id())) {
3310 RegionFactory::map_add(*i);
3314 in_partition = false;
3319 Playlist::fade_range (list<AudioRange>& ranges)
3321 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3322 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3323 (*i)->fade_range ((*r).start, (*r).end);
3329 Playlist::max_source_level () const
3331 RegionReadLock rlock (const_cast<Playlist *> (this));
3334 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3335 lvl = max (lvl, (*i)->max_source_level());
3342 Playlist::set_orig_track_id (const PBD::ID& id)
3344 if (shared_with(id)) {
3345 // Swap 'shared_id' / origin_track_id
3347 share_with (_orig_track_id);
3349 _orig_track_id = id;
3353 Playlist::share_with (const PBD::ID& id)
3355 if (!shared_with(id)) {
3356 _shared_with_ids.push_back (id);
3361 Playlist::unshare_with (const PBD::ID& id)
3363 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3364 while (it != _shared_with_ids.end()) {
3366 _shared_with_ids.erase (it);
3374 Playlist::shared_with (const PBD::ID& id) const
3376 bool shared = false;
3377 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3378 while (it != _shared_with_ids.end() && !shared) {
3389 Playlist::reset_shares ()
3391 _shared_with_ids.clear();
3394 /** Take a list of ranges, coalesce any that can be coalesced, then call
3395 * check_crossfades for each one.
3398 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3400 /* XXX: it's a shame that this coalesce algorithm also exists in
3401 TimeSelection::consolidate().
3404 /* XXX: xfade: this is implemented in Evoral::RangeList */
3407 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3408 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3414 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3415 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3416 i->from = min (i->from, j->from);
3417 i->to = max (i->to, j->to);
3426 Playlist::set_capture_insertion_in_progress (bool yn)
3428 _capture_insertion_underway = yn;