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;
190 layer_op_counter = other->layer_op_counter;
191 freeze_length = other->freeze_length;
194 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
195 : SessionObject(other->_session, str)
197 , _type(other->_type)
198 , _orig_track_id (other->_orig_track_id)
200 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
202 framepos_t end = start + cnt - 1;
208 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
210 boost::shared_ptr<Region> region;
211 boost::shared_ptr<Region> new_region;
212 frameoffset_t offset = 0;
213 framepos_t position = 0;
220 overlap = region->coverage (start, end);
226 case OverlapInternal:
227 offset = start - region->position();
234 position = region->position() - start;
235 len = end - region->position();
239 offset = start - region->position();
241 len = region->length() - offset;
244 case OverlapExternal:
246 position = region->position() - start;
247 len = region->length();
251 RegionFactory::region_name (new_name, region->name(), false);
255 plist.add (Properties::start, region->start() + offset);
256 plist.add (Properties::length, len);
257 plist.add (Properties::name, new_name);
258 plist.add (Properties::layer, region->layer());
260 new_region = RegionFactory::RegionFactory::create (region, plist);
262 add_region_internal (new_region, position);
266 first_set_state = false;
273 InUse (true); /* EMIT SIGNAL */
284 InUse (false); /* EMIT SIGNAL */
289 Playlist::copy_regions (RegionList& newlist) const
291 RegionLock rlock (const_cast<Playlist *> (this));
293 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
294 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
299 Playlist::init (bool hide)
301 add_property (regions);
302 _xml_node_name = X_("Playlist");
304 g_atomic_int_set (&block_notifications, 0);
305 g_atomic_int_set (&ignore_state_changes, 0);
306 pending_contents_change = false;
307 pending_layering = false;
308 first_set_state = true;
316 _edit_mode = Config->get_edit_mode();
318 in_partition = false;
321 layer_op_counter = 0;
323 _explicit_relayering = false;
326 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
327 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
329 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
332 Playlist::~Playlist ()
334 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
337 RegionLock rl (this);
339 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
340 (*i)->set_playlist (boost::shared_ptr<Playlist>());
344 /* GoingAway must be emitted by derived classes */
348 Playlist::_set_sort_id ()
351 Playlists are given names like <track name>.<id>
352 or <track name>.<edit group name>.<id> where id
353 is an integer. We extract the id and sort by that.
356 size_t dot_position = _name.val().find_last_of(".");
358 if (dot_position == string::npos) {
361 string t = _name.val().substr(dot_position + 1);
364 _sort_id = boost::lexical_cast<int>(t);
367 catch (boost::bad_lexical_cast e) {
374 Playlist::set_name (const string& str)
376 /* in a typical situation, a playlist is being used
377 by one diskstream and also is referenced by the
378 Session. if there are more references than that,
379 then don't change the name.
386 bool ret = SessionObject::set_name(str);
393 /***********************************************************************
394 CHANGE NOTIFICATION HANDLING
396 Notifications must be delayed till the region_lock is released. This
397 is necessary because handlers for the signals may need to acquire
398 the lock (e.g. to read from the playlist).
399 ***********************************************************************/
402 Playlist::begin_undo ()
409 Playlist::end_undo ()
418 delay_notifications ();
419 g_atomic_int_inc (&ignore_state_changes);
422 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
424 Playlist::thaw (bool from_undo)
426 g_atomic_int_dec_and_test (&ignore_state_changes);
427 release_notifications (from_undo);
432 Playlist::delay_notifications ()
434 g_atomic_int_inc (&block_notifications);
435 freeze_length = _get_extent().second;
438 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
440 Playlist::release_notifications (bool from_undo)
442 if (g_atomic_int_dec_and_test (&block_notifications)) {
443 flush_notifications (from_undo);
448 Playlist::notify_contents_changed ()
450 if (holding_state ()) {
451 pending_contents_change = true;
453 pending_contents_change = false;
454 ContentsChanged(); /* EMIT SIGNAL */
459 Playlist::notify_layering_changed ()
461 if (holding_state ()) {
462 pending_layering = true;
464 pending_layering = false;
465 LayeringChanged(); /* EMIT SIGNAL */
470 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
472 if (holding_state ()) {
473 pending_removes.insert (r);
474 pending_contents_change = true;
476 /* this might not be true, but we have to act
477 as though it could be.
479 pending_contents_change = false;
480 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
481 ContentsChanged (); /* EMIT SIGNAL */
486 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
488 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
490 if (holding_state ()) {
492 pending_range_moves.push_back (move);
496 list< Evoral::RangeMove<framepos_t> > m;
498 RangesMoved (m, false);
504 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
506 if (r->position() >= r->last_position()) {
507 /* trimmed shorter */
511 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
513 if (holding_state ()) {
515 pending_region_extensions.push_back (extra);
519 list<Evoral::Range<framepos_t> > r;
527 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
529 if (r->length() < r->last_length()) {
530 /* trimmed shorter */
533 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
535 if (holding_state ()) {
537 pending_region_extensions.push_back (extra);
541 list<Evoral::Range<framepos_t> > r;
549 Playlist::notify_region_added (boost::shared_ptr<Region> r)
551 /* the length change might not be true, but we have to act
552 as though it could be.
555 if (holding_state()) {
556 pending_adds.insert (r);
557 pending_contents_change = true;
560 pending_contents_change = false;
561 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
562 ContentsChanged (); /* EMIT SIGNAL */
566 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
568 Playlist::flush_notifications (bool from_undo)
570 set<boost::shared_ptr<Region> > dependent_checks_needed;
571 set<boost::shared_ptr<Region> >::iterator s;
572 uint32_t regions_changed = false;
580 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
581 regions_changed = true;
584 /* we have no idea what order the regions ended up in pending
585 bounds (it could be based on selection order, for example).
586 so, to preserve layering in the "most recently moved is higher"
587 model, sort them by existing layer, then timestamp them.
590 // RegionSortByLayer cmp;
591 // pending_bounds.sort (cmp);
593 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
594 if (_session.config.get_layer_model() == MoveAddHigher) {
595 timestamp_layer_op (*r);
597 dependent_checks_needed.insert (*r);
600 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
601 remove_dependents (*s);
602 // cerr << _name << " sends RegionRemoved\n";
603 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
606 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
607 // cerr << _name << " sends RegionAdded\n";
608 /* don't emit RegionAdded signal until relayering is done,
609 so that the region is fully setup by the time
610 anyone hear's that its been added
612 dependent_checks_needed.insert (*s);
615 if (regions_changed || pending_contents_change) {
619 pending_contents_change = false;
620 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
621 ContentsChanged (); /* EMIT SIGNAL */
622 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
625 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
626 (*s)->clear_changes ();
627 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
630 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
631 check_dependents (*s, false);
634 if (!pending_range_moves.empty ()) {
635 RangesMoved (pending_range_moves, from_undo);
638 if (!pending_region_extensions.empty ()) {
639 RegionsExtended (pending_region_extensions);
648 Playlist::clear_pending ()
650 pending_adds.clear ();
651 pending_removes.clear ();
652 pending_bounds.clear ();
653 pending_range_moves.clear ();
654 pending_region_extensions.clear ();
655 pending_contents_change = false;
658 /*************************************************************
660 *************************************************************/
663 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
665 RegionLock rlock (this);
666 times = fabs (times);
668 int itimes = (int) floor (times);
670 framepos_t pos = position;
672 if (times == 1 && auto_partition){
673 partition(pos - 1, (pos + region->length()), true);
677 add_region_internal (region, pos);
678 pos += region->length();
683 /* note that itimes can be zero if we being asked to just
684 insert a single fraction of the region.
687 for (int i = 0; i < itimes; ++i) {
688 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
689 add_region_internal (copy, pos);
690 pos += region->length();
693 framecnt_t length = 0;
695 if (floor (times) != times) {
696 length = (framecnt_t) floor (region->length() * (times - floor (times)));
698 RegionFactory::region_name (name, region->name(), false);
703 plist.add (Properties::start, region->start());
704 plist.add (Properties::length, length);
705 plist.add (Properties::name, name);
706 plist.add (Properties::layer, region->layer());
708 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
709 add_region_internal (sub, pos);
713 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
717 Playlist::set_region_ownership ()
719 RegionLock rl (this);
720 RegionList::iterator i;
721 boost::weak_ptr<Playlist> pl (shared_from_this());
723 for (i = regions.begin(); i != regions.end(); ++i) {
724 (*i)->set_playlist (pl);
729 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
731 if (region->data_type() != _type) {
735 RegionSortByPosition cmp;
737 if (!first_set_state) {
738 boost::shared_ptr<Playlist> foo (shared_from_this());
739 region->set_playlist (boost::weak_ptr<Playlist>(foo));
742 region->set_position (position);
744 timestamp_layer_op (region);
746 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
747 all_regions.insert (region);
749 possibly_splice_unlocked (position, region->length(), region);
751 if (!holding_state ()) {
752 /* layers get assigned from XML state, and are not reset during undo/redo */
756 /* we need to notify the existence of new region before checking dependents. Ick. */
758 notify_region_added (region);
760 if (!holding_state ()) {
761 check_dependents (region, false);
764 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
770 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
772 RegionLock rlock (this);
774 bool old_sp = _splicing;
777 remove_region_internal (old);
778 add_region_internal (newr, pos);
782 possibly_splice_unlocked (pos, old->length() - newr->length());
786 Playlist::remove_region (boost::shared_ptr<Region> region)
788 RegionLock rlock (this);
789 remove_region_internal (region);
793 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
795 RegionList::iterator i;
799 region->set_playlist (boost::weak_ptr<Playlist>());
802 /* XXX should probably freeze here .... */
804 for (i = regions.begin(); i != regions.end(); ++i) {
807 framepos_t pos = (*i)->position();
808 framecnt_t distance = (*i)->length();
812 possibly_splice_unlocked (pos, -distance);
814 if (!holding_state ()) {
816 remove_dependents (region);
819 notify_region_removed (region);
828 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
830 if (Config->get_use_overlap_equivalency()) {
831 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
832 if ((*i)->overlap_equivalent (other)) {
833 results.push_back ((*i));
837 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
838 if ((*i)->equivalent (other)) {
839 results.push_back ((*i));
846 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
848 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
850 if ((*i) && (*i)->region_list_equivalent (other)) {
851 results.push_back (*i);
857 Playlist::partition (framepos_t start, framepos_t end, bool cut)
861 partition_internal (start, end, cut, thawlist);
863 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
864 (*i)->resume_property_changes ();
869 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
871 RegionList new_regions;
874 RegionLock rlock (this);
876 boost::shared_ptr<Region> region;
877 boost::shared_ptr<Region> current;
879 RegionList::iterator tmp;
881 framepos_t pos1, pos2, pos3, pos4;
885 /* need to work from a copy, because otherwise the regions we add during the process
886 get operated on as well.
889 RegionList copy = regions.rlist();
891 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
898 if (current->first_frame() >= start && current->last_frame() < end) {
901 remove_region_internal (current);
907 /* coverage will return OverlapStart if the start coincides
908 with the end point. we do not partition such a region,
909 so catch this special case.
912 if (current->first_frame() >= end) {
916 if ((overlap = current->coverage (start, end)) == OverlapNone) {
920 pos1 = current->position();
923 pos4 = current->last_frame();
925 if (overlap == OverlapInternal) {
926 /* split: we need 3 new regions, the front, middle and end.
927 cut: we need 2 regions, the front and end.
932 ---------------*************************------------
935 ---------------*****++++++++++++++++====------------
937 ---------------*****----------------====------------
942 /* "middle" ++++++ */
944 RegionFactory::region_name (new_name, current->name(), false);
948 plist.add (Properties::start, current->start() + (pos2 - pos1));
949 plist.add (Properties::length, pos3 - pos2);
950 plist.add (Properties::name, new_name);
951 plist.add (Properties::layer, regions.size());
952 plist.add (Properties::automatic, true);
953 plist.add (Properties::left_of_split, true);
954 plist.add (Properties::right_of_split, true);
956 region = RegionFactory::create (current, plist);
957 add_region_internal (region, start);
958 new_regions.push_back (region);
963 RegionFactory::region_name (new_name, current->name(), false);
967 plist.add (Properties::start, current->start() + (pos3 - pos1));
968 plist.add (Properties::length, pos4 - pos3);
969 plist.add (Properties::name, new_name);
970 plist.add (Properties::layer, regions.size());
971 plist.add (Properties::automatic, true);
972 plist.add (Properties::right_of_split, true);
974 region = RegionFactory::create (current, plist);
976 add_region_internal (region, end);
977 new_regions.push_back (region);
981 current->suspend_property_changes ();
982 thawlist.push_back (current);
983 current->cut_end (pos2 - 1);
985 } else if (overlap == OverlapEnd) {
989 ---------------*************************------------
992 ---------------**************+++++++++++------------
994 ---------------**************-----------------------
1001 RegionFactory::region_name (new_name, current->name(), false);
1005 plist.add (Properties::start, current->start() + (pos2 - pos1));
1006 plist.add (Properties::length, pos4 - pos2);
1007 plist.add (Properties::name, new_name);
1008 plist.add (Properties::layer, regions.size());
1009 plist.add (Properties::automatic, true);
1010 plist.add (Properties::left_of_split, true);
1012 region = RegionFactory::create (current, plist);
1014 add_region_internal (region, start);
1015 new_regions.push_back (region);
1020 current->suspend_property_changes ();
1021 thawlist.push_back (current);
1022 current->cut_end (pos2 - 1);
1024 } else if (overlap == OverlapStart) {
1026 /* split: we need 2 regions: the front and the end.
1027 cut: just trim current to skip the cut area
1032 ---------------*************************------------
1036 ---------------****+++++++++++++++++++++------------
1038 -------------------*********************------------
1044 RegionFactory::region_name (new_name, current->name(), false);
1048 plist.add (Properties::start, current->start());
1049 plist.add (Properties::length, pos3 - pos1);
1050 plist.add (Properties::name, new_name);
1051 plist.add (Properties::layer, regions.size());
1052 plist.add (Properties::automatic, true);
1053 plist.add (Properties::right_of_split, true);
1055 region = RegionFactory::create (current, plist);
1057 add_region_internal (region, pos1);
1058 new_regions.push_back (region);
1063 current->suspend_property_changes ();
1064 thawlist.push_back (current);
1065 current->trim_front (pos3);
1066 } else if (overlap == OverlapExternal) {
1068 /* split: no split required.
1069 cut: remove the region.
1074 ---------------*************************------------
1078 ---------------*************************------------
1080 ----------------------------------------------------
1085 remove_region_internal (current);
1088 new_regions.push_back (current);
1092 in_partition = false;
1095 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1096 check_dependents (*i, false);
1100 boost::shared_ptr<Playlist>
1101 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1103 boost::shared_ptr<Playlist> ret;
1104 boost::shared_ptr<Playlist> pl;
1107 if (ranges.empty()) {
1108 return boost::shared_ptr<Playlist>();
1111 start = ranges.front().start;
1113 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1115 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1117 if (i == ranges.begin()) {
1121 /* paste the next section into the nascent playlist,
1122 offset to reflect the start of the first range we
1126 ret->paste (pl, (*i).start - start, 1.0f);
1133 boost::shared_ptr<Playlist>
1134 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1136 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1137 return cut_copy (pmf, ranges, result_is_hidden);
1140 boost::shared_ptr<Playlist>
1141 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1143 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1144 return cut_copy (pmf, ranges, result_is_hidden);
1147 boost::shared_ptr<Playlist>
1148 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1150 boost::shared_ptr<Playlist> the_copy;
1151 RegionList thawlist;
1154 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1155 string new_name = _name;
1159 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1160 return boost::shared_ptr<Playlist>();
1163 partition_internal (start, start+cnt-1, true, thawlist);
1165 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1166 (*i)->resume_property_changes();
1172 boost::shared_ptr<Playlist>
1173 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1177 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1178 string new_name = _name;
1182 cnt = min (_get_extent().second - start, cnt);
1183 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1187 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1189 times = fabs (times);
1192 RegionLock rl1 (this);
1193 RegionLock rl2 (other.get());
1195 int itimes = (int) floor (times);
1196 framepos_t pos = position;
1197 framecnt_t const shift = other->_get_extent().second;
1198 layer_t top_layer = regions.size();
1201 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1202 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1204 /* put these new regions on top of all existing ones, but preserve
1205 the ordering they had in the original playlist.
1208 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1209 add_region_internal (copy_of_region, (*i)->position() + pos);
1220 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1222 times = fabs (times);
1224 RegionLock rl (this);
1225 int itimes = (int) floor (times);
1226 framepos_t pos = position + 1;
1229 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1230 add_region_internal (copy, pos);
1231 pos += region->length();
1234 if (floor (times) != times) {
1235 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1237 RegionFactory::region_name (name, region->name(), false);
1242 plist.add (Properties::start, region->start());
1243 plist.add (Properties::length, length);
1244 plist.add (Properties::name, name);
1246 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1247 add_region_internal (sub, pos);
1253 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1255 RegionLock rlock (this);
1256 RegionList copy (regions.rlist());
1259 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1261 if ((*r)->last_frame() < at) {
1266 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1267 /* intersected region */
1268 if (!move_intersected) {
1273 /* do not move regions glued to music time - that
1274 has to be done separately.
1277 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1278 fixup.push_back (*r);
1282 (*r)->set_position ((*r)->position() + distance);
1285 /* XXX: may not be necessary; Region::post_set should do this, I think */
1286 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1287 (*r)->recompute_position_from_lock_style ();
1292 Playlist::split (framepos_t at)
1294 RegionLock rlock (this);
1295 RegionList copy (regions.rlist());
1297 /* use a copy since this operation can modify the region list
1300 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1301 _split_region (*r, at);
1306 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1308 RegionLock rl (this);
1309 _split_region (region, playlist_position);
1313 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1315 if (!region->covers (playlist_position)) {
1319 if (region->position() == playlist_position ||
1320 region->last_frame() == playlist_position) {
1324 boost::shared_ptr<Region> left;
1325 boost::shared_ptr<Region> right;
1326 frameoffset_t before;
1327 frameoffset_t after;
1331 /* split doesn't change anything about length, so don't try to splice */
1333 bool old_sp = _splicing;
1336 before = playlist_position - region->position();
1337 after = region->length() - before;
1339 RegionFactory::region_name (before_name, region->name(), false);
1344 plist.add (Properties::position, region->position ());
1345 plist.add (Properties::length, before);
1346 plist.add (Properties::name, before_name);
1347 plist.add (Properties::left_of_split, true);
1349 /* note: we must use the version of ::create with an offset here,
1350 since it supplies that offset to the Region constructor, which
1351 is necessary to get audio region gain envelopes right.
1353 left = RegionFactory::create (region, 0, plist);
1356 RegionFactory::region_name (after_name, region->name(), false);
1361 plist.add (Properties::position, region->position() + before);
1362 plist.add (Properties::length, after);
1363 plist.add (Properties::name, after_name);
1364 plist.add (Properties::right_of_split, true);
1366 /* same note as above */
1367 right = RegionFactory::create (region, before, plist);
1370 add_region_internal (left, region->position());
1371 add_region_internal (right, region->position() + before);
1373 uint64_t orig_layer_op = region->last_layer_op();
1374 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1375 if ((*i)->last_layer_op() > orig_layer_op) {
1376 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1380 left->set_last_layer_op ( orig_layer_op );
1381 right->set_last_layer_op ( orig_layer_op + 1);
1385 finalize_split_region (region, left, right);
1387 remove_region_internal (region);
1393 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1395 if (_splicing || in_set_state) {
1396 /* don't respond to splicing moves or state setting */
1400 if (_edit_mode == Splice) {
1401 splice_locked (at, distance, exclude);
1406 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1408 if (_splicing || in_set_state) {
1409 /* don't respond to splicing moves or state setting */
1413 if (_edit_mode == Splice) {
1414 splice_unlocked (at, distance, exclude);
1419 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1422 RegionLock rl (this);
1423 core_splice (at, distance, exclude);
1428 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1430 core_splice (at, distance, exclude);
1434 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1438 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1440 if (exclude && (*i) == exclude) {
1444 if ((*i)->position() >= at) {
1445 framepos_t new_pos = (*i)->position() + distance;
1448 } else if (new_pos >= max_framepos - (*i)->length()) {
1449 new_pos = max_framepos - (*i)->length();
1452 (*i)->set_position (new_pos);
1458 notify_contents_changed ();
1462 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1464 if (in_set_state || _splicing || _nudging || _shuffling) {
1468 if (what_changed.contains (Properties::position)) {
1470 /* remove it from the list then add it back in
1471 the right place again.
1474 RegionSortByPosition cmp;
1476 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1478 if (i == regions.end()) {
1479 /* the region bounds are being modified but its not currently
1480 in the region list. we will use its bounds correctly when/if
1487 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1490 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1492 frameoffset_t delta = 0;
1494 if (what_changed.contains (Properties::position)) {
1495 delta = region->position() - region->last_position();
1498 if (what_changed.contains (Properties::length)) {
1499 delta += region->length() - region->last_length();
1503 possibly_splice (region->last_position() + region->last_length(), delta, region);
1506 if (holding_state ()) {
1507 pending_bounds.push_back (region);
1509 if (_session.config.get_layer_model() == MoveAddHigher) {
1510 /* it moved or changed length, so change the timestamp */
1511 timestamp_layer_op (region);
1514 notify_contents_changed ();
1516 check_dependents (region, false);
1522 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1524 boost::shared_ptr<Region> region (weak_region.lock());
1530 /* this makes a virtual call to the right kind of playlist ... */
1532 region_changed (what_changed, region);
1536 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1538 PropertyChange our_interests;
1539 PropertyChange bounds;
1540 PropertyChange pos_and_length;
1543 if (in_set_state || in_flush) {
1547 our_interests.add (Properties::muted);
1548 our_interests.add (Properties::layer);
1549 our_interests.add (Properties::opaque);
1551 bounds.add (Properties::start);
1552 bounds.add (Properties::position);
1553 bounds.add (Properties::length);
1555 pos_and_length.add (Properties::position);
1556 pos_and_length.add (Properties::length);
1558 if (what_changed.contains (bounds)) {
1559 region_bounds_changed (what_changed, region);
1560 save = !(_splicing || _nudging);
1563 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1564 check_dependents (region, false);
1567 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1568 notify_region_moved (region);
1569 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1570 notify_region_end_trimmed (region);
1571 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1572 notify_region_start_trimmed (region);
1575 /* don't notify about layer changes, since we are the only object that can initiate
1576 them, and we notify in ::relayer()
1579 if (what_changed.contains (our_interests)) {
1587 Playlist::drop_regions ()
1589 RegionLock rl (this);
1591 all_regions.clear ();
1595 Playlist::sync_all_regions_with_regions ()
1597 RegionLock rl (this);
1599 all_regions.clear ();
1601 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1602 all_regions.insert (*i);
1607 Playlist::clear (bool with_signals)
1610 RegionLock rl (this);
1612 region_state_changed_connections.drop_connections ();
1614 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1615 pending_removes.insert (*i);
1620 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1621 remove_dependents (*s);
1627 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1628 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1631 pending_removes.clear ();
1632 pending_contents_change = false;
1638 /***********************************************************************
1640 **********************************************************************/
1642 Playlist::RegionList *
1643 Playlist::regions_at (framepos_t frame)
1646 RegionLock rlock (this);
1647 return find_regions_at (frame);
1651 Playlist::count_regions_at (framepos_t frame) const
1653 RegionLock rlock (const_cast<Playlist*>(this));
1656 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1657 if ((*i)->covers (frame)) {
1665 boost::shared_ptr<Region>
1666 Playlist::top_region_at (framepos_t frame)
1669 RegionLock rlock (this);
1670 RegionList *rlist = find_regions_at (frame);
1671 boost::shared_ptr<Region> region;
1673 if (rlist->size()) {
1674 RegionSortByLayer cmp;
1676 region = rlist->back();
1683 boost::shared_ptr<Region>
1684 Playlist::top_unmuted_region_at (framepos_t frame)
1687 RegionLock rlock (this);
1688 RegionList *rlist = find_regions_at (frame);
1690 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1692 RegionList::iterator tmp = i;
1695 if ((*i)->muted()) {
1702 boost::shared_ptr<Region> region;
1704 if (rlist->size()) {
1705 RegionSortByLayer cmp;
1707 region = rlist->back();
1714 Playlist::RegionList*
1715 Playlist::regions_to_read (framepos_t start, framepos_t end)
1717 /* Caller must hold lock */
1719 RegionList covering;
1720 set<framepos_t> to_check;
1721 set<boost::shared_ptr<Region> > unique;
1723 to_check.insert (start);
1724 to_check.insert (end);
1726 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1728 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1730 /* find all/any regions that span start+end */
1732 switch ((*i)->coverage (start, end)) {
1736 case OverlapInternal:
1737 covering.push_back (*i);
1738 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1742 to_check.insert ((*i)->position());
1743 if ((*i)->position() != 0) {
1744 to_check.insert ((*i)->position()-1);
1746 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1747 covering.push_back (*i);
1751 to_check.insert ((*i)->last_frame());
1752 to_check.insert ((*i)->last_frame()+1);
1753 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1754 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1755 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1756 covering.push_back (*i);
1759 case OverlapExternal:
1760 covering.push_back (*i);
1761 to_check.insert ((*i)->position());
1762 if ((*i)->position() != 0) {
1763 to_check.insert ((*i)->position()-1);
1765 to_check.insert ((*i)->last_frame());
1766 to_check.insert ((*i)->last_frame()+1);
1767 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1768 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1769 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1773 /* don't go too far */
1775 if ((*i)->position() > end) {
1780 RegionList* rlist = new RegionList;
1782 /* find all the regions that cover each position .... */
1784 if (covering.size() == 1) {
1786 rlist->push_back (covering.front());
1787 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1792 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1796 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1798 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1800 if ((*x)->covers (*t)) {
1801 here.push_back (*x);
1802 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1806 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1813 RegionSortByLayer cmp;
1816 /* ... and get the top/transparent regions at "here" */
1818 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1822 if ((*c)->opaque()) {
1824 /* the other regions at this position are hidden by this one */
1825 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1832 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1833 rlist->push_back (*s);
1836 if (rlist->size() > 1) {
1837 /* now sort by time order */
1839 RegionSortByPosition cmp;
1844 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1849 Playlist::RegionList *
1850 Playlist::find_regions_at (framepos_t frame)
1852 /* Caller must hold lock */
1854 RegionList *rlist = new RegionList;
1856 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1857 if ((*i)->covers (frame)) {
1858 rlist->push_back (*i);
1865 Playlist::RegionList *
1866 Playlist::regions_touched (framepos_t start, framepos_t end)
1868 RegionLock rlock (this);
1869 RegionList *rlist = new RegionList;
1871 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1872 if ((*i)->coverage (start, end) != OverlapNone) {
1873 rlist->push_back (*i);
1881 Playlist::find_next_transient (framepos_t from, int dir)
1883 RegionLock rlock (this);
1884 AnalysisFeatureList points;
1885 AnalysisFeatureList these_points;
1887 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1889 if ((*i)->last_frame() < from) {
1893 if ((*i)->first_frame() > from) {
1898 (*i)->get_transients (these_points);
1900 /* add first frame, just, err, because */
1902 these_points.push_back ((*i)->first_frame());
1904 points.insert (points.end(), these_points.begin(), these_points.end());
1905 these_points.clear ();
1908 if (points.empty()) {
1912 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1913 bool reached = false;
1916 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1921 if (reached && (*x) > from) {
1926 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1931 if (reached && (*x) < from) {
1940 boost::shared_ptr<Region>
1941 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1943 RegionLock rlock (this);
1944 boost::shared_ptr<Region> ret;
1945 framepos_t closest = max_framepos;
1947 bool end_iter = false;
1949 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1953 frameoffset_t distance;
1954 boost::shared_ptr<Region> r = (*i);
1959 pos = r->first_frame ();
1962 pos = r->last_frame ();
1965 pos = r->sync_position ();
1970 case 1: /* forwards */
1973 if ((distance = pos - frame) < closest) {
1982 default: /* backwards */
1985 if ((distance = frame - pos) < closest) {
2002 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2004 RegionLock rlock (this);
2006 framepos_t closest = max_framepos;
2007 framepos_t ret = -1;
2011 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2013 boost::shared_ptr<Region> r = (*i);
2014 frameoffset_t distance;
2016 if (r->first_frame() > frame) {
2018 distance = r->first_frame() - frame;
2020 if (distance < closest) {
2021 ret = r->first_frame();
2026 if (r->last_frame () > frame) {
2028 distance = r->last_frame () - frame;
2030 if (distance < closest) {
2031 ret = r->last_frame ();
2039 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2041 boost::shared_ptr<Region> r = (*i);
2042 frameoffset_t distance;
2044 if (r->last_frame() < frame) {
2046 distance = frame - r->last_frame();
2048 if (distance < closest) {
2049 ret = r->last_frame();
2054 if (r->first_frame() < frame) {
2056 distance = frame - r->first_frame();
2058 if (distance < closest) {
2059 ret = r->first_frame();
2070 /***********************************************************************/
2076 Playlist::mark_session_dirty ()
2078 if (!in_set_state && !holding_state ()) {
2079 _session.set_dirty();
2084 Playlist::rdiff (vector<Command*>& cmds) const
2086 RegionLock rlock (const_cast<Playlist *> (this));
2087 Stateful::rdiff (cmds);
2091 Playlist::clear_owned_changes ()
2093 RegionLock rlock (this);
2094 Stateful::clear_owned_changes ();
2098 Playlist::update (const RegionListProperty::ChangeRecord& change)
2100 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2101 name(), change.added.size(), change.removed.size()));
2104 /* add the added regions */
2105 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2106 add_region ((*i), (*i)->position());
2108 /* remove the removed regions */
2109 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2117 Playlist::set_state (const XMLNode& node, int version)
2121 XMLNodeConstIterator niter;
2122 XMLPropertyList plist;
2123 XMLPropertyConstIterator piter;
2125 boost::shared_ptr<Region> region;
2127 bool seen_region_nodes = false;
2132 if (node.name() != "Playlist") {
2139 plist = node.properties();
2143 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2147 if (prop->name() == X_("name")) {
2148 _name = prop->value();
2150 } else if (prop->name() == X_("orig-diskstream-id")) {
2151 /* XXX legacy session: fix up later */
2152 _orig_track_id = prop->value ();
2153 } else if (prop->name() == X_("orig-track-id")) {
2154 _orig_track_id = prop->value ();
2155 } else if (prop->name() == X_("frozen")) {
2156 _frozen = string_is_affirmative (prop->value());
2157 } else if (prop->name() == X_("combine-ops")) {
2158 _combine_ops = atoi (prop->value());
2164 nlist = node.children();
2166 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2170 if (child->name() == "Region") {
2172 seen_region_nodes = true;
2174 if ((prop = child->property ("id")) == 0) {
2175 error << _("region state node has no ID, ignored") << endmsg;
2179 ID id = prop->value ();
2181 if ((region = region_by_id (id))) {
2183 region->suspend_property_changes ();
2185 if (region->set_state (*child, version)) {
2186 region->resume_property_changes ();
2190 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2191 region->suspend_property_changes ();
2193 error << _("Playlist: cannot create region from XML") << endmsg;
2197 add_region (region, region->position(), 1.0);
2199 // So that layer_op ordering doesn't get screwed up
2200 region->set_last_layer_op( region->layer());
2201 region->resume_property_changes ();
2206 if (seen_region_nodes && regions.empty()) {
2210 /* update dependents, which was not done during add_region_internal
2211 due to in_set_state being true
2214 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2215 check_dependents (*r, false);
2220 notify_contents_changed ();
2223 first_set_state = false;
2228 Playlist::get_state()
2230 return state (true);
2234 Playlist::get_template()
2236 return state (false);
2239 /** @param full_state true to include regions in the returned state, otherwise false.
2242 Playlist::state (bool full_state)
2244 XMLNode *node = new XMLNode (X_("Playlist"));
2247 node->add_property (X_("id"), id().to_s());
2248 node->add_property (X_("name"), _name);
2249 node->add_property (X_("type"), _type.to_string());
2251 _orig_track_id.print (buf, sizeof (buf));
2252 node->add_property (X_("orig-track-id"), buf);
2253 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2256 RegionLock rlock (this, false);
2258 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2259 node->add_property ("combine-ops", buf);
2261 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2262 node->add_child_nocopy ((*i)->get_state());
2267 node->add_child_copy (*_extra_xml);
2274 Playlist::empty() const
2276 RegionLock rlock (const_cast<Playlist *>(this), false);
2277 return regions.empty();
2281 Playlist::n_regions() const
2283 RegionLock rlock (const_cast<Playlist *>(this), false);
2284 return regions.size();
2287 pair<framepos_t, framepos_t>
2288 Playlist::get_extent () const
2290 RegionLock rlock (const_cast<Playlist *>(this), false);
2291 return _get_extent ();
2294 pair<framepos_t, framepos_t>
2295 Playlist::_get_extent () const
2297 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2299 if (regions.empty()) {
2304 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2305 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2306 if (e.first < ext.first) {
2307 ext.first = e.first;
2309 if (e.second > ext.second) {
2310 ext.second = e.second;
2318 Playlist::bump_name (string name, Session &session)
2320 string newname = name;
2323 newname = bump_name_once (newname, '.');
2324 } while (session.playlists->by_name (newname)!=NULL);
2331 Playlist::top_layer() const
2333 RegionLock rlock (const_cast<Playlist *> (this));
2336 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2337 top = max (top, (*i)->layer());
2343 Playlist::set_edit_mode (EditMode mode)
2348 /********************
2350 ********************/
2353 Playlist::relayer ()
2355 /* never compute layers when changing state for undo/redo or setting from XML */
2357 if (in_update || in_set_state) {
2361 bool changed = false;
2363 /* Build up a new list of regions on each layer, stored in a set of lists
2364 each of which represent some period of time on some layer. The idea
2365 is to avoid having to search the entire region list to establish whether
2366 each region overlaps another */
2368 /* how many pieces to divide this playlist's time up into */
2369 int const divisions = 512;
2371 /* find the start and end positions of the regions on this playlist */
2372 framepos_t start = INT64_MAX;
2374 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2375 start = min (start, (*i)->position());
2376 end = max (end, (*i)->position() + (*i)->length());
2379 /* hence the size of each time division */
2380 double const division_size = (end - start) / double (divisions);
2382 vector<vector<RegionList> > layers;
2383 layers.push_back (vector<RegionList> (divisions));
2385 /* we want to go through regions from desired lowest to desired highest layer,
2386 which depends on the layer model
2389 RegionList copy = regions.rlist();
2391 /* sort according to the model and the layering mode that we're in */
2393 if (_explicit_relayering) {
2395 copy.sort (RegionSortByLayerWithPending ());
2397 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2399 copy.sort (RegionSortByLastLayerOp ());
2404 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2406 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2407 (*i)->set_pending_explicit_relayer (false);
2409 /* find the time divisions that this region covers; if there are no regions on the list,
2410 division_size will equal 0 and in this case we'll just say that
2411 start_division = end_division = 0.
2413 int start_division = 0;
2414 int end_division = 0;
2416 if (division_size > 0) {
2417 start_division = floor ( ((*i)->position() - start) / division_size);
2418 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2419 if (end_division == divisions) {
2424 assert (divisions == 0 || end_division < divisions);
2426 /* find the lowest layer that this region can go on */
2427 size_t j = layers.size();
2429 /* try layer j - 1; it can go on if it overlaps no other region
2430 that is already on that layer
2433 bool overlap = false;
2434 for (int k = start_division; k <= end_division; ++k) {
2435 RegionList::iterator l = layers[j-1][k].begin ();
2436 while (l != layers[j-1][k].end()) {
2437 if ((*l)->overlap_equivalent (*i)) {
2450 /* overlap, so we must use layer j */
2457 if (j == layers.size()) {
2458 /* we need a new layer for this region */
2459 layers.push_back (vector<RegionList> (divisions));
2462 /* put a reference to this region in each of the divisions that it exists in */
2463 for (int k = start_division; k <= end_division; ++k) {
2464 layers[j][k].push_back (*i);
2467 if ((*i)->layer() != j) {
2471 (*i)->set_layer (j);
2475 notify_layering_changed ();
2479 /* XXX these layer functions are all deprecated */
2482 Playlist::raise_region (boost::shared_ptr<Region> region)
2484 uint32_t top = regions.size() - 1;
2485 layer_t target = region->layer() + 1U;
2487 if (target >= top) {
2488 /* its already at the effective top */
2492 move_region_to_layer (target, region, 1);
2496 Playlist::lower_region (boost::shared_ptr<Region> region)
2498 if (region->layer() == 0) {
2499 /* its already at the bottom */
2503 layer_t target = region->layer() - 1U;
2505 move_region_to_layer (target, region, -1);
2509 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2511 /* does nothing useful if layering mode is later=higher */
2512 switch (_session.config.get_layer_model()) {
2519 layer_t top = regions.size() - 1;
2521 if (region->layer() >= top) {
2522 /* already on the top */
2526 move_region_to_layer (top, region, 1);
2527 /* mark the region's last_layer_op as now, so that it remains on top when
2528 doing future relayers (until something else takes over)
2530 timestamp_layer_op (region);
2534 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2536 /* does nothing useful if layering mode is later=higher */
2537 switch (_session.config.get_layer_model()) {
2544 if (region->layer() == 0) {
2545 /* already on the bottom */
2549 move_region_to_layer (0, region, -1);
2550 /* force region's last layer op to zero so that it stays at the bottom
2551 when doing future relayers
2553 region->set_last_layer_op (0);
2557 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2559 RegionList::iterator i;
2560 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2561 list<LayerInfo> layerinfo;
2564 RegionLock rlock (const_cast<Playlist *> (this));
2566 for (i = regions.begin(); i != regions.end(); ++i) {
2576 /* region is moving up, move all regions on intermediate layers
2580 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2581 dest = (*i)->layer() - 1;
2588 /* region is moving down, move all regions on intermediate layers
2592 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2593 dest = (*i)->layer() + 1;
2603 newpair.second = dest;
2605 layerinfo.push_back (newpair);
2611 /* now reset the layers without holding the region lock */
2613 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2614 x->first->set_layer (x->second);
2617 region->set_layer (target_layer);
2619 /* now check all dependents, since we changed the layering */
2621 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2622 check_dependents (x->first, false);
2625 check_dependents (region, false);
2626 notify_layering_changed ();
2634 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2636 RegionList::iterator i;
2642 RegionLock rlock (const_cast<Playlist *> (this));
2644 for (i = regions.begin(); i != regions.end(); ++i) {
2646 if ((*i)->position() >= start) {
2652 if ((*i)->last_frame() > max_framepos - distance) {
2653 new_pos = max_framepos - (*i)->length();
2655 new_pos = (*i)->position() + distance;
2660 if ((*i)->position() > distance) {
2661 new_pos = (*i)->position() - distance;
2667 (*i)->set_position (new_pos);
2675 notify_contents_changed ();
2681 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2683 RegionLock rlock (const_cast<Playlist*> (this));
2685 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2686 if ((*r)->uses_source (src)) {
2694 boost::shared_ptr<Region>
2695 Playlist::find_region (const ID& id) const
2697 RegionLock rlock (const_cast<Playlist*> (this));
2699 /* searches all regions currently in use by the playlist */
2701 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2702 if ((*i)->id() == id) {
2707 return boost::shared_ptr<Region> ();
2711 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2713 RegionLock rlock (const_cast<Playlist*> (this));
2716 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2725 boost::shared_ptr<Region>
2726 Playlist::region_by_id (const ID& id) const
2728 /* searches all regions ever added to this playlist */
2730 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2731 if ((*i)->id() == id) {
2735 return boost::shared_ptr<Region> ();
2739 Playlist::dump () const
2741 boost::shared_ptr<Region> r;
2743 cerr << "Playlist \"" << _name << "\" " << endl
2744 << regions.size() << " regions "
2747 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2749 cerr << " " << r->name() << " ["
2750 << r->start() << "+" << r->length()
2760 Playlist::set_frozen (bool yn)
2766 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2768 region->set_last_layer_op (++layer_op_counter);
2773 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2777 if (region->locked()) {
2784 RegionLock rlock (const_cast<Playlist*> (this));
2789 RegionList::iterator next;
2791 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2792 if ((*i) == region) {
2796 if (next != regions.end()) {
2798 if ((*next)->locked()) {
2804 if ((*next)->position() != region->last_frame() + 1) {
2805 /* they didn't used to touch, so after shuffle,
2806 just have them swap positions.
2808 new_pos = (*next)->position();
2810 /* they used to touch, so after shuffle,
2811 make sure they still do. put the earlier
2812 region where the later one will end after
2815 new_pos = region->position() + (*next)->length();
2818 (*next)->set_position (region->position());
2819 region->set_position (new_pos);
2821 /* avoid a full sort */
2823 regions.erase (i); // removes the region from the list */
2825 regions.insert (next, region); // adds it back after next
2834 RegionList::iterator prev = regions.end();
2836 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2837 if ((*i) == region) {
2839 if (prev != regions.end()) {
2841 if ((*prev)->locked()) {
2846 if (region->position() != (*prev)->last_frame() + 1) {
2847 /* they didn't used to touch, so after shuffle,
2848 just have them swap positions.
2850 new_pos = region->position();
2852 /* they used to touch, so after shuffle,
2853 make sure they still do. put the earlier
2854 one where the later one will end after
2856 new_pos = (*prev)->position() + region->length();
2859 region->set_position ((*prev)->position());
2860 (*prev)->set_position (new_pos);
2862 /* avoid a full sort */
2864 regions.erase (i); // remove region
2865 regions.insert (prev, region); // insert region before prev
2881 check_dependents (region, false);
2883 notify_contents_changed();
2889 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2891 RegionLock rlock (const_cast<Playlist*> (this));
2893 if (regions.size() > 1) {
2901 Playlist::update_after_tempo_map_change ()
2903 RegionLock rlock (const_cast<Playlist*> (this));
2904 RegionList copy (regions.rlist());
2908 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2909 (*i)->update_after_tempo_map_change ();
2916 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2918 RegionLock rl (this, false);
2919 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2925 Playlist::set_explicit_relayering (bool e)
2927 if (e == false && _explicit_relayering == true) {
2929 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2930 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2931 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2932 at this point would keep regions on the same layers.
2934 From then on in, it's just you and your towel.
2937 RegionLock rl (this);
2938 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2939 (*i)->set_last_layer_op ((*i)->layer ());
2943 _explicit_relayering = e;
2948 Playlist::has_region_at (framepos_t const p) const
2950 RegionLock (const_cast<Playlist *> (this));
2952 RegionList::const_iterator i = regions.begin ();
2953 while (i != regions.end() && !(*i)->covers (p)) {
2957 return (i != regions.end());
2960 /** Remove any region that uses a given source */
2962 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2964 RegionLock rl (this);
2966 RegionList::iterator i = regions.begin();
2967 while (i != regions.end()) {
2968 RegionList::iterator j = i;
2971 if ((*i)->uses_source (s)) {
2972 remove_region_internal (*i);
2979 /** Look from a session frame time and find the start time of the next region
2980 * which is on the top layer of this playlist.
2981 * @param t Time to look from.
2982 * @return Position of next top-layered region, or max_framepos if there isn't one.
2985 Playlist::find_next_top_layer_position (framepos_t t) const
2987 RegionLock rlock (const_cast<Playlist *> (this));
2989 layer_t const top = top_layer ();
2991 RegionList copy = regions.rlist ();
2992 copy.sort (RegionSortByPosition ());
2994 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2995 if ((*i)->position() >= t && (*i)->layer() == top) {
2996 return (*i)->position();
3000 return max_framepos;
3003 boost::shared_ptr<Region>
3004 Playlist::combine (const RegionList& r)
3007 uint32_t channels = 0;
3009 framepos_t earliest_position = max_framepos;
3010 vector<TwoRegions> old_and_new_regions;
3011 vector<boost::shared_ptr<Region> > originals;
3012 vector<boost::shared_ptr<Region> > copies;
3015 uint32_t max_level = 0;
3017 /* find the maximum depth of all the regions we're combining */
3019 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3020 max_level = max (max_level, (*i)->max_source_level());
3023 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3024 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3026 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3028 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3029 earliest_position = min (earliest_position, (*i)->position());
3032 /* enable this so that we do not try to create xfades etc. as we add
3036 pl->in_partition = true;
3038 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3040 /* copy the region */
3042 boost::shared_ptr<Region> original_region = (*i);
3043 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3045 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3046 originals.push_back (original_region);
3047 copies.push_back (copied_region);
3049 RegionFactory::add_compound_association (original_region, copied_region);
3051 /* make position relative to zero */
3053 pl->add_region (copied_region, original_region->position() - earliest_position);
3055 /* use the maximum number of channels for any region */
3057 channels = max (channels, original_region->n_channels());
3059 /* it will go above the layer of the highest existing region */
3061 layer = max (layer, original_region->layer());
3064 pl->in_partition = false;
3066 pre_combine (copies);
3068 /* add any dependent regions to the new playlist */
3070 copy_dependents (old_and_new_regions, pl.get());
3072 /* now create a new PlaylistSource for each channel in the new playlist */
3075 pair<framepos_t,framepos_t> extent = pl->get_extent();
3077 for (uint32_t chn = 0; chn < channels; ++chn) {
3078 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3082 /* now a new whole-file region using the list of sources */
3084 plist.add (Properties::start, 0);
3085 plist.add (Properties::length, extent.second);
3086 plist.add (Properties::name, parent_name);
3087 plist.add (Properties::whole_file, true);
3089 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3091 /* now the non-whole-file region that we will actually use in the
3096 plist.add (Properties::start, 0);
3097 plist.add (Properties::length, extent.second);
3098 plist.add (Properties::name, child_name);
3099 plist.add (Properties::layer, layer+1);
3101 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3103 /* remove all the selected regions from the current playlist
3108 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3112 /* do type-specific stuff with the originals and the new compound
3116 post_combine (originals, compound_region);
3118 /* add the new region at the right location */
3120 add_region (compound_region, earliest_position);
3126 return compound_region;
3130 Playlist::uncombine (boost::shared_ptr<Region> target)
3132 boost::shared_ptr<PlaylistSource> pls;
3133 boost::shared_ptr<const Playlist> pl;
3134 vector<boost::shared_ptr<Region> > originals;
3135 vector<TwoRegions> old_and_new_regions;
3137 // (1) check that its really a compound region
3139 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3143 pl = pls->playlist();
3145 framepos_t adjusted_start = 0; // gcc isn't smart enough
3146 framepos_t adjusted_end = 0; // gcc isn't smart enough
3148 /* the leftmost (earliest) edge of the compound region
3149 starts at zero in its source, or larger if it
3150 has been trimmed or content-scrolled.
3152 the rightmost (latest) edge of the compound region
3153 relative to its source is the starting point plus
3154 the length of the region.
3157 // (2) get all the original regions
3159 const RegionList& rl (pl->region_list().rlist());
3160 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3161 frameoffset_t move_offset = 0;
3163 /* there are two possibilities here:
3164 1) the playlist that the playlist source was based on
3165 is us, so just add the originals (which belonged to
3166 us anyway) back in the right place.
3168 2) the playlist that the playlist source was based on
3169 is NOT us, so we need to make copies of each of
3170 the original regions that we find, and add them
3173 bool same_playlist = (pls->original() == id());
3175 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3177 boost::shared_ptr<Region> current (*i);
3179 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3181 if (ca == cassocs.end()) {
3185 boost::shared_ptr<Region> original (ca->second);
3186 bool modified_region;
3188 if (i == rl.begin()) {
3189 move_offset = (target->position() - original->position()) - target->start();
3190 adjusted_start = original->position() + target->start();
3191 adjusted_end = adjusted_start + target->length();
3194 if (!same_playlist) {
3195 framepos_t pos = original->position();
3196 /* make a copy, but don't announce it */
3197 original = RegionFactory::create (original, false);
3198 /* the pure copy constructor resets position() to zero,
3201 original->set_position (pos);
3204 /* check to see how the original region (in the
3205 * playlist before compounding occured) overlaps
3206 * with the new state of the compound region.
3209 original->clear_changes ();
3210 modified_region = false;
3212 switch (original->coverage (adjusted_start, adjusted_end)) {
3214 /* original region does not cover any part
3215 of the current state of the compound region
3219 case OverlapInternal:
3220 /* overlap is just a small piece inside the
3221 * original so trim both ends
3223 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3224 modified_region = true;
3227 case OverlapExternal:
3228 /* overlap fully covers original, so leave it
3234 /* overlap starts within but covers end,
3235 so trim the front of the region
3237 original->trim_front (adjusted_start);
3238 modified_region = true;
3242 /* overlap covers start but ends within, so
3243 * trim the end of the region.
3245 original->trim_end (adjusted_end);
3246 modified_region = true;
3251 /* fix the position to match any movement of the compound region.
3253 original->set_position (original->position() + move_offset);
3254 modified_region = true;
3257 if (modified_region) {
3258 _session.add_command (new StatefulDiffCommand (original));
3261 /* and add to the list of regions waiting to be
3265 originals.push_back (original);
3266 old_and_new_regions.push_back (TwoRegions (*i, original));
3269 pre_uncombine (originals, target);
3271 in_partition = true;
3274 // (3) remove the compound region
3276 remove_region (target);
3278 // (4) add the constituent regions
3280 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3281 add_region ((*i), (*i)->position());
3284 /* now move dependent regions back from the compound to this playlist */
3286 pl->copy_dependents (old_and_new_regions, this);
3288 in_partition = false;
3293 Playlist::max_source_level () const
3295 RegionLock rlock (const_cast<Playlist *> (this));
3298 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3299 lvl = max (lvl, (*i)->max_source_level());
3307 Playlist::count_joined_regions () const
3309 RegionLock rlock (const_cast<Playlist *> (this));
3312 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3313 if ((*i)->max_source_level() > 0) {
3322 Playlist::set_orig_track_id (const PBD::ID& id)
3324 _orig_track_id = id;