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/types_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/types_convert.h"
42 #include "ardour/session_playlists.h"
43 #include "ardour/source_factory.h"
48 using namespace ARDOUR;
52 namespace Properties {
53 PBD::PropertyDescriptor<bool> regions;
57 struct ShowMeTheList {
58 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
60 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
62 boost::shared_ptr<Playlist> playlist;
69 Playlist::make_property_quarks ()
71 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
72 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
73 Properties::regions.property_id));
76 RegionListProperty::RegionListProperty (Playlist& pl)
77 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
83 RegionListProperty::RegionListProperty (RegionListProperty const & p)
84 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
85 , _playlist (p._playlist)
91 RegionListProperty::clone () const
93 return new RegionListProperty (*this);
97 RegionListProperty::create () const
99 return new RegionListProperty (_playlist);
103 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
105 /* All regions (even those which are deleted) have their state saved by other
106 code, so we can just store ID here.
109 node.set_property ("id", region->id());
112 boost::shared_ptr<Region>
113 RegionListProperty::get_content_from_xml (XMLNode const & node) const
116 if (!node.get_property ("id", id)) {
120 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
123 ret = RegionFactory::region_by_id (id);
129 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
130 : SessionObject(sess, nom)
135 first_set_state = false;
140 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
141 : SessionObject(sess, "unnamed playlist")
146 XMLProperty const * prop = node.property("type");
147 assert(!prop || DataType(prop->value()) == _type);
151 _name = "unnamed"; /* reset by set_state */
154 /* set state called by derived class */
157 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
158 : SessionObject(other->_session, namestr)
160 , _type(other->_type)
161 , _orig_track_id (other->_orig_track_id)
162 , _shared_with_ids (other->_shared_with_ids)
167 other->copy_regions (tmp);
171 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
172 add_region_internal( (*x), (*x)->position());
177 _splicing = other->_splicing;
178 _rippling = other->_rippling;
179 _nudging = other->_nudging;
180 _edit_mode = other->_edit_mode;
183 first_set_state = false;
185 in_partition = false;
187 _frozen = other->_frozen;
190 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
191 : SessionObject(other->_session, str)
193 , _type(other->_type)
194 , _orig_track_id (other->_orig_track_id)
195 , _shared_with_ids (other->_shared_with_ids)
197 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
199 framepos_t end = start + cnt - 1;
205 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
207 boost::shared_ptr<Region> region;
208 boost::shared_ptr<Region> new_region;
209 frameoffset_t offset = 0;
210 framepos_t position = 0;
213 Evoral::OverlapType overlap;
217 overlap = region->coverage (start, end);
220 case Evoral::OverlapNone:
223 case Evoral::OverlapInternal:
224 offset = start - region->position();
229 case Evoral::OverlapStart:
231 position = region->position() - start;
232 len = end - region->position();
235 case Evoral::OverlapEnd:
236 offset = start - region->position();
238 len = region->length() - offset;
241 case Evoral::OverlapExternal:
243 position = region->position() - start;
244 len = region->length();
248 RegionFactory::region_name (new_name, region->name(), false);
252 plist.add (Properties::start, region->start() + offset);
253 plist.add (Properties::length, len);
254 plist.add (Properties::name, new_name);
255 plist.add (Properties::layer, region->layer());
256 plist.add (Properties::layering_index, region->layering_index());
258 new_region = RegionFactory::create (region, plist);
260 add_region_internal (new_region, position);
263 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
264 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
265 _end_space = cnt - (get_extent().second - get_extent().first);
268 first_set_state = false;
275 InUse (true); /* EMIT SIGNAL */
286 InUse (false); /* EMIT SIGNAL */
291 Playlist::copy_regions (RegionList& newlist) const
293 RegionReadLock rlock (const_cast<Playlist *> (this));
295 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
296 newlist.push_back (RegionFactory::create (*i, true));
301 Playlist::init (bool hide)
303 add_property (regions);
304 _xml_node_name = X_("Playlist");
306 g_atomic_int_set (&block_notifications, 0);
307 g_atomic_int_set (&ignore_state_changes, 0);
308 pending_contents_change = false;
309 pending_layering = false;
310 first_set_state = true;
319 _edit_mode = Config->get_edit_mode();
321 in_partition = false;
324 _capture_insertion_underway = false;
328 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
329 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
331 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
334 Playlist::~Playlist ()
336 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
339 RegionReadLock rl (this);
341 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
342 (*i)->set_playlist (boost::shared_ptr<Playlist>());
346 /* GoingAway must be emitted by derived classes */
350 Playlist::_set_sort_id ()
353 Playlists are given names like <track name>.<id>
354 or <track name>.<edit group name>.<id> where id
355 is an integer. We extract the id and sort by that.
358 size_t dot_position = _name.val().find_last_of(".");
360 if (dot_position == string::npos) {
363 string t = _name.val().substr(dot_position + 1);
366 _sort_id = boost::lexical_cast<int>(t);
369 catch (boost::bad_lexical_cast e) {
376 Playlist::set_name (const string& str)
378 /* in a typical situation, a playlist is being used
379 by one diskstream and also is referenced by the
380 Session. if there are more references than that,
381 then don't change the name.
388 bool ret = SessionObject::set_name(str);
395 /***********************************************************************
396 CHANGE NOTIFICATION HANDLING
398 Notifications must be delayed till the region_lock is released. This
399 is necessary because handlers for the signals may need to acquire
400 the lock (e.g. to read from the playlist).
401 ***********************************************************************/
404 Playlist::begin_undo ()
411 Playlist::end_undo ()
420 delay_notifications ();
421 g_atomic_int_inc (&ignore_state_changes);
424 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
426 Playlist::thaw (bool from_undo)
428 g_atomic_int_dec_and_test (&ignore_state_changes);
429 release_notifications (from_undo);
434 Playlist::delay_notifications ()
436 g_atomic_int_inc (&block_notifications);
439 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
441 Playlist::release_notifications (bool from_undo)
443 if (g_atomic_int_dec_and_test (&block_notifications)) {
444 flush_notifications (from_undo);
449 Playlist::notify_contents_changed ()
451 if (holding_state ()) {
452 pending_contents_change = true;
454 pending_contents_change = false;
455 ContentsChanged(); /* EMIT SIGNAL */
460 Playlist::notify_layering_changed ()
462 if (holding_state ()) {
463 pending_layering = true;
465 pending_layering = false;
466 LayeringChanged(); /* EMIT SIGNAL */
471 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
473 if (holding_state ()) {
474 pending_removes.insert (r);
475 pending_contents_change = true;
477 /* this might not be true, but we have to act
478 as though it could be.
480 pending_contents_change = false;
481 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
482 ContentsChanged (); /* EMIT SIGNAL */
487 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
489 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
491 if (holding_state ()) {
493 pending_range_moves.push_back (move);
497 list< Evoral::RangeMove<framepos_t> > m;
499 RangesMoved (m, false);
505 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
507 if (r->position() >= r->last_position()) {
508 /* trimmed shorter */
512 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
514 if (holding_state ()) {
516 pending_region_extensions.push_back (extra);
520 list<Evoral::Range<framepos_t> > r;
528 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
530 if (r->length() < r->last_length()) {
531 /* trimmed shorter */
534 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
536 if (holding_state ()) {
538 pending_region_extensions.push_back (extra);
542 list<Evoral::Range<framepos_t> > r;
550 Playlist::notify_region_added (boost::shared_ptr<Region> r)
552 /* the length change might not be true, but we have to act
553 as though it could be.
556 if (holding_state()) {
557 pending_adds.insert (r);
558 pending_contents_change = true;
561 pending_contents_change = false;
562 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
563 ContentsChanged (); /* EMIT SIGNAL */
568 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
570 Playlist::flush_notifications (bool from_undo)
572 set<boost::shared_ptr<Region> >::iterator s;
573 bool regions_changed = false;
581 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
582 regions_changed = true;
585 /* XXX: it'd be nice if we could use pending_bounds for
586 RegionsExtended and RegionsMoved.
589 /* we have no idea what order the regions ended up in pending
590 bounds (it could be based on selection order, for example).
591 so, to preserve layering in the "most recently moved is higher"
592 model, sort them by existing layer, then timestamp them.
595 // RegionSortByLayer cmp;
596 // pending_bounds.sort (cmp);
598 list<Evoral::Range<framepos_t> > crossfade_ranges;
600 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
601 crossfade_ranges.push_back ((*r)->last_range ());
602 crossfade_ranges.push_back ((*r)->range ());
605 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
606 crossfade_ranges.push_back ((*s)->range ());
607 remove_dependents (*s);
608 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
611 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
612 crossfade_ranges.push_back ((*s)->range ());
613 /* don't emit RegionAdded signal until relayering is done,
614 so that the region is fully setup by the time
615 anyone hears that its been added
619 /* notify about contents/region changes first so that layering changes
620 * in a UI will take place on the new contents.
623 if (regions_changed || pending_contents_change) {
624 pending_layering = true;
625 ContentsChanged (); /* EMIT SIGNAL */
628 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
629 (*s)->clear_changes ();
630 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
633 if ((regions_changed && !in_set_state) || pending_layering) {
637 coalesce_and_check_crossfades (crossfade_ranges);
639 if (!pending_range_moves.empty ()) {
640 /* We don't need to check crossfades for these as pending_bounds has
643 RangesMoved (pending_range_moves, from_undo);
646 if (!pending_region_extensions.empty ()) {
647 RegionsExtended (pending_region_extensions);
656 Playlist::clear_pending ()
658 pending_adds.clear ();
659 pending_removes.clear ();
660 pending_bounds.clear ();
661 pending_range_moves.clear ();
662 pending_region_extensions.clear ();
663 pending_contents_change = false;
664 pending_layering = false;
667 /*************************************************************
669 *************************************************************/
671 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
673 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)
675 RegionWriteLock rlock (this);
676 times = fabs (times);
678 int itimes = (int) floor (times);
680 framepos_t pos = position;
682 if (times == 1 && auto_partition){
684 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
685 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
686 (*i)->resume_property_changes ();
687 _session.add_command (new StatefulDiffCommand (*i));
692 add_region_internal (region, pos, sub_num, quarter_note, for_music);
693 set_layer (region, DBL_MAX);
694 pos += region->length();
698 /* note that itimes can be zero if we being asked to just
699 insert a single fraction of the region.
702 for (int i = 0; i < itimes; ++i) {
703 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
704 add_region_internal (copy, pos, sub_num);
705 set_layer (copy, DBL_MAX);
706 pos += region->length();
709 framecnt_t length = 0;
711 if (floor (times) != times) {
712 length = (framecnt_t) floor (region->length() * (times - floor (times)));
714 RegionFactory::region_name (name, region->name(), false);
719 plist.add (Properties::start, region->start());
720 plist.add (Properties::length, length);
721 plist.add (Properties::name, name);
722 plist.add (Properties::layer, region->layer());
724 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
725 add_region_internal (sub, pos, sub_num);
726 set_layer (sub, DBL_MAX);
730 possibly_splice_unlocked (position, (pos + length) - position, region);
734 Playlist::set_region_ownership ()
736 RegionWriteLock rl (this);
737 RegionList::iterator i;
738 boost::weak_ptr<Playlist> pl (shared_from_this());
740 for (i = regions.begin(); i != regions.end(); ++i) {
741 (*i)->set_playlist (pl);
746 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, int32_t sub_num, double quarter_note, bool for_music)
748 if (region->data_type() != _type) {
752 RegionSortByPosition cmp;
754 if (!first_set_state) {
755 boost::shared_ptr<Playlist> foo (shared_from_this());
756 region->set_playlist (boost::weak_ptr<Playlist>(foo));
759 region->set_position_music (quarter_note);
761 region->set_position (position, sub_num);
764 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
765 all_regions.insert (region);
767 possibly_splice_unlocked (position, region->length(), region);
769 if (!holding_state ()) {
770 /* layers get assigned from XML state, and are not reset during undo/redo */
774 /* we need to notify the existence of new region before checking dependents. Ick. */
776 notify_region_added (region);
778 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
779 region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
785 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
787 RegionWriteLock rlock (this);
789 bool old_sp = _splicing;
792 remove_region_internal (old);
793 add_region_internal (newr, pos);
794 set_layer (newr, old->layer ());
798 possibly_splice_unlocked (pos, old->length() - newr->length());
802 Playlist::remove_region (boost::shared_ptr<Region> region)
804 RegionWriteLock rlock (this);
805 remove_region_internal (region);
809 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
811 RegionList::iterator i;
815 region->set_playlist (boost::weak_ptr<Playlist>());
818 /* XXX should probably freeze here .... */
820 for (i = regions.begin(); i != regions.end(); ++i) {
823 framepos_t pos = (*i)->position();
824 framecnt_t distance = (*i)->length();
828 possibly_splice_unlocked (pos, -distance);
830 if (!holding_state ()) {
832 remove_dependents (region);
835 notify_region_removed (region);
844 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
846 if (Config->get_use_overlap_equivalency()) {
847 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
848 if ((*i)->overlap_equivalent (other)) {
849 results.push_back (*i);
853 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
854 if ((*i)->equivalent (other)) {
855 results.push_back (*i);
862 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
864 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
866 if ((*i) && (*i)->region_list_equivalent (other)) {
867 results.push_back (*i);
873 Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
875 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
877 if ((*i) && (*i)->any_source_equivalent (other)) {
878 results.push_back (*i);
884 Playlist::partition (framepos_t start, framepos_t end, bool cut)
888 RegionWriteLock lock(this);
889 partition_internal (start, end, cut, thawlist);
892 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
893 (*i)->resume_property_changes ();
897 /** Go through each region on the playlist and cut them at start and end, removing the section between
898 * start and end if cutting == true. Regions that lie entirely within start and end are always
903 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
905 RegionList new_regions;
909 boost::shared_ptr<Region> region;
910 boost::shared_ptr<Region> current;
912 RegionList::iterator tmp;
913 Evoral::OverlapType overlap;
914 framepos_t pos1, pos2, pos3, pos4;
918 /* need to work from a copy, because otherwise the regions we add during the process
919 get operated on as well.
922 RegionList copy = regions.rlist();
924 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
931 if (current->first_frame() >= start && current->last_frame() < end) {
934 remove_region_internal (current);
940 /* coverage will return OverlapStart if the start coincides
941 with the end point. we do not partition such a region,
942 so catch this special case.
945 if (current->first_frame() >= end) {
949 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
953 pos1 = current->position();
956 pos4 = current->last_frame();
958 if (overlap == Evoral::OverlapInternal) {
959 /* split: we need 3 new regions, the front, middle and end.
960 cut: we need 2 regions, the front and end.
965 ---------------*************************------------
968 ---------------*****++++++++++++++++====------------
970 ---------------*****----------------====------------
975 /* "middle" ++++++ */
977 RegionFactory::region_name (new_name, current->name(), false);
981 plist.add (Properties::start, current->start() + (pos2 - pos1));
982 plist.add (Properties::length, pos3 - pos2);
983 plist.add (Properties::name, new_name);
984 plist.add (Properties::layer, current->layer ());
985 plist.add (Properties::layering_index, current->layering_index ());
986 plist.add (Properties::automatic, true);
987 plist.add (Properties::left_of_split, true);
988 plist.add (Properties::right_of_split, true);
990 region = RegionFactory::create (current, plist);
991 add_region_internal (region, start);
992 new_regions.push_back (region);
997 RegionFactory::region_name (new_name, current->name(), false);
1001 plist.add (Properties::start, current->start() + (pos3 - pos1));
1002 plist.add (Properties::length, pos4 - pos3);
1003 plist.add (Properties::name, new_name);
1004 plist.add (Properties::layer, current->layer ());
1005 plist.add (Properties::layering_index, current->layering_index ());
1006 plist.add (Properties::automatic, true);
1007 plist.add (Properties::right_of_split, true);
1009 region = RegionFactory::create (current, plist);
1011 add_region_internal (region, end);
1012 new_regions.push_back (region);
1016 current->clear_changes ();
1017 current->suspend_property_changes ();
1018 thawlist.push_back (current);
1019 current->cut_end (pos2 - 1);
1021 } else if (overlap == Evoral::OverlapEnd) {
1025 ---------------*************************------------
1028 ---------------**************+++++++++++------------
1030 ---------------**************-----------------------
1037 RegionFactory::region_name (new_name, current->name(), false);
1041 plist.add (Properties::start, current->start() + (pos2 - pos1));
1042 plist.add (Properties::length, pos4 - pos2);
1043 plist.add (Properties::name, new_name);
1044 plist.add (Properties::layer, current->layer ());
1045 plist.add (Properties::layering_index, current->layering_index ());
1046 plist.add (Properties::automatic, true);
1047 plist.add (Properties::left_of_split, true);
1049 region = RegionFactory::create (current, plist);
1051 add_region_internal (region, start);
1052 new_regions.push_back (region);
1057 current->clear_changes ();
1058 current->suspend_property_changes ();
1059 thawlist.push_back (current);
1060 current->cut_end (pos2 - 1);
1062 } else if (overlap == Evoral::OverlapStart) {
1064 /* split: we need 2 regions: the front and the end.
1065 cut: just trim current to skip the cut area
1070 ---------------*************************------------
1074 ---------------****+++++++++++++++++++++------------
1076 -------------------*********************------------
1082 RegionFactory::region_name (new_name, current->name(), false);
1086 plist.add (Properties::start, current->start());
1087 plist.add (Properties::length, pos3 - pos1);
1088 plist.add (Properties::name, new_name);
1089 plist.add (Properties::layer, current->layer ());
1090 plist.add (Properties::layering_index, current->layering_index ());
1091 plist.add (Properties::automatic, true);
1092 plist.add (Properties::right_of_split, true);
1094 region = RegionFactory::create (current, plist);
1096 add_region_internal (region, pos1);
1097 new_regions.push_back (region);
1102 current->clear_changes ();
1103 current->suspend_property_changes ();
1104 thawlist.push_back (current);
1105 current->trim_front (pos3);
1106 } else if (overlap == Evoral::OverlapExternal) {
1108 /* split: no split required.
1109 cut: remove the region.
1114 ---------------*************************------------
1118 ---------------*************************------------
1120 ----------------------------------------------------
1125 remove_region_internal (current);
1128 new_regions.push_back (current);
1132 in_partition = false;
1135 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1136 framepos_t wanted_length = end-start;
1137 _end_space = wanted_length - _get_extent().second - _get_extent().first;
1140 boost::shared_ptr<Playlist>
1141 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1143 boost::shared_ptr<Playlist> ret;
1144 boost::shared_ptr<Playlist> pl;
1147 if (ranges.empty()) {
1148 return boost::shared_ptr<Playlist>();
1151 start = ranges.front().start;
1153 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1155 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1157 if (i == ranges.begin()) {
1161 /* paste the next section into the nascent playlist,
1162 offset to reflect the start of the first range we
1166 ret->paste (pl, (*i).start - start, 1.0f, 0);
1173 boost::shared_ptr<Playlist>
1174 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1176 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1177 return cut_copy (pmf, ranges, result_is_hidden);
1180 boost::shared_ptr<Playlist>
1181 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1183 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1184 return cut_copy (pmf, ranges, result_is_hidden);
1187 boost::shared_ptr<Playlist>
1188 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1190 boost::shared_ptr<Playlist> the_copy;
1191 RegionList thawlist;
1194 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1195 string new_name = _name;
1199 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1200 return boost::shared_ptr<Playlist>();
1204 RegionWriteLock rlock (this);
1205 partition_internal (start, start+cnt-1, true, thawlist);
1208 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1209 (*i)->resume_property_changes();
1215 boost::shared_ptr<Playlist>
1216 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1220 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1221 string new_name = _name;
1225 // 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... )
1227 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1231 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1233 times = fabs (times);
1236 RegionReadLock rl2 (other.get());
1238 int itimes = (int) floor (times);
1239 framepos_t pos = position;
1240 framecnt_t const shift = other->_get_extent().second;
1241 layer_t top = top_layer ();
1244 RegionWriteLock rl1 (this);
1246 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1247 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1249 /* put these new regions on top of all existing ones, but preserve
1250 the ordering they had in the original playlist.
1253 add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1254 set_layer (copy_of_region, copy_of_region->layer() + top);
1266 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1268 duplicate(region, position, region->length(), times);
1271 /** @param gap from the beginning of the region to the next beginning */
1273 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1275 times = fabs (times);
1277 RegionWriteLock rl (this);
1278 int itimes = (int) floor (times);
1281 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1282 add_region_internal (copy, position);
1283 set_layer (copy, DBL_MAX);
1287 if (floor (times) != times) {
1288 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1290 RegionFactory::region_name (name, region->name(), false);
1295 plist.add (Properties::start, region->start());
1296 plist.add (Properties::length, length);
1297 plist.add (Properties::name, name);
1299 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1300 add_region_internal (sub, position);
1301 set_layer (sub, DBL_MAX);
1306 /** @param gap from the beginning of the region to the next beginning */
1307 /** @param end the first frame that does _not_ contain a duplicated frame */
1309 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1311 RegionWriteLock rl (this);
1313 while (position + region->length() - 1 < end) {
1314 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1315 add_region_internal (copy, position);
1316 set_layer (copy, DBL_MAX);
1320 if (position < end) {
1321 framecnt_t length = min (region->length(), end - position);
1323 RegionFactory::region_name (name, region->name(), false);
1328 plist.add (Properties::start, region->start());
1329 plist.add (Properties::length, length);
1330 plist.add (Properties::name, name);
1332 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1333 add_region_internal (sub, position);
1334 set_layer (sub, DBL_MAX);
1340 Playlist::duplicate_range (AudioRange& range, float times)
1342 boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1343 framecnt_t offset = range.end - range.start;
1344 paste (pl, range.start + offset, times, 0);
1348 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1350 if (ranges.empty()) {
1354 framepos_t min_pos = max_framepos;
1355 framepos_t max_pos = 0;
1357 for (std::list<AudioRange>::const_iterator i = ranges.begin();
1360 min_pos = min (min_pos, (*i).start);
1361 max_pos = max (max_pos, (*i).end);
1364 framecnt_t offset = max_pos - min_pos;
1367 int itimes = (int) floor (times);
1369 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1370 boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1371 paste (pl, (*i).start + (offset * count), 1.0f, 0);
1378 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1380 RegionWriteLock rlock (this);
1381 RegionList copy (regions.rlist());
1384 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1386 if ((*r)->last_frame() < at) {
1391 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1392 /* intersected region */
1393 if (!move_intersected) {
1398 /* do not move regions glued to music time - that
1399 has to be done separately.
1402 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1403 fixup.push_back (*r);
1407 (*r)->set_position ((*r)->position() + distance);
1410 /* XXX: may not be necessary; Region::post_set should do this, I think */
1411 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1412 (*r)->recompute_position_from_lock_style (0);
1417 Playlist::split (const MusicFrame& at)
1419 RegionWriteLock rlock (this);
1420 RegionList copy (regions.rlist());
1422 /* use a copy since this operation can modify the region list
1425 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1426 _split_region (*r, at);
1431 Playlist::split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1433 RegionWriteLock rl (this);
1434 _split_region (region, playlist_position);
1438 Playlist::_split_region (boost::shared_ptr<Region> region, const MusicFrame& playlist_position)
1440 if (!region->covers (playlist_position.frame)) {
1444 if (region->position() == playlist_position.frame ||
1445 region->last_frame() == playlist_position.frame) {
1449 boost::shared_ptr<Region> left;
1450 boost::shared_ptr<Region> right;
1452 MusicFrame before (playlist_position.frame - region->position(), playlist_position.division);
1453 MusicFrame after (region->length() - before.frame, 0);
1457 /* split doesn't change anything about length, so don't try to splice */
1459 bool old_sp = _splicing;
1462 RegionFactory::region_name (before_name, region->name(), false);
1467 plist.add (Properties::length, before.frame);
1468 plist.add (Properties::name, before_name);
1469 plist.add (Properties::left_of_split, true);
1470 plist.add (Properties::layering_index, region->layering_index ());
1471 plist.add (Properties::layer, region->layer ());
1473 /* note: we must use the version of ::create with an offset here,
1474 since it supplies that offset to the Region constructor, which
1475 is necessary to get audio region gain envelopes right.
1477 left = RegionFactory::create (region, MusicFrame (0, 0), plist, true);
1480 RegionFactory::region_name (after_name, region->name(), false);
1485 plist.add (Properties::length, after.frame);
1486 plist.add (Properties::name, after_name);
1487 plist.add (Properties::right_of_split, true);
1488 plist.add (Properties::layering_index, region->layering_index ());
1489 plist.add (Properties::layer, region->layer ());
1491 /* same note as above */
1492 right = RegionFactory::create (region, before, plist, true);
1495 add_region_internal (left, region->position(), 0);
1496 add_region_internal (right, region->position() + before.frame, before.division);
1498 remove_region_internal (region);
1504 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1506 if (_splicing || in_set_state) {
1507 /* don't respond to splicing moves or state setting */
1511 if (_edit_mode == Splice) {
1512 splice_locked (at, distance, exclude);
1517 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1519 if (_splicing || in_set_state) {
1520 /* don't respond to splicing moves or state setting */
1524 if (_edit_mode == Splice) {
1525 splice_unlocked (at, distance, exclude);
1530 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1533 RegionWriteLock rl (this);
1534 core_splice (at, distance, exclude);
1539 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1541 core_splice (at, distance, exclude);
1545 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1549 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1551 if (exclude && (*i) == exclude) {
1555 if ((*i)->position() >= at) {
1556 framepos_t new_pos = (*i)->position() + distance;
1559 } else if (new_pos >= max_framepos - (*i)->length()) {
1560 new_pos = max_framepos - (*i)->length();
1563 (*i)->set_position (new_pos);
1569 notify_contents_changed ();
1573 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1576 RegionWriteLock rl (this);
1577 core_ripple (at, distance, exclude);
1582 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1584 core_ripple (at, distance, exclude);
1588 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1590 if (distance == 0) {
1595 RegionListProperty copy = regions;
1596 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1597 assert (i != copy.end());
1600 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1605 if ((*i)->position() >= at) {
1606 framepos_t new_pos = (*i)->position() + distance;
1607 framepos_t limit = max_framepos - (*i)->length();
1610 } else if (new_pos >= limit ) {
1614 (*i)->set_position (new_pos);
1619 notify_contents_changed ();
1624 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1626 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1630 if (what_changed.contains (Properties::position)) {
1632 /* remove it from the list then add it back in
1633 the right place again.
1636 RegionSortByPosition cmp;
1638 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1640 if (i == regions.end()) {
1641 /* the region bounds are being modified but its not currently
1642 in the region list. we will use its bounds correctly when/if
1649 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1652 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1654 frameoffset_t delta = 0;
1656 if (what_changed.contains (Properties::position)) {
1657 delta = region->position() - region->last_position();
1660 if (what_changed.contains (Properties::length)) {
1661 delta += region->length() - region->last_length();
1665 possibly_splice (region->last_position() + region->last_length(), delta, region);
1668 if (holding_state ()) {
1669 pending_bounds.push_back (region);
1671 notify_contents_changed ();
1673 list<Evoral::Range<framepos_t> > xf;
1674 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1675 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1676 coalesce_and_check_crossfades (xf);
1682 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1684 boost::shared_ptr<Region> region (weak_region.lock());
1690 /* this makes a virtual call to the right kind of playlist ... */
1692 region_changed (what_changed, region);
1696 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1698 PropertyChange our_interests;
1699 PropertyChange bounds;
1700 PropertyChange pos_and_length;
1703 if (in_set_state || in_flush) {
1707 our_interests.add (Properties::muted);
1708 our_interests.add (Properties::layer);
1709 our_interests.add (Properties::opaque);
1711 bounds.add (Properties::start);
1712 bounds.add (Properties::position);
1713 bounds.add (Properties::length);
1715 pos_and_length.add (Properties::position);
1716 pos_and_length.add (Properties::length);
1718 if (what_changed.contains (bounds)) {
1719 region_bounds_changed (what_changed, region);
1720 save = !(_splicing || _nudging);
1723 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1724 notify_region_moved (region);
1725 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1726 notify_region_end_trimmed (region);
1727 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1728 notify_region_start_trimmed (region);
1731 /* don't notify about layer changes, since we are the only object that can initiate
1732 them, and we notify in ::relayer()
1735 if (what_changed.contains (our_interests)) {
1739 mark_session_dirty ();
1745 Playlist::drop_regions ()
1747 RegionWriteLock rl (this);
1749 all_regions.clear ();
1753 Playlist::sync_all_regions_with_regions ()
1755 RegionWriteLock rl (this);
1757 all_regions.clear ();
1759 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1760 all_regions.insert (*i);
1765 Playlist::clear (bool with_signals)
1768 RegionWriteLock rl (this);
1770 region_state_changed_connections.drop_connections ();
1771 region_drop_references_connections.drop_connections ();
1773 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1774 pending_removes.insert (*i);
1779 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1780 remove_dependents (*s);
1786 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1787 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1790 pending_removes.clear ();
1791 pending_contents_change = false;
1797 /* *********************************************************************
1799 **********************************************************************/
1801 boost::shared_ptr<RegionList>
1802 Playlist::region_list()
1804 RegionReadLock rlock (this);
1805 boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1810 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1812 RegionReadLock rlock (const_cast<Playlist*>(this));
1814 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1815 (*i)->deep_sources (sources);
1819 boost::shared_ptr<RegionList>
1820 Playlist::regions_at (framepos_t frame)
1822 RegionReadLock rlock (this);
1823 return find_regions_at (frame);
1827 Playlist::count_regions_at (framepos_t frame) const
1829 RegionReadLock rlock (const_cast<Playlist*>(this));
1832 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1833 if ((*i)->covers (frame)) {
1841 boost::shared_ptr<Region>
1842 Playlist::top_region_at (framepos_t frame)
1845 RegionReadLock rlock (this);
1846 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1847 boost::shared_ptr<Region> region;
1849 if (rlist->size()) {
1850 RegionSortByLayer cmp;
1852 region = rlist->back();
1858 boost::shared_ptr<Region>
1859 Playlist::top_unmuted_region_at (framepos_t frame)
1862 RegionReadLock rlock (this);
1863 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1865 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1867 RegionList::iterator tmp = i;
1871 if ((*i)->muted()) {
1878 boost::shared_ptr<Region> region;
1880 if (rlist->size()) {
1881 RegionSortByLayer cmp;
1883 region = rlist->back();
1889 boost::shared_ptr<RegionList>
1890 Playlist::find_regions_at (framepos_t frame)
1892 /* Caller must hold lock */
1894 boost::shared_ptr<RegionList> rlist (new RegionList);
1896 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1897 if ((*i)->covers (frame)) {
1898 rlist->push_back (*i);
1905 boost::shared_ptr<RegionList>
1906 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1908 RegionReadLock rlock (this);
1909 boost::shared_ptr<RegionList> rlist (new RegionList);
1911 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1912 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1913 rlist->push_back (*i);
1920 boost::shared_ptr<RegionList>
1921 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1923 RegionReadLock rlock (this);
1924 boost::shared_ptr<RegionList> rlist (new RegionList);
1926 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1927 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1928 rlist->push_back (*i);
1935 /** @param start Range start.
1936 * @param end Range end.
1937 * @return regions which have some part within this range.
1939 boost::shared_ptr<RegionList>
1940 Playlist::regions_touched (framepos_t start, framepos_t end)
1942 RegionReadLock rlock (this);
1943 return regions_touched_locked (start, end);
1946 boost::shared_ptr<RegionList>
1947 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1949 boost::shared_ptr<RegionList> rlist (new RegionList);
1951 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1952 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1953 rlist->push_back (*i);
1961 Playlist::find_next_transient (framepos_t from, int dir)
1963 RegionReadLock rlock (this);
1964 AnalysisFeatureList points;
1965 AnalysisFeatureList these_points;
1967 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1969 if ((*i)->last_frame() < from) {
1973 if ((*i)->first_frame() > from) {
1978 (*i)->get_transients (these_points);
1980 /* add first frame, just, err, because */
1982 these_points.push_back ((*i)->first_frame());
1984 points.insert (points.end(), these_points.begin(), these_points.end());
1985 these_points.clear ();
1988 if (points.empty()) {
1992 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1993 bool reached = false;
1996 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
2001 if (reached && (*x) > from) {
2006 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2011 if (reached && (*x) < from) {
2020 boost::shared_ptr<Region>
2021 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2023 RegionReadLock rlock (this);
2024 boost::shared_ptr<Region> ret;
2025 framepos_t closest = max_framepos;
2027 bool end_iter = false;
2029 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2033 frameoffset_t distance;
2034 boost::shared_ptr<Region> r = (*i);
2039 pos = r->first_frame ();
2042 pos = r->last_frame ();
2045 pos = r->sync_position ();
2050 case 1: /* forwards */
2053 if ((distance = pos - frame) < closest) {
2062 default: /* backwards */
2065 if ((distance = frame - pos) < closest) {
2081 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2083 RegionReadLock rlock (this);
2085 framepos_t closest = max_framepos;
2086 framepos_t ret = -1;
2090 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2092 boost::shared_ptr<Region> r = (*i);
2093 frameoffset_t distance;
2094 const framepos_t first_frame = r->first_frame();
2095 const framepos_t last_frame = r->last_frame();
2097 if (first_frame > frame) {
2099 distance = first_frame - frame;
2101 if (distance < closest) {
2107 if (last_frame > frame) {
2109 distance = last_frame - frame;
2111 if (distance < closest) {
2120 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2122 boost::shared_ptr<Region> r = (*i);
2123 frameoffset_t distance;
2124 const framepos_t first_frame = r->first_frame();
2125 const framepos_t last_frame = r->last_frame();
2127 if (last_frame < frame) {
2129 distance = frame - last_frame;
2131 if (distance < closest) {
2137 if (first_frame < frame) {
2139 distance = frame - first_frame;
2141 if (distance < closest) {
2153 /***********************************************************************/
2159 Playlist::mark_session_dirty ()
2161 if (!in_set_state && !holding_state ()) {
2162 _session.set_dirty();
2167 Playlist::rdiff (vector<Command*>& cmds) const
2169 RegionReadLock rlock (const_cast<Playlist *> (this));
2170 Stateful::rdiff (cmds);
2174 Playlist::clear_owned_changes ()
2176 RegionReadLock rlock (this);
2177 Stateful::clear_owned_changes ();
2181 Playlist::update (const RegionListProperty::ChangeRecord& change)
2183 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2184 name(), change.added.size(), change.removed.size()));
2187 /* add the added regions */
2188 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2189 add_region_internal ((*i), (*i)->position());
2191 /* remove the removed regions */
2192 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2200 Playlist::set_state (const XMLNode& node, int version)
2204 XMLNodeConstIterator niter;
2205 XMLPropertyConstIterator piter;
2206 boost::shared_ptr<Region> region;
2208 bool seen_region_nodes = false;
2213 if (node.name() != "Playlist") {
2223 if (node.get_property (X_("name"), name)) {
2228 /* XXX legacy session: fix up later */
2229 node.get_property (X_("orig-diskstream-id"), _orig_track_id);
2231 node.get_property (X_("orig-track-id"), _orig_track_id);
2232 node.get_property (X_("frozen"), _frozen);
2234 node.get_property (X_("combine-ops"), _combine_ops);
2237 if (node.get_property (X_("shared-with-ids"), shared_ids)) {
2238 if (!shared_ids.empty()) {
2239 vector<string> result;
2240 ::split (shared_ids, result, ',');
2241 vector<string>::iterator it = result.begin();
2242 for (; it != result.end(); ++it) {
2243 _shared_with_ids.push_back (PBD::ID(*it));
2250 nlist = node.children();
2252 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2256 if (child->name() == "Region") {
2258 seen_region_nodes = true;
2261 if (!child->get_property ("id", id)) {
2262 error << _("region state node has no ID, ignored") << endmsg;
2266 if ((region = region_by_id (id))) {
2268 region->suspend_property_changes ();
2270 if (region->set_state (*child, version)) {
2271 region->resume_property_changes ();
2275 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2276 region->suspend_property_changes ();
2278 error << _("Playlist: cannot create region from XML") << endmsg;
2283 RegionWriteLock rlock (this);
2284 add_region_internal (region, region->position());
2287 region->resume_property_changes ();
2292 if (seen_region_nodes && regions.empty()) {
2297 notify_contents_changed ();
2300 first_set_state = false;
2306 Playlist::get_state()
2308 return state (true);
2312 Playlist::get_template()
2314 return state (false);
2317 /** @param full_state true to include regions in the returned state, otherwise false.
2320 Playlist::state (bool full_state)
2322 XMLNode *node = new XMLNode (X_("Playlist"));
2324 node->set_property (X_("id"), id());
2325 node->set_property (X_("name"), name());
2326 node->set_property (X_("type"), _type);
2327 node->set_property (X_("orig-track-id"), _orig_track_id);
2330 list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2331 for (; it != _shared_with_ids.end(); ++it) {
2332 shared_ids += "," + (*it).to_s();
2334 if (!shared_ids.empty()) {
2335 shared_ids.erase(0,1);
2338 node->set_property (X_("shared-with-ids"), shared_ids);
2339 node->set_property (X_("frozen"), _frozen);
2342 RegionReadLock rlock (this);
2344 node->set_property ("combine-ops", _combine_ops);
2346 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2347 node->add_child_nocopy ((*i)->get_state());
2352 node->add_child_copy (*_extra_xml);
2359 Playlist::empty() const
2361 RegionReadLock rlock (const_cast<Playlist *>(this));
2362 return regions.empty();
2366 Playlist::n_regions() const
2368 RegionReadLock rlock (const_cast<Playlist *>(this));
2369 return regions.size();
2372 /** @return true if the all_regions list is empty, ie this playlist
2373 * has never had a region added to it.
2376 Playlist::all_regions_empty() const
2378 RegionReadLock rl (const_cast<Playlist *> (this));
2379 return all_regions.empty();
2382 pair<framepos_t, framepos_t>
2383 Playlist::get_extent () const
2385 RegionReadLock rlock (const_cast<Playlist *>(this));
2386 return _get_extent ();
2389 pair<framepos_t, framepos_t>
2390 Playlist::get_extent_with_endspace () const
2392 pair<framepos_t, framepos_t> l = get_extent();
2393 l.second += _end_space;
2397 pair<framepos_t, framepos_t>
2398 Playlist::_get_extent () const
2400 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2402 if (regions.empty()) {
2407 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2408 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2409 if (e.first < ext.first) {
2410 ext.first = e.first;
2412 if (e.second > ext.second) {
2413 ext.second = e.second;
2421 Playlist::bump_name (string name, Session &session)
2423 string newname = name;
2426 newname = bump_name_once (newname, '.');
2427 } while (session.playlists->by_name (newname)!=NULL);
2434 Playlist::top_layer() const
2436 RegionReadLock rlock (const_cast<Playlist *> (this));
2439 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2440 top = max (top, (*i)->layer());
2446 Playlist::set_edit_mode (EditMode mode)
2451 struct RelayerSort {
2452 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2453 return a->layering_index() < b->layering_index();
2457 /** Set a new layer for a region. This adjusts the layering indices of all
2458 * regions in the playlist to put the specified region in the appropriate
2459 * place. The actual layering will be fixed up when relayer() happens.
2463 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2465 /* Remove the layer we are setting from our region list, and sort it
2466 * using the layer indeces.
2469 RegionList copy = regions.rlist();
2470 copy.remove (region);
2471 copy.sort (RelayerSort ());
2473 /* Put region back in the right place */
2474 RegionList::iterator i = copy.begin();
2475 while (i != copy.end ()) {
2476 if ((*i)->layer() > new_layer) {
2482 copy.insert (i, region);
2484 setup_layering_indices (copy);
2488 Playlist::setup_layering_indices (RegionList const & regions)
2492 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2493 (*k)->set_layering_index (j++);
2497 struct LaterHigherSort {
2498 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2499 return a->position() < b->position();
2503 /** Take the layering indices of each of our regions, compute the layers
2504 * that they should be on, and write the layers back to the regions.
2507 Playlist::relayer ()
2509 /* never compute layers when setting from XML */
2515 /* Build up a new list of regions on each layer, stored in a set of lists
2516 each of which represent some period of time on some layer. The idea
2517 is to avoid having to search the entire region list to establish whether
2518 each region overlaps another */
2520 /* how many pieces to divide this playlist's time up into */
2521 int const divisions = 512;
2523 /* find the start and end positions of the regions on this playlist */
2524 framepos_t start = INT64_MAX;
2526 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2527 start = min (start, (*i)->position());
2528 end = max (end, (*i)->position() + (*i)->length());
2531 /* hence the size of each time division */
2532 double const division_size = (end - start) / double (divisions);
2534 vector<vector<RegionList> > layers;
2535 layers.push_back (vector<RegionList> (divisions));
2537 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2538 RegionList copy = regions.rlist();
2539 switch (Config->get_layer_model()) {
2541 copy.sort (LaterHigherSort ());
2544 copy.sort (RelayerSort ());
2548 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2549 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2550 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2553 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2555 /* find the time divisions that this region covers; if there are no regions on the list,
2556 division_size will equal 0 and in this case we'll just say that
2557 start_division = end_division = 0.
2559 int start_division = 0;
2560 int end_division = 0;
2562 if (division_size > 0) {
2563 start_division = floor ( ((*i)->position() - start) / division_size);
2564 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2565 if (end_division == divisions) {
2570 assert (divisions == 0 || end_division < divisions);
2572 /* find the lowest layer that this region can go on */
2573 size_t j = layers.size();
2575 /* try layer j - 1; it can go on if it overlaps no other region
2576 that is already on that layer
2579 bool overlap = false;
2580 for (int k = start_division; k <= end_division; ++k) {
2581 RegionList::iterator l = layers[j-1][k].begin ();
2582 while (l != layers[j-1][k].end()) {
2583 if ((*l)->overlap_equivalent (*i)) {
2596 /* overlap, so we must use layer j */
2603 if (j == layers.size()) {
2604 /* we need a new layer for this region */
2605 layers.push_back (vector<RegionList> (divisions));
2608 /* put a reference to this region in each of the divisions that it exists in */
2609 for (int k = start_division; k <= end_division; ++k) {
2610 layers[j][k].push_back (*i);
2613 (*i)->set_layer (j);
2616 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2617 relayering because we just removed the only region on the top layer, nothing will
2618 appear to have changed, but the StreamView must still sort itself out. We could
2619 probably keep a note of the top layer last time we relayered, and check that,
2620 but premature optimisation &c...
2622 notify_layering_changed ();
2624 /* This relayer() may have been called as a result of a region removal, in which
2625 case we need to setup layering indices to account for the one that has just
2628 setup_layering_indices (copy);
2632 Playlist::raise_region (boost::shared_ptr<Region> region)
2634 set_layer (region, region->layer() + 1.5);
2639 Playlist::lower_region (boost::shared_ptr<Region> region)
2641 set_layer (region, region->layer() - 1.5);
2646 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2648 set_layer (region, DBL_MAX);
2653 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2655 set_layer (region, -0.5);
2660 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2662 RegionList::iterator i;
2668 RegionWriteLock rlock (const_cast<Playlist *> (this));
2670 for (i = regions.begin(); i != regions.end(); ++i) {
2672 if ((*i)->position() >= start) {
2678 if ((*i)->last_frame() > max_framepos - distance) {
2679 new_pos = max_framepos - (*i)->length();
2681 new_pos = (*i)->position() + distance;
2686 if ((*i)->position() > distance) {
2687 new_pos = (*i)->position() - distance;
2693 (*i)->set_position (new_pos);
2701 notify_contents_changed ();
2707 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2709 RegionReadLock rlock (const_cast<Playlist*> (this));
2711 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2712 /* Note: passing the second argument as false can cause at best
2713 incredibly deep and time-consuming recursion, and at worst
2714 cycles if the user has managed to create cycles of reference
2715 between compound regions. We generally only this during
2716 cleanup, and @param shallow is passed as true.
2718 if ((*r)->uses_source (src, shallow)) {
2727 boost::shared_ptr<Region>
2728 Playlist::find_region (const ID& id) const
2730 RegionReadLock rlock (const_cast<Playlist*> (this));
2732 /* searches all regions currently in use by the playlist */
2734 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2735 if ((*i)->id() == id) {
2740 return boost::shared_ptr<Region> ();
2744 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2746 RegionReadLock rlock (const_cast<Playlist*> (this));
2749 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2755 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2756 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2757 /* check if region is used in a compound */
2758 if (it->second == r) {
2759 /* region is referenced as 'original' of a compound */
2763 if (r->whole_file() && r->max_source_level() > 0) {
2764 /* region itself ia a compound.
2765 * the compound regions are not referenced -> check regions inside compound
2767 const SourceList& sl = r->sources();
2768 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2769 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2771 if (ps->playlist()->region_use_count(it->first)) {
2772 // break out of both loops
2781 boost::shared_ptr<Region>
2782 Playlist::region_by_id (const ID& id) const
2784 /* searches all regions ever added to this playlist */
2786 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2787 if ((*i)->id() == id) {
2791 return boost::shared_ptr<Region> ();
2795 Playlist::dump () const
2797 boost::shared_ptr<Region> r;
2799 cerr << "Playlist \"" << _name << "\" " << endl
2800 << regions.size() << " regions "
2803 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2805 cerr << " " << r->name() << " ["
2806 << r->start() << "+" << r->length()
2816 Playlist::set_frozen (bool yn)
2822 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2826 if (region->locked()) {
2833 RegionWriteLock rlock (const_cast<Playlist*> (this));
2838 RegionList::iterator next;
2840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2841 if ((*i) == region) {
2845 if (next != regions.end()) {
2847 if ((*next)->locked()) {
2853 if ((*next)->position() != region->last_frame() + 1) {
2854 /* they didn't used to touch, so after shuffle,
2855 just have them swap positions.
2857 new_pos = (*next)->position();
2859 /* they used to touch, so after shuffle,
2860 make sure they still do. put the earlier
2861 region where the later one will end after
2864 new_pos = region->position() + (*next)->length();
2867 (*next)->set_position (region->position());
2868 region->set_position (new_pos);
2870 /* avoid a full sort */
2872 regions.erase (i); // removes the region from the list */
2874 regions.insert (next, region); // adds it back after next
2883 RegionList::iterator prev = regions.end();
2885 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2886 if ((*i) == region) {
2888 if (prev != regions.end()) {
2890 if ((*prev)->locked()) {
2895 if (region->position() != (*prev)->last_frame() + 1) {
2896 /* they didn't used to touch, so after shuffle,
2897 just have them swap positions.
2899 new_pos = region->position();
2901 /* they used to touch, so after shuffle,
2902 make sure they still do. put the earlier
2903 one where the later one will end after
2905 new_pos = (*prev)->position() + region->length();
2908 region->set_position ((*prev)->position());
2909 (*prev)->set_position (new_pos);
2911 /* avoid a full sort */
2913 regions.erase (i); // remove region
2914 regions.insert (prev, region); // insert region before prev
2930 notify_contents_changed();
2936 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2938 RegionReadLock rlock (const_cast<Playlist*> (this));
2940 if (regions.size() > 1) {
2948 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2950 ripple_locked (at, distance, exclude);
2954 Playlist::update_after_tempo_map_change ()
2956 RegionWriteLock rlock (const_cast<Playlist*> (this));
2957 RegionList copy (regions.rlist());
2961 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2962 (*i)->update_after_tempo_map_change ();
2964 /* possibly causes a contents changed notification (flush_notifications()) */
2969 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2971 RegionReadLock rl (this);
2972 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2978 Playlist::has_region_at (framepos_t const p) const
2980 RegionReadLock (const_cast<Playlist *> (this));
2982 RegionList::const_iterator i = regions.begin ();
2983 while (i != regions.end() && !(*i)->covers (p)) {
2987 return (i != regions.end());
2990 /** Look from a session frame time and find the start time of the next region
2991 * which is on the top layer of this playlist.
2992 * @param t Time to look from.
2993 * @return Position of next top-layered region, or max_framepos if there isn't one.
2996 Playlist::find_next_top_layer_position (framepos_t t) const
2998 RegionReadLock rlock (const_cast<Playlist *> (this));
3000 layer_t const top = top_layer ();
3002 RegionList copy = regions.rlist ();
3003 copy.sort (RegionSortByPosition ());
3005 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3006 if ((*i)->position() >= t && (*i)->layer() == top) {
3007 return (*i)->position();
3011 return max_framepos;
3014 boost::shared_ptr<Region>
3015 Playlist::combine (const RegionList& r)
3018 uint32_t channels = 0;
3020 framepos_t earliest_position = max_framepos;
3021 vector<TwoRegions> old_and_new_regions;
3022 vector<boost::shared_ptr<Region> > originals;
3023 vector<boost::shared_ptr<Region> > copies;
3026 uint32_t max_level = 0;
3028 /* find the maximum depth of all the regions we're combining */
3030 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3031 max_level = max (max_level, (*i)->max_source_level());
3034 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3035 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3037 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3039 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3040 earliest_position = min (earliest_position, (*i)->position());
3043 /* enable this so that we do not try to create xfades etc. as we add
3047 pl->in_partition = true;
3049 /* sort by position then layer.
3050 * route_time_axis passes 'selected_regions' - which is not sorted.
3051 * here we need the top-most first, then every layer's region sorted by position.
3053 RegionList sorted(r);
3054 sorted.sort(RegionSortByLayerAndPosition());
3056 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3058 /* copy the region */
3060 boost::shared_ptr<Region> original_region = (*i);
3061 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3063 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3064 originals.push_back (original_region);
3065 copies.push_back (copied_region);
3067 RegionFactory::add_compound_association (original_region, copied_region);
3069 /* make position relative to zero */
3071 pl->add_region (copied_region, original_region->position() - earliest_position);
3072 copied_region->set_layer (original_region->layer ());
3074 /* use the maximum number of channels for any region */
3076 channels = max (channels, original_region->n_channels());
3078 /* it will go above the layer of the highest existing region */
3080 layer = max (layer, original_region->layer());
3083 pl->in_partition = false;
3085 pre_combine (copies);
3087 /* now create a new PlaylistSource for each channel in the new playlist */
3090 pair<framepos_t,framepos_t> extent = pl->get_extent();
3092 for (uint32_t chn = 0; chn < channels; ++chn) {
3093 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3097 /* now a new whole-file region using the list of sources */
3099 plist.add (Properties::start, 0);
3100 plist.add (Properties::length, extent.second);
3101 plist.add (Properties::name, parent_name);
3102 plist.add (Properties::whole_file, true);
3104 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3106 /* now the non-whole-file region that we will actually use in the
3111 plist.add (Properties::start, 0);
3112 plist.add (Properties::length, extent.second);
3113 plist.add (Properties::name, child_name);
3114 plist.add (Properties::layer, layer+1);
3116 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3118 /* remove all the selected regions from the current playlist
3123 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3127 /* do type-specific stuff with the originals and the new compound
3131 post_combine (originals, compound_region);
3133 /* add the new region at the right location */
3135 add_region (compound_region, earliest_position);
3141 return compound_region;
3145 Playlist::uncombine (boost::shared_ptr<Region> target)
3147 boost::shared_ptr<PlaylistSource> pls;
3148 boost::shared_ptr<const Playlist> pl;
3149 vector<boost::shared_ptr<Region> > originals;
3150 vector<TwoRegions> old_and_new_regions;
3152 // (1) check that its really a compound region
3154 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3158 pl = pls->playlist();
3160 framepos_t adjusted_start = 0; // gcc isn't smart enough
3161 framepos_t adjusted_end = 0; // gcc isn't smart enough
3163 /* the leftmost (earliest) edge of the compound region
3164 starts at zero in its source, or larger if it
3165 has been trimmed or content-scrolled.
3167 the rightmost (latest) edge of the compound region
3168 relative to its source is the starting point plus
3169 the length of the region.
3172 // (2) get all the original regions
3174 const RegionList& rl (pl->region_list_property().rlist());
3175 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3176 frameoffset_t move_offset = 0;
3178 /* there are two possibilities here:
3179 1) the playlist that the playlist source was based on
3180 is us, so just add the originals (which belonged to
3181 us anyway) back in the right place.
3183 2) the playlist that the playlist source was based on
3184 is NOT us, so we need to make copies of each of
3185 the original regions that we find, and add them
3188 bool same_playlist = (pls->original() == id());
3190 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3192 boost::shared_ptr<Region> current (*i);
3194 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3196 if (ca == cassocs.end()) {
3200 boost::shared_ptr<Region> original (ca->second);
3202 bool modified_region;
3204 if (i == rl.begin()) {
3205 move_offset = (target->position() - original->position()) - target->start();
3206 adjusted_start = original->position() + target->start();
3207 adjusted_end = adjusted_start + target->length();
3210 if (!same_playlist) {
3211 framepos_t pos = original->position();
3212 /* make a copy, but don't announce it */
3213 original = RegionFactory::create (original, false);
3214 /* the pure copy constructor resets position() to zero,
3217 original->set_position (pos);
3220 /* check to see how the original region (in the
3221 * playlist before compounding occurred) overlaps
3222 * with the new state of the compound region.
3225 original->clear_changes ();
3226 modified_region = false;
3228 switch (original->coverage (adjusted_start, adjusted_end)) {
3229 case Evoral::OverlapNone:
3230 /* original region does not cover any part
3231 of the current state of the compound region
3235 case Evoral::OverlapInternal:
3236 /* overlap is just a small piece inside the
3237 * original so trim both ends
3239 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3240 modified_region = true;
3243 case Evoral::OverlapExternal:
3244 /* overlap fully covers original, so leave it
3249 case Evoral::OverlapEnd:
3250 /* overlap starts within but covers end,
3251 so trim the front of the region
3253 original->trim_front (adjusted_start);
3254 modified_region = true;
3257 case Evoral::OverlapStart:
3258 /* overlap covers start but ends within, so
3259 * trim the end of the region.
3261 original->trim_end (adjusted_end);
3262 modified_region = true;
3267 /* fix the position to match any movement of the compound region.
3269 original->set_position (original->position() + move_offset);
3270 modified_region = true;
3273 if (modified_region) {
3274 _session.add_command (new StatefulDiffCommand (original));
3277 /* and add to the list of regions waiting to be
3281 originals.push_back (original);
3282 old_and_new_regions.push_back (TwoRegions (*i, original));
3285 pre_uncombine (originals, target);
3287 in_partition = true;
3290 // (3) remove the compound region
3292 remove_region (target);
3294 // (4) add the constituent regions
3296 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3297 add_region ((*i), (*i)->position());
3298 set_layer((*i), (*i)->layer());
3299 if (!RegionFactory::region_by_id((*i)->id())) {
3300 RegionFactory::map_add(*i);
3304 in_partition = false;
3309 Playlist::fade_range (list<AudioRange>& ranges)
3311 RegionReadLock rlock (this);
3312 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
3313 list<AudioRange>::iterator tmpr = r;
3315 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ) {
3316 RegionList::const_iterator tmpi = i;
3318 (*i)->fade_range ((*r).start, (*r).end);
3326 Playlist::max_source_level () const
3328 RegionReadLock rlock (const_cast<Playlist *> (this));
3331 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3332 lvl = max (lvl, (*i)->max_source_level());
3339 Playlist::set_orig_track_id (const PBD::ID& id)
3341 if (shared_with(id)) {
3342 // Swap 'shared_id' / origin_track_id
3344 share_with (_orig_track_id);
3346 _orig_track_id = id;
3350 Playlist::share_with (const PBD::ID& id)
3352 if (!shared_with(id)) {
3353 _shared_with_ids.push_back (id);
3358 Playlist::unshare_with (const PBD::ID& id)
3360 list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3361 while (it != _shared_with_ids.end()) {
3363 _shared_with_ids.erase (it);
3371 Playlist::shared_with (const PBD::ID& id) const
3373 bool shared = false;
3374 list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3375 while (it != _shared_with_ids.end() && !shared) {
3386 Playlist::reset_shares ()
3388 _shared_with_ids.clear();
3391 /** Take a list of ranges, coalesce any that can be coalesced, then call
3392 * check_crossfades for each one.
3395 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3397 /* XXX: it's a shame that this coalesce algorithm also exists in
3398 TimeSelection::consolidate().
3401 /* XXX: xfade: this is implemented in Evoral::RangeList */
3404 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3405 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3411 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3412 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3413 i->from = min (i->from, j->from);
3414 i->to = max (i->to, j->to);
3423 Playlist::set_capture_insertion_in_progress (bool yn)
3425 _capture_insertion_underway = yn;