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);
1261 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1262 add_region_internal (copy, position);
1263 set_layer (copy, DBL_MAX);
1267 if (floor (times) != times) {
1268 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1270 RegionFactory::region_name (name, region->name(), false);
1275 plist.add (Properties::start, region->start());
1276 plist.add (Properties::length, length);
1277 plist.add (Properties::name, name);
1279 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1280 add_region_internal (sub, position);
1281 set_layer (sub, DBL_MAX);
1286 /** @param gap from the beginning of the region to the next beginning */
1287 /** @param end the first frame that does _not_ contain a duplicated frame */
1289 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1291 RegionWriteLock rl (this);
1293 while (position + region->length() - 1 < end) {
1294 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1295 add_region_internal (copy, position);
1296 set_layer (copy, DBL_MAX);
1300 if (position < end) {
1301 framecnt_t length = min (region->length(), end - position);
1303 RegionFactory::region_name (name, region->name(), false);
1308 plist.add (Properties::start, region->start());
1309 plist.add (Properties::length, length);
1310 plist.add (Properties::name, name);
1312 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1313 add_region_internal (sub, position);
1314 set_layer (sub, DBL_MAX);
1320 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1322 RegionWriteLock rlock (this);
1323 RegionList copy (regions.rlist());
1326 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1328 if ((*r)->last_frame() < at) {
1333 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1334 /* intersected region */
1335 if (!move_intersected) {
1340 /* do not move regions glued to music time - that
1341 has to be done separately.
1344 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1345 fixup.push_back (*r);
1349 (*r)->set_position ((*r)->position() + distance);
1352 /* XXX: may not be necessary; Region::post_set should do this, I think */
1353 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1354 (*r)->recompute_position_from_lock_style ();
1359 Playlist::split (framepos_t at)
1361 RegionWriteLock rlock (this);
1362 RegionList copy (regions.rlist());
1364 /* use a copy since this operation can modify the region list
1367 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1368 _split_region (*r, at);
1373 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1375 RegionWriteLock rl (this);
1376 _split_region (region, playlist_position);
1380 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1382 if (!region->covers (playlist_position)) {
1386 if (region->position() == playlist_position ||
1387 region->last_frame() == playlist_position) {
1391 boost::shared_ptr<Region> left;
1392 boost::shared_ptr<Region> right;
1393 frameoffset_t before;
1394 frameoffset_t after;
1398 /* split doesn't change anything about length, so don't try to splice */
1400 bool old_sp = _splicing;
1403 before = playlist_position - region->position();
1404 after = region->length() - before;
1406 RegionFactory::region_name (before_name, region->name(), false);
1411 plist.add (Properties::length, before);
1412 plist.add (Properties::name, before_name);
1413 plist.add (Properties::left_of_split, true);
1414 plist.add (Properties::layering_index, region->layering_index ());
1415 plist.add (Properties::layer, region->layer ());
1417 /* note: we must use the version of ::create with an offset here,
1418 since it supplies that offset to the Region constructor, which
1419 is necessary to get audio region gain envelopes right.
1421 left = RegionFactory::create (region, 0, plist);
1424 RegionFactory::region_name (after_name, region->name(), false);
1429 plist.add (Properties::length, after);
1430 plist.add (Properties::name, after_name);
1431 plist.add (Properties::right_of_split, true);
1432 plist.add (Properties::layering_index, region->layering_index ());
1433 plist.add (Properties::layer, region->layer ());
1435 /* same note as above */
1436 right = RegionFactory::create (region, before, plist);
1439 add_region_internal (left, region->position());
1440 add_region_internal (right, region->position() + before);
1441 remove_region_internal (region);
1447 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1449 if (_splicing || in_set_state) {
1450 /* don't respond to splicing moves or state setting */
1454 if (_edit_mode == Splice) {
1455 splice_locked (at, distance, exclude);
1460 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1462 if (_splicing || in_set_state) {
1463 /* don't respond to splicing moves or state setting */
1467 if (_edit_mode == Splice) {
1468 splice_unlocked (at, distance, exclude);
1473 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1476 RegionWriteLock rl (this);
1477 core_splice (at, distance, exclude);
1482 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1484 core_splice (at, distance, exclude);
1488 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1492 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1494 if (exclude && (*i) == exclude) {
1498 if ((*i)->position() >= at) {
1499 framepos_t new_pos = (*i)->position() + distance;
1502 } else if (new_pos >= max_framepos - (*i)->length()) {
1503 new_pos = max_framepos - (*i)->length();
1506 (*i)->set_position (new_pos);
1512 notify_contents_changed ();
1516 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1519 RegionWriteLock rl (this);
1520 core_ripple (at, distance, exclude);
1525 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1527 core_ripple (at, distance, exclude);
1531 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1533 if (distance == 0) {
1538 RegionListProperty copy = regions;
1539 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1540 assert (i != copy.end());
1543 if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1548 if ((*i)->position() >= at) {
1549 framepos_t new_pos = (*i)->position() + distance;
1550 framepos_t limit = max_framepos - (*i)->length();
1553 } else if (new_pos >= limit ) {
1557 (*i)->set_position (new_pos);
1562 notify_contents_changed ();
1567 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1569 if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1573 if (what_changed.contains (Properties::position)) {
1575 /* remove it from the list then add it back in
1576 the right place again.
1579 RegionSortByPosition cmp;
1581 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1583 if (i == regions.end()) {
1584 /* the region bounds are being modified but its not currently
1585 in the region list. we will use its bounds correctly when/if
1592 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1595 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1597 frameoffset_t delta = 0;
1599 if (what_changed.contains (Properties::position)) {
1600 delta = region->position() - region->last_position();
1603 if (what_changed.contains (Properties::length)) {
1604 delta += region->length() - region->last_length();
1608 possibly_splice (region->last_position() + region->last_length(), delta, region);
1611 if (holding_state ()) {
1612 pending_bounds.push_back (region);
1614 notify_contents_changed ();
1616 list<Evoral::Range<framepos_t> > xf;
1617 xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1618 xf.push_back (Evoral::Range<framepos_t> (region->range()));
1619 coalesce_and_check_crossfades (xf);
1625 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1627 boost::shared_ptr<Region> region (weak_region.lock());
1633 /* this makes a virtual call to the right kind of playlist ... */
1635 region_changed (what_changed, region);
1639 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1641 PropertyChange our_interests;
1642 PropertyChange bounds;
1643 PropertyChange pos_and_length;
1646 if (in_set_state || in_flush) {
1650 our_interests.add (Properties::muted);
1651 our_interests.add (Properties::layer);
1652 our_interests.add (Properties::opaque);
1654 bounds.add (Properties::start);
1655 bounds.add (Properties::position);
1656 bounds.add (Properties::length);
1658 pos_and_length.add (Properties::position);
1659 pos_and_length.add (Properties::length);
1661 if (what_changed.contains (bounds)) {
1662 region_bounds_changed (what_changed, region);
1663 save = !(_splicing || _nudging);
1666 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1667 notify_region_moved (region);
1668 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1669 notify_region_end_trimmed (region);
1670 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1671 notify_region_start_trimmed (region);
1674 /* don't notify about layer changes, since we are the only object that can initiate
1675 them, and we notify in ::relayer()
1678 if (what_changed.contains (our_interests)) {
1682 mark_session_dirty ();
1688 Playlist::drop_regions ()
1690 RegionWriteLock rl (this);
1692 all_regions.clear ();
1696 Playlist::sync_all_regions_with_regions ()
1698 RegionWriteLock rl (this);
1700 all_regions.clear ();
1702 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1703 all_regions.insert (*i);
1708 Playlist::clear (bool with_signals)
1711 RegionWriteLock rl (this);
1713 region_state_changed_connections.drop_connections ();
1715 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1716 pending_removes.insert (*i);
1721 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1722 remove_dependents (*s);
1728 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1729 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1732 pending_removes.clear ();
1733 pending_contents_change = false;
1739 /***********************************************************************
1741 **********************************************************************/
1743 boost::shared_ptr<RegionList>
1744 Playlist::regions_at (framepos_t frame)
1746 RegionReadLock rlock (this);
1747 return find_regions_at (frame);
1751 Playlist::count_regions_at (framepos_t frame) const
1753 RegionReadLock rlock (const_cast<Playlist*>(this));
1756 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1757 if ((*i)->covers (frame)) {
1765 boost::shared_ptr<Region>
1766 Playlist::top_region_at (framepos_t frame)
1769 RegionReadLock rlock (this);
1770 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1771 boost::shared_ptr<Region> region;
1773 if (rlist->size()) {
1774 RegionSortByLayer cmp;
1776 region = rlist->back();
1782 boost::shared_ptr<Region>
1783 Playlist::top_unmuted_region_at (framepos_t frame)
1786 RegionReadLock rlock (this);
1787 boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1789 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1791 RegionList::iterator tmp = i;
1794 if ((*i)->muted()) {
1801 boost::shared_ptr<Region> region;
1803 if (rlist->size()) {
1804 RegionSortByLayer cmp;
1806 region = rlist->back();
1812 boost::shared_ptr<RegionList>
1813 Playlist::find_regions_at (framepos_t frame)
1815 /* Caller must hold lock */
1817 boost::shared_ptr<RegionList> rlist (new RegionList);
1819 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1820 if ((*i)->covers (frame)) {
1821 rlist->push_back (*i);
1828 boost::shared_ptr<RegionList>
1829 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1831 RegionReadLock rlock (this);
1832 boost::shared_ptr<RegionList> rlist (new RegionList);
1834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1835 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1836 rlist->push_back (*i);
1843 boost::shared_ptr<RegionList>
1844 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1846 RegionReadLock rlock (this);
1847 boost::shared_ptr<RegionList> rlist (new RegionList);
1849 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1850 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1851 rlist->push_back (*i);
1858 /** @param start Range start.
1859 * @param end Range end.
1860 * @return regions which have some part within this range.
1862 boost::shared_ptr<RegionList>
1863 Playlist::regions_touched (framepos_t start, framepos_t end)
1865 RegionReadLock rlock (this);
1866 return regions_touched_locked (start, end);
1869 boost::shared_ptr<RegionList>
1870 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1872 boost::shared_ptr<RegionList> rlist (new RegionList);
1874 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1875 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1876 rlist->push_back (*i);
1884 Playlist::find_next_transient (framepos_t from, int dir)
1886 RegionReadLock rlock (this);
1887 AnalysisFeatureList points;
1888 AnalysisFeatureList these_points;
1890 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1892 if ((*i)->last_frame() < from) {
1896 if ((*i)->first_frame() > from) {
1901 (*i)->get_transients (these_points);
1903 /* add first frame, just, err, because */
1905 these_points.push_back ((*i)->first_frame());
1907 points.insert (points.end(), these_points.begin(), these_points.end());
1908 these_points.clear ();
1911 if (points.empty()) {
1915 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1916 bool reached = false;
1919 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1924 if (reached && (*x) > from) {
1929 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1934 if (reached && (*x) < from) {
1943 boost::shared_ptr<Region>
1944 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1946 RegionReadLock rlock (this);
1947 boost::shared_ptr<Region> ret;
1948 framepos_t closest = max_framepos;
1950 bool end_iter = false;
1952 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1956 frameoffset_t distance;
1957 boost::shared_ptr<Region> r = (*i);
1962 pos = r->first_frame ();
1965 pos = r->last_frame ();
1968 pos = r->sync_position ();
1973 case 1: /* forwards */
1976 if ((distance = pos - frame) < closest) {
1985 default: /* backwards */
1988 if ((distance = frame - pos) < closest) {
2004 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2006 RegionReadLock rlock (this);
2008 framepos_t closest = max_framepos;
2009 framepos_t ret = -1;
2013 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2015 boost::shared_ptr<Region> r = (*i);
2016 frameoffset_t distance;
2018 if (r->first_frame() > frame) {
2020 distance = r->first_frame() - frame;
2022 if (distance < closest) {
2023 ret = r->first_frame();
2028 if (r->last_frame () > frame) {
2030 distance = r->last_frame () - frame;
2032 if (distance < closest) {
2033 ret = r->last_frame ();
2041 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2043 boost::shared_ptr<Region> r = (*i);
2044 frameoffset_t distance;
2046 if (r->last_frame() < frame) {
2048 distance = frame - r->last_frame();
2050 if (distance < closest) {
2051 ret = r->last_frame();
2056 if (r->first_frame() < frame) {
2058 distance = frame - r->first_frame();
2060 if (distance < closest) {
2061 ret = r->first_frame();
2072 /***********************************************************************/
2078 Playlist::mark_session_dirty ()
2080 if (!in_set_state && !holding_state ()) {
2081 _session.set_dirty();
2086 Playlist::rdiff (vector<Command*>& cmds) const
2088 RegionReadLock rlock (const_cast<Playlist *> (this));
2089 Stateful::rdiff (cmds);
2093 Playlist::clear_owned_changes ()
2095 RegionReadLock rlock (this);
2096 Stateful::clear_owned_changes ();
2100 Playlist::update (const RegionListProperty::ChangeRecord& change)
2102 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2103 name(), change.added.size(), change.removed.size()));
2106 /* add the added regions */
2107 for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2108 add_region_internal ((*i), (*i)->position());
2110 /* remove the removed regions */
2111 for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2119 Playlist::set_state (const XMLNode& node, int version)
2123 XMLNodeConstIterator niter;
2124 XMLPropertyList plist;
2125 XMLPropertyConstIterator piter;
2127 boost::shared_ptr<Region> region;
2129 bool seen_region_nodes = false;
2134 if (node.name() != "Playlist") {
2141 plist = node.properties();
2145 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2149 if (prop->name() == X_("name")) {
2150 _name = prop->value();
2152 } else if (prop->name() == X_("orig-diskstream-id")) {
2153 /* XXX legacy session: fix up later */
2154 _orig_track_id = prop->value ();
2155 } else if (prop->name() == X_("orig-track-id")) {
2156 _orig_track_id = prop->value ();
2157 } else if (prop->name() == X_("frozen")) {
2158 _frozen = string_is_affirmative (prop->value());
2159 } else if (prop->name() == X_("combine-ops")) {
2160 _combine_ops = atoi (prop->value());
2166 nlist = node.children();
2168 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2172 if (child->name() == "Region") {
2174 seen_region_nodes = true;
2176 if ((prop = child->property ("id")) == 0) {
2177 error << _("region state node has no ID, ignored") << endmsg;
2181 ID id = prop->value ();
2183 if ((region = region_by_id (id))) {
2185 region->suspend_property_changes ();
2187 if (region->set_state (*child, version)) {
2188 region->resume_property_changes ();
2192 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2193 region->suspend_property_changes ();
2195 error << _("Playlist: cannot create region from XML") << endmsg;
2200 RegionWriteLock rlock (this);
2201 add_region_internal (region, region->position());
2204 region->resume_property_changes ();
2209 if (seen_region_nodes && regions.empty()) {
2214 notify_contents_changed ();
2217 first_set_state = false;
2223 Playlist::get_state()
2225 return state (true);
2229 Playlist::get_template()
2231 return state (false);
2234 /** @param full_state true to include regions in the returned state, otherwise false.
2237 Playlist::state (bool full_state)
2239 XMLNode *node = new XMLNode (X_("Playlist"));
2242 node->add_property (X_("id"), id().to_s());
2243 node->add_property (X_("name"), _name);
2244 node->add_property (X_("type"), _type.to_string());
2246 _orig_track_id.print (buf, sizeof (buf));
2247 node->add_property (X_("orig-track-id"), buf);
2248 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2251 RegionReadLock rlock (this);
2253 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2254 node->add_property ("combine-ops", buf);
2256 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2257 node->add_child_nocopy ((*i)->get_state());
2262 node->add_child_copy (*_extra_xml);
2269 Playlist::empty() const
2271 RegionReadLock rlock (const_cast<Playlist *>(this));
2272 return regions.empty();
2276 Playlist::n_regions() const
2278 RegionReadLock rlock (const_cast<Playlist *>(this));
2279 return regions.size();
2282 /** @return true if the all_regions list is empty, ie this playlist
2283 * has never had a region added to it.
2286 Playlist::all_regions_empty() const
2288 RegionReadLock rl (const_cast<Playlist *> (this));
2289 return all_regions.empty();
2292 pair<framepos_t, framepos_t>
2293 Playlist::get_extent () const
2295 RegionReadLock rlock (const_cast<Playlist *>(this));
2296 return _get_extent ();
2299 pair<framepos_t, framepos_t>
2300 Playlist::get_extent_with_endspace () const
2302 pair<framepos_t, framepos_t> l = get_extent();
2303 l.second += _end_space;
2307 pair<framepos_t, framepos_t>
2308 Playlist::_get_extent () const
2310 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2312 if (regions.empty()) {
2317 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2318 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2319 if (e.first < ext.first) {
2320 ext.first = e.first;
2322 if (e.second > ext.second) {
2323 ext.second = e.second;
2331 Playlist::bump_name (string name, Session &session)
2333 string newname = name;
2336 newname = bump_name_once (newname, '.');
2337 } while (session.playlists->by_name (newname)!=NULL);
2344 Playlist::top_layer() const
2346 RegionReadLock rlock (const_cast<Playlist *> (this));
2349 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2350 top = max (top, (*i)->layer());
2356 Playlist::set_edit_mode (EditMode mode)
2361 struct RelayerSort {
2362 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2363 return a->layering_index() < b->layering_index();
2367 /** Set a new layer for a region. This adjusts the layering indices of all
2368 * regions in the playlist to put the specified region in the appropriate
2369 * place. The actual layering will be fixed up when relayer() happens.
2373 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2375 /* Remove the layer we are setting from our region list, and sort it
2376 * using the layer indeces.
2379 RegionList copy = regions.rlist();
2380 copy.remove (region);
2381 copy.sort (RelayerSort ());
2383 /* Put region back in the right place */
2384 RegionList::iterator i = copy.begin();
2385 while (i != copy.end ()) {
2386 if ((*i)->layer() > new_layer) {
2392 copy.insert (i, region);
2394 setup_layering_indices (copy);
2398 Playlist::setup_layering_indices (RegionList const & regions)
2402 for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2403 (*k)->set_layering_index (j++);
2407 struct LaterHigherSort {
2408 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2409 return a->position() < b->position();
2413 /** Take the layering indices of each of our regions, compute the layers
2414 * that they should be on, and write the layers back to the regions.
2417 Playlist::relayer ()
2419 /* never compute layers when setting from XML */
2425 /* Build up a new list of regions on each layer, stored in a set of lists
2426 each of which represent some period of time on some layer. The idea
2427 is to avoid having to search the entire region list to establish whether
2428 each region overlaps another */
2430 /* how many pieces to divide this playlist's time up into */
2431 int const divisions = 512;
2433 /* find the start and end positions of the regions on this playlist */
2434 framepos_t start = INT64_MAX;
2436 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2437 start = min (start, (*i)->position());
2438 end = max (end, (*i)->position() + (*i)->length());
2441 /* hence the size of each time division */
2442 double const division_size = (end - start) / double (divisions);
2444 vector<vector<RegionList> > layers;
2445 layers.push_back (vector<RegionList> (divisions));
2447 /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2448 RegionList copy = regions.rlist();
2449 switch (Config->get_layer_model()) {
2451 copy.sort (LaterHigherSort ());
2454 copy.sort (RelayerSort ());
2458 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2459 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2460 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2463 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2465 /* find the time divisions that this region covers; if there are no regions on the list,
2466 division_size will equal 0 and in this case we'll just say that
2467 start_division = end_division = 0.
2469 int start_division = 0;
2470 int end_division = 0;
2472 if (division_size > 0) {
2473 start_division = floor ( ((*i)->position() - start) / division_size);
2474 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2475 if (end_division == divisions) {
2480 assert (divisions == 0 || end_division < divisions);
2482 /* find the lowest layer that this region can go on */
2483 size_t j = layers.size();
2485 /* try layer j - 1; it can go on if it overlaps no other region
2486 that is already on that layer
2489 bool overlap = false;
2490 for (int k = start_division; k <= end_division; ++k) {
2491 RegionList::iterator l = layers[j-1][k].begin ();
2492 while (l != layers[j-1][k].end()) {
2493 if ((*l)->overlap_equivalent (*i)) {
2506 /* overlap, so we must use layer j */
2513 if (j == layers.size()) {
2514 /* we need a new layer for this region */
2515 layers.push_back (vector<RegionList> (divisions));
2518 /* put a reference to this region in each of the divisions that it exists in */
2519 for (int k = start_division; k <= end_division; ++k) {
2520 layers[j][k].push_back (*i);
2523 (*i)->set_layer (j);
2526 /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2527 relayering because we just removed the only region on the top layer, nothing will
2528 appear to have changed, but the StreamView must still sort itself out. We could
2529 probably keep a note of the top layer last time we relayered, and check that,
2530 but premature optimisation &c...
2532 notify_layering_changed ();
2534 /* This relayer() may have been called as a result of a region removal, in which
2535 case we need to setup layering indices to account for the one that has just
2538 setup_layering_indices (copy);
2542 Playlist::raise_region (boost::shared_ptr<Region> region)
2544 set_layer (region, region->layer() + 1.5);
2549 Playlist::lower_region (boost::shared_ptr<Region> region)
2551 set_layer (region, region->layer() - 1.5);
2556 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2558 set_layer (region, DBL_MAX);
2563 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2565 set_layer (region, -0.5);
2570 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2572 RegionList::iterator i;
2578 RegionWriteLock rlock (const_cast<Playlist *> (this));
2580 for (i = regions.begin(); i != regions.end(); ++i) {
2582 if ((*i)->position() >= start) {
2588 if ((*i)->last_frame() > max_framepos - distance) {
2589 new_pos = max_framepos - (*i)->length();
2591 new_pos = (*i)->position() + distance;
2596 if ((*i)->position() > distance) {
2597 new_pos = (*i)->position() - distance;
2603 (*i)->set_position (new_pos);
2611 notify_contents_changed ();
2617 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2619 RegionReadLock rlock (const_cast<Playlist*> (this));
2621 for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2622 if ((*r)->uses_source (src)) {
2630 boost::shared_ptr<Region>
2631 Playlist::find_region (const ID& id) const
2633 RegionReadLock rlock (const_cast<Playlist*> (this));
2635 /* searches all regions currently in use by the playlist */
2637 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2638 if ((*i)->id() == id) {
2643 return boost::shared_ptr<Region> ();
2647 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2649 RegionReadLock rlock (const_cast<Playlist*> (this));
2652 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2658 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2659 for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2660 /* check if region is used in a compound */
2661 if (it->second == r) {
2662 /* region is referenced as 'original' of a compound */
2666 if (r->whole_file() && r->max_source_level() > 0) {
2667 /* region itself ia a compound.
2668 * the compound regions are not referenced -> check regions inside compound
2670 const SourceList& sl = r->sources();
2671 for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2672 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2674 if (ps->playlist()->region_use_count(it->first)) {
2675 // break out of both loops
2684 boost::shared_ptr<Region>
2685 Playlist::region_by_id (const ID& id) const
2687 /* searches all regions ever added to this playlist */
2689 for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2690 if ((*i)->id() == id) {
2694 return boost::shared_ptr<Region> ();
2698 Playlist::dump () const
2700 boost::shared_ptr<Region> r;
2702 cerr << "Playlist \"" << _name << "\" " << endl
2703 << regions.size() << " regions "
2706 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2708 cerr << " " << r->name() << " ["
2709 << r->start() << "+" << r->length()
2719 Playlist::set_frozen (bool yn)
2725 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2729 if (region->locked()) {
2736 RegionWriteLock rlock (const_cast<Playlist*> (this));
2741 RegionList::iterator next;
2743 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2744 if ((*i) == region) {
2748 if (next != regions.end()) {
2750 if ((*next)->locked()) {
2756 if ((*next)->position() != region->last_frame() + 1) {
2757 /* they didn't used to touch, so after shuffle,
2758 just have them swap positions.
2760 new_pos = (*next)->position();
2762 /* they used to touch, so after shuffle,
2763 make sure they still do. put the earlier
2764 region where the later one will end after
2767 new_pos = region->position() + (*next)->length();
2770 (*next)->set_position (region->position());
2771 region->set_position (new_pos);
2773 /* avoid a full sort */
2775 regions.erase (i); // removes the region from the list */
2777 regions.insert (next, region); // adds it back after next
2786 RegionList::iterator prev = regions.end();
2788 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2789 if ((*i) == region) {
2791 if (prev != regions.end()) {
2793 if ((*prev)->locked()) {
2798 if (region->position() != (*prev)->last_frame() + 1) {
2799 /* they didn't used to touch, so after shuffle,
2800 just have them swap positions.
2802 new_pos = region->position();
2804 /* they used to touch, so after shuffle,
2805 make sure they still do. put the earlier
2806 one where the later one will end after
2808 new_pos = (*prev)->position() + region->length();
2811 region->set_position ((*prev)->position());
2812 (*prev)->set_position (new_pos);
2814 /* avoid a full sort */
2816 regions.erase (i); // remove region
2817 regions.insert (prev, region); // insert region before prev
2833 notify_contents_changed();
2839 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2841 RegionReadLock rlock (const_cast<Playlist*> (this));
2843 if (regions.size() > 1) {
2851 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2853 ripple_locked (at, distance, exclude);
2857 Playlist::update_after_tempo_map_change ()
2859 RegionWriteLock rlock (const_cast<Playlist*> (this));
2860 RegionList copy (regions.rlist());
2864 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2865 (*i)->update_after_tempo_map_change ();
2872 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2874 RegionReadLock rl (this);
2875 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2881 Playlist::has_region_at (framepos_t const p) const
2883 RegionReadLock (const_cast<Playlist *> (this));
2885 RegionList::const_iterator i = regions.begin ();
2886 while (i != regions.end() && !(*i)->covers (p)) {
2890 return (i != regions.end());
2893 /** Remove any region that uses a given source */
2895 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2897 RegionWriteLock rl (this);
2899 RegionList::iterator i = regions.begin();
2900 while (i != regions.end()) {
2901 RegionList::iterator j = i;
2904 if ((*i)->uses_source (s)) {
2905 remove_region_internal (*i);
2912 /** Look from a session frame time and find the start time of the next region
2913 * which is on the top layer of this playlist.
2914 * @param t Time to look from.
2915 * @return Position of next top-layered region, or max_framepos if there isn't one.
2918 Playlist::find_next_top_layer_position (framepos_t t) const
2920 RegionReadLock rlock (const_cast<Playlist *> (this));
2922 layer_t const top = top_layer ();
2924 RegionList copy = regions.rlist ();
2925 copy.sort (RegionSortByPosition ());
2927 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2928 if ((*i)->position() >= t && (*i)->layer() == top) {
2929 return (*i)->position();
2933 return max_framepos;
2936 boost::shared_ptr<Region>
2937 Playlist::combine (const RegionList& r)
2940 uint32_t channels = 0;
2942 framepos_t earliest_position = max_framepos;
2943 vector<TwoRegions> old_and_new_regions;
2944 vector<boost::shared_ptr<Region> > originals;
2945 vector<boost::shared_ptr<Region> > copies;
2948 uint32_t max_level = 0;
2950 /* find the maximum depth of all the regions we're combining */
2952 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2953 max_level = max (max_level, (*i)->max_source_level());
2956 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2957 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2959 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2961 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2962 earliest_position = min (earliest_position, (*i)->position());
2965 /* enable this so that we do not try to create xfades etc. as we add
2969 pl->in_partition = true;
2971 /* sort by position then layer.
2972 * route_time_axis passes 'selected_regions' - which is not sorted.
2973 * here we need the top-most first, then every layer's region sorted by position.
2975 RegionList sorted(r);
2976 sorted.sort(RegionSortByLayerAndPosition());
2978 for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
2980 /* copy the region */
2982 boost::shared_ptr<Region> original_region = (*i);
2983 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2985 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2986 originals.push_back (original_region);
2987 copies.push_back (copied_region);
2989 RegionFactory::add_compound_association (original_region, copied_region);
2991 /* make position relative to zero */
2993 pl->add_region (copied_region, original_region->position() - earliest_position);
2994 copied_region->set_layer (original_region->layer ());
2996 /* use the maximum number of channels for any region */
2998 channels = max (channels, original_region->n_channels());
3000 /* it will go above the layer of the highest existing region */
3002 layer = max (layer, original_region->layer());
3005 pl->in_partition = false;
3007 pre_combine (copies);
3009 /* now create a new PlaylistSource for each channel in the new playlist */
3012 pair<framepos_t,framepos_t> extent = pl->get_extent();
3014 for (uint32_t chn = 0; chn < channels; ++chn) {
3015 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3019 /* now a new whole-file region using the list of sources */
3021 plist.add (Properties::start, 0);
3022 plist.add (Properties::length, extent.second);
3023 plist.add (Properties::name, parent_name);
3024 plist.add (Properties::whole_file, true);
3026 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3028 /* now the non-whole-file region that we will actually use in the
3033 plist.add (Properties::start, 0);
3034 plist.add (Properties::length, extent.second);
3035 plist.add (Properties::name, child_name);
3036 plist.add (Properties::layer, layer+1);
3038 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3040 /* remove all the selected regions from the current playlist
3045 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3049 /* do type-specific stuff with the originals and the new compound
3053 post_combine (originals, compound_region);
3055 /* add the new region at the right location */
3057 add_region (compound_region, earliest_position);
3063 return compound_region;
3067 Playlist::uncombine (boost::shared_ptr<Region> target)
3069 boost::shared_ptr<PlaylistSource> pls;
3070 boost::shared_ptr<const Playlist> pl;
3071 vector<boost::shared_ptr<Region> > originals;
3072 vector<TwoRegions> old_and_new_regions;
3074 // (1) check that its really a compound region
3076 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3080 pl = pls->playlist();
3082 framepos_t adjusted_start = 0; // gcc isn't smart enough
3083 framepos_t adjusted_end = 0; // gcc isn't smart enough
3085 /* the leftmost (earliest) edge of the compound region
3086 starts at zero in its source, or larger if it
3087 has been trimmed or content-scrolled.
3089 the rightmost (latest) edge of the compound region
3090 relative to its source is the starting point plus
3091 the length of the region.
3094 // (2) get all the original regions
3096 const RegionList& rl (pl->region_list().rlist());
3097 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3098 frameoffset_t move_offset = 0;
3100 /* there are two possibilities here:
3101 1) the playlist that the playlist source was based on
3102 is us, so just add the originals (which belonged to
3103 us anyway) back in the right place.
3105 2) the playlist that the playlist source was based on
3106 is NOT us, so we need to make copies of each of
3107 the original regions that we find, and add them
3110 bool same_playlist = (pls->original() == id());
3112 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3114 boost::shared_ptr<Region> current (*i);
3116 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3118 if (ca == cassocs.end()) {
3122 boost::shared_ptr<Region> original (ca->second);
3124 bool modified_region;
3126 if (i == rl.begin()) {
3127 move_offset = (target->position() - original->position()) - target->start();
3128 adjusted_start = original->position() + target->start();
3129 adjusted_end = adjusted_start + target->length();
3132 if (!same_playlist) {
3133 framepos_t pos = original->position();
3134 /* make a copy, but don't announce it */
3135 original = RegionFactory::create (original, false);
3136 /* the pure copy constructor resets position() to zero,
3139 original->set_position (pos);
3142 /* check to see how the original region (in the
3143 * playlist before compounding occured) overlaps
3144 * with the new state of the compound region.
3147 original->clear_changes ();
3148 modified_region = false;
3150 switch (original->coverage (adjusted_start, adjusted_end)) {
3151 case Evoral::OverlapNone:
3152 /* original region does not cover any part
3153 of the current state of the compound region
3157 case Evoral::OverlapInternal:
3158 /* overlap is just a small piece inside the
3159 * original so trim both ends
3161 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3162 modified_region = true;
3165 case Evoral::OverlapExternal:
3166 /* overlap fully covers original, so leave it
3171 case Evoral::OverlapEnd:
3172 /* overlap starts within but covers end,
3173 so trim the front of the region
3175 original->trim_front (adjusted_start);
3176 modified_region = true;
3179 case Evoral::OverlapStart:
3180 /* overlap covers start but ends within, so
3181 * trim the end of the region.
3183 original->trim_end (adjusted_end);
3184 modified_region = true;
3189 /* fix the position to match any movement of the compound region.
3191 original->set_position (original->position() + move_offset);
3192 modified_region = true;
3195 if (modified_region) {
3196 _session.add_command (new StatefulDiffCommand (original));
3199 /* and add to the list of regions waiting to be
3203 originals.push_back (original);
3204 old_and_new_regions.push_back (TwoRegions (*i, original));
3207 pre_uncombine (originals, target);
3209 in_partition = true;
3212 // (3) remove the compound region
3214 remove_region (target);
3216 // (4) add the constituent regions
3218 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3219 add_region ((*i), (*i)->position());
3220 set_layer((*i), (*i)->layer());
3221 if (!RegionFactory::region_by_id((*i)->id())) {
3222 RegionFactory::map_add(*i);
3226 in_partition = false;
3231 Playlist::fade_range (list<AudioRange>& ranges)
3233 for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3234 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3235 (*i)->fade_range ((*r).start, (*r).end);
3241 Playlist::max_source_level () const
3243 RegionReadLock rlock (const_cast<Playlist *> (this));
3246 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3247 lvl = max (lvl, (*i)->max_source_level());
3254 Playlist::set_orig_track_id (const PBD::ID& id)
3256 _orig_track_id = id;
3259 /** Take a list of ranges, coalesce any that can be coalesced, then call
3260 * check_crossfades for each one.
3263 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3265 /* XXX: it's a shame that this coalesce algorithm also exists in
3266 TimeSelection::consolidate().
3269 /* XXX: xfade: this is implemented in Evoral::RangeList */
3272 for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3273 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3279 // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3280 if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3281 i->from = min (i->from, j->from);
3282 i->to = max (i->to, j->to);
3291 Playlist::set_capture_insertion_in_progress (bool yn)
3293 _capture_insertion_underway = yn;