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 DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2422 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2423 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2426 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2428 /* find the time divisions that this region covers; if there are no regions on the list,
2429 division_size will equal 0 and in this case we'll just say that
2430 start_division = end_division = 0.
2432 int start_division = 0;
2433 int end_division = 0;
2435 if (division_size > 0) {
2436 start_division = floor ( ((*i)->position() - start) / division_size);
2437 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2438 if (end_division == divisions) {
2443 assert (divisions == 0 || end_division < divisions);
2445 /* find the lowest layer that this region can go on */
2446 size_t j = layers.size();
2448 /* try layer j - 1; it can go on if it overlaps no other region
2449 that is already on that layer
2452 bool overlap = false;
2453 for (int k = start_division; k <= end_division; ++k) {
2454 RegionList::iterator l = layers[j-1][k].begin ();
2455 while (l != layers[j-1][k].end()) {
2456 if ((*l)->overlap_equivalent (*i)) {
2469 /* overlap, so we must use layer j */
2476 if (j == layers.size()) {
2477 /* we need a new layer for this region */
2478 layers.push_back (vector<RegionList> (divisions));
2481 /* put a reference to this region in each of the divisions that it exists in */
2482 for (int k = start_division; k <= end_division; ++k) {
2483 layers[j][k].push_back (*i);
2486 if ((*i)->layer() != j) {
2490 (*i)->set_layer (j);
2494 notify_layering_changed ();
2499 Playlist::raise_region (boost::shared_ptr<Region> region)
2501 set_layer (region, region->layer() + 1.5);
2506 Playlist::lower_region (boost::shared_ptr<Region> region)
2508 set_layer (region, region->layer() - 1.5);
2513 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2515 set_layer (region, DBL_MAX);
2520 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2522 set_layer (region, -0.5);
2527 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2529 RegionList::iterator i;
2535 RegionLock rlock (const_cast<Playlist *> (this));
2537 for (i = regions.begin(); i != regions.end(); ++i) {
2539 if ((*i)->position() >= start) {
2545 if ((*i)->last_frame() > max_framepos - distance) {
2546 new_pos = max_framepos - (*i)->length();
2548 new_pos = (*i)->position() + distance;
2553 if ((*i)->position() > distance) {
2554 new_pos = (*i)->position() - distance;
2560 (*i)->set_position (new_pos);
2568 notify_contents_changed ();
2574 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2576 RegionLock rlock (const_cast<Playlist*> (this));
2578 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2579 if ((*r)->uses_source (src)) {
2587 boost::shared_ptr<Region>
2588 Playlist::find_region (const ID& id) const
2590 RegionLock rlock (const_cast<Playlist*> (this));
2592 /* searches all regions currently in use by the playlist */
2594 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2595 if ((*i)->id() == id) {
2600 return boost::shared_ptr<Region> ();
2604 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2606 RegionLock rlock (const_cast<Playlist*> (this));
2609 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2618 boost::shared_ptr<Region>
2619 Playlist::region_by_id (const ID& id) const
2621 /* searches all regions ever added to this playlist */
2623 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2624 if ((*i)->id() == id) {
2628 return boost::shared_ptr<Region> ();
2632 Playlist::dump () const
2634 boost::shared_ptr<Region> r;
2636 cerr << "Playlist \"" << _name << "\" " << endl
2637 << regions.size() << " regions "
2640 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2642 cerr << " " << r->name() << " ["
2643 << r->start() << "+" << r->length()
2653 Playlist::set_frozen (bool yn)
2659 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2663 if (region->locked()) {
2670 RegionLock rlock (const_cast<Playlist*> (this));
2675 RegionList::iterator next;
2677 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2678 if ((*i) == region) {
2682 if (next != regions.end()) {
2684 if ((*next)->locked()) {
2690 if ((*next)->position() != region->last_frame() + 1) {
2691 /* they didn't used to touch, so after shuffle,
2692 just have them swap positions.
2694 new_pos = (*next)->position();
2696 /* they used to touch, so after shuffle,
2697 make sure they still do. put the earlier
2698 region where the later one will end after
2701 new_pos = region->position() + (*next)->length();
2704 (*next)->set_position (region->position());
2705 region->set_position (new_pos);
2707 /* avoid a full sort */
2709 regions.erase (i); // removes the region from the list */
2711 regions.insert (next, region); // adds it back after next
2720 RegionList::iterator prev = regions.end();
2722 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2723 if ((*i) == region) {
2725 if (prev != regions.end()) {
2727 if ((*prev)->locked()) {
2732 if (region->position() != (*prev)->last_frame() + 1) {
2733 /* they didn't used to touch, so after shuffle,
2734 just have them swap positions.
2736 new_pos = region->position();
2738 /* they used to touch, so after shuffle,
2739 make sure they still do. put the earlier
2740 one where the later one will end after
2742 new_pos = (*prev)->position() + region->length();
2745 region->set_position ((*prev)->position());
2746 (*prev)->set_position (new_pos);
2748 /* avoid a full sort */
2750 regions.erase (i); // remove region
2751 regions.insert (prev, region); // insert region before prev
2767 check_dependents (region, false);
2769 notify_contents_changed();
2775 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2777 RegionLock rlock (const_cast<Playlist*> (this));
2779 if (regions.size() > 1) {
2787 Playlist::update_after_tempo_map_change ()
2789 RegionLock rlock (const_cast<Playlist*> (this));
2790 RegionList copy (regions.rlist());
2794 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2795 (*i)->update_after_tempo_map_change ();
2802 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2804 RegionLock rl (this, false);
2805 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2811 Playlist::has_region_at (framepos_t const p) const
2813 RegionLock (const_cast<Playlist *> (this));
2815 RegionList::const_iterator i = regions.begin ();
2816 while (i != regions.end() && !(*i)->covers (p)) {
2820 return (i != regions.end());
2823 /** Remove any region that uses a given source */
2825 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2827 RegionLock rl (this);
2829 RegionList::iterator i = regions.begin();
2830 while (i != regions.end()) {
2831 RegionList::iterator j = i;
2834 if ((*i)->uses_source (s)) {
2835 remove_region_internal (*i);
2842 /** Look from a session frame time and find the start time of the next region
2843 * which is on the top layer of this playlist.
2844 * @param t Time to look from.
2845 * @return Position of next top-layered region, or max_framepos if there isn't one.
2848 Playlist::find_next_top_layer_position (framepos_t t) const
2850 RegionLock rlock (const_cast<Playlist *> (this));
2852 layer_t const top = top_layer ();
2854 RegionList copy = regions.rlist ();
2855 copy.sort (RegionSortByPosition ());
2857 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2858 if ((*i)->position() >= t && (*i)->layer() == top) {
2859 return (*i)->position();
2863 return max_framepos;
2866 boost::shared_ptr<Region>
2867 Playlist::combine (const RegionList& r)
2870 uint32_t channels = 0;
2872 framepos_t earliest_position = max_framepos;
2873 vector<TwoRegions> old_and_new_regions;
2874 vector<boost::shared_ptr<Region> > originals;
2875 vector<boost::shared_ptr<Region> > copies;
2878 uint32_t max_level = 0;
2880 /* find the maximum depth of all the regions we're combining */
2882 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2883 max_level = max (max_level, (*i)->max_source_level());
2886 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2887 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2889 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2891 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2892 earliest_position = min (earliest_position, (*i)->position());
2895 /* enable this so that we do not try to create xfades etc. as we add
2899 pl->in_partition = true;
2901 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2903 /* copy the region */
2905 boost::shared_ptr<Region> original_region = (*i);
2906 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2908 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2909 originals.push_back (original_region);
2910 copies.push_back (copied_region);
2912 RegionFactory::add_compound_association (original_region, copied_region);
2914 /* make position relative to zero */
2916 pl->add_region (copied_region, original_region->position() - earliest_position);
2918 /* use the maximum number of channels for any region */
2920 channels = max (channels, original_region->n_channels());
2922 /* it will go above the layer of the highest existing region */
2924 layer = max (layer, original_region->layer());
2927 pl->in_partition = false;
2929 pre_combine (copies);
2931 /* add any dependent regions to the new playlist */
2933 copy_dependents (old_and_new_regions, pl.get());
2935 /* now create a new PlaylistSource for each channel in the new playlist */
2938 pair<framepos_t,framepos_t> extent = pl->get_extent();
2940 for (uint32_t chn = 0; chn < channels; ++chn) {
2941 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2945 /* now a new whole-file region using the list of sources */
2947 plist.add (Properties::start, 0);
2948 plist.add (Properties::length, extent.second);
2949 plist.add (Properties::name, parent_name);
2950 plist.add (Properties::whole_file, true);
2952 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2954 /* now the non-whole-file region that we will actually use in the
2959 plist.add (Properties::start, 0);
2960 plist.add (Properties::length, extent.second);
2961 plist.add (Properties::name, child_name);
2962 plist.add (Properties::layer, layer+1);
2964 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
2966 /* remove all the selected regions from the current playlist
2971 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2975 /* do type-specific stuff with the originals and the new compound
2979 post_combine (originals, compound_region);
2981 /* add the new region at the right location */
2983 add_region (compound_region, earliest_position);
2989 return compound_region;
2993 Playlist::uncombine (boost::shared_ptr<Region> target)
2995 boost::shared_ptr<PlaylistSource> pls;
2996 boost::shared_ptr<const Playlist> pl;
2997 vector<boost::shared_ptr<Region> > originals;
2998 vector<TwoRegions> old_and_new_regions;
3000 // (1) check that its really a compound region
3002 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3006 pl = pls->playlist();
3008 framepos_t adjusted_start = 0; // gcc isn't smart enough
3009 framepos_t adjusted_end = 0; // gcc isn't smart enough
3011 /* the leftmost (earliest) edge of the compound region
3012 starts at zero in its source, or larger if it
3013 has been trimmed or content-scrolled.
3015 the rightmost (latest) edge of the compound region
3016 relative to its source is the starting point plus
3017 the length of the region.
3020 // (2) get all the original regions
3022 const RegionList& rl (pl->region_list().rlist());
3023 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3024 frameoffset_t move_offset = 0;
3026 /* there are two possibilities here:
3027 1) the playlist that the playlist source was based on
3028 is us, so just add the originals (which belonged to
3029 us anyway) back in the right place.
3031 2) the playlist that the playlist source was based on
3032 is NOT us, so we need to make copies of each of
3033 the original regions that we find, and add them
3036 bool same_playlist = (pls->original() == id());
3038 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3040 boost::shared_ptr<Region> current (*i);
3042 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3044 if (ca == cassocs.end()) {
3048 boost::shared_ptr<Region> original (ca->second);
3049 bool modified_region;
3051 if (i == rl.begin()) {
3052 move_offset = (target->position() - original->position()) - target->start();
3053 adjusted_start = original->position() + target->start();
3054 adjusted_end = adjusted_start + target->length();
3057 if (!same_playlist) {
3058 framepos_t pos = original->position();
3059 /* make a copy, but don't announce it */
3060 original = RegionFactory::create (original, false);
3061 /* the pure copy constructor resets position() to zero,
3064 original->set_position (pos);
3067 /* check to see how the original region (in the
3068 * playlist before compounding occured) overlaps
3069 * with the new state of the compound region.
3072 original->clear_changes ();
3073 modified_region = false;
3075 switch (original->coverage (adjusted_start, adjusted_end)) {
3077 /* original region does not cover any part
3078 of the current state of the compound region
3082 case OverlapInternal:
3083 /* overlap is just a small piece inside the
3084 * original so trim both ends
3086 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3087 modified_region = true;
3090 case OverlapExternal:
3091 /* overlap fully covers original, so leave it
3097 /* overlap starts within but covers end,
3098 so trim the front of the region
3100 original->trim_front (adjusted_start);
3101 modified_region = true;
3105 /* overlap covers start but ends within, so
3106 * trim the end of the region.
3108 original->trim_end (adjusted_end);
3109 modified_region = true;
3114 /* fix the position to match any movement of the compound region.
3116 original->set_position (original->position() + move_offset);
3117 modified_region = true;
3120 if (modified_region) {
3121 _session.add_command (new StatefulDiffCommand (original));
3124 /* and add to the list of regions waiting to be
3128 originals.push_back (original);
3129 old_and_new_regions.push_back (TwoRegions (*i, original));
3132 pre_uncombine (originals, target);
3134 in_partition = true;
3137 // (3) remove the compound region
3139 remove_region (target);
3141 // (4) add the constituent regions
3143 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3144 add_region ((*i), (*i)->position());
3147 /* now move dependent regions back from the compound to this playlist */
3149 pl->copy_dependents (old_and_new_regions, this);
3151 in_partition = false;
3156 Playlist::max_source_level () const
3158 RegionLock rlock (const_cast<Playlist *> (this));
3161 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3162 lvl = max (lvl, (*i)->max_source_level());
3170 Playlist::count_joined_regions () const
3172 RegionLock rlock (const_cast<Playlist *> (this));
3175 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3176 if ((*i)->max_source_level() > 0) {
3185 Playlist::set_orig_track_id (const PBD::ID& id)
3187 _orig_track_id = id;
3191 Playlist::highest_layering_index () const
3193 RegionLock rlock (const_cast<Playlist *> (this));
3196 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3197 h = max (h, (*i)->layering_index ());