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) const
118 boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
121 ret = RegionFactory::region_by_id (id);
127 RegionListProperty* RegionListProperty::clone () const
129 return new RegionListProperty (*this);
132 RegionListProperty* RegionListProperty::create () const
134 return new RegionListProperty (_playlist);
137 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
138 : SessionObject(sess, nom)
143 first_set_state = false;
149 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
150 : SessionObject(sess, "unnamed playlist")
156 const XMLProperty* prop = node.property("type");
157 assert(!prop || DataType(prop->value()) == _type);
161 _name = "unnamed"; /* reset by set_state */
164 /* set state called by derived class */
167 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
168 : SessionObject(other->_session, namestr)
170 , _type(other->_type)
171 , _orig_diskstream_id (other->_orig_diskstream_id)
176 other->copy_regions (tmp);
180 for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
181 add_region_internal( (*x), (*x)->position());
186 _splicing = other->_splicing;
187 _nudging = other->_nudging;
188 _edit_mode = other->_edit_mode;
191 first_set_state = false;
193 in_partition = false;
195 _read_data_count = 0;
196 _frozen = other->_frozen;
198 layer_op_counter = other->layer_op_counter;
199 freeze_length = other->freeze_length;
202 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
203 : SessionObject(other->_session, str)
205 , _type(other->_type)
206 , _orig_diskstream_id (other->_orig_diskstream_id)
208 RegionLock rlock2 (const_cast<Playlist*> (other.get()));
210 framepos_t end = start + cnt - 1;
216 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
218 boost::shared_ptr<Region> region;
219 boost::shared_ptr<Region> new_region;
220 frameoffset_t offset = 0;
221 framepos_t position = 0;
228 overlap = region->coverage (start, end);
234 case OverlapInternal:
235 offset = start - region->position();
242 position = region->position() - start;
243 len = end - region->position();
247 offset = start - region->position();
249 len = region->length() - offset;
252 case OverlapExternal:
254 position = region->position() - start;
255 len = region->length();
259 RegionFactory::region_name (new_name, region->name(), false);
263 plist.add (Properties::start, region->start() + offset);
264 plist.add (Properties::length, len);
265 plist.add (Properties::name, new_name);
266 plist.add (Properties::layer, region->layer());
268 new_region = RegionFactory::RegionFactory::create (region, plist);
270 add_region_internal (new_region, position);
274 first_set_state = false;
276 /* this constructor does NOT notify others (session) */
283 InUse (true); /* EMIT SIGNAL */
294 InUse (false); /* EMIT SIGNAL */
299 Playlist::copy_regions (RegionList& newlist) const
301 RegionLock rlock (const_cast<Playlist *> (this));
303 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
304 newlist.push_back (RegionFactory::RegionFactory::create (*i));
309 Playlist::init (bool hide)
311 add_property (regions);
312 _xml_node_name = X_("Playlist");
314 g_atomic_int_set (&block_notifications, 0);
315 g_atomic_int_set (&ignore_state_changes, 0);
316 pending_contents_change = false;
317 pending_length = false;
318 pending_layering = false;
319 first_set_state = true;
327 _edit_mode = Config->get_edit_mode();
329 in_partition = false;
331 _read_data_count = 0;
333 layer_op_counter = 0;
335 _explicit_relayering = false;
337 _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
338 _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
340 ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
343 Playlist::~Playlist ()
345 DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
348 RegionLock rl (this);
350 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
351 (*i)->set_playlist (boost::shared_ptr<Playlist>());
355 /* GoingAway must be emitted by derived classes */
359 Playlist::_set_sort_id ()
362 Playlists are given names like <track name>.<id>
363 or <track name>.<edit group name>.<id> where id
364 is an integer. We extract the id and sort by that.
367 size_t dot_position = _name.val().find_last_of(".");
369 if (dot_position == string::npos) {
372 string t = _name.val().substr(dot_position + 1);
375 _sort_id = boost::lexical_cast<int>(t);
378 catch (boost::bad_lexical_cast e) {
385 Playlist::set_name (const string& str)
387 /* in a typical situation, a playlist is being used
388 by one diskstream and also is referenced by the
389 Session. if there are more references than that,
390 then don't change the name.
397 bool ret = SessionObject::set_name(str);
404 /***********************************************************************
405 CHANGE NOTIFICATION HANDLING
407 Notifications must be delayed till the region_lock is released. This
408 is necessary because handlers for the signals may need to acquire
409 the lock (e.g. to read from the playlist).
410 ***********************************************************************/
413 Playlist::begin_undo ()
420 Playlist::end_undo ()
429 delay_notifications ();
430 g_atomic_int_inc (&ignore_state_changes);
433 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
435 Playlist::thaw (bool from_undo)
437 g_atomic_int_dec_and_test (&ignore_state_changes);
438 release_notifications (from_undo);
443 Playlist::delay_notifications ()
445 g_atomic_int_inc (&block_notifications);
446 freeze_length = _get_extent().second;
449 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
451 Playlist::release_notifications (bool from_undo)
453 if (g_atomic_int_dec_and_test (&block_notifications)) {
454 flush_notifications (from_undo);
460 Playlist::notify_contents_changed ()
462 if (holding_state ()) {
463 pending_contents_change = true;
465 pending_contents_change = false;
466 ContentsChanged(); /* EMIT SIGNAL */
471 Playlist::notify_layering_changed ()
473 if (holding_state ()) {
474 pending_layering = true;
476 pending_layering = false;
477 LayeringChanged(); /* EMIT SIGNAL */
482 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
484 if (holding_state ()) {
485 pending_removes.insert (r);
486 pending_contents_change = true;
487 pending_length = true;
489 /* this might not be true, but we have to act
490 as though it could be.
492 pending_length = false;
493 LengthChanged (); /* EMIT SIGNAL */
494 pending_contents_change = false;
495 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
496 ContentsChanged (); /* EMIT SIGNAL */
501 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
503 Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
505 if (holding_state ()) {
507 pending_range_moves.push_back (move);
511 list< Evoral::RangeMove<framepos_t> > m;
513 RangesMoved (m, false);
519 Playlist::notify_region_added (boost::shared_ptr<Region> r)
521 /* the length change might not be true, but we have to act
522 as though it could be.
525 if (holding_state()) {
526 pending_adds.insert (r);
527 pending_contents_change = true;
528 pending_length = true;
531 pending_length = false;
532 LengthChanged (); /* EMIT SIGNAL */
533 pending_contents_change = false;
534 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
535 ContentsChanged (); /* EMIT SIGNAL */
540 Playlist::notify_length_changed ()
542 if (holding_state ()) {
543 pending_length = true;
545 pending_length = false;
546 LengthChanged(); /* EMIT SIGNAL */
547 pending_contents_change = false;
548 ContentsChanged (); /* EMIT SIGNAL */
552 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
554 Playlist::flush_notifications (bool from_undo)
556 set<boost::shared_ptr<Region> > dependent_checks_needed;
557 set<boost::shared_ptr<Region> >::iterator s;
558 uint32_t regions_changed = false;
559 bool check_length = false;
560 framecnt_t old_length = 0;
568 if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
569 regions_changed = true;
570 if (!pending_length) {
571 old_length = _get_extent ().second;
576 /* we have no idea what order the regions ended up in pending
577 bounds (it could be based on selection order, for example).
578 so, to preserve layering in the "most recently moved is higher"
579 model, sort them by existing layer, then timestamp them.
582 // RegionSortByLayer cmp;
583 // pending_bounds.sort (cmp);
585 for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
586 if (_session.config.get_layer_model() == MoveAddHigher) {
587 timestamp_layer_op (*r);
589 dependent_checks_needed.insert (*r);
592 for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
593 remove_dependents (*s);
594 // cerr << _name << " sends RegionRemoved\n";
595 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
598 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
599 // cerr << _name << " sends RegionAdded\n";
600 /* don't emit RegionAdded signal until relayering is done,
601 so that the region is fully setup by the time
602 anyone hear's that its been added
604 dependent_checks_needed.insert (*s);
608 if (old_length != _get_extent().second) {
609 pending_length = true;
610 // cerr << _name << " length has changed\n";
614 if (pending_length || (freeze_length != _get_extent().second)) {
615 pending_length = false;
616 // cerr << _name << " sends LengthChanged\n";
617 LengthChanged(); /* EMIT SIGNAL */
620 if (regions_changed || pending_contents_change) {
624 pending_contents_change = false;
625 // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
626 ContentsChanged (); /* EMIT SIGNAL */
627 // cerr << _name << "done contents change @ " << get_microseconds() << endl;
630 for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
631 (*s)->clear_changes ();
632 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
635 for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
636 check_dependents (*s, false);
639 if (!pending_range_moves.empty ()) {
640 RangesMoved (pending_range_moves, from_undo);
649 Playlist::clear_pending ()
651 pending_adds.clear ();
652 pending_removes.clear ();
653 pending_bounds.clear ();
654 pending_range_moves.clear ();
655 pending_contents_change = false;
656 pending_length = false;
659 /*************************************************************
661 *************************************************************/
664 Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
666 RegionLock rlock (this);
667 times = fabs (times);
669 int itimes = (int) floor (times);
671 framepos_t pos = position;
673 if (times == 1 && auto_partition){
674 partition(pos - 1, (pos + region->length()), true);
678 add_region_internal (region, pos);
679 pos += region->length();
684 /* note that itimes can be zero if we being asked to just
685 insert a single fraction of the region.
688 for (int i = 0; i < itimes; ++i) {
689 boost::shared_ptr<Region> copy = RegionFactory::create (region);
690 add_region_internal (copy, pos);
691 pos += region->length();
694 framecnt_t length = 0;
696 if (floor (times) != times) {
697 length = (framecnt_t) floor (region->length() * (times - floor (times)));
699 RegionFactory::region_name (name, region->name(), false);
704 plist.add (Properties::start, region->start());
705 plist.add (Properties::length, length);
706 plist.add (Properties::name, name);
707 plist.add (Properties::layer, region->layer());
709 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
710 add_region_internal (sub, pos);
714 possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
718 Playlist::set_region_ownership ()
720 RegionLock rl (this);
721 RegionList::iterator i;
722 boost::weak_ptr<Playlist> pl (shared_from_this());
724 for (i = regions.begin(); i != regions.end(); ++i) {
725 (*i)->set_playlist (pl);
730 Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
732 if (region->data_type() != _type){
736 RegionSortByPosition cmp;
738 framecnt_t old_length = 0;
740 if (!holding_state()) {
741 old_length = _get_extent().second;
744 if (!first_set_state) {
745 boost::shared_ptr<Playlist> foo (shared_from_this());
746 region->set_playlist (boost::weak_ptr<Playlist>(foo));
749 region->set_position (position, this);
751 timestamp_layer_op (region);
753 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
754 all_regions.insert (region);
756 possibly_splice_unlocked (position, region->length(), region);
758 if (!holding_state ()) {
759 /* layers get assigned from XML state, and are not reset during undo/redo */
763 /* we need to notify the existence of new region before checking dependents. Ick. */
765 notify_region_added (region);
767 if (!holding_state ()) {
769 check_dependents (region, false);
771 if (old_length != _get_extent().second) {
772 notify_length_changed ();
776 region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
782 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
784 RegionLock rlock (this);
786 bool old_sp = _splicing;
789 remove_region_internal (old);
790 add_region_internal (newr, pos);
794 possibly_splice_unlocked (pos, old->length() - newr->length());
798 Playlist::remove_region (boost::shared_ptr<Region> region)
800 RegionLock rlock (this);
801 remove_region_internal (region);
805 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
807 RegionList::iterator i;
808 framecnt_t old_length = 0;
811 if (!holding_state()) {
812 old_length = _get_extent().second;
817 region->set_playlist (boost::weak_ptr<Playlist>());
820 /* XXX should probably freeze here .... */
822 for (i = regions.begin(); i != regions.end(); ++i) {
825 framepos_t pos = (*i)->position();
826 framecnt_t distance = (*i)->length();
830 possibly_splice_unlocked (pos, -distance);
832 if (!holding_state ()) {
834 remove_dependents (region);
836 if (old_length != _get_extent().second) {
837 notify_length_changed ();
841 notify_region_removed (region);
851 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
853 if (Config->get_use_overlap_equivalency()) {
854 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
855 if ((*i)->overlap_equivalent (other)) {
856 results.push_back ((*i));
860 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
861 if ((*i)->equivalent (other)) {
862 results.push_back ((*i));
869 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
871 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
873 if ((*i) && (*i)->region_list_equivalent (other)) {
874 results.push_back (*i);
880 Playlist::partition (framepos_t start, framepos_t end, bool cut)
884 partition_internal (start, end, cut, thawlist);
886 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
887 (*i)->resume_property_changes ();
892 Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
894 RegionList new_regions;
897 RegionLock rlock (this);
899 boost::shared_ptr<Region> region;
900 boost::shared_ptr<Region> current;
902 RegionList::iterator tmp;
904 framepos_t pos1, pos2, pos3, pos4;
908 /* need to work from a copy, because otherwise the regions we add during the process
909 get operated on as well.
912 RegionList copy = regions.rlist();
914 for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
921 if (current->first_frame() >= start && current->last_frame() < end) {
924 remove_region_internal (current);
930 /* coverage will return OverlapStart if the start coincides
931 with the end point. we do not partition such a region,
932 so catch this special case.
935 if (current->first_frame() >= end) {
939 if ((overlap = current->coverage (start, end)) == OverlapNone) {
943 pos1 = current->position();
946 pos4 = current->last_frame();
948 if (overlap == OverlapInternal) {
949 /* split: we need 3 new regions, the front, middle and end.
950 cut: we need 2 regions, the front and end.
955 ---------------*************************------------
958 ---------------*****++++++++++++++++====------------
960 ---------------*****----------------====------------
965 /* "middle" ++++++ */
967 RegionFactory::region_name (new_name, current->name(), false);
971 plist.add (Properties::start, current->start() + (pos2 - pos1));
972 plist.add (Properties::length, pos3 - pos2);
973 plist.add (Properties::name, new_name);
974 plist.add (Properties::layer, regions.size());
975 plist.add (Properties::automatic, true);
976 plist.add (Properties::left_of_split, true);
977 plist.add (Properties::right_of_split, true);
979 region = RegionFactory::create (current, plist);
980 add_region_internal (region, start);
981 new_regions.push_back (region);
986 RegionFactory::region_name (new_name, current->name(), false);
990 plist.add (Properties::start, current->start() + (pos3 - pos1));
991 plist.add (Properties::length, pos4 - pos3);
992 plist.add (Properties::name, new_name);
993 plist.add (Properties::layer, regions.size());
994 plist.add (Properties::automatic, true);
995 plist.add (Properties::right_of_split, true);
997 region = RegionFactory::create (current, plist);
999 add_region_internal (region, end);
1000 new_regions.push_back (region);
1004 current->suspend_property_changes ();
1005 thawlist.push_back (current);
1006 current->cut_end (pos2 - 1, this);
1008 } else if (overlap == OverlapEnd) {
1012 ---------------*************************------------
1015 ---------------**************+++++++++++------------
1017 ---------------**************-----------------------
1024 RegionFactory::region_name (new_name, current->name(), false);
1028 plist.add (Properties::start, current->start() + (pos2 - pos1));
1029 plist.add (Properties::length, pos4 - pos2);
1030 plist.add (Properties::name, new_name);
1031 plist.add (Properties::layer, regions.size());
1032 plist.add (Properties::automatic, true);
1033 plist.add (Properties::left_of_split, true);
1035 region = RegionFactory::create (current, plist);
1037 add_region_internal (region, start);
1038 new_regions.push_back (region);
1043 current->suspend_property_changes ();
1044 thawlist.push_back (current);
1045 current->cut_end (pos2 - 1, this);
1047 } else if (overlap == OverlapStart) {
1049 /* split: we need 2 regions: the front and the end.
1050 cut: just trim current to skip the cut area
1055 ---------------*************************------------
1059 ---------------****+++++++++++++++++++++------------
1061 -------------------*********************------------
1067 RegionFactory::region_name (new_name, current->name(), false);
1071 plist.add (Properties::start, current->start());
1072 plist.add (Properties::length, pos3 - pos1);
1073 plist.add (Properties::name, new_name);
1074 plist.add (Properties::layer, regions.size());
1075 plist.add (Properties::automatic, true);
1076 plist.add (Properties::right_of_split, true);
1078 region = RegionFactory::create (current, plist);
1080 add_region_internal (region, pos1);
1081 new_regions.push_back (region);
1086 current->suspend_property_changes ();
1087 thawlist.push_back (current);
1088 current->trim_front (pos3, this);
1089 } else if (overlap == OverlapExternal) {
1091 /* split: no split required.
1092 cut: remove the region.
1097 ---------------*************************------------
1101 ---------------*************************------------
1103 ----------------------------------------------------
1108 remove_region_internal (current);
1111 new_regions.push_back (current);
1115 in_partition = false;
1118 for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
1119 check_dependents (*i, false);
1123 boost::shared_ptr<Playlist>
1124 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1126 boost::shared_ptr<Playlist> ret;
1127 boost::shared_ptr<Playlist> pl;
1130 if (ranges.empty()) {
1131 return boost::shared_ptr<Playlist>();
1134 start = ranges.front().start;
1136 for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1138 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1140 if (i == ranges.begin()) {
1144 /* paste the next section into the nascent playlist,
1145 offset to reflect the start of the first range we
1149 ret->paste (pl, (*i).start - start, 1.0f);
1156 boost::shared_ptr<Playlist>
1157 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1159 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1160 return cut_copy (pmf, ranges, result_is_hidden);
1163 boost::shared_ptr<Playlist>
1164 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1166 boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1167 return cut_copy (pmf, ranges, result_is_hidden);
1170 boost::shared_ptr<Playlist>
1171 Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1173 boost::shared_ptr<Playlist> the_copy;
1174 RegionList thawlist;
1177 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1178 string new_name = _name;
1182 if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1183 return boost::shared_ptr<Playlist>();
1186 partition_internal (start, start+cnt-1, true, thawlist);
1188 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1189 (*i)->resume_property_changes();
1195 boost::shared_ptr<Playlist>
1196 Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1200 snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1201 string new_name = _name;
1205 cnt = min (_get_extent().second - start, cnt);
1206 return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1210 Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
1212 times = fabs (times);
1215 RegionLock rl1 (this);
1216 RegionLock rl2 (other.get());
1218 framecnt_t const old_length = _get_extent().second;
1220 int itimes = (int) floor (times);
1221 framepos_t pos = position;
1222 framecnt_t const shift = other->_get_extent().second;
1223 layer_t top_layer = regions.size();
1226 for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1227 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
1229 /* put these new regions on top of all existing ones, but preserve
1230 the ordering they had in the original playlist.
1233 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
1234 add_region_internal (copy_of_region, copy_of_region->position() + pos);
1240 /* XXX shall we handle fractional cases at some point? */
1242 if (old_length != _get_extent().second) {
1243 notify_length_changed ();
1254 Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1256 times = fabs (times);
1258 RegionLock rl (this);
1259 int itimes = (int) floor (times);
1260 framepos_t pos = position + 1;
1263 boost::shared_ptr<Region> copy = RegionFactory::create (region);
1264 add_region_internal (copy, pos);
1265 pos += region->length();
1268 if (floor (times) != times) {
1269 framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1271 RegionFactory::region_name (name, region->name(), false);
1276 plist.add (Properties::start, region->start());
1277 plist.add (Properties::length, length);
1278 plist.add (Properties::name, name);
1280 boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1281 add_region_internal (sub, pos);
1287 Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1289 RegionLock rlock (this);
1290 RegionList copy (regions.rlist());
1293 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1295 if ((*r)->last_frame() < at) {
1300 if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1301 /* intersected region */
1302 if (!move_intersected) {
1307 /* do not move regions glued to music time - that
1308 has to be done separately.
1311 if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1312 fixup.push_back (*r);
1316 (*r)->set_position ((*r)->position() + distance, this);
1319 for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1320 (*r)->recompute_position_from_lock_style ();
1325 Playlist::split (framepos_t at)
1327 RegionLock rlock (this);
1328 RegionList copy (regions.rlist());
1330 /* use a copy since this operation can modify the region list
1333 for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1334 _split_region (*r, at);
1339 Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1341 RegionLock rl (this);
1342 _split_region (region, playlist_position);
1346 Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1348 if (!region->covers (playlist_position)) {
1352 if (region->position() == playlist_position ||
1353 region->last_frame() == playlist_position) {
1357 boost::shared_ptr<Region> left;
1358 boost::shared_ptr<Region> right;
1359 frameoffset_t before;
1360 frameoffset_t after;
1364 /* split doesn't change anything about length, so don't try to splice */
1366 bool old_sp = _splicing;
1369 before = playlist_position - region->position();
1370 after = region->length() - before;
1372 RegionFactory::region_name (before_name, region->name(), false);
1377 plist.add (Properties::length, before);
1378 plist.add (Properties::name, before_name);
1379 plist.add (Properties::left_of_split, true);
1381 /* note: we must use the version of ::create with an offset here,
1382 since it supplies that offset to the Region constructor, which
1383 is necessary to get audio region gain envelopes right.
1385 left = RegionFactory::create (region, 0, plist);
1388 RegionFactory::region_name (after_name, region->name(), false);
1393 plist.add (Properties::length, after);
1394 plist.add (Properties::name, after_name);
1395 plist.add (Properties::right_of_split, true);
1397 /* same note as above */
1398 right = RegionFactory::create (region, before, plist);
1401 add_region_internal (left, region->position());
1402 add_region_internal (right, region->position() + before);
1404 uint64_t orig_layer_op = region->last_layer_op();
1405 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1406 if ((*i)->last_layer_op() > orig_layer_op) {
1407 (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1411 left->set_last_layer_op ( orig_layer_op );
1412 right->set_last_layer_op ( orig_layer_op + 1);
1416 finalize_split_region (region, left, right);
1418 remove_region_internal (region);
1424 Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1426 if (_splicing || in_set_state) {
1427 /* don't respond to splicing moves or state setting */
1431 if (_edit_mode == Splice) {
1432 splice_locked (at, distance, exclude);
1437 Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1439 if (_splicing || in_set_state) {
1440 /* don't respond to splicing moves or state setting */
1444 if (_edit_mode == Splice) {
1445 splice_unlocked (at, distance, exclude);
1450 Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1453 RegionLock rl (this);
1454 core_splice (at, distance, exclude);
1459 Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1461 core_splice (at, distance, exclude);
1465 Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1469 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1471 if (exclude && (*i) == exclude) {
1475 if ((*i)->position() >= at) {
1476 framepos_t new_pos = (*i)->position() + distance;
1479 } else if (new_pos >= max_frames - (*i)->length()) {
1480 new_pos = max_frames - (*i)->length();
1483 (*i)->set_position (new_pos, this);
1489 notify_length_changed ();
1493 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1495 if (in_set_state || _splicing || _nudging || _shuffling) {
1499 if (what_changed.contains (Properties::position)) {
1501 /* remove it from the list then add it back in
1502 the right place again.
1505 RegionSortByPosition cmp;
1507 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1509 if (i == regions.end()) {
1510 /* the region bounds are being modified but its not currently
1511 in the region list. we will use its bounds correctly when/if
1518 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1521 if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1523 frameoffset_t delta = 0;
1525 if (what_changed.contains (Properties::position)) {
1526 delta = region->position() - region->last_position();
1529 if (what_changed.contains (Properties::length)) {
1530 delta += region->length() - region->last_length();
1534 possibly_splice (region->last_position() + region->last_length(), delta, region);
1537 if (holding_state ()) {
1538 pending_bounds.push_back (region);
1540 if (_session.config.get_layer_model() == MoveAddHigher) {
1541 /* it moved or changed length, so change the timestamp */
1542 timestamp_layer_op (region);
1545 notify_length_changed ();
1547 check_dependents (region, false);
1553 Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1555 boost::shared_ptr<Region> region (weak_region.lock());
1561 /* this makes a virtual call to the right kind of playlist ... */
1563 region_changed (what_changed, region);
1567 Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1569 PropertyChange our_interests;
1570 PropertyChange bounds;
1571 PropertyChange pos_and_length;
1574 if (in_set_state || in_flush) {
1578 our_interests.add (Properties::muted);
1579 our_interests.add (Properties::layer);
1580 our_interests.add (Properties::opaque);
1582 bounds.add (Properties::start);
1583 bounds.add (Properties::position);
1584 bounds.add (Properties::length);
1586 pos_and_length.add (Properties::position);
1587 pos_and_length.add (Properties::length);
1589 if (what_changed.contains (bounds)) {
1590 region_bounds_changed (what_changed, region);
1591 save = !(_splicing || _nudging);
1594 if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
1595 check_dependents (region, false);
1598 if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1599 notify_region_moved (region);
1603 /* don't notify about layer changes, since we are the only object that can initiate
1604 them, and we notify in ::relayer()
1607 if (what_changed.contains (our_interests)) {
1615 Playlist::drop_regions ()
1617 RegionLock rl (this);
1619 all_regions.clear ();
1623 Playlist::clear (bool with_signals)
1626 RegionLock rl (this);
1628 region_state_changed_connections.drop_connections ();
1630 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1631 pending_removes.insert (*i);
1636 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1637 remove_dependents (*s);
1643 for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1644 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1647 pending_removes.clear ();
1648 pending_length = false;
1650 pending_contents_change = false;
1656 /***********************************************************************
1658 **********************************************************************/
1660 Playlist::RegionList *
1661 Playlist::regions_at (framepos_t frame)
1664 RegionLock rlock (this);
1665 return find_regions_at (frame);
1669 Playlist::count_regions_at (framepos_t frame)
1671 RegionLock rlock (this);
1674 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1675 if ((*i)->covers (frame)) {
1683 boost::shared_ptr<Region>
1684 Playlist::top_region_at (framepos_t frame)
1687 RegionLock rlock (this);
1688 RegionList *rlist = find_regions_at (frame);
1689 boost::shared_ptr<Region> region;
1691 if (rlist->size()) {
1692 RegionSortByLayer cmp;
1694 region = rlist->back();
1701 boost::shared_ptr<Region>
1702 Playlist::top_unmuted_region_at (framepos_t frame)
1705 RegionLock rlock (this);
1706 RegionList *rlist = find_regions_at (frame);
1708 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1710 RegionList::iterator tmp = i;
1713 if ((*i)->muted()) {
1720 boost::shared_ptr<Region> region;
1722 if (rlist->size()) {
1723 RegionSortByLayer cmp;
1725 region = rlist->back();
1732 Playlist::RegionList*
1733 Playlist::regions_to_read (framepos_t start, framepos_t end)
1735 /* Caller must hold lock */
1737 RegionList covering;
1738 set<framepos_t> to_check;
1739 set<boost::shared_ptr<Region> > unique;
1741 to_check.insert (start);
1742 to_check.insert (end);
1744 DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
1746 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1748 /* find all/any regions that span start+end */
1750 switch ((*i)->coverage (start, end)) {
1754 case OverlapInternal:
1755 covering.push_back (*i);
1756 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
1760 to_check.insert ((*i)->position());
1761 if ((*i)->position() != 0) {
1762 to_check.insert ((*i)->position()-1);
1764 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1765 covering.push_back (*i);
1769 to_check.insert ((*i)->last_frame());
1770 to_check.insert ((*i)->last_frame()+1);
1771 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
1772 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1773 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1774 covering.push_back (*i);
1777 case OverlapExternal:
1778 covering.push_back (*i);
1779 to_check.insert ((*i)->position());
1780 if ((*i)->position() != 0) {
1781 to_check.insert ((*i)->position()-1);
1783 to_check.insert ((*i)->last_frame());
1784 to_check.insert ((*i)->last_frame()+1);
1785 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
1786 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
1787 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
1791 /* don't go too far */
1793 if ((*i)->position() > end) {
1798 RegionList* rlist = new RegionList;
1800 /* find all the regions that cover each position .... */
1802 if (covering.size() == 1) {
1804 rlist->push_back (covering.front());
1805 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
1810 for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1814 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
1816 for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1818 if ((*x)->covers (*t)) {
1819 here.push_back (*x);
1820 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
1824 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
1831 RegionSortByLayer cmp;
1834 /* ... and get the top/transparent regions at "here" */
1836 for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1840 if ((*c)->opaque()) {
1842 /* the other regions at this position are hidden by this one */
1843 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
1850 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1851 rlist->push_back (*s);
1854 if (rlist->size() > 1) {
1855 /* now sort by time order */
1857 RegionSortByPosition cmp;
1862 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
1867 Playlist::RegionList *
1868 Playlist::find_regions_at (framepos_t frame)
1870 /* Caller must hold lock */
1872 RegionList *rlist = new RegionList;
1874 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1875 if ((*i)->covers (frame)) {
1876 rlist->push_back (*i);
1883 Playlist::RegionList *
1884 Playlist::regions_touched (framepos_t start, framepos_t end)
1886 RegionLock rlock (this);
1887 RegionList *rlist = new RegionList;
1889 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1890 if ((*i)->coverage (start, end) != OverlapNone) {
1891 rlist->push_back (*i);
1899 Playlist::find_next_transient (framepos_t from, int dir)
1901 RegionLock rlock (this);
1902 AnalysisFeatureList points;
1903 AnalysisFeatureList these_points;
1905 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1907 if ((*i)->last_frame() < from) {
1911 if ((*i)->first_frame() > from) {
1916 (*i)->get_transients (these_points);
1918 /* add first frame, just, err, because */
1920 these_points.push_back ((*i)->first_frame());
1922 points.insert (points.end(), these_points.begin(), these_points.end());
1923 these_points.clear ();
1926 if (points.empty()) {
1930 TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1931 bool reached = false;
1934 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1939 if (reached && (*x) > from) {
1944 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1949 if (reached && (*x) < from) {
1958 boost::shared_ptr<Region>
1959 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1961 RegionLock rlock (this);
1962 boost::shared_ptr<Region> ret;
1963 framepos_t closest = max_frames;
1965 bool end_iter = false;
1967 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1971 frameoffset_t distance;
1972 boost::shared_ptr<Region> r = (*i);
1977 pos = r->first_frame ();
1980 pos = r->last_frame ();
1983 pos = r->sync_position ();
1984 // r->adjust_to_sync (r->first_frame());
1989 case 1: /* forwards */
1992 if ((distance = pos - frame) < closest) {
2001 default: /* backwards */
2004 if ((distance = frame - pos) < closest) {
2021 Playlist::find_next_region_boundary (framepos_t frame, int dir)
2023 RegionLock rlock (this);
2025 framepos_t closest = max_frames;
2026 framepos_t ret = -1;
2030 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2032 boost::shared_ptr<Region> r = (*i);
2033 frameoffset_t distance;
2035 if (r->first_frame() > frame) {
2037 distance = r->first_frame() - frame;
2039 if (distance < closest) {
2040 ret = r->first_frame();
2045 if (r->last_frame () > frame) {
2047 distance = r->last_frame () - frame;
2049 if (distance < closest) {
2050 ret = r->last_frame ();
2058 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2060 boost::shared_ptr<Region> r = (*i);
2061 frameoffset_t distance;
2063 if (r->last_frame() < frame) {
2065 distance = frame - r->last_frame();
2067 if (distance < closest) {
2068 ret = r->last_frame();
2073 if (r->first_frame() < frame) {
2075 distance = frame - r->first_frame();
2077 if (distance < closest) {
2078 ret = r->first_frame();
2089 /***********************************************************************/
2095 Playlist::mark_session_dirty ()
2097 if (!in_set_state && !holding_state ()) {
2098 _session.set_dirty();
2103 Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
2105 RegionLock rlock (const_cast<Playlist *> (this));
2106 Stateful::rdiff (cmds);
2110 Playlist::clear_owned_changes ()
2112 RegionLock rlock (this);
2113 Stateful::clear_owned_changes ();
2117 Playlist::update (const RegionListProperty::ChangeRecord& change)
2119 DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2120 name(), change.added.size(), change.removed.size()));
2123 /* add the added regions */
2124 for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
2125 add_region ((*i), (*i)->position());
2127 /* remove the removed regions */
2128 for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2136 Playlist::set_state (const XMLNode& node, int version)
2140 XMLNodeConstIterator niter;
2141 XMLPropertyList plist;
2142 XMLPropertyConstIterator piter;
2144 boost::shared_ptr<Region> region;
2149 if (node.name() != "Playlist") {
2156 plist = node.properties();
2158 for (piter = plist.begin(); piter != plist.end(); ++piter) {
2162 if (prop->name() == X_("name")) {
2163 _name = prop->value();
2165 } else if (prop->name() == X_("id")) {
2166 _id = prop->value();
2167 } else if (prop->name() == X_("orig_diskstream_id")) {
2168 _orig_diskstream_id = prop->value ();
2169 } else if (prop->name() == X_("frozen")) {
2170 _frozen = string_is_affirmative (prop->value());
2176 nlist = node.children();
2178 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2182 if (child->name() == "Region") {
2184 if ((prop = child->property ("id")) == 0) {
2185 error << _("region state node has no ID, ignored") << endmsg;
2189 ID id = prop->value ();
2191 if ((region = region_by_id (id))) {
2193 region->suspend_property_changes ();
2195 if (region->set_state (*child, version)) {
2196 region->resume_property_changes ();
2200 } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2201 region->suspend_property_changes ();
2203 error << _("Playlist: cannot create region from XML") << endmsg;
2208 add_region (region, region->position(), 1.0);
2210 // So that layer_op ordering doesn't get screwed up
2211 region->set_last_layer_op( region->layer());
2212 region->resume_property_changes ();
2216 /* update dependents, which was not done during add_region_internal
2217 due to in_set_state being true
2220 for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
2221 check_dependents (*r, false);
2225 notify_contents_changed ();
2228 first_set_state = false;
2233 Playlist::get_state()
2235 return state (true);
2239 Playlist::get_template()
2241 return state (false);
2244 /** @param full_state true to include regions in the returned state, otherwise false.
2247 Playlist::state (bool full_state)
2249 XMLNode *node = new XMLNode (X_("Playlist"));
2252 node->add_property (X_("id"), id().to_s());
2253 node->add_property (X_("name"), _name);
2254 node->add_property (X_("type"), _type.to_string());
2256 _orig_diskstream_id.print (buf, sizeof (buf));
2257 node->add_property (X_("orig_diskstream_id"), buf);
2258 node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2261 RegionLock rlock (this, false);
2263 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2264 node->add_child_nocopy ((*i)->get_state());
2269 node->add_child_copy (*_extra_xml);
2276 Playlist::empty() const
2278 RegionLock rlock (const_cast<Playlist *>(this), false);
2279 return regions.empty();
2283 Playlist::n_regions() const
2285 RegionLock rlock (const_cast<Playlist *>(this), false);
2286 return regions.size();
2289 pair<framecnt_t, framecnt_t>
2290 Playlist::get_extent () const
2292 RegionLock rlock (const_cast<Playlist *>(this), false);
2293 return _get_extent ();
2296 pair<framecnt_t, framecnt_t>
2297 Playlist::_get_extent () const
2299 pair<framecnt_t, framecnt_t> ext (max_frames, 0);
2301 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2302 pair<framecnt_t, framecnt_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2303 if (e.first < ext.first) {
2304 ext.first = e.first;
2306 if (e.second > ext.second) {
2307 ext.second = e.second;
2315 Playlist::bump_name (string name, Session &session)
2317 string newname = name;
2320 newname = bump_name_once (newname, '.');
2321 } while (session.playlists->by_name (newname)!=NULL);
2328 Playlist::top_layer() const
2330 RegionLock rlock (const_cast<Playlist *> (this));
2333 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2334 top = max (top, (*i)->layer());
2340 Playlist::set_edit_mode (EditMode mode)
2345 /********************
2347 ********************/
2350 Playlist::relayer ()
2352 /* never compute layers when changing state for undo/redo or setting from XML */
2354 if (in_update || in_set_state) {
2358 bool changed = false;
2360 /* Build up a new list of regions on each layer, stored in a set of lists
2361 each of which represent some period of time on some layer. The idea
2362 is to avoid having to search the entire region list to establish whether
2363 each region overlaps another */
2365 /* how many pieces to divide this playlist's time up into */
2366 int const divisions = 512;
2368 /* find the start and end positions of the regions on this playlist */
2369 framepos_t start = INT64_MAX;
2371 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2372 start = min (start, (*i)->position());
2373 end = max (end, (*i)->position() + (*i)->length());
2376 /* hence the size of each time division */
2377 double const division_size = (end - start) / double (divisions);
2379 vector<vector<RegionList> > layers;
2380 layers.push_back (vector<RegionList> (divisions));
2382 /* we want to go through regions from desired lowest to desired highest layer,
2383 which depends on the layer model
2386 RegionList copy = regions.rlist();
2388 /* sort according to the model and the layering mode that we're in */
2390 if (_explicit_relayering) {
2392 copy.sort (RegionSortByLayerWithPending ());
2394 } else if (_session.config.get_layer_model() == MoveAddHigher || _session.config.get_layer_model() == AddHigher) {
2396 copy.sort (RegionSortByLastLayerOp ());
2401 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2403 /* reset the pending explicit relayer flag for every region, now that we're relayering */
2404 (*i)->set_pending_explicit_relayer (false);
2406 /* find the time divisions that this region covers; if there are no regions on the list,
2407 division_size will equal 0 and in this case we'll just say that
2408 start_division = end_division = 0.
2410 int start_division = 0;
2411 int end_division = 0;
2413 if (division_size > 0) {
2414 start_division = floor ( ((*i)->position() - start) / division_size);
2415 end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2416 if (end_division == divisions) {
2421 assert (divisions == 0 || end_division < divisions);
2423 /* find the lowest layer that this region can go on */
2424 size_t j = layers.size();
2426 /* try layer j - 1; it can go on if it overlaps no other region
2427 that is already on that layer
2430 bool overlap = false;
2431 for (int k = start_division; k <= end_division; ++k) {
2432 RegionList::iterator l = layers[j-1][k].begin ();
2433 while (l != layers[j-1][k].end()) {
2434 if ((*l)->overlap_equivalent (*i)) {
2447 /* overlap, so we must use layer j */
2454 if (j == layers.size()) {
2455 /* we need a new layer for this region */
2456 layers.push_back (vector<RegionList> (divisions));
2459 /* put a reference to this region in each of the divisions that it exists in */
2460 for (int k = start_division; k <= end_division; ++k) {
2461 layers[j][k].push_back (*i);
2464 if ((*i)->layer() != j) {
2468 (*i)->set_layer (j);
2472 notify_layering_changed ();
2476 /* XXX these layer functions are all deprecated */
2479 Playlist::raise_region (boost::shared_ptr<Region> region)
2481 uint32_t top = regions.size() - 1;
2482 layer_t target = region->layer() + 1U;
2484 if (target >= top) {
2485 /* its already at the effective top */
2489 move_region_to_layer (target, region, 1);
2493 Playlist::lower_region (boost::shared_ptr<Region> region)
2495 if (region->layer() == 0) {
2496 /* its already at the bottom */
2500 layer_t target = region->layer() - 1U;
2502 move_region_to_layer (target, region, -1);
2506 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2508 /* does nothing useful if layering mode is later=higher */
2509 switch (_session.config.get_layer_model()) {
2516 layer_t top = regions.size() - 1;
2518 if (region->layer() >= top) {
2519 /* already on the top */
2523 move_region_to_layer (top, region, 1);
2524 /* mark the region's last_layer_op as now, so that it remains on top when
2525 doing future relayers (until something else takes over)
2527 timestamp_layer_op (region);
2531 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2533 /* does nothing useful if layering mode is later=higher */
2534 switch (_session.config.get_layer_model()) {
2541 if (region->layer() == 0) {
2542 /* already on the bottom */
2546 move_region_to_layer (0, region, -1);
2547 /* force region's last layer op to zero so that it stays at the bottom
2548 when doing future relayers
2550 region->set_last_layer_op (0);
2554 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
2556 RegionList::iterator i;
2557 typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
2558 list<LayerInfo> layerinfo;
2561 RegionLock rlock (const_cast<Playlist *> (this));
2563 for (i = regions.begin(); i != regions.end(); ++i) {
2573 /* region is moving up, move all regions on intermediate layers
2577 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
2578 dest = (*i)->layer() - 1;
2585 /* region is moving down, move all regions on intermediate layers
2589 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
2590 dest = (*i)->layer() + 1;
2600 newpair.second = dest;
2602 layerinfo.push_back (newpair);
2606 /* now reset the layers without holding the region lock */
2608 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2609 x->first->set_layer (x->second);
2612 region->set_layer (target_layer);
2615 /* now check all dependents */
2617 for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
2618 check_dependents (x->first, false);
2621 check_dependents (region, false);
2628 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2630 RegionList::iterator i;
2636 RegionLock rlock (const_cast<Playlist *> (this));
2638 for (i = regions.begin(); i != regions.end(); ++i) {
2640 if ((*i)->position() >= start) {
2646 if ((*i)->last_frame() > max_frames - distance) {
2647 new_pos = max_frames - (*i)->length();
2649 new_pos = (*i)->position() + distance;
2654 if ((*i)->position() > distance) {
2655 new_pos = (*i)->position() - distance;
2661 (*i)->set_position (new_pos, this);
2669 notify_length_changed ();
2674 boost::shared_ptr<Region>
2675 Playlist::find_region (const ID& id) const
2677 RegionLock rlock (const_cast<Playlist*> (this));
2679 /* searches all regions currently in use by the playlist */
2681 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2682 if ((*i)->id() == id) {
2687 return boost::shared_ptr<Region> ();
2690 boost::shared_ptr<Region>
2691 Playlist::region_by_id (const ID& id) const
2693 /* searches all regions ever added to this playlist */
2695 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2696 if ((*i)->id() == id) {
2700 return boost::shared_ptr<Region> ();
2704 Playlist::dump () const
2706 boost::shared_ptr<Region> r;
2708 cerr << "Playlist \"" << _name << "\" " << endl
2709 << regions.size() << " regions "
2712 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2714 cerr << " " << r->name() << " ["
2715 << r->start() << "+" << r->length()
2725 Playlist::set_frozen (bool yn)
2731 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2733 region->set_last_layer_op (++layer_op_counter);
2738 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2742 if (region->locked()) {
2749 RegionLock rlock (const_cast<Playlist*> (this));
2754 RegionList::iterator next;
2756 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2757 if ((*i) == region) {
2761 if (next != regions.end()) {
2763 if ((*next)->locked()) {
2769 if ((*next)->position() != region->last_frame() + 1) {
2770 /* they didn't used to touch, so after shuffle,
2771 just have them swap positions.
2773 new_pos = (*next)->position();
2775 /* they used to touch, so after shuffle,
2776 make sure they still do. put the earlier
2777 region where the later one will end after
2780 new_pos = region->position() + (*next)->length();
2783 (*next)->set_position (region->position(), this);
2784 region->set_position (new_pos, this);
2786 /* avoid a full sort */
2788 regions.erase (i); // removes the region from the list */
2790 regions.insert (next, region); // adds it back after next
2799 RegionList::iterator prev = regions.end();
2801 for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2802 if ((*i) == region) {
2804 if (prev != regions.end()) {
2806 if ((*prev)->locked()) {
2811 if (region->position() != (*prev)->last_frame() + 1) {
2812 /* they didn't used to touch, so after shuffle,
2813 just have them swap positions.
2815 new_pos = region->position();
2817 /* they used to touch, so after shuffle,
2818 make sure they still do. put the earlier
2819 one where the later one will end after
2821 new_pos = (*prev)->position() + region->length();
2824 region->set_position ((*prev)->position(), this);
2825 (*prev)->set_position (new_pos, this);
2827 /* avoid a full sort */
2829 regions.erase (i); // remove region
2830 regions.insert (prev, region); // insert region before prev
2846 check_dependents (region, false);
2848 notify_contents_changed();
2854 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2856 RegionLock rlock (const_cast<Playlist*> (this));
2858 if (regions.size() > 1) {
2866 Playlist::update_after_tempo_map_change ()
2868 RegionLock rlock (const_cast<Playlist*> (this));
2869 RegionList copy (regions.rlist());
2873 for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2874 (*i)->update_position_after_tempo_map_change ();
2881 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2883 RegionLock rl (this, false);
2884 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2890 Playlist::set_explicit_relayering (bool e)
2892 if (e == false && _explicit_relayering == true) {
2894 /* We are changing from explicit to implicit relayering; layering may have been changed whilst
2895 we were in explicit mode, and we don't want that to be undone next time an implicit relayer
2896 occurs. Hence now we'll set up region last_layer_op values so that an implicit relayer
2897 at this point would keep regions on the same layers.
2899 From then on in, it's just you and your towel.
2902 RegionLock rl (this);
2903 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2904 (*i)->set_last_layer_op ((*i)->layer ());
2908 _explicit_relayering = e;
2913 Playlist::has_region_at (framepos_t const p) const
2915 RegionLock (const_cast<Playlist *> (this));
2917 RegionList::const_iterator i = regions.begin ();
2918 while (i != regions.end() && !(*i)->covers (p)) {
2922 return (i != regions.end());
2925 /** Remove any region that uses a given source */
2927 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2929 RegionLock rl (this);
2931 RegionList::iterator i = regions.begin();
2932 while (i != regions.end()) {
2933 RegionList::iterator j = i;
2936 if ((*i)->uses_source (s)) {
2937 remove_region_internal (*i);