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.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/convert.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/stateful_diff_command.h"
34 #include "pbd/xml++.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/region_sorters.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/playlist_source.h"
44 #include "ardour/transient_detector.h"
45 #include "ardour/session_playlists.h"
46 #include "ardour/source_factory.h"
51 using namespace ARDOUR;
55 namespace Properties {
56 PBD::PropertyDescriptor<bool> regions;
60 struct ShowMeTheList {
61 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
63 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
65 boost::shared_ptr<Playlist> playlist;
72 Playlist::make_property_quarks ()
74 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
75 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
76 Properties::regions.property_id));
79 RegionListProperty::RegionListProperty (Playlist& pl)
80 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
86 RegionListProperty::RegionListProperty (RegionListProperty const & p)
87 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
88 , _playlist (p._playlist)
94 RegionListProperty::clone () const
96 return new RegionListProperty (*this);
100 RegionListProperty::create () const
102 return new RegionListProperty (_playlist);
106 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
108 /* All regions (even those which are deleted) have their state saved by other
109 code, so we can just store ID here.
112 node.add_property ("id", region->id().to_s ());
115 boost::shared_ptr<Region>
116 RegionListProperty::get_content_from_xml (XMLNode const & node) const
118 XMLProperty const * prop = node.property ("id");
121 PBD::ID id (prop->value ());
123 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
126 ret = RegionFactory::region_by_id (id);
132 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
133 : SessionObject(sess, nom)
138 first_set_state = false;
143 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
144 : SessionObject(sess, "unnamed playlist")
149 const XMLProperty* prop = node.property("type");
150 assert(!prop || DataType(prop->value()) == _type);
154 _name = "unnamed"; /* reset by set_state */
157 /* set state called by derived class */
160 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
161 : SessionObject(other->_session, namestr)
163 , _type(other->_type)
164 , _orig_track_id (other->_orig_track_id)
169 other->copy_regions (tmp);
173 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
174 add_region_internal( (*x), (*x)->position());
179 _splicing = other->_splicing;
180 _nudging = other->_nudging;
181 _edit_mode = other->_edit_mode;
184 first_set_state = false;
186 in_partition = false;
188 _frozen = other->_frozen;
191 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
192 : SessionObject(other->_session, str)
194 , _type(other->_type)
195 , _orig_track_id (other->_orig_track_id)
197 RegionLock 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::RegionFactory::create (region, plist);
260 add_region_internal (new_region, position);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
297 Playlist::init (bool hide)
299 add_property (regions);
300 _xml_node_name = X_("Playlist");
302 g_atomic_int_set (&block_notifications, 0);
303 g_atomic_int_set (&ignore_state_changes, 0);
304 pending_contents_change = false;
305 pending_layering = false;
306 first_set_state = true;
314 _edit_mode = Config->get_edit_mode();
316 in_partition = false;
321 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
322 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
324 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
327 Playlist::~Playlist ()
329 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
332 RegionLock rl (this);
334 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
335 (*i)->set_playlist (boost::shared_ptr<Playlist>());
339 /* GoingAway must be emitted by derived classes */
343 Playlist::_set_sort_id ()
346 Playlists are given names like <track name>.<id>
347 or <track name>.<edit group name>.<id> where id
348 is an integer. We extract the id and sort by that.
351 size_t dot_position = _name.val().find_last_of(".");
353 if (dot_position == string::npos) {
356 string t = _name.val().substr(dot_position + 1);
359 _sort_id = boost::lexical_cast<int>(t);
362 catch (boost::bad_lexical_cast e) {
369 Playlist::set_name (const string& str)
371 /* in a typical situation, a playlist is being used
372 by one diskstream and also is referenced by the
373 Session. if there are more references than that,
374 then don't change the name.
381 bool ret = SessionObject::set_name(str);
388 /***********************************************************************
389 CHANGE NOTIFICATION HANDLING
391 Notifications must be delayed till the region_lock is released. This
392 is necessary because handlers for the signals may need to acquire
393 the lock (e.g. to read from the playlist).
394 ***********************************************************************/
397 Playlist::begin_undo ()
404 Playlist::end_undo ()
413 delay_notifications ();
414 g_atomic_int_inc (&ignore_state_changes);
417 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
419 Playlist::thaw (bool from_undo)
421 g_atomic_int_dec_and_test (&ignore_state_changes);
422 release_notifications (from_undo);
427 Playlist::delay_notifications ()
429 g_atomic_int_inc (&block_notifications);
432 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
434 Playlist::release_notifications (bool from_undo)
436 if (g_atomic_int_dec_and_test (&block_notifications)) {
437 flush_notifications (from_undo);
442 Playlist::notify_contents_changed ()
444 if (holding_state ()) {
445 pending_contents_change = true;
447 pending_contents_change = false;
448 ContentsChanged(); /* EMIT SIGNAL */
453 Playlist::notify_layering_changed ()
455 if (holding_state ()) {
456 pending_layering = true;
458 pending_layering = false;
459 LayeringChanged(); /* EMIT SIGNAL */
464 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
466 if (holding_state ()) {
467 pending_removes.insert (r);
468 pending_contents_change = true;
470 /* this might not be true, but we have to act
471 as though it could be.
473 pending_contents_change = false;
474 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
475 ContentsChanged (); /* EMIT SIGNAL */
480 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
482 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
484 if (holding_state ()) {
486 pending_range_moves.push_back (move);
490 list< Evoral::RangeMove<framepos_t> > m;
492 RangesMoved (m, false);
498 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
500 if (r->position() >= r->last_position()) {
501 /* trimmed shorter */
505 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
507 if (holding_state ()) {
509 pending_region_extensions.push_back (extra);
513 list<Evoral::Range<framepos_t> > r;
521 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
523 if (r->length() < r->last_length()) {
524 /* trimmed shorter */
527 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
529 if (holding_state ()) {
531 pending_region_extensions.push_back (extra);
535 list<Evoral::Range<framepos_t> > r;
543 Playlist::notify_region_added (boost::shared_ptr<Region> r)
545 /* the length change might not be true, but we have to act
546 as though it could be.
549 if (holding_state()) {
550 pending_adds.insert (r);
551 pending_contents_change = true;
554 pending_contents_change = false;
555 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
556 ContentsChanged (); /* EMIT SIGNAL */
560 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
562 Playlist::flush_notifications (bool from_undo)
564 set<boost::shared_ptr<Region> >::iterator s;
565 bool regions_changed = false;
573 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
574 regions_changed = true;
577 /* XXX: it'd be nice if we could use pending_bounds for
578 RegionsExtended and RegionsMoved.
581 /* we have no idea what order the regions ended up in pending
582 bounds (it could be based on selection order, for example).
583 so, to preserve layering in the "most recently moved is higher"
584 model, sort them by existing layer, then timestamp them.
587 // RegionSortByLayer cmp;
588 // pending_bounds.sort (cmp);
590 list<Evoral::Range<framepos_t> > crossfade_ranges;
592 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
593 crossfade_ranges.push_back ((*r)->last_range ());
594 crossfade_ranges.push_back ((*r)->range ());
597 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
598 crossfade_ranges.push_back ((*s)->range ());
599 remove_dependents (*s);
600 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
603 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
604 crossfade_ranges.push_back ((*s)->range ());
605 /* don't emit RegionAdded signal until relayering is done,
606 so that the region is fully setup by the time
607 anyone hears that its been added
612 ((regions_changed || pending_contents_change) && !in_set_state) ||
619 if (regions_changed || pending_contents_change) {
620 pending_contents_change = false;
621 ContentsChanged (); /* EMIT SIGNAL */
624 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
625 (*s)->clear_changes ();
626 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
629 coalesce_and_check_crossfades (crossfade_ranges);
631 if (!pending_range_moves.empty ()) {
632 /* We don't need to check crossfades for these as pending_bounds has
635 RangesMoved (pending_range_moves, from_undo);
638 if (!pending_region_extensions.empty ()) {
639 RegionsExtended (pending_region_extensions);
648 Playlist::clear_pending ()
650 pending_adds.clear ();
651 pending_removes.clear ();
652 pending_bounds.clear ();
653 pending_range_moves.clear ();
654 pending_region_extensions.clear ();
655 pending_contents_change = false;
658 /*************************************************************
660 *************************************************************/
662 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
664 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
666 RegionLock rlock (this);
667 times = fabs (times);
669 int itimes = (int) floor (times);
671 framepos_t pos = position;
673 if (times == 1 && auto_partition){
674 partition(pos - 1, (pos + region->length()), true);
678 add_region_internal (region, pos);
679 set_layer (region, DBL_MAX);
680 pos += region->length();
685 /* note that itimes can be zero if we being asked to just
686 insert a single fraction of the region.
689 for (int i = 0; i < itimes; ++i) {
690 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
691 add_region_internal (copy, pos);
692 set_layer (copy, DBL_MAX);
693 pos += region->length();
696 framecnt_t length = 0;
698 if (floor (times) != times) {
699 length = (framecnt_t) floor (region->length() * (times - floor (times)));
701 RegionFactory::region_name (name, region->name(), false);
706 plist.add (Properties::start, region->start());
707 plist.add (Properties::length, length);
708 plist.add (Properties::name, name);
709 plist.add (Properties::layer, region->layer());
711 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
712 add_region_internal (sub, pos);
713 set_layer (sub, DBL_MAX);
717 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
721 Playlist::set_region_ownership ()
723 RegionLock rl (this);
724 RegionList::iterator i;
725 boost::weak_ptr<Playlist> pl (shared_from_this());
727 for (i = regions.begin(); i != regions.end(); ++i) {
728 (*i)->set_playlist (pl);
733 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
735 if (region->data_type() != _type) {
739 RegionSortByPosition cmp;
741 if (!first_set_state) {
742 boost::shared_ptr<Playlist> foo (shared_from_this());
743 region->set_playlist (boost::weak_ptr<Playlist>(foo));
746 region->set_position (position);
748 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
749 all_regions.insert (region);
751 possibly_splice_unlocked (position, region->length(), region);
753 if (!holding_state ()) {
754 /* layers get assigned from XML state, and are not reset during undo/redo */
758 /* we need to notify the existence of new region before checking dependents. Ick. */
760 notify_region_added (region);
762 if (!holding_state ()) {
763 check_crossfades (region->range ());
766 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
772 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
774 RegionLock rlock (this);
776 bool old_sp = _splicing;
779 remove_region_internal (old);
780 add_region_internal (newr, pos);
781 set_layer (newr, old->layer ());
785 possibly_splice_unlocked (pos, old->length() - newr->length());
789 Playlist::remove_region (boost::shared_ptr<Region> region)
791 RegionLock rlock (this);
792 remove_region_internal (region);
796 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
798 RegionList::iterator i;
802 region->set_playlist (boost::weak_ptr<Playlist>());
805 /* XXX should probably freeze here .... */
807 for (i = regions.begin(); i != regions.end(); ++i) {
810 framepos_t pos = (*i)->position();
811 framecnt_t distance = (*i)->length();
815 possibly_splice_unlocked (pos, -distance);
817 if (!holding_state ()) {
819 remove_dependents (region);
822 notify_region_removed (region);
831 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
833 if (Config->get_use_overlap_equivalency()) {
834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
835 if ((*i)->overlap_equivalent (other)) {
836 results.push_back (*i);
840 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
841 if ((*i)->equivalent (other)) {
842 results.push_back (*i);
849 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
851 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
853 if ((*i) && (*i)->region_list_equivalent (other)) {
854 results.push_back (*i);
860 Playlist::partition (framepos_t start, framepos_t end, bool cut)
864 partition_internal (start, end, cut, thawlist);
866 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
867 (*i)->resume_property_changes ();
871 /** Go through each region on the playlist and cut them at start and end, removing the section between
872 * start and end if cutting == true. Regions that lie entirely within start and end are always
877 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
879 RegionList new_regions;
882 RegionLock rlock (this);
884 boost::shared_ptr<Region> region;
885 boost::shared_ptr<Region> current;
887 RegionList::iterator tmp;
888 Evoral::OverlapType overlap;
889 framepos_t pos1, pos2, pos3, pos4;
893 /* need to work from a copy, because otherwise the regions we add during the process
894 get operated on as well.
897 RegionList copy = regions.rlist();
899 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
906 if (current->first_frame() >= start && current->last_frame() < end) {
909 remove_region_internal (current);
915 /* coverage will return OverlapStart if the start coincides
916 with the end point. we do not partition such a region,
917 so catch this special case.
920 if (current->first_frame() >= end) {
924 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
928 pos1 = current->position();
931 pos4 = current->last_frame();
933 if (overlap == Evoral::OverlapInternal) {
934 /* split: we need 3 new regions, the front, middle and end.
935 cut: we need 2 regions, the front and end.
940 ---------------*************************------------
943 ---------------*****++++++++++++++++====------------
945 ---------------*****----------------====------------
950 /* "middle" ++++++ */
952 RegionFactory::region_name (new_name, current->name(), false);
956 plist.add (Properties::start, current->start() + (pos2 - pos1));
957 plist.add (Properties::length, pos3 - pos2);
958 plist.add (Properties::name, new_name);
959 plist.add (Properties::layer, current->layer ());
960 plist.add (Properties::layering_index, current->layering_index ());
961 plist.add (Properties::automatic, true);
962 plist.add (Properties::left_of_split, true);
963 plist.add (Properties::right_of_split, true);
965 region = RegionFactory::create (current, plist);
966 add_region_internal (region, start);
967 new_regions.push_back (region);
972 RegionFactory::region_name (new_name, current->name(), false);
976 plist.add (Properties::start, current->start() + (pos3 - pos1));
977 plist.add (Properties::length, pos4 - pos3);
978 plist.add (Properties::name, new_name);
979 plist.add (Properties::layer, current->layer ());
980 plist.add (Properties::layering_index, current->layering_index ());
981 plist.add (Properties::automatic, true);
982 plist.add (Properties::right_of_split, true);
984 region = RegionFactory::create (current, plist);
986 add_region_internal (region, end);
987 new_regions.push_back (region);
991 current->suspend_property_changes ();
992 thawlist.push_back (current);
993 current->cut_end (pos2 - 1);
995 } else if (overlap == Evoral::OverlapEnd) {
999 ---------------*************************------------
1002 ---------------**************+++++++++++------------
1004 ---------------**************-----------------------
1011 RegionFactory::region_name (new_name, current->name(), false);
1015 plist.add (Properties::start, current->start() + (pos2 - pos1));
1016 plist.add (Properties::length, pos4 - pos2);
1017 plist.add (Properties::name, new_name);
1018 plist.add (Properties::layer, current->layer ());
1019 plist.add (Properties::layering_index, current->layering_index ());
1020 plist.add (Properties::automatic, true);
1021 plist.add (Properties::left_of_split, true);
1023 region = RegionFactory::create (current, plist);
1025 add_region_internal (region, start);
1026 new_regions.push_back (region);
1031 current->suspend_property_changes ();
1032 thawlist.push_back (current);
1033 current->cut_end (pos2 - 1);
1035 } else if (overlap == Evoral::OverlapStart) {
1037 /* split: we need 2 regions: the front and the end.
1038 cut: just trim current to skip the cut area
1043 ---------------*************************------------
1047 ---------------****+++++++++++++++++++++------------
1049 -------------------*********************------------
1055 RegionFactory::region_name (new_name, current->name(), false);
1059 plist.add (Properties::start, current->start());
1060 plist.add (Properties::length, pos3 - pos1);
1061 plist.add (Properties::name, new_name);
1062 plist.add (Properties::layer, current->layer ());
1063 plist.add (Properties::layering_index, current->layering_index ());
1064 plist.add (Properties::automatic, true);
1065 plist.add (Properties::right_of_split, true);
1067 region = RegionFactory::create (current, plist);
1069 add_region_internal (region, pos1);
1070 new_regions.push_back (region);
1075 current->suspend_property_changes ();
1076 thawlist.push_back (current);
1077 current->trim_front (pos3);
1078 } else if (overlap == Evoral::OverlapExternal) {
1080 /* split: no split required.
1081 cut: remove the region.
1086 ---------------*************************------------
1090 ---------------*************************------------
1092 ----------------------------------------------------
1097 remove_region_internal (current);
1100 new_regions.push_back (current);
1104 in_partition = false;
1107 check_crossfades (Evoral::Range<framepos_t> (start, end));
1110 boost::shared_ptr<Playlist>
1111 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1113 boost::shared_ptr<Playlist> ret;
1114 boost::shared_ptr<Playlist> pl;
1117 if (ranges.empty()) {
1118 return boost::shared_ptr<Playlist>();
1121 start = ranges.front().start;
1123 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1125 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1127 if (i == ranges.begin()) {
1131 /* paste the next section into the nascent playlist,
1132 offset to reflect the start of the first range we
1136 ret->paste (pl, (*i).start - start, 1.0f);
1143 boost::shared_ptr<Playlist>
1144 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1146 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1147 return cut_copy (pmf, ranges, result_is_hidden);
1150 boost::shared_ptr<Playlist>
1151 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1153 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1154 return cut_copy (pmf, ranges, result_is_hidden);
1157 boost::shared_ptr<Playlist>
1158 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1160 boost::shared_ptr<Playlist> the_copy;
1161 RegionList thawlist;
1164 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1165 string new_name = _name;
1169 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1170 return boost::shared_ptr<Playlist>();
1173 partition_internal (start, start+cnt-1, true, thawlist);
1175 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1176 (*i)->resume_property_changes();
1182 boost::shared_ptr<Playlist>
1183 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1187 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1188 string new_name = _name;
1192 cnt = min (_get_extent().second - start, cnt);
1193 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1197 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1199 times = fabs (times);
1202 RegionLock rl1 (this);
1203 RegionLock rl2 (other.get());
1205 int itimes = (int) floor (times);
1206 framepos_t pos = position;
1207 framecnt_t const shift = other->_get_extent().second;
1208 layer_t top = top_layer ();
1211 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1212 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1214 /* put these new regions on top of all existing ones, but preserve
1215 the ordering they had in the original playlist.
1218 add_region_internal (copy_of_region, (*i)->position() + pos);
1219 set_layer (copy_of_region, copy_of_region->layer() + top);
1230 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1232 times = fabs (times);
1234 RegionLock rl (this);
1235 int itimes = (int) floor (times);
1236 framepos_t pos = position + 1;
1239 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1240 add_region_internal (copy, pos);
1241 set_layer (copy, DBL_MAX);
1242 pos += region->length();
1245 if (floor (times) != times) {
1246 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1248 RegionFactory::region_name (name, region->name(), false);
1253 plist.add (Properties::start, region->start());
1254 plist.add (Properties::length, length);
1255 plist.add (Properties::name, name);
1257 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1258 add_region_internal (sub, pos);
1259 set_layer (sub, DBL_MAX);
1265 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1267 RegionLock rlock (this);
1268 RegionList copy (regions.rlist());
1271 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1273 if ((*r)->last_frame() < at) {
1278 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1279 /* intersected region */
1280 if (!move_intersected) {
1285 /* do not move regions glued to music time - that
1286 has to be done separately.
1289 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1290 fixup.push_back (*r);
1294 (*r)->set_position ((*r)->position() + distance);
1297 /* XXX: may not be necessary; Region::post_set should do this, I think */
1298 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1299 (*r)->recompute_position_from_lock_style ();
1304 Playlist::split (framepos_t at)
1306 RegionLock rlock (this);
1307 RegionList copy (regions.rlist());
1309 /* use a copy since this operation can modify the region list
1312 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1313 _split_region (*r, at);
1318 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1320 RegionLock rl (this);
1321 _split_region (region, playlist_position);
1325 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1327 if (!region->covers (playlist_position)) {
1331 if (region->position() == playlist_position ||
1332 region->last_frame() == playlist_position) {
1336 boost::shared_ptr<Region> left;
1337 boost::shared_ptr<Region> right;
1338 frameoffset_t before;
1339 frameoffset_t after;
1343 /* split doesn't change anything about length, so don't try to splice */
1345 bool old_sp = _splicing;
1348 before = playlist_position - region->position();
1349 after = region->length() - before;
1351 RegionFactory::region_name (before_name, region->name(), false);
1356 plist.add (Properties::position, region->position ());
1357 plist.add (Properties::length, before);
1358 plist.add (Properties::name, before_name);
1359 plist.add (Properties::left_of_split, true);
1360 plist.add (Properties::layering_index, region->layering_index ());
1361 plist.add (Properties::layer, region->layer ());
1363 /* note: we must use the version of ::create with an offset here,
1364 since it supplies that offset to the Region constructor, which
1365 is necessary to get audio region gain envelopes right.
1367 left = RegionFactory::create (region, 0, plist);
1370 RegionFactory::region_name (after_name, region->name(), false);
1375 plist.add (Properties::position, region->position() + before);
1376 plist.add (Properties::length, after);
1377 plist.add (Properties::name, after_name);
1378 plist.add (Properties::right_of_split, true);
1379 plist.add (Properties::layering_index, region->layering_index ());
1380 plist.add (Properties::layer, region->layer ());
1382 /* same note as above */
1383 right = RegionFactory::create (region, before, plist);
1386 add_region_internal (left, region->position());
1387 add_region_internal (right, region->position() + before);
1388 remove_region_internal (region);
1394 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1396 if (_splicing || in_set_state) {
1397 /* don't respond to splicing moves or state setting */
1401 if (_edit_mode == Splice) {
1402 splice_locked (at, distance, exclude);
1407 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1409 if (_splicing || in_set_state) {
1410 /* don't respond to splicing moves or state setting */
1414 if (_edit_mode == Splice) {
1415 splice_unlocked (at, distance, exclude);
1420 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1423 RegionLock rl (this);
1424 core_splice (at, distance, exclude);
1429 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1431 core_splice (at, distance, exclude);
1435 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1439 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1441 if (exclude && (*i) == exclude) {
1445 if ((*i)->position() >= at) {
1446 framepos_t new_pos = (*i)->position() + distance;
1449 } else if (new_pos >= max_framepos - (*i)->length()) {
1450 new_pos = max_framepos - (*i)->length();
1453 (*i)->set_position (new_pos);
1459 notify_contents_changed ();
1463 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1465 if (in_set_state || _splicing || _nudging || _shuffling) {
1469 if (what_changed.contains (Properties::position)) {
1471 /* remove it from the list then add it back in
1472 the right place again.
1475 RegionSortByPosition cmp;
1477 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1479 if (i == regions.end()) {
1480 /* the region bounds are being modified but its not currently
1481 in the region list. we will use its bounds correctly when/if
1488 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1491 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1493 frameoffset_t delta = 0;
1495 if (what_changed.contains (Properties::position)) {
1496 delta = region->position() - region->last_position();
1499 if (what_changed.contains (Properties::length)) {
1500 delta += region->length() - region->last_length();
1504 possibly_splice (region->last_position() + region->last_length(), delta, region);
1507 if (holding_state ()) {
1508 pending_bounds.push_back (region);
1510 notify_contents_changed ();
1512 list<Evoral::Range<framepos_t> > xf;
1513 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1514 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1515 coalesce_and_check_crossfades (xf);
1521 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1523 boost::shared_ptr<Region> region (weak_region.lock());
1529 /* this makes a virtual call to the right kind of playlist ... */
1531 region_changed (what_changed, region);
1535 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1537 PropertyChange our_interests;
1538 PropertyChange bounds;
1539 PropertyChange pos_and_length;
1542 if (in_set_state || in_flush) {
1546 our_interests.add (Properties::muted);
1547 our_interests.add (Properties::layer);
1548 our_interests.add (Properties::opaque);
1550 bounds.add (Properties::start);
1551 bounds.add (Properties::position);
1552 bounds.add (Properties::length);
1554 pos_and_length.add (Properties::position);
1555 pos_and_length.add (Properties::length);
1557 if (what_changed.contains (bounds)) {
1558 region_bounds_changed (what_changed, region);
1559 save = !(_splicing || _nudging);
1562 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1563 check_crossfades (region->range ());
1566 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1567 notify_region_moved (region);
1568 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1569 notify_region_end_trimmed (region);
1570 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1571 notify_region_start_trimmed (region);
1574 /* don't notify about layer changes, since we are the only object that can initiate
1575 them, and we notify in ::relayer()
1578 if (what_changed.contains (our_interests)) {
1586 Playlist::drop_regions ()
1588 RegionLock rl (this);
1590 all_regions.clear ();
1594 Playlist::sync_all_regions_with_regions ()
1596 RegionLock rl (this);
1598 all_regions.clear ();
1600 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1601 all_regions.insert (*i);
1606 Playlist::clear (bool with_signals)
1609 RegionLock rl (this);
1611 region_state_changed_connections.drop_connections ();
1613 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1614 pending_removes.insert (*i);
1619 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1620 remove_dependents (*s);
1626 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1627 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1630 pending_removes.clear ();
1631 pending_contents_change = false;
1637 /***********************************************************************
1639 **********************************************************************/
1641 boost::shared_ptr<RegionList>
1642 Playlist::regions_at (framepos_t frame)
1644 RegionLock rlock (this);
1645 return find_regions_at (frame);
1649 Playlist::count_regions_at (framepos_t frame) const
1651 RegionLock rlock (const_cast<Playlist*>(this));
1654 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1655 if ((*i)->covers (frame)) {
1663 boost::shared_ptr<Region>
1664 Playlist::top_region_at (framepos_t frame)
1667 RegionLock rlock (this);
1668 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1669 boost::shared_ptr<Region> region;
1671 if (rlist->size()) {
1672 RegionSortByLayer cmp;
1674 region = rlist->back();
1680 boost::shared_ptr<Region>
1681 Playlist::top_unmuted_region_at (framepos_t frame)
1684 RegionLock rlock (this);
1685 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1687 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1689 RegionList::iterator tmp = i;
1692 if ((*i)->muted()) {
1699 boost::shared_ptr<Region> region;
1701 if (rlist->size()) {
1702 RegionSortByLayer cmp;
1704 region = rlist->back();
1710 boost::shared_ptr<RegionList>
1711 Playlist::find_regions_at (framepos_t frame)
1713 /* Caller must hold lock */
1715 boost::shared_ptr<RegionList> rlist (new RegionList);
1717 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1718 if ((*i)->covers (frame)) {
1719 rlist->push_back (*i);
1726 boost::shared_ptr<RegionList>
1727 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1729 RegionLock rlock (this);
1730 boost::shared_ptr<RegionList> rlist (new RegionList);
1732 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1733 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1734 rlist->push_back (*i);
1741 boost::shared_ptr<RegionList>
1742 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1744 RegionLock rlock (this);
1745 boost::shared_ptr<RegionList> rlist (new RegionList);
1747 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1748 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1749 rlist->push_back (*i);
1756 /** @param start Range start.
1757 * @param end Range end.
1758 * @return regions which have some part within this range.
1760 boost::shared_ptr<RegionList>
1761 Playlist::regions_touched (framepos_t start, framepos_t end)
1763 RegionLock rlock (this);
1764 boost::shared_ptr<RegionList> rlist (new RegionList);
1766 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1767 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1768 rlist->push_back (*i);
1776 Playlist::find_next_transient (framepos_t from, int dir)
1778 RegionLock rlock (this);
1779 AnalysisFeatureList points;
1780 AnalysisFeatureList these_points;
1782 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1784 if ((*i)->last_frame() < from) {
1788 if ((*i)->first_frame() > from) {
1793 (*i)->get_transients (these_points);
1795 /* add first frame, just, err, because */
1797 these_points.push_back ((*i)->first_frame());
1799 points.insert (points.end(), these_points.begin(), these_points.end());
1800 these_points.clear ();
1803 if (points.empty()) {
1807 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1808 bool reached = false;
1811 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1816 if (reached && (*x) > from) {
1821 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1826 if (reached && (*x) < from) {
1835 boost::shared_ptr<Region>
1836 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1838 RegionLock rlock (this);
1839 boost::shared_ptr<Region> ret;
1840 framepos_t closest = max_framepos;
1842 bool end_iter = false;
1844 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1848 frameoffset_t distance;
1849 boost::shared_ptr<Region> r = (*i);
1854 pos = r->first_frame ();
1857 pos = r->last_frame ();
1860 pos = r->sync_position ();
1865 case 1: /* forwards */
1868 if ((distance = pos - frame) < closest) {
1877 default: /* backwards */
1880 if ((distance = frame - pos) < closest) {
1897 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1899 RegionLock rlock (this);
1901 framepos_t closest = max_framepos;
1902 framepos_t ret = -1;
1906 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1908 boost::shared_ptr<Region> r = (*i);
1909 frameoffset_t distance;
1911 if (r->first_frame() > frame) {
1913 distance = r->first_frame() - frame;
1915 if (distance < closest) {
1916 ret = r->first_frame();
1921 if (r->last_frame () > frame) {
1923 distance = r->last_frame () - frame;
1925 if (distance < closest) {
1926 ret = r->last_frame ();
1934 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1936 boost::shared_ptr<Region> r = (*i);
1937 frameoffset_t distance;
1939 if (r->last_frame() < frame) {
1941 distance = frame - r->last_frame();
1943 if (distance < closest) {
1944 ret = r->last_frame();
1949 if (r->first_frame() < frame) {
1951 distance = frame - r->first_frame();
1953 if (distance < closest) {
1954 ret = r->first_frame();
1965 /***********************************************************************/
1971 Playlist::mark_session_dirty ()
1973 if (!in_set_state && !holding_state ()) {
1974 _session.set_dirty();
1979 Playlist::rdiff (vector<Command*>& cmds) const
1981 RegionLock rlock (const_cast<Playlist *> (this));
1982 Stateful::rdiff (cmds);
1986 Playlist::clear_owned_changes ()
1988 RegionLock rlock (this);
1989 Stateful::clear_owned_changes ();
1993 Playlist::update (const RegionListProperty::ChangeRecord& change)
1995 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
1996 name(), change.added.size(), change.removed.size()));
1999 /* add the added regions */
2000 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2001 add_region_internal ((*i), (*i)->position());
2003 /* remove the removed regions */
2004 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2012 Playlist::set_state (const XMLNode& node, int version)
2016 XMLNodeConstIterator niter;
2017 XMLPropertyList plist;
2018 XMLPropertyConstIterator piter;
2020 boost::shared_ptr<Region> region;
2022 bool seen_region_nodes = false;
2027 if (node.name() != "Playlist") {
2034 plist = node.properties();
2038 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2042 if (prop->name() == X_("name")) {
2043 _name = prop->value();
2045 } else if (prop->name() == X_("orig-diskstream-id")) {
2046 /* XXX legacy session: fix up later */
2047 _orig_track_id = prop->value ();
2048 } else if (prop->name() == X_("orig-track-id")) {
2049 _orig_track_id = prop->value ();
2050 } else if (prop->name() == X_("frozen")) {
2051 _frozen = string_is_affirmative (prop->value());
2052 } else if (prop->name() == X_("combine-ops")) {
2053 _combine_ops = atoi (prop->value());
2059 nlist = node.children();
2061 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2065 if (child->name() == "Region") {
2067 seen_region_nodes = true;
2069 if ((prop = child->property ("id")) == 0) {
2070 error << _("region state node has no ID, ignored") << endmsg;
2074 ID id = prop->value ();
2076 if ((region = region_by_id (id))) {
2078 region->suspend_property_changes ();
2080 if (region->set_state (*child, version)) {
2081 region->resume_property_changes ();
2085 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2086 region->suspend_property_changes ();
2088 error << _("Playlist: cannot create region from XML") << endmsg;
2093 RegionLock rlock (this);
2094 add_region_internal (region, region->position());
2097 region->resume_property_changes ();
2102 if (seen_region_nodes && regions.empty()) {
2106 /* update dependents, which was not done during add_region_internal
2107 due to in_set_state being true
2110 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2111 check_crossfades ((*r)->range ());
2116 notify_contents_changed ();
2119 first_set_state = false;
2125 Playlist::get_state()
2127 return state (true);
2131 Playlist::get_template()
2133 return state (false);
2136 /** @param full_state true to include regions in the returned state, otherwise false.
2139 Playlist::state (bool full_state)
2141 XMLNode *node = new XMLNode (X_("Playlist"));
2144 node->add_property (X_("id"), id().to_s());
2145 node->add_property (X_("name"), _name);
2146 node->add_property (X_("type"), _type.to_string());
2148 _orig_track_id.print (buf, sizeof (buf));
2149 node->add_property (X_("orig-track-id"), buf);
2150 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2153 RegionLock rlock (this, false);
2155 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2156 node->add_property ("combine-ops", buf);
2158 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2159 node->add_child_nocopy ((*i)->get_state());
2164 node->add_child_copy (*_extra_xml);
2171 Playlist::empty() const
2173 RegionLock rlock (const_cast<Playlist *>(this), false);
2174 return regions.empty();
2178 Playlist::n_regions() const
2180 RegionLock rlock (const_cast<Playlist *>(this), false);
2181 return regions.size();
2184 pair<framepos_t, framepos_t>
2185 Playlist::get_extent () const
2187 RegionLock rlock (const_cast<Playlist *>(this), false);
2188 return _get_extent ();
2191 pair<framepos_t, framepos_t>
2192 Playlist::_get_extent () const
2194 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2196 if (regions.empty()) {
2201 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2202 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2203 if (e.first < ext.first) {
2204 ext.first = e.first;
2206 if (e.second > ext.second) {
2207 ext.second = e.second;
2215 Playlist::bump_name (string name, Session &session)
2217 string newname = name;
2220 newname = bump_name_once (newname, '.');
2221 } while (session.playlists->by_name (newname)!=NULL);
2228 Playlist::top_layer() const
2230 RegionLock rlock (const_cast<Playlist *> (this));
2233 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2234 top = max (top, (*i)->layer());
2240 Playlist::set_edit_mode (EditMode mode)
2245 struct RelayerSort {
2246 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2247 return a->layering_index() < b->layering_index();
2251 /** Set a new layer for a region. This adjusts the layering indices of all
2252 * regions in the playlist to put the specified region in the appropriate
2253 * place. The actual layering will be fixed up when relayer() happens.
2257 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2259 /* Remove the layer we are setting from our region list, and sort it */
2260 RegionList copy = regions.rlist();
2261 copy.remove (region);
2262 copy.sort (RelayerSort ());
2264 /* Put region back in the right place */
2265 RegionList::iterator i = copy.begin();
2266 while (i != copy.end ()) {
2267 if ((*i)->layer() > new_layer) {
2273 copy.insert (i, region);
2275 setup_layering_indices (copy);
2279 Playlist::setup_layering_indices (RegionList const & regions) const
2282 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2283 (*k)->set_layering_index (j++);
2288 /** Take the layering indices of each of our regions, compute the layers
2289 * that they should be on, and write the layers back to the regions.
2292 Playlist::relayer ()
2294 /* never compute layers when setting from XML */
2300 /* Build up a new list of regions on each layer, stored in a set of lists
2301 each of which represent some period of time on some layer. The idea
2302 is to avoid having to search the entire region list to establish whether
2303 each region overlaps another */
2305 /* how many pieces to divide this playlist's time up into */
2306 int const divisions = 512;
2308 /* find the start and end positions of the regions on this playlist */
2309 framepos_t start = INT64_MAX;
2311 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2312 start = min (start, (*i)->position());
2313 end = max (end, (*i)->position() + (*i)->length());
2316 /* hence the size of each time division */
2317 double const division_size = (end - start) / double (divisions);
2319 vector<vector<RegionList> > layers;
2320 layers.push_back (vector<RegionList> (divisions));
2322 /* Sort our regions into layering index order */
2323 RegionList copy = regions.rlist();
2324 copy.sort (RelayerSort ());
2326 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2327 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2328 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2331 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2333 /* find the time divisions that this region covers; if there are no regions on the list,
2334 division_size will equal 0 and in this case we'll just say that
2335 start_division = end_division = 0.
2337 int start_division = 0;
2338 int end_division = 0;
2340 if (division_size > 0) {
2341 start_division = floor ( ((*i)->position() - start) / division_size);
2342 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2343 if (end_division == divisions) {
2348 assert (divisions == 0 || end_division < divisions);
2350 /* find the lowest layer that this region can go on */
2351 size_t j = layers.size();
2353 /* try layer j - 1; it can go on if it overlaps no other region
2354 that is already on that layer
2357 bool overlap = false;
2358 for (int k = start_division; k <= end_division; ++k) {
2359 RegionList::iterator l = layers[j-1][k].begin ();
2360 while (l != layers[j-1][k].end()) {
2361 if ((*l)->overlap_equivalent (*i)) {
2374 /* overlap, so we must use layer j */
2381 if (j == layers.size()) {
2382 /* we need a new layer for this region */
2383 layers.push_back (vector<RegionList> (divisions));
2386 /* put a reference to this region in each of the divisions that it exists in */
2387 for (int k = start_division; k <= end_division; ++k) {
2388 layers[j][k].push_back (*i);
2391 (*i)->set_layer (j);
2394 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2395 relayering because we just removed the only region on the top layer, nothing will
2396 appear to have changed, but the StreamView must still sort itself out. We could
2397 probably keep a note of the top layer last time we relayered, and check that,
2398 but premature optimisation &c...
2400 notify_layering_changed ();
2402 /* This relayer() may have been called as a result of a region removal, in which
2403 case we need to setup layering indices so account for the one that has just
2406 setup_layering_indices (copy);
2410 Playlist::raise_region (boost::shared_ptr<Region> region)
2412 set_layer (region, region->layer() + 1.5);
2417 Playlist::lower_region (boost::shared_ptr<Region> region)
2419 set_layer (region, region->layer() - 1.5);
2424 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2426 set_layer (region, DBL_MAX);
2431 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2433 set_layer (region, -0.5);
2438 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2440 RegionList::iterator i;
2446 RegionLock rlock (const_cast<Playlist *> (this));
2448 for (i = regions.begin(); i != regions.end(); ++i) {
2450 if ((*i)->position() >= start) {
2456 if ((*i)->last_frame() > max_framepos - distance) {
2457 new_pos = max_framepos - (*i)->length();
2459 new_pos = (*i)->position() + distance;
2464 if ((*i)->position() > distance) {
2465 new_pos = (*i)->position() - distance;
2471 (*i)->set_position (new_pos);
2479 notify_contents_changed ();
2485 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2487 RegionLock rlock (const_cast<Playlist*> (this));
2489 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2490 if ((*r)->uses_source (src)) {
2498 boost::shared_ptr<Region>
2499 Playlist::find_region (const ID& id) const
2501 RegionLock rlock (const_cast<Playlist*> (this));
2503 /* searches all regions currently in use by the playlist */
2505 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2506 if ((*i)->id() == id) {
2511 return boost::shared_ptr<Region> ();
2515 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2517 RegionLock rlock (const_cast<Playlist*> (this));
2520 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2529 boost::shared_ptr<Region>
2530 Playlist::region_by_id (const ID& id) const
2532 /* searches all regions ever added to this playlist */
2534 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2535 if ((*i)->id() == id) {
2539 return boost::shared_ptr<Region> ();
2543 Playlist::dump () const
2545 boost::shared_ptr<Region> r;
2547 cerr << "Playlist \"" << _name << "\" " << endl
2548 << regions.size() << " regions "
2551 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2553 cerr << " " << r->name() << " ["
2554 << r->start() << "+" << r->length()
2564 Playlist::set_frozen (bool yn)
2570 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2574 if (region->locked()) {
2580 Evoral::Range<framepos_t> old_range = region->range ();
2583 RegionLock rlock (const_cast<Playlist*> (this));
2588 RegionList::iterator next;
2590 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2591 if ((*i) == region) {
2595 if (next != regions.end()) {
2597 if ((*next)->locked()) {
2603 if ((*next)->position() != region->last_frame() + 1) {
2604 /* they didn't used to touch, so after shuffle,
2605 just have them swap positions.
2607 new_pos = (*next)->position();
2609 /* they used to touch, so after shuffle,
2610 make sure they still do. put the earlier
2611 region where the later one will end after
2614 new_pos = region->position() + (*next)->length();
2617 (*next)->set_position (region->position());
2618 region->set_position (new_pos);
2620 /* avoid a full sort */
2622 regions.erase (i); // removes the region from the list */
2624 regions.insert (next, region); // adds it back after next
2633 RegionList::iterator prev = regions.end();
2635 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2636 if ((*i) == region) {
2638 if (prev != regions.end()) {
2640 if ((*prev)->locked()) {
2645 if (region->position() != (*prev)->last_frame() + 1) {
2646 /* they didn't used to touch, so after shuffle,
2647 just have them swap positions.
2649 new_pos = region->position();
2651 /* they used to touch, so after shuffle,
2652 make sure they still do. put the earlier
2653 one where the later one will end after
2655 new_pos = (*prev)->position() + region->length();
2658 region->set_position ((*prev)->position());
2659 (*prev)->set_position (new_pos);
2661 /* avoid a full sort */
2663 regions.erase (i); // remove region
2664 regions.insert (prev, region); // insert region before prev
2681 list<Evoral::Range<framepos_t> > xf;
2682 xf.push_back (old_range);
2683 xf.push_back (region->range ());
2684 coalesce_and_check_crossfades (xf);
2686 notify_contents_changed();
2692 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2694 RegionLock rlock (const_cast<Playlist*> (this));
2696 if (regions.size() > 1) {
2704 Playlist::update_after_tempo_map_change ()
2706 RegionLock rlock (const_cast<Playlist*> (this));
2707 RegionList copy (regions.rlist());
2711 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2712 (*i)->update_after_tempo_map_change ();
2719 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2721 RegionLock rl (this, false);
2722 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2728 Playlist::has_region_at (framepos_t const p) const
2730 RegionLock (const_cast<Playlist *> (this));
2732 RegionList::const_iterator i = regions.begin ();
2733 while (i != regions.end() && !(*i)->covers (p)) {
2737 return (i != regions.end());
2740 /** Remove any region that uses a given source */
2742 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2744 RegionLock rl (this);
2746 RegionList::iterator i = regions.begin();
2747 while (i != regions.end()) {
2748 RegionList::iterator j = i;
2751 if ((*i)->uses_source (s)) {
2752 remove_region_internal (*i);
2759 /** Look from a session frame time and find the start time of the next region
2760 * which is on the top layer of this playlist.
2761 * @param t Time to look from.
2762 * @return Position of next top-layered region, or max_framepos if there isn't one.
2765 Playlist::find_next_top_layer_position (framepos_t t) const
2767 RegionLock rlock (const_cast<Playlist *> (this));
2769 layer_t const top = top_layer ();
2771 RegionList copy = regions.rlist ();
2772 copy.sort (RegionSortByPosition ());
2774 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2775 if ((*i)->position() >= t && (*i)->layer() == top) {
2776 return (*i)->position();
2780 return max_framepos;
2783 boost::shared_ptr<Region>
2784 Playlist::combine (const RegionList& r)
2787 uint32_t channels = 0;
2789 framepos_t earliest_position = max_framepos;
2790 vector<TwoRegions> old_and_new_regions;
2791 vector<boost::shared_ptr<Region> > originals;
2792 vector<boost::shared_ptr<Region> > copies;
2795 uint32_t max_level = 0;
2797 /* find the maximum depth of all the regions we're combining */
2799 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2800 max_level = max (max_level, (*i)->max_source_level());
2803 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2804 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2806 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2808 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2809 earliest_position = min (earliest_position, (*i)->position());
2812 /* enable this so that we do not try to create xfades etc. as we add
2816 pl->in_partition = true;
2818 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2820 /* copy the region */
2822 boost::shared_ptr<Region> original_region = (*i);
2823 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2825 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2826 originals.push_back (original_region);
2827 copies.push_back (copied_region);
2829 RegionFactory::add_compound_association (original_region, copied_region);
2831 /* make position relative to zero */
2833 pl->add_region (copied_region, original_region->position() - earliest_position);
2835 /* use the maximum number of channels for any region */
2837 channels = max (channels, original_region->n_channels());
2839 /* it will go above the layer of the highest existing region */
2841 layer = max (layer, original_region->layer());
2844 pl->in_partition = false;
2846 pre_combine (copies);
2848 /* now create a new PlaylistSource for each channel in the new playlist */
2851 pair<framepos_t,framepos_t> extent = pl->get_extent();
2853 for (uint32_t chn = 0; chn < channels; ++chn) {
2854 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2858 /* now a new whole-file region using the list of sources */
2860 plist.add (Properties::start, 0);
2861 plist.add (Properties::length, extent.second);
2862 plist.add (Properties::name, parent_name);
2863 plist.add (Properties::whole_file, true);
2865 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2867 /* now the non-whole-file region that we will actually use in the
2872 plist.add (Properties::start, 0);
2873 plist.add (Properties::length, extent.second);
2874 plist.add (Properties::name, child_name);
2875 plist.add (Properties::layer, layer+1);
2877 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2879 /* remove all the selected regions from the current playlist
2884 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2888 /* do type-specific stuff with the originals and the new compound
2892 post_combine (originals, compound_region);
2894 /* add the new region at the right location */
2896 add_region (compound_region, earliest_position);
2902 return compound_region;
2906 Playlist::uncombine (boost::shared_ptr<Region> target)
2908 boost::shared_ptr<PlaylistSource> pls;
2909 boost::shared_ptr<const Playlist> pl;
2910 vector<boost::shared_ptr<Region> > originals;
2911 vector<TwoRegions> old_and_new_regions;
2913 // (1) check that its really a compound region
2915 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2919 pl = pls->playlist();
2921 framepos_t adjusted_start = 0; // gcc isn't smart enough
2922 framepos_t adjusted_end = 0; // gcc isn't smart enough
2924 /* the leftmost (earliest) edge of the compound region
2925 starts at zero in its source, or larger if it
2926 has been trimmed or content-scrolled.
2928 the rightmost (latest) edge of the compound region
2929 relative to its source is the starting point plus
2930 the length of the region.
2933 // (2) get all the original regions
2935 const RegionList& rl (pl->region_list().rlist());
2936 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2937 frameoffset_t move_offset = 0;
2939 /* there are two possibilities here:
2940 1) the playlist that the playlist source was based on
2941 is us, so just add the originals (which belonged to
2942 us anyway) back in the right place.
2944 2) the playlist that the playlist source was based on
2945 is NOT us, so we need to make copies of each of
2946 the original regions that we find, and add them
2949 bool same_playlist = (pls->original() == id());
2951 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2953 boost::shared_ptr<Region> current (*i);
2955 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2957 if (ca == cassocs.end()) {
2961 boost::shared_ptr<Region> original (ca->second);
2962 bool modified_region;
2964 if (i == rl.begin()) {
2965 move_offset = (target->position() - original->position()) - target->start();
2966 adjusted_start = original->position() + target->start();
2967 adjusted_end = adjusted_start + target->length();
2970 if (!same_playlist) {
2971 framepos_t pos = original->position();
2972 /* make a copy, but don't announce it */
2973 original = RegionFactory::create (original, false);
2974 /* the pure copy constructor resets position() to zero,
2977 original->set_position (pos);
2980 /* check to see how the original region (in the
2981 * playlist before compounding occured) overlaps
2982 * with the new state of the compound region.
2985 original->clear_changes ();
2986 modified_region = false;
2988 switch (original->coverage (adjusted_start, adjusted_end)) {
2989 case Evoral::OverlapNone:
2990 /* original region does not cover any part
2991 of the current state of the compound region
2995 case Evoral::OverlapInternal:
2996 /* overlap is just a small piece inside the
2997 * original so trim both ends
2999 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3000 modified_region = true;
3003 case Evoral::OverlapExternal:
3004 /* overlap fully covers original, so leave it
3009 case Evoral::OverlapEnd:
3010 /* overlap starts within but covers end,
3011 so trim the front of the region
3013 original->trim_front (adjusted_start);
3014 modified_region = true;
3017 case Evoral::OverlapStart:
3018 /* overlap covers start but ends within, so
3019 * trim the end of the region.
3021 original->trim_end (adjusted_end);
3022 modified_region = true;
3027 /* fix the position to match any movement of the compound region.
3029 original->set_position (original->position() + move_offset);
3030 modified_region = true;
3033 if (modified_region) {
3034 _session.add_command (new StatefulDiffCommand (original));
3037 /* and add to the list of regions waiting to be
3041 originals.push_back (original);
3042 old_and_new_regions.push_back (TwoRegions (*i, original));
3045 pre_uncombine (originals, target);
3047 in_partition = true;
3050 // (3) remove the compound region
3052 remove_region (target);
3054 // (4) add the constituent regions
3056 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3057 add_region ((*i), (*i)->position());
3060 in_partition = false;
3065 Playlist::max_source_level () const
3067 RegionLock rlock (const_cast<Playlist *> (this));
3070 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3071 lvl = max (lvl, (*i)->max_source_level());
3078 Playlist::set_orig_track_id (const PBD::ID& id)
3080 _orig_track_id = id;
3084 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3086 /* XXX: it's a shame that this coalesce algorithm also exists in
3087 TimeSelection::consolidate().
3090 /* XXX: xfade: this is implemented in Evoral::RangeList */
3093 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3094 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3100 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3101 i->from = min (i->from, j->from);
3102 i->to = max (i->to, j->to);
3109 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3110 check_crossfades (*i);