2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <boost/lexical_cast.hpp>
27 #include "pbd/convert.h"
28 #include "pbd/stateful_diff_command.h"
29 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/playlist.h"
33 #include "ardour/session.h"
34 #include "ardour/region.h"
35 #include "ardour/region_factory.h"
36 #include "ardour/region_sorters.h"
37 #include "ardour/playlist_factory.h"
38 #include "ardour/playlist_source.h"
39 #include "ardour/transient_detector.h"
40 #include "ardour/session_playlists.h"
41 #include "ardour/source_factory.h"
46 using namespace ARDOUR;
50 namespace Properties {
51 PBD::PropertyDescriptor<bool> regions;
55 struct ShowMeTheList {
56 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60 boost::shared_ptr<Playlist> playlist;
67 Playlist::make_property_quarks ()
69 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
70 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
71 Properties::regions.property_id));
74 RegionListProperty::RegionListProperty (Playlist& pl)
75 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
81 RegionListProperty::RegionListProperty (RegionListProperty const & p)
82 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
83 , _playlist (p._playlist)
89 RegionListProperty::clone () const
91 return new RegionListProperty (*this);
95 RegionListProperty::create () const
97 return new RegionListProperty (_playlist);
101 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
103 /* All regions (even those which are deleted) have their state saved by other
104 code, so we can just store ID here.
107 node.add_property ("id", region->id().to_s ());
110 boost::shared_ptr<Region>
111 RegionListProperty::get_content_from_xml (XMLNode const & node) const
113 XMLProperty const * prop = node.property ("id");
116 PBD::ID id (prop->value ());
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
128 : SessionObject(sess, nom)
133 first_set_state = false;
138 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
139 : SessionObject(sess, "unnamed playlist")
144 const XMLProperty* prop = node.property("type");
145 assert(!prop || DataType(prop->value()) == _type);
149 _name = "unnamed"; /* reset by set_state */
152 /* set state called by derived class */
155 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
156 : SessionObject(other->_session, namestr)
158 , _type(other->_type)
159 , _orig_track_id (other->_orig_track_id)
164 other->copy_regions (tmp);
168 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
169 add_region_internal( (*x), (*x)->position());
174 _splicing = other->_splicing;
175 _nudging = other->_nudging;
176 _edit_mode = other->_edit_mode;
179 first_set_state = false;
181 in_partition = false;
183 _frozen = other->_frozen;
186 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
187 : SessionObject(other->_session, str)
189 , _type(other->_type)
190 , _orig_track_id (other->_orig_track_id)
192 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
194 framepos_t end = start + cnt - 1;
200 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
202 boost::shared_ptr<Region> region;
203 boost::shared_ptr<Region> new_region;
204 frameoffset_t offset = 0;
205 framepos_t position = 0;
208 Evoral::OverlapType overlap;
212 overlap = region->coverage (start, end);
215 case Evoral::OverlapNone:
218 case Evoral::OverlapInternal:
219 offset = start - region->position();
224 case Evoral::OverlapStart:
226 position = region->position() - start;
227 len = end - region->position();
230 case Evoral::OverlapEnd:
231 offset = start - region->position();
233 len = region->length() - offset;
236 case Evoral::OverlapExternal:
238 position = region->position() - start;
239 len = region->length();
243 RegionFactory::region_name (new_name, region->name(), false);
247 plist.add (Properties::start, region->start() + offset);
248 plist.add (Properties::length, len);
249 plist.add (Properties::name, new_name);
250 plist.add (Properties::layer, region->layer());
251 plist.add (Properties::layering_index, region->layering_index());
253 new_region = RegionFactory::RegionFactory::create (region, plist);
255 add_region_internal (new_region, position);
259 first_set_state = false;
266 InUse (true); /* EMIT SIGNAL */
277 InUse (false); /* EMIT SIGNAL */
282 Playlist::copy_regions (RegionList& newlist) const
284 RegionReadLock rlock (const_cast<Playlist *> (this));
286 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
287 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
292 Playlist::init (bool hide)
294 add_property (regions);
295 _xml_node_name = X_("Playlist");
297 g_atomic_int_set (&block_notifications, 0);
298 g_atomic_int_set (&ignore_state_changes, 0);
299 pending_contents_change = false;
300 pending_layering = false;
301 first_set_state = true;
309 _edit_mode = Config->get_edit_mode();
311 in_partition = false;
314 _capture_insertion_underway = false;
317 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
318 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
320 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
323 Playlist::~Playlist ()
325 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
328 RegionReadLock rl (this);
330 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
331 (*i)->set_playlist (boost::shared_ptr<Playlist>());
335 /* GoingAway must be emitted by derived classes */
339 Playlist::_set_sort_id ()
342 Playlists are given names like <track name>.<id>
343 or <track name>.<edit group name>.<id> where id
344 is an integer. We extract the id and sort by that.
347 size_t dot_position = _name.val().find_last_of(".");
349 if (dot_position == string::npos) {
352 string t = _name.val().substr(dot_position + 1);
355 _sort_id = boost::lexical_cast<int>(t);
358 catch (boost::bad_lexical_cast e) {
365 Playlist::set_name (const string& str)
367 /* in a typical situation, a playlist is being used
368 by one diskstream and also is referenced by the
369 Session. if there are more references than that,
370 then don't change the name.
377 bool ret = SessionObject::set_name(str);
384 /***********************************************************************
385 CHANGE NOTIFICATION HANDLING
387 Notifications must be delayed till the region_lock is released. This
388 is necessary because handlers for the signals may need to acquire
389 the lock (e.g. to read from the playlist).
390 ***********************************************************************/
393 Playlist::begin_undo ()
400 Playlist::end_undo ()
409 delay_notifications ();
410 g_atomic_int_inc (&ignore_state_changes);
413 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
415 Playlist::thaw (bool from_undo)
417 g_atomic_int_dec_and_test (&ignore_state_changes);
418 release_notifications (from_undo);
423 Playlist::delay_notifications ()
425 g_atomic_int_inc (&block_notifications);
428 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
430 Playlist::release_notifications (bool from_undo)
432 if (g_atomic_int_dec_and_test (&block_notifications)) {
433 flush_notifications (from_undo);
438 Playlist::notify_contents_changed ()
440 if (holding_state ()) {
441 pending_contents_change = true;
443 pending_contents_change = false;
444 ContentsChanged(); /* EMIT SIGNAL */
449 Playlist::notify_layering_changed ()
451 if (holding_state ()) {
452 pending_layering = true;
454 pending_layering = false;
455 LayeringChanged(); /* EMIT SIGNAL */
460 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
462 if (holding_state ()) {
463 pending_removes.insert (r);
464 pending_contents_change = true;
466 /* this might not be true, but we have to act
467 as though it could be.
469 pending_contents_change = false;
470 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
471 ContentsChanged (); /* EMIT SIGNAL */
476 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
478 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
480 if (holding_state ()) {
482 pending_range_moves.push_back (move);
486 list< Evoral::RangeMove<framepos_t> > m;
488 RangesMoved (m, false);
494 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
496 if (r->position() >= r->last_position()) {
497 /* trimmed shorter */
501 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
503 if (holding_state ()) {
505 pending_region_extensions.push_back (extra);
509 list<Evoral::Range<framepos_t> > r;
517 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
519 if (r->length() < r->last_length()) {
520 /* trimmed shorter */
523 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
525 if (holding_state ()) {
527 pending_region_extensions.push_back (extra);
531 list<Evoral::Range<framepos_t> > r;
539 Playlist::notify_region_added (boost::shared_ptr<Region> r)
541 /* the length change might not be true, but we have to act
542 as though it could be.
545 if (holding_state()) {
546 pending_adds.insert (r);
547 pending_contents_change = true;
550 pending_contents_change = false;
551 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
552 ContentsChanged (); /* EMIT SIGNAL */
556 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
558 Playlist::flush_notifications (bool from_undo)
560 set<boost::shared_ptr<Region> >::iterator s;
561 bool regions_changed = false;
569 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
570 regions_changed = true;
573 /* XXX: it'd be nice if we could use pending_bounds for
574 RegionsExtended and RegionsMoved.
577 /* we have no idea what order the regions ended up in pending
578 bounds (it could be based on selection order, for example).
579 so, to preserve layering in the "most recently moved is higher"
580 model, sort them by existing layer, then timestamp them.
583 // RegionSortByLayer cmp;
584 // pending_bounds.sort (cmp);
586 list<Evoral::Range<framepos_t> > crossfade_ranges;
588 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
589 crossfade_ranges.push_back ((*r)->last_range ());
590 crossfade_ranges.push_back ((*r)->range ());
593 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
594 crossfade_ranges.push_back ((*s)->range ());
595 remove_dependents (*s);
596 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
599 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
600 crossfade_ranges.push_back ((*s)->range ());
601 /* don't emit RegionAdded signal until relayering is done,
602 so that the region is fully setup by the time
603 anyone hears that its been added
607 if (((regions_changed || pending_contents_change) && !in_set_state) || pending_layering) {
611 if (regions_changed || pending_contents_change) {
612 pending_contents_change = false;
613 ContentsChanged (); /* EMIT SIGNAL */
616 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
617 (*s)->clear_changes ();
618 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
621 coalesce_and_check_crossfades (crossfade_ranges);
623 if (!pending_range_moves.empty ()) {
624 /* We don't need to check crossfades for these as pending_bounds has
627 RangesMoved (pending_range_moves, from_undo);
630 if (!pending_region_extensions.empty ()) {
631 RegionsExtended (pending_region_extensions);
640 Playlist::clear_pending ()
642 pending_adds.clear ();
643 pending_removes.clear ();
644 pending_bounds.clear ();
645 pending_range_moves.clear ();
646 pending_region_extensions.clear ();
647 pending_contents_change = false;
650 /*************************************************************
652 *************************************************************/
654 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
656 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
658 RegionWriteLock rlock (this);
659 times = fabs (times);
661 int itimes = (int) floor (times);
663 framepos_t pos = position;
665 if (times == 1 && auto_partition){
666 partition(pos - 1, (pos + region->length()), true);
670 add_region_internal (region, pos);
671 set_layer (region, DBL_MAX);
672 pos += region->length();
677 /* note that itimes can be zero if we being asked to just
678 insert a single fraction of the region.
681 for (int i = 0; i < itimes; ++i) {
682 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
683 add_region_internal (copy, pos);
684 set_layer (copy, DBL_MAX);
685 pos += region->length();
688 framecnt_t length = 0;
690 if (floor (times) != times) {
691 length = (framecnt_t) floor (region->length() * (times - floor (times)));
693 RegionFactory::region_name (name, region->name(), false);
698 plist.add (Properties::start, region->start());
699 plist.add (Properties::length, length);
700 plist.add (Properties::name, name);
701 plist.add (Properties::layer, region->layer());
703 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
704 add_region_internal (sub, pos);
705 set_layer (sub, DBL_MAX);
709 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
713 Playlist::set_region_ownership ()
715 RegionWriteLock rl (this);
716 RegionList::iterator i;
717 boost::weak_ptr<Playlist> pl (shared_from_this());
719 for (i = regions.begin(); i != regions.end(); ++i) {
720 (*i)->set_playlist (pl);
725 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
727 if (region->data_type() != _type) {
731 RegionSortByPosition cmp;
733 if (!first_set_state) {
734 boost::shared_ptr<Playlist> foo (shared_from_this());
735 region->set_playlist (boost::weak_ptr<Playlist>(foo));
738 region->set_position (position);
740 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
741 all_regions.insert (region);
743 possibly_splice_unlocked (position, region->length(), region);
745 if (!holding_state ()) {
746 /* layers get assigned from XML state, and are not reset during undo/redo */
750 /* we need to notify the existence of new region before checking dependents. Ick. */
752 notify_region_added (region);
754 if (!holding_state ()) {
755 check_crossfades (region->range ());
758 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
764 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
766 RegionWriteLock rlock (this);
768 bool old_sp = _splicing;
771 remove_region_internal (old);
772 add_region_internal (newr, pos);
773 set_layer (newr, old->layer ());
777 possibly_splice_unlocked (pos, old->length() - newr->length());
781 Playlist::remove_region (boost::shared_ptr<Region> region)
783 RegionWriteLock rlock (this);
784 remove_region_internal (region);
788 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
790 RegionList::iterator i;
794 region->set_playlist (boost::weak_ptr<Playlist>());
797 /* XXX should probably freeze here .... */
799 for (i = regions.begin(); i != regions.end(); ++i) {
802 framepos_t pos = (*i)->position();
803 framecnt_t distance = (*i)->length();
807 possibly_splice_unlocked (pos, -distance);
809 if (!holding_state ()) {
811 remove_dependents (region);
814 notify_region_removed (region);
823 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
825 if (Config->get_use_overlap_equivalency()) {
826 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
827 if ((*i)->overlap_equivalent (other)) {
828 results.push_back (*i);
832 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
833 if ((*i)->equivalent (other)) {
834 results.push_back (*i);
841 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
843 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
845 if ((*i) && (*i)->region_list_equivalent (other)) {
846 results.push_back (*i);
852 Playlist::partition (framepos_t start, framepos_t end, bool cut)
856 partition_internal (start, end, cut, thawlist);
858 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
859 (*i)->resume_property_changes ();
863 /** Go through each region on the playlist and cut them at start and end, removing the section between
864 * start and end if cutting == true. Regions that lie entirely within start and end are always
869 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
871 RegionList new_regions;
874 RegionWriteLock rlock (this);
876 boost::shared_ptr<Region> region;
877 boost::shared_ptr<Region> current;
879 RegionList::iterator tmp;
880 Evoral::OverlapType overlap;
881 framepos_t pos1, pos2, pos3, pos4;
885 /* need to work from a copy, because otherwise the regions we add during the process
886 get operated on as well.
889 RegionList copy = regions.rlist();
891 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
898 if (current->first_frame() >= start && current->last_frame() < end) {
901 remove_region_internal (current);
907 /* coverage will return OverlapStart if the start coincides
908 with the end point. we do not partition such a region,
909 so catch this special case.
912 if (current->first_frame() >= end) {
916 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
920 pos1 = current->position();
923 pos4 = current->last_frame();
925 if (overlap == Evoral::OverlapInternal) {
926 /* split: we need 3 new regions, the front, middle and end.
927 cut: we need 2 regions, the front and end.
932 ---------------*************************------------
935 ---------------*****++++++++++++++++====------------
937 ---------------*****----------------====------------
942 /* "middle" ++++++ */
944 RegionFactory::region_name (new_name, current->name(), false);
948 plist.add (Properties::start, current->start() + (pos2 - pos1));
949 plist.add (Properties::length, pos3 - pos2);
950 plist.add (Properties::name, new_name);
951 plist.add (Properties::layer, current->layer ());
952 plist.add (Properties::layering_index, current->layering_index ());
953 plist.add (Properties::automatic, true);
954 plist.add (Properties::left_of_split, true);
955 plist.add (Properties::right_of_split, true);
957 region = RegionFactory::create (current, plist);
958 add_region_internal (region, start);
959 new_regions.push_back (region);
964 RegionFactory::region_name (new_name, current->name(), false);
968 plist.add (Properties::start, current->start() + (pos3 - pos1));
969 plist.add (Properties::length, pos4 - pos3);
970 plist.add (Properties::name, new_name);
971 plist.add (Properties::layer, current->layer ());
972 plist.add (Properties::layering_index, current->layering_index ());
973 plist.add (Properties::automatic, true);
974 plist.add (Properties::right_of_split, true);
976 region = RegionFactory::create (current, plist);
978 add_region_internal (region, end);
979 new_regions.push_back (region);
983 current->suspend_property_changes ();
984 thawlist.push_back (current);
985 current->cut_end (pos2 - 1);
987 } else if (overlap == Evoral::OverlapEnd) {
991 ---------------*************************------------
994 ---------------**************+++++++++++------------
996 ---------------**************-----------------------
1003 RegionFactory::region_name (new_name, current->name(), false);
1007 plist.add (Properties::start, current->start() + (pos2 - pos1));
1008 plist.add (Properties::length, pos4 - pos2);
1009 plist.add (Properties::name, new_name);
1010 plist.add (Properties::layer, current->layer ());
1011 plist.add (Properties::layering_index, current->layering_index ());
1012 plist.add (Properties::automatic, true);
1013 plist.add (Properties::left_of_split, true);
1015 region = RegionFactory::create (current, plist);
1017 add_region_internal (region, start);
1018 new_regions.push_back (region);
1023 current->suspend_property_changes ();
1024 thawlist.push_back (current);
1025 current->cut_end (pos2 - 1);
1027 } else if (overlap == Evoral::OverlapStart) {
1029 /* split: we need 2 regions: the front and the end.
1030 cut: just trim current to skip the cut area
1035 ---------------*************************------------
1039 ---------------****+++++++++++++++++++++------------
1041 -------------------*********************------------
1047 RegionFactory::region_name (new_name, current->name(), false);
1051 plist.add (Properties::start, current->start());
1052 plist.add (Properties::length, pos3 - pos1);
1053 plist.add (Properties::name, new_name);
1054 plist.add (Properties::layer, current->layer ());
1055 plist.add (Properties::layering_index, current->layering_index ());
1056 plist.add (Properties::automatic, true);
1057 plist.add (Properties::right_of_split, true);
1059 region = RegionFactory::create (current, plist);
1061 add_region_internal (region, pos1);
1062 new_regions.push_back (region);
1067 current->suspend_property_changes ();
1068 thawlist.push_back (current);
1069 current->trim_front (pos3);
1070 } else if (overlap == Evoral::OverlapExternal) {
1072 /* split: no split required.
1073 cut: remove the region.
1078 ---------------*************************------------
1082 ---------------*************************------------
1084 ----------------------------------------------------
1089 remove_region_internal (current);
1092 new_regions.push_back (current);
1096 in_partition = false;
1099 check_crossfades (Evoral::Range<framepos_t> (start, end));
1102 boost::shared_ptr<Playlist>
1103 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1105 boost::shared_ptr<Playlist> ret;
1106 boost::shared_ptr<Playlist> pl;
1109 if (ranges.empty()) {
1110 return boost::shared_ptr<Playlist>();
1113 start = ranges.front().start;
1115 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1117 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1119 if (i == ranges.begin()) {
1123 /* paste the next section into the nascent playlist,
1124 offset to reflect the start of the first range we
1128 ret->paste (pl, (*i).start - start, 1.0f);
1135 boost::shared_ptr<Playlist>
1136 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1138 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1139 return cut_copy (pmf, ranges, result_is_hidden);
1142 boost::shared_ptr<Playlist>
1143 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1145 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1146 return cut_copy (pmf, ranges, result_is_hidden);
1149 boost::shared_ptr<Playlist>
1150 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1152 boost::shared_ptr<Playlist> the_copy;
1153 RegionList thawlist;
1156 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1157 string new_name = _name;
1161 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1162 return boost::shared_ptr<Playlist>();
1165 partition_internal (start, start+cnt-1, true, thawlist);
1167 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1168 (*i)->resume_property_changes();
1174 boost::shared_ptr<Playlist>
1175 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1179 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1180 string new_name = _name;
1184 cnt = min (_get_extent().second - start, cnt);
1185 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1189 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1191 times = fabs (times);
1194 RegionReadLock rl2 (other.get());
1196 int itimes = (int) floor (times);
1197 framepos_t pos = position;
1198 framecnt_t const shift = other->_get_extent().second;
1199 layer_t top = top_layer ();
1202 RegionWriteLock rl1 (this);
1204 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1205 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1207 /* put these new regions on top of all existing ones, but preserve
1208 the ordering they had in the original playlist.
1211 add_region_internal (copy_of_region, (*i)->position() + pos);
1212 set_layer (copy_of_region, copy_of_region->layer() + top);
1224 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1226 times = fabs (times);
1228 RegionWriteLock rl (this);
1229 int itimes = (int) floor (times);
1230 framepos_t pos = position + 1;
1233 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1234 add_region_internal (copy, pos);
1235 set_layer (copy, DBL_MAX);
1236 pos += region->length();
1239 if (floor (times) != times) {
1240 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1242 RegionFactory::region_name (name, region->name(), false);
1247 plist.add (Properties::start, region->start());
1248 plist.add (Properties::length, length);
1249 plist.add (Properties::name, name);
1251 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1252 add_region_internal (sub, pos);
1253 set_layer (sub, DBL_MAX);
1259 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1261 RegionWriteLock rlock (this);
1262 RegionList copy (regions.rlist());
1265 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1267 if ((*r)->last_frame() < at) {
1272 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1273 /* intersected region */
1274 if (!move_intersected) {
1279 /* do not move regions glued to music time - that
1280 has to be done separately.
1283 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1284 fixup.push_back (*r);
1288 (*r)->set_position ((*r)->position() + distance);
1291 /* XXX: may not be necessary; Region::post_set should do this, I think */
1292 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1293 (*r)->recompute_position_from_lock_style ();
1298 Playlist::split (framepos_t at)
1300 RegionWriteLock rlock (this);
1301 RegionList copy (regions.rlist());
1303 /* use a copy since this operation can modify the region list
1306 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1307 _split_region (*r, at);
1312 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1314 RegionWriteLock rl (this);
1315 _split_region (region, playlist_position);
1319 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1321 if (!region->covers (playlist_position)) {
1325 if (region->position() == playlist_position ||
1326 region->last_frame() == playlist_position) {
1330 boost::shared_ptr<Region> left;
1331 boost::shared_ptr<Region> right;
1332 frameoffset_t before;
1333 frameoffset_t after;
1337 /* split doesn't change anything about length, so don't try to splice */
1339 bool old_sp = _splicing;
1342 before = playlist_position - region->position();
1343 after = region->length() - before;
1345 RegionFactory::region_name (before_name, region->name(), false);
1350 plist.add (Properties::position, region->position ());
1351 plist.add (Properties::length, before);
1352 plist.add (Properties::name, before_name);
1353 plist.add (Properties::left_of_split, true);
1354 plist.add (Properties::layering_index, region->layering_index ());
1355 plist.add (Properties::layer, region->layer ());
1357 /* note: we must use the version of ::create with an offset here,
1358 since it supplies that offset to the Region constructor, which
1359 is necessary to get audio region gain envelopes right.
1361 left = RegionFactory::create (region, 0, plist);
1364 RegionFactory::region_name (after_name, region->name(), false);
1369 plist.add (Properties::position, region->position() + before);
1370 plist.add (Properties::length, after);
1371 plist.add (Properties::name, after_name);
1372 plist.add (Properties::right_of_split, true);
1373 plist.add (Properties::layering_index, region->layering_index ());
1374 plist.add (Properties::layer, region->layer ());
1376 /* same note as above */
1377 right = RegionFactory::create (region, before, plist);
1380 add_region_internal (left, region->position());
1381 add_region_internal (right, region->position() + before);
1382 remove_region_internal (region);
1388 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1390 if (_splicing || in_set_state) {
1391 /* don't respond to splicing moves or state setting */
1395 if (_edit_mode == Splice) {
1396 splice_locked (at, distance, exclude);
1401 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1403 if (_splicing || in_set_state) {
1404 /* don't respond to splicing moves or state setting */
1408 if (_edit_mode == Splice) {
1409 splice_unlocked (at, distance, exclude);
1414 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1417 RegionWriteLock rl (this);
1418 core_splice (at, distance, exclude);
1423 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1425 core_splice (at, distance, exclude);
1429 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1433 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1435 if (exclude && (*i) == exclude) {
1439 if ((*i)->position() >= at) {
1440 framepos_t new_pos = (*i)->position() + distance;
1443 } else if (new_pos >= max_framepos - (*i)->length()) {
1444 new_pos = max_framepos - (*i)->length();
1447 (*i)->set_position (new_pos);
1453 notify_contents_changed ();
1457 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1459 if (in_set_state || _splicing || _nudging || _shuffling) {
1463 if (what_changed.contains (Properties::position)) {
1465 /* remove it from the list then add it back in
1466 the right place again.
1469 RegionSortByPosition cmp;
1471 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1473 if (i == regions.end()) {
1474 /* the region bounds are being modified but its not currently
1475 in the region list. we will use its bounds correctly when/if
1482 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1485 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1487 frameoffset_t delta = 0;
1489 if (what_changed.contains (Properties::position)) {
1490 delta = region->position() - region->last_position();
1493 if (what_changed.contains (Properties::length)) {
1494 delta += region->length() - region->last_length();
1498 possibly_splice (region->last_position() + region->last_length(), delta, region);
1501 if (holding_state ()) {
1502 pending_bounds.push_back (region);
1504 notify_contents_changed ();
1506 list<Evoral::Range<framepos_t> > xf;
1507 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1508 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1509 coalesce_and_check_crossfades (xf);
1515 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1517 boost::shared_ptr<Region> region (weak_region.lock());
1523 /* this makes a virtual call to the right kind of playlist ... */
1525 region_changed (what_changed, region);
1529 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1531 PropertyChange our_interests;
1532 PropertyChange bounds;
1533 PropertyChange pos_and_length;
1536 if (in_set_state || in_flush) {
1540 our_interests.add (Properties::muted);
1541 our_interests.add (Properties::layer);
1542 our_interests.add (Properties::opaque);
1544 bounds.add (Properties::start);
1545 bounds.add (Properties::position);
1546 bounds.add (Properties::length);
1548 pos_and_length.add (Properties::position);
1549 pos_and_length.add (Properties::length);
1551 if (what_changed.contains (bounds)) {
1552 region_bounds_changed (what_changed, region);
1553 save = !(_splicing || _nudging);
1556 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1557 check_crossfades (region->range ());
1560 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1561 notify_region_moved (region);
1562 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1563 notify_region_end_trimmed (region);
1564 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1565 notify_region_start_trimmed (region);
1568 /* don't notify about layer changes, since we are the only object that can initiate
1569 them, and we notify in ::relayer()
1572 if (what_changed.contains (our_interests)) {
1580 Playlist::drop_regions ()
1582 RegionWriteLock rl (this);
1584 all_regions.clear ();
1588 Playlist::sync_all_regions_with_regions ()
1590 RegionWriteLock rl (this);
1592 all_regions.clear ();
1594 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1595 all_regions.insert (*i);
1600 Playlist::clear (bool with_signals)
1603 RegionWriteLock rl (this);
1605 region_state_changed_connections.drop_connections ();
1607 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1608 pending_removes.insert (*i);
1613 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1614 remove_dependents (*s);
1620 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1621 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1624 pending_removes.clear ();
1625 pending_contents_change = false;
1631 /***********************************************************************
1633 **********************************************************************/
1635 boost::shared_ptr<RegionList>
1636 Playlist::regions_at (framepos_t frame)
1638 RegionReadLock rlock (this);
1639 return find_regions_at (frame);
1643 Playlist::count_regions_at (framepos_t frame) const
1645 RegionReadLock rlock (const_cast<Playlist*>(this));
1648 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1649 if ((*i)->covers (frame)) {
1657 boost::shared_ptr<Region>
1658 Playlist::top_region_at (framepos_t frame)
1661 RegionReadLock rlock (this);
1662 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1663 boost::shared_ptr<Region> region;
1665 if (rlist->size()) {
1666 RegionSortByLayer cmp;
1668 region = rlist->back();
1674 boost::shared_ptr<Region>
1675 Playlist::top_unmuted_region_at (framepos_t frame)
1678 RegionReadLock rlock (this);
1679 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1681 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1683 RegionList::iterator tmp = i;
1686 if ((*i)->muted()) {
1693 boost::shared_ptr<Region> region;
1695 if (rlist->size()) {
1696 RegionSortByLayer cmp;
1698 region = rlist->back();
1704 boost::shared_ptr<RegionList>
1705 Playlist::find_regions_at (framepos_t frame)
1707 /* Caller must hold lock */
1709 boost::shared_ptr<RegionList> rlist (new RegionList);
1711 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1712 if ((*i)->covers (frame)) {
1713 rlist->push_back (*i);
1720 boost::shared_ptr<RegionList>
1721 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1723 RegionReadLock rlock (this);
1724 boost::shared_ptr<RegionList> rlist (new RegionList);
1726 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1727 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1728 rlist->push_back (*i);
1735 boost::shared_ptr<RegionList>
1736 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1738 RegionReadLock rlock (this);
1739 boost::shared_ptr<RegionList> rlist (new RegionList);
1741 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1742 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1743 rlist->push_back (*i);
1750 /** @param start Range start.
1751 * @param end Range end.
1752 * @return regions which have some part within this range.
1754 boost::shared_ptr<RegionList>
1755 Playlist::regions_touched (framepos_t start, framepos_t end)
1757 RegionReadLock rlock (this);
1758 return regions_touched_locked (start, end);
1761 boost::shared_ptr<RegionList>
1762 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
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 RegionReadLock 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 RegionReadLock 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) {
1896 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1898 RegionReadLock rlock (this);
1900 framepos_t closest = max_framepos;
1901 framepos_t ret = -1;
1905 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1907 boost::shared_ptr<Region> r = (*i);
1908 frameoffset_t distance;
1910 if (r->first_frame() > frame) {
1912 distance = r->first_frame() - frame;
1914 if (distance < closest) {
1915 ret = r->first_frame();
1920 if (r->last_frame () > frame) {
1922 distance = r->last_frame () - frame;
1924 if (distance < closest) {
1925 ret = r->last_frame ();
1933 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1935 boost::shared_ptr<Region> r = (*i);
1936 frameoffset_t distance;
1938 if (r->last_frame() < frame) {
1940 distance = frame - r->last_frame();
1942 if (distance < closest) {
1943 ret = r->last_frame();
1948 if (r->first_frame() < frame) {
1950 distance = frame - r->first_frame();
1952 if (distance < closest) {
1953 ret = r->first_frame();
1964 /***********************************************************************/
1970 Playlist::mark_session_dirty ()
1972 if (!in_set_state && !holding_state ()) {
1973 _session.set_dirty();
1978 Playlist::rdiff (vector<Command*>& cmds) const
1980 RegionReadLock rlock (const_cast<Playlist *> (this));
1981 Stateful::rdiff (cmds);
1985 Playlist::clear_owned_changes ()
1987 RegionReadLock rlock (this);
1988 Stateful::clear_owned_changes ();
1992 Playlist::update (const RegionListProperty::ChangeRecord& change)
1994 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
1995 name(), change.added.size(), change.removed.size()));
1998 /* add the added regions */
1999 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2000 add_region_internal ((*i), (*i)->position());
2002 /* remove the removed regions */
2003 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2011 Playlist::set_state (const XMLNode& node, int version)
2015 XMLNodeConstIterator niter;
2016 XMLPropertyList plist;
2017 XMLPropertyConstIterator piter;
2019 boost::shared_ptr<Region> region;
2021 bool seen_region_nodes = false;
2026 if (node.name() != "Playlist") {
2033 plist = node.properties();
2037 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2041 if (prop->name() == X_("name")) {
2042 _name = prop->value();
2044 } else if (prop->name() == X_("orig-diskstream-id")) {
2045 /* XXX legacy session: fix up later */
2046 _orig_track_id = prop->value ();
2047 } else if (prop->name() == X_("orig-track-id")) {
2048 _orig_track_id = prop->value ();
2049 } else if (prop->name() == X_("frozen")) {
2050 _frozen = string_is_affirmative (prop->value());
2051 } else if (prop->name() == X_("combine-ops")) {
2052 _combine_ops = atoi (prop->value());
2058 nlist = node.children();
2060 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2064 if (child->name() == "Region") {
2066 seen_region_nodes = true;
2068 if ((prop = child->property ("id")) == 0) {
2069 error << _("region state node has no ID, ignored") << endmsg;
2073 ID id = prop->value ();
2075 if ((region = region_by_id (id))) {
2077 region->suspend_property_changes ();
2079 if (region->set_state (*child, version)) {
2080 region->resume_property_changes ();
2084 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2085 region->suspend_property_changes ();
2087 error << _("Playlist: cannot create region from XML") << endmsg;
2092 RegionWriteLock rlock (this);
2093 add_region_internal (region, region->position());
2096 region->resume_property_changes ();
2101 if (seen_region_nodes && regions.empty()) {
2105 /* update dependents, which was not done during add_region_internal
2106 due to in_set_state being true
2109 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2110 check_crossfades ((*r)->range ());
2115 notify_contents_changed ();
2118 first_set_state = false;
2124 Playlist::get_state()
2126 return state (true);
2130 Playlist::get_template()
2132 return state (false);
2135 /** @param full_state true to include regions in the returned state, otherwise false.
2138 Playlist::state (bool full_state)
2140 XMLNode *node = new XMLNode (X_("Playlist"));
2143 node->add_property (X_("id"), id().to_s());
2144 node->add_property (X_("name"), _name);
2145 node->add_property (X_("type"), _type.to_string());
2147 _orig_track_id.print (buf, sizeof (buf));
2148 node->add_property (X_("orig-track-id"), buf);
2149 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2152 RegionReadLock rlock (this);
2154 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2155 node->add_property ("combine-ops", buf);
2157 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2158 node->add_child_nocopy ((*i)->get_state());
2163 node->add_child_copy (*_extra_xml);
2170 Playlist::empty() const
2172 RegionReadLock rlock (const_cast<Playlist *>(this));
2173 return regions.empty();
2177 Playlist::n_regions() const
2179 RegionReadLock rlock (const_cast<Playlist *>(this));
2180 return regions.size();
2183 /** @return true if the all_regions list is empty, ie this playlist
2184 * has never had a region added to it.
2187 Playlist::all_regions_empty() const
2189 RegionReadLock rl (const_cast<Playlist *> (this));
2190 return all_regions.empty();
2193 pair<framepos_t, framepos_t>
2194 Playlist::get_extent () const
2196 RegionReadLock rlock (const_cast<Playlist *>(this));
2197 return _get_extent ();
2200 pair<framepos_t, framepos_t>
2201 Playlist::_get_extent () const
2203 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2205 if (regions.empty()) {
2210 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2211 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2212 if (e.first < ext.first) {
2213 ext.first = e.first;
2215 if (e.second > ext.second) {
2216 ext.second = e.second;
2224 Playlist::bump_name (string name, Session &session)
2226 string newname = name;
2229 newname = bump_name_once (newname, '.');
2230 } while (session.playlists->by_name (newname)!=NULL);
2237 Playlist::top_layer() const
2239 RegionReadLock rlock (const_cast<Playlist *> (this));
2242 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2243 top = max (top, (*i)->layer());
2249 Playlist::set_edit_mode (EditMode mode)
2254 struct RelayerSort {
2255 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2256 return a->layering_index() < b->layering_index();
2260 /** Set a new layer for a region. This adjusts the layering indices of all
2261 * regions in the playlist to put the specified region in the appropriate
2262 * place. The actual layering will be fixed up when relayer() happens.
2266 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2268 /* Remove the layer we are setting from our region list, and sort it */
2269 RegionList copy = regions.rlist();
2270 copy.remove (region);
2271 copy.sort (RelayerSort ());
2273 /* Put region back in the right place */
2274 RegionList::iterator i = copy.begin();
2275 while (i != copy.end ()) {
2276 if ((*i)->layer() > new_layer) {
2282 copy.insert (i, region);
2284 setup_layering_indices (copy);
2288 Playlist::setup_layering_indices (RegionList const & regions)
2291 list<Evoral::Range<framepos_t> > xf;
2293 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2294 (*k)->set_layering_index (j++);
2298 /** Take the layering indices of each of our regions, compute the layers
2299 * that they should be on, and write the layers back to the regions.
2302 Playlist::relayer ()
2304 /* never compute layers when setting from XML */
2310 /* Build up a new list of regions on each layer, stored in a set of lists
2311 each of which represent some period of time on some layer. The idea
2312 is to avoid having to search the entire region list to establish whether
2313 each region overlaps another */
2315 /* how many pieces to divide this playlist's time up into */
2316 int const divisions = 512;
2318 /* find the start and end positions of the regions on this playlist */
2319 framepos_t start = INT64_MAX;
2321 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2322 start = min (start, (*i)->position());
2323 end = max (end, (*i)->position() + (*i)->length());
2326 /* hence the size of each time division */
2327 double const division_size = (end - start) / double (divisions);
2329 vector<vector<RegionList> > layers;
2330 layers.push_back (vector<RegionList> (divisions));
2332 /* Sort our regions into layering index order */
2333 RegionList copy = regions.rlist();
2334 copy.sort (RelayerSort ());
2336 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2337 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2338 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2341 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2343 /* find the time divisions that this region covers; if there are no regions on the list,
2344 division_size will equal 0 and in this case we'll just say that
2345 start_division = end_division = 0.
2347 int start_division = 0;
2348 int end_division = 0;
2350 if (division_size > 0) {
2351 start_division = floor ( ((*i)->position() - start) / division_size);
2352 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2353 if (end_division == divisions) {
2358 assert (divisions == 0 || end_division < divisions);
2360 /* find the lowest layer that this region can go on */
2361 size_t j = layers.size();
2363 /* try layer j - 1; it can go on if it overlaps no other region
2364 that is already on that layer
2367 bool overlap = false;
2368 for (int k = start_division; k <= end_division; ++k) {
2369 RegionList::iterator l = layers[j-1][k].begin ();
2370 while (l != layers[j-1][k].end()) {
2371 if ((*l)->overlap_equivalent (*i)) {
2384 /* overlap, so we must use layer j */
2391 if (j == layers.size()) {
2392 /* we need a new layer for this region */
2393 layers.push_back (vector<RegionList> (divisions));
2396 /* put a reference to this region in each of the divisions that it exists in */
2397 for (int k = start_division; k <= end_division; ++k) {
2398 layers[j][k].push_back (*i);
2401 (*i)->set_layer (j);
2404 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2405 relayering because we just removed the only region on the top layer, nothing will
2406 appear to have changed, but the StreamView must still sort itself out. We could
2407 probably keep a note of the top layer last time we relayered, and check that,
2408 but premature optimisation &c...
2410 notify_layering_changed ();
2412 /* This relayer() may have been called as a result of a region removal, in which
2413 case we need to setup layering indices to account for the one that has just
2416 setup_layering_indices (copy);
2420 Playlist::raise_region (boost::shared_ptr<Region> region)
2422 set_layer (region, region->layer() + 1.5);
2424 check_crossfades (region->range ());
2428 Playlist::lower_region (boost::shared_ptr<Region> region)
2430 set_layer (region, region->layer() - 1.5);
2432 check_crossfades (region->range ());
2436 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2438 set_layer (region, DBL_MAX);
2440 check_crossfades (region->range ());
2444 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2446 set_layer (region, -0.5);
2448 check_crossfades (region->range ());
2452 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2454 RegionList::iterator i;
2460 RegionWriteLock rlock (const_cast<Playlist *> (this));
2462 for (i = regions.begin(); i != regions.end(); ++i) {
2464 if ((*i)->position() >= start) {
2470 if ((*i)->last_frame() > max_framepos - distance) {
2471 new_pos = max_framepos - (*i)->length();
2473 new_pos = (*i)->position() + distance;
2478 if ((*i)->position() > distance) {
2479 new_pos = (*i)->position() - distance;
2485 (*i)->set_position (new_pos);
2493 notify_contents_changed ();
2499 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2501 RegionReadLock rlock (const_cast<Playlist*> (this));
2503 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2504 if ((*r)->uses_source (src)) {
2512 boost::shared_ptr<Region>
2513 Playlist::find_region (const ID& id) const
2515 RegionReadLock rlock (const_cast<Playlist*> (this));
2517 /* searches all regions currently in use by the playlist */
2519 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2520 if ((*i)->id() == id) {
2525 return boost::shared_ptr<Region> ();
2529 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2531 RegionReadLock rlock (const_cast<Playlist*> (this));
2534 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2543 boost::shared_ptr<Region>
2544 Playlist::region_by_id (const ID& id) const
2546 /* searches all regions ever added to this playlist */
2548 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2549 if ((*i)->id() == id) {
2553 return boost::shared_ptr<Region> ();
2557 Playlist::dump () const
2559 boost::shared_ptr<Region> r;
2561 cerr << "Playlist \"" << _name << "\" " << endl
2562 << regions.size() << " regions "
2565 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2567 cerr << " " << r->name() << " ["
2568 << r->start() << "+" << r->length()
2578 Playlist::set_frozen (bool yn)
2584 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2588 if (region->locked()) {
2595 RegionWriteLock rlock (const_cast<Playlist*> (this));
2600 RegionList::iterator next;
2602 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2603 if ((*i) == region) {
2607 if (next != regions.end()) {
2609 if ((*next)->locked()) {
2615 if ((*next)->position() != region->last_frame() + 1) {
2616 /* they didn't used to touch, so after shuffle,
2617 just have them swap positions.
2619 new_pos = (*next)->position();
2621 /* they used to touch, so after shuffle,
2622 make sure they still do. put the earlier
2623 region where the later one will end after
2626 new_pos = region->position() + (*next)->length();
2629 (*next)->set_position (region->position());
2630 region->set_position (new_pos);
2632 /* avoid a full sort */
2634 regions.erase (i); // removes the region from the list */
2636 regions.insert (next, region); // adds it back after next
2645 RegionList::iterator prev = regions.end();
2647 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2648 if ((*i) == region) {
2650 if (prev != regions.end()) {
2652 if ((*prev)->locked()) {
2657 if (region->position() != (*prev)->last_frame() + 1) {
2658 /* they didn't used to touch, so after shuffle,
2659 just have them swap positions.
2661 new_pos = region->position();
2663 /* they used to touch, so after shuffle,
2664 make sure they still do. put the earlier
2665 one where the later one will end after
2667 new_pos = (*prev)->position() + region->length();
2670 region->set_position ((*prev)->position());
2671 (*prev)->set_position (new_pos);
2673 /* avoid a full sort */
2675 regions.erase (i); // remove region
2676 regions.insert (prev, region); // insert region before prev
2692 notify_contents_changed();
2698 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2700 RegionReadLock rlock (const_cast<Playlist*> (this));
2702 if (regions.size() > 1) {
2710 Playlist::update_after_tempo_map_change ()
2712 RegionWriteLock rlock (const_cast<Playlist*> (this));
2713 RegionList copy (regions.rlist());
2717 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2718 (*i)->update_after_tempo_map_change ();
2725 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2727 RegionWriteLock rl (this, false);
2728 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2734 Playlist::has_region_at (framepos_t const p) const
2736 RegionReadLock (const_cast<Playlist *> (this));
2738 RegionList::const_iterator i = regions.begin ();
2739 while (i != regions.end() && !(*i)->covers (p)) {
2743 return (i != regions.end());
2746 /** Remove any region that uses a given source */
2748 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2750 RegionWriteLock rl (this);
2752 RegionList::iterator i = regions.begin();
2753 while (i != regions.end()) {
2754 RegionList::iterator j = i;
2757 if ((*i)->uses_source (s)) {
2758 remove_region_internal (*i);
2765 /** Look from a session frame time and find the start time of the next region
2766 * which is on the top layer of this playlist.
2767 * @param t Time to look from.
2768 * @return Position of next top-layered region, or max_framepos if there isn't one.
2771 Playlist::find_next_top_layer_position (framepos_t t) const
2773 RegionReadLock rlock (const_cast<Playlist *> (this));
2775 layer_t const top = top_layer ();
2777 RegionList copy = regions.rlist ();
2778 copy.sort (RegionSortByPosition ());
2780 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2781 if ((*i)->position() >= t && (*i)->layer() == top) {
2782 return (*i)->position();
2786 return max_framepos;
2789 boost::shared_ptr<Region>
2790 Playlist::combine (const RegionList& r)
2793 uint32_t channels = 0;
2795 framepos_t earliest_position = max_framepos;
2796 vector<TwoRegions> old_and_new_regions;
2797 vector<boost::shared_ptr<Region> > originals;
2798 vector<boost::shared_ptr<Region> > copies;
2801 uint32_t max_level = 0;
2803 /* find the maximum depth of all the regions we're combining */
2805 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2806 max_level = max (max_level, (*i)->max_source_level());
2809 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2810 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2812 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2814 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2815 earliest_position = min (earliest_position, (*i)->position());
2818 /* enable this so that we do not try to create xfades etc. as we add
2822 pl->in_partition = true;
2824 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2826 /* copy the region */
2828 boost::shared_ptr<Region> original_region = (*i);
2829 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2831 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2832 originals.push_back (original_region);
2833 copies.push_back (copied_region);
2835 RegionFactory::add_compound_association (original_region, copied_region);
2837 /* make position relative to zero */
2839 pl->add_region (copied_region, original_region->position() - earliest_position);
2840 copied_region->set_layer (original_region->layer ());
2842 /* use the maximum number of channels for any region */
2844 channels = max (channels, original_region->n_channels());
2846 /* it will go above the layer of the highest existing region */
2848 layer = max (layer, original_region->layer());
2851 pl->in_partition = false;
2853 pre_combine (copies);
2855 /* now create a new PlaylistSource for each channel in the new playlist */
2858 pair<framepos_t,framepos_t> extent = pl->get_extent();
2860 for (uint32_t chn = 0; chn < channels; ++chn) {
2861 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2865 /* now a new whole-file region using the list of sources */
2867 plist.add (Properties::start, 0);
2868 plist.add (Properties::length, extent.second);
2869 plist.add (Properties::name, parent_name);
2870 plist.add (Properties::whole_file, true);
2872 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2874 /* now the non-whole-file region that we will actually use in the
2879 plist.add (Properties::start, 0);
2880 plist.add (Properties::length, extent.second);
2881 plist.add (Properties::name, child_name);
2882 plist.add (Properties::layer, layer+1);
2884 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2886 /* remove all the selected regions from the current playlist
2891 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2895 /* do type-specific stuff with the originals and the new compound
2899 post_combine (originals, compound_region);
2901 /* add the new region at the right location */
2903 add_region (compound_region, earliest_position);
2909 return compound_region;
2913 Playlist::uncombine (boost::shared_ptr<Region> target)
2915 boost::shared_ptr<PlaylistSource> pls;
2916 boost::shared_ptr<const Playlist> pl;
2917 vector<boost::shared_ptr<Region> > originals;
2918 vector<TwoRegions> old_and_new_regions;
2920 // (1) check that its really a compound region
2922 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
2926 pl = pls->playlist();
2928 framepos_t adjusted_start = 0; // gcc isn't smart enough
2929 framepos_t adjusted_end = 0; // gcc isn't smart enough
2931 /* the leftmost (earliest) edge of the compound region
2932 starts at zero in its source, or larger if it
2933 has been trimmed or content-scrolled.
2935 the rightmost (latest) edge of the compound region
2936 relative to its source is the starting point plus
2937 the length of the region.
2940 // (2) get all the original regions
2942 const RegionList& rl (pl->region_list().rlist());
2943 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2944 frameoffset_t move_offset = 0;
2946 /* there are two possibilities here:
2947 1) the playlist that the playlist source was based on
2948 is us, so just add the originals (which belonged to
2949 us anyway) back in the right place.
2951 2) the playlist that the playlist source was based on
2952 is NOT us, so we need to make copies of each of
2953 the original regions that we find, and add them
2956 bool same_playlist = (pls->original() == id());
2958 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
2960 boost::shared_ptr<Region> current (*i);
2962 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
2964 if (ca == cassocs.end()) {
2968 boost::shared_ptr<Region> original (ca->second);
2969 bool modified_region;
2971 if (i == rl.begin()) {
2972 move_offset = (target->position() - original->position()) - target->start();
2973 adjusted_start = original->position() + target->start();
2974 adjusted_end = adjusted_start + target->length();
2977 if (!same_playlist) {
2978 framepos_t pos = original->position();
2979 /* make a copy, but don't announce it */
2980 original = RegionFactory::create (original, false);
2981 /* the pure copy constructor resets position() to zero,
2984 original->set_position (pos);
2987 /* check to see how the original region (in the
2988 * playlist before compounding occured) overlaps
2989 * with the new state of the compound region.
2992 original->clear_changes ();
2993 modified_region = false;
2995 switch (original->coverage (adjusted_start, adjusted_end)) {
2996 case Evoral::OverlapNone:
2997 /* original region does not cover any part
2998 of the current state of the compound region
3002 case Evoral::OverlapInternal:
3003 /* overlap is just a small piece inside the
3004 * original so trim both ends
3006 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3007 modified_region = true;
3010 case Evoral::OverlapExternal:
3011 /* overlap fully covers original, so leave it
3016 case Evoral::OverlapEnd:
3017 /* overlap starts within but covers end,
3018 so trim the front of the region
3020 original->trim_front (adjusted_start);
3021 modified_region = true;
3024 case Evoral::OverlapStart:
3025 /* overlap covers start but ends within, so
3026 * trim the end of the region.
3028 original->trim_end (adjusted_end);
3029 modified_region = true;
3034 /* fix the position to match any movement of the compound region.
3036 original->set_position (original->position() + move_offset);
3037 modified_region = true;
3040 if (modified_region) {
3041 _session.add_command (new StatefulDiffCommand (original));
3044 /* and add to the list of regions waiting to be
3048 originals.push_back (original);
3049 old_and_new_regions.push_back (TwoRegions (*i, original));
3052 pre_uncombine (originals, target);
3054 in_partition = true;
3057 // (3) remove the compound region
3059 remove_region (target);
3061 // (4) add the constituent regions
3063 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3064 add_region ((*i), (*i)->position());
3067 in_partition = false;
3072 Playlist::max_source_level () const
3074 RegionReadLock rlock (const_cast<Playlist *> (this));
3077 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3078 lvl = max (lvl, (*i)->max_source_level());
3085 Playlist::set_orig_track_id (const PBD::ID& id)
3087 _orig_track_id = id;
3090 /** Take a list of ranges, coalesce any that can be coalesced, then call
3091 * check_crossfades for each one.
3094 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3096 /* XXX: it's a shame that this coalesce algorithm also exists in
3097 TimeSelection::consolidate().
3100 /* XXX: xfade: this is implemented in Evoral::RangeList */
3103 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3104 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3110 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3111 i->from = min (i->from, j->from);
3112 i->to = max (i->to, j->to);
3119 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3120 check_crossfades (*i);
3125 Playlist::set_capture_insertion_in_progress (bool yn)
3127 _capture_insertion_underway = yn;