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.
20 #define __STDC_LIMIT_MACROS
31 #include <boost/lexical_cast.hpp>
33 #include "pbd/failed_constructor.h"
34 #include "pbd/stateful_diff_command.h"
35 #include "pbd/xml++.h"
37 #include "ardour/debug.h"
38 #include "ardour/playlist.h"
39 #include "ardour/session.h"
40 #include "ardour/region.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/playlist_factory.h"
43 #include "ardour/transient_detector.h"
44 #include "ardour/session_playlists.h"
49 using namespace ARDOUR;
53 namespace Properties {
54 PBD::PropertyDescriptor<bool> regions;
58 struct ShowMeTheList {
59 ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
61 cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
63 boost::shared_ptr<Playlist> playlist;
67 struct RegionSortByLayer {
68 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
69 return a->layer() < b->layer();
73 struct RegionSortByLayerWithPending {
74 bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
76 double p = a->layer ();
77 if (a->pending_explicit_relayer()) {
81 double q = b->layer ();
82 if (b->pending_explicit_relayer()) {
90 struct RegionSortByPosition {
91 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
92 return a->position() < b->position();
96 struct RegionSortByLastLayerOp {
97 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
98 return a->last_layer_op() < b->last_layer_op();
103 Playlist::make_property_quarks ()
105 Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
106 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n", Properties::regions.property_id));
109 RegionListProperty::RegionListProperty (Playlist& pl)
110 : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
115 boost::shared_ptr<Region>
116 RegionListProperty::lookup_id (const ID& id)
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 SequenceProperty<std::list<boost::shared_ptr<Region> > >* RegionListProperty::create () const
129 return new RegionListProperty (_playlist);
132 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
133 : SessionObject(sess, nom)
138 first_set_state = false;
144 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
145 : SessionObject(sess, "unnamed playlist")
151 const XMLProperty* prop = node.property("type");
152 assert(!prop || DataType(prop->value()) == _type);
156 _name = "unnamed"; /* reset by set_state */
159 /* set state called by derived class */
162 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
163 : SessionObject(other->_session, namestr)
165 , _type(other->_type)
166 , _orig_diskstream_id (other->_orig_diskstream_id)
171 other->copy_regions (tmp);
175 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
176 add_region_internal( (*x), (*x)->position());
181 _splicing = other->_splicing;
182 _nudging = other->_nudging;
183 _edit_mode = other->_edit_mode;
186 first_set_state = false;
188 in_partition = false;
190 _read_data_count = 0;
191 _frozen = other->_frozen;
193 layer_op_counter = other->layer_op_counter;
194 freeze_length = other->freeze_length;
197 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
198 : SessionObject(other->_session, str)
200 , _type(other->_type)
201 , _orig_diskstream_id (other->_orig_diskstream_id)
203 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
205 framepos_t end = start + cnt - 1;
211 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
213 boost::shared_ptr<Region> region;
214 boost::shared_ptr<Region> new_region;
215 frameoffset_t offset = 0;
216 framepos_t position = 0;
223 overlap = region->coverage (start, end);
229 case OverlapInternal:
230 offset = start - region->position();
237 position = region->position() - start;
238 len = end - region->position();
242 offset = start - region->position();
244 len = region->length() - offset;
247 case OverlapExternal:
249 position = region->position() - start;
250 len = region->length();
254 RegionFactory::region_name (new_name, region->name(), false);
258 plist.add (Properties::start, region->start() + offset);
259 plist.add (Properties::length, len);
260 plist.add (Properties::name, new_name);
261 plist.add (Properties::layer, region->layer());
263 new_region = RegionFactory::RegionFactory::create (region, plist);
265 add_region_internal (new_region, position);
269 first_set_state = false;
271 /* this constructor does NOT notify others (session) */
278 InUse (true); /* EMIT SIGNAL */
289 InUse (false); /* EMIT SIGNAL */
294 Playlist::copy_regions (RegionList& newlist) const
296 RegionLock rlock (const_cast<Playlist *> (this));
298 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
299 newlist.push_back (RegionFactory::RegionFactory::create (*i));
304 Playlist::init (bool hide)
306 add_property (regions);
307 _xml_node_name = X_("Playlist");
309 g_atomic_int_set (&block_notifications, 0);
310 g_atomic_int_set (&ignore_state_changes, 0);
311 pending_contents_change = false;
312 pending_length = false;
313 pending_layering = false;
314 first_set_state = true;
322 _edit_mode = Config->get_edit_mode();
324 in_partition = false;
326 _read_data_count = 0;
328 layer_op_counter = 0;
330 _explicit_relayering = false;
332 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
333 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
335 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
338 Playlist::~Playlist ()
340 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
343 RegionLock rl (this);
345 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
346 (*i)->set_playlist (boost::shared_ptr<Playlist>());
350 /* GoingAway must be emitted by derived classes */
354 Playlist::_set_sort_id ()
357 Playlists are given names like <track name>.<id>
358 or <track name>.<edit group name>.<id> where id
359 is an integer. We extract the id and sort by that.
362 size_t dot_position = _name.val().find_last_of(".");
364 if (dot_position == string::npos) {
367 string t = _name.val().substr(dot_position + 1);
370 _sort_id = boost::lexical_cast<int>(t);
373 catch (boost::bad_lexical_cast e) {
380 Playlist::set_name (const string& str)
382 /* in a typical situation, a playlist is being used
383 by one diskstream and also is referenced by the
384 Session. if there are more references than that,
385 then don't change the name.
392 bool ret = SessionObject::set_name(str);
399 /***********************************************************************
400 CHANGE NOTIFICATION HANDLING
402 Notifications must be delayed till the region_lock is released. This
403 is necessary because handlers for the signals may need to acquire
404 the lock (e.g. to read from the playlist).
405 ***********************************************************************/
408 Playlist::begin_undo ()
415 Playlist::end_undo ()
424 delay_notifications ();
425 g_atomic_int_inc (&ignore_state_changes);
428 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
430 Playlist::thaw (bool from_undo)
432 g_atomic_int_dec_and_test (&ignore_state_changes);
433 release_notifications (from_undo);
438 Playlist::delay_notifications ()
440 g_atomic_int_inc (&block_notifications);
441 freeze_length = _get_extent().second;
444 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
446 Playlist::release_notifications (bool from_undo)
448 if (g_atomic_int_dec_and_test (&block_notifications)) {
449 flush_notifications (from_undo);
455 Playlist::notify_contents_changed ()
457 if (holding_state ()) {
458 pending_contents_change = true;
460 pending_contents_change = false;
461 ContentsChanged(); /* EMIT SIGNAL */
466 Playlist::notify_layering_changed ()
468 if (holding_state ()) {
469 pending_layering = true;
471 pending_layering = false;
472 LayeringChanged(); /* EMIT SIGNAL */
477 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
479 if (holding_state ()) {
480 pending_removes.insert (r);
481 pending_contents_change = true;
482 pending_length = true;
484 /* this might not be true, but we have to act
485 as though it could be.
487 pending_length = false;
488 LengthChanged (); /* EMIT SIGNAL */
489 pending_contents_change = false;
490 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
491 ContentsChanged (); /* EMIT SIGNAL */
496 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
498 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
500 if (holding_state ()) {
502 pending_range_moves.push_back (move);
506 list< Evoral::RangeMove<framepos_t> > m;
508 RangesMoved (m, false);
514 Playlist::notify_region_added (boost::shared_ptr<Region> r)
516 /* the length change might not be true, but we have to act
517 as though it could be.
520 if (holding_state()) {
521 pending_adds.insert (r);
522 pending_contents_change = true;
523 pending_length = true;
526 pending_length = false;
527 LengthChanged (); /* EMIT SIGNAL */
528 pending_contents_change = false;
529 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
530 ContentsChanged (); /* EMIT SIGNAL */
535 Playlist::notify_length_changed ()
537 if (holding_state ()) {
538 pending_length = true;
540 pending_length = false;
541 LengthChanged(); /* EMIT SIGNAL */
542 pending_contents_change = false;
543 ContentsChanged (); /* EMIT SIGNAL */
547 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
549 Playlist::flush_notifications (bool from_undo)
551 set<boost::shared_ptr<Region> > dependent_checks_needed;
552 set<boost::shared_ptr<Region> >::iterator s;
553 uint32_t regions_changed = false;
554 bool check_length = false;
555 framecnt_t old_length = 0;
563 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
564 regions_changed = true;
565 if (!pending_length) {
566 old_length = _get_extent ().second;
571 /* we have no idea what order the regions ended up in pending
572 bounds (it could be based on selection order, for example).
573 so, to preserve layering in the "most recently moved is higher"
574 model, sort them by existing layer, then timestamp them.
577 // RegionSortByLayer cmp;
578 // pending_bounds.sort (cmp);
580 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
581 if (_session.config.get_layer_model() == MoveAddHigher) {
582 timestamp_layer_op (*r);
584 dependent_checks_needed.insert (*r);
587 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
588 remove_dependents (*s);
589 // cerr << _name << " sends RegionRemoved\n";
590 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
593 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
594 // cerr << _name << " sends RegionAdded\n";
595 /* don't emit RegionAdded signal until relayering is done,
596 so that the region is fully setup by the time
597 anyone hear's that its been added
599 dependent_checks_needed.insert (*s);
603 if (old_length != _get_extent().second) {
604 pending_length = true;
605 // cerr << _name << " length has changed\n";
609 if (pending_length || (freeze_length != _get_extent().second)) {
610 pending_length = false;
611 // cerr << _name << " sends LengthChanged\n";
612 LengthChanged(); /* EMIT SIGNAL */
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_history ();
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);
644 Playlist::clear_pending ()
646 pending_adds.clear ();
647 pending_removes.clear ();
648 pending_bounds.clear ();
649 pending_range_moves.clear ();
650 pending_contents_change = false;
651 pending_length = false;
654 /*************************************************************
656 *************************************************************/
659 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
661 RegionLock rlock (this);
662 times = fabs (times);
664 int itimes = (int) floor (times);
666 framepos_t pos = position;
668 if (times == 1 && auto_partition){
669 partition(pos - 1, (pos + region->length()), true);
673 add_region_internal (region, pos);
674 pos += region->length();
679 /* note that itimes can be zero if we being asked to just
680 insert a single fraction of the region.
683 for (int i = 0; i < itimes; ++i) {
684 boost::shared_ptr<Region> copy = RegionFactory::create (region);
685 add_region_internal (copy, pos);
686 pos += region->length();
689 framecnt_t length = 0;
691 if (floor (times) != times) {
692 length = (framecnt_t) floor (region->length() * (times - floor (times)));
694 RegionFactory::region_name (name, region->name(), false);
699 plist.add (Properties::start, region->start());
700 plist.add (Properties::length, length);
701 plist.add (Properties::name, name);
702 plist.add (Properties::layer, region->layer());
704 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
705 add_region_internal (sub, pos);
709 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
713 Playlist::set_region_ownership ()
715 RegionLock rl (this);
716 RegionList::iterator i;
717 boost::weak_ptr<Playlist> pl (shared_from_this());
719 for (i = regions.begin(); i != regions.end(); ++i) {
720 (*i)->set_playlist (pl);
725 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
727 if (region->data_type() != _type){
731 RegionSortByPosition cmp;
733 framecnt_t old_length = 0;
735 if (!holding_state()) {
736 old_length = _get_extent().second;
739 if (!first_set_state) {
740 boost::shared_ptr<Playlist> foo (shared_from_this());
741 region->set_playlist (boost::weak_ptr<Playlist>(foo));
744 region->set_position (position, this);
746 timestamp_layer_op (region);
748 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
749 all_regions.insert (region);
751 possibly_splice_unlocked (position, region->length(), region);
753 if (!holding_state ()) {
754 /* layers get assigned from XML state, and are not reset during undo/redo */
758 /* we need to notify the existence of new region before checking dependents. Ick. */
760 notify_region_added (region);
762 if (!holding_state ()) {
764 check_dependents (region, false);
766 if (old_length != _get_extent().second) {
767 notify_length_changed ();
771 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
777 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
779 RegionLock rlock (this);
781 bool old_sp = _splicing;
784 remove_region_internal (old);
785 add_region_internal (newr, pos);
789 possibly_splice_unlocked (pos, old->length() - newr->length());
793 Playlist::remove_region (boost::shared_ptr<Region> region)
795 RegionLock rlock (this);
796 remove_region_internal (region);
800 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
802 RegionList::iterator i;
803 framecnt_t old_length = 0;
806 if (!holding_state()) {
807 old_length = _get_extent().second;
812 region->set_playlist (boost::weak_ptr<Playlist>());
815 /* XXX should probably freeze here .... */
817 for (i = regions.begin(); i != regions.end(); ++i) {
820 framepos_t pos = (*i)->position();
821 framecnt_t distance = (*i)->length();
825 possibly_splice_unlocked (pos, -distance);
827 if (!holding_state ()) {
829 remove_dependents (region);
831 if (old_length != _get_extent().second) {
832 notify_length_changed ();
836 notify_region_removed (region);
846 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
848 if (Config->get_use_overlap_equivalency()) {
849 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
850 if ((*i)->overlap_equivalent (other)) {
851 results.push_back ((*i));
855 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
856 if ((*i)->equivalent (other)) {
857 results.push_back ((*i));
864 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
866 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
868 if ((*i) && (*i)->region_list_equivalent (other)) {
869 results.push_back (*i);
875 Playlist::partition (framepos_t start, framepos_t end, bool cut)
879 partition_internal (start, end, cut, thawlist);
881 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
882 (*i)->resume_property_changes ();
887 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
889 RegionList new_regions;
892 RegionLock rlock (this);
894 boost::shared_ptr<Region> region;
895 boost::shared_ptr<Region> current;
897 RegionList::iterator tmp;
899 framepos_t pos1, pos2, pos3, pos4;
903 /* need to work from a copy, because otherwise the regions we add during the process
904 get operated on as well.
907 RegionList copy = regions.rlist();
909 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
916 if (current->first_frame() >= start && current->last_frame() < end) {
919 remove_region_internal (current);
925 /* coverage will return OverlapStart if the start coincides
926 with the end point. we do not partition such a region,
927 so catch this special case.
930 if (current->first_frame() >= end) {
934 if ((overlap = current->coverage (start, end)) == OverlapNone) {
938 pos1 = current->position();
941 pos4 = current->last_frame();
943 if (overlap == OverlapInternal) {
944 /* split: we need 3 new regions, the front, middle and end.
945 cut: we need 2 regions, the front and end.
950 ---------------*************************------------
953 ---------------*****++++++++++++++++====------------
955 ---------------*****----------------====------------
960 /* "middle" ++++++ */
962 RegionFactory::region_name (new_name, current->name(), false);
966 plist.add (Properties::start, current->start() + (pos2 - pos1));
967 plist.add (Properties::length, pos3 - pos2);
968 plist.add (Properties::name, new_name);
969 plist.add (Properties::layer, regions.size());
970 plist.add (Properties::automatic, true);
971 plist.add (Properties::left_of_split, true);
972 plist.add (Properties::right_of_split, true);
974 region = RegionFactory::create (current, plist);
975 add_region_internal (region, start);
976 new_regions.push_back (region);
981 RegionFactory::region_name (new_name, current->name(), false);
985 plist.add (Properties::start, current->start() + (pos3 - pos1));
986 plist.add (Properties::length, pos4 - pos3);
987 plist.add (Properties::name, new_name);
988 plist.add (Properties::layer, regions.size());
989 plist.add (Properties::automatic, true);
990 plist.add (Properties::right_of_split, true);
992 region = RegionFactory::create (current, plist);
994 add_region_internal (region, end);
995 new_regions.push_back (region);
999 current->suspend_property_changes ();
1000 thawlist.push_back (current);
1001 current->cut_end (pos2 - 1, this);
1003 } else if (overlap == OverlapEnd) {
1007 ---------------*************************------------
1010 ---------------**************+++++++++++------------
1012 ---------------**************-----------------------
1019 RegionFactory::region_name (new_name, current->name(), false);
1023 plist.add (Properties::start, current->start() + (pos2 - pos1));
1024 plist.add (Properties::length, pos4 - pos2);
1025 plist.add (Properties::name, new_name);
1026 plist.add (Properties::layer, regions.size());
1027 plist.add (Properties::automatic, true);
1028 plist.add (Properties::left_of_split, true);
1030 region = RegionFactory::create (current, plist);
1032 add_region_internal (region, start);
1033 new_regions.push_back (region);
1038 current->suspend_property_changes ();
1039 thawlist.push_back (current);
1040 current->cut_end (pos2 - 1, this);
1042 } else if (overlap == OverlapStart) {
1044 /* split: we need 2 regions: the front and the end.
1045 cut: just trim current to skip the cut area
1050 ---------------*************************------------
1054 ---------------****+++++++++++++++++++++------------
1056 -------------------*********************------------
1062 RegionFactory::region_name (new_name, current->name(), false);
1066 plist.add (Properties::start, current->start());
1067 plist.add (Properties::length, pos3 - pos1);
1068 plist.add (Properties::name, new_name);
1069 plist.add (Properties::layer, regions.size());
1070 plist.add (Properties::automatic, true);
1071 plist.add (Properties::right_of_split, true);
1073 region = RegionFactory::create (current, plist);
1075 add_region_internal (region, pos1);
1076 new_regions.push_back (region);
1081 current->suspend_property_changes ();
1082 thawlist.push_back (current);
1083 current->trim_front (pos3, this);
1084 } else if (overlap == OverlapExternal) {
1086 /* split: no split required.
1087 cut: remove the region.
1092 ---------------*************************------------
1096 ---------------*************************------------
1098 ----------------------------------------------------
1103 remove_region_internal (current);
1106 new_regions.push_back (current);
1110 in_partition = false;
1113 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1114 check_dependents (*i, false);
1118 boost::shared_ptr<Playlist>
1119 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1121 boost::shared_ptr<Playlist> ret;
1122 boost::shared_ptr<Playlist> pl;
1125 if (ranges.empty()) {
1126 return boost::shared_ptr<Playlist>();
1129 start = ranges.front().start;
1131 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1133 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1135 if (i == ranges.begin()) {
1139 /* paste the next section into the nascent playlist,
1140 offset to reflect the start of the first range we
1144 ret->paste (pl, (*i).start - start, 1.0f);
1151 boost::shared_ptr<Playlist>
1152 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1154 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1155 return cut_copy (pmf, ranges, result_is_hidden);
1158 boost::shared_ptr<Playlist>
1159 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1161 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1162 return cut_copy (pmf, ranges, result_is_hidden);
1165 boost::shared_ptr<Playlist>
1166 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1168 boost::shared_ptr<Playlist> the_copy;
1169 RegionList thawlist;
1172 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1173 string new_name = _name;
1177 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1178 return boost::shared_ptr<Playlist>();
1181 partition_internal (start, start+cnt-1, true, thawlist);
1183 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1184 (*i)->resume_property_changes();
1190 boost::shared_ptr<Playlist>
1191 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1195 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1196 string new_name = _name;
1200 cnt = min (_get_extent().second - start, cnt);
1201 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1205 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1207 times = fabs (times);
1210 RegionLock rl1 (this);
1211 RegionLock rl2 (other.get());
1213 framecnt_t const old_length = _get_extent().second;
1215 int itimes = (int) floor (times);
1216 framepos_t pos = position;
1217 framecnt_t const shift = other->_get_extent().second;
1218 layer_t top_layer = regions.size();
1221 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1222 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1224 /* put these new regions on top of all existing ones, but preserve
1225 the ordering they had in the original playlist.
1228 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1229 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1235 /* XXX shall we handle fractional cases at some point? */
1237 if (old_length != _get_extent().second) {
1238 notify_length_changed ();
1249 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1251 times = fabs (times);
1253 RegionLock rl (this);
1254 int itimes = (int) floor (times);
1255 framepos_t pos = position + 1;
1258 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1259 add_region_internal (copy, pos);
1260 pos += region->length();
1263 if (floor (times) != times) {
1264 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1266 RegionFactory::region_name (name, region->name(), false);
1271 plist.add (Properties::start, region->start());
1272 plist.add (Properties::length, length);
1273 plist.add (Properties::name, name);
1275 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1276 add_region_internal (sub, pos);
1282 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1284 RegionLock rlock (this);
1285 RegionList copy (regions.rlist());
1288 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1290 if ((*r)->last_frame() < at) {
1295 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1296 /* intersected region */
1297 if (!move_intersected) {
1302 /* do not move regions glued to music time - that
1303 has to be done separately.
1306 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1307 fixup.push_back (*r);
1311 (*r)->set_position ((*r)->position() + distance, this);
1314 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1315 (*r)->recompute_position_from_lock_style ();
1320 Playlist::split (framepos_t at)
1322 RegionLock rlock (this);
1323 RegionList copy (regions.rlist());
1325 /* use a copy since this operation can modify the region list
1328 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1329 _split_region (*r, at);
1334 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1336 RegionLock rl (this);
1337 _split_region (region, playlist_position);
1341 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1343 if (!region->covers (playlist_position)) {
1347 if (region->position() == playlist_position ||
1348 region->last_frame() == playlist_position) {
1352 boost::shared_ptr<Region> left;
1353 boost::shared_ptr<Region> right;
1354 frameoffset_t before;
1355 frameoffset_t after;
1359 /* split doesn't change anything about length, so don't try to splice */
1361 bool old_sp = _splicing;
1364 before = playlist_position - region->position();
1365 after = region->length() - before;
1367 RegionFactory::region_name (before_name, region->name(), false);
1372 plist.add (Properties::length, before);
1373 plist.add (Properties::name, before_name);
1374 plist.add (Properties::left_of_split, true);
1376 /* note: we must use the version of ::create with an offset here,
1377 since it supplies that offset to the Region constructor, which
1378 is necessary to get audio region gain envelopes right.
1380 left = RegionFactory::create (region, 0, plist);
1383 RegionFactory::region_name (after_name, region->name(), false);
1388 plist.add (Properties::length, after);
1389 plist.add (Properties::name, after_name);
1390 plist.add (Properties::right_of_split, true);
1392 /* same note as above */
1393 right = RegionFactory::create (region, before, plist);
1396 add_region_internal (left, region->position());
1397 add_region_internal (right, region->position() + before);
1399 uint64_t orig_layer_op = region->last_layer_op();
1400 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1401 if ((*i)->last_layer_op() > orig_layer_op) {
1402 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1406 left->set_last_layer_op ( orig_layer_op );
1407 right->set_last_layer_op ( orig_layer_op + 1);
1411 finalize_split_region (region, left, right);
1413 remove_region_internal (region);
1419 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1421 if (_splicing || in_set_state) {
1422 /* don't respond to splicing moves or state setting */
1426 if (_edit_mode == Splice) {
1427 splice_locked (at, distance, exclude);
1432 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1434 if (_splicing || in_set_state) {
1435 /* don't respond to splicing moves or state setting */
1439 if (_edit_mode == Splice) {
1440 splice_unlocked (at, distance, exclude);
1445 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1448 RegionLock rl (this);
1449 core_splice (at, distance, exclude);
1454 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1456 core_splice (at, distance, exclude);
1460 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1464 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1466 if (exclude && (*i) == exclude) {
1470 if ((*i)->position() >= at) {
1471 framepos_t new_pos = (*i)->position() + distance;
1474 } else if (new_pos >= max_frames - (*i)->length()) {
1475 new_pos = max_frames - (*i)->length();
1478 (*i)->set_position (new_pos, this);
1484 notify_length_changed ();
1488 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1490 if (in_set_state || _splicing || _nudging || _shuffling) {
1494 if (what_changed.contains (Properties::position)) {
1496 /* remove it from the list then add it back in
1497 the right place again.
1500 RegionSortByPosition cmp;
1502 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1504 if (i == regions.end()) {
1505 /* the region bounds are being modified but its not currently
1506 in the region list. we will use its bounds correctly when/if
1513 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1516 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1518 frameoffset_t delta = 0;
1520 if (what_changed.contains (Properties::position)) {
1521 delta = region->position() - region->last_position();
1524 if (what_changed.contains (Properties::length)) {
1525 delta += region->length() - region->last_length();
1529 possibly_splice (region->last_position() + region->last_length(), delta, region);
1532 if (holding_state ()) {
1533 pending_bounds.push_back (region);
1535 if (_session.config.get_layer_model() == MoveAddHigher) {
1536 /* it moved or changed length, so change the timestamp */
1537 timestamp_layer_op (region);
1540 notify_length_changed ();
1542 check_dependents (region, false);
1548 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1550 boost::shared_ptr<Region> region (weak_region.lock());
1556 /* this makes a virtual call to the right kind of playlist ... */
1558 region_changed (what_changed, region);
1562 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1564 PropertyChange our_interests;
1565 PropertyChange bounds;
1566 PropertyChange pos_and_length;
1569 if (in_set_state || in_flush) {
1573 our_interests.add (Properties::muted);
1574 our_interests.add (Properties::layer);
1575 our_interests.add (Properties::opaque);
1577 bounds.add (Properties::start);
1578 bounds.add (Properties::position);
1579 bounds.add (Properties::length);
1581 pos_and_length.add (Properties::position);
1582 pos_and_length.add (Properties::length);
1584 if (what_changed.contains (bounds)) {
1585 region_bounds_changed (what_changed, region);
1586 save = !(_splicing || _nudging);
1589 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1590 check_dependents (region, false);
1593 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1594 notify_region_moved (region);
1598 /* don't notify about layer changes, since we are the only object that can initiate
1599 them, and we notify in ::relayer()
1602 if (what_changed.contains (our_interests)) {
1610 Playlist::drop_regions ()
1612 RegionLock rl (this);
1614 all_regions.clear ();
1618 Playlist::clear (bool with_signals)
1621 RegionLock rl (this);
1623 region_state_changed_connections.drop_connections ();
1625 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1626 pending_removes.insert (*i);
1631 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1632 remove_dependents (*s);
1638 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1639 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1642 pending_removes.clear ();
1643 pending_length = false;
1645 pending_contents_change = false;
1651 /***********************************************************************
1653 **********************************************************************/
1655 Playlist::RegionList *
1656 Playlist::regions_at (framepos_t frame)
1659 RegionLock rlock (this);
1660 return find_regions_at (frame);
1663 boost::shared_ptr<Region>
1664 Playlist::top_region_at (framepos_t frame)
1667 RegionLock rlock (this);
1668 RegionList *rlist = find_regions_at (frame);
1669 boost::shared_ptr<Region> region;
1671 if (rlist->size()) {
1672 RegionSortByLayer cmp;
1674 region = rlist->back();
1681 boost::shared_ptr<Region>
1682 Playlist::top_unmuted_region_at (framepos_t frame)
1685 RegionLock rlock (this);
1686 RegionList *rlist = find_regions_at (frame);
1688 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1690 RegionList::iterator tmp = i;
1693 if ((*i)->muted()) {
1700 boost::shared_ptr<Region> region;
1702 if (rlist->size()) {
1703 RegionSortByLayer cmp;
1705 region = rlist->back();
1712 Playlist::RegionList*
1713 Playlist::regions_to_read (framepos_t start, framepos_t end)
1715 /* Caller must hold lock */
1717 RegionList covering;
1718 set<framepos_t> to_check;
1719 set<boost::shared_ptr<Region> > unique;
1721 to_check.insert (start);
1722 to_check.insert (end);
1724 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1726 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1728 /* find all/any regions that span start+end */
1730 switch ((*i)->coverage (start, end)) {
1734 case OverlapInternal:
1735 covering.push_back (*i);
1736 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1740 to_check.insert ((*i)->position());
1741 if ((*i)->position() != 0) {
1742 to_check.insert ((*i)->position()-1);
1744 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1745 covering.push_back (*i);
1749 to_check.insert ((*i)->last_frame());
1750 to_check.insert ((*i)->last_frame()+1);
1751 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1752 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1753 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1754 covering.push_back (*i);
1757 case OverlapExternal:
1758 covering.push_back (*i);
1759 to_check.insert ((*i)->position());
1760 if ((*i)->position() != 0) {
1761 to_check.insert ((*i)->position()-1);
1763 to_check.insert ((*i)->last_frame());
1764 to_check.insert ((*i)->last_frame()+1);
1765 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1766 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1767 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1771 /* don't go too far */
1773 if ((*i)->position() > end) {
1778 RegionList* rlist = new RegionList;
1780 /* find all the regions that cover each position .... */
1782 if (covering.size() == 1) {
1784 rlist->push_back (covering.front());
1785 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1790 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1794 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1796 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1798 if ((*x)->covers (*t)) {
1799 here.push_back (*x);
1800 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1804 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1811 RegionSortByLayer cmp;
1814 /* ... and get the top/transparent regions at "here" */
1816 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1820 if ((*c)->opaque()) {
1822 /* the other regions at this position are hidden by this one */
1823 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1830 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1831 rlist->push_back (*s);
1834 if (rlist->size() > 1) {
1835 /* now sort by time order */
1837 RegionSortByPosition cmp;
1842 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1847 Playlist::RegionList *
1848 Playlist::find_regions_at (framepos_t frame)
1850 /* Caller must hold lock */
1852 RegionList *rlist = new RegionList;
1854 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1855 if ((*i)->covers (frame)) {
1856 rlist->push_back (*i);
1863 Playlist::RegionList *
1864 Playlist::regions_touched (framepos_t start, framepos_t end)
1866 RegionLock rlock (this);
1867 RegionList *rlist = new RegionList;
1869 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1870 if ((*i)->coverage (start, end) != OverlapNone) {
1871 rlist->push_back (*i);
1879 Playlist::find_next_transient (framepos_t from, int dir)
1881 RegionLock rlock (this);
1882 AnalysisFeatureList points;
1883 AnalysisFeatureList these_points;
1885 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1887 if ((*i)->last_frame() < from) {
1891 if ((*i)->first_frame() > from) {
1896 (*i)->get_transients (these_points);
1898 /* add first frame, just, err, because */
1900 these_points.push_back ((*i)->first_frame());
1902 points.insert (points.end(), these_points.begin(), these_points.end());
1903 these_points.clear ();
1906 if (points.empty()) {
1910 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1911 bool reached = false;
1914 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1919 if (reached && (*x) > from) {
1924 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1929 if (reached && (*x) < from) {
1938 boost::shared_ptr<Region>
1939 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1941 RegionLock rlock (this);
1942 boost::shared_ptr<Region> ret;
1943 framepos_t closest = max_frames;
1945 bool end_iter = false;
1947 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1951 frameoffset_t distance;
1952 boost::shared_ptr<Region> r = (*i);
1957 pos = r->first_frame ();
1960 pos = r->last_frame ();
1963 pos = r->sync_position ();
1964 // r->adjust_to_sync (r->first_frame());
1969 case 1: /* forwards */
1972 if ((distance = pos - frame) < closest) {
1981 default: /* backwards */
1984 if ((distance = frame - pos) < closest) {
2001 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2003 RegionLock rlock (this);
2005 framepos_t closest = max_frames;
2006 framepos_t ret = -1;
2010 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2012 boost::shared_ptr<Region> r = (*i);
2013 frameoffset_t distance;
2015 if (r->first_frame() > frame) {
2017 distance = r->first_frame() - frame;
2019 if (distance < closest) {
2020 ret = r->first_frame();
2025 if (r->last_frame () > frame) {
2027 distance = r->last_frame () - frame;
2029 if (distance < closest) {
2030 ret = r->last_frame ();
2038 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2040 boost::shared_ptr<Region> r = (*i);
2041 frameoffset_t distance;
2043 if (r->last_frame() < frame) {
2045 distance = frame - r->last_frame();
2047 if (distance < closest) {
2048 ret = r->last_frame();
2053 if (r->first_frame() < frame) {
2055 distance = frame - r->first_frame();
2057 if (distance < closest) {
2058 ret = r->first_frame();
2069 /***********************************************************************/
2075 Playlist::mark_session_dirty ()
2077 if (!in_set_state && !holding_state ()) {
2078 _session.set_dirty();
2083 Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
2085 RegionLock rlock (const_cast<Playlist *> (this));
2086 Stateful::rdiff (cmds);
2090 Playlist::clear_owned_history ()
2092 RegionLock rlock (this);
2093 Stateful::clear_owned_history ();
2097 Playlist::update (const RegionListProperty::ChangeRecord& change)
2099 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2100 name(), change.added.size(), change.removed.size()));
2103 /* add the added regions */
2104 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2105 add_region ((*i), (*i)->position());
2107 /* remove the removed regions */
2108 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2116 Playlist::set_state (const XMLNode& node, int version)
2120 XMLNodeConstIterator niter;
2121 XMLPropertyList plist;
2122 XMLPropertyConstIterator piter;
2124 boost::shared_ptr<Region> region;
2129 if (node.name() != "Playlist") {
2136 plist = node.properties();
2138 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2142 if (prop->name() == X_("name")) {
2143 _name = prop->value();
2145 } else if (prop->name() == X_("id")) {
2146 _id = prop->value();
2147 } else if (prop->name() == X_("orig_diskstream_id")) {
2148 _orig_diskstream_id = prop->value ();
2149 } else if (prop->name() == X_("frozen")) {
2150 _frozen = string_is_affirmative (prop->value());
2156 nlist = node.children();
2158 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2162 if (child->name() == "Region") {
2164 if ((prop = child->property ("id")) == 0) {
2165 error << _("region state node has no ID, ignored") << endmsg;
2169 ID id = prop->value ();
2171 if ((region = region_by_id (id))) {
2173 region->suspend_property_changes ();
2175 if (region->set_state (*child, version)) {
2176 region->resume_property_changes ();
2180 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2181 region->suspend_property_changes ();
2183 error << _("Playlist: cannot create region from XML") << endmsg;
2188 add_region (region, region->position(), 1.0);
2190 // So that layer_op ordering doesn't get screwed up
2191 region->set_last_layer_op( region->layer());
2192 region->resume_property_changes ();
2196 /* update dependents, which was not done during add_region_internal
2197 due to in_set_state being true
2200 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2201 check_dependents (*r, false);
2205 notify_contents_changed ();
2208 first_set_state = false;
2213 Playlist::get_state()
2215 return state (true);
2219 Playlist::get_template()
2221 return state (false);
2224 /** @param full_state true to include regions in the returned state, otherwise false.
2227 Playlist::state (bool full_state)
2229 XMLNode *node = new XMLNode (X_("Playlist"));
2232 node->add_property (X_("id"), id().to_s());
2233 node->add_property (X_("name"), _name);
2234 node->add_property (X_("type"), _type.to_string());
2236 _orig_diskstream_id.print (buf, sizeof (buf));
2237 node->add_property (X_("orig_diskstream_id"), buf);
2238 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2241 RegionLock rlock (this, false);
2243 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2244 node->add_child_nocopy ((*i)->get_state());
2249 node->add_child_copy (*_extra_xml);
2256 Playlist::empty() const
2258 RegionLock rlock (const_cast<Playlist *>(this), false);
2259 return regions.empty();
2263 Playlist::n_regions() const
2265 RegionLock rlock (const_cast<Playlist *>(this), false);
2266 return regions.size();
2269 pair<framecnt_t, framecnt_t>
2270 Playlist::get_extent () const
2272 RegionLock rlock (const_cast<Playlist *>(this), false);
2273 return _get_extent ();
2276 pair<framecnt_t, framecnt_t>
2277 Playlist::_get_extent () const
2279 pair<framecnt_t, framecnt_t> ext (max_frames, 0);
2281 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2282 pair<framecnt_t, framecnt_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2283 if (e.first < ext.first) {
2284 ext.first = e.first;
2286 if (e.second > ext.second) {
2287 ext.second = e.second;
2295 Playlist::bump_name (string name, Session &session)
2297 string newname = name;
2300 newname = bump_name_once (newname, '.');
2301 } while (session.playlists->by_name (newname)!=NULL);
2308 Playlist::top_layer() const
2310 RegionLock rlock (const_cast<Playlist *> (this));
2313 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2314 top = max (top, (*i)->layer());
2320 Playlist::set_edit_mode (EditMode mode)
2325 /********************
2327 ********************/
2330 Playlist::relayer ()
2332 /* never compute layers when changing state for undo/redo or setting from XML */
2334 if (in_update || in_set_state) {
2338 bool changed = false;
2340 /* Build up a new list of regions on each layer, stored in a set of lists
2341 each of which represent some period of time on some layer. The idea
2342 is to avoid having to search the entire region list to establish whether
2343 each region overlaps another */
2345 /* how many pieces to divide this playlist's time up into */
2346 int const divisions = 512;
2348 /* find the start and end positions of the regions on this playlist */
2349 framepos_t start = INT64_MAX;
2351 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2352 start = min (start, (*i)->position());
2353 end = max (end, (*i)->position() + (*i)->length());
2356 /* hence the size of each time division */
2357 double const division_size = (end - start) / double (divisions);
2359 vector<vector<RegionList> > layers;
2360 layers.push_back (vector<RegionList> (divisions));
2362 /* we want to go through regions from desired lowest to desired highest layer,
2363 which depends on the layer model
2366 RegionList copy = regions.rlist();
2368 /* sort according to the model and the layering mode that we're in */
2370 if (_explicit_relayering) {
2372 copy.sort (RegionSortByLayerWithPending ());
2374 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2376 copy.sort (RegionSortByLastLayerOp ());
2381 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2383 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2384 (*i)->set_pending_explicit_relayer (false);
2386 /* find the time divisions that this region covers; if there are no regions on the list,
2387 division_size will equal 0 and in this case we'll just say that
2388 start_division = end_division = 0.
2390 int start_division = 0;
2391 int end_division = 0;
2393 if (division_size > 0) {
2394 start_division = floor ( ((*i)->position() - start) / division_size);
2395 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2396 if (end_division == divisions) {
2401 assert (divisions == 0 || end_division < divisions);
2403 /* find the lowest layer that this region can go on */
2404 size_t j = layers.size();
2406 /* try layer j - 1; it can go on if it overlaps no other region
2407 that is already on that layer
2410 bool overlap = false;
2411 for (int k = start_division; k <= end_division; ++k) {
2412 RegionList::iterator l = layers[j-1][k].begin ();
2413 while (l != layers[j-1][k].end()) {
2414 if ((*l)->overlap_equivalent (*i)) {
2427 /* overlap, so we must use layer j */
2434 if (j == layers.size()) {
2435 /* we need a new layer for this region */
2436 layers.push_back (vector<RegionList> (divisions));
2439 /* put a reference to this region in each of the divisions that it exists in */
2440 for (int k = start_division; k <= end_division; ++k) {
2441 layers[j][k].push_back (*i);
2444 if ((*i)->layer() != j) {
2448 (*i)->set_layer (j);
2452 notify_layering_changed ();
2456 /* XXX these layer functions are all deprecated */
2459 Playlist::raise_region (boost::shared_ptr<Region> region)
2461 uint32_t rsz = regions.size();
2462 layer_t target = region->layer() + 1U;
2464 if (target >= rsz) {
2465 /* its already at the effective top */
2469 move_region_to_layer (target, region, 1);
2473 Playlist::lower_region (boost::shared_ptr<Region> region)
2475 if (region->layer() == 0) {
2476 /* its already at the bottom */
2480 layer_t target = region->layer() - 1U;
2482 move_region_to_layer (target, region, -1);
2486 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2488 /* does nothing useful if layering mode is later=higher */
2489 switch (_session.config.get_layer_model()) {
2496 layer_t top = regions.size() - 1;
2498 if (region->layer() >= top) {
2499 /* already on the top */
2503 move_region_to_layer (top, region, 1);
2504 /* mark the region's last_layer_op as now, so that it remains on top when
2505 doing future relayers (until something else takes over)
2507 timestamp_layer_op (region);
2511 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2513 /* does nothing useful if layering mode is later=higher */
2514 switch (_session.config.get_layer_model()) {
2521 if (region->layer() == 0) {
2522 /* already on the bottom */
2526 move_region_to_layer (0, region, -1);
2527 /* force region's last layer op to zero so that it stays at the bottom
2528 when doing future relayers
2530 region->set_last_layer_op (0);
2534 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2536 RegionList::iterator i;
2537 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2538 list<LayerInfo> layerinfo;
2541 RegionLock rlock (const_cast<Playlist *> (this));
2543 for (i = regions.begin(); i != regions.end(); ++i) {
2553 /* region is moving up, move all regions on intermediate layers
2557 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2558 dest = (*i)->layer() - 1;
2565 /* region is moving down, move all regions on intermediate layers
2569 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2570 dest = (*i)->layer() + 1;
2580 newpair.second = dest;
2582 layerinfo.push_back (newpair);
2586 /* now reset the layers without holding the region lock */
2588 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2589 x->first->set_layer (x->second);
2592 region->set_layer (target_layer);
2595 /* now check all dependents */
2597 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2598 check_dependents (x->first, false);
2601 check_dependents (region, false);
2608 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2610 RegionList::iterator i;
2616 RegionLock rlock (const_cast<Playlist *> (this));
2618 for (i = regions.begin(); i != regions.end(); ++i) {
2620 if ((*i)->position() >= start) {
2626 if ((*i)->last_frame() > max_frames - distance) {
2627 new_pos = max_frames - (*i)->length();
2629 new_pos = (*i)->position() + distance;
2634 if ((*i)->position() > distance) {
2635 new_pos = (*i)->position() - distance;
2641 (*i)->set_position (new_pos, this);
2649 notify_length_changed ();
2654 boost::shared_ptr<Region>
2655 Playlist::find_region (const ID& id) const
2657 RegionLock rlock (const_cast<Playlist*> (this));
2659 /* searches all regions currently in use by the playlist */
2661 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2662 if ((*i)->id() == id) {
2667 return boost::shared_ptr<Region> ();
2670 boost::shared_ptr<Region>
2671 Playlist::region_by_id (const ID& id)
2673 /* searches all regions ever added to this playlist */
2675 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2676 if ((*i)->id() == id) {
2680 return boost::shared_ptr<Region> ();
2684 Playlist::dump () const
2686 boost::shared_ptr<Region> r;
2688 cerr << "Playlist \"" << _name << "\" " << endl
2689 << regions.size() << " regions "
2692 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2694 cerr << " " << r->name() << " ["
2695 << r->start() << "+" << r->length()
2705 Playlist::set_frozen (bool yn)
2711 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2713 region->set_last_layer_op (++layer_op_counter);
2718 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2722 if (region->locked()) {
2729 RegionLock rlock (const_cast<Playlist*> (this));
2734 RegionList::iterator next;
2736 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2737 if ((*i) == region) {
2741 if (next != regions.end()) {
2743 if ((*next)->locked()) {
2749 if ((*next)->position() != region->last_frame() + 1) {
2750 /* they didn't used to touch, so after shuffle,
2751 just have them swap positions.
2753 new_pos = (*next)->position();
2755 /* they used to touch, so after shuffle,
2756 make sure they still do. put the earlier
2757 region where the later one will end after
2760 new_pos = region->position() + (*next)->length();
2763 (*next)->set_position (region->position(), this);
2764 region->set_position (new_pos, this);
2766 /* avoid a full sort */
2768 regions.erase (i); // removes the region from the list */
2770 regions.insert (next, region); // adds it back after next
2779 RegionList::iterator prev = regions.end();
2781 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2782 if ((*i) == region) {
2784 if (prev != regions.end()) {
2786 if ((*prev)->locked()) {
2791 if (region->position() != (*prev)->last_frame() + 1) {
2792 /* they didn't used to touch, so after shuffle,
2793 just have them swap positions.
2795 new_pos = region->position();
2797 /* they used to touch, so after shuffle,
2798 make sure they still do. put the earlier
2799 one where the later one will end after
2801 new_pos = (*prev)->position() + region->length();
2804 region->set_position ((*prev)->position(), this);
2805 (*prev)->set_position (new_pos, this);
2807 /* avoid a full sort */
2809 regions.erase (i); // remove region
2810 regions.insert (prev, region); // insert region before prev
2826 check_dependents (region, false);
2828 notify_contents_changed();
2834 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2836 RegionLock rlock (const_cast<Playlist*> (this));
2838 if (regions.size() > 1) {
2846 Playlist::update_after_tempo_map_change ()
2848 RegionLock rlock (const_cast<Playlist*> (this));
2849 RegionList copy (regions.rlist());
2853 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2854 (*i)->update_position_after_tempo_map_change ();
2861 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2863 RegionLock rl (this, false);
2864 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2870 Playlist::set_explicit_relayering (bool e)
2872 if (e == false && _explicit_relayering == true) {
2874 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2875 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2876 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2877 at this point would keep regions on the same layers.
2879 From then on in, it's just you and your towel.
2882 RegionLock rl (this);
2883 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2884 (*i)->set_last_layer_op ((*i)->layer ());
2888 _explicit_relayering = e;
2893 Playlist::has_region_at (framepos_t const p) const
2895 RegionLock (const_cast<Playlist *> (this));
2897 RegionList::const_iterator i = regions.begin ();
2898 while (i != regions.end() && !(*i)->covers (p)) {
2902 return (i != regions.end());
2905 /** Remove any region that uses a given source */
2907 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2909 RegionLock rl (this);
2911 RegionList::iterator i = regions.begin();
2912 while (i != regions.end()) {
2913 RegionList::iterator j = i;
2916 if ((*i)->uses_source (s)) {
2917 remove_region_internal (*i);