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 "pbd/types_convert.h"
26 #include "pbd/stateful_diff_command.h"
27 #include "pbd/strsplit.h"
28 #include "pbd/xml++.h"
30 #include "ardour/debug.h"
31 #include "ardour/playlist.h"
32 #include "ardour/session.h"
33 #include "ardour/region.h"
34 #include "ardour/region_factory.h"
35 #include "ardour/region_sorters.h"
36 #include "ardour/playlist_factory.h"
37 #include "ardour/playlist_source.h"
38 #include "ardour/transient_detector.h"
39 #include "ardour/types_convert.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
67 Playlist::make_property_quarks ()
69 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
70 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
71 Properties::regions.property_id));
74 RegionListProperty::RegionListProperty (Playlist& pl)
75 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
81 RegionListProperty::RegionListProperty (RegionListProperty const & p)
82 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
83 , _playlist (p._playlist)
89 RegionListProperty::clone () const
91 return new RegionListProperty (*this);
95 RegionListProperty::create () const
97 return new RegionListProperty (_playlist);
101 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
103 /* All regions (even those which are deleted) have their state saved by other
104 code, so we can just store ID here.
107 node.set_property ("id", region->id());
110 boost::shared_ptr<Region>
111 RegionListProperty::get_content_from_xml (XMLNode const & node) const
114 if (!node.get_property ("id", id)) {
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
128 : SessionObject(sess, nom)
133 first_set_state = false;
138 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
139 : SessionObject(sess, "unnamed playlist")
144 XMLProperty const * prop = node.property("type");
145 assert(!prop || DataType(prop->value()) == _type);
149 _name = "unnamed"; /* reset by set_state */
152 /* set state called by derived class */
155 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
156 : SessionObject(other->_session, namestr)
158 , _type(other->_type)
159 , _orig_track_id (other->_orig_track_id)
160 , _shared_with_ids (other->_shared_with_ids)
165 other->copy_regions (tmp);
169 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
170 add_region_internal( (*x), (*x)->position());
175 _splicing = other->_splicing;
176 _rippling = other->_rippling;
177 _nudging = other->_nudging;
178 _edit_mode = other->_edit_mode;
181 first_set_state = false;
183 in_partition = false;
185 _frozen = other->_frozen;
188 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
189 : SessionObject(other->_session, str)
191 , _type(other->_type)
192 , _orig_track_id (other->_orig_track_id)
193 , _shared_with_ids (other->_shared_with_ids)
195 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
197 framepos_t end = start + cnt - 1;
203 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
205 boost::shared_ptr<Region> region;
206 boost::shared_ptr<Region> new_region;
207 frameoffset_t offset = 0;
208 framepos_t position = 0;
211 Evoral::OverlapType overlap;
215 overlap = region->coverage (start, end);
218 case Evoral::OverlapNone:
221 case Evoral::OverlapInternal:
222 offset = start - region->position();
227 case Evoral::OverlapStart:
229 position = region->position() - start;
230 len = end - region->position();
233 case Evoral::OverlapEnd:
234 offset = start - region->position();
236 len = region->length() - offset;
239 case Evoral::OverlapExternal:
241 position = region->position() - start;
242 len = region->length();
246 RegionFactory::region_name (new_name, region->name(), false);
250 plist.add (Properties::start, region->start() + offset);
251 plist.add (Properties::length, len);
252 plist.add (Properties::name, new_name);
253 plist.add (Properties::layer, region->layer());
254 plist.add (Properties::layering_index, region->layering_index());
256 new_region = RegionFactory::create (region, plist);
258 add_region_internal (new_region, position);
261 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
262 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
263 _end_space = cnt - (get_extent().second - get_extent().first);
266 first_set_state = false;
273 InUse (true); /* EMIT SIGNAL */
284 InUse (false); /* EMIT SIGNAL */
289 Playlist::copy_regions (RegionList& newlist) const
291 RegionReadLock rlock (const_cast<Playlist *> (this));
293 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
294 newlist.push_back (RegionFactory::create (*i, true, true));
299 Playlist::init (bool hide)
301 add_property (regions);
302 _xml_node_name = X_("Playlist");
304 g_atomic_int_set (&block_notifications, 0);
305 g_atomic_int_set (&ignore_state_changes, 0);
306 pending_contents_change = false;
307 pending_layering = false;
308 first_set_state = true;
317 _edit_mode = Config->get_edit_mode();
319 in_partition = false;
322 _capture_insertion_underway = false;
326 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
327 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
329 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
332 Playlist::~Playlist ()
334 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
337 RegionReadLock rl (this);
339 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
340 (*i)->set_playlist (boost::shared_ptr<Playlist>());
344 /* GoingAway must be emitted by derived classes */
348 Playlist::_set_sort_id ()
351 Playlists are given names like <track name>.<id>
352 or <track name>.<edit group name>.<id> where id
353 is an integer. We extract the id and sort by that.
356 size_t dot_position = _name.val().find_last_of(".");
358 if (dot_position == string::npos) {
361 string t = _name.val().substr(dot_position + 1);
363 if (!string_to_uint32 (t, _sort_id)) {
370 Playlist::set_name (const string& str)
372 /* in a typical situation, a playlist is being used
373 by one diskstream and also is referenced by the
374 Session. if there are more references than that,
375 then don't change the name.
382 bool ret = SessionObject::set_name(str);
389 /***********************************************************************
390 CHANGE NOTIFICATION HANDLING
392 Notifications must be delayed till the region_lock is released. This
393 is necessary because handlers for the signals may need to acquire
394 the lock (e.g. to read from the playlist).
395 ***********************************************************************/
398 Playlist::begin_undo ()
405 Playlist::end_undo ()
414 delay_notifications ();
415 g_atomic_int_inc (&ignore_state_changes);
418 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
420 Playlist::thaw (bool from_undo)
422 g_atomic_int_dec_and_test (&ignore_state_changes);
423 release_notifications (from_undo);
428 Playlist::delay_notifications ()
430 g_atomic_int_inc (&block_notifications);
433 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
435 Playlist::release_notifications (bool from_undo)
437 if (g_atomic_int_dec_and_test (&block_notifications)) {
438 flush_notifications (from_undo);
443 Playlist::notify_contents_changed ()
445 if (holding_state ()) {
446 pending_contents_change = true;
448 pending_contents_change = false;
449 ContentsChanged(); /* EMIT SIGNAL */
454 Playlist::notify_layering_changed ()
456 if (holding_state ()) {
457 pending_layering = true;
459 pending_layering = false;
460 LayeringChanged(); /* EMIT SIGNAL */
465 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
467 if (holding_state ()) {
468 pending_removes.insert (r);
469 pending_contents_change = true;
471 /* this might not be true, but we have to act
472 as though it could be.
474 pending_contents_change = false;
475 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
476 ContentsChanged (); /* EMIT SIGNAL */
481 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
483 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
485 if (holding_state ()) {
487 pending_range_moves.push_back (move);
491 list< Evoral::RangeMove<framepos_t> > m;
493 RangesMoved (m, false);
499 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
501 if (r->position() >= r->last_position()) {
502 /* trimmed shorter */
506 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
508 if (holding_state ()) {
510 pending_region_extensions.push_back (extra);
514 list<Evoral::Range<framepos_t> > r;
522 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
524 if (r->length() < r->last_length()) {
525 /* trimmed shorter */
528 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
530 if (holding_state ()) {
532 pending_region_extensions.push_back (extra);
536 list<Evoral::Range<framepos_t> > r;
544 Playlist::notify_region_added (boost::shared_ptr<Region> r)
546 /* the length change might not be true, but we have to act
547 as though it could be.
550 if (holding_state()) {
551 pending_adds.insert (r);
552 pending_contents_change = true;
555 pending_contents_change = false;
556 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
557 ContentsChanged (); /* EMIT SIGNAL */
562 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
564 Playlist::flush_notifications (bool from_undo)
566 set<boost::shared_ptr<Region> >::iterator s;
567 bool regions_changed = false;
575 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
576 regions_changed = true;
579 /* XXX: it'd be nice if we could use pending_bounds for
580 RegionsExtended and RegionsMoved.
583 /* we have no idea what order the regions ended up in pending
584 bounds (it could be based on selection order, for example).
585 so, to preserve layering in the "most recently moved is higher"
586 model, sort them by existing layer, then timestamp them.
589 // RegionSortByLayer cmp;
590 // pending_bounds.sort (cmp);
592 list<Evoral::Range<framepos_t> > crossfade_ranges;
594 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
595 crossfade_ranges.push_back ((*r)->last_range ());
596 crossfade_ranges.push_back ((*r)->range ());
599 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
600 crossfade_ranges.push_back ((*s)->range ());
601 remove_dependents (*s);
602 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
605 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
606 crossfade_ranges.push_back ((*s)->range ());
607 /* don't emit RegionAdded signal until relayering is done,
608 so that the region is fully setup by the time
609 anyone hears that its been added
613 /* notify about contents/region changes first so that layering changes
614 * in a UI will take place on the new contents.
617 if (regions_changed || pending_contents_change) {
618 pending_layering = true;
619 ContentsChanged (); /* EMIT SIGNAL */
622 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
623 (*s)->clear_changes ();
624 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
627 if ((regions_changed && !in_set_state) || pending_layering) {
631 coalesce_and_check_crossfades (crossfade_ranges);
633 if (!pending_range_moves.empty ()) {
634 /* We don't need to check crossfades for these as pending_bounds has
637 RangesMoved (pending_range_moves, from_undo);
640 if (!pending_region_extensions.empty ()) {
641 RegionsExtended (pending_region_extensions);
650 Playlist::clear_pending ()
652 pending_adds.clear ();
653 pending_removes.clear ();
654 pending_bounds.clear ();
655 pending_range_moves.clear ();
656 pending_region_extensions.clear ();
657 pending_contents_change = false;
658 pending_layering = false;
661 /*************************************************************
663 *************************************************************/
665 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
667 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)
669 RegionWriteLock rlock (this);
670 times = fabs (times);
672 int itimes = (int) floor (times);
674 framepos_t pos = position;
676 if (times == 1 && auto_partition){
678 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
679 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
680 (*i)->resume_property_changes ();
681 _session.add_command (new StatefulDiffCommand (*i));
686 add_region_internal (region, pos, sub_num, quarter_note, for_music);
687 set_layer (region, DBL_MAX);
688 pos += region->length();
692 /* note that itimes can be zero if we being asked to just
693 insert a single fraction of the region.
696 for (int i = 0; i < itimes; ++i) {
697 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
698 add_region_internal (copy, pos, sub_num);
699 set_layer (copy, DBL_MAX);
700 pos += region->length();
703 framecnt_t length = 0;
705 if (floor (times) != times) {
706 length = (framecnt_t) floor (region->length() * (times - floor (times)));
708 RegionFactory::region_name (name, region->name(), false);
713 plist.add (Properties::start, region->start());
714 plist.add (Properties::length, length);
715 plist.add (Properties::name, name);
716 plist.add (Properties::layer, region->layer());
718 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
719 add_region_internal (sub, pos, sub_num);
720 set_layer (sub, DBL_MAX);
724 possibly_splice_unlocked (position, (pos + length) - position, region);
728 Playlist::set_region_ownership ()
730 RegionWriteLock rl (this);
731 RegionList::iterator i;
732 boost::weak_ptr<Playlist> pl (shared_from_this());
734 for (i = regions.begin(); i != regions.end(); ++i) {
735 (*i)->set_playlist (pl);
740 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, int32_t sub_num, double quarter_note, bool for_music)
742 if (region->data_type() != _type) {
746 RegionSortByPosition cmp;
748 if (!first_set_state) {
749 boost::shared_ptr<Playlist> foo (shared_from_this());
750 region->set_playlist (boost::weak_ptr<Playlist>(foo));
753 region->set_position_music (quarter_note);
755 region->set_position (position, sub_num);
758 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
759 all_regions.insert (region);
761 possibly_splice_unlocked (position, region->length(), region);
763 if (!holding_state ()) {
764 /* layers get assigned from XML state, and are not reset during undo/redo */
768 /* we need to notify the existence of new region before checking dependents. Ick. */
770 notify_region_added (region);
772 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
773 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
779 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
781 RegionWriteLock rlock (this);
783 bool old_sp = _splicing;
786 remove_region_internal (old);
787 add_region_internal (newr, pos);
788 set_layer (newr, old->layer ());
792 possibly_splice_unlocked (pos, old->length() - newr->length());
796 Playlist::remove_region (boost::shared_ptr<Region> region)
798 RegionWriteLock rlock (this);
799 remove_region_internal (region);
803 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
805 RegionList::iterator i;
809 region->set_playlist (boost::weak_ptr<Playlist>());
812 /* XXX should probably freeze here .... */
814 for (i = regions.begin(); i != regions.end(); ++i) {
817 framepos_t pos = (*i)->position();
818 framecnt_t distance = (*i)->length();
822 possibly_splice_unlocked (pos, -distance);
824 if (!holding_state ()) {
826 remove_dependents (region);
829 notify_region_removed (region);
838 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
840 if (Config->get_use_overlap_equivalency()) {
841 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
842 if ((*i)->overlap_equivalent (other)) {
843 results.push_back (*i);
847 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
848 if ((*i)->equivalent (other)) {
849 results.push_back (*i);
856 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
858 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
860 if ((*i) && (*i)->region_list_equivalent (other)) {
861 results.push_back (*i);
867 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
869 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
871 if ((*i) && (*i)->any_source_equivalent (other)) {
872 results.push_back (*i);
878 Playlist::partition (framepos_t start, framepos_t end, bool cut)
882 RegionWriteLock lock(this);
883 partition_internal (start, end, cut, thawlist);
886 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
887 (*i)->resume_property_changes ();
891 /** Go through each region on the playlist and cut them at start and end, removing the section between
892 * start and end if cutting == true. Regions that lie entirely within start and end are always
897 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
899 RegionList new_regions;
903 boost::shared_ptr<Region> region;
904 boost::shared_ptr<Region> current;
906 RegionList::iterator tmp;
907 Evoral::OverlapType overlap;
908 framepos_t pos1, pos2, pos3, pos4;
912 /* need to work from a copy, because otherwise the regions we add during the process
913 get operated on as well.
916 RegionList copy = regions.rlist();
918 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
925 if (current->first_frame() >= start && current->last_frame() < end) {
928 remove_region_internal (current);
934 /* coverage will return OverlapStart if the start coincides
935 with the end point. we do not partition such a region,
936 so catch this special case.
939 if (current->first_frame() >= end) {
943 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
947 pos1 = current->position();
950 pos4 = current->last_frame();
952 if (overlap == Evoral::OverlapInternal) {
953 /* split: we need 3 new regions, the front, middle and end.
954 cut: we need 2 regions, the front and end.
959 ---------------*************************------------
962 ---------------*****++++++++++++++++====------------
964 ---------------*****----------------====------------
969 /* "middle" ++++++ */
971 RegionFactory::region_name (new_name, current->name(), false);
975 plist.add (Properties::start, current->start() + (pos2 - pos1));
976 plist.add (Properties::length, pos3 - pos2);
977 plist.add (Properties::name, new_name);
978 plist.add (Properties::layer, current->layer ());
979 plist.add (Properties::layering_index, current->layering_index ());
980 plist.add (Properties::automatic, true);
981 plist.add (Properties::left_of_split, true);
982 plist.add (Properties::right_of_split, true);
984 region = RegionFactory::create (current, plist);
985 add_region_internal (region, start);
986 new_regions.push_back (region);
991 RegionFactory::region_name (new_name, current->name(), false);
995 plist.add (Properties::start, current->start() + (pos3 - pos1));
996 plist.add (Properties::length, pos4 - pos3);
997 plist.add (Properties::name, new_name);
998 plist.add (Properties::layer, current->layer ());
999 plist.add (Properties::layering_index, current->layering_index ());
1000 plist.add (Properties::automatic, true);
1001 plist.add (Properties::right_of_split, true);
1003 region = RegionFactory::create (current, plist);
1005 add_region_internal (region, end);
1006 new_regions.push_back (region);
1010 current->clear_changes ();
1011 current->suspend_property_changes ();
1012 thawlist.push_back (current);
1013 current->cut_end (pos2 - 1);
1015 } else if (overlap == Evoral::OverlapEnd) {
1019 ---------------*************************------------
1022 ---------------**************+++++++++++------------
1024 ---------------**************-----------------------
1031 RegionFactory::region_name (new_name, current->name(), false);
1035 plist.add (Properties::start, current->start() + (pos2 - pos1));
1036 plist.add (Properties::length, pos4 - pos2);
1037 plist.add (Properties::name, new_name);
1038 plist.add (Properties::layer, current->layer ());
1039 plist.add (Properties::layering_index, current->layering_index ());
1040 plist.add (Properties::automatic, true);
1041 plist.add (Properties::left_of_split, true);
1043 region = RegionFactory::create (current, plist);
1045 add_region_internal (region, start);
1046 new_regions.push_back (region);
1051 current->clear_changes ();
1052 current->suspend_property_changes ();
1053 thawlist.push_back (current);
1054 current->cut_end (pos2 - 1);
1056 } else if (overlap == Evoral::OverlapStart) {
1058 /* split: we need 2 regions: the front and the end.
1059 cut: just trim current to skip the cut area
1064 ---------------*************************------------
1068 ---------------****+++++++++++++++++++++------------
1070 -------------------*********************------------
1076 RegionFactory::region_name (new_name, current->name(), false);
1080 plist.add (Properties::start, current->start());
1081 plist.add (Properties::length, pos3 - pos1);
1082 plist.add (Properties::name, new_name);
1083 plist.add (Properties::layer, current->layer ());
1084 plist.add (Properties::layering_index, current->layering_index ());
1085 plist.add (Properties::automatic, true);
1086 plist.add (Properties::right_of_split, true);
1088 region = RegionFactory::create (current, plist);
1090 add_region_internal (region, pos1);
1091 new_regions.push_back (region);
1096 current->clear_changes ();
1097 current->suspend_property_changes ();
1098 thawlist.push_back (current);
1099 current->trim_front (pos3);
1100 } else if (overlap == Evoral::OverlapExternal) {
1102 /* split: no split required.
1103 cut: remove the region.
1108 ---------------*************************------------
1112 ---------------*************************------------
1114 ----------------------------------------------------
1119 remove_region_internal (current);
1122 new_regions.push_back (current);
1126 in_partition = false;
1129 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1130 framepos_t wanted_length = end-start;
1131 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1134 boost::shared_ptr<Playlist>
1135 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1137 boost::shared_ptr<Playlist> ret;
1138 boost::shared_ptr<Playlist> pl;
1141 if (ranges.empty()) {
1142 return boost::shared_ptr<Playlist>();
1145 start = ranges.front().start;
1147 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1149 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1151 if (i == ranges.begin()) {
1155 /* paste the next section into the nascent playlist,
1156 offset to reflect the start of the first range we
1160 ret->paste (pl, (*i).start - start, 1.0f, 0);
1167 boost::shared_ptr<Playlist>
1168 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1170 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1171 return cut_copy (pmf, ranges, result_is_hidden);
1174 boost::shared_ptr<Playlist>
1175 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1177 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1178 return cut_copy (pmf, ranges, result_is_hidden);
1181 boost::shared_ptr<Playlist>
1182 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1184 boost::shared_ptr<Playlist> the_copy;
1185 RegionList thawlist;
1188 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1189 string new_name = _name;
1193 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1194 return boost::shared_ptr<Playlist>();
1198 RegionWriteLock rlock (this);
1199 partition_internal (start, start+cnt-1, true, thawlist);
1202 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1203 (*i)->resume_property_changes();
1209 boost::shared_ptr<Playlist>
1210 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1214 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1215 string new_name = _name;
1219 // 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... )
1221 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1225 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1227 times = fabs (times);
1230 RegionReadLock rl2 (other.get());
1232 int itimes = (int) floor (times);
1233 framepos_t pos = position;
1234 framecnt_t const shift = other->_get_extent().second;
1235 layer_t top = top_layer ();
1238 RegionWriteLock rl1 (this);
1240 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1241 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1243 /* put these new regions on top of all existing ones, but preserve
1244 the ordering they had in the original playlist.
1247 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1248 set_layer (copy_of_region, copy_of_region->layer() + top);
1260 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1262 duplicate(region, position, region->length(), times);
1265 /** @param gap from the beginning of the region to the next beginning */
1267 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1269 times = fabs (times);
1271 RegionWriteLock rl (this);
1272 int itimes = (int) floor (times);
1275 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1276 add_region_internal (copy, position);
1277 set_layer (copy, DBL_MAX);
1281 if (floor (times) != times) {
1282 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1284 RegionFactory::region_name (name, region->name(), false);
1289 plist.add (Properties::start, region->start());
1290 plist.add (Properties::length, length);
1291 plist.add (Properties::name, name);
1293 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1294 add_region_internal (sub, position);
1295 set_layer (sub, DBL_MAX);
1300 /** @param gap from the beginning of the region to the next beginning */
1301 /** @param end the first frame that does _not_ contain a duplicated frame */
1303 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1305 RegionWriteLock rl (this);
1307 while (position + region->length() - 1 < end) {
1308 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1309 add_region_internal (copy, position);
1310 set_layer (copy, DBL_MAX);
1314 if (position < end) {
1315 framecnt_t length = min (region->length(), end - position);
1317 RegionFactory::region_name (name, region->name(), false);
1322 plist.add (Properties::start, region->start());
1323 plist.add (Properties::length, length);
1324 plist.add (Properties::name, name);
1326 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1327 add_region_internal (sub, position);
1328 set_layer (sub, DBL_MAX);
1334 Playlist::duplicate_range (AudioRange& range, float times)
1336 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1337 framecnt_t offset = range.end - range.start;
1338 paste (pl, range.start + offset, times, 0);
1342 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1344 if (ranges.empty()) {
1348 framepos_t min_pos = max_framepos;
1349 framepos_t max_pos = 0;
1351 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1354 min_pos = min (min_pos, (*i).start);
1355 max_pos = max (max_pos, (*i).end);
1358 framecnt_t offset = max_pos - min_pos;
1361 int itimes = (int) floor (times);
1363 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1364 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1365 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1372 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1374 RegionWriteLock rlock (this);
1375 RegionList copy (regions.rlist());
1378 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1380 if ((*r)->last_frame() < at) {
1385 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1386 /* intersected region */
1387 if (!move_intersected) {
1392 /* do not move regions glued to music time - that
1393 has to be done separately.
1396 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1397 fixup.push_back (*r);
1401 (*r)->set_position ((*r)->position() + distance);
1404 /* XXX: may not be necessary; Region::post_set should do this, I think */
1405 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1406 (*r)->recompute_position_from_lock_style (0);
1411 Playlist::split (const MusicFrame& at)
1413 RegionWriteLock rlock (this);
1414 RegionList copy (regions.rlist());
1416 /* use a copy since this operation can modify the region list
1419 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1420 _split_region (*r, at);
1425 Playlist::split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1427 RegionWriteLock rl (this);
1428 _split_region (region, playlist_position);
1432 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1434 if (!region->covers (playlist_position.frame)) {
1438 if (region->position() == playlist_position.frame ||
1439 region->last_frame() == playlist_position.frame) {
1443 boost::shared_ptr<Region> left;
1444 boost::shared_ptr<Region> right;
1446 MusicFrame before (playlist_position.frame - region->position(), playlist_position.division);
1447 MusicFrame after (region->length() - before.frame, 0);
1451 /* split doesn't change anything about length, so don't try to splice */
1453 bool old_sp = _splicing;
1456 RegionFactory::region_name (before_name, region->name(), false);
1461 plist.add (Properties::length, before.frame);
1462 plist.add (Properties::name, before_name);
1463 plist.add (Properties::left_of_split, true);
1464 plist.add (Properties::layering_index, region->layering_index ());
1465 plist.add (Properties::layer, region->layer ());
1467 /* note: we must use the version of ::create with an offset here,
1468 since it supplies that offset to the Region constructor, which
1469 is necessary to get audio region gain envelopes right.
1471 left = RegionFactory::create (region, MusicFrame (0, 0), plist, true);
1474 RegionFactory::region_name (after_name, region->name(), false);
1479 plist.add (Properties::length, after.frame);
1480 plist.add (Properties::name, after_name);
1481 plist.add (Properties::right_of_split, true);
1482 plist.add (Properties::layering_index, region->layering_index ());
1483 plist.add (Properties::layer, region->layer ());
1485 /* same note as above */
1486 right = RegionFactory::create (region, before, plist, true);
1489 add_region_internal (left, region->position(), 0);
1490 add_region_internal (right, region->position() + before.frame, before.division);
1492 remove_region_internal (region);
1498 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1500 if (_splicing || in_set_state) {
1501 /* don't respond to splicing moves or state setting */
1505 if (_edit_mode == Splice) {
1506 splice_locked (at, distance, exclude);
1511 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1513 if (_splicing || in_set_state) {
1514 /* don't respond to splicing moves or state setting */
1518 if (_edit_mode == Splice) {
1519 splice_unlocked (at, distance, exclude);
1524 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1527 RegionWriteLock rl (this);
1528 core_splice (at, distance, exclude);
1533 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1535 core_splice (at, distance, exclude);
1539 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1543 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1545 if (exclude && (*i) == exclude) {
1549 if ((*i)->position() >= at) {
1550 framepos_t new_pos = (*i)->position() + distance;
1553 } else if (new_pos >= max_framepos - (*i)->length()) {
1554 new_pos = max_framepos - (*i)->length();
1557 (*i)->set_position (new_pos);
1563 notify_contents_changed ();
1567 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1570 RegionWriteLock rl (this);
1571 core_ripple (at, distance, exclude);
1576 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1578 core_ripple (at, distance, exclude);
1582 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1584 if (distance == 0) {
1589 RegionListProperty copy = regions;
1590 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1591 assert (i != copy.end());
1594 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1599 if ((*i)->position() >= at) {
1600 framepos_t new_pos = (*i)->position() + distance;
1601 framepos_t limit = max_framepos - (*i)->length();
1604 } else if (new_pos >= limit ) {
1608 (*i)->set_position (new_pos);
1613 notify_contents_changed ();
1618 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1620 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1624 if (what_changed.contains (Properties::position)) {
1626 /* remove it from the list then add it back in
1627 the right place again.
1630 RegionSortByPosition cmp;
1632 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1634 if (i == regions.end()) {
1635 /* the region bounds are being modified but its not currently
1636 in the region list. we will use its bounds correctly when/if
1643 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1646 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1648 frameoffset_t delta = 0;
1650 if (what_changed.contains (Properties::position)) {
1651 delta = region->position() - region->last_position();
1654 if (what_changed.contains (Properties::length)) {
1655 delta += region->length() - region->last_length();
1659 possibly_splice (region->last_position() + region->last_length(), delta, region);
1662 if (holding_state ()) {
1663 pending_bounds.push_back (region);
1665 notify_contents_changed ();
1667 list<Evoral::Range<framepos_t> > xf;
1668 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1669 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1670 coalesce_and_check_crossfades (xf);
1676 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1678 boost::shared_ptr<Region> region (weak_region.lock());
1684 /* this makes a virtual call to the right kind of playlist ... */
1686 region_changed (what_changed, region);
1690 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1692 PropertyChange our_interests;
1693 PropertyChange bounds;
1694 PropertyChange pos_and_length;
1697 if (in_set_state || in_flush) {
1701 our_interests.add (Properties::muted);
1702 our_interests.add (Properties::layer);
1703 our_interests.add (Properties::opaque);
1705 bounds.add (Properties::start);
1706 bounds.add (Properties::position);
1707 bounds.add (Properties::length);
1709 pos_and_length.add (Properties::position);
1710 pos_and_length.add (Properties::length);
1712 if (what_changed.contains (bounds)) {
1713 region_bounds_changed (what_changed, region);
1714 save = !(_splicing || _nudging);
1717 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1718 notify_region_moved (region);
1719 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1720 notify_region_end_trimmed (region);
1721 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1722 notify_region_start_trimmed (region);
1725 /* don't notify about layer changes, since we are the only object that can initiate
1726 them, and we notify in ::relayer()
1729 if (what_changed.contains (our_interests)) {
1733 mark_session_dirty ();
1739 Playlist::drop_regions ()
1741 RegionWriteLock rl (this);
1743 all_regions.clear ();
1747 Playlist::sync_all_regions_with_regions ()
1749 RegionWriteLock rl (this);
1751 all_regions.clear ();
1753 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1754 all_regions.insert (*i);
1759 Playlist::clear (bool with_signals)
1762 RegionWriteLock rl (this);
1764 region_state_changed_connections.drop_connections ();
1765 region_drop_references_connections.drop_connections ();
1767 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1768 pending_removes.insert (*i);
1773 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1774 remove_dependents (*s);
1780 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1781 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1784 pending_removes.clear ();
1785 pending_contents_change = false;
1791 /* *********************************************************************
1793 **********************************************************************/
1795 boost::shared_ptr<RegionList>
1796 Playlist::region_list()
1798 RegionReadLock rlock (this);
1799 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1804 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1806 RegionReadLock rlock (const_cast<Playlist*>(this));
1808 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1809 (*i)->deep_sources (sources);
1813 boost::shared_ptr<RegionList>
1814 Playlist::regions_at (framepos_t frame)
1816 RegionReadLock rlock (this);
1817 return find_regions_at (frame);
1821 Playlist::count_regions_at (framepos_t frame) const
1823 RegionReadLock rlock (const_cast<Playlist*>(this));
1826 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1827 if ((*i)->covers (frame)) {
1835 boost::shared_ptr<Region>
1836 Playlist::top_region_at (framepos_t frame)
1839 RegionReadLock rlock (this);
1840 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1841 boost::shared_ptr<Region> region;
1843 if (rlist->size()) {
1844 RegionSortByLayer cmp;
1846 region = rlist->back();
1852 boost::shared_ptr<Region>
1853 Playlist::top_unmuted_region_at (framepos_t frame)
1856 RegionReadLock rlock (this);
1857 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1859 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1861 RegionList::iterator tmp = i;
1865 if ((*i)->muted()) {
1872 boost::shared_ptr<Region> region;
1874 if (rlist->size()) {
1875 RegionSortByLayer cmp;
1877 region = rlist->back();
1883 boost::shared_ptr<RegionList>
1884 Playlist::find_regions_at (framepos_t frame)
1886 /* Caller must hold lock */
1888 boost::shared_ptr<RegionList> rlist (new RegionList);
1890 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1891 if ((*i)->covers (frame)) {
1892 rlist->push_back (*i);
1899 boost::shared_ptr<RegionList>
1900 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1902 RegionReadLock rlock (this);
1903 boost::shared_ptr<RegionList> rlist (new RegionList);
1905 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1906 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1907 rlist->push_back (*i);
1914 boost::shared_ptr<RegionList>
1915 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1917 RegionReadLock rlock (this);
1918 boost::shared_ptr<RegionList> rlist (new RegionList);
1920 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1921 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1922 rlist->push_back (*i);
1929 /** @param start Range start.
1930 * @param end Range end.
1931 * @return regions which have some part within this range.
1933 boost::shared_ptr<RegionList>
1934 Playlist::regions_touched (framepos_t start, framepos_t end)
1936 RegionReadLock rlock (this);
1937 return regions_touched_locked (start, end);
1940 boost::shared_ptr<RegionList>
1941 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1943 boost::shared_ptr<RegionList> rlist (new RegionList);
1945 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1946 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1947 rlist->push_back (*i);
1955 Playlist::find_next_transient (framepos_t from, int dir)
1957 RegionReadLock rlock (this);
1958 AnalysisFeatureList points;
1959 AnalysisFeatureList these_points;
1961 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1963 if ((*i)->last_frame() < from) {
1967 if ((*i)->first_frame() > from) {
1972 (*i)->get_transients (these_points);
1974 /* add first frame, just, err, because */
1976 these_points.push_back ((*i)->first_frame());
1978 points.insert (points.end(), these_points.begin(), these_points.end());
1979 these_points.clear ();
1982 if (points.empty()) {
1986 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1987 bool reached = false;
1990 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1995 if (reached && (*x) > from) {
2000 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2005 if (reached && (*x) < from) {
2014 boost::shared_ptr<Region>
2015 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2017 RegionReadLock rlock (this);
2018 boost::shared_ptr<Region> ret;
2019 framepos_t closest = max_framepos;
2021 bool end_iter = false;
2023 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2027 frameoffset_t distance;
2028 boost::shared_ptr<Region> r = (*i);
2033 pos = r->first_frame ();
2036 pos = r->last_frame ();
2039 pos = r->sync_position ();
2044 case 1: /* forwards */
2047 if ((distance = pos - frame) < closest) {
2056 default: /* backwards */
2059 if ((distance = frame - pos) < closest) {
2075 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2077 RegionReadLock rlock (this);
2079 framepos_t closest = max_framepos;
2080 framepos_t ret = -1;
2084 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2086 boost::shared_ptr<Region> r = (*i);
2087 frameoffset_t distance;
2088 const framepos_t first_frame = r->first_frame();
2089 const framepos_t last_frame = r->last_frame();
2091 if (first_frame > frame) {
2093 distance = first_frame - frame;
2095 if (distance < closest) {
2101 if (last_frame > frame) {
2103 distance = last_frame - frame;
2105 if (distance < closest) {
2114 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2116 boost::shared_ptr<Region> r = (*i);
2117 frameoffset_t distance;
2118 const framepos_t first_frame = r->first_frame();
2119 const framepos_t last_frame = r->last_frame();
2121 if (last_frame < frame) {
2123 distance = frame - last_frame;
2125 if (distance < closest) {
2131 if (first_frame < frame) {
2133 distance = frame - first_frame;
2135 if (distance < closest) {
2147 /***********************************************************************/
2153 Playlist::mark_session_dirty ()
2155 if (!in_set_state && !holding_state ()) {
2156 _session.set_dirty();
2161 Playlist::rdiff (vector<Command*>& cmds) const
2163 RegionReadLock rlock (const_cast<Playlist *> (this));
2164 Stateful::rdiff (cmds);
2168 Playlist::clear_owned_changes ()
2170 RegionReadLock rlock (this);
2171 Stateful::clear_owned_changes ();
2175 Playlist::update (const RegionListProperty::ChangeRecord& change)
2177 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2178 name(), change.added.size(), change.removed.size()));
2181 /* add the added regions */
2182 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2183 add_region_internal ((*i), (*i)->position());
2185 /* remove the removed regions */
2186 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2194 Playlist::set_state (const XMLNode& node, int version)
2198 XMLNodeConstIterator niter;
2199 XMLPropertyConstIterator piter;
2200 boost::shared_ptr<Region> region;
2202 bool seen_region_nodes = false;
2207 if (node.name() != "Playlist") {
2217 if (node.get_property (X_("name"), name)) {
2222 /* XXX legacy session: fix up later */
2223 node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2225 node.get_property (X_("orig-track-id"), _orig_track_id);
2226 node.get_property (X_("frozen"), _frozen);
2228 node.get_property (X_("combine-ops"), _combine_ops);
2231 if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2232 if (!shared_ids.empty()) {
2233 vector<string> result;
2234 ::split (shared_ids, result, ',');
2235 vector<string>::iterator it = result.begin();
2236 for (; it != result.end(); ++it) {
2237 _shared_with_ids.push_back (PBD::ID(*it));
2244 nlist = node.children();
2246 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2250 if (child->name() == "Region") {
2252 seen_region_nodes = true;
2255 if (!child->get_property ("id", id)) {
2256 error << _("region state node has no ID, ignored") << endmsg;
2260 if ((region = region_by_id (id))) {
2262 region->suspend_property_changes ();
2264 if (region->set_state (*child, version)) {
2265 region->resume_property_changes ();
2269 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2270 region->suspend_property_changes ();
2272 error << _("Playlist: cannot create region from XML") << endmsg;
2277 RegionWriteLock rlock (this);
2278 add_region_internal (region, region->position());
2281 region->resume_property_changes ();
2286 if (seen_region_nodes && regions.empty()) {
2291 notify_contents_changed ();
2294 first_set_state = false;
2300 Playlist::get_state()
2302 return state (true);
2306 Playlist::get_template()
2308 return state (false);
2311 /** @param full_state true to include regions in the returned state, otherwise false.
2314 Playlist::state (bool full_state)
2316 XMLNode *node = new XMLNode (X_("Playlist"));
2318 node->set_property (X_("id"), id());
2319 node->set_property (X_("name"), name());
2320 node->set_property (X_("type"), _type);
2321 node->set_property (X_("orig-track-id"), _orig_track_id);
2324 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2325 for (; it != _shared_with_ids.end(); ++it) {
2326 shared_ids += "," + (*it).to_s();
2328 if (!shared_ids.empty()) {
2329 shared_ids.erase(0,1);
2332 node->set_property (X_("shared-with-ids"), shared_ids);
2333 node->set_property (X_("frozen"), _frozen);
2336 RegionReadLock rlock (this);
2338 node->set_property ("combine-ops", _combine_ops);
2340 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2341 node->add_child_nocopy ((*i)->get_state());
2346 node->add_child_copy (*_extra_xml);
2353 Playlist::empty() const
2355 RegionReadLock rlock (const_cast<Playlist *>(this));
2356 return regions.empty();
2360 Playlist::n_regions() const
2362 RegionReadLock rlock (const_cast<Playlist *>(this));
2363 return regions.size();
2366 /** @return true if the all_regions list is empty, ie this playlist
2367 * has never had a region added to it.
2370 Playlist::all_regions_empty() const
2372 RegionReadLock rl (const_cast<Playlist *> (this));
2373 return all_regions.empty();
2376 pair<framepos_t, framepos_t>
2377 Playlist::get_extent () const
2379 RegionReadLock rlock (const_cast<Playlist *>(this));
2380 return _get_extent ();
2383 pair<framepos_t, framepos_t>
2384 Playlist::get_extent_with_endspace () const
2386 pair<framepos_t, framepos_t> l = get_extent();
2387 l.second += _end_space;
2391 pair<framepos_t, framepos_t>
2392 Playlist::_get_extent () const
2394 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2396 if (regions.empty()) {
2401 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2402 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2403 if (e.first < ext.first) {
2404 ext.first = e.first;
2406 if (e.second > ext.second) {
2407 ext.second = e.second;
2415 Playlist::bump_name (string name, Session &session)
2417 string newname = name;
2420 newname = bump_name_once (newname, '.');
2421 } while (session.playlists->by_name (newname)!=NULL);
2428 Playlist::top_layer() const
2430 RegionReadLock rlock (const_cast<Playlist *> (this));
2433 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2434 top = max (top, (*i)->layer());
2440 Playlist::set_edit_mode (EditMode mode)
2445 struct RelayerSort {
2446 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2447 return a->layering_index() < b->layering_index();
2451 /** Set a new layer for a region. This adjusts the layering indices of all
2452 * regions in the playlist to put the specified region in the appropriate
2453 * place. The actual layering will be fixed up when relayer() happens.
2457 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2459 /* Remove the layer we are setting from our region list, and sort it
2460 * using the layer indeces.
2463 RegionList copy = regions.rlist();
2464 copy.remove (region);
2465 copy.sort (RelayerSort ());
2467 /* Put region back in the right place */
2468 RegionList::iterator i = copy.begin();
2469 while (i != copy.end ()) {
2470 if ((*i)->layer() > new_layer) {
2476 copy.insert (i, region);
2478 setup_layering_indices (copy);
2482 Playlist::setup_layering_indices (RegionList const & regions)
2486 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2487 (*k)->set_layering_index (j++);
2491 struct LaterHigherSort {
2492 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2493 return a->position() < b->position();
2497 /** Take the layering indices of each of our regions, compute the layers
2498 * that they should be on, and write the layers back to the regions.
2501 Playlist::relayer ()
2503 /* never compute layers when setting from XML */
2509 /* Build up a new list of regions on each layer, stored in a set of lists
2510 each of which represent some period of time on some layer. The idea
2511 is to avoid having to search the entire region list to establish whether
2512 each region overlaps another */
2514 /* how many pieces to divide this playlist's time up into */
2515 int const divisions = 512;
2517 /* find the start and end positions of the regions on this playlist */
2518 framepos_t start = INT64_MAX;
2520 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2521 start = min (start, (*i)->position());
2522 end = max (end, (*i)->position() + (*i)->length());
2525 /* hence the size of each time division */
2526 double const division_size = (end - start) / double (divisions);
2528 vector<vector<RegionList> > layers;
2529 layers.push_back (vector<RegionList> (divisions));
2531 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2532 RegionList copy = regions.rlist();
2533 switch (Config->get_layer_model()) {
2535 copy.sort (LaterHigherSort ());
2538 copy.sort (RelayerSort ());
2542 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2543 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2544 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2547 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2549 /* find the time divisions that this region covers; if there are no regions on the list,
2550 division_size will equal 0 and in this case we'll just say that
2551 start_division = end_division = 0.
2553 int start_division = 0;
2554 int end_division = 0;
2556 if (division_size > 0) {
2557 start_division = floor ( ((*i)->position() - start) / division_size);
2558 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2559 if (end_division == divisions) {
2564 assert (divisions == 0 || end_division < divisions);
2566 /* find the lowest layer that this region can go on */
2567 size_t j = layers.size();
2569 /* try layer j - 1; it can go on if it overlaps no other region
2570 that is already on that layer
2573 bool overlap = false;
2574 for (int k = start_division; k <= end_division; ++k) {
2575 RegionList::iterator l = layers[j-1][k].begin ();
2576 while (l != layers[j-1][k].end()) {
2577 if ((*l)->overlap_equivalent (*i)) {
2590 /* overlap, so we must use layer j */
2597 if (j == layers.size()) {
2598 /* we need a new layer for this region */
2599 layers.push_back (vector<RegionList> (divisions));
2602 /* put a reference to this region in each of the divisions that it exists in */
2603 for (int k = start_division; k <= end_division; ++k) {
2604 layers[j][k].push_back (*i);
2607 (*i)->set_layer (j);
2610 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2611 relayering because we just removed the only region on the top layer, nothing will
2612 appear to have changed, but the StreamView must still sort itself out. We could
2613 probably keep a note of the top layer last time we relayered, and check that,
2614 but premature optimisation &c...
2616 notify_layering_changed ();
2618 /* This relayer() may have been called as a result of a region removal, in which
2619 case we need to setup layering indices to account for the one that has just
2622 setup_layering_indices (copy);
2626 Playlist::raise_region (boost::shared_ptr<Region> region)
2628 set_layer (region, region->layer() + 1.5);
2633 Playlist::lower_region (boost::shared_ptr<Region> region)
2635 set_layer (region, region->layer() - 1.5);
2640 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2642 set_layer (region, DBL_MAX);
2647 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2649 set_layer (region, -0.5);
2654 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2656 RegionList::iterator i;
2662 RegionWriteLock rlock (const_cast<Playlist *> (this));
2664 for (i = regions.begin(); i != regions.end(); ++i) {
2666 if ((*i)->position() >= start) {
2672 if ((*i)->last_frame() > max_framepos - distance) {
2673 new_pos = max_framepos - (*i)->length();
2675 new_pos = (*i)->position() + distance;
2680 if ((*i)->position() > distance) {
2681 new_pos = (*i)->position() - distance;
2687 (*i)->set_position (new_pos);
2695 notify_contents_changed ();
2701 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2703 RegionReadLock rlock (const_cast<Playlist*> (this));
2705 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2706 /* Note: passing the second argument as false can cause at best
2707 incredibly deep and time-consuming recursion, and at worst
2708 cycles if the user has managed to create cycles of reference
2709 between compound regions. We generally only this during
2710 cleanup, and @param shallow is passed as true.
2712 if ((*r)->uses_source (src, shallow)) {
2721 boost::shared_ptr<Region>
2722 Playlist::find_region (const ID& id) const
2724 RegionReadLock rlock (const_cast<Playlist*> (this));
2726 /* searches all regions currently in use by the playlist */
2728 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2729 if ((*i)->id() == id) {
2734 return boost::shared_ptr<Region> ();
2738 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2740 RegionReadLock rlock (const_cast<Playlist*> (this));
2743 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2749 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2750 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2751 /* check if region is used in a compound */
2752 if (it->second == r) {
2753 /* region is referenced as 'original' of a compound */
2757 if (r->whole_file() && r->max_source_level() > 0) {
2758 /* region itself ia a compound.
2759 * the compound regions are not referenced -> check regions inside compound
2761 const SourceList& sl = r->sources();
2762 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2763 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2765 if (ps->playlist()->region_use_count(it->first)) {
2766 // break out of both loops
2775 boost::shared_ptr<Region>
2776 Playlist::region_by_id (const ID& id) const
2778 /* searches all regions ever added to this playlist */
2780 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2781 if ((*i)->id() == id) {
2785 return boost::shared_ptr<Region> ();
2789 Playlist::dump () const
2791 boost::shared_ptr<Region> r;
2793 cerr << "Playlist \"" << _name << "\" " << endl
2794 << regions.size() << " regions "
2797 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2799 cerr << " " << r->name() << " ["
2800 << r->start() << "+" << r->length()
2810 Playlist::set_frozen (bool yn)
2816 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2820 if (region->locked()) {
2827 RegionWriteLock rlock (const_cast<Playlist*> (this));
2832 RegionList::iterator next;
2834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2835 if ((*i) == region) {
2839 if (next != regions.end()) {
2841 if ((*next)->locked()) {
2847 if ((*next)->position() != region->last_frame() + 1) {
2848 /* they didn't used to touch, so after shuffle,
2849 just have them swap positions.
2851 new_pos = (*next)->position();
2853 /* they used to touch, so after shuffle,
2854 make sure they still do. put the earlier
2855 region where the later one will end after
2858 new_pos = region->position() + (*next)->length();
2861 (*next)->set_position (region->position());
2862 region->set_position (new_pos);
2864 /* avoid a full sort */
2866 regions.erase (i); // removes the region from the list */
2868 regions.insert (next, region); // adds it back after next
2877 RegionList::iterator prev = regions.end();
2879 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2880 if ((*i) == region) {
2882 if (prev != regions.end()) {
2884 if ((*prev)->locked()) {
2889 if (region->position() != (*prev)->last_frame() + 1) {
2890 /* they didn't used to touch, so after shuffle,
2891 just have them swap positions.
2893 new_pos = region->position();
2895 /* they used to touch, so after shuffle,
2896 make sure they still do. put the earlier
2897 one where the later one will end after
2899 new_pos = (*prev)->position() + region->length();
2902 region->set_position ((*prev)->position());
2903 (*prev)->set_position (new_pos);
2905 /* avoid a full sort */
2907 regions.erase (i); // remove region
2908 regions.insert (prev, region); // insert region before prev
2924 notify_contents_changed();
2930 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2932 RegionReadLock rlock (const_cast<Playlist*> (this));
2934 if (regions.size() > 1) {
2942 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2944 ripple_locked (at, distance, exclude);
2948 Playlist::update_after_tempo_map_change ()
2950 RegionWriteLock rlock (const_cast<Playlist*> (this));
2951 RegionList copy (regions.rlist());
2955 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2956 (*i)->update_after_tempo_map_change ();
2958 /* possibly causes a contents changed notification (flush_notifications()) */
2963 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2965 RegionReadLock rl (this);
2966 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2972 Playlist::has_region_at (framepos_t const p) const
2974 RegionReadLock (const_cast<Playlist *> (this));
2976 RegionList::const_iterator i = regions.begin ();
2977 while (i != regions.end() && !(*i)->covers (p)) {
2981 return (i != regions.end());
2984 /** Look from a session frame time and find the start time of the next region
2985 * which is on the top layer of this playlist.
2986 * @param t Time to look from.
2987 * @return Position of next top-layered region, or max_framepos if there isn't one.
2990 Playlist::find_next_top_layer_position (framepos_t t) const
2992 RegionReadLock rlock (const_cast<Playlist *> (this));
2994 layer_t const top = top_layer ();
2996 RegionList copy = regions.rlist ();
2997 copy.sort (RegionSortByPosition ());
2999 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3000 if ((*i)->position() >= t && (*i)->layer() == top) {
3001 return (*i)->position();
3005 return max_framepos;
3008 boost::shared_ptr<Region>
3009 Playlist::combine (const RegionList& r)
3012 uint32_t channels = 0;
3014 framepos_t earliest_position = max_framepos;
3015 vector<TwoRegions> old_and_new_regions;
3016 vector<boost::shared_ptr<Region> > originals;
3017 vector<boost::shared_ptr<Region> > copies;
3020 uint32_t max_level = 0;
3022 /* find the maximum depth of all the regions we're combining */
3024 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3025 max_level = max (max_level, (*i)->max_source_level());
3028 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3029 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3031 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3033 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3034 earliest_position = min (earliest_position, (*i)->position());
3037 /* enable this so that we do not try to create xfades etc. as we add
3041 pl->in_partition = true;
3043 /* sort by position then layer.
3044 * route_time_axis passes 'selected_regions' - which is not sorted.
3045 * here we need the top-most first, then every layer's region sorted by position.
3047 RegionList sorted(r);
3048 sorted.sort(RegionSortByLayerAndPosition());
3050 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3052 /* copy the region */
3054 boost::shared_ptr<Region> original_region = (*i);
3055 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3057 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3058 originals.push_back (original_region);
3059 copies.push_back (copied_region);
3061 RegionFactory::add_compound_association (original_region, copied_region);
3063 /* make position relative to zero */
3065 pl->add_region (copied_region, original_region->position() - earliest_position);
3066 copied_region->set_layer (original_region->layer ());
3068 /* use the maximum number of channels for any region */
3070 channels = max (channels, original_region->n_channels());
3072 /* it will go above the layer of the highest existing region */
3074 layer = max (layer, original_region->layer());
3077 pl->in_partition = false;
3079 pre_combine (copies);
3081 /* now create a new PlaylistSource for each channel in the new playlist */
3084 pair<framepos_t,framepos_t> extent = pl->get_extent();
3086 for (uint32_t chn = 0; chn < channels; ++chn) {
3087 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3091 /* now a new whole-file region using the list of sources */
3093 plist.add (Properties::start, 0);
3094 plist.add (Properties::length, extent.second);
3095 plist.add (Properties::name, parent_name);
3096 plist.add (Properties::whole_file, true);
3098 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3100 /* now the non-whole-file region that we will actually use in the
3105 plist.add (Properties::start, 0);
3106 plist.add (Properties::length, extent.second);
3107 plist.add (Properties::name, child_name);
3108 plist.add (Properties::layer, layer+1);
3110 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3112 /* remove all the selected regions from the current playlist
3117 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3121 /* do type-specific stuff with the originals and the new compound
3125 post_combine (originals, compound_region);
3127 /* add the new region at the right location */
3129 add_region (compound_region, earliest_position);
3135 return compound_region;
3139 Playlist::uncombine (boost::shared_ptr<Region> target)
3141 boost::shared_ptr<PlaylistSource> pls;
3142 boost::shared_ptr<const Playlist> pl;
3143 vector<boost::shared_ptr<Region> > originals;
3144 vector<TwoRegions> old_and_new_regions;
3146 // (1) check that its really a compound region
3148 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3152 pl = pls->playlist();
3154 framepos_t adjusted_start = 0; // gcc isn't smart enough
3155 framepos_t adjusted_end = 0; // gcc isn't smart enough
3157 /* the leftmost (earliest) edge of the compound region
3158 starts at zero in its source, or larger if it
3159 has been trimmed or content-scrolled.
3161 the rightmost (latest) edge of the compound region
3162 relative to its source is the starting point plus
3163 the length of the region.
3166 // (2) get all the original regions
3168 const RegionList& rl (pl->region_list_property().rlist());
3169 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3170 frameoffset_t move_offset = 0;
3172 /* there are two possibilities here:
3173 1) the playlist that the playlist source was based on
3174 is us, so just add the originals (which belonged to
3175 us anyway) back in the right place.
3177 2) the playlist that the playlist source was based on
3178 is NOT us, so we need to make copies of each of
3179 the original regions that we find, and add them
3182 bool same_playlist = (pls->original() == id());
3184 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3186 boost::shared_ptr<Region> current (*i);
3188 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3190 if (ca == cassocs.end()) {
3194 boost::shared_ptr<Region> original (ca->second);
3196 bool modified_region;
3198 if (i == rl.begin()) {
3199 move_offset = (target->position() - original->position()) - target->start();
3200 adjusted_start = original->position() + target->start();
3201 adjusted_end = adjusted_start + target->length();
3204 if (!same_playlist) {
3205 framepos_t pos = original->position();
3206 /* make a copy, but don't announce it */
3207 original = RegionFactory::create (original, false);
3208 /* the pure copy constructor resets position() to zero,
3211 original->set_position (pos);
3214 /* check to see how the original region (in the
3215 * playlist before compounding occurred) overlaps
3216 * with the new state of the compound region.
3219 original->clear_changes ();
3220 modified_region = false;
3222 switch (original->coverage (adjusted_start, adjusted_end)) {
3223 case Evoral::OverlapNone:
3224 /* original region does not cover any part
3225 of the current state of the compound region
3229 case Evoral::OverlapInternal:
3230 /* overlap is just a small piece inside the
3231 * original so trim both ends
3233 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3234 modified_region = true;
3237 case Evoral::OverlapExternal:
3238 /* overlap fully covers original, so leave it
3243 case Evoral::OverlapEnd:
3244 /* overlap starts within but covers end,
3245 so trim the front of the region
3247 original->trim_front (adjusted_start);
3248 modified_region = true;
3251 case Evoral::OverlapStart:
3252 /* overlap covers start but ends within, so
3253 * trim the end of the region.
3255 original->trim_end (adjusted_end);
3256 modified_region = true;
3261 /* fix the position to match any movement of the compound region.
3263 original->set_position (original->position() + move_offset);
3264 modified_region = true;
3267 if (modified_region) {
3268 _session.add_command (new StatefulDiffCommand (original));
3271 /* and add to the list of regions waiting to be
3275 originals.push_back (original);
3276 old_and_new_regions.push_back (TwoRegions (*i, original));
3279 pre_uncombine (originals, target);
3281 in_partition = true;
3284 // (3) remove the compound region
3286 remove_region (target);
3288 // (4) add the constituent regions
3290 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3291 add_region ((*i), (*i)->position());
3292 set_layer((*i), (*i)->layer());
3293 if (!RegionFactory::region_by_id((*i)->id())) {
3294 RegionFactory::map_add(*i);
3298 in_partition = false;
3303 Playlist::fade_range (list<AudioRange>& ranges)
3305 RegionReadLock rlock (this);
3306 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3307 list<AudioRange>::iterator tmpr = r;
3309 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3310 RegionList::const_iterator tmpi = i;
3312 (*i)->fade_range ((*r).start, (*r).end);
3320 Playlist::max_source_level () const
3322 RegionReadLock rlock (const_cast<Playlist *> (this));
3325 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3326 lvl = max (lvl, (*i)->max_source_level());
3333 Playlist::set_orig_track_id (const PBD::ID& id)
3335 if (shared_with(id)) {
3336 // Swap 'shared_id' / origin_track_id
3338 share_with (_orig_track_id);
3340 _orig_track_id = id;
3344 Playlist::share_with (const PBD::ID& id)
3346 if (!shared_with(id)) {
3347 _shared_with_ids.push_back (id);
3352 Playlist::unshare_with (const PBD::ID& id)
3354 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3355 while (it != _shared_with_ids.end()) {
3357 _shared_with_ids.erase (it);
3365 Playlist::shared_with (const PBD::ID& id) const
3367 bool shared = false;
3368 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3369 while (it != _shared_with_ids.end() && !shared) {
3380 Playlist::reset_shares ()
3382 _shared_with_ids.clear();
3385 /** Take a list of ranges, coalesce any that can be coalesced, then call
3386 * check_crossfades for each one.
3389 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3391 /* XXX: it's a shame that this coalesce algorithm also exists in
3392 TimeSelection::consolidate().
3395 /* XXX: xfade: this is implemented in Evoral::RangeList */
3398 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3399 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3405 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3406 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3407 i->from = min (i->from, j->from);
3408 i->to = max (i->to, j->to);
3417 Playlist::set_capture_insertion_in_progress (bool yn)
3419 _capture_insertion_underway = yn;