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 _rippling = other->_rippling;
176 _nudging = other->_nudging;
177 _edit_mode = other->_edit_mode;
180 first_set_state = false;
182 in_partition = false;
184 _frozen = other->_frozen;
187 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
188 : SessionObject(other->_session, str)
190 , _type(other->_type)
191 , _orig_track_id (other->_orig_track_id)
193 RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
195 framepos_t end = start + cnt - 1;
201 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
203 boost::shared_ptr<Region> region;
204 boost::shared_ptr<Region> new_region;
205 frameoffset_t offset = 0;
206 framepos_t position = 0;
209 Evoral::OverlapType overlap;
213 overlap = region->coverage (start, end);
216 case Evoral::OverlapNone:
219 case Evoral::OverlapInternal:
220 offset = start - region->position();
225 case Evoral::OverlapStart:
227 position = region->position() - start;
228 len = end - region->position();
231 case Evoral::OverlapEnd:
232 offset = start - region->position();
234 len = region->length() - offset;
237 case Evoral::OverlapExternal:
239 position = region->position() - start;
240 len = region->length();
244 RegionFactory::region_name (new_name, region->name(), false);
248 plist.add (Properties::start, region->start() + offset);
249 plist.add (Properties::length, len);
250 plist.add (Properties::name, new_name);
251 plist.add (Properties::layer, region->layer());
252 plist.add (Properties::layering_index, region->layering_index());
254 new_region = RegionFactory::create (region, plist);
256 add_region_internal (new_region, position);
259 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
260 //at the end of construction, any length of cnt beyond the extents of the regions is end_space
261 _end_space = cnt - (get_extent().second - get_extent().first);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionReadLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (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;
315 _edit_mode = Config->get_edit_mode();
317 in_partition = false;
320 _capture_insertion_underway = false;
324 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
325 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
327 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
330 Playlist::~Playlist ()
332 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
335 RegionReadLock rl (this);
337 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
338 (*i)->set_playlist (boost::shared_ptr<Playlist>());
342 /* GoingAway must be emitted by derived classes */
346 Playlist::_set_sort_id ()
349 Playlists are given names like <track name>.<id>
350 or <track name>.<edit group name>.<id> where id
351 is an integer. We extract the id and sort by that.
354 size_t dot_position = _name.val().find_last_of(".");
356 if (dot_position == string::npos) {
359 string t = _name.val().substr(dot_position + 1);
362 _sort_id = boost::lexical_cast<int>(t);
365 catch (boost::bad_lexical_cast e) {
372 Playlist::set_name (const string& str)
374 /* in a typical situation, a playlist is being used
375 by one diskstream and also is referenced by the
376 Session. if there are more references than that,
377 then don't change the name.
384 bool ret = SessionObject::set_name(str);
391 /***********************************************************************
392 CHANGE NOTIFICATION HANDLING
394 Notifications must be delayed till the region_lock is released. This
395 is necessary because handlers for the signals may need to acquire
396 the lock (e.g. to read from the playlist).
397 ***********************************************************************/
400 Playlist::begin_undo ()
407 Playlist::end_undo ()
416 delay_notifications ();
417 g_atomic_int_inc (&ignore_state_changes);
420 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
422 Playlist::thaw (bool from_undo)
424 g_atomic_int_dec_and_test (&ignore_state_changes);
425 release_notifications (from_undo);
430 Playlist::delay_notifications ()
432 g_atomic_int_inc (&block_notifications);
435 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
437 Playlist::release_notifications (bool from_undo)
439 if (g_atomic_int_dec_and_test (&block_notifications)) {
440 flush_notifications (from_undo);
445 Playlist::notify_contents_changed ()
447 if (holding_state ()) {
448 pending_contents_change = true;
450 pending_contents_change = false;
451 ContentsChanged(); /* EMIT SIGNAL */
456 Playlist::notify_layering_changed ()
458 if (holding_state ()) {
459 pending_layering = true;
461 pending_layering = false;
462 LayeringChanged(); /* EMIT SIGNAL */
467 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
469 if (holding_state ()) {
470 pending_removes.insert (r);
471 pending_contents_change = true;
473 /* this might not be true, but we have to act
474 as though it could be.
476 pending_contents_change = false;
477 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
478 ContentsChanged (); /* EMIT SIGNAL */
483 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
485 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
487 if (holding_state ()) {
489 pending_range_moves.push_back (move);
493 list< Evoral::RangeMove<framepos_t> > m;
495 RangesMoved (m, false);
501 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
503 if (r->position() >= r->last_position()) {
504 /* trimmed shorter */
508 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
510 if (holding_state ()) {
512 pending_region_extensions.push_back (extra);
516 list<Evoral::Range<framepos_t> > r;
524 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
526 if (r->length() < r->last_length()) {
527 /* trimmed shorter */
530 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
532 if (holding_state ()) {
534 pending_region_extensions.push_back (extra);
538 list<Evoral::Range<framepos_t> > r;
546 Playlist::notify_region_added (boost::shared_ptr<Region> r)
548 /* the length change might not be true, but we have to act
549 as though it could be.
552 if (holding_state()) {
553 pending_adds.insert (r);
554 pending_contents_change = true;
557 pending_contents_change = false;
558 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
559 ContentsChanged (); /* EMIT SIGNAL */
564 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
566 Playlist::flush_notifications (bool from_undo)
568 set<boost::shared_ptr<Region> >::iterator s;
569 bool regions_changed = false;
577 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
578 regions_changed = true;
581 /* XXX: it'd be nice if we could use pending_bounds for
582 RegionsExtended and RegionsMoved.
585 /* we have no idea what order the regions ended up in pending
586 bounds (it could be based on selection order, for example).
587 so, to preserve layering in the "most recently moved is higher"
588 model, sort them by existing layer, then timestamp them.
591 // RegionSortByLayer cmp;
592 // pending_bounds.sort (cmp);
594 list<Evoral::Range<framepos_t> > crossfade_ranges;
596 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
597 crossfade_ranges.push_back ((*r)->last_range ());
598 crossfade_ranges.push_back ((*r)->range ());
601 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
602 crossfade_ranges.push_back ((*s)->range ());
603 remove_dependents (*s);
604 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
607 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
608 crossfade_ranges.push_back ((*s)->range ());
609 /* don't emit RegionAdded signal until relayering is done,
610 so that the region is fully setup by the time
611 anyone hears that its been added
615 /* notify about contents/region changes first so that layering changes
616 * in a UI will take place on the new contents.
619 if (regions_changed || pending_contents_change) {
620 pending_layering = true;
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 if ((regions_changed && !in_set_state) || pending_layering) {
633 coalesce_and_check_crossfades (crossfade_ranges);
635 if (!pending_range_moves.empty ()) {
636 /* We don't need to check crossfades for these as pending_bounds has
639 RangesMoved (pending_range_moves, from_undo);
642 if (!pending_region_extensions.empty ()) {
643 RegionsExtended (pending_region_extensions);
652 Playlist::clear_pending ()
654 pending_adds.clear ();
655 pending_removes.clear ();
656 pending_bounds.clear ();
657 pending_range_moves.clear ();
658 pending_region_extensions.clear ();
659 pending_contents_change = false;
662 /*************************************************************
664 *************************************************************/
666 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
668 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
670 RegionWriteLock rlock (this);
671 times = fabs (times);
673 int itimes = (int) floor (times);
675 framepos_t pos = position;
677 if (times == 1 && auto_partition){
678 partition(pos - 1, (pos + region->length()), true);
682 add_region_internal (region, pos);
683 set_layer (region, DBL_MAX);
684 pos += region->length();
689 /* note that itimes can be zero if we being asked to just
690 insert a single fraction of the region.
693 for (int i = 0; i < itimes; ++i) {
694 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
695 add_region_internal (copy, pos);
696 set_layer (copy, DBL_MAX);
697 pos += region->length();
700 framecnt_t length = 0;
702 if (floor (times) != times) {
703 length = (framecnt_t) floor (region->length() * (times - floor (times)));
705 RegionFactory::region_name (name, region->name(), false);
710 plist.add (Properties::start, region->start());
711 plist.add (Properties::length, length);
712 plist.add (Properties::name, name);
713 plist.add (Properties::layer, region->layer());
715 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
716 add_region_internal (sub, pos);
717 set_layer (sub, DBL_MAX);
721 possibly_splice_unlocked (position, (pos + length) - position, region);
725 Playlist::set_region_ownership ()
727 RegionWriteLock rl (this);
728 RegionList::iterator i;
729 boost::weak_ptr<Playlist> pl (shared_from_this());
731 for (i = regions.begin(); i != regions.end(); ++i) {
732 (*i)->set_playlist (pl);
737 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
739 if (region->data_type() != _type) {
743 RegionSortByPosition cmp;
745 if (!first_set_state) {
746 boost::shared_ptr<Playlist> foo (shared_from_this());
747 region->set_playlist (boost::weak_ptr<Playlist>(foo));
750 region->set_position (position);
752 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
753 all_regions.insert (region);
755 possibly_splice_unlocked (position, region->length(), region);
757 if (!holding_state ()) {
758 /* layers get assigned from XML state, and are not reset during undo/redo */
762 /* we need to notify the existence of new region before checking dependents. Ick. */
764 notify_region_added (region);
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 RegionWriteLock 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 RegionWriteLock 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::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
862 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
864 if ((*i) && (*i)->any_source_equivalent (other)) {
865 results.push_back (*i);
871 Playlist::partition (framepos_t start, framepos_t end, bool cut)
875 partition_internal (start, end, cut, thawlist);
877 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
878 (*i)->resume_property_changes ();
882 /** Go through each region on the playlist and cut them at start and end, removing the section between
883 * start and end if cutting == true. Regions that lie entirely within start and end are always
888 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
890 RegionList new_regions;
893 RegionWriteLock rlock (this);
895 boost::shared_ptr<Region> region;
896 boost::shared_ptr<Region> current;
898 RegionList::iterator tmp;
899 Evoral::OverlapType overlap;
900 framepos_t pos1, pos2, pos3, pos4;
904 /* need to work from a copy, because otherwise the regions we add during the process
905 get operated on as well.
908 RegionList copy = regions.rlist();
910 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
917 if (current->first_frame() >= start && current->last_frame() < end) {
920 remove_region_internal (current);
926 /* coverage will return OverlapStart if the start coincides
927 with the end point. we do not partition such a region,
928 so catch this special case.
931 if (current->first_frame() >= end) {
935 if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
939 pos1 = current->position();
942 pos4 = current->last_frame();
944 if (overlap == Evoral::OverlapInternal) {
945 /* split: we need 3 new regions, the front, middle and end.
946 cut: we need 2 regions, the front and end.
951 ---------------*************************------------
954 ---------------*****++++++++++++++++====------------
956 ---------------*****----------------====------------
961 /* "middle" ++++++ */
963 RegionFactory::region_name (new_name, current->name(), false);
967 plist.add (Properties::start, current->start() + (pos2 - pos1));
968 plist.add (Properties::length, pos3 - pos2);
969 plist.add (Properties::name, new_name);
970 plist.add (Properties::layer, current->layer ());
971 plist.add (Properties::layering_index, current->layering_index ());
972 plist.add (Properties::automatic, true);
973 plist.add (Properties::left_of_split, true);
974 plist.add (Properties::right_of_split, true);
976 region = RegionFactory::create (current, plist);
977 add_region_internal (region, start);
978 new_regions.push_back (region);
983 RegionFactory::region_name (new_name, current->name(), false);
987 plist.add (Properties::start, current->start() + (pos3 - pos1));
988 plist.add (Properties::length, pos4 - pos3);
989 plist.add (Properties::name, new_name);
990 plist.add (Properties::layer, current->layer ());
991 plist.add (Properties::layering_index, current->layering_index ());
992 plist.add (Properties::automatic, true);
993 plist.add (Properties::right_of_split, true);
995 region = RegionFactory::create (current, plist);
997 add_region_internal (region, end);
998 new_regions.push_back (region);
1002 current->suspend_property_changes ();
1003 thawlist.push_back (current);
1004 current->cut_end (pos2 - 1);
1006 } else if (overlap == Evoral::OverlapEnd) {
1010 ---------------*************************------------
1013 ---------------**************+++++++++++------------
1015 ---------------**************-----------------------
1022 RegionFactory::region_name (new_name, current->name(), false);
1026 plist.add (Properties::start, current->start() + (pos2 - pos1));
1027 plist.add (Properties::length, pos4 - pos2);
1028 plist.add (Properties::name, new_name);
1029 plist.add (Properties::layer, current->layer ());
1030 plist.add (Properties::layering_index, current->layering_index ());
1031 plist.add (Properties::automatic, true);
1032 plist.add (Properties::left_of_split, true);
1034 region = RegionFactory::create (current, plist);
1036 add_region_internal (region, start);
1037 new_regions.push_back (region);
1042 current->suspend_property_changes ();
1043 thawlist.push_back (current);
1044 current->cut_end (pos2 - 1);
1046 } else if (overlap == Evoral::OverlapStart) {
1048 /* split: we need 2 regions: the front and the end.
1049 cut: just trim current to skip the cut area
1054 ---------------*************************------------
1058 ---------------****+++++++++++++++++++++------------
1060 -------------------*********************------------
1066 RegionFactory::region_name (new_name, current->name(), false);
1070 plist.add (Properties::start, current->start());
1071 plist.add (Properties::length, pos3 - pos1);
1072 plist.add (Properties::name, new_name);
1073 plist.add (Properties::layer, current->layer ());
1074 plist.add (Properties::layering_index, current->layering_index ());
1075 plist.add (Properties::automatic, true);
1076 plist.add (Properties::right_of_split, true);
1078 region = RegionFactory::create (current, plist);
1080 add_region_internal (region, pos1);
1081 new_regions.push_back (region);
1086 current->suspend_property_changes ();
1087 thawlist.push_back (current);
1088 current->trim_front (pos3);
1089 } else if (overlap == Evoral::OverlapExternal) {
1091 /* split: no split required.
1092 cut: remove the region.
1097 ---------------*************************------------
1101 ---------------*************************------------
1103 ----------------------------------------------------
1108 remove_region_internal (current);
1111 new_regions.push_back (current);
1115 in_partition = false;
1118 //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1119 framepos_t wanted_length = end-start;
1120 _end_space = wanted_length - get_extent().second-get_extent().first;
1123 boost::shared_ptr<Playlist>
1124 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1126 boost::shared_ptr<Playlist> ret;
1127 boost::shared_ptr<Playlist> pl;
1130 if (ranges.empty()) {
1131 return boost::shared_ptr<Playlist>();
1134 start = ranges.front().start;
1136 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1138 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1140 if (i == ranges.begin()) {
1144 /* paste the next section into the nascent playlist,
1145 offset to reflect the start of the first range we
1149 ret->paste (pl, (*i).start - start, 1.0f);
1156 boost::shared_ptr<Playlist>
1157 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1159 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1160 return cut_copy (pmf, ranges, result_is_hidden);
1163 boost::shared_ptr<Playlist>
1164 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1167 return cut_copy (pmf, ranges, result_is_hidden);
1170 boost::shared_ptr<Playlist>
1171 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> the_copy;
1174 RegionList thawlist;
1177 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1178 string new_name = _name;
1182 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1183 return boost::shared_ptr<Playlist>();
1186 partition_internal (start, start+cnt-1, true, thawlist);
1188 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1189 (*i)->resume_property_changes();
1195 boost::shared_ptr<Playlist>
1196 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1200 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1201 string new_name = _name;
1205 // cnt = min (_get_extent().second - start, cnt); (We need the full range length when copy/pasting in Ripple. Why was this limit here? It's not in CUT... )
1207 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1211 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1213 times = fabs (times);
1216 RegionReadLock rl2 (other.get());
1218 int itimes = (int) floor (times);
1219 framepos_t pos = position;
1220 framecnt_t const shift = other->_get_extent().second;
1221 layer_t top = top_layer ();
1224 RegionWriteLock rl1 (this);
1226 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1227 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1229 /* put these new regions on top of all existing ones, but preserve
1230 the ordering they had in the original playlist.
1233 add_region_internal (copy_of_region, (*i)->position() + pos);
1234 set_layer (copy_of_region, copy_of_region->layer() + top);
1246 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1248 duplicate(region, position, region->length(), times);
1251 /** @param gap from the beginning of the region to the next beginning */
1253 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1255 times = fabs (times);
1257 RegionWriteLock rl (this);
1258 int itimes = (int) floor (times);
1259 framepos_t pos = position + 1;
1262 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1263 add_region_internal (copy, pos);
1264 set_layer (copy, DBL_MAX);
1268 if (floor (times) != times) {
1269 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1271 RegionFactory::region_name (name, region->name(), false);
1276 plist.add (Properties::start, region->start());
1277 plist.add (Properties::length, length);
1278 plist.add (Properties::name, name);
1280 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1281 add_region_internal (sub, pos);
1282 set_layer (sub, DBL_MAX);
1288 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1290 RegionWriteLock rlock (this);
1291 RegionList copy (regions.rlist());
1294 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1296 if ((*r)->last_frame() < at) {
1301 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1302 /* intersected region */
1303 if (!move_intersected) {
1308 /* do not move regions glued to music time - that
1309 has to be done separately.
1312 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1313 fixup.push_back (*r);
1317 (*r)->set_position ((*r)->position() + distance);
1320 /* XXX: may not be necessary; Region::post_set should do this, I think */
1321 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1322 (*r)->recompute_position_from_lock_style ();
1327 Playlist::split (framepos_t at)
1329 RegionWriteLock rlock (this);
1330 RegionList copy (regions.rlist());
1332 /* use a copy since this operation can modify the region list
1335 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1336 _split_region (*r, at);
1341 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1343 RegionWriteLock rl (this);
1344 _split_region (region, playlist_position);
1348 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1350 if (!region->covers (playlist_position)) {
1354 if (region->position() == playlist_position ||
1355 region->last_frame() == playlist_position) {
1359 boost::shared_ptr<Region> left;
1360 boost::shared_ptr<Region> right;
1361 frameoffset_t before;
1362 frameoffset_t after;
1366 /* split doesn't change anything about length, so don't try to splice */
1368 bool old_sp = _splicing;
1371 before = playlist_position - region->position();
1372 after = region->length() - before;
1374 RegionFactory::region_name (before_name, region->name(), false);
1379 plist.add (Properties::position, region->position ());
1380 plist.add (Properties::length, before);
1381 plist.add (Properties::name, before_name);
1382 plist.add (Properties::left_of_split, true);
1383 plist.add (Properties::layering_index, region->layering_index ());
1384 plist.add (Properties::layer, region->layer ());
1386 /* note: we must use the version of ::create with an offset here,
1387 since it supplies that offset to the Region constructor, which
1388 is necessary to get audio region gain envelopes right.
1390 left = RegionFactory::create (region, 0, plist);
1393 RegionFactory::region_name (after_name, region->name(), false);
1398 plist.add (Properties::position, region->position() + before);
1399 plist.add (Properties::length, after);
1400 plist.add (Properties::name, after_name);
1401 plist.add (Properties::right_of_split, true);
1402 plist.add (Properties::layering_index, region->layering_index ());
1403 plist.add (Properties::layer, region->layer ());
1405 /* same note as above */
1406 right = RegionFactory::create (region, before, plist);
1409 add_region_internal (left, region->position());
1410 add_region_internal (right, region->position() + before);
1411 remove_region_internal (region);
1417 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1419 if (_splicing || in_set_state) {
1420 /* don't respond to splicing moves or state setting */
1424 if (_edit_mode == Splice) {
1425 splice_locked (at, distance, exclude);
1430 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1432 if (_splicing || in_set_state) {
1433 /* don't respond to splicing moves or state setting */
1437 if (_edit_mode == Splice) {
1438 splice_unlocked (at, distance, exclude);
1443 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1446 RegionWriteLock rl (this);
1447 core_splice (at, distance, exclude);
1452 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1454 core_splice (at, distance, exclude);
1458 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1462 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1464 if (exclude && (*i) == exclude) {
1468 if ((*i)->position() >= at) {
1469 framepos_t new_pos = (*i)->position() + distance;
1472 } else if (new_pos >= max_framepos - (*i)->length()) {
1473 new_pos = max_framepos - (*i)->length();
1476 (*i)->set_position (new_pos);
1482 notify_contents_changed ();
1486 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1489 RegionWriteLock rl (this);
1490 core_ripple (at, distance, exclude);
1495 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1497 core_ripple (at, distance, exclude);
1501 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1503 if (distance == 0) {
1508 RegionListProperty copy = regions;
1509 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1510 assert (i != copy.end());
1513 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1518 if ((*i)->position() >= at) {
1519 framepos_t new_pos = (*i)->position() + distance;
1520 framepos_t limit = max_framepos - (*i)->length();
1523 } else if (new_pos >= limit ) {
1527 (*i)->set_position (new_pos);
1532 notify_contents_changed ();
1537 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1539 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1543 if (what_changed.contains (Properties::position)) {
1545 /* remove it from the list then add it back in
1546 the right place again.
1549 RegionSortByPosition cmp;
1551 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1553 if (i == regions.end()) {
1554 /* the region bounds are being modified but its not currently
1555 in the region list. we will use its bounds correctly when/if
1562 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1565 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1567 frameoffset_t delta = 0;
1569 if (what_changed.contains (Properties::position)) {
1570 delta = region->position() - region->last_position();
1573 if (what_changed.contains (Properties::length)) {
1574 delta += region->length() - region->last_length();
1578 possibly_splice (region->last_position() + region->last_length(), delta, region);
1581 if (holding_state ()) {
1582 pending_bounds.push_back (region);
1584 notify_contents_changed ();
1586 list<Evoral::Range<framepos_t> > xf;
1587 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1588 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1589 coalesce_and_check_crossfades (xf);
1595 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1597 boost::shared_ptr<Region> region (weak_region.lock());
1603 /* this makes a virtual call to the right kind of playlist ... */
1605 region_changed (what_changed, region);
1609 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1611 PropertyChange our_interests;
1612 PropertyChange bounds;
1613 PropertyChange pos_and_length;
1616 if (in_set_state || in_flush) {
1620 our_interests.add (Properties::muted);
1621 our_interests.add (Properties::layer);
1622 our_interests.add (Properties::opaque);
1624 bounds.add (Properties::start);
1625 bounds.add (Properties::position);
1626 bounds.add (Properties::length);
1628 pos_and_length.add (Properties::position);
1629 pos_and_length.add (Properties::length);
1631 if (what_changed.contains (bounds)) {
1632 region_bounds_changed (what_changed, region);
1633 save = !(_splicing || _nudging);
1636 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1637 notify_region_moved (region);
1638 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1639 notify_region_end_trimmed (region);
1640 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1641 notify_region_start_trimmed (region);
1644 /* don't notify about layer changes, since we are the only object that can initiate
1645 them, and we notify in ::relayer()
1648 if (what_changed.contains (our_interests)) {
1652 mark_session_dirty ();
1658 Playlist::drop_regions ()
1660 RegionWriteLock rl (this);
1662 all_regions.clear ();
1666 Playlist::sync_all_regions_with_regions ()
1668 RegionWriteLock rl (this);
1670 all_regions.clear ();
1672 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1673 all_regions.insert (*i);
1678 Playlist::clear (bool with_signals)
1681 RegionWriteLock rl (this);
1683 region_state_changed_connections.drop_connections ();
1685 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1686 pending_removes.insert (*i);
1691 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1692 remove_dependents (*s);
1698 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1699 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1702 pending_removes.clear ();
1703 pending_contents_change = false;
1709 /***********************************************************************
1711 **********************************************************************/
1713 boost::shared_ptr<RegionList>
1714 Playlist::regions_at (framepos_t frame)
1716 RegionReadLock rlock (this);
1717 return find_regions_at (frame);
1721 Playlist::count_regions_at (framepos_t frame) const
1723 RegionReadLock rlock (const_cast<Playlist*>(this));
1726 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1727 if ((*i)->covers (frame)) {
1735 boost::shared_ptr<Region>
1736 Playlist::top_region_at (framepos_t frame)
1739 RegionReadLock rlock (this);
1740 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1741 boost::shared_ptr<Region> region;
1743 if (rlist->size()) {
1744 RegionSortByLayer cmp;
1746 region = rlist->back();
1752 boost::shared_ptr<Region>
1753 Playlist::top_unmuted_region_at (framepos_t frame)
1756 RegionReadLock rlock (this);
1757 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1759 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1761 RegionList::iterator tmp = i;
1764 if ((*i)->muted()) {
1771 boost::shared_ptr<Region> region;
1773 if (rlist->size()) {
1774 RegionSortByLayer cmp;
1776 region = rlist->back();
1782 boost::shared_ptr<RegionList>
1783 Playlist::find_regions_at (framepos_t frame)
1785 /* Caller must hold lock */
1787 boost::shared_ptr<RegionList> rlist (new RegionList);
1789 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1790 if ((*i)->covers (frame)) {
1791 rlist->push_back (*i);
1798 boost::shared_ptr<RegionList>
1799 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1801 RegionReadLock rlock (this);
1802 boost::shared_ptr<RegionList> rlist (new RegionList);
1804 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1805 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1806 rlist->push_back (*i);
1813 boost::shared_ptr<RegionList>
1814 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1816 RegionReadLock rlock (this);
1817 boost::shared_ptr<RegionList> rlist (new RegionList);
1819 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1820 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1821 rlist->push_back (*i);
1828 /** @param start Range start.
1829 * @param end Range end.
1830 * @return regions which have some part within this range.
1832 boost::shared_ptr<RegionList>
1833 Playlist::regions_touched (framepos_t start, framepos_t end)
1835 RegionReadLock rlock (this);
1836 return regions_touched_locked (start, end);
1839 boost::shared_ptr<RegionList>
1840 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1842 boost::shared_ptr<RegionList> rlist (new RegionList);
1844 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1845 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1846 rlist->push_back (*i);
1854 Playlist::find_next_transient (framepos_t from, int dir)
1856 RegionReadLock rlock (this);
1857 AnalysisFeatureList points;
1858 AnalysisFeatureList these_points;
1860 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1862 if ((*i)->last_frame() < from) {
1866 if ((*i)->first_frame() > from) {
1871 (*i)->get_transients (these_points);
1873 /* add first frame, just, err, because */
1875 these_points.push_back ((*i)->first_frame());
1877 points.insert (points.end(), these_points.begin(), these_points.end());
1878 these_points.clear ();
1881 if (points.empty()) {
1885 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1886 bool reached = false;
1889 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1894 if (reached && (*x) > from) {
1899 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1904 if (reached && (*x) < from) {
1913 boost::shared_ptr<Region>
1914 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1916 RegionReadLock rlock (this);
1917 boost::shared_ptr<Region> ret;
1918 framepos_t closest = max_framepos;
1920 bool end_iter = false;
1922 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1926 frameoffset_t distance;
1927 boost::shared_ptr<Region> r = (*i);
1932 pos = r->first_frame ();
1935 pos = r->last_frame ();
1938 pos = r->sync_position ();
1943 case 1: /* forwards */
1946 if ((distance = pos - frame) < closest) {
1955 default: /* backwards */
1958 if ((distance = frame - pos) < closest) {
1974 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1976 RegionReadLock rlock (this);
1978 framepos_t closest = max_framepos;
1979 framepos_t ret = -1;
1983 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1985 boost::shared_ptr<Region> r = (*i);
1986 frameoffset_t distance;
1988 if (r->first_frame() > frame) {
1990 distance = r->first_frame() - frame;
1992 if (distance < closest) {
1993 ret = r->first_frame();
1998 if (r->last_frame () > frame) {
2000 distance = r->last_frame () - frame;
2002 if (distance < closest) {
2003 ret = r->last_frame ();
2011 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2013 boost::shared_ptr<Region> r = (*i);
2014 frameoffset_t distance;
2016 if (r->last_frame() < frame) {
2018 distance = frame - r->last_frame();
2020 if (distance < closest) {
2021 ret = r->last_frame();
2026 if (r->first_frame() < frame) {
2028 distance = frame - r->first_frame();
2030 if (distance < closest) {
2031 ret = r->first_frame();
2042 /***********************************************************************/
2048 Playlist::mark_session_dirty ()
2050 if (!in_set_state && !holding_state ()) {
2051 _session.set_dirty();
2056 Playlist::rdiff (vector<Command*>& cmds) const
2058 RegionReadLock rlock (const_cast<Playlist *> (this));
2059 Stateful::rdiff (cmds);
2063 Playlist::clear_owned_changes ()
2065 RegionReadLock rlock (this);
2066 Stateful::clear_owned_changes ();
2070 Playlist::update (const RegionListProperty::ChangeRecord& change)
2072 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2073 name(), change.added.size(), change.removed.size()));
2076 /* add the added regions */
2077 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2078 add_region_internal ((*i), (*i)->position());
2080 /* remove the removed regions */
2081 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2089 Playlist::set_state (const XMLNode& node, int version)
2093 XMLNodeConstIterator niter;
2094 XMLPropertyList plist;
2095 XMLPropertyConstIterator piter;
2097 boost::shared_ptr<Region> region;
2099 bool seen_region_nodes = false;
2104 if (node.name() != "Playlist") {
2111 plist = node.properties();
2115 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2119 if (prop->name() == X_("name")) {
2120 _name = prop->value();
2122 } else if (prop->name() == X_("orig-diskstream-id")) {
2123 /* XXX legacy session: fix up later */
2124 _orig_track_id = prop->value ();
2125 } else if (prop->name() == X_("orig-track-id")) {
2126 _orig_track_id = prop->value ();
2127 } else if (prop->name() == X_("frozen")) {
2128 _frozen = string_is_affirmative (prop->value());
2129 } else if (prop->name() == X_("combine-ops")) {
2130 _combine_ops = atoi (prop->value());
2136 nlist = node.children();
2138 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2142 if (child->name() == "Region") {
2144 seen_region_nodes = true;
2146 if ((prop = child->property ("id")) == 0) {
2147 error << _("region state node has no ID, ignored") << endmsg;
2151 ID id = prop->value ();
2153 if ((region = region_by_id (id))) {
2155 region->suspend_property_changes ();
2157 if (region->set_state (*child, version)) {
2158 region->resume_property_changes ();
2162 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2163 region->suspend_property_changes ();
2165 error << _("Playlist: cannot create region from XML") << endmsg;
2170 RegionWriteLock rlock (this);
2171 add_region_internal (region, region->position());
2174 region->resume_property_changes ();
2179 if (seen_region_nodes && regions.empty()) {
2184 notify_contents_changed ();
2187 first_set_state = false;
2193 Playlist::get_state()
2195 return state (true);
2199 Playlist::get_template()
2201 return state (false);
2204 /** @param full_state true to include regions in the returned state, otherwise false.
2207 Playlist::state (bool full_state)
2209 XMLNode *node = new XMLNode (X_("Playlist"));
2212 node->add_property (X_("id"), id().to_s());
2213 node->add_property (X_("name"), _name);
2214 node->add_property (X_("type"), _type.to_string());
2216 _orig_track_id.print (buf, sizeof (buf));
2217 node->add_property (X_("orig-track-id"), buf);
2218 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2221 RegionReadLock rlock (this);
2223 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2224 node->add_property ("combine-ops", buf);
2226 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2227 node->add_child_nocopy ((*i)->get_state());
2232 node->add_child_copy (*_extra_xml);
2239 Playlist::empty() const
2241 RegionReadLock rlock (const_cast<Playlist *>(this));
2242 return regions.empty();
2246 Playlist::n_regions() const
2248 RegionReadLock rlock (const_cast<Playlist *>(this));
2249 return regions.size();
2252 /** @return true if the all_regions list is empty, ie this playlist
2253 * has never had a region added to it.
2256 Playlist::all_regions_empty() const
2258 RegionReadLock rl (const_cast<Playlist *> (this));
2259 return all_regions.empty();
2262 pair<framepos_t, framepos_t>
2263 Playlist::get_extent () const
2265 RegionReadLock rlock (const_cast<Playlist *>(this));
2266 return _get_extent ();
2269 pair<framepos_t, framepos_t>
2270 Playlist::get_extent_with_endspace () const
2272 pair<framepos_t, framepos_t> l = get_extent();
2273 l.second += _end_space;
2277 pair<framepos_t, framepos_t>
2278 Playlist::_get_extent () const
2280 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2282 if (regions.empty()) {
2287 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2288 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2289 if (e.first < ext.first) {
2290 ext.first = e.first;
2292 if (e.second > ext.second) {
2293 ext.second = e.second;
2301 Playlist::bump_name (string name, Session &session)
2303 string newname = name;
2306 newname = bump_name_once (newname, '.');
2307 } while (session.playlists->by_name (newname)!=NULL);
2314 Playlist::top_layer() const
2316 RegionReadLock rlock (const_cast<Playlist *> (this));
2319 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2320 top = max (top, (*i)->layer());
2326 Playlist::set_edit_mode (EditMode mode)
2331 struct RelayerSort {
2332 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2333 return a->layering_index() < b->layering_index();
2337 /** Set a new layer for a region. This adjusts the layering indices of all
2338 * regions in the playlist to put the specified region in the appropriate
2339 * place. The actual layering will be fixed up when relayer() happens.
2343 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2345 /* Remove the layer we are setting from our region list, and sort it
2346 * using the layer indeces.
2349 RegionList copy = regions.rlist();
2350 copy.remove (region);
2351 copy.sort (RelayerSort ());
2353 /* Put region back in the right place */
2354 RegionList::iterator i = copy.begin();
2355 while (i != copy.end ()) {
2356 if ((*i)->layer() > new_layer) {
2362 copy.insert (i, region);
2364 setup_layering_indices (copy);
2368 Playlist::setup_layering_indices (RegionList const & regions)
2372 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2373 (*k)->set_layering_index (j++);
2377 struct LaterHigherSort {
2378 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2379 return a->position() < b->position();
2383 /** Take the layering indices of each of our regions, compute the layers
2384 * that they should be on, and write the layers back to the regions.
2387 Playlist::relayer ()
2389 /* never compute layers when setting from XML */
2395 /* Build up a new list of regions on each layer, stored in a set of lists
2396 each of which represent some period of time on some layer. The idea
2397 is to avoid having to search the entire region list to establish whether
2398 each region overlaps another */
2400 /* how many pieces to divide this playlist's time up into */
2401 int const divisions = 512;
2403 /* find the start and end positions of the regions on this playlist */
2404 framepos_t start = INT64_MAX;
2406 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2407 start = min (start, (*i)->position());
2408 end = max (end, (*i)->position() + (*i)->length());
2411 /* hence the size of each time division */
2412 double const division_size = (end - start) / double (divisions);
2414 vector<vector<RegionList> > layers;
2415 layers.push_back (vector<RegionList> (divisions));
2417 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2418 RegionList copy = regions.rlist();
2419 switch (Config->get_layer_model()) {
2421 copy.sort (LaterHigherSort ());
2424 copy.sort (RelayerSort ());
2428 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2429 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2430 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2433 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2435 /* find the time divisions that this region covers; if there are no regions on the list,
2436 division_size will equal 0 and in this case we'll just say that
2437 start_division = end_division = 0.
2439 int start_division = 0;
2440 int end_division = 0;
2442 if (division_size > 0) {
2443 start_division = floor ( ((*i)->position() - start) / division_size);
2444 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2445 if (end_division == divisions) {
2450 assert (divisions == 0 || end_division < divisions);
2452 /* find the lowest layer that this region can go on */
2453 size_t j = layers.size();
2455 /* try layer j - 1; it can go on if it overlaps no other region
2456 that is already on that layer
2459 bool overlap = false;
2460 for (int k = start_division; k <= end_division; ++k) {
2461 RegionList::iterator l = layers[j-1][k].begin ();
2462 while (l != layers[j-1][k].end()) {
2463 if ((*l)->overlap_equivalent (*i)) {
2476 /* overlap, so we must use layer j */
2483 if (j == layers.size()) {
2484 /* we need a new layer for this region */
2485 layers.push_back (vector<RegionList> (divisions));
2488 /* put a reference to this region in each of the divisions that it exists in */
2489 for (int k = start_division; k <= end_division; ++k) {
2490 layers[j][k].push_back (*i);
2493 (*i)->set_layer (j);
2496 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2497 relayering because we just removed the only region on the top layer, nothing will
2498 appear to have changed, but the StreamView must still sort itself out. We could
2499 probably keep a note of the top layer last time we relayered, and check that,
2500 but premature optimisation &c...
2502 notify_layering_changed ();
2504 /* This relayer() may have been called as a result of a region removal, in which
2505 case we need to setup layering indices to account for the one that has just
2508 setup_layering_indices (copy);
2512 Playlist::raise_region (boost::shared_ptr<Region> region)
2514 set_layer (region, region->layer() + 1.5);
2519 Playlist::lower_region (boost::shared_ptr<Region> region)
2521 set_layer (region, region->layer() - 1.5);
2526 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2528 set_layer (region, DBL_MAX);
2533 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2535 set_layer (region, -0.5);
2540 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2542 RegionList::iterator i;
2548 RegionWriteLock rlock (const_cast<Playlist *> (this));
2550 for (i = regions.begin(); i != regions.end(); ++i) {
2552 if ((*i)->position() >= start) {
2558 if ((*i)->last_frame() > max_framepos - distance) {
2559 new_pos = max_framepos - (*i)->length();
2561 new_pos = (*i)->position() + distance;
2566 if ((*i)->position() > distance) {
2567 new_pos = (*i)->position() - distance;
2573 (*i)->set_position (new_pos);
2581 notify_contents_changed ();
2587 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2589 RegionReadLock rlock (const_cast<Playlist*> (this));
2591 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2592 if ((*r)->uses_source (src)) {
2600 boost::shared_ptr<Region>
2601 Playlist::find_region (const ID& id) const
2603 RegionReadLock rlock (const_cast<Playlist*> (this));
2605 /* searches all regions currently in use by the playlist */
2607 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2608 if ((*i)->id() == id) {
2613 return boost::shared_ptr<Region> ();
2617 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2619 RegionReadLock rlock (const_cast<Playlist*> (this));
2622 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2628 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2629 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2630 /* check if region is used in a compound */
2631 if (it->second == r) {
2632 /* region is referenced as 'original' of a compound */
2636 if (r->whole_file() && r->max_source_level() > 0) {
2637 /* region itself ia a compound.
2638 * the compound regions are not referenced -> check regions inside compound
2640 const SourceList& sl = r->sources();
2641 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2642 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2644 if (ps->playlist()->region_use_count(it->first)) {
2645 // break out of both loops
2654 boost::shared_ptr<Region>
2655 Playlist::region_by_id (const ID& id) const
2657 /* searches all regions ever added to this playlist */
2659 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2660 if ((*i)->id() == id) {
2664 return boost::shared_ptr<Region> ();
2668 Playlist::dump () const
2670 boost::shared_ptr<Region> r;
2672 cerr << "Playlist \"" << _name << "\" " << endl
2673 << regions.size() << " regions "
2676 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2678 cerr << " " << r->name() << " ["
2679 << r->start() << "+" << r->length()
2689 Playlist::set_frozen (bool yn)
2695 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2699 if (region->locked()) {
2706 RegionWriteLock rlock (const_cast<Playlist*> (this));
2711 RegionList::iterator next;
2713 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2714 if ((*i) == region) {
2718 if (next != regions.end()) {
2720 if ((*next)->locked()) {
2726 if ((*next)->position() != region->last_frame() + 1) {
2727 /* they didn't used to touch, so after shuffle,
2728 just have them swap positions.
2730 new_pos = (*next)->position();
2732 /* they used to touch, so after shuffle,
2733 make sure they still do. put the earlier
2734 region where the later one will end after
2737 new_pos = region->position() + (*next)->length();
2740 (*next)->set_position (region->position());
2741 region->set_position (new_pos);
2743 /* avoid a full sort */
2745 regions.erase (i); // removes the region from the list */
2747 regions.insert (next, region); // adds it back after next
2756 RegionList::iterator prev = regions.end();
2758 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2759 if ((*i) == region) {
2761 if (prev != regions.end()) {
2763 if ((*prev)->locked()) {
2768 if (region->position() != (*prev)->last_frame() + 1) {
2769 /* they didn't used to touch, so after shuffle,
2770 just have them swap positions.
2772 new_pos = region->position();
2774 /* they used to touch, so after shuffle,
2775 make sure they still do. put the earlier
2776 one where the later one will end after
2778 new_pos = (*prev)->position() + region->length();
2781 region->set_position ((*prev)->position());
2782 (*prev)->set_position (new_pos);
2784 /* avoid a full sort */
2786 regions.erase (i); // remove region
2787 regions.insert (prev, region); // insert region before prev
2803 notify_contents_changed();
2809 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2811 RegionReadLock rlock (const_cast<Playlist*> (this));
2813 if (regions.size() > 1) {
2821 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2823 ripple_locked (at, distance, exclude);
2827 Playlist::update_after_tempo_map_change ()
2829 RegionWriteLock rlock (const_cast<Playlist*> (this));
2830 RegionList copy (regions.rlist());
2834 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2835 (*i)->update_after_tempo_map_change ();
2842 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2844 RegionWriteLock rl (this, false);
2845 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2851 Playlist::has_region_at (framepos_t const p) const
2853 RegionReadLock (const_cast<Playlist *> (this));
2855 RegionList::const_iterator i = regions.begin ();
2856 while (i != regions.end() && !(*i)->covers (p)) {
2860 return (i != regions.end());
2863 /** Remove any region that uses a given source */
2865 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2867 RegionWriteLock rl (this);
2869 RegionList::iterator i = regions.begin();
2870 while (i != regions.end()) {
2871 RegionList::iterator j = i;
2874 if ((*i)->uses_source (s)) {
2875 remove_region_internal (*i);
2882 /** Look from a session frame time and find the start time of the next region
2883 * which is on the top layer of this playlist.
2884 * @param t Time to look from.
2885 * @return Position of next top-layered region, or max_framepos if there isn't one.
2888 Playlist::find_next_top_layer_position (framepos_t t) const
2890 RegionReadLock rlock (const_cast<Playlist *> (this));
2892 layer_t const top = top_layer ();
2894 RegionList copy = regions.rlist ();
2895 copy.sort (RegionSortByPosition ());
2897 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2898 if ((*i)->position() >= t && (*i)->layer() == top) {
2899 return (*i)->position();
2903 return max_framepos;
2906 boost::shared_ptr<Region>
2907 Playlist::combine (const RegionList& r)
2910 uint32_t channels = 0;
2912 framepos_t earliest_position = max_framepos;
2913 vector<TwoRegions> old_and_new_regions;
2914 vector<boost::shared_ptr<Region> > originals;
2915 vector<boost::shared_ptr<Region> > copies;
2918 uint32_t max_level = 0;
2920 /* find the maximum depth of all the regions we're combining */
2922 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2923 max_level = max (max_level, (*i)->max_source_level());
2926 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2927 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2929 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2931 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2932 earliest_position = min (earliest_position, (*i)->position());
2935 /* enable this so that we do not try to create xfades etc. as we add
2939 pl->in_partition = true;
2941 /* sort by position then layer.
2942 * route_time_axis passes 'selected_regions' - which is not sorted.
2943 * here we need the top-most first, then every layer's region sorted by position.
2945 RegionList sorted(r);
2946 sorted.sort(RegionSortByLayerAndPosition());
2948 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
2950 /* copy the region */
2952 boost::shared_ptr<Region> original_region = (*i);
2953 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2955 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2956 originals.push_back (original_region);
2957 copies.push_back (copied_region);
2959 RegionFactory::add_compound_association (original_region, copied_region);
2961 /* make position relative to zero */
2963 pl->add_region (copied_region, original_region->position() - earliest_position);
2964 copied_region->set_layer (original_region->layer ());
2966 /* use the maximum number of channels for any region */
2968 channels = max (channels, original_region->n_channels());
2970 /* it will go above the layer of the highest existing region */
2972 layer = max (layer, original_region->layer());
2975 pl->in_partition = false;
2977 pre_combine (copies);
2979 /* now create a new PlaylistSource for each channel in the new playlist */
2982 pair<framepos_t,framepos_t> extent = pl->get_extent();
2984 for (uint32_t chn = 0; chn < channels; ++chn) {
2985 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2989 /* now a new whole-file region using the list of sources */
2991 plist.add (Properties::start, 0);
2992 plist.add (Properties::length, extent.second);
2993 plist.add (Properties::name, parent_name);
2994 plist.add (Properties::whole_file, true);
2996 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2998 /* now the non-whole-file region that we will actually use in the
3003 plist.add (Properties::start, 0);
3004 plist.add (Properties::length, extent.second);
3005 plist.add (Properties::name, child_name);
3006 plist.add (Properties::layer, layer+1);
3008 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3010 /* remove all the selected regions from the current playlist
3015 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3019 /* do type-specific stuff with the originals and the new compound
3023 post_combine (originals, compound_region);
3025 /* add the new region at the right location */
3027 add_region (compound_region, earliest_position);
3033 return compound_region;
3037 Playlist::uncombine (boost::shared_ptr<Region> target)
3039 boost::shared_ptr<PlaylistSource> pls;
3040 boost::shared_ptr<const Playlist> pl;
3041 vector<boost::shared_ptr<Region> > originals;
3042 vector<TwoRegions> old_and_new_regions;
3044 // (1) check that its really a compound region
3046 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3050 pl = pls->playlist();
3052 framepos_t adjusted_start = 0; // gcc isn't smart enough
3053 framepos_t adjusted_end = 0; // gcc isn't smart enough
3055 /* the leftmost (earliest) edge of the compound region
3056 starts at zero in its source, or larger if it
3057 has been trimmed or content-scrolled.
3059 the rightmost (latest) edge of the compound region
3060 relative to its source is the starting point plus
3061 the length of the region.
3064 // (2) get all the original regions
3066 const RegionList& rl (pl->region_list().rlist());
3067 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3068 frameoffset_t move_offset = 0;
3070 /* there are two possibilities here:
3071 1) the playlist that the playlist source was based on
3072 is us, so just add the originals (which belonged to
3073 us anyway) back in the right place.
3075 2) the playlist that the playlist source was based on
3076 is NOT us, so we need to make copies of each of
3077 the original regions that we find, and add them
3080 bool same_playlist = (pls->original() == id());
3082 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3084 boost::shared_ptr<Region> current (*i);
3086 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3088 if (ca == cassocs.end()) {
3092 boost::shared_ptr<Region> original (ca->second);
3094 bool modified_region;
3096 if (i == rl.begin()) {
3097 move_offset = (target->position() - original->position()) - target->start();
3098 adjusted_start = original->position() + target->start();
3099 adjusted_end = adjusted_start + target->length();
3102 if (!same_playlist) {
3103 framepos_t pos = original->position();
3104 /* make a copy, but don't announce it */
3105 original = RegionFactory::create (original, false);
3106 /* the pure copy constructor resets position() to zero,
3109 original->set_position (pos);
3112 /* check to see how the original region (in the
3113 * playlist before compounding occured) overlaps
3114 * with the new state of the compound region.
3117 original->clear_changes ();
3118 modified_region = false;
3120 switch (original->coverage (adjusted_start, adjusted_end)) {
3121 case Evoral::OverlapNone:
3122 /* original region does not cover any part
3123 of the current state of the compound region
3127 case Evoral::OverlapInternal:
3128 /* overlap is just a small piece inside the
3129 * original so trim both ends
3131 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3132 modified_region = true;
3135 case Evoral::OverlapExternal:
3136 /* overlap fully covers original, so leave it
3141 case Evoral::OverlapEnd:
3142 /* overlap starts within but covers end,
3143 so trim the front of the region
3145 original->trim_front (adjusted_start);
3146 modified_region = true;
3149 case Evoral::OverlapStart:
3150 /* overlap covers start but ends within, so
3151 * trim the end of the region.
3153 original->trim_end (adjusted_end);
3154 modified_region = true;
3159 /* fix the position to match any movement of the compound region.
3161 original->set_position (original->position() + move_offset);
3162 modified_region = true;
3165 if (modified_region) {
3166 _session.add_command (new StatefulDiffCommand (original));
3169 /* and add to the list of regions waiting to be
3173 originals.push_back (original);
3174 old_and_new_regions.push_back (TwoRegions (*i, original));
3177 pre_uncombine (originals, target);
3179 in_partition = true;
3182 // (3) remove the compound region
3184 remove_region (target);
3186 // (4) add the constituent regions
3188 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3189 add_region ((*i), (*i)->position());
3190 set_layer((*i), (*i)->layer());
3191 if (!RegionFactory::region_by_id((*i)->id())) {
3192 RegionFactory::map_add(*i);
3196 in_partition = false;
3201 Playlist::fade_range (list<AudioRange>& ranges)
3203 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3204 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3205 (*i)->fade_range ((*r).start, (*r).end);
3211 Playlist::max_source_level () const
3213 RegionReadLock rlock (const_cast<Playlist *> (this));
3216 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3217 lvl = max (lvl, (*i)->max_source_level());
3224 Playlist::set_orig_track_id (const PBD::ID& id)
3226 _orig_track_id = id;
3229 /** Take a list of ranges, coalesce any that can be coalesced, then call
3230 * check_crossfades for each one.
3233 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3235 /* XXX: it's a shame that this coalesce algorithm also exists in
3236 TimeSelection::consolidate().
3239 /* XXX: xfade: this is implemented in Evoral::RangeList */
3242 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3243 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3249 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3250 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3251 i->from = min (i->from, j->from);
3252 i->to = max (i->to, j->to);
3261 Playlist::set_capture_insertion_in_progress (bool yn)
3263 _capture_insertion_underway = yn;