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_diskstream_id (other->_orig_diskstream_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 _read_data_count = 0;
189 _frozen = other->_frozen;
191 layer_op_counter = other->layer_op_counter;
192 freeze_length = other->freeze_length;
195 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
196 : SessionObject(other->_session, str)
198 , _type(other->_type)
199 , _orig_diskstream_id (other->_orig_diskstream_id)
201 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
203 framepos_t end = start + cnt - 1;
209 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
211 boost::shared_ptr<Region> region;
212 boost::shared_ptr<Region> new_region;
213 frameoffset_t offset = 0;
214 framepos_t position = 0;
221 overlap = region->coverage (start, end);
227 case OverlapInternal:
228 offset = start - region->position();
235 position = region->position() - start;
236 len = end - region->position();
240 offset = start - region->position();
242 len = region->length() - offset;
245 case OverlapExternal:
247 position = region->position() - start;
248 len = region->length();
252 RegionFactory::region_name (new_name, region->name(), false);
256 plist.add (Properties::start, region->start() + offset);
257 plist.add (Properties::length, len);
258 plist.add (Properties::name, new_name);
259 plist.add (Properties::layer, region->layer());
261 new_region = RegionFactory::RegionFactory::create (region, plist);
263 add_region_internal (new_region, position);
267 first_set_state = false;
274 InUse (true); /* EMIT SIGNAL */
285 InUse (false); /* EMIT SIGNAL */
290 Playlist::copy_regions (RegionList& newlist) const
292 RegionLock rlock (const_cast<Playlist *> (this));
294 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
295 newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
300 Playlist::init (bool hide)
302 add_property (regions);
303 _xml_node_name = X_("Playlist");
305 g_atomic_int_set (&block_notifications, 0);
306 g_atomic_int_set (&ignore_state_changes, 0);
307 pending_contents_change = false;
308 pending_length = false;
309 pending_layering = false;
310 first_set_state = true;
318 _edit_mode = Config->get_edit_mode();
320 in_partition = false;
322 _read_data_count = 0;
324 layer_op_counter = 0;
326 _explicit_relayering = false;
329 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
330 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
332 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
335 Playlist::~Playlist ()
337 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
340 RegionLock rl (this);
342 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
343 (*i)->set_playlist (boost::shared_ptr<Playlist>());
347 /* GoingAway must be emitted by derived classes */
351 Playlist::_set_sort_id ()
354 Playlists are given names like <track name>.<id>
355 or <track name>.<edit group name>.<id> where id
356 is an integer. We extract the id and sort by that.
359 size_t dot_position = _name.val().find_last_of(".");
361 if (dot_position == string::npos) {
364 string t = _name.val().substr(dot_position + 1);
367 _sort_id = boost::lexical_cast<int>(t);
370 catch (boost::bad_lexical_cast e) {
377 Playlist::set_name (const string& str)
379 /* in a typical situation, a playlist is being used
380 by one diskstream and also is referenced by the
381 Session. if there are more references than that,
382 then don't change the name.
389 bool ret = SessionObject::set_name(str);
396 /***********************************************************************
397 CHANGE NOTIFICATION HANDLING
399 Notifications must be delayed till the region_lock is released. This
400 is necessary because handlers for the signals may need to acquire
401 the lock (e.g. to read from the playlist).
402 ***********************************************************************/
405 Playlist::begin_undo ()
412 Playlist::end_undo ()
421 delay_notifications ();
422 g_atomic_int_inc (&ignore_state_changes);
425 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
427 Playlist::thaw (bool from_undo)
429 g_atomic_int_dec_and_test (&ignore_state_changes);
430 release_notifications (from_undo);
435 Playlist::delay_notifications ()
437 g_atomic_int_inc (&block_notifications);
438 freeze_length = _get_extent().second;
441 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
443 Playlist::release_notifications (bool from_undo)
445 if (g_atomic_int_dec_and_test (&block_notifications)) {
446 flush_notifications (from_undo);
451 Playlist::notify_contents_changed ()
453 if (holding_state ()) {
454 pending_contents_change = true;
456 pending_contents_change = false;
457 ContentsChanged(); /* EMIT SIGNAL */
462 Playlist::notify_layering_changed ()
464 if (holding_state ()) {
465 pending_layering = true;
467 pending_layering = false;
468 LayeringChanged(); /* EMIT SIGNAL */
473 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
475 if (holding_state ()) {
476 pending_removes.insert (r);
477 pending_contents_change = true;
478 pending_length = true;
480 /* this might not be true, but we have to act
481 as though it could be.
483 pending_length = false;
484 LengthChanged (); /* EMIT SIGNAL */
485 pending_contents_change = false;
486 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
487 ContentsChanged (); /* EMIT SIGNAL */
492 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
494 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
496 if (holding_state ()) {
498 pending_range_moves.push_back (move);
502 list< Evoral::RangeMove<framepos_t> > m;
504 RangesMoved (m, false);
510 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
512 if (r->position() >= r->last_position()) {
513 /* trimmed shorter */
517 Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
519 if (holding_state ()) {
521 pending_region_extensions.push_back (extra);
525 list<Evoral::Range<framepos_t> > r;
533 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
535 if (r->length() < r->last_length()) {
536 /* trimmed shorter */
539 Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
541 if (holding_state ()) {
543 pending_region_extensions.push_back (extra);
547 list<Evoral::Range<framepos_t> > r;
555 Playlist::notify_region_added (boost::shared_ptr<Region> r)
557 /* the length change might not be true, but we have to act
558 as though it could be.
561 if (holding_state()) {
562 pending_adds.insert (r);
563 pending_contents_change = true;
564 pending_length = true;
567 pending_length = false;
568 LengthChanged (); /* EMIT SIGNAL */
569 pending_contents_change = false;
570 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
571 ContentsChanged (); /* EMIT SIGNAL */
576 Playlist::notify_length_changed ()
578 if (holding_state ()) {
579 pending_length = true;
581 pending_length = false;
582 LengthChanged(); /* EMIT SIGNAL */
583 pending_contents_change = false;
584 ContentsChanged (); /* EMIT SIGNAL */
588 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
590 Playlist::flush_notifications (bool from_undo)
592 set<boost::shared_ptr<Region> > dependent_checks_needed;
593 set<boost::shared_ptr<Region> >::iterator s;
594 uint32_t regions_changed = false;
595 bool check_length = false;
596 framecnt_t old_length = 0;
604 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
605 regions_changed = true;
606 if (!pending_length) {
607 old_length = _get_extent ().second;
612 /* we have no idea what order the regions ended up in pending
613 bounds (it could be based on selection order, for example).
614 so, to preserve layering in the "most recently moved is higher"
615 model, sort them by existing layer, then timestamp them.
618 // RegionSortByLayer cmp;
619 // pending_bounds.sort (cmp);
621 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
622 if (_session.config.get_layer_model() == MoveAddHigher) {
623 timestamp_layer_op (*r);
625 dependent_checks_needed.insert (*r);
628 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
629 remove_dependents (*s);
630 // cerr << _name << " sends RegionRemoved\n";
631 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
634 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
635 // cerr << _name << " sends RegionAdded\n";
636 /* don't emit RegionAdded signal until relayering is done,
637 so that the region is fully setup by the time
638 anyone hear's that its been added
640 dependent_checks_needed.insert (*s);
644 if (old_length != _get_extent().second) {
645 pending_length = true;
646 // cerr << _name << " length has changed\n";
650 if (pending_length || (freeze_length != _get_extent().second)) {
651 pending_length = false;
652 // cerr << _name << " sends LengthChanged\n";
653 LengthChanged(); /* EMIT SIGNAL */
656 if (regions_changed || pending_contents_change) {
660 pending_contents_change = false;
661 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
662 ContentsChanged (); /* EMIT SIGNAL */
663 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
666 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
667 (*s)->clear_changes ();
668 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
671 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
672 check_dependents (*s, false);
675 if (!pending_range_moves.empty ()) {
676 RangesMoved (pending_range_moves, from_undo);
679 if (!pending_region_extensions.empty ()) {
680 RegionsExtended (pending_region_extensions);
689 Playlist::clear_pending ()
691 pending_adds.clear ();
692 pending_removes.clear ();
693 pending_bounds.clear ();
694 pending_range_moves.clear ();
695 pending_region_extensions.clear ();
696 pending_contents_change = false;
697 pending_length = false;
700 /*************************************************************
702 *************************************************************/
705 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
707 RegionLock rlock (this);
708 times = fabs (times);
710 int itimes = (int) floor (times);
712 framepos_t pos = position;
714 if (times == 1 && auto_partition){
715 partition(pos - 1, (pos + region->length()), true);
719 add_region_internal (region, pos);
720 pos += region->length();
725 /* note that itimes can be zero if we being asked to just
726 insert a single fraction of the region.
729 for (int i = 0; i < itimes; ++i) {
730 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
731 add_region_internal (copy, pos);
732 pos += region->length();
735 framecnt_t length = 0;
737 if (floor (times) != times) {
738 length = (framecnt_t) floor (region->length() * (times - floor (times)));
740 RegionFactory::region_name (name, region->name(), false);
745 plist.add (Properties::start, region->start());
746 plist.add (Properties::length, length);
747 plist.add (Properties::name, name);
748 plist.add (Properties::layer, region->layer());
750 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
751 add_region_internal (sub, pos);
755 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
759 Playlist::set_region_ownership ()
761 RegionLock rl (this);
762 RegionList::iterator i;
763 boost::weak_ptr<Playlist> pl (shared_from_this());
765 for (i = regions.begin(); i != regions.end(); ++i) {
766 (*i)->set_playlist (pl);
771 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
773 if (region->data_type() != _type){
777 RegionSortByPosition cmp;
779 framecnt_t old_length = 0;
781 if (!holding_state()) {
782 old_length = _get_extent().second;
785 if (!first_set_state) {
786 boost::shared_ptr<Playlist> foo (shared_from_this());
787 region->set_playlist (boost::weak_ptr<Playlist>(foo));
790 region->set_position (position);
792 timestamp_layer_op (region);
794 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
795 all_regions.insert (region);
797 possibly_splice_unlocked (position, region->length(), region);
799 if (!holding_state ()) {
800 /* layers get assigned from XML state, and are not reset during undo/redo */
804 /* we need to notify the existence of new region before checking dependents. Ick. */
806 notify_region_added (region);
809 if (!holding_state ()) {
811 check_dependents (region, false);
813 if (old_length != _get_extent().second) {
814 notify_length_changed ();
818 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
824 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
826 RegionLock rlock (this);
828 bool old_sp = _splicing;
831 remove_region_internal (old);
832 add_region_internal (newr, pos);
836 possibly_splice_unlocked (pos, old->length() - newr->length());
840 Playlist::remove_region (boost::shared_ptr<Region> region)
842 RegionLock rlock (this);
843 remove_region_internal (region);
847 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
849 RegionList::iterator i;
850 framecnt_t old_length = 0;
853 if (!holding_state()) {
854 old_length = _get_extent().second;
859 region->set_playlist (boost::weak_ptr<Playlist>());
862 /* XXX should probably freeze here .... */
864 for (i = regions.begin(); i != regions.end(); ++i) {
867 framepos_t pos = (*i)->position();
868 framecnt_t distance = (*i)->length();
872 possibly_splice_unlocked (pos, -distance);
874 if (!holding_state ()) {
876 remove_dependents (region);
878 if (old_length != _get_extent().second) {
879 notify_length_changed ();
883 notify_region_removed (region);
893 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
895 if (Config->get_use_overlap_equivalency()) {
896 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
897 if ((*i)->overlap_equivalent (other)) {
898 results.push_back ((*i));
902 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
903 if ((*i)->equivalent (other)) {
904 results.push_back ((*i));
911 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
913 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
915 if ((*i) && (*i)->region_list_equivalent (other)) {
916 results.push_back (*i);
922 Playlist::partition (framepos_t start, framepos_t end, bool cut)
926 partition_internal (start, end, cut, thawlist);
928 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
929 (*i)->resume_property_changes ();
934 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
936 RegionList new_regions;
939 RegionLock rlock (this);
941 boost::shared_ptr<Region> region;
942 boost::shared_ptr<Region> current;
944 RegionList::iterator tmp;
946 framepos_t pos1, pos2, pos3, pos4;
950 /* need to work from a copy, because otherwise the regions we add during the process
951 get operated on as well.
954 RegionList copy = regions.rlist();
956 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
963 if (current->first_frame() >= start && current->last_frame() < end) {
966 remove_region_internal (current);
972 /* coverage will return OverlapStart if the start coincides
973 with the end point. we do not partition such a region,
974 so catch this special case.
977 if (current->first_frame() >= end) {
981 if ((overlap = current->coverage (start, end)) == OverlapNone) {
985 pos1 = current->position();
988 pos4 = current->last_frame();
990 if (overlap == OverlapInternal) {
991 /* split: we need 3 new regions, the front, middle and end.
992 cut: we need 2 regions, the front and end.
997 ---------------*************************------------
1000 ---------------*****++++++++++++++++====------------
1002 ---------------*****----------------====------------
1007 /* "middle" ++++++ */
1009 RegionFactory::region_name (new_name, current->name(), false);
1013 plist.add (Properties::start, current->start() + (pos2 - pos1));
1014 plist.add (Properties::length, pos3 - pos2);
1015 plist.add (Properties::name, new_name);
1016 plist.add (Properties::layer, regions.size());
1017 plist.add (Properties::automatic, true);
1018 plist.add (Properties::left_of_split, true);
1019 plist.add (Properties::right_of_split, true);
1021 region = RegionFactory::create (current, plist);
1022 add_region_internal (region, start);
1023 new_regions.push_back (region);
1028 RegionFactory::region_name (new_name, current->name(), false);
1032 plist.add (Properties::start, current->start() + (pos3 - pos1));
1033 plist.add (Properties::length, pos4 - pos3);
1034 plist.add (Properties::name, new_name);
1035 plist.add (Properties::layer, regions.size());
1036 plist.add (Properties::automatic, true);
1037 plist.add (Properties::right_of_split, true);
1039 region = RegionFactory::create (current, plist);
1041 add_region_internal (region, end);
1042 new_regions.push_back (region);
1046 current->suspend_property_changes ();
1047 thawlist.push_back (current);
1048 current->cut_end (pos2 - 1);
1050 } else if (overlap == OverlapEnd) {
1054 ---------------*************************------------
1057 ---------------**************+++++++++++------------
1059 ---------------**************-----------------------
1066 RegionFactory::region_name (new_name, current->name(), false);
1070 plist.add (Properties::start, current->start() + (pos2 - pos1));
1071 plist.add (Properties::length, pos4 - pos2);
1072 plist.add (Properties::name, new_name);
1073 plist.add (Properties::layer, regions.size());
1074 plist.add (Properties::automatic, true);
1075 plist.add (Properties::left_of_split, true);
1077 region = RegionFactory::create (current, plist);
1079 add_region_internal (region, start);
1080 new_regions.push_back (region);
1085 current->suspend_property_changes ();
1086 thawlist.push_back (current);
1087 current->cut_end (pos2 - 1);
1089 } else if (overlap == OverlapStart) {
1091 /* split: we need 2 regions: the front and the end.
1092 cut: just trim current to skip the cut area
1097 ---------------*************************------------
1101 ---------------****+++++++++++++++++++++------------
1103 -------------------*********************------------
1109 RegionFactory::region_name (new_name, current->name(), false);
1113 plist.add (Properties::start, current->start());
1114 plist.add (Properties::length, pos3 - pos1);
1115 plist.add (Properties::name, new_name);
1116 plist.add (Properties::layer, regions.size());
1117 plist.add (Properties::automatic, true);
1118 plist.add (Properties::right_of_split, true);
1120 region = RegionFactory::create (current, plist);
1122 add_region_internal (region, pos1);
1123 new_regions.push_back (region);
1128 current->suspend_property_changes ();
1129 thawlist.push_back (current);
1130 current->trim_front (pos3);
1131 } else if (overlap == OverlapExternal) {
1133 /* split: no split required.
1134 cut: remove the region.
1139 ---------------*************************------------
1143 ---------------*************************------------
1145 ----------------------------------------------------
1150 remove_region_internal (current);
1153 new_regions.push_back (current);
1157 in_partition = false;
1160 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1161 check_dependents (*i, false);
1165 boost::shared_ptr<Playlist>
1166 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1168 boost::shared_ptr<Playlist> ret;
1169 boost::shared_ptr<Playlist> pl;
1172 if (ranges.empty()) {
1173 return boost::shared_ptr<Playlist>();
1176 start = ranges.front().start;
1178 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1180 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1182 if (i == ranges.begin()) {
1186 /* paste the next section into the nascent playlist,
1187 offset to reflect the start of the first range we
1191 ret->paste (pl, (*i).start - start, 1.0f);
1198 boost::shared_ptr<Playlist>
1199 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1201 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1202 return cut_copy (pmf, ranges, result_is_hidden);
1205 boost::shared_ptr<Playlist>
1206 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1208 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1209 return cut_copy (pmf, ranges, result_is_hidden);
1212 boost::shared_ptr<Playlist>
1213 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1215 boost::shared_ptr<Playlist> the_copy;
1216 RegionList thawlist;
1219 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1220 string new_name = _name;
1224 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1225 return boost::shared_ptr<Playlist>();
1228 partition_internal (start, start+cnt-1, true, thawlist);
1230 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1231 (*i)->resume_property_changes();
1237 boost::shared_ptr<Playlist>
1238 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1242 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1243 string new_name = _name;
1247 cnt = min (_get_extent().second - start, cnt);
1248 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1252 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1254 times = fabs (times);
1257 RegionLock rl1 (this);
1258 RegionLock rl2 (other.get());
1260 framecnt_t const old_length = _get_extent().second;
1262 int itimes = (int) floor (times);
1263 framepos_t pos = position;
1264 framecnt_t const shift = other->_get_extent().second;
1265 layer_t top_layer = regions.size();
1268 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1269 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1271 /* put these new regions on top of all existing ones, but preserve
1272 the ordering they had in the original playlist.
1275 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1276 add_region_internal (copy_of_region, (*i)->position() + pos);
1282 /* XXX shall we handle fractional cases at some point? */
1284 if (old_length != _get_extent().second) {
1285 notify_length_changed ();
1296 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1298 times = fabs (times);
1300 RegionLock rl (this);
1301 int itimes = (int) floor (times);
1302 framepos_t pos = position + 1;
1305 boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1306 add_region_internal (copy, pos);
1307 pos += region->length();
1310 if (floor (times) != times) {
1311 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1313 RegionFactory::region_name (name, region->name(), false);
1318 plist.add (Properties::start, region->start());
1319 plist.add (Properties::length, length);
1320 plist.add (Properties::name, name);
1322 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1323 add_region_internal (sub, pos);
1329 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1331 RegionLock rlock (this);
1332 RegionList copy (regions.rlist());
1335 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1337 if ((*r)->last_frame() < at) {
1342 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1343 /* intersected region */
1344 if (!move_intersected) {
1349 /* do not move regions glued to music time - that
1350 has to be done separately.
1353 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1354 fixup.push_back (*r);
1358 (*r)->set_position ((*r)->position() + distance);
1361 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1362 (*r)->recompute_position_from_lock_style ();
1367 Playlist::split (framepos_t at)
1369 RegionLock rlock (this);
1370 RegionList copy (regions.rlist());
1372 /* use a copy since this operation can modify the region list
1375 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1376 _split_region (*r, at);
1381 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1383 RegionLock rl (this);
1384 _split_region (region, playlist_position);
1388 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1390 if (!region->covers (playlist_position)) {
1394 if (region->position() == playlist_position ||
1395 region->last_frame() == playlist_position) {
1399 boost::shared_ptr<Region> left;
1400 boost::shared_ptr<Region> right;
1401 frameoffset_t before;
1402 frameoffset_t after;
1406 /* split doesn't change anything about length, so don't try to splice */
1408 bool old_sp = _splicing;
1411 before = playlist_position - region->position();
1412 after = region->length() - before;
1414 RegionFactory::region_name (before_name, region->name(), false);
1419 plist.add (Properties::position, region->position ());
1420 plist.add (Properties::length, before);
1421 plist.add (Properties::name, before_name);
1422 plist.add (Properties::left_of_split, true);
1424 /* note: we must use the version of ::create with an offset here,
1425 since it supplies that offset to the Region constructor, which
1426 is necessary to get audio region gain envelopes right.
1428 left = RegionFactory::create (region, 0, plist);
1431 RegionFactory::region_name (after_name, region->name(), false);
1436 plist.add (Properties::position, region->position() + before);
1437 plist.add (Properties::length, after);
1438 plist.add (Properties::name, after_name);
1439 plist.add (Properties::right_of_split, true);
1441 /* same note as above */
1442 right = RegionFactory::create (region, before, plist);
1445 add_region_internal (left, region->position());
1446 add_region_internal (right, region->position() + before);
1448 uint64_t orig_layer_op = region->last_layer_op();
1449 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1450 if ((*i)->last_layer_op() > orig_layer_op) {
1451 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1455 left->set_last_layer_op ( orig_layer_op );
1456 right->set_last_layer_op ( orig_layer_op + 1);
1460 finalize_split_region (region, left, right);
1462 remove_region_internal (region);
1468 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1470 if (_splicing || in_set_state) {
1471 /* don't respond to splicing moves or state setting */
1475 if (_edit_mode == Splice) {
1476 splice_locked (at, distance, exclude);
1481 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1483 if (_splicing || in_set_state) {
1484 /* don't respond to splicing moves or state setting */
1488 if (_edit_mode == Splice) {
1489 splice_unlocked (at, distance, exclude);
1494 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1497 RegionLock rl (this);
1498 core_splice (at, distance, exclude);
1503 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1505 core_splice (at, distance, exclude);
1509 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1513 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1515 if (exclude && (*i) == exclude) {
1519 if ((*i)->position() >= at) {
1520 framepos_t new_pos = (*i)->position() + distance;
1523 } else if (new_pos >= max_framepos - (*i)->length()) {
1524 new_pos = max_framepos - (*i)->length();
1527 (*i)->set_position (new_pos);
1533 notify_length_changed ();
1537 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1539 if (in_set_state || _splicing || _nudging || _shuffling) {
1543 if (what_changed.contains (Properties::position)) {
1545 /* remove it from the list then add it back in
1546 the right place again.
1549 RegionSortByPosition cmp;
1551 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1553 if (i == regions.end()) {
1554 /* the region bounds are being modified but its not currently
1555 in the region list. we will use its bounds correctly when/if
1562 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1565 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1567 frameoffset_t delta = 0;
1569 if (what_changed.contains (Properties::position)) {
1570 delta = region->position() - region->last_position();
1573 if (what_changed.contains (Properties::length)) {
1574 delta += region->length() - region->last_length();
1578 possibly_splice (region->last_position() + region->last_length(), delta, region);
1581 if (holding_state ()) {
1582 pending_bounds.push_back (region);
1584 if (_session.config.get_layer_model() == MoveAddHigher) {
1585 /* it moved or changed length, so change the timestamp */
1586 timestamp_layer_op (region);
1589 notify_length_changed ();
1591 check_dependents (region, false);
1597 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1599 boost::shared_ptr<Region> region (weak_region.lock());
1605 /* this makes a virtual call to the right kind of playlist ... */
1607 region_changed (what_changed, region);
1611 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1613 PropertyChange our_interests;
1614 PropertyChange bounds;
1615 PropertyChange pos_and_length;
1618 if (in_set_state || in_flush) {
1622 our_interests.add (Properties::muted);
1623 our_interests.add (Properties::layer);
1624 our_interests.add (Properties::opaque);
1626 bounds.add (Properties::start);
1627 bounds.add (Properties::position);
1628 bounds.add (Properties::length);
1630 pos_and_length.add (Properties::position);
1631 pos_and_length.add (Properties::length);
1633 if (what_changed.contains (bounds)) {
1634 region_bounds_changed (what_changed, region);
1635 save = !(_splicing || _nudging);
1638 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1639 check_dependents (region, false);
1642 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1643 notify_region_moved (region);
1644 } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1645 notify_region_end_trimmed (region);
1646 } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1647 notify_region_start_trimmed (region);
1650 /* don't notify about layer changes, since we are the only object that can initiate
1651 them, and we notify in ::relayer()
1654 if (what_changed.contains (our_interests)) {
1662 Playlist::drop_regions ()
1664 RegionLock rl (this);
1666 all_regions.clear ();
1670 Playlist::sync_all_regions_with_regions ()
1672 RegionLock rl (this);
1674 all_regions.clear ();
1676 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1677 all_regions.insert (*i);
1682 Playlist::clear (bool with_signals)
1685 RegionLock rl (this);
1687 region_state_changed_connections.drop_connections ();
1689 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1690 pending_removes.insert (*i);
1695 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1696 remove_dependents (*s);
1702 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1703 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1706 pending_removes.clear ();
1707 pending_length = false;
1709 pending_contents_change = false;
1715 /***********************************************************************
1717 **********************************************************************/
1719 Playlist::RegionList *
1720 Playlist::regions_at (framepos_t frame)
1723 RegionLock rlock (this);
1724 return find_regions_at (frame);
1728 Playlist::count_regions_at (framepos_t frame) const
1730 RegionLock rlock (const_cast<Playlist*>(this));
1733 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1734 if ((*i)->covers (frame)) {
1742 boost::shared_ptr<Region>
1743 Playlist::top_region_at (framepos_t frame)
1746 RegionLock rlock (this);
1747 RegionList *rlist = find_regions_at (frame);
1748 boost::shared_ptr<Region> region;
1750 if (rlist->size()) {
1751 RegionSortByLayer cmp;
1753 region = rlist->back();
1760 boost::shared_ptr<Region>
1761 Playlist::top_unmuted_region_at (framepos_t frame)
1764 RegionLock rlock (this);
1765 RegionList *rlist = find_regions_at (frame);
1767 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1769 RegionList::iterator tmp = i;
1772 if ((*i)->muted()) {
1779 boost::shared_ptr<Region> region;
1781 if (rlist->size()) {
1782 RegionSortByLayer cmp;
1784 region = rlist->back();
1791 Playlist::RegionList*
1792 Playlist::regions_to_read (framepos_t start, framepos_t end)
1794 /* Caller must hold lock */
1796 RegionList covering;
1797 set<framepos_t> to_check;
1798 set<boost::shared_ptr<Region> > unique;
1800 to_check.insert (start);
1801 to_check.insert (end);
1803 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1805 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1807 /* find all/any regions that span start+end */
1809 switch ((*i)->coverage (start, end)) {
1813 case OverlapInternal:
1814 covering.push_back (*i);
1815 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1819 to_check.insert ((*i)->position());
1820 if ((*i)->position() != 0) {
1821 to_check.insert ((*i)->position()-1);
1823 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1824 covering.push_back (*i);
1828 to_check.insert ((*i)->last_frame());
1829 to_check.insert ((*i)->last_frame()+1);
1830 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1831 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1832 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1833 covering.push_back (*i);
1836 case OverlapExternal:
1837 covering.push_back (*i);
1838 to_check.insert ((*i)->position());
1839 if ((*i)->position() != 0) {
1840 to_check.insert ((*i)->position()-1);
1842 to_check.insert ((*i)->last_frame());
1843 to_check.insert ((*i)->last_frame()+1);
1844 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1845 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1846 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1850 /* don't go too far */
1852 if ((*i)->position() > end) {
1857 RegionList* rlist = new RegionList;
1859 /* find all the regions that cover each position .... */
1861 if (covering.size() == 1) {
1863 rlist->push_back (covering.front());
1864 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1869 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1873 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1875 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1877 if ((*x)->covers (*t)) {
1878 here.push_back (*x);
1879 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1883 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1890 RegionSortByLayer cmp;
1893 /* ... and get the top/transparent regions at "here" */
1895 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1899 if ((*c)->opaque()) {
1901 /* the other regions at this position are hidden by this one */
1902 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1909 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1910 rlist->push_back (*s);
1913 if (rlist->size() > 1) {
1914 /* now sort by time order */
1916 RegionSortByPosition cmp;
1921 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1926 Playlist::RegionList *
1927 Playlist::find_regions_at (framepos_t frame)
1929 /* Caller must hold lock */
1931 RegionList *rlist = new RegionList;
1933 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1934 if ((*i)->covers (frame)) {
1935 rlist->push_back (*i);
1942 Playlist::RegionList *
1943 Playlist::regions_touched (framepos_t start, framepos_t end)
1945 RegionLock rlock (this);
1946 RegionList *rlist = new RegionList;
1948 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1949 if ((*i)->coverage (start, end) != OverlapNone) {
1950 rlist->push_back (*i);
1958 Playlist::find_next_transient (framepos_t from, int dir)
1960 RegionLock rlock (this);
1961 AnalysisFeatureList points;
1962 AnalysisFeatureList these_points;
1964 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1966 if ((*i)->last_frame() < from) {
1970 if ((*i)->first_frame() > from) {
1975 (*i)->get_transients (these_points);
1977 /* add first frame, just, err, because */
1979 these_points.push_back ((*i)->first_frame());
1981 points.insert (points.end(), these_points.begin(), these_points.end());
1982 these_points.clear ();
1985 if (points.empty()) {
1989 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1990 bool reached = false;
1993 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1998 if (reached && (*x) > from) {
2003 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2008 if (reached && (*x) < from) {
2017 boost::shared_ptr<Region>
2018 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2020 RegionLock rlock (this);
2021 boost::shared_ptr<Region> ret;
2022 framepos_t closest = max_framepos;
2024 bool end_iter = false;
2026 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2030 frameoffset_t distance;
2031 boost::shared_ptr<Region> r = (*i);
2036 pos = r->first_frame ();
2039 pos = r->last_frame ();
2042 pos = r->sync_position ();
2047 case 1: /* forwards */
2050 if ((distance = pos - frame) < closest) {
2059 default: /* backwards */
2062 if ((distance = frame - pos) < closest) {
2079 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2081 RegionLock rlock (this);
2083 framepos_t closest = max_framepos;
2084 framepos_t ret = -1;
2088 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2090 boost::shared_ptr<Region> r = (*i);
2091 frameoffset_t distance;
2093 if (r->first_frame() > frame) {
2095 distance = r->first_frame() - frame;
2097 if (distance < closest) {
2098 ret = r->first_frame();
2103 if (r->last_frame () > frame) {
2105 distance = r->last_frame () - frame;
2107 if (distance < closest) {
2108 ret = r->last_frame ();
2116 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2118 boost::shared_ptr<Region> r = (*i);
2119 frameoffset_t distance;
2121 if (r->last_frame() < frame) {
2123 distance = frame - r->last_frame();
2125 if (distance < closest) {
2126 ret = r->last_frame();
2131 if (r->first_frame() < frame) {
2133 distance = frame - r->first_frame();
2135 if (distance < closest) {
2136 ret = r->first_frame();
2147 /***********************************************************************/
2153 Playlist::mark_session_dirty ()
2155 if (!in_set_state && !holding_state ()) {
2156 _session.set_dirty();
2161 Playlist::rdiff (vector<Command*>& cmds) const
2163 RegionLock rlock (const_cast<Playlist *> (this));
2164 Stateful::rdiff (cmds);
2168 Playlist::clear_owned_changes ()
2170 RegionLock rlock (this);
2171 Stateful::clear_owned_changes ();
2175 Playlist::update (const RegionListProperty::ChangeRecord& change)
2177 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2178 name(), change.added.size(), change.removed.size()));
2181 /* add the added regions */
2182 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2183 add_region ((*i), (*i)->position());
2185 /* remove the removed regions */
2186 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2194 Playlist::set_state (const XMLNode& node, int version)
2198 XMLNodeConstIterator niter;
2199 XMLPropertyList plist;
2200 XMLPropertyConstIterator piter;
2202 boost::shared_ptr<Region> region;
2207 if (node.name() != "Playlist") {
2214 plist = node.properties();
2216 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2220 if (prop->name() == X_("name")) {
2221 _name = prop->value();
2223 } else if (prop->name() == X_("id")) {
2224 _id = prop->value();
2225 } else if (prop->name() == X_("orig-diskstream-id")) {
2226 _orig_diskstream_id = prop->value ();
2227 } else if (prop->name() == X_("frozen")) {
2228 _frozen = string_is_affirmative (prop->value());
2229 } else if (prop->name() == X_("combine-ops")) {
2230 _combine_ops = atoi (prop->value());
2236 nlist = node.children();
2238 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2242 if (child->name() == "Region") {
2244 if ((prop = child->property ("id")) == 0) {
2245 error << _("region state node has no ID, ignored") << endmsg;
2249 ID id = prop->value ();
2251 if ((region = region_by_id (id))) {
2253 region->suspend_property_changes ();
2255 if (region->set_state (*child, version)) {
2256 region->resume_property_changes ();
2260 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2261 region->suspend_property_changes ();
2263 error << _("Playlist: cannot create region from XML") << endmsg;
2268 add_region (region, region->position(), 1.0);
2270 // So that layer_op ordering doesn't get screwed up
2271 region->set_last_layer_op( region->layer());
2272 region->resume_property_changes ();
2277 /* update dependents, which was not done during add_region_internal
2278 due to in_set_state being true
2281 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2282 check_dependents (*r, false);
2286 notify_contents_changed ();
2289 first_set_state = false;
2294 Playlist::get_state()
2296 return state (true);
2300 Playlist::get_template()
2302 return state (false);
2305 /** @param full_state true to include regions in the returned state, otherwise false.
2308 Playlist::state (bool full_state)
2310 XMLNode *node = new XMLNode (X_("Playlist"));
2313 node->add_property (X_("id"), id().to_s());
2314 node->add_property (X_("name"), _name);
2315 node->add_property (X_("type"), _type.to_string());
2317 _orig_diskstream_id.print (buf, sizeof (buf));
2318 node->add_property (X_("orig-diskstream-id"), buf);
2319 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2322 RegionLock rlock (this, false);
2324 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2325 node->add_property ("combine-ops", buf);
2327 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2328 node->add_child_nocopy ((*i)->get_state());
2333 node->add_child_copy (*_extra_xml);
2340 Playlist::empty() const
2342 RegionLock rlock (const_cast<Playlist *>(this), false);
2343 return regions.empty();
2347 Playlist::n_regions() const
2349 RegionLock rlock (const_cast<Playlist *>(this), false);
2350 return regions.size();
2353 pair<framepos_t, framepos_t>
2354 Playlist::get_extent () const
2356 RegionLock rlock (const_cast<Playlist *>(this), false);
2357 return _get_extent ();
2360 pair<framepos_t, framepos_t>
2361 Playlist::_get_extent () const
2363 pair<framepos_t, framepos_t> ext (max_framepos, 0);
2365 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2366 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2367 if (e.first < ext.first) {
2368 ext.first = e.first;
2370 if (e.second > ext.second) {
2371 ext.second = e.second;
2379 Playlist::bump_name (string name, Session &session)
2381 string newname = name;
2384 newname = bump_name_once (newname, '.');
2385 } while (session.playlists->by_name (newname)!=NULL);
2392 Playlist::top_layer() const
2394 RegionLock rlock (const_cast<Playlist *> (this));
2397 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2398 top = max (top, (*i)->layer());
2404 Playlist::set_edit_mode (EditMode mode)
2409 /********************
2411 ********************/
2414 Playlist::relayer ()
2416 /* never compute layers when changing state for undo/redo or setting from XML */
2418 if (in_update || in_set_state) {
2422 bool changed = false;
2424 /* Build up a new list of regions on each layer, stored in a set of lists
2425 each of which represent some period of time on some layer. The idea
2426 is to avoid having to search the entire region list to establish whether
2427 each region overlaps another */
2429 /* how many pieces to divide this playlist's time up into */
2430 int const divisions = 512;
2432 /* find the start and end positions of the regions on this playlist */
2433 framepos_t start = INT64_MAX;
2435 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2436 start = min (start, (*i)->position());
2437 end = max (end, (*i)->position() + (*i)->length());
2440 /* hence the size of each time division */
2441 double const division_size = (end - start) / double (divisions);
2443 vector<vector<RegionList> > layers;
2444 layers.push_back (vector<RegionList> (divisions));
2446 /* we want to go through regions from desired lowest to desired highest layer,
2447 which depends on the layer model
2450 RegionList copy = regions.rlist();
2452 /* sort according to the model and the layering mode that we're in */
2454 if (_explicit_relayering) {
2456 copy.sort (RegionSortByLayerWithPending ());
2458 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2460 copy.sort (RegionSortByLastLayerOp ());
2465 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2467 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2468 (*i)->set_pending_explicit_relayer (false);
2470 /* find the time divisions that this region covers; if there are no regions on the list,
2471 division_size will equal 0 and in this case we'll just say that
2472 start_division = end_division = 0.
2474 int start_division = 0;
2475 int end_division = 0;
2477 if (division_size > 0) {
2478 start_division = floor ( ((*i)->position() - start) / division_size);
2479 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2480 if (end_division == divisions) {
2485 assert (divisions == 0 || end_division < divisions);
2487 /* find the lowest layer that this region can go on */
2488 size_t j = layers.size();
2490 /* try layer j - 1; it can go on if it overlaps no other region
2491 that is already on that layer
2494 bool overlap = false;
2495 for (int k = start_division; k <= end_division; ++k) {
2496 RegionList::iterator l = layers[j-1][k].begin ();
2497 while (l != layers[j-1][k].end()) {
2498 if ((*l)->overlap_equivalent (*i)) {
2511 /* overlap, so we must use layer j */
2518 if (j == layers.size()) {
2519 /* we need a new layer for this region */
2520 layers.push_back (vector<RegionList> (divisions));
2523 /* put a reference to this region in each of the divisions that it exists in */
2524 for (int k = start_division; k <= end_division; ++k) {
2525 layers[j][k].push_back (*i);
2528 if ((*i)->layer() != j) {
2532 (*i)->set_layer (j);
2536 notify_layering_changed ();
2540 /* XXX these layer functions are all deprecated */
2543 Playlist::raise_region (boost::shared_ptr<Region> region)
2545 uint32_t top = regions.size() - 1;
2546 layer_t target = region->layer() + 1U;
2548 if (target >= top) {
2549 /* its already at the effective top */
2553 move_region_to_layer (target, region, 1);
2557 Playlist::lower_region (boost::shared_ptr<Region> region)
2559 if (region->layer() == 0) {
2560 /* its already at the bottom */
2564 layer_t target = region->layer() - 1U;
2566 move_region_to_layer (target, region, -1);
2570 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2572 /* does nothing useful if layering mode is later=higher */
2573 switch (_session.config.get_layer_model()) {
2580 layer_t top = regions.size() - 1;
2582 if (region->layer() >= top) {
2583 /* already on the top */
2587 move_region_to_layer (top, region, 1);
2588 /* mark the region's last_layer_op as now, so that it remains on top when
2589 doing future relayers (until something else takes over)
2591 timestamp_layer_op (region);
2595 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2597 /* does nothing useful if layering mode is later=higher */
2598 switch (_session.config.get_layer_model()) {
2605 if (region->layer() == 0) {
2606 /* already on the bottom */
2610 move_region_to_layer (0, region, -1);
2611 /* force region's last layer op to zero so that it stays at the bottom
2612 when doing future relayers
2614 region->set_last_layer_op (0);
2618 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2620 RegionList::iterator i;
2621 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2622 list<LayerInfo> layerinfo;
2625 RegionLock rlock (const_cast<Playlist *> (this));
2627 for (i = regions.begin(); i != regions.end(); ++i) {
2637 /* region is moving up, move all regions on intermediate layers
2641 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2642 dest = (*i)->layer() - 1;
2649 /* region is moving down, move all regions on intermediate layers
2653 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2654 dest = (*i)->layer() + 1;
2664 newpair.second = dest;
2666 layerinfo.push_back (newpair);
2672 /* now reset the layers without holding the region lock */
2674 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2675 x->first->set_layer (x->second);
2678 region->set_layer (target_layer);
2680 /* now check all dependents, since we changed the layering */
2682 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2683 check_dependents (x->first, false);
2686 check_dependents (region, false);
2687 notify_layering_changed ();
2695 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2697 RegionList::iterator i;
2703 RegionLock rlock (const_cast<Playlist *> (this));
2705 for (i = regions.begin(); i != regions.end(); ++i) {
2707 if ((*i)->position() >= start) {
2713 if ((*i)->last_frame() > max_framepos - distance) {
2714 new_pos = max_framepos - (*i)->length();
2716 new_pos = (*i)->position() + distance;
2721 if ((*i)->position() > distance) {
2722 new_pos = (*i)->position() - distance;
2728 (*i)->set_position (new_pos);
2736 notify_length_changed ();
2742 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2744 RegionLock rlock (const_cast<Playlist*> (this));
2746 for (set<boost::shared_ptr<Region> >::iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2747 if ((*r)->uses_source (src)) {
2755 boost::shared_ptr<Region>
2756 Playlist::find_region (const ID& id) const
2758 RegionLock rlock (const_cast<Playlist*> (this));
2760 /* searches all regions currently in use by the playlist */
2762 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2763 if ((*i)->id() == id) {
2768 return boost::shared_ptr<Region> ();
2772 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2774 RegionLock rlock (const_cast<Playlist*> (this));
2777 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2786 boost::shared_ptr<Region>
2787 Playlist::region_by_id (const ID& id) const
2789 /* searches all regions ever added to this playlist */
2791 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2792 if ((*i)->id() == id) {
2796 return boost::shared_ptr<Region> ();
2800 Playlist::dump () const
2802 boost::shared_ptr<Region> r;
2804 cerr << "Playlist \"" << _name << "\" " << endl
2805 << regions.size() << " regions "
2808 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2810 cerr << " " << r->name() << " ["
2811 << r->start() << "+" << r->length()
2821 Playlist::set_frozen (bool yn)
2827 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2829 region->set_last_layer_op (++layer_op_counter);
2834 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2838 if (region->locked()) {
2845 RegionLock rlock (const_cast<Playlist*> (this));
2850 RegionList::iterator next;
2852 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2853 if ((*i) == region) {
2857 if (next != regions.end()) {
2859 if ((*next)->locked()) {
2865 if ((*next)->position() != region->last_frame() + 1) {
2866 /* they didn't used to touch, so after shuffle,
2867 just have them swap positions.
2869 new_pos = (*next)->position();
2871 /* they used to touch, so after shuffle,
2872 make sure they still do. put the earlier
2873 region where the later one will end after
2876 new_pos = region->position() + (*next)->length();
2879 (*next)->set_position (region->position());
2880 region->set_position (new_pos);
2882 /* avoid a full sort */
2884 regions.erase (i); // removes the region from the list */
2886 regions.insert (next, region); // adds it back after next
2895 RegionList::iterator prev = regions.end();
2897 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2898 if ((*i) == region) {
2900 if (prev != regions.end()) {
2902 if ((*prev)->locked()) {
2907 if (region->position() != (*prev)->last_frame() + 1) {
2908 /* they didn't used to touch, so after shuffle,
2909 just have them swap positions.
2911 new_pos = region->position();
2913 /* they used to touch, so after shuffle,
2914 make sure they still do. put the earlier
2915 one where the later one will end after
2917 new_pos = (*prev)->position() + region->length();
2920 region->set_position ((*prev)->position());
2921 (*prev)->set_position (new_pos);
2923 /* avoid a full sort */
2925 regions.erase (i); // remove region
2926 regions.insert (prev, region); // insert region before prev
2942 check_dependents (region, false);
2944 notify_contents_changed();
2950 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2952 RegionLock rlock (const_cast<Playlist*> (this));
2954 if (regions.size() > 1) {
2962 Playlist::update_after_tempo_map_change ()
2964 RegionLock rlock (const_cast<Playlist*> (this));
2965 RegionList copy (regions.rlist());
2969 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2970 (*i)->update_position_after_tempo_map_change ();
2977 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2979 RegionLock rl (this, false);
2980 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2986 Playlist::set_explicit_relayering (bool e)
2988 if (e == false && _explicit_relayering == true) {
2990 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2991 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2992 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2993 at this point would keep regions on the same layers.
2995 From then on in, it's just you and your towel.
2998 RegionLock rl (this);
2999 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
3000 (*i)->set_last_layer_op ((*i)->layer ());
3004 _explicit_relayering = e;
3009 Playlist::has_region_at (framepos_t const p) const
3011 RegionLock (const_cast<Playlist *> (this));
3013 RegionList::const_iterator i = regions.begin ();
3014 while (i != regions.end() && !(*i)->covers (p)) {
3018 return (i != regions.end());
3021 /** Remove any region that uses a given source */
3023 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
3025 RegionLock rl (this);
3027 RegionList::iterator i = regions.begin();
3028 while (i != regions.end()) {
3029 RegionList::iterator j = i;
3032 if ((*i)->uses_source (s)) {
3033 remove_region_internal (*i);
3040 /** Look from a session frame time and find the start time of the next region
3041 * which is on the top layer of this playlist.
3042 * @param t Time to look from.
3043 * @return Position of next top-layered region, or max_framepos if there isn't one.
3046 Playlist::find_next_top_layer_position (framepos_t t) const
3048 RegionLock rlock (const_cast<Playlist *> (this));
3050 layer_t const top = top_layer ();
3052 RegionList copy = regions.rlist ();
3053 copy.sort (RegionSortByPosition ());
3055 for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3056 if ((*i)->position() >= t && (*i)->layer() == top) {
3057 return (*i)->position();
3061 return max_framepos;
3064 boost::shared_ptr<Region>
3065 Playlist::combine (const RegionList& r)
3068 uint32_t channels = 0;
3070 framepos_t earliest_position = max_framepos;
3071 vector<TwoRegions> old_and_new_regions;
3072 vector<boost::shared_ptr<Region> > originals;
3073 vector<boost::shared_ptr<Region> > copies;
3076 uint32_t max_level = 0;
3078 /* find the maximum depth of all the regions we're combining */
3080 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3081 max_level = max (max_level, (*i)->max_source_level());
3084 parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3085 child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3087 boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3089 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3090 earliest_position = min (earliest_position, (*i)->position());
3093 /* enable this so that we do not try to create xfades etc. as we add
3097 pl->in_partition = true;
3099 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3101 /* copy the region */
3103 boost::shared_ptr<Region> original_region = (*i);
3104 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3106 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3107 originals.push_back (original_region);
3108 copies.push_back (copied_region);
3110 RegionFactory::add_compound_association (original_region, copied_region);
3112 /* make position relative to zero */
3114 pl->add_region (copied_region, original_region->position() - earliest_position);
3116 /* use the maximum number of channels for any region */
3118 channels = max (channels, original_region->n_channels());
3120 /* it will go above the layer of the highest existing region */
3122 layer = max (layer, original_region->layer());
3125 pl->in_partition = false;
3127 pre_combine (copies);
3129 /* add any dependent regions to the new playlist */
3131 copy_dependents (old_and_new_regions, pl.get());
3133 /* now create a new PlaylistSource for each channel in the new playlist */
3136 pair<framepos_t,framepos_t> extent = pl->get_extent();
3138 for (uint32_t chn = 0; chn < channels; ++chn) {
3139 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3143 /* now a new whole-file region using the list of sources */
3145 plist.add (Properties::start, 0);
3146 plist.add (Properties::length, extent.second);
3147 plist.add (Properties::name, parent_name);
3148 plist.add (Properties::whole_file, true);
3150 boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3152 /* now the non-whole-file region that we will actually use in the
3157 plist.add (Properties::start, 0);
3158 plist.add (Properties::length, extent.second);
3159 plist.add (Properties::name, child_name);
3160 plist.add (Properties::layer, layer+1);
3162 boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3164 /* remove all the selected regions from the current playlist
3169 for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3173 /* do type-specific stuff with the originals and the new compound
3177 post_combine (originals, compound_region);
3179 /* add the new region at the right location */
3181 add_region (compound_region, earliest_position);
3187 return compound_region;
3191 Playlist::uncombine (boost::shared_ptr<Region> target)
3193 boost::shared_ptr<PlaylistSource> pls;
3194 boost::shared_ptr<const Playlist> pl;
3195 vector<boost::shared_ptr<Region> > originals;
3197 // (1) check that its really a compound region
3199 if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3203 pl = pls->playlist();
3205 framepos_t adjusted_start = 0; // gcc isn't smart enough
3206 framepos_t adjusted_end = 0; // gcc isn't smart enough
3208 /* the leftmost (earliest) edge of the compound region
3209 starts at zero in its source, or larger if it
3210 has been trimmed or content-scrolled.
3212 the rightmost (latest) edge of the compound region
3213 relative to its source is the starting point plus
3214 the length of the region.
3217 // (2) get all the original regions
3219 const RegionList& rl (pl->region_list().rlist());
3220 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3221 frameoffset_t move_offset = 0;
3223 /* there are two possibilities here:
3224 1) the playlist that the playlist source was based on
3225 is us, so just add the originals (which belonged to
3226 us anyway) back in the right place.
3228 2) the playlist that the playlist source was based on
3229 is NOT us, so we need to make copies of each of
3230 the original regions that we find, and add them
3233 bool same_playlist = (pls->original() == id());
3235 for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3237 boost::shared_ptr<Region> current (*i);
3239 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3241 if (ca == cassocs.end()) {
3245 boost::shared_ptr<Region> original (ca->second);
3246 bool modified_region;
3248 if (i == rl.begin()) {
3249 move_offset = (target->position() - original->position()) - target->start();
3250 adjusted_start = original->position() + target->start();
3251 adjusted_end = adjusted_start + target->length();
3254 if (!same_playlist) {
3255 framepos_t pos = original->position();
3256 /* make a copy, but don't announce it */
3257 original = RegionFactory::create (original, false);
3258 /* the pure copy constructor resets position() to zero,
3261 original->set_position (pos);
3264 /* check to see how the original region (in the
3265 * playlist before compounding occured) overlaps
3266 * with the new state of the compound region.
3269 original->clear_changes ();
3270 modified_region = false;
3272 switch (original->coverage (adjusted_start, adjusted_end)) {
3274 /* original region does not cover any part
3275 of the current state of the compound region
3279 case OverlapInternal:
3280 /* overlap is just a small piece inside the
3281 * original so trim both ends
3283 original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3284 modified_region = true;
3287 case OverlapExternal:
3288 /* overlap fully covers original, so leave it
3294 /* overlap starts within but covers end,
3295 so trim the front of the region
3297 original->trim_front (adjusted_start);
3298 modified_region = true;
3302 /* overlap covers start but ends within, so
3303 * trim the end of the region.
3305 original->trim_end (adjusted_end);
3306 modified_region = true;
3311 /* fix the position to match any movement of the compound region.
3313 original->set_position (original->position() + move_offset);
3314 modified_region = true;
3317 if (modified_region) {
3318 _session.add_command (new StatefulDiffCommand (original));
3321 /* and add to the list of regions waiting to be
3325 originals.push_back (original);
3328 pre_uncombine (originals, target);
3330 in_partition = true;
3333 // (3) remove the compound region
3335 remove_region (target);
3337 // (4) add the constituent regions
3339 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3340 add_region ((*i), (*i)->position());
3343 in_partition = false;
3348 Playlist::max_source_level () const
3350 RegionLock rlock (const_cast<Playlist *> (this));
3353 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3354 lvl = max (lvl, (*i)->max_source_level());
3362 Playlist::count_joined_regions () const
3364 RegionLock rlock (const_cast<Playlist *> (this));
3367 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3368 if ((*i)->max_source_level() > 0) {