2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <boost/lexical_cast.hpp>
31 #include "pbd/convert.h"
32 #include "pbd/failed_constructor.h"
33 #include "pbd/stateful_diff_command.h"
34 #include "pbd/xml++.h"
36 #include "ardour/debug.h"
37 #include "ardour/playlist.h"
38 #include "ardour/session.h"
39 #include "ardour/region.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/region_sorters.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/playlist_source.h"
44 #include "ardour/transient_detector.h"
45 #include "ardour/session_playlists.h"
46 #include "ardour/source_factory.h"
51 using namespace ARDOUR;
55 namespace Properties {
56 PBD::PropertyDescriptor<bool> regions;
60 struct ShowMeTheList {
61 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
63 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
65 boost::shared_ptr<Playlist> playlist;
72 Playlist::make_property_quarks ()
74 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
75 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
76 Properties::regions.property_id));
79 RegionListProperty::RegionListProperty (Playlist& pl)
80 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
86 RegionListProperty::RegionListProperty (RegionListProperty const & p)
87 : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
88 , _playlist (p._playlist)
94 RegionListProperty::clone () const
96 return new RegionListProperty (*this);
100 RegionListProperty::create () const
102 return new RegionListProperty (_playlist);
106 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
108 /* All regions (even those which are deleted) have their state saved by other
109 code, so we can just store ID here.
112 node.add_property ("id", region->id().to_s ());
115 boost::shared_ptr<Region>
116 RegionListProperty::get_content_from_xml (XMLNode const & node) const
118 XMLProperty const * prop = node.property ("id");
121 PBD::ID id (prop->value ());
123 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
126 ret = RegionFactory::region_by_id (id);
132 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
133 : SessionObject(sess, nom)
138 first_set_state = false;
143 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
144 : SessionObject(sess, "unnamed playlist")
149 const XMLProperty* prop = node.property("type");
150 assert(!prop || DataType(prop->value()) == _type);
154 _name = "unnamed"; /* reset by set_state */
157 /* set state called by derived class */
160 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
161 : SessionObject(other->_session, namestr)
163 , _type(other->_type)
164 , _orig_track_id (other->_orig_track_id)
169 other->copy_regions (tmp);
173 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
174 add_region_internal( (*x), (*x)->position());
179 _splicing = other->_splicing;
180 _nudging = other->_nudging;
181 _edit_mode = other->_edit_mode;
184 first_set_state = false;
186 in_partition = false;
188 _frozen = other->_frozen;
191 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
192 : SessionObject(other->_session, str)
194 , _type(other->_type)
195 , _orig_track_id (other->_orig_track_id)
197 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
199 framepos_t end = start + cnt - 1;
205 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
207 boost::shared_ptr<Region> region;
208 boost::shared_ptr<Region> new_region;
209 frameoffset_t offset = 0;
210 framepos_t position = 0;
217 overlap = region->coverage (start, end);
223 case OverlapInternal:
224 offset = start - region->position();
231 position = region->position() - start;
232 len = end - region->position();
236 offset = start - region->position();
238 len = region->length() - offset;
241 case OverlapExternal:
243 position = region->position() - start;
244 len = region->length();
248 RegionFactory::region_name (new_name, region->name(), false);
252 plist.add (Properties::start, region->start() + offset);
253 plist.add (Properties::length, len);
254 plist.add (Properties::name, new_name);
255 plist.add (Properties::layer, region->layer());
256 plist.add (Properties::layering_index, region->layering_index());
258 new_region = RegionFactory::RegionFactory::create (region, plist);
260 add_region_internal (new_region, position);
264 first_set_state = false;
271 InUse (true); /* EMIT SIGNAL */
282 InUse (false); /* EMIT SIGNAL */
287 Playlist::copy_regions (RegionList& newlist) const
289 RegionLock rlock (const_cast<Playlist *> (this));
291 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
292 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
297 Playlist::init (bool hide)
299 add_property (regions);
300 _xml_node_name = X_("Playlist");
302 g_atomic_int_set (&block_notifications, 0);
303 g_atomic_int_set (&ignore_state_changes, 0);
304 pending_contents_change = false;
305 pending_layering = false;
306 first_set_state = true;
314 _edit_mode = Config->get_edit_mode();
316 in_partition = false;
321 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
322 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
324 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
327 Playlist::~Playlist ()
329 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
332 RegionLock rl (this);
334 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
335 (*i)->set_playlist (boost::shared_ptr<Playlist>());
339 /* GoingAway must be emitted by derived classes */
343 Playlist::_set_sort_id ()
346 Playlists are given names like <track name>.<id>
347 or <track name>.<edit group name>.<id> where id
348 is an integer. We extract the id and sort by that.
351 size_t dot_position = _name.val().find_last_of(".");
353 if (dot_position == string::npos) {
356 string t = _name.val().substr(dot_position + 1);
359 _sort_id = boost::lexical_cast<int>(t);
362 catch (boost::bad_lexical_cast e) {
369 Playlist::set_name (const string& str)
371 /* in a typical situation, a playlist is being used
372 by one diskstream and also is referenced by the
373 Session. if there are more references than that,
374 then don't change the name.
381 bool ret = SessionObject::set_name(str);
388 /***********************************************************************
389 CHANGE NOTIFICATION HANDLING
391 Notifications must be delayed till the region_lock is released. This
392 is necessary because handlers for the signals may need to acquire
393 the lock (e.g. to read from the playlist).
394 ***********************************************************************/
397 Playlist::begin_undo ()
404 Playlist::end_undo ()
413 delay_notifications ();
414 g_atomic_int_inc (&ignore_state_changes);
417 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
419 Playlist::thaw (bool from_undo)
421 g_atomic_int_dec_and_test (&ignore_state_changes);
422 release_notifications (from_undo);
427 Playlist::delay_notifications ()
429 g_atomic_int_inc (&block_notifications);
432 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
434 Playlist::release_notifications (bool from_undo)
436 if (g_atomic_int_dec_and_test (&block_notifications)) {
437 flush_notifications (from_undo);
442 Playlist::notify_contents_changed ()
444 if (holding_state ()) {
445 pending_contents_change = true;
447 pending_contents_change = false;
448 ContentsChanged(); /* EMIT SIGNAL */
453 Playlist::notify_layering_changed ()
455 if (holding_state ()) {
456 pending_layering = true;
458 pending_layering = false;
459 LayeringChanged(); /* EMIT SIGNAL */
464 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
466 if (holding_state ()) {
467 pending_removes.insert (r);
468 pending_contents_change = true;
470 /* this might not be true, but we have to act
471 as though it could be.
473 pending_contents_change = false;
474 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
475 ContentsChanged (); /* EMIT SIGNAL */
480 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
482 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
484 if (holding_state ()) {
486 pending_range_moves.push_back (move);
490 list< Evoral::RangeMove<framepos_t> > m;
492 RangesMoved (m, false);
498 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
500 if (r->position() >= r->last_position()) {
501 /* trimmed shorter */
505 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
507 if (holding_state ()) {
509 pending_region_extensions.push_back (extra);
513 list<Evoral::Range<framepos_t> > r;
521 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
523 if (r->length() < r->last_length()) {
524 /* trimmed shorter */
527 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
529 if (holding_state ()) {
531 pending_region_extensions.push_back (extra);
535 list<Evoral::Range<framepos_t> > r;
543 Playlist::notify_region_added (boost::shared_ptr<Region> r)
545 /* the length change might not be true, but we have to act
546 as though it could be.
549 if (holding_state()) {
550 pending_adds.insert (r);
551 pending_contents_change = true;
554 pending_contents_change = false;
555 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
556 ContentsChanged (); /* EMIT SIGNAL */
560 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
562 Playlist::flush_notifications (bool from_undo)
564 set<boost::shared_ptr<Region> > dependent_checks_needed;
565 set<boost::shared_ptr<Region> >::iterator s;
566 bool regions_changed = false;
574 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
575 regions_changed = true;
578 /* we have no idea what order the regions ended up in pending
579 bounds (it could be based on selection order, for example).
580 so, to preserve layering in the "most recently moved is higher"
581 model, sort them by existing layer, then timestamp them.
584 // RegionSortByLayer cmp;
585 // pending_bounds.sort (cmp);
587 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
588 dependent_checks_needed.insert (*r);
591 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
592 remove_dependents (*s);
593 // cerr << _name << " sends RegionRemoved\n";
594 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
597 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
598 // cerr << _name << " sends RegionAdded\n";
599 /* don't emit RegionAdded signal until relayering is done,
600 so that the region is fully setup by the time
601 anyone hear's that its been added
603 dependent_checks_needed.insert (*s);
607 ((regions_changed || pending_contents_change) && !in_set_state) ||
614 if (regions_changed || pending_contents_change) {
615 pending_contents_change = false;
616 ContentsChanged (); /* EMIT SIGNAL */
619 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
620 (*s)->clear_changes ();
621 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
624 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
625 check_dependents (*s, false);
628 if (!pending_range_moves.empty ()) {
629 RangesMoved (pending_range_moves, from_undo);
632 if (!pending_region_extensions.empty ()) {
633 RegionsExtended (pending_region_extensions);
642 Playlist::clear_pending ()
644 pending_adds.clear ();
645 pending_removes.clear ();
646 pending_bounds.clear ();
647 pending_range_moves.clear ();
648 pending_region_extensions.clear ();
649 pending_contents_change = false;
652 /*************************************************************
654 *************************************************************/
657 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
659 RegionLock rlock (this);
660 times = fabs (times);
662 int itimes = (int) floor (times);
664 framepos_t pos = position;
666 if (times == 1 && auto_partition){
667 partition(pos - 1, (pos + region->length()), true);
671 add_region_internal (region, pos);
672 set_layer (region, DBL_MAX);
673 pos += region->length();
678 /* note that itimes can be zero if we being asked to just
679 insert a single fraction of the region.
682 for (int i = 0; i < itimes; ++i) {
683 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
684 add_region_internal (copy, pos);
685 set_layer (copy, DBL_MAX);
686 pos += region->length();
689 framecnt_t length = 0;
691 if (floor (times) != times) {
692 length = (framecnt_t) floor (region->length() * (times - floor (times)));
694 RegionFactory::region_name (name, region->name(), false);
699 plist.add (Properties::start, region->start());
700 plist.add (Properties::length, length);
701 plist.add (Properties::name, name);
702 plist.add (Properties::layer, region->layer());
704 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
705 add_region_internal (sub, pos);
706 set_layer (sub, DBL_MAX);
710 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
714 Playlist::set_region_ownership ()
716 RegionLock rl (this);
717 RegionList::iterator i;
718 boost::weak_ptr<Playlist> pl (shared_from_this());
720 for (i = regions.begin(); i != regions.end(); ++i) {
721 (*i)->set_playlist (pl);
726 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
728 if (region->data_type() != _type) {
732 RegionSortByPosition cmp;
734 if (!first_set_state) {
735 boost::shared_ptr<Playlist> foo (shared_from_this());
736 region->set_playlist (boost::weak_ptr<Playlist>(foo));
739 region->set_position (position);
741 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
742 all_regions.insert (region);
744 possibly_splice_unlocked (position, region->length(), region);
746 if (!holding_state ()) {
747 /* layers get assigned from XML state, and are not reset during undo/redo */
751 /* we need to notify the existence of new region before checking dependents. Ick. */
753 notify_region_added (region);
755 if (!holding_state ()) {
756 check_dependents (region, false);
759 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
765 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
767 RegionLock rlock (this);
769 bool old_sp = _splicing;
772 remove_region_internal (old);
773 add_region_internal (newr, pos);
774 set_layer (newr, old->layer ());
778 possibly_splice_unlocked (pos, old->length() - newr->length());
782 Playlist::remove_region (boost::shared_ptr<Region> region)
784 RegionLock rlock (this);
785 remove_region_internal (region);
789 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
791 RegionList::iterator i;
795 region->set_playlist (boost::weak_ptr<Playlist>());
798 /* XXX should probably freeze here .... */
800 for (i = regions.begin(); i != regions.end(); ++i) {
803 framepos_t pos = (*i)->position();
804 framecnt_t distance = (*i)->length();
808 possibly_splice_unlocked (pos, -distance);
810 if (!holding_state ()) {
812 remove_dependents (region);
815 notify_region_removed (region);
824 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
826 if (Config->get_use_overlap_equivalency()) {
827 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
828 if ((*i)->overlap_equivalent (other)) {
829 results.push_back ((*i));
833 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
834 if ((*i)->equivalent (other)) {
835 results.push_back ((*i));
842 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
844 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
846 if ((*i) && (*i)->region_list_equivalent (other)) {
847 results.push_back (*i);
853 Playlist::partition (framepos_t start, framepos_t end, bool cut)
857 partition_internal (start, end, cut, thawlist);
859 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
860 (*i)->resume_property_changes ();
864 /** Go through each region on the playlist and cut them at start and end, removing the section between
865 * start and end if cutting == true. Regions that lie entirely within start and end are always
870 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
872 RegionList new_regions;
875 RegionLock rlock (this);
877 boost::shared_ptr<Region> region;
878 boost::shared_ptr<Region> current;
880 RegionList::iterator tmp;
882 framepos_t pos1, pos2, pos3, pos4;
886 /* need to work from a copy, because otherwise the regions we add during the process
887 get operated on as well.
890 RegionList copy = regions.rlist();
892 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
899 if (current->first_frame() >= start && current->last_frame() < end) {
902 remove_region_internal (current);
908 /* coverage will return OverlapStart if the start coincides
909 with the end point. we do not partition such a region,
910 so catch this special case.
913 if (current->first_frame() >= end) {
917 if ((overlap = current->coverage (start, end)) == OverlapNone) {
921 pos1 = current->position();
924 pos4 = current->last_frame();
926 if (overlap == OverlapInternal) {
927 /* split: we need 3 new regions, the front, middle and end.
928 cut: we need 2 regions, the front and end.
933 ---------------*************************------------
936 ---------------*****++++++++++++++++====------------
938 ---------------*****----------------====------------
943 /* "middle" ++++++ */
945 RegionFactory::region_name (new_name, current->name(), false);
949 plist.add (Properties::start, current->start() + (pos2 - pos1));
950 plist.add (Properties::length, pos3 - pos2);
951 plist.add (Properties::name, new_name);
952 plist.add (Properties::layer, current->layer ());
953 plist.add (Properties::layering_index, current->layering_index ());
954 plist.add (Properties::automatic, true);
955 plist.add (Properties::left_of_split, true);
956 plist.add (Properties::right_of_split, true);
958 region = RegionFactory::create (current, plist);
959 add_region_internal (region, start);
960 new_regions.push_back (region);
965 RegionFactory::region_name (new_name, current->name(), false);
969 plist.add (Properties::start, current->start() + (pos3 - pos1));
970 plist.add (Properties::length, pos4 - pos3);
971 plist.add (Properties::name, new_name);
972 plist.add (Properties::layer, current->layer ());
973 plist.add (Properties::layering_index, current->layering_index ());
974 plist.add (Properties::automatic, true);
975 plist.add (Properties::right_of_split, true);
977 region = RegionFactory::create (current, plist);
979 add_region_internal (region, end);
980 new_regions.push_back (region);
984 current->suspend_property_changes ();
985 thawlist.push_back (current);
986 current->cut_end (pos2 - 1);
988 } else if (overlap == OverlapEnd) {
992 ---------------*************************------------
995 ---------------**************+++++++++++------------
997 ---------------**************-----------------------
1004 RegionFactory::region_name (new_name, current->name(), false);
1008 plist.add (Properties::start, current->start() + (pos2 - pos1));
1009 plist.add (Properties::length, pos4 - pos2);
1010 plist.add (Properties::name, new_name);
1011 plist.add (Properties::layer, current->layer ());
1012 plist.add (Properties::layering_index, current->layering_index ());
1013 plist.add (Properties::automatic, true);
1014 plist.add (Properties::left_of_split, true);
1016 region = RegionFactory::create (current, plist);
1018 add_region_internal (region, start);
1019 new_regions.push_back (region);
1024 current->suspend_property_changes ();
1025 thawlist.push_back (current);
1026 current->cut_end (pos2 - 1);
1028 } else if (overlap == OverlapStart) {
1030 /* split: we need 2 regions: the front and the end.
1031 cut: just trim current to skip the cut area
1036 ---------------*************************------------
1040 ---------------****+++++++++++++++++++++------------
1042 -------------------*********************------------
1048 RegionFactory::region_name (new_name, current->name(), false);
1052 plist.add (Properties::start, current->start());
1053 plist.add (Properties::length, pos3 - pos1);
1054 plist.add (Properties::name, new_name);
1055 plist.add (Properties::layer, current->layer ());
1056 plist.add (Properties::layering_index, current->layering_index ());
1057 plist.add (Properties::automatic, true);
1058 plist.add (Properties::right_of_split, true);
1060 region = RegionFactory::create (current, plist);
1062 add_region_internal (region, pos1);
1063 new_regions.push_back (region);
1068 current->suspend_property_changes ();
1069 thawlist.push_back (current);
1070 current->trim_front (pos3);
1071 } else if (overlap == OverlapExternal) {
1073 /* split: no split required.
1074 cut: remove the region.
1079 ---------------*************************------------
1083 ---------------*************************------------
1085 ----------------------------------------------------
1090 remove_region_internal (current);
1093 new_regions.push_back (current);
1097 in_partition = false;
1100 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1101 check_dependents (*i, false);
1105 boost::shared_ptr<Playlist>
1106 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1108 boost::shared_ptr<Playlist> ret;
1109 boost::shared_ptr<Playlist> pl;
1112 if (ranges.empty()) {
1113 return boost::shared_ptr<Playlist>();
1116 start = ranges.front().start;
1118 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1120 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1122 if (i == ranges.begin()) {
1126 /* paste the next section into the nascent playlist,
1127 offset to reflect the start of the first range we
1131 ret->paste (pl, (*i).start - start, 1.0f);
1138 boost::shared_ptr<Playlist>
1139 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1141 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1142 return cut_copy (pmf, ranges, result_is_hidden);
1145 boost::shared_ptr<Playlist>
1146 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1148 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1149 return cut_copy (pmf, ranges, result_is_hidden);
1152 boost::shared_ptr<Playlist>
1153 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1155 boost::shared_ptr<Playlist> the_copy;
1156 RegionList thawlist;
1159 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1160 string new_name = _name;
1164 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1165 return boost::shared_ptr<Playlist>();
1168 partition_internal (start, start+cnt-1, true, thawlist);
1170 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1171 (*i)->resume_property_changes();
1177 boost::shared_ptr<Playlist>
1178 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1182 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1183 string new_name = _name;
1187 cnt = min (_get_extent().second - start, cnt);
1188 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1192 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1194 times = fabs (times);
1197 RegionLock rl1 (this);
1198 RegionLock rl2 (other.get());
1200 int itimes = (int) floor (times);
1201 framepos_t pos = position;
1202 framecnt_t const shift = other->_get_extent().second;
1203 layer_t top = top_layer ();
1206 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1207 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1209 /* put these new regions on top of all existing ones, but preserve
1210 the ordering they had in the original playlist.
1213 add_region_internal (copy_of_region, (*i)->position() + pos);
1214 set_layer (copy_of_region, copy_of_region->layer() + top);
1225 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1227 times = fabs (times);
1229 RegionLock rl (this);
1230 int itimes = (int) floor (times);
1231 framepos_t pos = position + 1;
1234 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1235 add_region_internal (copy, pos);
1236 set_layer (copy, DBL_MAX);
1237 pos += region->length();
1240 if (floor (times) != times) {
1241 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1243 RegionFactory::region_name (name, region->name(), false);
1248 plist.add (Properties::start, region->start());
1249 plist.add (Properties::length, length);
1250 plist.add (Properties::name, name);
1252 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1253 add_region_internal (sub, pos);
1254 set_layer (sub, DBL_MAX);
1260 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1262 RegionLock rlock (this);
1263 RegionList copy (regions.rlist());
1266 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1268 if ((*r)->last_frame() < at) {
1273 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1274 /* intersected region */
1275 if (!move_intersected) {
1280 /* do not move regions glued to music time - that
1281 has to be done separately.
1284 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1285 fixup.push_back (*r);
1289 (*r)->set_position ((*r)->position() + distance);
1292 /* XXX: may not be necessary; Region::post_set should do this, I think */
1293 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1294 (*r)->recompute_position_from_lock_style ();
1299 Playlist::split (framepos_t at)
1301 RegionLock rlock (this);
1302 RegionList copy (regions.rlist());
1304 /* use a copy since this operation can modify the region list
1307 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1308 _split_region (*r, at);
1313 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1315 RegionLock rl (this);
1316 _split_region (region, playlist_position);
1320 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1322 if (!region->covers (playlist_position)) {
1326 if (region->position() == playlist_position ||
1327 region->last_frame() == playlist_position) {
1331 boost::shared_ptr<Region> left;
1332 boost::shared_ptr<Region> right;
1333 frameoffset_t before;
1334 frameoffset_t after;
1338 /* split doesn't change anything about length, so don't try to splice */
1340 bool old_sp = _splicing;
1343 before = playlist_position - region->position();
1344 after = region->length() - before;
1346 RegionFactory::region_name (before_name, region->name(), false);
1351 plist.add (Properties::position, region->position ());
1352 plist.add (Properties::length, before);
1353 plist.add (Properties::name, before_name);
1354 plist.add (Properties::left_of_split, true);
1355 plist.add (Properties::layering_index, region->layering_index ());
1356 plist.add (Properties::layer, region->layer ());
1358 /* note: we must use the version of ::create with an offset here,
1359 since it supplies that offset to the Region constructor, which
1360 is necessary to get audio region gain envelopes right.
1362 left = RegionFactory::create (region, 0, plist);
1365 RegionFactory::region_name (after_name, region->name(), false);
1370 plist.add (Properties::position, region->position() + before);
1371 plist.add (Properties::length, after);
1372 plist.add (Properties::name, after_name);
1373 plist.add (Properties::right_of_split, true);
1374 plist.add (Properties::layering_index, region->layering_index ());
1375 plist.add (Properties::layer, region->layer ());
1377 /* same note as above */
1378 right = RegionFactory::create (region, before, plist);
1381 add_region_internal (left, region->position());
1382 add_region_internal (right, region->position() + before);
1384 finalize_split_region (region, left, right);
1386 remove_region_internal (region);
1392 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1394 if (_splicing || in_set_state) {
1395 /* don't respond to splicing moves or state setting */
1399 if (_edit_mode == Splice) {
1400 splice_locked (at, distance, exclude);
1405 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1407 if (_splicing || in_set_state) {
1408 /* don't respond to splicing moves or state setting */
1412 if (_edit_mode == Splice) {
1413 splice_unlocked (at, distance, exclude);
1418 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1421 RegionLock rl (this);
1422 core_splice (at, distance, exclude);
1427 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1429 core_splice (at, distance, exclude);
1433 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1437 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1439 if (exclude && (*i) == exclude) {
1443 if ((*i)->position() >= at) {
1444 framepos_t new_pos = (*i)->position() + distance;
1447 } else if (new_pos >= max_framepos - (*i)->length()) {
1448 new_pos = max_framepos - (*i)->length();
1451 (*i)->set_position (new_pos);
1457 notify_contents_changed ();
1461 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1463 if (in_set_state || _splicing || _nudging || _shuffling) {
1467 if (what_changed.contains (Properties::position)) {
1469 /* remove it from the list then add it back in
1470 the right place again.
1473 RegionSortByPosition cmp;
1475 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1477 if (i == regions.end()) {
1478 /* the region bounds are being modified but its not currently
1479 in the region list. we will use its bounds correctly when/if
1486 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1489 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1491 frameoffset_t delta = 0;
1493 if (what_changed.contains (Properties::position)) {
1494 delta = region->position() - region->last_position();
1497 if (what_changed.contains (Properties::length)) {
1498 delta += region->length() - region->last_length();
1502 possibly_splice (region->last_position() + region->last_length(), delta, region);
1505 if (holding_state ()) {
1506 pending_bounds.push_back (region);
1508 notify_contents_changed ();
1510 check_dependents (region, false);
1516 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1518 boost::shared_ptr<Region> region (weak_region.lock());
1524 /* this makes a virtual call to the right kind of playlist ... */
1526 region_changed (what_changed, region);
1530 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1532 PropertyChange our_interests;
1533 PropertyChange bounds;
1534 PropertyChange pos_and_length;
1537 if (in_set_state || in_flush) {
1541 our_interests.add (Properties::muted);
1542 our_interests.add (Properties::layer);
1543 our_interests.add (Properties::opaque);
1545 bounds.add (Properties::start);
1546 bounds.add (Properties::position);
1547 bounds.add (Properties::length);
1549 pos_and_length.add (Properties::position);
1550 pos_and_length.add (Properties::length);
1552 if (what_changed.contains (bounds)) {
1553 region_bounds_changed (what_changed, region);
1554 save = !(_splicing || _nudging);
1557 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1558 check_dependents (region, false);
1561 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1562 notify_region_moved (region);
1563 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1564 notify_region_end_trimmed (region);
1565 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1566 notify_region_start_trimmed (region);
1569 /* don't notify about layer changes, since we are the only object that can initiate
1570 them, and we notify in ::relayer()
1573 if (what_changed.contains (our_interests)) {
1581 Playlist::drop_regions ()
1583 RegionLock rl (this);
1585 all_regions.clear ();
1589 Playlist::sync_all_regions_with_regions ()
1591 RegionLock rl (this);
1593 all_regions.clear ();
1595 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1596 all_regions.insert (*i);
1601 Playlist::clear (bool with_signals)
1604 RegionLock rl (this);
1606 region_state_changed_connections.drop_connections ();
1608 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1609 pending_removes.insert (*i);
1614 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1615 remove_dependents (*s);
1621 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1622 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1625 pending_removes.clear ();
1626 pending_contents_change = false;
1632 /***********************************************************************
1634 **********************************************************************/
1636 Playlist::RegionList *
1637 Playlist::regions_at (framepos_t frame)
1640 RegionLock rlock (this);
1641 return find_regions_at (frame);
1645 Playlist::count_regions_at (framepos_t frame) const
1647 RegionLock rlock (const_cast<Playlist*>(this));
1650 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1651 if ((*i)->covers (frame)) {
1659 boost::shared_ptr<Region>
1660 Playlist::top_region_at (framepos_t frame)
1663 RegionLock rlock (this);
1664 RegionList *rlist = find_regions_at (frame);
1665 boost::shared_ptr<Region> region;
1667 if (rlist->size()) {
1668 RegionSortByLayer cmp;
1670 region = rlist->back();
1677 boost::shared_ptr<Region>
1678 Playlist::top_unmuted_region_at (framepos_t frame)
1681 RegionLock rlock (this);
1682 RegionList *rlist = find_regions_at (frame);
1684 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1686 RegionList::iterator tmp = i;
1689 if ((*i)->muted()) {
1696 boost::shared_ptr<Region> region;
1698 if (rlist->size()) {
1699 RegionSortByLayer cmp;
1701 region = rlist->back();
1708 Playlist::RegionList*
1709 Playlist::regions_to_read (framepos_t start, framepos_t end)
1711 /* Caller must hold lock */
1713 RegionList covering;
1714 set<framepos_t> to_check;
1715 set<boost::shared_ptr<Region> > unique;
1717 to_check.insert (start);
1718 to_check.insert (end);
1720 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1722 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1724 /* find all/any regions that span start+end */
1726 switch ((*i)->coverage (start, end)) {
1730 case OverlapInternal:
1731 covering.push_back (*i);
1732 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1736 to_check.insert ((*i)->position());
1737 if ((*i)->position() != 0) {
1738 to_check.insert ((*i)->position()-1);
1740 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1741 covering.push_back (*i);
1745 to_check.insert ((*i)->last_frame());
1746 to_check.insert ((*i)->last_frame()+1);
1747 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1748 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1749 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1750 covering.push_back (*i);
1753 case OverlapExternal:
1754 covering.push_back (*i);
1755 to_check.insert ((*i)->position());
1756 if ((*i)->position() != 0) {
1757 to_check.insert ((*i)->position()-1);
1759 to_check.insert ((*i)->last_frame());
1760 to_check.insert ((*i)->last_frame()+1);
1761 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1762 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1763 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1767 /* don't go too far */
1769 if ((*i)->position() > end) {
1774 RegionList* rlist = new RegionList;
1776 /* find all the regions that cover each position .... */
1778 if (covering.size() == 1) {
1780 rlist->push_back (covering.front());
1781 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1786 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1790 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1792 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1794 if ((*x)->covers (*t)) {
1795 here.push_back (*x);
1796 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1800 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1807 RegionSortByLayer cmp;
1810 /* ... and get the top/transparent regions at "here" */
1812 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1816 if ((*c)->opaque()) {
1818 /* the other regions at this position are hidden by this one */
1819 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1826 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1827 rlist->push_back (*s);
1830 if (rlist->size() > 1) {
1831 /* now sort by time order */
1833 RegionSortByPosition cmp;
1838 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1843 Playlist::RegionList *
1844 Playlist::find_regions_at (framepos_t frame)
1846 /* Caller must hold lock */
1848 RegionList *rlist = new RegionList;
1850 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1851 if ((*i)->covers (frame)) {
1852 rlist->push_back (*i);
1859 Playlist::RegionList *
1860 Playlist::regions_touched (framepos_t start, framepos_t end)
1862 RegionLock rlock (this);
1863 RegionList *rlist = new RegionList;
1865 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1866 if ((*i)->coverage (start, end) != OverlapNone) {
1867 rlist->push_back (*i);
1875 Playlist::find_next_transient (framepos_t from, int dir)
1877 RegionLock rlock (this);
1878 AnalysisFeatureList points;
1879 AnalysisFeatureList these_points;
1881 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1883 if ((*i)->last_frame() < from) {
1887 if ((*i)->first_frame() > from) {
1892 (*i)->get_transients (these_points);
1894 /* add first frame, just, err, because */
1896 these_points.push_back ((*i)->first_frame());
1898 points.insert (points.end(), these_points.begin(), these_points.end());
1899 these_points.clear ();
1902 if (points.empty()) {
1906 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1907 bool reached = false;
1910 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1915 if (reached && (*x) > from) {
1920 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1925 if (reached && (*x) < from) {
1934 boost::shared_ptr<Region>
1935 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1937 RegionLock rlock (this);
1938 boost::shared_ptr<Region> ret;
1939 framepos_t closest = max_framepos;
1941 bool end_iter = false;
1943 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1947 frameoffset_t distance;
1948 boost::shared_ptr<Region> r = (*i);
1953 pos = r->first_frame ();
1956 pos = r->last_frame ();
1959 pos = r->sync_position ();
1964 case 1: /* forwards */
1967 if ((distance = pos - frame) < closest) {
1976 default: /* backwards */
1979 if ((distance = frame - pos) < closest) {
1996 Playlist::find_next_region_boundary (framepos_t frame, int dir)
1998 RegionLock rlock (this);
2000 framepos_t closest = max_framepos;
2001 framepos_t ret = -1;
2005 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2007 boost::shared_ptr<Region> r = (*i);
2008 frameoffset_t distance;
2010 if (r->first_frame() > frame) {
2012 distance = r->first_frame() - frame;
2014 if (distance < closest) {
2015 ret = r->first_frame();
2020 if (r->last_frame () > frame) {
2022 distance = r->last_frame () - frame;
2024 if (distance < closest) {
2025 ret = r->last_frame ();
2033 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2035 boost::shared_ptr<Region> r = (*i);
2036 frameoffset_t distance;
2038 if (r->last_frame() < frame) {
2040 distance = frame - r->last_frame();
2042 if (distance < closest) {
2043 ret = r->last_frame();
2048 if (r->first_frame() < frame) {
2050 distance = frame - r->first_frame();
2052 if (distance < closest) {
2053 ret = r->first_frame();
2064 /***********************************************************************/
2070 Playlist::mark_session_dirty ()
2072 if (!in_set_state && !holding_state ()) {
2073 _session.set_dirty();
2078 Playlist::rdiff (vector<Command*>& cmds) const
2080 RegionLock rlock (const_cast<Playlist *> (this));
2081 Stateful::rdiff (cmds);
2085 Playlist::clear_owned_changes ()
2087 RegionLock rlock (this);
2088 Stateful::clear_owned_changes ();
2092 Playlist::update (const RegionListProperty::ChangeRecord& change)
2094 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2095 name(), change.added.size(), change.removed.size()));
2098 /* add the added regions */
2099 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2100 add_region ((*i), (*i)->position());
2102 /* remove the removed regions */
2103 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2111 Playlist::set_state (const XMLNode& node, int version)
2115 XMLNodeConstIterator niter;
2116 XMLPropertyList plist;
2117 XMLPropertyConstIterator piter;
2119 boost::shared_ptr<Region> region;
2121 bool seen_region_nodes = false;
2126 if (node.name() != "Playlist") {
2133 plist = node.properties();
2137 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2141 if (prop->name() == X_("name")) {
2142 _name = prop->value();
2144 } else if (prop->name() == X_("orig-diskstream-id")) {
2145 /* XXX legacy session: fix up later */
2146 _orig_track_id = prop->value ();
2147 } else if (prop->name() == X_("orig-track-id")) {
2148 _orig_track_id = prop->value ();
2149 } else if (prop->name() == X_("frozen")) {
2150 _frozen = string_is_affirmative (prop->value());
2151 } else if (prop->name() == X_("combine-ops")) {
2152 _combine_ops = atoi (prop->value());
2158 nlist = node.children();
2160 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2164 if (child->name() == "Region") {
2166 seen_region_nodes = true;
2168 if ((prop = child->property ("id")) == 0) {
2169 error << _("region state node has no ID, ignored") << endmsg;
2173 ID id = prop->value ();
2175 if ((region = region_by_id (id))) {
2177 region->suspend_property_changes ();
2179 if (region->set_state (*child, version)) {
2180 region->resume_property_changes ();
2184 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2185 region->suspend_property_changes ();
2187 error << _("Playlist: cannot create region from XML") << endmsg;
2192 RegionLock rlock (this);
2193 add_region_internal (region, region->position());
2196 region->resume_property_changes ();
2201 if (seen_region_nodes && regions.empty()) {
2205 /* update dependents, which was not done during add_region_internal
2206 due to in_set_state being true
2209 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2210 check_dependents (*r, false);
2215 notify_contents_changed ();
2218 first_set_state = false;
2224 Playlist::get_state()
2226 return state (true);
2230 Playlist::get_template()
2232 return state (false);
2235 /** @param full_state true to include regions in the returned state, otherwise false.
2238 Playlist::state (bool full_state)
2240 XMLNode *node = new XMLNode (X_("Playlist"));
2243 node->add_property (X_("id"), id().to_s());
2244 node->add_property (X_("name"), _name);
2245 node->add_property (X_("type"), _type.to_string());
2247 _orig_track_id.print (buf, sizeof (buf));
2248 node->add_property (X_("orig-track-id"), buf);
2249 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2252 RegionLock rlock (this, false);
2254 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2255 node->add_property ("combine-ops", buf);
2257 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2258 node->add_child_nocopy ((*i)->get_state());
2263 node->add_child_copy (*_extra_xml);
2270 Playlist::empty() const
2272 RegionLock rlock (const_cast<Playlist *>(this), false);
2273 return regions.empty();
2277 Playlist::n_regions() const
2279 RegionLock rlock (const_cast<Playlist *>(this), false);
2280 return regions.size();
2283 pair<framepos_t, framepos_t>
2284 Playlist::get_extent () const
2286 RegionLock rlock (const_cast<Playlist *>(this), false);
2287 return _get_extent ();
2290 pair<framepos_t, framepos_t>
2291 Playlist::_get_extent () const
2293 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2295 if (regions.empty()) {
2300 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2301 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2302 if (e.first < ext.first) {
2303 ext.first = e.first;
2305 if (e.second > ext.second) {
2306 ext.second = e.second;
2314 Playlist::bump_name (string name, Session &session)
2316 string newname = name;
2319 newname = bump_name_once (newname, '.');
2320 } while (session.playlists->by_name (newname)!=NULL);
2327 Playlist::top_layer() const
2329 RegionLock rlock (const_cast<Playlist *> (this));
2332 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2333 top = max (top, (*i)->layer());
2339 Playlist::set_edit_mode (EditMode mode)
2344 struct RelayerSort {
2345 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2346 return a->layering_index() < b->layering_index();
2350 /** Set a new layer for a region. This adjusts the layering indices of all
2351 * regions in the playlist to put the specified region in the appropriate
2352 * place. The actual layering will be fixed up when relayer() happens.
2356 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2358 /* Remove the layer we are setting from our region list, and sort it */
2359 RegionList copy = regions.rlist();
2360 copy.remove (region);
2361 copy.sort (RelayerSort ());
2363 /* Put region back in the right place */
2364 RegionList::iterator i = copy.begin();
2365 while (i != copy.end ()) {
2366 if ((*i)->layer() > new_layer) {
2372 copy.insert (i, region);
2374 /* Then re-write layering indices */
2376 for (RegionList::iterator k = copy.begin(); k != copy.end(); ++k) {
2377 (*k)->set_layering_index (j++);
2381 /** Take the layering indices of each of our regions, compute the layers
2382 * that they should be on, and write the layers back to the regions.
2385 Playlist::relayer ()
2387 /* never compute layers when setting from XML */
2393 bool changed = false;
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 */
2418 RegionList copy = regions.rlist();
2419 copy.sort (RelayerSort ());
2421 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2423 /* find the time divisions that this region covers; if there are no regions on the list,
2424 division_size will equal 0 and in this case we'll just say that
2425 start_division = end_division = 0.
2427 int start_division = 0;
2428 int end_division = 0;
2430 if (division_size > 0) {
2431 start_division = floor ( ((*i)->position() - start) / division_size);
2432 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2433 if (end_division == divisions) {
2438 assert (divisions == 0 || end_division < divisions);
2440 /* find the lowest layer that this region can go on */
2441 size_t j = layers.size();
2443 /* try layer j - 1; it can go on if it overlaps no other region
2444 that is already on that layer
2447 bool overlap = false;
2448 for (int k = start_division; k <= end_division; ++k) {
2449 RegionList::iterator l = layers[j-1][k].begin ();
2450 while (l != layers[j-1][k].end()) {
2451 if ((*l)->overlap_equivalent (*i)) {
2464 /* overlap, so we must use layer j */
2471 if (j == layers.size()) {
2472 /* we need a new layer for this region */
2473 layers.push_back (vector<RegionList> (divisions));
2476 /* put a reference to this region in each of the divisions that it exists in */
2477 for (int k = start_division; k <= end_division; ++k) {
2478 layers[j][k].push_back (*i);
2481 if ((*i)->layer() != j) {
2485 (*i)->set_layer (j);
2489 notify_layering_changed ();
2494 Playlist::raise_region (boost::shared_ptr<Region> region)
2496 set_layer (region, region->layer() + 1.5);
2501 Playlist::lower_region (boost::shared_ptr<Region> region)
2503 set_layer (region, region->layer() - 1.5);
2508 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2510 set_layer (region, DBL_MAX);
2515 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2517 set_layer (region, -0.5);
2522 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2524 RegionList::iterator i;
2530 RegionLock rlock (const_cast<Playlist *> (this));
2532 for (i = regions.begin(); i != regions.end(); ++i) {
2534 if ((*i)->position() >= start) {
2540 if ((*i)->last_frame() > max_framepos - distance) {
2541 new_pos = max_framepos - (*i)->length();
2543 new_pos = (*i)->position() + distance;
2548 if ((*i)->position() > distance) {
2549 new_pos = (*i)->position() - distance;
2555 (*i)->set_position (new_pos);
2563 notify_contents_changed ();
2569 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2571 RegionLock rlock (const_cast<Playlist*> (this));
2573 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2574 if ((*r)->uses_source (src)) {
2582 boost::shared_ptr<Region>
2583 Playlist::find_region (const ID& id) const
2585 RegionLock rlock (const_cast<Playlist*> (this));
2587 /* searches all regions currently in use by the playlist */
2589 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2590 if ((*i)->id() == id) {
2595 return boost::shared_ptr<Region> ();
2599 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2601 RegionLock rlock (const_cast<Playlist*> (this));
2604 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2613 boost::shared_ptr<Region>
2614 Playlist::region_by_id (const ID& id) const
2616 /* searches all regions ever added to this playlist */
2618 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2619 if ((*i)->id() == id) {
2623 return boost::shared_ptr<Region> ();
2627 Playlist::dump () const
2629 boost::shared_ptr<Region> r;
2631 cerr << "Playlist \"" << _name << "\" " << endl
2632 << regions.size() << " regions "
2635 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2637 cerr << " " << r->name() << " ["
2638 << r->start() << "+" << r->length()
2648 Playlist::set_frozen (bool yn)
2654 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2658 if (region->locked()) {
2665 RegionLock rlock (const_cast<Playlist*> (this));
2670 RegionList::iterator next;
2672 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2673 if ((*i) == region) {
2677 if (next != regions.end()) {
2679 if ((*next)->locked()) {
2685 if ((*next)->position() != region->last_frame() + 1) {
2686 /* they didn't used to touch, so after shuffle,
2687 just have them swap positions.
2689 new_pos = (*next)->position();
2691 /* they used to touch, so after shuffle,
2692 make sure they still do. put the earlier
2693 region where the later one will end after
2696 new_pos = region->position() + (*next)->length();
2699 (*next)->set_position (region->position());
2700 region->set_position (new_pos);
2702 /* avoid a full sort */
2704 regions.erase (i); // removes the region from the list */
2706 regions.insert (next, region); // adds it back after next
2715 RegionList::iterator prev = regions.end();
2717 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2718 if ((*i) == region) {
2720 if (prev != regions.end()) {
2722 if ((*prev)->locked()) {
2727 if (region->position() != (*prev)->last_frame() + 1) {
2728 /* they didn't used to touch, so after shuffle,
2729 just have them swap positions.
2731 new_pos = region->position();
2733 /* they used to touch, so after shuffle,
2734 make sure they still do. put the earlier
2735 one where the later one will end after
2737 new_pos = (*prev)->position() + region->length();
2740 region->set_position ((*prev)->position());
2741 (*prev)->set_position (new_pos);
2743 /* avoid a full sort */
2745 regions.erase (i); // remove region
2746 regions.insert (prev, region); // insert region before prev
2762 check_dependents (region, false);
2764 notify_contents_changed();
2770 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2772 RegionLock rlock (const_cast<Playlist*> (this));
2774 if (regions.size() > 1) {
2782 Playlist::update_after_tempo_map_change ()
2784 RegionLock rlock (const_cast<Playlist*> (this));
2785 RegionList copy (regions.rlist());
2789 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2790 (*i)->update_after_tempo_map_change ();
2797 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2799 RegionLock rl (this, false);
2800 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2806 Playlist::has_region_at (framepos_t const p) const
2808 RegionLock (const_cast<Playlist *> (this));
2810 RegionList::const_iterator i = regions.begin ();
2811 while (i != regions.end() && !(*i)->covers (p)) {
2815 return (i != regions.end());
2818 /** Remove any region that uses a given source */
2820 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2822 RegionLock rl (this);
2824 RegionList::iterator i = regions.begin();
2825 while (i != regions.end()) {
2826 RegionList::iterator j = i;
2829 if ((*i)->uses_source (s)) {
2830 remove_region_internal (*i);
2837 /** Look from a session frame time and find the start time of the next region
2838 * which is on the top layer of this playlist.
2839 * @param t Time to look from.
2840 * @return Position of next top-layered region, or max_framepos if there isn't one.
2843 Playlist::find_next_top_layer_position (framepos_t t) const
2845 RegionLock rlock (const_cast<Playlist *> (this));
2847 layer_t const top = top_layer ();
2849 RegionList copy = regions.rlist ();
2850 copy.sort (RegionSortByPosition ());
2852 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2853 if ((*i)->position() >= t && (*i)->layer() == top) {
2854 return (*i)->position();
2858 return max_framepos;
2861 boost::shared_ptr<Region>
2862 Playlist::combine (const RegionList& r)
2865 uint32_t channels = 0;
2867 framepos_t earliest_position = max_framepos;
2868 vector<TwoRegions> old_and_new_regions;
2869 vector<boost::shared_ptr<Region> > originals;
2870 vector<boost::shared_ptr<Region> > copies;
2873 uint32_t max_level = 0;
2875 /* find the maximum depth of all the regions we're combining */
2877 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2878 max_level = max (max_level, (*i)->max_source_level());
2881 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2882 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2884 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2886 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2887 earliest_position = min (earliest_position, (*i)->position());
2890 /* enable this so that we do not try to create xfades etc. as we add
2894 pl->in_partition = true;
2896 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2898 /* copy the region */
2900 boost::shared_ptr<Region> original_region = (*i);
2901 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2903 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2904 originals.push_back (original_region);
2905 copies.push_back (copied_region);
2907 RegionFactory::add_compound_association (original_region, copied_region);
2909 /* make position relative to zero */
2911 pl->add_region (copied_region, original_region->position() - earliest_position);
2913 /* use the maximum number of channels for any region */
2915 channels = max (channels, original_region->n_channels());
2917 /* it will go above the layer of the highest existing region */
2919 layer = max (layer, original_region->layer());
2922 pl->in_partition = false;
2924 pre_combine (copies);
2926 /* add any dependent regions to the new playlist */
2928 copy_dependents (old_and_new_regions, pl.get());
2930 /* now create a new PlaylistSource for each channel in the new playlist */
2933 pair<framepos_t,framepos_t> extent = pl->get_extent();
2935 for (uint32_t chn = 0; chn < channels; ++chn) {
2936 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2940 /* now a new whole-file region using the list of sources */
2942 plist.add (Properties::start, 0);
2943 plist.add (Properties::length, extent.second);
2944 plist.add (Properties::name, parent_name);
2945 plist.add (Properties::whole_file, true);
2947 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2949 /* now the non-whole-file region that we will actually use in the
2954 plist.add (Properties::start, 0);
2955 plist.add (Properties::length, extent.second);
2956 plist.add (Properties::name, child_name);
2957 plist.add (Properties::layer, layer+1);
2959 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2961 /* remove all the selected regions from the current playlist
2966 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2970 /* do type-specific stuff with the originals and the new compound
2974 post_combine (originals, compound_region);
2976 /* add the new region at the right location */
2978 add_region (compound_region, earliest_position);
2984 return compound_region;
2988 Playlist::uncombine (boost::shared_ptr<Region> target)
2990 boost::shared_ptr<PlaylistSource> pls;
2991 boost::shared_ptr<const Playlist> pl;
2992 vector<boost::shared_ptr<Region> > originals;
2993 vector<TwoRegions> old_and_new_regions;
2995 // (1) check that its really a compound region
2997 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3001 pl = pls->playlist();
3003 framepos_t adjusted_start = 0; // gcc isn't smart enough
3004 framepos_t adjusted_end = 0; // gcc isn't smart enough
3006 /* the leftmost (earliest) edge of the compound region
3007 starts at zero in its source, or larger if it
3008 has been trimmed or content-scrolled.
3010 the rightmost (latest) edge of the compound region
3011 relative to its source is the starting point plus
3012 the length of the region.
3015 // (2) get all the original regions
3017 const RegionList& rl (pl->region_list().rlist());
3018 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3019 frameoffset_t move_offset = 0;
3021 /* there are two possibilities here:
3022 1) the playlist that the playlist source was based on
3023 is us, so just add the originals (which belonged to
3024 us anyway) back in the right place.
3026 2) the playlist that the playlist source was based on
3027 is NOT us, so we need to make copies of each of
3028 the original regions that we find, and add them
3031 bool same_playlist = (pls->original() == id());
3033 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3035 boost::shared_ptr<Region> current (*i);
3037 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3039 if (ca == cassocs.end()) {
3043 boost::shared_ptr<Region> original (ca->second);
3044 bool modified_region;
3046 if (i == rl.begin()) {
3047 move_offset = (target->position() - original->position()) - target->start();
3048 adjusted_start = original->position() + target->start();
3049 adjusted_end = adjusted_start + target->length();
3052 if (!same_playlist) {
3053 framepos_t pos = original->position();
3054 /* make a copy, but don't announce it */
3055 original = RegionFactory::create (original, false);
3056 /* the pure copy constructor resets position() to zero,
3059 original->set_position (pos);
3062 /* check to see how the original region (in the
3063 * playlist before compounding occured) overlaps
3064 * with the new state of the compound region.
3067 original->clear_changes ();
3068 modified_region = false;
3070 switch (original->coverage (adjusted_start, adjusted_end)) {
3072 /* original region does not cover any part
3073 of the current state of the compound region
3077 case OverlapInternal:
3078 /* overlap is just a small piece inside the
3079 * original so trim both ends
3081 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3082 modified_region = true;
3085 case OverlapExternal:
3086 /* overlap fully covers original, so leave it
3092 /* overlap starts within but covers end,
3093 so trim the front of the region
3095 original->trim_front (adjusted_start);
3096 modified_region = true;
3100 /* overlap covers start but ends within, so
3101 * trim the end of the region.
3103 original->trim_end (adjusted_end);
3104 modified_region = true;
3109 /* fix the position to match any movement of the compound region.
3111 original->set_position (original->position() + move_offset);
3112 modified_region = true;
3115 if (modified_region) {
3116 _session.add_command (new StatefulDiffCommand (original));
3119 /* and add to the list of regions waiting to be
3123 originals.push_back (original);
3124 old_and_new_regions.push_back (TwoRegions (*i, original));
3127 pre_uncombine (originals, target);
3129 in_partition = true;
3132 // (3) remove the compound region
3134 remove_region (target);
3136 // (4) add the constituent regions
3138 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3139 add_region ((*i), (*i)->position());
3142 /* now move dependent regions back from the compound to this playlist */
3144 pl->copy_dependents (old_and_new_regions, this);
3146 in_partition = false;
3151 Playlist::max_source_level () const
3153 RegionLock rlock (const_cast<Playlist *> (this));
3156 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3157 lvl = max (lvl, (*i)->max_source_level());
3165 Playlist::count_joined_regions () const
3167 RegionLock rlock (const_cast<Playlist *> (this));
3170 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3171 if ((*i)->max_source_level() > 0) {
3180 Playlist::set_orig_track_id (const PBD::ID& id)
3182 _orig_track_id = id;
3186 Playlist::highest_layering_index () const
3188 RegionLock rlock (const_cast<Playlist *> (this));
3191 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3192 h = max (h, (*i)->layering_index ());