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, int32_t sub_num, double quarter_note, bool for_music)
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, quarter_note, for_music);
692 set_layer (region, DBL_MAX);
693 pos += region->length();
697 /* note that itimes can be zero if we being asked to just
698 insert a single fraction of the region.
701 for (int i = 0; i < itimes; ++i) {
702 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
703 add_region_internal (copy, pos, sub_num);
704 set_layer (copy, DBL_MAX);
705 pos += region->length();
708 framecnt_t length = 0;
710 if (floor (times) != times) {
711 length = (framecnt_t) floor (region->length() * (times - floor (times)));
713 RegionFactory::region_name (name, region->name(), false);
718 plist.add (Properties::start, region->start());
719 plist.add (Properties::length, length);
720 plist.add (Properties::name, name);
721 plist.add (Properties::layer, region->layer());
723 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
724 add_region_internal (sub, pos, sub_num);
725 set_layer (sub, DBL_MAX);
729 possibly_splice_unlocked (position, (pos + length) - position, region);
733 Playlist::set_region_ownership ()
735 RegionWriteLock rl (this);
736 RegionList::iterator i;
737 boost::weak_ptr<Playlist> pl (shared_from_this());
739 for (i = regions.begin(); i != regions.end(); ++i) {
740 (*i)->set_playlist (pl);
745 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, int32_t sub_num, double quarter_note, bool for_music)
747 if (region->data_type() != _type) {
751 RegionSortByPosition cmp;
753 if (!first_set_state) {
754 boost::shared_ptr<Playlist> foo (shared_from_this());
755 region->set_playlist (boost::weak_ptr<Playlist>(foo));
758 region->set_position_music (quarter_note);
760 region->set_position (position, sub_num);
763 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
764 all_regions.insert (region);
766 possibly_splice_unlocked (position, region->length(), region);
768 if (!holding_state ()) {
769 /* layers get assigned from XML state, and are not reset during undo/redo */
773 /* we need to notify the existence of new region before checking dependents. Ick. */
775 notify_region_added (region);
777 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
778 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
784 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
786 RegionWriteLock rlock (this);
788 bool old_sp = _splicing;
791 remove_region_internal (old);
792 add_region_internal (newr, pos);
793 set_layer (newr, old->layer ());
797 possibly_splice_unlocked (pos, old->length() - newr->length());
801 Playlist::remove_region (boost::shared_ptr<Region> region)
803 RegionWriteLock rlock (this);
804 remove_region_internal (region);
808 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
810 RegionList::iterator i;
814 region->set_playlist (boost::weak_ptr<Playlist>());
817 /* XXX should probably freeze here .... */
819 for (i = regions.begin(); i != regions.end(); ++i) {
822 framepos_t pos = (*i)->position();
823 framecnt_t distance = (*i)->length();
827 possibly_splice_unlocked (pos, -distance);
829 if (!holding_state ()) {
831 remove_dependents (region);
834 notify_region_removed (region);
843 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
845 if (Config->get_use_overlap_equivalency()) {
846 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
847 if ((*i)->overlap_equivalent (other)) {
848 results.push_back (*i);
852 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
853 if ((*i)->equivalent (other)) {
854 results.push_back (*i);
861 Playlist::get_region_list_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)->region_list_equivalent (other)) {
866 results.push_back (*i);
872 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
874 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
876 if ((*i) && (*i)->any_source_equivalent (other)) {
877 results.push_back (*i);
883 Playlist::partition (framepos_t start, framepos_t end, bool cut)
887 RegionWriteLock lock(this);
888 partition_internal (start, end, cut, thawlist);
891 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
892 (*i)->resume_property_changes ();
896 /** Go through each region on the playlist and cut them at start and end, removing the section between
897 * start and end if cutting == true. Regions that lie entirely within start and end are always
902 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
904 RegionList new_regions;
908 boost::shared_ptr<Region> region;
909 boost::shared_ptr<Region> current;
911 RegionList::iterator tmp;
912 Evoral::OverlapType overlap;
913 framepos_t pos1, pos2, pos3, pos4;
917 /* need to work from a copy, because otherwise the regions we add during the process
918 get operated on as well.
921 RegionList copy = regions.rlist();
923 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
930 if (current->first_frame() >= start && current->last_frame() < end) {
933 remove_region_internal (current);
939 /* coverage will return OverlapStart if the start coincides
940 with the end point. we do not partition such a region,
941 so catch this special case.
944 if (current->first_frame() >= end) {
948 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
952 pos1 = current->position();
955 pos4 = current->last_frame();
957 if (overlap == Evoral::OverlapInternal) {
958 /* split: we need 3 new regions, the front, middle and end.
959 cut: we need 2 regions, the front and end.
964 ---------------*************************------------
967 ---------------*****++++++++++++++++====------------
969 ---------------*****----------------====------------
974 /* "middle" ++++++ */
976 RegionFactory::region_name (new_name, current->name(), false);
980 plist.add (Properties::start, current->start() + (pos2 - pos1));
981 plist.add (Properties::length, pos3 - pos2);
982 plist.add (Properties::name, new_name);
983 plist.add (Properties::layer, current->layer ());
984 plist.add (Properties::layering_index, current->layering_index ());
985 plist.add (Properties::automatic, true);
986 plist.add (Properties::left_of_split, true);
987 plist.add (Properties::right_of_split, true);
989 region = RegionFactory::create (current, plist);
990 add_region_internal (region, start);
991 new_regions.push_back (region);
996 RegionFactory::region_name (new_name, current->name(), false);
1000 plist.add (Properties::start, current->start() + (pos3 - pos1));
1001 plist.add (Properties::length, pos4 - pos3);
1002 plist.add (Properties::name, new_name);
1003 plist.add (Properties::layer, current->layer ());
1004 plist.add (Properties::layering_index, current->layering_index ());
1005 plist.add (Properties::automatic, true);
1006 plist.add (Properties::right_of_split, true);
1008 region = RegionFactory::create (current, plist);
1010 add_region_internal (region, end);
1011 new_regions.push_back (region);
1015 current->clear_changes ();
1016 current->suspend_property_changes ();
1017 thawlist.push_back (current);
1018 current->cut_end (pos2 - 1);
1020 } else if (overlap == Evoral::OverlapEnd) {
1024 ---------------*************************------------
1027 ---------------**************+++++++++++------------
1029 ---------------**************-----------------------
1036 RegionFactory::region_name (new_name, current->name(), false);
1040 plist.add (Properties::start, current->start() + (pos2 - pos1));
1041 plist.add (Properties::length, pos4 - pos2);
1042 plist.add (Properties::name, new_name);
1043 plist.add (Properties::layer, current->layer ());
1044 plist.add (Properties::layering_index, current->layering_index ());
1045 plist.add (Properties::automatic, true);
1046 plist.add (Properties::left_of_split, true);
1048 region = RegionFactory::create (current, plist);
1050 add_region_internal (region, start);
1051 new_regions.push_back (region);
1056 current->clear_changes ();
1057 current->suspend_property_changes ();
1058 thawlist.push_back (current);
1059 current->cut_end (pos2 - 1);
1061 } else if (overlap == Evoral::OverlapStart) {
1063 /* split: we need 2 regions: the front and the end.
1064 cut: just trim current to skip the cut area
1069 ---------------*************************------------
1073 ---------------****+++++++++++++++++++++------------
1075 -------------------*********************------------
1081 RegionFactory::region_name (new_name, current->name(), false);
1085 plist.add (Properties::start, current->start());
1086 plist.add (Properties::length, pos3 - pos1);
1087 plist.add (Properties::name, new_name);
1088 plist.add (Properties::layer, current->layer ());
1089 plist.add (Properties::layering_index, current->layering_index ());
1090 plist.add (Properties::automatic, true);
1091 plist.add (Properties::right_of_split, true);
1093 region = RegionFactory::create (current, plist);
1095 add_region_internal (region, pos1);
1096 new_regions.push_back (region);
1101 current->clear_changes ();
1102 current->suspend_property_changes ();
1103 thawlist.push_back (current);
1104 current->trim_front (pos3);
1105 } else if (overlap == Evoral::OverlapExternal) {
1107 /* split: no split required.
1108 cut: remove the region.
1113 ---------------*************************------------
1117 ---------------*************************------------
1119 ----------------------------------------------------
1124 remove_region_internal (current);
1127 new_regions.push_back (current);
1131 in_partition = false;
1134 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1135 framepos_t wanted_length = end-start;
1136 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1139 boost::shared_ptr<Playlist>
1140 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1142 boost::shared_ptr<Playlist> ret;
1143 boost::shared_ptr<Playlist> pl;
1146 if (ranges.empty()) {
1147 return boost::shared_ptr<Playlist>();
1150 start = ranges.front().start;
1152 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1154 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1156 if (i == ranges.begin()) {
1160 /* paste the next section into the nascent playlist,
1161 offset to reflect the start of the first range we
1165 ret->paste (pl, (*i).start - start, 1.0f, 0);
1172 boost::shared_ptr<Playlist>
1173 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1175 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1176 return cut_copy (pmf, ranges, result_is_hidden);
1179 boost::shared_ptr<Playlist>
1180 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1182 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1183 return cut_copy (pmf, ranges, result_is_hidden);
1186 boost::shared_ptr<Playlist>
1187 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1189 boost::shared_ptr<Playlist> the_copy;
1190 RegionList thawlist;
1193 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1194 string new_name = _name;
1198 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1199 return boost::shared_ptr<Playlist>();
1203 RegionWriteLock rlock (this);
1204 partition_internal (start, start+cnt-1, true, thawlist);
1207 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1208 (*i)->resume_property_changes();
1214 boost::shared_ptr<Playlist>
1215 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1219 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1220 string new_name = _name;
1224 // 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... )
1226 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1230 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1232 times = fabs (times);
1235 RegionReadLock rl2 (other.get());
1237 int itimes = (int) floor (times);
1238 framepos_t pos = position;
1239 framecnt_t const shift = other->_get_extent().second;
1240 layer_t top = top_layer ();
1243 RegionWriteLock rl1 (this);
1245 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1246 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1248 /* put these new regions on top of all existing ones, but preserve
1249 the ordering they had in the original playlist.
1252 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1253 set_layer (copy_of_region, copy_of_region->layer() + top);
1265 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1267 duplicate(region, position, region->length(), times);
1270 /** @param gap from the beginning of the region to the next beginning */
1272 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1274 times = fabs (times);
1276 RegionWriteLock rl (this);
1277 int itimes = (int) floor (times);
1280 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1281 add_region_internal (copy, position);
1282 set_layer (copy, DBL_MAX);
1286 if (floor (times) != times) {
1287 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1289 RegionFactory::region_name (name, region->name(), false);
1294 plist.add (Properties::start, region->start());
1295 plist.add (Properties::length, length);
1296 plist.add (Properties::name, name);
1298 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1299 add_region_internal (sub, position);
1300 set_layer (sub, DBL_MAX);
1305 /** @param gap from the beginning of the region to the next beginning */
1306 /** @param end the first frame that does _not_ contain a duplicated frame */
1308 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1310 RegionWriteLock rl (this);
1312 while (position + region->length() - 1 < end) {
1313 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1314 add_region_internal (copy, position);
1315 set_layer (copy, DBL_MAX);
1319 if (position < end) {
1320 framecnt_t length = min (region->length(), end - position);
1322 RegionFactory::region_name (name, region->name(), false);
1327 plist.add (Properties::start, region->start());
1328 plist.add (Properties::length, length);
1329 plist.add (Properties::name, name);
1331 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1332 add_region_internal (sub, position);
1333 set_layer (sub, DBL_MAX);
1339 Playlist::duplicate_range (AudioRange& range, float times)
1341 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1342 framecnt_t offset = range.end - range.start;
1343 paste (pl, range.start + offset, times, 0);
1347 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1349 if (ranges.empty()) {
1353 framepos_t min_pos = max_framepos;
1354 framepos_t max_pos = 0;
1356 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1359 min_pos = min (min_pos, (*i).start);
1360 max_pos = max (max_pos, (*i).end);
1363 framecnt_t offset = max_pos - min_pos;
1366 int itimes = (int) floor (times);
1368 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1369 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1370 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1377 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1379 RegionWriteLock rlock (this);
1380 RegionList copy (regions.rlist());
1383 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1385 if ((*r)->last_frame() < at) {
1390 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1391 /* intersected region */
1392 if (!move_intersected) {
1397 /* do not move regions glued to music time - that
1398 has to be done separately.
1401 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1402 fixup.push_back (*r);
1406 (*r)->set_position ((*r)->position() + distance);
1409 /* XXX: may not be necessary; Region::post_set should do this, I think */
1410 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1411 (*r)->recompute_position_from_lock_style (0);
1416 Playlist::split (const MusicFrame& at)
1418 RegionWriteLock rlock (this);
1419 RegionList copy (regions.rlist());
1421 /* use a copy since this operation can modify the region list
1424 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1425 _split_region (*r, at);
1430 Playlist::split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1432 RegionWriteLock rl (this);
1433 _split_region (region, playlist_position);
1437 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1439 if (!region->covers (playlist_position.frame)) {
1443 if (region->position() == playlist_position.frame ||
1444 region->last_frame() == playlist_position.frame) {
1448 boost::shared_ptr<Region> left;
1449 boost::shared_ptr<Region> right;
1451 MusicFrame before (playlist_position.frame - region->position(), playlist_position.division);
1452 MusicFrame after (region->length() - before.frame, 0);
1456 /* split doesn't change anything about length, so don't try to splice */
1458 bool old_sp = _splicing;
1461 RegionFactory::region_name (before_name, region->name(), false);
1466 plist.add (Properties::length, before.frame);
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, MusicFrame (0, 0), plist, true);
1479 RegionFactory::region_name (after_name, region->name(), false);
1484 plist.add (Properties::length, after.frame);
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);
1494 add_region_internal (left, region->position(), 0);
1495 add_region_internal (right, region->position() + before.frame, before.division);
1497 remove_region_internal (region);
1503 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1505 if (_splicing || in_set_state) {
1506 /* don't respond to splicing moves or state setting */
1510 if (_edit_mode == Splice) {
1511 splice_locked (at, distance, exclude);
1516 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1518 if (_splicing || in_set_state) {
1519 /* don't respond to splicing moves or state setting */
1523 if (_edit_mode == Splice) {
1524 splice_unlocked (at, distance, exclude);
1529 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1532 RegionWriteLock rl (this);
1533 core_splice (at, distance, exclude);
1538 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1540 core_splice (at, distance, exclude);
1544 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1548 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1550 if (exclude && (*i) == exclude) {
1554 if ((*i)->position() >= at) {
1555 framepos_t new_pos = (*i)->position() + distance;
1558 } else if (new_pos >= max_framepos - (*i)->length()) {
1559 new_pos = max_framepos - (*i)->length();
1562 (*i)->set_position (new_pos);
1568 notify_contents_changed ();
1572 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1575 RegionWriteLock rl (this);
1576 core_ripple (at, distance, exclude);
1581 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1583 core_ripple (at, distance, exclude);
1587 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1589 if (distance == 0) {
1594 RegionListProperty copy = regions;
1595 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1596 assert (i != copy.end());
1599 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1604 if ((*i)->position() >= at) {
1605 framepos_t new_pos = (*i)->position() + distance;
1606 framepos_t limit = max_framepos - (*i)->length();
1609 } else if (new_pos >= limit ) {
1613 (*i)->set_position (new_pos);
1618 notify_contents_changed ();
1623 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1625 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1629 if (what_changed.contains (Properties::position)) {
1631 /* remove it from the list then add it back in
1632 the right place again.
1635 RegionSortByPosition cmp;
1637 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1639 if (i == regions.end()) {
1640 /* the region bounds are being modified but its not currently
1641 in the region list. we will use its bounds correctly when/if
1648 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1651 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1653 frameoffset_t delta = 0;
1655 if (what_changed.contains (Properties::position)) {
1656 delta = region->position() - region->last_position();
1659 if (what_changed.contains (Properties::length)) {
1660 delta += region->length() - region->last_length();
1664 possibly_splice (region->last_position() + region->last_length(), delta, region);
1667 if (holding_state ()) {
1668 pending_bounds.push_back (region);
1670 notify_contents_changed ();
1672 list<Evoral::Range<framepos_t> > xf;
1673 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1674 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1675 coalesce_and_check_crossfades (xf);
1681 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1683 boost::shared_ptr<Region> region (weak_region.lock());
1689 /* this makes a virtual call to the right kind of playlist ... */
1691 region_changed (what_changed, region);
1695 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1697 PropertyChange our_interests;
1698 PropertyChange bounds;
1699 PropertyChange pos_and_length;
1702 if (in_set_state || in_flush) {
1706 our_interests.add (Properties::muted);
1707 our_interests.add (Properties::layer);
1708 our_interests.add (Properties::opaque);
1710 bounds.add (Properties::start);
1711 bounds.add (Properties::position);
1712 bounds.add (Properties::length);
1714 pos_and_length.add (Properties::position);
1715 pos_and_length.add (Properties::length);
1717 if (what_changed.contains (bounds)) {
1718 region_bounds_changed (what_changed, region);
1719 save = !(_splicing || _nudging);
1722 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1723 notify_region_moved (region);
1724 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1725 notify_region_end_trimmed (region);
1726 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1727 notify_region_start_trimmed (region);
1730 /* don't notify about layer changes, since we are the only object that can initiate
1731 them, and we notify in ::relayer()
1734 if (what_changed.contains (our_interests)) {
1738 mark_session_dirty ();
1744 Playlist::drop_regions ()
1746 RegionWriteLock rl (this);
1748 all_regions.clear ();
1752 Playlist::sync_all_regions_with_regions ()
1754 RegionWriteLock rl (this);
1756 all_regions.clear ();
1758 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1759 all_regions.insert (*i);
1764 Playlist::clear (bool with_signals)
1767 RegionWriteLock rl (this);
1769 region_state_changed_connections.drop_connections ();
1770 region_drop_references_connections.drop_connections ();
1772 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1773 pending_removes.insert (*i);
1778 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1779 remove_dependents (*s);
1785 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1786 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1789 pending_removes.clear ();
1790 pending_contents_change = false;
1796 /* *********************************************************************
1798 **********************************************************************/
1800 boost::shared_ptr<RegionList>
1801 Playlist::region_list()
1803 RegionReadLock rlock (this);
1804 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1809 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1811 RegionReadLock rlock (const_cast<Playlist*>(this));
1813 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1814 (*i)->deep_sources (sources);
1818 boost::shared_ptr<RegionList>
1819 Playlist::regions_at (framepos_t frame)
1821 RegionReadLock rlock (this);
1822 return find_regions_at (frame);
1826 Playlist::count_regions_at (framepos_t frame) const
1828 RegionReadLock rlock (const_cast<Playlist*>(this));
1831 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1832 if ((*i)->covers (frame)) {
1840 boost::shared_ptr<Region>
1841 Playlist::top_region_at (framepos_t frame)
1844 RegionReadLock rlock (this);
1845 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1846 boost::shared_ptr<Region> region;
1848 if (rlist->size()) {
1849 RegionSortByLayer cmp;
1851 region = rlist->back();
1857 boost::shared_ptr<Region>
1858 Playlist::top_unmuted_region_at (framepos_t frame)
1861 RegionReadLock rlock (this);
1862 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1864 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1866 RegionList::iterator tmp = i;
1870 if ((*i)->muted()) {
1877 boost::shared_ptr<Region> region;
1879 if (rlist->size()) {
1880 RegionSortByLayer cmp;
1882 region = rlist->back();
1888 boost::shared_ptr<RegionList>
1889 Playlist::find_regions_at (framepos_t frame)
1891 /* Caller must hold lock */
1893 boost::shared_ptr<RegionList> rlist (new RegionList);
1895 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1896 if ((*i)->covers (frame)) {
1897 rlist->push_back (*i);
1904 boost::shared_ptr<RegionList>
1905 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1907 RegionReadLock rlock (this);
1908 boost::shared_ptr<RegionList> rlist (new RegionList);
1910 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1911 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1912 rlist->push_back (*i);
1919 boost::shared_ptr<RegionList>
1920 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1922 RegionReadLock rlock (this);
1923 boost::shared_ptr<RegionList> rlist (new RegionList);
1925 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1926 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1927 rlist->push_back (*i);
1934 /** @param start Range start.
1935 * @param end Range end.
1936 * @return regions which have some part within this range.
1938 boost::shared_ptr<RegionList>
1939 Playlist::regions_touched (framepos_t start, framepos_t end)
1941 RegionReadLock rlock (this);
1942 return regions_touched_locked (start, end);
1945 boost::shared_ptr<RegionList>
1946 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1948 boost::shared_ptr<RegionList> rlist (new RegionList);
1950 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1951 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1952 rlist->push_back (*i);
1960 Playlist::find_next_transient (framepos_t from, int dir)
1962 RegionReadLock rlock (this);
1963 AnalysisFeatureList points;
1964 AnalysisFeatureList these_points;
1966 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1968 if ((*i)->last_frame() < from) {
1972 if ((*i)->first_frame() > from) {
1977 (*i)->get_transients (these_points);
1979 /* add first frame, just, err, because */
1981 these_points.push_back ((*i)->first_frame());
1983 points.insert (points.end(), these_points.begin(), these_points.end());
1984 these_points.clear ();
1987 if (points.empty()) {
1991 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1992 bool reached = false;
1995 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
2000 if (reached && (*x) > from) {
2005 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2010 if (reached && (*x) < from) {
2019 boost::shared_ptr<Region>
2020 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2022 RegionReadLock rlock (this);
2023 boost::shared_ptr<Region> ret;
2024 framepos_t closest = max_framepos;
2026 bool end_iter = false;
2028 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2032 frameoffset_t distance;
2033 boost::shared_ptr<Region> r = (*i);
2038 pos = r->first_frame ();
2041 pos = r->last_frame ();
2044 pos = r->sync_position ();
2049 case 1: /* forwards */
2052 if ((distance = pos - frame) < closest) {
2061 default: /* backwards */
2064 if ((distance = frame - pos) < closest) {
2080 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2082 RegionReadLock rlock (this);
2084 framepos_t closest = max_framepos;
2085 framepos_t ret = -1;
2089 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2091 boost::shared_ptr<Region> r = (*i);
2092 frameoffset_t distance;
2093 const framepos_t first_frame = r->first_frame();
2094 const framepos_t last_frame = r->last_frame();
2096 if (first_frame > frame) {
2098 distance = first_frame - frame;
2100 if (distance < closest) {
2106 if (last_frame > frame) {
2108 distance = last_frame - frame;
2110 if (distance < closest) {
2119 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2121 boost::shared_ptr<Region> r = (*i);
2122 frameoffset_t distance;
2123 const framepos_t first_frame = r->first_frame();
2124 const framepos_t last_frame = r->last_frame();
2126 if (last_frame < frame) {
2128 distance = frame - last_frame;
2130 if (distance < closest) {
2136 if (first_frame < frame) {
2138 distance = frame - first_frame;
2140 if (distance < closest) {
2152 /***********************************************************************/
2158 Playlist::mark_session_dirty ()
2160 if (!in_set_state && !holding_state ()) {
2161 _session.set_dirty();
2166 Playlist::rdiff (vector<Command*>& cmds) const
2168 RegionReadLock rlock (const_cast<Playlist *> (this));
2169 Stateful::rdiff (cmds);
2173 Playlist::clear_owned_changes ()
2175 RegionReadLock rlock (this);
2176 Stateful::clear_owned_changes ();
2180 Playlist::update (const RegionListProperty::ChangeRecord& change)
2182 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2183 name(), change.added.size(), change.removed.size()));
2186 /* add the added regions */
2187 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2188 add_region_internal ((*i), (*i)->position());
2190 /* remove the removed regions */
2191 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2199 Playlist::set_state (const XMLNode& node, int version)
2203 XMLNodeConstIterator niter;
2204 XMLPropertyList plist;
2205 XMLPropertyConstIterator piter;
2206 XMLProperty const * prop;
2207 boost::shared_ptr<Region> region;
2209 bool seen_region_nodes = false;
2214 if (node.name() != "Playlist") {
2221 plist = node.properties();
2225 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2229 if (prop->name() == X_("name")) {
2230 _name = prop->value();
2232 } else if (prop->name() == X_("orig-diskstream-id")) {
2233 /* XXX legacy session: fix up later */
2234 _orig_track_id = prop->value ();
2235 } else if (prop->name() == X_("orig-track-id")) {
2236 _orig_track_id = prop->value ();
2237 } else if (prop->name() == X_("frozen")) {
2238 _frozen = string_is_affirmative (prop->value());
2239 } else if (prop->name() == X_("combine-ops")) {
2240 _combine_ops = atoi (prop->value());
2241 } else if (prop->name() == X_("shared-with-ids")) {
2242 string shared_ids = prop->value ();
2243 if (!shared_ids.empty()) {
2244 vector<string> result;
2245 ::split (shared_ids, result, ',');
2246 vector<string>::iterator it = result.begin();
2247 for (; it != result.end(); ++it) {
2248 _shared_with_ids.push_back (PBD::ID(*it));
2256 nlist = node.children();
2258 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2262 if (child->name() == "Region") {
2264 seen_region_nodes = true;
2266 if ((prop = child->property ("id")) == 0) {
2267 error << _("region state node has no ID, ignored") << endmsg;
2271 ID id = prop->value ();
2273 if ((region = region_by_id (id))) {
2275 region->suspend_property_changes ();
2277 if (region->set_state (*child, version)) {
2278 region->resume_property_changes ();
2282 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2283 region->suspend_property_changes ();
2285 error << _("Playlist: cannot create region from XML") << endmsg;
2290 RegionWriteLock rlock (this);
2291 add_region_internal (region, region->position());
2294 region->resume_property_changes ();
2299 if (seen_region_nodes && regions.empty()) {
2304 notify_contents_changed ();
2307 first_set_state = false;
2313 Playlist::get_state()
2315 return state (true);
2319 Playlist::get_template()
2321 return state (false);
2324 /** @param full_state true to include regions in the returned state, otherwise false.
2327 Playlist::state (bool full_state)
2329 XMLNode *node = new XMLNode (X_("Playlist"));
2332 node->add_property (X_("id"), id().to_s());
2333 node->add_property (X_("name"), _name);
2334 node->add_property (X_("type"), _type.to_string());
2336 _orig_track_id.print (buf, sizeof (buf));
2337 node->add_property (X_("orig-track-id"), buf);
2340 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2341 for (; it != _shared_with_ids.end(); ++it) {
2342 shared_ids += "," + (*it).to_s();
2344 if (!shared_ids.empty()) {
2345 shared_ids.erase(0,1);
2348 node->add_property (X_("shared-with-ids"), shared_ids);
2349 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2352 RegionReadLock rlock (this);
2354 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2355 node->add_property ("combine-ops", buf);
2357 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2358 node->add_child_nocopy ((*i)->get_state());
2363 node->add_child_copy (*_extra_xml);
2370 Playlist::empty() const
2372 RegionReadLock rlock (const_cast<Playlist *>(this));
2373 return regions.empty();
2377 Playlist::n_regions() const
2379 RegionReadLock rlock (const_cast<Playlist *>(this));
2380 return regions.size();
2383 /** @return true if the all_regions list is empty, ie this playlist
2384 * has never had a region added to it.
2387 Playlist::all_regions_empty() const
2389 RegionReadLock rl (const_cast<Playlist *> (this));
2390 return all_regions.empty();
2393 pair<framepos_t, framepos_t>
2394 Playlist::get_extent () const
2396 RegionReadLock rlock (const_cast<Playlist *>(this));
2397 return _get_extent ();
2400 pair<framepos_t, framepos_t>
2401 Playlist::get_extent_with_endspace () const
2403 pair<framepos_t, framepos_t> l = get_extent();
2404 l.second += _end_space;
2408 pair<framepos_t, framepos_t>
2409 Playlist::_get_extent () const
2411 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2413 if (regions.empty()) {
2418 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2419 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2420 if (e.first < ext.first) {
2421 ext.first = e.first;
2423 if (e.second > ext.second) {
2424 ext.second = e.second;
2432 Playlist::bump_name (string name, Session &session)
2434 string newname = name;
2437 newname = bump_name_once (newname, '.');
2438 } while (session.playlists->by_name (newname)!=NULL);
2445 Playlist::top_layer() const
2447 RegionReadLock rlock (const_cast<Playlist *> (this));
2450 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2451 top = max (top, (*i)->layer());
2457 Playlist::set_edit_mode (EditMode mode)
2462 struct RelayerSort {
2463 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2464 return a->layering_index() < b->layering_index();
2468 /** Set a new layer for a region. This adjusts the layering indices of all
2469 * regions in the playlist to put the specified region in the appropriate
2470 * place. The actual layering will be fixed up when relayer() happens.
2474 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2476 /* Remove the layer we are setting from our region list, and sort it
2477 * using the layer indeces.
2480 RegionList copy = regions.rlist();
2481 copy.remove (region);
2482 copy.sort (RelayerSort ());
2484 /* Put region back in the right place */
2485 RegionList::iterator i = copy.begin();
2486 while (i != copy.end ()) {
2487 if ((*i)->layer() > new_layer) {
2493 copy.insert (i, region);
2495 setup_layering_indices (copy);
2499 Playlist::setup_layering_indices (RegionList const & regions)
2503 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2504 (*k)->set_layering_index (j++);
2508 struct LaterHigherSort {
2509 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2510 return a->position() < b->position();
2514 /** Take the layering indices of each of our regions, compute the layers
2515 * that they should be on, and write the layers back to the regions.
2518 Playlist::relayer ()
2520 /* never compute layers when setting from XML */
2526 /* Build up a new list of regions on each layer, stored in a set of lists
2527 each of which represent some period of time on some layer. The idea
2528 is to avoid having to search the entire region list to establish whether
2529 each region overlaps another */
2531 /* how many pieces to divide this playlist's time up into */
2532 int const divisions = 512;
2534 /* find the start and end positions of the regions on this playlist */
2535 framepos_t start = INT64_MAX;
2537 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2538 start = min (start, (*i)->position());
2539 end = max (end, (*i)->position() + (*i)->length());
2542 /* hence the size of each time division */
2543 double const division_size = (end - start) / double (divisions);
2545 vector<vector<RegionList> > layers;
2546 layers.push_back (vector<RegionList> (divisions));
2548 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2549 RegionList copy = regions.rlist();
2550 switch (Config->get_layer_model()) {
2552 copy.sort (LaterHigherSort ());
2555 copy.sort (RelayerSort ());
2559 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2560 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2561 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2564 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2566 /* find the time divisions that this region covers; if there are no regions on the list,
2567 division_size will equal 0 and in this case we'll just say that
2568 start_division = end_division = 0.
2570 int start_division = 0;
2571 int end_division = 0;
2573 if (division_size > 0) {
2574 start_division = floor ( ((*i)->position() - start) / division_size);
2575 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2576 if (end_division == divisions) {
2581 assert (divisions == 0 || end_division < divisions);
2583 /* find the lowest layer that this region can go on */
2584 size_t j = layers.size();
2586 /* try layer j - 1; it can go on if it overlaps no other region
2587 that is already on that layer
2590 bool overlap = false;
2591 for (int k = start_division; k <= end_division; ++k) {
2592 RegionList::iterator l = layers[j-1][k].begin ();
2593 while (l != layers[j-1][k].end()) {
2594 if ((*l)->overlap_equivalent (*i)) {
2607 /* overlap, so we must use layer j */
2614 if (j == layers.size()) {
2615 /* we need a new layer for this region */
2616 layers.push_back (vector<RegionList> (divisions));
2619 /* put a reference to this region in each of the divisions that it exists in */
2620 for (int k = start_division; k <= end_division; ++k) {
2621 layers[j][k].push_back (*i);
2624 (*i)->set_layer (j);
2627 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2628 relayering because we just removed the only region on the top layer, nothing will
2629 appear to have changed, but the StreamView must still sort itself out. We could
2630 probably keep a note of the top layer last time we relayered, and check that,
2631 but premature optimisation &c...
2633 notify_layering_changed ();
2635 /* This relayer() may have been called as a result of a region removal, in which
2636 case we need to setup layering indices to account for the one that has just
2639 setup_layering_indices (copy);
2643 Playlist::raise_region (boost::shared_ptr<Region> region)
2645 set_layer (region, region->layer() + 1.5);
2650 Playlist::lower_region (boost::shared_ptr<Region> region)
2652 set_layer (region, region->layer() - 1.5);
2657 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2659 set_layer (region, DBL_MAX);
2664 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2666 set_layer (region, -0.5);
2671 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2673 RegionList::iterator i;
2679 RegionWriteLock rlock (const_cast<Playlist *> (this));
2681 for (i = regions.begin(); i != regions.end(); ++i) {
2683 if ((*i)->position() >= start) {
2689 if ((*i)->last_frame() > max_framepos - distance) {
2690 new_pos = max_framepos - (*i)->length();
2692 new_pos = (*i)->position() + distance;
2697 if ((*i)->position() > distance) {
2698 new_pos = (*i)->position() - distance;
2704 (*i)->set_position (new_pos);
2712 notify_contents_changed ();
2718 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2720 RegionReadLock rlock (const_cast<Playlist*> (this));
2722 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2723 /* Note: passing the second argument as false can cause at best
2724 incredibly deep and time-consuming recursion, and at worst
2725 cycles if the user has managed to create cycles of reference
2726 between compound regions. We generally only this during
2727 cleanup, and @param shallow is passed as true.
2729 if ((*r)->uses_source (src, shallow)) {
2738 boost::shared_ptr<Region>
2739 Playlist::find_region (const ID& id) const
2741 RegionReadLock rlock (const_cast<Playlist*> (this));
2743 /* searches all regions currently in use by the playlist */
2745 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2746 if ((*i)->id() == id) {
2751 return boost::shared_ptr<Region> ();
2755 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2757 RegionReadLock rlock (const_cast<Playlist*> (this));
2760 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2766 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2767 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2768 /* check if region is used in a compound */
2769 if (it->second == r) {
2770 /* region is referenced as 'original' of a compound */
2774 if (r->whole_file() && r->max_source_level() > 0) {
2775 /* region itself ia a compound.
2776 * the compound regions are not referenced -> check regions inside compound
2778 const SourceList& sl = r->sources();
2779 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2780 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2782 if (ps->playlist()->region_use_count(it->first)) {
2783 // break out of both loops
2792 boost::shared_ptr<Region>
2793 Playlist::region_by_id (const ID& id) const
2795 /* searches all regions ever added to this playlist */
2797 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2798 if ((*i)->id() == id) {
2802 return boost::shared_ptr<Region> ();
2806 Playlist::dump () const
2808 boost::shared_ptr<Region> r;
2810 cerr << "Playlist \"" << _name << "\" " << endl
2811 << regions.size() << " regions "
2814 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2816 cerr << " " << r->name() << " ["
2817 << r->start() << "+" << r->length()
2827 Playlist::set_frozen (bool yn)
2833 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2837 if (region->locked()) {
2844 RegionWriteLock rlock (const_cast<Playlist*> (this));
2849 RegionList::iterator next;
2851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2852 if ((*i) == region) {
2856 if (next != regions.end()) {
2858 if ((*next)->locked()) {
2864 if ((*next)->position() != region->last_frame() + 1) {
2865 /* they didn't used to touch, so after shuffle,
2866 just have them swap positions.
2868 new_pos = (*next)->position();
2870 /* they used to touch, so after shuffle,
2871 make sure they still do. put the earlier
2872 region where the later one will end after
2875 new_pos = region->position() + (*next)->length();
2878 (*next)->set_position (region->position());
2879 region->set_position (new_pos);
2881 /* avoid a full sort */
2883 regions.erase (i); // removes the region from the list */
2885 regions.insert (next, region); // adds it back after next
2894 RegionList::iterator prev = regions.end();
2896 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2897 if ((*i) == region) {
2899 if (prev != regions.end()) {
2901 if ((*prev)->locked()) {
2906 if (region->position() != (*prev)->last_frame() + 1) {
2907 /* they didn't used to touch, so after shuffle,
2908 just have them swap positions.
2910 new_pos = region->position();
2912 /* they used to touch, so after shuffle,
2913 make sure they still do. put the earlier
2914 one where the later one will end after
2916 new_pos = (*prev)->position() + region->length();
2919 region->set_position ((*prev)->position());
2920 (*prev)->set_position (new_pos);
2922 /* avoid a full sort */
2924 regions.erase (i); // remove region
2925 regions.insert (prev, region); // insert region before prev
2941 notify_contents_changed();
2947 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2949 RegionReadLock rlock (const_cast<Playlist*> (this));
2951 if (regions.size() > 1) {
2959 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2961 ripple_locked (at, distance, exclude);
2965 Playlist::update_after_tempo_map_change ()
2967 RegionWriteLock rlock (const_cast<Playlist*> (this));
2968 RegionList copy (regions.rlist());
2972 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2973 (*i)->update_after_tempo_map_change ();
2975 /* possibly causes a contents changed notification (flush_notifications()) */
2980 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2982 RegionReadLock rl (this);
2983 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2989 Playlist::has_region_at (framepos_t const p) const
2991 RegionReadLock (const_cast<Playlist *> (this));
2993 RegionList::const_iterator i = regions.begin ();
2994 while (i != regions.end() && !(*i)->covers (p)) {
2998 return (i != regions.end());
3001 /** Look from a session frame time and find the start time of the next region
3002 * which is on the top layer of this playlist.
3003 * @param t Time to look from.
3004 * @return Position of next top-layered region, or max_framepos if there isn't one.
3007 Playlist::find_next_top_layer_position (framepos_t t) const
3009 RegionReadLock rlock (const_cast<Playlist *> (this));
3011 layer_t const top = top_layer ();
3013 RegionList copy = regions.rlist ();
3014 copy.sort (RegionSortByPosition ());
3016 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3017 if ((*i)->position() >= t && (*i)->layer() == top) {
3018 return (*i)->position();
3022 return max_framepos;
3025 boost::shared_ptr<Region>
3026 Playlist::combine (const RegionList& r)
3029 uint32_t channels = 0;
3031 framepos_t earliest_position = max_framepos;
3032 vector<TwoRegions> old_and_new_regions;
3033 vector<boost::shared_ptr<Region> > originals;
3034 vector<boost::shared_ptr<Region> > copies;
3037 uint32_t max_level = 0;
3039 /* find the maximum depth of all the regions we're combining */
3041 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3042 max_level = max (max_level, (*i)->max_source_level());
3045 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3046 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3048 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3050 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3051 earliest_position = min (earliest_position, (*i)->position());
3054 /* enable this so that we do not try to create xfades etc. as we add
3058 pl->in_partition = true;
3060 /* sort by position then layer.
3061 * route_time_axis passes 'selected_regions' - which is not sorted.
3062 * here we need the top-most first, then every layer's region sorted by position.
3064 RegionList sorted(r);
3065 sorted.sort(RegionSortByLayerAndPosition());
3067 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3069 /* copy the region */
3071 boost::shared_ptr<Region> original_region = (*i);
3072 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3074 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3075 originals.push_back (original_region);
3076 copies.push_back (copied_region);
3078 RegionFactory::add_compound_association (original_region, copied_region);
3080 /* make position relative to zero */
3082 pl->add_region (copied_region, original_region->position() - earliest_position);
3083 copied_region->set_layer (original_region->layer ());
3085 /* use the maximum number of channels for any region */
3087 channels = max (channels, original_region->n_channels());
3089 /* it will go above the layer of the highest existing region */
3091 layer = max (layer, original_region->layer());
3094 pl->in_partition = false;
3096 pre_combine (copies);
3098 /* now create a new PlaylistSource for each channel in the new playlist */
3101 pair<framepos_t,framepos_t> extent = pl->get_extent();
3103 for (uint32_t chn = 0; chn < channels; ++chn) {
3104 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3108 /* now a new whole-file region using the list of sources */
3110 plist.add (Properties::start, 0);
3111 plist.add (Properties::length, extent.second);
3112 plist.add (Properties::name, parent_name);
3113 plist.add (Properties::whole_file, true);
3115 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3117 /* now the non-whole-file region that we will actually use in the
3122 plist.add (Properties::start, 0);
3123 plist.add (Properties::length, extent.second);
3124 plist.add (Properties::name, child_name);
3125 plist.add (Properties::layer, layer+1);
3127 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3129 /* remove all the selected regions from the current playlist
3134 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3138 /* do type-specific stuff with the originals and the new compound
3142 post_combine (originals, compound_region);
3144 /* add the new region at the right location */
3146 add_region (compound_region, earliest_position);
3152 return compound_region;
3156 Playlist::uncombine (boost::shared_ptr<Region> target)
3158 boost::shared_ptr<PlaylistSource> pls;
3159 boost::shared_ptr<const Playlist> pl;
3160 vector<boost::shared_ptr<Region> > originals;
3161 vector<TwoRegions> old_and_new_regions;
3163 // (1) check that its really a compound region
3165 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3169 pl = pls->playlist();
3171 framepos_t adjusted_start = 0; // gcc isn't smart enough
3172 framepos_t adjusted_end = 0; // gcc isn't smart enough
3174 /* the leftmost (earliest) edge of the compound region
3175 starts at zero in its source, or larger if it
3176 has been trimmed or content-scrolled.
3178 the rightmost (latest) edge of the compound region
3179 relative to its source is the starting point plus
3180 the length of the region.
3183 // (2) get all the original regions
3185 const RegionList& rl (pl->region_list_property().rlist());
3186 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3187 frameoffset_t move_offset = 0;
3189 /* there are two possibilities here:
3190 1) the playlist that the playlist source was based on
3191 is us, so just add the originals (which belonged to
3192 us anyway) back in the right place.
3194 2) the playlist that the playlist source was based on
3195 is NOT us, so we need to make copies of each of
3196 the original regions that we find, and add them
3199 bool same_playlist = (pls->original() == id());
3201 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3203 boost::shared_ptr<Region> current (*i);
3205 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3207 if (ca == cassocs.end()) {
3211 boost::shared_ptr<Region> original (ca->second);
3213 bool modified_region;
3215 if (i == rl.begin()) {
3216 move_offset = (target->position() - original->position()) - target->start();
3217 adjusted_start = original->position() + target->start();
3218 adjusted_end = adjusted_start + target->length();
3221 if (!same_playlist) {
3222 framepos_t pos = original->position();
3223 /* make a copy, but don't announce it */
3224 original = RegionFactory::create (original, false);
3225 /* the pure copy constructor resets position() to zero,
3228 original->set_position (pos);
3231 /* check to see how the original region (in the
3232 * playlist before compounding occurred) overlaps
3233 * with the new state of the compound region.
3236 original->clear_changes ();
3237 modified_region = false;
3239 switch (original->coverage (adjusted_start, adjusted_end)) {
3240 case Evoral::OverlapNone:
3241 /* original region does not cover any part
3242 of the current state of the compound region
3246 case Evoral::OverlapInternal:
3247 /* overlap is just a small piece inside the
3248 * original so trim both ends
3250 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3251 modified_region = true;
3254 case Evoral::OverlapExternal:
3255 /* overlap fully covers original, so leave it
3260 case Evoral::OverlapEnd:
3261 /* overlap starts within but covers end,
3262 so trim the front of the region
3264 original->trim_front (adjusted_start);
3265 modified_region = true;
3268 case Evoral::OverlapStart:
3269 /* overlap covers start but ends within, so
3270 * trim the end of the region.
3272 original->trim_end (adjusted_end);
3273 modified_region = true;
3278 /* fix the position to match any movement of the compound region.
3280 original->set_position (original->position() + move_offset);
3281 modified_region = true;
3284 if (modified_region) {
3285 _session.add_command (new StatefulDiffCommand (original));
3288 /* and add to the list of regions waiting to be
3292 originals.push_back (original);
3293 old_and_new_regions.push_back (TwoRegions (*i, original));
3296 pre_uncombine (originals, target);
3298 in_partition = true;
3301 // (3) remove the compound region
3303 remove_region (target);
3305 // (4) add the constituent regions
3307 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3308 add_region ((*i), (*i)->position());
3309 set_layer((*i), (*i)->layer());
3310 if (!RegionFactory::region_by_id((*i)->id())) {
3311 RegionFactory::map_add(*i);
3315 in_partition = false;
3320 Playlist::fade_range (list<AudioRange>& ranges)
3322 RegionReadLock rlock (this);
3323 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3324 list<AudioRange>::iterator tmpr = r;
3326 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3327 RegionList::const_iterator tmpi = i;
3329 (*i)->fade_range ((*r).start, (*r).end);
3337 Playlist::max_source_level () const
3339 RegionReadLock rlock (const_cast<Playlist *> (this));
3342 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3343 lvl = max (lvl, (*i)->max_source_level());
3350 Playlist::set_orig_track_id (const PBD::ID& id)
3352 if (shared_with(id)) {
3353 // Swap 'shared_id' / origin_track_id
3355 share_with (_orig_track_id);
3357 _orig_track_id = id;
3361 Playlist::share_with (const PBD::ID& id)
3363 if (!shared_with(id)) {
3364 _shared_with_ids.push_back (id);
3369 Playlist::unshare_with (const PBD::ID& id)
3371 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3372 while (it != _shared_with_ids.end()) {
3374 _shared_with_ids.erase (it);
3382 Playlist::shared_with (const PBD::ID& id) const
3384 bool shared = false;
3385 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3386 while (it != _shared_with_ids.end() && !shared) {
3397 Playlist::reset_shares ()
3399 _shared_with_ids.clear();
3402 /** Take a list of ranges, coalesce any that can be coalesced, then call
3403 * check_crossfades for each one.
3406 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3408 /* XXX: it's a shame that this coalesce algorithm also exists in
3409 TimeSelection::consolidate().
3412 /* XXX: xfade: this is implemented in Evoral::RangeList */
3415 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3416 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3422 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3423 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3424 i->from = min (i->from, j->from);
3425 i->to = max (i->to, j->to);
3434 Playlist::set_capture_insertion_in_progress (bool yn)
3436 _capture_insertion_underway = yn;